xbyak_abi.h 6.4 KB

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