Skip to content

Commit a971763

Browse files
authored
JIT: Add support for frozen structs in Swift reverse pinvokes (#100344)
This adds the final support for frozen structs in UCO methods. This passes 2500 auto generated tests locally on both macOS x64 and arm64. This PR includes only 100 tests. Frozen struct parameters are handled via the new ABI representation added in #100138. When such a parameter exists we always allocate space for it on the local stack frame. The struct is then reassembled from its passed constituents as the first thing in the codegen. One complication is that there can be an arbitrary amount of codegen to handle this reassembling. We cannot handle an arbitrary amount of codegen in the prolog, so the reassembling is handled in two places. First, since the amount of register passed data is limited, we handle those in the prolog (which frees them up to be used for other things). If some pieces were passed on the stack the JIT then ensures that there is a scratch BB and generates the code to reassemble the remaining parts as the first thing in the scratch BB. Since Swift structs can be passed by reference in certain cases this PR also enables `FEATURE_IMPLICIT_BYREFS` for SysV x64 to handle those cases. Depending on the TP impact we can refine some of the ifdefs around this.
1 parent 1a7904e commit a971763

14 files changed

+12637
-435
lines changed

src/coreclr/jit/abi.cpp

+152
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@ regNumber ABIPassingSegment::GetRegister() const
4141
return m_register;
4242
}
4343

44+
//-----------------------------------------------------------------------------
45+
// GetRegisterMask:
46+
// Get the mask of registers that this segment is passed in.
47+
//
48+
// Return Value:
49+
// The register mask.
50+
//
51+
regMaskTP ABIPassingSegment::GetRegisterMask() const
52+
{
53+
assert(IsPassedInRegister());
54+
regMaskTP reg = genRegMask(m_register);
55+
56+
#ifdef TARGET_ARM
57+
if (genIsValidFloatReg(m_register) && (Size == 8))
58+
{
59+
reg |= genRegMask(REG_NEXT(m_register));
60+
}
61+
#endif
62+
63+
return reg;
64+
}
65+
4466
//-----------------------------------------------------------------------------
4567
// GetStackOffset:
4668
// Get the stack offset where this segment is passed.
@@ -54,6 +76,53 @@ unsigned ABIPassingSegment::GetStackOffset() const
5476
return m_stackOffset;
5577
}
5678

79+
//-----------------------------------------------------------------------------
80+
// GetRegisterStoreType:
81+
// Return a type that can be used to store from the register this segment is
82+
// in, taking the segment's size into account.
83+
//
84+
// Return Value:
85+
// A type that matches ABIPassingSegment::Size and the register type.
86+
//
87+
var_types ABIPassingSegment::GetRegisterStoreType() const
88+
{
89+
assert(IsPassedInRegister());
90+
if (genIsValidFloatReg(m_register))
91+
{
92+
switch (Size)
93+
{
94+
case 4:
95+
return TYP_FLOAT;
96+
case 8:
97+
return TYP_DOUBLE;
98+
#ifdef FEATURE_SIMD
99+
case 16:
100+
return TYP_SIMD16;
101+
#endif
102+
default:
103+
return TYP_UNDEF;
104+
}
105+
}
106+
else
107+
{
108+
switch (Size)
109+
{
110+
case 1:
111+
return TYP_UBYTE;
112+
case 2:
113+
return TYP_USHORT;
114+
case 4:
115+
return TYP_INT;
116+
#ifdef TARGET_64BIT
117+
case 8:
118+
return TYP_LONG;
119+
#endif
120+
default:
121+
return TYP_UNDEF;
122+
}
123+
}
124+
}
125+
57126
//-----------------------------------------------------------------------------
58127
// InRegister:
59128
// Create an ABIPassingSegment representing that a segment is passed in a
@@ -101,6 +170,56 @@ ABIPassingSegment ABIPassingSegment::OnStack(unsigned stackOffset, unsigned offs
101170
return segment;
102171
}
103172

