/* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * This software may be used and distributed according to the terms of the GNU * General Public License version 2 or any later version. */ #include #include #include #include "backend_x64/abi.h" #include "backend_x64/block_of_code.h" #include "backend_x64/jitstate.h" #include "common/assert.h" #include "dynarmic/callbacks.h" namespace Dynarmic { namespace BackendX64 { constexpr size_t TOTAL_CODE_SIZE = 128 * 1024 * 1024; constexpr size_t FAR_CODE_OFFSET = 100 * 1024 * 1024; BlockOfCode::BlockOfCode(UserCallbacks cb, LookupBlockCallback lookup_block, void* lookup_block_arg) : Xbyak::CodeGenerator(TOTAL_CODE_SIZE) , cb(cb) , lookup_block(lookup_block) , lookup_block_arg(lookup_block_arg) , constant_pool(this, 256) { GenRunCode(); GenMemoryAccessors(); exception_handler.Register(this); near_code_begin = getCurr(); far_code_begin = getCurr() + FAR_CODE_OFFSET; ClearCache(); } void BlockOfCode::ClearCache() { in_far_code = false; near_code_ptr = near_code_begin; far_code_ptr = far_code_begin; SetCodePtr(near_code_begin); } size_t BlockOfCode::RunCode(JitState* jit_state, size_t cycles_to_run) const { constexpr size_t max_cycles_to_run = static_cast(std::numeric_limitscycles_remaining)>::max()); ASSERT(cycles_to_run <= max_cycles_to_run); jit_state->cycles_remaining = cycles_to_run; run_code(jit_state); return cycles_to_run - jit_state->cycles_remaining; // Return number of cycles actually run. } void BlockOfCode::ReturnFromRunCode(bool MXCSR_switch) { size_t index = 0; if (!MXCSR_switch) index |= NO_SWITCH_MXCSR; jmp(return_from_run_code[index]); } void BlockOfCode::ForceReturnFromRunCode(bool MXCSR_switch) { size_t index = FORCE_RETURN; if (!MXCSR_switch) index |= NO_SWITCH_MXCSR; jmp(return_from_run_code[index]); } void BlockOfCode::GenRunCode() { Xbyak::Label loop; align(); run_code = getCurr(); // This serves two purposes: // 1. It saves all the registers we as a callee need to save. // 2. It aligns the stack so that the code the JIT emits can assume // that the stack is appropriately aligned for CALLs. ABI_PushCalleeSaveRegistersAndAdjustStack(this); mov(r15, ABI_PARAM1); L(loop); mov(ABI_PARAM1, u64(lookup_block_arg)); CallFunction(lookup_block); SwitchMxcsrOnEntry(); jmp(ABI_RETURN); // Return from run code variants const auto emit_return_from_run_code = [this, &loop](bool no_mxcsr_switch, bool force_return){ if (!no_mxcsr_switch) { SwitchMxcsrOnExit(); } if (!force_return) { cmp(qword[r15 + offsetof(JitState, cycles_remaining)], 0); jg(loop); } ABI_PopCalleeSaveRegistersAndAdjustStack(this); ret(); }; align(); return_from_run_code[0] = getCurr(); emit_return_from_run_code(false, false); align(); return_from_run_code[NO_SWITCH_MXCSR] = getCurr(); emit_return_from_run_code(true, false); align(); return_from_run_code[FORCE_RETURN] = getCurr(); emit_return_from_run_code(false, true); align(); return_from_run_code[NO_SWITCH_MXCSR | FORCE_RETURN] = getCurr(); emit_return_from_run_code(true, true); } void BlockOfCode::GenMemoryAccessors() { align(); read_memory_8 = getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(this); CallFunction(cb.memory.Read8); ABI_PopCallerSaveRegistersAndAdjustStack(this); ret(); align(); read_memory_16 = getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(this); CallFunction(cb.memory.Read16); ABI_PopCallerSaveRegistersAndAdjustStack(this); ret(); align(); read_memory_32 = getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(this); CallFunction(cb.memory.Read32); ABI_PopCallerSaveRegistersAndAdjustStack(this); ret(); align(); read_memory_64 = getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(this); CallFunction(cb.memory.Read64); ABI_PopCallerSaveRegistersAndAdjustStack(this); ret(); align(); write_memory_8 = getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(this); CallFunction(cb.memory.Write8); ABI_PopCallerSaveRegistersAndAdjustStack(this); ret(); align(); write_memory_16 = getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(this); CallFunction(cb.memory.Write16); ABI_PopCallerSaveRegistersAndAdjustStack(this); ret(); align(); write_memory_32 = getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(this); CallFunction(cb.memory.Write32); ABI_PopCallerSaveRegistersAndAdjustStack(this); ret(); align(); write_memory_64 = getCurr(); ABI_PushCallerSaveRegistersAndAdjustStack(this); CallFunction(cb.memory.Write64); ABI_PopCallerSaveRegistersAndAdjustStack(this); ret(); } void BlockOfCode::SwitchMxcsrOnEntry() { stmxcsr(dword[r15 + offsetof(JitState, save_host_MXCSR)]); ldmxcsr(dword[r15 + offsetof(JitState, guest_MXCSR)]); } void BlockOfCode::SwitchMxcsrOnExit() { stmxcsr(dword[r15 + offsetof(JitState, guest_MXCSR)]); ldmxcsr(dword[r15 + offsetof(JitState, save_host_MXCSR)]); } Xbyak::Address BlockOfCode::MConst(u64 constant) { return constant_pool.GetConstant(constant); } void BlockOfCode::SwitchToFarCode() { ASSERT(!in_far_code); in_far_code = true; near_code_ptr = getCurr(); SetCodePtr(far_code_ptr); ASSERT_MSG(near_code_ptr < far_code_begin, "Near code has overwritten far code!"); } void BlockOfCode::SwitchToNearCode() { ASSERT(in_far_code); in_far_code = false; far_code_ptr = getCurr(); SetCodePtr(near_code_ptr); } void BlockOfCode::nop(size_t size) { switch (size) { case 0: return; case 1: db(0x90); return; case 2: db(0x66); db(0x90); return; case 3: db(0x0f); db(0x1f); db(0x00); return; case 4: db(0x0f); db(0x1f); db(0x40); db(0x00); return; case 5: db(0x0f); db(0x1f); db(0x44); db(0x00); db(0x00); return; case 6: db(0x66); db(0x0f); db(0x1f); db(0x44); db(0x00); db(0x00); return; case 7: db(0x0f); db(0x1f); db(0x80); db(0x00); db(0x00); db(0x00); db(0x00); return; case 8: db(0x0f); db(0x1f); db(0x84); db(0x00); db(0x00); db(0x00); db(0x00); db(0x00); return; case 9: db(0x66); db(0x0f); db(0x1f); db(0x84); db(0x00); db(0x00); db(0x00); db(0x00); db(0x00); return; case 10: default: db(0x66); db(0x2e); db(0x0f); db(0x1f); db(0x84); db(0x00); db(0x00); db(0x00); db(0x00); db(0x00); nop(size - 10); return; } } void* BlockOfCode::AllocateFromCodeSpace(size_t alloc_size) { if (size_ + alloc_size >= maxSize_) { throw Xbyak::Error(Xbyak::ERR_CODE_IS_TOO_BIG); } void* ret = getCurr(); size_ += alloc_size; memset(ret, 0, alloc_size); return ret; } void BlockOfCode::SetCodePtr(CodePtr code_ptr) { // The "size" defines where top_, the insertion point, is. size_t required_size = reinterpret_cast(code_ptr) - getCode(); setSize(required_size); } void BlockOfCode::EnsurePatchLocationSize(CodePtr begin, size_t size) { size_t current_size = getCurr() - reinterpret_cast(begin); ASSERT(current_size <= size); nop(size - current_size); } } // namespace BackendX64 } // namespace Dynarmic