Skip to content

Commit 1d51dc3

Browse files
committed
[SimplifyCFG][LoopRotate] SimplifyCFG: disable common instruction hoisting by default, enable late in pipeline
I've been looking at missed vectorizations in one codebase. One particular thing that stands out is that some of the loops reach vectorizer in a rather mangled form, with weird PHI's, and some of the loops aren't even in a rotated form. After taking a more detailed look, that happened because the loop's headers were too big by then. It is evident that SimplifyCFG's common code hoisting transform is at fault there, because the pattern it handles is precisely the unrotated loop basic block structure. Surprizingly, `SimplifyCFGOpt::HoistThenElseCodeToIf()` is enabled by default, and is always run, unlike it's friend, common code sinking transform, `SinkCommonCodeFromPredecessors()`, which is not enabled by default and is only run once very late in the pipeline. I'm proposing to harmonize this, and disable common code hoisting until //late// in pipeline. Definition of //late// may vary, here currently i've picked the same one as for code sinking, but i suppose we could enable it as soon as right after loop rotation happens. Experimentation shows that this does indeed unsurprizingly help, more loops got rotated, although other issues remain elsewhere. Now, this undoubtedly seriously shakes phase ordering. This will undoubtedly be a mixed bag in terms of both compile- and run- time performance, codesize. Since we no longer aggressively hoist+deduplicate common code, we don't pay the price of said hoisting (which wasn't big). That may allow more loops to be rotated, so we pay that price. That, in turn, that may enable all the transforms that require canonical (rotated) loop form, including but not limited to vectorization, so we pay that too. And in general, no deduplication means more [duplicate] instructions going through the optimizations. But there's still late hoisting, some of them will be caught late. As per benchmarks i've run {F12360204}, this is mostly within the noise, there are some small improvements, some small regressions. One big regression i saw i fixed in rG8d487668d09fb0e4e54f36207f07c1480ffabbfd, but i'm sure this will expose many more pre-existing missed optimizations, as usual :S llvm-compile-time-tracker.com thoughts on this: http://llvm-compile-time-tracker.com/compare.php?from=e40315d2b4ed1e38962a8f33ff151693ed4ada63&to=c8289c0ecbf235da9fb0e3bc052e3c0d6bff5cf9&stat=instructions * this does regress compile-time by +0.5% geomean (unsurprizingly) * size impact varies; for ThinLTO it's actually an improvement The largest fallout appears to be in GVN's load partial redundancy elimination, it spends *much* more time in `MemoryDependenceResults::getNonLocalPointerDependency()`. Non-local `MemoryDependenceResults` is widely-known to be, uh, costly. There does not appear to be a proper solution to this issue, other than silencing the compile-time performance regression by tuning cut-off thresholds in `MemoryDependenceResults`, at the cost of potentially regressing run-time performance. D84609 attempts to move in that direction, but the path is unclear and is going to take some time. If we look at stats before/after diffs, some excerpts: * RawSpeed (the target) {F12360200} * -14 (-73.68%) loops not rotated due to the header size (yay) * -272 (-0.67%) `"Number of live out of a loop variables"` - good for vectorizer * -3937 (-64.19%) common instructions hoisted * +561 (+0.06%) x86 asm instructions * -2 basic blocks * +2418 (+0.11%) IR instructions * vanilla test-suite + RawSpeed + darktable {F12360201} * -36396 (-65.29%) common instructions hoisted * +1676 (+0.02%) x86 asm instructions * +662 (+0.06%) basic blocks * +4395 (+0.04%) IR instructions It is likely to be sub-optimal for when optimizing for code size, so one might want to change tune pipeline by enabling sinking/hoisting when optimizing for size. Reviewed By: mkazantsev Differential Revision: https://reviews.llvm.org/D84108
1 parent 802c043 commit 1d51dc3

File tree

10 files changed

+39
-26
lines changed

10 files changed

+39
-26
lines changed

llvm/include/llvm/Transforms/Utils/SimplifyCFGOptions.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct SimplifyCFGOptions {
2525
bool ForwardSwitchCondToPhi = false;
2626
bool ConvertSwitchToLookupTable = false;
2727
bool NeedCanonicalLoop = true;
28-
bool HoistCommonInsts = true;
28+
bool HoistCommonInsts = false;
2929
bool SinkCommonInsts = false;
3030
bool SimplifyCondBranch = true;
3131
bool FoldTwoEntryPHINode = true;

llvm/lib/Passes/PassBuilder.cpp

+8-5
Original file line numberDiff line numberDiff line change
@@ -1144,11 +1144,14 @@ ModulePassManager PassBuilder::buildModuleOptimizationPipeline(
11441144
// convert to more optimized IR using more aggressive simplify CFG options.
11451145
// The extra sinking transform can create larger basic blocks, so do this
11461146
// before SLP vectorization.
1147-
OptimizePM.addPass(SimplifyCFGPass(SimplifyCFGOptions().
1148-
forwardSwitchCondToPhi(true).
1149-
convertSwitchToLookupTable(true).
1150-
needCanonicalLoops(false).
1151-
sinkCommonInsts(true)));
1147+
// FIXME: study whether hoisting and/or sinking of common instructions should
1148+
// be delayed until after SLP vectorizer.
1149+
OptimizePM.addPass(SimplifyCFGPass(SimplifyCFGOptions()
1150+
.forwardSwitchCondToPhi(true)
1151+
.convertSwitchToLookupTable(true)
1152+
.needCanonicalLoops(false)
1153+
.hoistCommonInsts(true)
1154+
.sinkCommonInsts(true)));
11521155

11531156
// Optimize parallel scalar instruction chains into SIMD instructions.
11541157
if (PTO.SLPVectorization)

llvm/lib/Target/AArch64/AArch64TargetMachine.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ void AArch64PassConfig::addIRPasses() {
457457
.forwardSwitchCondToPhi(true)
458458
.convertSwitchToLookupTable(true)
459459
.needCanonicalLoops(false)
460+
.hoistCommonInsts(true)
460461
.sinkCommonInsts(true)));
461462

462463
// Run LoopDataPrefetch

llvm/lib/Target/ARM/ARMTargetMachine.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,8 @@ void ARMPassConfig::addIRPasses() {
409409
// ldrex/strex loops to simplify this, but it needs tidying up.
410410
if (TM->getOptLevel() != CodeGenOpt::None && EnableAtomicTidy)
411411
addPass(createCFGSimplificationPass(
412-
SimplifyCFGOptions().sinkCommonInsts(true), [this](const Function &F) {
412+
SimplifyCFGOptions().hoistCommonInsts(true).sinkCommonInsts(true),
413+
[this](const Function &F) {
413414
const auto &ST = this->TM->getSubtarget<ARMSubtarget>(F);
414415
return ST.hasAnyDataBarrier() && !ST.isThumb1Only();
415416
}));

llvm/lib/Target/Hexagon/HexagonTargetMachine.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ void HexagonPassConfig::addIRPasses() {
324324
.forwardSwitchCondToPhi(true)
325325
.convertSwitchToLookupTable(true)
326326
.needCanonicalLoops(false)
327+
.hoistCommonInsts(true)
327328
.sinkCommonInsts(true)));
328329
if (EnableLoopPrefetch)
329330
addPass(createLoopDataPrefetchPass());

llvm/lib/Transforms/IPO/PassManagerBuilder.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -784,10 +784,13 @@ void PassManagerBuilder::populateModulePassManager(
784784
// convert to more optimized IR using more aggressive simplify CFG options.
785785
// The extra sinking transform can create larger basic blocks, so do this
786786
// before SLP vectorization.
787+
// FIXME: study whether hoisting and/or sinking of common instructions should
788+
// be delayed until after SLP vectorizer.
787789
MPM.add(createCFGSimplificationPass(SimplifyCFGOptions()
788790
.forwardSwitchCondToPhi(true)
789791
.convertSwitchToLookupTable(true)
790792
.needCanonicalLoops(false)
793+
.hoistCommonInsts(true)
791794
.sinkCommonInsts(true)));
792795

793796
if (SLPVectorize) {

llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ static cl::opt<bool> UserForwardSwitchCond(
6363
cl::desc("Forward switch condition to phi ops (default = false)"));
6464

6565
static cl::opt<bool> UserHoistCommonInsts(
66-
"hoist-common-insts", cl::Hidden, cl::init(true),
67-
cl::desc("hoist common instructions (default = true)"));
66+
"hoist-common-insts", cl::Hidden, cl::init(false),
67+
cl::desc("hoist common instructions (default = false)"));
6868

6969
static cl::opt<bool> UserSinkCommonInsts(
7070
"sink-common-insts", cl::Hidden, cl::init(false),

llvm/test/Transforms/PGOProfile/chr.ll

+7
Original file line numberDiff line numberDiff line change
@@ -2008,9 +2008,16 @@ define i64 @test_chr_22(i1 %i, i64* %j, i64 %v0) !prof !14 {
20082008
; CHECK-NEXT: bb0:
20092009
; CHECK-NEXT: [[REASS_ADD:%.*]] = shl i64 [[V0:%.*]], 1
20102010
; CHECK-NEXT: [[V2:%.*]] = add i64 [[REASS_ADD]], 3
2011+
; CHECK-NEXT: [[C1:%.*]] = icmp slt i64 [[V2]], 100
2012+
; CHECK-NEXT: br i1 [[C1]], label [[BB0_SPLIT:%.*]], label [[BB0_SPLIT_NONCHR:%.*]], !prof !15
2013+
; CHECK: bb0.split:
20112014
; CHECK-NEXT: [[V299:%.*]] = mul i64 [[V2]], 7860086430977039991
20122015
; CHECK-NEXT: store i64 [[V299]], i64* [[J:%.*]], align 4
20132016
; CHECK-NEXT: ret i64 99
2017+
; CHECK: bb0.split.nonchr:
2018+
; CHECK-NEXT: [[V299_NONCHR:%.*]] = mul i64 [[V2]], 7860086430977039991
2019+
; CHECK-NEXT: store i64 [[V299_NONCHR]], i64* [[J]], align 4
2020+
; CHECK-NEXT: ret i64 99
20142021
;
20152022
bb0:
20162023
%v1 = add i64 %v0, 3

llvm/test/Transforms/PhaseOrdering/loop-rotation-vs-common-code-hoisting.ll

+13-16
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,11 @@
55
; RUN: opt -O3 -rotation-max-header-size=1 -S < %s | FileCheck %s --check-prefixes=HOIST,THR1,FALLBACK2
66
; RUN: opt -passes='default<O3>' -rotation-max-header-size=1 -S < %s | FileCheck %s --check-prefixes=HOIST,THR1,FALLBACK3
77

8-
; RUN: opt -O3 -rotation-max-header-size=2 -S < %s | FileCheck %s --check-prefixes=HOIST,THR2,FALLBACK4
9-
; RUN: opt -passes='default<O3>' -rotation-max-header-size=2 -S < %s | FileCheck %s --check-prefixes=HOIST,THR2,FALLBACK5
8+
; RUN: opt -O3 -rotation-max-header-size=2 -S < %s | FileCheck %s --check-prefixes=ROTATED_LATER,ROTATED_LATER_OLDPM,FALLBACK4
9+
; RUN: opt -passes='default<O3>' -rotation-max-header-size=2 -S < %s | FileCheck %s --check-prefixes=ROTATED_LATER,ROTATED_LATER_NEWPM,FALLBACK5
1010

11-
; RUN: opt -O3 -rotation-max-header-size=3 -S < %s | FileCheck %s --check-prefixes=ROTATED_LATER,ROTATED_LATER_OLDPM,FALLBACK6
12-
; RUN: opt -passes='default<O3>' -rotation-max-header-size=3 -S < %s | FileCheck %s --check-prefixes=ROTATED_LATER,ROTATED_LATER_NEWPM,FALLBACK7
13-
14-
; RUN: opt -O3 -rotation-max-header-size=4 -S < %s | FileCheck %s --check-prefixes=ROTATE,ROTATE_OLDPM,FALLBACK8
15-
; RUN: opt -passes='default<O3>' -rotation-max-header-size=4 -S < %s | FileCheck %s --check-prefixes=ROTATE,ROTATE_NEWPM,FALLBACK9
11+
; RUN: opt -O3 -rotation-max-header-size=3 -S < %s | FileCheck %s --check-prefixes=ROTATE,ROTATE_OLDPM,FALLBACK6
12+
; RUN: opt -passes='default<O3>' -rotation-max-header-size=3 -S < %s | FileCheck %s --check-prefixes=ROTATE,ROTATE_NEWPM,FALLBACK7
1613

1714
; This example is produced from a very basic C code:
1815
;
@@ -61,8 +58,8 @@ define void @_Z4loopi(i32 %width) {
6158
; HOIST-NEXT: br label [[FOR_COND:%.*]]
6259
; HOIST: for.cond:
6360
; HOIST-NEXT: [[I_0:%.*]] = phi i32 [ [[INC:%.*]], [[FOR_BODY:%.*]] ], [ 0, [[FOR_COND_PREHEADER]] ]
64-
; HOIST-NEXT: tail call void @f0()
6561
; HOIST-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i32 [[I_0]], [[TMP0]]
62+
; HOIST-NEXT: tail call void @f0()
6663
; HOIST-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY]]
6764
; HOIST: for.cond.cleanup:
6865
; HOIST-NEXT: tail call void @f2()
@@ -80,17 +77,17 @@ define void @_Z4loopi(i32 %width) {
8077
; ROTATED_LATER_OLDPM-NEXT: br i1 [[CMP]], label [[RETURN:%.*]], label [[FOR_COND_PREHEADER:%.*]]
8178
; ROTATED_LATER_OLDPM: for.cond.preheader:
8279
; ROTATED_LATER_OLDPM-NEXT: [[TMP0:%.*]] = add nsw i32 [[WIDTH]], -1
83-
; ROTATED_LATER_OLDPM-NEXT: tail call void @f0()
8480
; ROTATED_LATER_OLDPM-NEXT: [[EXITCOND_NOT3:%.*]] = icmp eq i32 [[TMP0]], 0
8581
; ROTATED_LATER_OLDPM-NEXT: br i1 [[EXITCOND_NOT3]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY:%.*]]
8682
; ROTATED_LATER_OLDPM: for.cond.cleanup:
83+
; ROTATED_LATER_OLDPM-NEXT: tail call void @f0()
8784
; ROTATED_LATER_OLDPM-NEXT: tail call void @f2()
8885
; ROTATED_LATER_OLDPM-NEXT: br label [[RETURN]]
8986
; ROTATED_LATER_OLDPM: for.body:
9087
; ROTATED_LATER_OLDPM-NEXT: [[I_04:%.*]] = phi i32 [ [[INC:%.*]], [[FOR_BODY]] ], [ 0, [[FOR_COND_PREHEADER]] ]
88+
; ROTATED_LATER_OLDPM-NEXT: tail call void @f0()
9189
; ROTATED_LATER_OLDPM-NEXT: tail call void @f1()
9290
; ROTATED_LATER_OLDPM-NEXT: [[INC]] = add nuw i32 [[I_04]], 1
93-
; ROTATED_LATER_OLDPM-NEXT: tail call void @f0()
9491
; ROTATED_LATER_OLDPM-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC]], [[TMP0]]
9592
; ROTATED_LATER_OLDPM-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]]
9693
; ROTATED_LATER_OLDPM: return:
@@ -102,19 +99,19 @@ define void @_Z4loopi(i32 %width) {
10299
; ROTATED_LATER_NEWPM-NEXT: br i1 [[CMP]], label [[RETURN:%.*]], label [[FOR_COND_PREHEADER:%.*]]
103100
; ROTATED_LATER_NEWPM: for.cond.preheader:
104101
; ROTATED_LATER_NEWPM-NEXT: [[TMP0:%.*]] = add nsw i32 [[WIDTH]], -1
105-
; ROTATED_LATER_NEWPM-NEXT: tail call void @f0()
106102
; ROTATED_LATER_NEWPM-NEXT: [[EXITCOND_NOT3:%.*]] = icmp eq i32 [[TMP0]], 0
107103
; ROTATED_LATER_NEWPM-NEXT: br i1 [[EXITCOND_NOT3]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_COND_PREHEADER_FOR_BODY_CRIT_EDGE:%.*]]
108104
; ROTATED_LATER_NEWPM: for.cond.preheader.for.body_crit_edge:
109105
; ROTATED_LATER_NEWPM-NEXT: [[INC_1:%.*]] = add nuw i32 0, 1
110106
; ROTATED_LATER_NEWPM-NEXT: br label [[FOR_BODY:%.*]]
111107
; ROTATED_LATER_NEWPM: for.cond.cleanup:
108+
; ROTATED_LATER_NEWPM-NEXT: tail call void @f0()
112109
; ROTATED_LATER_NEWPM-NEXT: tail call void @f2()
113110
; ROTATED_LATER_NEWPM-NEXT: br label [[RETURN]]
114111
; ROTATED_LATER_NEWPM: for.body:
115112
; ROTATED_LATER_NEWPM-NEXT: [[INC_PHI:%.*]] = phi i32 [ [[INC_0:%.*]], [[FOR_BODY_FOR_BODY_CRIT_EDGE:%.*]] ], [ [[INC_1]], [[FOR_COND_PREHEADER_FOR_BODY_CRIT_EDGE]] ]
116-
; ROTATED_LATER_NEWPM-NEXT: tail call void @f1()
117113
; ROTATED_LATER_NEWPM-NEXT: tail call void @f0()
114+
; ROTATED_LATER_NEWPM-NEXT: tail call void @f1()
118115
; ROTATED_LATER_NEWPM-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC_PHI]], [[TMP0]]
119116
; ROTATED_LATER_NEWPM-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY_FOR_BODY_CRIT_EDGE]]
120117
; ROTATED_LATER_NEWPM: for.body.for.body_crit_edge:
@@ -129,19 +126,19 @@ define void @_Z4loopi(i32 %width) {
129126
; ROTATE_OLDPM-NEXT: br i1 [[CMP]], label [[RETURN:%.*]], label [[FOR_COND_PREHEADER:%.*]]
130127
; ROTATE_OLDPM: for.cond.preheader:
131128
; ROTATE_OLDPM-NEXT: [[CMP13_NOT:%.*]] = icmp eq i32 [[WIDTH]], 1
132-
; ROTATE_OLDPM-NEXT: tail call void @f0()
133129
; ROTATE_OLDPM-NEXT: br i1 [[CMP13_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_PREHEADER:%.*]]
134130
; ROTATE_OLDPM: for.body.preheader:
135131
; ROTATE_OLDPM-NEXT: [[TMP0:%.*]] = add nsw i32 [[WIDTH]], -1
136132
; ROTATE_OLDPM-NEXT: br label [[FOR_BODY:%.*]]
137133
; ROTATE_OLDPM: for.cond.cleanup:
134+
; ROTATE_OLDPM-NEXT: tail call void @f0()
138135
; ROTATE_OLDPM-NEXT: tail call void @f2()
139136
; ROTATE_OLDPM-NEXT: br label [[RETURN]]
140137
; ROTATE_OLDPM: for.body:
141138
; ROTATE_OLDPM-NEXT: [[I_04:%.*]] = phi i32 [ [[INC:%.*]], [[FOR_BODY]] ], [ 0, [[FOR_BODY_PREHEADER]] ]
139+
; ROTATE_OLDPM-NEXT: tail call void @f0()
142140
; ROTATE_OLDPM-NEXT: tail call void @f1()
143141
; ROTATE_OLDPM-NEXT: [[INC]] = add nuw nsw i32 [[I_04]], 1
144-
; ROTATE_OLDPM-NEXT: tail call void @f0()
145142
; ROTATE_OLDPM-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC]], [[TMP0]]
146143
; ROTATE_OLDPM-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]]
147144
; ROTATE_OLDPM: return:
@@ -153,19 +150,19 @@ define void @_Z4loopi(i32 %width) {
153150
; ROTATE_NEWPM-NEXT: br i1 [[CMP]], label [[RETURN:%.*]], label [[FOR_COND_PREHEADER:%.*]]
154151
; ROTATE_NEWPM: for.cond.preheader:
155152
; ROTATE_NEWPM-NEXT: [[CMP13_NOT:%.*]] = icmp eq i32 [[WIDTH]], 1
156-
; ROTATE_NEWPM-NEXT: tail call void @f0()
157153
; ROTATE_NEWPM-NEXT: br i1 [[CMP13_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_PREHEADER:%.*]]
158154
; ROTATE_NEWPM: for.body.preheader:
159155
; ROTATE_NEWPM-NEXT: [[TMP0:%.*]] = add nsw i32 [[WIDTH]], -1
160156
; ROTATE_NEWPM-NEXT: [[INC_1:%.*]] = add nuw nsw i32 0, 1
161157
; ROTATE_NEWPM-NEXT: br label [[FOR_BODY:%.*]]
162158
; ROTATE_NEWPM: for.cond.cleanup:
159+
; ROTATE_NEWPM-NEXT: tail call void @f0()
163160
; ROTATE_NEWPM-NEXT: tail call void @f2()
164161
; ROTATE_NEWPM-NEXT: br label [[RETURN]]
165162
; ROTATE_NEWPM: for.body:
166163
; ROTATE_NEWPM-NEXT: [[INC_PHI:%.*]] = phi i32 [ [[INC_0:%.*]], [[FOR_BODY_FOR_BODY_CRIT_EDGE:%.*]] ], [ [[INC_1]], [[FOR_BODY_PREHEADER]] ]
167-
; ROTATE_NEWPM-NEXT: tail call void @f1()
168164
; ROTATE_NEWPM-NEXT: tail call void @f0()
165+
; ROTATE_NEWPM-NEXT: tail call void @f1()
169166
; ROTATE_NEWPM-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC_PHI]], [[TMP0]]
170167
; ROTATE_NEWPM-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY_FOR_BODY_CRIT_EDGE]]
171168
; ROTATE_NEWPM: for.body.for.body_crit_edge:

llvm/test/Transforms/SimplifyCFG/common-code-hoisting.ll

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
22
; RUN: opt -simplifycfg -hoist-common-insts=1 -S < %s | FileCheck %s --check-prefixes=HOIST
33
; RUN: opt -simplifycfg -hoist-common-insts=0 -S < %s | FileCheck %s --check-prefixes=NOHOIST
4-
; RUN: opt -simplifycfg -S < %s | FileCheck %s --check-prefixes=HOIST,DEFAULT
4+
; RUN: opt -simplifycfg -S < %s | FileCheck %s --check-prefixes=NOHOIST,DEFAULT
55

66
; This example is produced from a very basic C code:
77
;

0 commit comments

Comments
 (0)