]> granicus.if.org Git - llvm/commitdiff
[Analysis] add isSplatValue() for vectors in IR
authorSanjay Patel <spatel@rotateright.com>
Tue, 11 Jun 2019 22:25:18 +0000 (22:25 +0000)
committerSanjay Patel <spatel@rotateright.com>
Tue, 11 Jun 2019 22:25:18 +0000 (22:25 +0000)
We have the related getSplatValue() already in IR (see code just above the proposed addition).
But sometimes we only need to know that the value is a splat rather than capture the splatted
scalar value. Also, we have an isSplatValue() function already in SDAG.

Motivation - recent bugs that would potentially benefit from improved splat analysis in IR:
https://bugs.llvm.org/show_bug.cgi?id=37428
https://bugs.llvm.org/show_bug.cgi?id=42174

Differential Revision: https://reviews.llvm.org/D63138

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@363106 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/Analysis/VectorUtils.h
lib/Analysis/VectorUtils.cpp
unittests/Analysis/VectorUtilsTest.cpp

index f79bdc17502d87a171990d6fcf512d8993dca373..ac3588594377a0f25df08f3aabd3bb7eb4bb57a3 100644 (file)
@@ -78,6 +78,12 @@ Value *findScalarElement(Value *V, unsigned EltNo);
 /// a sequence of instructions that broadcast a single value into a vector.
 const Value *getSplatValue(const Value *V);
 
+/// Return true if the input value is known to be a vector with all identical
+/// elements (potentially including undefined elements).
+/// This may be more powerful than the related getSplatValue() because it is
+/// not limited by finding a scalar source value to a splatted vector.
+bool isSplatValue(const Value *V, unsigned Depth = 0);
+
 /// Compute a map of integer instructions to their minimum legal type
 /// size.
 ///
index ea00f0a7dd3daf02bc394b86258fd8b858a9d2cb..8040af12d7c9453505e185f2bf045d79e893b662 100644 (file)
@@ -321,6 +321,45 @@ const llvm::Value *llvm::getSplatValue(const Value *V) {
   return nullptr;
 }
 
+// This setting is based on its counterpart in value tracking, but it could be
+// adjusted if needed.
+const unsigned MaxDepth = 6;
+
+bool llvm::isSplatValue(const Value *V, unsigned Depth) {
+  assert(Depth <= MaxDepth && "Limit Search Depth");
+
+  if (isa<VectorType>(V->getType())) {
+    if (isa<UndefValue>(V))
+      return true;
+    // FIXME: Constant splat analysis does not allow undef elements.
+    if (auto *C = dyn_cast<Constant>(V))
+      return C->getSplatValue() != nullptr;
+  }
+
+  // FIXME: Constant splat analysis does not allow undef elements.
+  Constant *Mask;
+  if (match(V, m_ShuffleVector(m_Value(), m_Value(), m_Constant(Mask))))
+    return Mask->getSplatValue() != nullptr;
+
+  // The remaining tests are all recursive, so bail out if we hit the limit.
+  if (Depth++ == MaxDepth)
+    return false;
+
+  // If both operands of a binop are splats, the result is a splat.
+  Value *X, *Y, *Z;
+  if (match(V, m_BinOp(m_Value(X), m_Value(Y))))
+    return isSplatValue(X, Depth) && isSplatValue(Y, Depth);
+
+  // If all operands of a select are splats, the result is a splat.
+  if (match(V, m_Select(m_Value(X), m_Value(Y), m_Value(Z))))
+    return isSplatValue(X, Depth) && isSplatValue(Y, Depth) &&
+           isSplatValue(Z, Depth);
+
+  // TODO: Add support for unary ops (fneg), casts, intrinsics (overflow ops).
+
+  return false;
+}
+
 MapVector<Instruction *, uint64_t>
 llvm::computeMinimumValueSizes(ArrayRef<BasicBlock *> Blocks, DemandedBits &DB,
                                const TargetTransformInfo *TTI) {
index 8ff6744bf7806df94b8ef65941732c6f202a22f0..a33fdb503bb42f7088afcb1ebd5e216e9f41e4a3 100644 (file)
 #include "llvm/AsmParser/Parser.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/InstIterator.h"
+#include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
+#include "llvm/IR/NoFolder.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/KnownBits.h"
@@ -56,8 +58,196 @@ protected:
   Instruction *A;
 };
 
+struct BasicTest : public testing::Test {
+  LLVMContext Ctx;
+  std::unique_ptr<Module> M;
+  Function *F;
+  BasicBlock *BB;
+  IRBuilder<NoFolder> IRB;
+
+  BasicTest()
+      : M(new Module("VectorUtils", Ctx)),
+        F(Function::Create(
+            FunctionType::get(Type::getVoidTy(Ctx), /* IsVarArg */ false),
+            Function::ExternalLinkage, "f", M.get())),
+        BB(BasicBlock::Create(Ctx, "entry", F)), IRB(BB) {}
+};
+
+
 } // namespace
 
