Skip to content
3 changes: 3 additions & 0 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,7 @@ CallArgs::CallArgs()
, m_hasRetBuffer(false)
, m_isVarArgs(false)
, m_abiInformationDetermined(false)
, m_newAbiInformationDetermined(false)
, m_hasRegArgs(false)
, m_hasStackArgs(false)
, m_argsComplete(false)
Expand Down Expand Up @@ -2623,6 +2624,8 @@ bool GenTreeCall::Equals(GenTreeCall* c1, GenTreeCall* c2)
//
void CallArgs::ResetFinalArgsAndABIInfo()
{
m_newAbiInformationDetermined = false;

if (!IsAbiInformationDetermined())
{
return;
Expand Down
23 changes: 17 additions & 6 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -4808,10 +4808,11 @@ class CallArgs
// made for this call.
unsigned m_padStkAlign;
#endif
bool m_hasThisPointer : 1;
bool m_hasRetBuffer : 1;
bool m_isVarArgs : 1;
bool m_abiInformationDetermined : 1;
bool m_hasThisPointer : 1;
bool m_hasRetBuffer : 1;
bool m_isVarArgs : 1;
bool m_abiInformationDetermined : 1;
bool m_newAbiInformationDetermined : 1;
// True if we have one or more register arguments.
bool m_hasRegArgs : 1;
// True if we have one or more stack arguments.
Expand Down Expand Up @@ -4869,8 +4870,10 @@ class CallArgs
PushFront(comp, arg);
}

void ResetFinalArgsAndABIInfo();
void AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call);
void ResetFinalArgsAndABIInfo();

void DetermineNewABIInfo(Compiler* comp, GenTreeCall* call);

void ArgsComplete(Compiler* comp, GenTreeCall* call);
void EvalArgsToTemps(Compiler* comp, GenTreeCall* call);
Expand All @@ -4886,7 +4889,15 @@ class CallArgs
void SetIsVarArgs() { m_isVarArgs = true; }
void ClearIsVarArgs() { m_isVarArgs = false; }
bool IsAbiInformationDetermined() const { return m_abiInformationDetermined; }
bool AreArgsComplete() const { return m_argsComplete; }
bool IsNewAbiInformationDetermined() const { return m_newAbiInformationDetermined; }

// TODO-Remove: Workaround for bad codegen in MSVC versions < 19.41, see
// https://github.com/dotnet/runtime/pull/104370#issuecomment-2222910359
#ifdef _MSC_VER
__declspec(noinline)
#endif
bool AreArgsComplete() const { return m_argsComplete; }

bool HasRegArgs() const { return m_hasRegArgs; }
bool HasStackArgs() const { return m_hasStackArgs; }
bool NeedsTemps() const { return m_needsTemps; }
Expand Down
134 changes: 105 additions & 29 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1087,8 +1087,9 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call)
// In "fgMorphMultiRegStructArg" we will expand the arg into a GT_FIELD_LIST with multiple indirections, so
// here we consider spilling it into a local. We also need to spill it in case we have a node that we do not
// currently handle in multi-reg morphing.
// This logic can be skipped when the arg is already in the right multireg arg shape.
//
if (varTypeIsStruct(argx) && !arg.m_needTmp)
if (varTypeIsStruct(argx) && !arg.m_needTmp && !argx->OperIs(GT_FIELD_LIST))
{
if ((arg.AbiInfo.NumRegs > 0) && ((arg.AbiInfo.NumRegs + arg.AbiInfo.GetStackSlotsNumber()) > 1))
{
Expand Down Expand Up @@ -1650,36 +1651,64 @@ void CallArgs::EvalArgsToTemps(Compiler* comp, GenTreeCall* call)
noway_assert(argx->gtType != TYP_STRUCT);
#endif

unsigned tmpVarNum = comp->lvaGrabTemp(true DEBUGARG("argument with side effect"));

setupArg = comp->gtNewTempStore(tmpVarNum, argx);

LclVarDsc* varDsc = comp->lvaGetDesc(tmpVarNum);
var_types lclVarType = genActualType(argx->gtType);
var_types scalarType = TYP_UNKNOWN;

if (setupArg->OperIsCopyBlkOp())
if (argx->OperIs(GT_FIELD_LIST))
{
setupArg = comp->fgMorphCopyBlock(setupArg);
#if defined(TARGET_ARMARCH) || defined(UNIX_AMD64_ABI) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
if ((lclVarType == TYP_STRUCT) && (arg.AbiInfo.ArgType != TYP_STRUCT))
GenTreeFieldList* fieldList = argx->AsFieldList();
fieldList->gtFlags &= ~GTF_ALL_EFFECT;
for (GenTreeFieldList::Use& use : fieldList->Uses())
{
scalarType = arg.AbiInfo.ArgType;
unsigned tmpVarNum = comp->lvaGrabTemp(true DEBUGARG("argument with side effect"));
GenTree* store = comp->gtNewTempStore(tmpVarNum, use.GetNode());

if (setupArg == nullptr)
{
setupArg = store;
}
else
{
setupArg = comp->gtNewOperNode(GT_COMMA, TYP_VOID, setupArg, store);
}

use.SetNode(comp->gtNewLclvNode(tmpVarNum, genActualType(use.GetNode())));
fieldList->AddAllEffectsFlags(use.GetNode());
}
#endif // TARGET_ARMARCH || defined (UNIX_AMD64_ABI) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
}

// scalarType can be set to a wider type for ARM or unix amd64 architectures: (3 => 4) or (5,6,7 =>
// 8)
if ((scalarType != TYP_UNKNOWN) && (scalarType != lclVarType))
{
// Create a GT_LCL_FLD using the wider type to go to the late argument list
defArg = comp->gtNewLclFldNode(tmpVarNum, scalarType, 0);
// Keep the field list in the late list
defArg = fieldList;
}
else
{
// Create a copy of the temp to go to the late argument list
defArg = comp->gtNewLclvNode(tmpVarNum, lclVarType);
unsigned tmpVarNum = comp->lvaGrabTemp(true DEBUGARG("argument with side effect"));

setupArg = comp->gtNewTempStore(tmpVarNum, argx);

LclVarDsc* varDsc = comp->lvaGetDesc(tmpVarNum);
var_types lclVarType = genActualType(argx->gtType);
var_types scalarType = TYP_UNKNOWN;

if (setupArg->OperIsCopyBlkOp())
{
setupArg = comp->fgMorphCopyBlock(setupArg);
#if defined(TARGET_ARMARCH) || defined(UNIX_AMD64_ABI) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
if ((lclVarType == TYP_STRUCT) && (arg.AbiInfo.ArgType != TYP_STRUCT))
{
scalarType = arg.AbiInfo.ArgType;
}
#endif // TARGET_ARMARCH || defined (UNIX_AMD64_ABI) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
}

// scalarType can be set to a wider type for ARM or unix amd64 architectures: (3 => 4) or (5,6,7 =>
// 8)
if ((scalarType != TYP_UNKNOWN) && (scalarType != lclVarType))
{
// Create a GT_LCL_FLD using the wider type to go to the late argument list
defArg = comp->gtNewLclFldNode(tmpVarNum, scalarType, 0);
}
else
{
// Create a copy of the temp to go to the late argument list
defArg = comp->gtNewLclvNode(tmpVarNum, lclVarType);
}
}

#ifdef DEBUG
Expand Down Expand Up @@ -2270,7 +2299,54 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
}
#endif

m_abiInformationDetermined = true;
m_abiInformationDetermined = true;
m_newAbiInformationDetermined = true;
}

