Skip to content

Commit 1e4f55d

Browse files
Merge pull request #15046 from aschwaighofer/without_actually_escaping_verification
Implement withoutActuallyEscaping verification
2 parents fce138f + b960275 commit 1e4f55d

33 files changed

+351
-57
lines changed

docs/SIL.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2855,6 +2855,19 @@ memory object or a reference to a pinned object. Returns 1 if the
28552855
strong reference count is 1 or the object has been marked pinned by
28562856
strong_pin.
28572857

2858+
is_escaping_closure
2859+
```````````````````
2860+
2861+
::
2862+
sil-instruction ::= 'is_escaping_closure' sil-operand
2863+
2864+
%1 = is_escaping_closure %0 : $@callee_guaranteed () -> ()
2865+
// %0 must be an escaping swift closure.
2866+
// %1 will be of type Builtin.Int1
2867+
2868+
Checks whether the context reference is not nil and bigger than one and returns
2869+
true if it is.
2870+
28582871
copy_block
28592872
``````````
28602873
::

include/swift/Runtime/Debug.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#define _SWIFT_RUNTIME_DEBUG_HELPERS_
1919

2020
#include <llvm/Support/Compiler.h>
21+
#include <cstdarg>
22+
#include <cstdio>
2123
#include <stdint.h>
2224
#include "swift/Runtime/Config.h"
2325
#include "swift/Runtime/Unreachable.h"
@@ -222,6 +224,39 @@ void _swift_reportToDebugger(uintptr_t flags, const char *message,
222224
SWIFT_RUNTIME_STDLIB_SPI
223225
bool _swift_reportFatalErrorsToDebugger;
224226

227+
LLVM_ATTRIBUTE_ALWAYS_INLINE
228+
inline static int swift_asprintf(char **strp, const char *fmt, ...) {
229+
va_list args;
230+
va_start(args, fmt);
231+
#if defined(_WIN32)
232+
#pragma GCC diagnostic push
233+
#pragma GCC diagnostic ignored "-Wuninitialized"
234+
int len = _vscprintf(fmt, args);
235+
#pragma GCC diagnostic pop
236+
if (len < 0) {
237+
va_end(args);
238+
return -1;
239+
}
240+
char *buffer = static_cast<char *>(malloc(len + 1));
241+
if (!buffer) {
242+
va_end(args);
243+
return -1;
244+
}
245+
int result = vsprintf(buffer, fmt, args);
246+
if (result < 0) {
247+
va_end(args);
248+
free(buffer);
249+
return -1;
250+
}
251+
*strp = buffer;
252+
#else
253+
int result = vasprintf(strp, fmt, args);
254+
#endif
255+
va_end(args);
256+
return result;
257+
}
258+
259+
225260
// namespace swift
226261
}
227262

include/swift/Runtime/HeapObject.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,16 @@ SWIFT_RUNTIME_EXPORT
280280
bool swift_isUniquelyReferencedOrPinned_nonNull_native(
281281
const struct HeapObject *);
282282

283+
/// Is this native Swift pointer non-null and has a reference count greater than
284+
/// one.
285+
/// This runtime call will print an error message with file name and location if
286+
/// the closure is escaping but it will not abort.
287+
SWIFT_RUNTIME_EXPORT
288+
bool swift_isEscapingClosureAtFileLocation(const struct HeapObject *object,
289+
const unsigned char *filename,
290+
int32_t filenameLength,
291+
int32_t line);
292+
283293
/// Deallocate the given memory.
284294
///
285295
/// It must have been returned by swift_allocObject and the strong reference

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,16 @@ FUNCTION(IsUniquelyReferencedOrPinned_nonNull_native,
677677
ARGS(RefCountedPtrTy),
678678
ATTRS(NoUnwind, ZExt))
679679

680+
// bool swift_isEscapingClosureAtFileLocation(const struct HeapObject *object,
681+
// const unsigned char *filename,
682+
// int32_t filenameLength,
683+
// int32_t line);
684+
FUNCTION(IsEscapingClosureAtFileLocation, swift_isEscapingClosureAtFileLocation,
685+
C_CC,
686+
RETURNS(Int1Ty),
687+
ARGS(RefCountedPtrTy, Int8PtrTy, Int32Ty, Int32Ty),
688+
ATTRS(NoUnwind, ZExt))
689+
680690
// void swift_arrayInitWithCopy(opaque*, opaque*, size_t, type*);
681691
FUNCTION(ArrayInitWithCopy, swift_arrayInitWithCopy, C_CC,
682692
RETURNS(VoidTy),

include/swift/SIL/SILBuilder.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1538,6 +1538,12 @@ class SILBuilder {
15381538
return insert(new (getModule()) IsUniqueOrPinnedInst(
15391539
getSILDebugLocation(Loc), value, Int1Ty));
15401540
}
1541+
IsEscapingClosureInst *createIsEscapingClosure(SILLocation Loc,
1542+
SILValue operand) {
1543+
auto Int1Ty = SILType::getBuiltinIntegerType(1, getASTContext());
1544+
return insert(new (getModule()) IsEscapingClosureInst(
1545+
getSILDebugLocation(Loc), operand, Int1Ty));
1546+
}
15411547

15421548
DeallocStackInst *createDeallocStack(SILLocation Loc, SILValue operand) {
15431549
return insert(new (getModule())
@@ -1615,7 +1621,14 @@ class SILBuilder {
16151621
// Runtime failure
16161622
//===--------------------------------------------------------------------===//
16171623

1618-
CondFailInst *createCondFail(SILLocation Loc, SILValue Operand) {
1624+
CondFailInst *createCondFail(SILLocation Loc, SILValue Operand,
1625+
bool Inverted = false) {
1626+
if (Inverted) {
1627+
SILType Ty = Operand->getType();
1628+
SILValue True(createIntegerLiteral(Loc, Ty, 1));
1629+
Operand =
1630+
createBuiltinBinaryFunction(Loc, "xor", Ty, Ty, {Operand, True});
1631+
}
16191632
return insert(new (getModule())
16201633
CondFailInst(getSILDebugLocation(Loc), Operand));
16211634
}

include/swift/SIL/SILCloner.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2013,6 +2013,14 @@ visitIsUniqueOrPinnedInst(IsUniqueOrPinnedInst *Inst) {
20132013
getBuilder().createIsUniqueOrPinned(getOpLocation(Inst->getLoc()),
20142014
getOpValue(Inst->getOperand())));
20152015
}
2016+
template <typename ImplClass>
2017+
void SILCloner<ImplClass>::visitIsEscapingClosureInst(
2018+
IsEscapingClosureInst *Inst) {
2019+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
2020+
doPostProcess(
2021+
Inst, getBuilder().createIsEscapingClosure(
2022+
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand())));
2023+
}
20162024

20172025
template<typename ImplClass>
20182026
void

include/swift/SIL/SILInstruction.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6194,6 +6194,18 @@ class IsUniqueOrPinnedInst
61946194
: UnaryInstructionBase(DebugLoc, Operand, BoolTy) {}
61956195
};
61966196

6197+
/// Given an escaping closure return true iff it has a non-nil context and the
6198+
/// context has a strong reference count greater than 1.
6199+
class IsEscapingClosureInst
6200+
: public UnaryInstructionBase<SILInstructionKind::IsEscapingClosureInst,
6201+
SingleValueInstruction> {
6202+
friend SILBuilder;
6203+
6204+
IsEscapingClosureInst(SILDebugLocation DebugLoc, SILValue Operand,
6205+
SILType BoolTy)
6206+
: UnaryInstructionBase(DebugLoc, Operand, BoolTy) {}
6207+
};
6208+
61976209
//===----------------------------------------------------------------------===//
61986210
// DeallocationInsts
61996211
//===----------------------------------------------------------------------===//

include/swift/SIL/SILNodes.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,9 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
379379
SINGLE_VALUE_INST(IsUniqueOrPinnedInst, is_unique_or_pinned,
380380
SingleValueInstruction, MayHaveSideEffects, DoesNotRelease)
381381

382+
SINGLE_VALUE_INST(IsEscapingClosureInst, is_escaping_closure,
383+
SingleValueInstruction, MayRead, DoesNotRelease)
384+
382385
// Accessing memory
383386
SINGLE_VALUE_INST(LoadInst, load,
384387
SingleValueInstruction, MayRead, DoesNotRelease)

lib/IRGen/GenHeap.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include "swift/Basic/SourceLoc.h"
2626
#include "swift/ABI/MetadataValues.h"
27+
#include "swift/AST/ASTContext.h"
2728
#include "swift/AST/GenericEnvironment.h"
2829
#include "swift/AST/IRGenOptions.h"
2930

@@ -1430,6 +1431,20 @@ emitIsUniqueCall(llvm::Value *value, SourceLoc loc, bool isNonNull,
14301431
return call;
14311432
}
14321433

1434+
llvm::Value *IRGenFunction::emitIsEscapingClosureCall(llvm::Value *value,
1435+
SourceLoc sourceLoc) {
1436+
auto loc = SILLocation::decode(sourceLoc, IGM.Context.SourceMgr);
1437+
auto line = llvm::ConstantInt::get(IGM.Int32Ty, loc.Line);
1438+
auto filename = IGM.getAddrOfGlobalString(loc.Filename);
1439+
auto filenameLength =
1440+
llvm::ConstantInt::get(IGM.Int32Ty, loc.Filename.size());
1441+
llvm::CallInst *call =
1442+
Builder.CreateCall(IGM.getIsEscapingClosureAtFileLocationFn(),
1443+
{value, filename, filenameLength, line});
1444+
call->setDoesNotThrow();
1445+
return call;
1446+
}
1447+
14331448
namespace {
14341449
/// Basic layout and common operations for box types.
14351450
class BoxTypeInfo : public HeapTypeInfo<BoxTypeInfo> {

lib/IRGen/IRGenFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ class IRGenFunction {
435435
llvm::Value *emitIsUniqueCall(llvm::Value *value, SourceLoc loc,
436436
bool isNonNull, bool checkPinned);
437437

438+
llvm::Value *emitIsEscapingClosureCall(llvm::Value *value, SourceLoc loc);
439+
438440
//--- Expression emission ------------------------------------------------------
439441
public:
440442
void emitFakeExplosion(const TypeInfo &type, Explosion &explosion);

lib/IRGen/IRGenSIL.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,7 @@ class IRGenSILFunction :
10271027
void visitStoreUnownedInst(StoreUnownedInst *i);
10281028
void visitIsUniqueInst(IsUniqueInst *i);
10291029
void visitIsUniqueOrPinnedInst(IsUniqueOrPinnedInst *i);
1030+
void visitIsEscapingClosureInst(IsEscapingClosureInst *i);
10301031
void visitDeallocStackInst(DeallocStackInst *i);
10311032
void visitDeallocBoxInst(DeallocBoxInst *i);
10321033
void visitDeallocRefInst(DeallocRefInst *i);
@@ -3883,6 +3884,27 @@ visitIsUniqueOrPinnedInst(swift::IsUniqueOrPinnedInst *i) {
38833884
setLoweredExplosion(i, out);
38843885
}
38853886

3887+
void IRGenSILFunction::visitIsEscapingClosureInst(
3888+
swift::IsEscapingClosureInst *i) {
3889+
assert(i->getOperand()->getType().is<SILFunctionType>() &&
3890+
i->getOperand()
3891+
->getType()
3892+
.getAs<SILFunctionType>()
3893+
->getExtInfo()
3894+
.hasContext() &&
3895+
"Must have a closure operand");
3896+
3897+
Explosion closure = getLoweredExplosion(i->getOperand());
3898+
auto func = closure.claimNext();
3899+
(void)func;
3900+
auto context = closure.claimNext();
3901+
3902+
auto result = emitIsEscapingClosureCall(context, i->getLoc().getSourceLoc());
3903+
Explosion out;
3904+
out.add(result);
3905+
setLoweredExplosion(i, out);
3906+
}
3907+
38863908
void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i,
38873909
const TypeInfo &type,
38883910
llvm::Value *addr) {

lib/ParseSIL/ParseSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2655,6 +2655,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
26552655
UNARY_INSTRUCTION(CopyBlock)
26562656
UNARY_INSTRUCTION(IsUnique)
26572657
UNARY_INSTRUCTION(IsUniqueOrPinned)
2658+
UNARY_INSTRUCTION(IsEscapingClosure)
26582659
UNARY_INSTRUCTION(DestroyAddr)
26592660
UNARY_INSTRUCTION(CopyValue)
26602661
UNARY_INSTRUCTION(CopyUnownedValue)

lib/SIL/InstructionUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,7 @@ void swift::visitAccessedAddress(SILInstruction *I,
753753
case SILInstructionKind::FixLifetimeInst:
754754
case SILInstructionKind::InitExistentialValueInst:
755755
case SILInstructionKind::IsUniqueInst:
756+
case SILInstructionKind::IsEscapingClosureInst:
756757
case SILInstructionKind::IsUniqueOrPinnedInst:
757758
case SILInstructionKind::KeyPathInst:
758759
case SILInstructionKind::OpenExistentialBoxInst:

lib/SIL/SILInstruction.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,7 @@ bool SILInstruction::mayRelease() const {
10681068
bool SILInstruction::mayReleaseOrReadRefCount() const {
10691069
switch (getKind()) {
10701070
case SILInstructionKind::IsUniqueInst:
1071+
case SILInstructionKind::IsEscapingClosureInst:
10711072
case SILInstructionKind::IsUniqueOrPinnedInst:
10721073
return true;
10731074
default:

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ NO_OPERAND_INST(Unwind)
450450
return {compatibleWithOwnership(ValueOwnershipKind::OWNERSHIP), \
451451
UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT}; \
452452
}
453+
CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, IsEscapingClosure)
453454
CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, RefElementAddr)
454455
CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialValue)
455456
CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialBoxValue)

lib/SIL/SILPrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,9 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
17951795
void visitIsUniqueOrPinnedInst(IsUniqueOrPinnedInst *CUI) {
17961796
*this << getIDAndType(CUI->getOperand());
17971797
}
1798+
void visitIsEscapingClosureInst(IsEscapingClosureInst *CUI) {
1799+
*this << getIDAndType(CUI->getOperand());
1800+
}
17981801
void visitDeallocStackInst(DeallocStackInst *DI) {
17991802
*this << getIDAndType(DI->getOperand());
18001803
}

lib/SIL/SILVerifier.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4082,6 +4082,16 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
40824082
"final component should match leaf value type of key path type");
40834083
}
40844084

4085+
void checkIsEscapingClosureInst(IsEscapingClosureInst *IEC) {
4086+
auto fnType = IEC->getOperand()->getType().getAs<SILFunctionType>();
4087+
require(fnType && fnType->getExtInfo().hasContext() &&
4088+
!fnType->isNoEscape() &&
4089+
fnType->getExtInfo().getRepresentation() ==
4090+
SILFunctionTypeRepresentation::Thick,
4091+
"is_escaping_closure must have a thick "
4092+
"function operand");
4093+
}
4094+
40854095
// This verifies that the entry block of a SIL function doesn't have
40864096
// any predecessors and also verifies the entry point arguments.
40874097
void verifyEntryBlock(SILBasicBlock *entry) {

lib/SIL/ValueOwnershipKindClassifier.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ CONSTANT_OWNERSHIP_INST(Trivial, InitExistentialMetatype)
7575
CONSTANT_OWNERSHIP_INST(Trivial, IntegerLiteral)
7676
CONSTANT_OWNERSHIP_INST(Trivial, IsUnique)
7777
CONSTANT_OWNERSHIP_INST(Trivial, IsUniqueOrPinned)
78+
CONSTANT_OWNERSHIP_INST(Trivial, IsEscapingClosure)
7879
CONSTANT_OWNERSHIP_INST(Trivial, MarkUninitializedBehavior)
7980
CONSTANT_OWNERSHIP_INST(Trivial, Metatype)
8081
CONSTANT_OWNERSHIP_INST(Trivial, ObjCToThickMetatype)

lib/SILGen/SILGenExpr.cpp

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5332,29 +5332,41 @@ RValue RValueEmitter::visitOpenExistentialExpr(OpenExistentialExpr *E,
53325332
}
53335333

53345334
RValue RValueEmitter::visitMakeTemporarilyEscapableExpr(
5335-
MakeTemporarilyEscapableExpr *E,
5336-
SGFContext C) {
5335+
MakeTemporarilyEscapableExpr *E, SGFContext C) {
53375336
// Emit the non-escaping function value.
53385337
auto functionValue =
53395338
visit(E->getNonescapingClosureValue()).getAsSingleValue(SGF, E);
53405339

53415340
auto escapingFnTy = SGF.getLoweredType(E->getOpaqueValue()->getType());
53425341

53435342
// Convert it to an escaping function value.
5344-
functionValue =
5343+
auto escapingClosure =
53455344
SGF.createWithoutActuallyEscapingClosure(E, functionValue, escapingFnTy);
53465345

5347-
// Bind the opaque value to the escaping function.
5348-
SILGenFunction::OpaqueValueState opaqueValue{
5349-
functionValue,
5350-
/*consumable*/ true,
5351-
/*hasBeenConsumed*/ false,
5352-
};
5353-
SILGenFunction::OpaqueValueRAII pushOpaqueValue(SGF, E->getOpaqueValue(),
5354-
opaqueValue);
5346+
RValue rvalue;
5347+
auto loc = SILLocation(E);
5348+
auto borrowedClosure = escapingClosure.borrow(SGF, loc);
5349+
{
5350+
// Bind the opaque value to the escaping function.
5351+
SILGenFunction::OpaqueValueState opaqueValue{
5352+
borrowedClosure,
5353+
/*consumable*/ false,
5354+
/*hasBeenConsumed*/ false,
5355+
};
5356+
SILGenFunction::OpaqueValueRAII pushOpaqueValue(SGF, E->getOpaqueValue(),
5357+
opaqueValue);
53555358

5356-
// Emit the guarded expression.
5357-
return visit(E->getSubExpr(), C);
5359+
// Emit the guarded expression.
5360+
rvalue = visit(E->getSubExpr(), C);
5361+
}
5362+
5363+
// Now create the verification of the withoutActuallyEscaping operand.
5364+
// Either we fail the uniquenes check (which means the closure has escaped)
5365+
// and abort or we continue and destroy the ultimate reference.
5366+
auto isEscaping =
5367+
SGF.B.createIsEscapingClosure(loc, borrowedClosure.getValue());
5368+
SGF.B.createCondFail(loc, isEscaping);
5369+
return rvalue;
53585370
}
53595371

53605372
RValue RValueEmitter::visitOpaqueValueExpr(OpaqueValueExpr *E, SGFContext C) {

lib/SILGen/SILGenPoly.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
//===----------------------------------------------------------------------===//
8383

8484
#include "SILGen.h"
85+
#include "SILGenFunction.h"
8586
#include "Scope.h"
8687
#include "swift/AST/GenericSignatureBuilder.h"
8788
#include "swift/AST/Decl.h"
@@ -3075,7 +3076,8 @@ static void buildWithoutActuallyEscapingThunkBody(SILGenFunction &SGF) {
30753076
SGF.B.createReturn(loc, result);
30763077
}
30773078

3078-
ManagedValue SILGenFunction::createWithoutActuallyEscapingClosure(
3079+
ManagedValue
3080+
SILGenFunction::createWithoutActuallyEscapingClosure(
30793081
SILLocation loc, ManagedValue noEscapingFunctionValue, SILType escapingTy) {
30803082

30813083
auto escapingFnTy = escapingTy.castTo<SILFunctionType>();
@@ -3117,7 +3119,6 @@ ManagedValue SILGenFunction::createWithoutActuallyEscapingClosure(
31173119
loc, thunkValue, SILType::getPrimitiveObjectType(substFnType), subs,
31183120
noEscapeValue,
31193121
SILType::getPrimitiveObjectType(escapingFnTy));
3120-
31213122
// We need to ensure the 'lifetime' of the trivial values context captures. As
31223123
// long as we rerpresent these captures by the same value the following works.
31233124
thunkedFn = B.createMarkDependence(loc, thunkedFn, noEscapeValue);

0 commit comments

Comments
 (0)