Skip to content

Commit 0ea9b8f

Browse files
authored
Support frozen struct returns for Swift calls (#99704)
Adds support for pinvokes to Swift functions that return frozen structs in multiple registers. This turned out to be simpler than I expected on the JIT side; there is a small change necessary in `genMultiRegStoreToLocal` to take into account that the Swift fields are going into offsets that don't necessarily correspond to the register sizes (we already DNER the cases where things don't work out, it seems). Also adds 100 tests. The support is complicated by the fact that Swift calls take the ret buffer in rax on x64. This requires some VM side changes to avoid using rax in the interop thunks.
1 parent 172ecdf commit 0ea9b8f

24 files changed

+5826
-69
lines changed

src/coreclr/jit/codegenarmarch.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3410,7 +3410,7 @@ void CodeGen::genCall(GenTreeCall* call)
34103410
for (unsigned i = 0; i < regCount; ++i)
34113411
{
34123412
var_types regType = pRetTypeDesc->GetReturnRegType(i);
3413-
returnReg = pRetTypeDesc->GetABIReturnReg(i);
3413+
returnReg = pRetTypeDesc->GetABIReturnReg(i, call->GetUnmanagedCallConv());
34143414
regNumber allocatedReg = call->GetRegNumByIdx(i);
34153415
inst_Mov(regType, allocatedReg, returnReg, /* canSkip */ true);
34163416
}
@@ -4828,7 +4828,7 @@ void CodeGen::genSIMDSplitReturn(GenTree* src, ReturnTypeDesc* retTypeDesc)
48284828
for (unsigned i = 0; i < regCount; ++i)
48294829
{
48304830
var_types type = retTypeDesc->GetReturnRegType(i);
4831-
regNumber reg = retTypeDesc->GetABIReturnReg(i);
4831+
regNumber reg = retTypeDesc->GetABIReturnReg(i, compiler->info.compCallConv);
48324832
if (varTypeIsFloating(type))
48334833
{
48344834
// If the register piece is to be passed in a floating point register

src/coreclr/jit/codegencommon.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7770,7 +7770,8 @@ void CodeGen::genReturn(GenTree* treeNode)
77707770
{
77717771
if (varTypeIsGC(retTypeDesc.GetReturnRegType(i)))
77727772
{
7773-
gcInfo.gcMarkRegPtrVal(retTypeDesc.GetABIReturnReg(i), retTypeDesc.GetReturnRegType(i));
7773+
gcInfo.gcMarkRegPtrVal(retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv),
7774+
retTypeDesc.GetReturnRegType(i));
77747775
}
77757776
}
77767777
}
@@ -7787,7 +7788,7 @@ void CodeGen::genReturn(GenTree* treeNode)
77877788
{
77887789
if (varTypeIsGC(retTypeDesc.GetReturnRegType(i)))
77897790
{
7790-
gcInfo.gcMarkRegSetNpt(genRegMask(retTypeDesc.GetABIReturnReg(i)));
7791+
gcInfo.gcMarkRegSetNpt(genRegMask(retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv)));
77917792
}
77927793
}
77937794
}
@@ -7894,23 +7895,23 @@ void CodeGen::genStructReturn(GenTree* treeNode)
78947895
// On LoongArch64, for a struct like "{ int, double }", "retTypeDesc" will be "{ TYP_INT, TYP_DOUBLE }",
78957896
// i. e. not include the padding for the first field, and so the general loop below won't work.
78967897
var_types type = retTypeDesc.GetReturnRegType(0);
7897-
regNumber toReg = retTypeDesc.GetABIReturnReg(0);
7898+
regNumber toReg = retTypeDesc.GetABIReturnReg(0, compiler->info.compCallConv);
78987899
GetEmitter()->emitIns_R_S(ins_Load(type), emitTypeSize(type), toReg, lclNode->GetLclNum(), 0);
78997900
if (regCount > 1)
79007901
{
79017902
assert(regCount == 2);
79027903
int offset = genTypeSize(type);
79037904
type = retTypeDesc.GetReturnRegType(1);
79047905
offset = (int)((unsigned int)offset < genTypeSize(type) ? genTypeSize(type) : offset);
7905-
toReg = retTypeDesc.GetABIReturnReg(1);
7906+
toReg = retTypeDesc.GetABIReturnReg(1, compiler->info.compCallConv);
79067907
GetEmitter()->emitIns_R_S(ins_Load(type), emitTypeSize(type), toReg, lclNode->GetLclNum(), offset);
79077908
}
79087909
#else // !TARGET_LOONGARCH64 && !TARGET_RISCV64
79097910
int offset = 0;
79107911
for (unsigned i = 0; i < regCount; ++i)
79117912
{
79127913
var_types type = retTypeDesc.GetReturnRegType(i);
7913-
regNumber toReg = retTypeDesc.GetABIReturnReg(i);
7914+
regNumber toReg = retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv);
79147915
GetEmitter()->emitIns_R_S(ins_Load(type), emitTypeSize(type), toReg, lclNode->GetLclNum(), offset);
79157916
offset += genTypeSize(type);
79167917
}
@@ -7921,7 +7922,7 @@ void CodeGen::genStructReturn(GenTree* treeNode)
79217922
for (unsigned i = 0; i < regCount; ++i)
79227923
{
79237924
var_types type = retTypeDesc.GetReturnRegType(i);
7924-
regNumber toReg = retTypeDesc.GetABIReturnReg(i);
7925+
regNumber toReg = retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv);
79257926
regNumber fromReg = op1->GetRegByIndex(i);
79267927
if ((fromReg == REG_NA) && op1->OperIs(GT_COPY))
79277928
{
@@ -8045,6 +8046,16 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode)
80458046
assert(regCount == varDsc->lvFieldCnt);
80468047
}
80478048

