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