Skip to content

Commit d369aa4

Browse files
authored
Support @NoEscape SIL function types. (#12420)
Support for @NoEscape SILFunctionTypes. These are the underlying SIL changes necessary to implement the new closure capture ABI. Note: This includes a change to function name mangling that primarily affects reabstraction thunks. The new ABI will allow stack allocation of non-escaping closures as a simple optimization. The new ABI, and the stack allocation optimization, also require closure context to be @guaranteed. That will be implemented as the next step. Many SIL passes pattern match partial_apply sequences. These all needed to be fixed to handle the convert_function that SILGen now emits. The conversion is now needed whenever a function declaration, which has an escaping type, is passed into a @NoEscape argument. In addition to supporting new SIL patterns, some optimizations like inlining and SIL combine are now stronger which could perturb some benchmark results. These underlying SIL changes should be merged now to avoid conflicting with other work. Minor benchmark discrepancies can be investigated as part of the stack-allocation work. * Add a noescape attribute to SILFunctionType. And set this attribute correctly when lowering formal function types to SILFunctionTypes based on @escaping. This will allow stack allocation of closures, and unblock a related ABI change. * Flip the polarity on @NoEscape on SILFunctionType and clarify that we don't default it. * Emit withoutActuallyEscaping using a convert_function instruction. It might be better to use a specialized instruction here, but I'll leave that up to Andy. Andy: And I'll leave that to Arnold who is implementing SIL support for guaranteed ownership of thick function types. * Fix SILGen and SIL Parsing. * Fix the LoadableByAddress pass. * Fix ClosureSpecializer. * Fix performance inliner constant propagation. * Fix the PartialApplyCombiner. * Adjust SILFunctionType for thunks. * Add mangling for @noescape/@escaping. * Fix test cases for @NoEscape attribute, mangling, convert_function, etc. * Fix exclusivity test cases. * Fix AccessEnforcement. * Fix SILCombine of convert_function -> apply. * Fix ObjC bridging thunks. * Various MandatoryInlining fixes. * Fix SILCombine optimizeApplyOfConvertFunction. * Fix more test cases after merging (again). * Fix ClosureSpecializer. Hande convert_function cloning. Be conservative when combining convert_function. Most of our code doesn't know how to deal with function type mismatches yet. * Fix MandatoryInlining. Be conservative with function conversion. The inliner does not yet know how to cast arguments or convert between throwing forms. * Fix PartialApplyCombiner.
1 parent c9f4df8 commit d369aa4

File tree

93 files changed

+1045
-502
lines changed

Some content is hidden

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

93 files changed

+1045
-502
lines changed

docs/ABI/Mangling.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,10 +404,12 @@ mangled in to disambiguate.
404404
impl-function-type ::= type* 'I' FUNC-ATTRIBUTES '_'
405405
impl-function-type ::= type* generic-signature 'I' PSEUDO-GENERIC? FUNC-ATTRIBUTES '_'
406406

407-
FUNC-ATTRIBUTES ::= CALLEE-CONVENTION? FUNC-REPRESENTATION PARAM-CONVENTION* RESULT-CONVENTION* ('z' RESULT-CONVENTION)
407+
FUNC-ATTRIBUTES ::= CALLEE-ESCAPE? CALLEE-CONVENTION FUNC-REPRESENTATION? PARAM-CONVENTION* RESULT-CONVENTION* ('z' RESULT-CONVENTION)
408408

409409
PSEUDO-GENERIC ::= 'P'
410410

411+
CALLEE-ESCAPE ::= 'e' // @escaping (inverse of SIL @noescape)
412+
411413
CALLEE-CONVENTION ::= 'y' // @callee_unowned
412414
CALLEE-CONVENTION ::= 'g' // @callee_guaranteed
413415
CALLEE-CONVENTION ::= 'x' // @callee_owned

include/swift/AST/Types.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3207,11 +3207,12 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
32073207
// you'll need to adjust both the Bits field below and
32083208
// TypeBase::AnyFunctionTypeBits.
32093209

3210-
// |representation|pseudogeneric|
3211-
// | 0 .. 3 | 4 |
3210+
// |representation|pseudogeneric| noescape |
3211+
// | 0 .. 3 | 4 | 5 |
32123212
//
32133213
enum : uint16_t { RepresentationMask = 0x00F };
32143214
enum : uint16_t { PseudogenericMask = 0x010 };
3215+
enum : uint16_t { NoEscapeMask = 0x020 };
32153216

32163217
uint16_t Bits;
32173218

@@ -3224,15 +3225,19 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
32243225
ExtInfo() : Bits(0) { }
32253226

32263227
// Constructor for polymorphic type.
3227-
ExtInfo(Representation rep, bool isPseudogeneric) {
3228+
ExtInfo(Representation rep, bool isPseudogeneric, bool isNoEscape) {
32283229
Bits = ((unsigned) rep) |
3229-
(isPseudogeneric ? PseudogenericMask : 0);
3230+
(isPseudogeneric ? PseudogenericMask : 0) |
3231+
(isNoEscape ? NoEscapeMask : 0);
32303232
}
32313233

32323234
/// Is this function pseudo-generic? A pseudo-generic function
32333235
/// is not permitted to dynamically depend on its type arguments.
32343236
bool isPseudogeneric() const { return Bits & PseudogenericMask; }
32353237

3238+
// Is this function guaranteed to be no-escape by the type system?
3239+
bool isNoEscape() const { return Bits & NoEscapeMask; }
3240+
32363241
/// What is the abstract representation of this function value?
32373242
Representation getRepresentation() const {
32383243
return Representation(Bits & RepresentationMask);
@@ -3305,6 +3310,12 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
33053310
else
33063311
return ExtInfo(Bits & ~PseudogenericMask);
33073312
}
3313+
ExtInfo withNoEscape(bool NoEscape = true) const {
3314+
if (NoEscape)
3315+
return ExtInfo(Bits | NoEscapeMask);
3316+
else
3317+
return ExtInfo(Bits & ~NoEscapeMask);
3318+
}
33083319

33093320
uint16_t getFuncAttrKey() const {
33103321
return Bits;
@@ -3556,6 +3567,10 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
35563567
return getExtInfo().isPseudogeneric();
35573568
}
35583569

3570+
bool isNoEscape() const {
3571+
return getExtInfo().isNoEscape();
3572+
}
3573+
35593574
bool isNoReturnFunction(); // Defined in SILType.cpp
35603575

35613576
CanSILFunctionType substGenericArgs(SILModule &silModule,

include/swift/Demangling/DemangleNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ NODE(Identifier)
8585
NODE(Index)
8686
CONTEXT_NODE(IVarInitializer)
8787
CONTEXT_NODE(IVarDestroyer)
88+
NODE(ImplEscaping)
8889
NODE(ImplConvention)
8990
NODE(ImplFunctionAttribute)
9091
NODE(ImplFunctionType)

include/swift/Remote/MetadataReader.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ class TypeDecoder {
270270
// Minimal support for lowered function types. These come up in
271271
// reflection as capture types. For the reflection library's
272272
// purposes, the only part that matters is the convention.
273+
//
274+
// TODO: Do we want to reflect @escaping?
273275
FunctionTypeFlags flags;
274276

275277
for (unsigned i = 0; i < Node->getNumChildren(); i++) {

include/swift/SILOptimizer/Analysis/ARCAnalysis.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,22 +340,31 @@ class ReleaseTracker {
340340
llvm::SmallSetVector<SILInstruction *, 4> TrackedUsers;
341341
llvm::SmallSetVector<SILInstruction *, 4> FinalReleases;
342342
std::function<bool(SILInstruction *)> AcceptableUserQuery;
343+
std::function<bool(SILInstruction *)> TransitiveUserQuery;
343344

344345
public:
345-
ReleaseTracker(std::function<bool(SILInstruction *)> AcceptableUserQuery)
346+
ReleaseTracker(std::function<bool(SILInstruction *)> AcceptableUserQuery,
347+
std::function<bool(SILInstruction *)> TransitiveUserQuery)
346348
: TrackedUsers(), FinalReleases(),
347-
AcceptableUserQuery(AcceptableUserQuery) {}
349+
AcceptableUserQuery(AcceptableUserQuery),
350+
TransitiveUserQuery(TransitiveUserQuery) {}
348351

349352
void trackLastRelease(SILInstruction *Inst) { FinalReleases.insert(Inst); }
350353

351354
bool isUserAcceptable(SILInstruction *User) const {
352355
return AcceptableUserQuery(User);
353356
}
357+
bool isUserTransitive(SILInstruction *User) const {
358+
return TransitiveUserQuery(User);
359+
}
360+
361+
bool isUser(SILInstruction *User) { return TrackedUsers.count(User); }
354362

355363
void trackUser(SILInstruction *User) { TrackedUsers.insert(User); }
356364

357365
using range = iterator_range<llvm::SmallSetVector<SILInstruction *, 4>::iterator>;
358366

367+
// An ordered list of users, with "casts" before their transitive uses.
359368
range getTrackedUsers() { return {TrackedUsers.begin(), TrackedUsers.end()}; }
360369

361370
range getFinalReleases() {

include/swift/Serialization/ModuleFormat.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
5454
/// in source control, you should also update the comment to briefly
5555
/// describe what change you made. The content of this comment isn't important;
5656
/// it just ensures a conflict if two people change the module format.
57-
const uint16_t VERSION_MINOR = 370; // Last change: remove is_nonnull
57+
const uint16_t VERSION_MINOR = 371; // Last change: SILFunctionType.noescape
5858

5959
using DeclID = PointerEmbeddedInt<unsigned, 31>;
6060
using DeclIDField = BCFixed<31>;
@@ -734,6 +734,7 @@ namespace decls_block {
734734
ParameterConventionField, // callee convention
735735
SILFunctionTypeRepresentationField, // representation
736736
BCFixed<1>, // pseudogeneric?
737+
BCFixed<1>, // noescape?
737738
BCFixed<1>, // error result?
738739
BCFixed<30>, // number of parameters
739740
BCFixed<30>, // number of results

lib/AST/ASTMangler.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,9 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) {
11001100
if (fn->isPolymorphic() && fn->isPseudogeneric())
11011101
OpArgs.push_back('P');
11021102

1103+
if (!fn->isNoEscape())
1104+
OpArgs.push_back('e');
1105+
11031106
// <impl-callee-convention>
11041107
if (fn->getExtInfo().hasContext()) {
11051108
OpArgs.push_back(getParamConvention(fn->getCalleeConvention()));

lib/AST/ASTPrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3543,6 +3543,9 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
35433543
if (info.isPseudogeneric()) {
35443544
Printer.printSimpleAttr("@pseudogeneric") << " ";
35453545
}
3546+
if (info.isNoEscape()) {
3547+
Printer.printSimpleAttr("@noescape") << " ";
3548+
}
35463549
}
35473550

35483551
void visitFunctionType(FunctionType *T) {

lib/Demangling/Demangler.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,9 @@ NodePointer Demangler::demangleImplFunctionType() {
10121012
if (GenSig && nextIf('P'))
10131013
GenSig = changeKind(GenSig, Node::Kind::DependentPseudogenericSignature);
10141014

1015+
if (nextIf('e'))
1016+
type->addChild(createNode(Node::Kind::ImplEscaping), *this);
1017+
10151018
const char *CAttr = nullptr;
10161019
switch (nextChar()) {
10171020
case 'y': CAttr = "@callee_unowned"; break;

lib/Demangling/NodePrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ class NodePrinter {
330330
case Node::Kind::Index:
331331
case Node::Kind::IVarInitializer:
332332
case Node::Kind::IVarDestroyer:
333+
case Node::Kind::ImplEscaping:
333334
case Node::Kind::ImplConvention:
334335
case Node::Kind::ImplFunctionAttribute:
335336
case Node::Kind::ImplFunctionType:
@@ -1563,6 +1564,9 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
15631564
case Node::Kind::TypeList:
15641565
printChildren(Node);
15651566
return nullptr;
1567+
case Node::Kind::ImplEscaping:
1568+
Printer << "@escaping";
1569+
return nullptr;
15661570
case Node::Kind::ImplConvention:
15671571
Printer << Node->getText();
15681572
return nullptr;

lib/Demangling/OldRemangler.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,10 @@ void Remangler::mangleImplResult(Node *node) {
11641164
mangleChildNodes(node); // impl convention, type
11651165
}
11661166

1167+
void Remangler::mangleImplEscaping(Node *node) {
1168+
// The old mangler does not encode escaping.
1169+
}
1170+
11671171
void Remangler::mangleImplConvention(Node *node) {
11681172
assert(node->getKind() == Node::Kind::ImplConvention);
11691173
StringRef text = node->getText();

lib/Demangling/Remangler.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,10 @@ void Remangler::mangleIVarDestroyer(Node *node) {
11231123
Buffer << "fE";
11241124
}
11251125

1126+
void Remangler::mangleImplEscaping(Node *node) {
1127+
Buffer << 'e';
1128+
}
1129+
11261130
void Remangler::mangleImplConvention(Node *node) {
11271131
char ConvCh = llvm::StringSwitch<char>(node->getText())
11281132
.Case("@callee_unowned", 'y')
@@ -1163,6 +1167,9 @@ void Remangler::mangleImplFunctionType(Node *node) {
11631167
Buffer << 'I' << PseudoGeneric;
11641168
for (NodePointer Child : *node) {
11651169
switch (Child->getKind()) {
1170+
case Node::Kind::ImplEscaping:
1171+
Buffer << 'e';
1172+
break;
11661173
case Node::Kind::ImplConvention: {
11671174
char ConvCh = llvm::StringSwitch<char>(Child->getText())
11681175
.Case("@callee_unowned", 'y')

lib/IDE/TypeReconstruction.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,7 @@ static void VisitNodeFunction(
12551255
static void CreateFunctionType(ASTContext *ast,
12561256
const VisitNodeResult &arg_type_result,
12571257
const VisitNodeResult &return_type_result,
1258+
bool escaping,
12581259
bool throws,
12591260
VisitNodeResult &result) {
12601261
Type arg_clang_type;
@@ -1287,7 +1288,8 @@ static void CreateFunctionType(ASTContext *ast,
12871288
if (arg_clang_type && return_clang_type) {
12881289
result._types.push_back(
12891290
FunctionType::get(arg_clang_type, return_clang_type,
1290-
FunctionType::ExtInfo().withThrows(throws)));
1291+
FunctionType::ExtInfo().withNoEscape(!escaping)
1292+
.withThrows(throws)));
12911293
}
12921294
}
12931295

@@ -1314,7 +1316,8 @@ static void VisitNodeFunctionType(
13141316
break;
13151317
}
13161318
}
1317-
CreateFunctionType(ast, arg_type_result, return_type_result, throws, result);
1319+
CreateFunctionType(ast, arg_type_result, return_type_result,
1320+
/*escaping=*/true, throws, result);
13181321
}
13191322

13201323
static void VisitNodeImplFunctionType(
@@ -1323,10 +1326,14 @@ static void VisitNodeImplFunctionType(
13231326
VisitNodeResult arg_type_result;
13241327
VisitNodeResult return_type_result;
13251328
Demangle::Node::iterator end = cur_node->end();
1329+
bool escaping = false;
13261330
bool throws = false;
13271331
for (Demangle::Node::iterator pos = cur_node->begin(); pos != end; ++pos) {
13281332
const Demangle::Node::Kind child_node_kind = (*pos)->getKind();
13291333
switch (child_node_kind) {
1334+
case Demangle::Node::Kind::ImplEscaping:
1335+
escaping = true;
1336+
break;
13301337
case Demangle::Node::Kind::ImplConvention:
13311338
// Ignore the ImplConvention it is only a hint for the SIL ARC optimizer.
13321339
break;
@@ -1343,7 +1350,8 @@ static void VisitNodeImplFunctionType(
13431350
break;
13441351
}
13451352
}
1346-
CreateFunctionType(ast, arg_type_result, return_type_result, throws, result);
1353+
CreateFunctionType(ast, arg_type_result, return_type_result, escaping, throws,
1354+
result);
13471355
}
13481356

13491357

lib/IRGen/LoadableByAddress.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,6 +2553,7 @@ void LoadableByAddress::run() {
25532553
}
25542554
break;
25552555
}
2556+
case SILInstructionKind::ConvertFunctionInst:
25562557
case SILInstructionKind::ThinFunctionToPointerInst:
25572558
case SILInstructionKind::ThinToThickFunctionInst: {
25582559
conversionInstrs.insert(

lib/Parse/ParseDecl.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,11 +1681,14 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
16811681
}
16821682

16831683
// @noescape is deprecated and no longer used
1684-
diagnose(Loc, Context.isSwiftVersion3()
1685-
? diag::swift3_attr_noescape_deprecated
1686-
: diag::attr_noescape_deprecated)
1684+
// In SIL, the polarity of @escaping is reversed.
1685+
// @escaping is the default and @noescape is explicit.
1686+
if (!isInSILMode()) {
1687+
diagnose(Loc, Context.isSwiftVersion3()
1688+
? diag::swift3_attr_noescape_deprecated
1689+
: diag::attr_noescape_deprecated)
16871690
.fixItRemove({Attributes.AtLoc, Loc});
1688-
1691+
}
16891692
break;
16901693
case TAK_escaping:
16911694
// You can't specify @noescape and @escaping together.

lib/SIL/SILFunctionType.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -951,11 +951,10 @@ static CanSILFunctionType getSILFunctionType(SILModule &M,
951951
bool pseudogeneric = (constant ? isPseudogeneric(*constant) : false);
952952

953953
// Always strip the auto-closure and no-escape bit.
954-
// TODO: The noescape bit could be of interest to SIL optimizations.
955-
// We should bring it back when we have those optimizations.
956954
auto silExtInfo = SILFunctionType::ExtInfo()
957955
.withRepresentation(extInfo.getSILRepresentation())
958-
.withIsPseudogeneric(pseudogeneric);
956+
.withIsPseudogeneric(pseudogeneric)
957+
.withNoEscape(extInfo.isNoEscape());
959958

960959
return SILFunctionType::get(genericSig,
961960
silExtInfo, calleeConvention,

lib/SIL/SILModule.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ SILFunction *SILModule::getOrCreateFunction(
236236
CanSILFunctionType type, IsBare_t isBareSILFunction,
237237
IsTransparent_t isTransparent, IsSerialized_t isSerialized,
238238
ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope subclassScope) {
239+
assert(!type->isNoEscape() && "Function decls always have escaping types.");
239240
if (auto fn = lookUpFunction(name)) {
240241
assert(fn->getLoweredFunctionType() == type);
241242
assert(fn->getLinkage() == linkage ||

lib/SILGen/SILGenBridging.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -881,10 +881,14 @@ SILGenFunction::emitBlockToFunc(SILLocation loc,
881881

882882
// Create it in the current function.
883883
auto thunkValue = B.createFunctionRef(loc, thunk);
884-
auto thunkedFn = B.createPartialApply(loc, thunkValue,
885-
SILType::getPrimitiveObjectType(substFnTy),
886-
subs, block.forward(*this),
887-
SILType::getPrimitiveObjectType(loweredFuncTy));
884+
SingleValueInstruction *thunkedFn = B.createPartialApply(
885+
loc, thunkValue, SILType::getPrimitiveObjectType(substFnTy), subs,
886+
block.forward(*this), SILType::getPrimitiveObjectType(loweredFuncTy));
887+
if (loweredFuncTy->isNoEscape()) {
888+
auto &funcTL = getTypeLowering(loweredFuncTy);
889+
thunkedFn =
890+
B.createConvertFunction(loc, thunkedFn, funcTL.getLoweredType());
891+
}
888892
return emitManagedRValueWithCleanup(thunkedFn);
889893
}
890894

0 commit comments

Comments
 (0)