diff --git a/llvm/include/llvm/ADT/GenericConvergenceVerifier.h b/llvm/include/llvm/ADT/GenericConvergenceVerifier.h --- a/llvm/include/llvm/ADT/GenericConvergenceVerifier.h +++ b/llvm/include/llvm/ADT/GenericConvergenceVerifier.h @@ -63,9 +63,9 @@ // and not the token values. DenseMap Tokens; + static bool isInsideConvergentFunction(const InstructionT &I); + static bool isConvergent(const InstructionT &I); const InstructionT *findAndCheckConvergenceTokenUsed(const InstructionT &I); - bool isControlledConvergent(const InstructionT &I); - bool isConvergent(const InstructionT &I) const; void reportFailure(const Twine &Message, ArrayRef Values); }; diff --git a/llvm/include/llvm/IR/GenericConvergenceVerifierImpl.h b/llvm/include/llvm/IR/GenericConvergenceVerifierImpl.h --- a/llvm/include/llvm/IR/GenericConvergenceVerifierImpl.h +++ b/llvm/include/llvm/IR/GenericConvergenceVerifierImpl.h @@ -69,9 +69,44 @@ template void GenericConvergenceVerifier::visit(const InstructionT &I) { - if (isControlledConvergent(I)) { + auto ID = ContextT::getIntrinsicID(I); + auto *TokenDef = findAndCheckConvergenceTokenUsed(I); + + bool IsCtrlIntrinsic = true; + + switch (ID) { + case Intrinsic::experimental_convergence_entry: + Check(isInsideConvergentFunction(I), + "Entry intrinsic can occur only in a convergent function.", + {Context.print(&I)}); + Check(I.getParent()->isEntryBlock(), + "Entry intrinsic can occur only in the entry block.", + {Context.print(&I)}); + Check(I.getParent()->getFirstNonPHI() == &I, + "Entry intrinsic can occur only at the start of the basic block.", + {Context.print(&I)}); + LLVM_FALLTHROUGH; + case Intrinsic::experimental_convergence_anchor: + Check(!TokenDef, + "Entry or anchor intrinsic cannot have a convergencectrl token " + "operand.", + {Context.print(&I)}); + break; + case Intrinsic::experimental_convergence_loop: + Check(TokenDef, "Loop intrinsic must have a convergencectrl token operand.", + {Context.print(&I)}); + Check(I.getParent()->getFirstNonPHI() == &I, + "Loop intrinsic can occur only at the start of the basic block.", + {Context.print(&I)}); + break; + default: + IsCtrlIntrinsic = false; + break; + } + + if (TokenDef || IsCtrlIntrinsic) { Check(isConvergent(I), - "Expected convergent attribute on a controlled convergent call.", + "Convergence control token can only be used in a convergent call.", {Context.print(&I)}); Check(ConvergenceKind != UncontrolledConvergence, "Cannot mix controlled and uncontrolled convergence in the same " diff --git a/llvm/lib/IR/ConvergenceVerifier.cpp b/llvm/lib/IR/ConvergenceVerifier.cpp --- a/llvm/lib/IR/ConvergenceVerifier.cpp +++ b/llvm/lib/IR/ConvergenceVerifier.cpp @@ -51,26 +51,18 @@ } template <> -bool GenericConvergenceVerifier::isConvergent( - const InstructionT &I) const { - if (auto *CB = dyn_cast(&I)) { - return CB->isConvergent(); - } - return false; +bool GenericConvergenceVerifier::isInsideConvergentFunction( + const InstructionT &I) { + auto *F = I.getFunction(); + return F->isConvergent(); } template <> -bool GenericConvergenceVerifier::isControlledConvergent( +bool GenericConvergenceVerifier::isConvergent( const InstructionT &I) { - // First find a token and place it in the map. - if (findAndCheckConvergenceTokenUsed(I)) - return true; - - // The entry and anchor intrinsics do not use a token, so we do a broad check - // here. The loop intrinsic will be checked separately for a missing token. - if (isConvergenceControlIntrinsic(SSAContext::getIntrinsicID(I))) - return true; - + if (auto *CB = dyn_cast(&I)) { + return CB->isConvergent(); + } return false; } diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -5927,23 +5927,10 @@ break; } case Intrinsic::experimental_convergence_entry: - Check(Call.getFunction()->isConvergent(), - "Entry intrinsic can occur only in a convergent function.", &Call); - Check(Call.getParent()->isEntryBlock(), - "Entry intrinsic must occur in the entry block.", &Call); - Check(Call.getParent()->getFirstNonPHI() == &Call, - "Entry intrinsic must occur at the start of the basic block.", &Call); LLVM_FALLTHROUGH; case Intrinsic::experimental_convergence_anchor: - Check(!Call.getOperandBundle(LLVMContext::OB_convergencectrl), - "Entry or anchor intrinsic must not have a convergencectrl bundle.", - &Call); break; case Intrinsic::experimental_convergence_loop: - Check(Call.getOperandBundle(LLVMContext::OB_convergencectrl), - "Loop intrinsic must have a convergencectrl bundle.", &Call); - Check(Call.getParent()->getFirstNonPHI() == &Call, - "Loop intrinsic must occur at the start of the basic block.", &Call); break; }; diff --git a/llvm/test/Verifier/convergencectrl-invalid.ll b/llvm/test/Verifier/convergencectrl-invalid.ll --- a/llvm/test/Verifier/convergencectrl-invalid.ll +++ b/llvm/test/Verifier/convergencectrl-invalid.ll @@ -1,8 +1,8 @@ ; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s -; CHECK: Entry or anchor intrinsic must not have a convergencectrl bundle. +; CHECK: Entry or anchor intrinsic cannot have a convergencectrl token operand. ; CHECK-NEXT: %t04_tok2 = call token -; CHECK: Loop intrinsic must have a convergencectrl bundle. +; 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() @@ -20,7 +20,7 @@ ret void } -; CHECK: Expected convergent attribute on a controlled convergent call. +; 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() @@ -109,7 +109,7 @@ br label %B } -; CHECK: Entry intrinsic must occur at the start of the basic block. +; CHECK: Entry intrinsic can occur only at the start of the basic block. ; CHECK: %t60_tok1 define void @entry_at_start(i32 %x, i32 %y) convergent { %z = add i32 %x, %y @@ -124,7 +124,7 @@ ret void } -; CHECK: Loop intrinsic must occur at the start of the basic block. +; CHECK: Loop intrinsic can occur only at the start of the basic block. ; CHECK: %t60_tok3 define void @loop_at_start(i32 %x, i32 %y) convergent { A: @@ -136,7 +136,7 @@ ret void } -; CHECK: Entry intrinsic must occur in the entry block. +; CHECK: Entry intrinsic can occur only in the entry block. ; CHECK: %t60_tok4 define void @entry_at_entry(i32 %x, i32 %y) convergent { A: