Outgoing call arg preliminary refactoring
authorCarol Eidt <carol.eidt@microsoft.com>
Mon, 1 Feb 2016 17:41:08 +0000 (09:41 -0800)
committerCarol Eidt <carol.eidt@microsoft.com>
Tue, 2 Feb 2016 15:47:43 +0000 (07:47 -0800)
Preliminary renaming and cleanup in preparation for further refactoring of outgoing call arguments, particularly multi-reg arguments.

15 files changed:
src/jit/codegencommon.cpp
src/jit/codegenxarch.cpp
src/jit/compiler.cpp
src/jit/compiler.h
src/jit/ee_il_dll.cpp
src/jit/flowgraph.cpp
src/jit/gentree.cpp
src/jit/gschecks.cpp
src/jit/importer.cpp
src/jit/lclvars.cpp
src/jit/lowerxarch.cpp
src/jit/lsra.cpp
src/jit/morph.cpp
src/jit/regalloc.cpp
src/jit/target.h

index 5c62b18..ea80ebf 100644 (file)
@@ -3990,7 +3990,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
 
             slots = 1;
 
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
 #ifdef _TARGET_ARM64_
             if (varDsc->TypeGet() == TYP_STRUCT)
             {
@@ -4013,7 +4013,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
                 }
             }
 #endif // _TARGET_ARM64_
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
         }
 
 #ifdef _TARGET_ARM_
@@ -4187,7 +4187,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
                 {
                     destRegNum = varDsc->lvRegNum;
                 }
-#if FEATURE_MULTIREG_STRUCT_ARGS && defined(FEATURE_SIMD) && defined(_TARGET_AMD64_)
+#if FEATURE_MULTIREG_ARGS && defined(FEATURE_SIMD) && defined(_TARGET_AMD64_)
                 else
                 {
                     assert(regArgTab[argNum].slot == 2);
@@ -4850,7 +4850,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
             assert(!regArgTab[argNum].processed);
             regArgTab[argNum].processed = true;
             regArgMaskLive &= ~genRegMask(regNum);
-#ifdef FEATURE_MULTIREG_STRUCTS
+#if FEATURE_MULTIREG_ARGS
             int argRegCount = 1;
 #ifdef _TARGET_ARM_
             if (genActualType(destMemType) == TYP_DOUBLE)
@@ -4885,7 +4885,7 @@ void            CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg,
                 regNumber nextRegNum = genMapRegArgNumToRegNum(nextArgNum, regArgTab[nextArgNum].getRegType(compiler));
                 regArgMaskLive &= ~genRegMask(nextRegNum);               
             }
-#endif // FEATURE_MULTIREG_STRUCTS
+#endif // FEATURE_MULTIREG_ARGS
         }
     
         noway_assert(regArgMaskLiveSave != regArgMaskLive);   // if it doesn't change, we have an infinite loop
@@ -11166,7 +11166,7 @@ void                CodeGen::genRestoreCalleeSavedFltRegs(unsigned lclFrameSize)
 
 
 //------------------------------------------------------------------------
-// Methods used to support FEATURE_MULTIREG_STRUCTS and HFA support for ARM32
+// Methods used to support FEATURE_MULTIREG_ARGS_OR_RET and HFA support for ARM32
 //------------------------------------------------------------------------
 
 #ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
index 1510748..8911d7f 100644 (file)
@@ -1801,7 +1801,7 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
                 {
                     GenTreeLclVarCommon* lclVarPtr = treeNode->gtOp.gtOp1->AsLclVarCommon();
                     LclVarDsc* varDsc = &(compiler->lvaTable[lclVarPtr->gtLclNum]);
-                    assert(varDsc->lvDontPromote);
+                    assert(varDsc->lvIsMultiRegArgOrRet);
 
                     CORINFO_CLASS_HANDLE typeHnd = varDsc->lvVerTypeInfo.GetClassHandle();
                     assert(typeHnd != nullptr);
@@ -3620,10 +3620,10 @@ void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode, unsigned baseV
         {
             // Load
             genCodeForLoadOffset(INS_movdqu, EA_8BYTE, xmmReg, src->gtGetOp1(), offset); // Load the address of the child of the LdObj node.
-            
+
             // Store
             emit->emitIns_S_R(INS_movdqu,
-                              EA_8BYTE, 
+                              EA_8BYTE,
                               xmmReg,
                               baseVarNum,
                               putArgOffset + offset);
@@ -7905,7 +7905,8 @@ CodeGen::genIntrinsic(GenTreePtr treeNode)
 //
 // Note:
 //    On Windows the caller always creates slots (homing space) in its frame for the 
-//    first 4 arguments of a calee (register passed args). So, the baseVarNum is always 0.
+//    first 4 arguments of a calee (register passed args). So, the the variable number
+//    (lclNum) for the first argument with a stack slot is always 0.
 //    For System V systems there is no such calling convention requirement, and the code needs to find
 //    the first stack passed argument from the caller. This is done by iterating over 
 //    all the lvParam variables and finding the first with lvArgReg equals to REG_STK.
@@ -7925,9 +7926,10 @@ CodeGen::getFirstArgWithStackSlot()
         // Iterate over all the local variables in the lclvartable. 
         // They contain all the implicit argumets - thisPtr, retBuf, 
         // generic context, PInvoke cookie, var arg cookie,no-standard args, etc.
+        LclVarDsc* varDsc = nullptr;
         for (unsigned i = 0; i < compiler->lvaCount; i++)
         {
-            LclVarDsc* varDsc = &(compiler->lvaTable[i]);
+            varDsc = &(compiler->lvaTable[i]);
 
             // We are iterating over the arguments only.
             assert(varDsc->lvIsParam);
@@ -7938,6 +7940,7 @@ CodeGen::getFirstArgWithStackSlot()
                 break;
             }
         }
+        assert(varDsc != nullptr);
     }
 
     return baseVarNum;
