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);
|
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) {
|
uint32_t IntCode_SET_RETURN_ADDRESS(IntCodeState& ics, const IntCode* i) {
|
||||||
ics.call_return_address = ics.rf[i->src1_reg].u32;
|
ics.call_return_address = ics.rf[i->src1_reg].u32;
|
||||||
return IA_NEXT;
|
return IA_NEXT;
|
||||||
|
@ -3978,6 +4027,7 @@ static const TranslateFn dispatch_table[] = {
|
||||||
Translate_CALL_INDIRECT,
|
Translate_CALL_INDIRECT,
|
||||||
Translate_CALL_INDIRECT_TRUE,
|
Translate_CALL_INDIRECT_TRUE,
|
||||||
Translate_RETURN,
|
Translate_RETURN,
|
||||||
|
Translate_RETURN_TRUE,
|
||||||
Translate_SET_RETURN_ADDRESS,
|
Translate_SET_RETURN_ADDRESS,
|
||||||
|
|
||||||
Translate_BRANCH,
|
Translate_BRANCH,
|
||||||
|
|
|
@ -308,6 +308,17 @@ void alloy::backend::x64::lowering::RegisterSequences(LoweringTable* table) {
|
||||||
return true;
|
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) {
|
table->AddSequence(OPCODE_SET_RETURN_ADDRESS, [](X64Emitter& e, Instr*& i) {
|
||||||
//UNIMPLEMENTED_SEQ();
|
//UNIMPLEMENTED_SEQ();
|
||||||
i = e.Advance(i);
|
i = e.Advance(i);
|
||||||
|
|
|
@ -25,7 +25,8 @@ namespace ppc {
|
||||||
|
|
||||||
int InstrEmit_branch(
|
int InstrEmit_branch(
|
||||||
PPCHIRBuilder& f, const char* src, uint64_t cia,
|
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;
|
uint32_t call_flags = 0;
|
||||||
|
|
||||||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
// 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.
|
// TODO(benvanik): set CALL_TAIL if !lk and the last block in the fn.
|
||||||
// This is almost always a jump to restore gpr.
|
// This is almost always a jump to restore gpr.
|
||||||
|
|
||||||
// TODO(benvanik): detect call-self.
|
|
||||||
|
|
||||||
if (nia->IsConstant()) {
|
if (nia->IsConstant()) {
|
||||||
// Direct branch to address.
|
// Direct branch to address.
|
||||||
// If it's a block inside of ourself, setup a fast jump.
|
// 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;
|
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) {
|
if (label) {
|
||||||
// Branch to label.
|
// Branch to label.
|
||||||
uint32_t branch_flags = 0;
|
uint32_t branch_flags = 0;
|
||||||
|
@ -79,6 +85,39 @@ int InstrEmit_branch(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Indirect branch to pointer.
|
// Indirect branch to pointer.
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Jump to pointer.
|
||||||
if (cond) {
|
if (cond) {
|
||||||
if (!expect_true) {
|
if (!expect_true) {
|
||||||
cond = f.IsFalse(cond);
|
cond = f.IsFalse(cond);
|
||||||
|
@ -88,6 +127,7 @@ int InstrEmit_branch(
|
||||||
f.CallIndirect(nia, call_flags);
|
f.CallIndirect(nia, call_flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -285,32 +325,8 @@ XEEMITTER(bclrx, 0x4C000020, XL )(PPCHIRBuilder& f, InstrData& i) {
|
||||||
expect_true = !not_cond_ok;
|
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(
|
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);
|
int Emit(runtime::FunctionInfo* symbol_info, bool with_debug_info);
|
||||||
|
|
||||||
|
runtime::FunctionInfo* symbol_info() const { return symbol_info_; }
|
||||||
runtime::FunctionInfo* LookupFunction(uint64_t address);
|
runtime::FunctionInfo* LookupFunction(uint64_t address);
|
||||||
Label* LookupLabel(uint64_t address);
|
Label* LookupLabel(uint64_t address);
|
||||||
|
|
||||||
|
|
|
@ -563,6 +563,21 @@ void HIRBuilder::Return() {
|
||||||
EndBlock();
|
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) {
|
void HIRBuilder::SetReturnAddress(Value* value) {
|
||||||
Instr* i = AppendInstr(OPCODE_SET_RETURN_ADDRESS_info, 0);
|
Instr* i = AppendInstr(OPCODE_SET_RETURN_ADDRESS_info, 0);
|
||||||
i->set_src1(value);
|
i->set_src1(value);
|
||||||
|
|
|
@ -75,6 +75,7 @@ public:
|
||||||
void CallIndirect(Value* value, uint32_t call_flags = 0);
|
void CallIndirect(Value* value, uint32_t call_flags = 0);
|
||||||
void CallIndirectTrue(Value* cond, Value* value, uint32_t call_flags = 0);
|
void CallIndirectTrue(Value* cond, Value* value, uint32_t call_flags = 0);
|
||||||
void Return();
|
void Return();
|
||||||
|
void ReturnTrue(Value* cond);
|
||||||
void SetReturnAddress(Value* value);
|
void SetReturnAddress(Value* value);
|
||||||
|
|
||||||
void Branch(Label* label, uint32_t branch_flags = 0);
|
void Branch(Label* label, uint32_t branch_flags = 0);
|
||||||
|
|
|
@ -95,6 +95,7 @@ enum Opcode {
|
||||||
OPCODE_CALL_INDIRECT,
|
OPCODE_CALL_INDIRECT,
|
||||||
OPCODE_CALL_INDIRECT_TRUE,
|
OPCODE_CALL_INDIRECT_TRUE,
|
||||||
OPCODE_RETURN,
|
OPCODE_RETURN,
|
||||||
|
OPCODE_RETURN_TRUE,
|
||||||
OPCODE_SET_RETURN_ADDRESS,
|
OPCODE_SET_RETURN_ADDRESS,
|
||||||
|
|
||||||
OPCODE_BRANCH,
|
OPCODE_BRANCH,
|
||||||
|
|
|
@ -80,6 +80,12 @@ DEFINE_OPCODE(
|
||||||
OPCODE_SIG_X,
|
OPCODE_SIG_X,
|
||||||
OPCODE_FLAG_BRANCH);
|
OPCODE_FLAG_BRANCH);
|
||||||
|
|
||||||
|
DEFINE_OPCODE(
|
||||||
|
OPCODE_RETURN_TRUE,
|
||||||
|
"return_true",
|
||||||
|
OPCODE_SIG_X_V,
|
||||||
|
OPCODE_FLAG_BRANCH);
|
||||||
|
|
||||||
DEFINE_OPCODE(
|
DEFINE_OPCODE(
|
||||||
OPCODE_SET_RETURN_ADDRESS,
|
OPCODE_SET_RETURN_ADDRESS,
|
||||||
"set_return_address",
|
"set_return_address",
|
||||||
|
|
Loading…
Reference in New Issue