ARM64: ABI - Support for using register x8 as the return buffer argument for structs
authorBrian Sullivan <briansul@microsoft.com>
Mon, 6 Jun 2016 23:25:26 +0000 (16:25 -0700)
committerBrian Sullivan <briansul@microsoft.com>
Tue, 7 Jun 2016 22:55:51 +0000 (15:55 -0700)
This is feature complete and fixes dotnet/coreclr#4949 but is being checked in disabled as it has dependencies
on some additional work in the VM dotnet/coreclr#4950 (saving and restoring register x8 in thePrestub, etc..)

In target.h
Added defines on ARM64 for the additional argument register used to pass the return buffer
This is register x8 and when it used it is considered the 9th argiment with an arg num of 8 (zero based)
Added new method hasFixedRetBuffReg() that will return true on ARM64 (when this feature is fully enabled)
Added new method theFixedRetBuffReg() that returns register REG_R8 (x8) for ARM64
Added new method theFixedRetBuffArgNum that returns RET_BUFF_ARGNUM (8) for ARM64
Updated isValidIntArgReg to allow for a fixed return buff register

In codegencommon.cpp:
Increase the static size of regArgTab[] by one for the integer registers
This allows us to optionally support having a fixed return buffer registers
We now track the maximum index into regArgTab[] using argMax which is
either the old value of  regState->rsCalleeRegArgCount or thefixedRetBufArgNum()
Corrected the emitAttr size value for 64-bit targets to use EA_PTRSIZE instead of EA_4BYTE

In codegeninterface.cpp: (and other places)
Renamed rsCalleeRegNum to be rsCalleeRegArgCount to better reflect what it represents

In compiler.hpp
Updated genMapIntRegArgNumToRegNum and genMapIntRegNumToRegArgNum to handle
the fixed return buffer register for ARM64

In lclvars.cpp
Updated lvaInitRetBuffArg to handle the fixed return buffer register for ARM64

In morph.cpp
Updated fgMorphArgs to properly handle the fixed return buffer register for ARM64

In regalloc.cpp
Updated the assert check raUpdateRegStateForArg to allow for the fixed return buffer register for ARM64

Updated with changes from code review feedback

Commit migrated from https://github.com/dotnet/coreclr/commit/e7abaeb795a6ff8556b871822f7ec092eb50835c

src/coreclr/src/jit/codegencommon.cpp
src/coreclr/src/jit/codegeninterface.h
src/coreclr/src/jit/codegenlegacy.cpp
src/coreclr/src/jit/compiler.hpp
src/coreclr/src/jit/gcencode.cpp
src/coreclr/src/jit/lclvars.cpp
src/coreclr/src/jit/morph.cpp
src/coreclr/src/jit/regalloc.cpp
src/coreclr/src/jit/target.h

index 623aabc..1de9cc2 100755 (executable)
@@ -3749,9 +3749,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 #pragma warning(push)
 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
 #endif
-void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,                                            
-                                                   bool *    pXtraRegClobbered,
-                                                   RegState *regState)
+void            CodeGen::genFnPrologCalleeRegArgs(regNumber  xtraReg,                                            
+                                                  bool*      pXtraRegClobbered,
+                                                  RegState*  regState)
 {
 #ifdef DEBUG
     if (verbose) 
@@ -3767,30 +3767,69 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
     }
 #endif
 
-    assert(compiler->compGeneratingProlog);
-    noway_assert(regState->rsCalleeRegArgMaskLiveIn != 0);
-    noway_assert(regState->rsCalleeRegArgNum <= MAX_REG_ARG       ||  regState->rsIsFloat);
-    noway_assert(regState->rsCalleeRegArgNum <= MAX_FLOAT_REG_ARG || !regState->rsIsFloat);
-
-    unsigned    argNum         = 0;
-    unsigned    regArgNum;
+    unsigned    argMax;            // maximum argNum value plus 1, (including the RetBuffArg)
+    unsigned    argNum;            // current argNum, always in [0..argMax-1]
+    unsigned    fixedRetBufIndex;  // argNum value used by the fixed return buffer argument (ARM64)
+    unsigned    regArgNum;         // index into the regArgTab[] table
     regMaskTP   regArgMaskLive = regState->rsCalleeRegArgMaskLiveIn;
     bool        doingFloat     = regState->rsIsFloat;
 
