From 8789fd4134097a52b7cdba9ece5a7d805ccf2c5f Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Sun, 26 Jan 2014 01:09:25 -0800 Subject: [PATCH] Heuristically detecting ret - probably breaks some things. --- src/alloy/backend/ivm/ivm_intcode.cc | 50 +++++++++++ .../x64/lowering/lowering_sequences.cc | 11 +++ src/alloy/frontend/ppc/ppc_emit_control.cc | 84 +++++++++++-------- src/alloy/frontend/ppc/ppc_hir_builder.h | 1 + src/alloy/hir/hir_builder.cc | 15 ++++ src/alloy/hir/hir_builder.h | 1 + src/alloy/hir/opcodes.h | 1 + src/alloy/hir/opcodes.inl | 6 ++ 8 files changed, 135 insertions(+), 34 deletions(-) diff --git a/src/alloy/backend/ivm/ivm_intcode.cc b/src/alloy/backend/ivm/ivm_intcode.cc index a987c60e0..9c741818e 100644 --- a/src/alloy/backend/ivm/ivm_intcode.cc +++ b/src/alloy/backend/ivm/ivm_intcode.cc @@ -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, diff --git a/src/alloy/backend/x64/lowering/lowering_sequences.cc b/src/alloy/backend/x64/lowering/lowering_sequences.cc index 90877011f..f7888b635 100644 --- a/src/alloy/backend/x64/lowering/lowering_sequences.cc +++ b/src/alloy/backend/x64/lowering/lowering_sequences.cc @@ -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); diff --git a/src/alloy/frontend/ppc/ppc_emit_control.cc b/src/alloy/frontend/ppc/ppc_emit_control.cc index eb2205baa..0fcead5e8 100644 --- a/src/alloy/frontend/ppc/ppc_emit_control.cc +++ b/src/alloy/frontend/ppc/ppc_emit_control.cc @@ -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); } diff --git a/src/alloy/frontend/ppc/ppc_hir_builder.h b/src/alloy/frontend/ppc/ppc_hir_builder.h index c920576f2..b386d95a1 100644 --- a/src/alloy/frontend/ppc/ppc_hir_builder.h +++ b/src/alloy/frontend/ppc/ppc_hir_builder.h @@ -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); diff --git a/src/alloy/hir/hir_builder.cc b/src/alloy/hir/hir_builder.cc index ae8c5c93a..67900036f 100644 --- a/src/alloy/hir/hir_builder.cc +++ b/src/alloy/hir/hir_builder.cc @@ -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); diff --git a/src/alloy/hir/hir_builder.h b/src/alloy/hir/hir_builder.h index 11ea2b0f6..f18ad2d21 100644 --- a/src/alloy/hir/hir_builder.h +++ b/src/alloy/hir/hir_builder.h @@ -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); diff --git a/src/alloy/hir/opcodes.h b/src/alloy/hir/opcodes.h index 5fbbf198c..646eb659f 100644 --- a/src/alloy/hir/opcodes.h +++ b/src/alloy/hir/opcodes.h @@ -95,6 +95,7 @@ enum Opcode { OPCODE_CALL_INDIRECT, OPCODE_CALL_INDIRECT_TRUE, OPCODE_RETURN, + OPCODE_RETURN_TRUE, OPCODE_SET_RETURN_ADDRESS, OPCODE_BRANCH, diff --git a/src/alloy/hir/opcodes.inl b/src/alloy/hir/opcodes.inl index 42449ca17..e3cc5953c 100644 --- a/src/alloy/hir/opcodes.inl +++ b/src/alloy/hir/opcodes.inl @@ -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",