diff --git a/docs/SIL.rst b/docs/SIL.rst index 0864b265f77..4691a7fa0f0 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -1425,6 +1425,108 @@ of the values from which the addresses were derived, ignoring "blessed" alias-introducing operations such as ``pointer_to_address``, the ``bitcast`` intrinsic, and the ``inttoptr`` intrinsic. +Value Dependence +---------------- + +In general, analyses can assume that independent values are +independently assured of validity. For example, a class method may +return a class reference:: + + bb0(%0 : $MyClass): + %1 = class_method %0 : $MyClass, #MyClass.foo!1 + %2 = apply %1(%0) : $@cc(method) @thin (@guaranteed MyClass) -> @owned MyOtherClass + // use of %2 goes here; no use of %1 + strong_release %2 : $MyOtherClass + strong_release %1 : $MyClass + +The optimizer is free to move the release of ``%1`` to immediately +after the call here, because ``%2`` can be assumed to be an +independently-managed value, and because Swift generally permits the +reordering of destructors. + +However, some instructions do create values that are intrinsically +dependent on their operands. For example, the result of +``ref_element_addr`` will become a dangling pointer if the base is +released too soon. This is captured by the concept of *value dependence*, +and any transformation which can reorder of destruction of a value +around another operation must remain conscious of it. + +A value ``%1`` is said to be *value-dependent* on a value ``%0`` if: + +- ``%1`` is the result and ``%0`` is the first operand of one of the + following instructions: + + - ``ref_element_addr`` + - ``struct_element_addr`` + - ``tuple_element_addr`` + - ``unchecked_take_enum_data_addr`` + - ``pointer_to_address`` + - ``address_to_pointer`` + - ``index_addr`` + - ``index_raw_pointer`` + - possibly some other conversions + +- ``%1`` is the result of ``mark_dependence`` and ``%0`` is either of + the operands. + +- ``%1`` is the value address of an allocation instruction of which + ``%0`` is the local storage token or box reference. + +- ``%1`` is the result of a ``struct``, ``tuple``, or ``enum`` + instruction and ``%0`` is an operand. + +- ``%1`` is the result of projecting out a subobject of ``%0`` + with ``tuple_extract``, ``struct_extract``, ``unchecked_enum_data``, + ``select_enum``, or ``select_enum_addr``. + +- ``%1`` is the result of ``select_value`` and ``%0`` is one of the cases. + +- ``%1`` is a basic block parameter and ``%0`` is the corresponding + argument from a branch to that block. + +- ``%1`` is the result of a ``load`` from ``%0``. However, the value + dependence is cut after the first attempt to manage the value of + ``%1``, e.g. by retaining it. + +- Transitivity: there exists a value ``%2`` which ``%1`` depends on + and which depends on ``%0``. However, transitivity does not apply + to different subobjects of a struct, tuple, or enum. + +Note, however, that an analysis is not required to track dependence +through memory. Nor is it required to consider the possibility of +dependence being established "behind the scenes" by opaque code, such +as by a method returning an unsafe pointer to a class property. The +dependence is required to be locally obvious in a function's SIL +instructions. Precautions must be taken against this either by SIL +generators (by using ``mark_dependence`` appropriately) or by the user +(by using the appropriate intrinsics and attributes with unsafe +language or library features). + +Only certain types of SIL value can carry value-dependence: + +- SIL address types +- unmanaged pointer types: + - ``@sil_unmanaged`` types + - ``Builtin.RawPointer`` + - aggregates containing such a type, such as ``UnsafePointer``, + possibly recursively +- non-trivial types (but they can be independently managed) + +This rule means that casting a pointer to an integer type breaks +value-dependence. This restriction is necessary so that reading an +``Int`` from a class doesn't force the class to be kept around! +A class holding an unsafe reference to an object must use some +sort of unmanaged pointer type to do so. + +This rule does not include generic or resilient value types which +might contain unmanaged pointer types. Analyses are free to assume +that e.g. a ``copy_addr`` of a generic or resilient value type yields +an independently-managed value. The extension of value dependence to +types containing obvious unmanaged pointer types is an affordance to +make the use of such types more convenient; it does not shift the +ultimate responsibility for assuring the safety of unsafe +language/library features away from the user. + Instruction Set --------------- @@ -2013,6 +2115,30 @@ address operand. Optimizations may not move operations that would destroy the value, such as ``release_value``, ``strong_release``, ``copy_addr [take]``, or ``destroy_addr``, past this instruction. +mark_dependence +``````````````` + +:: + + sil-instruction :: 'mark_dependence' sil-operand 'on' sil-operand + + %2 = mark_dependence %0 : $*T on %1 : $Builtin.NativeObject + +Indicates that the validity of the first operand depends on the value +of the second operand. Operations that would destroy the second value +must not be moved before any instructions which depend on the result +of this instruction, exactly as if the address had been obviously +derived from that operand (e.g. using ``ref_element_addr``). + +The result is always equal to the first operand. The first operand +will typically be an address, but it could be an address in a +non-obvious form, such as a Builtin.RawPointer or a struct containing +the same. Transformations should be somewhat forgiving here. + +The second operand may have either object or address type. In the +latter case, the dependency is on the current value stored in the +address. + copy_block `````````` :: diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 9be463e09a5..e9f9fd3256c 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -806,6 +806,11 @@ public: return; createFixLifetime(Loc, Operand); } + MarkDependenceInst *createMarkDependence(SILLocation loc, + SILValue value, + SILValue base) { + return insert(new (F.getModule()) MarkDependenceInst(loc, value, base)); + } DeallocStackInst *createDeallocStack(SILLocation loc, SILValue operand) { return insert(new (F.getModule()) DeallocStackInst(loc, operand)); diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index ba3173b2908..b11d3421c39 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1223,6 +1223,15 @@ SILCloner::visitFixLifetimeInst(FixLifetimeInst *Inst) { getOpValue(Inst->getOperand()))); } +template +void +SILCloner::visitMarkDependenceInst(MarkDependenceInst *Inst) { + doPostProcess(Inst, + getBuilder().createMarkDependence(getOpLocation(Inst->getLoc()), + getOpValue(Inst->getValue()), + getOpValue(Inst->getBase()))); +} + template void SILCloner::visitStrongReleaseInst(StrongReleaseInst *Inst) { diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 23480a8c8e5..8069905126a 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -2653,6 +2653,30 @@ public: : UnaryInstructionBase(Loc, Operand) {} }; +/// MarkDependenceInst - Marks that one value depends on another for +/// validity in a non-obvious way. +class MarkDependenceInst : public SILInstruction { + enum { Value, Base }; + FixedOperandList<2> Operands; +public: + MarkDependenceInst(SILLocation loc, SILValue value, SILValue base) + : SILInstruction(ValueKind::MarkDependenceInst, loc, value.getType()), + Operands{this, value, base} + {} + + SILValue getValue() const { return Operands[Value].get(); } + SILValue getBase() const { return Operands[Base].get(); } + + ArrayRef getAllOperands() const { return Operands.asArray(); } + MutableArrayRef getAllOperands() { return Operands.asArray(); } + + SILType getType(unsigned i = 0) const { return ValueBase::getType(i); } + + static bool classof(const ValueBase *V) { + return V->getKind() == ValueKind::MarkDependenceInst; + } +}; + /// Promote an Objective-C block that is on the stack to the heap, or simply /// retain a block that is already on the heap. class CopyBlockInst : diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index b5b11cc141f..30e93705f9a 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -108,6 +108,7 @@ ABSTRACT_VALUE(SILInstruction, ValueBase) VALUE_RANGE(RefCountingInst, StrongRetainInst, AutoreleaseValueInst) // FIXME: Is MayHaveSideEffects appropriate? INST(FixLifetimeInst, SILInstruction, MayHaveSideEffects) + INST(MarkDependenceInst, SILInstruction, None) INST(CopyBlockInst, SILInstruction, MayHaveSideEffects) // Literals diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 03d7713533b..af018ed290d 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -626,6 +626,7 @@ public: void visitInitBlockStorageHeaderInst(InitBlockStorageHeaderInst *i); void visitFixLifetimeInst(FixLifetimeInst *i); + void visitMarkDependenceInst(MarkDependenceInst *i); void visitCopyBlockInst(CopyBlockInst *i); void visitStrongRetainInst(StrongRetainInst *i); void visitStrongReleaseInst(StrongReleaseInst *i); @@ -2740,6 +2741,19 @@ void IRGenSILFunction::visitFixLifetimeInst(swift::FixLifetimeInst *i) { .fixLifetime(*this, in); } +void IRGenSILFunction::visitMarkDependenceInst(swift::MarkDependenceInst *i) { + // Dependency-marking is purely for SIL. Just forward the input as + // the result. + + SILValue value = i->getValue(); + if (value.getType().isAddress()) { + setLoweredAddress(i, getLoweredAddress(value)); + } else { + Explosion temp = getLoweredExplosion(value); + setLoweredExplosion(i, temp); + } +} + void IRGenSILFunction::visitCopyBlockInst(CopyBlockInst *i) { Explosion lowered = getLoweredExplosion(i->getOperand()); llvm::Value *copied = emitBlockCopyCall(lowered.claimNext()); diff --git a/lib/Parse/ParseSIL.cpp b/lib/Parse/ParseSIL.cpp index 44524ad8bfc..aeb15b33643 100644 --- a/lib/Parse/ParseSIL.cpp +++ b/lib/Parse/ParseSIL.cpp @@ -1188,6 +1188,7 @@ bool SILParser::parseSILOpcode(ValueKind &Opcode, SourceLoc &OpcodeLoc, .Case("function_ref", ValueKind::FunctionRefInst) .Case("load", ValueKind::LoadInst) .Case("load_weak", ValueKind::LoadWeakInst) + .Case("mark_dependence", ValueKind::MarkDependenceInst) .Case("mark_uninitialized", ValueKind::MarkUninitializedInst) .Case("mark_function_escape", ValueKind::MarkFunctionEscapeInst) .Case("metatype", ValueKind::MetatypeInst) @@ -1844,6 +1845,17 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB) { ResultVal = B.createLoadWeak(InstLoc, Val, IsTake_t(isTake)); break; } + + case ValueKind::MarkDependenceInst: { + SILValue Base; + if (parseTypedValueRef(Val) || + parseVerbatim("on") || + parseTypedValueRef(Base)) + return true; + + ResultVal = B.createMarkDependence(InstLoc, Val, Base); + break; + } // Conversion instructions. case ValueKind::UncheckedRefCastInst: diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index 8cfe4fb9e44..760a10aa0b4 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -1093,6 +1093,10 @@ public: void visitFixLifetimeInst(FixLifetimeInst *RI) { OS << "fix_lifetime " << getIDAndType(RI->getOperand()); } + void visitMarkDependenceInst(MarkDependenceInst *MDI) { + OS << "mark_dependence " << getIDAndType(MDI->getValue()) + << " on " << getIDAndType(MDI->getBase()); + } void visitCopyBlockInst(CopyBlockInst *RI) { OS << "copy_block " << getIDAndType(RI->getOperand()); } diff --git a/lib/SILAnalysis/ValueTracking.cpp b/lib/SILAnalysis/ValueTracking.cpp index 814a9258522..76aba48a20e 100644 --- a/lib/SILAnalysis/ValueTracking.cpp +++ b/lib/SILAnalysis/ValueTracking.cpp @@ -115,6 +115,7 @@ static bool isTransitiveEscapeInst(SILInstruction *Inst) { case ValueKind::UncheckedAddrCastInst: case ValueKind::UncheckedTrivialBitCastInst: case ValueKind::UncheckedRefBitCastInst: + case ValueKind::MarkDependenceInst: case ValueKind::OpenExistentialInst: case ValueKind::OpenExistentialMetatypeInst: case ValueKind::OpenExistentialRefInst: diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index a524ff001cb..505133c06be 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -777,6 +777,11 @@ namespace { SILValue address = addressAndNeedsWriteback.first; + // Mark a value-dependence on the base. + if (base && base.hasCleanup()) { + address = gen.B.createMarkDependence(loc, address, base.getValue()); + } + // TODO: maybe needsWriteback should be a thin function pointer // to which we pass the base? That would let us use direct // access for stored properties with didSet. diff --git a/lib/SILPasses/Utils/SILInliner.cpp b/lib/SILPasses/Utils/SILInliner.cpp index 1389f9b4f0d..4b14f293e00 100644 --- a/lib/SILPasses/Utils/SILInliner.cpp +++ b/lib/SILPasses/Utils/SILInliner.cpp @@ -214,6 +214,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case ValueKind::DebugValueAddrInst: case ValueKind::StringLiteralInst: case ValueKind::FixLifetimeInst: + case ValueKind::MarkDependenceInst: case ValueKind::FunctionRefInst: case ValueKind::GlobalAddrInst: return InlineCost::Free; diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 5002c686ce8..f803c885e7c 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1000,6 +1000,16 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, getSILType(Ty, (SILValueCategory)TyCategory))); break; } + case ValueKind::MarkDependenceInst: { + auto Ty = MF->getType(TyID); + auto Ty2 = MF->getType(TyID2); + ResultVal = Builder.createMarkDependence(Loc, + getLocalValue(ValID, ValResNum, + getSILType(Ty, (SILValueCategory)TyCategory)), + getLocalValue(ValID2, ValResNum2, + getSILType(Ty2, (SILValueCategory)TyCategory2))); + break; + } case ValueKind::IndexAddrInst: { auto Ty = MF->getType(TyID); auto Ty2 = MF->getType(TyID2); diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 97e9a6e3d89..93514f6451a 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -831,6 +831,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { FuncsToDeclare.insert(ReferencedFunction); break; } + case ValueKind::MarkDependenceInst: case ValueKind::IndexAddrInst: case ValueKind::IndexRawPointerInst: { SILValue operand, operand2; @@ -839,6 +840,10 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { const IndexRawPointerInst *IRP = cast(&SI); operand = IRP->getBase(); operand2 = IRP->getIndex(); + } else if (SI.getKind() == ValueKind::MarkDependenceInst) { + const MarkDependenceInst *MDI = cast(&SI); + operand = MDI->getValue(); + operand2 = MDI->getBase(); } else { const IndexAddrInst *IAI = cast(&SI); operand = IAI->getBase(); diff --git a/test/SILGen/accessors.swift b/test/SILGen/accessors.swift index b8c685bad45..144556293d2 100644 --- a/test/SILGen/accessors.swift +++ b/test/SILGen/accessors.swift @@ -51,8 +51,9 @@ func test0(ref: A) { // CHECK-NEXT: [[T1:%.*]] = class_method %0 : $A, #A.array!materializeForSet.1 // CHECK-NEXT: [[T2:%.*]] = apply [[T1]]([[T0]], %0) // CHECK-NEXT: [[T3:%.*]] = tuple_extract [[T2]] {{.*}}, 0 -// CHECK-NEXT: [[TEMP2:%.*]] = pointer_to_address [[T3]] +// CHECK-NEXT: [[T4:%.*]] = pointer_to_address [[T3]] // CHECK-NEXT: [[SHOULD_WRITEBACK:%.*]] = tuple_extract [[T2]] {{.*}}, 1 +// CHECK-NEXT: [[TEMP2:%.*]] = mark_dependence [[T4]] : $*OrdinarySub on %0 : $A // CHECK-NEXT: // function_ref accessors.OrdinarySub.subscript.setter (Swift.Int) -> Swift.Int // CHECK-NEXT: [[T0:%.*]] = function_ref @_TFV9accessors11OrdinarySubs9subscriptFSiSi // CHECK-NEXT: apply [[T0]]([[VALUE]], [[INDEX0]], [[TEMP2]]) @@ -106,8 +107,9 @@ func test1(ref: B) { // CHECK-NEXT: [[T1:%.*]] = class_method %0 : $B, #B.array!materializeForSet.1 // CHECK-NEXT: [[T2:%.*]] = apply [[T1]]([[T0]], %0) // CHECK-NEXT: [[T3:%.*]] = tuple_extract [[T2]] {{.*}}, 0 -// CHECK-NEXT: [[TEMP:%.*]] = pointer_to_address [[T3]] +// CHECK-NEXT: [[T4:%.*]] = pointer_to_address [[T3]] // CHECK-NEXT: [[SHOULD_WRITEBACK:%.*]] = tuple_extract [[T2]] {{.*}}, 1 +// CHECK-NEXT: [[TEMP:%.*]] = mark_dependence [[T4]] : $*MutatingSub on %0 : $B // CHECK-NEXT: // function_ref accessors.MutatingSub.subscript.getter (Swift.Int) -> Swift.Int // CHECK-NEXT: [[T0:%.*]] = function_ref @_TFV9accessors11MutatingSubg9subscriptFSiSi : $@cc(method) @thin (Int, @inout MutatingSub) -> Int // CHECK-NEXT: [[VALUE:%.*]] = apply [[T0]]([[INDEX1]], [[TEMP]]) @@ -126,8 +128,9 @@ func test1(ref: B) { // CHECK-NEXT: [[T1:%.*]] = class_method %0 : $B, #B.array!materializeForSet.1 // CHECK-NEXT: [[T2:%.*]] = apply [[T1]]([[T0]], %0) // CHECK-NEXT: [[T3:%.*]] = tuple_extract [[T2]] {{.*}}, 0 -// CHECK-NEXT: [[TEMP:%.*]] = pointer_to_address [[T3]] +// CHECK-NEXT: [[T4:%.*]] = pointer_to_address [[T3]] // CHECK-NEXT: [[SHOULD_WRITEBACK:%.*]] = tuple_extract [[T2]] {{.*}}, 1 +// CHECK-NEXT: [[TEMP:%.*]] = mark_dependence [[T4]] : $*MutatingSub on %0 : $B // CHECK-NEXT: // function_ref accessors.MutatingSub.subscript.setter (Swift.Int) -> Swift.Int // CHECK-NEXT: [[T0:%.*]] = function_ref @_TFV9accessors11MutatingSubs9subscriptFSiSi : $@cc(method) @thin (Int, Int, @inout MutatingSub) -> () // CHECK-NEXT: apply [[T0]]([[VALUE]], [[INDEX0]], [[TEMP]]) diff --git a/test/SILGen/properties.swift b/test/SILGen/properties.swift index 7c9d192d6f6..6aa071bf02d 100644 --- a/test/SILGen/properties.swift +++ b/test/SILGen/properties.swift @@ -195,8 +195,9 @@ func logical_struct_in_reftype_set(inout value: Val, z1: Int) { // CHECK: [[MAT_VAL_PROP_METHOD:%[0-9]+]] = class_method {{.*}} : $Ref, #Ref.val_prop!materializeForSet.1 : Ref -> (Builtin.RawPointer) -> (Builtin.RawPointer, Builtin.Int1) // CHECK: [[MAT_RESULT:%[0-9]+]] = apply [[MAT_VAL_PROP_METHOD]]([[T0]], [[VAL_REF]]) // CHECK: [[T0:%.*]] = tuple_extract [[MAT_RESULT]] : $(Builtin.RawPointer, Builtin.Int1), 0 - // CHECK: [[VAL_REF_VAL_PROP_MAT:%[0-9]+]] = pointer_to_address [[T0]] : $Builtin.RawPointer to $*Val + // CHECK: [[T1:%[0-9]+]] = pointer_to_address [[T0]] : $Builtin.RawPointer to $*Val // CHECK: [[NEEDS_WRITEBACK:%.*]] = tuple_extract [[MAT_RESULT]] : $(Builtin.RawPointer, Builtin.Int1), 1 + // CHECK: [[VAL_REF_VAL_PROP_MAT:%.*]] = mark_dependence [[T1]] : $*Val on [[VAL_REF]] // CHECK: [[V_R_VP_Z_TUPLE_MAT:%[0-9]+]] = alloc_stack $(Int, Int) // CHECK: [[LD:%[0-9]+]] = load [[VAL_REF_VAL_PROP_MAT]] // CHECK: retain_value [[LD]]