Skip to content

Commit 0aa3197

Browse files
committed
JIT: Support bitwise field insertions for call arguments
Add support using bitwise operations to reconstruct registers passed into calls from multiple promoted fields. Remove the IR invariant that `FIELD_LIST` args must always map cleanly to registers; instead, any `FIELD_LIST` is allowed for register-only arguments before lowering, and lowering takes care to normalize them into a handled shape. `fgTryMorphStructArg` is changed to take advantage of this by now producing `FIELD_LIST` even when a promoted arg does not match the target ABI. Support in physical promotion will be added in a follow up. Split arguments are not handled and retain the old IR invariant of requiring registers and stack slots to make cleanly from `FIELD_LIST`. win-x64 examples: ```csharp static void Foo(int x) { Use<int?>(x); Use<int?>(5); Use<int?>(null); } ``` ```diff G_M7200_IG02: ;; offset=0x0004 - mov byte ptr [rsp+0x20], 1 - mov dword ptr [rsp+0x24], ecx - mov rcx, qword ptr [rsp+0x20] + mov ecx, ecx + shl rcx, 32 + or rcx, 1 call [Program:Bar(System.Nullable`1[int])] - mov dword ptr [rsp+0x24], 5 - mov rcx, qword ptr [rsp+0x20] + mov rcx, 0x500000001 call [Program:Bar(System.Nullable`1[int])] - mov byte ptr [rsp+0x20], 0 xor ecx, ecx - mov dword ptr [rsp+0x24], ecx - mov rcx, qword ptr [rsp+0x20] - ;; size=55 bbWeight=1 PerfScore 14.25 + ;; size=34 bbWeight=1 PerfScore 7.50 G_M7200_IG03: add rsp, 40 tail.jmp [Program:Bar(System.Nullable`1[int])] ;; size=10 bbWeight=1 PerfScore 2.25 ``` ```csharp static void Foo(int x, float y) { Use((x, y)); } ``` ```diff G_M42652_IG01: ;; offset=0x0000 - push rax - ;; size=1 bbWeight=1 PerfScore 1.00 + ;; size=0 bbWeight=1 PerfScore 0.00 G_M42652_IG02: - mov dword ptr [rsp], ecx - vmovss dword ptr [rsp+0x04], xmm1 - mov rcx, qword ptr [rsp] + vmovd eax, xmm1 + shl rax, 32 + mov ecx, ecx + or rcx, rax ;; size=13 bbWeight=1 PerfScore 3.00 G_M42652_IG03: - add rsp, 8 tail.jmp [Program:Use[System.ValueTuple`2[int,float]](System.ValueTuple`2[int,float])] ``` A win-arm64 example: ```diff G_M33990_IG01: - stp fp, lr, [sp, #-0x20]! + stp fp, lr, [sp, #-0x10]! mov fp, sp - str xzr, [fp, #0x10] // [V03 tmp2] - ;; size=12 bbWeight=1 PerfScore 2.50 + ;; size=8 bbWeight=1 PerfScore 1.50 G_M33990_IG02: cbz x0, G_M33990_IG04 ;; size=4 bbWeight=1 PerfScore 1.00 G_M33990_IG03: - str x0, [fp, #0x10] // [V07 tmp6] - str wzr, [fp, #0x18] // [V08 tmp7] - ldr w0, [x0, #0x08] - str w0, [fp, #0x1C] // [V09 tmp8] + ldr w1, [x0, #0x08] b G_M33990_IG05 - ;; size=20 bbWeight=0.50 PerfScore 3.50 + ;; size=8 bbWeight=0.50 PerfScore 2.00 G_M33990_IG04: - str xzr, [fp, #0x10] // [V07 tmp6] - str xzr, [fp, #0x18] - ;; size=8 bbWeight=0.50 PerfScore 1.00 + mov x0, xzr + mov w1, wzr + ;; size=8 bbWeight=0.50 PerfScore 0.50 G_M33990_IG05: - ldp x0, x1, [fp, #0x10] // [V03 tmp2], [V03 tmp2+0x08] - movz x2, #0xD920 // code for Program:Use[System.Memory`1[int]](System.Memory`1[int]) - movk x2, #0x4590 LSL #16 + mov w1, w1 + lsl x1, x1, #32 + movz x2, #0xD950 // code for Program:Use[System.Memory`1[int]](System.Memory`1[int]) + movk x2, #0x4592 LSL #16 movk x2, #0x7FFE LSL #32 ldr x2, [x2] - ;; size=20 bbWeight=1 PerfScore 7.50 + ;; size=24 bbWeight=1 PerfScore 6.00 G_M33990_IG06: - ldp fp, lr, [sp], #0x20 + ldp fp, lr, [sp], #0x10 br x2 ;; size=8 bbWeight=1 PerfScore 2.00 -; Total bytes of code: 72 +; Total bytes of code: 60 ```
1 parent 766faec commit 0aa3197

File tree

8 files changed

+236
-133
lines changed

8 files changed

+236
-133
lines changed

src/coreclr/jit/abi.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,29 @@ var_types ABIPassingSegment::GetRegisterType() const
154154
}
155155
}
156156