173+
//-----------------------------------------------------------------------------
174+
// HasAnyRegisterSegment:
175+
// Check if any part of this value is passed in a register.
176+
//
177+
// Return Value:
178+
// True if so.
179+
//
180+
bool ABIPassingInformation::HasAnyRegisterSegment() const
181+
{
182+
for (unsigned i = 0; i < NumSegments; i++)
183+
{
184+
if (Segments[i].IsPassedInRegister())
185+
{
186+
return true;
187+
}
188+
}
189+
return false;
190+
}
191+
192+
//-----------------------------------------------------------------------------
193+
// HasAnyStackSegment:
194+
// Check if any part of this value is passed on the stack.
195+
//
196+
// Return Value:
197+
// True if so.
198+
//
199+
bool ABIPassingInformation::HasAnyStackSegment() const
200+
{
201+
for (unsigned i = 0; i < NumSegments; i++)
202+
{
203+
if (Segments[i].IsPassedOnStack())
204+
{
205+
return true;
206+
}
207+
}
208+
return false;
209+
}
210+
211+
//-----------------------------------------------------------------------------
212+
// HasExactlyOneStackSegment:
213+
// Check if this value is passed as a single stack segment.
214+
//
215+
// Return Value:
216+
// True if so.
217+
//
218+
bool ABIPassingInformation::HasExactlyOneStackSegment() const
219+
{
220+
return (NumSegments == 1) && Segments[0].IsPassedOnStack();
221+
}
222+
104223
//-----------------------------------------------------------------------------
105224
// IsSplitAcrossRegistersAndStack:
106225
// Check if this ABIPassingInformation represents passing a value in both
@@ -253,6 +372,39 @@ ABIPassingInformation SwiftABIClassifier::Classify(Compiler* comp,
253372
TARGET_POINTER_SIZE));
254373
}
255374

375+
if (type == TYP_STRUCT)
376+
{
377+
const CORINFO_SWIFT_LOWERING* lowering = comp->GetSwiftLowering(structLayout->GetClassHandle());
378+
if (lowering->byReference)
379+
{
380+
return m_classifier.Classify(comp, TYP_I_IMPL, nullptr, WellKnownArg::None);
381+
}
382+
383+
ArrayStack<ABIPassingSegment> segments(comp->getAllocator(CMK_ABI));
384+
for (unsigned i = 0; i < lowering->numLoweredElements; i++)
385+
{
386+
var_types elemType = JITtype2varType(lowering->loweredElements[i]);
387+
ABIPassingInformation elemInfo = m_classifier.Classify(comp, elemType, nullptr, WellKnownArg::None);
388+
389+
for (unsigned j = 0; j < elemInfo.NumSegments; j++)
390+
{
391+
ABIPassingSegment newSegment = elemInfo.Segments[j];
392+
newSegment.Offset += lowering->offsets[i];
393+
segments.Push(newSegment);
394+
}
395+
}
396+
397+
ABIPassingInformation result;
398+
result.NumSegments = static_cast<unsigned>(segments.Height());
399+
result.Segments = new (comp, CMK_ABI) ABIPassingSegment[result.NumSegments];
400+
for (int i = 0; i < segments.Height(); i++)
401+
{
402+
result.Segments[i] = segments.Bottom(i);
403+
}
404+
405+
return result;
406+
}
407+
256408
return m_classifier.Classify(comp, type, structLayout, wellKnownParam);
257409
}
258410
#endif

src/coreclr/jit/abi.h

+7
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@ class ABIPassingSegment
2525
// If this segment is passed in a register, return the particular register.
2626
regNumber GetRegister() const;
2727

28+
regMaskTP GetRegisterMask() const;
29+
2830
// If this segment is passed on the stack then return the particular stack
2931
// offset, relative to the first stack argument's offset.
3032
unsigned GetStackOffset() const;
3133

34+
var_types GetRegisterStoreType() const;
35+
3236
static ABIPassingSegment InRegister(regNumber reg, unsigned offset, unsigned size);
3337
static ABIPassingSegment OnStack(unsigned stackOffset, unsigned offset, unsigned size);
3438
};
@@ -47,6 +51,9 @@ struct ABIPassingInformation
4751
unsigned NumSegments = 0;
4852
ABIPassingSegment* Segments = nullptr;
4953

