1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This pass strips convergence intrinsics and convergencectrl operand bundles,
10// as those are only useful when modifying the CFG during IR passes.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Transforms/Utils/StripConvergenceIntrinsics.h"
15#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/SmallVector.h"
17#include "llvm/IR/IntrinsicInst.h"
18#include "llvm/IR/Intrinsics.h"
19#include "llvm/InitializePasses.h"
20#include "llvm/Pass.h"
21#include "llvm/Transforms/Utils.h"
22
23using namespace llvm;
24
25static bool stripConvergenceIntrinsics(Function &F) {
26 SmallVector<IntrinsicInst *> ConvergenceIntrinsics;
27 bool Changed = false;
28
29 for (BasicBlock &BB : F) {
30 for (Instruction &I : make_early_inc_range(Range&: BB)) {
31 auto *CI = dyn_cast<CallInst>(Val: &I);
32 if (!CI)
33 continue;
34
35 // Strip a convergencectrl operand bundle if present. Note that
36 // convergence intrinsics (e.g. convergence.loop) may use a
37 // convergencectrl bundle.
38 if (CI->getOperandBundle(ID: LLVMContext::OB_convergencectrl)) {
39 auto *NewCall = CallBase::removeOperandBundle(
40 CB: CI, ID: LLVMContext::OB_convergencectrl, InsertPt: CI->getIterator());
41 NewCall->copyMetadata(SrcInst: *CI);
42 CI->replaceAllUsesWith(V: NewCall);
43 CI->eraseFromParent();
44 CI = cast<CallInst>(Val: NewCall);
45 Changed = true;
46 }
47
48 // Collect convergence intrinsics for deferred removal.
49 if (auto *II = dyn_cast<IntrinsicInst>(Val: CI))
50 if (II->getIntrinsicID() == Intrinsic::experimental_convergence_entry ||
51 II->getIntrinsicID() == Intrinsic::experimental_convergence_loop ||
52 II->getIntrinsicID() == Intrinsic::experimental_convergence_anchor)
53 ConvergenceIntrinsics.push_back(Elt: II);
54 }
55 }
56
57 // Erase all convergence intrinsics now that convergence tokens are no
58 // longer in use.
59 for (IntrinsicInst *II : ConvergenceIntrinsics)
60 II->eraseFromParent();
61 Changed |= !ConvergenceIntrinsics.empty();
62
63 return Changed;
64}
65
66PreservedAnalyses
67StripConvergenceIntrinsicsPass::run(Function &F, FunctionAnalysisManager &) {
68 if (!stripConvergenceIntrinsics(F))
69 return PreservedAnalyses::all();
70 PreservedAnalyses PA;
71 PA.preserveSet<CFGAnalyses>();
72 return PA;
73}
74
75namespace {
76class StripConvergenceIntrinsicsLegacyPass : public FunctionPass {
77public:
78 static char ID;
79
80 StripConvergenceIntrinsicsLegacyPass() : FunctionPass(ID) {
81 initializeStripConvergenceIntrinsicsLegacyPassPass(
82 *PassRegistry::getPassRegistry());
83 }
84
85 bool runOnFunction(Function &F) override {
86 return stripConvergenceIntrinsics(F);
87 }
88};
89} // namespace
90
91char StripConvergenceIntrinsicsLegacyPass::ID = 0;
92INITIALIZE_PASS(StripConvergenceIntrinsicsLegacyPass,
93 "strip-convergence-intrinsics",
94 "Strip convergence intrinsics and operand bundles", false,
95 false)
96
97FunctionPass *llvm::createStripConvergenceIntrinsicsPass() {
98 return new StripConvergenceIntrinsicsLegacyPass();
99}
100