8049+
#ifdef SWIFT_SUPPORT
8050+
const uint32_t* offsets = nullptr;
8051+
if (op1->IsCall() && (op1->AsCall()->GetUnmanagedCallConv() == CorInfoCallConvExtension::Swift))
8052+
{
8053+
const CORINFO_SWIFT_LOWERING* lowering = compiler->GetSwiftLowering(op1->AsCall()->gtRetClsHnd);
8054+
assert(!lowering->byReference && (regCount == lowering->numLoweredElements));
8055+
offsets = lowering->offsets;
8056+
}
8057+
#endif
8058+
80488059
for (unsigned i = 0; i < regCount; ++i)
80498060
{
80508061
regNumber reg = genConsumeReg(op1, i);
@@ -8087,6 +8098,12 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode)
80878098
#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
80888099
// should consider the padding field within a struct.
80898100
offset = (offset % genTypeSize(srcType)) ? AlignUp(offset, genTypeSize(srcType)) : offset;
8101+
#endif
8102+
#ifdef SWIFT_SUPPORT
8103+
if (offsets != nullptr)
8104+
{
8105+
offset = offsets[i];
8106+
}
80908107
#endif
80918108
// Several fields could be passed in one register, copy using the register type.
80928109
// It could rewrite memory outside of the fields but local on the stack are rounded to POINTER_SIZE so

src/coreclr/jit/codegenloongarch64.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6436,7 +6436,7 @@ void CodeGen::genCall(GenTreeCall* call)
64366436
for (unsigned i = 0; i < regCount; ++i)
64376437
{
64386438
var_types regType = pRetTypeDesc->GetReturnRegType(i);
6439-
returnReg = pRetTypeDesc->GetABIReturnReg(i);
6439+
returnReg = pRetTypeDesc->GetABIReturnReg(i, call->GetUnmanagedCallConv());
64406440
regNumber allocatedReg = call->GetRegNumByIdx(i);
64416441
inst_Mov(regType, allocatedReg, returnReg, /* canSkip */ true);
64426442
}

src/coreclr/jit/codegenriscv64.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6509,7 +6509,7 @@ void CodeGen::genCall(GenTreeCall* call)
65096509
for (unsigned i = 0; i < regCount; ++i)
65106510
{
65116511
var_types regType = pRetTypeDesc->GetReturnRegType(i);
6512-
returnReg = pRetTypeDesc->GetABIReturnReg(i);
6512+
returnReg = pRetTypeDesc->GetABIReturnReg(i, call->GetUnmanagedCallConv());
65136513
regNumber allocatedReg = call->GetRegNumByIdx(i);
65146514
inst_Mov(regType, allocatedReg, returnReg, /* canSkip */ true);
65156515
}