@@ -8138,8 +8141,8 @@ CodeGen::genPutStructArgStk(GenTreePtr treeNode, unsigned baseVarNum)
     {
         regNumber srcReg = genConsumeReg(treeNode->gtGetOp1());
         assert((srcReg != REG_NA) && (genIsValidFloatReg(srcReg)));
-        getEmitter()->emitIns_S_R(ins_Store(targetType),
-                                  emitTypeSize(targetType), 
+        getEmitter()->emitIns_S_R(ins_Store(targetType), 
+                                  emitTypeSize(targetType),
                                   srcReg,
                                   baseVarNum,
                                   treeNode->AsPutArgStk()->getArgOffset());
index cd021f5..2b83635 100644 (file)
@@ -451,7 +451,7 @@ var_types    Compiler::argOrReturnTypeForStruct(unsigned size, CORINFO_CLASS_HAN
     case 3:
         useType = TYP_INT;
         break;
-#endif // _TARGET_AMD64_
+#endif // _TARGET_XARCH_
 
 #ifdef _TARGET_64BIT_
     case 4:
@@ -490,7 +490,7 @@ var_types    Compiler::argOrReturnTypeForStruct(unsigned size, CORINFO_CLASS_HAN
         break;
 
     default:
-#if FEATURE_MULTIREG_STRUCT_RET
+#if FEATURE_MULTIREG_RET
         if (forReturn)
         {
             if (size <= MAX_RET_MULTIREG_BYTES)
@@ -505,9 +505,9 @@ var_types    Compiler::argOrReturnTypeForStruct(unsigned size, CORINFO_CLASS_HAN
 #endif // _TARGET_ARM64_
             }
         }
-#endif // FEATURE_MULTIREG_STRUCT_RET
+#endif // FEATURE_MULTIREG_RET
 
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
         if (!forReturn)
         {
             if (size <= MAX_PASS_MULTIREG_BYTES)
@@ -522,7 +522,7 @@ var_types    Compiler::argOrReturnTypeForStruct(unsigned size, CORINFO_CLASS_HAN
 #endif // _TARGET_ARM64_
             }
         }
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
         break;
     }
     return useType;
@@ -5714,7 +5714,8 @@ START:
 // args:
 //   classType: classification type
 //   size: size of the eightbyte.
-//   
+//
+// static 
 var_types Compiler::GetTypeFromClassificationAndSizes(SystemVClassificationType classType, int size)
 {
     var_types type = TYP_UNKNOWN;
index f8d780c..9e4640f 100644 (file)
@@ -274,13 +274,14 @@ public:
     unsigned char       lvOverlappingFields :1;  // True when we have a struct with possibly overlapping fields
     unsigned char       lvContainsHoles     :1;  // True when we have a promoted struct that contains holes
     unsigned char       lvCustomLayout      :1;  // True when this struct has "CustomLayout"
-#if FEATURE_MULTIREG_STRUCTS
-    unsigned char       lvDontPromote:1;         // Should struct promotion consider this local variable for promotion?
+#if FEATURE_MULTIREG_ARGS_OR_RET
+    unsigned char       lvIsMultiRegArgOrRet:1; // Is this argument variable holding a value passed or returned in multiple registers?
 #endif
 #ifdef _TARGET_ARM_
+    // TODO-Cleanup: Can this be subsumed by the above?
     unsigned char       lvIsHfaRegArg:1;        // Is this argument variable holding a HFA register argument.
     unsigned char       lvHfaTypeIsFloat:1;     // Is the HFA type float or double?
-#endif
+#endif // _TARGET_ARM_
 
 #ifdef DEBUG
     // TODO-Cleanup: See the note on lvSize() - this flag is only in use by asserts that are checking for struct
@@ -315,7 +316,7 @@ public:
     unsigned char       lvFldOffset;
     unsigned char       lvFldOrdinal;
 
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
     regNumber lvRegNumForSlot(unsigned slotNum)
     {
         if (slotNum == 0)
@@ -333,7 +334,7 @@ public:
 
         unreached();
     }
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
 
 private:
 
@@ -347,10 +348,10 @@ private:
 
     regNumberSmall      _lvArgReg;      // The register in which this argument is passed.
 
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
     regNumberSmall      _lvOtherArgReg;    // Used for the second part of the struct passed in a register.
                                            // Note this is defined but not used by ARM32
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
 
 #ifndef LEGACY_BACKEND
     union
@@ -429,7 +430,7 @@ public:
         assert(_lvArgReg == reg);
     }
 
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
     __declspec(property(get = GetOtherArgReg, put = SetOtherArgReg))
     regNumber           lvOtherArgReg;
 
@@ -443,7 +444,7 @@ public:
         _lvOtherArgReg = (regNumberSmall)reg;
         assert(_lvOtherArgReg == reg);
     }
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
 
 #ifdef FEATURE_SIMD
     // Is this is a SIMD struct?
@@ -1382,7 +1383,7 @@ struct fgArgTabEntry
     unsigned       tmpNum;      // the LclVar number if we had to force evaluation of this arg
 
     bool           isSplit      :1; // True when this argument is split between the registers and OutArg area 
-    bool           needTmp      :1; // True when we force this arguments evaluation into a temp LclVar
+    bool           needTmp      :1; // True when we force this argument's evaluation into a temp LclVar
     bool           needPlace    :1; // True when we must replace this argument with a placeholder node
     bool           isTmp        :1; // True when we setup a temp LclVar for this argument due to size issues with the struct 
     bool           processed    :1; // True when we have decided the evaluation order for this argument in the gtCallLateArgs 
@@ -1391,8 +1392,9 @@ struct fgArgTabEntry
     bool           isNonStandard:1; // True if it is an arg that is passed in a reg other than a standard arg reg
 
 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+    bool           isStruct     :1; // True if this is a struct arg
+
     regNumber             otherRegNum;              // The (second) register to use when passing this argument.
-    bool                  isStruct;                 // is this a struct arg
 
     SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
 #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
@@ -1656,9 +1658,9 @@ public:
     DWORD expensiveDebugCheckLevel;
 #endif
 
-#if FEATURE_MULTIREG_STRUCT_RET
+#if FEATURE_MULTIREG_RET
     GenTreePtr               impAssignStructClassToVar(GenTreePtr op, CORINFO_CLASS_HANDLE hClass);
-#endif
+#endif // FEATURE_MULTIREG_RET
 
 #ifdef _TARGET_ARM_
 
@@ -8991,7 +8993,7 @@ public:
     static HelperCallProperties s_helperCallProperties;
 
 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
-    var_types GetTypeFromClassificationAndSizes(SystemVClassificationType classType, int size);
+    static var_types GetTypeFromClassificationAndSizes(SystemVClassificationType classType, int size);
     var_types getEightByteType(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc, unsigned slotNum);
     void fgMorphSystemVStructArgs(GenTreeCall* call, bool hasStructArgument);
 #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
index ed5c4c9..cdf51fc 100644 (file)
@@ -320,7 +320,7 @@ unsigned           Compiler::eeGetArgSize(CORINFO_ARG_LIST_HANDLE list, CORINFO_
         // make certain the EE passes us back the right thing for refanys
         assert(argTypeJit != CORINFO_TYPE_REFANY || structSize == 2*sizeof(void*));
 
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
 #ifdef _TARGET_ARM64_
         if (structSize > MAX_PASS_MULTIREG_BYTES)
         {
@@ -328,7 +328,7 @@ unsigned           Compiler::eeGetArgSize(CORINFO_ARG_LIST_HANDLE list, CORINFO_
             return TARGET_POINTER_SIZE;
         }
 #endif // _TARGET_ARM64_
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
 
         return (unsigned)roundUp(structSize, TARGET_POINTER_SIZE);
     }
index 77358aa..a2710c3 100644 (file)
@@ -8220,7 +8220,7 @@ void                Compiler::fgAddInternal()
                 lvaTable[genReturnLocal].lvVerTypeInfo = typeInfo(TI_STRUCT, info.compMethodInfo->args.retTypeClass);
             }
 
-            lvaTable[genReturnLocal].lvDontPromote = true;
+            lvaTable[genReturnLocal].lvIsMultiRegArgOrRet = true;
         }
 #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
 
index d83e72a..773ab72 100644 (file)
@@ -4525,7 +4525,7 @@ void            GenTree::InsertAfterSelf(GenTree* node, GenTreeStmt* stmt /* = n
 //    'parent' must be non-null
 //
 // Notes:
-//    When FEATURE_MULTIREG_STRUCT_ARGS is defined we can get here with GT_LDOBJ tree. 
+//    When FEATURE_MULTIREG__ARGS is defined we can get here with GT_LDOBJ tree. 
 //    This happens when we have a struct that is passed in multiple registers.
 //
 //    Also note that when FEATURE_UNIX_AMD64_STRUCT_PASSING is defined the GT_LDOBJ 
@@ -4543,14 +4543,14 @@ GenTreePtr*         GenTree::gtGetChildPointer(GenTreePtr parent)
         if (this == parent->gtOp.gtOp2)                    return &(parent->gtOp.gtOp2);
         break;
 
-#if !FEATURE_MULTIREG_STRUCT_ARGS
-        // Note that when FEATURE_MULTIREG_STRUCT_ARGS==1 
+#if !FEATURE_MULTIREG_ARGS
+        // Note that when FEATURE_MULTIREG__ARGS==1 
         //  a GT_LDOBJ node is handled above by the default case
     case GT_LDOBJ:
         // Any GT_LDOBJ with a field must be lowered before this point.
         noway_assert(!"GT_LDOBJ encountered in GenTree::gtGetChildPointer");
         break;
-#endif // !FEATURE_MULTIREG_STRUCT_ARGS
+#endif // !FEATURE_MULTIREG_ARGS
 
     case GT_CMPXCHG:
         if (this == parent->gtCmpXchg.gtOpLocation)        return &(parent->gtCmpXchg.gtOpLocation);
index 68e411e..9d10499 100644 (file)
@@ -473,9 +473,9 @@ void Compiler::gsParamsToShadows()
             dst = gtNewOperNode(GT_ADDR, TYP_BYREF, dst);
 
             opAssign = gtNewCpObjNode(dst, src, clsHnd, false);
-#if FEATURE_MULTIREG_STRUCTS
-            lvaTable[shadowVar].lvDontPromote = lvaTable[lclNum].lvDontPromote;
-#endif // FEATURE_MULTIREG_STRUCTS
+#if FEATURE_MULTIREG_ARGS_OR_RET
+            lvaTable[shadowVar].lvIsMultiRegArgOrRet = lvaTable[lclNum].lvIsMultiRegArgOrRet;
+#endif // FEATURE_MULTIREG_ARGS_OR_RET
         }
         else
         {
index 44ca538..0ac4cf1 100644 (file)
@@ -1210,7 +1210,7 @@ GenTreePtr Compiler::impAssignStructPtr(GenTreePtr      dest,
                 assert(!src->gtCall.IsVarargs() && "varargs not allowed for System V OSs.");
 
                 // Make the struct non promotable. The eightbytes could contain multiple fields.
-                lvaTable[lcl->gtLclVarCommon.gtLclNum].lvDontPromote = true;
+                lvaTable[lcl->gtLclVarCommon.gtLclNum].lvIsMultiRegArgOrRet = true;
 #endif
             }
             else
@@ -6964,7 +6964,7 @@ bool  Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO * methInfo)
     }
 #endif
 
-#if FEATURE_MULTIREG_STRUCT_RET
+#if FEATURE_MULTIREG_RET
 
     // Support for any additional cases that don't use a Return Buffer Argument
     //  on targets that support multi-reg return valuetypes.
@@ -6977,7 +6977,7 @@ bool  Compiler::impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO * methInfo)
     }
   #endif
 
-#endif
+#endif // FEATURE_MULTIREG_RET
 
     // Otherwise we require that a RetBuffArg be used
     return true;
@@ -7169,7 +7169,7 @@ GenTreePtr          Compiler::impFixupStructReturnType(GenTreePtr op, CORINFO_CL
                 // This LCL_VAR is a register return value, it stays as a TYP_STRUCT
                 unsigned lclNum = op->gtLclVarCommon.gtLclNum;
                 // Make sure this struct type stays as struct so that we can return it in registers.
-                lvaTable[lclNum].lvDontPromote = true;
+                lvaTable[lclNum].lvIsMultiRegArgOrRet = true;
                 return op;
             }
 
@@ -7190,12 +7190,12 @@ GenTreePtr          Compiler::impFixupStructReturnType(GenTreePtr op, CORINFO_CL
     {
         if (op->gtOper == GT_LCL_VAR)
         {
-#if FEATURE_MULTIREG_STRUCT_RET
+#if FEATURE_MULTIREG_RET
             // This LCL_VAR is an HFA return value, it stays as a TYP_STRUCT
             unsigned lclNum = op->gtLclVarCommon.gtLclNum;
             // Make sure this struct type stays as struct so that we can return it as an HFA
-            lvaTable[lclNum].lvDontPromote = true;
-#endif // FEATURE_MULTIREG_STRUCT_RET
+            lvaTable[lclNum].lvIsMultiRegArgOrRet = true;
+#endif // FEATURE_MULTIREG_RET
             return op;
         }
          
@@ -12791,7 +12791,7 @@ FIELD_DONE:
                         // rdi/rsi (depending whether there is a "this").
 
                         unsigned   tmp = lvaGrabTemp(true DEBUGARG("UNBOXing a register returnable nullable"));
-                        lvaTable[tmp].lvDontPromote = true;
+                        lvaTable[tmp].lvIsMultiRegArgOrRet = true;
                         lvaSetStruct(tmp, resolvedToken.hClass, true  /* unsafe value cls check */);
 
                         op2 = gtNewLclvNode(tmp, TYP_STRUCT);
@@ -13651,16 +13651,16 @@ void Compiler::impMarkLclDstNotPromotable(unsigned tmpNum, GenTreePtr src, CORIN
             (hfaType == TYP_FLOAT && hfaSlots == sizeof(float) / REGSIZE_BYTES))
         {
             // Make sure this struct type stays as struct so we can receive the call in a struct.
-            lvaTable[tmpNum].lvDontPromote = true;
+            lvaTable[tmpNum].lvIsMultiRegArgOrRet = true;
         }
     }
 }
 #endif
 
-#if FEATURE_MULTIREG_STRUCT_RET
+#if FEATURE_MULTIREG_RET
 GenTreePtr Compiler::impAssignStructClassToVar(GenTreePtr op, CORINFO_CLASS_HANDLE hClass)
 {
-    unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Return value temp for multireg structs."));
+    unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Return value temp for multireg return."));
     impAssignTempGen(tmpNum, op, hClass, (unsigned) CHECK_SPILL_NONE);
     GenTreePtr ret = gtNewLclvNode(tmpNum, op->gtType);
 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
@@ -13674,11 +13674,11 @@ GenTreePtr Compiler::impAssignStructClassToVar(GenTreePtr op, CORINFO_CLASS_HAND
     // Mark the var to store the eightbytes on stack non promotable.
     // The return value is based on eightbytes, so all the fields need 
     // to be on stack before loading the eightbyte in the corresponding return register.
-    lvaTable[tmpNum].lvDontPromote = true;
+    lvaTable[tmpNum].lvIsMultiRegArgOrRet = true;
 #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
     return ret;
 }
-#endif // FEATURE_MULTIREG_STRUCT_RET
+#endif // FEATURE_MULTIREG_RET
 
 // do import for a return
 // returns false if inlining was aborted
index 21dca7c..e02dd3e 100644 (file)
@@ -428,7 +428,7 @@ void                Compiler::lvaInitThisPtr(InitVarDscInfo *       varDscInfo)
         noway_assert(varDscInfo->intRegArgNum == 0);
 
         varDsc->lvArgReg  = genMapRegArgNumToRegNum(varDscInfo->allocRegArg(TYP_INT), varDsc->TypeGet());
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG__ARGS
         varDsc->lvOtherArgReg = REG_NA;
 #endif
         varDsc->setPrefReg(varDsc->lvArgReg, this);
@@ -473,7 +473,7 @@ void                Compiler::lvaInitRetBuffArg(InitVarDscInfo *    varDscInfo)
         varDsc->lvSingleDef = 1;
 #endif
         varDsc->lvArgReg  = genMapRegArgNumToRegNum(varDscInfo->allocRegArg(TYP_INT), varDsc->TypeGet());
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG__ARGS
         varDsc->lvOtherArgReg = REG_NA;
 #endif
         varDsc->setPrefReg(varDsc->lvArgReg, this);
@@ -727,9 +727,9 @@ void                Compiler::lvaInitUserArgs(InitVarDscInfo *      varDscInfo)
             // to the stack happens.
             unsigned firstAllocatedRegArgNum = 0;
 
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
             varDsc->lvOtherArgReg = REG_NA;
-#endif
+#endif // FEATURE_MULTIREG_ARGS
 
 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
             unsigned secondAllocatedRegArgNum = 0;
@@ -761,7 +761,7 @@ void                Compiler::lvaInitUserArgs(InitVarDscInfo *      varDscInfo)
 
             varDsc->lvIsRegArg = 1;
 
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
             if (varTypeIsStruct(argType))
             {
 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
@@ -791,7 +791,7 @@ void                Compiler::lvaInitUserArgs(InitVarDscInfo *      varDscInfo)
 #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
             }
             else
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
             {
                 varDsc->lvArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum, argType);
             }
