Sync may31 release/8.0-tizen (#510)
[platform/upstream/dotnet/runtime.git] / src / coreclr / vm / riscv64 / profiler.cpp
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
4 #include "common.h"
5
6 #ifdef PROFILING_SUPPORTED
7 #include "asmconstants.h"
8 #include "proftoeeinterfaceimpl.h"
9
10 UINT_PTR ProfileGetIPFromPlatformSpecificHandle(void* pPlatformSpecificHandle)
11 {
12     LIMITED_METHOD_CONTRACT;
13
14     PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(pPlatformSpecificHandle);
15     return (UINT_PTR)pData->Pc;
16 }
17
18 void ProfileSetFunctionIDInPlatformSpecificHandle(void* pPlatformSpecificHandle, FunctionID functionId)
19 {
20     LIMITED_METHOD_CONTRACT;
21
22     _ASSERTE(pPlatformSpecificHandle != nullptr);
23     _ASSERTE(functionId != 0);
24
25     PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(pPlatformSpecificHandle);
26     pData->functionId = functionId;
27 }
28
29 ProfileArgIterator::ProfileArgIterator(MetaSig* pSig, void* pPlatformSpecificHandle)
30     : m_argIterator(pSig), m_bufferPos(0)
31 {
32     WRAPPER_NO_CONTRACT;
33
34     _ASSERTE(pSig != nullptr);
35     _ASSERTE(pPlatformSpecificHandle != nullptr);
36     m_handle = pPlatformSpecificHandle;
37
38     PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(pPlatformSpecificHandle);
39 #ifdef _DEBUG
40     // Unwind a frame and get the SP for the profiled method to make sure it matches
41     // what the JIT gave us
42
43     // Setup the context to represent the frame that called ProfileEnterNaked
44     CONTEXT ctx;
45     memset(&ctx, 0, sizeof(CONTEXT));
46
47     ctx.Sp = (DWORD64)pData->probeSp;
48     ctx.Fp = (DWORD64)pData->Fp;
49     ctx.Pc = (DWORD64)pData->Pc;
50
51     // Walk up a frame to the caller frame (called the managed method which called ProfileEnterNaked)
52     Thread::VirtualUnwindCallFrame(&ctx);
53
54     _ASSERTE(pData->profiledSp == (void*)ctx.Sp);
55 #endif
56
57     // Get the hidden arg if there is one
58     MethodDesc* pMD = FunctionIdToMethodDesc(pData->functionId);
59
60     if ((pData->hiddenArg == nullptr) && (pMD->RequiresInstArg() || pMD->AcquiresInstMethodTableFromThis()))
61     {
62         if ((pData->flags & PROFILE_ENTER) != 0)
63         {
64             if (pMD->AcquiresInstMethodTableFromThis())
65             {
66                 pData->hiddenArg = GetThis();
67             }
68             else
69             {
70                 // On ARM64 the generic instantiation parameter comes after the optional "this" pointer.
71                 if (m_argIterator.HasThis())
72                 {
73                     pData->hiddenArg = (void*)pData->argumentRegisters.a[1];
74                 }
75                 else
76                 {
77                     pData->hiddenArg = (void*)pData->argumentRegisters.a[0];
78                 }
79             }
80         }
81         else
82         {
83             EECodeInfo codeInfo((PCODE)pData->Pc);
84
85             // We want to pass the caller SP here.
86             pData->hiddenArg = EECodeManager::GetExactGenericsToken((SIZE_T)(pData->profiledSp), &codeInfo);
87         }
88     }
89 }
90
91 ProfileArgIterator::~ProfileArgIterator()
92 {
93     LIMITED_METHOD_CONTRACT;
94
95     m_handle = nullptr;
96 }
97
98 LPVOID ProfileArgIterator::CopyStructFromRegisters(const ArgLocDesc* sir)
99 {
100     struct Func
101     {
102         static inline const BYTE* postIncrement(const BYTE *&p, int offset)
103         {
104             const BYTE* orig = p;
105             p += offset;
106             return orig;
107         }
108     };
109
110     WRAPPER_NO_CONTRACT;
111     _ASSERTE(m_handle);
112     PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(m_handle);
113
114     struct { bool isFloat, is8; } fields[] = {
115         { (bool) (sir->m_structFields & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_ONLY_ONE)),
116           (bool) (sir->m_structFields & STRUCT_FIRST_FIELD_SIZE_IS8) },
117         { (bool) (sir->m_structFields & (STRUCT_FLOAT_FIELD_SECOND | STRUCT_FLOAT_FIELD_ONLY_TWO)),
118           (bool) (sir->m_structFields & STRUCT_SECOND_FIELD_SIZE_IS8) },
119     };
120     int fieldCount = (sir->m_structFields & STRUCT_FLOAT_FIELD_ONLY_ONE) ? 1 : 2;
121     UINT64 bufferPosBegin = m_bufferPos;
122     const double *fRegBegin = &pData->floatArgumentRegisters.f[sir->m_idxFloatReg], *fReg = fRegBegin;
123     const double *fRegEnd = &pData->floatArgumentRegisters.f[0] + NUM_FLOAT_ARGUMENT_REGISTERS;
124     const INT64 *aRegBegin = &pData->argumentRegisters.a[sir->m_idxGenReg], *aReg = aRegBegin;
125     const INT64 *aRegEnd = &pData->argumentRegisters.a[0] + NUM_ARGUMENT_REGISTERS;
126     const BYTE *stackBegin = (BYTE*)pData->profiledSp + sir->m_byteStackIndex, *stack = stackBegin;
127
128     for (int i = 0; i < fieldCount; ++i)
129     {
130         bool inFloatReg = fields[i].isFloat && fReg < fRegEnd;
131         bool inGenReg = aReg < aRegEnd;
132
133         if (fields[i].is8)
134         {
135             UINT64 alignedTo8 = ALIGN_UP(m_bufferPos, 8);
136             _ASSERTE(alignedTo8 + 8 <= sizeof(pData->buffer));
137             m_bufferPos = alignedTo8;
138             const INT64* src =
139                 inFloatReg ? (const INT64*)fReg++ :
140                 inGenReg   ? aReg++ : (const INT64*)Func::postIncrement(stack, 8);
141             *((INT64*)&pData->buffer[m_bufferPos]) = *src;
142             m_bufferPos += 8;
143         }
144         else
145         {
146             _ASSERTE(m_bufferPos + 4 <= sizeof(pData->buffer));
147             const INT32* src =
148                 inFloatReg ? (const INT32*)fReg++ :
149                 inGenReg   ? (const INT32*)aReg++ : (const INT32*)Func::postIncrement(stack, 4);
150             *((INT32*)&pData->buffer[m_bufferPos]) = *src;
151             m_bufferPos += 4;
152         }
153     }
154     // Sanity checks, make sure we've run through (and not overrun) all locations from ArgLocDesc
155     _ASSERTE(sir->m_cFloatReg < 0 || fReg - fRegBegin == sir->m_cFloatReg);
156     _ASSERTE(sir->m_cGenReg < 0   || aReg - aRegBegin == sir->m_cGenReg);
157     _ASSERTE(sir->m_byteStackSize < 0 || stack - stackBegin == sir->m_byteStackSize);
158
159     return &pData->buffer[bufferPosBegin];
160 }
161
162 LPVOID ProfileArgIterator::GetNextArgAddr()
163 {
164     WRAPPER_NO_CONTRACT;
165
166     _ASSERTE(m_handle != nullptr);
167
168     PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(m_handle);
169
170     if ((pData->flags & (PROFILE_LEAVE | PROFILE_TAILCALL)) != 0)
171     {
172         _ASSERTE(!"GetNextArgAddr() - arguments are not available in leave and tailcall probes");
173         return nullptr;
174     }
175
176     int argOffset = m_argIterator.GetNextOffset();
177     if (argOffset == TransitionBlock::InvalidOffset)
178     {
179         return nullptr;
180     }
181
182     const ArgLocDesc* sir = m_argIterator.GetArgLocDescForStructInRegs();
183     if (sir)
184     {
185         // If both fields are in registers of same kind (either float or general) and both are 8 bytes, no need to copy.
186         // We can get away with returning a ptr to argumentRegisters since the struct would have the same layout.
187         if ((sir->m_cFloatReg ^ sir->m_cGenReg) != 2 ||
188             (sir->m_structFields & STRUCT_HAS_8BYTES_FIELDS_MASK) != STRUCT_HAS_8BYTES_FIELDS_MASK)
189         {
190             return CopyStructFromRegisters(sir);
191         }
192     }
193
194     int argSize = m_argIterator.IsArgPassedByRef() ? (int)sizeof(void*) : m_argIterator.GetArgSize();
195     if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
196     {
197         int offset = argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters();
198         _ASSERTE(offset + argSize <= (int)sizeof(pData->floatArgumentRegisters));
199         return (LPBYTE)&pData->floatArgumentRegisters + offset;
200     }
201
202     LPVOID pArg = nullptr;
203
204     if (TransitionBlock::IsArgumentRegisterOffset(argOffset))
205     {
206         int offset = argOffset - TransitionBlock::GetOffsetOfArgumentRegisters();
207         if (offset + argSize > (int)sizeof(pData->argumentRegisters))
208         {
209             // Struct partially spilled on stack
210             const int regIndex = NUM_ARGUMENT_REGISTERS - 1;  // first part of struct must be in last register
211             _ASSERTE(regIndex == offset / sizeof(pData->argumentRegisters.a[0]));
212             const int neededSpace = 2 * sizeof(INT64);
213             _ASSERTE(argSize <= neededSpace);
214             _ASSERTE(m_bufferPos + neededSpace <= sizeof(pData->buffer));
215             INT64* dest = (INT64*)&pData->buffer[m_bufferPos];
216             dest[0] = pData->argumentRegisters.a[regIndex];
217             // spilled part must be first on stack (if we copy too much, that's ok)
218             dest[1] = *(INT64*)pData->profiledSp;
219             m_bufferPos += neededSpace;
220             return dest;
221         }
222         pArg = (LPBYTE)&pData->argumentRegisters + offset;
223     }
224     else
225     {
226         _ASSERTE(TransitionBlock::IsStackArgumentOffset(argOffset));
227         pArg = (LPBYTE)pData->profiledSp + (argOffset - TransitionBlock::GetOffsetOfArgs());
228     }
229
230     if (m_argIterator.IsArgPassedByRef())
231     {
232         pArg = *(LPVOID*)pArg;
233     }
234
235     return pArg;
236 }
237
238 LPVOID ProfileArgIterator::GetHiddenArgValue(void)
239 {
240     LIMITED_METHOD_CONTRACT;
241
242     PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(m_handle);
243
244     return pData->hiddenArg;
245 }
246
247 LPVOID ProfileArgIterator::GetThis(void)
248 {
249     CONTRACTL
250     {
251         NOTHROW;
252         GC_NOTRIGGER;
253     }
254     CONTRACTL_END;
255
256     PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle;
257     MethodDesc* pMD = FunctionIdToMethodDesc(pData->functionId);
258
259     // We guarantee to return the correct "this" pointer in the enter probe.
260     // For the leave and tailcall probes, we only return a valid "this" pointer if it is the generics token.
261     if (pData->hiddenArg != nullptr)
262     {
263         if (pMD->AcquiresInstMethodTableFromThis())
264         {
265             return pData->hiddenArg;
266         }
267     }
268
269     if ((pData->flags & PROFILE_ENTER) != 0)
270     {
271         if (m_argIterator.HasThis())
272         {
273             return (LPVOID)pData->argumentRegisters.a[0];
274         }
275     }
276
277     return nullptr;
278 }
279
280 LPVOID ProfileArgIterator::GetReturnBufferAddr(void)
281 {
282     CONTRACTL
283     {
284         NOTHROW;
285         GC_NOTRIGGER;
286     }
287     CONTRACTL_END;
288
289     PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(m_handle);
290
291     if ((pData->flags & PROFILE_TAILCALL) != 0)
292     {
293         _ASSERTE(!"GetReturnBufferAddr() - return buffer address is not available in tailcall probe");
294         return nullptr;
295     }
296
297     if (m_argIterator.HasRetBuffArg())
298     {
299         // On RISC-V the method is not required to preserve the return buffer address passed in a0.
300         // However, JIT does that anyway if leave hook needs to be generated.
301         _ASSERTE((pData->flags & PROFILE_LEAVE) != 0);
302         return (LPVOID)pData->argumentRegisters.a[0];
303     }
304
305     UINT fpReturnSize = m_argIterator.GetFPReturnSize();
306     if (fpReturnSize)
307     {
308         if ((fpReturnSize & STRUCT_HAS_8BYTES_FIELDS_MASK) == STRUCT_HAS_8BYTES_FIELDS_MASK ||
309             (fpReturnSize & STRUCT_FLOAT_FIELD_ONLY_ONE))
310         {
311             return &pData->floatArgumentRegisters.f[0];
312         }
313         ArgLocDesc sir;
314         sir.m_idxFloatReg = 0;
315         sir.m_cFloatReg = -1;
316         sir.m_idxGenReg = 0;
317         sir.m_cGenReg = -1;
318         sir.m_byteStackIndex = 0;
319         sir.m_byteStackSize = -1;
320         sir.m_structFields = fpReturnSize;
321         return CopyStructFromRegisters(&sir);
322     }
323
324     if (!m_argIterator.GetSig()->IsReturnTypeVoid())
325     {
326         return &pData->argumentRegisters.a[0];
327     }
328
329     return nullptr;
330 }
331
332 #endif // PROFILING_SUPPORTED