Skip to content

Commit 8054e7d

Browse files
Merge pull request #16236 from aschwaighofer/objc_without_actually_escaping_noescape_wip
Implement verification that noescape closures passed to Objective-C are not escaped
2 parents 9c22fb5 + 1f65ee2 commit 8054e7d

Some content is hidden

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

42 files changed

+802
-113
lines changed

docs/SIL.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2880,6 +2880,25 @@ Performs a copy of an Objective-C block. Unlike retains of other
28802880
reference-counted types, this can produce a different value from the operand
28812881
if the block is copied from the stack to the heap.
28822882

2883+
copy_block_without_escaping
2884+
``````````
2885+
::
2886+
2887+
sil-instruction :: 'copy_block_without_escaping' sil-operand 'withoutEscaping' sil-operand
2888+
2889+
%1 = copy_block %0 : $@convention(block) T -> U withoutEscaping %1 : $T -> U
2890+
2891+
Performs a copy of an Objective-C block. Unlike retains of other
2892+
reference-counted types, this can produce a different value from the operand if
2893+
the block is copied from the stack to the heap.
2894+
2895+
Additionally, consumes the ``withoutEscaping`` operand ``%1`` which is the
2896+
closure sentinel. SILGen emits these instructions when it passes @noescape
2897+
swift closures to Objective C. A mandatory SIL pass will lower this instruction
2898+
into a ``copy_block`` and a ``is_escaping``/``cond_fail``/``destroy_value`` at
2899+
the end of the lifetime of the objective c closure parameter to check whether
2900+
the sentinel closure was escaped.
2901+
28832902
builtin "unsafeGuaranteed"
28842903
``````````````````````````
28852904

include/swift/Runtime/HeapObject.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,19 @@ bool swift_isUniquelyReferencedOrPinned_nonNull_native(
286286
/// one.
287287
/// This runtime call will print an error message with file name and location if
288288
/// the closure is escaping but it will not abort.
289+
///
290+
/// \p type: 0 - withoutActuallyEscaping verification
291+
/// Was the closure passed to a withoutActuallyEscaping block
292+
/// escaped in the block?
293+
/// 1 - @objc closure sentinel verfication
294+
/// Was the closure passed to Objective-C escaped?
289295
SWIFT_RUNTIME_EXPORT
290296
bool swift_isEscapingClosureAtFileLocation(const struct HeapObject *object,
291297
const unsigned char *filename,
292298
int32_t filenameLength,
293-
int32_t line);
299+
int32_t line,
300+
int32_t column,
301+
unsigned type);
294302

295303
/// Deallocate the given memory.
296304
///

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -680,11 +680,13 @@ FUNCTION(IsUniquelyReferencedOrPinned_nonNull_native,
680680
// bool swift_isEscapingClosureAtFileLocation(const struct HeapObject *object,
681681
// const unsigned char *filename,
682682
// int32_t filenameLength,
683-
// int32_t line);
683+
// int32_t line,
684+
// int32_t col,
685+
// unsigned type);
684686
FUNCTION(IsEscapingClosureAtFileLocation, swift_isEscapingClosureAtFileLocation,
685687
C_CC,
686688
RETURNS(Int1Ty),
687-
ARGS(RefCountedPtrTy, Int8PtrTy, Int32Ty, Int32Ty),
689+
ARGS(RefCountedPtrTy, Int8PtrTy, Int32Ty, Int32Ty, Int32Ty, Int32Ty),
688690
ATTRS(NoUnwind, ZExt))
689691

690692
// void swift_arrayInitWithCopy(opaque*, opaque*, size_t, type*);

include/swift/SIL/SILBuilder.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,6 +1482,14 @@ class SILBuilder {
14821482
return insert(new (getModule())
14831483
CopyBlockInst(getSILDebugLocation(Loc), Operand));
14841484
}
1485+
1486+
CopyBlockWithoutEscapingInst *
1487+
createCopyBlockWithoutEscaping(SILLocation Loc, SILValue Block,
1488+
SILValue Closure) {
1489+
return insert(new (getModule()) CopyBlockWithoutEscapingInst(
1490+
getSILDebugLocation(Loc), Block, Closure));
1491+
}
1492+
14851493
StrongRetainInst *createStrongRetain(SILLocation Loc, SILValue Operand,
14861494
Atomicity atomicity) {
14871495
assert(isParsing || !getFunction().hasQualifiedOwnership());
@@ -1561,10 +1569,11 @@ class SILBuilder {
15611569
getSILDebugLocation(Loc), value, Int1Ty));
15621570
}
15631571
IsEscapingClosureInst *createIsEscapingClosure(SILLocation Loc,
1564-
SILValue operand) {
1572+
SILValue operand,
1573+
unsigned VerificationType) {
15651574
auto Int1Ty = SILType::getBuiltinIntegerType(1, getASTContext());
15661575
return insert(new (getModule()) IsEscapingClosureInst(
1567-
getSILDebugLocation(Loc), operand, Int1Ty));
1576+
getSILDebugLocation(Loc), operand, Int1Ty, VerificationType));
15681577
}
15691578

