Skip to content

Commit 27ece6f

Browse files
authored
Merge pull request #34968 from gottesmm/pr-50e8ac8f699f7e3ef7096b4b56510ffc897669e5
[mandatory-combine] Add a canonicalization impl and prepare to use mandatory-combine to test ownership RAUW code
2 parents 65db86b + 28e7e95 commit 27ece6f

File tree

5 files changed

+218
-3
lines changed

5 files changed

+218
-3
lines changed

lib/SILOptimizer/Mandatory/MandatoryCombine.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "swift/SIL/SILVisitor.h"
3232
#include "swift/SILOptimizer/PassManager/Passes.h"
3333
#include "swift/SILOptimizer/PassManager/Transforms.h"
34+
#include "swift/SILOptimizer/Utils/CanonicalizeInstruction.h"
3435
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
3536
#include "swift/SILOptimizer/Utils/StackNesting.h"
3637
#include "llvm/ADT/STLExtras.h"
@@ -53,6 +54,54 @@ static bool areAllValuesTrivial(Values values, SILFunction &function) {
5354
});
5455
}
5556

57+
//===----------------------------------------------------------------------===//
58+
// CanonicalizeInstruction subclass for use in Mandatory Combiner.
59+
//===----------------------------------------------------------------------===//
60+
61+
namespace {
62+
63+
class MandatoryCombineCanonicalize final : CanonicalizeInstruction {
64+
public:
65+
using Worklist = SmallSILInstructionWorklist<256>;
66+
67+
private:
68+
Worklist &worklist;
69+
bool changed = false;
70+
71+
public:
72+
MandatoryCombineCanonicalize(Worklist &worklist)
73+
: CanonicalizeInstruction(DEBUG_TYPE), worklist(worklist) {}
74+
75+
void notifyNewInstruction(SILInstruction *inst) override {
76+
worklist.add(inst);
77+
worklist.addUsersOfAllResultsToWorklist(inst);
78+
changed = true;
79+
}
80+
81+
// Just delete the given 'inst' and record its operands. The callback isn't
82+
// allowed to mutate any other instructions.
83+
void killInstruction(SILInstruction *inst) override {
84+
worklist.eraseSingleInstFromFunction(*inst,
85+
/*AddOperandsToWorklist*/ true);
86+
changed = true;
87+
}
88+
89+
void notifyHasNewUsers(SILValue value) override {
90+
if (worklist.size() < 10000) {
91+
worklist.addUsersToWorklist(value);
92+
}
93+
changed = true;
94+
}
95+
96+
bool tryCanonicalize(SILInstruction *inst) {
97+
changed = false;
98+
canonicalize(inst);
99+
return changed;
100+
}
101+
};
102+
103+
} // anonymous namespace
104+
56105
//===----------------------------------------------------------------------===//
57106
// MandatoryCombiner Interface
58107
//===----------------------------------------------------------------------===//
@@ -137,6 +186,13 @@ class MandatoryCombiner final
137186
// MandatoryCombiner Non-Visitor Utility Methods
138187
//===----------------------------------------------------------------------===//
139188

