Skip to content

Support @noescape SIL function types. #12420

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

Merged
merged 23 commits into from
Oct 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
29af491
Add a noescape attribute to SILFunctionType.
atrick May 22, 2017
0ef243e
Flip the polarity on @noescape on SILFunctionType and clarify that
rjmccall Sep 1, 2017
99750f1
Emit withoutActuallyEscaping using a convert_function instruction.
rjmccall Sep 13, 2017
0407e02
Fix SILGen and SIL Parsing.
atrick Oct 6, 2017
2132277
Fix the LoadableByAddress pass.
atrick Oct 6, 2017
1bdc3e1
Fix ClosureSpecializer.
atrick Oct 7, 2017
5e92057
Fix MandatoryInlining.
atrick Oct 8, 2017
e46ad2c
Fix performance inliner constant propagation.
atrick Oct 8, 2017
435418f
Fix the PartialApplyCombiner.
atrick Oct 9, 2017
e27bdbb
Adjust SILFunctionType for thunks.
atrick Oct 10, 2017
1b2c226
Add mangling for @noescape/@escaping.
atrick Oct 10, 2017
6513a06
Fix AccessEnforcement.
atrick Oct 10, 2017
5cca4c9
Fix exclusivity test cases.
atrick Oct 10, 2017
fd15af5
Fix SILCombine of convert_function -> apply.
atrick Oct 11, 2017
8db3f7f
Fix ObjC bridging thunks.
atrick Oct 11, 2017
d1850d0
Various MandatoryInlining fixes.
atrick Oct 11, 2017
7469446
Fix test cases for @noescape attribute, mangling, convert_function, etc.
atrick Oct 10, 2017
0d02b59
Fix SILCombine optimizeApplyOfConvertFunction.
atrick Oct 13, 2017
5bcb842
Fix more test cases after merging (again).
atrick Oct 13, 2017
4c71855
Fix ClosureSpecializer. Hande convert_function cloning.
atrick Oct 13, 2017
1febbda
Fix PartialApplyCombiner.
atrick Oct 14, 2017
8559536
Fix MandatoryInlining.
atrick Oct 14, 2017
affa1b7
Fix PartialApplyCombiner.
atrick Oct 17, 2017
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
4 changes: 3 additions & 1 deletion docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -404,10 +404,12 @@ mangled in to disambiguate.
impl-function-type ::= type* 'I' FUNC-ATTRIBUTES '_'
impl-function-type ::= type* generic-signature 'I' PSEUDO-GENERIC? FUNC-ATTRIBUTES '_'

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

PSEUDO-GENERIC ::= 'P'

CALLEE-ESCAPE ::= 'e' // @escaping (inverse of SIL @noescape)

CALLEE-CONVENTION ::= 'y' // @callee_unowned
CALLEE-CONVENTION ::= 'g' // @callee_guaranteed
CALLEE-CONVENTION ::= 'x' // @callee_owned
Expand Down
23 changes: 19 additions & 4 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -3207,11 +3207,12 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
// you'll need to adjust both the Bits field below and
// TypeBase::AnyFunctionTypeBits.

// |representation|pseudogeneric|
// | 0 .. 3 | 4 |
// |representation|pseudogeneric| noescape |
// | 0 .. 3 | 4 | 5 |
//
enum : uint16_t { RepresentationMask = 0x00F };
enum : uint16_t { PseudogenericMask = 0x010 };
enum : uint16_t { NoEscapeMask = 0x020 };

uint16_t Bits;

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

