|  | ; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s | 
|  |  | 
|  | ; CHECK: Entry or anchor intrinsic cannot have a convergencectrl token operand. | 
|  | ; CHECK-NEXT: %t04_tok2 = call token | 
|  | ; CHECK: Loop intrinsic must have a convergencectrl token operand. | 
|  | ; CHECK-NEXT: %t04_tok3 = call token | 
|  | define void @basic_syntax() { | 
|  | %t04_tok1 = call token @llvm.experimental.convergence.anchor() | 
|  | %t04_tok2 = call token @llvm.experimental.convergence.anchor() [ "convergencectrl"(token %t04_tok1) ] | 
|  | %t04_tok3 = call token @llvm.experimental.convergence.loop() | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: Convergence control tokens can only be produced by calls to the convergence control intrinsics. | 
|  | ; CHECK-NEXT:  %t04_tok1 = call token @produce_token() | 
|  | ; CHECK-NEXT:  call void @f() [ "convergencectrl"(token %t04_tok1) ] | 
|  | define void @wrong_token() { | 
|  | %t04_tok1 = call token @produce_token() | 
|  | call void @f() [ "convergencectrl"(token %t04_tok1) ] | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: The 'convergencectrl' bundle requires exactly one token use. | 
|  | ; CHECK-NEXT:  call void @g() | 
|  | define void @multiple_tokens() { | 
|  | %t06_tok1 = call token @llvm.experimental.convergence.anchor() | 
|  | %t06_tok2 = call token @llvm.experimental.convergence.anchor() | 
|  | call void @g() [ "convergencectrl"(token %t06_tok2, token %t06_tok1) ] | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: The 'convergencectrl' bundle can occur at most once on a call | 
|  | ; CHECK-NEXT:  call void @g() | 
|  | define void @multiple_bundles() { | 
|  | %t07_tok1 = call token @llvm.experimental.convergence.anchor() | 
|  | %t07_tok2 = call token @llvm.experimental.convergence.anchor() | 
|  | call void @g() [ "convergencectrl"(token %t07_tok2), "convergencectrl"(token %t07_tok1) ] | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: Cannot mix controlled and uncontrolled convergence in the same function | 
|  | ; CHECK-NEXT  call void @f() | 
|  | define void @mixed1() { | 
|  | call void @g() ; not convergent | 
|  | %t10_tok1 = call token @llvm.experimental.convergence.anchor() | 
|  | call void @f() [ "convergencectrl"(token %t10_tok1) ] | 
|  | call void @g() | 
|  | call void @f() ; uncontrolled convergent | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: Cannot mix controlled and uncontrolled convergence in the same function | 
|  | ; CHECK:  %t20_tok1 = call token @llvm.experimental.convergence.anchor() | 
|  | ; CHECK: Cannot mix controlled and uncontrolled convergence in the same function | 
|  | ; CHECK:  call void @f() [ "convergencectrl"(token %t20_tok1) ] | 
|  | define void @mixed2() { | 
|  | call void @g() ; not convergent | 
|  | call void @f() ; uncontrolled convergent | 
|  | call void @g() | 
|  | %t20_tok1 = call token @llvm.experimental.convergence.anchor() | 
|  | call void @f() [ "convergencectrl"(token %t20_tok1) ] | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: Convergence region is not well-nested. | 
|  | ; CHECK:   %t30_tok2 | 
|  | define void @region_nesting1() { | 
|  | %t30_tok1 = call token @llvm.experimental.convergence.anchor() | 
|  | %t30_tok2 = call token @llvm.experimental.convergence.anchor() | 
|  | call void @f() [ "convergencectrl"(token %t30_tok1) ] | 
|  | call void @f() [ "convergencectrl"(token %t30_tok2) ] | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: Convergence region is not well-nested. | 
|  | ; CHECK:   %t40_tok2 | 
|  | define void @region_nesting2(i1 %cond) { | 
|  | A: | 
|  | %t40_tok1 = call token @llvm.experimental.convergence.anchor() | 
|  | %t40_tok2 = call token @llvm.experimental.convergence.anchor() | 
|  | br i1 %cond, label %B, label %C | 
|  |  | 
|  | B: | 
|  | call void @f() [ "convergencectrl"(token %t40_tok1) ] | 
|  | br label %C | 
|  |  | 
|  | C: | 
|  | call void @f() [ "convergencectrl"(token %t40_tok2) ] | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: Convergence token used by an instruction other than llvm.experimental.convergence.loop in a cycle that does not contain the token's definition. | 
|  | ; CHECK:   token %t50_tok1 | 
|  | define void @use_in_cycle() { | 
|  | A: | 
|  | %t50_tok1 = call token @llvm.experimental.convergence.anchor() | 
|  | br label %B | 
|  |  | 
|  | B: | 
|  | call void @f() [ "convergencectrl"(token %t50_tok1) ] | 
|  | br label %B | 
|  | } | 
|  |  | 
|  | ; CHECK: Entry intrinsic cannot be preceded by a convergent operation in the same basic block. | 
|  | ; CHECK:   %t60_tok1 | 
|  | define void @entry_at_start(i32 %x, i32 %y) convergent { | 
|  | %z = add i32 %x, %y | 
|  | call void @f() | 
|  | %t60_tok1 = call token @llvm.experimental.convergence.entry() | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: Entry intrinsic can occur only in a convergent function. | 
|  | ; CHECK:   %t60_tok2 | 
|  | define void @entry_in_convergent(i32 %x, i32 %y) { | 
|  | %t60_tok2 = call token @llvm.experimental.convergence.entry() | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: Loop intrinsic cannot be preceded by a convergent operation in the same basic block. | 
|  | ; CHECK-NEXT: %h1 | 
|  | ; CHECK-SAME: %t60_tok3 | 
|  | define void @loop_at_start(i32 %x, i32 %y) convergent { | 
|  | A: | 
|  | %t60_tok3 = call token @llvm.experimental.convergence.entry() | 
|  | br label %B | 
|  | B: | 
|  | %z = add i32 %x, %y | 
|  | ; This is not an error | 
|  | %h2 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %t60_tok3) ] | 
|  | br label %C | 
|  | C: | 
|  | call void @f() | 
|  | %h1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %t60_tok3) ] | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: Entry intrinsic can occur only in the entry block. | 
|  | ; CHECK:   %t60_tok4 | 
|  | define void @entry_at_entry(i32 %x, i32 %y) convergent { | 
|  | A: | 
|  | %z = add i32 %x, %y | 
|  | br label %B | 
|  | B: | 
|  | %t60_tok4 = call token @llvm.experimental.convergence.entry() | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; CHECK: Two static convergence token uses in a cycle that does not contain either token's definition. | 
|  | ; CHECK:   token %t70_tok1 | 
|  | ; CHECK:   token %t70_tok2 | 
|  | define void @multiple_hearts() { | 
|  | A: | 
|  | %t70_tok1 = call token @llvm.experimental.convergence.anchor() | 
|  | %t70_tok2 = call token @llvm.experimental.convergence.anchor() | 
|  | br label %B | 
|  |  | 
|  | B: | 
|  | %h2 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %t70_tok2) ] | 
|  | %h1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %t70_tok1) ] | 
|  | br label %B | 
|  | } | 
|  |  | 
|  | ; CHECK: Two static convergence token uses in a cycle that does not contain either token's definition. | 
|  | ; CHECK:   token %h0 | 
|  | ; CHECK:   token %h0 | 
|  | define void @multiple_hearts_nested(i1 %cond1, i1 %cond2) { | 
|  | A: | 
|  | %t70_tok3 = call token @llvm.experimental.convergence.anchor() | 
|  | br label %B | 
|  |  | 
|  | B: | 
|  | %h0 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %t70_tok3) ] | 
|  | br i1 %cond1, label %C, label %B | 
|  |  | 
|  | C: | 
|  | %h1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %h0) ] | 
|  | %h2 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %h0) ] | 
|  | br i1 %cond2, label %C, label %B | 
|  | } | 
|  |  | 
|  | ; CHECK: Cycle heart must dominate all blocks in the cycle. | 
|  | ; CHECK: %h3 = call token | 
|  | ; CHECK:   label %C | 
|  | define void @invalid_heart_nested(i1 %cond1, i1 %cond2) { | 
|  | A: | 
|  | %t70_tok4 = call token @llvm.experimental.convergence.anchor() | 
|  | br label %B | 
|  |  | 
|  | B: | 
|  | br i1 %cond1, label %C, label %B | 
|  |  | 
|  | C: | 
|  | %h3 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %t70_tok4) ] | 
|  | br i1 %cond2, label %C, label %B | 
|  | } | 
|  |  | 
|  | ; CHECK: Cycle heart must dominate all blocks in the cycle. | 
|  | ; CHECK: %h4 = call token | 
|  | ; CHECK: label %C | 
|  | define void @irreducible1(i1 %cond) { | 
|  | A: | 
|  | %a = call token @llvm.experimental.convergence.anchor() | 
|  | br i1 %cond, label %B, label %C | 
|  |  | 
|  | B: | 
|  | %b = call token @llvm.experimental.convergence.anchor() | 
|  | br i1 %cond, label %C, label %D | 
|  |  | 
|  | C: | 
|  | %h4 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %a) ] | 
|  | br i1 %cond, label %B, label %E | 
|  |  | 
|  | D: | 
|  | call void @f() [ "convergencectrl"(token %b) ] | 
|  | br i1 %cond, label %B, label %F | 
|  |  | 
|  | E: | 
|  | call void @f() [ "convergencectrl"(token %h4) ] | 
|  | br i1 %cond, label %C, label %F | 
|  |  | 
|  | F: | 
|  | call void @f() [ "convergencectrl"(token %a) ] | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; Mirror image of @irreducible1 | 
|  | ; CHECK: Cycle heart must dominate all blocks in the cycle. | 
|  | ; CHECK: %h5 = call token | 
|  | ; CHECK: label %B | 
|  | define void @irreducible2(i1 %cond) { | 
|  | A: | 
|  | %a = call token @llvm.experimental.convergence.anchor() | 
|  | br i1 %cond, label %B, label %C | 
|  |  | 
|  | B: | 
|  | %h5 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %a) ] | 
|  | br i1 %cond, label %C, label %D | 
|  |  | 
|  | C: | 
|  | %c = call token @llvm.experimental.convergence.anchor() | 
|  | br i1 %cond, label %B, label %E | 
|  |  | 
|  | D: | 
|  | call void @f() [ "convergencectrl"(token %h5) ] | 
|  | br i1 %cond, label %B, label %F | 
|  |  | 
|  | E: | 
|  | call void @f() [ "convergencectrl"(token %c) ] | 
|  | br i1 %cond, label %C, label %F | 
|  |  | 
|  | F: | 
|  | call void @f() [ "convergencectrl"(token %a) ] | 
|  | ret void | 
|  | } | 
|  |  | 
|  | declare token @produce_token() | 
|  |  | 
|  | declare void @f() convergent | 
|  | declare void @g() | 
|  |  | 
|  | declare token @llvm.experimental.convergence.entry() | 
|  | declare token @llvm.experimental.convergence.anchor() | 
|  | declare token @llvm.experimental.convergence.loop() |