Heuristically detecting ret - probably breaks some things.
This commit is contained in:
parent
c74f35552a
commit
8789fd4134
|
@ -728,6 +728,55 @@ int Translate_RETURN(TranslationContext& ctx, Instr* i) {
|
|||
return DispatchToC(ctx, i, IntCode_RETURN);
|
||||
}
|
||||
|
||||
uint32_t IntCode_RETURN_TRUE_I8(IntCodeState& ics, const IntCode* i) {
|
||||
if (ics.rf[i->src1_reg].u8) {
|
||||
return IA_RETURN;
|
||||
}
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_RETURN_TRUE_I16(IntCodeState& ics, const IntCode* i) {
|
||||
if (ics.rf[i->src1_reg].u16) {
|
||||
return IA_RETURN;
|
||||
}
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_RETURN_TRUE_I32(IntCodeState& ics, const IntCode* i) {
|
||||
if (ics.rf[i->src1_reg].u32) {
|
||||
return IA_RETURN;
|
||||
}
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_RETURN_TRUE_I64(IntCodeState& ics, const IntCode* i) {
|
||||
if (ics.rf[i->src1_reg].u64) {
|
||||
return IA_RETURN;
|
||||
}
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_RETURN_TRUE_F32(IntCodeState& ics, const IntCode* i) {
|
||||
if (ics.rf[i->src1_reg].f32) {
|
||||
return IA_RETURN;
|
||||
}
|
||||
return IA_NEXT;
|
||||
}
|
||||
uint32_t IntCode_RETURN_TRUE_F64(IntCodeState& ics, const IntCode* i) {
|
||||
if (ics.rf[i->src1_reg].f64) {
|
||||
return IA_RETURN;
|
||||
}
|
||||
return IA_NEXT;
|
||||
}
|
||||
int Translate_RETURN_TRUE(TranslationContext& ctx, Instr* i) {
|
||||
static IntCodeFn fns[] = {
|
||||
IntCode_RETURN_TRUE_I8,
|
||||
IntCode_RETURN_TRUE_I16,
|
||||
IntCode_RETURN_TRUE_I32,
|
||||
IntCode_RETURN_TRUE_I64,
|
||||
IntCode_RETURN_TRUE_F32,
|
||||
IntCode_RETURN_TRUE_F64,
|
||||
IntCode_INVALID_TYPE,
|
||||
};
|
||||
return DispatchToC(ctx, i, fns[i->src1.value->type]);
|
||||
}
|
||||
|
||||
uint32_t IntCode_SET_RETURN_ADDRESS(IntCodeState& ics, const IntCode* i) {
|
||||
ics.call_return_address = ics.rf[i->src1_reg].u32;
|
||||
return IA_NEXT;
|
||||
|
@ -3978,6 +4027,7 @@ static const TranslateFn dispatch_table[] = {
|
|||
Translate_CALL_INDIRECT,
|
||||
Translate_CALL_INDIRECT_TRUE,
|
||||
Translate_RETURN,
|
||||
Translate_RETURN_TRUE,
|
||||
Translate_SET_RETURN_ADDRESS,
|
||||
|
||||
Translate_BRANCH,
|
||||
|
|
|
@ -308,6 +308,17 @@ void alloy::backend::x64::lowering::RegisterSequences(LoweringTable* table) {
|
|||
return true;
|
||||
});
|
||||
|
||||
table->AddSequence(OPCODE_RETURN_TRUE, [](X64Emitter& e, Instr*& i) {
|
||||
e.inLocalLabel();
|
||||
CheckBoolean(e, i->src1.value);
|
||||
e.jne(".x", e.T_SHORT);
|
||||
e.ret();
|
||||
e.L(".x");
|
||||
e.outLocalLabel();
|
||||
i = e.Advance(i);
|
||||
return true;
|
||||
});
|
||||
|
||||
table->AddSequence(OPCODE_SET_RETURN_ADDRESS, [](X64Emitter& e, Instr*& i) {
|
||||
//UNIMPLEMENTED_SEQ();
|
||||
i = e.Advance(i);
|
||||
|
|
|
@ -25,7 +25,8 @@ namespace ppc {
|
|||
|
||||
int InstrEmit_branch(
|
||||
PPCHIRBuilder& f, const char* src, uint64_t cia,
|
||||
Value* nia, bool lk, Value* cond = NULL, bool expect_true = true) {
|
||||
Value* nia, bool lk, Value* cond = NULL, bool expect_true = true,
|
||||
bool nia_is_lr = false) {
|
||||
uint32_t call_flags = 0;
|
||||
|
||||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||
|
@ -46,13 +47,18 @@ int InstrEmit_branch(
|
|||
// TODO(benvanik): set CALL_TAIL if !lk and the last block in the fn.
|
||||
// This is almost always a jump to restore gpr.
|
||||
|
||||
// TODO(benvanik): detect call-self.
|
||||
|
||||
if (nia->IsConstant()) {
|
||||
// Direct branch to address.
|
||||
// If it's a block inside of ourself, setup a fast jump.
|
||||
// Unless it's to ourselves directly, in which case it's
|
||||
// recursion.
|
||||
uint64_t nia_value = nia->AsUint64() & 0xFFFFFFFF;
|
||||
Label* label = f.LookupLabel(nia_value);
|
||||
bool is_recursion = false;
|
||||
if (nia_value == f.symbol_info()->address() &&
|
||||
lk) {
|
||||
is_recursion = true;
|
||||
}
|
||||
Label* label = is_recursion ? NULL : f.LookupLabel(nia_value);
|
||||
if (label) {
|
||||
// Branch to label.
|
||||
uint32_t branch_flags = 0;
|
||||
|
@ -79,13 +85,47 @@ int InstrEmit_branch(
|
|||
}
|
||||
} else {
|
||||
// Indirect branch to pointer.
|
||||
if (cond) {
|
||||
if (!expect_true) {
|
||||
cond = f.IsFalse(cond);
|
||||
|
||||
// TODO(benvanik): runtime recursion detection?
|
||||
|
||||
// TODO(benvanik): run a DFA pass to see if we can detect whether this is
|
||||
// a normal function return that is pulling the LR from the stack that
|
||||
// it set in the prolog. If so, we can omit the dynamic check!
|
||||
|
||||
//// Dynamic test when branching to LR, which is usually used for the return.
|
||||
//// We only do this if LK=0 as returns wouldn't set LR.
|
||||
//// Ideally it's a return and we can just do a simple ret and be done.
|
||||
//// If it's not, we fall through to the full indirection logic.
|
||||
//if (!lk && reg == kXEPPCRegLR) {
|
||||
// // The return block will spill registers for us.
|
||||
// // TODO(benvanik): 'lr_mismatch' debug info.
|
||||
// // Note: we need to test on *only* the 32-bit target, as the target ptr may
|
||||
// // have garbage in the upper 32 bits.
|
||||
// c.cmp(target.r32(), c.getGpArg(1).r32());
|
||||
// // TODO(benvanik): evaluate hint here.
|
||||
// c.je(e.GetReturnLabel(), kCondHintLikely);
|
||||
//}
|
||||
if (!lk && nia_is_lr) {
|
||||
// Return (most likely).
|
||||
// TODO(benvanik): test? ReturnCheck()?
|
||||
if (cond) {
|
||||
if (!expect_true) {
|
||||
cond = f.IsFalse(cond);
|
||||
}
|
||||
f.ReturnTrue(cond);
|
||||
} else {
|
||||
f.Return();
|
||||
}
|
||||
f.CallIndirectTrue(cond, nia, call_flags);
|
||||
} else {
|
||||
f.CallIndirect(nia, call_flags);
|
||||
// Jump to pointer.
|
||||
if (cond) {
|
||||
if (!expect_true) {
|
||||
cond = f.IsFalse(cond);
|
||||
}
|
||||
f.CallIndirectTrue(cond, nia, call_flags);
|
||||
} else {
|
||||
f.CallIndirect(nia, call_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,32 +325,8 @@ XEEMITTER(bclrx, 0x4C000020, XL )(PPCHIRBuilder& f, InstrData& i) {
|
|||
expect_true = !not_cond_ok;
|
||||
}
|
||||
|
||||
// TODO(benvanik): run a DFA pass to see if we can detect whether this is
|
||||
// a normal function return that is pulling the LR from the stack that
|
||||
// it set in the prolog. If so, we can omit the dynamic check!
|
||||
|
||||
//// Dynamic test when branching to LR, which is usually used for the return.
|
||||
//// We only do this if LK=0 as returns wouldn't set LR.
|
||||
//// Ideally it's a return and we can just do a simple ret and be done.
|
||||
//// If it's not, we fall through to the full indirection logic.
|
||||
//if (!lk && reg == kXEPPCRegLR) {
|
||||
// // The return block will spill registers for us.
|
||||
// // TODO(benvanik): 'lr_mismatch' debug info.
|
||||
// // Note: we need to test on *only* the 32-bit target, as the target ptr may
|
||||
// // have garbage in the upper 32 bits.
|
||||
// c.cmp(target.r32(), c.getGpArg(1).r32());
|
||||
// // TODO(benvanik): evaluate hint here.
|
||||
// c.je(e.GetReturnLabel(), kCondHintLikely);
|
||||
//}
|
||||
if (!i.XL.LK && !ok) {
|
||||
// Return (most likely).
|
||||
// TODO(benvanik): test? ReturnCheck()?
|
||||
//f.Return();
|
||||
//return 0;
|
||||
}
|
||||
|
||||
return InstrEmit_branch(
|
||||
f, "bclrx", i.address, f.LoadLR(), i.XL.LK, ok, expect_true);
|
||||
f, "bclrx", i.address, f.LoadLR(), i.XL.LK, ok, expect_true, true);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ public:
|
|||
|
||||
int Emit(runtime::FunctionInfo* symbol_info, bool with_debug_info);
|
||||
|
||||
runtime::FunctionInfo* symbol_info() const { return symbol_info_; }
|
||||
runtime::FunctionInfo* LookupFunction(uint64_t address);
|
||||
Label* LookupLabel(uint64_t address);
|
||||
|
||||
|
|
|
@ -563,6 +563,21 @@ void HIRBuilder::Return() {
|
|||
EndBlock();
|
||||
}
|
||||
|
||||
void HIRBuilder::ReturnTrue(Value* cond) {
|
||||
if (cond->IsConstant()) {
|
||||
if (cond->IsConstantTrue()) {
|
||||
Return();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_ADDRESS_TYPE(value);
|
||||
Instr* i = AppendInstr(OPCODE_RETURN_TRUE_info, 0);
|
||||
i->set_src1(cond);
|
||||
i->src2.value = i->src3.value = NULL;
|
||||
EndBlock();
|
||||
}
|
||||
|
||||
void HIRBuilder::SetReturnAddress(Value* value) {
|
||||
Instr* i = AppendInstr(OPCODE_SET_RETURN_ADDRESS_info, 0);
|
||||
i->set_src1(value);
|
||||
|
|
|
@ -75,6 +75,7 @@ public:
|
|||
void CallIndirect(Value* value, uint32_t call_flags = 0);
|
||||
void CallIndirectTrue(Value* cond, Value* value, uint32_t call_flags = 0);
|
||||
void Return();
|
||||
void ReturnTrue(Value* cond);
|
||||
void SetReturnAddress(Value* value);
|
||||
|
||||
void Branch(Label* label, uint32_t branch_flags = 0);
|
||||
|
|
|
@ -95,6 +95,7 @@ enum Opcode {
|
|||
OPCODE_CALL_INDIRECT,
|
||||
OPCODE_CALL_INDIRECT_TRUE,
|
||||
OPCODE_RETURN,
|
||||
OPCODE_RETURN_TRUE,
|
||||
OPCODE_SET_RETURN_ADDRESS,
|
||||
|
||||
OPCODE_BRANCH,
|
||||
|
|
|
@ -80,6 +80,12 @@ DEFINE_OPCODE(
|
|||
OPCODE_SIG_X,
|
||||
OPCODE_FLAG_BRANCH);
|
||||
|
||||
DEFINE_OPCODE(
|
||||
OPCODE_RETURN_TRUE,
|
||||
"return_true",
|
||||
OPCODE_SIG_X_V,
|
||||
OPCODE_FLAG_BRANCH);
|
||||
|
||||
DEFINE_OPCODE(
|
||||
OPCODE_SET_RETURN_ADDRESS,
|
||||
"set_return_address",
|
||||
|
|
Loading…
Reference in New Issue