From: Carol Eidt Date: Mon, 1 Feb 2016 17:41:08 +0000 (-0800) Subject: Outgoing call arg preliminary refactoring X-Git-Tag: accepted/tizen/base/20180629.140029~5679^2~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f0a33fe2a544393d197b721061bb51aca6dd64ab;p=platform%2Fupstream%2Fcoreclr.git Outgoing call arg preliminary refactoring Preliminary renaming and cleanup in preparation for further refactoring of outgoing call arguments, particularly multi-reg arguments. --- diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 5c62b18..ea80ebf 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -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 diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp index 1510748..8911d7f 100644 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -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()); diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp index cd021f5..2b83635 100644 --- a/src/jit/compiler.cpp +++ b/src/jit/compiler.cpp @@ -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; diff --git a/src/jit/compiler.h b/src/jit/compiler.h index f8d780c..9e4640f 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -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) diff --git a/src/jit/ee_il_dll.cpp b/src/jit/ee_il_dll.cpp index ed5c4c9..cdf51fc 100644 --- a/src/jit/ee_il_dll.cpp +++ b/src/jit/ee_il_dll.cpp @@ -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); } diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp index 77358aa..a2710c3 100644 --- a/src/jit/flowgraph.cpp +++ b/src/jit/flowgraph.cpp @@ -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) diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp index d83e72a..773ab72 100644 --- a/src/jit/gentree.cpp +++ b/src/jit/gentree.cpp @@ -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); diff --git a/src/jit/gschecks.cpp b/src/jit/gschecks.cpp index 68e411e..9d10499 100644 --- a/src/jit/gschecks.cpp +++ b/src/jit/gschecks.cpp @@ -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 { diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index 44ca538..0ac4cf1 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -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 diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp index 21dca7c..e02dd3e 100644 --- a/src/jit/lclvars.cpp +++ b/src/jit/lclvars.cpp @@ -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); diff --git a/src/jit/lowerxarch.cpp b/src/jit/lowerxarch.cpp index 290b57e..a3a11a6 100644 --- a/src/jit/lowerxarch.cpp +++ b/src/jit/lowerxarch.cpp @@ -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 diff --git a/src/jit/lsra.cpp b/src/jit/lsra.cpp index 391c81c..cc462a8 100644 --- a/src/jit/lsra.cpp +++ b/src/jit/lsra.cpp @@ -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); } } diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp index d2dc660..79290dd 100644 --- a/src/jit/morph.cpp +++ b/src/jit/morph.cpp @@ -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) { diff --git a/src/jit/regalloc.cpp b/src/jit/regalloc.cpp index 62568bf..65b487d 100644 --- a/src/jit/regalloc.cpp +++ b/src/jit/regalloc.cpp @@ -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) diff --git a/src/jit/target.h b/src/jit/target.h index e2bff4a..b1139ec 100644 --- a/src/jit/target.h +++ b/src/jit/target.h @@ -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