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