// Constructor for polymorphic type.
ExtInfo(Representation rep, bool isPseudogeneric) {
ExtInfo(Representation rep, bool isPseudogeneric, bool isNoEscape) {
Bits = ((unsigned) rep) |
(isPseudogeneric ? PseudogenericMask : 0);
(isPseudogeneric ? PseudogenericMask : 0) |
(isNoEscape ? NoEscapeMask : 0);
}

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

// Is this function guaranteed to be no-escape by the type system?
bool isNoEscape() const { return Bits & NoEscapeMask; }

/// What is the abstract representation of this function value?
Representation getRepresentation() const {
return Representation(Bits & RepresentationMask);
Expand Down Expand Up @@ -3305,6 +3310,12 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
else
return ExtInfo(Bits & ~PseudogenericMask);
}
ExtInfo withNoEscape(bool NoEscape = true) const {
if (NoEscape)
return ExtInfo(Bits | NoEscapeMask);
else
return ExtInfo(Bits & ~NoEscapeMask);
}

uint16_t getFuncAttrKey() const {
return Bits;
Expand Down Expand Up @@ -3556,6 +3567,10 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
return getExtInfo().isPseudogeneric();
}

bool isNoEscape() const {
return getExtInfo().isNoEscape();
}

bool isNoReturnFunction(); // Defined in SILType.cpp

CanSILFunctionType substGenericArgs(SILModule &silModule,
Expand Down
1 change: 1 addition & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ NODE(Identifier)
NODE(Index)
CONTEXT_NODE(IVarInitializer)
CONTEXT_NODE(IVarDestroyer)
NODE(ImplEscaping)
NODE(ImplConvention)
NODE(ImplFunctionAttribute)
NODE(ImplFunctionType)
Expand Down
2 changes: 2 additions & 0 deletions include/swift/Remote/MetadataReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ class TypeDecoder {
// Minimal support for lowered function types. These come up in
// reflection as capture types. For the reflection library's
// purposes, the only part that matters is the convention.
//
// TODO: Do we want to reflect @escaping?
FunctionTypeFlags flags;

for (unsigned i = 0; i < Node->getNumChildren(); i++) {
Expand Down
13 changes: 11 additions & 2 deletions include/swift/SILOptimizer/Analysis/ARCAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -340,22 +340,31 @@ class ReleaseTracker {
llvm::SmallSetVector<SILInstruction *, 4> TrackedUsers;
llvm::SmallSetVector<SILInstruction *, 4> FinalReleases;
std::function<bool(SILInstruction *)> AcceptableUserQuery;
std::function<bool(SILInstruction *)> TransitiveUserQuery;

public:
ReleaseTracker(std::function<bool(SILInstruction *)> AcceptableUserQuery)
ReleaseTracker(std::function<bool(SILInstruction *)> AcceptableUserQuery,
std::function<bool(SILInstruction *)> TransitiveUserQuery)
: TrackedUsers(), FinalReleases(),
AcceptableUserQuery(AcceptableUserQuery) {}
AcceptableUserQuery(AcceptableUserQuery),
TransitiveUserQuery(TransitiveUserQuery) {}

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

bool isUserAcceptable(SILInstruction *User) const {
return AcceptableUserQuery(User);
}
bool isUserTransitive(SILInstruction *User) const {
return TransitiveUserQuery(User);
}

bool isUser(SILInstruction *User) { return TrackedUsers.count(User); }

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

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

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

range getFinalReleases() {
Expand Down
3 changes: 2 additions & 1 deletion include/swift/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
/// in source control, you should also update the comment to briefly
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
const uint16_t VERSION_MINOR = 370; // Last change: remove is_nonnull
const uint16_t VERSION_MINOR = 371; // Last change: SILFunctionType.noescape

using DeclID = PointerEmbeddedInt<unsigned, 31>;
using DeclIDField = BCFixed<31>;
Expand Down Expand Up @@ -734,6 +734,7 @@ namespace decls_block {
ParameterConventionField, // callee convention
SILFunctionTypeRepresentationField, // representation
BCFixed<1>, // pseudogeneric?
BCFixed<1>, // noescape?
BCFixed<1>, // error result?
BCFixed<30>, // number of parameters
BCFixed<30>, // number of results
Expand Down
3 changes: 3 additions & 0 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,9 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn) {
if (fn->isPolymorphic() && fn->isPseudogeneric())
OpArgs.push_back('P');

if (!fn->isNoEscape())
OpArgs.push_back('e');

// <impl-callee-convention>
if (fn->getExtInfo().hasContext()) {
OpArgs.push_back(getParamConvention(fn->getCalleeConvention()));
Expand Down
3 changes: 3 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3543,6 +3543,9 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
if (info.isPseudogeneric()) {
Printer.printSimpleAttr("@pseudogeneric") << " ";
}
if (info.isNoEscape()) {
Printer.printSimpleAttr("@noescape") << " ";
}
}

void visitFunctionType(FunctionType *T) {
Expand Down
3 changes: 3 additions & 0 deletions lib/Demangling/Demangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,9 @@ NodePointer Demangler::demangleImplFunctionType() {
if (GenSig && nextIf('P'))
GenSig = changeKind(GenSig, Node::Kind::DependentPseudogenericSignature);

if (nextIf('e'))
type->addChild(createNode(Node::Kind::ImplEscaping), *this);

const char *CAttr = nullptr;
switch (nextChar()) {
case 'y': CAttr = "@callee_unowned"; break;
Expand Down
4 changes: 4 additions & 0 deletions lib/Demangling/NodePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ class NodePrinter {
case Node::Kind::Index:
case Node::Kind::IVarInitializer:
case Node::Kind::IVarDestroyer:
case Node::Kind::ImplEscaping:
case Node::Kind::ImplConvention:
case Node::Kind::ImplFunctionAttribute:
case Node::Kind::ImplFunctionType:
Expand Down Expand Up @@ -1566,6 +1567,9 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
case Node::Kind::TypeList:
printChildren(Node);
return nullptr;
case Node::Kind::ImplEscaping:
Printer << "@escaping";
return nullptr;
case Node::Kind::ImplConvention:
Printer << Node->getText();
return nullptr;
Expand Down
4 changes: 4 additions & 0 deletions lib/Demangling/OldRemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,10 @@ void Remangler::mangleImplResult(Node *node) {
mangleChildNodes(node); // impl convention, type
}

void Remangler::mangleImplEscaping(Node *node) {
// The old mangler does not encode escaping.
}

void Remangler::mangleImplConvention(Node *node) {
assert(node->getKind() == Node::Kind::ImplConvention);
StringRef text = node->getText();
Expand Down
7 changes: 7 additions & 0 deletions lib/Demangling/Remangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,10 @@ void Remangler::mangleIVarDestroyer(Node *node) {
Buffer << "fE";
}

void Remangler::mangleImplEscaping(Node *node) {
Buffer << 'e';
}

void Remangler::mangleImplConvention(Node *node) {
char ConvCh = llvm::StringSwitch<char>(node->getText())
.Case("@callee_unowned", 'y')
Expand Down Expand Up @@ -1163,6 +1167,9 @@ void Remangler::mangleImplFunctionType(Node *node) {
Buffer << 'I' << PseudoGeneric;
for (NodePointer Child : *node) {
switch (Child->getKind()) {
case Node::Kind::ImplEscaping:
Buffer << 'e';
break;
case Node::Kind::ImplConvention: {
char ConvCh = llvm::StringSwitch<char>(Child->getText())
.Case("@callee_unowned", 'y')
Expand Down
14 changes: 11 additions & 3 deletions lib/IDE/TypeReconstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,7 @@ static void VisitNodeFunction(
static void CreateFunctionType(ASTContext *ast,
const VisitNodeResult &arg_type_result,
const VisitNodeResult &return_type_result,
bool escaping,
bool throws,
VisitNodeResult &result) {
Type arg_clang_type;
Expand Down Expand Up @@ -1287,7 +1288,8 @@ static void CreateFunctionType(ASTContext *ast,
if (arg_clang_type && return_clang_type) {
result._types.push_back(
FunctionType::get(arg_clang_type, return_clang_type,
FunctionType::ExtInfo().withThrows(throws)));
FunctionType::ExtInfo().withNoEscape(!escaping)
.withThrows(throws)));
}
}

Expand All @@ -1314,7 +1316,8 @@ static void VisitNodeFunctionType(
break;
}
}
CreateFunctionType(ast, arg_type_result, return_type_result, throws, result);
CreateFunctionType(ast, arg_type_result, return_type_result,
/*escaping=*/true, throws, result);
}

static void VisitNodeImplFunctionType(
Expand All @@ -1323,10 +1326,14 @@ static void VisitNodeImplFunctionType(
VisitNodeResult arg_type_result;
VisitNodeResult return_type_result;
Demangle::Node::iterator end = cur_node->end();
bool escaping = false;
bool throws = false;
for (Demangle::Node::iterator pos = cur_node->begin(); pos != end; ++pos) {
const Demangle::Node::Kind child_node_kind = (*pos)->getKind();
switch (child_node_kind) {
case Demangle::Node::Kind::ImplEscaping:
escaping = true;
break;
case Demangle::Node::Kind::ImplConvention:
// Ignore the ImplConvention it is only a hint for the SIL ARC optimizer.
break;
Expand All @@ -1343,7 +1350,8 @@ static void VisitNodeImplFunctionType(
break;
}
}
CreateFunctionType(ast, arg_type_result, return_type_result, throws, result);
CreateFunctionType(ast, arg_type_result, return_type_result, escaping, throws,
result);
}


Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/LoadableByAddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2553,6 +2553,7 @@ void LoadableByAddress::run() {
}
break;
}
case SILInstructionKind::ConvertFunctionInst:
case SILInstructionKind::ThinFunctionToPointerInst:
case SILInstructionKind::ThinToThickFunctionInst: {
conversionInstrs.insert(
Expand Down
11 changes: 7 additions & 4 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1681,11 +1681,14 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
}

// @noescape is deprecated and no longer used
diagnose(Loc, Context.isSwiftVersion3()
? diag::swift3_attr_noescape_deprecated
: diag::attr_noescape_deprecated)
// In SIL, the polarity of @escaping is reversed.
// @escaping is the default and @noescape is explicit.
if (!isInSILMode()) {
diagnose(Loc, Context.isSwiftVersion3()
? diag::swift3_attr_noescape_deprecated
: diag::attr_noescape_deprecated)
.fixItRemove({Attributes.AtLoc, Loc});

}
break;
case TAK_escaping:
// You can't specify @noescape and @escaping together.
Expand Down
5 changes: 2 additions & 3 deletions lib/SIL/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -951,11 +951,10 @@ static CanSILFunctionType getSILFunctionType(SILModule &M,
bool pseudogeneric = (constant ? isPseudogeneric(*constant) : false);

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

return SILFunctionType::get(genericSig,
silExtInfo, calleeConvention,
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/SILModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ SILFunction *SILModule::getOrCreateFunction(
CanSILFunctionType type, IsBare_t isBareSILFunction,
IsTransparent_t isTransparent, IsSerialized_t isSerialized,
ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope subclassScope) {
assert(!type->isNoEscape() && "Function decls always have escaping types.");
if (auto fn = lookUpFunction(name)) {
assert(fn->getLoweredFunctionType() == type);
assert(fn->getLinkage() == linkage ||
Expand Down
12 changes: 8 additions & 4 deletions lib/SILGen/SILGenBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -881,10 +881,14 @@ SILGenFunction::emitBlockToFunc(SILLocation loc,

// Create it in the current function.
auto thunkValue = B.createFunctionRef(loc, thunk);
auto thunkedFn = B.createPartialApply(loc, thunkValue,
SILType::getPrimitiveObjectType(substFnTy),
subs, block.forward(*this),
SILType::getPrimitiveObjectType(loweredFuncTy));
SingleValueInstruction *thunkedFn = B.createPartialApply(
loc, thunkValue, SILType::getPrimitiveObjectType(substFnTy), subs,
block.forward(*this), SILType::getPrimitiveObjectType(loweredFuncTy));
if (loweredFuncTy->isNoEscape()) {
auto &funcTL = getTypeLowering(loweredFuncTy);
thunkedFn =
B.createConvertFunction(loc, thunkedFn, funcTL.getLoweredType());
}
return emitManagedRValueWithCleanup(thunkedFn);
}

Expand Down
Loading