diff --git a/src/alloy/backend/ivm/ivm_function.cc b/src/alloy/backend/ivm/ivm_function.cc index e4bcaca61..0782f3a4b 100644 --- a/src/alloy/backend/ivm/ivm_function.cc +++ b/src/alloy/backend/ivm/ivm_function.cc @@ -34,7 +34,7 @@ void IVMFunction::Setup(TranslationContext& ctx) { intcodes_ = (IntCode*)ctx.intcode_arena->CloneContents(); } -int IVMFunction::CallImpl(ThreadState* thread_state) { +int IVMFunction::CallImpl(ThreadState* thread_state, uint64_t return_address) { // Setup register file on stack. size_t register_file_size = register_count_ * sizeof(Register); Register* register_file = (Register*)alloca(register_file_size); @@ -46,7 +46,8 @@ int IVMFunction::CallImpl(ThreadState* thread_state) { ics.did_carry = 0; ics.access_callbacks = thread_state->runtime()->access_callbacks(); ics.thread_state = thread_state; - ics.return_address = 0xBEBEBEBE; + ics.return_address = return_address; + ics.call_return_address = 0; // TODO(benvanik): DID_CARRY -- need HIR to set a OPCODE_FLAG_SET_CARRY // or something so the fns can set an ics flag. diff --git a/src/alloy/backend/ivm/ivm_function.h b/src/alloy/backend/ivm/ivm_function.h index c9fde00f3..369fd123a 100644 --- a/src/alloy/backend/ivm/ivm_function.h +++ b/src/alloy/backend/ivm/ivm_function.h @@ -29,7 +29,8 @@ public: void Setup(TranslationContext& ctx); protected: - virtual int CallImpl(runtime::ThreadState* thread_state); + virtual int CallImpl(runtime::ThreadState* thread_state, + uint64_t return_address); private: diff --git a/src/alloy/backend/ivm/ivm_intcode.cc b/src/alloy/backend/ivm/ivm_intcode.cc index 4735d8ccd..d4f98d19a 100644 --- a/src/alloy/backend/ivm/ivm_intcode.cc +++ b/src/alloy/backend/ivm/ivm_intcode.cc @@ -523,10 +523,13 @@ int Translate_TRAP_TRUE(TranslationContext& ctx, Instr* i) { uint32_t IntCode_CALL_XX(IntCodeState& ics, const IntCode* i, uint32_t reg) { FunctionInfo* symbol_info = (FunctionInfo*)ics.rf[reg].u64; - Function* fn; + Function* fn = NULL; ics.thread_state->runtime()->ResolveFunction(symbol_info->address(), &fn); + XEASSERTNOTNULL(fn); // TODO(benvanik): proper tail call support, somehow. - fn->Call(ics.thread_state); + uint64_t return_address = + (i->flags & CALL_TAIL) ? ics.return_address : ics.call_return_address; + fn->Call(ics.thread_state, return_address); if (i->flags & CALL_TAIL) { return IA_RETURN; } @@ -589,16 +592,21 @@ int Translate_CALL_TRUE(TranslationContext& ctx, Instr* i) { } uint32_t IntCode_CALL_INDIRECT_XX(IntCodeState& ics, const IntCode* i, uint32_t reg) { + uint64_t target = ics.rf[reg].u64; + // Check if return address - if so, return. - /*if (ics.rf[reg].u64 == ics.return_address) { + if (target == ics.return_address) { return IA_RETURN; - }*/ + } // Real call. - Function* fn; - ics.thread_state->runtime()->ResolveFunction(ics.rf[reg].u64, &fn); + Function* fn = NULL; + ics.thread_state->runtime()->ResolveFunction(target, &fn); + XEASSERTNOTNULL(fn); // TODO(benvanik): proper tail call support, somehow. - fn->Call(ics.thread_state); + uint64_t return_address = + (i->flags & CALL_TAIL) ? ics.return_address : ics.call_return_address; + fn->Call(ics.thread_state, return_address); if (i->flags & CALL_TAIL) { return IA_RETURN; } @@ -667,6 +675,14 @@ int Translate_RETURN(TranslationContext& ctx, Instr* i) { return DispatchToC(ctx, i, IntCode_RETURN); } +uint32_t IntCode_SET_RETURN_ADDRESS(IntCodeState& ics, const IntCode* i) { + ics.call_return_address = ics.rf[i->src1_reg].u32; + return IA_NEXT; +} +int Translate_SET_RETURN_ADDRESS(TranslationContext& ctx, Instr* i) { + return DispatchToC(ctx, i, IntCode_SET_RETURN_ADDRESS); +} + uint32_t IntCode_BRANCH_XX(IntCodeState& ics, const IntCode* i, uint32_t reg) { return ics.rf[reg].u32; } @@ -1167,6 +1183,7 @@ uint32_t IntCode_LOAD_I32(IntCodeState& ics, const IntCode* i) { if (DYNAMIC_REGISTER_ACCESS_CHECK(address)) { return IntCode_LOAD_REGISTER_I32_DYNAMIC(ics, i); } + DFLUSH(); DPRINT("%d (%X) = load.i32 %.8X\n", *((int32_t*)(ics.membase + address)), *((uint32_t*)(ics.membase + address)), @@ -2827,6 +2844,7 @@ static const TranslateFn dispatch_table[] = { Translate_CALL_INDIRECT, Translate_CALL_INDIRECT_TRUE, Translate_RETURN, + Translate_SET_RETURN_ADDRESS, Translate_BRANCH, Translate_BRANCH_IF, diff --git a/src/alloy/backend/ivm/ivm_intcode.h b/src/alloy/backend/ivm/ivm_intcode.h index e80dd0ee2..68aa69763 100644 --- a/src/alloy/backend/ivm/ivm_intcode.h +++ b/src/alloy/backend/ivm/ivm_intcode.h @@ -47,6 +47,7 @@ typedef struct { runtime::RegisterAccessCallbacks* access_callbacks; runtime::ThreadState* thread_state; uint64_t return_address; + uint64_t call_return_address; } IntCodeState; diff --git a/src/alloy/frontend/ppc/ppc_emit_control.cc b/src/alloy/frontend/ppc/ppc_emit_control.cc index 09d8a1ca8..45b12649b 100644 --- a/src/alloy/frontend/ppc/ppc_emit_control.cc +++ b/src/alloy/frontend/ppc/ppc_emit_control.cc @@ -32,10 +32,12 @@ int InstrEmit_branch( // The docs say always, though... // Note that we do the update before we branch/call as we need it to // be correct for returns. + Value* return_address = f.LoadConstant(cia + 4); + f.SetReturnAddress(return_address); if (lk) { - f.StoreLR(f.LoadConstant(cia + 4)); + f.StoreLR(return_address); } - + if (!lk) { // If LR is not set this call will never return here. call_flags |= CALL_TAIL; @@ -301,8 +303,8 @@ XEEMITTER(bclrx, 0x4C000020, XL )(PPCFunctionBuilder& f, InstrData& i) { if (!i.XL.LK && !ok) { // Return (most likely). // TODO(benvanik): test? ReturnCheck()? - f.Return(); - return 0; + //f.Return(); + //return 0; } return InstrEmit_branch( diff --git a/src/alloy/hir/function_builder.cc b/src/alloy/hir/function_builder.cc index f738ae5d6..c64159aa8 100644 --- a/src/alloy/hir/function_builder.cc +++ b/src/alloy/hir/function_builder.cc @@ -459,6 +459,12 @@ void FunctionBuilder::Return() { EndBlock(); } +void FunctionBuilder::SetReturnAddress(Value* value) { + Instr* i = AppendInstr(OPCODE_SET_RETURN_ADDRESS_info, 0); + i->set_src1(value); + i->src2.value = i->src3.value = NULL; +} + void FunctionBuilder::Branch(Label* label, uint32_t branch_flags) { Instr* i = AppendInstr(OPCODE_BRANCH_info, branch_flags); i->src1.label = label; diff --git a/src/alloy/hir/function_builder.h b/src/alloy/hir/function_builder.h index 931c8c2c1..a1b160796 100644 --- a/src/alloy/hir/function_builder.h +++ b/src/alloy/hir/function_builder.h @@ -70,6 +70,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 SetReturnAddress(Value* value); void Branch(Label* label, uint32_t branch_flags = 0); void BranchIf(Value* cond, Label* true_label, Label* false_label, diff --git a/src/alloy/hir/opcodes.h b/src/alloy/hir/opcodes.h index 071a5f356..00907d789 100644 --- a/src/alloy/hir/opcodes.h +++ b/src/alloy/hir/opcodes.h @@ -75,6 +75,7 @@ enum Opcode { OPCODE_CALL_INDIRECT, OPCODE_CALL_INDIRECT_TRUE, OPCODE_RETURN, + OPCODE_SET_RETURN_ADDRESS, OPCODE_BRANCH, OPCODE_BRANCH_IF, diff --git a/src/alloy/hir/opcodes.inl b/src/alloy/hir/opcodes.inl index 0e150f19e..848c8dd95 100644 --- a/src/alloy/hir/opcodes.inl +++ b/src/alloy/hir/opcodes.inl @@ -74,6 +74,12 @@ DEFINE_OPCODE( OPCODE_SIG_X, OPCODE_FLAG_BRANCH); +DEFINE_OPCODE( + OPCODE_SET_RETURN_ADDRESS, + "set_return_address", + OPCODE_SIG_X_V, + 0); + DEFINE_OPCODE( OPCODE_BRANCH, "branch", diff --git a/src/alloy/runtime/function.cc b/src/alloy/runtime/function.cc index dcead6d8a..c01e039ee 100644 --- a/src/alloy/runtime/function.cc +++ b/src/alloy/runtime/function.cc @@ -23,12 +23,12 @@ Function::Function(Type type, uint64_t address) : Function::~Function() { } -int Function::Call(ThreadState* thread_state) { +int Function::Call(ThreadState* thread_state, uint64_t return_address) { ThreadState* original_thread_state = ThreadState::Get(); if (original_thread_state != thread_state) { ThreadState::Bind(thread_state); } - int result = CallImpl(thread_state); + int result = CallImpl(thread_state, return_address); if (original_thread_state != thread_state) { ThreadState::Bind(original_thread_state); } @@ -44,7 +44,8 @@ ExternFunction::ExternFunction( ExternFunction::~ExternFunction() { } -int ExternFunction::CallImpl(ThreadState* thread_state) { +int ExternFunction::CallImpl(ThreadState* thread_state, + uint64_t return_address) { if (!handler_) { XELOGW("undefined extern call to %.8X", address()); return 0; diff --git a/src/alloy/runtime/function.h b/src/alloy/runtime/function.h index b3bc0509d..0620324d3 100644 --- a/src/alloy/runtime/function.h +++ b/src/alloy/runtime/function.h @@ -34,10 +34,10 @@ public: Type type() const { return type_; } uint64_t address() const { return address_; } - int Call(ThreadState* thread_state); + int Call(ThreadState* thread_state, uint64_t return_address); protected: - virtual int CallImpl(ThreadState* thread_state) = 0; + virtual int CallImpl(ThreadState* thread_state, uint64_t return_address) = 0; protected: Type type_; @@ -57,7 +57,7 @@ public: void* arg1() const { return arg1_; } protected: - virtual int CallImpl(ThreadState* thread_state); + virtual int CallImpl(ThreadState* thread_state, uint64_t return_address); protected: Handler handler_; diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 6cf2b92de..4064062f5 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -117,7 +117,7 @@ int Processor::Execute(XenonThreadState* thread_state, uint64_t address) { context->lr = lr; // Execute the function. - fn->Call(thread_state); + fn->Call(thread_state, lr); return 0; } diff --git a/tools/alloy-sandbox/alloy-sandbox.cc b/tools/alloy-sandbox/alloy-sandbox.cc index fc981cdea..6afe7b75f 100644 --- a/tools/alloy-sandbox/alloy-sandbox.cc +++ b/tools/alloy-sandbox/alloy-sandbox.cc @@ -47,7 +47,7 @@ int alloy_sandbox(int argc, xechar_t** argv) { Function* fn; runtime->ResolveFunction(0x82000000, &fn); - fn->Call(thread_state); + fn->Call(thread_state, 0xBEBEBEBE); delete thread_state;