+    // We should be generating the prolog block when we are called
+    assert(compiler->compGeneratingProlog);
+
+    // We expect to have some registers of the type we are doing, that are LiveIn, otherwise we don't need to be called.
+    noway_assert(regArgMaskLive != 0);
+
+    // If a method has 3 args (and no fixed return buffer) then argMax is 3 and valid indexes are 0,1,2
+    // If a method has a fixed return buffer (on ARM64) then argMax gets set to 9 and valid index are 0-8
+    //
+    // The regArgTab can always have unused entries, 
+    //    for example if an architecture always increments the arg register number but uses either
+    //    an integer register or a floating point register to hold the next argument 
+    //    then with a mix of float and integer args you could have:
+    //
+    //    sampleMethod(int i, float x, int j, float y, int k, float z);
+    //          r0, r2 and r4 as valid integer arguments with argMax as 5 
+    //      and f1, f3 and f5 and valid floating point arguments with argMax as 6
+    //    The first one is doingFloat==false and the second one is doingFloat==true 
+    //
+    //    If a fixed return buffer (in r8) was also present then the first one would become:
+    //          r0, r2, r4 and r8 as valid integer arguments with argMax as 9
+    //
+
+    argMax = regState->rsCalleeRegArgCount;
+    fixedRetBufIndex = (unsigned)-1;   // Invalid value
+
     // If necessary we will select a correct xtraReg for circular floating point args later.
     if (doingFloat)
+    {
         xtraReg = REG_NA;
+        noway_assert(argMax <= MAX_FLOAT_REG_ARG);
+    }
+    else  // we are doing the integer registers
+    {
+        noway_assert(argMax <= MAX_REG_ARG);
+        if (hasFixedRetBuffReg())
+        {
+            fixedRetBufIndex = theFixedRetBuffArgNum();
+            // We have an additional integer register argument when hasFixedRetBuffReg() is true
+            argMax = fixedRetBufIndex+1;
+            assert(argMax == (MAX_REG_ARG + 1));
+        }
+    }
 
-    /* Construct a table with the register arguments, for detecting circular and
-     * non-circular dependencies between the register arguments. A dependency is when
-     * an argument register Rn needs to be moved to register Rm that is also an argument
-     * register. The table is constructed in the order the arguments are passed in
-     * registers: the first register argument is in regArgTab[0], the second in
-     * regArgTab[1], etc. Note that on ARM, a TYP_DOUBLE takes two entries, starting
-     * at an even index. regArgTab is indexed from 0 to regState->rsCalleeRegArgNum - 1.
-     */
-
-    struct
+    //
+    // Construct a table with the register arguments, for detecting circular and
+    // non-circular dependencies between the register arguments. A dependency is when
+    // an argument register Rn needs to be moved to register Rm that is also an argument
+    // register. The table is constructed in the order the arguments are passed in
+    // registers: the first register argument is in regArgTab[0], the second in
+    // regArgTab[1], etc. Note that on ARM, a TYP_DOUBLE takes two entries, starting
+    // at an even index. The regArgTab is indexed from 0 to argMax - 1.
+    // Note that due to an extra argument register for ARM64 (i.e  theFixedRetBuffReg())
+    // we have increased the allocated size of the regArgTab[] by one.
+    //
+    struct regArgElem
     {
         unsigned    varNum;     // index into compiler->lvaTable[] for this register argument
 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
@@ -3832,15 +3871,15 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
         }
 
 #endif // !FEATURE_UNIX_AMD64_STRUCT_PASSING
-    } regArgTab [max(MAX_REG_ARG,MAX_FLOAT_REG_ARG)] = { };
+    } regArgTab[max(MAX_REG_ARG+1, MAX_FLOAT_REG_ARG)] = {};
 