//------------------------------------------------------------------------
// DetermineNewABIInfo:
// Determine the new ABI info for all call args without making any IR
// changes.
//
// Parameters:
// comp - The compiler object.
// call - The call to which the CallArgs belongs.
//
void CallArgs::DetermineNewABIInfo(Compiler* comp, GenTreeCall* call)
{
ClassifierInfo info;
info.CallConv = call->GetUnmanagedCallConv();
// X86 tailcall helper is considered varargs, but not for ABI classification purposes.
info.IsVarArgs = call->IsVarargs() && !call->IsTailCallViaJitHelper();
info.HasThis = call->gtArgs.HasThisPointer();
info.HasRetBuff = call->gtArgs.HasRetBuffer();
PlatformClassifier classifier(info);

for (CallArg& arg : Args())
{
const var_types argSigType = arg.GetSignatureType();
const CORINFO_CLASS_HANDLE argSigClass = arg.GetSignatureClassHandle();
ClassLayout* argLayout = argSigClass == NO_CLASS_HANDLE ? nullptr : comp->typGetObjLayout(argSigClass);

// Some well known args have custom register assignment.
// These should not affect the placement of any other args or stack space required.
// Example: on AMD64 R10 and R11 are used for indirect VSD (generic interface) and cookie calls.
// TODO-Cleanup: Integrate this into the new style ABI classifiers.
regNumber nonStdRegNum = GetCustomRegister(comp, call->GetUnmanagedCallConv(), arg.GetWellKnownArg());

if (nonStdRegNum == REG_NA)
{
arg.NewAbiInfo = classifier.Classify(comp, argSigType, argLayout, arg.GetWellKnownArg());
}
else
{
ABIPassingSegment segment = ABIPassingSegment::InRegister(nonStdRegNum, 0, TARGET_POINTER_SIZE);
arg.NewAbiInfo = ABIPassingInformation::FromSegment(comp, segment);
}
}

m_argsStackSize = classifier.StackSize();
m_newAbiInformationDetermined = true;
}

//------------------------------------------------------------------------
Expand Down Expand Up @@ -2436,10 +2512,10 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
GenTree* argObj = argx->gtEffectiveVal();
bool makeOutArgCopy = false;

if (isStructArg && !reMorphing)
if (isStructArg && !reMorphing && !argObj->OperIs(GT_FIELD_LIST))
{
unsigned originalSize;
if (argObj->TypeGet() == TYP_STRUCT)
if (argObj->TypeIs(TYP_STRUCT))
{
assert(argObj->OperIs(GT_BLK, GT_LCL_VAR, GT_LCL_FLD));
originalSize = argObj->GetLayout(this)->GetSize();
Expand Down Expand Up @@ -2763,12 +2839,12 @@ void Compiler::fgMorphMultiregStructArgs(GenTreeCall* call)
{
if ((arg.AbiInfo.ArgType == TYP_STRUCT) && !arg.AbiInfo.PassedByRef)
{
foundStructArg = true;
GenTree*& argx = (arg.GetLateNode() != nullptr) ? arg.LateNodeRef() : arg.EarlyNodeRef();

if (!argx->OperIs(GT_FIELD_LIST))
{
argx = fgMorphMultiregStructArg(&arg);
foundStructArg = true;
argx = fgMorphMultiregStructArg(&arg);
}
}
}
Expand Down
Loading