Files
swift-mirror/test/SILOptimizer/enum_payload_modification_opt.swift
Erik Eckstein 0e08976600 SILOptimizer: a new optimization to hoist destroys of memory locations
DestroyHoisting moves destroys of memory locations up the control flow as far as possible.
Beside destroy_addr, also "store [assign]" is considered a destroy, because is is equivalent to an destroy_addr + a "store [init]".
The main purpose of this optimization is to minimize copy-on-write operations for arrays, etc. Especially if such COW containers  are used as enum payloads and modified in-place. E.g.

switch e {
  case .A(var arr):
    arr.append(x)
    self = .A(arr)
...

In such a case DestroyHoisting can move the destroy of the self-assignment up before the switch and thus let the array buffer only be single-referenced at the time of the append.

When we have ownership SIL throughout the pass pipeline this optimization will replace the current destroy hoisting optimization in CopyForwarding.
For now, this optimization only runs in the mandatory pipeline (but not for -Onone) where we already have ownership SIL.

SR-10605
rdar://problem/50463362
2019-08-28 15:40:05 +02:00

116 lines
2.2 KiB
Swift

// RUN: %empty-directory(%t)
// RUN: %target-build-swift -O -module-name=test %s -o %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s
// REQUIRES: executable_test,swift_stdlib_no_asserts,optimized_stdlib
final class Storage {
var v : Int
init(_ v : Int) {
self.v = v
}
}
struct IntBox {
var s : Storage
init(_ x : Int) {
s = Storage(x)
}
var value: Int { return s.v }
mutating func increment(_ delta: Int = 1) {
if (!isKnownUniquelyReferenced(&s)) {
// We should never see this message
print("## copy on write")
s = Storage(s.v)
}
s.v += delta
}
}
enum E: CustomStringConvertible {
case value(IntBox)
case none
@inline(never)
mutating func simpleIncrement() {
switch self {
case .value(var i):
i.increment()
self = .value(i)
case .none:
break
}
}
@inline(never)
mutating func incrementWithControlFlow(_ n: Int, _ c: Bool) {
switch self {
case .value(var i):
i.increment()
for _ in [0..<n] {
print(" loop iter")
}
if c {
i.increment(10)
self = .value(i)
} else {
i.increment(20)
self = .value(i)
}
case .none:
break
}
}
var description: String {
switch self {
case .value(let i):
return i.s.v.description
case .none:
return "none"
}
}
}
struct ContainingStruct {
var e: E = .value(IntBox(27))
@inline(never)
mutating func doSomething() {
switch self.e {
case .value(var i):
i.increment()
self.e = .value(i)
case .none:
break
}
}
}
// CHECK: simpleIncrement start
print("simpleIncrement start")
var e1 = E.value(IntBox(27))
e1.simpleIncrement()
// CHECK-NEXT: simpleIncrement end: 28
print("simpleIncrement end: \(e1)")
// CHECK-NEXT: incrementWithControlFlow start
print("incrementWithControlFlow start")
var e2 = E.value(IntBox(27))
// CHECK-NEXT: loop iter
e2.incrementWithControlFlow(1, true)
// CHECK-NEXT: incrementWithControlFlow end: 38
print("incrementWithControlFlow end: \(e2)")
// CHECK-NEXT: ContainingStruct start
print("ContainingStruct start")
var s = ContainingStruct()
s.doSomething()
// CHECK-NEXT: ContainingStruct end: 28
print("ContainingStruct end: \(s.e)")