Heuristically detecting ret - probably breaks some things.

This commit is contained in:
Ben Vanik 2014-01-26 01:09:25 -08:00
parent c74f35552a
commit 8789fd4134
8 changed files with 135 additions and 34 deletions

View File

@ -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,

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -95,6 +95,7 @@ enum Opcode {
OPCODE_CALL_INDIRECT,
OPCODE_CALL_INDIRECT_TRUE,
OPCODE_RETURN,
OPCODE_RETURN_TRUE,
OPCODE_SET_RETURN_ADDRESS,
OPCODE_BRANCH,

View File

@ -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",