@@ -976,7 +976,7 @@ void                Compiler::lvaInitGenericsCtxt(InitVarDscInfo *  varDscInfo)
 
             varDsc->lvIsRegArg = 1;
             varDsc->lvArgReg   = genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet());
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG__ARGS
             varDsc->lvOtherArgReg = REG_NA;
 #endif
             varDsc->setPrefReg(varDsc->lvArgReg, this);
@@ -1034,7 +1034,7 @@ void                Compiler::lvaInitVarArgsHandle(InitVarDscInfo * varDscInfo)
 
             varDsc->lvIsRegArg = 1;
             varDsc->lvArgReg   = genMapRegArgNumToRegNum(varArgHndArgNum, TYP_I_IMPL);
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG__ARGS
             varDsc->lvOtherArgReg = REG_NA;
 #endif
             varDsc->setPrefReg(varDsc->lvArgReg, this);
index 290b57e..a3a11a6 100644 (file)
@@ -303,7 +303,7 @@ void Lowering::TreeNodeInfoInit(GenTree* stmt)
 #ifdef DEBUG
                     GenTreeLclVarCommon* lclVarPtr = tree->gtOp.gtOp1->AsLclVarCommon();
                     LclVarDsc* varDsc = &(compiler->lvaTable[lclVarPtr->gtLclNum]);
