Preliminary renaming and cleanup in preparation for further refactoring of outgoing call arguments, particularly multi-reg arguments.
slots = 1;
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
#ifdef _TARGET_ARM64_
if (varDsc->TypeGet() == TYP_STRUCT)
{
}
}
#endif // _TARGET_ARM64_
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
}
#ifdef _TARGET_ARM_
{
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);
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)
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
//------------------------------------------------------------------------
-// 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
{
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);
{
// 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);
//
// 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.
// 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);
break;
}
}
+ assert(varDsc != nullptr);
}
return 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());
case 3:
useType = TYP_INT;
break;
-#endif // _TARGET_AMD64_
+#endif // _TARGET_XARCH_
#ifdef _TARGET_64BIT_
case 4:
break;
default:
-#if FEATURE_MULTIREG_STRUCT_RET
+#if FEATURE_MULTIREG_RET
if (forReturn)
{
if (size <= MAX_RET_MULTIREG_BYTES)
#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)
#endif // _TARGET_ARM64_
}
}
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
break;
}
return useType;
// args:
// classType: classification type
// size: size of the eightbyte.
-//
+//
+// static
var_types Compiler::GetTypeFromClassificationAndSizes(SystemVClassificationType classType, int size)
{
var_types type = TYP_UNKNOWN;
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
unsigned char lvFldOffset;
unsigned char lvFldOrdinal;
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
regNumber lvRegNumForSlot(unsigned slotNum)
{
if (slotNum == 0)
unreached();
}
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
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
assert(_lvArgReg == reg);
}
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
__declspec(property(get = GetOtherArgReg, put = SetOtherArgReg))
regNumber lvOtherArgReg;
_lvOtherArgReg = (regNumberSmall)reg;
assert(_lvOtherArgReg == reg);
}
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
#ifdef FEATURE_SIMD
// Is this is a SIMD struct?
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
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)
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_
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)
// 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)
{
return TARGET_POINTER_SIZE;
}
#endif // _TARGET_ARM64_
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
return (unsigned)roundUp(structSize, TARGET_POINTER_SIZE);
}
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)
// '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
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);
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
{
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
}
#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.
}
#endif
-#endif
+#endif // FEATURE_MULTIREG_RET
// Otherwise we require that a RetBuffArg be used
return true;
// 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;
}
{
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;
}
// 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);
(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)
// 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
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);
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);
// 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;
varDsc->lvIsRegArg = 1;
-#if FEATURE_MULTIREG_STRUCT_ARGS
+#if FEATURE_MULTIREG_ARGS
if (varTypeIsStruct(argType))
{
#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
}
else
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
{
varDsc->lvArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum, argType);
}
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);
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);
#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
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);
}
}
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];
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;
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;
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;
// 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))
{
{
BADCODE("Unhandled TYP_STRUCT argument tree in fgMorphArgs");
}
- }
+ }
#endif // !(defined(_TARGET_AMD64_) && !defined(FEATURE_UNIX_AMD64_STRUCT_PASSING))
}
+//------------------------------------------------------------------------
+// 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
{
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;
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))
{
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;
// 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
{
// 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;
}
// 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;
}
}
}
#ifdef _TARGET_AMD64_
#ifndef FEATURE_UNIX_AMD64_STRUCT_PASSING
- assert(size == 1);
+ assert(size == 1);
#endif
#endif
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));
// 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);
// 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
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)
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
//
continue;
}
#endif // _TARGET_ARM64_
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
if (varDsc->lvIsParam)
{
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))
{
regState->rsCalleeRegArgMaskLiveIn |= genRegMask(secondArgReg);
}
#endif // TARGET_ARM64_
-#endif // FEATURE_MULTIREG_STRUCT_ARGS
+#endif // FEATURE_MULTIREG_ARGS
#ifdef _TARGET_ARM_
if (argDsc->lvType == TYP_DOUBLE)
#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
#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
#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
#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