Optimizer: do mark_dependence simplification at Onone

* re-implement the SILCombine peephole as a Swift instruction simplification
* run this simplification also in the OnoneSimplification pass
This commit is contained in:
Erik Eckstein
2025-05-22 12:34:48 +02:00
parent d10602ea28
commit 85228f2c75
15 changed files with 419 additions and 118 deletions

View File

@@ -29,6 +29,7 @@ swift_compiler_sources(Optimizer
SimplifyInitEnumDataAddr.swift
SimplifyKeyPath.swift
SimplifyLoad.swift
SimplifyMarkDependence.swift
SimplifyMisc.swift
SimplifyPartialApply.swift
SimplifyPointerToAddress.swift

View File

@@ -0,0 +1,112 @@
//===--- SimplifyMarkDependence.swift -------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SIL
// Note: this simplification cannot run before dependency diagnostics.
// See `var isRedundant` below.
extension MarkDependenceInst : OnoneSimplifiable, SILCombineSimplifiable {
func simplify(_ context: SimplifyContext) {
if isRedundant ||
// A literal lives forever, so no mark_dependence is needed.
// This pattern can occur after StringOptimization when a utf8CString of a literal is replace
// by the string_literal itself.
value.isLiteral
{
replace(with: value, context)
return
}
simplifyBaseOperand(context)
}
}
extension MarkDependenceAddrInst : OnoneSimplifiable, SILCombineSimplifiable {
func simplify(_ context: SimplifyContext) {
if isRedundant {
context.erase(instruction: self)
return
}
simplifyBaseOperand(context)
}
}
private extension MarkDependenceInstruction {
var isRedundant: Bool {
if base.type.isObject && base.type.isTrivial(in: base.parentFunction) {
// Sometimes due to specialization/builtins, we can get a mark_dependence whose base is a trivial
// typed object. Trivial values live forever. Therefore the mark_dependence does not have a meaning.
// Note: the mark_dependence is still needed for lifetime diagnostics. So it's important that this
// simplification does not run before the lifetime diagnostic pass.
return true
}
// If the value is an address projection from the base the mark_dependence is not needed because the
// base cannot be destroyed before the accessing the value, anyway.
if valueOrAddress.type.isAddress, base.type.isAddress,
// But we still need to keep the mark_dependence for non-escapable types because a non-escapable
// value can be copied and copies must not outlive the base.
valueOrAddress.type.isEscapable(in: parentFunction),
base.accessPath.isEqualOrContains(valueOrAddress.accessPath)
{
return true
}
return false
}
func simplifyBaseOperand(_ context: SimplifyContext) {
/// In OSSA, the `base` is a borrow introducing operand. It is pretty complicated to change the base.
/// So, for simplicity, we only do this optimization when OSSA is already lowered.
if parentFunction.hasOwnership {
return
}
// Replace the base operand with the operand of the base value if it's a certain kind of forwarding
// instruction.
let rootBase = base.lookThroughEnumAndExistentialRef
if rootBase != base {
baseOperand.set(to: rootBase, context)
}
}
}
private extension Value {
/// True, if this is a literal instruction or a struct of a literal instruction.
/// What we want to catch here is a `UnsafePointer<Int8>` of a string literal.
var isLiteral: Bool {
switch self {
case let s as StructInst:
if let singleOperand = s.operands.singleElement {
return singleOperand.value.isLiteral
}
return false
case is IntegerLiteralInst, is FloatLiteralInst, is StringLiteralInst:
return true
default:
return false
}
}
var lookThroughEnumAndExistentialRef: Value {
switch self {
case let e as EnumInst:
if let payload = e.payload {
return payload.lookThroughEnumAndExistentialRef
}
return self
case let ier as InitExistentialRefInst:
return ier.instance.lookThroughEnumAndExistentialRef
case let oer as OpenExistentialRefInst:
return oer.existential.lookThroughEnumAndExistentialRef
default:
return self
}
}
}

View File

@@ -122,6 +122,8 @@ private func registerSwiftPasses() {
registerForSILCombine(DestructureTupleInst.self, { run(DestructureTupleInst.self, $0) })
registerForSILCombine(TypeValueInst.self, { run(TypeValueInst.self, $0) })
registerForSILCombine(ClassifyBridgeObjectInst.self, { run(ClassifyBridgeObjectInst.self, $0) })
registerForSILCombine(MarkDependenceInst.self, { run(MarkDependenceInst.self, $0) })
registerForSILCombine(MarkDependenceAddrInst.self, { run(MarkDependenceAddrInst.self, $0) })
registerForSILCombine(PointerToAddressInst.self, { run(PointerToAddressInst.self, $0) })
registerForSILCombine(UncheckedEnumDataInst.self, { run(UncheckedEnumDataInst.self, $0) })
registerForSILCombine(WitnessMethodInst.self, { run(WitnessMethodInst.self, $0) })

View File

@@ -291,8 +291,6 @@ public:
SILInstruction *visitUnreachableInst(UnreachableInst *UI);
SILInstruction *visitAllocRefDynamicInst(AllocRefDynamicInst *ARDI);
SILInstruction *visitMarkDependenceInst(MarkDependenceInst *MDI);
SILInstruction *visitMarkDependenceAddrInst(MarkDependenceAddrInst *MDI);
SILInstruction *visitConvertFunctionInst(ConvertFunctionInst *CFI);
SILInstruction *
visitConvertEscapeToNoEscapeInst(ConvertEscapeToNoEscapeInst *Cvt);

View File

@@ -1641,90 +1641,6 @@ visitAllocRefDynamicInst(AllocRefDynamicInst *ARDI) {
return NewInst;
}
/// Returns true if \p val is a literal instruction or a struct of a literal
/// instruction.
/// What we want to catch here is a UnsafePointer<Int8> of a string literal.
static bool isLiteral(SILValue val) {
while (auto *str = dyn_cast<StructInst>(val)) {
if (str->getNumOperands() != 1)
return false;
val = str->getOperand(0);
}
return isa<LiteralInst>(val);
}
template<SILInstructionKind Opc, typename Derived>
static SILInstruction *combineMarkDependenceBaseInst(
MarkDependenceInstBase<Opc, Derived> *mdi,
SILCombiner *C) {
if (!mdi->getFunction()->hasOwnership()) {
// Simplify the base operand of a MarkDependenceInst to eliminate
// unnecessary instructions that aren't adding value.
//
// Conversions to Optional.Some(x) often happen here, this isn't important
// for us, we can just depend on 'x' directly.
if (auto *eiBase = dyn_cast<EnumInst>(mdi->getBase())) {
if (eiBase->hasOperand()) {
mdi->setBase(eiBase->getOperand());
if (eiBase->use_empty()) {
C->eraseInstFromFunction(*eiBase);
}
return mdi;
}
}
// Conversions from a class to AnyObject also happen a lot, we can just
// depend on the class reference.
if (auto *ier = dyn_cast<InitExistentialRefInst>(mdi->getBase())) {
mdi->setBase(ier->getOperand());
if (ier->use_empty())
C->eraseInstFromFunction(*ier);
return mdi;
}
// Conversions from a class to AnyObject also happen a lot, we can just
// depend on the class reference.
if (auto *oeri = dyn_cast<OpenExistentialRefInst>(mdi->getBase())) {
mdi->setBase(oeri->getOperand());
if (oeri->use_empty())
C->eraseInstFromFunction(*oeri);
return mdi;
}
}
// Sometimes due to specialization/builtins, we can get a mark_dependence
// whose base is a trivial typed object. In such a case, the mark_dependence
// does not have a meaning, so just eliminate it.
{
SILType baseType = mdi->getBase()->getType();
if (baseType.getObjectType().isTrivial(*mdi->getFunction())) {
if (auto mdValue = dyn_cast<MarkDependenceInst>(mdi)) {
auto &valOper = mdi->getAllOperands()[MarkDependenceInst::Dependent];
mdValue->replaceAllUsesWith(valOper.get());
}
return C->eraseInstFromFunction(*mdi);
}
}
return nullptr;
}
SILInstruction *SILCombiner::visitMarkDependenceInst(MarkDependenceInst *mdi) {
if (isLiteral(mdi->getValue())) {
// A literal lives forever, so no mark_dependence is needed.
// This pattern can occur after StringOptimization when a utf8CString of
// a literal is replace by the string_literal itself.
replaceInstUsesWith(*mdi, mdi->getValue());
return eraseInstFromFunction(*mdi);
}
return combineMarkDependenceBaseInst(mdi, this);
}
SILInstruction *
SILCombiner::visitMarkDependenceAddrInst(MarkDependenceAddrInst *mdi) {
return combineMarkDependenceBaseInst(mdi, this);
}
/// Returns true if reference counting and debug_value users of a global_value
/// can be deleted.
static bool checkGlobalValueUsers(SILValue val,

View File

@@ -45,6 +45,8 @@ INSTRUCTION_SIMPLIFICATION(CopyBlockInst)
INSTRUCTION_SIMPLIFICATION(DestroyValueInst)
INSTRUCTION_SIMPLIFICATION(DestructureStructInst)
INSTRUCTION_SIMPLIFICATION(DestructureTupleInst)
INSTRUCTION_SIMPLIFICATION(MarkDependenceInst)
INSTRUCTION_SIMPLIFICATION(MarkDependenceAddrInst)
INSTRUCTION_SIMPLIFICATION(PointerToAddressInst)
INSTRUCTION_SIMPLIFICATION(TypeValueInst)
INSTRUCTION_SIMPLIFICATION(UncheckedAddrCastInst)

View File

@@ -65,8 +65,7 @@ func test0() {
// CHECK: [[T1:%.*]] = apply [[T0]]({{%.*}}, [[AVAL]])
// CHECK: [[T2:%.*]] = struct_extract [[T1]] : $UnsafePointer<Int32>, #UnsafePointer._rawValue
// CHECK: [[T3:%.*]] = pointer_to_address [[T2]] : $Builtin.RawPointer to [strict] $*Int32
// CHECK: [[MD:%.*]] = mark_dependence [nonescaping] [[T3]] : $*Int32 on [[AVAL]] : $A
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unsafe] [[MD]] : $*Int32
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unsafe] [[T3]] : $*Int32
// CHECK: [[Z:%.*]] = load [[ACCESS]] : $*Int32
let z = a[10]
@@ -102,8 +101,7 @@ func test1() -> Int32 {
// CHECK: [[PTR:%.*]] = apply [[ACCESSOR]]({{%.*}}, [[A]]) : $@convention(method) (Int32, A) -> UnsafePointer<Int32>
// CHECK: [[T0:%.*]] = struct_extract [[PTR]] : $UnsafePointer<Int32>, #UnsafePointer._rawValue
// CHECK: [[T1:%.*]] = pointer_to_address [[T0]] : $Builtin.RawPointer to [strict] $*Int32
// CHECK: [[MD:%.*]] = mark_dependence [nonescaping] [[T1]] : $*Int32 on [[A]] : $A
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unsafe] [[MD]] : $*Int32
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unsafe] [[T1]] : $*Int32
// CHECK: [[T2:%.*]] = load [[ACCESS]] : $*Int32
// CHECK: return [[T2]] : $Int32
return A()[0]
@@ -198,9 +196,8 @@ func test_carray(_ array: inout CArray<(Int32) -> Int32>) -> Int32 {
// CHECK: [[T2:%.*]] = apply [[T1]]<(Int32) -> Int32>({{%.*}}, [[T0]])
// CHECK: [[T3:%.*]] = struct_extract [[T2]] : $UnsafePointer<(Int32) -> Int32>, #UnsafePointer._rawValue
// CHECK: [[T4:%.*]] = pointer_to_address [[T3]] : $Builtin.RawPointer to [strict] $*@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Int32, Int32>
// CHECK: [[MD:%.*]] = mark_dependence [nonescaping] [[T4]] : $*@callee_guaranteed @substituted <τ_0_0, τ_0_1>
// (@in_guaranteed τ_0_0) -> @out τ_0_1 for <Int32, Int32> on [[T0]] : $CArray<(Int32) -> Int32>
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unsafe] [[MD]]
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unsafe] [[T4]]
// CHECK: [[T5:%.*]] = load [[ACCESS]]
return array[1](5)
}
@@ -289,8 +286,7 @@ struct E {
// CHECK: [[T1:%.*]] = apply [[T0]]([[E]])
// CHECK: [[T2:%.*]] = struct_extract [[T1]]
// CHECK: [[T3:%.*]] = pointer_to_address [[T2]]
// CHECK: [[MD:%.*]] = mark_dependence [nonescaping] [[T3]] : $*Int32 on %0 : $E
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unsafe] [[MD]] : $*Int32
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unsafe] [[T3]] : $*Int32
// CHECK: store {{%.*}} to [[ACCESS]] : $*Int32
func test_e(_ e: E) {
e.value = 0

View File

@@ -95,7 +95,7 @@ public func returnOptionalEscape() -> (() ->())?
// NOPEEPHOLE-NEXT: strong_retain [[V2]]
// NOPEEPHOLE-NEXT: [[CVT:%.*]] = convert_escape_to_noescape [[V2]]
// NOPEEPHOLE-NEXT: [[SOME:%.*]] = enum $Optional<{{.*}}>, #Optional.some!enumelt, [[V2]]
// NOPEEPHOLE-NEXT: [[MDI:%.*]] = mark_dependence [[CVT]] : $@noescape @callee_guaranteed () -> () on [[SOME]] : $Optional<@callee_guaranteed () -> ()>
// NOPEEPHOLE-NEXT: [[MDI:%.*]] = mark_dependence [[CVT]] : $@noescape @callee_guaranteed () -> () on [[V2]]
// NOPEEPHOLE-NEXT: [[NOESCAPE_SOME:%.*]] = enum $Optional<{{.*}}>, #Optional.some!enumelt, [[MDI]]
// NOPEEPHOLE-NEXT: strong_release [[V2]]
// NOPEEPHOLE-NEXT: br bb2([[NOESCAPE_SOME]] : $Optional<{{.*}}>, [[SOME]] : $Optional<{{.*}}>, [[SOME]] : $Optional<{{.*}}>)

View File

@@ -86,8 +86,7 @@ func use(_ o : borrowing View) {}
// let wrapper = nc.wrapper
// CHECK: [[ACCESS:%.*]] = begin_access [read] [static] [[NC]]
// CHECK: [[NCVAL:%.*]] = load [[ACCESS]]
// CHECK: ([[YIELD1:%.*]], [[TOKEN1:%.*]]) = begin_apply %{{.*}}([[NCVAL]]) : $@yield_once @convention(method) (@guaranteed NCContainer) -> @lifetime(borrow 0) @yields @guaranteed Wrapper
// CHECK: [[WRAPPER:%.*]] = mark_dependence [nonescaping] [[YIELD1]] on [[TOKEN1]]
// CHECK: ([[WRAPPER:%.*]], [[TOKEN1:%.*]]) = begin_apply %{{.*}}([[NCVAL]]) : $@yield_once @convention(method) (@guaranteed NCContainer) -> @lifetime(borrow 0) @yields @guaranteed Wrapper
// CHECK: retain_value [[WRAPPER]]
// CHECK: debug_value [[WRAPPER]], let, name "wrapper"
// let view = wrapper.view
@@ -99,7 +98,7 @@ func use(_ o : borrowing View) {}
// CHECK: apply %{{.*}}([[VIEW]]) : $@convention(thin) (@guaranteed View) -> ()
// CHECK: release_value [[VIEW]]
// CHECK: release_value [[WRAPPER]]
// CHECK: %24 = end_apply [[TOKEN1]] as $()
// CHECK: end_apply [[TOKEN1]] as $()
// CHECK: end_access [[ACCESS]]
// CHECK: destroy_addr [[NC]]
// CHECK-LABEL: } // end sil function '$s9coroutine20testDoubleNestedRead2ncyAA11NCContainerVn_tF'

View File

@@ -1,5 +1,5 @@
// RUN: %target-sil-opt %s \
// RUN: -diagnostics -sil-verify-all \
// RUN: -diagnostics -sil-verify-all -sil-disable-pass=onone-simplification \
// RUN: -enable-experimental-feature LifetimeDependence \
// RUN: | %FileCheck %s

View File

@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend %s -Xllvm -sil-print-types -emit-sil \
// RUN: %target-swift-frontend %s -Xllvm -sil-print-types -Xllvm -sil-disable-pass=onone-simplification -emit-sil \
// RUN: -enable-experimental-feature LifetimeDependence \
// RUN: | %FileCheck %s

View File

@@ -3759,12 +3759,9 @@ bb2(%5 : $MyErrorType):
}
// CHECK-LABEL: sil @mark_dependence_base
// CHECK: bb0(
// CHECK-NOT: init_existential_ref
// CHECK-NOT: enum
// CHECK-NEXT: mark_dependence
// CHECK-NEXT: load
// CHECK: return
// CHECK: [[MD:%.*]] = mark_dependence %0 : $*Builtin.Int64 on %1 : $B
// CHECK-NEXT: load [[MD]]
// CHECK: } // end sil function 'mark_dependence_base'
sil @mark_dependence_base : $@convention(thin) (@inout Builtin.Int64, @owned B) -> Builtin.Int64 {
bb0(%0 : $*Builtin.Int64, %1 : $B):
%x = init_existential_ref %1 : $B : $B, $AnyObject
@@ -3788,10 +3785,8 @@ bb0(%0 : $B):
}
// CHECK-LABEL: sil @mark_dependence_trivial_address_base :
// CHECK: bb0(
// CHECK-NEXT: strong_retain
// CHECK-NEXT: return
// CHECK: } // end sil function 'mark_dependence_trivial_address_base'
// CHECK: mark_dependence
// CHECK: } // end sil function 'mark_dependence_trivial_address_base'
sil @mark_dependence_trivial_address_base : $@convention(thin) (@guaranteed B) -> @owned B {
bb0(%0 : $B):
strong_retain %0 : $B
@@ -3804,12 +3799,9 @@ bb0(%0 : $B):
protocol _NSArrayCore {}
// CHECK-LABEL: sil @mark_dependence_base2
// CHECK: bb0(
// CHECK-NOT: open_existential_ref
// CHECK-NOT: enum
// CHECK-NEXT: mark_dependence
// CHECK-NEXT: load
// CHECK: return
// CHECK: [[MD:%.*]] = mark_dependence %0 : $*Builtin.Int64 on %1 : $B
// CHECK-NEXT: load [[MD]]
// CHECK: } // end sil function 'mark_dependence_base2'
sil @mark_dependence_base2 : $@convention(thin) (@inout Builtin.Int64, @owned B) -> Builtin.Int64 {
bb0(%0 : $*Builtin.Int64, %1 : $B):
%2 = init_existential_ref %1 : $B : $B, $AnyObject

View File

@@ -0,0 +1,157 @@
// RUN: %target-sil-opt %s -onone-simplification -simplify-instruction=mark_dependence -enable-experimental-feature LifetimeDependence | %FileCheck %s
// REQUIRES: swift_feature_LifetimeDependence
import Swift
import Builtin
class B {}
struct S {
var a: B
var b: B
}
struct NE: ~Escapable {}
struct S2: ~Escapable {
var a: NE
}
// CHECK-LABEL: sil [ossa] @mark_dependence_trivial_base :
// CHECK: %2 = copy_value %0
// CHECK-NEXT: return %2
// CHECK: } // end sil function 'mark_dependence_trivial_base'
sil [ossa] @mark_dependence_trivial_base : $@convention(thin) (@guaranteed B, Int) -> @owned B {
bb0(%0 : @guaranteed $B, %1 : $Int):
%2 = copy_value %0
%3 = mark_dependence %2 on %1
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_trivial_base_addr :
// CHECK: %3 = mark_dependence %2 on %1
// CHECK: } // end sil function 'mark_dependence_trivial_base_addr'
sil [ossa] @mark_dependence_trivial_base_addr : $@convention(thin) (@guaranteed B, @in_guaranteed Int) -> @owned B {
bb0(%0 : @guaranteed $B, %1 : $*Int):
%2 = copy_value %0
%3 = mark_dependence %2 on %1
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_non_trivial_base :
// CHECK: %2 = copy_value %0
// CHECK-NEXT: %3 = mark_dependence %2 on %1
// CHECK-NEXT: return %3
// CHECK: } // end sil function 'mark_dependence_non_trivial_base'
sil [ossa] @mark_dependence_non_trivial_base : $@convention(thin) (@guaranteed B, @guaranteed B) -> @owned B {
bb0(%0 : @guaranteed $B, %1 : @guaranteed $B):
%2 = copy_value %0
%3 = mark_dependence %2 on %1
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_projection :
// CHECK: %1 = struct_element_addr %0, #S.a
// CHECK: %2 = load [copy] %1
// CHECK-NEXT: return %2
// CHECK: } // end sil function 'mark_dependence_projection'
sil [ossa] @mark_dependence_projection : $@convention(thin) (@in_guaranteed S) -> @owned B {
bb0(%0 : $*S):
%1 = struct_element_addr %0, #S.a
%2 = mark_dependence %1 on %0
%3 = load [copy] %2
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_wrong_projection :
// CHECK: %2 = mark_dependence %0 on %1
// CHECK: } // end sil function 'mark_dependence_wrong_projection'
sil [ossa] @mark_dependence_wrong_projection : $@convention(thin) (@in_guaranteed S) -> @owned S {
bb0(%0 : $*S):
%1 = struct_element_addr %0, #S.a
%2 = mark_dependence %0 on %1
%3 = load [copy] %2
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_wrong_projection2 :
// CHECK: %3 = mark_dependence %1 on %2
// CHECK: } // end sil function 'mark_dependence_wrong_projection2'
sil [ossa] @mark_dependence_wrong_projection2 : $@convention(thin) (@in_guaranteed S) -> @owned B {
bb0(%0 : $*S):
%1 = struct_element_addr %0, #S.a
%2 = struct_element_addr %0, #S.b
%3 = mark_dependence %1 on %2
%4 = load [copy] %3
return %4
}
// CHECK-LABEL: sil [ossa] @mark_dependence_non_escapable_projection :
// CHECK: %2 = mark_dependence %1 on %0
// CHECK: } // end sil function 'mark_dependence_non_escapable_projection'
sil [ossa] @mark_dependence_non_escapable_projection : $@convention(thin) (@in_guaranteed S2) -> @owned NE {
bb0(%0 : $*S2):
%1 = struct_element_addr %0, #S2.a
%2 = mark_dependence %1 on %0
%3 = load [copy] %2
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_string_literal :
// CHECK: %1 = string_literal utf8 "a"
// CHECK: %2 = struct $UnsafePointer<Int8> (%1)
// CHECK: return %2
// CHECK: } // end sil function 'mark_dependence_string_literal'
sil [ossa] @mark_dependence_string_literal : $@convention(thin) (@guaranteed Builtin.NativeObject) -> UnsafePointer<Int8> {
bb0(%0 : @guaranteed $Builtin.NativeObject):
%1 = string_literal utf8 "a"
%2 = struct $UnsafePointer<Int8> (%1)
%3 = mark_dependence %2 on %0
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_non_literal :
// CHECK: %2 = struct
// CHECK-NEXT: %3 = mark_dependence %2 on %1
// CHECK-NEXT: return %3
// CHECK: } // end sil function 'mark_dependence_non_literal'
sil [ossa] @mark_dependence_non_literal : $@convention(thin) (Builtin.RawPointer, @guaranteed Builtin.NativeObject) -> UnsafePointer<Int8> {
bb0(%0 : $Builtin.RawPointer, %1 : @guaranteed $Builtin.NativeObject):
%2 = struct $UnsafePointer<Int8> (%0)
%3 = mark_dependence %2 on %1
return %3
}
// CHECK-LABEL: sil @mark_dependence_base :
// CHECK-NOT: init_existential_ref
// CHECK-NOT: enum
// CHECK: %2 = mark_dependence %0 on %1
// CHECK-NEXT: %3 = load %2
// CHECK-NEXT: return %3
// CHECK: } // end sil function 'mark_dependence_base'
sil @mark_dependence_base : $@convention(thin) (@inout Builtin.Int64, @owned B) -> Builtin.Int64 {
bb0(%0 : $*Builtin.Int64, %1 : $B):
%2 = init_existential_ref %1 : $B : $B, $AnyObject
%3 = enum $Optional<AnyObject>, #Optional.some!enumelt, %2
%4 = mark_dependence %0 on %3
%5 = load %4
return %5
}
// CHECK-LABEL: sil @mark_dependence_base2 :
// CHECK-NOT: init_existential_ref
// CHECK-NOT: open_existential_ref
// CHECK: %2 = mark_dependence %0 on %1
// CHECK-NEXT: %3 = load %2
// CHECK-NEXT: return %3
// CHECK: } // end sil function 'mark_dependence_base2'
sil @mark_dependence_base2 : $@convention(thin) (@inout Builtin.Int64, @owned B) -> Builtin.Int64 {
bb0(%0 : $*Builtin.Int64, %1 : $B):
%2 = init_existential_ref %1 : $B : $B, $AnyObject
%3 = open_existential_ref %2 to $@opened("B674783A-EF08-11E7-97D6-8C85900CB088", AnyObject) Self
%4 = mark_dependence %0 on %3
%5 = load %4
return %5
}

View File

@@ -0,0 +1,124 @@
// RUN: %target-sil-opt %s -onone-simplification -simplify-instruction=mark_dependence_addr -enable-experimental-feature LifetimeDependence | %FileCheck %s
// REQUIRES: swift_feature_LifetimeDependence
import Swift
import Builtin
class B {}
struct S {
var a: B
var b: B
}
struct NE: ~Escapable {}
struct S2: ~Escapable {
var a: NE
}
// CHECK-LABEL: sil [ossa] @mark_dependence_trivial_base :
// CHECK-NOT: mark_dependence_addr
// CHECK: } // end sil function 'mark_dependence_trivial_base'
sil [ossa] @mark_dependence_trivial_base : $@convention(thin) (@in_guaranteed B, Int) -> () {
bb0(%0 : $*B, %1 : $Int):
mark_dependence_addr %0 on %1
%3 = tuple ()
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_trivial_base_addr :
// CHECK: mark_dependence_addr %0 on %1
// CHECK: } // end sil function 'mark_dependence_trivial_base_addr'
sil [ossa] @mark_dependence_trivial_base_addr : $@convention(thin) (@in_guaranteed B, @in_guaranteed Int) -> () {
bb0(%0 : $*B, %1 : $*Int):
mark_dependence_addr %0 on %1
%3 = tuple ()
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_non_trivial_base :
// CHECK: mark_dependence_addr %0 on %1
// CHECK: } // end sil function 'mark_dependence_non_trivial_base'
sil [ossa] @mark_dependence_non_trivial_base : $@convention(thin) (@in_guaranteed B, @guaranteed B) -> () {
bb0(%0 : $*B, %1 : @guaranteed $B):
mark_dependence_addr %0 on %1
%3 = tuple ()
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_projection :
// CHECK-NOT: mark_dependence_addr
// CHECK: } // end sil function 'mark_dependence_projection'
sil [ossa] @mark_dependence_projection : $@convention(thin) (@in_guaranteed S) -> () {
bb0(%0 : $*S):
%1 = struct_element_addr %0, #S.a
mark_dependence_addr %1 on %0
%3 = tuple ()
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_wrong_projection :
// CHECK: mark_dependence_addr %0 on %1
// CHECK: } // end sil function 'mark_dependence_wrong_projection'
sil [ossa] @mark_dependence_wrong_projection : $@convention(thin) (@in_guaranteed S) -> () {
bb0(%0 : $*S):
%1 = struct_element_addr %0, #S.a
mark_dependence_addr %0 on %1
%3 = tuple ()
return %3
}
// CHECK-LABEL: sil [ossa] @mark_dependence_wrong_projection2 :
// CHECK: mark_dependence_addr %1 on %2
// CHECK: } // end sil function 'mark_dependence_wrong_projection2'
sil [ossa] @mark_dependence_wrong_projection2 : $@convention(thin) (@in_guaranteed S) -> () {
bb0(%0 : $*S):
%1 = struct_element_addr %0, #S.a
%2 = struct_element_addr %0, #S.b
mark_dependence_addr %1 on %2
%4 = tuple ()
return %4
}
// CHECK-LABEL: sil [ossa] @mark_dependence_non_escapable_projection :
// CHECK: mark_dependence_addr %1 on %0
// CHECK: } // end sil function 'mark_dependence_non_escapable_projection'
sil [ossa] @mark_dependence_non_escapable_projection : $@convention(thin) (@in_guaranteed S2) -> () {
bb0(%0 : $*S2):
%1 = struct_element_addr %0, #S2.a
mark_dependence_addr %1 on %0
%4 = tuple ()
return %4
}
// CHECK-LABEL: sil @mark_dependence_base :
// CHECK-NOT: init_existential_ref
// CHECK-NOT: enum
// CHECK: mark_dependence_addr %0 on %1
// CHECK: } // end sil function 'mark_dependence_base'
sil @mark_dependence_base : $@convention(thin) (@inout Builtin.Int64, @owned B) -> () {
bb0(%0 : $*Builtin.Int64, %1 : $B):
%2 = init_existential_ref %1 : $B : $B, $AnyObject
%3 = enum $Optional<AnyObject>, #Optional.some!enumelt, %2
mark_dependence_addr %0 on %3
%5 = tuple ()
return %5
}
// CHECK-LABEL: sil @mark_dependence_base2 :
// CHECK-NOT: init_existential_ref
// CHECK-NOT: open_existential_ref
// CHECK: mark_dependence_addr %0 on %1
// CHECK: } // end sil function 'mark_dependence_base2'
sil @mark_dependence_base2 : $@convention(thin) (@inout Builtin.Int64, @owned B) -> () {
bb0(%0 : $*Builtin.Int64, %1 : $B):
%2 = init_existential_ref %1 : $B : $B, $AnyObject
%3 = open_existential_ref %2 to $@opened("B674783A-EF08-11E7-97D6-8C85900CB088", AnyObject) Self
mark_dependence_addr %0 on %3
%5 = tuple ()
return %5
}

View File

@@ -15,6 +15,8 @@ struct NC: ~Copyable {
}
struct S {
var s: String
var data: NC {
unsafeAddress { return makeUpAPointer() }
}
@@ -72,7 +74,7 @@ func testCMod(c: C) {
mod(&c.mutableData)
}
// CHECK-LABEL: sil hidden @$s4main11testSBorrow1syAA1SV_tF : $@convention(thin) (S) -> () {
// CHECK-LABEL: sil hidden @$s4main11testSBorrow1syAA1SV_tF : $@convention(thin) (@guaranteed S) -> () {
// CHECK: [[ADR:%.*]] = pointer_to_address %{{.*}} to [strict] $*NC
// CHECK: [[MD:%.*]] = mark_dependence [nonescaping] [[ADR]] on %0
// CHECK: begin_access [read] [unsafe] [[MD]]
@@ -114,7 +116,7 @@ func testSInoutBorrow(mut_s s: inout S) {
// CHECK: [[ACCESS:%.*]] = begin_access [read] [static] %0
// CHECK: [[LD:%.*]] = load [[ACCESS]]
// CHECK: [[ADR:%.*]] = pointer_to_address %{{.*}} to [strict] $*NC
// CHECK: [[MD:%.*]] = mark_dependence [nonescaping] [[ADR]] on [[LD]]
// CHECK: [[MD:%.*]] = mark_dependence [nonescaping] [[ADR]] on [[ACCESS]]
// CHECK: begin_access [read] [unsafe] [[MD]]
// CHECK: apply
// CHECK: end_access