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