157+
//-----------------------------------------------------------------------------
158+
// GetRegisterType:
159+
// Return the smallest type larger or equal to Size that most naturally
160+
// represents the register this segment is passed in, taking into account the
161+
// GC info of the specified layout.
162+
//
163+
// Return Value:
164+
// A type that matches ABIPassingSegment::Size and the register.
165+
//
166+
var_types ABIPassingSegment::GetRegisterType(ClassLayout* layout) const
167+
{
168+
if (genIsValidIntReg(GetRegister()))
169+
{
170+
assert(Offset < layout->GetSize());
171+
if (((Offset % TARGET_POINTER_SIZE) == 0) && (Size == TARGET_POINTER_SIZE))
172+
{
173+
return layout->GetGCPtrType(Offset / TARGET_POINTER_SIZE);
174+
}
175+
}
176+
177+
return GetRegisterType();
178+
}
179+
157180
//-----------------------------------------------------------------------------
158181
// InRegister:
159182
// Create an ABIPassingSegment representing that a segment is passed in a

src/coreclr/jit/abi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class ABIPassingSegment
4141
unsigned GetStackSize() const;
4242

4343
var_types GetRegisterType() const;
44+
var_types GetRegisterType(ClassLayout* layout) const;
4445

4546
static ABIPassingSegment InRegister(regNumber reg, unsigned offset, unsigned size);
4647
static ABIPassingSegment OnStack(unsigned stackOffset, unsigned offset, unsigned size);

