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