-                    assert(varDsc->lvDontPromote);
+                    assert(varDsc->lvIsMultiRegArgOrRet);
 #endif // DEBUG
                     // If this is a two eightbyte return, make the var
                     // contained by the return expression. The code gen will put
index 391c81c..cc462a8 100644 (file)
@@ -3414,12 +3414,12 @@ LinearScan::updateRegStateForArg(LclVarDsc* argDsc)
         else
         {
             JITDUMP("Int arg V%02u in reg %s\n", (argDsc - compiler->lvaTable), getRegName(argDsc->lvArgReg));
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
             if (argDsc->lvOtherArgReg != REG_NA)
             {
                 JITDUMP("(second half) in reg %s\n", getRegName(argDsc->lvOtherArgReg));
             }
-#endif
+#endif // FEATURE_MULTIREG_ARGS
             compiler->raUpdateRegStateForArg(intRegState, argDsc);
         }
     }
index d2dc660..79290dd 100644 (file)
@@ -1568,7 +1568,7 @@ void fgArgInfo::ArgsComplete()
                 curArgTabEntry->needTmp = true;
             }
 
-            // For all previous arguments they may need to be evaluated into a temps
+            // All previous arguments may need to be evaluated into temps
             for (unsigned prevInx = 0; prevInx < curInx; prevInx++)
             {
                 fgArgTabEntryPtr prevArgTabEntry  = argTable[prevInx];
@@ -2069,10 +2069,8 @@ GenTreePtr    Compiler::fgMakeTmpArgNode(unsigned tmpVarNum
             if (structSize <= MAX_PASS_MULTIREG_BYTES)
             {
                 assert(structSize > TARGET_POINTER_SIZE);  // structSize must be 9..16
-
                 // ToDo-ARM64: Consider using:  arg->ChangeOper(GT_LCL_FLD);
                 // as that is how FEATURE_UNIX_AMD64_STRUCT_PASSING works.
-            
                 // Pass by value in two registers
                 arg = gtNewOperNode(GT_ADDR, TYP_BYREF, arg);
                 addrNode = arg;
@@ -2145,8 +2143,8 @@ void fgArgInfo::EvalArgsToTemps()
         assert(curArgTabEntry->needPlace == false);
 
         // On x86 and other archs that use push instructions to pass arguments:
-        //   Only the register arguments need to be replaced with placeholders node
-        //   stacked arguments are evaluated and pushed in order
+        //   Only the register arguments need to be replaced with placeholder nodes.
+        //   Stacked arguments are evaluated and pushed (or stored into the stack) in order.
         //
         if (curArgTabEntry->regNum == REG_STK) 
             continue;
@@ -2281,13 +2279,14 @@ void fgArgInfo::EvalArgsToTemps()
         else // curArgTabEntry->needTmp == false
         {
             //   On x86 -
-            //      Only register args are replaced with placeholders node
-            //      and the stack based arguments are evaluated and pushed in order
+            //      Only register args are replaced with placeholder nodes
+            //      and the stack based arguments are evaluated and pushed in order.
             //
-            //   On Arm/x64 - Only when needTmp is false and needPlace is false
-            //      The non-register arguments are evaluated and stored in order
+            //   On Arm/x64 - When needTmp is false and needPlace is false,
+            //      the non-register arguments are evaluated and stored in order.
             //      When needPlace is true we have a nested call that comes after
-            //      this argument so we have to replace it with a placeholder
+            //      this argument so we have to replace it in the gtCallArgs list
+            //      (the initial argument evaluation list) with a placeholder.
             //
             if ((curArgTabEntry->regNum == REG_STK) && (curArgTabEntry->needPlace == false))
                 continue;
@@ -2301,13 +2300,15 @@ void fgArgInfo::EvalArgsToTemps()
             // Create a placeholder node to put in its place in gtCallLateArgs.
 
             // For a struct type we also need to record the class handle of the arg.
-            CORINFO_CLASS_HANDLE clsHnd = NULL;
+            CORINFO_CLASS_HANDLE clsHnd = NO_CLASS_HANDLE;
 
 #if defined(_TARGET_AMD64_) && !defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
 
+            // All structs are either passed (and retyped) as integral types, OR they
+            // are passed by reference.
             noway_assert(argx->gtType != TYP_STRUCT);
 
-#else // !(defined(_TARGET_AMD64_) && !defined(FEATURE_UNIX_AMD64_STRUCT_PASSING))
+#else // !defined(_TARGET_AMD64_) || defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
 
             if (varTypeIsStruct(defArg))
             {
@@ -2334,7 +2335,7 @@ void fgArgInfo::EvalArgsToTemps()
                 {
                     BADCODE("Unhandled TYP_STRUCT argument tree in fgMorphArgs");
                 }
-            }
+        }
 
 #endif // !(defined(_TARGET_AMD64_) && !defined(FEATURE_UNIX_AMD64_STRUCT_PASSING))
 
@@ -2538,6 +2539,41 @@ GenTree*   Compiler::fgInsertCommaFormTemp(GenTree** ppTree, CORINFO_CLASS_HANDL
 }
 
 
+//------------------------------------------------------------------------
+// fgMorphArgs: Walk and transform (morph) the arguments of a call
+//
+// Arguments:
+//    callNode - the call for which we are doing the argument morphing
+//
+// Return Value:
+//    Like most morph methods, this method returns the morphed node,
+//    though in this case there are currently no scenarios where the
+//    node itself is re-created.
+//
+// Notes:
+//    This method is even less idempotent than most morph methods.
+//    That is, it makes changes that should not be redone. It uses the existence
+//    of gtCallLateArgs (the late arguments list) to determine if it has
+//    already done that work.
+//
+//    The first time it is called (i.e. during global morphing), this method
+//    computes the "late arguments". This is when it determines which arguments
+//    need to be evaluated to temps prior to the main argument setup, and which
+//    can be directly evaluated into the argument location. It also creates a
+//    second argument list (gtCallLateArgs) that does the final placement of the
+//    arguments, e.g. into registers or onto the stack.
+//
+//    The "non-late arguments", aka the gtCallArgs, are doing the in-order
+//    evaluation of the arguments that might have side-effects, such as embedded
+//    assignments, calls or possible throws. In these cases, it and earlier
+//    arguments must be evaluated to temps.
+//
+//    On targets with a fixed outgoing argument area (FEATURE_FIXED_OUT_ARGS),
+//    if we have any nested calls, we need to defer the copying of the argument
+//    into the fixed argument area until after the call. If the argument did not
+//    otherwise need to be computed into a temp, it is moved to gtCallLateArgs and
+//    replaced in the "early" arg list (gtCallArgs) with a placeholder node.
+
 #ifdef _PREFAST_
 #pragma warning(push)
 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
@@ -2887,12 +2923,12 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
     {
         GenTreePtr * parentArgx = &args->gtOp.gtOp1;
 
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_ARGS
         if (!hasStructArgument)
         {
             hasStructArgument = varTypeIsStruct(args->gtOp.gtOp1);
         }
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#endif // FEATURE_MULTIREG_ARGS
         argx = fgMorphTree(*parentArgx);
         *parentArgx = argx;
         flagsSummary |= argx->gtFlags;
@@ -3132,7 +3168,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
             else // struct type
             {
                 /* We handle two opcodes: GT_MKREFANY and GT_LDOBJ */
-                if (argx->gtOper == GT_MKREFANY) 
+                if (argx->gtOper == GT_MKREFANY)
                 {
                     if (varTypeIsStruct(argx))
                     {
@@ -3209,9 +3245,9 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
                         if (!structDesc.passedInRegisters)
                         {
                             passStructInRegisters = false;
-                            copyBlkClass = NULL;
+                            copyBlkClass = NO_CLASS_HANDLE;
                         }
-                        else 
+                        else
                         {
                             // The ldObjClass is used to materialize the struct on stack.
                             passStructInRegisters = true;
@@ -3301,9 +3337,9 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
                                             // to a new type
                                             argLdobj->ChangeOper(GT_LCL_FLD);
                                             argLdobj->gtType = structBaseType;
-                                        }                                   
+                                        }
                                         assert(varTypeCanReg(argLdobj->TypeGet()));
-                                        assert(copyBlkClass == nullptr);
+                                        assert(copyBlkClass == NO_CLASS_HANDLE);
                                     }
                                     else
                                     {
@@ -3337,8 +3373,8 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
                             // Not a GT_LCL_VAR, so we can just change the type on the node
                             argLdobj->gtType = structBaseType;
                         }
-                        assert( varTypeCanReg(argLdobj->TypeGet()) ||
-                               ((copyBlkClass != NULL) && varTypeIsIntegral(structBaseType)));
+                        assert(varTypeCanReg(argLdobj->TypeGet()) ||
+                               ((copyBlkClass != NO_CLASS_HANDLE) && varTypeIsIntegral(structBaseType)));
 
                         size = 1;
                     }
@@ -3375,12 +3411,12 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
                                 // exception : no need to use CopyBlk if the valuetype is on the stack
                                 if (ldObjOp1->gtFlags & GTF_ADDR_ONSTACK)
                                 {
-                                    copyBlkClass = NULL;
+                                    copyBlkClass = NO_CLASS_HANDLE;
                                 }
                                 // exception : no need to use CopyBlk if the valuetype is already a struct local
                                 else if (ldObjOp1->gtOp.gtOp1->gtOper == GT_LCL_VAR)
                                 {
-                                    copyBlkClass = NULL;
+                                    copyBlkClass = NO_CLASS_HANDLE;
                                 }
                             }
                         }
@@ -3619,7 +3655,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
 
 #ifdef _TARGET_AMD64_
 #ifndef FEATURE_UNIX_AMD64_STRUCT_PASSING
-       assert(size == 1);
+            assert(size == 1);
 #endif
 #endif
 
@@ -3764,7 +3800,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
                 call->fgArgInfo->RemorphStkArg(argIndex, argx, args, size, argAlign);
             }
         }
