Skip to content

Commit a9fa8c9

Browse files
Merge pull request #14514 from aschwaighofer/wip_closure_capture_abi_part2
WIP: Closure ABI - Make @NoEscape Swift closures trivial
2 parents 5a1a34b + 0887224 commit a9fa8c9

File tree

82 files changed

+1138
-482
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+1138
-482
lines changed

include/swift/AST/Types.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3843,6 +3843,12 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
38433843
return getExtInfo().isNoEscape();
38443844
}
38453845

3846+
/// Thick swift noescape function types are trivial.
3847+
bool isTrivialNoEscape() const {
3848+
return isNoEscape() &&
3849+
getRepresentation() == SILFunctionTypeRepresentation::Thick;
3850+
}
3851+
38463852
bool isNoReturnFunction(); // Defined in SILType.cpp
38473853

38483854
class ABICompatibilityCheckResult {
@@ -3851,6 +3857,7 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
38513857
enum innerty {
38523858
None,
38533859
DifferentFunctionRepresentations,
3860+
ABIEscapeToNoEscapeConversion,
38543861
DifferentNumberOfResults,
38553862
DifferentReturnValueConventions,
38563863
ABIIncompatibleReturnValues,
@@ -3870,6 +3877,10 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
38703877
ABICompatibilityCheckResult() = delete;
38713878

38723879
bool isCompatible() const { return kind == innerty::None; }
3880+
bool isCompatibleUpToNoEscapeConversion() {
3881+
return kind == innerty::None ||
3882+
kind == innerty::ABIEscapeToNoEscapeConversion;
3883+
}
38733884

38743885
bool hasPayload() const { return payload.hasValue(); }
38753886
uintptr_t getPayload() const { return payload.getValue(); }

include/swift/SIL/InstructionUtils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ bool isIncidentalUse(SILInstruction *user);
102102
/// only used in recognizable patterns without otherwise "escaping".
103103
bool onlyAffectsRefCount(SILInstruction *user);
104104

105-
/// If V is a convert_function, return its operand recursively.
105+
/// If V is a convert_function or convert_escape_to_noescape return its operand
106+
/// recursively.
106107
SILValue stripConvertFunctions(SILValue V);
107108

108109
/// Given an address accessed by an instruction that reads or modifies

include/swift/SIL/SILInstruction.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3830,7 +3830,13 @@ class ConvertFunctionInst final
38303830
ConvertFunctionInst(SILDebugLocation DebugLoc, SILValue Operand,
38313831
ArrayRef<SILValue> TypeDependentOperands, SILType Ty)
38323832
: UnaryInstructionWithTypeDependentOperandsBase(
3833-
DebugLoc, Operand, TypeDependentOperands, Ty) {}
3833+
DebugLoc, Operand, TypeDependentOperands, Ty) {
3834+
assert((Operand->getType().castTo<SILFunctionType>()->isNoEscape() ==
3835+
Ty.castTo<SILFunctionType>()->isNoEscape() ||
3836+
Ty.castTo<SILFunctionType>()->getRepresentation() !=
3837+
SILFunctionType::Representation::Thick) &&
3838+
"Change of escapeness is not ABI compatible");
3839+
}
38343840

38353841
static ConvertFunctionInst *
38363842
create(SILDebugLocation DebugLoc, SILValue Operand, SILType Ty,

include/swift/SILOptimizer/Analysis/CFG.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class SILBasicBlock;
3232
///
3333
/// 1. Has a return terminator.
3434
/// 2. unreachable + noreturn terminator sequence.
35+
/// 3. has a throw terminator.
3536
///
3637
/// If we just have an unreachable without a noreturn call before it, we must
3738
/// have a failure BB.

lib/IRGen/GenCall.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,6 +1919,8 @@ void CallEmission::setFromCallee() {
19191919

19201920
// Fill in the context pointer if necessary.
19211921
if (!contextPtr) {
1922+
assert(!CurCallee.getOrigFunctionType()->isNoEscape() &&
1923+
"Missing context?");
19221924
contextPtr = llvm::UndefValue::get(IGF.IGM.RefCountedPtrTy);
19231925
}
19241926
}
@@ -3369,14 +3371,17 @@ Callee irgen::getBlockPointerCallee(IRGenFunction &IGF,
33693371
return Callee(std::move(info), fn, blockPtr);
33703372
}
33713373

3372-
Callee irgen::getSwiftFunctionPointerCallee(IRGenFunction &IGF,
3373-
llvm::Value *fnPtr,
3374-
llvm::Value *dataPtr,
3375-
CalleeInfo &&calleeInfo) {
3374+
Callee irgen::getSwiftFunctionPointerCallee(
3375+
IRGenFunction &IGF, llvm::Value *fnPtr, llvm::Value *dataPtr,
3376+
CalleeInfo &&calleeInfo, bool castOpaqueToRefcountedContext) {
33763377
auto sig = emitCastOfFunctionPointer(IGF, fnPtr, calleeInfo.OrigFnType);
33773378

33783379
FunctionPointer fn(fnPtr, sig);
3379-
3380+
if (castOpaqueToRefcountedContext) {
3381+
assert(dataPtr && dataPtr->getType() == IGF.IGM.OpaquePtrTy &&
3382+
"Expecting trivial closure context");
3383+
dataPtr = IGF.Builder.CreateBitCast(dataPtr, IGF.IGM.RefCountedPtrTy);
3384+
}
33803385
return Callee(std::move(calleeInfo), fn, dataPtr);
33813386
}
33823387

lib/IRGen/GenCall.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ namespace irgen {
120120
Callee getSwiftFunctionPointerCallee(IRGenFunction &IGF,
121121
llvm::Value *fnPtr,
122122
llvm::Value *contextPtr,
123-
CalleeInfo &&info);
123+
CalleeInfo &&info,
124+
bool castOpaqueToRefcountedContext);
124125

125126
Address emitAllocYieldOnceCoroutineBuffer(IRGenFunction &IGF);
126127
void emitDeallocYieldOnceCoroutineBuffer(IRGenFunction &IGF, Address buffer);

lib/IRGen/GenFunc.cpp

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -578,12 +578,16 @@ const TypeInfo *TypeConverter::convertFunctionType(SILFunctionType *T) {
578578
spareBits.append(IGM.getFunctionPointerSpareBits());
579579
spareBits.append(IGM.getHeapObjectSpareBits());
580580

581-
return FuncTypeInfo::create(CanSILFunctionType(T),
582-
IGM.FunctionPairTy,
583-
IGM.getPointerSize() * 2,
584-
IGM.getPointerAlignment(),
585-
std::move(spareBits),
586-
IsNotPOD);
581+
if (T->isNoEscape()) {
582+
// @noescape thick functions are trivial types.
583+
return FuncTypeInfo::create(
584+
CanSILFunctionType(T), IGM.NoEscapeFunctionPairTy,
585+
IGM.getPointerSize() * 2, IGM.getPointerAlignment(),
586+
std::move(spareBits), IsPOD);
587+
}
588+
return FuncTypeInfo::create(
589+
CanSILFunctionType(T), IGM.FunctionPairTy, IGM.getPointerSize() * 2,
590+
IGM.getPointerAlignment(), std::move(spareBits), IsNotPOD);
587591
}
588592
}
589593
llvm_unreachable("bad function type representation");
@@ -1171,8 +1175,10 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM,
11711175
// If the parameters can live independent of the context, release it now
11721176
// so we can tail call. The safety of this assumes that neither this release
11731177
// nor any of the loads can throw.
1174-
if (consumesContext && !dependsOnContextLifetime && rawData)
1178+
if (consumesContext && !dependsOnContextLifetime && rawData) {
1179+
assert(!outType->isNoEscape() && "Trivial context must not be released");
11751180
subIGF.emitNativeStrongRelease(rawData, subIGF.getDefaultAtomicity());
1181+
}
11761182

11771183
// Now that we have bound generic parameters from the captured arguments
11781184
// emit the polymorphic arguments.
@@ -1272,8 +1278,10 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM,
12721278
}
12731279

12741280
// If the parameters depended on the context, consume the context now.
1275-
if (rawData && consumesContext && dependsOnContextLifetime)
1281+
if (rawData && consumesContext && dependsOnContextLifetime) {
1282+
assert(!outType->isNoEscape() && "Trivial context must not be released");
12761283
subIGF.emitNativeStrongRelease(rawData, subIGF.getDefaultAtomicity());
1284+
}
12771285

12781286
// Reabstract the result value as substituted.
12791287
SILFunctionConventions origConv(origType, IGM.getSILModule());
@@ -1337,6 +1345,8 @@ void irgen::emitFunctionPartialApplication(
13371345
SmallVector<SILType, 4> argValTypes;
13381346
SmallVector<ParameterConvention, 4> argConventions;
13391347

1348+
bool isNoEscapeFunction = outType->isNoEscape();
1349+
13401350
// Reserve space for polymorphic bindings.
13411351
SubstitutionMap subMap;
13421352
if (auto genericSig = origType->getGenericSignature())
@@ -1465,7 +1475,10 @@ void irgen::emitFunctionPartialApplication(
14651475
fnPtr = IGF.Builder.CreateBitCast(fnPtr, IGF.IGM.Int8PtrTy);
14661476
out.add(fnPtr);
14671477
llvm::Value *ctx = args.claimNext();
1468-
ctx = IGF.Builder.CreateBitCast(ctx, IGF.IGM.RefCountedPtrTy);
1478+
if (isNoEscapeFunction)
1479+
ctx = IGF.Builder.CreateBitCast(ctx, IGF.IGM.OpaquePtrTy);
1480+
else
1481+
ctx = IGF.Builder.CreateBitCast(ctx, IGF.IGM.RefCountedPtrTy);
14691482
out.add(ctx);
14701483
return;
14711484
}
@@ -1506,7 +1519,10 @@ void irgen::emitFunctionPartialApplication(
15061519
llvm::Value *ctx = args.claimNext();
15071520
if (isIndirectFormalParameter(*singleRefcountedConvention))
15081521
ctx = IGF.Builder.CreateLoad(ctx, IGF.IGM.getPointerAlignment());
1509-
ctx = IGF.Builder.CreateBitCast(ctx, IGF.IGM.RefCountedPtrTy);
1522+
if (isNoEscapeFunction)
1523+
ctx = IGF.Builder.CreateBitCast(ctx, IGF.IGM.OpaquePtrTy);
1524+
else
1525+
ctx = IGF.Builder.CreateBitCast(ctx, IGF.IGM.RefCountedPtrTy);
15101526
out.add(ctx);
15111527
return;
15121528
}
@@ -1597,6 +1613,8 @@ void irgen::emitFunctionPartialApplication(
15971613
argConventions);
15981614
forwarder = IGF.Builder.CreateBitCast(forwarder, IGF.IGM.Int8PtrTy);
15991615
out.add(forwarder);
1616+
if (isNoEscapeFunction)
1617+
data = IGF.Builder.CreateBitCast(data, IGF.IGM.OpaquePtrTy);
16001618
out.add(data);
16011619
}
16021620

lib/IRGen/IRGenSIL.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2091,7 +2091,7 @@ Callee LoweredValue::getCallee(IRGenFunction &IGF,
20912091
case SILFunctionType::Representation::Closure:
20922092
case SILFunctionType::Representation::Method:
20932093
return getSwiftFunctionPointerCallee(IGF, functionValue, selfValue,
2094-
std::move(calleeInfo));
2094+
std::move(calleeInfo), false);
20952095

20962096
case SILFunctionType::Representation::CFunctionPointer:
20972097
assert(!selfValue && "C function pointer has self?");
@@ -2110,8 +2110,10 @@ Callee LoweredValue::getCallee(IRGenFunction &IGF,
21102110
assert(vector.size() == 2 && "thick function pointer with size != 2");
21112111
llvm::Value *functionValue = vector[0];
21122112
llvm::Value *contextValue = vector[1];
2113+
bool castToRefcountedContext = calleeInfo.OrigFnType->isNoEscape();
21132114
return getSwiftFunctionPointerCallee(IGF, functionValue, contextValue,
2114-
std::move(calleeInfo));
2115+
std::move(calleeInfo),
2116+
castToRefcountedContext);
21152117
}
21162118

