The only real requirement is that entry and loop intrinsics should not be preceded by convergent operations in the same basic block. They do not need to be the first in the block. Relaxing the constraint on the entry and loop intrinsics avoids having to make changes in the construction of LLVM IR, such as getFirstInsertionPt(). It also avoids added complexity in the lowering to Machine IR, where COPY instructions may be added to the start of the basic block.
268 lines
8.3 KiB
LLVM
268 lines
8.3 KiB
LLVM
; 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: Convergence control token can only be used in a convergent call.
|
|
; CHECK-NEXT call void @g(){{.*}}%t05_tok1
|
|
define void @missing.attribute() {
|
|
%t05_tok1 = call token @llvm.experimental.convergence.anchor()
|
|
call void @g() [ "convergencectrl"(token %t05_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: %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
|
|
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()
|