54+
bool HasAnyRegisterSegment() const;
55+
bool HasAnyStackSegment() const;
56+
bool HasExactlyOneStackSegment() const;
5057
bool IsSplitAcrossRegistersAndStack() const;
5158

5259
static ABIPassingInformation FromSegment(Compiler* comp, const ABIPassingSegment& segment);

src/coreclr/jit/codegen.h

+3
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,9 @@ class CodeGen final : public CodeGenInterface
273273
#else
274274
void genEnregisterOSRArgsAndLocals();
275275
#endif
276+
277+
void genHomeSwiftStructParameters(bool handleStack);
278+
276279
void genCheckUseBlockInit();
277280
#if defined(UNIX_AMD64_ABI) && defined(FEATURE_SIMD)
278281
void genClearStackVec3ArgUpperBits();

src/coreclr/jit/codegencommon.cpp

+123-12
Original file line numberDiff line numberDiff line change
@@ -4963,6 +4963,110 @@ void CodeGen::genEnregisterOSRArgsAndLocals()
49634963
}
49644964
}
49654965

4966+
#ifdef SWIFT_SUPPORT
4967+
4968+
//-----------------------------------------------------------------------------
4969+
// genHomeSwiftStructParameters:
4970+
// Reassemble Swift struct parameters if necessary.
4971+
//
4972+
// Parameters:
4973+
// handleStack - If true, reassemble the segments that were passed on the stack.
4974+
// If false, reassemble the segments that were passed in registers.
4975+
//
4976+
void CodeGen::genHomeSwiftStructParameters(bool handleStack)
4977+
{
4978+
for (unsigned lclNum = 0; lclNum < compiler->info.compArgsCount; lclNum++)
4979+
{
4980+
if (lclNum == compiler->lvaSwiftSelfArg)
4981+
{
4982+
continue;
4983+
}
4984+
4985+
LclVarDsc* dsc = compiler->lvaGetDesc(lclNum);
4986+
if ((dsc->TypeGet() != TYP_STRUCT) || compiler->lvaIsImplicitByRefLocal(lclNum) || !dsc->lvOnFrame)
4987+
{
4988+
continue;
4989+
}
4990+
4991+
JITDUMP("Homing Swift parameter V%02u: ", lclNum);
4992+
const ABIPassingInformation& abiInfo = compiler->lvaParameterPassingInfo[lclNum];
4993+
DBEXEC(VERBOSE, abiInfo.Dump());
4994+
4995+
for (unsigned i = 0; i < abiInfo.NumSegments; i++)
4996+
{
4997+
const ABIPassingSegment& seg = abiInfo.Segments[i];
4998+
if (seg.IsPassedOnStack() != handleStack)
4999+
{
5000+
continue;
5001+
}
5002+
5003+
if (seg.IsPassedInRegister())
5004+
{
5005+
RegState* regState = genIsValidFloatReg(seg.GetRegister()) ? &floatRegState : &intRegState;
5006+
regMaskTP regs = seg.GetRegisterMask();
5007+
5008+
if ((regState->rsCalleeRegArgMaskLiveIn & regs) != RBM_NONE)
5009+
{
5010+
var_types storeType = seg.GetRegisterStoreType();
5011+
assert(storeType != TYP_UNDEF);
5012+
GetEmitter()->emitIns_S_R(ins_Store(storeType), emitTypeSize(storeType), seg.GetRegister(), lclNum,
5013+
seg.Offset);
5014+
5015+
regState->rsCalleeRegArgMaskLiveIn &= ~regs;
5016+
}
5017+
}
5018+
else
5019+
{
5020+
var_types loadType = TYP_UNDEF;
5021+
switch (seg.Size)
5022+
{
5023+
case 1:
5024+
loadType = TYP_UBYTE;
5025+
break;
5026+
case 2:
5027+
loadType = TYP_USHORT;
5028+
break;
5029+
case 4:
5030+
loadType = TYP_INT;
5031+
break;
5032+
case 8:
5033+
loadType = TYP_LONG;
5034+
break;
5035+
default:
5036+
assert(!"Unexpected segment size for struct parameter not passed implicitly by ref");
5037+
continue;
5038+
}
5039+
5040+
int offset;
5041+
if (isFramePointerUsed())
5042+
{
5043+
offset = -genCallerSPtoFPdelta();
5044+
}
5045+
else
5046+
{
5047+
offset = -genCallerSPtoInitialSPdelta();
5048+
}
5049+
5050+
offset += (int)seg.GetStackOffset();
5051+
5052+
// Move the incoming segment to the local stack frame. We can
5053+
// use REG_SCRATCH as a temporary register here as we ensured
5054+
// that during LSRA build.
5055+
#ifdef TARGET_XARCH
5056+
GetEmitter()->emitIns_R_AR(ins_Load(loadType), emitTypeSize(loadType), REG_SCRATCH,
5057+
genFramePointerReg(), offset);
5058+
#else
5059+
genInstrWithConstant(ins_Load(loadType), emitTypeSize(loadType), REG_SCRATCH, genFramePointerReg(),
5060+
offset, REG_SCRATCH);
5061+
#endif
5062+
5063+
GetEmitter()->emitIns_S_R(ins_Store(loadType), emitTypeSize(loadType), REG_SCRATCH, lclNum, seg.Offset);
5064+
}
5065+
}
5066+
}
5067+
}
5068+
#endif
5069+
49665070
/*-----------------------------------------------------------------------------
49675071
*
49685072
* Save the generic context argument.
@@ -6133,18 +6237,6 @@ void CodeGen::genFnProlog()
61336237
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SECRET_STUB_PARAM;
61346238
}
61356239

6136-
#ifdef SWIFT_SUPPORT
6137-
if ((compiler->lvaSwiftSelfArg != BAD_VAR_NUM) && ((intRegState.rsCalleeRegArgMaskLiveIn & RBM_SWIFT_SELF) != 0))
6138-
{
6139-
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SWIFT_SELF, compiler->lvaSwiftSelfArg, 0);
6140-
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_SELF;
6141-
}
6142-
else if (compiler->lvaSwiftErrorArg != BAD_VAR_NUM)
6143-
{
6144-
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_ERROR;
6145-
}
6146-
#endif
6147-
61486240
//
61496241
// Zero out the frame as needed
61506242
//
@@ -6236,6 +6328,25 @@ void CodeGen::genFnProlog()
62366328
* Take care of register arguments first
62376329
*/
62386330