21172119
case LoweredValue::Kind::EmptyExplosion:
@@ -4589,7 +4591,10 @@ void IRGenSILFunction::visitThinToThickFunctionInst(
45894591
Explosion from = getLoweredExplosion(i->getOperand());
45904592
Explosion to;
45914593
to.add(from.claimNext());
4592-
to.add(IGM.RefCountedNull);
4594+
if (i->getType().castTo<SILFunctionType>()->isNoEscape())
4595+
to.add(llvm::ConstantPointerNull::get(IGM.OpaquePtrTy));
4596+
else
4597+
to.add(IGM.RefCountedNull);
45934598
setLoweredExplosion(i, to);
45944599
}
45954600

lib/IRGen/LoadableByAddress.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2402,7 +2402,19 @@ void LoadableByAddress::recreateConvInstrs() {
24022402
instr->getLoc(), instr->getOperand(), newType);
24032403
break;
24042404
}
2405-
default:
2405+
case SILInstructionKind::ConvertEscapeToNoEscapeInst: {
2406+
auto instr = cast<ConvertEscapeToNoEscapeInst>(convInstr);
2407+
newInstr = convBuilder.createConvertEscapeToNoEscape(
2408+
instr->getLoc(), instr->getOperand(), newType);
2409+
break;
2410+
}
2411+
case SILInstructionKind::MarkDependenceInst: {
2412+
auto instr = cast<MarkDependenceInst>(convInstr);
2413+
newInstr = convBuilder.createMarkDependence(
2414+
instr->getLoc(), instr->getValue(), instr->getBase());
2415+
break;
2416+
}
2417+
default:
24062418
llvm_unreachable("Unexpected conversion instruction");
24072419
}
24082420
convInstr->replaceAllUsesWith(newInstr);
@@ -2484,6 +2496,8 @@ void LoadableByAddress::run() {
24842496
break;
24852497
}
24862498
case SILInstructionKind::ConvertFunctionInst:
2499+
case SILInstructionKind::ConvertEscapeToNoEscapeInst:
2500+
case SILInstructionKind::MarkDependenceInst:
24872501
case SILInstructionKind::ThinFunctionToPointerInst:
24882502
case SILInstructionKind::ThinToThickFunctionInst: {
24892503
conversionInstrs.insert(
@@ -2505,6 +2519,22 @@ void LoadableByAddress::run() {
25052519
}
25062520
funcRefs.insert(FRI);
25072521
}
2522+
} else if (auto *Cvt = dyn_cast<MarkDependenceInst>(&I)) {
2523+
SILValue val = Cvt->getValue();
2524+
SILType currType = val->getType();
2525+
if (auto fType = currType.getAs<SILFunctionType>()) {
2526+
if (modifiableFunction(fType)) {
2527+
conversionInstrs.insert(Cvt);
2528+
}
2529+
}
2530+
} else if (auto *Cvt = dyn_cast<ConvertEscapeToNoEscapeInst>(&I)) {
2531+
SILValue val = Cvt->getConverted();
2532+
SILType currType = val->getType();
2533+
auto fType = currType.getAs<SILFunctionType>();
2534+
assert(fType && "Expected SILFunctionType");
2535+
if (modifiableFunction(fType)) {
2536+
conversionInstrs.insert(Cvt);
2537+
}
25082538
} else if (auto *CFI = dyn_cast<ConvertFunctionInst>(&I)) {
25092539
SILValue val = CFI->getConverted();
25102540
SILType currType = val->getType();

lib/SIL/InstructionUtils.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,15 @@ bool swift::onlyAffectsRefCount(SILInstruction *user) {
283283

284284
SILValue swift::stripConvertFunctions(SILValue V) {
285285
while (true) {
286-
auto CFI = dyn_cast<ConvertFunctionInst>(V);
287-
if (!CFI)
288-
return V;
289-
V = CFI->getOperand();
286+
if (auto CFI = dyn_cast<ConvertFunctionInst>(V)) {
287+
V = CFI->getOperand();
288+
continue;
289+
}
290+
else if (auto *Cvt = dyn_cast<ConvertEscapeToNoEscapeInst>(V)) {
291+
V = Cvt->getOperand();
292+
continue;
293+
}
294+
break;
290295
}
291296
return V;
292297
}
@@ -435,8 +440,10 @@ SILValue swift::isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI) {
435440

436441
// The argument should be a closure.
437442
auto Arg = PAI->getArgument(0);
438-
if (!Arg->getType().is<SILFunctionType>()
439-
|| !Arg->getType().isReferenceCounted(PAI->getFunction()->getModule()))
443+
if (!Arg->getType().is<SILFunctionType>() ||
444+
(!Arg->getType().isReferenceCounted(PAI->getFunction()->getModule()) &&
445+
Arg->getType().getAs<SILFunctionType>()->getRepresentation() !=
446+
SILFunctionType::Representation::Thick))
440447
return SILValue();
441448

442449
return Arg;

lib/SIL/SILFunctionType.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2890,6 +2890,12 @@ SILFunctionType::isABICompatibleWith(CanSILFunctionType other) const {
28902890
return {ABICompatibilityCheckResult::ABIIncompatibleParameterType, i};
28912891
}
28922892

2893+
// This needs to be checked last because the result implies everying else has
2894+
// already been checked and this is the only difference.
2895+
if (isNoEscape() != other->isNoEscape() &&
2896+
(getRepresentation() == SILFunctionType::Representation::Thick))
2897+
return ABICompatibilityCheckResult::ABIEscapeToNoEscapeConversion;
2898+
28932899
return ABICompatibilityCheckResult::None;
28942900
}
28952901

@@ -2918,6 +2924,8 @@ StringRef SILFunctionType::ABICompatibilityCheckResult::getMessage() const {
29182924
return "Differing parameter convention";
29192925
case innerty::ABIIncompatibleParameterType:
29202926
return "ABI incompatible parameter type.";
2927+
case innerty::ABIEscapeToNoEscapeConversion:
2928+
return "Escape to no escape conversion";
29212929
}
29222930
llvm_unreachable("Covered switch isn't completely covered?!");
29232931
}

