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;
|
||||
|
||||
|
@ -961,6 +961,15 @@ void spu_recompiler::branch_fixed(u32 target)
|
|||
|
||||
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->jz(local->second);
|
||||
c->lea(addr->r64(), get_pc(target));
|
||||
|
@ -969,14 +978,30 @@ void spu_recompiler::branch_fixed(u32 target)
|
|||
c->mov(*arg0, *cpu);
|
||||
c->call(imm_ptr(&check_state));
|
||||
c->jmp(local->second);
|
||||
|
||||
if (absolute)
|
||||
{
|
||||
c->bind(fail);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto ppptr = !g_cfg.core.spu_verification ? nullptr : m_spurt->make_branch_patchpoint();
|
||||
|
||||
if (absolute)
|
||||
{
|
||||
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->cmp(SPU_OFF_32(state), 0);
|
||||
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);
|
||||
|
||||
if (target != m_pos + 4)
|
||||
{
|
||||
branch_fixed(target);
|
||||
branch_fixed(target, true);
|
||||
m_pos = -1;
|
||||
}
|
||||
}
|
||||
|
||||
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->movdqa(SPU_OFF_128(gpr, op.rt), vr);
|
||||
|
||||
if (target != m_pos + 4)
|
||||
{
|
||||
branch_set_link(m_pos + 4);
|
||||
branch_fixed(target);
|
||||
branch_fixed(target, true);
|
||||
m_pos = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void spu_recompiler::BR(spu_opcode_t op)
|
||||
|
|
|
@ -90,7 +90,7 @@ private:
|
|||
asmjit::X86Mem XmmConst(__m128i data);
|
||||
|
||||
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_set_link(u32 target);
|
||||
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);
|
||||
|
||||
if (target == pos + 4)
|
||||
{
|
||||
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);
|
||||
}
|
||||
LOG_WARNING(SPU, "[0x%x] At 0x%x: indirect branch to 0x%x%s", result[0], pos, target, op.d ? " (D)" : op.e ? " (E)" : "");
|
||||
|
||||
m_targets[pos].push_back(target);
|
||||
|
||||
|
@ -1367,12 +1360,8 @@ const std::vector<u32>& spu_recompiler_base::analyse(const be_t<u32>* ls, u32 en
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (op.d || op.e)
|
||||
{
|
||||
m_entry_info[target / 4] = true;
|
||||
}
|
||||
|
||||
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;
|
||||
values[op.rt] = pos + 4;
|
||||
|
||||
if (target == pos + 4)
|
||||
if (type == spu_itype::BRSL && target == pos + 4)
|
||||
{
|
||||
// Get next instruction address idiom
|
||||
break;
|
||||
|
@ -1616,14 +1605,39 @@ const std::vector<u32>& spu_recompiler_base::analyse(const be_t<u32>* ls, u32 en
|
|||
break;
|
||||
}
|
||||
|
||||
case spu_itype::BR:
|
||||
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::BRNZ:
|
||||
case spu_itype::BRHZ:
|
||||
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)
|
||||
{
|
||||
|
@ -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);
|
||||
add_block(target);
|
||||
|
||||
if (type != spu_itype::BR && type != spu_itype::BRA)
|
||||
if (type != spu_itype::BR)
|
||||
{
|
||||
m_targets[pos].push_back(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:
|
||||
is_call = spu_branch_target(0, op.i16) != ia + 4;
|
||||
break;
|
||||
case spu_itype::BRA:
|
||||
is_call = true;
|
||||
is_tail = true;
|
||||
break;
|
||||
case spu_itype::BISL:
|
||||
case spu_itype::BISLED:
|
||||
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)
|
||||
{
|
||||
case spu_itype::BR:
|
||||
case spu_itype::BRA:
|
||||
case spu_itype::BRNZ:
|
||||
case spu_itype::BRZ:
|
||||
case spu_itype::BRHNZ:
|
||||
case spu_itype::BRHZ:
|
||||
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)
|
||||
{
|
||||
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
|
||||
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;
|
||||
}
|
||||
case spu_itype::BRA:
|
||||
case spu_itype::BRASL:
|
||||
{
|
||||
bb.terminator = term_type::indirect_call;
|
||||
break;
|
||||
}
|
||||
case spu_itype::BI:
|
||||
{
|
||||
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
|
||||
llvm::BasicBlock* add_block(u32 target)
|
||||
llvm::BasicBlock* add_block(u32 target, bool absolute = false)
|
||||
{
|
||||
// Check the predecessor
|
||||
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);
|
||||
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)
|
||||
{
|
||||
// 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 result = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||
m_ir->SetInsertPoint(result);
|
||||
|
||||
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);
|
||||
m_ir->SetInsertPoint(cblock);
|
||||
return result;
|
||||
}
|
||||
|
||||
verify(HERE), !absolute;
|
||||
|
||||
auto& result = m_blocks[target].block;
|
||||
|
||||
if (!result)
|
||||
|
@ -7639,34 +7687,11 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
// Convert an indirect branch into a static one if possible
|
||||
if (const auto _int = llvm::dyn_cast<llvm::ConstantInt>(addr.value); _int && op.opcode)
|
||||
if (llvm::isa<llvm::Constant>(addr.value))
|
||||
{
|
||||
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)
|
||||
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)
|
||||
{
|
||||
|
@ -8011,33 +8036,13 @@ public:
|
|||
|
||||
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));
|
||||
}
|
||||
m_ir->CreateBr(add_block(target, true));
|
||||
}
|
||||
|
||||
void BRASL(spu_opcode_t 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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue