Skip to content

Commit 2876194

Browse files
committed
LoopRotate: limit the size of the block to be duplicated.
This avoids significant code size regressions if the loop block to be duplicated is very large. Also remove the ShouldRotate option. It's not needed anymore as disabling the pass can be done by -sil-disable-pass. rdar://problem/33970453
1 parent 26357ee commit 2876194

File tree

2 files changed

+104
-24
lines changed

2 files changed

+104
-24
lines changed

lib/SILOptimizer/LoopTransforms/LoopRotate.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,20 @@
2525
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
2626
#include "swift/SILOptimizer/Utils/LoopUtils.h"
2727
#include "swift/SILOptimizer/Utils/SILSSAUpdater.h"
28+
#include "swift/SILOptimizer/Utils/SILInliner.h"
2829

2930
#include "llvm/Support/Debug.h"
3031
#include "llvm/Support/CommandLine.h"
3132

3233
using namespace swift;
3334

34-
static llvm::cl::opt<bool> ShouldRotate("sil-looprotate",
35-
llvm::cl::init(true));
35+
/// The size limit for the loop block to duplicate.
36+
///
37+
/// Larger blocks will not be duplicated to avoid too much code size increase.
38+
/// It's very seldom that the default value of 20 is exceeded (< 0.3% of all
39+
/// loops in the swift benchmarks).
40+
static llvm::cl::opt<int> LoopRotateSizeLimit("looprotate-size-limit",
41+
llvm::cl::init(20));
3642

3743
/// Check whether all operands are loop invariant.
3844
static bool
@@ -59,6 +65,7 @@ canDuplicateOrMoveToPreheader(SILLoop *loop, SILBasicBlock *preheader,
5965
SILBasicBlock *bb,
6066
SmallVectorImpl<SILInstruction *> &moves) {
6167
llvm::DenseSet<SILInstruction *> invariants;
68+
int cost = 0;
6269
for (auto &instRef : *bb) {
6370
auto *inst = &instRef;
6471
if (auto *MI = dyn_cast<MethodInst>(inst)) {
@@ -92,10 +99,12 @@ canDuplicateOrMoveToPreheader(SILLoop *loop, SILBasicBlock *preheader,
9299
hasLoopInvariantOperands(inst, loop, invariants)) {
93100
moves.push_back(inst);
94101
invariants.insert(inst);
102+
} else {
103+
cost += (int)instructionInlineCost(instRef);
95104
}
96105
}
97106

98-
return true;
107+
return cost < LoopRotateSizeLimit;
99108
}
100109

101110
static void mapOperands(SILInstruction *inst,
@@ -440,11 +449,6 @@ class LoopRotation : public SILFunctionTransform {
440449
LLVM_DEBUG(llvm::dbgs() << "No loops in " << f->getName() << "\n");
441450
return;
442451
}
443-
if (!ShouldRotate) {
444-
LLVM_DEBUG(llvm::dbgs()
445-
<< "Skipping loop rotation in " << f->getName() << "\n");
446-
return;
447-
}
448452
LLVM_DEBUG(llvm::dbgs() << "Rotating loops in " << f->getName() << "\n");
449453
bool shouldVerify = getOptions().VerifyAll;
450454

test/SILOptimizer/looprotate.sil

Lines changed: 92 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,6 @@ sil_stage canonical
88
import Builtin
99
import Swift
1010

11-
// CHECK-LABEL: looprotate
12-
// CHECK: bb0(%0 : $Int32, %1 : $Bar):
13-
// CHECK: class_method
14-
// CHECK: cond_br {{.*}}, bb3({{.*}} : $Builtin.Int32), bb1
15-
16-
// CHECK: bb1:
17-
// CHECK: br bb2({{.*}} : $Builtin.Int32, {{.*}} : $Builtin.Int32, {{.*}}: $Int32)
18-
19-
// CHECK: bb2({{.*}}: $Builtin.Int32, {{.*}} : $Builtin.Int32, {{.*}} : $Int32):
20-
// CHECK: class_method
21-
// CHECK: cond_br {{%.*}}, bb3({{.*}} : $Builtin.Int32), bb2({{.*}} : $Builtin.Int32, {{.*}} : $Builtin.Int32, {{.*}} : $Int32)
22-
23-
// CHECK: bb3({{.*}} : $Builtin.Int32):
24-
// CHECK: [[STRUCT:%.*]] = struct $Int32 ({{%.*}} : $Builtin.Int32)
25-
// CHECK: return [[STRUCT]] : $Int32
26-
2711
protocol P {
2812
func boo() -> Int64
2913
}
@@ -44,6 +28,24 @@ sil_vtable Bar {
4428
#Bar.foo_objc: @_TFC4main3Bar3foofS0_FT_T_
4529
}
4630

31+
// CHECK-LABEL: looprotate
32+
// CHECK: bb0(%0 : $Int32, %1 : $Bar):
33+
// CHECK: class_method
34+
// CHECK: apply
35+
// CHECK: cond_br {{.*}}, bb3({{.*}} : $Builtin.Int32), bb1
36+
37+
// CHECK: bb1:
38+
// CHECK: br bb2({{.*}} : $Builtin.Int32, {{.*}} : $Builtin.Int32, {{.*}}: $Int32)
39+
40+
// CHECK: bb2({{.*}}: $Builtin.Int32, {{.*}} : $Builtin.Int32, {{.*}} : $Int32):
41+
// CHECK: class_method
42+
// CHECK: apply
43+
// CHECK: cond_br {{%.*}}, bb3({{.*}} : $Builtin.Int32), bb2({{.*}} : $Builtin.Int32, {{.*}} : $Builtin.Int32, {{.*}} : $Int32)
44+
45+
// CHECK: bb3({{.*}} : $Builtin.Int32):
46+
// CHECK: [[STRUCT:%.*]] = struct $Int32 ({{%.*}} : $Builtin.Int32)
47+
// CHECK: return [[STRUCT]] : $Int32
48+
// CHECK: } // end sil function 'looprotate'
4749
sil @looprotate : $@convention(thin) (Int32, @owned Bar) -> Int32 {
4850
bb0(%0 : $Int32, %25: $Bar):
4951
%1 = struct_extract %0 : $Int32, #Int32._value
@@ -77,6 +79,80 @@ bb3:
7779
return %23 : $Int32
7880
}
7981

82+
// CHECK-LABEL: dont_duplicate_large_block
83+
// CHECK: bb0(%0 : $Int32, %1 : $Bar):
84+
// CHECK-NOT: class_method
85+
// CHECK-NOT: apply
86+
// CHECK: br bb1
87+
88+
// CHECK: bb1({{.*}}):
89+
// CHECK: class_method
90+
// CHECK: apply
91+
// CHECK: cond_br
92+
93+
// CHECK: bb2:
94+
// CHECK: br bb1
95+
96+
// CHECK: bb3:
97+
// CHECK: return
98+
// CHECK: } // end sil function 'dont_duplicate_large_block'
99+
sil @dont_duplicate_large_block : $@convention(thin) (Int32, @owned Bar) -> Int32 {
100+
bb0(%0 : $Int32, %25: $Bar):
101+
%1 = struct_extract %0 : $Int32, #Int32._value
102+
%2 = integer_literal $Builtin.Int32, 0
103+
%30 = alloc_box $<τ_0_0> { var τ_0_0 } <Bool>
104+
%30a = project_box %30 : $<τ_0_0> { var τ_0_0 } <Bool>, 0
105+
br bb1(%1 : $Builtin.Int32, %2 : $Builtin.Int32, %25: $Bar, %30 : $<τ_0_0> { var τ_0_0 } <Bool>, %30a : $*Bool)
106+
107+
bb1(%4 : $Builtin.Int32, %5 : $Builtin.Int32, %26: $Bar, %31 : $<τ_0_0> { var τ_0_0 } <Bool>, %32 : $*Bool):
108+
%24 = class_method %26 : $Bar, #Bar.foo : (Bar) -> () -> (), $@convention(method) (@guaranteed Bar) -> () // user: %6
109+
%27 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
110+
%28 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
111+
%29 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
112+
%33 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
113+
%34 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
114+
%35 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
115+
%36 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
116+
%37 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
117+
%38 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
118+
%39 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
119+
%40 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
120+
%41 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
121+
%42 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
122+
%43 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
123+
%44 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
124+
%45 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
125+
%46 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
126+
%47 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
127+
%48 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
128+
%49 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
129+
%50 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
130+
%51 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
131+
%52 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
132+
%53 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
133+
%54 = apply %24(%25) : $@convention(method) (@guaranteed Bar) -> ()
134+
%6 = struct $Int32 (%5 : $Builtin.Int32)
135+
%8 = builtin "cmp_eq_Word"(%5 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1
136+
cond_br %8, bb3, bb2
137+
138+
bb2:
139+
%10 = integer_literal $Builtin.Int32, 1
140+
%12 = integer_literal $Builtin.Int1, -1
141+
%13 = builtin "sadd_with_overflow_Word"(%5 : $Builtin.Int32, %10 : $Builtin.Int32, %12 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
142+
%14 = tuple_extract %13 : $(Builtin.Int32, Builtin.Int1), 0
143+
%15 = enum $Optional<Int32>, #Optional.some!enumelt, %6 : $Int32
144+
%16 = unchecked_enum_data %15 : $Optional<Int32>, #Optional.some!enumelt
145+
%17 = struct_extract %16 : $Int32, #Int32._value
146+
%19 = integer_literal $Builtin.Int1, -1
147+
%20 = builtin "sadd_with_overflow_Word"(%4 : $Builtin.Int32, %17 : $Builtin.Int32, %19 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
148+
%21 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 0
149+
br bb1(%21 : $Builtin.Int32, %14 : $Builtin.Int32, %26: $Bar, %31 : $<τ_0_0> { var τ_0_0 } <Bool>, %32 : $*Bool)
150+
151+
bb3:
152+
%23 = struct $Int32 (%4 : $Builtin.Int32)
153+
return %23 : $Int32
154+
}
155+
80156
// This example illustrates the problem with using ValueUseIterators.
81157
// As part of updating SSA form we will introduce a phi node argument to the
82158
// branch to bb2. This means we change "cond_br %8, bb3(%4 : $Builtin.Int32),

0 commit comments

Comments
 (0)