blob: 4240c8978fd6d33c6f2070dadeee0d3dd8b1774d [file] [log] [blame] [edit]
#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<WebAssemblySubtarget>(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<ICmpInst>(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<IntrinsicInst>(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 <N x i1> to <N x iX>
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);
}