189+
static llvm::cl::opt<bool> EnableCanonicalizationAndTrivialDCE(
190+
"sil-mandatory-combine-enable-canon-and-simple-dce", llvm::cl::Hidden,
191+
llvm::cl::init(false),
192+
llvm::cl::desc("An option for compiler developers that cause the Mandatory "
193+
"Combiner to be more aggressive at eliminating trivially "
194+
"dead code and canonicalizing SIL"));
195+
140196
void MandatoryCombiner::addReachableCodeToWorklist(SILFunction &function) {
141197
SmallVector<SILBasicBlock *, 32> blockWorklist;
142198
SmallPtrSet<SILBasicBlock *, 32> blockAlreadyAddedToWorklist;
@@ -148,6 +204,8 @@ void MandatoryCombiner::addReachableCodeToWorklist(SILFunction &function) {
148204
blockAlreadyAddedToWorklist.insert(firstBlock);
149205
}
150206

207+
bool compilingWithOptimization = function.getEffectiveOptimizationMode() !=
208+
OptimizationMode::NoOptimization;
151209
while (!blockWorklist.empty()) {
152210
auto *block = blockWorklist.pop_back_val();
153211

@@ -156,6 +214,12 @@ void MandatoryCombiner::addReachableCodeToWorklist(SILFunction &function) {
156214
++iterator;
157215

158216
if (isInstructionTriviallyDead(instruction)) {
217+
if (EnableCanonicalizationAndTrivialDCE) {
218+
if (compilingWithOptimization) {
219+
instruction->replaceAllUsesOfAllResultsWithUndef();
220+
instruction->eraseFromParent();
221+
}
222+
}
159223
continue;
160224
}
161225

@@ -177,13 +241,32 @@ bool MandatoryCombiner::doOneIteration(SILFunction &function,
177241
madeChange = false;
178242

179243
addReachableCodeToWorklist(function);
244+
MandatoryCombineCanonicalize mcCanonicialize(worklist);
245+
246+
bool compilingWithOptimization = function.getEffectiveOptimizationMode() !=
247+
OptimizationMode::NoOptimization;
180248

181249
while (!worklist.isEmpty()) {
182250
auto *instruction = worklist.pop_back_val();
183251
if (instruction == nullptr) {
184252
continue;
185253
}
186254

255+
if (EnableCanonicalizationAndTrivialDCE) {
256+
if (compilingWithOptimization) {
257+
if (isInstructionTriviallyDead(instruction)) {
258+
worklist.eraseInstFromFunction(*instruction);
259+
madeChange = true;
260+
continue;
261+
}
262+
}
263+
264+
if (mcCanonicialize.tryCanonicalize(instruction)) {
265+
madeChange = true;
266+
continue;
267+
}
268+
}
269+
187270
#ifndef NDEBUG
188271
std::string instructionDescription;
189272
#endif

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ swift::createDecrementBefore(SILValue ptr, SILInstruction *insertPt) {
138138
return builder.createReleaseValue(loc, ptr, builder.getDefaultAtomicity());
139139
}
140140

141+
static bool isOSSAEndScopeWithNoneOperand(SILInstruction *i) {
142+
if (!isa<EndBorrowInst>(i) && !isa<DestroyValueInst>(i))
143+
return false;
144+
return i->getOperand(0).getOwnershipKind() == OwnershipKind::None;
145+
}
146+
141147
/// Perform a fast local check to see if the instruction is dead.
142148
///
143149
/// This routine only examines the state of the instruction at hand.
@@ -178,6 +184,15 @@ bool swift::isInstructionTriviallyDead(SILInstruction *inst) {
178184
if (isa<UncheckedTakeEnumDataAddrInst>(inst))
179185
return true;
180186

187+
// An ossa end scope instruction is trivially dead if its operand has
188+
// OwnershipKind::None. This can occur after CFG simplification in the
189+
// presence of non-payloaded or trivial payload cases of non-trivial enums.
190+
//
191+
// Examples of ossa end_scope instructions: end_borrow, destroy_value.
192+
if (inst->getFunction()->hasOwnership() &&
193+
isOSSAEndScopeWithNoneOperand(inst))
194+
return true;
195+
181196
if (!inst->mayHaveSideEffects())
182197
return true;
183198

test/SILOptimizer/mandatory_combiner.sil

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ sil @myint_from_myint_and_proto : $@convention(thin) (MyInt, @guaranteed { var P
7575

7676
sil @myint_from_proto_and_myint : $@convention(thin) (@guaranteed { var Proto }, MyInt) -> MyInt
7777

78+
// Optional support
79+
enum FakeOptional<T> {
80+
case none
81+
case some(T)
82+
}
83+
7884
///////////
7985
// Tests //
8086
///////////
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// RUN: %target-sil-opt -mandatory-combine -sil-mandatory-combine-enable-canon-and-simple-dce %s | %FileCheck %s
2+
3+
// Tests for when the mandatory combiner is running with optimizations
4+
// enabled. Only put tests here for functionality that only occurs when the
5+
// Mandatory Combiner runs in between the diagnostics/perf pipeline at -O,
6+
// -Osize.
7+
8+
sil_stage canonical
9+
10+
import Builtin
11+
12+
// Trivial declarations
13+
14+
struct MyInt {
15+
var value: Builtin.Int64
16+
}
17+
18+
// Generic declarations
19+
20+
protocol Addable {
21+
static var an: Self { get }
22+
}
23+
24+
// Class declarations
25+
26+
class Klass {
27+
init()
28+
deinit
29+
}
30+
31+
// Existential declarations
32+
33+
protocol Proto {
34+
static var an: Proto { get }
35+
}
36+
37+
// Trivial support
38+
39+
sil @first_of_three_ints : $@convention(thin) (MyInt, MyInt, MyInt) -> MyInt
40+
41+
sil @constant_zero : $@convention(thin) () -> MyInt
42+
43+
sil @identity_int : $@convention(thin) (MyInt) -> MyInt
44+
45+
// Generic support
46+
47+
sil @first_of_three_addables : $@convention(thin) <A where A : Addable> (@in_guaranteed A, @guaranteed <τ_0_0 where τ_0_0 : Addable> { var τ_0_0 } <A>, @guaranteed <τ_0_0 where τ_0_0 : Addable> { var τ_0_0 } <A>) -> @
48+
out A
49+
50+
// Class support
51+
52+
sil [exact_self_class] @klass_alloc_init : $@convention(method) (@thick Klass.Type) -> @owned Klass
53+
54+
// Klass.init()
55+
sil @klass_init : $@convention(method) (@owned Klass) -> @owned Klass
56+
// Klass.deinit
57+
sil @klass_deinit : $@convention(method) (@guaranteed Klass) -> @owned Builtin.NativeObject
58+
59+
// Klass.__deallocating_deinit
60+
sil @klass_dealloc_deinit : $@convention(method) (@owned Klass) -> ()
61+
62+
sil_vtable Klass {
63+
#Klass.init!allocator: (Klass.Type) -> () -> Klass : @klass_alloc_init
64+
#Klass.deinit!deallocator: @klass_dealloc_deinit
65+
}
66+
67+
sil @first_of_three_klasses : $@convention(thin) (@guaranteed Klass, @guaranteed Klass, @guaranteed Klass) -> @owned Klass
68+
69+
// Existential support
70+
71+
sil @first_of_three_protos : $@convention(thin) (@in_guaranteed Proto, @guaranteed { var Proto }, @guaranteed { var Proto }) -> @out Proto
72+
73+
sil @get_proto : $@convention(thin) () -> @out Proto
74+
75+
// Mixed support
76+
77+
sil @proto_from_proto_and_myint : $@convention(thin) (@in_guaranteed Proto, MyInt) -> @out Proto
78+
79+
sil @myint_from_myint_and_proto : $@convention(thin) (MyInt, @guaranteed { var Proto }) -> MyInt
80+
81+
sil @myint_from_proto_and_myint : $@convention(thin) (@guaranteed { var Proto }, MyInt) -> MyInt
82+
83+
// Optional support
84+
enum FakeOptional<T> {
85+
case none
86+
case some(T)
87+
}
88+
89+
///////////
90+
// Tests //
91+
///////////
92+
93+
94+
// CHECK-LABEL: sil [ossa] @eliminate_simple_arc_traffic : $@convention(thin) (@guaranteed Klass) -> () {
95+
// CHECK-NOT: copy_value
96+
// CHECK-NOT: destroy_value
97+
// CHECK-NOT: enum
98+
// CHECK-NOT: end_borrow
99+
// CHECK: } // end sil function 'eliminate_simple_arc_traffic'
100+
sil [ossa] @eliminate_simple_arc_traffic : $@convention(thin) (@guaranteed Klass) -> () {
101+
bb0(%0 : @guaranteed $Klass):
102+
%1 = copy_value %0 : $Klass
103+
destroy_value %1 : $Klass
104+
%2 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt
105+
end_borrow %2 : $FakeOptional<Klass>
106+
%9999 = tuple()
107+
return %9999 : $()
108+
}

test/stdlib/Inputs/CommonArrayTests.gyb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
% # - Suite -- an identifier for the test suite to append tests to.
66
% # - ArrayType -- the type being tested.
77

8+
// We use this global array to prevent ARC from eliminating temporary ARC
9+
// traffic in nonUniqueCode below. It is only ever assigned to.
10+
var globalArrayForNonUnique = ${ArrayType}<Int>()
811

912
extension ${ArrayType} {
1013
typealias _BufferID = UnsafeRawPointer?
@@ -107,7 +110,7 @@ ${Suite}.test("${ArrayType}/appendNonUnique")
107110
x.reserveCapacity(10002)
108111
let capacity = x.capacity
109112
for _ in 1...100 {
110-
let y = x
113+
globalArrayForNonUnique = x
111114
x.append(1)
112115
expectTrue(x.capacity == capacity)
113116
}
@@ -120,7 +123,7 @@ ${Suite}.test("${ArrayType}/removeNonUnique")
120123
var x = ${ArrayType}<Int>(repeating: 27, count: 200)
121124
x.reserveCapacity(10002)
122125
for _ in 1...100 {
123-
let y = x
126+
globalArrayForNonUnique = x
124127
x.remove(at: 0)
125128
expectTrue(x.capacity < 1000)
126129
}
@@ -133,7 +136,7 @@ ${Suite}.test("${ArrayType}/mutateNonUnique")
133136
var x = ${ArrayType}<Int>(repeating: 27, count: 200)
134137
x.reserveCapacity(10002)
135138
for _ in 1...100 {
136-
let y = x
139+
globalArrayForNonUnique = x
137140
x[0] = 0
138141
expectTrue(x.capacity < 1000)
139142
}

0 commit comments

Comments
 (0)