xref: /llvm-project/llvm/test/Transforms/SimplifyCFG/common-code-hoisting.ll (revision 8979ae42769e529b0f6fce3268492ffb49bd54b9)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -hoist-common-insts=1 -S < %s                    | FileCheck %s --check-prefix=HOIST
3; RUN: opt -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -hoist-common-insts=0 -S < %s                    | FileCheck %s --check-prefix=NOHOIST
4; RUN: opt -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1                       -S < %s                    | FileCheck %s --check-prefix=NOHOIST
5
6; This example is produced from a very basic C code:
7;
8;   void f0();
9;   void f1();
10;   void f2();
11;
12;   void loop(int width) {
13;       if(width < 1)
14;           return;
15;       for(int i = 0; i < width - 1;+i) {
16;           f0();
17;           f1();
18;       }
19;       f0();
20;       f2();
21;   }
22
23; We have a choice here. We can either
24; * hoist the f0() call into loop header,
25;   * which potentially makes loop rotation unprofitable since loop header might
26;     have grown above certain threshold, and such unrotated loops will be
27;     ignored by LoopVectorizer, preventing vectorization
28;   * or loop rotation will succeed, resulting in some weird PHIs that will also
29;     harm vectorization
30; * or not hoist f0() call before performing loop rotation,
31;   at the cost of potential code bloat and/or potentially successfully rotating
32;   the loops, vectorizing them at the cost of compile time.
33
34target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
35
36declare i1 @gen1()
37
38declare void @f0()
39declare void @f1()
40declare void @f2()
41
42declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
43declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)
44
45define void @_Z4loopi(i1 %cmp) {
46; HOIST-LABEL: @_Z4loopi(
47; HOIST-NEXT:  entry:
48; HOIST-NEXT:    br i1 [[CMP:%.*]], label [[RETURN:%.*]], label [[FOR_COND:%.*]]
49; HOIST:       for.cond:
50; HOIST-NEXT:    [[CMP1:%.*]] = call i1 @gen1()
51; HOIST-NEXT:    call void @f0()
52; HOIST-NEXT:    br i1 [[CMP1]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
53; HOIST:       for.body:
54; HOIST-NEXT:    call void @f1()
55; HOIST-NEXT:    br label [[FOR_COND]]
56; HOIST:       for.end:
57; HOIST-NEXT:    call void @f2()
58; HOIST-NEXT:    br label [[RETURN]]
59; HOIST:       return:
60; HOIST-NEXT:    ret void
61;
62; NOHOIST-LABEL: @_Z4loopi(
63; NOHOIST-NEXT:  entry:
64; NOHOIST-NEXT:    br i1 [[CMP:%.*]], label [[RETURN:%.*]], label [[FOR_COND:%.*]]
65; NOHOIST:       for.cond:
66; NOHOIST-NEXT:    [[CMP1:%.*]] = call i1 @gen1()
67; NOHOIST-NEXT:    br i1 [[CMP1]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]]
68; NOHOIST:       for.body:
69; NOHOIST-NEXT:    call void @f0()
70; NOHOIST-NEXT:    call void @f1()
71; NOHOIST-NEXT:    br label [[FOR_COND]]
72; NOHOIST:       for.end:
73; NOHOIST-NEXT:    call void @f0()
74; NOHOIST-NEXT:    call void @f2()
75; NOHOIST-NEXT:    br label [[RETURN]]
76; NOHOIST:       return:
77; NOHOIST-NEXT:    ret void
78;
79entry:
80  br i1 %cmp, label %if.then, label %if.end
81
82if.then:
83  br label %return
84
85if.end:
86  br label %for.cond
87
88for.cond:
89  %cmp1 = call i1 @gen1()
90  br i1 %cmp1, label %for.body, label %for.cond.cleanup
91
92for.cond.cleanup:
93  br label %for.end
94
95for.body:
96  call void @f0()
97  call void @f1()
98  br label %for.inc
99
100for.inc:
101  br label %for.cond
102
103for.end:
104  call void @f0()
105  call void @f2()
106  br label %return
107
108return:
109  ret void
110}
111
112; A example where only the branch instructions from %if.then2 and %if.end3 need
113; to be hoisted, which effectively replaces the original branch in %if.end and
114; only requires selects for PHIs in the successor.
115define float @clamp_float_value(float %value, float %minimum_value, float %maximum_value) {
116; HOIST-LABEL: @clamp_float_value(
117; HOIST-NEXT:  entry:
118; HOIST-NEXT:    [[CMP:%.*]] = fcmp ogt float [[VALUE:%.*]], [[MAXIMUM_VALUE:%.*]]
119; HOIST-NEXT:    [[CMP1:%.*]] = fcmp olt float [[VALUE]], [[MINIMUM_VALUE:%.*]]
120; HOIST-NEXT:    [[MINIMUM_VALUE_VALUE:%.*]] = select i1 [[CMP1]], float [[MINIMUM_VALUE]], float [[VALUE]]
121; HOIST-NEXT:    [[RETVAL_0:%.*]] = select i1 [[CMP]], float [[MAXIMUM_VALUE]], float [[MINIMUM_VALUE_VALUE]]
122; HOIST-NEXT:    ret float [[RETVAL_0]]
123;
124; NOHOIST-LABEL: @clamp_float_value(
125; NOHOIST-NEXT:  entry:
126; NOHOIST-NEXT:    [[CMP:%.*]] = fcmp ogt float [[VALUE:%.*]], [[MAXIMUM_VALUE:%.*]]
127; NOHOIST-NEXT:    [[CMP1:%.*]] = fcmp olt float [[VALUE]], [[MINIMUM_VALUE:%.*]]
128; NOHOIST-NEXT:    [[MINIMUM_VALUE_VALUE:%.*]] = select i1 [[CMP1]], float [[MINIMUM_VALUE]], float [[VALUE]]
129; NOHOIST-NEXT:    [[RETVAL_0:%.*]] = select i1 [[CMP]], float [[MAXIMUM_VALUE]], float [[MINIMUM_VALUE_VALUE]]
130; NOHOIST-NEXT:    ret float [[RETVAL_0]]
131;
132entry:
133  %cmp = fcmp ogt float %value, %maximum_value
134  br i1 %cmp, label %if.then, label %if.end
135
136if.then:                                          ; preds = %entry
137  br label %return
138
139if.end:                                           ; preds = %entry
140  %cmp1 = fcmp olt float %value, %minimum_value
141  br i1 %cmp1, label %if.then2, label %if.end3
142
143if.then2:                                         ; preds = %if.end
144  br label %return
145
146if.end3:                                          ; preds = %if.end
147  br label %return
148
149return:                                           ; preds = %if.end3, %if.then2, %if.then
150  %retval.0 = phi float [ %maximum_value, %if.then ], [ %minimum_value, %if.then2 ], [ %value, %if.end3 ]
151  ret float %retval.0
152}
153