6331+
#ifdef SWIFT_SUPPORT
6332+
if (compiler->info.compCallConv == CorInfoCallConvExtension::Swift)
6333+
{
6334+
if ((compiler->lvaSwiftSelfArg != BAD_VAR_NUM) &&
6335+
((intRegState.rsCalleeRegArgMaskLiveIn & RBM_SWIFT_SELF) != 0))
6336+
{
6337+
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SWIFT_SELF, compiler->lvaSwiftSelfArg, 0);
6338+
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_SELF;
6339+
}
6340+
6341+
if (compiler->lvaSwiftErrorArg != BAD_VAR_NUM)
6342+
{
6343+
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_ERROR;
6344+
}
6345+
6346+
genHomeSwiftStructParameters(/* handleStack */ false);
6347+
}
6348+
#endif
6349+
62396350
// Home incoming arguments and generate any required inits.
62406351
// OSR handles this by moving the values from the original frame.
62416352
//

src/coreclr/jit/codegenlinear.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,16 @@ void CodeGen::genCodeForBBlist()
387387
compiler->compCurStmt = nullptr;
388388
compiler->compCurLifeTree = nullptr;
389389

390+
#ifdef SWIFT_SUPPORT
391+
// Reassemble Swift struct parameters on the local stack frame in the
392+
// scratch BB right after the prolog. There can be arbitrary amounts of
393+
// codegen related to doing this, so it cannot be done in the prolog.
394+
if (compiler->fgBBisScratch(block) && compiler->lvaHasAnySwiftStackParamToReassemble())
395+
{
396+
genHomeSwiftStructParameters(/* handleStack */ true);
397+
}
398+
#endif
399+
390400
// Emit poisoning into scratch BB that comes right after prolog.
391401
// We cannot emit this code in the prolog as it might make the prolog too large.
392402
if (compiler->compShouldPoisonFrame() && compiler->fgBBisScratch(block))

0 commit comments

Comments
 (0)