src/coreclr/jit/codegenxarch.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
121121

122122
for (unsigned i = 0; i < regCount; ++i)
123123
{
124-
gcInfo.gcMarkRegPtrVal(retTypeDesc.GetABIReturnReg(i), retTypeDesc.GetReturnRegType(i));
124+
gcInfo.gcMarkRegPtrVal(retTypeDesc.GetABIReturnReg(i, compiler->info.compCallConv),
125+
retTypeDesc.GetReturnRegType(i));
125126
}
126127
}
127128
}
@@ -1318,8 +1319,8 @@ void CodeGen::genSIMDSplitReturn(GenTree* src, ReturnTypeDesc* retTypeDesc)
13181319
// This is a case of operand is in a single reg and needs to be
13191320
// returned in multiple ABI return registers.
13201321
regNumber opReg = src->GetRegNum();
1321-
regNumber reg0 = retTypeDesc->GetABIReturnReg(0);
1322-
regNumber reg1 = retTypeDesc->GetABIReturnReg(1);
1322+
regNumber reg0 = retTypeDesc->GetABIReturnReg(0, compiler->info.compCallConv);
1323+
regNumber reg1 = retTypeDesc->GetABIReturnReg(1, compiler->info.compCallConv);
13231324

13241325
assert((reg0 != REG_NA) && (reg1 != REG_NA) && (opReg != REG_NA));
13251326

@@ -2247,7 +2248,6 @@ void CodeGen::genMultiRegStoreToSIMDLocal(GenTreeLclVar* lclNode)
22472248
// This case is always a call (AsCall() will assert if it is not).
22482249
GenTreeCall* call = actualOp1->AsCall();
22492250
const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc();
2250-
assert(retTypeDesc->GetReturnRegCount() == MAX_RET_REG_COUNT);
22512251

22522252
assert(regCount == 2);
22532253
regNumber targetReg = lclNode->GetRegNum();
@@ -6063,7 +6063,7 @@ void CodeGen::genCall(GenTreeCall* call)
60636063
for (unsigned i = 0; i < regCount; ++i)
60646064
{
60656065
var_types regType = retTypeDesc->GetReturnRegType(i);
6066-
returnReg = retTypeDesc->GetABIReturnReg(i);
6066+
returnReg = retTypeDesc->GetABIReturnReg(i, call->GetUnmanagedCallConv());
60676067
regNumber allocatedReg = call->GetRegNumByIdx(i);
60686068
inst_Mov(regType, allocatedReg, returnReg, /* canSkip */ true);
60696069
}
@@ -6074,7 +6074,7 @@ void CodeGen::genCall(GenTreeCall* call)
60746074
// the native compiler doesn't guarantee it.
60756075
if (call->IsUnmanaged() && (returnType == TYP_SIMD12))
60766076
{
6077-
returnReg = retTypeDesc->GetABIReturnReg(1);
6077+
returnReg = retTypeDesc->GetABIReturnReg(1, call->GetUnmanagedCallConv());
60786078
genSimd12UpperClear(returnReg);
60796079
}
60806080
#endif // FEATURE_SIMD

src/coreclr/jit/compiler.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,42 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
854854
}
855855
assert(structSize > 0);
856856

857+
#ifdef SWIFT_SUPPORT
858+
if (callConv == CorInfoCallConvExtension::Swift)
859+
{
860+
const CORINFO_SWIFT_LOWERING* lowering = GetSwiftLowering(clsHnd);
861+
if (lowering->byReference)
862+
{
863+
howToReturnStruct = SPK_ByReference;
864+
useType = TYP_UNKNOWN;
865+
}
866+
else if (lowering->numLoweredElements == 1)
867+
{
868+
useType = JITtype2varType(lowering->loweredElements[0]);
869+
if (genTypeSize(useType) == structSize)
870+
{
871+
howToReturnStruct = SPK_PrimitiveType;
872+
}
873+
else
874+
{
875+
howToReturnStruct = SPK_EnclosingType;
876+
}
877+
}
878+
else
879+
{
880+
howToReturnStruct = SPK_ByValue;
881+
useType = TYP_STRUCT;
882+
}
883+
884+
if (wbReturnStruct != nullptr)
885+
{
886+
*wbReturnStruct = howToReturnStruct;
887+
}
888+
889+
return useType;
890+
}
891+
#endif
892+
857893
#ifdef UNIX_AMD64_ABI
858894
// An 8-byte struct may need to be returned in a floating point register
859895
// So we always consult the struct "Classifier" routine
@@ -1950,6 +1986,10 @@ void Compiler::compInit(ArenaAllocator* pAlloc,
19501986
fgSsaValid = false;
19511987
fgVNPassesCompleted = 0;
19521988

1989+
#ifdef SWIFT_SUPPORT
1990+
m_swiftLoweringCache = nullptr;
1991+
#endif
1992+
19531993
// check that HelperCallProperties are initialized
19541994

19551995
assert(s_helperCallProperties.IsPure(CORINFO_HELP_GETSHARED_GCSTATIC_BASE));

src/coreclr/jit/compiler.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5504,6 +5504,12 @@ class Compiler
55045504
return m_signatureToLookupInfoMap;
55055505
}
55065506

