1 // Copyright 2016 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #pragma once
6
7 #include <bitset>
8 #include <initializer_list>
9 #include <xbyak.h>
10 #include "common/assert.h"
11
12 namespace Common::X64 {
13
RegToIndex(const Xbyak::Reg & reg)14 constexpr std::size_t RegToIndex(const Xbyak::Reg& reg) {
15 using Kind = Xbyak::Reg::Kind;
16 ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
17 "RegSet only support GPRs and XMM registers.");
18 ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15.");
19 return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
20 }
21
IndexToReg64(std::size_t reg_index)22 constexpr Xbyak::Reg64 IndexToReg64(std::size_t reg_index) {
23 ASSERT(reg_index < 16);
24 return Xbyak::Reg64(static_cast<int>(reg_index));
25 }
26
IndexToXmm(std::size_t reg_index)27 constexpr Xbyak::Xmm IndexToXmm(std::size_t reg_index) {
28 ASSERT(reg_index >= 16 && reg_index < 32);
29 return Xbyak::Xmm(static_cast<int>(reg_index - 16));
30 }
31
IndexToReg(std::size_t reg_index)32 constexpr Xbyak::Reg IndexToReg(std::size_t reg_index) {
33 if (reg_index < 16) {
34 return IndexToReg64(reg_index);
35 } else {
36 return IndexToXmm(reg_index);
37 }
38 }
39
BuildRegSet(std::initializer_list<Xbyak::Reg> regs)40 inline std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
41 std::bitset<32> bits;
42 for (const Xbyak::Reg& reg : regs) {
43 bits[RegToIndex(reg)] = true;
44 }
45 return bits;
46 }
47
48 constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
49 constexpr inline std::bitset<32> ABI_ALL_XMMS(0xFFFF0000);
50
51 #ifdef _WIN32
52
53 // Microsoft x64 ABI
54 constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
55 constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
56 constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
57 constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
58 constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
59
60 const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
61 // GPRs
62 Xbyak::util::rcx,
63 Xbyak::util::rdx,
64 Xbyak::util::r8,
65 Xbyak::util::r9,
66 Xbyak::util::r10,
67 Xbyak::util::r11,
68 // XMMs
69 Xbyak::util::xmm0,
70 Xbyak::util::xmm1,
71 Xbyak::util::xmm2,
72 Xbyak::util::xmm3,
73 Xbyak::util::xmm4,
74 Xbyak::util::xmm5,
75 });
76
77 const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
78 // GPRs
79 Xbyak::util::rbx,
80 Xbyak::util::rsi,
81 Xbyak::util::rdi,
82 Xbyak::util::rbp,
83 Xbyak::util::r12,
84 Xbyak::util::r13,
85 Xbyak::util::r14,
86 Xbyak::util::r15,
87 // XMMs
88 Xbyak::util::xmm6,
89 Xbyak::util::xmm7,
90 Xbyak::util::xmm8,
91 Xbyak::util::xmm9,
92 Xbyak::util::xmm10,
93 Xbyak::util::xmm11,
94 Xbyak::util::xmm12,
95 Xbyak::util::xmm13,
96 Xbyak::util::xmm14,
97 Xbyak::util::xmm15,
98 });
99
100 constexpr std::size_t ABI_SHADOW_SPACE = 0x20;
101
102 #else
103
104 // System V x86-64 ABI
105 constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
106 constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
107 constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
108 constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
109 constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
110
111 const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
112 // GPRs
113 Xbyak::util::rcx,
114 Xbyak::util::rdx,
115 Xbyak::util::rdi,
116 Xbyak::util::rsi,
117 Xbyak::util::r8,
118 Xbyak::util::r9,
119 Xbyak::util::r10,
120 Xbyak::util::r11,
121 // XMMs
122 Xbyak::util::xmm0,
123 Xbyak::util::xmm1,
124 Xbyak::util::xmm2,
125 Xbyak::util::xmm3,
126 Xbyak::util::xmm4,
127 Xbyak::util::xmm5,
128 Xbyak::util::xmm6,
129 Xbyak::util::xmm7,
130 Xbyak::util::xmm8,
131 Xbyak::util::xmm9,
132 Xbyak::util::xmm10,
133 Xbyak::util::xmm11,
134 Xbyak::util::xmm12,
135 Xbyak::util::xmm13,
136 Xbyak::util::xmm14,
137 Xbyak::util::xmm15,
138 });
139
140 const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
141 // GPRs
142 Xbyak::util::rbx,
143 Xbyak::util::rbp,
144 Xbyak::util::r12,
145 Xbyak::util::r13,
146 Xbyak::util::r14,
147 Xbyak::util::r15,
148 });
149
150 constexpr std::size_t ABI_SHADOW_SPACE = 0;
151
152 #endif
153
154 struct ABIFrameInfo {
155 s32 subtraction;
156 s32 xmm_offset;
157 };
158
ABI_CalculateFrameSize(std::bitset<32> regs,std::size_t rsp_alignment,std::size_t needed_frame_size)159 inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, std::size_t rsp_alignment,
160 std::size_t needed_frame_size) {
161 int count = (regs & ABI_ALL_GPRS).count();
162 rsp_alignment -= count * 8;
163 std::size_t subtraction = 0;
164 int xmm_count = (regs & ABI_ALL_XMMS).count();
165 if (xmm_count) {
166 // If we have any XMMs to save, we must align the stack here.
167 subtraction = rsp_alignment & 0xF;
168 }
169 subtraction += 0x10 * xmm_count;
170 std::size_t xmm_base_subtraction = subtraction;
171 subtraction += needed_frame_size;
172 subtraction += ABI_SHADOW_SPACE;
173 // Final alignment.
174 rsp_alignment -= subtraction;
175 subtraction += rsp_alignment & 0xF;
176
177 return ABIFrameInfo{static_cast<s32>(subtraction),
178 static_cast<s32>(subtraction - xmm_base_subtraction)};
179 }
180
181 inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
182 std::size_t rsp_alignment,
183 std::size_t needed_frame_size = 0) {
184 auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
185
186 for (std::size_t i = 0; i < regs.size(); ++i) {
187 if (regs[i] && ABI_ALL_GPRS[i]) {
188 code.push(IndexToReg64(i));
189 }
190 }
191
192 if (frame_info.subtraction != 0) {
193 code.sub(code.rsp, frame_info.subtraction);
194 }
195
196 for (std::size_t i = 0; i < regs.size(); ++i) {
197 if (regs[i] && ABI_ALL_XMMS[i]) {
198 code.movaps(code.xword[code.rsp + frame_info.xmm_offset], IndexToXmm(i));
199 frame_info.xmm_offset += 0x10;
200 }
201 }
202
203 return ABI_SHADOW_SPACE;
204 }
205
206 inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
207 std::size_t rsp_alignment,
208 std::size_t needed_frame_size = 0) {
209 auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
210
211 for (std::size_t i = 0; i < regs.size(); ++i) {
212 if (regs[i] && ABI_ALL_XMMS[i]) {
213 code.movaps(IndexToXmm(i), code.xword[code.rsp + frame_info.xmm_offset]);
214 frame_info.xmm_offset += 0x10;
215 }
216 }
217
218 if (frame_info.subtraction != 0) {
219 code.add(code.rsp, frame_info.subtraction);
220 }
221
222 // GPRs need to be popped in reverse order
223 for (int reg_index = 15; reg_index >= 0; reg_index--) {
224 if (regs[reg_index]) {
225 code.pop(IndexToReg64(reg_index));
226 }
227 }
228 }
229
230 } // namespace Common::X64
231