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