ldp d6, d7, [x9, #48]
LNoFloatingPoint
- ;; Copy [pArgumentRegisters, ..., pArgumentRegisters + 64]
- ;; into x0, ..., x7, x8
+ ;; Copy [pArgumentRegisters, ..., pArgumentRegisters + 56]
+ ;; into x0, ..., x7
ldr x9, [x19,#CallDescrData__pArgumentRegisters]
ldp x0, x1, [x9]
ldp x2, x3, [x9, #16]
ldp x4, x5, [x9, #32]
ldp x6, x7, [x9, #48]
- ldr x8, [x9, #64]
+
+ ;; Copy pRetBuffArg into x8
+ ldr x9, [x19,#CallDescrData__pRetBuffArg]
+ ldr x8, [x9]
;; call pTarget
ldr x9, [x19,#CallDescrData__pTarget]
#define METHODDESC_REGISTER x12
-#define SIZEOF__ArgumentRegisters 0x48
+#define SIZEOF__ArgumentRegisters 0x40
ASMCONSTANTS_C_ASSERT(SIZEOF__ArgumentRegisters == sizeof(ArgumentRegisters))
#define SIZEOF__FloatArgumentRegisters 0x40
ASMCONSTANTS_C_ASSERT(SIZEOF__FloatArgumentRegisters == sizeof(FloatArgumentRegisters))
-#define CallDescrData__pSrc 0x00
-#define CallDescrData__numStackSlots 0x08
-#define CallDescrData__pArgumentRegisters 0x10
-#define CallDescrData__pFloatArgumentRegisters 0x18
-#define CallDescrData__fpReturnSize 0x20
-#define CallDescrData__pTarget 0x28
-#define CallDescrData__returnValue 0x30
+#define CallDescrData__pSrc 0x00
+#define CallDescrData__numStackSlots 0x08
+#define CallDescrData__pArgumentRegisters 0x10
+#define CallDescrData__pFloatArgumentRegisters 0x18
+#define CallDescrData__fpReturnSize 0x20
+#define CallDescrData__pTarget 0x28
+#define CallDescrData__pRetBuffArg 0x30
+#define CallDescrData__returnValue 0x38
ASMCONSTANTS_C_ASSERT(CallDescrData__pSrc == offsetof(CallDescrData, pSrc))
ASMCONSTANTS_C_ASSERT(CallDescrData__numStackSlots == offsetof(CallDescrData, numStackSlots))
ASMCONSTANTS_C_ASSERT(CallDescrData__pFloatArgumentRegisters == offsetof(CallDescrData, pFloatArgumentRegisters))
ASMCONSTANTS_C_ASSERT(CallDescrData__fpReturnSize == offsetof(CallDescrData, fpReturnSize))
ASMCONSTANTS_C_ASSERT(CallDescrData__pTarget == offsetof(CallDescrData, pTarget))
+ASMCONSTANTS_C_ASSERT(CallDescrData__pRetBuffArg == offsetof(CallDescrData, pRetBuffArg))
ASMCONSTANTS_C_ASSERT(CallDescrData__returnValue == offsetof(CallDescrData, returnValue))
#define CORINFO_NullReferenceException_ASM 0
GBLA ComCallPreStub_FirstStackAdjust
ComCallPreStub_FrameSize SETA (SIZEOF__GSCookie + SIZEOF__ComMethodFrame)
-ComCallPreStub_FirstStackAdjust SETA (SIZEOF__ArgumentRegisters + 2 * 8) ; reg args , fp & lr already pushed
+ComCallPreStub_FirstStackAdjust SETA (8 + SIZEOF__ArgumentRegisters + 2 * 8) ; x8, reg args , fp & lr already pushed
ComCallPreStub_StackAlloc SETA ComCallPreStub_FrameSize - ComCallPreStub_FirstStackAdjust
ComCallPreStub_StackAlloc SETA ComCallPreStub_StackAlloc + SIZEOF__FloatArgumentRegisters + 8; 8 for ErrorReturn
IF ComCallPreStub_StackAlloc:MOD:16 != 0
GBLA GenericComCallStub_FirstStackAdjust
GenericComCallStub_FrameSize SETA (SIZEOF__GSCookie + SIZEOF__ComMethodFrame)
-GenericComCallStub_FirstStackAdjust SETA (SIZEOF__ArgumentRegisters + 2 * 8)
+GenericComCallStub_FirstStackAdjust SETA (8 + SIZEOF__ArgumentRegisters + 2 * 8)
GenericComCallStub_StackAlloc SETA GenericComCallStub_FrameSize - GenericComCallStub_FirstStackAdjust
GenericComCallStub_StackAlloc SETA GenericComCallStub_StackAlloc + SIZEOF__FloatArgumentRegisters
ldp x2, x3, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters + 16)]
ldp x4, x5, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters + 32)]
ldp x6, x7, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters + 48)]
- ldr x8, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters + 64)]
+ ldr x8, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters - 8)]
ldr x1, [x1, #(SIZEOF__ComMethodFrame - SIZEOF__ArgumentRegisters + 8)]
ENDIF
__PWTB_StackAlloc SETA __PWTB_TransitionBlock
-__PWTB_ArgumentRegisters SETA __PWTB_StackAlloc + 96
+__PWTB_ArgumentRegisters SETA __PWTB_StackAlloc + 104
PROLOG_SAVE_REG_PAIR fp, lr, #-176!
; Spill callee saved registers
__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET SETA 0
ENDIF
- stp x0, x1, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET)]
- stp x2, x3, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 16)]
- stp x4, x5, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 32)]
- stp x6, x7, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 48)]
- str x8, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 64)]
+ str x8, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET)]
+ stp x0, x1, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 8)]
+ stp x2, x3, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 24)]
+ stp x4, x5, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 40)]
+ stp x6, x7, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 56)]
+
MEND
; Reserve 64 bytes of memory before calling SAVE_FLOAT_ARGUMENT_REGISTERS
__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET SETA 0
ENDIF
- ldp x0, x1, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET)]
- ldp x2, x3, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 16)]
- ldp x4, x5, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 32)]
- ldp x6, x7, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 48)]
- ldr x8, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 64)]
+ ldr x8, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET)]
+ ldp x0, x1, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 8)]
+ ldp x2, x3, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 24)]
+ ldp x4, x5, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 40)]
+ ldp x6, x7, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 56)]
+
MEND
MACRO
#define CALLDESCR_ARGREGS 1 // CallDescrWorker has ArgumentRegister parameter
#define CALLDESCR_FPARGREGS 1 // CallDescrWorker has FloatArgumentRegisters parameter
+#define CALLDESCR_RETBUFFARGREG 1 // CallDescrWorker has RetBuffArg parameter that's separate from arg regs
// Given a return address retrieved during stackwalk,
// this is the offset by which it should be decremented to arrive at the callsite.
typedef INT64 StackElemType;
#define STACK_ELEM_SIZE sizeof(StackElemType)
-// !! This expression assumes STACK_ELEM_SIZE is a power of 2.
+// The expression below assumes STACK_ELEM_SIZE is a power of 2, so check that.
+static_assert(((STACK_ELEM_SIZE & (STACK_ELEM_SIZE-1)) == 0), "STACK_ELEM_SIZE must be a power of 2");
+
#define StackElemSize(parmSize) (((parmSize) + STACK_ELEM_SIZE - 1) & ~((ULONG)(STACK_ELEM_SIZE - 1)))
//
// will probably have to communicate this back to the PromoteCallerStack
// routine to avoid a double promotion.
//--------------------------------------------------------------------
+#define NUM_ARGUMENT_REGISTERS 8
typedef DPTR(struct ArgumentRegisters) PTR_ArgumentRegisters;
struct ArgumentRegisters {
- INT64 x[9]; // x0 ....x7 & x8 can contain return buffer address
+ INT64 x[NUM_ARGUMENT_REGISTERS]; // x0 ....x7. Note that x8 (return buffer address) is not included.
};
-#define NUM_ARGUMENT_REGISTERS 9
#define ARGUMENTREGISTERS_SIZE sizeof(ArgumentRegisters)
callDescrData.pSrc = pSrc;
callDescrData.numStackSlots = numStackSlotsToCopy;
#endif
+
+#ifdef CALLDESCR_RETBUFFARGREG
+ UINT64 retBuffArgPlaceholder = 0;
+ callDescrData.pRetBuffArg = &retBuffArgPlaceholder;
+#endif
+
#ifdef CALLDESCR_FPARGREGS
callDescrData.pFloatArgumentRegisters = NULL;
#endif
#ifdef CALLDESCR_ARGREGS
callDescrData.pArgumentRegisters = (ArgumentRegisters*)(pTransitionBlock + TransitionBlock::GetOffsetOfArgumentRegisters());
#endif
+#ifdef CALLDESCR_RETBUFFARGREG
+ callDescrData.pRetBuffArg = (UINT64*)(pTransitionBlock + TransitionBlock::GetOffsetOfRetBuffArgReg());
+#endif
#ifdef CALLDESCR_FPARGREGS
callDescrData.pFloatArgumentRegisters = pFloatArgumentRegisters;
#endif
UINT32 fpReturnSize;
PCODE pTarget;
+#ifdef CALLDESCR_RETBUFFARGREG
+ // Pointer to return buffer arg location
+ UINT64* pRetBuffArg;
+#endif
+
//
// Return value
//
INT64 x19, x20, x21, x22, x23, x24, x25, x26, x27, x28;
};
};
- ArgumentRegisters m_argumentRegisters;
TADDR padding; // Keep size of TransitionBlock as multiple of 16-byte. Simplifies code in PROLOG_WITH_TRANSITION_BLOCK
+ INT64 m_x8RetBuffReg;
+ ArgumentRegisters m_argumentRegisters;
#else
PORTABILITY_ASSERT("TransitionBlock");
#endif
return offsetof(TransitionBlock, m_ReturnAddress);
}
+#ifdef _TARGET_ARM64_
+ static int GetOffsetOfRetBuffArgReg()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return offsetof(TransitionBlock, m_x8RetBuffReg);
+ }
+#endif
+
static BYTE GetOffsetOfArgs()
{
LIMITED_METHOD_CONTRACT;
+
+ // Offset of the stack args (which are after the TransitionBlock)
return sizeof(TransitionBlock);
}
// in signatures (this pointer and the like). Whether or not these can be used successfully before all the
// explicit arguments have been scanned is platform dependent.
void GetThisLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetThisOffset(), pLoc); }
- void GetRetBuffArgLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetRetBuffArgOffset(), pLoc); }
void GetParamTypeLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetParamTypeArgOffset(), pLoc); }
void GetVASigCookieLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetVASigCookieOffset(), pLoc); }
+
+#ifndef CALLDESCR_RETBUFFARGREG
+ void GetRetBuffArgLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetRetBuffArgOffset(), pLoc); }
+#endif
+
#endif // !_TARGET_X86_
ArgLocDesc* GetArgLocDescForStructInRegs()
cSlots = 1;
}
+#ifdef _TARGET_ARM64_
+ // Sanity check to make sure no caller is trying to get an ArgLocDesc that
+ // describes the return buffer reg field that's in the TransitionBlock.
+ _ASSERTE(argOffset != TransitionBlock::GetOffsetOfRetBuffArgReg());
+#endif
+
if (!TransitionBlock::IsStackArgumentOffset(argOffset))
{
pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
pLoc->m_cGenReg = cSlots;
- }
+ }
else
{
pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset);
void GetSimpleLoc(int offset, ArgLocDesc * pLoc)
{
WRAPPER_NO_CONTRACT;
+
+#ifdef CALLDESCR_RETBUFFARGREG
+ // Codepaths where this could happen have been removed. If this occurs, something
+ // has been missed and this needs another look.
+ _ASSERTE(offset != TransitionBlock::GetOffsetOfRetBuffArgReg());
+#endif
+
pLoc->Init();
pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(offset);
pLoc->m_cGenReg = 1;
// x86 is special as always
ret += this->HasThis() ? offsetof(ArgumentRegisters, EDX) : offsetof(ArgumentRegisters, ECX);
#elif _TARGET_ARM64_
- ret += (int) offsetof(ArgumentRegisters, x[8]);
+ ret = TransitionBlock::GetOffsetOfRetBuffArgReg();
#else
if (this->HasThis())
ret += TARGET_POINTER_SIZE;
m_dwFlags |= ITERATION_STARTED;
}
+ // We're done going through the args for this MetaSig
if (m_argNum == this->NumFixedArgs())
return TransitionBlock::InvalidOffset;
}
else
{
+ // Only x0-x7 are valid argument registers (x8 is always the return buffer)
if (m_idxGenReg + cArgSlots <= 8)
{
+ // The entirety of the arg fits in the register slots.
+
int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
m_idxGenReg += cArgSlots;
return argOfs;
}
else
{
- m_idxGenReg = 8;
+#ifdef _WIN32
+ if (this->IsVarArg() && m_idxGenReg < 8)
+ {
+ // Address the Windows ARM64 varargs case where an arg is split between regs and stack.
+ // This can happen in the varargs case because the first 64 bytes of the stack are loaded
+ // into x0-x7, and any remaining stack arguments are placed normally.
+ int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
+
+ // Increase m_idxStack to account for the space used for the remainder of the arg after
+ // register slots are filled.
+ m_idxStack += (m_idxGenReg + cArgSlots - 8);
+
+ // We used up the remaining reg slots.
+ m_idxGenReg = 8;
+
+ return argOfs;
+ }
+ else
+#endif
+ {
+ // Don't use reg slots for this. It will be passed purely on the stack arg space.
+ m_idxGenReg = 8;
+ }
}
}
{
// The return buffer argument is implicit in both signatures.
+#if !defined(_TARGET_ARM64_) || !defined(CALLDESCR_RETBUFFARGREG)
+ // The ifdef above disables this code if the ret buff arg is always in the same register, which
+ // means that we don't need to do any shuffling for it.
+
sArgPlacerSrc.GetRetBuffArgLoc(&sArgSrc);
sArgPlacerDst.GetRetBuffArgLoc(&sArgDst);
// along) in the case where it's not a no-op (i.e. the source and destination ops are different).
if (entry.srcofs != entry.dstofs)
pShuffleEntryArray->Append(entry);
+#endif // !defined(_TARGET_ARM64_) || !defined(CALLDESCR_RETBUFFARGREG)
}
// Iterate all the regular arguments. mapping source registers and stack locations to the corresponding
#elif defined (_TARGET_ARM64_)
TADDR m_fp;
TADDR m_ReturnAddress;
+ TADDR m_x8; // ret buff arg
ArgumentRegisters m_argumentRegisters;
#else
TADDR m_ReturnAddress; // return address into unmanaged code
#ifdef CALLDESCR_ARGREGS
callDescrData.pArgumentRegisters = (ArgumentRegisters*)(pTransitionBlock + TransitionBlock::GetOffsetOfArgumentRegisters());
#endif
+#ifdef CALLDESCR_RETBUFFARGREG
+ callDescrData.pRetBuffArg = (UINT64*)(pTransitionBlock + TransitionBlock::GetOffsetOfRetBuffArgReg());
+#endif
#ifdef CALLDESCR_FPARGREGS
callDescrData.pFloatArgumentRegisters = NULL;
#endif