src/coreclr/jit/codegencommon.cpp

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2895,42 +2895,31 @@ var_types CodeGen::genParamStackType(LclVarDsc* dsc, const ABIPassingSegment& se
28952895
return dsc->TypeGet();
28962896
case TYP_STRUCT:
28972897
{
2898-
if (genIsValidFloatReg(seg.GetRegister()))
2899-
{
2900-
return seg.GetRegisterType();
2901-
}
2902-
2903-
ClassLayout* layout = dsc->GetLayout();
2904-
assert(seg.Offset < layout->GetSize());
2905-
if (((seg.Offset % TARGET_POINTER_SIZE) == 0) && (seg.Size == TARGET_POINTER_SIZE))
2906-
{
2907-
return layout->GetGCPtrType(seg.Offset / TARGET_POINTER_SIZE);
2908-
}
2909-
2898+
var_types type = seg.GetRegisterType(dsc->GetLayout());
29102899
// For the Swift calling convention the enregistered segments do
29112900
// not match the memory layout, so we need to use exact store sizes
29122901
// for the same reason as RISCV64/LA64 below.
29132902
if (compiler->info.compCallConv == CorInfoCallConvExtension::Swift)
29142903
{
2915-
return seg.GetRegisterType();
2904+
return type;
29162905
}
29172906

29182907
#if defined(TARGET_ARM64)
29192908
// We round struct sizes up to TYP_I_IMPL on the stack frame so we
29202909
// can always use the full register size here. This allows us to
29212910
// use stp more often.
2922-
return TYP_I_IMPL;
2911+
return genTypeSize(type) < TARGET_POINTER_SIZE ? TYP_I_IMPL : type;
29232912
#elif defined(TARGET_XARCH)
29242913
// Round up to use smallest possible encoding
2925-
return genActualType(seg.GetRegisterType());
2914+
return genActualType(type);
29262915
#else
29272916
// On other platforms, a safer default is to use the exact size always. For example, for
29282917
// RISC-V/LoongArch structs passed according to floating-point calling convention are enregistered one
29292918
// field per register regardless of the field layout in memory, so the small int load/store instructions
29302919
// must not be upsized to 4 bytes, otherwise for example:
29312920
// * struct { struct{} e1,e2,e3; byte b; float f; } -- 4-byte store for 'b' would trash 'f'
29322921
// * struct { float f; struct{} e1,e2,e3; byte b; } -- 4-byte store for 'b' would trash adjacent stack slot
2933-
return seg.GetRegisterType();
2922+
return type;
29342923
#endif
29352924
}
29362925
default:

src/coreclr/jit/compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11633,6 +11633,7 @@ class Compiler
1163311633
#endif // defined(UNIX_AMD64_ABI)
1163411634

1163511635
bool fgTryMorphStructArg(CallArg* arg);
11636+
bool FieldsMatchAbi(LclVarDsc* varDsc, const ABIPassingInformation& abiInfo);
1163611637

1163711638
bool killGCRefs(GenTree* tree);
1163811639

src/coreclr/jit/gentree.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2725,16 +2725,10 @@ struct GenTreeFieldList : public GenTree
27252725

27262726
class UseList
27272727
{
2728-
Use* m_head;
2729-
Use* m_tail;
2728+
Use* m_head = nullptr;
2729+
Use* m_tail = nullptr;
27302730

27312731
public:
2732-
UseList()
2733-
: m_head(nullptr)
2734-
, m_tail(nullptr)
2735-
{
2736-
}
2737-
27382732
Use* GetHead() const
27392733
{
27402734
return m_head;
@@ -2792,6 +2786,12 @@ struct GenTreeFieldList : public GenTree
27922786
}
27932787
}
27942788

2789+
void Clear()
2790+
{
2791+
m_head = nullptr;
2792+
m_tail = nullptr;
2793+
}
2794+
27952795
bool IsSorted() const
27962796
{
27972797
unsigned offset = 0;

src/coreclr/jit/lower.cpp

Lines changed: 127 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,20 +1725,12 @@ void Lowering::LowerArg(GenTreeCall* call, CallArg* callArg)
17251725
{
17261726
if (abiInfo.HasAnyRegisterSegment())
17271727
{
1728-
#if FEATURE_MULTIREG_ARGS
1729-
if ((abiInfo.NumSegments > 1) && arg->OperIs(GT_FIELD_LIST))
1728+
if (arg->OperIs(GT_FIELD_LIST))
17301729
{
1731-
unsigned int regIndex = 0;
1732-
for (GenTreeFieldList::Use& use : arg->AsFieldList()->Uses())
1733-
{
1734-
const ABIPassingSegment& segment = abiInfo.Segment(regIndex);
1735-
InsertPutArgReg(&use.NodeRef(), segment);
1736-
1737-
regIndex++;
1738-
}
1730+
LowerArgFieldList(callArg, arg->AsFieldList());
1731+
arg = *ppArg;
17391732
}
17401733
else
1741-
#endif // FEATURE_MULTIREG_ARGS
17421734
{
17431735
assert(abiInfo.HasExactlyOneRegisterSegment());
17441736
InsertPutArgReg(ppArg, abiInfo.Segment(0));
@@ -4809,6 +4801,18 @@ void Lowering::LowerRet(GenTreeOp* ret)
48094801
ContainCheckRet(ret);
48104802
}
48114803

4804+
struct LowerFieldListRegisterInfo
4805+
{
4806+
unsigned Offset;
4807+
var_types RegType;
4808+
4809+
LowerFieldListRegisterInfo(unsigned offset, var_types regType)
4810+
: Offset(offset)
4811+
, RegType(regType)
4812+
{
4813+
}
4814+
};
4815+
48124816
//----------------------------------------------------------------------------------------------
48134817
// LowerRetFieldList:
48144818
// Lower a returned FIELD_LIST node.
@@ -4822,21 +4826,18 @@ void Lowering::LowerRetFieldList(GenTreeOp* ret, GenTreeFieldList* fieldList)
48224826
const ReturnTypeDesc& retDesc = comp->compRetTypeDesc;
48234827
unsigned numRegs = retDesc.GetReturnRegCount();
48244828

4825-
bool isCompatible = IsFieldListCompatibleWithReturn(fieldList);
4829+
auto getRegInfo = [=, &retDesc](unsigned regIndex) {
4830+
unsigned offset = retDesc.GetReturnFieldOffset(regIndex);
4831+
var_types regType = genActualType(retDesc.GetReturnRegType(regIndex));
4832+
return LowerFieldListRegisterInfo(offset, regType);
4833+
};
4834+
4835+
bool isCompatible = IsFieldListCompatibleWithRegisters(fieldList, numRegs, getRegInfo);
48264836
if (!isCompatible)
48274837
{
4828-
JITDUMP("Spilling field list [%06u] to stack\n", Compiler::dspTreeID(fieldList));
4829-
unsigned lclNum = comp->lvaGrabTemp(true DEBUGARG("Spilled local for return value"));
4838+
unsigned lclNum =
4839+
StoreFieldListToNewLocal(comp->typGetObjLayout(comp->info.compMethodInfo->args.retTypeClass), fieldList);
48304840
LclVarDsc* varDsc = comp->lvaGetDesc(lclNum);
4831-
comp->lvaSetStruct(lclNum, comp->info.compMethodInfo->args.retTypeClass, false);
4832-
comp->lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::BlockOpRet));
4833-
4834-
for (GenTreeFieldList::Use& use : fieldList->Uses())
4835-
{
4836-
GenTree* store = comp->gtNewStoreLclFldNode(lclNum, use.GetType(), use.GetOffset(), use.GetNode());
4837-
BlockRange().InsertAfter(use.GetNode(), store);
4838-
LowerNode(store);
4839-
}
48404841

48414842
GenTree* retValue = comp->gtNewLclvNode(lclNum, varDsc->TypeGet());
48424843
ret->SetReturnValue(retValue);
@@ -4859,7 +4860,89 @@ void Lowering::LowerRetFieldList(GenTreeOp* ret, GenTreeFieldList* fieldList)
48594860
return;
48604861
}
48614862

4862-
LowerFieldListToFieldListOfRegisters(fieldList);
4863+
LowerFieldListToFieldListOfRegisters(fieldList, numRegs, getRegInfo);
4864+
}
4865+
4866+
//----------------------------------------------------------------------------------------------
4867+
// StoreFieldListToNewLocal:
4868+
// Create a new local with the specified layout and store the specified
4869+
// fields of the specified FIELD_LIST into it.
4870+
//
4871+
// Arguments:
4872+
// layout - Layout of the new local
4873+
// fieldList - Fields to store to it
4874+
//
4875+
// Returns:
4876+
// Var number of new local.
4877+
//
4878+
unsigned Lowering::StoreFieldListToNewLocal(ClassLayout* layout, GenTreeFieldList* fieldList)
4879+
{
4880+
JITDUMP("Spilling field list [%06u] to stack\n", Compiler::dspTreeID(fieldList));
4881+
unsigned lclNum = comp->lvaGrabTemp(true DEBUGARG("Spilled local for field list"));
4882+
LclVarDsc* varDsc = comp->lvaGetDesc(lclNum);
4883+
comp->lvaSetStruct(lclNum, layout, false);
4884+
comp->lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::LocalField));
4885+
4886+
for (GenTreeFieldList::Use& use : fieldList->Uses())
4887+
{
4888+
GenTree* store = comp->gtNewStoreLclFldNode(lclNum, use.GetType(), use.GetOffset(), use.GetNode());
4889+
BlockRange().InsertAfter(use.GetNode(), store);
4890+
LowerNode(store);
4891+
}
4892+
4893+
return lclNum;
4894+
}
4895+
4896+
//----------------------------------------------------------------------------------------------
4897+
// LowerArgFieldList:
4898+
// Lower an argument FIELD_LIST node.
4899+
//
4900+
// Arguments:
4901+
// arg - The argument
4902+
// fieldList - The FIELD_LIST node
4903+
//
4904+
void Lowering::LowerArgFieldList(CallArg* arg, GenTreeFieldList* fieldList)
4905+
{
4906+
assert(!arg->AbiInfo.HasAnyStackSegment());
4907+
4908+
auto getRegInfo = [=](unsigned regIndex) {
4909+
const ABIPassingSegment& seg = arg->AbiInfo.Segment(regIndex);
4910+
return LowerFieldListRegisterInfo(seg.Offset, seg.GetRegisterType());
4911+
};
4912+
4913+
bool isCompatible = IsFieldListCompatibleWithRegisters(fieldList, arg->AbiInfo.NumSegments, getRegInfo);
4914+
if (!isCompatible)
4915+
{
4916+
ClassLayout* layout = comp->typGetObjLayout(arg->GetSignatureClassHandle());
4917+
unsigned lclNum = StoreFieldListToNewLocal(layout, fieldList);
4918+
fieldList->Uses().Clear();
4919+
for (const ABIPassingSegment& seg : arg->AbiInfo.Segments())
4920+
{
4921+
GenTreeLclFld* fld = comp->gtNewLclFldNode(lclNum, seg.GetRegisterType(layout), seg.Offset);
4922+
fieldList->AddFieldLIR(comp, fld, seg.Offset, fld->TypeGet());
4923+
BlockRange().InsertBefore(fieldList, fld);
4924+
}
4925+
}
4926+
else
4927+
{
4928+
LowerFieldListToFieldListOfRegisters(fieldList, arg->AbiInfo.NumSegments, getRegInfo);
4929+
}
4930+
4931+
GenTreeFieldList::Use* field = fieldList->Uses().GetHead();
4932+
for (const ABIPassingSegment& seg : arg->AbiInfo.Segments())
4933+
{
4934+
assert((field != nullptr) && "Ran out of fields while inserting PUTARG_REG");
4935+
InsertPutArgReg(&field->NodeRef(), seg);
4936+
field = field->GetNext();
4937+
}
4938+
4939+
assert((field == nullptr) && "Missed fields while inserting PUTARG_REG");
4940+
4941+
arg->NodeRef() = fieldList->SoleFieldOrThis();
4942+
if (arg->GetNode() != fieldList)
4943+
{
4944+
BlockRange().Remove(fieldList);
4945+
}
48634946
}
48644947

