Skip to content

Fix LifetimeDependenceDefUseWalker for address yields. #72764

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 4 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ extension AddressUseVisitor {
is InitEnumDataAddrInst, is UncheckedTakeEnumDataAddrInst,
is InitExistentialAddrInst, is OpenExistentialAddrInst,
is ProjectBlockStorageInst, is UncheckedAddrCastInst,
is UnconditionalCheckedCastAddrInst,
is MarkUninitializedInst, is DropDeinitInst,
is CopyableToMoveOnlyWrapperAddrInst,
is MoveOnlyWrapperToCopyableAddrInst,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1022,12 +1022,14 @@ extension LifetimeDependenceDefUseWalker {
assert(!mdi.isUnresolved && !mdi.isNonEscaping,
"should be handled as a dependence by AddressUseVisitor")
}
if operand.instruction is ReturnInst, !operand.value.isEscapable {
return returnedDependence(result: operand)
}
if operand.instruction is YieldInst, !operand.value.isEscapable {
return yieldedDependence(result: operand)
if operand.instruction is YieldInst {
if operand.value.isEscapable {
return leafUse(of: operand)
} else {
return yieldedDependence(result: operand)
}
}
// Escaping an address
return escapingDependence(on: operand)
}

Expand Down
60 changes: 33 additions & 27 deletions SwiftCompilerSources/Sources/SIL/Instruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,9 @@ final public class DebugStepInst : Instruction {}

final public class SpecifyTestInst : Instruction {}

final public class UnconditionalCheckedCastAddrInst : Instruction {
final public class UnconditionalCheckedCastAddrInst : Instruction, SourceDestAddrInstruction {
public var isTakeOfSrc: Bool { true }
public var isInitializationOfDest: Bool { true }
public override var mayTrap: Bool { true }
}

Expand Down Expand Up @@ -527,9 +529,6 @@ final public class DeallocStackInst : Instruction, UnaryInstruction, Deallocatio
}
}

final public class DeallocPackInst : Instruction, UnaryInstruction, Deallocation {}
final public class DeallocPackMetadataInst : Instruction, Deallocation {}

final public class DeallocStackRefInst : Instruction, UnaryInstruction, Deallocation {
public var allocRef: AllocRefInstBase { operand.value as! AllocRefInstBase }
}
Expand Down Expand Up @@ -701,12 +700,6 @@ class ValueMetatypeInst : SingleValueInstruction, UnaryInstruction {}
final public
class ExistentialMetatypeInst : SingleValueInstruction, UnaryInstruction {}

final public class OpenPackElementInst : SingleValueInstruction {}
final public class PackLengthInst : SingleValueInstruction {}
final public class DynamicPackIndexInst : SingleValueInstruction {}
final public class PackPackIndexInst : SingleValueInstruction {}
final public class ScalarPackIndexInst : SingleValueInstruction {}

final public class ObjCProtocolInst : SingleValueInstruction {}

