Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: Add support for frozen structs in Swift reverse pinvokes #100344

Merged
merged 56 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
fea5284
JIT: Split ABI classification by ABI
jakobbotsch Mar 25, 2024
150cb28
Refactor, fix arm64
jakobbotsch Mar 25, 2024
af2fc72
Run jit-format
jakobbotsch Mar 25, 2024
1d09c9f
Comments
jakobbotsch Mar 26, 2024
dcccedd
Fix varargs retbuf case
jakobbotsch Mar 26, 2024
7fe878b
Fix SysV
jakobbotsch Mar 26, 2024
9297865
Add function headers
jakobbotsch Mar 26, 2024
25e2c49
Nit
jakobbotsch Mar 26, 2024
9954062
Fix macOS arm64 ABI bug
jakobbotsch Mar 26, 2024
5fab654
Preemptively fix build
jakobbotsch Mar 26, 2024
3f1c738
More build fixes
jakobbotsch Mar 26, 2024
b8d9334
Further fix build
jakobbotsch Mar 26, 2024
f12a9d7
Move classifiers into target specific files
jakobbotsch Mar 26, 2024
70a0aff
Fix x86
jakobbotsch Mar 26, 2024
0e82029
Fix Apple ARM64 ABI for HVAs
jakobbotsch Mar 26, 2024
8cf09c7
Initial work
jakobbotsch Mar 26, 2024
10bbcce
Fix build
jakobbotsch Mar 26, 2024
6d2a284
More fixes
jakobbotsch Mar 26, 2024
90bab28
Check for swift call conv
jakobbotsch Mar 26, 2024
b5586da
More
jakobbotsch Mar 26, 2024
a17cdeb
More fixing
jakobbotsch Mar 26, 2024
6214044
Fix further
jakobbotsch Mar 26, 2024
60eecf1
Fixes
jakobbotsch Mar 27, 2024
10d1f3b
More fixing
jakobbotsch Mar 27, 2024
8db32af
Fix
jakobbotsch Mar 27, 2024
1eab0d9
Fix
jakobbotsch Mar 27, 2024
be0137e
Handle stack and registers separately
jakobbotsch Mar 27, 2024
3faedce
More
jakobbotsch Mar 27, 2024
5b72edd
Enable implicit byrefs on SysV x64
jakobbotsch Mar 27, 2024
27cb1b1
Fix SysV x64 arg asserts
jakobbotsch Mar 27, 2024
a99d809
Fix warning
jakobbotsch Mar 27, 2024
eab21e7
Maybe fix?
jakobbotsch Mar 27, 2024
b015690
Run jit-format
jakobbotsch Mar 27, 2024
bae830c
Generate final tests
jakobbotsch Mar 27, 2024
925ad52
Nits
jakobbotsch Mar 27, 2024
ac6c825
Merge branch 'main' of github.com:dotnet/runtime into swift-reverse-p…
jakobbotsch Mar 27, 2024
3c1c261
Merge branch 'main' of github.com:dotnet/runtime into swift-reverse-p…
jakobbotsch Apr 2, 2024
12058f5
Return some dumping code
jakobbotsch Apr 2, 2024
a901d90
Return code removed by mistake
jakobbotsch Apr 3, 2024
06c4e1b
Fix arm64 varargs bug, small formatting nit
jakobbotsch Apr 2, 2024
c8dd128
Run jit-format
jakobbotsch Apr 3, 2024
3f50708
Refactor to fix TP
jakobbotsch Apr 3, 2024
09c0c1f
Fixes for no refs case
jakobbotsch Apr 3, 2024
80343cd
Fix build
jakobbotsch Apr 3, 2024
ba7da24
Another assert
jakobbotsch Apr 3, 2024
5929425
Maybe that does it
jakobbotsch Apr 3, 2024
0bf62c0
Run jit-format
jakobbotsch Apr 3, 2024
955e5bf
Clean up
jakobbotsch Apr 3, 2024
efc5637
Fix an x64 assert
jakobbotsch Apr 3, 2024
c4874e0
Clean up
jakobbotsch Apr 3, 2024
ee436f6
Straggler
jakobbotsch Apr 3, 2024
700864a
Merge branch 'main' of github.com:dotnet/runtime into swift-reverse-p…
jakobbotsch Apr 4, 2024
ef478a0
Run jit-format
jakobbotsch Apr 4, 2024
fa1860c
Merge branch 'main' of github.com:dotnet/runtime into swift-reverse-p…
jakobbotsch Apr 4, 2024
010552a
Fix bad merge
jakobbotsch Apr 4, 2024
4f272f7
Address feedback
jakobbotsch Apr 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions src/coreclr/jit/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,28 @@ regNumber ABIPassingSegment::GetRegister() const
return m_register;
}