-    unsigned    varNum;
-    LclVarDsc * varDsc;
+    unsigned     varNum;
+    LclVarDsc*   varDsc;
     for (varNum = 0, varDsc = compiler->lvaTable;
          varNum < compiler->lvaCount;
          varNum++, varDsc++)
     {
-        /* Is this variable a register arg? */
+        // Is this variable a register arg?
         if (!varDsc->lvIsParam)
         {
             continue;
@@ -3974,7 +4013,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
                     }
 
                     // Bingo - add it to our table
-                    noway_assert(regArgNum < regState->rsCalleeRegArgNum);
+                    noway_assert(regArgNum < argMax);
                     noway_assert(regArgTab[regArgNum].slot == 0); // we better not have added it already (there better not be multiple vars representing this argument register)
                     regArgTab[regArgNum].varNum = varNum;
                     regArgTab[regArgNum].slot = (char)(slotCounter + 1);
@@ -3995,7 +4034,8 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
         {
             // Bingo - add it to our table
             regArgNum = genMapRegNumToRegArgNum(varDsc->lvArgReg, regType);
-            noway_assert(regArgNum < regState->rsCalleeRegArgNum);
+
+            noway_assert(regArgNum < argMax);
             // we better not have added it already (there better not be multiple vars representing this argument register)
             noway_assert(regArgTab[regArgNum].slot == 0);
 
@@ -4028,12 +4068,12 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
                 // Note that regArgNum+1 represents an argument index not an actual argument register.  
                 // see genMapRegArgNumToRegNum(unsigned argNum, var_types type)
 
-
                 // This is the setup for the rest of a multireg struct arg
-                noway_assert((regArgNum + (slots - 1)) < regState->rsCalleeRegArgNum);
 
                 for (int i = 1; i<slots; i++)
                 {
+                    noway_assert((regArgNum + i) < argMax);
+
                     // we better not have added it already (there better not be multiple vars representing this argument register)
                     noway_assert(regArgTab[regArgNum + i].slot == 0);
 
@@ -4184,7 +4224,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
         {
             change = false;
 
-            for (argNum = 0; argNum < regState->rsCalleeRegArgNum; argNum++)
+            for (argNum = 0; argNum < argMax; argNum++)
             {
                 // If we already marked the argument as non-circular then continue
 
@@ -4246,8 +4286,8 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
                 {
                     /* we are trashing a live argument register - record it */
                     unsigned destRegArgNum = genMapRegNumToRegArgNum(destRegNum, regType);
-                    noway_assert(destRegArgNum < regState->rsCalleeRegArgNum);
-                    regArgTab[destRegArgNum].trashBy  = argNum;
+                    noway_assert(destRegArgNum < argMax);
+                    regArgTab[destRegArgNum].trashBy = argNum;
                 }
                 else
                 {
@@ -4286,7 +4326,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
      * free some registers. */
 
     regArgMaskLive = regState->rsCalleeRegArgMaskLiveIn; // reset the live in to what it was at the start
-    for (argNum = 0; argNum < regState->rsCalleeRegArgNum; argNum++)
+    for (argNum = 0; argNum < argMax; argNum++)
     {
         emitAttr        size;
 
@@ -4486,7 +4526,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
 #endif
         }
 
-        for (argNum = 0; argNum < regState->rsCalleeRegArgNum; argNum++)
+        for (argNum = 0; argNum < argMax; argNum++)
         {
             // If not a circular dependency then continue
             if (!regArgTab[argNum].circular)
@@ -4508,18 +4548,18 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
 
             destReg = begReg = argNum;
             srcReg  = regArgTab[argNum].trashBy;
-            noway_assert(srcReg < regState->rsCalleeRegArgNum);
 
             varNumDest = regArgTab[destReg].varNum; 
             noway_assert(varNumDest < compiler->lvaCount);
             varDscDest = compiler->lvaTable + varNumDest;
             noway_assert(varDscDest->lvIsParam && varDscDest->lvIsRegArg);
 
+            noway_assert(srcReg < argMax);
             varNumSrc = regArgTab[srcReg].varNum; noway_assert(varNumSrc < compiler->lvaCount);
             varDscSrc = compiler->lvaTable + varNumSrc;
             noway_assert(varDscSrc->lvIsParam && varDscSrc->lvIsRegArg);
 
-            emitAttr size = EA_4BYTE;
+            emitAttr  size = EA_PTRSIZE;
 
 #ifdef _TARGET_XARCH_
             // 
@@ -4571,13 +4611,6 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
             else
 #endif // _TARGET_XARCH_
             {
-                // Treat doubles as floats for ARM because we could have partial circular
-                // dependencies of a float with a lo/hi part of the double. We mark the
-                // trashBy values for each slot of the double, so let the circular dependency
-                // logic work its way out for floats rather than doubles. If a cycle has all
-                // doubles, then optimize so that instead of two vmov.f32's to move a double,
-                // we can use one vmov.f64.
-
                 var_types destMemType = varDscDest->TypeGet();
 
 #ifdef _TARGET_ARM_
@@ -4595,6 +4628,13 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
                 }
                 while (iter != begReg);
 
+                // We may treat doubles as floats for ARM because we could have partial circular
+                // dependencies of a float with a lo/hi part of the double. We mark the
+                // trashBy values for each slot of the double, so let the circular dependency
+                // logic work its way out for floats rather than doubles. If a cycle has all
+                // doubles, then optimize so that instead of two vmov.f32's to move a double,
+                // we can use one vmov.f64.
+                //
                 if (!cycleAllDouble && destMemType == TYP_DOUBLE)
                 {
                     destMemType = TYP_FLOAT;
@@ -4605,11 +4645,15 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
                 {
                     size = EA_GCREF;
                 }
-                else if  (destMemType == TYP_DOUBLE)
+                else if (destMemType == TYP_BYREF)
+                {
+                    size = EA_BYREF;
+                }
+                else if (destMemType == TYP_DOUBLE)
                 {
                     size = EA_8BYTE;
                 }
-                else
+                else  if (destMemType == TYP_FLOAT)
                 {
                     size = EA_4BYTE;
                 }
@@ -4619,10 +4663,8 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
                 assert(xtraReg != REG_NA);
 
                 regNumber begRegNum = genMapRegArgNumToRegNum(begReg, destMemType);
-                getEmitter()->emitIns_R_R (insCopy,
-                                         size,
-                                         xtraReg,
-                                         begRegNum);
+
+                getEmitter()->emitIns_R_R (insCopy, size, xtraReg, begRegNum);
 
                 regTracker.rsTrackRegCopy(xtraReg, begRegNum);
 
@@ -4639,14 +4681,12 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
                     regNumber destRegNum = genMapRegArgNumToRegNum(destReg, destMemType);
                     regNumber srcRegNum  = genMapRegArgNumToRegNum(srcReg, destMemType);
 
-                    getEmitter()->emitIns_R_R(insCopy,
-                                            size,
-                                            destRegNum,
-                                            srcRegNum);
+                    getEmitter()->emitIns_R_R(insCopy, size, destRegNum, srcRegNum);
 
                     regTracker.rsTrackRegCopy(destRegNum, srcRegNum);
 
                     /* mark 'src' as processed */
+                    noway_assert(srcReg < argMax);
                     regArgTab[srcReg].processed  = true;
 #ifdef _TARGET_ARM_
                     if (size == EA_8BYTE)
@@ -4656,7 +4696,8 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
 
                     /* move to the next pair */
                     destReg = srcReg;
-                    srcReg = regArgTab[srcReg].trashBy; noway_assert(srcReg < regState->rsCalleeRegArgNum);
+                    srcReg = regArgTab[srcReg].trashBy; 
+
                     varDscDest = varDscSrc;
                     destMemType = varDscDest->TypeGet();
 #ifdef _TARGET_ARM_
@@ -4665,7 +4706,8 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
                         destMemType = TYP_FLOAT;
                     }
 #endif
-                    varNumSrc = regArgTab[srcReg].varNum; noway_assert(varNumSrc < compiler->lvaCount);
+                    varNumSrc = regArgTab[srcReg].varNum; 
+                    noway_assert(varNumSrc < compiler->lvaCount);
                     varDscSrc = compiler->lvaTable + varNumSrc;
                     noway_assert(varDscSrc->lvIsParam && varDscSrc->lvIsRegArg);
 
@@ -4691,9 +4733,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
 
                 regNumber destRegNum = genMapRegArgNumToRegNum(destReg, destMemType);
 
-                getEmitter()->emitIns_R_R(insCopy, size,
-                                        destRegNum,
-                                        xtraReg);
+                getEmitter()->emitIns_R_R(insCopy, size, destRegNum, xtraReg);
 
                 regTracker.rsTrackRegCopy(destRegNum, xtraReg);
 
@@ -4716,7 +4756,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
     {
         regMaskTP regArgMaskLiveSave = regArgMaskLive;
 
-        for (argNum = 0; argNum < regState->rsCalleeRegArgNum; argNum++)
+        for (argNum = 0; argNum < argMax; argNum++)
         {
             /* If already processed go to the next one */
             if (regArgTab[argNum].processed)
@@ -4885,7 +4925,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
 #endif
 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) && defined(FEATURE_SIMD)
             if (varTypeIsStruct(varDsc) &&
-                argNum < (regState->rsCalleeRegArgNum - 1) &&
+                argNum < (argMax - 1) &&
                 regArgTab[argNum+1].slot == 2)
             {
                 argRegCount = 2;
@@ -9349,8 +9389,8 @@ void                CodeGen::genFnEpilog(BasicBlock* block)
 
 #if defined(_TARGET_X86_)
 
-        noway_assert(compiler->compArgSize >= intRegState.rsCalleeRegArgNum * sizeof(void *));
-        stkArgSize = compiler->compArgSize - intRegState.rsCalleeRegArgNum * sizeof(void *);
+        noway_assert(compiler->compArgSize >= intRegState.rsCalleeRegArgCount * sizeof(void *));
+        stkArgSize = compiler->compArgSize - intRegState.rsCalleeRegArgCount * sizeof(void *);
 
         noway_assert(compiler->compArgSize < 0x10000); // "ret" only has 2 byte operand
 
index 285b397..1b1196d 100644 (file)
@@ -36,7 +36,7 @@ class emitter;
 struct RegState
 {
     unsigned            rsCurRegArgNum;             // current argument register (for caller)
-    unsigned            rsCalleeRegArgNum;          // total number of incoming register arguments
+    unsigned            rsCalleeRegArgCount;        // total number of incoming register arguments
     regMaskTP           rsCalleeRegArgMaskLiveIn;   // mask of register arguments (live on entry to method)
     bool                rsIsFloat;
     unsigned            rsMaxRegArgNum;             // maximum register argument number + 1 (that is, exclusive of end of range)
index 5939557..b8d03af 100644 (file)
@@ -20116,7 +20116,7 @@ regMaskTP           CodeGen::genCodeForCall(GenTreePtr  call,
 
         // Push the count of the incoming stack arguments
 
-        unsigned nOldStkArgs = (unsigned)((compiler->compArgSize - (intRegState.rsCalleeRegArgNum * sizeof(void *)))/sizeof(void*));
+        unsigned nOldStkArgs = (unsigned)((compiler->compArgSize - (intRegState.rsCalleeRegArgCount * sizeof(void *)))/sizeof(void*));
         getEmitter()->emitIns_I(INS_push, EA_4BYTE, nOldStkArgs);
         genSinglePush(); // Keep track of ESP for EBP-less frames
         args += sizeof(void*);
@@ -21217,7 +21217,7 @@ void        CodeGen::genSetScopeInfo  (unsigned                 which,
 
         noway_assert(cookieOffset < varOffset);
         unsigned offset = varOffset - cookieOffset;
-        unsigned stkArgSize = compiler->compArgSize - intRegState.rsCalleeRegArgNum * sizeof(void *);
+        unsigned stkArgSize = compiler->compArgSize - intRegState.rsCalleeRegArgCount * sizeof(void *);
         noway_assert(offset < stkArgSize);
         offset = stkArgSize - offset;
 
index 4d8a9a8..734e051 100644 (file)
@@ -3147,11 +3147,18 @@ DWORD               StrictCheckForNonVirtualCallToVirtualMethod()
  *      [0, MAX_FLOAT_REG_ARG)  -- for floating point registers
  * Note that RegArgNum's are overlapping for integer and floating-point registers,
  * while RegNum's are not (for ARM anyway, though for x86, it might be different).
+ * If we have a fixed return buffer register and are given it's index
+ * we return the fixed return buffer register
  */
 
 inline
 regNumber           genMapIntRegArgNumToRegNum(unsigned argNum)
 {
+    if (hasFixedRetBuffReg() && (argNum == theFixedRetBuffArgNum()))
+    {
+        return theFixedRetBuffReg();
+    }
+
     assert (argNum < ArrLen(intArgRegs));
     
     return intArgRegs[argNum];
@@ -3230,11 +3237,19 @@ __forceinline regMaskTP genMapArgNumToRegMask(unsigned argNum, var_types type)
 
 /*****************************************************************************/
 /* Map a register number ("RegNum") to a register argument number ("RegArgNum")
+ * If we have a fixed return buffer register we return theFixedRetBuffArgNum
  */
 
 inline
 unsigned           genMapIntRegNumToRegArgNum(regNumber regNum)
 {
+    // First check for the Arm64 fixed return buffer argument register
+    // as it is not in the RBM_ARG_REGS set of registers
+    if (hasFixedRetBuffReg() && (regNum == theFixedRetBuffReg()))
+    {
+        return theFixedRetBuffArgNum();
+    }
+
     assert (genRegMask(regNum) & RBM_ARG_REGS);
 
     switch (regNum)
@@ -3261,7 +3276,9 @@ unsigned           genMapIntRegNumToRegArgNum(regNumber regNum)
 #endif
 #endif
 #endif
-    default: assert(!"invalid register arg register"); return (unsigned)-1;
+    default: 
+        assert(!"invalid register arg register"); 
+        return (unsigned)-1;
     }
 }
 
index 1965073..c3ae12b 100644 (file)
@@ -1196,7 +1196,7 @@ size_t              GCInfo::gcInfoBlockHdrSave(BYTE*      dest,
 
     assert((compiler->compArgSize & 0x3) == 0);
 
-    size_t argCount = (compiler->compArgSize - (compiler->codeGen->intRegState.rsCalleeRegArgNum * sizeof(void *))) / sizeof(void*);
+    size_t argCount = (compiler->compArgSize - (compiler->codeGen->intRegState.rsCalleeRegArgCount * sizeof(void *))) / sizeof(void*);
     assert(argCount <= MAX_USHORT_SIZE_T);
     header->argCount  = static_cast<unsigned short>(argCount);
 
index 74ba79d..8160950 100644 (file)
@@ -127,8 +127,8 @@ void                Compiler::lvaInitTypeRef()
     }
 #endif // FEATURE_SIMD
 
-    // Are we returning a struct by value? 
-    
+    // Are we returning a struct using a return buffer argument?
+    //
     const bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo);
 
     // Change the compRetNativeType if we are returning a struct by value in a register
@@ -352,10 +352,9 @@ void                Compiler::lvaInitArgs(InitVarDscInfo *          varDscInfo)
     noway_assert(varDscInfo->varNum == info.compArgsCount);
     assert (varDscInfo->intRegArgNum <= MAX_REG_ARG);
 
-    codeGen->intRegState.rsCalleeRegArgNum = varDscInfo->intRegArgNum;
-
+    codeGen->intRegState.rsCalleeRegArgCount = varDscInfo->intRegArgNum;
 #if !FEATURE_STACK_FP_X87
-    codeGen->floatRegState.rsCalleeRegArgNum = varDscInfo->floatRegArgNum;
+    codeGen->floatRegState.rsCalleeRegArgCount = varDscInfo->floatRegArgNum;
 #endif // FEATURE_STACK_FP_X87
 
     // The total argument size must be aligned.
@@ -453,15 +452,8 @@ void                Compiler::lvaInitRetBuffArg(InitVarDscInfo *    varDscInfo)
     LclVarDsc * varDsc = varDscInfo->varDsc;
     bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo);
 
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
-    if (varTypeIsStruct(info.compRetNativeType))
-    {
-        if (IsRegisterPassable(info.compMethodInfo->args.retTypeClass))
-        {
-            hasRetBuffArg = false;
-        }
-    }
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+    // These two should always match
+    noway_assert(hasRetBuffArg == varDscInfo->hasRetBuf);
 
     if (hasRetBuffArg)
     {
@@ -472,7 +464,16 @@ void                Compiler::lvaInitRetBuffArg(InitVarDscInfo *    varDscInfo)
 #if ASSERTION_PROP
         varDsc->lvSingleDef = 1;
 #endif
-        varDsc->lvArgReg  = genMapRegArgNumToRegNum(varDscInfo->allocRegArg(TYP_INT), varDsc->TypeGet());
+        if (hasFixedRetBuffReg())
+        {
+            varDsc->lvArgReg = theFixedRetBuffReg();
+        }
+        else
+        {
+            unsigned retBuffArgNum = varDscInfo->allocRegArg(TYP_INT);
+            varDsc->lvArgReg = genMapIntRegArgNumToRegNum(retBuffArgNum);
+        }
+
 #if FEATURE_MULTIREG__ARGS
         varDsc->lvOtherArgReg = REG_NA;
 #endif
@@ -494,8 +495,7 @@ void                Compiler::lvaInitRetBuffArg(InitVarDscInfo *    varDscInfo)
                 varDsc->lvType = TYP_I_IMPL;
             }
         }
-
-        assert(genMapIntRegNumToRegArgNum(varDsc->lvArgReg) < MAX_REG_ARG);
+        assert(isValidIntArgReg(varDsc->lvArgReg));
 
 #ifdef  DEBUG
         if  (verbose)
@@ -985,7 +985,7 @@ void                Compiler::lvaInitGenericsCtxt(InitVarDscInfo *  varDscInfo)
 
             varDsc->lvIsRegArg = 1;
             varDsc->lvArgReg   = genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet());
-#if FEATURE_MULTIREG__ARGS
+#if FEATURE_MULTIREG_ARGS
             varDsc->lvOtherArgReg = REG_NA;
 #endif
             varDsc->setPrefReg(varDsc->lvArgReg, this);
@@ -4150,11 +4150,11 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
 
     /* Update the argOffs to reflect arguments that are passed in registers */
 
-    noway_assert(codeGen->intRegState.rsCalleeRegArgNum <= MAX_REG_ARG);
-    noway_assert(compArgSize >= codeGen->intRegState.rsCalleeRegArgNum * sizeof(void *));
+    noway_assert(codeGen->intRegState.rsCalleeRegArgCount <= MAX_REG_ARG); 
+    noway_assert(compArgSize >= codeGen->intRegState.rsCalleeRegArgCount * sizeof(void *));
 
 #ifdef _TARGET_X86_
-    argOffs -= codeGen->intRegState.rsCalleeRegArgNum * sizeof(void *);
+    argOffs -= codeGen->intRegState.rsCalleeRegArgCount * sizeof(void *);
 #endif
 
 #ifndef LEGACY_BACKEND
index 1cd9eab..1ef8d2a 100644 (file)
@@ -2840,7 +2840,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
         call->fgArgInfo = new (this, CMK_Unknown) fgArgInfo(this, call, numArgs);
     }
 
-
     fgFixupStructReturn(call);
 
     /* First we morph the argument subtrees ('this' pointer, arguments, etc.).
@@ -2996,6 +2995,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
     SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
 #endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
 
+    bool expectRetBuffArg      = call->HasRetBufArg();
     bool hasStructArgument     = false;   // @TODO-ARM64-UNIX: Remove this bool during a future refactoring 
     bool hasMultiregStructArgs = false;
     for (args = call->gtCallArgs; args; args = args->gtOp.gtOp2)
@@ -3813,6 +3813,24 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
             assert(size == 1);
 #endif
 #endif
+            // If 'expectRetBuffArg' is true then the next argument is the RetBufArg
+            // and we may need to change nextRegNum to the theFixedRetBuffReg
+            //
+            if (expectRetBuffArg)
+            {
+                assert(passUsingFloatRegs == false);
+              
+                if (hasFixedRetBuffReg())
+                {
+                    // Change the register used to pass the next argument to the fixed return buffer register
+                    nextRegNum = theFixedRetBuffReg();
+                    // Note that later in this method we don't increment intArgRegNum when we 
+                    // have setup nextRegRun to be the fixed retrurn buffer register
+                }
+
+                // We no longer are expecting the RetBufArg
+                expectRetBuffArg = false;
+            }
 
 #ifndef LEGACY_BACKEND
             // If there are nonstandard args (outside the calling convention) they were inserted above
@@ -3916,7 +3934,17 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
                     }
                     else
                     {
-                        intArgRegNum += size;
+                        if (hasFixedRetBuffReg() && (nextRegNum == theFixedRetBuffReg()))
+                        {
+                            // we are setting up the fixed return buffer register argument
+                            // so don't increment intArgRegNum
+                            assert(size == 1);
+                        }
+                        else
+                        {
+                            // Increment intArgRegNum by 'size' registers
+                            intArgRegNum += size;
+                        }
 
 #if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
                         fltArgSkippedRegMask |= genMapArgNumToRegMask(fltArgRegNum, TYP_DOUBLE);
@@ -4015,7 +4043,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
     if  (!lateArgsComputed)
     {
         call->fgArgInfo->ArgsComplete();
-
         call->gtCallRegUsedMask = genIntAllRegArgMask(intArgRegNum) & ~argSkippedRegMask;
         if (fltArgRegNum > 0)
         {
@@ -4095,7 +4122,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
 #ifdef DEBUG
         if (verbose)
         {
-            printf("argSlots=%d, preallocatedArgCount=%d, nextSlotNum=%d, lvaOutgoingArgSpaceSize=%d",
+            printf("argSlots=%d, preallocatedArgCount=%d, nextSlotNum=%d, lvaOutgoingArgSpaceSize=%d\n",
                    argSlots, preallocatedArgCount, call->fgArgInfo->GetNextSlotNum(), lvaOutgoingArgSpaceSize);
         }
 #endif
@@ -5676,7 +5703,7 @@ GenTreePtr          Compiler::fgMorphStackArgForVarArgs(unsigned lclNum, var_typ
         GenTreePtr ptrArg = gtNewOperNode(GT_SUB, TYP_I_IMPL,
                                             gtNewLclvNode(lvaVarargsBaseOfStkArgs, TYP_I_IMPL),
                                             gtNewIconNode(varDsc->lvStkOffs 
-                                                        - codeGen->intRegState.rsCalleeRegArgNum*sizeof(void*)
+                                                        - codeGen->intRegState.rsCalleeRegArgCount*sizeof(void*)
                                                         + lclOffs));
 
         // Access the argument through the local
index 78c8dff..b491747 100644 (file)
@@ -664,11 +664,31 @@ void                Compiler::raSetupArgMasks(RegState *regState)
 // by linear scan. (It is not shared for System V AMD64 platform.)
 regNumber     Compiler::raUpdateRegStateForArg(RegState *regState, LclVarDsc *argDsc)
 {
-    regNumber inArgReg = argDsc->lvArgReg;
+    regNumber inArgReg  = argDsc->lvArgReg;
+    regMaskTP inArgMask = genRegMask(inArgReg);
 
-    noway_assert(genRegMask(inArgReg) & (regState->rsIsFloat ? RBM_FLTARG_REGS : RBM_ARG_REGS));
+    if (regState->rsIsFloat)
+    {
+        noway_assert(inArgMask & RBM_FLTARG_REGS);
+    }
+    else //  regState is for the integer registers
+    {
+        // This might be the fixed return buffer register argument (on ARM64)
+        // We check and allow inArgReg to be theFixedRetBuffReg
+        if (hasFixedRetBuffReg() && (inArgReg == theFixedRetBuffReg()))
+        {
+            // We should have a TYP_BYREF or TYP_I_IMPL arg and not a TYP_STRUCT arg
+            noway_assert(argDsc->lvType == TYP_BYREF || argDsc->lvType == TYP_I_IMPL);
+            // We should have recorded the variable number for the return buffer arg
+            noway_assert(info.compRetBuffArg != BAD_VAR_NUM); 
+        }
+        else  // we have a regular arg 
+        {
+            noway_assert(inArgMask & RBM_ARG_REGS);
+        }
+    }
 
-    regState->rsCalleeRegArgMaskLiveIn |= genRegMask(inArgReg);
+    regState->rsCalleeRegArgMaskLiveIn |= inArgMask;
 
 #ifdef _TARGET_ARM_
     if (argDsc->lvType == TYP_DOUBLE)
index 81c3f53..c0f53fc 100644 (file)
@@ -1670,6 +1670,15 @@ typedef unsigned short          regPairNoSmall; // arm: need 12 bits
 
   #define FIRST_ARG_STACK_OFFS    (2*REGSIZE_BYTES)   // Caller's saved FP and return address
 
+  // On ARM64 the calling convention defines REG_R8 (x8) as an additional argument register
+  // It isn't allocated for the normal user arguments, so it isn't counted by MAX_REG_ARG
+  // whether we use this register to pass the RetBuff is controlled by the function hasFixedRetBuffReg()
+  // it is consider to be the next integer argnum, which is 8 
+  //
+  #define REG_ARG_RET_BUFF         REG_R8
+  #define RBM_ARG_RET_BUFF         RBM_R8
+  #define RET_BUFF_ARGNUM          8
+
   #define MAX_REG_ARG              8
   #define MAX_FLOAT_REG_ARG        8
 
@@ -1748,7 +1757,6 @@ typedef unsigned short          regPairNoSmall; // arm: need 12 bits
   #error Unsupported or unset target architecture
 #endif
 
-
 #ifdef _TARGET_XARCH_
 
   #define JMP_DIST_SMALL_MAX_NEG  (-128)
@@ -1883,39 +1891,85 @@ inline bool         genIsValidDoubleReg(regNumber reg)
 
 #endif // defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
 
-/*****************************************************************************
- *
- *  Returns true if the register is a valid integer argument register 
- */
+//-------------------------------------------------------------------------------------------
+// hasFixedRetBuffReg: 
+//     Returns true if our target architecture uses a fixed return buffer register
+//
+inline bool         hasFixedRetBuffReg()
+{
+    // Disable this until the VM changes are also enabled
+#if 0 //def _TARGET_ARM64_
+    return true;
+#else
+    return false;
+#endif
+}
+
+//-------------------------------------------------------------------------------------------
+// theFixedRetBuffReg: 
+//     Returns the regNumber to use for the fixed return buffer 
+// 
+inline regNumber    theFixedRetBuffReg()
+{
+    assert(hasFixedRetBuffReg());   // This predicate should be checked before calling this method
+#ifdef _TARGET_ARM64_
+    return REG_ARG_RET_BUFF;
+#else
+    return REG_NA;
+#endif
+}
+
+//-------------------------------------------------------------------------------------------
+// theFixedRetBuffArgNum: 
+//     Returns the argNum to use for the fixed return buffer 
+// 
+inline unsigned     theFixedRetBuffArgNum()
+{
+    assert(hasFixedRetBuffReg());   // This predicate should be checked before calling this method
+#ifdef _TARGET_ARM64_
+    return RET_BUFF_ARGNUM;
+#else
+    return BAD_VAR_NUM;
+#endif
+}
+
+//-------------------------------------------------------------------------------------------
+// isValidIntArgReg: 
+//     Returns true if the register is a valid integer argument register 
+//     Note this method also returns true on Arm64 when 'reg' is the RetBuff register 
+//
 inline bool         isValidIntArgReg(regNumber reg)
 {
+    if (hasFixedRetBuffReg() && (reg == theFixedRetBuffReg()))
+    {
+        return true;
+    }
     return (genRegMask(reg) & RBM_ARG_REGS) != 0;
 }
 
-/*****************************************************************************
- *
- *  Given a register that is an integer argument register 
- *   returns the next integer argument register 
- */
+//-------------------------------------------------------------------------------------------
+// genRegArgNext:
+//     Given a register that is an integer argument register 
+//     returns the next integer argument register 
+//
 regNumber           genRegArgNext(regNumber argReg);
 
-
 #if !defined(_TARGET_X86_)
 
-/*****************************************************************************
- *
- *  Returns true if the register is a valid floating-point argument register 
- */
+//-------------------------------------------------------------------------------------------
+// isValidFloatArgReg:
+//     Returns true if the register is a valid floating-point argument register 
+//
 inline bool         isValidFloatArgReg(regNumber reg)
 {
-    return reg >= FIRST_FP_ARGREG && reg <= LAST_FP_ARGREG;
+    return (reg >= FIRST_FP_ARGREG) && (reg <= LAST_FP_ARGREG);
 }
 
-/*****************************************************************************
- *
- *  Given a register that is a floating-point argument register 
- *   returns the next floating-point argument register 
- */
+//-------------------------------------------------------------------------------------------
+// genRegArgNextFloat:
+//     Given a register that is a floating-point argument register 
+//     returns the next floating-point argument register 
+//
 regNumber           genRegArgNextFloat(regNumber argReg);
 
 #endif // !defined(_TARGET_X86_)