1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5
6#ifndef __ARGDESTINATION_H__
7#define __ARGDESTINATION_H__
8
9// The ArgDestination class represents a destination location of an argument.
10class ArgDestination
11{
12 // Base address to which the m_offset is applied to get the actual argument location.
13 PTR_VOID m_base;
14 // Offset of the argument relative to the m_base. On AMD64 on Unix, it can have a special
15 // value that represent a struct that contain both general purpose and floating point fields
16 // passed in registers.
17 int m_offset;
18 // For structs passed in registers, this member points to an ArgLocDesc that contains
19 // details on the layout of the struct in general purpose and floating point registers.
20 ArgLocDesc* m_argLocDescForStructInRegs;
21
22public:
23
24 // Construct the ArgDestination
25 ArgDestination(PTR_VOID base, int offset, ArgLocDesc* argLocDescForStructInRegs)
26 : m_base(base),
27 m_offset(offset),
28 m_argLocDescForStructInRegs(argLocDescForStructInRegs)
29 {
30 LIMITED_METHOD_CONTRACT;
31#if defined(UNIX_AMD64_ABI)
32 _ASSERTE((argLocDescForStructInRegs != NULL) || (offset != TransitionBlock::StructInRegsOffset));
33#elif defined(_TARGET_ARM64_)
34 // This assert is not interesting on arm64. argLocDescForStructInRegs could be
35 // initialized if the args are being enregistered.
36#else
37 _ASSERTE(argLocDescForStructInRegs == NULL);
38#endif
39 }
40
41 // Get argument destination address for arguments that are not structs passed in registers.
42 PTR_VOID GetDestinationAddress()
43 {
44 LIMITED_METHOD_CONTRACT;
45 return dac_cast<PTR_VOID>(dac_cast<TADDR>(m_base) + m_offset);
46 }
47
48#if defined(_TARGET_ARM64_)
49#ifndef DACCESS_COMPILE
50
51 // Returns true if the ArgDestination represents an HFA struct
52 bool IsHFA()
53 {
54 return m_argLocDescForStructInRegs != NULL;
55 }
56
57 // Copy struct argument into registers described by the current ArgDestination.
58 // Arguments:
59 // src = source data of the structure
60 // fieldBytes - size of the structure
61 void CopyHFAStructToRegister(void *src, int fieldBytes)
62 {
63 // We are either copying either a float or double HFA and need to
64 // enregister each field.
65
66 int floatRegCount = m_argLocDescForStructInRegs->m_cFloatReg;
67 bool typeFloat = m_argLocDescForStructInRegs->m_isSinglePrecision;
68 void* dest = this->GetDestinationAddress();
69
70 if (typeFloat)
71 {
72 for (int i = 0; i < floatRegCount; ++i)
73 {
74 // Copy 4 bytes on 8 bytes alignment
75 *((UINT64*)dest + i) = *((UINT32*)src + i);
76 }
77 }
78 else
79 {
80 // We can just do a memcpy.
81 memcpyNoGCRefs(dest, src, fieldBytes);
82 }
83 }
84
85#endif // !DACCESS_COMPILE
86#endif // defined(_TARGET_ARM64_)
87
88#if defined(UNIX_AMD64_ABI)
89
90 // Returns true if the ArgDestination represents a struct passed in registers.
91 bool IsStructPassedInRegs()
92 {
93 LIMITED_METHOD_CONTRACT;
94 return m_offset == TransitionBlock::StructInRegsOffset;
95 }
96
97 // Get destination address for floating point fields of a struct passed in registers.
98 PTR_VOID GetStructFloatRegDestinationAddress()
99 {
100 LIMITED_METHOD_CONTRACT;
101 _ASSERTE(IsStructPassedInRegs());
102 int offset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_argLocDescForStructInRegs->m_idxFloatReg * 16;
103 return dac_cast<PTR_VOID>(dac_cast<TADDR>(m_base) + offset);
104 }
105
106 // Get destination address for non-floating point fields of a struct passed in registers.
107 PTR_VOID GetStructGenRegDestinationAddress()
108 {
109 LIMITED_METHOD_CONTRACT;
110 _ASSERTE(IsStructPassedInRegs());
111 int offset = TransitionBlock::GetOffsetOfArgumentRegisters() + m_argLocDescForStructInRegs->m_idxGenReg * 8;
112 return dac_cast<PTR_VOID>(dac_cast<TADDR>(m_base) + offset);
113 }
114
115#ifndef DACCESS_COMPILE
116 // Zero struct argument stored in registers described by the current ArgDestination.
117 // Arguments:
118 // fieldBytes - size of the structure
119 void ZeroStructInRegisters(int fieldBytes)
120 {
121 STATIC_CONTRACT_NOTHROW;
122 STATIC_CONTRACT_GC_NOTRIGGER;
123 STATIC_CONTRACT_FORBID_FAULT;
124 STATIC_CONTRACT_MODE_COOPERATIVE;
125
126 // To zero the struct, we create a zero filled array of large enough size and
127 // then copy it to the registers. It is implemented this way to keep the complexity
128 // of dealing with the eightbyte classification in single function.
129 // This function is used rarely and so the overhead of reading the zeros from
130 // the stack is negligible.
131 long long zeros[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS] = {};
132 _ASSERTE(sizeof(zeros) >= fieldBytes);
133
134 CopyStructToRegisters(zeros, fieldBytes, 0);
135 }
136
137 // Copy struct argument into registers described by the current ArgDestination.
138 // Arguments:
139 // src = source data of the structure
140 // fieldBytes - size of the structure
141 // destOffset - nonzero when copying values into Nullable<T>, it is the offset
142 // of the T value inside of the Nullable<T>
143 void CopyStructToRegisters(void *src, int fieldBytes, int destOffset)
144 {
145 STATIC_CONTRACT_NOTHROW;
146 STATIC_CONTRACT_GC_NOTRIGGER;
147 STATIC_CONTRACT_FORBID_FAULT;
148 STATIC_CONTRACT_MODE_COOPERATIVE;
149
150 _ASSERTE(IsStructPassedInRegs());
151
152 BYTE* genRegDest = (BYTE*)GetStructGenRegDestinationAddress() + destOffset;
153 BYTE* floatRegDest = (BYTE*)GetStructFloatRegDestinationAddress();
154 INDEBUG(int remainingBytes = fieldBytes;)
155
156 EEClass* eeClass = m_argLocDescForStructInRegs->m_eeClass;
157 _ASSERTE(eeClass != NULL);
158
159 // We start at the first eightByte that the destOffset didn't skip completely.
160 for (int i = destOffset / 8; i < eeClass->GetNumberEightBytes(); i++)
161 {
162 int eightByteSize = eeClass->GetEightByteSize(i);
163 SystemVClassificationType eightByteClassification = eeClass->GetEightByteClassification(i);
164
165 // Adjust the size of the first eightByte by the destOffset
166 eightByteSize -= (destOffset & 7);
167 destOffset = 0;
168
169 _ASSERTE(remainingBytes >= eightByteSize);
170
171 if (eightByteClassification == SystemVClassificationTypeSSE)
172 {
173 if (eightByteSize == 8)
174 {
175 *(UINT64*)floatRegDest = *(UINT64*)src;
176 }
177 else
178 {
179 _ASSERTE(eightByteSize == 4);
180 *(UINT32*)floatRegDest = *(UINT32*)src;
181 }
182 floatRegDest += 16;
183 }
184 else
185 {
186 if (eightByteSize == 8)
187 {
188 _ASSERTE((eightByteClassification == SystemVClassificationTypeInteger) ||
189 (eightByteClassification == SystemVClassificationTypeIntegerReference) ||
190 (eightByteClassification == SystemVClassificationTypeIntegerByRef));
191
192 _ASSERTE(IS_ALIGNED((SIZE_T)genRegDest, 8));
193 *(UINT64*)genRegDest = *(UINT64*)src;
194 }
195 else
196 {
197 _ASSERTE(eightByteClassification == SystemVClassificationTypeInteger);
198 memcpyNoGCRefs(genRegDest, src, eightByteSize);
199 }
200
201 genRegDest += eightByteSize;
202 }
203
204 src = (BYTE*)src + eightByteSize;
205 INDEBUG(remainingBytes -= eightByteSize;)
206 }
207
208 _ASSERTE(remainingBytes == 0);
209 }
210
211#endif //DACCESS_COMPILE
212
213 // Report managed object pointers in the struct in registers
214 // Arguments:
215 // fn - promotion function to apply to each managed object pointer
216 // sc - scan context to pass to the promotion function
217 // fieldBytes - size of the structure
218 void ReportPointersFromStructInRegisters(promote_func *fn, ScanContext *sc, int fieldBytes)
219 {
220 LIMITED_METHOD_CONTRACT;
221
222 // SPAN-TODO: GC reporting - https://github.com/dotnet/coreclr/issues/8517
223
224 _ASSERTE(IsStructPassedInRegs());
225
226 TADDR genRegDest = dac_cast<TADDR>(GetStructGenRegDestinationAddress());
227 INDEBUG(int remainingBytes = fieldBytes;)
228
229 EEClass* eeClass = m_argLocDescForStructInRegs->m_eeClass;
230 _ASSERTE(eeClass != NULL);
231
232 for (int i = 0; i < eeClass->GetNumberEightBytes(); i++)
233 {
234 int eightByteSize = eeClass->GetEightByteSize(i);
235 SystemVClassificationType eightByteClassification = eeClass->GetEightByteClassification(i);
236
237 _ASSERTE(remainingBytes >= eightByteSize);
238
239 if (eightByteClassification != SystemVClassificationTypeSSE)
240 {
241 if ((eightByteClassification == SystemVClassificationTypeIntegerReference) ||
242 (eightByteClassification == SystemVClassificationTypeIntegerByRef))
243 {
244 _ASSERTE(eightByteSize == 8);
245 _ASSERTE(IS_ALIGNED((SIZE_T)genRegDest, 8));
246
247 uint32_t flags = eightByteClassification == SystemVClassificationTypeIntegerByRef ? GC_CALL_INTERIOR : 0;
248 (*fn)(dac_cast<PTR_PTR_Object>(genRegDest), sc, flags);
249 }
250
251 genRegDest += eightByteSize;
252 }
253
254 INDEBUG(remainingBytes -= eightByteSize;)
255 }
256
257 _ASSERTE(remainingBytes == 0);
258 }
259
260#endif // UNIX_AMD64_ABI
261
262};
263
264#endif // __ARGDESTINATION_H__
265