//-----------------------------------------------------------------------------
// GetRegisterMask:
// Get the mask of registers that this segment is passed in.
//
// Return Value:
// The register mask.
//
regMaskTP ABIPassingSegment::GetRegisterMask() const
{
assert(IsPassedInRegister());
regMaskTP reg = genRegMask(m_register);

#ifdef TARGET_ARM
if (genIsValidFloatReg(m_register) && (Size == 8))
{
reg |= genRegMask(REG_NEXT(m_register));
}
#endif

return reg;
}

//-----------------------------------------------------------------------------
// GetStackOffset:
// Get the stack offset where this segment is passed.
Expand All @@ -54,6 +76,53 @@ unsigned ABIPassingSegment::GetStackOffset() const
return m_stackOffset;
}

//-----------------------------------------------------------------------------
// GetRegisterStoreType:
// Return a type that can be used to store from the register this segment is
// in, taking the segment's size into account.
//
// Return Value:
// A type that matches ABIPassingSegment::Size and the register type.
//
var_types ABIPassingSegment::GetRegisterStoreType() const
{
assert(IsPassedInRegister());
if (genIsValidFloatReg(m_register))
{
switch (Size)
{
case 4:
return TYP_FLOAT;
case 8:
return TYP_DOUBLE;
#ifdef FEATURE_SIMD
case 16:
return TYP_SIMD16;
#endif
default:
return TYP_UNDEF;
}
}
else
{
switch (Size)
{
case 1:
return TYP_UBYTE;
case 2:
return TYP_USHORT;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we use unsigned types for these ones?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally normalize up to at least 4 bytes on all loads, and zero extensions sometimes have smaller encodings than sign extensions on xarch (similar change was made in #86631).

case 4:
return TYP_INT;
#ifdef TARGET_64BIT
case 8:
return TYP_LONG;
#endif
default:
return TYP_UNDEF;
}
}
}

//-----------------------------------------------------------------------------
// InRegister:
// Create an ABIPassingSegment representing that a segment is passed in a
Expand Down Expand Up @@ -101,6 +170,56 @@ ABIPassingSegment ABIPassingSegment::OnStack(unsigned stackOffset, unsigned offs
return segment;
}

//-----------------------------------------------------------------------------
// HasAnyRegisterSegment:
// Check if any part of this value is passed in a register.
//
// Return Value:
// True if so.
//
bool ABIPassingInformation::HasAnyRegisterSegment() const
{
for (unsigned i = 0; i < NumSegments; i++)
{
if (Segments[i].IsPassedInRegister())
{
return true;
}
}
return false;
}

//-----------------------------------------------------------------------------
// HasAnyStackSegment:
// Check if any part of this value is passed on the stack.
//
// Return Value:
// True if so.
//
bool ABIPassingInformation::HasAnyStackSegment() const
{
for (unsigned i = 0; i < NumSegments; i++)
{
if (Segments[i].IsPassedOnStack())
{
return true;
}
}
return false;
}

//-----------------------------------------------------------------------------
// HasExactlyOneStackSegment:
// Check if this value is passed as a single stack segment.
//
// Return Value:
// True if so.
//
bool ABIPassingInformation::HasExactlyOneStackSegment() const
{
return (NumSegments == 1) && Segments[0].IsPassedOnStack();
}

//-----------------------------------------------------------------------------
// IsSplitAcrossRegistersAndStack:
// Check if this ABIPassingInformation represents passing a value in both
Expand Down Expand Up @@ -253,6 +372,39 @@ ABIPassingInformation SwiftABIClassifier::Classify(Compiler* comp,
TARGET_POINTER_SIZE));
}

