1 /* This file is part of the dynarmic project.
2 * Copyright (c) 2020 MerryMage
3 * SPDX-License-Identifier: 0BSD
4 */
5
6 #include <algorithm>
7 #include <vector>
8
9 #include <xbyak.h>
10
11 #include "backend/x64/abi.h"
12 #include "backend/x64/block_of_code.h"
13 #include "common/common_types.h"
14 #include "common/iterator_util.h"
15
16 namespace Dynarmic::Backend::X64 {
17
18 constexpr size_t XMM_SIZE = 16;
19
20 struct FrameInfo {
21 size_t stack_subtraction;
22 size_t xmm_offset;
23 size_t frame_offset;
24 };
25
CalculateFrameInfo(size_t num_gprs,size_t num_xmms,size_t frame_size)26 static FrameInfo CalculateFrameInfo(size_t num_gprs, size_t num_xmms, size_t frame_size) {
27 // We are initially 8 byte aligned because the return value is pushed onto an aligned stack after a call.
28 const size_t rsp_alignment = (num_gprs % 2 == 0) ? 8 : 0;
29 const size_t total_xmm_size = num_xmms * XMM_SIZE;
30
31 if (frame_size & 0xF) {
32 frame_size += 0x10 - (frame_size & 0xF);
33 }
34
35 return {
36 rsp_alignment + total_xmm_size + frame_size + ABI_SHADOW_SPACE,
37 frame_size + ABI_SHADOW_SPACE,
38 ABI_SHADOW_SPACE,
39 };
40 }
41
42 template<typename RegisterArrayT>
ABI_PushRegistersAndAdjustStack(BlockOfCode & code,size_t frame_size,const RegisterArrayT & regs)43 void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, size_t frame_size, const RegisterArrayT& regs) {
44 using namespace Xbyak::util;
45
46 const size_t num_gprs = std::count_if(regs.begin(), regs.end(), HostLocIsGPR);
47 const size_t num_xmms = std::count_if(regs.begin(), regs.end(), HostLocIsXMM);
48
49 FrameInfo frame_info = CalculateFrameInfo(num_gprs, num_xmms, frame_size);
50
51 for (HostLoc gpr : regs) {
52 if (HostLocIsGPR(gpr)) {
53 code.push(HostLocToReg64(gpr));
54 }
55 }
56
57 if (frame_info.stack_subtraction != 0) {
58 code.sub(rsp, u32(frame_info.stack_subtraction));
59 }
60
61 size_t xmm_offset = frame_info.xmm_offset;
62 for (HostLoc xmm : regs) {
63 if (HostLocIsXMM(xmm)) {
64 if (code.HasAVX()) {
65 code.vmovaps(code.xword[rsp + xmm_offset], HostLocToXmm(xmm));
66 } else {
67 code.movaps(code.xword[rsp + xmm_offset], HostLocToXmm(xmm));
68 }
69 xmm_offset += XMM_SIZE;
70 }
71 }
72 }
73
74 template<typename RegisterArrayT>
ABI_PopRegistersAndAdjustStack(BlockOfCode & code,size_t frame_size,const RegisterArrayT & regs)75 void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, size_t frame_size, const RegisterArrayT& regs) {
76 using namespace Xbyak::util;
77
78 const size_t num_gprs = std::count_if(regs.begin(), regs.end(), HostLocIsGPR);
79 const size_t num_xmms = std::count_if(regs.begin(), regs.end(), HostLocIsXMM);
80
81 FrameInfo frame_info = CalculateFrameInfo(num_gprs, num_xmms, frame_size);
82
83 size_t xmm_offset = frame_info.xmm_offset;
84 for (HostLoc xmm : regs) {
85 if (HostLocIsXMM(xmm)) {
86 if (code.HasAVX()) {
87 code.vmovaps(HostLocToXmm(xmm), code.xword[rsp + xmm_offset]);
88 } else {
89 code.movaps(HostLocToXmm(xmm), code.xword[rsp + xmm_offset]);
90 }
91 xmm_offset += XMM_SIZE;
92 }
93 }
94
95 if (frame_info.stack_subtraction != 0) {
96 code.add(rsp, u32(frame_info.stack_subtraction));
97 }
98
99 for (HostLoc gpr : Common::Reverse(regs)) {
100 if (HostLocIsGPR(gpr)) {
101 code.pop(HostLocToReg64(gpr));
102 }
103 }
104 }
105
ABI_PushCalleeSaveRegistersAndAdjustStack(BlockOfCode & code,size_t frame_size)106 void ABI_PushCalleeSaveRegistersAndAdjustStack(BlockOfCode& code, size_t frame_size) {
107 ABI_PushRegistersAndAdjustStack(code, frame_size, ABI_ALL_CALLEE_SAVE);
108 }
109
ABI_PopCalleeSaveRegistersAndAdjustStack(BlockOfCode & code,size_t frame_size)110 void ABI_PopCalleeSaveRegistersAndAdjustStack(BlockOfCode& code, size_t frame_size) {
111 ABI_PopRegistersAndAdjustStack(code, frame_size, ABI_ALL_CALLEE_SAVE);
112 }
113
ABI_PushCallerSaveRegistersAndAdjustStack(BlockOfCode & code,size_t frame_size)114 void ABI_PushCallerSaveRegistersAndAdjustStack(BlockOfCode& code, size_t frame_size) {
115 ABI_PushRegistersAndAdjustStack(code, frame_size, ABI_ALL_CALLER_SAVE);
116 }
117
ABI_PopCallerSaveRegistersAndAdjustStack(BlockOfCode & code,size_t frame_size)118 void ABI_PopCallerSaveRegistersAndAdjustStack(BlockOfCode& code, size_t frame_size) {
119 ABI_PopRegistersAndAdjustStack(code, frame_size, ABI_ALL_CALLER_SAVE);
120 }
121
ABI_PushCallerSaveRegistersAndAdjustStackExcept(BlockOfCode & code,HostLoc exception)122 void ABI_PushCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, HostLoc exception) {
123 std::vector<HostLoc> regs;
124 std::remove_copy(ABI_ALL_CALLER_SAVE.begin(), ABI_ALL_CALLER_SAVE.end(), std::back_inserter(regs), exception);
125 ABI_PushRegistersAndAdjustStack(code, 0, regs);
126 }
127
ABI_PopCallerSaveRegistersAndAdjustStackExcept(BlockOfCode & code,HostLoc exception)128 void ABI_PopCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, HostLoc exception) {
129 std::vector<HostLoc> regs;
130 std::remove_copy(ABI_ALL_CALLER_SAVE.begin(), ABI_ALL_CALLER_SAVE.end(), std::back_inserter(regs), exception);
131 ABI_PopRegistersAndAdjustStack(code, 0, regs);
132 }
133
134 } // namespace Dynarmic::Backend::X64
135