Make VM-side changes for ARM64 Windows calling convention.
authorAditya Mandaleeka <adityam@microsoft.com>
Tue, 7 Aug 2018 22:17:52 +0000 (15:17 -0700)
committerAditya Mandaleeka <adityam@microsoft.com>
Tue, 7 Aug 2018 22:17:52 +0000 (15:17 -0700)
src/vm/arm64/CallDescrWorkerARM64.asm
src/vm/arm64/asmconstants.h
src/vm/arm64/asmhelpers.asm
src/vm/arm64/asmmacros.h
src/vm/arm64/cgencpu.h
src/vm/callhelpers.cpp
src/vm/callhelpers.h
src/vm/callingconvention.h
src/vm/comdelegate.cpp
src/vm/frames.h
src/vm/reflectioninvocation.cpp

index b9f8a6090d16ed925517de392117bb1ce1e7d425..65c7db6f3fe157bbcc853946bc36ead85ed01f46 100644 (file)
@@ -62,15 +62,18 @@ Ldonestack
         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]
index dca845d000a73a3478452928c8f6fb52e704edc0..262e481b53197aea54b2f5fd67ba286c649a0530 100644 (file)
@@ -55,19 +55,20 @@ ASMCONSTANTS_C_ASSERT(AppDomain__m_dwId == offsetof(AppDomain, m_dwId));
 
 #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))
@@ -75,6 +76,7 @@ ASMCONSTANTS_C_ASSERT(CallDescrData__pArgumentRegisters   == offsetof(CallDescrD
 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
index 376bbbcec7b07f115efbd9ec99f873de54e6f0df..60a896a80cd30fcde5fc14932f2564793131b0bf 100644 (file)
@@ -533,7 +533,7 @@ LNullThis
     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
@@ -603,7 +603,7 @@ ComCallPreStub_ErrorExit
     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
 
@@ -676,7 +676,7 @@ COMToCLRDispatchHelper_RegSetup
     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)]
 
index 74613daa8fb246f17ba176340d09be4f96b9ee7d..e70b987ce4900e6408a0d6f351c5c7891ecb72cb 100644 (file)
@@ -72,7 +72,7 @@ __PWTB_TransitionBlock SETA __PWTB_FloatArgumentRegisters
         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 
@@ -172,11 +172,12 @@ __PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET SETA $offset
 __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
@@ -208,11 +209,12 @@ __PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET SETA $offset
 __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
index 7e3d62056a73f16d100ac8257a67cb88054c5d6c..bb85052a688fe5fd771dc05c750f69f7377e8f10 100644 (file)
@@ -57,6 +57,7 @@ extern PCODE GetPreStubEntryPoint();
 
 #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.
@@ -77,7 +78,9 @@ extern PCODE GetPreStubEntryPoint();
 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)))
 
 //
@@ -115,11 +118,11 @@ struct CalleeSavedRegisters {
 // 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)
 
index 55073a8a1ebf4865adc46885a6bc53e32e27f29c..d11c4e83d4b73e7270d00ef0a3c65a0a29538d04 100644 (file)
@@ -208,6 +208,12 @@ void * DispatchCallSimple(
     callDescrData.pSrc = pSrc;
     callDescrData.numStackSlots = numStackSlotsToCopy;
 #endif
+
+#ifdef CALLDESCR_RETBUFFARGREG
+    UINT64 retBuffArgPlaceholder = 0;
+    callDescrData.pRetBuffArg = &retBuffArgPlaceholder;
+#endif
+
 #ifdef CALLDESCR_FPARGREGS
     callDescrData.pFloatArgumentRegisters = NULL;
 #endif
@@ -597,6 +603,9 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *
 #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
index a10ad765beb54d378eb9fac8f376661cc99e5087..bcd553dcc89888bc677031c3df1b19dfbaea4dad 100644 (file)
@@ -30,6 +30,11 @@ struct CallDescrData
     UINT32                      fpReturnSize;
     PCODE                       pTarget;
 
+#ifdef CALLDESCR_RETBUFFARGREG
+    // Pointer to return buffer arg location
+    UINT64*                     pRetBuffArg;
+#endif
+
     //
     // Return value
     //
index a0d7bd4fa02c385c1a220b8d355dd265a0283b14..a653d8570f331f8dd0802426436d477422e12c88 100644 (file)
@@ -120,8 +120,9 @@ struct TransitionBlock
             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
@@ -135,9 +136,19 @@ struct TransitionBlock
         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);
     }
 
@@ -491,9 +502,13 @@ public:
     // 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()
@@ -584,11 +599,17 @@ public:
             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);
@@ -709,6 +730,13 @@ protected:
     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;
@@ -747,7 +775,7 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetRetBuffArgOffset()
     // 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;
@@ -929,6 +957,7 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
         m_dwFlags |= ITERATION_STARTED;
     }
 
+    // We're done going through the args for this MetaSig
     if (m_argNum == this->NumFixedArgs())
         return TransitionBlock::InvalidOffset;
 
@@ -1312,15 +1341,40 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
     }
     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;
+            }
         }
     }
 
index 5db7ce7053f21ab7b370e69b406417564147e4a6..a792fe6435a774e211ed1d6d9506f2619bcd1507 100644 (file)
@@ -405,6 +405,10 @@ VOID GenerateShuffleArray(MethodDesc* pInvoke, MethodDesc *pTargetMeth, SArray<S
     {
         // 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);
 
@@ -419,6 +423,7 @@ VOID GenerateShuffleArray(MethodDesc* pInvoke, MethodDesc *pTargetMeth, SArray<S
         // 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
index 09803788bc6c8db3d3c03c695b79ea6342e44279..5da6bcdacde378a915c469556b9577a3821c7a73 100644 (file)
@@ -2004,6 +2004,7 @@ protected:
 #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
index 742a26e7a4f671cb95a738b00d755f415e2b01dc..4d7ae83a544897a3ed4997537a53183d5d7d9203 100644 (file)
@@ -1061,6 +1061,9 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
 #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