#include "WebAssembly.h" #include "WebAssemblySubtarget.h" #include "WebAssemblyTargetMachine.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/IntrinsicsWebAssembly.h" #include "llvm/IR/Module.h" #include "llvm/IR/PatternMatch.h" #include "llvm/Pass.h" using namespace llvm; using namespace llvm::PatternMatch; namespace { struct WebAssemblyReduceToAnyAllTrue final : FunctionPass { static char ID; WebAssemblyTargetMachine &TM; const Module *CachedModule = nullptr; bool ModuleHasInterestingIntrinsics = false; WebAssemblyReduceToAnyAllTrue(WebAssemblyTargetMachine &TM) : FunctionPass(ID), TM(TM) {} StringRef getPassName() const override { return "WebAssembly convert reduce to any_true/all_true"; } bool hasInterestingIntrinsics(const Module &M) { if (CachedModule == &M) return ModuleHasInterestingIntrinsics; CachedModule = &M; ModuleHasInterestingIntrinsics = false; for (const Function &Fn : M.functions()) { switch (Fn.getIntrinsicID()) { case Intrinsic::vector_reduce_or: case Intrinsic::vector_reduce_and: ModuleHasInterestingIntrinsics = true; return true; default: break; } } return false; } bool runOnFunction(Function &F) override { if (!TM.getSubtarget(F).hasSIMD128()) return false; if (!hasInterestingIntrinsics(*F.getParent())) return false; bool Changed = false; for (auto &BB : F) { for (auto It = BB.begin(), E = BB.end(); It != E;) { Instruction *I = &*It++; auto *Cmp = dyn_cast(I); if (!Cmp || Cmp->getPredicate() != ICmpInst::ICMP_NE) continue; Value *Reduce = nullptr; if (!match(Cmp, m_ICmp(m_Value(Reduce), m_ZeroInt()))) continue; auto *II = dyn_cast(Reduce); if (!II || !II->hasOneUse()) continue; IRBuilder<> B(Cmp); Value *Vec = II->getArgOperand(0); Module *M = F.getParent(); auto makeIntrinsic = [&](Intrinsic::ID ID, Value *Arg) { Function *Fn = Intrinsic::getOrInsertDeclaration(M, ID, {Arg->getType()}); return B.CreateCall(Fn, {Arg}); }; Value *New = nullptr; switch (II->getIntrinsicID()) { case Intrinsic::vector_reduce_or: { // reduce.or(X) != 0 -> anytrue(X) Value *Any = makeIntrinsic(Intrinsic::wasm_anytrue, Vec); New = B.CreateICmpNE(Any, ConstantInt::get(Any->getType(), 0)); break; } case Intrinsic::vector_reduce_and: { // reduce.and(zext (icmp ne X, zeroinitializer)) != 0 -> alltrue(X) // Match: zext (icmp ne X, 0) from to CmpPredicate Pred; Value *LHS = nullptr; if (!match(Vec, m_ZExt(m_c_ICmp(Pred, m_Value(LHS), m_Zero())))) continue; if (Pred != ICmpInst::ICMP_NE) continue; Value *All = makeIntrinsic(Intrinsic::wasm_alltrue, LHS); New = B.CreateICmpNE(All, ConstantInt::get(All->getType(), 0)); break; } default: continue; } Cmp->replaceAllUsesWith(New); Cmp->eraseFromParent(); if (II->use_empty()) II->eraseFromParent(); Changed = true; } } return Changed; } }; } // end anonymous namespace char WebAssemblyReduceToAnyAllTrue::ID = 0; FunctionPass * llvm::createWebAssemblyReduceToAnyAllTrue(WebAssemblyTargetMachine &TM) { return new WebAssemblyReduceToAnyAllTrue(TM); }