if (type == TYP_STRUCT)
{
const CORINFO_SWIFT_LOWERING* lowering = comp->GetSwiftLowering(structLayout->GetClassHandle());
if (lowering->byReference)
{
return m_classifier.Classify(comp, TYP_I_IMPL, nullptr, WellKnownArg::None);
}

ArrayStack<ABIPassingSegment> segments(comp->getAllocator(CMK_ABI));
for (unsigned i = 0; i < lowering->numLoweredElements; i++)
{
var_types elemType = JITtype2varType(lowering->loweredElements[i]);
ABIPassingInformation elemInfo = m_classifier.Classify(comp, elemType, nullptr, WellKnownArg::None);

for (unsigned j = 0; j < elemInfo.NumSegments; j++)
{
ABIPassingSegment newSegment = elemInfo.Segments[j];
newSegment.Offset += lowering->offsets[i];
segments.Push(newSegment);
}
}

ABIPassingInformation result;
result.NumSegments = static_cast<unsigned>(segments.Height());
result.Segments = new (comp, CMK_ABI) ABIPassingSegment[result.NumSegments];
for (int i = 0; i < segments.Height(); i++)
{
result.Segments[i] = segments.Bottom(i);
}

return result;
}

return m_classifier.Classify(comp, type, structLayout, wellKnownParam);
}
#endif
7 changes: 7 additions & 0 deletions src/coreclr/jit/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ class ABIPassingSegment
// If this segment is passed in a register, return the particular register.
regNumber GetRegister() const;

regMaskTP GetRegisterMask() const;

// If this segment is passed on the stack then return the particular stack
// offset, relative to the first stack argument's offset.
unsigned GetStackOffset() const;

var_types GetRegisterStoreType() const;

static ABIPassingSegment InRegister(regNumber reg, unsigned offset, unsigned size);
static ABIPassingSegment OnStack(unsigned stackOffset, unsigned offset, unsigned size);
};
Expand All @@ -47,6 +51,9 @@ struct ABIPassingInformation
unsigned NumSegments = 0;
ABIPassingSegment* Segments = nullptr;

bool HasAnyRegisterSegment() const;
bool HasAnyStackSegment() const;
bool HasExactlyOneStackSegment() const;
bool IsSplitAcrossRegistersAndStack() const;

static ABIPassingInformation FromSegment(Compiler* comp, const ABIPassingSegment& segment);
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ class CodeGen final : public CodeGenInterface
#else
void genEnregisterOSRArgsAndLocals();
#endif

void genHomeSwiftStructParameters(bool handleStack);

void genCheckUseBlockInit();
#if defined(UNIX_AMD64_ABI) && defined(FEATURE_SIMD)
void genClearStackVec3ArgUpperBits();
Expand Down
135 changes: 123 additions & 12 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4969,6 +4969,110 @@ void CodeGen::genEnregisterOSRArgsAndLocals()
}
}

#ifdef SWIFT_SUPPORT