5507+
#ifdef SWIFT_SUPPORT
5508+
typedef JitHashTable<CORINFO_CLASS_HANDLE, JitPtrKeyFuncs<struct CORINFO_CLASS_STRUCT_>, CORINFO_SWIFT_LOWERING*> SwiftLoweringMap;
5509+
SwiftLoweringMap* m_swiftLoweringCache;
5510+
const CORINFO_SWIFT_LOWERING* GetSwiftLowering(CORINFO_CLASS_HANDLE clsHnd);
5511+
#endif
5512+
55075513
void optRecordLoopMemoryDependence(GenTree* tree, BasicBlock* block, ValueNum memoryVN);
55085514
void optCopyLoopMemoryDependence(GenTree* fromTree, GenTree* toTree);
55095515

src/coreclr/jit/gentree.cpp

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1817,6 +1817,15 @@ regNumber CallArgs::GetCustomRegister(Compiler* comp, CorInfoCallConvExtension c
18171817
}
18181818
}
18191819

1820+
#if defined(TARGET_AMD64) && defined(SWIFT_SUPPORT)
1821+
// TODO-Cleanup: Unify this with hasFixedRetBuffReg. That will
1822+
// likely be necessary for the reverse pinvoke support regardless.
1823+
if (cc == CorInfoCallConvExtension::Swift)
1824+
{
1825+
return REG_SWIFT_ARG_RET_BUFF;
1826+
}
1827+
#endif
1828+
18201829
break;
18211830

18221831
case WellKnownArg::VirtualStubCell:
@@ -26856,6 +26865,14 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp,
2685626865
{
2685726866
assert(varTypeIsStruct(returnType));
2685826867

26868+
#ifdef SWIFT_SUPPORT
26869+
if (callConv == CorInfoCallConvExtension::Swift)
26870+
{
26871+
InitializeSwiftReturnRegs(comp, retClsHnd);
26872+
break;
26873+
}
26874+
#endif
26875+
2685926876
#ifdef UNIX_AMD64_ABI
2686026877

2686126878
SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
@@ -26959,6 +26976,31 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp,
2695926976
#endif
2696026977
}
2696126978

