summaryrefslogtreecommitdiff
path: root/src/jit/lclvars.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/lclvars.cpp')
-rw-r--r--src/jit/lclvars.cpp613
1 files changed, 420 insertions, 193 deletions
diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp
index c12f735f68..b9e89f156d 100644
--- a/src/jit/lclvars.cpp
+++ b/src/jit/lclvars.cpp
@@ -103,8 +103,8 @@ void Compiler::lvaInitTypeRef()
/* Set compArgsCount and compLocalsCount */
info.compArgsCount = info.compMethodInfo->args.numArgs;
-
- /* Is there a 'this' pointer */
+
+ // Is there a 'this' pointer
if (!info.compIsStatic)
{
@@ -133,6 +133,18 @@ void Compiler::lvaInitTypeRef()
else
#endif
{
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
+ eeGetSystemVAmd64PassStructInRegisterDescriptor(info.compMethodInfo->args.retTypeClass, &structDesc);
+ if (structDesc.eightByteCount > 1)
+ {
+ info.compRetNativeType = TYP_STRUCT;
+ }
+ else
+ {
+ info.compRetNativeType = getEightByteType(structDesc, 0);
+ }
+#else // !FEATURE_UNIX_AMD64_STRUCT_PASSING
unsigned size = info.compCompHnd->getClassSize(info.compMethodInfo->args.retTypeClass);
// Check for TYP_STRUCT argument that can fit into a single register
@@ -173,6 +185,7 @@ void Compiler::lvaInitTypeRef()
assert(!"Unexpected size when returning struct by value");
break;
}
+#endif // !FEATURE_UNIX_AMD64_STRUCT_PASSING
}
}
@@ -191,7 +204,9 @@ void Compiler::lvaInitTypeRef()
calling convention is varargs */
if (info.compIsVarArgs)
+ {
info.compArgsCount++;
+ }
// Is there an extra parameter used to pass instantiation info to
// shared generic methods and shared generic struct instance methods?
@@ -356,18 +371,17 @@ void Compiler::lvaInitArgs(InitVarDscInfo * varDscInfo)
//----------------------------------------------------------------------
- /* We have set info.compArgsCount in compCompile() */
-
+ // We have set info.compArgsCount in compCompile()
noway_assert(varDscInfo->varNum == info.compArgsCount);
assert (varDscInfo->intRegArgNum <= MAX_REG_ARG);
-
+
codeGen->intRegState.rsCalleeRegArgNum = varDscInfo->intRegArgNum;
#if !FEATURE_STACK_FP_X87
codeGen->floatRegState.rsCalleeRegArgNum = varDscInfo->floatRegArgNum;
#endif // FEATURE_STACK_FP_X87
- /* The total argument size must be aligned. */
+ // The total argument size must be aligned.
noway_assert((compArgSize % sizeof(void*)) == 0);
#ifdef _TARGET_X86_
@@ -440,6 +454,7 @@ void Compiler::lvaInitThisPtr(InitVarDscInfo * varDscInfo)
}
#endif
compArgSize += TARGET_POINTER_SIZE;
+
varDscInfo->varNum++;
varDscInfo->varDsc++;
}
@@ -449,7 +464,17 @@ void Compiler::lvaInitThisPtr(InitVarDscInfo * varDscInfo)
void Compiler::lvaInitRetBuffArg(InitVarDscInfo * varDscInfo)
{
LclVarDsc * varDsc = varDscInfo->varDsc;
- const bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo);
+ bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo);
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ if (info.compRetNativeType == TYP_STRUCT)
+ {
+ if (IsRegisterPassable(info.compMethodInfo->args.retTypeClass))
+ {
+ hasRetBuffArg = false;
+ }
+ }
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
if (hasRetBuffArg)
{
@@ -594,7 +619,6 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo * varDscInfo)
// the type as a float or double.
argType = hfaType;
}
-
if (isRegParamType(argType))
{
compArgSize += varDscInfo->alignReg(argType, cAlign) * REGSIZE_BYTES;
@@ -644,19 +668,94 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo * varDscInfo)
}
#else // !_TARGET_ARM_
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
+ if (argType == TYP_STRUCT)
+ {
+ assert(typeHnd != nullptr);
+ eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc);
+ if (structDesc.passedInRegisters)
+ {
+ unsigned intRegCount = 0;
+ unsigned floatRegCount = 0;
- varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
+ for (unsigned int i = 0; i < structDesc.eightByteCount; i++)
+ {
+ switch (structDesc.eightByteClassifications[i])
+ {
+ case SystemVClassificationTypeInteger:
+ case SystemVClassificationTypeIntegerReference:
+ intRegCount++;
+ break;
+ case SystemVClassificationTypeSSE:
+ floatRegCount++;
+ break;
+ default:
+ assert(false && "Invalid eightbyte classification type.");
+ break;
+ }
+ }
+
+ if (intRegCount != 0 && !varDscInfo->canEnreg(TYP_INT, intRegCount))
+ {
+ structDesc.passedInRegisters = false; // No register to enregister the eightbytes.
+ }
+
+ if (floatRegCount != 0 && !varDscInfo->canEnreg(TYP_FLOAT, floatRegCount))
+ {
+ structDesc.passedInRegisters = false; // No register to enregister the eightbytes.
+ }
+ }
+ }
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+ // The final home for this incoming register might be our local stack frame
+ // For System V platforms the final home will always be on the local stack frame.
+ varDsc->lvOnFrame = true;
#endif // !_TARGET_ARM_
- if (varDscInfo->canEnreg(argType, cSlotsToEnregister))
+ bool canPassArgInRegisters = false;
+
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ if (argType == TYP_STRUCT)
+ {
+ canPassArgInRegisters = structDesc.passedInRegisters;
+ }
+ else
+#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ {
+ canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister);
+ }
+
+ if (canPassArgInRegisters)
{
/* Another register argument */
// Allocate the registers we need. allocRegArg() returns the first argument register number of the set.
// For non-HFA structs, we still "try" to enregister the whole thing; it will just max out if splitting
// to the stack happens.
- unsigned firstAllocatedRegArgNum = varDscInfo->allocRegArg(argType, cSlots);
+ unsigned firstAllocatedRegArgNum = 0;
+
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ unsigned secondAllocatedRegArgNum = 0;
+ var_types firstEightByteType = TYP_UNDEF;
+ var_types secondEightByteType = TYP_UNDEF;
+ varDsc->lvOtherArgReg = REG_NA;
+
+ if (argType == TYP_STRUCT)
+ {
+ if (structDesc.eightByteCount >= 1)
+ {
+ firstEightByteType = getEightByteType(structDesc, 0);
+ firstAllocatedRegArgNum = varDscInfo->allocRegArg(firstEightByteType, 1);
+ }
+ }
+ else
+#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ {
+ firstAllocatedRegArgNum = varDscInfo->allocRegArg(argType, cSlots);
+ }
#ifdef _TARGET_ARM_
if (isHfaArg)
@@ -668,7 +767,31 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo * varDscInfo)
#endif // _TARGET_ARM_
varDsc->lvIsRegArg = 1;
- varDsc->lvArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum, argType);
+
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ if (argType == TYP_STRUCT)
+ {
+ varDsc->lvArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum, firstEightByteType);
+
+ // If there is a second eightbyte, get a register for it too and map the arg to the reg number.
+ if (structDesc.eightByteCount >= 2)
+ {
+ secondEightByteType = getEightByteType(structDesc, 1);
+ secondAllocatedRegArgNum = varDscInfo->allocRegArg(secondEightByteType, 1);
+ }
+
+ if (secondEightByteType != TYP_UNDEF)
+ {
+ varDsc->lvOtherArgReg = genMapRegArgNumToRegNum(secondAllocatedRegArgNum, secondEightByteType);
+ varDsc->addPrefReg(genRegMask(varDsc->lvOtherArgReg), this);
+ }
+ }
+ else
+#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING))
+ {
+ varDsc->lvArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum, argType);
+ }
+
varDsc->setPrefReg(varDsc->lvArgReg, this);
#ifdef _TARGET_ARM_
@@ -682,52 +805,91 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo * varDscInfo)
#ifdef DEBUG
if (verbose)
{
- printf("Arg #%u passed in register ", varDscInfo->varNum);
-
- bool isFloat = varTypeIsFloating(argType);
- unsigned regArgNum = genMapRegNumToRegArgNum(varDsc->lvArgReg, argType);
+ printf("Arg #%u passed in register(s) ", varDscInfo->varNum);
+ bool isFloat = false;
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ // In case of one eightbyte struct the type is already normalized earlier.
+ // The varTypeIsFloating(argType) is good for this case.
+ if ((argType == TYP_STRUCT) && (structDesc.eightByteCount >= 1))
+ {
+ isFloat = varTypeIsFloating(firstEightByteType);
+ }
+ else
+#else // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ {
+ isFloat = varTypeIsFloating(argType);
+ }
+#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
- for (unsigned ix = 0; ix < cSlots; ix++, regArgNum++)
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ if (argType == TYP_STRUCT)
{
- if (ix > 0)
- printf(",");
+ // Print both registers, just to be clear
+ if (firstEightByteType == TYP_UNDEF)
+ {
+ printf("firstEightByte: <not used>");
+ }
+ else
+ {
+ printf("firstEightByte: %s", getRegName(genMapRegArgNumToRegNum(firstAllocatedRegArgNum, firstEightByteType), isFloat));
+ }
- if (!isFloat && (regArgNum >= varDscInfo->maxIntRegArgNum)) // a struct has been split between registers and stack
+ if (secondEightByteType == TYP_UNDEF)
{
- printf(" stack slots:%d", cSlots - ix);
- break;
+ printf(", secondEightByte: <not used>");
}
+ else
+ {
+ printf(", secondEightByte: %s", getRegName(genMapRegArgNumToRegNum(secondAllocatedRegArgNum, secondEightByteType), varTypeIsFloating(secondEightByteType)));
+ }
+ }
+ else
+#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ {
+ unsigned regArgNum = genMapRegNumToRegArgNum(varDsc->lvArgReg, argType);
-#ifdef _TARGET_ARM_
- if (isFloat)
+ for (unsigned ix = 0; ix < cSlots; ix++, regArgNum++)
{
- // Print register size prefix
- if (argType == TYP_DOUBLE)
+ if (ix > 0)
+ printf(",");
+
+ if (!isFloat && (regArgNum >= varDscInfo->maxIntRegArgNum)) // a struct has been split between registers and stack
+ {
+ printf(" stack slots:%d", cSlots - ix);
+ break;
+ }
+
+#ifdef _TARGET_ARM_
+ if (isFloat)
{
- // Print both registers, just to be clear
- printf("%s/%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType), isFloat),
- getRegName(genMapRegArgNumToRegNum(regArgNum + 1, argType), isFloat));
-
- // doubles take 2 slots
- assert(ix + 1 < cSlots);
- ++ix;
- ++regArgNum;
+ // Print register size prefix
+ if (argType == TYP_DOUBLE)
+ {
+ // Print both registers, just to be clear
+ printf("%s/%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType), isFloat),
+ getRegName(genMapRegArgNumToRegNum(regArgNum + 1, argType), isFloat));
+
+ // doubles take 2 slots
+ assert(ix + 1 < cSlots);
+ ++ix;
+ ++regArgNum;
+ }
+ else
+ {
+ printf("%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType), isFloat));
+ }
}
else
+#endif // _TARGET_ARM_
{
printf("%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType), isFloat));
}
}
- else
-#endif // _TARGET_ARM_
- {
- printf("%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType), isFloat));
- }
}
printf("\n");
}
#endif // DEBUG
- } // if canEnreg()
+ } // end if (canPassArgInRegisters)
else
{
#ifdef _TARGET_ARM_
@@ -739,8 +901,13 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo * varDscInfo)
#endif
}
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ // The arg size is returning the number of bytes of the argument. For a struct it could return a size not a multiple of
+ // TARGET_POINTER_SIZE. The stack allocated space should always be multiple of TARGET_POINTER_SIZE, so round it up.
+ compArgSize += (unsigned)roundUp(argSize, TARGET_POINTER_SIZE);
+#else // !FEATURE_UNIX_AMD64_STRUCT_PASSING
compArgSize += argSize;
-
+#endif // !FEATURE_UNIX_AMD64_STRUCT_PASSING
if (info.compIsVarArgs)
{
#if defined(_TARGET_X86_)
@@ -807,6 +974,7 @@ void Compiler::lvaInitGenericsCtxt(InitVarDscInfo * varDscInfo)
varDsc->lvArgReg = genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet());
varDsc->setPrefReg(varDsc->lvArgReg, this);
varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
+
varDscInfo->intRegArgNum++;
#ifdef DEBUG
@@ -1180,11 +1348,6 @@ void Compiler::lvaCanPromoteStructType(CORINFO_CLASS_HANDLE typeHnd,
lvaStructPromotionInfo * StructPromotionInfo,
bool sortFields)
{
-#ifdef UNIX_AMD64_ABI
- // TODO-Amd64-Unix: For now don't promote structs on Linux.
- // This should be brought online with the full SystemVStruct passing work.
- return;
-#endif // UNIX_AMD64_ABI
assert(eeIsValueClass(typeHnd));
if (typeHnd != StructPromotionInfo->typeHnd)
@@ -2844,14 +3007,21 @@ void Compiler::lvaMarkLclRefs(GenTreePtr tree)
}
#endif // ASSERTION_PROP
+ bool allowStructs = false;
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ // On System V the type of the var could be a TYP_STRUCT.
+ allowStructs = varDsc->lvType == TYP_STRUCT;
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
/* Variables must be used as the same type throughout the method */
- noway_assert(tiVerificationNeeded ||
- varDsc->lvType == TYP_UNDEF || tree->gtType == TYP_UNKNOWN ||
- genActualType(varDsc->TypeGet()) == genActualType(tree->gtType) ||
- (tree->gtType == TYP_BYREF && varDsc->TypeGet() == TYP_I_IMPL) ||
- (tree->gtType == TYP_I_IMPL && varDsc->TypeGet() == TYP_BYREF) ||
- (tree->gtFlags & GTF_VAR_CAST) ||
- varTypeIsFloating(varDsc->TypeGet()) && varTypeIsFloating(tree->gtType));
+ noway_assert(tiVerificationNeeded ||
+ varDsc->lvType == TYP_UNDEF || tree->gtType == TYP_UNKNOWN ||
+ allowStructs ||
+ genActualType(varDsc->TypeGet()) == genActualType(tree->gtType) ||
+ (tree->gtType == TYP_BYREF && varDsc->TypeGet() == TYP_I_IMPL) ||
+ (tree->gtType == TYP_I_IMPL && varDsc->TypeGet() == TYP_BYREF) ||
+ (tree->gtFlags & GTF_VAR_CAST) ||
+ varTypeIsFloating(varDsc->TypeGet()) && varTypeIsFloating(tree->gtType));
/* Remember the type of the reference */
@@ -3690,7 +3860,6 @@ void Compiler::lvaFixVirtualFrameOffsets()
delta += codeGen->genTotalFrameSize() - codeGen->genSPtoFPdelta();
}
#endif //_TARGET_AMD64_
-
unsigned lclNum;
LclVarDsc * varDsc;
for (lclNum = 0, varDsc = lvaTable;
@@ -3735,6 +3904,7 @@ void Compiler::lvaFixVirtualFrameOffsets()
if (doAssignStkOffs)
{
varDsc->lvStkOffs += delta;
+
#if DOUBLE_ALIGN
if (genDoubleAlign() && !codeGen->isFramePointerUsed())
{
@@ -3886,11 +4056,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
{
noway_assert(lclNum == info.compThisArg);
#ifndef _TARGET_X86_
-#ifdef UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs, &callerArgOffset);
-#else // !UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs);
-#endif // !UNIX_AMD64_ABI
+ argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
#endif // _TARGET_X86_
lclNum++;
}
@@ -3902,11 +4068,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
noway_assert(lclNum == info.compRetBuffArg);
noway_assert(lvaTable[lclNum].lvIsRegArg);
#ifndef _TARGET_X86_
-#ifdef UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs, &callerArgOffset);
-#else // !UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs);
-#endif // !UNIX_AMD64_ABI
+ argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
#endif // _TARGET_X86_
lclNum++;
}
@@ -3917,20 +4079,12 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
{
noway_assert(lclNum == (unsigned)info.compTypeCtxtArg);
-#ifdef UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs, &callerArgOffset);
-#else // UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs);
-#endif // UNIX_AMD64_ABI
+ argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
}
if (info.compIsVarArgs)
{
-#ifdef UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs, &callerArgOffset);
-#else // !UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs);
-#endif // !UNIX_AMD64_ABI
+ argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
}
#endif // USER_ARGS_COME_LAST
@@ -3976,18 +4130,10 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
if (lvaIsPreSpilled(preSpillLclNum, preSpillMask))
{
unsigned argSize = eeGetArgSize(argLst, &info.compMethodInfo->args);
-#ifdef UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(
- preSpillLclNum,
- argSize,
- argOffs,
- &callerArgOffset);
-#else // !UNIX_AMD64_ABI
argOffs = lvaAssignVirtualFrameOffsetToArg(
preSpillLclNum,
argSize,
argOffs);
-#endif // !UNIX_AMD64_ABI
argLcls++;
// Early out if we can. If size is 8 and base reg is 2, then the mask is 0x1100
@@ -4008,18 +4154,10 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
{
if (!lvaIsPreSpilled(stkLclNum, preSpillMask))
{
-#ifdef UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(
- stkLclNum,
- eeGetArgSize(argLst, &info.compMethodInfo->args),
- argOffs,
- &callerArgOffset);
-#else // !UNIX_AMD64_ABI
argOffs = lvaAssignVirtualFrameOffsetToArg(
stkLclNum,
eeGetArgSize(argLst, &info.compMethodInfo->args),
argOffs);
-#endif // !UNIX_AMD64_ABI
argLcls++;
}
argLst = info.compCompHnd->getArgNext(argLst);
@@ -4029,16 +4167,18 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
#else // !_TARGET_ARM_
for (unsigned i = 0; i < argSigLen; i++)
{
-#ifdef UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++,
- eeGetArgSize(argLst, &info.compMethodInfo->args),
- argOffs,
- &callerArgOffset);
-#else // !UNIX_AMD64_ABI
+ unsigned argumentSize = eeGetArgSize(argLst, &info.compMethodInfo->args);
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ // On the stack frame the homed arg always takes a full number of slots
+ // for proper stack alignment. Make sure the real struct size is properly rounded up.
+ argumentSize = (unsigned)roundUp(argumentSize, TARGET_POINTER_SIZE);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++,
- eeGetArgSize(argLst, &info.compMethodInfo->args),
- argOffs);
-#endif // UNIX_AMD64_ABI
+ argumentSize,
+ argOffs
+ UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
argLst = info.compCompHnd->getArgNext(argLst);
}
#endif // !_TARGET_ARM_
@@ -4049,26 +4189,19 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
{
noway_assert(lclNum == (unsigned)info.compTypeCtxtArg);
-#ifdef UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs, &callerArgOffset);
-#else // !UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs);
-#endif // !UNIX_AMD64_ABI
+ argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs, UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
}
if (info.compIsVarArgs)
{
-#ifdef UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs, &callerArgOffset);
-#else // !UNIX_AMD64_ABI
- argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs);
-#endif // !UNIX_AMD64_ABI
+ argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, sizeof(void *), argOffs, UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
}
#endif // USER_ARGS_COME_LAST
}
+#ifdef UNIX_AMD64_ABI
//
// lvaAssignVirtualFrameOffsetToArg() : Assign virtual stack offsets to an
// individual argument, and return the offset for the next argument.
@@ -4076,12 +4209,9 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
// (if any - the RA might decide to spill(home on the stack) register passed arguments, if rarely used.)
// The final offset is calculated in lvaFixVirtualFrameOffsets method. It accounts for FP existance,
// ret address slot, stack frame padding, alloca instructions, etc.
+// Note: This is the implementation for UNIX_AMD64 System V platforms.
//
-#ifdef UNIX_AMD64_ABI
-int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize, int argOffs, int * callerArgOffset)
-#else // !UNIX_AMD64_ABI
-int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize, int argOffs)
-#endif // !UNIX_AMD64_ABI
+int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize, int argOffs UNIX_AMD64_ABI_ONLY_ARG(int * callerArgOffset))
{
noway_assert(lclNum < info.compArgsCount);
noway_assert(argSize);
@@ -4114,30 +4244,131 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize
if (varDsc->lvIsRegArg)
{
- /* Argument is passed in a register, don't count it
- * when updating the current offset on the stack */
-
-#ifndef _TARGET_ARM_
- noway_assert(argSize == sizeof(void *));
-#endif
+ // Argument is passed in a register, don't count it
+ // when updating the current offset on the stack.
-#if defined(_TARGET_X86_)
- argOffs += sizeof(void *);
-#elif defined(_TARGET_AMD64_)
-#ifdef UNIX_AMD64_ABI
if (varDsc->lvOnFrame)
-#endif
{
// The offset for args needs to be set only for the stack homed arguments for System V.
varDsc->lvStkOffs = argOffs;
- argOffs += sizeof(void *);
}
-#ifdef UNIX_AMD64_ABI
- else
+ else
{
varDsc->lvStkOffs = 0;
}
+ }
+ else
+ {
+ // For Windows AMD64 there are 4 slots for the register passed arguments on the top of the caller's stack. This is where they are always homed.
+ // So, they can be accessed with positive offset.
+ // On System V platforms, if the RA decides to home a register passed arg on the stack,
+ // it creates a stack location on the callee stack (like any other local var.) In such a case, the register passed, stack homed arguments
+ // are accessed using negative offsets and the stack passed arguments are accessed using positive offset (from the caller's stack.)
+ // For System V platforms if there is no frame pointer the caller stack parameter offset should include the callee allocated space.
+ // If frame register is used, the callee allocated space should not be included for accessing the caller stack parameters.
+ // The last two requirements are met in lvaFixVirtualFrameOffsets method, which fixes the offsets, based on frame pointer existence,
+ // existence of alloca instructions, ret address pushed, ets.
+
+ varDsc->lvStkOffs = *callerArgOffset;
+ // Structs passed on stack could be of size less than TARGET_POINTER_SIZE.
+ // Make sure they get at least TARGET_POINTER_SIZE on the stack - this is required for alignment.
+ if (varDsc->lvType == TYP_STRUCT)
+ {
+ *callerArgOffset += (int)roundUp(argSize, TARGET_POINTER_SIZE);
+ }
+ else
+ {
+ *callerArgOffset += TARGET_POINTER_SIZE;
+ }
+ }
+
+ // For struct promoted parameters we need to set the offsets for both LclVars.
+ //
+ // For a dependent promoted struct we also assign the struct fields stack offset
+ if (varDsc->lvPromotedStruct())
+ {
+ lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
+
+ if (promotionType == PROMOTION_TYPE_DEPENDENT)
+ {
+ noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
+
+ assert(fieldVarNum == varDsc->lvFieldLclStart);
+ lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
+ }
+ }
+ // For an independent promoted struct field we also assign the parent struct stack offset
+ else if (varDsc->lvIsStructField)
+ {
+ noway_assert(varDsc->lvParentLcl < lvaCount);
+ lvaTable[varDsc->lvParentLcl].lvStkOffs = varDsc->lvStkOffs;
+ }
+
+ if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L && !varDsc->lvIsRegArg)
+ argOffs += argSize;
+
+ return argOffs;
+}
+
+#else // !UNIX_AMD64_ABI
+
+//
+// lvaAssignVirtualFrameOffsetToArg() : Assign virtual stack offsets to an
+// individual argument, and return the offset for the next argument.
+// Note: This method only calculates the initial offset of the stack passed/spilled arguments
+// (if any - the RA might decide to spill(home on the stack) register passed arguments, if rarely used.)
+// The final offset is calculated in lvaFixVirtualFrameOffsets method. It accounts for FP existance,
+// ret address slot, stack frame padding, alloca instructions, etc.
+// Note: This implementation for all the platforms but UNIX_AMD64 OSs (System V 64 bit.)
+int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize, int argOffs UNIX_AMD64_ABI_ONLY_ARG(int * callerArgOffset))
+{
+ noway_assert(lclNum < info.compArgsCount);
+ noway_assert(argSize);
+
+ if (Target::g_tgtArgOrder == Target::ARG_ORDER_L2R)
+ argOffs -= argSize;
+
+ unsigned fieldVarNum = BAD_VAR_NUM;
+
+ noway_assert(lclNum < lvaCount);
+ LclVarDsc * varDsc = lvaTable + lclNum;
+
+ if (varDsc->lvPromotedStruct())
+ {
+ noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
+ fieldVarNum = varDsc->lvFieldLclStart;
+
+ lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
+
+ if (promotionType == PROMOTION_TYPE_INDEPENDENT)
+ {
+ lclNum = fieldVarNum;
+ noway_assert(lclNum < lvaCount);
+ varDsc = lvaTable + lclNum;
+ assert(varDsc->lvIsStructField);
+ }
+ }
+
+ noway_assert(varDsc->lvIsParam);
+
+ if (varDsc->lvIsRegArg)
+ {
+ /* Argument is passed in a register, don't count it
+ * when updating the current offset on the stack */
+
+#ifndef _TARGET_ARM_
+#if DEBUG
+ noway_assert(argSize == sizeof(void *));
+#endif // DEBUG
#endif
+
+#if defined(_TARGET_X86_)
+ argOffs += sizeof(void *);
+#elif defined(_TARGET_AMD64_)
+ // The offset for args needs to be set only for the stack homed arguments for System V.
+ varDsc->lvStkOffs = argOffs;
+ // Register arguments also take stack space.
+ argOffs += sizeof(void *);
#elif defined(_TARGET_ARM64_)
// Register arguments don't take stack space.
#elif defined(_TARGET_ARM_)
@@ -4181,32 +4412,32 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize
case TYP_DOUBLE:
case TYP_LONG:
+ {
+ //
+ // Let's assign offsets to arg1, a double in r2. argOffs has to be 4 not 8.
+ //
+ // ------- CALLER SP -------
+ // r3
+ // r2 double -- argOffs = 4, but it doesn't need to be skipped, because there is no skipping.
+ // r1 VACookie -- argOffs = 0
+ // -------------------------
+ //
+ // Consider argOffs as if it accounts for number of prespilled registers before the current register.
+ // In the above example, for r2, it is r1 that is prespilled, but since r1 is accounted for by argOffs
+ // being 4, there should have been no skipping. Instead, if we didn't assign r1 to any variable, then
+ // argOffs would still be 0 which implies it is not accounting for r1, equivalently r1 is skipped.
+ //
+ // If prevRegsSize is unaccounted for by a corresponding argOffs, we must have skipped a register.
+ int prevRegsSize = genCountBits(codeGen->regSet.rsMaskPreSpillRegArg & (regMask - 1)) * TARGET_POINTER_SIZE;
+ if (argOffs < prevRegsSize)
{
- //
- // Let's assign offsets to arg1, a double in r2. argOffs has to be 4 not 8.
- //
- // ------- CALLER SP -------
- // r3
- // r2 double -- argOffs = 4, but it doesn't need to be skipped, because there is no skipping.
- // r1 VACookie -- argOffs = 0
- // -------------------------
- //
- // Consider argOffs as if it accounts for number of prespilled registers before the current register.
- // In the above example, for r2, it is r1 that is prespilled, but since r1 is accounted for by argOffs
- // being 4, there should have been no skipping. Instead, if we didn't assign r1 to any variable, then
- // argOffs would still be 0 which implies it is not accounting for r1, equivalently r1 is skipped.
- //
- // If prevRegsSize is unaccounted for by a corresponding argOffs, we must have skipped a register.
- int prevRegsSize = genCountBits(codeGen->regSet.rsMaskPreSpillRegArg & (regMask - 1)) * TARGET_POINTER_SIZE;
- if (argOffs < prevRegsSize)
- {
- // We must align up the argOffset to a multiple of 8 to account for skipped registers.
- argOffs = roundUp(argOffs, 2*TARGET_POINTER_SIZE);
- }
- // We should've skipped only a single register.
- assert(argOffs == prevRegsSize);
+ // We must align up the argOffset to a multiple of 8 to account for skipped registers.
+ argOffs = roundUp(argOffs, 2 * TARGET_POINTER_SIZE);
}
- break;
+ // We should've skipped only a single register.
+ assert(argOffs == prevRegsSize);
+ }
+ break;
default:
// No alignment of argOffs required
@@ -4292,16 +4523,16 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize
if (!compIsProfilerHookNeeded())
#endif
{
- bool cond = (info.compIsVarArgs &&
- // Does cur stk arg require double alignment?
- ((varDsc->lvType == TYP_STRUCT && varDsc->lvStructDoubleAlign) ||
- (varDsc->lvType == TYP_DOUBLE) ||
- (varDsc->lvType == TYP_LONG))
- ) ||
- // Did first reg arg require alignment?
- (codeGen->regSet.rsMaskPreSpillAlign & genRegMask(REG_ARG_LAST));
-
- noway_assert(cond);
+ bool cond = (info.compIsVarArgs &&
+ // Does cur stk arg require double alignment?
+ ((varDsc->lvType == TYP_STRUCT && varDsc->lvStructDoubleAlign) ||
+ (varDsc->lvType == TYP_DOUBLE) ||
+ (varDsc->lvType == TYP_LONG))
+ ) ||
+ // Did first reg arg require alignment?
+ (codeGen->regSet.rsMaskPreSpillAlign & genRegMask(REG_ARG_LAST));
+
+ noway_assert(cond);
noway_assert(sizeofPreSpillRegArgs <= argOffs + TARGET_POINTER_SIZE); // at most one register of alignment
}
argOffs = sizeofPreSpillRegArgs;
@@ -4321,7 +4552,7 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize
case TYP_DOUBLE:
case TYP_LONG:
// We must align up the argOffset to a multiple of 8
- argOffs = roundUp(argOffsWithoutPreSpillRegArgs, 2*TARGET_POINTER_SIZE) + sizeofPreSpillRegArgs;
+ argOffs = roundUp(argOffsWithoutPreSpillRegArgs, 2 * TARGET_POINTER_SIZE) + sizeofPreSpillRegArgs;
break;
default:
@@ -4330,21 +4561,7 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize
}
#endif // _TARGET_ARM_
-#ifdef UNIX_AMD64_ABI
- // For Windows there are 4 slots for the register passed arguments on the top of the caller's stack. This is where they are always homed.
- // So, they can be accessed with positive offset.
- // On System V platforms, if the RA decides to home a register passed arg on the stack,
- // it creates a stack location on the callee stack (like any other local var.) In such a case, the register passed, stack homed arguments
- // are accessed using negative offsets and the stack passed arguments are accessed using positive offset (from the caller's stack.)
- // For System V platforms if there is no frame pointer the caller stack parameter offset should include the callee allocated space.
- // If frame register is used, the callee allocated space should not be included for accessing the caller stack parameters.
- // The last two requirements are met in lvaFixVirtualFrameOffsets method, which fixes the offsets, based on frame pointer existence,
- // existence of alloca instructions, ret address pushed, ets.
- varDsc->lvStkOffs = *callerArgOffset;
- *callerArgOffset += TARGET_POINTER_SIZE;
-#else // !UNIX_AMD64_ABI
varDsc->lvStkOffs = argOffs;
-#endif // !UNIX_AMD64_ABI
}
// For struct promoted parameters we need to set the offsets for both LclVars.
@@ -4360,31 +4577,31 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize
}
else
#endif // !defined(_TARGET_64BIT_)
- if (varDsc->lvPromotedStruct())
- {
- lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
-
- if (promotionType == PROMOTION_TYPE_DEPENDENT)
+ if (varDsc->lvPromotedStruct())
{
- noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
+ lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
- assert(fieldVarNum == varDsc->lvFieldLclStart);
- lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
+ if (promotionType == PROMOTION_TYPE_DEPENDENT)
+ {
+ noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
+
+ assert(fieldVarNum == varDsc->lvFieldLclStart);
+ lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
+ }
}
- }
// For an independent promoted struct field we also assign the parent struct stack offset
- else if (varDsc->lvIsStructField)
- {
- noway_assert(varDsc->lvParentLcl < lvaCount);
- lvaTable[varDsc->lvParentLcl].lvStkOffs = varDsc->lvStkOffs;
- }
+ else if (varDsc->lvIsStructField)
+ {
+ noway_assert(varDsc->lvParentLcl < lvaCount);
+ lvaTable[varDsc->lvParentLcl].lvStkOffs = varDsc->lvStkOffs;
+ }
if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L && !varDsc->lvIsRegArg)
argOffs += argSize;
return argOffs;
}
-
+#endif // !UNIX_AMD64_ABI
/*****************************************************************************
* lvaAssignVirtualFrameOffsetsToLocals() : Assign virtual stack offsets to
@@ -5261,8 +5478,18 @@ void Compiler::lvaAssignFrameOffsetsToPromotedStructs()
{
// For promoted struct fields that are params, we will
// assign their offsets in lvaAssignVirtualFrameOffsetToArg().
+ // This is not true for the System V systems since there is no
+ // outgoing args space. Assign the dependently promoted fields properly.
//
- if (varDsc->lvIsStructField && !varDsc->lvIsParam)
+ if (varDsc->lvIsStructField
+#ifndef UNIX_AMD64_ABI
+ // For System V platforms there is no outgoing args space.
+ // A register passed struct arg is homed on the stack in a separate local var.
+ // The offset of these structs is already calculated in lvaAssignVirtualFrameOffsetToArg methos.
+ // Make sure the code below is not executed for these structs and the offset is not changed.
+ && !varDsc->lvIsParam
+#endif // UNIX_AMD64_ABI
+ )
{
LclVarDsc * parentvarDsc = &lvaTable[varDsc->lvParentLcl];
lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc);