#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)
}
#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)
}
#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;
}
// 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);
{
// 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);
// 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);
{
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
{
/* 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
{
* 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;
#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)
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_
//
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_
}
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;
{
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;
}
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);
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)
/* 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_
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);
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);
{
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)
#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;
#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
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)
// 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*);
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;
* [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];
/*****************************************************************************/
/* 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)
#endif
#endif
#endif
- default: assert(!"invalid register arg register"); return (unsigned)-1;
+ default:
+ assert(!"invalid register arg register");
+ return (unsigned)-1;
}
}
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);
}
#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
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.
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)
{
#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
varDsc->lvType = TYP_I_IMPL;
}
}
-
- assert(genMapIntRegNumToRegArgNum(varDsc->lvArgReg) < MAX_REG_ARG);
+ assert(isValidIntArgReg(varDsc->lvArgReg));
#ifdef DEBUG
if (verbose)
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);
/* 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
call->fgArgInfo = new (this, CMK_Unknown) fgArgInfo(this, call, numArgs);
}
-
fgFixupStructReturn(call);
/* First we morph the argument subtrees ('this' pointer, arguments, etc.).
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)
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
}
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);
if (!lateArgsComputed)
{
call->fgArgInfo->ArgsComplete();
-
call->gtCallRegUsedMask = genIntAllRegArgMask(intArgRegNum) & ~argSkippedRegMask;
if (fltArgRegNum > 0)
{
#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
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
// 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)
#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
#error Unsupported or unset target architecture
#endif
-
#ifdef _TARGET_XARCH_
#define JMP_DIST_SMALL_MAX_NEG (-128)
#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_)