// 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{{[^}]*}} }