xref: /llvm-project/mlir/test/Transforms/sccp-structured.mlir (revision 13bd41096286305ee603428f6adf161f52981827)
1// RUN: mlir-opt -allow-unregistered-dialect %s -pass-pipeline="builtin.module(func.func(sccp))" -split-input-file | FileCheck %s
2
3/// Check that a constant is properly propagated when only one edge is taken.
4
5// CHECK-LABEL: func @simple(
6func.func @simple(%arg0 : i32) -> i32 {
7  // CHECK: %[[CST:.*]] = arith.constant 1 : i32
8  // CHECK-NOT: scf.if
9  // CHECK: return %[[CST]] : i32
10
11  %cond = arith.constant true
12  %res = scf.if %cond -> (i32) {
13    %1 = arith.constant 1 : i32
14    scf.yield %1 : i32
15  } else {
16    scf.yield %arg0 : i32
17  }
18  return %res : i32
19}
20
21/// Check that a constant is properly propagated when both edges produce the
22/// same value.
23
24// CHECK-LABEL: func @simple_both_same(
25func.func @simple_both_same(%cond : i1) -> i32 {
26  // CHECK: %[[CST:.*]] = arith.constant 1 : i32
27  // CHECK-NOT: scf.if
28  // CHECK: return %[[CST]] : i32
29
30  %res = scf.if %cond -> (i32) {
31    %1 = arith.constant 1 : i32
32    scf.yield %1 : i32
33  } else {
34    %2 = arith.constant 1 : i32
35    scf.yield %2 : i32
36  }
37  return %res : i32
38}
39
40/// Check that the arguments go to overdefined if the branch cannot detect when
41/// a specific successor is taken.
42
43// CHECK-LABEL: func @overdefined_unknown_condition(
44func.func @overdefined_unknown_condition(%cond : i1, %arg0 : i32) -> i32 {
45  // CHECK: %[[RES:.*]] = scf.if
46  // CHECK: return %[[RES]] : i32
47
48  %res = scf.if %cond -> (i32) {
49    %1 = arith.constant 1 : i32
50    scf.yield %1 : i32
51  } else {
52    scf.yield %arg0 : i32
53  }
54  return %res : i32
55}
56
57/// Check that the arguments go to overdefined if there are conflicting
58/// constants.
59
60// CHECK-LABEL: func @overdefined_different_constants(
61func.func @overdefined_different_constants(%cond : i1) -> i32 {
62  // CHECK: %[[RES:.*]] = scf.if
63  // CHECK: return %[[RES]] : i32
64
65  %res = scf.if %cond -> (i32) {
66    %1 = arith.constant 1 : i32
67    scf.yield %1 : i32
68  } else {
69    %2 = arith.constant 2 : i32
70    scf.yield %2 : i32
71  }
72  return %res : i32
73}
74
75/// Check that arguments are properly merged across loop-like control flow.
76
77// CHECK-LABEL: func @simple_loop(
78func.func @simple_loop(%arg0 : index, %arg1 : index, %arg2 : index) -> i32 {
79  // CHECK: %[[CST:.*]] = arith.constant 0 : i32
80  // CHECK-NOT: scf.for
81  // CHECK: return %[[CST]] : i32
82
83  %s0 = arith.constant 0 : i32
84  %result = scf.for %i0 = %arg0 to %arg1 step %arg2 iter_args(%si = %s0) -> (i32) {
85    %sn = arith.addi %si, %si : i32
86    scf.yield %sn : i32
87  }
88  return %result : i32
89}
90
91/// Check that arguments go to overdefined when loop backedges produce a
92/// conflicting value.
93
94// CHECK-LABEL: func @loop_overdefined(
95func.func @loop_overdefined(%arg0 : index, %arg1 : index, %arg2 : index) -> i32 {
96  // CHECK: %[[RES:.*]] = scf.for
97  // CHECK: return %[[RES]] : i32
98
99  %s0 = arith.constant 1 : i32
100  %result = scf.for %i0 = %arg0 to %arg1 step %arg2 iter_args(%si = %s0) -> (i32) {
101    %sn = arith.addi %si, %si : i32
102    scf.yield %sn : i32
103  }
104  return %result : i32
105}
106
107/// Test that we can properly propagate within inner control, and in situations
108/// where the executable edges within the CFG are sensitive to the current state
109/// of the analysis.
110
111// CHECK-LABEL: func @loop_inner_control_flow(
112func.func @loop_inner_control_flow(%arg0 : index, %arg1 : index, %arg2 : index) -> i32 {
113  // CHECK: %[[CST:.*]] = arith.constant 1 : i32
114  // CHECK-NOT: scf.for
115  // CHECK-NOT: scf.if
116  // CHECK: return %[[CST]] : i32
117
118  %cst_1 = arith.constant 1 : i32
119  %result = scf.for %i0 = %arg0 to %arg1 step %arg2 iter_args(%si = %cst_1) -> (i32) {
120    %cst_20 = arith.constant 20 : i32
121    %cond = arith.cmpi ult, %si, %cst_20 : i32
122    %inner_res = scf.if %cond -> (i32) {
123      %1 = arith.constant 1 : i32
124      scf.yield %1 : i32
125    } else {
126      %si_inc = arith.addi %si, %cst_1 : i32
127      scf.yield %si_inc : i32
128    }
129    scf.yield %inner_res : i32
130  }
131  return %result : i32
132}
133
134/// Test that we can properly visit region successors when the terminator
135/// implements the RegionBranchTerminatorOpInterface.
136
137// CHECK-LABEL: func @loop_region_branch_terminator_op(
138func.func @loop_region_branch_terminator_op(%arg1 : i32) {
139  // CHECK:      %c2_i32 = arith.constant 2 : i32
140  // CHECK-NEXT: return
141
142  %c2_i32 = arith.constant 2 : i32
143   %0 = scf.while (%arg2 = %c2_i32) : (i32) -> (i32) {
144    %1 = arith.cmpi sgt, %arg1, %arg2 : i32
145    scf.condition(%1) %arg2 : i32
146  } do {
147  ^bb0(%arg2: i32):
148    scf.yield %arg2 : i32
149  }
150  return
151}
152
153/// Check that propgation happens for affine.for -- tests its region branch op
154/// interface as well.
155
156// CHECK-LABEL: func @affine_loop_one_iter(
157func.func @affine_loop_one_iter() -> i32 {
158  // CHECK: %[[C1:.*]] = arith.constant 1 : i32
159  %s0 = arith.constant 0 : i32
160  %s1 = arith.constant 1 : i32
161  %result = affine.for %i = 0 to 1 iter_args(%si = %s0) -> (i32) {
162    %sn = arith.addi %si, %s1 : i32
163    affine.yield %sn : i32
164  }
165  // CHECK: return %[[C1]] : i32
166  return %result : i32
167}
168
169// CHECK-LABEL: func @affine_loop_zero_iter(
170func.func @affine_loop_zero_iter() -> i32 {
171  // CHECK: %[[C1:.*]] = arith.constant 1 : i32
172  %s1 = arith.constant 1 : i32
173  %result = affine.for %i = 0 to 0 iter_args(%si = %s1) -> (i32) {
174   %sn = arith.addi %si, %si : i32
175   affine.yield %sn : i32
176  }
177  // CHECK: return %[[C1]] : i32
178  return %result : i32
179}
180
181// CHECK-LABEL: func @affine_loop_unknown_trip_count(
182func.func @affine_loop_unknown_trip_count(%ub: index) -> i32 {
183  // CHECK: %[[C0:.*]] = arith.constant 0 : i32
184  %s0 = arith.constant 0 : i32
185  %result = affine.for %i = 0 to %ub iter_args(%si = %s0) -> (i32) {
186   %sn = arith.addi %si, %si : i32
187   affine.yield %sn : i32
188  }
189  // CHECK: return %[[C0]] : i32
190  return %result : i32
191}
192
193// CHECK-LABEL: func @while_loop_different_arg_count
194func.func @while_loop_different_arg_count() -> index {
195  // CHECK-DAG: %[[TRUE:.*]] = arith.constant true
196  // CHECK-DAG: %[[C0:.*]] = arith.constant 0
197  // CHECK-DAG: %[[C1:.*]] = arith.constant 1
198  %c0 = arith.constant 0 : index
199  %c1 = arith.constant 1 : index
200  // CHECK: %[[WHILE:.*]] = scf.while
201  %0 = scf.while (%arg3 = %c0, %arg4 = %c1) : (index, index) -> index {
202    %1 = arith.cmpi slt, %arg3, %c1 : index
203    // CHECK: scf.condition(%[[TRUE]]) %[[C1]]
204    scf.condition(%1) %arg4 : index
205  } do {
206  ^bb0(%arg3: index):
207    %1 = arith.muli %arg3, %c1 : index
208    // CHECK: scf.yield %[[C0]], %[[C1]]
209    scf.yield %c0, %1 : index, index
210  }
211  // CHECK: return %[[WHILE]]
212  return %0 : index
213}
214
215// CHECK-LABEL: func @while_loop_false_condition
216func.func @while_loop_false_condition(%arg0 : index) -> index {
217  // CHECK: %[[C0:.*]] = arith.constant 0
218  %c0 = arith.constant 0 : index
219  %c1 = arith.constant 1 : index
220  %0 = arith.muli %arg0, %c0 : index
221  %1 = scf.while (%arg1 = %0) : (index) -> index {
222    %2 = arith.cmpi slt, %arg1, %c0 : index
223    scf.condition(%2) %arg1 : index
224  } do {
225  ^bb0(%arg2 : index):
226    %3 = arith.addi %arg2, %c1 : index
227    scf.yield %3 : index
228  }
229  // CHECK: return %[[C0]]
230  func.return %1 : index
231}
232