15701579
DeallocStackInst *createDeallocStack(SILLocation Loc, SILValue operand) {

include/swift/SIL/SILCloner.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1880,6 +1880,16 @@ SILCloner<ImplClass>::visitCopyBlockInst(CopyBlockInst *Inst) {
18801880
getOpValue(Inst->getOperand())));
18811881
}
18821882

1883+
template <typename ImplClass>
1884+
void SILCloner<ImplClass>::visitCopyBlockWithoutEscapingInst(
1885+
CopyBlockWithoutEscapingInst *Inst) {
1886+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
1887+
doPostProcess(Inst,
1888+
Builder.createCopyBlockWithoutEscaping(
1889+
getOpLocation(Inst->getLoc()), getOpValue(Inst->getBlock()),
1890+
getOpValue(Inst->getClosure())));
1891+
}
1892+
18831893
template<typename ImplClass>
18841894
void
18851895
SILCloner<ImplClass>::visitStrongRetainInst(StrongRetainInst *Inst) {
@@ -2021,8 +2031,9 @@ void SILCloner<ImplClass>::visitIsEscapingClosureInst(
20212031
IsEscapingClosureInst *Inst) {
20222032
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
20232033
doPostProcess(
2024-
Inst, getBuilder().createIsEscapingClosure(
2025-
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand())));
2034+
Inst, getBuilder().createIsEscapingClosure(getOpLocation(Inst->getLoc()),
2035+
getOpValue(Inst->getOperand()),
2036+
Inst->getVerificationType()));
20262037
}
20272038

20282039
template<typename ImplClass>

include/swift/SIL/SILInstruction.h

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,6 +1279,8 @@ class AllocationInst : public SingleValueInstruction {
12791279
DEFINE_ABSTRACT_SINGLE_VALUE_INST_BOILERPLATE(AllocationInst)
12801280
};
12811281

1282+
class DeallocStackInst;
1283+
12821284
/// AllocStackInst - This represents the allocation of an unboxed (i.e., no
12831285
/// reference count) stack memory. The memory is provided uninitialized.
12841286
class AllocStackInst final
@@ -1351,6 +1353,9 @@ class AllocStackInst final
13511353
MutableArrayRef<Operand> getTypeDependentOperands() {
13521354
return getAllOperands();
13531355
}
1356+
1357+
/// Return a single dealloc_stack user or null.
1358+
DeallocStackInst *getSingleDeallocStack() const;
13541359
};
13551360

13561361
/// The base class for AllocRefInst and AllocRefDynamicInst.
@@ -6211,14 +6216,15 @@ class MarkDependenceInst
62116216
SingleValueInstruction> {
62126217
friend SILBuilder;
62136218

6214-
enum { Value, Base };
62156219
FixedOperandList<2> Operands;
62166220

62176221
MarkDependenceInst(SILDebugLocation DebugLoc, SILValue value, SILValue base)
62186222
: InstructionBase(DebugLoc, value->getType()),
62196223
Operands{this, value, base} {}
62206224

62216225
public:
6226+
enum { Value, Base };
6227+
62226228
SILValue getValue() const { return Operands[Value].get(); }
62236229
SILValue getBase() const { return Operands[Base].get(); }
62246230

@@ -6245,6 +6251,35 @@ class CopyBlockInst
62456251
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
62466252
};
62476253

6254+
class CopyBlockWithoutEscapingInst
6255+
: public InstructionBase<SILInstructionKind::CopyBlockWithoutEscapingInst,
6256+
SingleValueInstruction> {
6257+
friend SILBuilder;
6258+
6259+
FixedOperandList<2> Operands;
6260+
6261+
CopyBlockWithoutEscapingInst(SILDebugLocation DebugLoc, SILValue block,
6262+
SILValue closure)
6263+
: InstructionBase(DebugLoc, block->getType()), Operands{this, block,
6264+
closure} {}
6265+
6266+
public:
6267+
enum { Block, Closure };
6268+
6269+
SILValue getBlock() const { return Operands[Block].get(); }
6270+
SILValue getClosure() const { return Operands[Closure].get(); }
6271+
6272+
void setBlock(SILValue block) {
6273+
Operands[Block].set(block);
6274+
}
6275+
void setClosure(SILValue closure) {
6276+
Operands[Closure].set(closure);
6277+
}
6278+
6279+
ArrayRef<Operand> getAllOperands() const { return Operands.asArray(); }
6280+
MutableArrayRef<Operand> getAllOperands() { return Operands.asArray(); }
6281+
};
6282+
62486283
class CopyValueInst
62496284
: public UnaryInstructionBase<SILInstructionKind::CopyValueInst,
62506285
SingleValueInstruction> {
@@ -6306,9 +6341,17 @@ class IsEscapingClosureInst
63066341
SingleValueInstruction> {
63076342
friend SILBuilder;
63086343

6344+
unsigned VerificationType;
6345+
63096346
IsEscapingClosureInst(SILDebugLocation DebugLoc, SILValue Operand,
6310-
SILType BoolTy)
6311-
: UnaryInstructionBase(DebugLoc, Operand, BoolTy) {}
6347+
SILType BoolTy, unsigned VerificationType)
6348+
: UnaryInstructionBase(DebugLoc, Operand, BoolTy),
6349+
VerificationType(VerificationType) {}
6350+
6351+
public:
6352+
enum { WithoutActuallyEscaping, ObjCEscaping };
6353+
6354+
unsigned getVerificationType() const { return VerificationType; }
63126355
};
63136356

63146357
//===----------------------------------------------------------------------===//

include/swift/SIL/SILNodes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,8 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
361361
SingleValueInstruction, None, DoesNotRelease)
362362
SINGLE_VALUE_INST(CopyBlockInst, copy_block,
363363
SingleValueInstruction, MayHaveSideEffects, DoesNotRelease)
364+
SINGLE_VALUE_INST(CopyBlockWithoutEscapingInst, copy_block_without_escaping,
365+
SingleValueInstruction, MayHaveSideEffects, DoesNotRelease)
364366
SINGLE_VALUE_INST(CopyValueInst, copy_value,
365367
SingleValueInstruction, MayHaveSideEffects, DoesNotRelease)
366368
SINGLE_VALUE_INST(CopyUnownedValueInst, copy_unowned_value,

include/swift/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const uint16_t VERSION_MAJOR = 0;
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.
5757
/// Don't worry about adhering to the 80-column limit for this line.
58-
const uint16_t VERSION_MINOR = 410; // Last change: NameAlias substitution map
58+
const uint16_t VERSION_MINOR = 411; // Last change: copy_block_without_escaping
5959

6060
using DeclIDField = BCFixed<31>;
6161

lib/IRGen/AllocStackHoisting.cpp

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,7 @@ void Partition::assignStackLocation(
144144

145145
/// Returns a single dealloc_stack user of the alloc_stack or nullptr otherwise.
146146
static SILInstruction *getSingleDeallocStack(AllocStackInst *ASI) {
147-
SILInstruction *Dealloc = nullptr;
148-
for (auto *U : ASI->getUses()) {
149-
auto *Inst = U->getUser();
150-
if (isa<DeallocStackInst>(Inst)) {
151-
if (Dealloc == nullptr) {
152-
Dealloc = Inst;
153-
continue;
154-
}
155-
// Already saw a dealloc_stack.
156-
return nullptr;
157-
}
158-
}
159-
return Dealloc;
147+
return ASI->getSingleDeallocStack();
160148
}
161149

162150
namespace {

lib/IRGen/GenHeap.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,16 +1433,18 @@ emitIsUniqueCall(llvm::Value *value, SourceLoc loc, bool isNonNull,
14331433
return call;
14341434
}
14351435

1436-
llvm::Value *IRGenFunction::emitIsEscapingClosureCall(llvm::Value *value,
1437-
SourceLoc sourceLoc) {
1436+
llvm::Value *IRGenFunction::emitIsEscapingClosureCall(
1437+
llvm::Value *value, SourceLoc sourceLoc, unsigned verificationType) {
14381438
auto loc = SILLocation::decode(sourceLoc, IGM.Context.SourceMgr);
14391439
auto line = llvm::ConstantInt::get(IGM.Int32Ty, loc.Line);
1440+
auto col = llvm::ConstantInt::get(IGM.Int32Ty, loc.Column);
14401441
auto filename = IGM.getAddrOfGlobalString(loc.Filename);
14411442
auto filenameLength =
14421443
llvm::ConstantInt::get(IGM.Int32Ty, loc.Filename.size());
1444+
auto type = llvm::ConstantInt::get(IGM.Int32Ty, verificationType);
14431445
llvm::CallInst *call =
14441446
Builder.CreateCall(IGM.getIsEscapingClosureAtFileLocationFn(),
1445-
{value, filename, filenameLength, line});
1447+
{value, filename, filenameLength, line, col, type});
14461448
call->setDoesNotThrow();
14471449
return call;
14481450
}

lib/IRGen/IRGenFunction.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,9 +445,11 @@ class IRGenFunction {
445445
llvm::Value *emitIsUniqueCall(llvm::Value *value, SourceLoc loc,
446446
bool isNonNull, bool checkPinned);
447447

448-
llvm::Value *emitIsEscapingClosureCall(llvm::Value *value, SourceLoc loc);
448+
llvm::Value *emitIsEscapingClosureCall(llvm::Value *value, SourceLoc loc,
449+
unsigned verificationType);
449450

450-
//--- Expression emission ------------------------------------------------------
451+
//--- Expression emission
452+
//------------------------------------------------------
451453
public:
452454
void emitFakeExplosion(const TypeInfo &type, Explosion &explosion);
453455

lib/IRGen/IRGenSIL.cpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,9 @@ class IRGenSILFunction :
10391039
}
10401040
void visitMarkDependenceInst(MarkDependenceInst *i);
10411041
void visitCopyBlockInst(CopyBlockInst *i);
1042+
void visitCopyBlockWithoutEscapingInst(CopyBlockWithoutEscapingInst *i) {
1043+
llvm_unreachable("not valid in canonical SIL");
1044+
}
10421045
void visitStrongPinInst(StrongPinInst *i);
10431046
void visitStrongUnpinInst(StrongUnpinInst *i);
10441047
void visitStrongRetainInst(StrongRetainInst *i);
@@ -3894,20 +3897,30 @@ visitIsUniqueOrPinnedInst(swift::IsUniqueOrPinnedInst *i) {
38943897

38953898
void IRGenSILFunction::visitIsEscapingClosureInst(
38963899
swift::IsEscapingClosureInst *i) {
3897-
assert(i->getOperand()->getType().is<SILFunctionType>() &&
3898-
i->getOperand()
3899-
->getType()
3900-
.getAs<SILFunctionType>()
3901-
->getExtInfo()
3902-
.hasContext() &&
3903-
"Must have a closure operand");
3900+
// The closure operand is allowed to be an optional closure.
3901+
auto operandType = i->getOperand()->getType();
3902+
if (operandType.getOptionalObjectType())
3903+
operandType = operandType.getOptionalObjectType();
3904+
3905+
auto fnType = operandType.getAs<SILFunctionType>();
3906+
assert(fnType->getExtInfo().hasContext() && "Must have a closure operand");
3907+
3908+
// This code relies on that an optional<()->()>'s tag fits in the function
3909+
// pointer.
3910+
auto &TI = cast<LoadableTypeInfo>(getTypeInfo(operandType));
3911+
assert(TI.mayHaveExtraInhabitants(IGM) &&
3912+
"Must have extra inhabitants to be able to handle the optional "
3913+
"closure case");
39043914

39053915
Explosion closure = getLoweredExplosion(i->getOperand());
39063916
auto func = closure.claimNext();
39073917
(void)func;
39083918
auto context = closure.claimNext();
3909-
3910-
auto result = emitIsEscapingClosureCall(context, i->getLoc().getSourceLoc());
3919+
assert(closure.empty());
3920+
if (context->getType()->isIntegerTy())
3921+
context = Builder.CreateIntToPtr(context, IGM.RefCountedPtrTy);
3922+
auto result = emitIsEscapingClosureCall(context, i->getLoc().getSourceLoc(),
3923+
i->getVerificationType());
39113924
Explosion out;
39123925
out.add(result);
39133926
setLoweredExplosion(i, out);

lib/ParseSIL/ParseSIL.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2687,7 +2687,6 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
26872687
UNARY_INSTRUCTION(CopyBlock)
26882688
UNARY_INSTRUCTION(IsUnique)
26892689
UNARY_INSTRUCTION(IsUniqueOrPinned)
2690-
UNARY_INSTRUCTION(IsEscapingClosure)
26912690
UNARY_INSTRUCTION(DestroyAddr)
26922691
UNARY_INSTRUCTION(CopyValue)
26932692
UNARY_INSTRUCTION(CopyUnownedValue)
@@ -2715,6 +2714,20 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
27152714
#undef UNARY_INSTRUCTION
27162715
#undef REFCOUNTING_INSTRUCTION
27172716

2717+
case SILInstructionKind::IsEscapingClosureInst: {
2718+
bool IsObjcVerifcationType = false;
2719+
if (parseSILOptional(IsObjcVerifcationType, *this, "objc"))
2720+
return true;
2721+
if (parseTypedValueRef(Val, B) ||
2722+
parseSILDebugLocation(InstLoc, B))
2723+
return true;
2724+
ResultVal = B.createIsEscapingClosure(
2725+
InstLoc, Val,
2726+
IsObjcVerifcationType ? IsEscapingClosureInst::ObjCEscaping
2727+
: IsEscapingClosureInst::WithoutActuallyEscaping);
2728+
break;
2729+
}
2730+
27182731
case SILInstructionKind::DebugValueInst:
27192732
case SILInstructionKind::DebugValueAddrInst: {
27202733
SILDebugVariable VarInfo;
@@ -2811,6 +2824,18 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
28112824
break;
28122825
}
28132826

2827+
case SILInstructionKind::CopyBlockWithoutEscapingInst: {
2828+
SILValue Closure;
2829+
if (parseTypedValueRef(Val, B) ||
2830+
parseVerbatim("withoutEscaping") ||
2831+
parseTypedValueRef(Closure, B) ||
2832+
parseSILDebugLocation(InstLoc, B))
2833+
return true;
2834+
2835+
ResultVal = B.createCopyBlockWithoutEscaping(InstLoc, Val, Closure);
2836+
break;
2837+
}
2838+
28142839
case SILInstructionKind::MarkDependenceInst: {
28152840
SILValue Base;
28162841
if (parseTypedValueRef(Val, B) ||

0 commit comments

Comments
 (0)