1; RUN: opt -S -mtriple=amdgcn-- -structurizecfg -si-annotate-control-flow < %s | FileCheck -check-prefix=OPT %s
2; RUN: llc -march=amdgcn -verify-machineinstrs < %s | FileCheck -check-prefix=GCN %s
3
4; OPT-LABEL: {{^}}define amdgpu_vs void @multi_else_break(
5; OPT: main_body:
6; OPT: LOOP.outer:
7; OPT: LOOP:
8; OPT:     [[if:%[0-9]+]] = call { i1, i64 } @llvm.amdgcn.if.i64(
9; OPT:     [[if_exec:%[0-9]+]] = extractvalue { i1, i64 } [[if]], 1
10;
11; OPT: Flow:
12;
13; Ensure two if.break calls, for both the inner and outer loops
14
15; OPT:        call void @llvm.amdgcn.end.cf
16; OPT-NEXT:   call i64 @llvm.amdgcn.if.break.i64.i64(i1
17; OPT-NEXT:   call i1 @llvm.amdgcn.loop.i64(i64
18; OPT-NEXT:   call i64 @llvm.amdgcn.if.break.i64.i64(i1
19;
20; OPT: Flow1:
21
22; GCN-LABEL: {{^}}multi_else_break:
23
24; GCN: ; %main_body
25; GCN:      s_mov_b64           [[LEFT_OUTER:s\[[0-9]+:[0-9]+\]]], 0{{$}}
26
27; GCN: [[FLOW2:BB[0-9]+_[0-9]+]]: ; %Flow2
28; GCN:      s_or_b64            exec, exec, [[TMP0:s\[[0-9]+:[0-9]+\]]]
29; GCN:      s_and_b64           [[TMP1:s\[[0-9]+:[0-9]+\]]], exec, [[BREAK_OUTER:s\[[0-9]+:[0-9]+\]]]
30; GCN:      s_or_b64            [[TMP1]], [[TMP1]], [[LEFT_OUTER]]
31; GCN:      s_mov_b64           [[LEFT_OUTER]], [[TMP1]]
32; GCN:      s_andn2_b64         exec, exec, [[TMP1]]
33; GCN:      s_cbranch_execz    [[IF_BLOCK:BB[0-9]+_[0-9]+]]
34
35; GCN: [[OUTER_LOOP:BB[0-9]+_[0-9]+]]: ; %LOOP.outer{{$}}
36; GCN:      s_mov_b64           [[LEFT_INNER:s\[[0-9]+:[0-9]+\]]], 0{{$}}
37
38; GCN: ; %Flow
39; GCN:      s_or_b64            exec, exec, [[SAVE_EXEC:s\[[0-9]+:[0-9]+\]]]
40; GCN:      s_and_b64           [[TMP0]], exec, [[BREAK_INNER:s\[[0-9]+:[0-9]+\]]]
41; GCN:      s_or_b64            [[TMP0]], [[TMP0]], [[LEFT_INNER]]
42; GCN:      s_mov_b64           [[LEFT_INNER]], [[TMP0]]
43; GCN:      s_andn2_b64         exec, exec, [[TMP0]]
44; GCN:      s_cbranch_execz    [[FLOW2]]
45
46; GCN: [[INNER_LOOP:BB[0-9]+_[0-9]+]]: ; %LOOP{{$}}
47; GCN:      s_and_saveexec_b64  [[SAVE_EXEC]], vcc
48
49; FIXME: duplicate comparison
50; GCN: ; %ENDIF
51; GCN-DAG:  v_cmp_eq_u32_e32    vcc,
52; GCN-DAG:  v_cmp_ne_u32_e64    [[TMP51NEG:s\[[0-9]+:[0-9]+\]]],
53
54; GCN: [[IF_BLOCK]]: ; %IF
55; GCN-NEXT: s_endpgm
56define amdgpu_vs void @multi_else_break(<4 x float> %vec, i32 %ub, i32 %cont) {
57main_body:
58  br label %LOOP.outer
59
60LOOP.outer:                                       ; preds = %ENDIF, %main_body
61  %tmp43 = phi i32 [ 0, %main_body ], [ %tmp47, %ENDIF ]
62  br label %LOOP
63
64LOOP:                                             ; preds = %ENDIF, %LOOP.outer
65  %tmp45 = phi i32 [ %tmp43, %LOOP.outer ], [ %tmp47, %ENDIF ]
66  %tmp47 = add i32 %tmp45, 1
67  %tmp48 = icmp slt i32 %tmp45, %ub
68  br i1 %tmp48, label %ENDIF, label %IF
69
70IF:                                               ; preds = %LOOP
71  ret void
72
73ENDIF:                                            ; preds = %LOOP
74  %tmp51 = icmp eq i32 %tmp47, %cont
75  br i1 %tmp51, label %LOOP, label %LOOP.outer
76}
77
78; OPT-LABEL: define amdgpu_kernel void @multi_if_break_loop(
79; OPT: llvm.amdgcn.if.break
80; OPT: llvm.amdgcn.loop
81; OPT: llvm.amdgcn.if.break
82; OPT: llvm.amdgcn.end.cf
83
84; GCN-LABEL: {{^}}multi_if_break_loop:
85; GCN:      s_mov_b64          [[BROKEN_THREADS_MASK:s\[[0-9]+:[0-9]+\]]], 0{{$}}
86
87; GCN: [[LOOP:BB[0-9]+_[0-9]+]]: ; %Flow4
88; GCN:      s_and_b64          [[BROKEN_THREADS_MASK]], exec, [[BROKEN_THREADS_MASK]]
89; GCN:      s_or_b64           [[BROKEN_THREADS_MASK]], [[BROKEN_THREADS_MASK]], [[SAVED:s\[[0-9]+:[0-9]+\]]]
90; GCN:      s_andn2_b64        exec, exec, [[BROKEN_THREADS_MASK]]
91; GCN-NEXT: s_cbranch_execz [[LOOP_EXIT:BB[0-9]+_[0-9]+]]
92
93; GCN: ; %bb1{{$}}
94; GCN:      buffer_load_dword  [[LOAD0:v[0-9]+]],
95; GCN:      s_mov_b64          [[SAVED]], [[BROKEN_THREADS_MASK]]
96
97; GCN: ; %LeafBlock1
98; GCN:      v_cmp_eq_u32_e32 vcc, 1, [[LOAD0]]
99; GCN:      s_and_b64 vcc, exec, vcc
100; GCN:      s_cbranch_vccz [[FLOW:BB[0-9]+_[0-9]+]]
101
102; GCN: ; %case1
103; GCN:      buffer_load_dword  [[LOAD2:v[0-9]+]],
104; GCN:      v_cmp_ge_i32_e32   vcc, {{v[0-9]+}}, [[LOAD2]]
105; GCN:      s_orn2_b64 [[BROKEN_THREADS_MASK]], vcc, exec
106; GCN:  BB1_{{[0-9]+}}:
107; GCN:      s_mov_b64 [[FALSE_MASK:s\[[0-9]+:[0-9]+\]]], 0
108; GCN:      s_and_b64 vcc, exec, [[FALSE_MASK]]
109; GCN:      s_cbranch_vccz [[LOOP]]
110
111; GCN: ; %LeafBlock
112; GCN:     v_cmp_eq_u32_e32 vcc, 0, [[LOAD0]]
113; GCN:     s_and_b64 vcc, exec, vcc
114; GCN:     s_cbranch_vccz [[LOOP]]
115
116; GCN: ; %case0
117; GCN:      buffer_load_dword  [[LOAD1:v[0-9]+]],
118; GCN-DAG:  v_cmp_ge_i32_e32   vcc, {{v[0-9]+}}, [[LOAD1]]
119; GCN:      s_andn2_b64 [[BROKEN_THREADS_MASK]], [[BROKEN_THREADS_MASK]], exec
120; GCN:      s_and_b64 [[TMP_MASK:s\[[0-9]+:[0-9]+\]]], vcc, exec
121; GCN:      s_or_b64 [[BROKEN_THREADS_MASK]], [[BROKEN_THREADS_MASK]], [[TMP_MASK]]
122; GCN:      s_branch [[LOOP]]
123
124; GCN: [[LOOP_EXIT]]: ; %Flow6
125; GCN: 	s_or_b64 exec, exec, [[BROKEN_THREADS_MASK]]
126
127define amdgpu_kernel void @multi_if_break_loop(i32 %arg) #0 {
128bb:
129  %id = call i32 @llvm.amdgcn.workitem.id.x()
130  %tmp = sub i32 %id, %arg
131  br label %bb1
132
133bb1:
134  %lsr.iv = phi i32 [ undef, %bb ], [ %lsr.iv.next, %case0 ], [ %lsr.iv.next, %case1 ]
135  %lsr.iv.next = add i32 %lsr.iv, 1
136  %cmp0 = icmp slt i32 %lsr.iv.next, 0
137  %load0 = load volatile i32, i32 addrspace(1)* undef, align 4
138  switch i32 %load0, label %bb9 [
139    i32 0, label %case0
140    i32 1, label %case1
141  ]
142
143case0:
144  %load1 = load volatile i32, i32 addrspace(1)* undef, align 4
145  %cmp1 = icmp slt i32 %tmp, %load1
146  br i1 %cmp1, label %bb1, label %bb9
147
148case1:
149  %load2 = load volatile i32, i32 addrspace(1)* undef, align 4
150  %cmp2 = icmp slt i32 %tmp, %load2
151  br i1 %cmp2, label %bb1, label %bb9
152
153bb9:
154  ret void
155}
156
157declare i32 @llvm.amdgcn.workitem.id.x() #1
158
159attributes #0 = { nounwind }
160attributes #1 = { nounwind readnone }
161