lib/SIL/SILInstructions.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,8 +2014,9 @@ ConvertEscapeToNoEscapeInst *ConvertEscapeToNoEscapeInst::create(
20142014
(void)opTI;
20152015
CanSILFunctionType resTI = CFI->getType().castTo<SILFunctionType>();
20162016
(void)resTI;
2017-
assert(opTI->isABICompatibleWith(resTI).isCompatible() &&
2018-
"Can not convert in between ABI incompatible function types");
2017+
assert(
2018+
opTI->isABICompatibleWith(resTI).isCompatibleUpToNoEscapeConversion() &&
2019+
"Can not convert in between ABI incompatible function types");
20192020
}
20202021
return CFI;
20212022
}

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,9 @@ OwnershipUseCheckerResult OwnershipCompatibilityUseChecker::visitCallee(
10091009
return {compatibleWithOwnership(ValueOwnershipKind::Owned),
10101010
UseLifetimeConstraint::MustBeInvalidated};
10111011
case ParameterConvention::Direct_Guaranteed:
1012+
if (SubstCalleeType->isNoEscape())
1013+
return {compatibleWithOwnership(ValueOwnershipKind::Trivial),
1014+
UseLifetimeConstraint::MustBeLive};
10121015
return {compatibleWithOwnership(ValueOwnershipKind::Guaranteed),
10131016
UseLifetimeConstraint::MustBeLive};
10141017
}
@@ -1189,6 +1192,16 @@ OwnershipCompatibilityUseChecker::visitStoreInst(StoreInst *I) {
11891192
OwnershipUseCheckerResult
11901193
OwnershipCompatibilityUseChecker::visitMarkDependenceInst(
11911194
MarkDependenceInst *MDI) {
1195+
1196+
// Forward ownership if the mark_dependence instruction marks a dependence
1197+
// on a @noescape function type for an escaping function type.
1198+
if (getValue() == MDI->getValue())
1199+
if (auto ResFnTy = MDI->getType().getAs<SILFunctionType>())
1200+
if (auto BaseFnTy = MDI->getBase()->getType().getAs<SILFunctionType>())
1201+
if (!ResFnTy->isNoEscape() && BaseFnTy->isNoEscape())
1202+
return {compatibleWithOwnership(ValueOwnershipKind::Owned),
1203+
UseLifetimeConstraint::MustBeInvalidated};
1204+
11921205
// We always treat mark dependence as a use that keeps a value alive. We will
11931206
// be introducing a begin_dependence/end_dependence version of this later.
11941207
return {true, UseLifetimeConstraint::MustBeLive};

lib/SIL/SILVerifier.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2898,8 +2898,10 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
28982898
require(resFTy->getRepresentation() == SILFunctionType::Representation::Thick,
28992899
"result of thin_to_thick_function must be thick");
29002900

2901-
auto adjustedOperandExtInfo = opFTy->getExtInfo().withRepresentation(
2902-
SILFunctionType::Representation::Thick);
2901+
auto adjustedOperandExtInfo =
2902+
opFTy->getExtInfo()
2903+
.withRepresentation(SILFunctionType::Representation::Thick)
2904+
.withNoEscape(resFTy->isNoEscape());
29032905
require(adjustedOperandExtInfo == resFTy->getExtInfo(),
29042906
"operand and result of thin_to_think_function must agree in particulars");
29052907
}

0 commit comments

Comments
 (0)