convergent.cl
4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// RUN: %clang_cc1 -triple spir-unknown-unknown -emit-llvm %s -o - -fno-experimental-new-pass-manager | opt -instnamer -S | FileCheck -enable-var-scope %s --check-prefixes=CHECK,CHECK-LEGACY
// RUN: %clang_cc1 -triple spir-unknown-unknown -emit-llvm %s -o - -fexperimental-new-pass-manager | opt -instnamer -S | FileCheck -enable-var-scope %s --check-prefixes=CHECK,CHECK-NEWPM
// This is initially assumed convergent, but can be deduced to not require it.
// CHECK-LABEL: define spir_func void @non_convfun() local_unnamed_addr #0
// CHECK: ret void
__attribute__((noinline))
void non_convfun(void) {
volatile int* p;
*p = 0;
}
void convfun(void) __attribute__((convergent));
void nodupfun(void) __attribute__((noduplicate));
// External functions should be assumed convergent.
void f(void);
void g(void);
// Test two if's are merged and non_convfun duplicated.
// The LLVM IR is equivalent to:
// if (a) {
// f();
// non_convfun();
// g();
// } else {
// non_convfun();
// }
//
// CHECK-LABEL: define spir_func void @test_merge_if(i32 %a) local_unnamed_addr #1 {
// CHECK: %[[tobool:.+]] = icmp eq i32 %a, 0
// CHECK: br i1 %[[tobool]], label %[[if_end3_critedge:.+]], label %[[if_then:.+]]
// CHECK: [[if_then]]:
// CHECK: tail call spir_func void @f()
// CHECK: tail call spir_func void @non_convfun()
// CHECK: tail call spir_func void @g()
// CHECK: br label %[[if_end3:.+]]
// CHECK: [[if_end3_critedge]]:
// CHECK: tail call spir_func void @non_convfun()
// CHECK: br label %[[if_end3]]
// CHECK: [[if_end3]]:
// CHECK: ret void
void test_merge_if(int a) {
if (a) {
f();
}
non_convfun();
if (a) {
g();
}
}
// CHECK-DAG: declare spir_func void @f() local_unnamed_addr #2
// CHECK-DAG: declare spir_func void @g() local_unnamed_addr #2
// Test two if's are not merged.
// CHECK-LABEL: define spir_func void @test_no_merge_if(i32 %a) local_unnamed_addr #1
// CHECK: %[[tobool:.+]] = icmp eq i32 %a, 0
// CHECK: br i1 %[[tobool]], label %[[if_end:.+]], label %[[if_then:.+]]
// CHECK: [[if_then]]:
// CHECK: tail call spir_func void @f()
// CHECK-NOT: call spir_func void @convfun()
// CHECK-NOT: call spir_func void @g()
// CHECK: br label %[[if_end]]
// CHECK: [[if_end]]:
// CHECK: %[[tobool_pr:.+]] = phi i1 [ true, %[[if_then]] ], [ false, %{{.+}} ]
// CHECK: tail call spir_func void @convfun() #[[attr4:.+]]
// CHECK: br i1 %[[tobool_pr]], label %[[if_then2:.+]], label %[[if_end3:.+]]
// CHECK: [[if_then2]]:
// CHECK: tail call spir_func void @g()
// CHECK: br label %[[if_end3:.+]]
// CHECK: [[if_end3]]:
// CHECK-LABEL: ret void
void test_no_merge_if(int a) {
if (a) {
f();
}
convfun();
if(a) {
g();
}
}
// CHECK: declare spir_func void @convfun(){{[^#]*}} #2
// Test loop is unrolled for convergent function.
// CHECK-LABEL: define spir_func void @test_unroll() local_unnamed_addr #1
// CHECK: tail call spir_func void @convfun() #[[attr4:[0-9]+]]
// CHECK: tail call spir_func void @convfun() #[[attr4]]
// CHECK: tail call spir_func void @convfun() #[[attr4]]
// CHECK: tail call spir_func void @convfun() #[[attr4]]
// CHECK: tail call spir_func void @convfun() #[[attr4]]
// CHECK: tail call spir_func void @convfun() #[[attr4]]
// CHECK: tail call spir_func void @convfun() #[[attr4]]
// CHECK: tail call spir_func void @convfun() #[[attr4]]
// CHECK: tail call spir_func void @convfun() #[[attr4]]
// CHECK: tail call spir_func void @convfun() #[[attr4]]
// CHECK-LABEL: ret void
void test_unroll() {
for (int i = 0; i < 10; i++)
convfun();
}
// Test loop is not unrolled for noduplicate function.
// CHECK-LABEL: define spir_func void @test_not_unroll()
// CHECK: br label %[[for_body:.+]]
// CHECK: [[for_cond_cleanup:.+]]:
// CHECK: ret void
// CHECK: [[for_body]]:
// CHECK: tail call spir_func void @nodupfun() #[[attr5:[0-9]+]]
// CHECK-NOT: call spir_func void @nodupfun()
// The new PM produces a slightly different IR for the loop from the legacy PM,
// but the test still checks that the loop is not unrolled.
// CHECK-LEGACY: br i1 %{{.+}}, label %[[for_body]], label %[[for_cond_cleanup]]
// CHECK-NEW: br i1 %{{.+}}, label %[[for_body_crit_edge:.+]], label %[[for_cond_cleanup]]
// CHECK-NEW: [[for_body_crit_edge]]:
void test_not_unroll() {
for (int i = 0; i < 10; i++)
nodupfun();
}
// CHECK: declare spir_func void @nodupfun(){{[^#]*}} #[[attr3:[0-9]+]]
// CHECK-LABEL: @assume_convergent_asm
// CHECK: tail call void asm sideeffect "s_barrier", ""() #5
kernel void assume_convergent_asm()
{
__asm__ volatile("s_barrier");
}
// CHECK: attributes #0 = { nofree noinline norecurse nounwind "
// CHECK: attributes #1 = { {{[^}]*}}convergent{{[^}]*}} }
// CHECK: attributes #2 = { {{[^}]*}}convergent{{[^}]*}} }
// CHECK: attributes #3 = { {{[^}]*}}convergent noduplicate{{[^}]*}} }
// CHECK: attributes #4 = { {{[^}]*}}convergent{{[^}]*}} }
// CHECK: attributes #5 = { {{[^}]*}}convergent{{[^}]*}} }
// CHECK: attributes #6 = { {{[^}]*}}convergent noduplicate{{[^}]*}} }