+TEST_F(BasicTest, isSplat) {
+  Value *UndefVec = UndefValue::get(VectorType::get(IRB.getInt8Ty(), 4));
+  EXPECT_TRUE(isSplatValue(UndefVec));
+
+  Constant *UndefScalar = UndefValue::get(IRB.getInt8Ty());
+  EXPECT_FALSE(isSplatValue(UndefScalar));
+
+  Constant *ScalarC = IRB.getInt8(42);
+  EXPECT_FALSE(isSplatValue(ScalarC));
+
+  Constant *OtherScalarC = IRB.getInt8(-42);
+  Constant *NonSplatC = ConstantVector::get({ScalarC, OtherScalarC});
+  EXPECT_FALSE(isSplatValue(NonSplatC));
+
+  Value *SplatC = IRB.CreateVectorSplat(5, ScalarC);
+  EXPECT_TRUE(isSplatValue(SplatC));
+
+  // FIXME: Constant splat analysis does not allow undef elements.
+  Constant *SplatWithUndefC = ConstantVector::get({ScalarC, UndefScalar});
+  EXPECT_FALSE(isSplatValue(SplatWithUndefC));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_00) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i8> %x) {\n"
+      "  %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> zeroinitializer\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_TRUE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_11) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i8> %x) {\n"
+      "  %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_TRUE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_01) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i8> %x) {\n"
+      "  %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 1>\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_FALSE(isSplatValue(A));
+}
+
+// FIXME: Constant (mask) splat analysis does not allow undef elements.
+
+TEST_F(VectorUtilsTest, isSplatValue_0u) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i8> %x) {\n"
+      "  %A = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 undef>\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_FALSE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_Binop) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i8> %x) {\n"
+      "  %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
+      "  %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %A = udiv <2 x i8> %v0, %v1\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_TRUE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_Binop_ConstantOp0) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i8> %x) {\n"
+      "  %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %A = ashr <2 x i8> <i8 42, i8 42>, %v1\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_TRUE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_Binop_Not_Op0) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i8> %x) {\n"
+      "  %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 0>\n"
+      "  %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %A = add <2 x i8> %v0, %v1\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_FALSE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_Binop_Not_Op1) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i8> %x) {\n"
+      "  %v0 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %v1 = shufflevector <2 x i8> %x, <2 x i8> undef, <2 x i32> <i32 0, i32 1>\n"
+      "  %A = shl <2 x i8> %v0, %v1\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_FALSE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_Select) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
+      "  %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
+      "  %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %A = select <2 x i1> %v0, <2 x i8> %v1, <2 x i8> %v2\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_TRUE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_Select_ConstantOp) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
+      "  %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %A = select <2 x i1> %v0, <2 x i8> <i8 42, i8 42>, <2 x i8> %v2\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_TRUE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_Select_NotCond) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
+      "  %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
+      "  %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %A = select <2 x i1> %x, <2 x i8> %v1, <2 x i8> %v2\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_FALSE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_Select_NotOp1) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
+      "  %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %A = select <2 x i1> %v0, <2 x i8> %y, <2 x i8> %v2\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_FALSE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_Select_NotOp2) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
+      "  %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
+      "  %A = select <2 x i1> %v0, <2 x i8> %v1, <2 x i8> %z\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_FALSE(isSplatValue(A));
+}
+
+TEST_F(VectorUtilsTest, isSplatValue_SelectBinop) {
+  parseAssembly(
+      "define <2 x i8> @test(<2 x i1> %x, <2 x i8> %y, <2 x i8> %z) {\n"
+      "  %v0 = shufflevector <2 x i1> %x, <2 x i1> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %v1 = shufflevector <2 x i8> %y, <2 x i8> undef, <2 x i32> <i32 0, i32 0>\n"
+      "  %v2 = shufflevector <2 x i8> %z, <2 x i8> undef, <2 x i32> <i32 1, i32 1>\n"
+      "  %bo = xor <2 x i8> %v1, %v2\n"
+      "  %A = select <2 x i1> %v0, <2 x i8> %bo, <2 x i8> %v2\n"
+      "  ret <2 x i8> %A\n"
+      "}\n");
+  EXPECT_TRUE(isSplatValue(A));
+}
+
 TEST_F(VectorUtilsTest, getSplatValueElt0) {
   parseAssembly(
       "define <2 x i8> @test(i8 %x) {\n"