//-----------------------------------------------------------------------------
// genHomeSwiftStructParameters:
// Reassemble Swift struct parameters if necessary.
//
// Parameters:
// handleStack - If true, reassemble the segments that were passed on the stack.
// If false, reassemble the segments that were passed in registers.
//
void CodeGen::genHomeSwiftStructParameters(bool handleStack)
{
for (unsigned lclNum = 0; lclNum < compiler->info.compArgsCount; lclNum++)
{
if (lclNum == compiler->lvaSwiftSelfArg)
{
continue;
}

LclVarDsc* dsc = compiler->lvaGetDesc(lclNum);
if ((dsc->TypeGet() != TYP_STRUCT) || compiler->lvaIsImplicitByRefLocal(lclNum) || !dsc->lvOnFrame)
{
continue;
}

JITDUMP("Homing Swift parameter V%02u: ", lclNum);
const ABIPassingInformation& abiInfo = compiler->lvaParameterPassingInfo[lclNum];
DBEXEC(VERBOSE, abiInfo.Dump());

for (unsigned i = 0; i < abiInfo.NumSegments; i++)
{
const ABIPassingSegment& seg = abiInfo.Segments[i];
if (seg.IsPassedOnStack() != handleStack)
{
continue;
}

if (seg.IsPassedInRegister())
{
RegState* regState = genIsValidFloatReg(seg.GetRegister()) ? &floatRegState : &intRegState;
regMaskTP regs = seg.GetRegisterMask();

if ((regState->rsCalleeRegArgMaskLiveIn & regs) != RBM_NONE)
{
var_types storeType = seg.GetRegisterStoreType();
assert(storeType != TYP_UNDEF);
GetEmitter()->emitIns_S_R(ins_Store(storeType), emitTypeSize(storeType), seg.GetRegister(), lclNum,
seg.Offset);

regState->rsCalleeRegArgMaskLiveIn &= ~regs;
}
}
else
{
var_types loadType = TYP_UNDEF;
switch (seg.Size)
{
case 1:
loadType = TYP_UBYTE;
break;
case 2:
loadType = TYP_USHORT;
break;
case 4:
loadType = TYP_INT;
break;
case 8:
loadType = TYP_LONG;
break;
default:
assert(!"Unexpected segment size for struct parameter not passed implicitly by ref");
continue;
}

int offset;
if (isFramePointerUsed())
{
offset = -genCallerSPtoFPdelta();
}
else
{
offset = -genCallerSPtoInitialSPdelta();
}

offset += (int)seg.GetStackOffset();

// Move the incoming segment to the local stack frame. We can
// use REG_SCRATCH as a temporary register here as we ensured
// that during LSRA build.
#ifdef TARGET_XARCH
GetEmitter()->emitIns_R_AR(ins_Load(loadType), emitTypeSize(loadType), REG_SCRATCH,
genFramePointerReg(), offset);
#else
genInstrWithConstant(ins_Load(loadType), emitTypeSize(loadType), REG_SCRATCH, genFramePointerReg(),
offset, REG_SCRATCH);
#endif

GetEmitter()->emitIns_S_R(ins_Store(loadType), emitTypeSize(loadType), REG_SCRATCH, lclNum, seg.Offset);
}
}
}
}
#endif

/*-----------------------------------------------------------------------------
*
* Save the generic context argument.
Expand Down Expand Up @@ -6142,18 +6246,6 @@ void CodeGen::genFnProlog()
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SECRET_STUB_PARAM;
}

#ifdef SWIFT_SUPPORT
if ((compiler->lvaSwiftSelfArg != BAD_VAR_NUM) && ((intRegState.rsCalleeRegArgMaskLiveIn & RBM_SWIFT_SELF) != 0))
{
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SWIFT_SELF, compiler->lvaSwiftSelfArg, 0);
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_SELF;
}
else if (compiler->lvaSwiftErrorArg != BAD_VAR_NUM)
{
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_ERROR;
}
#endif

//
// Zero out the frame as needed
//
Expand Down Expand Up @@ -6245,6 +6337,25 @@ void CodeGen::genFnProlog()
* Take care of register arguments first
*/

#ifdef SWIFT_SUPPORT
if (compiler->info.compCallConv == CorInfoCallConvExtension::Swift)
{
if ((compiler->lvaSwiftSelfArg != BAD_VAR_NUM) &&
((intRegState.rsCalleeRegArgMaskLiveIn & RBM_SWIFT_SELF) != 0))
{
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SWIFT_SELF, compiler->lvaSwiftSelfArg, 0);
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_SELF;
}

if (compiler->lvaSwiftErrorArg != BAD_VAR_NUM)
amanasifkhalid marked this conversation as resolved.
Show resolved Hide resolved
{
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_ERROR;
}

genHomeSwiftStructParameters(/* handleStack */ false);
}
#endif

// Home incoming arguments and generate any required inits.
// OSR handles this by moving the values from the original frame.
//
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,16 @@ void CodeGen::genCodeForBBlist()
compiler->compCurStmt = nullptr;
compiler->compCurLifeTree = nullptr;

#ifdef SWIFT_SUPPORT
// Reassemble Swift struct parameters on the local stack frame in the
// scratch BB right after the prolog. There can be arbitrary amounts of
// codegen related to doing this, so it cannot be done in the prolog.
if (compiler->fgBBisScratch(block) && compiler->lvaHasAnySwiftStackParamToReassemble())
{
genHomeSwiftStructParameters(/* handleStack */ true);
}
#endif

// Emit poisoning into scratch BB that comes right after prolog.
// We cannot emit this code in the prolog as it might make the prolog too large.
if (compiler->compShouldPoisonFrame() && compiler->fgBBisScratch(block))
Expand Down
Loading
Loading