Files
swift-mirror/test/SILOptimizer/merge_exclusivity.swift
Andrew Trick 30994389f2 Disable merging of read/modify exclusivity access markers.
When two access scopes have different access types, it is not safe to
merge them into one access. Doing so will introduce false conflicts
which manifest as crashes in user code.

To do this optimization, we would need additional data flow information
about *potential* read conflicts before converting a read to a modify
access.

Fixes rdar://48239213: Fatal access conflict detected.
2019-02-21 13:05:20 -08:00

397 lines
14 KiB
Swift

// RUN: %target-swift-frontend -O -enforce-exclusivity=checked -emit-sil -primary-file %s | %FileCheck %s --check-prefix=TESTSIL
// REQUIRES: optimized_stdlib,asserts
// REQUIRES: PTRSIZE=64
public var check: UInt64 = 0
@inline(never)
func sum(_ x: UInt64, _ y: UInt64) -> UInt64 {
return x &+ y
}
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// TESTSIL-LABEL: sil [noinline] @$s17merge_exclusivity10MergeTest1yySiF : $@convention(thin)
// TESTSIL: bb0
// TESTSIL: [[GLOBALVAR:%.*]] = global_addr @$s17merge_exclusivity5checks6UInt64Vvp
// TESTSIL: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL: end_access [[B1]]
// TESTSIL: bb5
// TESTSIL: [[B2a:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL-NEXT: load [[B2a]]
// TESTSIL: end_access [[B2a]]
// TESTSIL: [[B2b:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL: store {{.*}} to [[B2b]]
// TESTSIL: end_access [[B2b]]
// TESTSIL: bb6
// TESTSIL: [[B3a:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL-NEXT: load [[B3a]]
// TESTSIL: end_access [[B3a]]
// TESTSIL: [[B3b:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL: store {{.*}} to [[B3b]]
// TESTSIL: end_access [[B3b]]
// TESTSIL: bb7
// TESTSIL: [[B4a:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL-NEXT: load [[B4a]]
// TESTSIL: end_access [[B4a]]
// TESTSIL: [[B4b:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL: store {{.*}} to [[B4b]]
// TESTSIL: end_access [[B4b]]
// TESTSIL-NOT: begin_access
// TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity10MergeTest1yySiF'
@inline(never)
public func MergeTest1(_ N: Int) {
let range = 0..<10000
check = 0
for _ in 1...N {
for e in range {
check = sum(check, UInt64(e))
if (e == 0) {
check = sum(check, UInt64(1))
}
else {
check = sum(check, UInt64(2))
}
}
}
}
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// TESTSIL-LABEL: sil [noinline] @$s17merge_exclusivity10MergeTest2yySiF : $@convention(thin)
// TESTSIL: bb0
// TESTSIL: [[GLOBALVAR:%.*]] = global_addr @$s17merge_exclusivity5checks6UInt64Vvp
// TESTSIL: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL: end_access [[B1]]
// TESTSIL: bb6
// TESTSIL: [[B2a:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL-NEXT: load [[B2a]]
// TESTSIL: end_access [[B2a]]
// TESTSIL: [[B2b:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL: store {{.*}} to [[B2b]]
// TESTSIL: end_access [[B2b]]
// TESTSIL: bb7
// TESTSIL: [[B3a:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL-NEXT: load [[B3a]]
// TESTSIL: end_access [[B3a]]
// TESTSIL: [[B3b:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBALVAR]]
// TESTSIL: store {{.*}} to [[B3b]]
// TESTSIL: end_access [[B3b]]
// TESTSIL-NOT: begin_access
// TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity10MergeTest2yySiF'
@inline(never)
public func MergeTest2(_ N: Int) {
let range = 0..<10000
check = 0
for _ in 1...N {
for e in range {
if (e == 0) {
check = sum(check, UInt64(1))
}
else {
check = sum(check, UInt64(2))
}
}
}
}
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// FIXME_TESTSIL-LABEL: sil [noinline] @$s17merge_exclusivity10MergeTest3yySiF : $@convention(thin)
// FIXME_TESTSIL: bb0
// FIXME_TESTSIL: [[GLOBALVAR:%.*]] = global_addr @$s17merge_exclusivity5checks6UInt64Vvp
// FIXME_TESTSIL: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALVAR]]
// FIXME_TESTSIL: end_access [[B1]]
// FIXME_TESTSIL-NOT: begin_access
// FIXME_TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity10MergeTest3yySiF'
@inline(never)
public func MergeTest3(_ N: Int) {
let range = 0..<10000
check = 0
for _ in 1...N {
for e in range {
check = sum(check, UInt64(e))
}
}
}
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// FIXME_TESTSIL-LABEL: sil [noinline] @$s17merge_exclusivity10MergeTest4yySiF : $@convention(thin)
// FIXME_TESTSIL: bb0
// FIXME_TESTSIL: [[GLOBALVAR:%.*]] = global_addr @$s17merge_exclusivity5checks6UInt64Vvp
// FIXME_TESTSIL: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALVAR]]
// FIXME_TESTSIL: end_access [[B1]]
// FIXME_TESTSIL: bb7
// FIXME_TESTSIL: [[B2:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBALVAR]]
// FIXME_TESTSIL-NEXT: load [[B2]]
// FIXME_TESTSIL: store {{.*}} to [[B2]]
// FIXME_TESTSIL: end_access [[B2]]
// FIXME_TESTSIL: bb8
// FIXME_TESTSIL: [[B3:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBALVAR]]
// FIXME_TESTSIL-NEXT: load [[B3]]
// FIXME_TESTSIL: store {{.*}} to [[B3]]
// FIXME_TESTSIL: end_access [[B3]]
// FIXME_TESTSIL-NOT: begin_access
// FIXME_TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity10MergeTest4yySiF'
@inline(never)
public func MergeTest4(_ N: Int) {
let range = 0..<10000
check = 0
for _ in 1...N {
for e in range {
if (e == 0) {
check = sum(check, UInt64(1))
}
check = sum(check, UInt64(e))
}
}
}
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// FIXME_TESTSIL-LABEL: sil [noinline] @$s17merge_exclusivity10MergeTest5yySiF : $@convention(thin)
// FIXME_TESTSIL: bb0
// FIXME_TESTSIL: [[GLOBALVAR:%.*]] = global_addr @$s17merge_exclusivity5checks6UInt64Vvp
// FIXME_TESTSIL: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALVAR]]
// FIXME_TESTSIL: end_access [[B1]]
// FIXME_TESTSIL: bb6
// FIXME_TESTSIL: [[B2:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBALVAR]]
// FIXME_TESTSIL-NEXT: load [[B2]]
// FIXME_TESTSIL: store {{.*}} to [[B2]]
// FIXME_TESTSIL: end_access [[B2]]
// FIXME_TESTSIL: bb7
// FIXME_TESTSIL: [[B3:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBALVAR]]
// FIXME_TESTSIL-NEXT: load [[B3]]
// FIXME_TESTSIL: store {{.*}} to [[B3]]
// FIXME_TESTSIL: end_access [[B3]]
// FIXME_TESTSIL: bb8
// FIXME_TESTSIL: [[B4:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBALVAR]]
// FIXME_TESTSIL-NEXT: load [[B4]]
// FIXME_TESTSIL: store {{.*}} to [[B4]]
// FIXME_TESTSIL: end_access [[B4]]
// FIXME_TESTSIL-NOT: begin_access
// FIXME_TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity10MergeTest5yySiF'
@inline(never)
public func MergeTest5(_ N: Int) {
let range = 0..<10000
check = 0
for _ in 1...N {
for e in range {
if (e == 0) {
check = sum(check, UInt64(1))
}
else {
check = sum(check, UInt64(2))
}
check = sum(check, UInt64(e))
}
}
}
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// FIXME_TESTSIL-LABEL: sil [noinline] @$s17merge_exclusivity10MergeTest6yySiF : $@convention(thin)
// FIXME_TESTSIL: bb0
// FIXME_TESTSIL: [[GLOBALVAR:%.*]] = global_addr @$s17merge_exclusivity5checks6UInt64Vvp
// FIXME_TESTSIL: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALVAR]]
// FIXME_TESTSIL: end_access [[B1]]
// FIXME_TESTSIL-NOT: begin_access
// FIXME_TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity10MergeTest6yySiF'
@inline(never)
public func MergeTest6(_ N: Int) {
let range = 0..<10000
check = 0
for _ in 1...N {
for e in range {
check = sum(check, UInt64(e))
for _ in range {
check = sum(check, UInt64(e))
}
check = sum(check, UInt64(e))
}
}
}
@inline(never)
public func foo() {
}
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// FIXME_TESTSIL-LABEL: sil [noinline] @$s17merge_exclusivity10MergeTest7yySiF : $@convention(thin)
// FIXME_TESTSIL: bb0
// FIXME_TESTSIL: [[GLOBALVAR:%.*]] = global_addr @$s17merge_exclusivity5checks6UInt64Vvp
// FIXME_TESTSIL: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALVAR]]
// FIXME_TESTSIL: end_access [[B1]]
// FIXME_TESTSIL-NOT: begin_access
// FIXME_TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity10MergeTest7yySiF'
@inline(never)
public func MergeTest7(_ N: Int) {
let range = 0..<10000
check = 0
for _ in 1...N {
for e in range {
check = sum(check, UInt64(e))
for _ in range {
foo()
}
check = sum(check, UInt64(e))
}
}
}
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// FIXME_TESTSIL-LABEL: sil [noinline] @$s17merge_exclusivity10MergeTest8yySiF : $@convention(thin)
// FIXME_TESTSIL: bb0
// FIXME_TESTSIL: [[GLOBALVAR:%.*]] = global_addr @$s17merge_exclusivity5checks6UInt64Vvp
// FIXME_TESTSIL: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALVAR]]
// FIXME_TESTSIL: end_access [[B1]]
// FIXME_TESTSIL-NOT: begin_access
// FIXME_TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity10MergeTest8yySiF'
@inline(never)
public func MergeTest8(_ N: Int) {
let range = 0..<10000
check = 0
for _ in 1...N {
for e in range {
check = sum(check, UInt64(e))
for _ in range {
foo()
}
check = sum(check, UInt64(e))
}
}
for _ in 1...N {
for e in range {
check = sum(check, UInt64(e))
for _ in range {
foo()
}
check = sum(check, UInt64(e))
}
}
}
// Large, test that tests the interaction between access merging,
// and the rest of the access optimizations.
public protocol WriteProt {
func writeTo(_ stream: StreamClass)
}
public final class StreamClass {
private var buffer: [UInt8]
public init() {
self.buffer = []
}
public func write(_ byte: UInt8) {
buffer.append(byte)
}
public func write(_ value: WriteProt) {
value.writeTo(self)
}
public func writeEscaped(_ string: String) {
writeEscaped(string: string.utf8)
}
public func writeEscaped<T: Collection>(
string sequence: T
) where T.Iterator.Element == UInt8 {
for character in sequence {
buffer.append(character)
buffer.append(character)
}
}
}
public func toStream(_ stream: StreamClass, _ value: WriteProt) -> StreamClass {
stream.write(value)
return stream
}
extension UInt8: WriteProt {
public func writeTo(_ stream: StreamClass) {
stream.write(self)
}
}
public func asWriteProt(_ string: String) -> WriteProt {
return EscapedString(value: string)
}
private struct EscapedString: WriteProt {
let value: String
func writeTo(_ stream: StreamClass) {
_ = toStream(stream, UInt8(ascii: "a"))
stream.writeEscaped(value)
_ = toStream(stream, UInt8(ascii: "a"))
}
}
public func asWriteProt<T>(_ items: [T], transform: @escaping (T) -> String) -> WriteProt {
return EscapedTransforme(items: items, transform: transform)
}
private struct EscapedTransforme<T>: WriteProt {
let items: [T]
let transform: (T) -> String
func writeTo(_ stream: StreamClass) {
for (i, item) in items.enumerated() {
if i != 0 { _ = toStream(stream, asWriteProt(transform(item))) }
_ = toStream(stream, asWriteProt(transform(item)))
}
}
}
// TESTSIL-LABEL: sil [noinline] @$s17merge_exclusivity14run_MergeTest9yySiF : $@convention(thin)
// TESTSIL: [[REFADDR:%.*]] = ref_element_addr {{.*}} : $StreamClass, #StreamClass.buffer
// TESTSIL-NEXT: [[B1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[REFADDR]]
// TESTSIL: end_access [[B1]]
// TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]]
// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array<UInt8>) -> ()
// TESTSIL: end_access [[BCONF]]
// TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]]
// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array<UInt8>) -> ()
// TESTSIL: end_access [[BCONF]]
// TESTSIL: [[BCONF:%.*]] = begin_access [modify] [dynamic] [[REFADDR]]
// TESTSIL: apply {{.*}} : $@convention(method) (Int, @inout Array<UInt8>) -> ()
// TESTSIL: end_access [[BCONF]]
// TESTSIL-LABEL: } // end sil function '$s17merge_exclusivity14run_MergeTest9yySiF'
@inline(never)
public func run_MergeTest9(_ N: Int) {
struct Thing {
var value: String
init(_ value: String) { self.value = value }
}
let listOfStrings: [String] = (0..<10).map { "This is the number: \($0)!\n" }
let listOfThings: [Thing] = listOfStrings.map(Thing.init)
for _ in 1...N {
let stream = StreamClass()
_ = toStream(stream, asWriteProt(listOfThings, transform: { $0.value }))
}
}