Skip to content
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 @@ -142,6 +142,10 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(

private extension Type {
func mayHaveMutableSpan(in function: Function, _ context: FunctionPassContext) -> Bool {
// Escapable and Copyable types cannot have MutableSpan
if isEscapable || !isMoveOnly {
return false
}
if hasArchetype {
return true
}
Expand Down Expand Up @@ -176,6 +180,24 @@ private extension Type {
}
return false
}

// Returns true if a type maybe Array/ArraySlice/ContiguousArray which are optimized COW types.
// The standard library introduces builtins begin_cow_mutation/end_cow_mutation for such types which are then used to optimize uniqueness checks.
func mayHaveOptimizedCOWType(in function: Function) -> Bool {
// Trivial types cannot be Array/ArraySlice/ContiguousArray.
if isTrivial(in: function) {
return false
}
// Builtin types do not contain Array/ArraySlice/ContiguousArray.
if isBuiltinType {
return false
}
// ~Copyable types cannot contain Array/ArraySlice/ContiguousArray.
if isMoveOnly {
return false
}
return true
}
}

/// Insert end_cow_mutation_addr for lifetime dependent values that maybe of type MutableSpan and depend on a mutable address.
Expand Down Expand Up @@ -203,7 +225,8 @@ private func createEndCOWMutationIfNeeded(lifetimeDep: LifetimeDependence, _ con
return
}

guard lifetimeDep.dependentValue.type.mayHaveMutableSpan(in: lifetimeDep.dependentValue.parentFunction, context) else {
guard lifetimeDep.dependentValue.type.mayHaveMutableSpan(in: lifetimeDep.dependentValue.parentFunction, context) &&
lifetimeDep.parentValue.type.mayHaveOptimizedCOWType(in: lifetimeDep.dependentValue.parentFunction) else {
return
}

Expand Down
31 changes: 31 additions & 0 deletions test/SILOptimizer/mutable_span_stdlib_bounds_check_tests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// RUN: %target-swift-frontend -O -emit-sil %s -disable-availability-checking | %FileCheck %s --check-prefix=CHECK-SIL

public protocol P {
mutating func mutate(_ other: Self)
}

// CHECK-SIL-LABEL: sil @$s38mutable_span_stdlib_bounds_check_tests0a1_B7_doubleyys11MutableSpanVyxGzAA1PRzlF :
// CHECK-SIL: bb3({{.*}}):
// CHECK-SIL-NOT: end_cow_mutation
// CHECK-SIL-NOT: cond_fail "index out of bounds"
// CHECK-SIL-LABEL: } // end sil function '$s38mutable_span_stdlib_bounds_check_tests0a1_B7_doubleyys11MutableSpanVyxGzAA1PRzlF'
public func mutable_span_double<T: P>(_ ms: inout MutableSpan<T>) {
for i in ms.indices {
ms[i].mutate(ms[i])
}
}

extension Int : P {
public mutating func mutate(_ other: Int) {
self += other
}
}

// CHECK-SIL-LABEL: sil @$s38mutable_span_stdlib_bounds_check_tests17specializedCalleryySaySiGzF : $@convention(thin) (@inout Array<Int>) -> () {
// CHECK-SIL-NOT: cond_fail "index out of bounds"
// CHECK-SIL-LABEL: } // end sil function '$s38mutable_span_stdlib_bounds_check_tests17specializedCalleryySaySiGzF'
public func specializedCaller(_ array: inout Array<Int>) {
var mut = array.mutableSpan
mutable_span_double(&mut)
}