-        if (copyBlkClass != NULL)
+        if (copyBlkClass != NO_CLASS_HANDLE)
         {
             noway_assert(!lateArgsComputed);
             fgMakeOutgoingStructArgCopy(call, args, argIndex, copyBlkClass FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(&structDesc));
@@ -4045,8 +4081,8 @@ void Compiler::fgMorphSystemVStructArgs(GenTreeCall* call, bool hasStructArgumen
                         // Second eightbyte.
                         GenTreeLclFld* newLclField = new(this, GT_LCL_FLD) GenTreeLclFld(
                             GetTypeFromClassificationAndSizes(
-                            fgEntryPtr->structDesc.eightByteClassifications[1],
-                            fgEntryPtr->structDesc.eightByteSizes[1]),
+                                fgEntryPtr->structDesc.eightByteClassifications[1],
+                                fgEntryPtr->structDesc.eightByteSizes[1]),
                             lclCommon->gtLclNum,
                             fgEntryPtr->structDesc.eightByteOffsets[1]);
                         GenTreeArgList* secondNode = gtNewListNode(newLclField, nullptr);
@@ -4136,7 +4172,7 @@ void Compiler::fgMorphSystemVStructArgs(GenTreeCall* call, bool hasStructArgumen
     // Update the flags
     call->gtFlags |= (flagsSummary & GTF_ALL_EFFECT);
 }
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#endif // FEATURE_MULTIREG_ARGS
 
 // Make a copy of a struct variable if necessary, to pass to a callee.
 // returns: tree that computes address of the outgoing arg
@@ -15126,13 +15162,13 @@ void                Compiler::fgPromoteStructs()
             JITDUMP("Stopped promoting struct fields, due to too many locals.\n");
             break;
         }
