1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
6 #ifdef PROFILING_SUPPORTED
7 #include "asmconstants.h"
8 #include "proftoeeinterfaceimpl.h"
10 UINT_PTR ProfileGetIPFromPlatformSpecificHandle(void* pPlatformSpecificHandle)
12 LIMITED_METHOD_CONTRACT;
14 PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(pPlatformSpecificHandle);
15 return (UINT_PTR)pData->Pc;
18 void ProfileSetFunctionIDInPlatformSpecificHandle(void* pPlatformSpecificHandle, FunctionID functionId)
20 LIMITED_METHOD_CONTRACT;
22 _ASSERTE(pPlatformSpecificHandle != nullptr);
23 _ASSERTE(functionId != 0);
25 PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(pPlatformSpecificHandle);
26 pData->functionId = functionId;
29 ProfileArgIterator::ProfileArgIterator(MetaSig* pSig, void* pPlatformSpecificHandle)
30 : m_argIterator(pSig), m_bufferPos(0)
34 _ASSERTE(pSig != nullptr);
35 _ASSERTE(pPlatformSpecificHandle != nullptr);
36 m_handle = pPlatformSpecificHandle;
38 PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(pPlatformSpecificHandle);
40 // Unwind a frame and get the SP for the profiled method to make sure it matches
41 // what the JIT gave us
43 // Setup the context to represent the frame that called ProfileEnterNaked
45 memset(&ctx, 0, sizeof(CONTEXT));
47 ctx.Sp = (DWORD64)pData->probeSp;
48 ctx.Fp = (DWORD64)pData->Fp;
49 ctx.Pc = (DWORD64)pData->Pc;
51 // Walk up a frame to the caller frame (called the managed method which called ProfileEnterNaked)
52 Thread::VirtualUnwindCallFrame(&ctx);
54 _ASSERTE(pData->profiledSp == (void*)ctx.Sp);
57 // Get the hidden arg if there is one
58 MethodDesc* pMD = FunctionIdToMethodDesc(pData->functionId);
60 if ((pData->hiddenArg == nullptr) && (pMD->RequiresInstArg() || pMD->AcquiresInstMethodTableFromThis()))
62 if ((pData->flags & PROFILE_ENTER) != 0)
64 if (pMD->AcquiresInstMethodTableFromThis())
66 pData->hiddenArg = GetThis();
70 // On ARM64 the generic instantiation parameter comes after the optional "this" pointer.
71 if (m_argIterator.HasThis())
73 pData->hiddenArg = (void*)pData->argumentRegisters.a[1];
77 pData->hiddenArg = (void*)pData->argumentRegisters.a[0];
83 EECodeInfo codeInfo((PCODE)pData->Pc);
85 // We want to pass the caller SP here.
86 pData->hiddenArg = EECodeManager::GetExactGenericsToken((SIZE_T)(pData->profiledSp), &codeInfo);
91 ProfileArgIterator::~ProfileArgIterator()
93 LIMITED_METHOD_CONTRACT;
98 LPVOID ProfileArgIterator::CopyStructFromRegisters(const ArgLocDesc* sir)
102 static inline const BYTE* postIncrement(const BYTE *&p, int offset)
104 const BYTE* orig = p;
112 PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(m_handle);
114 struct { bool isFloat, is8; } fields[] = {
115 { sir->m_structFields & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_ONLY_ONE),
116 sir->m_structFields & STRUCT_FIRST_FIELD_SIZE_IS8 },
117 { sir->m_structFields & (STRUCT_FLOAT_FIELD_SECOND | STRUCT_FLOAT_FIELD_ONLY_TWO),
118 sir->m_structFields & STRUCT_SECOND_FIELD_SIZE_IS8 },
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;
128 for (int i = 0; i < fieldCount; ++i)
130 bool inFloatReg = fields[i].isFloat && fReg < fRegEnd;
131 bool inGenReg = aReg < aRegEnd;
135 UINT64 alignedTo8 = ALIGN_UP(m_bufferPos, 8);
136 _ASSERTE(alignedTo8 + 8 <= sizeof(pData->buffer));
137 m_bufferPos = alignedTo8;
139 inFloatReg ? (const INT64*)fReg++ :
140 inGenReg ? aReg++ : (const INT64*)Func::postIncrement(stack, 8);
141 *((INT64*)&pData->buffer[m_bufferPos]) = *src;
146 _ASSERTE(m_bufferPos + 4 <= sizeof(pData->buffer));
148 inFloatReg ? (const INT32*)fReg++ :
149 inGenReg ? (const INT32*)aReg++ : (const INT32*)Func::postIncrement(stack, 4);
150 *((INT32*)&pData->buffer[m_bufferPos]) = *src;
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);
159 return &pData->buffer[bufferPosBegin];
162 LPVOID ProfileArgIterator::GetNextArgAddr()
166 _ASSERTE(m_handle != nullptr);
168 PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(m_handle);
170 if ((pData->flags & (PROFILE_LEAVE | PROFILE_TAILCALL)) != 0)
172 _ASSERTE(!"GetNextArgAddr() - arguments are not available in leave and tailcall probes");
176 int argOffset = m_argIterator.GetNextOffset();
177 if (argOffset == TransitionBlock::InvalidOffset)
182 const ArgLocDesc* sir = m_argIterator.GetArgLocDescForStructInRegs();
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)
190 return CopyStructFromRegisters(sir);
194 int argSize = m_argIterator.IsArgPassedByRef() ? sizeof(void*) : m_argIterator.GetArgSize();
195 if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
197 int offset = argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters();
198 _ASSERTE(offset + argSize <= sizeof(pData->floatArgumentRegisters));
199 return (LPBYTE)&pData->floatArgumentRegisters + offset;
202 LPVOID pArg = nullptr;
204 if (TransitionBlock::IsArgumentRegisterOffset(argOffset))
206 int offset = argOffset - TransitionBlock::GetOffsetOfArgumentRegisters();
207 if (offset + argSize > sizeof(pData->argumentRegisters))
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;
222 pArg = (LPBYTE)&pData->argumentRegisters + offset;
226 _ASSERTE(TransitionBlock::IsStackArgumentOffset(argOffset));
227 pArg = (LPBYTE)pData->profiledSp + (argOffset - TransitionBlock::GetOffsetOfArgs());
230 if (m_argIterator.IsArgPassedByRef())
232 pArg = *(LPVOID*)pArg;
238 LPVOID ProfileArgIterator::GetHiddenArgValue(void)
240 LIMITED_METHOD_CONTRACT;
242 PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(m_handle);
244 return pData->hiddenArg;
247 LPVOID ProfileArgIterator::GetThis(void)
256 PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle;
257 MethodDesc* pMD = FunctionIdToMethodDesc(pData->functionId);
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)
263 if (pMD->AcquiresInstMethodTableFromThis())
265 return pData->hiddenArg;
269 if ((pData->flags & PROFILE_ENTER) != 0)
271 if (m_argIterator.HasThis())
273 return (LPVOID)pData->argumentRegisters.a[0];
280 LPVOID ProfileArgIterator::GetReturnBufferAddr(void)
289 PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA*>(m_handle);
291 if ((pData->flags & PROFILE_TAILCALL) != 0)
293 _ASSERTE(!"GetReturnBufferAddr() - return buffer address is not available in tailcall probe");
297 if (m_argIterator.HasRetBuffArg())
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];
305 UINT fpReturnSize = m_argIterator.GetFPReturnSize();
308 if ((fpReturnSize & STRUCT_HAS_8BYTES_FIELDS_MASK) == STRUCT_HAS_8BYTES_FIELDS_MASK ||
309 (fpReturnSize & STRUCT_FLOAT_FIELD_ONLY_ONE))
311 return &pData->floatArgumentRegisters.f[0];
314 sir.m_idxFloatReg = 0;
315 sir.m_cFloatReg = -1;
318 sir.m_byteStackIndex = 0;
319 sir.m_byteStackSize = -1;
320 sir.m_structFields = fpReturnSize;
321 return CopyStructFromRegisters(&sir);
324 if (!m_argIterator.GetSig()->IsReturnTypeVoid())
326 return &pData->argumentRegisters.a[0];
332 #endif // PROFILING_SUPPORTED