public class GlobalAccessInstruction : SingleValueInstruction {
Expand Down Expand Up @@ -1093,20 +1086,6 @@ final public class ObjectInst : SingleValueInstruction {
final public class VectorInst : SingleValueInstruction {
}

final public class TuplePackExtractInst: SingleValueInstruction {
public var indexOperand: Operand { operands[0] }
public var tupleOperand: Operand { operands[1] }
}

final public class TuplePackElementAddrInst: SingleValueInstruction {
public var indexOperand: Operand { operands[0] }
public var tupleOperand: Operand { operands[1] }
}

final public class PackElementGetInst: SingleValueInstruction {}

final public class PackElementSetInst: SingleValueInstruction {}

final public class DifferentiableFunctionInst: SingleValueInstruction {}

final public class LinearFunctionInst: SingleValueInstruction {}
Expand Down Expand Up @@ -1140,9 +1119,6 @@ final public class AllocVectorInst : SingleValueInstruction, Allocation, UnaryIn
public var capacity: Value { operand.value }
}

final public class AllocPackInst : SingleValueInstruction, Allocation {}
final public class AllocPackMetadataInst : SingleValueInstruction, Allocation {}

public class AllocRefInstBase : SingleValueInstruction, Allocation {
final public var isObjC: Bool { bridged.AllocRefInstBase_isObjc() }

Expand Down Expand Up @@ -1361,6 +1337,36 @@ final public class DestructureTupleInst : MultipleValueInstruction, UnaryInstruc
public var `tuple`: Value { operand.value }
}

//===----------------------------------------------------------------------===//
// parameter pack instructions
//===----------------------------------------------------------------------===//

final public class AllocPackInst : SingleValueInstruction, Allocation {}
final public class AllocPackMetadataInst : SingleValueInstruction, Allocation {}

final public class DeallocPackInst : Instruction, UnaryInstruction, Deallocation {}
final public class DeallocPackMetadataInst : Instruction, Deallocation {}

final public class OpenPackElementInst : SingleValueInstruction {}
final public class PackLengthInst : SingleValueInstruction {}
final public class DynamicPackIndexInst : SingleValueInstruction {}
final public class PackPackIndexInst : SingleValueInstruction {}
final public class ScalarPackIndexInst : SingleValueInstruction {}

final public class TuplePackExtractInst: SingleValueInstruction {
public var indexOperand: Operand { operands[0] }
public var tupleOperand: Operand { operands[1] }
}

final public class TuplePackElementAddrInst: SingleValueInstruction {
public var indexOperand: Operand { operands[0] }
public var tupleOperand: Operand { operands[1] }
}

final public class PackElementGetInst: SingleValueInstruction {}

final public class PackElementSetInst: Instruction {}

//===----------------------------------------------------------------------===//
// terminator instructions
//===----------------------------------------------------------------------===//
Expand Down
17 changes: 17 additions & 0 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,17 @@ struct ImmutableAddressUseVerifier {
return false;
}

/// Handle instructions that move a value from one address into another
/// address.
bool isConsumingOrMutatingMoveAddrUse(Operand *use) {
assert(use->getUser()->getNumOperands() == 2);
auto opIdx = use->getOperandNumber();
if (opIdx == CopyLikeInstruction::Dest)
return true;
assert(opIdx == CopyLikeInstruction::Src);
return false;
}

bool isAddrCastToNonConsuming(SingleValueInstruction *i) {
// Check if any of our uses are consuming. If none of them are consuming, we
// are good to go.
Expand Down Expand Up @@ -685,6 +696,12 @@ struct ImmutableAddressUseVerifier {
}
return true;
}
case SILInstructionKind::UnconditionalCheckedCastAddrInst:
case SILInstructionKind::UncheckedRefCastAddrInst:
if (isConsumingOrMutatingMoveAddrUse(use)) {
return true;
}
break;
case SILInstructionKind::CheckedCastAddrBranchInst:
switch (cast<CheckedCastAddrBranchInst>(inst)->getConsumptionKind()) {
case CastConsumptionKind::BorrowAlways:
Expand Down
26 changes: 24 additions & 2 deletions test/SILOptimizer/lifetime_dependence_diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,29 @@ func bv_copy(_ bv: borrowing BV) -> dependsOn(bv) BV {
}

struct NCInt: ~Copyable {
var value: Int
var i: Int
}

public struct NEInt: ~Escapable {
var i: Int

// Test yielding an address.
// CHECK-LABEL: sil hidden @$s4test5NEIntV5ipropSivM : $@yield_once @convention(method) (@inout NEInt) -> @yields @inout Int {
// CHECK: bb0(%0 : $*NEInt):
// CHECK: [[A:%.*]] = begin_access [modify] [static] %0 : $*NEInt
// CHECK: [[E:%.*]] = struct_element_addr [[A]] : $*NEInt, #NEInt.i
// CHECK: yield [[E]] : $*Int, resume bb1, unwind bb2
// CHECK: end_access [[A]] : $*NEInt
// CHECK: end_access [[A]] : $*NEInt
// CHECK-LABEL: } // end sil function '$s4test5NEIntV5ipropSivM'
var iprop: Int {
_read { yield i }
_modify { yield &i }
}

init(owner: borrowing NCInt) -> dependsOn(owner) Self {
self.i = owner.i
}
}

func takeClosure(_: () -> ()) {}
Expand Down Expand Up @@ -56,5 +78,5 @@ func bv_borrow_borrow(bv: borrowing BV) -> dependsOn(scoped bv) BV {
// because lifetime dependence does not expect a dependence directly on an 'inout' address without any 'begin_access'
// marker.
func ncint_capture(ncInt: inout NCInt) {
takeClosure { _ = ncInt.value }
takeClosure { _ = ncInt.i }
}
38 changes: 38 additions & 0 deletions test/SILOptimizer/ownership_liveness_unit.sil
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,44 @@ bb0(%0 : @guaranteed $C):
return %99 : $()
}

// CHECK-LABEL: testInteriorUnconditionalAddrCast: interior-liveness with: %1
// CHECK: Interior liveness: %1 = argument of bb0 : $D
// CHECK-NEXT: bb0: LiveWithin
// CHECK-NEXT: regular user: [[FIELD:%.*]] = ref_element_addr %1 : $D, #D.object
// CHECK-NEXT: regular user: unconditional_checked_cast_addr C in [[FIELD]] : $*C to D in %0 : $*D
// CHECK-NEXT: regular user: copy_addr [take] %4 to [init] [[FIELD]] : $*C
// CHECK-NEXT: regular user: unchecked_ref_cast_addr C in [[FIELD]] : $*C to D in %0 : $*D
// CHECK-NEXT: Complete liveness
// CHECK-NEXT: Unenclosed phis {
// CHECK-NEXT: }
// CHECK-NEXT: last user: unchecked_ref_cast_addr C in [[FIELD]] : $*C to D in %0 : $*D
// CHECK-NEXT: testInteriorUnconditionalAddrCast: interior-liveness with: %1

// CHECK-LABEL: testInteriorUnconditionalAddrCast: interior_liveness_swift with: %1
// CHECK: Interior liveness: %1 = argument of bb0 : $D
// CHECK-NEXT: begin: [[FIELD]] = ref_element_addr %1 : $D, #D.object
// CHECK-NEXT: ends: unchecked_ref_cast_addr C in [[FIELD]] : $*C to D in %0 : $*D
// CHECK-NEXT: exits:
// CHECK-NEXT: interiors: copy_addr [take] %4 to [init] [[FIELD]] : $*C
// CHECK-NEXT: unconditional_checked_cast_addr C in [[FIELD]] : $*C to D in %0 : $*D
// CHECK-NEXT: [[FIELD]] = ref_element_addr %1 : $D, #D.object
// CHECK-NEXT: Unenclosed phis {
// CHECK-NEXT: }
// CHECK-NEXT: last user: unchecked_ref_cast_addr C in [[FIELD]] : $*C to D in %0 : $*D
// CHECK-NEXT: testInteriorUnconditionalAddrCast: interior_liveness_swift with: %1
sil [ossa] @testInteriorUnconditionalAddrCast : $@convention(thin) (@guaranteed D) -> @out D {
bb0(%0 : $*D, %1 : @guaranteed $D):
specify_test "interior-liveness %1"
specify_test "interior_liveness_swift %1"
%c1 = ref_element_addr %1 : $D, #D.object
unconditional_checked_cast_addr C in %c1 : $*C to D in %0 : $*D
%c2 = unchecked_addr_cast %0 : $*D to $*C
copy_addr [take] %c2 to [init] %c1 : $*C
unchecked_ref_cast_addr C in %c1 : $*C to D in %0 : $*D
%99 = tuple()
return %99 : $()
}

// CHECK-LABEL: testInteriorReborrow: interior-liveness with: %borrow
// CHECK: Complete liveness
// CHECK-NEXT: Unenclosed phis {
Expand Down