1 /* This file is part of the dynarmic project. 2 * Copyright (c) 2016 MerryMage 3 * SPDX-License-Identifier: 0BSD 4 */ 5 6 #pragma once 7 8 #include <array> 9 #include <functional> 10 #include <memory> 11 #include <type_traits> 12 13 #include <xbyak.h> 14 #include <xbyak_util.h> 15 16 #include "backend/x64/callback.h" 17 #include "backend/x64/constant_pool.h" 18 #include "backend/x64/jitstate_info.h" 19 #include "common/cast_util.h" 20 #include "common/common_types.h" 21 22 namespace Dynarmic::Backend::X64 { 23 24 using CodePtr = const void*; 25 26 struct RunCodeCallbacks { 27 std::unique_ptr<Callback> LookupBlock; 28 std::unique_ptr<Callback> AddTicks; 29 std::unique_ptr<Callback> GetTicksRemaining; 30 }; 31 32 class BlockOfCode final : public Xbyak::CodeGenerator { 33 public: 34 BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, std::function<void(BlockOfCode&)> rcp); 35 BlockOfCode(const BlockOfCode&) = delete; 36 37 /// Call when external emitters have finished emitting their preludes. 38 void PreludeComplete(); 39 40 /// Change permissions to RW. This is required to support systems with W^X enforced. 41 void EnableWriting(); 42 /// Change permissions to RX. This is required to support systems with W^X enforced. 43 void DisableWriting(); 44 45 /// Clears this block of code and resets code pointer to beginning. 46 void ClearCache(); 47 /// Calculates how much space is remaining to use. This is the minimum of near code and far code. 48 size_t SpaceRemaining() const; 49 50 /// Runs emulated code from code_ptr. 51 void RunCode(void* jit_state, CodePtr code_ptr) const; 52 /// Runs emulated code from code_ptr for a single cycle. 53 void StepCode(void* jit_state, CodePtr code_ptr) const; 54 /// Code emitter: Returns to dispatcher 55 void ReturnFromRunCode(bool mxcsr_already_exited = false); 56 /// Code emitter: Returns to dispatcher, forces return to host 57 void ForceReturnFromRunCode(bool mxcsr_already_exited = false); 58 /// Code emitter: Makes guest MXCSR the current MXCSR 59 void SwitchMxcsrOnEntry(); 60 /// Code emitter: Makes saved host MXCSR the current MXCSR 61 void SwitchMxcsrOnExit(); 62 /// Code emitter: Enter standard ASIMD MXCSR region 63 void EnterStandardASIMD(); 64 /// Code emitter: Leave standard ASIMD MXCSR region 65 void LeaveStandardASIMD(); 66 /// Code emitter: Updates cycles remaining my calling cb.AddTicks and cb.GetTicksRemaining 67 /// @note this clobbers ABI caller-save registers 68 void UpdateTicks(); 69 /// Code emitter: Performs a block lookup based on current state 70 /// @note this clobbers ABI caller-save registers 71 void LookupBlock(); 72 73 /// Code emitter: Calls the function 74 template <typename FunctionPointer> CallFunction(FunctionPointer fn)75 void CallFunction(FunctionPointer fn) { 76 static_assert(std::is_pointer_v<FunctionPointer> && std::is_function_v<std::remove_pointer_t<FunctionPointer>>, 77 "Supplied type must be a pointer to a function"); 78 79 const u64 address = reinterpret_cast<u64>(fn); 80 const u64 distance = address - (getCurr<u64>() + 5); 81 82 if (distance >= 0x0000000080000000ULL && distance < 0xFFFFFFFF80000000ULL) { 83 // Far call 84 mov(rax, address); 85 call(rax); 86 } else { 87 call(fn); 88 } 89 } 90 91 /// Code emitter: Calls the lambda. Lambda must not have any captures. 92 template <typename Lambda> CallLambda(Lambda l)93 void CallLambda(Lambda l) { 94 CallFunction(Common::FptrCast(l)); 95 } 96 97 Xbyak::Address MConst(const Xbyak::AddressFrame& frame, u64 lower, u64 upper = 0); 98 99 /// Far code sits far away from the near code. Execution remains primarily in near code. 100 /// "Cold" / Rarely executed instructions sit in far code, so the CPU doesn't fetch them unless necessary. 101 void SwitchToFarCode(); 102 void SwitchToNearCode(); 103 104 CodePtr GetCodeBegin() const; 105 size_t GetTotalCodeSize() const; 106 GetReturnFromRunCodeAddress()107 const void* GetReturnFromRunCodeAddress() const { 108 return return_from_run_code[0]; 109 } 110 GetForceReturnFromRunCodeAddress()111 const void* GetForceReturnFromRunCodeAddress() const { 112 return return_from_run_code[FORCE_RETURN]; 113 } 114 int3()115 void int3() { db(0xCC); } 116 117 /// Allocate memory of `size` bytes from the same block of memory the code is in. 118 /// This is useful for objects that need to be placed close to or within code. 119 /// The lifetime of this memory is the same as the code around it. 120 void* AllocateFromCodeSpace(size_t size); 121 122 void SetCodePtr(CodePtr code_ptr); 123 void EnsurePatchLocationSize(CodePtr begin, size_t size); 124 125 // ABI registers 126 #ifdef _WIN32 127 static const Xbyak::Reg64 ABI_RETURN; 128 static const Xbyak::Reg64 ABI_PARAM1; 129 static const Xbyak::Reg64 ABI_PARAM2; 130 static const Xbyak::Reg64 ABI_PARAM3; 131 static const Xbyak::Reg64 ABI_PARAM4; 132 static const std::array<Xbyak::Reg64, 4> ABI_PARAMS; 133 #else 134 static const Xbyak::Reg64 ABI_RETURN; 135 static const Xbyak::Reg64 ABI_RETURN2; 136 static const Xbyak::Reg64 ABI_PARAM1; 137 static const Xbyak::Reg64 ABI_PARAM2; 138 static const Xbyak::Reg64 ABI_PARAM3; 139 static const Xbyak::Reg64 ABI_PARAM4; 140 static const Xbyak::Reg64 ABI_PARAM5; 141 static const Xbyak::Reg64 ABI_PARAM6; 142 static const std::array<Xbyak::Reg64, 6> ABI_PARAMS; 143 #endif 144 GetJitStateInfo()145 JitStateInfo GetJitStateInfo() const { return jsi; } 146 147 bool HasSSSE3() const; 148 bool HasSSE41() const; 149 bool HasSSE42() const; 150 bool HasPCLMULQDQ() const; 151 bool HasAVX() const; 152 bool HasF16C() const; 153 bool HasAESNI() const; 154 bool HasLZCNT() const; 155 bool HasBMI1() const; 156 bool HasBMI2() const; 157 bool HasFastBMI2() const; 158 bool HasFMA() const; 159 bool HasAVX2() const; 160 bool HasAVX512_Skylake() const; 161 bool HasAVX512_BITALG() const; 162 163 private: 164 RunCodeCallbacks cb; 165 JitStateInfo jsi; 166 167 bool prelude_complete = false; 168 CodePtr near_code_begin; 169 CodePtr far_code_begin; 170 171 ConstantPool constant_pool; 172 173 bool in_far_code = false; 174 CodePtr near_code_ptr; 175 CodePtr far_code_ptr; 176 177 using RunCodeFuncType = void(*)(void*, CodePtr); 178 RunCodeFuncType run_code = nullptr; 179 RunCodeFuncType step_code = nullptr; 180 static constexpr size_t MXCSR_ALREADY_EXITED = 1 << 0; 181 static constexpr size_t FORCE_RETURN = 1 << 1; 182 std::array<const void*, 4> return_from_run_code; 183 void GenRunCode(std::function<void(BlockOfCode&)> rcp); 184 185 Xbyak::util::Cpu cpu_info; 186 bool DoesCpuSupport(Xbyak::util::Cpu::Type type) const; 187 }; 188 189 } // namespace Dynarmic::Backend::X64 190