48654948
//----------------------------------------------------------------------------------------------
@@ -4874,21 +4957,23 @@ void Lowering::LowerRetFieldList(GenTreeOp* ret, GenTreeFieldList* fieldList)
48744957
// True if the fields of the FIELD_LIST are all direct insertions into the
48754958
// return registers.
48764959
//
4877-
bool Lowering::IsFieldListCompatibleWithReturn(GenTreeFieldList* fieldList)
4960+
template <typename GetRegisterInfoFunc>
4961+
bool Lowering::IsFieldListCompatibleWithRegisters(GenTreeFieldList* fieldList,
4962+
unsigned numRegs,
4963+
GetRegisterInfoFunc getRegInfo)
48784964
{
4879-
JITDUMP("Checking if field list [%06u] is compatible with return ABI: ", Compiler::dspTreeID(fieldList));
4880-
const ReturnTypeDesc& retDesc = comp->compRetTypeDesc;
4881-
unsigned numRetRegs = retDesc.GetReturnRegCount();
4965+
JITDUMP("Checking if field list [%06u] is compatible with registers: ", Compiler::dspTreeID(fieldList));
48824966

48834967
GenTreeFieldList::Use* use = fieldList->Uses().GetHead();
4884-
for (unsigned i = 0; i < numRetRegs; i++)
4968+
for (unsigned i = 0; i < numRegs; i++)
48854969
{
4886-
unsigned regStart = retDesc.GetReturnFieldOffset(i);
4887-
var_types regType = retDesc.GetReturnRegType(i);
4888-
unsigned regEnd = regStart + genTypeSize(regType);
4970+
LowerFieldListRegisterInfo regInfo = getRegInfo(i);
4971+
unsigned regStart = regInfo.Offset;
4972+
var_types regType = regInfo.RegType;
4973+
unsigned regEnd = regStart + genTypeSize(regType);
48894974

48904975
// TODO-CQ: Could just create a 0 for this.
4891-
if (use == nullptr)
4976+
if ((use == nullptr) || (use->GetOffset() >= regEnd))
48924977
{
48934978
JITDUMP("it is not; register %u has no corresponding field\n", i);
48944979
return false;
@@ -4949,19 +5034,20 @@ bool Lowering::IsFieldListCompatibleWithReturn(GenTreeFieldList* fieldList)
49495034
// Arguments:
49505035
// fieldList - The field list
49515036
//
4952-
void Lowering::LowerFieldListToFieldListOfRegisters(GenTreeFieldList* fieldList)
5037+
template <typename GetRegisterInfoFunc>
5038+
void Lowering::LowerFieldListToFieldListOfRegisters(GenTreeFieldList* fieldList,
5039+
unsigned numRegs,
5040+
GetRegisterInfoFunc getRegInfo)
49535041
{
4954-
const ReturnTypeDesc& retDesc = comp->compRetTypeDesc;
4955-
unsigned numRegs = retDesc.GetReturnRegCount();
4956-
49575042
GenTreeFieldList::Use* use = fieldList->Uses().GetHead();
49585043
assert(fieldList->Uses().IsSorted());
49595044

49605045
for (unsigned i = 0; i < numRegs; i++)
49615046
{
4962-
unsigned regStart = retDesc.GetReturnFieldOffset(i);
4963-
var_types regType = genActualType(retDesc.GetReturnRegType(i));
4964-
unsigned regEnd = regStart + genTypeSize(regType);
5047+
LowerFieldListRegisterInfo regInfo = getRegInfo(i);
5048+
unsigned regStart = regInfo.Offset;
5049+
var_types regType = regInfo.RegType;
5050+
unsigned regEnd = regStart + genTypeSize(regType);
49655051

49665052
GenTreeFieldList::Use* regEntry = use;
49675053

@@ -5001,7 +5087,7 @@ void Lowering::LowerFieldListToFieldListOfRegisters(GenTreeFieldList* fieldList)
50015087
}
50025088

50035089
// If this is a float -> int insertion, then we need the bitcast now.
5004-
if (varTypeUsesFloatReg(value) && varTypeUsesIntReg(regType))
5090+
if (varTypeUsesFloatReg(value) && varTypeUsesIntReg(regInfo.RegType))
50055091
{
50065092
assert((genTypeSize(value) == 4) || (genTypeSize(value) == 8));
50075093
var_types castType = genTypeSize(value) == 4 ? TYP_INT : TYP_LONG;

src/coreclr/jit/lower.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,14 @@ class Lowering final : public Phase
187187
GenTree* LowerAsyncContinuation(GenTree* asyncCont);
188188
void LowerReturnSuspend(GenTree* retSuspend);
189189
void LowerRetFieldList(GenTreeOp* ret, GenTreeFieldList* fieldList);
190-
bool IsFieldListCompatibleWithReturn(GenTreeFieldList* fieldList);
191-
void LowerFieldListToFieldListOfRegisters(GenTreeFieldList* fieldList);
192-
void LowerCallStruct(GenTreeCall* call);
193-
void LowerStoreSingleRegCallStruct(GenTreeBlk* store);
190+
unsigned StoreFieldListToNewLocal(ClassLayout* layout, GenTreeFieldList* fieldList);
191+
void LowerArgFieldList(CallArg* arg, GenTreeFieldList* fieldList);
192+
template <typename GetRegisterInfoFunc>
193+
bool IsFieldListCompatibleWithRegisters(GenTreeFieldList* fieldList, unsigned numRegs, GetRegisterInfoFunc func);
194+
template <typename GetRegisterInfoFunc>
195+
void LowerFieldListToFieldListOfRegisters(GenTreeFieldList* fieldList, unsigned numRegs, GetRegisterInfoFunc func);
196+
void LowerCallStruct(GenTreeCall* call);
197+
void LowerStoreSingleRegCallStruct(GenTreeBlk* store);
194198
#if !defined(WINDOWS_AMD64_ABI)
195199
GenTreeLclVar* SpillStructCallResult(GenTreeCall* call) const;
196200
#endif // WINDOWS_AMD64_ABI

0 commit comments

Comments
 (0)