26979+
#ifdef SWIFT_SUPPORT
26980+
//---------------------------------------------------------------------------------------
26981+
// InitializeSwiftReturnRegs:
26982+
// Initialize the Return Type Descriptor for a method that returns with the
26983+
// Swift calling convention.
26984+
//
26985+
// Parameters:
26986+
// comp - Compiler instance
26987+
// clsHnd - Struct type being returned
26988+
//
26989+
void ReturnTypeDesc::InitializeSwiftReturnRegs(Compiler* comp, CORINFO_CLASS_HANDLE clsHnd)
26990+
{
26991+
const CORINFO_SWIFT_LOWERING* lowering = comp->GetSwiftLowering(clsHnd);
26992+
assert(!lowering->byReference);
26993+
26994+
static_assert_no_msg(MAX_SWIFT_LOWERED_ELEMENTS <= MAX_RET_REG_COUNT);
26995+
assert(lowering->numLoweredElements <= MAX_RET_REG_COUNT);
26996+
26997+
for (size_t i = 0; i < lowering->numLoweredElements; i++)
26998+
{
26999+
m_regType[i] = JITtype2varType(lowering->loweredElements[i]);
27000+
}
27001+
}
27002+
#endif
27003+
2696227004
//---------------------------------------------------------------------------------------
2696327005
// InitializeLongReturnType:
2696427006
// Initialize the Return Type Descriptor for a method that returns a TYP_LONG
@@ -27022,8 +27064,9 @@ void ReturnTypeDesc::InitializeReturnType(Compiler* comp,
2702227064
// GetABIReturnReg: Return i'th return register as per target ABI
2702327065
//
2702427066
// Arguments:
27025-
// idx - Index of the return register.
27026-
// The first return register has an index of 0 and so on.
27067+
// idx - Index of the return register.
27068+
// The first return register has an index of 0 and so on.
27069+
// callConv - Associated calling convention
2702727070
//
2702827071
// Return Value:
2702927072
// Returns i'th return register as per target ABI.
@@ -27032,13 +27075,44 @@ void ReturnTypeDesc::InitializeReturnType(Compiler* comp,
2703227075
// x86 and ARM return long in multiple registers.
2703327076
// ARM and ARM64 return HFA struct in multiple registers.
2703427077
//
27035-
regNumber ReturnTypeDesc::GetABIReturnReg(unsigned idx) const
27078+
regNumber ReturnTypeDesc::GetABIReturnReg(unsigned idx, CorInfoCallConvExtension callConv) const
2703627079
{
2703727080
unsigned count = GetReturnRegCount();
2703827081
assert(idx < count);
2703927082

2704027083
regNumber resultReg = REG_NA;
2704127084

27085+
#ifdef SWIFT_SUPPORT
27086+
if (callConv == CorInfoCallConvExtension::Swift)
27087+
{
27088+
static const regNumber swiftIntReturnRegs[] = {REG_SWIFT_INTRET_ORDER};
27089+
static const regNumber swiftFloatReturnRegs[] = {REG_SWIFT_FLOATRET_ORDER};
27090+
assert((idx < ArrLen(swiftIntReturnRegs)) && (idx < ArrLen(swiftFloatReturnRegs)));
27091+
unsigned intRegIdx = 0;
27092+
unsigned floatRegIdx = 0;
27093+
for (unsigned i = 0; i < idx; i++)
27094+
{
27095+
if (varTypeUsesIntReg(GetReturnRegType(i)))
27096+
{
27097+
intRegIdx++;
27098+
}
27099+
else
27100+
{
27101+
floatRegIdx++;
27102+
}
27103+
}
27104+
27105+
if (varTypeUsesIntReg(GetReturnRegType(idx)))
27106+
{
27107+
return swiftIntReturnRegs[intRegIdx];
27108+
}
27109+
else
27110+
{
27111+
return swiftFloatReturnRegs[floatRegIdx];
27112+
}
27113+
}
27114+
#endif
27115+
2704227116
#ifdef UNIX_AMD64_ABI
2704327117
var_types regType0 = GetReturnRegType(0);
2704427118

@@ -27186,7 +27260,7 @@ regNumber ReturnTypeDesc::GetABIReturnReg(unsigned idx) const
2718627260
// GetABIReturnRegs: get the mask of return registers as per target arch ABI.
2718727261
//
2718827262
// Arguments:
27189-
// None
27263+
// callConv - The calling convention
2719027264
//
2719127265
// Return Value:
2719227266
// reg mask of return registers in which the return type is returned.
@@ -27196,14 +27270,14 @@ regNumber ReturnTypeDesc::GetABIReturnReg(unsigned idx) const
2719627270
// of return registers and wants to know the set of return registers.
2719727271
//
2719827272
// static
27199-
regMaskTP ReturnTypeDesc::GetABIReturnRegs() const
27273+
regMaskTP ReturnTypeDesc::GetABIReturnRegs(CorInfoCallConvExtension callConv) const
2720027274
{
2720127275
regMaskTP resultMask = RBM_NONE;
2720227276

2720327277
unsigned count = GetReturnRegCount();
2720427278
for (unsigned i = 0; i < count; ++i)
2720527279
{
27206-
resultMask |= genRegMask(GetABIReturnReg(i));
27280+
resultMask |= genRegMask(GetABIReturnReg(i, callConv));
2720727281
}
2720827282

2720927283
return resultMask;

0 commit comments

Comments
 (0)