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.
7 // This file contains stub functions for unimplemented features need to
8 // run on the ARM64 platform.
11 #include "dllimportcallback.h"
12 #include "comdelegate.h"
13 #include "asmconstants.h"
14 #include "virtualcallstub.h"
15 #include "jitinterface.h"
18 EXTERN_C void JIT_UpdateWriteBarrierState(bool skipEphemeralCheck);
21 #ifndef DACCESS_COMPILE
22 //-----------------------------------------------------------------------
23 // InstructionFormat for B.cond
24 //-----------------------------------------------------------------------
25 class ConditionalBranchInstructionFormat : public InstructionFormat
29 ConditionalBranchInstructionFormat() : InstructionFormat(InstructionFormat::k32)
31 LIMITED_METHOD_CONTRACT;
34 virtual UINT GetSizeOfInstruction(UINT refsize, UINT variationCode)
36 LIMITED_METHOD_CONTRACT;
38 _ASSERTE(refsize == InstructionFormat::k32);
43 virtual UINT GetHotSpotOffset(UINT refsize, UINT variationCode)
50 virtual BOOL CanReach(UINT refSize, UINT variationCode, BOOL fExternal, INT_PTR offset)
52 _ASSERTE(!fExternal || "ARM64:NYI - CompareAndBranchInstructionFormat::CanReach external");
56 if (offset < -1048576 || offset > 1048572)
61 // Encoding 0|1|0|1|0|1|0|0|imm19|0|cond
62 // cond = Bits3-0(variation)
63 // imm19 = bits19-0(fixedUpReference/4), will be SignExtended
64 virtual VOID EmitInstruction(UINT refSize, __int64 fixedUpReference, BYTE *pOutBuffer, UINT variationCode, BYTE *pDataBuffer)
66 LIMITED_METHOD_CONTRACT;
68 _ASSERTE(refSize == InstructionFormat::k32);
70 if (fixedUpReference < -1048576 || fixedUpReference > 1048572)
71 COMPlusThrow(kNotSupportedException);
73 _ASSERTE((fixedUpReference & 0x3) == 0);
74 DWORD imm19 = (DWORD)(0x7FFFF & (fixedUpReference >> 2));
76 pOutBuffer[0] = static_cast<BYTE>((0x7 & imm19 /* Bits2-0(imm19) */) << 5 | (0xF & variationCode /* cond */));
77 pOutBuffer[1] = static_cast<BYTE>((0x7F8 & imm19 /* Bits10-3(imm19) */) >> 3);
78 pOutBuffer[2] = static_cast<BYTE>((0x7F800 & imm19 /* Bits19-11(imm19) */) >> 11);
79 pOutBuffer[3] = static_cast<BYTE>(0x54);
83 //-----------------------------------------------------------------------
84 // InstructionFormat for B(L)(R) (unconditional branch)
85 //-----------------------------------------------------------------------
86 class BranchInstructionFormat : public InstructionFormat
88 // Encoding of the VariationCode:
89 // bit(0) indicates whether this is a direct or an indirect jump.
90 // bit(1) indicates whether this is a branch with link -a.k.a call- (BL(R)) or not (B(R))
91 // bit(2) indicates whether this is a relative indirect branch or not
96 BIF_VAR_INDIRECT = 0x00000001,
97 BIF_VAR_CALL = 0x00000002,
99 BIF_VAR_JUMP = 0x00000000,
100 BIF_VAR_INDIRECT_CALL = 0x00000003,
102 BIF_VAR_RELATIVE_INDIRECT = 0x00000004
105 BOOL IsIndirect(UINT variationCode)
107 return (variationCode & BIF_VAR_INDIRECT) != 0;
109 BOOL IsCall(UINT variationCode)
111 return (variationCode & BIF_VAR_CALL) != 0;
113 BOOL IsRelativeIndirect(UINT variationCode)
115 BOOL result = (variationCode & BIF_VAR_RELATIVE_INDIRECT) != 0;
116 _ASSERTE(result && IsIndirect(variationCode) || !result);
122 BranchInstructionFormat() : InstructionFormat(InstructionFormat::k64)
124 LIMITED_METHOD_CONTRACT;
127 virtual UINT GetSizeOfInstruction(UINT refSize, UINT variationCode)
129 LIMITED_METHOD_CONTRACT;
130 _ASSERTE(refSize == InstructionFormat::k64);
132 if (IsRelativeIndirect(variationCode))
134 else if (IsIndirect(variationCode))
140 virtual UINT GetSizeOfData(UINT refSize, UINT variationCode)
146 virtual UINT GetHotSpotOffset(UINT refsize, UINT variationCode)
152 virtual BOOL CanReach(UINT refSize, UINT variationCode, BOOL fExternal, INT_PTR offset)
156 // Note that the parameter 'offset' is not an offset but the target address itself (when fExternal is true)
157 return (refSize == InstructionFormat::k64);
161 return ((offset >= -134217728 && offset <= 134217724) || (refSize == InstructionFormat::k64));
165 virtual VOID EmitInstruction(UINT refSize, __int64 fixedUpReference, BYTE *pOutBuffer, UINT variationCode, BYTE *pDataBuffer)
167 LIMITED_METHOD_CONTRACT;
169 _ASSERTE(((UINT_PTR)pDataBuffer & 7) == 0);
170 __int64 dataOffset = pDataBuffer - pOutBuffer;
172 if (dataOffset < -1048576 || dataOffset > 1048572)
173 COMPlusThrow(kNotSupportedException);
175 DWORD imm19 = (DWORD)(0x7FFFF & (dataOffset >> 2));
177 // +0: ldr x16, [pc, #dataOffset]
178 *((DWORD*)pOutBuffer) = (0x58000010 | (imm19 << 5));
179 DWORD offsetbranch = 0;
181 if (IsRelativeIndirect(variationCode))
183 // TODO-ARM64-CQ: update this!
184 // REG_IP1 (x17) is always reserved on arm64 (see Compiler::compRsvdRegCheck)
185 // and is used as a temporary register. Use it as temp register here too.
188 // +8: ldr x16, [x16]
189 // +12: add x16, x16, x17
190 *((DWORD*)(pOutBuffer+4)) = 0xAA1003F1;
191 *((DWORD*)(pOutBuffer+8)) = 0xF9400210;
192 *((DWORD*)(pOutBuffer+12)) = 0x8B110210;
195 else if (IsIndirect(variationCode))
197 // +4: ldr x16, [x16]
198 *((DWORD*)(pOutBuffer+4)) = 0xF9400210;
206 _ASSERTE(offsetbranch != 0);
208 // +offsetbranch: b(l)r x16
209 if (IsCall(variationCode))
211 *((DWORD*)(pOutBuffer+offsetbranch)) = 0xD63F0200; // blr x16
215 *((DWORD*)(pOutBuffer+offsetbranch)) = 0xD61F0200; // br x16
218 if (!ClrSafeInt<__int64>::addition(fixedUpReference, (__int64)pOutBuffer, fixedUpReference))
219 COMPlusThrowArithmetic();
220 *((__int64*)pDataBuffer) = fixedUpReference;
224 //-----------------------------------------------------------------------
225 // InstructionFormat for loading a label to the register (ADRP/ADR)
226 //-----------------------------------------------------------------------
227 class LoadFromLabelInstructionFormat : public InstructionFormat
230 LoadFromLabelInstructionFormat() : InstructionFormat( InstructionFormat::k32)
232 LIMITED_METHOD_CONTRACT;
235 virtual UINT GetSizeOfInstruction(UINT refSize, UINT variationCode)
242 virtual UINT GetHotSpotOffset(UINT refsize, UINT variationCode)
248 virtual BOOL CanReach(UINT refSize, UINT variationCode, BOOL fExternal, INT_PTR offset)
253 virtual VOID EmitInstruction(UINT refSize, __int64 fixedUpReference, BYTE *pOutBuffer, UINT variationCode, BYTE *pDataBuffer)
255 LIMITED_METHOD_CONTRACT;
256 // VariationCode is used to indicate the register the label is going to be loaded
258 DWORD imm =(DWORD)(fixedUpReference>>12);
260 COMPlusThrow(kNotSupportedException);
262 // Can't use SP or XZR
263 _ASSERTE((variationCode & 0x1F) != 31);
265 // adrp Xt, #Page_of_fixedUpReference
266 *((DWORD*)pOutBuffer) = ((9<<28) | ((imm & 3)<<29) | (imm>>2)<<5 | (variationCode&0x1F));
268 // ldr Xt, [Xt, #offset_of_fixedUpReference_to_its_page]
269 UINT64 target = (UINT64)(fixedUpReference + pOutBuffer)>>3;
270 *((DWORD*)(pOutBuffer+4)) = ( 0xF9400000 | ((target & 0x1FF)<<10) | (variationCode & 0x1F)<<5 | (variationCode & 0x1F));
276 static BYTE gConditionalBranchIF[sizeof(ConditionalBranchInstructionFormat)];
277 static BYTE gBranchIF[sizeof(BranchInstructionFormat)];
278 static BYTE gLoadFromLabelIF[sizeof(LoadFromLabelInstructionFormat)];
282 void ClearRegDisplayArgumentAndScratchRegisters(REGDISPLAY * pRD)
284 for (int i=0; i < 18; i++)
285 pRD->volatileCurrContextPointers.X[i] = NULL;
288 #ifndef CROSSGEN_COMPILE
289 void LazyMachState::unwindLazyState(LazyMachState* baseState,
290 MachState* unwoundstate,
293 HostCallPreference hostCallPreference)
296 T_KNONVOLATILE_CONTEXT_POINTERS nonVolContextPtrs;
298 context.X19 = unwoundstate->captureX19_X29[0] = baseState->captureX19_X29[0];
299 context.X20 = unwoundstate->captureX19_X29[1] = baseState->captureX19_X29[1];
300 context.X21 = unwoundstate->captureX19_X29[2] = baseState->captureX19_X29[2];
301 context.X22 = unwoundstate->captureX19_X29[3] = baseState->captureX19_X29[3];
302 context.X23 = unwoundstate->captureX19_X29[4] = baseState->captureX19_X29[4];
303 context.X24 = unwoundstate->captureX19_X29[5] = baseState->captureX19_X29[5];
304 context.X25 = unwoundstate->captureX19_X29[6] = baseState->captureX19_X29[6];
305 context.X26 = unwoundstate->captureX19_X29[7] = baseState->captureX19_X29[7];
306 context.X27 = unwoundstate->captureX19_X29[8] = baseState->captureX19_X29[8];
307 context.X28 = unwoundstate->captureX19_X29[9] = baseState->captureX19_X29[9];
308 context.Fp = unwoundstate->captureX19_X29[10] = baseState->captureX19_X29[10];
309 context.Lr = NULL; // Filled by the unwinder
311 context.Sp = baseState->captureSp;
312 context.Pc = baseState->captureIp;
314 #if !defined(DACCESS_COMPILE)
315 // For DAC, if we get here, it means that the LazyMachState is uninitialized and we have to unwind it.
316 // The API we use to unwind in DAC is StackWalk64(), which does not support the context pointers.
318 // Restore the integer registers to KNONVOLATILE_CONTEXT_POINTERS to be used for unwinding.
319 nonVolContextPtrs.X19 = &unwoundstate->captureX19_X29[0];
320 nonVolContextPtrs.X20 = &unwoundstate->captureX19_X29[1];
321 nonVolContextPtrs.X21 = &unwoundstate->captureX19_X29[2];
322 nonVolContextPtrs.X22 = &unwoundstate->captureX19_X29[3];
323 nonVolContextPtrs.X23 = &unwoundstate->captureX19_X29[4];
324 nonVolContextPtrs.X24 = &unwoundstate->captureX19_X29[5];
325 nonVolContextPtrs.X25 = &unwoundstate->captureX19_X29[6];
326 nonVolContextPtrs.X26 = &unwoundstate->captureX19_X29[7];
327 nonVolContextPtrs.X27 = &unwoundstate->captureX19_X29[8];
328 nonVolContextPtrs.X28 = &unwoundstate->captureX19_X29[9];
329 nonVolContextPtrs.Fp = &unwoundstate->captureX19_X29[10];
330 nonVolContextPtrs.Lr = NULL; // Filled by the unwinder
332 #endif // DACCESS_COMPILE
334 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK LazyMachState::unwindLazyState(ip:%p,sp:%p)\n", baseState->captureIp, baseState->captureSp));
341 pvControlPc = Thread::VirtualUnwindCallFrame(&context, &nonVolContextPtrs);
342 #else // !FEATURE_PAL
343 #ifdef DACCESS_COMPILE
344 HRESULT hr = DacVirtualUnwind(threadId, &context, &nonVolContextPtrs);
349 #else // DACCESS_COMPILE
350 BOOL success = PAL_VirtualUnwind(&context, &nonVolContextPtrs);
353 _ASSERTE(!"unwindLazyState: Unwinding failed");
354 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
356 #endif // DACCESS_COMPILE
357 pvControlPc = GetIP(&context);
358 #endif // !FEATURE_PAL
360 if (funCallDepth > 0)
363 if (funCallDepth == 0)
368 // Determine whether given IP resides in JITted code. (It returns nonzero in that case.)
369 // Use it now to see if we've unwound to managed code yet.
370 BOOL fFailedReaderLock = FALSE;
371 BOOL fIsManagedCode = ExecutionManager::IsManagedCode(pvControlPc, hostCallPreference, &fFailedReaderLock);
372 if (fFailedReaderLock)
374 // We don't know if we would have been able to find a JIT
375 // manager, because we couldn't enter the reader lock without
376 // yielding (and our caller doesn't want us to yield). So abort
379 // Invalidate the lazyState we're returning, so the caller knows
380 // we aborted before we could fully unwind
381 unwoundstate->_isValid = false;
392 unwoundstate->captureX19_X29[0] = context.X19;
393 unwoundstate->captureX19_X29[1] = context.X20;
394 unwoundstate->captureX19_X29[2] = context.X21;
395 unwoundstate->captureX19_X29[3] = context.X22;
396 unwoundstate->captureX19_X29[4] = context.X23;
397 unwoundstate->captureX19_X29[5] = context.X24;
398 unwoundstate->captureX19_X29[6] = context.X25;
399 unwoundstate->captureX19_X29[7] = context.X26;
400 unwoundstate->captureX19_X29[8] = context.X27;
401 unwoundstate->captureX19_X29[9] = context.X28;
402 unwoundstate->captureX19_X29[10] = context.Fp;
405 #ifdef DACCESS_COMPILE
406 // For DAC builds, we update the registers directly since we dont have context pointers
407 unwoundstate->captureX19_X29[0] = context.X19;
408 unwoundstate->captureX19_X29[1] = context.X20;
409 unwoundstate->captureX19_X29[2] = context.X21;
410 unwoundstate->captureX19_X29[3] = context.X22;
411 unwoundstate->captureX19_X29[4] = context.X23;
412 unwoundstate->captureX19_X29[5] = context.X24;
413 unwoundstate->captureX19_X29[6] = context.X25;
414 unwoundstate->captureX19_X29[7] = context.X26;
415 unwoundstate->captureX19_X29[8] = context.X27;
416 unwoundstate->captureX19_X29[9] = context.X28;
417 unwoundstate->captureX19_X29[10] = context.Fp;
418 #else // !DACCESS_COMPILE
419 // For non-DAC builds, update the register state from context pointers
420 unwoundstate->ptrX19_X29[0] = nonVolContextPtrs.X19;
421 unwoundstate->ptrX19_X29[1] = nonVolContextPtrs.X20;
422 unwoundstate->ptrX19_X29[2] = nonVolContextPtrs.X21;
423 unwoundstate->ptrX19_X29[3] = nonVolContextPtrs.X22;
424 unwoundstate->ptrX19_X29[4] = nonVolContextPtrs.X23;
425 unwoundstate->ptrX19_X29[5] = nonVolContextPtrs.X24;
426 unwoundstate->ptrX19_X29[6] = nonVolContextPtrs.X25;
427 unwoundstate->ptrX19_X29[7] = nonVolContextPtrs.X26;
428 unwoundstate->ptrX19_X29[8] = nonVolContextPtrs.X27;
429 unwoundstate->ptrX19_X29[9] = nonVolContextPtrs.X28;
430 unwoundstate->ptrX19_X29[10] = nonVolContextPtrs.Fp;
431 #endif // DACCESS_COMPILE
433 unwoundstate->_pc = context.Pc;
434 unwoundstate->_sp = context.Sp;
436 unwoundstate->_isValid = TRUE;
439 void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
450 pRD->IsCallerContextValid = FALSE;
451 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
454 // Copy the saved state from the frame to the current context.
457 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK HelperMethodFrame::UpdateRegDisplay cached ip:%p, sp:%p\n", m_MachState._pc, m_MachState._sp));
459 #if defined(DACCESS_COMPILE)
460 // For DAC, we may get here when the HMF is still uninitialized.
461 // So we may need to unwind here.
462 if (!m_MachState.isValid())
464 // This allocation throws on OOM.
465 MachState* pUnwoundState = (MachState*)DacAllocHostOnlyInstance(sizeof(*pUnwoundState), true);
467 InsureInit(false, pUnwoundState);
469 pRD->pCurrentContext->Pc = pRD->ControlPC = pUnwoundState->_pc;
470 pRD->pCurrentContext->Sp = pRD->SP = pUnwoundState->_sp;
472 pRD->pCurrentContext->X19 = (DWORD64)(pUnwoundState->captureX19_X29[0]);
473 pRD->pCurrentContext->X20 = (DWORD64)(pUnwoundState->captureX19_X29[1]);
474 pRD->pCurrentContext->X21 = (DWORD64)(pUnwoundState->captureX19_X29[2]);
475 pRD->pCurrentContext->X22 = (DWORD64)(pUnwoundState->captureX19_X29[3]);
476 pRD->pCurrentContext->X23 = (DWORD64)(pUnwoundState->captureX19_X29[4]);
477 pRD->pCurrentContext->X24 = (DWORD64)(pUnwoundState->captureX19_X29[5]);
478 pRD->pCurrentContext->X25 = (DWORD64)(pUnwoundState->captureX19_X29[6]);
479 pRD->pCurrentContext->X26 = (DWORD64)(pUnwoundState->captureX19_X29[7]);
480 pRD->pCurrentContext->X27 = (DWORD64)(pUnwoundState->captureX19_X29[8]);
481 pRD->pCurrentContext->X28 = (DWORD64)(pUnwoundState->captureX19_X29[9]);
482 pRD->pCurrentContext->Fp = (DWORD64)(pUnwoundState->captureX19_X29[10]);
483 pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC
485 pRD->pCurrentContextPointers->X19 = pUnwoundState->ptrX19_X29[0];
486 pRD->pCurrentContextPointers->X20 = pUnwoundState->ptrX19_X29[1];
487 pRD->pCurrentContextPointers->X21 = pUnwoundState->ptrX19_X29[2];
488 pRD->pCurrentContextPointers->X22 = pUnwoundState->ptrX19_X29[3];
489 pRD->pCurrentContextPointers->X23 = pUnwoundState->ptrX19_X29[4];
490 pRD->pCurrentContextPointers->X24 = pUnwoundState->ptrX19_X29[5];
491 pRD->pCurrentContextPointers->X25 = pUnwoundState->ptrX19_X29[6];
492 pRD->pCurrentContextPointers->X26 = pUnwoundState->ptrX19_X29[7];
493 pRD->pCurrentContextPointers->X27 = pUnwoundState->ptrX19_X29[8];
494 pRD->pCurrentContextPointers->X28 = pUnwoundState->ptrX19_X29[9];
495 pRD->pCurrentContextPointers->Fp = pUnwoundState->ptrX19_X29[10];
496 pRD->pCurrentContextPointers->Lr = NULL;
500 #endif // DACCESS_COMPILE
502 // reset pContext; it's only valid for active (top-most) frame
503 pRD->pContext = NULL;
504 pRD->ControlPC = GetReturnAddress(); // m_MachState._pc;
505 pRD->SP = (DWORD64)(size_t)m_MachState._sp;
507 pRD->pCurrentContext->Pc = pRD->ControlPC;
508 pRD->pCurrentContext->Sp = pRD->SP;
511 pRD->pCurrentContext->X19 = m_MachState.ptrX19_X29[0] ? *m_MachState.ptrX19_X29[0] : m_MachState.captureX19_X29[0];
512 pRD->pCurrentContext->X20 = m_MachState.ptrX19_X29[1] ? *m_MachState.ptrX19_X29[1] : m_MachState.captureX19_X29[1];
513 pRD->pCurrentContext->X21 = m_MachState.ptrX19_X29[2] ? *m_MachState.ptrX19_X29[2] : m_MachState.captureX19_X29[2];
514 pRD->pCurrentContext->X22 = m_MachState.ptrX19_X29[3] ? *m_MachState.ptrX19_X29[3] : m_MachState.captureX19_X29[3];
515 pRD->pCurrentContext->X23 = m_MachState.ptrX19_X29[4] ? *m_MachState.ptrX19_X29[4] : m_MachState.captureX19_X29[4];
516 pRD->pCurrentContext->X24 = m_MachState.ptrX19_X29[5] ? *m_MachState.ptrX19_X29[5] : m_MachState.captureX19_X29[5];
517 pRD->pCurrentContext->X25 = m_MachState.ptrX19_X29[6] ? *m_MachState.ptrX19_X29[6] : m_MachState.captureX19_X29[6];
518 pRD->pCurrentContext->X26 = m_MachState.ptrX19_X29[7] ? *m_MachState.ptrX19_X29[7] : m_MachState.captureX19_X29[7];
519 pRD->pCurrentContext->X27 = m_MachState.ptrX19_X29[8] ? *m_MachState.ptrX19_X29[8] : m_MachState.captureX19_X29[8];
520 pRD->pCurrentContext->X28 = m_MachState.ptrX19_X29[9] ? *m_MachState.ptrX19_X29[9] : m_MachState.captureX19_X29[9];
521 pRD->pCurrentContext->Fp = m_MachState.ptrX19_X29[10] ? *m_MachState.ptrX19_X29[10] : m_MachState.captureX19_X29[10];
522 pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC
524 pRD->pCurrentContext->X19 = *m_MachState.ptrX19_X29[0];
525 pRD->pCurrentContext->X20 = *m_MachState.ptrX19_X29[1];
526 pRD->pCurrentContext->X21 = *m_MachState.ptrX19_X29[2];
527 pRD->pCurrentContext->X22 = *m_MachState.ptrX19_X29[3];
528 pRD->pCurrentContext->X23 = *m_MachState.ptrX19_X29[4];
529 pRD->pCurrentContext->X24 = *m_MachState.ptrX19_X29[5];
530 pRD->pCurrentContext->X25 = *m_MachState.ptrX19_X29[6];
531 pRD->pCurrentContext->X26 = *m_MachState.ptrX19_X29[7];
532 pRD->pCurrentContext->X27 = *m_MachState.ptrX19_X29[8];
533 pRD->pCurrentContext->X28 = *m_MachState.ptrX19_X29[9];
534 pRD->pCurrentContext->Fp = *m_MachState.ptrX19_X29[10];
535 pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC
538 #if !defined(DACCESS_COMPILE)
539 pRD->pCurrentContextPointers->X19 = m_MachState.ptrX19_X29[0];
540 pRD->pCurrentContextPointers->X20 = m_MachState.ptrX19_X29[1];
541 pRD->pCurrentContextPointers->X21 = m_MachState.ptrX19_X29[2];
542 pRD->pCurrentContextPointers->X22 = m_MachState.ptrX19_X29[3];
543 pRD->pCurrentContextPointers->X23 = m_MachState.ptrX19_X29[4];
544 pRD->pCurrentContextPointers->X24 = m_MachState.ptrX19_X29[5];
545 pRD->pCurrentContextPointers->X25 = m_MachState.ptrX19_X29[6];
546 pRD->pCurrentContextPointers->X26 = m_MachState.ptrX19_X29[7];
547 pRD->pCurrentContextPointers->X27 = m_MachState.ptrX19_X29[8];
548 pRD->pCurrentContextPointers->X28 = m_MachState.ptrX19_X29[9];
549 pRD->pCurrentContextPointers->Fp = m_MachState.ptrX19_X29[10];
550 pRD->pCurrentContextPointers->Lr = NULL; // Unwind again to get Caller's PC
553 ClearRegDisplayArgumentAndScratchRegisters(pRD);
555 #endif // CROSSGEN_COMPILE
557 TADDR FixupPrecode::GetMethodDesc()
559 LIMITED_METHOD_DAC_CONTRACT;
561 // This lookup is also manually inlined in PrecodeFixupThunk assembly code
562 TADDR base = *PTR_TADDR(GetBase());
565 return base + (m_MethodDescChunkIndex * MethodDesc::ALIGNMENT);
568 #ifdef DACCESS_COMPILE
569 void FixupPrecode::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
572 DacEnumMemoryRegion(dac_cast<TADDR>(this), sizeof(FixupPrecode));
574 DacEnumMemoryRegion(GetBase(), sizeof(TADDR));
576 #endif // DACCESS_COMPILE
578 #ifndef DACCESS_COMPILE
579 void StubPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator)
585 m_rgCode[n++] = 0x10000089; // adr x9, #16
586 m_rgCode[n++] = 0xA940312A; // ldp x10,x12,[x9]
587 m_rgCode[n++] = 0xD61F0140; // br x10
589 _ASSERTE(n+1 == _countof(m_rgCode));
591 m_pTarget = GetPreStubEntryPoint();
592 m_pMethodDesc = (TADDR)pMD;
595 #ifdef FEATURE_NATIVE_IMAGE_GENERATION
596 void StubPrecode::Fixup(DataImage *image)
600 image->FixupFieldToNode(this, offsetof(StubPrecode, m_pTarget),
601 image->GetHelperThunk(CORINFO_HELP_EE_PRESTUB),
603 IMAGE_REL_BASED_PTR);
605 image->FixupField(this, offsetof(StubPrecode, m_pMethodDesc),
606 (void*)GetMethodDesc(),
608 IMAGE_REL_BASED_PTR);
610 #endif // FEATURE_NATIVE_IMAGE_GENERATION
612 void NDirectImportPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator)
618 m_rgCode[n++] = 0x1000008B; // adr x11, #16
619 m_rgCode[n++] = 0xA940316A; // ldp x10,x12,[x11]
620 m_rgCode[n++] = 0xD61F0140; // br x10
622 _ASSERTE(n+1 == _countof(m_rgCode));
624 m_pTarget = GetEEFuncEntryPoint(NDirectImportThunk);
625 m_pMethodDesc = (TADDR)pMD;
628 #ifdef FEATURE_NATIVE_IMAGE_GENERATION
629 void NDirectImportPrecode::Fixup(DataImage *image)
633 image->FixupField(this, offsetof(NDirectImportPrecode, m_pMethodDesc),
634 (void*)GetMethodDesc(),
636 IMAGE_REL_BASED_PTR);
638 image->FixupFieldToNode(this, offsetof(NDirectImportPrecode, m_pTarget),
639 image->GetHelperThunk(CORINFO_HELP_EE_PINVOKE_FIXUP),
641 IMAGE_REL_BASED_PTR);
645 void FixupPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator, int iMethodDescChunkIndex /*=0*/, int iPrecodeChunkIndex /*=0*/)
651 // Initialize chunk indices only if they are not initialized yet. This is necessary to make MethodDesc::Reset work.
652 if (m_PrecodeChunkIndex == 0)
654 _ASSERTE(FitsInU1(iPrecodeChunkIndex));
655 m_PrecodeChunkIndex = static_cast<BYTE>(iPrecodeChunkIndex);
658 if (iMethodDescChunkIndex != -1)
660 if (m_MethodDescChunkIndex == 0)
662 _ASSERTE(FitsInU1(iMethodDescChunkIndex));
663 m_MethodDescChunkIndex = static_cast<BYTE>(iMethodDescChunkIndex);
666 if (*(void**)GetBase() == NULL)
667 *(void**)GetBase() = (BYTE*)pMD - (iMethodDescChunkIndex * MethodDesc::ALIGNMENT);
670 _ASSERTE(GetMethodDesc() == (TADDR)pMD);
672 if (pLoaderAllocator != NULL)
674 m_pTarget = GetEEFuncEntryPoint(PrecodeFixupThunk);
678 #ifdef FEATURE_NATIVE_IMAGE_GENERATION
679 // Partial initialization. Used to save regrouped chunks.
680 void FixupPrecode::InitForSave(int iPrecodeChunkIndex)
682 STANDARD_VM_CONTRACT;
686 _ASSERTE(FitsInU1(iPrecodeChunkIndex));
687 m_PrecodeChunkIndex = static_cast<BYTE>(iPrecodeChunkIndex);
688 // The rest is initialized in code:FixupPrecode::Fixup
691 void FixupPrecode::Fixup(DataImage *image, MethodDesc * pMD)
693 STANDARD_VM_CONTRACT;
695 // Note that GetMethodDesc() does not return the correct value because of
696 // regrouping of MethodDescs into hot and cold blocks. That's why the caller
697 // has to supply the actual MethodDesc
699 SSIZE_T mdChunkOffset;
700 ZapNode * pMDChunkNode = image->GetNodeForStructure(pMD, &mdChunkOffset);
701 ZapNode * pHelperThunk = image->GetHelperThunk(CORINFO_HELP_EE_PRECODE_FIXUP);
703 image->FixupFieldToNode(this, offsetof(FixupPrecode, m_pTarget), pHelperThunk);
705 // Set the actual chunk index
706 FixupPrecode * pNewPrecode = (FixupPrecode *)image->GetImagePointer(this);
708 size_t mdOffset = mdChunkOffset - sizeof(MethodDescChunk);
709 size_t chunkIndex = mdOffset / MethodDesc::ALIGNMENT;
710 _ASSERTE(FitsInU1(chunkIndex));
711 pNewPrecode->m_MethodDescChunkIndex = (BYTE)chunkIndex;
713 // Fixup the base of MethodDescChunk
714 if (m_PrecodeChunkIndex == 0)
716 image->FixupFieldToNode(this, (BYTE *)GetBase() - (BYTE *)this,
717 pMDChunkNode, sizeof(MethodDescChunk));
720 #endif // FEATURE_NATIVE_IMAGE_GENERATION
723 void ThisPtrRetBufPrecode::Init(MethodDesc* pMD, LoaderAllocator *pLoaderAllocator)
731 m_rgCode[n++] = 0x91000010; // mov x16, x0
732 m_rgCode[n++] = 0x91000020; // mov x0, x1
733 m_rgCode[n++] = 0x91000201; // mov x1, x16
734 m_rgCode[n++] = 0x58000070; // ldr x16, [pc, #12]
735 _ASSERTE((UINT32*)&m_pTarget == &m_rgCode[n + 2]);
736 m_rgCode[n++] = 0xd61f0200; // br x16
737 n++; // empty 4 bytes for data alignment below
738 _ASSERTE(n == _countof(m_rgCode));
741 m_pTarget = GetPreStubEntryPoint();
742 m_pMethodDesc = (TADDR)pMD;
745 #ifndef CROSSGEN_COMPILE
746 BOOL DoesSlotCallPrestub(PCODE pCode)
748 PTR_DWORD pInstr = dac_cast<PTR_DWORD>(PCODEToPINSTR(pCode));
751 #if defined(HAS_FIXUP_PRECODE)
752 if (FixupPrecode::IsFixupPrecodeByASM(pCode))
754 PCODE pTarget = dac_cast<PTR_FixupPrecode>(pInstr)->m_pTarget;
758 pTarget = decodeJump(pTarget);
761 return pTarget == (TADDR)PrecodeFixupThunk;
766 if (pInstr[0] == 0x10000089 && // adr x9, #16
767 pInstr[1] == 0xA940312A && // ldp x10,x12,[x9]
768 pInstr[2] == 0xD61F0140) // br x10
770 PCODE pTarget = dac_cast<PTR_StubPrecode>(pInstr)->m_pTarget;
774 pTarget = decodeJump(pTarget);
777 return pTarget == GetPreStubEntryPoint();
784 #endif // CROSSGEN_COMPILE
786 #endif // !DACCESS_COMPILE
788 void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * pCalleeSaved)
790 LIMITED_METHOD_CONTRACT;
792 pRD->pCurrentContext->X19 = pCalleeSaved->x19;
793 pRD->pCurrentContext->X20 = pCalleeSaved->x20;
794 pRD->pCurrentContext->X21 = pCalleeSaved->x21;
795 pRD->pCurrentContext->X22 = pCalleeSaved->x22;
796 pRD->pCurrentContext->X23 = pCalleeSaved->x23;
797 pRD->pCurrentContext->X24 = pCalleeSaved->x24;
798 pRD->pCurrentContext->X25 = pCalleeSaved->x25;
799 pRD->pCurrentContext->X26 = pCalleeSaved->x26;
800 pRD->pCurrentContext->X27 = pCalleeSaved->x27;
801 pRD->pCurrentContext->X28 = pCalleeSaved->x28;
802 pRD->pCurrentContext->Fp = pCalleeSaved->x29;
803 pRD->pCurrentContext->Lr = pCalleeSaved->x30;
805 T_KNONVOLATILE_CONTEXT_POINTERS * pContextPointers = pRD->pCurrentContextPointers;
806 pContextPointers->X19 = (PDWORD64)&pCalleeSaved->x19;
807 pContextPointers->X20 = (PDWORD64)&pCalleeSaved->x20;
808 pContextPointers->X21 = (PDWORD64)&pCalleeSaved->x21;
809 pContextPointers->X22 = (PDWORD64)&pCalleeSaved->x22;
810 pContextPointers->X23 = (PDWORD64)&pCalleeSaved->x23;
811 pContextPointers->X24 = (PDWORD64)&pCalleeSaved->x24;
812 pContextPointers->X25 = (PDWORD64)&pCalleeSaved->x25;
813 pContextPointers->X26 = (PDWORD64)&pCalleeSaved->x26;
814 pContextPointers->X27 = (PDWORD64)&pCalleeSaved->x27;
815 pContextPointers->X28 = (PDWORD64)&pCalleeSaved->x28;
816 pContextPointers->Fp = (PDWORD64)&pCalleeSaved->x29;
817 pContextPointers->Lr = (PDWORD64)&pCalleeSaved->x30;
820 #ifndef CROSSGEN_COMPILE
822 void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
824 pRD->IsCallerContextValid = FALSE;
825 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
827 // copy the callee saved regs
828 CalleeSavedRegisters *pCalleeSaved = GetCalleeSavedRegisters();
829 UpdateRegDisplayFromCalleeSavedRegisters(pRD, pCalleeSaved);
831 ClearRegDisplayArgumentAndScratchRegisters(pRD);
833 // copy the control registers
834 pRD->pCurrentContext->Fp = pCalleeSaved->x29;
835 pRD->pCurrentContext->Lr = pCalleeSaved->x30;
836 pRD->pCurrentContext->Pc = GetReturnAddress();
837 pRD->pCurrentContext->Sp = this->GetSP();
839 // Finally, syncup the regdisplay with the context
840 SyncRegDisplayToCurrentContext(pRD);
842 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
848 #ifndef CROSSGEN_COMPILE
850 void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
852 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TailCallFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
853 _ASSERTE(!"ARM64:NYI");
856 #ifndef DACCESS_COMPILE
857 void TailCallFrame::InitFromContext(T_CONTEXT * pContext)
859 _ASSERTE(!"ARM64:NYI");
861 #endif // !DACCESS_COMPILE
863 #endif // CROSSGEN_COMPILE
865 void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
867 LIMITED_METHOD_DAC_CONTRACT;
869 // Copy the context to regdisplay
870 memcpy(pRD->pCurrentContext, &m_ctx, sizeof(T_CONTEXT));
872 pRD->ControlPC = ::GetIP(&m_ctx);
873 pRD->SP = ::GetSP(&m_ctx);
875 // Update the integer registers in KNONVOLATILE_CONTEXT_POINTERS from
876 // the exception context we have.
877 pRD->pCurrentContextPointers->X19 = (PDWORD64)&m_ctx.X19;
878 pRD->pCurrentContextPointers->X20 = (PDWORD64)&m_ctx.X20;
879 pRD->pCurrentContextPointers->X21 = (PDWORD64)&m_ctx.X21;
880 pRD->pCurrentContextPointers->X22 = (PDWORD64)&m_ctx.X22;
881 pRD->pCurrentContextPointers->X23 = (PDWORD64)&m_ctx.X23;
882 pRD->pCurrentContextPointers->X24 = (PDWORD64)&m_ctx.X24;
883 pRD->pCurrentContextPointers->X25 = (PDWORD64)&m_ctx.X25;
884 pRD->pCurrentContextPointers->X26 = (PDWORD64)&m_ctx.X26;
885 pRD->pCurrentContextPointers->X27 = (PDWORD64)&m_ctx.X27;
886 pRD->pCurrentContextPointers->X28 = (PDWORD64)&m_ctx.X28;
887 pRD->pCurrentContextPointers->Fp = (PDWORD64)&m_ctx.Fp;
888 pRD->pCurrentContextPointers->Lr = (PDWORD64)&m_ctx.Lr;
890 ClearRegDisplayArgumentAndScratchRegisters(pRD);
892 pRD->IsCallerContextValid = FALSE;
893 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
895 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK FaultingExceptionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
898 void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
904 #ifdef PROFILING_SUPPORTED
905 PRECONDITION(CORProfilerStackSnapshotEnabled() || InlinedCallFrame::FrameHasActiveCall(this));
913 if (!InlinedCallFrame::FrameHasActiveCall(this))
915 LOG((LF_CORDB, LL_ERROR, "WARNING: InlinedCallFrame::UpdateRegDisplay called on inactive frame %p\n", this));
919 pRD->IsCallerContextValid = FALSE;
920 pRD->IsCallerSPValid = FALSE;
922 pRD->pCurrentContext->Pc = *(DWORD64 *)&m_pCallerReturnAddress;
923 pRD->pCurrentContext->Sp = *(DWORD64 *)&m_pCallSiteSP;
924 pRD->pCurrentContext->Fp = *(DWORD64 *)&m_pCalleeSavedFP;
926 pRD->pCurrentContextPointers->X19 = NULL;
927 pRD->pCurrentContextPointers->X20 = NULL;
928 pRD->pCurrentContextPointers->X21 = NULL;
929 pRD->pCurrentContextPointers->X22 = NULL;
930 pRD->pCurrentContextPointers->X23 = NULL;
931 pRD->pCurrentContextPointers->X24 = NULL;
932 pRD->pCurrentContextPointers->X25 = NULL;
933 pRD->pCurrentContextPointers->X26 = NULL;
934 pRD->pCurrentContextPointers->X27 = NULL;
935 pRD->pCurrentContextPointers->X28 = NULL;
937 pRD->ControlPC = m_pCallerReturnAddress;
938 pRD->SP = (DWORD) dac_cast<TADDR>(m_pCallSiteSP);
940 // reset pContext; it's only valid for active (top-most) frame
941 pRD->pContext = NULL;
943 ClearRegDisplayArgumentAndScratchRegisters(pRD);
946 // Update the frame pointer in the current context.
947 pRD->pCurrentContextPointers->Fp = &m_pCalleeSavedFP;
949 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK InlinedCallFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
954 #ifdef FEATURE_HIJACK
955 TADDR ResumableFrame::GetReturnAddressPtr(void)
957 LIMITED_METHOD_DAC_CONTRACT;
958 return dac_cast<TADDR>(m_Regs) + offsetof(T_CONTEXT, Pc);
961 void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
972 CopyMemory(pRD->pCurrentContext, m_Regs, sizeof(T_CONTEXT));
974 pRD->ControlPC = m_Regs->Pc;
975 pRD->SP = m_Regs->Sp;
977 pRD->pCurrentContextPointers->X19 = &m_Regs->X19;
978 pRD->pCurrentContextPointers->X20 = &m_Regs->X20;
979 pRD->pCurrentContextPointers->X21 = &m_Regs->X21;
980 pRD->pCurrentContextPointers->X22 = &m_Regs->X22;
981 pRD->pCurrentContextPointers->X23 = &m_Regs->X23;
982 pRD->pCurrentContextPointers->X24 = &m_Regs->X24;
983 pRD->pCurrentContextPointers->X25 = &m_Regs->X25;
984 pRD->pCurrentContextPointers->X26 = &m_Regs->X26;
985 pRD->pCurrentContextPointers->X27 = &m_Regs->X27;
986 pRD->pCurrentContextPointers->X28 = &m_Regs->X28;
987 pRD->pCurrentContextPointers->Fp = &m_Regs->Fp;
988 pRD->pCurrentContextPointers->Lr = &m_Regs->Lr;
990 for (int i=0; i < 18; i++)
991 pRD->volatileCurrContextPointers.X[i] = &m_Regs->X[i];
993 pRD->IsCallerContextValid = FALSE;
994 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
996 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK ResumableFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
1001 void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
1003 LIMITED_METHOD_CONTRACT;
1005 pRD->IsCallerContextValid = FALSE;
1006 pRD->IsCallerSPValid = FALSE;
1008 pRD->pCurrentContext->Pc = m_ReturnAddress;
1009 size_t s = sizeof(struct HijackArgs);
1010 _ASSERTE(s%8 == 0); // HijackArgs contains register values and hence will be a multiple of 8
1011 // stack must be multiple of 16. So if s is not multiple of 16 then there must be padding of 8 bytes
1013 pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ;
1015 pRD->pCurrentContext->X0 = m_Args->X0;
1017 pRD->pCurrentContext->X19 = m_Args->X19;
1018 pRD->pCurrentContext->X20 = m_Args->X20;
1019 pRD->pCurrentContext->X21 = m_Args->X21;
1020 pRD->pCurrentContext->X22 = m_Args->X22;
1021 pRD->pCurrentContext->X23 = m_Args->X23;
1022 pRD->pCurrentContext->X24 = m_Args->X24;
1023 pRD->pCurrentContext->X25 = m_Args->X25;
1024 pRD->pCurrentContext->X26 = m_Args->X26;
1025 pRD->pCurrentContext->X27 = m_Args->X27;
1026 pRD->pCurrentContext->X28 = m_Args->X28;
1027 pRD->pCurrentContext->Fp = m_Args->X29;
1028 pRD->pCurrentContext->Lr = m_Args->Lr;
1030 pRD->pCurrentContextPointers->X19 = &m_Args->X19;
1031 pRD->pCurrentContextPointers->X20 = &m_Args->X20;
1032 pRD->pCurrentContextPointers->X21 = &m_Args->X21;
1033 pRD->pCurrentContextPointers->X22 = &m_Args->X22;
1034 pRD->pCurrentContextPointers->X23 = &m_Args->X23;
1035 pRD->pCurrentContextPointers->X24 = &m_Args->X24;
1036 pRD->pCurrentContextPointers->X25 = &m_Args->X25;
1037 pRD->pCurrentContextPointers->X26 = &m_Args->X26;
1038 pRD->pCurrentContextPointers->X27 = &m_Args->X27;
1039 pRD->pCurrentContextPointers->X28 = &m_Args->X28;
1040 pRD->pCurrentContextPointers->Fp = &m_Args->X29;
1041 pRD->pCurrentContextPointers->Lr = NULL;
1043 SyncRegDisplayToCurrentContext(pRD);
1045 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK HijackFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
1047 #endif // FEATURE_HIJACK
1049 #ifdef FEATURE_COMINTEROP
1051 void emitCOMStubCall (ComCallMethodDesc *pCOMMethod, PCODE target)
1053 WRAPPER_NO_CONTRACT;
1055 // adr x12, label_comCallMethodDesc
1056 // ldr x10, label_target
1058 // 4 byte padding for alignment
1060 // target address (8 bytes)
1061 // label_comCallMethodDesc:
1068 BYTE *pBuffer = (BYTE*)pCOMMethod - COMMETHOD_CALL_PRESTUB_SIZE;
1070 memcpy(pBuffer, rgCode, sizeof(rgCode));
1071 *((PCODE*)(pBuffer + sizeof(rgCode) + 4)) = target;
1073 // Ensure that the updated instructions get actually written
1074 ClrFlushInstructionCache(pBuffer, COMMETHOD_CALL_PRESTUB_SIZE);
1076 _ASSERTE(IS_ALIGNED(pBuffer + COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET, sizeof(void*)) &&
1077 *((PCODE*)(pBuffer + COMMETHOD_CALL_PRESTUB_ADDRESS_OFFSET)) == target);
1079 #endif // FEATURE_COMINTEROP
1083 _ASSERTE(!"ARM64:NYI");
1086 #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
1087 void InitJITHelpers1()
1089 STANDARD_VM_CONTRACT;
1091 _ASSERTE(g_SystemInfo.dwNumberOfProcessors != 0);
1093 // Allocation helpers, faster but non-logging
1094 if (!((TrackAllocationsEnabled()) ||
1095 (LoggingOn(LF_GCALLOC, LL_INFO10))
1097 || (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP) != 0)
1101 if (GCHeapUtilities::UseThreadAllocationContexts())
1103 SetJitHelperFunction(CORINFO_HELP_NEWSFAST, JIT_NewS_MP_FastPortable);
1104 SetJitHelperFunction(CORINFO_HELP_NEWSFAST_ALIGN8, JIT_NewS_MP_FastPortable);
1105 SetJitHelperFunction(CORINFO_HELP_NEWARR_1_VC, JIT_NewArr1VC_MP_FastPortable);
1106 SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable);
1108 ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateString_MP_FastPortable), ECall::FastAllocateString);
1112 JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
1116 EXTERN_C void JIT_UpdateWriteBarrierState(bool) {}
1117 #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
1119 PTR_CONTEXT GetCONTEXTFromRedirectedStubStackFrame(T_DISPATCHER_CONTEXT * pDispatcherContext)
1121 LIMITED_METHOD_DAC_CONTRACT;
1123 DWORD64 stackSlot = pDispatcherContext->EstablisherFrame + REDIRECTSTUB_SP_OFFSET_CONTEXT;
1124 PTR_PTR_CONTEXT ppContext = dac_cast<PTR_PTR_CONTEXT>((TADDR)stackSlot);
1128 PTR_CONTEXT GetCONTEXTFromRedirectedStubStackFrame(T_CONTEXT * pContext)
1130 LIMITED_METHOD_DAC_CONTRACT;
1132 DWORD64 stackSlot = pContext->Sp + REDIRECTSTUB_SP_OFFSET_CONTEXT;
1133 PTR_PTR_CONTEXT ppContext = dac_cast<PTR_PTR_CONTEXT>((TADDR)stackSlot);
1137 void RedirectForThreadAbort()
1139 // ThreadAbort is not supported in .net core
1143 #if !defined(DACCESS_COMPILE) && !defined (CROSSGEN_COMPILE)
1144 FaultingExceptionFrame *GetFrameFromRedirectedStubStackFrame (DISPATCHER_CONTEXT *pDispatcherContext)
1146 LIMITED_METHOD_CONTRACT;
1148 return (FaultingExceptionFrame*)((TADDR)pDispatcherContext->ContextRecord->X19);
1153 AdjustContextForVirtualStub(
1154 EXCEPTION_RECORD *pExceptionRecord,
1157 LIMITED_METHOD_CONTRACT;
1159 Thread * pThread = GetThread();
1161 // We may not have a managed thread object. Example is an AV on the helper thread.
1162 // (perhaps during StubManager::IsStub)
1163 if (pThread == NULL)
1168 PCODE f_IP = GetIP(pContext);
1170 VirtualCallStubManager::StubKind sk;
1171 VirtualCallStubManager::FindStubManager(f_IP, &sk);
1173 if (sk == VirtualCallStubManager::SK_DISPATCH)
1175 if (*PTR_DWORD(f_IP) != DISPATCH_STUB_FIRST_DWORD)
1177 _ASSERTE(!"AV in DispatchStub at unknown instruction");
1182 if (sk == VirtualCallStubManager::SK_RESOLVE)
1184 if (*PTR_DWORD(f_IP) != RESOLVE_STUB_FIRST_DWORD)
1186 _ASSERTE(!"AV in ResolveStub at unknown instruction");
1195 PCODE callsite = GetAdjustedCallAddress(GetLR(pContext));
1197 // Lr must already have been saved before calling so it should not be necessary to restore Lr
1199 pExceptionRecord->ExceptionAddress = (PVOID)callsite;
1200 SetIP(pContext, callsite);
1204 #endif // !(DACCESS_COMPILE && CROSSGEN_COMPILE)
1206 UMEntryThunk * UMEntryThunk::Decode(void *pCallback)
1208 _ASSERTE(offsetof(UMEntryThunkCode, m_code) == 0);
1209 UMEntryThunkCode * pCode = (UMEntryThunkCode*)pCallback;
1211 // We may be called with an unmanaged external code pointer instead. So if it doesn't look like one of our
1212 // stubs (see UMEntryThunkCode::Encode below) then we'll return NULL. Luckily in these scenarios our
1213 // caller will perform a hash lookup on successful return to verify our result in case random unmanaged
1214 // code happens to look like ours.
1215 if ((pCode->m_code[0] == 0x1000008c) &&
1216 (pCode->m_code[1] == 0xa9403190) &&
1217 (pCode->m_code[2] == 0xd61f0200))
1219 return (UMEntryThunk*)pCode->m_pvSecretParam;
1225 void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
1228 // ldp x16, x12, [x12]
1232 // m_pTargetCode data
1233 // m_pvSecretParam data
1235 m_code[0] = 0x1000008c;
1236 m_code[1] = 0xa9403190;
1237 m_code[2] = 0xd61f0200;
1240 m_pTargetCode = (TADDR)pTargetCode;
1241 m_pvSecretParam = (TADDR)pvSecretParam;
1243 FlushInstructionCache(GetCurrentProcess(),&m_code,sizeof(m_code));
1246 #ifndef DACCESS_COMPILE
1248 void UMEntryThunkCode::Poison()
1250 m_pTargetCode = (TADDR)UMEntryThunk::ReportViolation;
1252 // ldp x16, x0, [x12]
1253 m_code[1] = 0xa9400190;
1255 ClrFlushInstructionCache(&m_code,sizeof(m_code));
1258 #endif // DACCESS_COMPILE
1260 #if !defined(DACCESS_COMPILE)
1261 VOID ResetCurrentContext()
1263 LIMITED_METHOD_CONTRACT;
1267 LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv)
1269 return EXCEPTION_CONTINUE_SEARCH;
1272 void FlushWriteBarrierInstructionCache()
1274 // this wouldn't be called in arm64, just to comply with gchelpers.h
1277 #ifndef CROSSGEN_COMPILE
1278 int StompWriteBarrierEphemeral(bool isRuntimeSuspended)
1280 JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
1284 int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
1286 JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
1290 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1291 int SwitchToWriteWatchBarrier(bool isRuntimeSuspended)
1293 JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
1297 int SwitchToNonWriteWatchBarrier(bool isRuntimeSuspended)
1299 JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
1302 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1303 #endif // CROSSGEN_COMPILE
1305 #ifdef DACCESS_COMPILE
1306 BOOL GetAnyThunkTarget (T_CONTEXT *pctx, TADDR *pTarget, TADDR *pTargetMethodDesc)
1308 _ASSERTE(!"ARM64:NYI");
1311 #endif // DACCESS_COMPILE
1313 #ifndef DACCESS_COMPILE
1314 // ----------------------------------------------------------------
1315 // StubLinkerCPU methods
1316 // ----------------------------------------------------------------
1318 void StubLinkerCPU::EmitMovConstant(IntReg target, UINT64 constant)
1320 #define WORD_MASK 0xFFFF
1322 // Move the 64bit constant in 4 chunks (of 16 bits).
1323 // MOVZ Rd, <1st word>, LSL 0
1324 // MOVK Rd, <2nd word>, LSL 1
1325 // MOVK Rd, <3nd word>, LSL 2
1326 // MOVK Rd, <4nd word>, LSL 3
1327 WORD word = (WORD) (constant & WORD_MASK);
1328 Emit32((DWORD)(0xD2<<24 | (4)<<21 | word<<5 | target));
1329 if (!(constant & 0xFFFF)) return;
1331 word = (WORD) ((constant>>16) & WORD_MASK);
1333 Emit32((DWORD)(0xF2<<24 | (5)<<21 | word<<5 | target));
1334 if (!(constant & 0xFFFFFFFF)) return;
1336 word = (WORD) ((constant>>32) & WORD_MASK);
1338 Emit32((DWORD)(0xF2<<24 | (6)<<21 | word<<5 | target));
1339 if (!(constant & 0xFFFFFFFFFFFF)) return;
1341 word = (WORD) ((constant>>48) & WORD_MASK);
1343 Emit32((DWORD)(0xF2<<24 | (7)<<21 | word<<5 | target));
1347 void StubLinkerCPU::EmitCmpImm(IntReg reg, int imm)
1350 if (0 <= imm && imm < 4096)
1352 // CMP <Xn|SP>, #<imm>{, <shift>}
1353 // Encoding: 1|1|1|1|0|0|0|0|shift(2)|imm(12)|Rn|Rt
1354 // Where I encode shift as 0 and Rt has to be 1F
1355 Emit32((DWORD) ((0xF1<<24) | ((0xFFF & imm)<<10) | (reg<<5) | (0x1F)) );
1359 _ASSERTE(!"ARM64: NYI");
1362 void StubLinkerCPU::EmitCmpReg(IntReg Xn, IntReg Xm)
1365 // Encoding for CMP (shifted register)
1366 // sf|1|1|0|1|0|1|1|shift(2)|0|Xm(5)|imm(6)|Xn(5)|XZR(5)
1368 // sf = 1 for 64-bit variant,
1369 // shift will be set to 00 (LSL)
1370 // imm(6), which is the shift amount, will be set to 0
1372 Emit32((DWORD) (0xEB<<24) | (Xm<<16) | (Xn<<5) | 0x1F);
1375 void StubLinkerCPU::EmitCondFlagJump(CodeLabel * target, UINT cond)
1377 WRAPPER_NO_CONTRACT;
1378 EmitLabelRef(target, reinterpret_cast<ConditionalBranchInstructionFormat&>(gConditionalBranchIF), cond);
1381 void StubLinkerCPU::EmitJumpRegister(IntReg regTarget)
1384 Emit32((DWORD) (0x3587C0<<10 | regTarget<<5));
1387 void StubLinkerCPU::EmitProlog(unsigned short cIntRegArgs, unsigned short cVecRegArgs, unsigned short cCalleeSavedRegs, unsigned short cbStackSpace)
1390 _ASSERTE(!m_fProlog);
1392 unsigned short numberOfEntriesOnStack = 2 + cIntRegArgs + cVecRegArgs + cCalleeSavedRegs; // 2 for fp, lr
1394 // Stack needs to be 16 byte (2 qword) aligned. Compute the required padding before saving it
1395 unsigned short totalPaddedFrameSize = static_cast<unsigned short>(ALIGN_UP(cbStackSpace + numberOfEntriesOnStack *sizeof(void*), 2*sizeof(void*)));
1396 // The padding is going to be applied to the local stack
1397 cbStackSpace = totalPaddedFrameSize - numberOfEntriesOnStack *sizeof(void*);
1399 // Record the parameters of this prolog so that we can generate a matching epilog and unwind info.
1400 DescribeProlog(cIntRegArgs, cVecRegArgs, cCalleeSavedRegs, cbStackSpace);
1404 // N.B Despite the range of a jump with a sub sp is 4KB, we're limiting to 504 to save from emiting right prolog that's
1405 // expressable in unwind codes efficiently. The largest offset in typical unwindinfo encodings that we use is 504.
1406 // so allocations larger than 504 bytes would require setting the SP in multiple strides, which would complicate both
1407 // prolog and epilog generation as well as unwindinfo generation.
1408 _ASSERTE((totalPaddedFrameSize <= 504) && "NYI:ARM64 Implement StubLinker prologs with larger than 504 bytes of frame size");
1409 if (totalPaddedFrameSize > 504)
1410 COMPlusThrow(kNotSupportedException);
1412 // Here is how the stack would look like (Stack grows up)
1416 // : : | Stack Frame, (i.e outgoing arguments) including padding
1425 // : : | Callee-saved registers
1441 // Old SP -> |[Stack Args]|
1446 // Regarding the order of operations in the prolog and epilog;
1447 // If the prolog and the epilog matches each other we can simplify emitting the unwind codes and save a few
1448 // bytes of unwind codes by making prolog and epilog share the same unwind codes.
1449 // In order to do that we need to make the epilog be the reverse of the prolog.
1450 // But we wouldn't want to add restoring of the argument registers as that's completely unnecessary.
1451 // Besides, saving argument registers cannot be expressed by the unwind code encodings.
1452 // So, we'll push saving the argument registers to the very last in the prolog, skip restoring it in epilog,
1453 // and also skip reporting it to the OS.
1455 // Another bit that we can save is resetting the frame pointer.
1456 // This is not necessary when the SP doesn't get modified beyond prolog and epilog. (i.e no alloca/localloc)
1457 // And in that case we don't need to report setting up the FP either.
1462 EmitSubImm(RegSp, RegSp, totalPaddedFrameSize);
1464 unsigned cbOffset = 2*sizeof(void*) + cbStackSpace; // 2 is for fp,lr
1466 // 2. Store callee-saved registers
1467 _ASSERTE(cCalleeSavedRegs <= 10);
1468 for (unsigned short i=0; i<(cCalleeSavedRegs/2)*2; i+=2)
1469 EmitLoadStoreRegPairImm(eSTORE, IntReg(19+i), IntReg(19+i+1), RegSp, cbOffset + i*sizeof(void*));
1470 if ((cCalleeSavedRegs %2) ==1)
1471 EmitLoadStoreRegImm(eSTORE, IntReg(cCalleeSavedRegs-1), RegSp, cbOffset + (cCalleeSavedRegs-1)*sizeof(void*));
1474 EmitLoadStoreRegPairImm(eSTORE, RegFp, RegLr, RegSp, cbStackSpace);
1476 // 4. Set the frame pointer
1477 EmitMovReg(RegFp, RegSp);
1479 // 5. Store floating point argument registers
1480 cbOffset += cCalleeSavedRegs*sizeof(void*);
1481 _ASSERTE(cVecRegArgs <= 8);
1482 for (unsigned short i=0; i<(cVecRegArgs/2)*2; i+=2)
1483 EmitLoadStoreRegPairImm(eSTORE, VecReg(i), VecReg(i+1), RegSp, cbOffset + i*sizeof(void*));
1484 if ((cVecRegArgs % 2) == 1)
1485 EmitLoadStoreRegImm(eSTORE, VecReg(cVecRegArgs-1), RegSp, cbOffset + (cVecRegArgs-1)*sizeof(void*));
1487 // 6. Store int argument registers
1488 cbOffset += cVecRegArgs*sizeof(void*);
1489 _ASSERTE(cIntRegArgs <= 8);
1490 for (unsigned short i=0 ; i<(cIntRegArgs/2)*2; i+=2)
1491 EmitLoadStoreRegPairImm(eSTORE, IntReg(i), IntReg(i+1), RegSp, cbOffset + i*sizeof(void*));
1492 if ((cIntRegArgs % 2) == 1)
1493 EmitLoadStoreRegImm(eSTORE,IntReg(cIntRegArgs-1), RegSp, cbOffset + (cIntRegArgs-1)*sizeof(void*));
1496 void StubLinkerCPU::EmitEpilog()
1498 _ASSERTE(m_fProlog);
1500 // 6. Restore int argument registers
1501 // nop: We don't need to. They are scratch registers
1503 // 5. Restore floating point argument registers
1504 // nop: We don't need to. They are scratch registers
1506 // 4. Restore the SP from FP
1507 // N.B. We're assuming that the stublinker stubs doesn't do alloca, hence nop
1510 EmitLoadStoreRegPairImm(eLOAD, RegFp, RegLr, RegSp, m_cbStackSpace);
1512 // 2. restore the calleeSavedRegisters
1513 unsigned cbOffset = 2*sizeof(void*) + m_cbStackSpace; // 2 is for fp,lr
1514 if ((m_cCalleeSavedRegs %2) ==1)
1515 EmitLoadStoreRegImm(eLOAD, IntReg(m_cCalleeSavedRegs-1), RegSp, cbOffset + (m_cCalleeSavedRegs-1)*sizeof(void*));
1516 for (int i=(m_cCalleeSavedRegs/2)*2-2; i>=0; i-=2)
1517 EmitLoadStoreRegPairImm(eLOAD, IntReg(19+i), IntReg(19+i+1), RegSp, cbOffset + i*sizeof(void*));
1520 EmitAddImm(RegSp, RegSp, GetStackFrameSize());
1524 void StubLinkerCPU::EmitRet(IntReg Xn)
1526 // Encoding: 1101011001011111000000| Rn |00000
1527 Emit32((DWORD)(0xD65F0000 | (Xn << 5)));
1530 void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, IntReg Xt1, IntReg Xt2, IntReg Xn, int offset)
1532 EmitLoadStoreRegPairImm(flags, (int)Xt1, (int)Xt2, Xn, offset, FALSE);
1535 void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, VecReg Vt1, VecReg Vt2, IntReg Xn, int offset)
1537 EmitLoadStoreRegPairImm(flags, (int)Vt1, (int)Vt2, Xn, offset, TRUE);
1540 void StubLinkerCPU::EmitLoadStoreRegPairImm(DWORD flags, int regNum1, int regNum2, IntReg Xn, int offset, BOOL isVec)
1543 // [opc(2)] | 1 | 0 | 1 | [IsVec(1)] | 0 | [!postIndex(1)] | [writeBack(1)] | [isLoad(1)] | [imm(7)] | [Xt2(5)] | [Xn(5)] | [Xt1(5)]
1544 // where opc=01 and if isVec==1, opc=10 otherwise
1546 BOOL isLoad = flags & 1;
1547 BOOL writeBack = flags & 2;
1548 BOOL postIndex = flags & 4;
1549 _ASSERTE((-512 <= offset) && (offset <= 504));
1550 _ASSERTE((offset & 7) == 0);
1551 int opc = isVec ? 1 : 2;
1552 Emit32((DWORD) ( (opc<<30) | // opc
1558 ((0x7F & (offset >> 3)) << 15) |
1567 void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, IntReg Xt, IntReg Xn, int offset)
1569 EmitLoadStoreRegImm(flags, (int)Xt, Xn, offset, FALSE);
1571 void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, VecReg Vt, IntReg Xn, int offset)
1573 EmitLoadStoreRegImm(flags, (int)Vt, Xn, offset, TRUE);
1576 void StubLinkerCPU::EmitLoadStoreRegImm(DWORD flags, int regNum, IntReg Xn, int offset, BOOL isVec)
1579 // wb=1 : [size(2)=11] | 1 | 1 | 1 | [IsVec(1)] | 0 | [!writeBack(1)] | 0 | [isLoad(1)] | 0 | [imm(7)] | [!postIndex(1)] | [Xn(5)] | [Xt(5)]
1580 // wb=0 : [size(2)=11] | 1 | 1 | 1 | [IsVec(1)] | 0 | [!writeBack(1)] | 0 | [isLoad(1)] | [ imm(12) ] | [Xn(5)] | [Xt(5)]
1581 // where IsVec=0 for IntReg, 1 for VecReg
1583 BOOL isLoad = flags & 1;
1584 BOOL writeBack = flags & 2;
1585 BOOL postIndex = flags & 4;
1588 _ASSERTE(-256 <= offset && offset <= 255);
1589 Emit32((DWORD) ( (0x1F<<27) |
1593 ((0x1FF & offset) << 12) |
1602 _ASSERTE((0 <= offset) && (offset <= 32760));
1603 _ASSERTE((offset & 7) == 0);
1604 Emit32((DWORD) ( (0x1F<<27) |
1608 ((0xFFF & (offset >> 3)) << 10) |
1618 // Load Register (Register Offset)
1619 void StubLinkerCPU::EmitLoadRegReg(IntReg Xt, IntReg Xn, IntReg Xm, DWORD option)
1621 Emit32((DWORD) ( (0xF8600800) |
1630 void StubLinkerCPU::EmitMovReg(IntReg Xd, IntReg Xm)
1632 if (Xd == RegSp || Xm == RegSp)
1634 // This is a different encoding than the regular MOV (register) below.
1635 // Note that RegSp and RegZero share the same encoding.
1636 // TODO: check that the intention is not mov Xd, XZR
1637 // MOV <Xd|SP>, <Xn|SP>
1638 // which is equivalent to
1639 // ADD <Xd|SP>, <Xn|SP>, #0
1640 // Encoding: sf|0|0|1|0|0|0|1|shift(2)|imm(12)|Xn|Xd
1642 // sf = 1 -> 64-bit variant
1643 // shift and imm12 are both 0
1644 Emit32((DWORD) (0x91000000 | (Xm << 5) | Xd));
1649 // which is eqivalent to
1650 // ORR <Xd>. XZR, <Xm>
1651 // Encoding: sf|0|1|0|1|0|1|0|shift(2)|0|Xm|imm(6)|Xn|Xd
1653 // sf = 1 -> 64-bit variant
1654 // shift and imm6 are both 0
1656 Emit32((DWORD) ( (0xAA << 24) | (Xm << 16) | (0x1F << 5) | Xd));
1660 void StubLinkerCPU::EmitSubImm(IntReg Xd, IntReg Xn, unsigned int value)
1662 // sub <Xd|SP>, <Xn|SP>, #imm{, <shift>}
1663 // Encoding: sf|1|0|1|0|0|0|1|shift(2)|imm(12)|Rn|Rd
1664 // where <shift> is encoded as LSL #0 (no shift) when shift=00 and LSL #12 when shift=01. (No shift in this impl)
1665 // imm(12) is an unsigned immediate in the range of 0 to 4095
1666 // Rn and Rd are both encoded as SP=31
1667 // sf = 1 for 64-bit variant
1668 _ASSERTE((0 <= value) && (value <= 4095));
1669 Emit32((DWORD) ((0xD1 << 24) | (value << 10) | (Xd << 5) | Xn));
1673 void StubLinkerCPU::EmitAddImm(IntReg Xd, IntReg Xn, unsigned int value)
1675 // add SP, SP, #imm{, <shift>}
1676 // Encoding: sf|0|0|1|0|0|0|1|shift(2)|imm(12)|Rn|Rd
1677 // where <shift> is encoded as LSL #0 (no shift) when shift=00 and LSL #12 when shift=01. (No shift in this impl)
1678 // imm(12) is an unsigned immediate in the range of 0 to 4095
1679 // Rn and Rd are both encoded as SP=31
1680 // sf = 1 for 64-bit variant
1681 _ASSERTE((0 <= value) && (value <= 4095));
1682 Emit32((DWORD) ((0x91 << 24) | (value << 10) | (Xn << 5) | Xd));
1685 void StubLinkerCPU::EmitCallRegister(IntReg reg)
1688 // Encoding: 1|1|0|1|0|1|1|0|0|0|1|1|1|1|1|1|0|0|0|0|0|Rn|0|0|0|0|0
1689 Emit32((DWORD) (0xD63F0000 | (reg << 5)));
1692 void StubLinkerCPU::Init()
1694 new (gConditionalBranchIF) ConditionalBranchInstructionFormat();
1695 new (gBranchIF) BranchInstructionFormat();
1696 new (gLoadFromLabelIF) LoadFromLabelInstructionFormat();
1699 // Emits code to adjust arguments for static delegate target.
1700 VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray)
1702 // On entry x0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux
1703 // field and save it in x16(ip). Tailcall to the target method after re-arranging the arguments
1704 // ldr x16, [x0, #offsetof(DelegateObject, _methodPtrAux)]
1705 EmitLoadStoreRegImm(eLOAD, IntReg(16), IntReg(0), DelegateObject::GetOffsetOfMethodPtrAux());
1706 //add x11, x0, DelegateObject::GetOffsetOfMethodPtrAux() - load the indirection cell into x11 used by ResolveWorkerAsmStub
1707 EmitAddImm(IntReg(11), IntReg(0), DelegateObject::GetOffsetOfMethodPtrAux());
1709 for (ShuffleEntry* pEntry = pShuffleEntryArray; pEntry->srcofs != ShuffleEntry::SENTINEL; pEntry++)
1711 if (pEntry->srcofs & ShuffleEntry::REGMASK)
1713 // If source is present in register then destination must also be a register
1714 _ASSERTE(pEntry->dstofs & ShuffleEntry::REGMASK);
1716 EmitMovReg(IntReg(pEntry->dstofs & ShuffleEntry::OFSMASK), IntReg(pEntry->srcofs & ShuffleEntry::OFSMASK));
1718 else if (pEntry->dstofs & ShuffleEntry::REGMASK)
1720 // source must be on the stack
1721 _ASSERTE(!(pEntry->srcofs & ShuffleEntry::REGMASK));
1723 EmitLoadStoreRegImm(eLOAD, IntReg(pEntry->dstofs & ShuffleEntry::OFSMASK), RegSp, pEntry->srcofs * sizeof(void*));
1727 // source must be on the stack
1728 _ASSERTE(!(pEntry->srcofs & ShuffleEntry::REGMASK));
1730 // dest must be on the stack
1731 _ASSERTE(!(pEntry->dstofs & ShuffleEntry::REGMASK));
1733 EmitLoadStoreRegImm(eLOAD, IntReg(9), RegSp, pEntry->srcofs * sizeof(void*));
1734 EmitLoadStoreRegImm(eSTORE, IntReg(9), RegSp, pEntry->dstofs * sizeof(void*));
1738 // Tailcall to target
1740 EmitJumpRegister(IntReg(16));
1743 // Emits code to adjust arguments for static delegate target.
1744 VOID StubLinkerCPU::EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg)
1746 STANDARD_VM_CONTRACT;
1748 for (ShuffleEntry* pEntry = pShuffleEntryArray; pEntry->srcofs != ShuffleEntry::SENTINEL; pEntry++)
1750 _ASSERTE(pEntry->dstofs & ShuffleEntry::REGMASK);
1751 _ASSERTE(pEntry->srcofs & ShuffleEntry::REGMASK);
1752 _ASSERTE(!(pEntry->dstofs & ShuffleEntry::FPREGMASK));
1753 _ASSERTE(!(pEntry->srcofs & ShuffleEntry::FPREGMASK));
1754 _ASSERTE(pEntry->dstofs != ShuffleEntry::HELPERREG);
1755 _ASSERTE(pEntry->srcofs != ShuffleEntry::HELPERREG);
1757 EmitMovReg(IntReg(pEntry->dstofs & ShuffleEntry::OFSMASK), IntReg(pEntry->srcofs & ShuffleEntry::OFSMASK));
1760 MetaSig msig(pSharedMD);
1761 ArgIterator argit(&msig);
1763 if (argit.HasParamType())
1765 ArgLocDesc sInstArgLoc;
1766 argit.GetParamTypeLoc(&sInstArgLoc);
1767 int regHidden = sInstArgLoc.m_idxGenReg;
1768 _ASSERTE(regHidden != -1);
1770 if (extraArg == NULL)
1772 if (pSharedMD->RequiresInstMethodTableArg())
1774 // Unboxing stub case
1775 // Fill param arg with methodtable of this pointer
1776 // ldr regHidden, [x0, #0]
1777 EmitLoadStoreRegImm(eLOAD, IntReg(regHidden), IntReg(0), 0);
1782 EmitMovConstant(IntReg(regHidden), (UINT64)extraArg);
1786 if (extraArg == NULL)
1788 // Unboxing stub case
1789 // Address of the value type is address of the boxed instance plus sizeof(MethodDesc*).
1790 // add x0, #sizeof(MethodDesc*)
1791 EmitAddImm(IntReg(0), IntReg(0), sizeof(MethodDesc*));
1794 // Tail call the real target.
1795 EmitCallManagedMethod(pSharedMD, TRUE /* tail call */);
1798 void StubLinkerCPU::EmitCallLabel(CodeLabel *target, BOOL fTailCall, BOOL fIndirect, BOOL fRelativeIndirect)
1800 _ASSERTE(fRelativeIndirect && fIndirect || !fRelativeIndirect);
1802 BranchInstructionFormat::VariationCodes variationCode = BranchInstructionFormat::VariationCodes::BIF_VAR_JUMP;
1804 variationCode = static_cast<BranchInstructionFormat::VariationCodes>(variationCode | BranchInstructionFormat::VariationCodes::BIF_VAR_CALL);
1806 variationCode = static_cast<BranchInstructionFormat::VariationCodes>(variationCode | BranchInstructionFormat::VariationCodes::BIF_VAR_INDIRECT);
1807 if (fRelativeIndirect)
1808 variationCode = static_cast<BranchInstructionFormat::VariationCodes>(variationCode | BranchInstructionFormat::VariationCodes::BIF_VAR_RELATIVE_INDIRECT);
1810 EmitLabelRef(target, reinterpret_cast<BranchInstructionFormat&>(gBranchIF), (UINT)variationCode);
1813 void StubLinkerCPU::EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall)
1815 // Use direct call if possible.
1816 if (pMD->HasStableEntryPoint())
1818 EmitCallLabel(NewExternalCodeLabel((LPVOID)pMD->GetStableEntryPoint()), fTailCall, FALSE, FALSE);
1822 BOOL isRelative = MethodTable::VTableIndir2_t::isRelative
1823 && pMD->IsVtableSlot();
1825 #ifndef FEATURE_NGEN_RELOCS_OPTIMIZATIONS
1826 _ASSERTE(!isRelative);
1829 EmitCallLabel(NewExternalCodeLabel((LPVOID)pMD->GetAddrOfSlot()), fTailCall, TRUE, isRelative);
1833 #ifndef CROSSGEN_COMPILE
1835 #ifdef FEATURE_READYTORUN
1838 // Allocation of dynamic helpers
1841 #define DYNAMIC_HELPER_ALIGNMENT sizeof(TADDR)
1843 #define BEGIN_DYNAMIC_HELPER_EMIT(size) \
1845 SIZE_T cbAligned = ALIGN_UP(cb, DYNAMIC_HELPER_ALIGNMENT); \
1846 BYTE * pStart = (BYTE *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(cbAligned, DYNAMIC_HELPER_ALIGNMENT); \
1849 #define END_DYNAMIC_HELPER_EMIT() \
1850 _ASSERTE(pStart + cb == p); \
1851 while (p < pStart + cbAligned) { *(DWORD*)p = 0xBADC0DF0; p += 4; }\
1852 ClrFlushInstructionCache(pStart, cbAligned); \
1853 return (PCODE)pStart
1855 // Uses x8 as scratch register to store address of data label
1856 // After load x8 is increment to point to next data
1857 // only accepts positive offsets
1858 static void LoadRegPair(BYTE* p, int reg1, int reg2, UINT32 offset)
1860 LIMITED_METHOD_CONTRACT;
1863 *(DWORD*)(p + 0) = 0x10000008 | ((offset >> 2) << 5);
1864 // ldp reg1, reg2, [x8], #16 ; postindex & wback
1865 *(DWORD*)(p + 4) = 0xa8c10100 | (reg2 << 10) | reg1;
1868 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1870 STANDARD_VM_CONTRACT;
1872 BEGIN_DYNAMIC_HELPER_EMIT(32);
1875 // ldp x0, x12, [x8]
1876 LoadRegPair(p, 0, 12, 16);
1879 *(DWORD*)p = 0xd61f0180;
1882 // padding to make 8 byte aligned
1883 *(DWORD*)p = 0xBADC0DF0; p += 4;
1890 *(PCODE*)p = target;
1893 END_DYNAMIC_HELPER_EMIT();
1896 // Caller must ensure sufficient byte are allocated including padding (if applicable)
1897 void DynamicHelpers::EmitHelperWithArg(BYTE*& p, LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1899 STANDARD_VM_CONTRACT;
1901 // if p is already aligned at 8-byte then padding is required for data alignment
1902 bool padding = (((uintptr_t)p & 0x7) == 0);
1905 // ldp x1, x12, [x8]
1906 LoadRegPair(p, 1, 12, padding?16:12);
1910 *(DWORD*)p = 0xd61f0180;
1915 // padding to make 8 byte aligned
1916 *(DWORD*)p = 0xBADC0DF0;
1925 *(PCODE*)p = target;
1929 PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1931 STANDARD_VM_CONTRACT;
1933 BEGIN_DYNAMIC_HELPER_EMIT(32);
1935 EmitHelperWithArg(p, pAllocator, arg, target);
1937 END_DYNAMIC_HELPER_EMIT();
1940 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
1942 STANDARD_VM_CONTRACT;
1944 BEGIN_DYNAMIC_HELPER_EMIT(40);
1947 // ldp x0, x1, [x8] ; wback
1948 LoadRegPair(p, 0, 1, 16);
1952 *(DWORD*)p = 0xf940010c;
1955 *(DWORD*)p = 0xd61f0180;
1965 *(TADDR*)p = target;
1968 END_DYNAMIC_HELPER_EMIT();
1971 PCODE DynamicHelpers::CreateHelperArgMove(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1973 STANDARD_VM_CONTRACT;
1975 BEGIN_DYNAMIC_HELPER_EMIT(32);
1978 *(DWORD*)p = 0x91000001;
1982 // ldp x0, x12, [x8]
1983 LoadRegPair(p, 0, 12, 12);
1987 *(DWORD*)p = 0xd61f0180;
1995 *(TADDR*)p = target;
1998 END_DYNAMIC_HELPER_EMIT();
2001 PCODE DynamicHelpers::CreateReturn(LoaderAllocator * pAllocator)
2003 STANDARD_VM_CONTRACT;
2005 BEGIN_DYNAMIC_HELPER_EMIT(4);
2008 *(DWORD*)p = 0xd61f03c0;
2010 END_DYNAMIC_HELPER_EMIT();
2013 PCODE DynamicHelpers::CreateReturnConst(LoaderAllocator * pAllocator, TADDR arg)
2015 STANDARD_VM_CONTRACT;
2017 BEGIN_DYNAMIC_HELPER_EMIT(16);
2020 *(DWORD*)p = 0x58000040;
2024 *(DWORD*)p = 0xd61f03c0;
2032 END_DYNAMIC_HELPER_EMIT();
2035 PCODE DynamicHelpers::CreateReturnIndirConst(LoaderAllocator * pAllocator, TADDR arg, INT8 offset)
2037 STANDARD_VM_CONTRACT;
2039 BEGIN_DYNAMIC_HELPER_EMIT(24);
2042 *(DWORD*)p = 0x58000080;
2046 *(DWORD*)p = 0xf9400000;
2049 // add x0, x0, offset
2050 *(DWORD*)p = 0x91000000 | (offset << 10);
2054 *(DWORD*)p = 0xd61f03c0;
2062 END_DYNAMIC_HELPER_EMIT();
2065 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
2067 STANDARD_VM_CONTRACT;
2069 BEGIN_DYNAMIC_HELPER_EMIT(32);
2072 // ldp x2, x12, [x8]
2073 LoadRegPair(p, 2, 12, 16);
2077 *(DWORD*)p = 0xd61f0180;
2080 // padding to make 8 byte aligned
2081 *(DWORD*)p = 0xBADC0DF0; p += 4;
2089 *(TADDR*)p = target;
2091 END_DYNAMIC_HELPER_EMIT();
2094 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
2096 STANDARD_VM_CONTRACT;
2098 BEGIN_DYNAMIC_HELPER_EMIT(40);
2101 // ldp x2, x3, [x8]; wback
2102 LoadRegPair(p, 2, 3, 16);
2106 *(DWORD*)p = 0xf940010c;
2110 *(DWORD*)p = 0xd61f0180;
2121 *(TADDR*)p = target;
2123 END_DYNAMIC_HELPER_EMIT();
2126 PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule)
2128 STANDARD_VM_CONTRACT;
2130 PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
2131 GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
2132 GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
2134 GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
2135 pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot;
2136 pArgs->signature = pLookup->signature;
2137 pArgs->module = (CORINFO_MODULE_HANDLE)pModule;
2139 // It's available only via the run-time helper function
2140 if (pLookup->indirections == CORINFO_USEHELPER)
2142 BEGIN_DYNAMIC_HELPER_EMIT(32);
2144 // X0 already contains generic context parameter
2145 // reuse EmitHelperWithArg for below two operations
2147 // branch to helperAddress
2148 EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
2150 END_DYNAMIC_HELPER_EMIT();
2154 int indirectionsCodeSize = 0;
2155 int indirectionsDataSize = 0;
2156 for (WORD i = 0; i < pLookup->indirections; i++)
2158 if ((i == 0 && pLookup->indirectFirstOffset) || (i == 1 && pLookup->indirectSecondOffset))
2160 indirectionsCodeSize += (pLookup->offsets[i] > 4095 ? 16 : 12); // if( > 4095) (16 code bytes) else 12 bytes for instruction with offset encoded in instruction
2161 indirectionsDataSize += (pLookup->offsets[i] > 4095 ? 4 : 0); // 4 bytes for storing indirection offset values
2165 indirectionsCodeSize += (pLookup->offsets[i] > 32760 ? 8 : 4); // if( > 32760) (8 code bytes) else 4 bytes for instruction with offset encoded in instruction
2166 indirectionsDataSize += (pLookup->offsets[i] > 32760 ? 4 : 0); // 4 bytes for storing indirection offset values
2170 int codeSize = indirectionsCodeSize;
2171 if(pLookup->testForNull)
2173 codeSize += 4; // mov
2174 codeSize += 12; // cbz-ret-mov
2175 //padding for 8-byte align (required by EmitHelperWithArg)
2176 if((codeSize & 0x7) == 0)
2178 codeSize += 28; // size of EmitHelperWithArg
2182 codeSize += 4 ; /* ret */
2185 codeSize += indirectionsDataSize;
2187 BEGIN_DYNAMIC_HELPER_EMIT(codeSize);
2189 if (pLookup->testForNull)
2192 *(DWORD*)p = 0x91000009;
2196 // moving offset value wrt PC. Currently points to first indirection offset data.
2197 uint dataOffset = codeSize - indirectionsDataSize - (pLookup->testForNull ? 4 : 0);
2198 for (WORD i = 0; i < pLookup->indirections; i++)
2200 if ((i == 0 && pLookup->indirectFirstOffset) || (i == 1 && pLookup->indirectSecondOffset))
2202 if(pLookup->offsets[i] > 4095)
2204 // ldr w10, [PC, #dataOffset]
2205 *(DWORD*)p = 0x1800000a | ((dataOffset>>2)<<5);
2208 *(DWORD*)p = 0x8b0a0000;
2211 // move to next indirection offset data
2212 dataOffset = dataOffset - 16 + 4; // subtract 16 as we have moved PC by 16 and add 4 as next data is at 4 bytes from previous data
2216 // add x0, #(pLookup->offsets[i])
2217 *(DWORD*)p = 0x91000000 | (((UINT32)pLookup->offsets[i])<<10);
2220 dataOffset -= 12; // subtract 12 as we have moved PC by 12
2224 *(DWORD*)p = 0xf940000a;
2227 *(DWORD*)p = 0x8b0a0000;
2232 if(pLookup->offsets[i] > 32760)
2234 // ldr w10, [PC, #dataOffset]
2235 *(DWORD*)p = 0x1800000a | ((dataOffset>>2)<<5);
2237 // ldr x0, [x0, x10]
2238 *(DWORD*)p = 0xf86a6800;
2241 // move to next indirection offset data
2242 dataOffset = dataOffset - 8 + 4; // subtract 8 as we have moved PC by 8 and add 4 as next data is at 4 bytes from previous data
2246 // offset must be 8 byte aligned
2247 _ASSERTE((pLookup->offsets[i] & 0x7) == 0);
2249 // ldr x0, [x0, #(pLookup->offsets[i])]
2250 *(DWORD*)p = 0xf9400000 | ( ((UINT32)pLookup->offsets[i]>>3) <<10 );
2252 dataOffset -= 4; // subtract 4 as we have moved PC by 4
2257 // No null test required
2258 if (!pLookup->testForNull)
2261 *(DWORD*)p = 0xd65f03c0;
2266 // cbz x0, nullvaluelabel
2267 *(DWORD*)p = 0xb4000040;
2270 *(DWORD*)p = 0xd65f03c0;
2274 *(DWORD*)p = 0x91000120;
2276 // reuse EmitHelperWithArg for below two operations
2278 // branch to helperAddress
2279 EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
2283 for (WORD i = 0; i < pLookup->indirections; i++)
2285 if ((i == 0 && pLookup->indirectFirstOffset) || (i == 1 && pLookup->indirectSecondOffset))
2287 if(pLookup->offsets[i] > 4095)
2289 *(UINT32*)p = (UINT32)pLookup->offsets[i];
2295 if(pLookup->offsets[i] > 32760)
2297 _ASSERTE((pLookup->offsets[i] & 0xffffffff00000000) == 0);
2298 *(UINT32*)p = (UINT32)pLookup->offsets[i];
2304 END_DYNAMIC_HELPER_EMIT();
2307 #endif // FEATURE_READYTORUN
2309 #endif // CROSSGEN_COMPILE
2311 #endif // #ifndef DACCESS_COMPILE