xref: /llvm-project/llvm/test/Transforms/CodeGenPrepare/X86/computedgoto.ll (revision f1ec0d12bb0843f0deab83ef2b5cf1339cbc4f0b)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt -passes='require<profile-summary>,function(codegenprepare)' -S < %s | FileCheck %s
3target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
4target triple = "x86_64-unknown-linux-gnu"
5
6declare void @use(i32) local_unnamed_addr
7declare void @useptr(ptr) local_unnamed_addr
8
9; CHECK: @simple.targets = constant [2 x ptr] [ptr blockaddress(@simple, %bb0), ptr blockaddress(@simple, %bb1)], align 16
10@simple.targets = constant [2 x ptr] [ptr blockaddress(@simple, %bb0), ptr blockaddress(@simple, %bb1)], align 16
11
12; CHECK: @multi.targets = constant [2 x ptr] [ptr blockaddress(@multi, %bb0), ptr blockaddress(@multi, %bb1)], align 16
13@multi.targets = constant [2 x ptr] [ptr blockaddress(@multi, %bb0), ptr blockaddress(@multi, %bb1)], align 16
14
15; CHECK: @loop.targets = constant [2 x ptr] [ptr blockaddress(@loop, %bb0), ptr blockaddress(@loop, %bb1)], align 16
16@loop.targets = constant [2 x ptr] [ptr blockaddress(@loop, %bb0), ptr blockaddress(@loop, %bb1)], align 16
17
18; CHECK: @nophi.targets = constant [2 x ptr] [ptr blockaddress(@nophi, %bb0), ptr blockaddress(@nophi, %bb1)], align 16
19@nophi.targets = constant [2 x ptr] [ptr blockaddress(@nophi, %bb0), ptr blockaddress(@nophi, %bb1)], align 16
20
21; CHECK: @noncritical.targets = constant [2 x ptr] [ptr blockaddress(@noncritical, %bb0), ptr blockaddress(@noncritical, %bb1)], align 16
22@noncritical.targets = constant [2 x ptr] [ptr blockaddress(@noncritical, %bb0), ptr blockaddress(@noncritical, %bb1)], align 16
23
24; Check that we break the critical edge when an jump table has only one use.
25define void @simple(ptr nocapture readonly %p) {
26; CHECK-LABEL: @simple(
27; CHECK-NEXT:  entry:
28; CHECK-NEXT:    [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
29; CHECK-NEXT:    [[INITVAL:%.*]] = load i32, ptr [[P]], align 4
30; CHECK-NEXT:    [[INITOP:%.*]] = load i32, ptr [[INCDEC_PTR]], align 4
31; CHECK-NEXT:    switch i32 [[INITOP]], label [[EXIT:%.*]] [
32; CHECK-NEXT:    i32 0, label [[BB0_CLONE:%.*]]
33; CHECK-NEXT:    i32 1, label [[BB1_CLONE:%.*]]
34; CHECK-NEXT:    ]
35; CHECK:       bb0:
36; CHECK-NEXT:    br label [[DOTSPLIT:%.*]]
37; CHECK:       .split:
38; CHECK-NEXT:    [[MERGE:%.*]] = phi ptr [ [[PTR:%.*]], [[BB0:%.*]] ], [ [[INCDEC_PTR]], [[BB0_CLONE]] ]
39; CHECK-NEXT:    [[MERGE2:%.*]] = phi i32 [ 0, [[BB0]] ], [ [[INITVAL]], [[BB0_CLONE]] ]
40; CHECK-NEXT:    tail call void @use(i32 [[MERGE2]])
41; CHECK-NEXT:    br label [[INDIRECTGOTO:%.*]]
42; CHECK:       bb1:
43; CHECK-NEXT:    br label [[DOTSPLIT3:%.*]]
44; CHECK:       .split3:
45; CHECK-NEXT:    [[MERGE5:%.*]] = phi ptr [ [[PTR]], [[BB1:%.*]] ], [ [[INCDEC_PTR]], [[BB1_CLONE]] ]
46; CHECK-NEXT:    [[MERGE7:%.*]] = phi i32 [ 1, [[BB1]] ], [ [[INITVAL]], [[BB1_CLONE]] ]
47; CHECK-NEXT:    tail call void @use(i32 [[MERGE7]])
48; CHECK-NEXT:    br label [[INDIRECTGOTO]]
49; CHECK:       indirectgoto:
50; CHECK-NEXT:    [[P_ADDR_SINK:%.*]] = phi ptr [ [[MERGE5]], [[DOTSPLIT3]] ], [ [[MERGE]], [[DOTSPLIT]] ]
51; CHECK-NEXT:    [[PTR]] = getelementptr inbounds i32, ptr [[P_ADDR_SINK]], i64 1
52; CHECK-NEXT:    [[NEWP:%.*]] = load i32, ptr [[P_ADDR_SINK]], align 4
53; CHECK-NEXT:    [[IDX:%.*]] = sext i32 [[NEWP]] to i64
54; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x ptr], ptr @simple.targets, i64 0, i64 [[IDX]]
55; CHECK-NEXT:    [[NEWOP:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8
56; CHECK-NEXT:    indirectbr ptr [[NEWOP]], [label [[BB0]], label %bb1]
57; CHECK:       exit:
58; CHECK-NEXT:    ret void
59; CHECK:       bb0.clone:
60; CHECK-NEXT:    br label [[DOTSPLIT]]
61; CHECK:       bb1.clone:
62; CHECK-NEXT:    br label [[DOTSPLIT3]]
63;
64entry:
65  %incdec.ptr = getelementptr inbounds i32, ptr %p, i64 1
66  %initval = load i32, ptr %p, align 4
67  %initop = load i32, ptr %incdec.ptr, align 4
68  switch i32 %initop, label %exit [
69  i32 0, label %bb0
70  i32 1, label %bb1
71  ]
72
73bb0:
74  %p.addr.0 = phi ptr [ %incdec.ptr, %entry ], [ %ptr, %indirectgoto ]
75  %opcode.0 = phi i32 [ %initval, %entry ], [ 0, %indirectgoto ]
76  tail call void @use(i32 %opcode.0)
77  br label %indirectgoto
78
79bb1:
80  %p.addr.1 = phi ptr [ %incdec.ptr, %entry ], [ %ptr, %indirectgoto ]
81  %opcode.1 = phi i32 [ %initval, %entry ], [ 1, %indirectgoto ]
82  tail call void @use(i32 %opcode.1)
83  br label %indirectgoto
84
85indirectgoto:
86  %p.addr.sink = phi ptr [ %p.addr.1, %bb1 ], [ %p.addr.0, %bb0 ]
87  %ptr = getelementptr inbounds i32, ptr %p.addr.sink, i64 1
88  %newp = load i32, ptr %p.addr.sink, align 4
89  %idx = sext i32 %newp to i64
90  %arrayidx = getelementptr inbounds [2 x ptr], ptr @simple.targets, i64 0, i64 %idx
91  %newop = load ptr, ptr %arrayidx, align 8
92  indirectbr ptr %newop, [label %bb0, label %bb1]
93
94exit:
95  ret void
96}
97
98; Don't try to break critical edges when several indirectbr point to a single block
99define void @multi(ptr nocapture readonly %p) {
100; CHECK-LABEL: @multi(
101; CHECK-NEXT:  entry:
102; CHECK-NEXT:    [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
103; CHECK-NEXT:    [[INITVAL:%.*]] = load i32, ptr [[P]], align 4
104; CHECK-NEXT:    [[INITOP:%.*]] = load i32, ptr [[INCDEC_PTR]], align 4
105; CHECK-NEXT:    switch i32 [[INITOP]], label [[EXIT:%.*]] [
106; CHECK-NEXT:    i32 0, label [[BB0:%.*]]
107; CHECK-NEXT:    i32 1, label [[BB1:%.*]]
108; CHECK-NEXT:    ]
109; CHECK:       bb0:
110; CHECK-NEXT:    [[P_ADDR_0:%.*]] = phi ptr [ [[INCDEC_PTR]], [[ENTRY:%.*]] ], [ [[NEXT0:%.*]], [[BB0]] ], [ [[NEXT1:%.*]], [[BB1]] ]
111; CHECK-NEXT:    [[OPCODE_0:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 0, [[BB0]] ], [ 1, [[BB1]] ]
112; CHECK-NEXT:    tail call void @use(i32 [[OPCODE_0]])
113; CHECK-NEXT:    [[NEXT0]] = getelementptr inbounds i32, ptr [[P_ADDR_0]], i64 1
114; CHECK-NEXT:    [[NEWP0:%.*]] = load i32, ptr [[P_ADDR_0]], align 4
115; CHECK-NEXT:    [[IDX0:%.*]] = sext i32 [[NEWP0]] to i64
116; CHECK-NEXT:    [[ARRAYIDX0:%.*]] = getelementptr inbounds [2 x ptr], ptr @multi.targets, i64 0, i64 [[IDX0]]
117; CHECK-NEXT:    [[NEWOP0:%.*]] = load ptr, ptr [[ARRAYIDX0]], align 8
118; CHECK-NEXT:    indirectbr ptr [[NEWOP0]], [label [[BB0]], label %bb1]
119; CHECK:       bb1:
120; CHECK-NEXT:    [[P_ADDR_1:%.*]] = phi ptr [ [[INCDEC_PTR]], [[ENTRY]] ], [ [[NEXT0]], [[BB0]] ], [ [[NEXT1]], [[BB1]] ]
121; CHECK-NEXT:    [[OPCODE_1:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 0, [[BB0]] ], [ 1, [[BB1]] ]
122; CHECK-NEXT:    tail call void @use(i32 [[OPCODE_1]])
123; CHECK-NEXT:    [[NEXT1]] = getelementptr inbounds i32, ptr [[P_ADDR_1]], i64 1
124; CHECK-NEXT:    [[NEWP1:%.*]] = load i32, ptr [[P_ADDR_1]], align 4
125; CHECK-NEXT:    [[IDX1:%.*]] = sext i32 [[NEWP1]] to i64
126; CHECK-NEXT:    [[ARRAYIDX1:%.*]] = getelementptr inbounds [2 x ptr], ptr @multi.targets, i64 0, i64 [[IDX1]]
127; CHECK-NEXT:    [[NEWOP1:%.*]] = load ptr, ptr [[ARRAYIDX1]], align 8
128; CHECK-NEXT:    indirectbr ptr [[NEWOP1]], [label [[BB0]], label %bb1]
129; CHECK:       exit:
130; CHECK-NEXT:    ret void
131;
132entry:
133  %incdec.ptr = getelementptr inbounds i32, ptr %p, i64 1
134  %initval = load i32, ptr %p, align 4
135  %initop = load i32, ptr %incdec.ptr, align 4
136  switch i32 %initop, label %exit [
137  i32 0, label %bb0
138  i32 1, label %bb1
139  ]
140
141bb0:
142  %p.addr.0 = phi ptr [ %incdec.ptr, %entry ], [ %next0, %bb0 ], [ %next1, %bb1 ]
143  %opcode.0 = phi i32 [ %initval, %entry ], [ 0, %bb0 ], [ 1, %bb1 ]
144  tail call void @use(i32 %opcode.0)
145  %next0 = getelementptr inbounds i32, ptr %p.addr.0, i64 1
146  %newp0 = load i32, ptr %p.addr.0, align 4
147  %idx0 = sext i32 %newp0 to i64
148  %arrayidx0 = getelementptr inbounds [2 x ptr], ptr @multi.targets, i64 0, i64 %idx0
149  %newop0 = load ptr, ptr %arrayidx0, align 8
150  indirectbr ptr %newop0, [label %bb0, label %bb1]
151
152bb1:
153  %p.addr.1 = phi ptr [ %incdec.ptr, %entry ], [ %next0, %bb0 ], [ %next1, %bb1 ]
154  %opcode.1 = phi i32 [ %initval, %entry ], [ 0, %bb0 ], [ 1, %bb1 ]
155  tail call void @use(i32 %opcode.1)
156  %next1 = getelementptr inbounds i32, ptr %p.addr.1, i64 1
157  %newp1 = load i32, ptr %p.addr.1, align 4
158  %idx1 = sext i32 %newp1 to i64
159  %arrayidx1 = getelementptr inbounds [2 x ptr], ptr @multi.targets, i64 0, i64 %idx1
160  %newop1 = load ptr, ptr %arrayidx1, align 8
161  indirectbr ptr %newop1, [label %bb0, label %bb1]
162
163exit:
164  ret void
165}
166
167; Make sure we do the right thing for cases where the indirectbr branches to
168; the block it terminates.
169define i64 @loop(ptr nocapture readonly %p) {
170; CHECK-LABEL: @loop(
171; CHECK-NEXT:  entry:
172; CHECK-NEXT:    br label [[DOTSPLIT:%.*]]
173; CHECK:       bb0:
174; CHECK-NEXT:    br label [[DOTSPLIT]]
175; CHECK:       .split:
176; CHECK-NEXT:    [[MERGE:%.*]] = phi i64 [ [[I_NEXT:%.*]], [[BB0:%.*]] ], [ 0, [[ENTRY:%.*]] ]
177; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 [[MERGE]]
178; CHECK-NEXT:    store i64 [[MERGE]], ptr [[TMP0]], align 4
179; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i64 [[MERGE]], 1
180; CHECK-NEXT:    [[IDX:%.*]] = srem i64 [[MERGE]], 2
181; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x ptr], ptr @loop.targets, i64 0, i64 [[IDX]]
182; CHECK-NEXT:    [[TARGET:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8
183; CHECK-NEXT:    indirectbr ptr [[TARGET]], [label [[BB0]], label %bb1]
184; CHECK:       bb1:
185; CHECK-NEXT:    ret i64 [[I_NEXT]]
186;
187entry:
188  br label %bb0
189
190bb0:
191  %i = phi i64 [ %i.next, %bb0 ], [ 0, %entry ]
192  %tmp0 = getelementptr inbounds i64, ptr %p, i64 %i
193  store i64 %i, ptr %tmp0, align 4
194  %i.next = add nuw nsw i64 %i, 1
195  %idx = srem i64 %i, 2
196  %arrayidx = getelementptr inbounds [2 x ptr], ptr @loop.targets, i64 0, i64 %idx
197  %target = load ptr, ptr %arrayidx, align 8
198  indirectbr ptr %target, [label %bb0, label %bb1]
199
200bb1:
201  ret i64 %i.next
202}
203
204; Don't do anything for cases that contain no phis.
205define void @nophi(ptr %p) {
206; CHECK-LABEL: @nophi(
207; CHECK-NEXT:  entry:
208; CHECK-NEXT:    [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
209; CHECK-NEXT:    [[INITOP:%.*]] = load i32, ptr [[INCDEC_PTR]], align 4
210; CHECK-NEXT:    switch i32 [[INITOP]], label [[EXIT:%.*]] [
211; CHECK-NEXT:    i32 0, label [[BB0:%.*]]
212; CHECK-NEXT:    i32 1, label [[BB1:%.*]]
213; CHECK-NEXT:    ]
214; CHECK:       bb0:
215; CHECK-NEXT:    tail call void @use(i32 0)
216; CHECK-NEXT:    br label [[INDIRECTGOTO:%.*]]
217; CHECK:       bb1:
218; CHECK-NEXT:    tail call void @use(i32 1)
219; CHECK-NEXT:    br label [[INDIRECTGOTO]]
220; CHECK:       indirectgoto:
221; CHECK-NEXT:    [[SUNKADDR:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4
222; CHECK-NEXT:    [[NEWP:%.*]] = load i32, ptr [[SUNKADDR]], align 4
223; CHECK-NEXT:    [[IDX:%.*]] = sext i32 [[NEWP]] to i64
224; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x ptr], ptr @nophi.targets, i64 0, i64 [[IDX]]
225; CHECK-NEXT:    [[NEWOP:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8
226; CHECK-NEXT:    indirectbr ptr [[NEWOP]], [label [[BB0]], label %bb1]
227; CHECK:       exit:
228; CHECK-NEXT:    ret void
229;
230entry:
231  %incdec.ptr = getelementptr inbounds i32, ptr %p, i64 1
232  %initop = load i32, ptr %incdec.ptr, align 4
233  switch i32 %initop, label %exit [
234  i32 0, label %bb0
235  i32 1, label %bb1
236  ]
237
238bb0:
239  tail call void @use(i32 0)  br label %indirectgoto
240
241bb1:
242  tail call void @use(i32 1)
243  br label %indirectgoto
244
245indirectgoto:
246  %newp = load i32, ptr %incdec.ptr, align 4
247  %idx = sext i32 %newp to i64
248  %arrayidx = getelementptr inbounds [2 x ptr], ptr @nophi.targets, i64 0, i64 %idx
249  %newop = load ptr, ptr %arrayidx, align 8
250  indirectbr ptr %newop, [label %bb0, label %bb1]
251
252exit:
253  ret void
254}
255
256; Don't do anything if the edge isn't critical.
257define i32 @noncritical(i32 %k, ptr %p)
258; CHECK-LABEL: @noncritical(
259; CHECK-NEXT:  entry:
260; CHECK-NEXT:    [[D:%.*]] = add i32 [[K:%.*]], 1
261; CHECK-NEXT:    indirectbr ptr [[P:%.*]], [label [[BB0:%.*]], label %bb1]
262; CHECK:       bb0:
263; CHECK-NEXT:    [[R0:%.*]] = sub i32 [[K]], [[D]]
264; CHECK-NEXT:    br label [[EXIT:%.*]]
265; CHECK:       bb1:
266; CHECK-NEXT:    [[R1:%.*]] = sub i32 [[D]], [[K]]
267; CHECK-NEXT:    br label [[EXIT]]
268; CHECK:       exit:
269; CHECK-NEXT:    [[V:%.*]] = phi i32 [ [[R0]], [[BB0]] ], [ [[R1]], [[BB1:%.*]] ]
270; CHECK-NEXT:    ret i32 [[V]]
271;
272{
273entry:
274  %d = add i32 %k, 1
275  indirectbr ptr %p, [label %bb0, label %bb1]
276
277bb0:
278  %v00 = phi i32 [%k, %entry]
279  %v01 = phi i32 [%d, %entry]
280  %r0 = sub i32 %v00, %v01
281  br label %exit
282
283bb1:
284  %v10 = phi i32 [%d, %entry]
285  %v11 = phi i32 [%k, %entry]
286  %r1 = sub i32 %v10, %v11
287  br label %exit
288
289exit:
290  %v = phi i32 [%r0, %bb0], [%r1, %bb1]
291  ret i32 %v
292}
293