mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -29,6 +29,7 @@ swift_compiler_sources(Optimizer
|
||||
SimplifyInitEnumDataAddr.swift
|
||||
SimplifyKeyPath.swift
|
||||
SimplifyLoad.swift
|
||||
SimplifyMarkDependence.swift
|
||||
SimplifyMisc.swift
|
||||
SimplifyPartialApply.swift
|
||||
SimplifyPointerToAddress.swift
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) })
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<{{.*}}>)
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
157
test/SILOptimizer/simplify_mark_dependence.sil
Normal file
157
test/SILOptimizer/simplify_mark_dependence.sil
Normal 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
|
||||
}
|
||||
|
||||
124
test/SILOptimizer/simplify_mark_dependence_addr.sil
Normal file
124
test/SILOptimizer/simplify_mark_dependence_addr.sil
Normal 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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user