-#if FEATURE_MULTIREG_STRUCTS
-        if (varDsc->lvDontPromote)
+#if FEATURE_MULTIREG_ARGS_OR_RET
+        if (varDsc->lvIsMultiRegArgOrRet)
         {
-            JITDUMP("Skipping V%02u: marked as lvDontPromote.\n", lclNum);
+            JITDUMP("Skipping V%02u: marked lvIsMultiRegArgOrRet.\n", lclNum);
             continue;
         }
-#endif // FEATURE_MULTIREG_STRUCTS
+#endif // FEATURE_MULTIREG_ARGS_OR_RET
 
 #ifdef FEATURE_SIMD
         if (varDsc->lvSIMDType && varDsc->lvUsedInSIMDIntrinsic)
@@ -15182,7 +15218,7 @@ void                Compiler::fgPromoteStructs()
                     continue;
                 }
 #endif // _TARGET_AMD64_ || _TARGET_ARM64_
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
 #if defined(_TARGET_ARM64_)
                 // TODO-PERF - Only do this when the LclVar is used in an argument context
                 //
@@ -15196,7 +15232,7 @@ void                Compiler::fgPromoteStructs()
                     continue;
                 }
 #endif // _TARGET_ARM64_
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
 
                 if (varDsc->lvIsParam)
                 {
index 62568bf..65b487d 100644 (file)
@@ -675,7 +675,7 @@ regNumber     Compiler::raUpdateRegStateForArg(RegState *regState, LclVarDsc *ar
 
     regState->rsCalleeRegArgMaskLiveIn |= genRegMask(inArgReg);
 
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
 #ifdef _TARGET_ARM64_
     if ((argDsc->lvOtherArgReg != REG_STK) && (argDsc->lvOtherArgReg != REG_NA))
     {
@@ -687,7 +687,7 @@ regNumber     Compiler::raUpdateRegStateForArg(RegState *regState, LclVarDsc *ar
         regState->rsCalleeRegArgMaskLiveIn |= genRegMask(secondArgReg);
     }
 #endif // TARGET_ARM64_
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
 
 #ifdef _TARGET_ARM_
     if (argDsc->lvType == TYP_DOUBLE)
index e2bff4a..b1139ec 100644 (file)
@@ -375,9 +375,11 @@ typedef unsigned short          regPairNoSmall; // arm: need 12 bits
   #define FEATURE_FASTTAILCALL     0       // Tail calls made as epilog+jmp
   #define FEATURE_TAILCALL_OPT     0       // opportunistic Tail calls (without ".tail" prefix) made as fast tail calls.
   #define FEATURE_SET_FLAGS        0       // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when the flags need to be set
-  #define FEATURE_MULTIREG_STRUCTS      0  // Support for passing and/or returning structs in more than one register
-  #define FEATURE_MULTIREG_STRUCT_ARGS  0  // Support for passing structs in more than one register  
-  #define FEATURE_MULTIREG_STRUCT_RET   0  // Support for returning structs in more than one register  
+  #define FEATURE_MULTIREG_ARGS_OR_RET  0  // Support for passing and/or returning single values in more than one register
+  #define FEATURE_MULTIREG_ARGS         0  // Support for passing a single argument in more than one register  
+  #define FEATURE_MULTIREG_RET          0  // Support for returning a single value in more than one register  
+  #define MAX_ARG_REG_COUNT             2  // Maximum registers used to pass an argument.
+  #define MAX_RET_REG_COUNT             2  // Maximum registers used to return a value.
 
 #ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS
   #define NOGC_WRITE_BARRIERS      1       // We have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers
@@ -693,16 +695,20 @@ typedef unsigned short          regPairNoSmall; // arm: need 12 bits
   #define FEATURE_TAILCALL_OPT     1       // opportunistic Tail calls (i.e. without ".tail" prefix) made as fast tail calls.
   #define FEATURE_SET_FLAGS        0       // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when the flags need to be set
 #ifdef    UNIX_AMD64_ABI
-  #define FEATURE_MULTIREG_STRUCTS      1  // Support for passing and/or returning structs in more than one register
-  #define FEATURE_MULTIREG_STRUCT_ARGS  1  // Support for passing structs in more than one register  
-  #define FEATURE_MULTIREG_STRUCT_RET   1  // Support for returning structs in more than one register
+  #define FEATURE_MULTIREG_ARGS_OR_RET  1  // Support for passing and/or returning single values in more than one register
+  #define FEATURE_MULTIREG_ARGS         1  // Support for passing a single argument in more than one register  
+  #define FEATURE_MULTIREG_RET          1  // Support for returning a single value in more than one register
   #define FEATURE_STRUCT_CLASSIFIER     1  // Uses a classifier function to determine if structs are passed/returned in more than one register
   #define MAX_PASS_MULTIREG_BYTES      32  // Maximum size of a struct that could be passed in more than one register
   #define MAX_RET_MULTIREG_BYTES       32  // Maximum size of a struct that could be returned in more than one register
+  #define MAX_ARG_REG_COUNT             2  // Maximum registers used to pass an argument.
+  #define MAX_RET_REG_COUNT             2  // Maximum registers used to return a value.
 #else // !UNIX_AMD64_ABI
-  #define FEATURE_MULTIREG_STRUCTS      0  // Support for passing and/or returning structs in more than one register
-  #define FEATURE_MULTIREG_STRUCT_ARGS  0  // Support for passing structs in more than one register  
-  #define FEATURE_MULTIREG_STRUCT_RET   0  // Support for returning structs in more than one register  
+  #define FEATURE_MULTIREG_ARGS_OR_RET  0  // Support for passing and/or returning single values in more than one register
+  #define FEATURE_MULTIREG_ARGS         0  // Support for passing a single argument in more than one register  
+  #define FEATURE_MULTIREG_RET          0  // Support for returning a single value in more than one register  
+  #define MAX_ARG_REG_COUNT             1  // Maximum registers used to pass an argument.
+  #define MAX_RET_REG_COUNT             1  // Maximum registers used to return a value.
 #endif // !UNIX_AMD64_ABI
 
 #ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS
@@ -1121,13 +1127,14 @@ typedef unsigned short          regPairNoSmall; // arm: need 12 bits
   #define FEATURE_FASTTAILCALL     0       // Tail calls made as epilog+jmp
   #define FEATURE_TAILCALL_OPT     0       // opportunistic Tail calls (i.e. without ".tail" prefix) made as fast tail calls.
   #define FEATURE_SET_FLAGS        1       // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when the flags need to be set
-  #define FEATURE_MULTIREG_STRUCTS      1  // Support for passing and/or returning structs in more than one register (including HFA support)
-  #define FEATURE_MULTIREG_STRUCT_ARGS  1  // Support for passing structs in more than one register (including passing HFAs)
-  #define FEATURE_MULTIREG_STRUCT_RET   1  // Support for returning structs in more than one register (including HFA returns)
+  #define FEATURE_MULTIREG_ARGS_OR_RET  1  // Support for passing and/or returning single values in more than one register (including HFA support)
+  #define FEATURE_MULTIREG_ARGS         1  // Support for passing a single argument in more than one register (including passing HFAs)
+  #define FEATURE_MULTIREG_RET          1  // Support for returning a single value in more than one register (including HFA returns)
   #define FEATURE_STRUCT_CLASSIFIER     0  // Uses a classifier function to determine is structs are passed/returned in more than one register
   #define MAX_PASS_MULTIREG_BYTES      32  // Maximum size of a struct that could be passed in more than one register (Max is an HFA of 4 doubles)
   #define MAX_RET_MULTIREG_BYTES       32  // Maximum size of a struct that could be returned in more than one register (Max is an HFA of 4 doubles)
-
+  #define MAX_ARG_REG_COUNT             4  // Maximum registers used to pass an argument.
+  #define MAX_RET_REG_COUNT             4  // Maximum registers used to return a value.
 #ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS
   #define NOGC_WRITE_BARRIERS      0       // We DO-NOT have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers
 #else
@@ -1433,11 +1440,13 @@ typedef unsigned short          regPairNoSmall; // arm: need 12 bits
   #define FEATURE_FASTTAILCALL     0       // Tail calls made as epilog+jmp
   #define FEATURE_TAILCALL_OPT     0       // opportunistic Tail calls (i.e. without ".tail" prefix) made as fast tail calls.
   #define FEATURE_SET_FLAGS        1       // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when the flags need to be set
-  #define FEATURE_MULTIREG_STRUCTS      1  // Support for passing and/or returning structs in more than one register  
-  #define FEATURE_MULTIREG_STRUCT_ARGS  1  // Support for passing structs in more than one register  
-  #define FEATURE_MULTIREG_STRUCT_RET   0  // Support for returning structs in more than one register  
+  #define FEATURE_MULTIREG_ARGS_OR_RET  1  // Support for passing and/or returning single values in more than one register  
+  #define FEATURE_MULTIREG_ARGS         1  // Support for passing a single argument in more than one register  
+  #define FEATURE_MULTIREG_RET          0  // Support for returning a single value in more than one register  
   #define FEATURE_STRUCT_CLASSIFIER     0   // Uses a classifier function to determine is structs are passed/returned in more than one register
   #define MAX_PASS_MULTIREG_BYTES      16   // Maximum size of a struct that could be passed in more than one register
+  #define MAX_ARG_REG_COUNT             2  // Maximum registers used to pass an argument.
+  #define MAX_RET_REG_COUNT             2  // Maximum registers used to return a value.
 
 #ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS
   #define NOGC_WRITE_BARRIERS      1       // We have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers