Skip to content

Commit 264cbae

Browse files
committedJan 4, 2024
Add mark_dependence [nonescaping] flag.
The dependent 'value' may be marked 'nonescaping', which guarantees that the lifetime dependence is statically enforceable. In this case, the compiler must be able to follow all values forwarded from the dependent 'value', and recognize all final (non-forwarded, non-escaping) use points. This implies that `findPointerEscape` is false. A diagnostic pass checks that the incoming SIL to verify that these use points are all initially within the 'base' lifetime. Regular 'mark_dependence' semantics ensure that optimizations cannot violate the lifetime dependence after diagnostics.

28 files changed

+156
-51
lines changed
 

‎SwiftCompilerSources/Sources/SIL/Builder.swift

+5
Original file line numberDiff line numberDiff line change
@@ -375,4 +375,9 @@ public struct Builder {
375375
let endMutation = bridged.createEndCOWMutation(instance.bridged, keepUnique)
376376
return notifyNew(endMutation.getAs(EndCOWMutationInst.self))
377377
}
378+
379+
public func createMarkDependence(value: Value, base: Value, isNonEscaping: Bool) -> MarkDependenceInst {
380+
let markDependence = bridged.createMarkDependence(value.bridged, base.bridged, isNonEscaping)
381+
return notifyNew(markDependence.getAs(MarkDependenceInst.self))
382+
}
378383
}

‎SwiftCompilerSources/Sources/SIL/Instruction.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -824,13 +824,13 @@ class GetAsyncContinuationInst : SingleValueInstruction {}
824824
final public
825825
class GetAsyncContinuationAddrInst : SingleValueInstruction, UnaryInstruction {}
826826

827-
828827
final public
829828
class MarkDependenceInst : SingleValueInstruction, ForwardingInstruction {
830829
public var valueOperand: Operand { operands[0] }
831830
public var baseOperand: Operand { operands[1] }
832831
public var value: Value { return valueOperand.value }
833832
public var base: Value { return baseOperand.value }
833+
public var isNonEscaping: Bool { bridged.MarkDependenceInst_isNonEscaping() }
834834
}
835835

836836
final public class RefToBridgeObjectInst : SingleValueInstruction, ForwardingInstruction {

‎docs/SIL.rst

+29-18
Original file line numberDiff line numberDiff line change
@@ -5503,24 +5503,35 @@ mark_dependence
55035503

55045504
::
55055505

5506-
sil-instruction :: 'mark_dependence' sil-operand 'on' sil-operand
5507-
5508-
%2 = mark_dependence %0 : $*T on %1 : $Builtin.NativeObject
5509-
5510-
Indicates that the validity of the first operand depends on the value
5511-
of the second operand. Operations that would destroy the second value
5512-
must not be moved before any instructions which depend on the result
5513-
of this instruction, exactly as if the address had been obviously
5514-
derived from that operand (e.g. using ``ref_element_addr``).
5515-
5516-
The result is always equal to the first operand. The first operand
5517-
will typically be an address, but it could be an address in a
5518-
non-obvious form, such as a Builtin.RawPointer or a struct containing
5519-
the same. Transformations should be somewhat forgiving here.
5520-
5521-
The second operand may have either object or address type. In the
5522-
latter case, the dependency is on the current value stored in the
5523-
address.
5506+
sil-instruction :: 'mark_dependence' '[nonescaping]'? sil-operand 'on' sil-operand
5507+
5508+
%2 = mark_dependence %value : $*T on %base : $Builtin.NativeObject
5509+
5510+
Indicates that the validity of ``%value`` depends on the value of
5511+
``%base``. Operations that would destroy ``%base`` must not be moved
5512+
before any instructions which depend on the result of this
5513+
instruction, exactly as if the address had been directly derived from
5514+
that operand (e.g. using ``ref_element_addr``).
5515+
5516+
The result is the forwarded value of ``%value``. ``%value`` may be an
5517+
address, but it could be an address in a non-obvious form, such as a
5518+
Builtin.RawPointer or a struct containing the same.
5519+
5520+
``%base`` may have either object or address type. In the latter case,
5521+
the dependency is on the current value stored in the address.
5522+
5523+
The optional ``nonescaping`` attribute indicates that no value derived
5524+
from ``%value`` escapes the lifetime of ``%base``. As with escaping
5525+
``mark_dependence``, all values transitively forwarded from ``%value``
5526+
must be destroyed within the lifetime of ``%base``. Unlike escaping
5527+
``mark_dependence``, this must be statically verifiable. Additionally,
5528+
unlike escaping ``mark_dependence``, derived values include copies of
5529+
``%value`` and values transitively forwarded from those copies. If
5530+
``%base`` is identical to ``%value`` this simply means that copies of
5531+
``%value`` do not outlive the original OSSA lifetime of
5532+
``%value``. Furthermore, unlike escaping ``mark_dependence``, no value
5533+
derived from ``%value`` may have a bitwise escape (conversion to
5534+
UnsafePointer) or pointer escape (unknown use).
55245535

55255536
is_unique
55265537
`````````

‎include/swift/SIL/SILBridging.h

+2
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,7 @@ struct BridgedInstruction {
794794
BRIDGED_INLINE SwiftInt SwitchEnumInst_getCaseIndex(SwiftInt idx) const;
795795
BRIDGED_INLINE SwiftInt StoreInst_getStoreOwnership() const;
796796
BRIDGED_INLINE SwiftInt AssignInst_getAssignOwnership() const;
797+
BRIDGED_INLINE bool MarkDependenceInst_isNonEscaping() const;
797798
BRIDGED_INLINE AccessKind BeginAccessInst_getAccessKind() const;
798799
BRIDGED_INLINE bool BeginAccessInst_isStatic() const;
799800
BRIDGED_INLINE bool CopyAddrInst_isTakeOfSrc() const;
@@ -1118,6 +1119,7 @@ struct BridgedBuilder{
11181119
BridgedType::MetatypeRepresentation representation) const;
11191120
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createEndCOWMutation(BridgedValue instance,
11201121
bool keepUnique) const;
1122+
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createMarkDependence(BridgedValue value, BridgedValue base, bool isNonEscaping) const;
11211123
};
11221124

11231125
// Passmanager and Context

‎include/swift/SIL/SILBridgingImpl.h

+8
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,10 @@ SwiftInt BridgedInstruction::AssignInst_getAssignOwnership() const {
10401040
return (SwiftInt)getAs<swift::AssignInst>()->getOwnershipQualifier();
10411041
}
10421042

1043+
bool BridgedInstruction::MarkDependenceInst_isNonEscaping() const {
1044+
return getAs<swift::MarkDependenceInst>()->isNonEscaping();
1045+
}
1046+
10431047
BridgedInstruction::AccessKind BridgedInstruction::BeginAccessInst_getAccessKind() const {
10441048
return (AccessKind)getAs<swift::BeginAccessInst>()->getAccessKind();
10451049
}
@@ -1599,6 +1603,10 @@ BridgedInstruction BridgedBuilder::createEndCOWMutation(BridgedValue instance, b
15991603
keepUnique)};
16001604
}
16011605

1606+
BridgedInstruction BridgedBuilder::createMarkDependence(BridgedValue value, BridgedValue base, bool isNonEscaping) const {
1607+
return {unbridged().createMarkDependence(regularLoc(), value.getSILValue(), base.getSILValue(), isNonEscaping)};
1608+
}
1609+
16021610
SWIFT_END_NULLABILITY_ANNOTATIONS
16031611

16041612
#endif

‎include/swift/SIL/SILBuilder.h

+7-4
Original file line numberDiff line numberDiff line change
@@ -2284,15 +2284,18 @@ class SILBuilder {
22842284
SILValue value);
22852285

22862286
MarkDependenceInst *createMarkDependence(SILLocation Loc, SILValue value,
2287-
SILValue base) {
2288-
return createMarkDependence(Loc, value, base, value->getOwnershipKind());
2287+
SILValue base, bool isNonEscaping) {
2288+
return createMarkDependence(Loc, value, base, value->getOwnershipKind(),
2289+
isNonEscaping);
22892290
}
22902291

22912292
MarkDependenceInst *
22922293
createMarkDependence(SILLocation Loc, SILValue value, SILValue base,
2293-
ValueOwnershipKind forwardingOwnershipKind) {
2294+
ValueOwnershipKind forwardingOwnershipKind,
2295+
bool isNonEscaping) {
22942296
return insert(new (getModule()) MarkDependenceInst(
2295-
getSILDebugLocation(Loc), value, base, forwardingOwnershipKind));
2297+
getSILDebugLocation(Loc), value, base,
2298+
forwardingOwnershipKind, isNonEscaping));
22962299
}
22972300

22982301
IsUniqueInst *createIsUnique(SILLocation Loc, SILValue operand) {

‎include/swift/SIL/SILCloner.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -2950,8 +2950,9 @@ void SILCloner<ImplClass>::visitMarkDependenceInst(MarkDependenceInst *Inst) {
29502950
getOpLocation(Inst->getLoc()), getOpValue(Inst->getValue()),
29512951
getOpValue(Inst->getBase()),
29522952
getBuilder().hasOwnership()
2953-
? Inst->getForwardingOwnershipKind()
2954-
: ValueOwnershipKind(OwnershipKind::None)));
2953+
? Inst->getForwardingOwnershipKind()
2954+
: ValueOwnershipKind(OwnershipKind::None),
2955+
/*isNonEscaping*/false));
29552956
}
29562957

29572958
template<typename ImplClass>

‎include/swift/SIL/SILInstruction.h

+22-2
Original file line numberDiff line numberDiff line change
@@ -8290,17 +8290,33 @@ class UncheckedOwnershipConversionInst
82908290
/// result) have a dependence on "base" being alive. Do not allow for things
82918291
/// that /may/ destroy base to be moved earlier than any of these uses of
82928292
/// "value"'.
8293+
///
8294+
/// The dependent 'value' may be marked 'nonescaping', which guarantees that the
8295+
/// lifetime dependence is statically enforceable. In this case, the compiler
8296+
/// must be able to follow all values forwarded from the dependent 'value', and
8297+
/// recognize all final (non-forwarded, non-escaping) use points. This implies
8298+
/// that `findPointerEscape` is false. A diagnostic pass checks that the
8299+
/// incoming SIL to verify that these use points are all initially within the
8300+
/// 'base' lifetime. Regular 'mark_dependence' semantics ensure that
8301+
/// optimizations cannot violate the lifetime dependence after diagnostics.
82938302
class MarkDependenceInst
82948303
: public InstructionBase<SILInstructionKind::MarkDependenceInst,
82958304
OwnershipForwardingSingleValueInstruction> {
82968305
friend SILBuilder;
82978306

82988307
FixedOperandList<2> Operands;
82998308

8309+
USE_SHARED_UINT8;
8310+
83008311
MarkDependenceInst(SILDebugLocation DebugLoc, SILValue value, SILValue base,
8301-
ValueOwnershipKind forwardingOwnershipKind)
8312+
ValueOwnershipKind forwardingOwnershipKind,
8313+
bool isNonEscaping)
83028314
: InstructionBase(DebugLoc, value->getType(), forwardingOwnershipKind),
8303-
Operands{this, value, base} {}
8315+
Operands{this, value, base} {
8316+
if (isNonEscaping) {
8317+
sharedUInt8().MarkDependenceInst.nonEscaping = true;
8318+
}
8319+
}
83048320

83058321
public:
83068322
enum { Value, Base };
@@ -8318,6 +8334,10 @@ class MarkDependenceInst
83188334

83198335
ArrayRef<Operand> getAllOperands() const { return Operands.asArray(); }
83208336
MutableArrayRef<Operand> getAllOperands() { return Operands.asArray(); }
8337+
8338+
bool isNonEscaping() const {
8339+
return sharedUInt8().MarkDependenceInst.nonEscaping;
8340+
}
83218341
};
83228342

83238343
/// Promote an Objective-C block that is on the stack to the heap, or simply

‎include/swift/SIL/SILNode.h

+3
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ class alignas(8) SILNode :
276276
pointerEscape : 1,
277277
fromVarDecl : 1);
278278

279+
SHARED_FIELD(MarkDependenceInst, uint8_t
280+
nonEscaping : 1);
281+
279282
// Do not use `_sharedUInt8_private` outside of SILNode.
280283
} _sharedUInt8_private;
281284
// clang-format on

‎lib/IRGen/LoadableByAddress.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -2840,7 +2840,8 @@ bool LoadableByAddress::recreateConvInstr(SILInstruction &I,
28402840
case SILInstructionKind::MarkDependenceInst: {
28412841
auto instr = cast<MarkDependenceInst>(convInstr);
28422842
newInstr = convBuilder.createMarkDependence(
2843-
instr->getLoc(), instr->getValue(), instr->getBase());
2843+
instr->getLoc(), instr->getValue(), instr->getBase(),
2844+
instr->isNonEscaping());
28442845
break;
28452846
}
28462847
case SILInstructionKind::DifferentiableFunctionInst: {

‎lib/SIL/IR/SILPrinter.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -2526,6 +2526,9 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
25262526
*this << getIDAndType(CBOI->getOperand());
25272527
}
25282528
void visitMarkDependenceInst(MarkDependenceInst *MDI) {
2529+
if (MDI->isNonEscaping()) {
2530+
*this << "[nonescaping] ";
2531+
}
25292532
*this << getIDAndType(MDI->getValue()) << " on "
25302533
<< getIDAndType(MDI->getBase());
25312534
printForwardingOwnershipKind(MDI, MDI->getValue());

‎lib/SIL/Parser/ParseSIL.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -3756,17 +3756,20 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
37563756
}
37573757

37583758
case SILInstructionKind::MarkDependenceInst: {
3759+
bool nonEscaping = false;
37593760
SILValue Base;
3760-
if (parseTypedValueRef(Val, B) || parseVerbatim("on") ||
3761-
parseTypedValueRef(Base, B))
3761+
if (parseSILOptional(nonEscaping, *this, "nonescaping")
3762+
|| parseTypedValueRef(Val, B) || parseVerbatim("on")
3763+
|| parseTypedValueRef(Base, B))
37623764
return true;
37633765

37643766
ValueOwnershipKind forwardingOwnership = Val->getOwnershipKind();
37653767
if (parseForwardingOwnershipKind(forwardingOwnership)
37663768
|| parseSILDebugLocation(InstLoc, B))
37673769
return true;
37683770

3769-
ResultVal = B.createMarkDependence(InstLoc, Val, Base, forwardingOwnership);
3771+
ResultVal = B.createMarkDependence(InstLoc, Val, Base, forwardingOwnership,
3772+
nonEscaping);
37703773
break;
37713774
}
37723775

‎lib/SILGen/SILGenApply.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -2908,7 +2908,7 @@ class DelayedArgument {
29082908
if (valueTL.isTrivial()) {
29092909
SILValue dependentValue =
29102910
SGF.B.createMarkDependence(eval, value.forward(SGF),
2911-
owner.getValue());
2911+
owner.getValue(), /*isNonEscaping*/false);
29122912
value = SGF.emitManagedRValueWithCleanup(dependentValue, valueTL);
29132913
}
29142914
}
@@ -6352,7 +6352,8 @@ SILGenFunction::emitUninitializedArrayAllocation(Type ArrayTy,
63526352

63536353
// Add a mark_dependence between the interior pointer and the array value
63546354
auto dependentValue = B.createMarkDependence(Loc, resultElts[1].getValue(),
6355-
resultElts[0].getValue());
6355+
resultElts[0].getValue(),
6356+
/*isNonEscaping*/false);
63566357
return {resultElts[0], dependentValue};
63576358
}
63586359

‎lib/SILGen/SILGenBuilder.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -1024,10 +1024,12 @@ ManagedValue SILGenBuilder::createProjectBox(SILLocation loc, ManagedValue mv,
10241024

10251025
ManagedValue SILGenBuilder::createMarkDependence(SILLocation loc,
10261026
ManagedValue value,
1027-
ManagedValue base) {
1027+
ManagedValue base,
1028+
bool isNonEscaping) {
10281029
CleanupCloner cloner(*this, value);
10291030
auto *mdi = createMarkDependence(loc, value.forward(getSILGenFunction()),
1030-
base.forward(getSILGenFunction()));
1031+
base.forward(getSILGenFunction()),
1032+
isNonEscaping);
10311033
return cloner.clone(mdi);
10321034
}
10331035

‎lib/SILGen/SILGenBuilder.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ class SILGenBuilder : public SILBuilder {
461461

462462
using SILBuilder::createMarkDependence;
463463
ManagedValue createMarkDependence(SILLocation loc, ManagedValue value,
464-
ManagedValue base);
464+
ManagedValue base, bool isNonEscaping);
465465

466466
using SILBuilder::createBeginBorrow;
467467
ManagedValue createBeginBorrow(SILLocation loc, ManagedValue value,

‎lib/SILGen/SILGenBuiltin.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1442,7 +1442,8 @@ static ManagedValue emitBuiltinConvertUnownedUnsafeToGuaranteed(
14421442
auto guaranteedNonTrivialRefMV =
14431443
SGF.emitManagedBorrowedRValueWithCleanup(guaranteedNonTrivialRef);
14441444
// Now create a mark dependence on our base and return the result.
1445-
return SGF.B.createMarkDependence(loc, guaranteedNonTrivialRefMV, baseMV);
1445+
return SGF.B.createMarkDependence(loc, guaranteedNonTrivialRefMV, baseMV,
1446+
/*isNonEscaping*/false);
14461447
}
14471448

14481449
// Emit SIL for the named builtin: getCurrentAsyncTask.

‎lib/SILGen/SILGenExpr.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -5876,7 +5876,8 @@ class AutoreleasingWritebackComponent : public LogicalPathComponent {
58765876
// is important to ensure that the destroy of the assign is not hoisted
58775877
// above the retain. We are doing unmanaged things here so we need to be
58785878
// extra careful.
5879-
ownedMV = SGF.B.createMarkDependence(loc, ownedMV, base);
5879+
ownedMV = SGF.B.createMarkDependence(loc, ownedMV, base,
5880+
/*isNonEscaping*/false);
58805881

58815882
// Then reassign the mark dependence into the +1 storage.
58825883
ownedMV.assignInto(SGF, loc, base.getUnmanagedValue());
@@ -6191,7 +6192,8 @@ SILGenFunction::emitArrayToPointer(SILLocation loc, ManagedValue array,
61916192
// Mark the dependence of the pointer on the owner value.
61926193
auto owner = resultScalars[0];
61936194
auto pointer = resultScalars[1].forward(*this);
6194-
pointer = B.createMarkDependence(loc, pointer, owner.getValue());
6195+
pointer = B.createMarkDependence(loc, pointer, owner.getValue(),
6196+
/*isNonEscaping*/false);
61956197

61966198
// The owner's already in its own cleanup. Return the pointer.
61976199
return {ManagedValue::forObjectRValueWithoutOwnership(pointer), owner};
@@ -6226,7 +6228,8 @@ SILGenFunction::emitStringToPointer(SILLocation loc, ManagedValue stringValue,
62266228
// Mark the dependence of the pointer on the owner value.
62276229
auto owner = results[0];
62286230
auto pointer = results[1].forward(*this);
6229-
pointer = B.createMarkDependence(loc, pointer, owner.getValue());
6231+
pointer = B.createMarkDependence(loc, pointer, owner.getValue(),
6232+
/*isNonEscaping*/false);
62306233

62316234
return {ManagedValue::forObjectRValueWithoutOwnership(pointer), owner};
62326235
}

‎lib/SILGen/SILGenPoly.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -5707,7 +5707,8 @@ SILGenFunction::createWithoutActuallyEscapingClosure(
57075707
// long as we represent these captures by the same value the following works.
57085708
thunkedFn = emitManagedRValueWithCleanup(
57095709
B.createMarkDependence(loc, thunkedFn.forward(*this),
5710-
noEscapingFunctionValue.getValue()));
5710+
noEscapingFunctionValue.getValue(),
5711+
/*isNonEscaping*/false));
57115712

57125713
return thunkedFn;
57135714
}

‎lib/SILOptimizer/IPO/ClosureSpecializer.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,8 @@ SILValue ClosureSpecCloner::cloneCalleeConversion(
783783
return addToOldToNewClosureMap(
784784
origCalleeValue,
785785
Builder.createMarkDependence(CallSiteDesc.getLoc(), calleeValue,
786-
CapturedMap[MD->getBase()]));
786+
CapturedMap[MD->getBase()],
787+
/*isNonEscaping*/false));
787788
}
788789

789790

‎lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,9 @@ static void extendLifetimeToEndOfFunction(SILFunction &fn,
277277
// Create a borrow scope and a mark_dependence to prevent the enum being
278278
// optimized away.
279279
auto *borrow = lifetimeExtendBuilder.createBeginBorrow(loc, optionalSome);
280-
auto *mdi = lifetimeExtendBuilder.createMarkDependence(loc, cvt, borrow);
280+
auto *mdi =
281+
lifetimeExtendBuilder.createMarkDependence(loc, cvt, borrow,
282+
/*isNonEscaping*/false);
281283

282284
// Replace all uses of the non escaping closure with mark_dependence
283285
SmallVector<Operand *, 4> convertUses;
@@ -402,7 +404,8 @@ static SILValue insertMarkDependenceForCapturedArguments(PartialApplyInst *pai,
402404
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(arg.get()))
403405
if (m->hasGuaranteedInitialKind())
404406
continue;
405-
curr = b.createMarkDependence(pai->getLoc(), curr, arg.get());
407+
curr = b.createMarkDependence(pai->getLoc(), curr, arg.get(),
408+
/*isNonEscaping*/false);
406409
}
407410

408411
return curr;

‎lib/SILOptimizer/Mandatory/LowerHopToActor.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ SILValue LowerHopToActor::emitGetExecutor(SILBuilderWithScope &B,
228228

229229
// Mark the dependence of the resulting value on the actor value to
230230
// force the actor to stay alive.
231-
executor = B.createMarkDependence(loc, unmarkedExecutor, actor);
231+
executor = B.createMarkDependence(loc, unmarkedExecutor, actor,
232+
/*isNonEscaping*/false);
232233

233234
// Cache the non-optional result for later.
234235
ExecutorForActor.insert(actor, executor);

‎lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,8 @@ bool OwnershipModelEliminatorVisitor::visitPartialApplyInst(
450450
}
451451

452452
// If this is a nontrivial value argument, insert the mark_dependence.
453-
auto mdi = b.createMarkDependence(loc, newValue, op);
453+
auto mdi = b.createMarkDependence(loc, newValue, op,
454+
/*isNonEscaping*/false);
454455
if (!firstNewMDI)
455456
firstNewMDI = mdi;
456457
newValue = mdi;

0 commit comments

Comments
 (0)
Please sign in to comment.