mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-05 06:21:26 +12:00
SPU LLVM/ASMJIT: fix BRA/BRASL instructions for PIC
Handle absolute branch addressing correctly.
This commit is contained in:
parent
f95ec8a37c
commit
91897fa69d
3 changed files with 110 additions and 86 deletions
|
@ -952,7 +952,7 @@ static void check_state(spu_thread* _spu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void spu_recompiler::branch_fixed(u32 target)
|
void spu_recompiler::branch_fixed(u32 target, bool absolute)
|
||||||
{
|
{
|
||||||
using namespace asmjit;
|
using namespace asmjit;
|
||||||
|
|
||||||
|
@ -961,6 +961,15 @@ void spu_recompiler::branch_fixed(u32 target)
|
||||||
|
|
||||||
if (local != instr_labels.end() && local->second.isValid())
|
if (local != instr_labels.end() && local->second.isValid())
|
||||||
{
|
{
|
||||||
|
Label fail;
|
||||||
|
|
||||||
|
if (absolute)
|
||||||
|
{
|
||||||
|
fail = c->newLabel();
|
||||||
|
c->cmp(pc0->r32(), m_base);
|
||||||
|
c->jne(fail);
|
||||||
|
}
|
||||||
|
|
||||||
c->cmp(SPU_OFF_32(state), 0);
|
c->cmp(SPU_OFF_32(state), 0);
|
||||||
c->jz(local->second);
|
c->jz(local->second);
|
||||||
c->lea(addr->r64(), get_pc(target));
|
c->lea(addr->r64(), get_pc(target));
|
||||||
|
@ -969,14 +978,30 @@ void spu_recompiler::branch_fixed(u32 target)
|
||||||
c->mov(*arg0, *cpu);
|
c->mov(*arg0, *cpu);
|
||||||
c->call(imm_ptr(&check_state));
|
c->call(imm_ptr(&check_state));
|
||||||
c->jmp(local->second);
|
c->jmp(local->second);
|
||||||
return;
|
|
||||||
|
if (absolute)
|
||||||
|
{
|
||||||
|
c->bind(fail);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto ppptr = !g_cfg.core.spu_verification ? nullptr : m_spurt->make_branch_patchpoint();
|
const auto ppptr = !g_cfg.core.spu_verification ? nullptr : m_spurt->make_branch_patchpoint();
|
||||||
|
|
||||||
c->lea(addr->r64(), get_pc(target));
|
if (absolute)
|
||||||
c->and_(*addr, 0x3fffc);
|
{
|
||||||
c->mov(SPU_OFF_32(pc), *addr);
|
c->mov(SPU_OFF_32(pc), target);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c->lea(addr->r64(), get_pc(target));
|
||||||
|
c->and_(*addr, 0x3fffc);
|
||||||
|
c->mov(SPU_OFF_32(pc), *addr);
|
||||||
|
}
|
||||||
|
|
||||||
c->xor_(rip->r32(), rip->r32());
|
c->xor_(rip->r32(), rip->r32());
|
||||||
c->cmp(SPU_OFF_32(state), 0);
|
c->cmp(SPU_OFF_32(state), 0);
|
||||||
c->jnz(label_stop);
|
c->jnz(label_stop);
|
||||||
|
@ -4132,11 +4157,8 @@ void spu_recompiler::BRA(spu_opcode_t op)
|
||||||
{
|
{
|
||||||
const u32 target = spu_branch_target(0, op.i16);
|
const u32 target = spu_branch_target(0, op.i16);
|
||||||
|
|
||||||
if (target != m_pos + 4)
|
branch_fixed(target, true);
|
||||||
{
|
m_pos = -1;
|
||||||
branch_fixed(target);
|
|
||||||
m_pos = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void spu_recompiler::LQA(spu_opcode_t op)
|
void spu_recompiler::LQA(spu_opcode_t op)
|
||||||
|
@ -4170,12 +4192,9 @@ void spu_recompiler::BRASL(spu_opcode_t op)
|
||||||
c->pslldq(vr, 12);
|
c->pslldq(vr, 12);
|
||||||
c->movdqa(SPU_OFF_128(gpr, op.rt), vr);
|
c->movdqa(SPU_OFF_128(gpr, op.rt), vr);
|
||||||
|
|
||||||
if (target != m_pos + 4)
|
branch_set_link(m_pos + 4);
|
||||||
{
|
branch_fixed(target, true);
|
||||||
branch_set_link(m_pos + 4);
|
m_pos = -1;
|
||||||
branch_fixed(target);
|
|
||||||
m_pos = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void spu_recompiler::BR(spu_opcode_t op)
|
void spu_recompiler::BR(spu_opcode_t op)
|
||||||
|
|
|
@ -90,7 +90,7 @@ private:
|
||||||
asmjit::X86Mem XmmConst(__m128i data);
|
asmjit::X86Mem XmmConst(__m128i data);
|
||||||
|
|
||||||
asmjit::X86Mem get_pc(u32 addr);
|
asmjit::X86Mem get_pc(u32 addr);
|
||||||
void branch_fixed(u32 target);
|
void branch_fixed(u32 target, bool absolute = false);
|
||||||
void branch_indirect(spu_opcode_t op, bool jt = false, bool ret = true);
|
void branch_indirect(spu_opcode_t op, bool jt = false, bool ret = true);
|
||||||
void branch_set_link(u32 target);
|
void branch_set_link(u32 target);
|
||||||
void fall(spu_opcode_t op);
|
void fall(spu_opcode_t op);
|
||||||
|
|
|
@ -1344,14 +1344,7 @@ const std::vector<u32>& spu_recompiler_base::analyse(const be_t<u32>* ls, u32 en
|
||||||
{
|
{
|
||||||
const u32 target = spu_branch_target(av);
|
const u32 target = spu_branch_target(av);
|
||||||
|
|
||||||
if (target == pos + 4)
|
LOG_WARNING(SPU, "[0x%x] At 0x%x: indirect branch to 0x%x%s", result[0], pos, target, op.d ? " (D)" : op.e ? " (E)" : "");
|
||||||
{
|
|
||||||
LOG_WARNING(SPU, "[0x%x] At 0x%x: indirect branch to next%s", result[0], pos, op.d ? " (D)" : op.e ? " (E)" : "");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG_WARNING(SPU, "[0x%x] At 0x%x: indirect branch to 0x%x", result[0], pos, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_targets[pos].push_back(target);
|
m_targets[pos].push_back(target);
|
||||||
|
|
||||||
|
@ -1368,11 +1361,7 @@ const std::vector<u32>& spu_recompiler_base::analyse(const be_t<u32>* ls, u32 en
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (op.d || op.e)
|
m_entry_info[target / 4] = true;
|
||||||
{
|
|
||||||
m_entry_info[target / 4] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_block(target);
|
add_block(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1578,7 +1567,7 @@ const std::vector<u32>& spu_recompiler_base::analyse(const be_t<u32>* ls, u32 en
|
||||||
vflags[op.rt] = +vf::is_const;
|
vflags[op.rt] = +vf::is_const;
|
||||||
values[op.rt] = pos + 4;
|
values[op.rt] = pos + 4;
|
||||||
|
|
||||||
if (target == pos + 4)
|
if (type == spu_itype::BRSL && target == pos + 4)
|
||||||
{
|
{
|
||||||
// Get next instruction address idiom
|
// Get next instruction address idiom
|
||||||
break;
|
break;
|
||||||
|
@ -1616,14 +1605,39 @@ const std::vector<u32>& spu_recompiler_base::analyse(const be_t<u32>* ls, u32 en
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case spu_itype::BR:
|
|
||||||
case spu_itype::BRA:
|
case spu_itype::BRA:
|
||||||
|
{
|
||||||
|
const u32 target = spu_branch_target(0, op.i16);
|
||||||
|
|
||||||
|
if (g_cfg.core.spu_block_size == spu_block_size_type::giga && !sync)
|
||||||
|
{
|
||||||
|
m_entry_info[target / 4] = true;
|
||||||
|
add_block(target);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (g_cfg.core.spu_block_size == spu_block_size_type::giga)
|
||||||
|
{
|
||||||
|
LOG_NOTICE(SPU, "[0x%x] At 0x%x: ignoring fixed tail call to 0x%x (SYNC)", result[0], pos, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target > entry_point)
|
||||||
|
{
|
||||||
|
limit = std::min<u32>(limit, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next_block();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case spu_itype::BR:
|
||||||
case spu_itype::BRZ:
|
case spu_itype::BRZ:
|
||||||
case spu_itype::BRNZ:
|
case spu_itype::BRNZ:
|
||||||
case spu_itype::BRHZ:
|
case spu_itype::BRHZ:
|
||||||
case spu_itype::BRHNZ:
|
case spu_itype::BRHNZ:
|
||||||
{
|
{
|
||||||
const u32 target = spu_branch_target(type == spu_itype::BRA ? 0 : pos, op.i16);
|
const u32 target = spu_branch_target(pos, op.i16);
|
||||||
|
|
||||||
if (target == pos + 4)
|
if (target == pos + 4)
|
||||||
{
|
{
|
||||||
|
@ -1634,7 +1648,7 @@ const std::vector<u32>& spu_recompiler_base::analyse(const be_t<u32>* ls, u32 en
|
||||||
m_targets[pos].push_back(target);
|
m_targets[pos].push_back(target);
|
||||||
add_block(target);
|
add_block(target);
|
||||||
|
|
||||||
if (type != spu_itype::BR && type != spu_itype::BRA)
|
if (type != spu_itype::BR)
|
||||||
{
|
{
|
||||||
m_targets[pos].push_back(pos + 4);
|
m_targets[pos].push_back(pos + 4);
|
||||||
add_block(pos + 4);
|
add_block(pos + 4);
|
||||||
|
@ -2193,6 +2207,10 @@ const std::vector<u32>& spu_recompiler_base::analyse(const be_t<u32>* ls, u32 en
|
||||||
case spu_itype::BRASL:
|
case spu_itype::BRASL:
|
||||||
is_call = spu_branch_target(0, op.i16) != ia + 4;
|
is_call = spu_branch_target(0, op.i16) != ia + 4;
|
||||||
break;
|
break;
|
||||||
|
case spu_itype::BRA:
|
||||||
|
is_call = true;
|
||||||
|
is_tail = true;
|
||||||
|
break;
|
||||||
case spu_itype::BISL:
|
case spu_itype::BISL:
|
||||||
case spu_itype::BISLED:
|
case spu_itype::BISLED:
|
||||||
is_call = true;
|
is_call = true;
|
||||||
|
@ -2779,21 +2797,19 @@ const std::vector<u32>& spu_recompiler_base::analyse(const be_t<u32>* ls, u32 en
|
||||||
switch (last_inst)
|
switch (last_inst)
|
||||||
{
|
{
|
||||||
case spu_itype::BR:
|
case spu_itype::BR:
|
||||||
case spu_itype::BRA:
|
|
||||||
case spu_itype::BRNZ:
|
case spu_itype::BRNZ:
|
||||||
case spu_itype::BRZ:
|
case spu_itype::BRZ:
|
||||||
case spu_itype::BRHNZ:
|
case spu_itype::BRHNZ:
|
||||||
case spu_itype::BRHZ:
|
case spu_itype::BRHZ:
|
||||||
case spu_itype::BRSL:
|
case spu_itype::BRSL:
|
||||||
case spu_itype::BRASL:
|
|
||||||
{
|
{
|
||||||
const u32 target = spu_branch_target(last_inst == spu_itype::BRA || last_inst == spu_itype::BRASL ? 0 : tia, op.i16);
|
const u32 target = spu_branch_target(tia, op.i16);
|
||||||
|
|
||||||
if (target == tia + 4)
|
if (target == tia + 4)
|
||||||
{
|
{
|
||||||
bb.terminator = term_type::fallthrough;
|
bb.terminator = term_type::fallthrough;
|
||||||
}
|
}
|
||||||
else if (last_inst != spu_itype::BRSL && last_inst != spu_itype::BRASL)
|
else if (last_inst != spu_itype::BRSL)
|
||||||
{
|
{
|
||||||
// No-op terminator or simple branch instruction
|
// No-op terminator or simple branch instruction
|
||||||
bb.terminator = term_type::br;
|
bb.terminator = term_type::br;
|
||||||
|
@ -2815,6 +2831,12 @@ const std::vector<u32>& spu_recompiler_base::analyse(const be_t<u32>* ls, u32 en
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case spu_itype::BRA:
|
||||||
|
case spu_itype::BRASL:
|
||||||
|
{
|
||||||
|
bb.terminator = term_type::indirect_call;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case spu_itype::BI:
|
case spu_itype::BI:
|
||||||
{
|
{
|
||||||
if (op.d || op.e || bb.targets.size() == 1)
|
if (op.d || op.e || bb.targets.size() == 1)
|
||||||
|
@ -3449,7 +3471,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add block with current block as a predecessor
|
// Add block with current block as a predecessor
|
||||||
llvm::BasicBlock* add_block(u32 target)
|
llvm::BasicBlock* add_block(u32 target, bool absolute = false)
|
||||||
{
|
{
|
||||||
// Check the predecessor
|
// Check the predecessor
|
||||||
const bool pred_found = m_block_info[target / 4] && m_preds[target].find_first_of(m_pos) + 1;
|
const bool pred_found = m_block_info[target / 4] && m_preds[target].find_first_of(m_pos) + 1;
|
||||||
|
@ -3497,6 +3519,19 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||||
m_ir->SetInsertPoint(result);
|
m_ir->SetInsertPoint(result);
|
||||||
const auto pfinfo = add_function(target);
|
const auto pfinfo = add_function(target);
|
||||||
|
|
||||||
|
if (absolute)
|
||||||
|
{
|
||||||
|
verify(HERE), !m_finfo->fn;
|
||||||
|
|
||||||
|
const auto next = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||||
|
const auto fail = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||||
|
m_ir->CreateCondBr(m_ir->CreateICmpEQ(m_base_pc, m_ir->getInt32(m_base)), next, fail);
|
||||||
|
m_ir->SetInsertPoint(fail);
|
||||||
|
m_ir->CreateStore(m_ir->getInt32(target), spu_ptr<u32>(&spu_thread::pc), true);
|
||||||
|
tail_chunk(nullptr);
|
||||||
|
m_ir->SetInsertPoint(next);
|
||||||
|
}
|
||||||
|
|
||||||
if (pfinfo->fn)
|
if (pfinfo->fn)
|
||||||
{
|
{
|
||||||
// Tail call to the real function
|
// Tail call to the real function
|
||||||
|
@ -3525,12 +3560,25 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||||
const auto cblock = m_ir->GetInsertBlock();
|
const auto cblock = m_ir->GetInsertBlock();
|
||||||
const auto result = llvm::BasicBlock::Create(m_context, "", m_function);
|
const auto result = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||||
m_ir->SetInsertPoint(result);
|
m_ir->SetInsertPoint(result);
|
||||||
update_pc(target);
|
|
||||||
|
if (absolute)
|
||||||
|
{
|
||||||
|
verify(HERE), !m_finfo->fn;
|
||||||
|
|
||||||
|
m_ir->CreateStore(m_ir->getInt32(target), spu_ptr<u32>(&spu_thread::pc), true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
update_pc(target);
|
||||||
|
}
|
||||||
|
|
||||||
tail_chunk(nullptr);
|
tail_chunk(nullptr);
|
||||||
m_ir->SetInsertPoint(cblock);
|
m_ir->SetInsertPoint(cblock);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verify(HERE), !absolute;
|
||||||
|
|
||||||
auto& result = m_blocks[target].block;
|
auto& result = m_blocks[target].block;
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
|
@ -7639,34 +7687,11 @@ public:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert an indirect branch into a static one if possible
|
if (llvm::isa<llvm::Constant>(addr.value))
|
||||||
if (const auto _int = llvm::dyn_cast<llvm::ConstantInt>(addr.value); _int && op.opcode)
|
|
||||||
{
|
{
|
||||||
const u32 target = ::narrow<u32>(_int->getZExtValue(), HERE);
|
|
||||||
|
|
||||||
LOG_WARNING(SPU, "[0x%x] Fixed branch to 0x%x", m_pos, target);
|
|
||||||
|
|
||||||
if (!op.e && !op.d)
|
|
||||||
{
|
|
||||||
return add_block(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_entry_info[target / 4])
|
|
||||||
{
|
|
||||||
LOG_ERROR(SPU, "[0x%x] Fixed branch to 0x%x", m_pos, target);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
add_function(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fixed branch excludes the possibility it's a function return (TODO)
|
// Fixed branch excludes the possibility it's a function return (TODO)
|
||||||
ret = false;
|
ret = false;
|
||||||
}
|
}
|
||||||
else if (llvm::isa<llvm::Constant>(addr.value) && op.opcode)
|
|
||||||
{
|
|
||||||
LOG_ERROR(SPU, "[0x%x] Unexpected constant (add_block_indirect)", m_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_finfo && m_finfo->fn && op.opcode)
|
if (m_finfo && m_finfo->fn && op.opcode)
|
||||||
{
|
{
|
||||||
|
@ -8011,33 +8036,13 @@ public:
|
||||||
|
|
||||||
const u32 target = spu_branch_target(0, op.i16);
|
const u32 target = spu_branch_target(0, op.i16);
|
||||||
|
|
||||||
if (target != m_pos + 4)
|
m_block->block_end = m_ir->GetInsertBlock();
|
||||||
{
|
m_ir->CreateBr(add_block(target, true));
|
||||||
m_block->block_end = m_ir->GetInsertBlock();
|
|
||||||
m_ir->CreateBr(add_block(target));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BRASL(spu_opcode_t op) //
|
void BRASL(spu_opcode_t op) //
|
||||||
{
|
{
|
||||||
set_link(op);
|
set_link(op);
|
||||||
|
|
||||||
const u32 target = spu_branch_target(0, op.i16);
|
|
||||||
|
|
||||||
if (m_finfo && m_finfo->fn && target != m_pos + 4)
|
|
||||||
{
|
|
||||||
if (auto fn = add_function(target)->fn)
|
|
||||||
{
|
|
||||||
call_function(fn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG_FATAL(SPU, "[0x%x] Can't add function 0x%x", m_pos, target);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BRA(op);
|
BRA(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue