regSet.AddMaskVars(genRegMask(argReg));
gcInfo.gcMarkRegPtrVal(argReg, loadType);
- if (compiler->lvaIsMultiregStruct(varDsc))
+ if (compiler->lvaIsMultiregStruct(varDsc, compiler->info.compIsVarArgs))
{
if (varDsc->lvIsHfa())
{
fixedIntArgMask |= genRegMask(argReg);
- if (compiler->lvaIsMultiregStruct(varDsc))
+ if (compiler->lvaIsMultiregStruct(varDsc, compiler->info.compIsVarArgs))
{
assert(argRegNext != REG_NA);
fixedIntArgMask |= genRegMask(argRegNext);
// Change regType to the HFA type when we have a HFA argument
if (varDsc->lvIsHfaRegArg())
{
+#if defined(_TARGET_WINDOWS_) && defined(_TARGET_ARM64_)
+ if (compiler->info.compIsVarArgs)
+ {
+ assert(!"Illegal incoming HFA arg encountered in Vararg method.");
+ }
+#endif // defined(_TARGET_WINDOWS_) && defined(_TARGET_ARM64_)
regType = varDsc->GetHfaType();
}
slots = 1;
#if FEATURE_MULTIREG_ARGS
- if (compiler->lvaIsMultiregStruct(varDsc))
+ if (compiler->lvaIsMultiregStruct(varDsc, compiler->info.compIsVarArgs))
{
if (varDsc->lvIsHfaRegArg())
{
#endif // defined(_TARGET_XARCH_)
//-----------------------------------------------------------------------------------
-// IsMultiRegPassedType: Returns true if the type is returned in multiple registers
-//
-// Arguments:
-// hClass - type handle
-//
-// Return Value:
-// true if type is passed in multiple registers, false otherwise.
-//
-bool Compiler::IsMultiRegPassedType(CORINFO_CLASS_HANDLE hClass)
-{
- if (hClass == NO_CLASS_HANDLE)
- {
- return false;
- }
-
- structPassingKind howToPassStruct;
- var_types returnType = getArgTypeForStruct(hClass, &howToPassStruct);
-
- return (varTypeIsStruct(returnType));
-}
-
-//-----------------------------------------------------------------------------------
// IsMultiRegReturnedType: Returns true if the type is returned in multiple registers
//
// Arguments:
// For ARM32 if we have an HFA struct that wraps a 64-bit double
// we will return TYP_DOUBLE.
//
-var_types Compiler::getPrimitiveTypeForStruct(unsigned structSize, CORINFO_CLASS_HANDLE clsHnd)
+var_types Compiler::getPrimitiveTypeForStruct(unsigned structSize, CORINFO_CLASS_HANDLE clsHnd, bool isVarArg)
{
assert(structSize != 0);
// For ARM_SOFTFP, HFA is unsupported so we need to check in another way
// This matters only for size-4 struct cause bigger structs would be processed with RetBuf
if (isSingleFloat32Struct(clsHnd))
-#else // !ARM_SOFTFP
- if (IsHfa(clsHnd))
+#else // !ARM_SOFTFP
+ if (IsHfa(clsHnd)
+#if defined(_TARGET_WINDOWS_) && defined(_TARGET_ARM64_)
+ // Arm64 Windows VarArg methods arguments will not
+ // classify HFA types, they will need to be treated
+ // as if they are not HFA types.
+ && !isVarArg
+#endif // defined(_TARGET_WINDOWS_) && defined(_TARGET_ARM64_)
+ )
#endif // ARM_SOFTFP
{
#ifdef _TARGET_64BIT_
// clsHnd - the handle for the struct type
// wbPassStruct - An "out" argument with information about how
// the struct is to be passed
+// isVarArg - is vararg, used to ignore HFA types for Arm64 windows varargs
// structSize - the size of the struct type,
// or zero if we should call getClassSize(clsHnd)
//
//
var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
structPassingKind* wbPassStruct,
+ bool isVarArg,
unsigned structSize /* = 0 */)
{
var_types useType = TYP_UNKNOWN;
{
// We set the "primitive" useType based upon the structSize
// and also examine the clsHnd to see if it is an HFA of count one
- useType = getPrimitiveTypeForStruct(structSize, clsHnd);
+ useType = getPrimitiveTypeForStruct(structSize, clsHnd, isVarArg);
}
#endif // !_TARGET_X86_
if (structSize <= MAX_PASS_MULTIREG_BYTES)
{
// Structs that are HFA's are passed by value in multiple registers
- if (IsHfa(clsHnd))
+ if (IsHfa(clsHnd)
+#if defined(_TARGET_WINDOWS_) && defined(_TARGET_ARM64_)
+ && !isVarArg // Arm64 Windows VarArg methods arguments will not
+ // classify HFA types, they will need to be treated
+ // as if they are not HFA types.
+#endif // defined(_TARGET_WINDOWS_) && defined(_TARGET_ARM64_)
+ )
{
// HFA's of count one should have been handled by getPrimitiveTypeForStruct
assert(GetHfaCount(clsHnd) >= 2);
{
// We set the "primitive" useType based upon the structSize
// and also examine the clsHnd to see if it is an HFA of count one
- useType = getPrimitiveTypeForStruct(structSize, clsHnd);
+ //
+ // The ABI for struct returns in varArg methods, is same as the normal case, so pass false for isVararg
+ useType = getPrimitiveTypeForStruct(structSize, clsHnd, /*isVararg=*/false);
}
#endif // UNIX_AMD64_ABI
bool isNonStandard : 1; // True if it is an arg that is passed in a reg other than a standard arg reg, or is forced
// to be on the stack despite its arg list position.
bool isStruct : 1; // True if this is a struct arg
+ bool _isVararg : 1; // True if the argument is in a vararg context.
#ifdef _TARGET_ARM_
bool _isSplit : 1; // True when this argument is split between the registers and OutArg area
#endif
#endif
}
+ __declspec(property(get = getIsVararg, put = setIsVararg)) bool isVararg;
+ bool getIsVararg()
+ {
+#ifdef FEATURE_VARARG
+ return _isVararg;
+#else
+ return false;
+#endif
+ }
+ void setIsVararg(bool value)
+ {
+#ifdef FEATURE_VARARG
+ _isVararg = value;
+#endif // FEATURE_VARARG
+ }
+
__declspec(property(get = getIsHfaRegArg)) bool isHfaRegArg;
bool getIsHfaRegArg()
{
regNumber regNum,
unsigned numRegs,
unsigned alignment,
- bool isStruct);
+ bool isStruct,
+ bool isVararg = false);
#ifdef UNIX_AMD64_ABI
fgArgTabEntry* AddRegArg(unsigned argNum,
unsigned numRegs,
unsigned alignment,
const bool isStruct,
+ const bool isVararg,
const regNumber otherRegNum,
const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* const structDescPtr = nullptr);
#endif // UNIX_AMD64_ABI
- fgArgTabEntry* AddStkArg(
- unsigned argNum, GenTree* node, GenTree* parent, unsigned numSlots, unsigned alignment, bool isStruct);
+ fgArgTabEntry* AddStkArg(unsigned argNum,
+ GenTree* node,
+ GenTree* parent,
+ unsigned numSlots,
+ unsigned alignment,
+ bool isStruct,
+ bool isVararg = false);
void RemorphReset();
fgArgTabEntry* RemorphRegArg(
var_types GetHfaType(CORINFO_CLASS_HANDLE hClass);
unsigned GetHfaCount(CORINFO_CLASS_HANDLE hClass);
- bool IsMultiRegPassedType(CORINFO_CLASS_HANDLE hClass);
bool IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass);
//-------------------------------------------------------------------------
}
// Returns true if this local var is a multireg struct
- bool lvaIsMultiregStruct(LclVarDsc* varDsc);
+ bool lvaIsMultiregStruct(LclVarDsc* varDsc, bool isVararg);
// If the local is a TYP_STRUCT, get/set a class handle describing it
CORINFO_CLASS_HANDLE lvaGetStruct(unsigned varNum);
void lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool unsafeValueClsCheck, bool setTypeInfo = true);
+ void lvaSetStructUsedAsVarArg(unsigned varNum);
// If the local is TYP_REF, set or update the associated class information.
void lvaSetClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool isExact = false);
GenTree* impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool isCastClass);
- bool VarTypeIsMultiByteAndCanEnreg(var_types type,
- CORINFO_CLASS_HANDLE typeClass,
- unsigned* typeSize,
- bool forReturn);
+ bool VarTypeIsMultiByteAndCanEnreg(
+ var_types type, CORINFO_CLASS_HANDLE typeClass, unsigned* typeSize, bool forReturn, bool isVarArg);
bool IsIntrinsicImplementedByUserCall(CorInfoIntrinsics intrinsicId);
bool IsTargetIntrinsic(CorInfoIntrinsics intrinsicId);
// A "primitive" type is one of the scalar types: byte, short, int, long, ref, float, double
// If we can't or shouldn't use a "primitive" type then TYP_UNKNOWN is returned.
//
- var_types getPrimitiveTypeForStruct(unsigned structSize, CORINFO_CLASS_HANDLE clsHnd);
+ // isVarArg is passed for use on Windows Arm64 to change the decision returned regarding
+ // hfa types.
+ //
+ var_types getPrimitiveTypeForStruct(unsigned structSize, CORINFO_CLASS_HANDLE clsHnd, bool isVarArg);
// Get the type that is used to pass values of the given struct type.
- // If you have already retrieved the struct size then pass it as the optional third argument
+ // If you have already retrieved the struct size then pass it as the optional fourth argument
+ //
+ // isVarArg is passed for use on Windows Arm64 to change the decision returned regarding
+ // hfa types.
//
var_types getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
structPassingKind* wbPassStruct,
+ bool isVarArg,
unsigned structSize = 0);
// Get the type that is used to return values of the given struct type.
- // If you have already retrieved the struct size then pass it as the optional third argument
- //
+ // If you have already retrieved the struct size then pass it as the optional fourth argument
var_types getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
structPassingKind* wbPassStruct = nullptr,
unsigned structSize = 0);
// typeSize - Out param (if non-null) is updated with the size of 'type'.
// forReturn - this is true when we asking about a GT_RETURN context;
// this is false when we are asking about an argument context
+// isVarArg - whether or not this is a vararg fixed arg or variable argument
+// - if so on arm64 windows getArgTypeForStruct will ignore HFA
+// - types
//
-inline bool Compiler::VarTypeIsMultiByteAndCanEnreg(var_types type,
- CORINFO_CLASS_HANDLE typeClass,
- unsigned* typeSize,
- bool forReturn)
+inline bool Compiler::VarTypeIsMultiByteAndCanEnreg(
+ var_types type, CORINFO_CLASS_HANDLE typeClass, unsigned* typeSize, bool forReturn, bool isVarArg)
{
bool result = false;
unsigned size = 0;
else
{
structPassingKind howToPassStruct;
- type = getArgTypeForStruct(typeClass, &howToPassStruct, size);
+ type = getArgTypeForStruct(typeClass, &howToPassStruct, isVarArg, size);
}
if (type != TYP_UNKNOWN)
{
{
var_types hfaType = GetHfaType(argClass); // set to float or double if it is an HFA, otherwise TYP_UNDEF
bool isHfa = (hfaType != TYP_UNDEF);
+#ifndef _TARGET_UNIX_
+ if (info.compIsVarArgs)
+ {
+ // Arm64 Varargs ABI requires passing in general purpose
+ // registers. Force the decision of whether this is an HFA
+ // to false to correctly pass as if it was not an HFA.
+ isHfa = false;
+ }
+#endif // _TARGET_UNIX_
if (!isHfa)
{
// This struct is passed by reference using a single 'slot'
unsigned callerRetTypeSize = 0;
unsigned calleeRetTypeSize = 0;
bool isCallerRetTypMBEnreg =
- VarTypeIsMultiByteAndCanEnreg(callerRetType, callerRetTypeClass, &callerRetTypeSize, true);
+ VarTypeIsMultiByteAndCanEnreg(callerRetType, callerRetTypeClass, &callerRetTypeSize, true, info.compIsVarArgs);
bool isCalleeRetTypMBEnreg =
- VarTypeIsMultiByteAndCanEnreg(calleeRetType, calleeRetTypeClass, &calleeRetTypeSize, true);
+ VarTypeIsMultiByteAndCanEnreg(calleeRetType, calleeRetTypeClass, &calleeRetTypeSize, true, info.compIsVarArgs);
if (varTypeIsIntegral(callerRetType) || isCallerRetTypMBEnreg)
{
if (varTypeIsStruct(lclTyp))
{
lvaSetStruct(tmpNum, lclInfo.lclVerTypeInfo.GetClassHandle(), true /* unsafe value cls check */);
+ if (info.compIsVarArgs)
+ {
+ lvaSetStructUsedAsVarArg(tmpNum);
+ }
}
else
{
#define _TARGET_UNIX_
#endif
+#ifndef _TARGET_UNIX_
+#define _TARGET_WINDOWS_
+#endif // !_TARGET_UNIX_
+
// --------------------------------------------------------------------------------
// IMAGE_FILE_MACHINE_TARGET
// --------------------------------------------------------------------------------
bool isHfaArg = false;
var_types hfaType = TYP_UNDEF;
+#if defined(_TARGET_ARM64_) && defined(_TARGET_UNIX_)
+ // Native varargs on arm64 unix use the regular calling convention.
+ if (!opts.compUseSoftFP)
+#else
// Methods that use VarArg or SoftFP cannot have HFA arguments
if (!info.compIsVarArgs && !opts.compUseSoftFP)
+#endif // defined(_TARGET_ARM64_) && defined(_TARGET_UNIX_)
{
// If the argType is a struct, then check if it is an HFA
if (varTypeIsStruct(argType))
if ((varTypeIsStruct(type)))
{
lvaSetStruct(varNum, typeHnd, typeHnd != nullptr, !tiVerificationNeeded);
+ if (info.compIsVarArgs)
+ {
+ lvaSetStructUsedAsVarArg(varNum);
+ }
}
else
{
#if FEATURE_MULTIREG_STRUCT_PROMOTE
// Is this a variable holding a value with exactly two fields passed in
// multiple registers?
- if ((structPromotionInfo->fieldCnt != 2) && lvaIsMultiregStruct(varDsc))
+ if ((structPromotionInfo->fieldCnt != 2) && lvaIsMultiregStruct(varDsc, this->info.compIsVarArgs))
{
JITDUMP("Not promoting multireg struct local V%02u, because lvIsParam is true and #fields != 2\n", lclNum);
shouldPromote = false;
}
// Returns true if this local var is a multireg struct
-bool Compiler::lvaIsMultiregStruct(LclVarDsc* varDsc)
+bool Compiler::lvaIsMultiregStruct(LclVarDsc* varDsc, bool isVarArg)
{
if (varTypeIsStruct(varDsc->TypeGet()))
{
CORINFO_CLASS_HANDLE clsHnd = varDsc->lvVerTypeInfo.GetClassHandleForValueClass();
structPassingKind howToPassStruct;
- var_types type = getArgTypeForStruct(clsHnd, &howToPassStruct, varDsc->lvExactSize);
+ var_types type = getArgTypeForStruct(clsHnd, &howToPassStruct, isVarArg, varDsc->lvExactSize);
if (howToPassStruct == SPK_ByValueAsHfa)
{
}
//------------------------------------------------------------------------
+// lvaSetStructUsedAsVarArg: update hfa information for vararg struct args
+//
+// Arguments:
+// varNum -- number of the variable
+//
+// Notes:
+// This only affects arm64 varargs on windows where we need to pass
+// hfa arguments as if they are not HFAs.
+//
+// This function should only be called if the struct is used in a varargs
+// method.
+
+void Compiler::lvaSetStructUsedAsVarArg(unsigned varNum)
+{
+#if defined(_TARGET_WINDOWS_) && defined(_TARGET_ARM64_)
+ LclVarDsc* varDsc = &lvaTable[varNum];
+ // For varargs methods incoming and outgoing arguments should not be treated
+ // as HFA.
+ varDsc->_lvIsHfa = false;
+ varDsc->_lvHfaTypeIsFloat = false;
+#endif // defined(_TARGET_WINDOWS_) && defined(_TARGET_ARM64_)
+}
+
+//------------------------------------------------------------------------
// lvaSetClass: set class information for a local var.
//
// Arguments:
regNumber regNum,
unsigned numRegs,
unsigned alignment,
- bool isStruct)
+ bool isStruct,
+ bool isVararg /*=false*/)
{
fgArgTabEntry* curArgTabEntry = new (compiler, CMK_fgArgInfo) fgArgTabEntry;
curArgTabEntry->isBackFilled = false;
curArgTabEntry->isNonStandard = false;
curArgTabEntry->isStruct = isStruct;
+ curArgTabEntry->isVararg = isVararg;
hasRegArgs = true;
AddArg(curArgTabEntry);
unsigned numRegs,
unsigned alignment,
const bool isStruct,
+ const bool isVararg,
const regNumber otherRegNum,
const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* const structDescPtr)
{
- fgArgTabEntry* curArgTabEntry = AddRegArg(argNum, node, parent, regNum, numRegs, alignment, isStruct);
+ fgArgTabEntry* curArgTabEntry = AddRegArg(argNum, node, parent, regNum, numRegs, alignment, isStruct, isVararg);
assert(curArgTabEntry != nullptr);
curArgTabEntry->isStruct = isStruct; // is this a struct arg
{
curArgTabEntry->setRegNum(1, otherRegNum);
}
- curArgTabEntry->isStruct = isStruct; // is this a struct arg
if (isStruct && structDescPtr != nullptr)
{
}
#endif // defined(UNIX_AMD64_ABI)
-fgArgTabEntry* fgArgInfo::AddStkArg(
- unsigned argNum, GenTree* node, GenTree* parent, unsigned numSlots, unsigned alignment, bool isStruct)
+fgArgTabEntry* fgArgInfo::AddStkArg(unsigned argNum,
+ GenTree* node,
+ GenTree* parent,
+ unsigned numSlots,
+ unsigned alignment,
+ bool isStruct,
+ bool isVararg /*=false*/)
{
fgArgTabEntry* curArgTabEntry = new (compiler, CMK_fgArgInfo) fgArgTabEntry;
curArgTabEntry->isBackFilled = false;
curArgTabEntry->isNonStandard = false;
curArgTabEntry->isStruct = isStruct;
+ curArgTabEntry->isVararg = isVararg;
hasStackArgs = true;
AddArg(curArgTabEntry);
// of the evaluation of arguments.
//
// Arguments:
-// tmpVarNum - the var num which we clone into the newly created temp var.
+// curArgTabEntry
//
// Return Value:
// the newly created temp var tree.
bool passedAsPrimitive = false;
if (curArgTabEntry->isSingleRegOrSlot())
{
- CORINFO_CLASS_HANDLE clsHnd = varDsc->lvVerTypeInfo.GetClassHandle();
- var_types structBaseType = getPrimitiveTypeForStruct(lvaLclExactSize(tmpVarNum), clsHnd);
+ CORINFO_CLASS_HANDLE clsHnd = varDsc->lvVerTypeInfo.GetClassHandle();
+ var_types structBaseType =
+ getPrimitiveTypeForStruct(lvaLclExactSize(tmpVarNum), clsHnd, curArgTabEntry->isVararg);
if (structBaseType != TYP_UNKNOWN)
{
#if FEATURE_MULTIREG_ARGS
#ifdef _TARGET_ARM64_
assert(varTypeIsStruct(type));
- if (lvaIsMultiregStruct(varDsc))
+ if (lvaIsMultiregStruct(varDsc, curArgTabEntry->isVararg))
{
// ToDo-ARM64: Consider using: arg->ChangeOper(GT_LCL_FLD);
// as that is how UNIX_AMD64_ABI works.
return arg;
}
+//------------------------------------------------------------------------------
+// EvalArgsToTemps : Create temp assignments and populate the LateArgs list.
+
void fgArgInfo::EvalArgsToTemps()
{
assert(argsSorted == true);
if (curArgTabEntry->needTmp)
{
- unsigned tmpVarNum;
-
if (curArgTabEntry->isTmp == true)
{
// Create a copy of the temp to go into the late argument list
noway_assert(argx->gtType != TYP_STRUCT);
#endif
- tmpVarNum = compiler->lvaGrabTemp(true DEBUGARG("argument with side effect"));
+ unsigned tmpVarNum = compiler->lvaGrabTemp(true DEBUGARG("argument with side effect"));
if (argx->gtOper == GT_MKREFANY)
{
// For GT_MKREFANY, typically the actual struct copying does
CORINFO_CLASS_HANDLE clsHnd = compiler->lvaGetStruct(tmpVarNum);
unsigned structSize = varDsc->lvExactSize;
- scalarType = compiler->getPrimitiveTypeForStruct(structSize, clsHnd);
+ scalarType = compiler->getPrimitiveTypeForStruct(structSize, clsHnd, curArgTabEntry->isVararg);
#endif // _TARGET_ARMARCH_
}
assert(varTypeIsGC(call->gtCallObjp->gtType) || (call->gtCallObjp->gtType == TYP_I_IMPL));
/* this is a register argument - put it in the table */
- call->fgArgInfo->AddRegArg(argIndex, argx, nullptr, genMapIntRegArgNumToRegNum(intArgRegNum), 1, 1,
- false UNIX_AMD64_ABI_ONLY_ARG(REG_STK) UNIX_AMD64_ABI_ONLY_ARG(nullptr));
+ call->fgArgInfo->AddRegArg(argIndex, argx, nullptr, genMapIntRegArgNumToRegNum(intArgRegNum), 1, 1, false,
+ callIsVararg UNIX_AMD64_ABI_ONLY_ARG(REG_STK) UNIX_AMD64_ABI_ONLY_ARG(nullptr));
}
// this can't be a struct.
assert(argx->gtType != TYP_STRUCT);
structSize = originalSize;
structPassingKind howToPassStruct;
- structBaseType = getArgTypeForStruct(objClass, &howToPassStruct, originalSize);
+
+ structBaseType = getArgTypeForStruct(objClass, &howToPassStruct, callIsVararg, originalSize);
#if defined(_TARGET_ARM64_) || defined(UNIX_AMD64_ABI)
// For ARM64 or AMD64/UX we can pass non-power-of-2 structs in a register.
else
{
// This is a register argument - put it in the table
- newArgEntry = call->fgArgInfo->AddRegArg(argIndex, argx, args, nextRegNum, size, argAlign,
- isStructArg UNIX_AMD64_ABI_ONLY_ARG(nextOtherRegNum)
+ newArgEntry = call->fgArgInfo->AddRegArg(argIndex, argx, args, nextRegNum, size, argAlign, isStructArg,
+ callIsVararg UNIX_AMD64_ABI_ONLY_ARG(nextOtherRegNum)
UNIX_AMD64_ABI_ONLY_ARG(&structDesc));
#ifdef FEATURE_HFA
// call fgMorphMultiregStructArg on each of them.
//
// Arguments:
-// call: a GenTreeCall node that has one or more TYP_STRUCT arguments
+// call : a GenTreeCall node that has one or more TYP_STRUCT arguments\
//
// Notes:
// We only call fgMorphMultiregStructArg for struct arguments that are not passed as simple types.
var_types type[MAX_ARG_REG_COUNT] = {}; // TYP_UNDEF = 0
hfaType = GetHfaType(objClass); // set to float or double if it is an HFA, otherwise TYP_UNDEF
- if (varTypeIsFloating(hfaType))
+ if (varTypeIsFloating(hfaType)
+#if !defined(_HOST_UNIX_) && defined(_TARGET_ARM64_)
+ && !fgEntryPtr->isVararg
+#endif // !defined(_HOST_UNIX_) && defined(_TARGET_ARM64_)
+ )
{
elemType = hfaType;
elemSize = genTypeSize(elemType);
#ifndef UNIX_AMD64_ABI
// This local variable must match the layout of the 'objClass' type exactly
- if (varDsc->lvIsHfa())
+ if (varDsc->lvIsHfa()
+#if !defined(_HOST_UNIX_) && defined(_TARGET_ARM64_)
+ && !fgEntryPtr->isVararg
+#endif // !defined(_HOST_UNIX_) && defined(_TARGET_ARM64_)
+ )
{
// We have a HFA struct
noway_assert(elemType == (varDsc->lvHfaTypeIsFloat() ? TYP_FLOAT : TYP_DOUBLE));
#if defined(_TARGET_ARM64_) || defined(UNIX_AMD64_ABI)
// Is this LclVar a promoted struct with exactly 2 fields?
// TODO-ARM64-CQ: Support struct promoted HFA types here
- if (varDsc->lvPromoted && (varDsc->lvFieldCnt == 2) && !varDsc->lvIsHfa())
+ if (varDsc->lvPromoted && (varDsc->lvFieldCnt == 2) && (!varDsc->lvIsHfa()
+#if !defined(_HOST_UNIX_) && defined(_TARGET_ARM64_)
+ && !fgEntryPtr->isVararg
+#endif // !defined(_HOST_UNIX_) && defined(_TARGET_ARM64_)
+ ))
{
// See if we have two promoted fields that start at offset 0 and 8?
unsigned loVarNum = lvaGetFieldLocal(varDsc, 0);
// Here We don't need unsafe value cls check, since the addr of this temp is used only in copyblk.
tmp = lvaGrabTemp(true DEBUGARG("by-value struct argument"));
lvaSetStruct(tmp, copyBlkClass, false);
+ if (call->IsVarargs())
+ {
+ lvaSetStructUsedAsVarArg(tmp);
+ }
+
fgOutgoingArgTemps->setBit(tmp);
}
unsigned typeSize = 0;
// We should have already broken out of the loop if we've set hasMultiByteStackArgs to true.
assert(!hasMultiByteStackArgs);
- hasMultiByteStackArgs = !VarTypeIsMultiByteAndCanEnreg(argx->TypeGet(), objClass, &typeSize, false);
+ hasMultiByteStackArgs =
+ !VarTypeIsMultiByteAndCanEnreg(argx->TypeGet(), objClass, &typeSize, false, false);
#if defined(UNIX_AMD64_ABI)
SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
#if defined(_TARGET_AMD64_)
if (size > REGSIZE_BYTES || (size & (size - 1)) != 0)
#elif defined(_TARGET_ARM64_)
- if ((size > TARGET_POINTER_SIZE) && !lvaIsMultiregStruct(varDsc))
+ if ((size > TARGET_POINTER_SIZE) && !lvaIsMultiregStruct(varDsc, this->info.compIsVarArgs))
#endif
{
// Previously nobody was ever setting lvIsParam and lvIsTemp on the same local
// promoted struct before rewriting this parameter as a pointer.
unsigned newLclNum = lvaGrabTemp(false DEBUGARG("Promoted implicit byref"));
lvaSetStruct(newLclNum, lvaGetStruct(lclNum), true);
+ if (info.compIsVarArgs)
+ {
+ lvaSetStructUsedAsVarArg(newLclNum);
+ }
+
// Update varDsc since lvaGrabTemp might have re-allocated the var dsc array.
varDsc = &lvaTable[lclNum];