1#include "WebAssembly.h"
2#include "WebAssemblySubtarget.h"
3#include "WebAssemblyTargetMachine.h"
4#include "llvm/IR/Analysis.h"
5#include "llvm/IR/IRBuilder.h"
6#include "llvm/IR/IntrinsicsWebAssembly.h"
7#include "llvm/IR/Module.h"
8#include "llvm/IR/PassManager.h"
9#include "llvm/IR/PatternMatch.h"
10#include "llvm/Pass.h"
11
12using namespace llvm;
13using namespace llvm::PatternMatch;
14
15namespace {
16struct WebAssemblyReduceToAnyAllTrueLegacy final : FunctionPass {
17 static char ID;
18
19 WebAssemblyTargetMachine &TM;
20 Module *CachedModule = nullptr;
21 bool ModuleHasInterestingIntrinsics = false;
22
23 WebAssemblyReduceToAnyAllTrueLegacy(WebAssemblyTargetMachine &TM)
24 : FunctionPass(ID), TM(TM) {}
25
26 StringRef getPassName() const override {
27 return "WebAssembly convert reduce to any_true/all_true";
28 }
29
30 bool runOnFunction(Function &F) override;
31};
32} // end anonymous namespace
33
34char WebAssemblyReduceToAnyAllTrueLegacy::ID = 0;
35
36static bool hasInterestingIntrinsics(Module &M, Module *&CachedModule,
37 bool &ModuleHasInterestingIntrinsics) {
38 if (CachedModule == &M)
39 return ModuleHasInterestingIntrinsics;
40
41 CachedModule = &M;
42 ModuleHasInterestingIntrinsics = false;
43
44 for (const Function &Fn : M.functions()) {
45 switch (Fn.getIntrinsicID()) {
46 case Intrinsic::vector_reduce_or:
47 case Intrinsic::vector_reduce_and:
48 ModuleHasInterestingIntrinsics = true;
49 return true;
50 default:
51 break;
52 }
53 }
54
55 return false;
56}
57
58static bool reduceToAnyAllTrue(Function &F, WebAssemblyTargetMachine &TM,
59 Module *&CachedModule,
60 bool &ModuleHasInterestingIntrinsics) {
61 if (!TM.getSubtarget<WebAssemblySubtarget>(F).hasSIMD128())
62 return false;
63
64 if (!hasInterestingIntrinsics(M&: *F.getParent(), CachedModule,
65 ModuleHasInterestingIntrinsics))
66 return false;
67
68 bool Changed = false;
69
70 for (auto &BB : F) {
71 for (auto It = BB.begin(), E = BB.end(); It != E;) {
72 Instruction *I = &*It++;
73 auto *Cmp = dyn_cast<ICmpInst>(Val: I);
74 if (!Cmp || Cmp->getPredicate() != ICmpInst::ICMP_NE)
75 continue;
76
77 Value *Reduce = nullptr;
78 if (!match(V: Cmp, P: m_ICmp(L: m_Value(V&: Reduce), R: m_ZeroInt())))
79 continue;
80
81 auto *II = dyn_cast<IntrinsicInst>(Val: Reduce);
82 if (!II || !II->hasOneUse())
83 continue;
84
85 IRBuilder<> B(Cmp);
86 Value *Vec = II->getArgOperand(i: 0);
87 Module *M = F.getParent();
88
89 auto makeIntrinsic = [&](Intrinsic::ID ID, Value *Arg) {
90 Function *Fn =
91 Intrinsic::getOrInsertDeclaration(M, id: ID, OverloadTys: {Arg->getType()});
92 return B.CreateCall(Callee: Fn, Args: {Arg});
93 };
94
95 Value *New = nullptr;
96
97 switch (II->getIntrinsicID()) {
98 case Intrinsic::vector_reduce_or: {
99 // reduce.or(X) != 0 -> anytrue(X)
100 Value *Any = makeIntrinsic(Intrinsic::wasm_anytrue, Vec);
101 New = B.CreateICmpNE(LHS: Any, RHS: ConstantInt::get(Ty: Any->getType(), V: 0));
102 break;
103 }
104
105 case Intrinsic::vector_reduce_and: {
106 // reduce.and(zext (icmp ne X, zeroinitializer)) != 0 -> alltrue(X)
107
108 // Match: zext (icmp ne X, 0) from <N x i1> to <N x iX>
109 CmpPredicate Pred;
110 Value *LHS = nullptr;
111 if (!match(V: Vec, P: m_ZExt(Op: m_c_ICmp(Pred, L: m_Value(V&: LHS), R: m_Zero()))))
112 continue;
113 if (Pred != ICmpInst::ICMP_NE)
114 continue;
115
116 Value *All = makeIntrinsic(Intrinsic::wasm_alltrue, LHS);
117 New = B.CreateICmpNE(LHS: All, RHS: ConstantInt::get(Ty: All->getType(), V: 0));
118 break;
119 }
120
121 default:
122 continue;
123 }
124
125 Cmp->replaceAllUsesWith(V: New);
126 Cmp->eraseFromParent();
127
128 if (II->use_empty())
129 II->eraseFromParent();
130
131 Changed = true;
132 }
133 }
134
135 return Changed;
136}
137
138bool WebAssemblyReduceToAnyAllTrueLegacy::runOnFunction(Function &F) {
139 return reduceToAnyAllTrue(F, TM, CachedModule,
140 ModuleHasInterestingIntrinsics);
141}
142
143PreservedAnalyses
144WebAssemblyReduceToAnyAllTruePass::run(Function &F,
145 FunctionAnalysisManager &FAM) {
146 return reduceToAnyAllTrue(F, TM, CachedModule, ModuleHasInterestingIntrinsics)
147 ? PreservedAnalyses::none().preserveSet<CFGAnalyses>()
148 : PreservedAnalyses::all();
149}
150
151FunctionPass *llvm::createWebAssemblyReduceToAnyAllTrueLegacyPass(
152 WebAssemblyTargetMachine &TM) {
153 return new WebAssemblyReduceToAnyAllTrueLegacy(TM);
154}
155