Skip to content

Commit 5090603

Browse files
committed
libswift: add an instruction pass to simplify begin_cow_mutation instructions
* Replace the uniqueness result of a begin_cow_mutation of an empty Array/Set/Dictionary singleton with zero. * Remove empty begin_cow_mutation - end_cow_mutation pairs * Remove empty end_cow_mutation - begin_cow_mutation pairs
1 parent 27dc5af commit 5090603

File tree

7 files changed

+244
-37
lines changed

7 files changed

+244
-37
lines changed

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ PASS(PruneVTables, "prune-vtables",
431431
"Mark class methods that do not require vtable dispatch")
432432
PASS_RANGE(AllPasses, AADumper, PruneVTables)
433433

434+
SWIFT_INSTRUCTION_PASS(BeginCOWMutationInst, "simplify-begin_cow_mutation")
434435
SWIFT_INSTRUCTION_PASS_WITH_LEGACY(GlobalValueInst, "simplify-global_value")
435436
SWIFT_INSTRUCTION_PASS_WITH_LEGACY(StrongRetainInst, "simplify-strong_retain")
436437
SWIFT_INSTRUCTION_PASS_WITH_LEGACY(StrongReleaseInst, "simplify-strong_release")

lib/SILOptimizer/Transforms/COWOpts.cpp

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,6 @@ namespace {
5858
/// The optimization can also handle def-use chains between end_cow_mutation and
5959
/// begin_cow_mutation which involve phi-arguments.
6060
///
61-
/// An additional peephole optimization is performed: if the begin_cow_mutation
62-
/// is the only use of the end_cow_mutation, the whole pair of instructions
63-
/// is eliminated.
64-
///
6561
class COWOptsPass : public SILFunctionTransform {
6662
public:
6763
COWOptsPass() {}
@@ -86,18 +82,19 @@ void COWOptsPass::run() {
8682
if (!F->shouldOptimize())
8783
return;
8884

89-
LLVM_DEBUG(llvm::dbgs() << "*** RedundantPhiElimination on function: "
85+
LLVM_DEBUG(llvm::dbgs() << "*** COW optimization on function: "
9086
<< F->getName() << " ***\n");
9187

9288
AA = PM->getAnalysis<AliasAnalysis>(F);
9389

9490
bool changed = false;
9591
for (SILBasicBlock &block : *F) {
96-
auto iter = block.begin();
97-
while (iter != block.end()) {
98-
SILInstruction *inst = &*iter++;
99-
if (auto *beginCOW = dyn_cast<BeginCOWMutationInst>(inst))
100-
changed |= optimizeBeginCOW(beginCOW);
92+
93+
for (SILInstruction &inst : block) {
94+
if (auto *beginCOW = dyn_cast<BeginCOWMutationInst>(&inst)) {
95+
if (optimizeBeginCOW(beginCOW))
96+
changed = true;
97+
}
10198
}
10299
}
103100

@@ -212,20 +209,6 @@ bool COWOptsPass::optimizeBeginCOW(BeginCOWMutationInst *BCM) {
212209
BCM->getUniquenessResult()->getType(), 1);
213210
BCM->getUniquenessResult()->replaceAllUsesWith(IL);
214211

215-
// Try the peephole optimization: remove an end_cow_mutation/begin_cow_mutation
216-
// pair completely if the begin_cow_mutation is the only use of
217-
// end_cow_mutation.
218-
if (auto *singleEndCOW = dyn_cast<EndCOWMutationInst>(BCM->getOperand())) {
219-
assert(endCOWMutationInsts.size() == 1 &&
220-
*endCOWMutationInsts.begin() == singleEndCOW);
221-
if (singleEndCOW->hasOneUse()) {
222-
BCM->getBufferResult()->replaceAllUsesWith(singleEndCOW->getOperand());
223-
BCM->eraseFromParent();
224-
singleEndCOW->eraseFromParent();
225-
return true;
226-
}
227-
}
228-
229212
for (EndCOWMutationInst *ECM : endCOWMutationInsts) {
230213
// This is important for other optimizations: The code is now relying on
231214
// the buffer to be unique.

libswift/Sources/Optimizer/InstructionPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
libswift_sources(Optimizer
10+
SimplifyBeginCOWMutation.swift
1011
SimplifyGlobalValue.swift
1112
SimplifyStrongRetainRelease.swift)
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//===--- SimplifyBeginCOWMutation.swift - Simplify begin_cow_mutation -----===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
/// Simplify begin_cow_mutation instructions.
16+
let simplifyBeginCOWMutationPass = InstructionPass<BeginCOWMutationInst>(
17+
name: "simplify-begin_cow_mutation", {
18+
(beginCOW: BeginCOWMutationInst, context: PassContext) in
19+
20+
/// The buffer of an empty Array/Set/Dictionary singleton is known to be not
21+
/// unique. Replace the uniqueness result of such a
22+
/// `begin_cow_mutation` with a zero `integer_literal`, e.g.
23+
///
24+
/// %3 = global_addr @_swiftEmptyArrayStorage
25+
/// %4 = address_to_pointer %3
26+
/// %5 = raw_pointer_to_ref %4
27+
/// %6 = unchecked_ref_cast %5
28+
/// (%u, %b) = begin_cow_mutation %6
29+
/// ->
30+
/// [...]
31+
/// (%not_used, %b) = begin_cow_mutation %6
32+
/// %u = integer_literal $Builtin.Int1, 0
33+
///
34+
optimizeEmptySingleton(beginCOW, context)
35+
36+
/// If the only use of the `begin_cow_instruction` is an `end_cow_instruction`,
37+
/// remove the pair, e.g.
38+
///
39+
/// (%u, %b) = begin_cow_mutation %0 : $Buffer
40+
/// %e = end_cow_mutation %b : $Buffer
41+
///
42+
if optimizeEmptyBeginEndPair(beginCOW, context) {
43+
return
44+
}
45+
46+
/// If the operand of the `begin_cow_instruction` is an `end_cow_instruction`,
47+
/// which has no other uses, remove the pair, e.g.
48+
///
49+
/// %e = end_cow_mutation %0 : $Buffer
50+
/// (%u, %b) = begin_cow_mutation %e : $Buffer
51+
///
52+
if optimizeEmptyEndBeginPair(beginCOW, context) {
53+
return
54+
}
55+
})
56+
57+
private func optimizeEmptySingleton(_ beginCOW: BeginCOWMutationInst,
58+
_ context: PassContext) {
59+
if !isEmptyCOWSingleton(beginCOW.operand) {
60+
return
61+
}
62+
if beginCOW.uniquenessResult.nonDebugUses.isEmpty {
63+
/// Don't create an integer_literal which would be dead. This would result
64+
/// in an infinite loop in SILCombine.
65+
return
66+
}
67+
let builder = Builder(at: beginCOW, location: beginCOW.location, context)
68+
let zero = builder.createIntegerLiteral(0, type: beginCOW.uniquenessResult.type);
69+
context.replaceAllUses(of: beginCOW.uniquenessResult, with: zero)
70+
}
71+
72+
private func isEmptyCOWSingleton(_ value: Value) -> Bool {
73+
var v = value
74+
while true {
75+
switch v {
76+
case is UncheckedRefCastInst,
77+
is UpcastInst,
78+
is RawPointerToRefInst,
79+
is AddressToPointerInst,
80+
is CopyValueInst:
81+
v = (v as! UnaryInstruction).operand
82+
case let globalAddr as GlobalAddrInst:
83+
let name = globalAddr.global.name
84+
return name == "_swiftEmptyArrayStorage" ||
85+
name == "_swiftEmptyDictionarySingleton" ||
86+
name == "_swiftEmptySetSingleton"
87+
default:
88+
return false
89+
}
90+
}
91+
}
92+
93+
private func optimizeEmptyBeginEndPair(_ beginCOW: BeginCOWMutationInst,
94+
_ context: PassContext) -> Bool {
95+
if !beginCOW.uniquenessResult.nonDebugUses.isEmpty {
96+
return false
97+
}
98+
let buffer = beginCOW.bufferResult
99+
if buffer.nonDebugUses.contains(where: { !($0.instruction is EndCOWMutationInst) }) {
100+
return false
101+
}
102+
103+
for use in buffer.nonDebugUses {
104+
let endCOW = use.instruction as! EndCOWMutationInst
105+
context.replaceAllUses(of: endCOW, with: beginCOW.operand)
106+
context.erase(instruction: endCOW)
107+
}
108+
context.erase(instruction: beginCOW, .includingDebugUses)
109+
return true
110+
}
111+
112+
private func optimizeEmptyEndBeginPair(_ beginCOW: BeginCOWMutationInst,
113+
_ context: PassContext) -> Bool {
114+
if !beginCOW.uniquenessResult.nonDebugUses.isEmpty {
115+
return false
116+
}
117+
guard let endCOW = beginCOW.operand as? EndCOWMutationInst else {
118+
return false
119+
}
120+
if endCOW.nonDebugUses.contains(where: { $0.instruction != beginCOW }) {
121+
return false
122+
}
123+
124+
context.replaceAllUses(of: beginCOW.bufferResult, with: endCOW.operand)
125+
context.erase(instruction: beginCOW, .includingDebugUses)
126+
context.erase(instruction: endCOW, .includingDebugUses)
127+
return true
128+
}

libswift/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ private func registerPass<InstType: Instruction>(
4040
private func registerSwiftPasses() {
4141
registerPass(silPrinterPass, { silPrinterPass.run($0) })
4242
registerPass(mergeCondFailsPass, { mergeCondFailsPass.run($0) })
43+
registerPass(simplifyBeginCOWMutationPass, { simplifyBeginCOWMutationPass.run($0) })
4344
registerPass(simplifyGlobalValuePass, { simplifyGlobalValuePass.run($0) })
4445
registerPass(simplifyStrongRetainPass, { simplifyStrongRetainPass.run($0) })
4546
registerPass(simplifyStrongReleasePass, { simplifyStrongReleasePass.run($0) })

test/SILOptimizer/cow_opts.sil

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,6 @@ final class Buffer {
1313

1414
sil @unknown : $@convention(thin) (@guaranteed Buffer) -> ()
1515

16-
// CHECK-LABEL: sil @test_complete_removal
17-
// CHECK: [[I:%[0-9]+]] = integer_literal $Builtin.Int1, -1
18-
// CHECK: [[T:%[0-9]+]] = tuple ([[I]] : $Builtin.Int1, %0 : $Buffer)
19-
// CHECK: return [[T]]
20-
// CHECK: } // end sil function 'test_complete_removal'
21-
sil @test_complete_removal : $@convention(thin) (@owned Buffer) -> (Builtin.Int1, @owned Buffer) {
22-
bb0(%0 : $Buffer):
23-
%e = end_cow_mutation %0 : $Buffer
24-
(%u, %b) = begin_cow_mutation %e : $Buffer
25-
%t = tuple (%u : $Builtin.Int1, %b : $Buffer)
26-
return %t : $(Builtin.Int1, Buffer)
27-
}
28-
2916
// CHECK-LABEL: sil @test_simple
3017
// CHECK: [[I:%[0-9]+]] = integer_literal $Builtin.Int1, -1
3118
// CHECK: ({{.*}}, [[B:%[0-9]+]]) = begin_cow_mutation

test/SILOptimizer/sil_combine_cow.sil

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -sil-combine | %FileCheck %s
2+
3+
// REQUIRES: libswift
4+
5+
import Builtin
6+
import Swift
7+
import SwiftShims
8+
9+
class Buffer {}
10+
11+
sil_global @_swiftEmptyArrayStorage : $_SwiftEmptyArrayStorage
12+
13+
// CHECK-LABEL: sil [ossa] @remove_end_begin_cow_pair
14+
// CHECK-NOT: end_cow_mutation
15+
// CHECK-NOT: begin_cow_mutation
16+
// CHECK: return %0
17+
// CHECK: } // end sil function 'remove_end_begin_cow_pair'
18+
sil [ossa] @remove_end_begin_cow_pair : $@convention(thin) (@owned Buffer) -> @owned Buffer {
19+
bb0(%0 : @owned $Buffer):
20+
%e = end_cow_mutation %0 : $Buffer
21+
debug_value %e : $Buffer, var, name "x"
22+
(%u, %b) = begin_cow_mutation %e : $Buffer
23+
return %b : $Buffer
24+
}
25+
26+
// CHECK-LABEL: sil [ossa] @dont_remove_end_begin_cow_pair1
27+
// CHECK: end_cow_mutation
28+
// CHECK: begin_cow_mutation
29+
// CHECK: } // end sil function 'dont_remove_end_begin_cow_pair1'
30+
sil [ossa] @dont_remove_end_begin_cow_pair1 : $@convention(thin) (@owned Buffer) -> @owned (Buffer, Buffer) {
31+
bb0(%0 : @owned $Buffer):
32+
%e = end_cow_mutation %0 : $Buffer
33+
%c = copy_value %e : $Buffer
34+
(%u, %b) = begin_cow_mutation %e : $Buffer
35+
%t = tuple (%c : $Buffer, %b : $Buffer)
36+
return %t : $(Buffer, Buffer)
37+
}
38+
39+
// CHECK-LABEL: sil [ossa] @dont_remove_end_begin_cow_pair2
40+
// CHECK: end_cow_mutation
41+
// CHECK: begin_cow_mutation
42+
// CHECK: } // end sil function 'dont_remove_end_begin_cow_pair2'
43+
sil [ossa] @dont_remove_end_begin_cow_pair2 : $@convention(thin) (@owned Buffer) -> @owned (Builtin.Int1, Buffer) {
44+
bb0(%0 : @owned $Buffer):
45+
%e = end_cow_mutation %0 : $Buffer
46+
(%u, %b) = begin_cow_mutation %e : $Buffer
47+
%t = tuple (%u : $Builtin.Int1, %b : $Buffer)
48+
return %t : $(Builtin.Int1, Buffer)
49+
}
50+
51+
// CHECK-LABEL: sil [ossa] @remove_begin_end_cow_pair
52+
// CHECK-NOT: end_cow_mutation
53+
// CHECK-NOT: begin_cow_mutation
54+
// CHECK: return %0
55+
// CHECK: } // end sil function 'remove_begin_end_cow_pair'
56+
sil [ossa] @remove_begin_end_cow_pair : $@convention(thin) (@owned Buffer) -> @owned Buffer {
57+
bb0(%0 : @owned $Buffer):
58+
(%u, %b) = begin_cow_mutation %0 : $Buffer
59+
debug_value %u : $Builtin.Int1, var, name "x"
60+
debug_value %b : $Buffer, var, name "y"
61+
%e = end_cow_mutation %b : $Buffer
62+
return %e : $Buffer
63+
}
64+
65+
// CHECK-LABEL: sil [ossa] @dont_remove_begin_end_cow_pair1
66+
// CHECK: begin_cow_mutation
67+
// CHECK: end_cow_mutation
68+
// CHECK: } // end sil function 'dont_remove_begin_end_cow_pair1'
69+
sil [ossa] @dont_remove_begin_end_cow_pair1 : $@convention(thin) (@owned Buffer) -> @owned (Buffer, Buffer) {
70+
bb0(%0 : @owned $Buffer):
71+
(%u, %b) = begin_cow_mutation %0 : $Buffer
72+
%c = copy_value %b : $Buffer
73+
%e = end_cow_mutation %b : $Buffer
74+
%t = tuple (%c : $Buffer, %e : $Buffer)
75+
return %t : $(Buffer, Buffer)
76+
}
77+
78+
// CHECK-LABEL: sil [ossa] @dont_remove_begin_end_cow_pair2
79+
// CHECK: begin_cow_mutation
80+
// CHECK: end_cow_mutation
81+
// CHECK: } // end sil function 'dont_remove_begin_end_cow_pair2'
82+
sil [ossa] @dont_remove_begin_end_cow_pair2 : $@convention(thin) (@owned Buffer) -> @owned (Builtin.Int1, Buffer) {
83+
bb0(%0 : @owned $Buffer):
84+
(%u, %b) = begin_cow_mutation %0 : $Buffer
85+
%e = end_cow_mutation %b : $Buffer
86+
%t = tuple (%u : $Builtin.Int1, %e : $Buffer)
87+
return %t : $(Builtin.Int1, Buffer)
88+
}
89+
90+
// CHECK-LABEL: sil @optimize_empty_cow_singleton
91+
// CHECK: [[I:%[0-9]+]] = integer_literal $Builtin.Int1, 0
92+
// CHECK: begin_cow_mutation
93+
// CHECK: [[T:%[0-9]+]] = tuple ([[I]]
94+
// CHECK: return [[T]]
95+
// CHECK: } // end sil function 'optimize_empty_cow_singleton'
96+
sil @optimize_empty_cow_singleton : $@convention(thin) () -> (Builtin.Int1, @owned Builtin.BridgeObject) {
97+
bb0:
98+
%3 = global_addr @_swiftEmptyArrayStorage : $*_SwiftEmptyArrayStorage
99+
%4 = address_to_pointer %3 : $*_SwiftEmptyArrayStorage to $Builtin.RawPointer
100+
%5 = raw_pointer_to_ref %4 : $Builtin.RawPointer to $__EmptyArrayStorage
101+
%6 = unchecked_ref_cast %5 : $__EmptyArrayStorage to $Builtin.BridgeObject
102+
(%u, %b) = begin_cow_mutation %6 : $Builtin.BridgeObject
103+
%t = tuple (%u : $Builtin.Int1, %b : $Builtin.BridgeObject)
104+
return %t : $(Builtin.Int1, Builtin.BridgeObject)
105+
}
106+

0 commit comments

Comments
 (0)