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