summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Vasilache <ntv@google.com>2018-11-20 08:36:07 -0800
committerjpienaar <jpienaar@google.com>2019-03-29 14:02:17 -0700
commit89d9913a2071c3b50cb43f6bfda70cf58fe09b09 (patch)
tree5d216444d93ca23f86ba039e110d8ab8ae72085d
parentf10f48ee6339bcd4af13fba98f89fac307d56530 (diff)
downloadbcm5719-llvm-89d9913a2071c3b50cb43f6bfda70cf58fe09b09.tar.gz
bcm5719-llvm-89d9913a2071c3b50cb43f6bfda70cf58fe09b09.zip
[MLIR][VectorAnalysis] Add a VectorAnalysis and standalone tests
This CL adds some vector support in prevision of the upcoming vector materialization pass. In particular this CL adds 2 functions to: 1. compute the multiplicity of a subvector shape in a supervector shape; 2. help match operations on strict super-vectors. This is defined for a given subvector shape as an operation that manipulates a vector type that is an integral multiple of the subtype, with multiplicity at least 2. This CL also adds a TestUtil pass where we can dump arbitrary testing of functions and analysis that operate at a much smaller granularity than a pass (e.g. an analysis for which it is convenient to write a bit of artificial MLIR and write some custom test). This is in order to keep using Filecheck for things that essentially look and feel like C++ unit tests. PiperOrigin-RevId: 222250910
-rw-r--r--mlir/include/mlir/Analysis/LoopAnalysis.h4
-rw-r--r--mlir/include/mlir/Analysis/VectorAnalysis.h68
-rw-r--r--mlir/include/mlir/Support/Functional.h19
-rw-r--r--mlir/include/mlir/Transforms/Passes.h6
-rw-r--r--mlir/lib/Analysis/LoopAnalysis.cpp5
-rw-r--r--mlir/lib/Analysis/VectorAnalysis.cpp166
-rw-r--r--mlir/lib/Transforms/Vectorization/VectorizerTestPass.cpp116
-rw-r--r--mlir/lib/Transforms/Vectorize.cpp1
-rw-r--r--mlir/test/Transforms/vector_utils.mlir37
9 files changed, 409 insertions, 13 deletions
diff --git a/mlir/include/mlir/Analysis/LoopAnalysis.h b/mlir/include/mlir/Analysis/LoopAnalysis.h
index 6ee38d28441..91c8e747836 100644
--- a/mlir/include/mlir/Analysis/LoopAnalysis.h
+++ b/mlir/include/mlir/Analysis/LoopAnalysis.h
@@ -32,10 +32,6 @@ class ForStmt;
class MemRefType;
class MLValue;
-// TODO(ntv): Drop this once we have proper Ops.
-static constexpr auto kVectorTransferReadOpName = "vector_transfer_read";
-static constexpr auto kVectorTransferWriteOpName = "vector_transfer_write";
-
/// Returns the trip count of the loop as an affine expression if the latter is
/// expressible as an affine expression, and nullptr otherwise. The trip count
/// expression is simplified before returning.
diff --git a/mlir/include/mlir/Analysis/VectorAnalysis.h b/mlir/include/mlir/Analysis/VectorAnalysis.h
new file mode 100644
index 00000000000..82bffb8fa7d
--- /dev/null
+++ b/mlir/include/mlir/Analysis/VectorAnalysis.h
@@ -0,0 +1,68 @@
+//===- VectorAnalysis.h - Analysis for Vectorization -------*- C++ -*-=======//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+
+#ifndef MLIR_ANALYSIS_VECTORANALYSIS_H_
+#define MLIR_ANALYSIS_VECTORANALYSIS_H_
+
+#include "mlir/Support/LLVM.h"
+
+namespace mlir {
+
+class OperationStmt;
+class VectorType;
+
+// TODO(ntv): Drop this once we have proper Ops.
+static constexpr auto kVectorTransferReadOpName = "vector_transfer_read";
+static constexpr auto kVectorTransferWriteOpName = "vector_transfer_write";
+bool isaVectorTransferRead(const OperationStmt &stmt);
+bool isaVectorTransferWrite(const OperationStmt &stmt);
+
+/// Computes and returns the multi-dimensional ratio of `superShape` to
+/// `subShape`. This is calculated by performing a traversal from minor to major
+/// dimensions (i.e. in reverse shape order). If integral division is not
+/// possible, returns None.
+///
+/// Examples:
+/// - shapeRatio({3, 4, 5, 8}, {2, 5, 2}) returns {3, 2, 1, 4}
+/// - shapeRatio({3, 4, 4, 8}, {2, 5, 2}) returns None
+/// - shapeRatio({1, 2, 10, 32}, {2, 5, 2}) returns {1, 1, 2, 16}
+llvm::Optional<llvm::SmallVector<unsigned, 4>>
+shapeRatio(ArrayRef<int> superShape, ArrayRef<int> subShape);
+
+/// Computes and returns the multi-dimensional ratio of the shapes of
+/// `superVecto` to `subVector`. If integral division is not possible, returns
+/// None.
+llvm::Optional<llvm::SmallVector<unsigned, 4>>
+shapeRatio(VectorType superVectorType, VectorType subVectorType);
+
+namespace matcher {
+
+/// Matches vector_transfer_read, vector_transfer_write and ops that return a
+/// vector type that is at least a 2-multiple of the sub-vector type. This
+/// allows passing over other smaller vector types in the function and avoids
+/// interfering with operations on those.
+/// This is a first approximation, it can easily be extended in the future.
+/// TODO(ntv): this could all be much simpler if we added a bit that a vector
+/// type to mark that a vector is a strict super-vector but it still does not
+/// warrant adding even 1 extra bit in the IR for now.
+bool operatesOnStrictSuperVectors(const OperationStmt &stmt,
+ VectorType subVectorType);
+
+} // end namespace matcher
+} // end namespace mlir
+
+#endif // MLIR_ANALYSIS_VECTORANALYSIS_H_
diff --git a/mlir/include/mlir/Support/Functional.h b/mlir/include/mlir/Support/Functional.h
index e108e32c126..7740c5d27c9 100644
--- a/mlir/include/mlir/Support/Functional.h
+++ b/mlir/include/mlir/Support/Functional.h
@@ -18,10 +18,9 @@
#ifndef MLIR_SUPPORT_FUNCTIONAL_H_
#define MLIR_SUPPORT_FUNCTIONAL_H_
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/iterator_range.h"
-#include <functional>
-#include <type_traits>
+#include "llvm/ADT/STLExtras.h"
+//#include <functional>
+//#include <type_traits>
/// This file provides some simple template functional-style sugar to operate
/// on **value** types. Make sure when using that the stored type is cheap to
@@ -64,12 +63,22 @@ void apply(Fun fun, IterType begin, IterType end) {
}
}
-/// Map with templated container.
+/// Apply with templated container.
template <typename Fun, typename ContainerType>
void apply(Fun fun, ContainerType input) {
return apply(fun, std::begin(input), std::end(input));
}
+/// Zip with 2 templated container.
+/// TODO(ntv): make variadic.
+template <typename Fun, typename ContainerType1, typename ContainerType2>
+void zip(Fun fun, ContainerType1 input1, ContainerType2 input2) {
+ auto zipIter = llvm::zip(input1, input2);
+ for (auto it : zipIter) {
+ fun(std::get<0>(it), std::get<1>(it));
+ }
+}
+
/// Simple ScopeGuard.
struct ScopeGuard {
explicit ScopeGuard(std::function<void(void)> destruct)
diff --git a/mlir/include/mlir/Transforms/Passes.h b/mlir/include/mlir/Transforms/Passes.h
index 05aa04fc70b..fa509ac6337 100644
--- a/mlir/include/mlir/Transforms/Passes.h
+++ b/mlir/include/mlir/Transforms/Passes.h
@@ -37,9 +37,13 @@ FunctionPass *createConstantFoldPass();
FunctionPass *createCanonicalizerPass();
/// Creates a pass to vectorize loops, operations and data types using a
-/// target-independent, n-D virtual vector abstraction.
+/// target-independent, n-D super-vector abstraction.
FunctionPass *createVectorizePass();
+/// Creates a pass to allow independent testing of vectorizer functionality with
+/// FileCheck.
+FunctionPass *createVectorizerTestPass();
+
/// Creates a loop unrolling pass. Default option or command-line options take
/// effect if -1 is passed as parameter.
FunctionPass *createLoopUnrollPass(int unrollFactor = -1, int unrollFull = -1);
diff --git a/mlir/lib/Analysis/LoopAnalysis.cpp b/mlir/lib/Analysis/LoopAnalysis.cpp
index 78a8e2d7a46..8406a37d793 100644
--- a/mlir/lib/Analysis/LoopAnalysis.cpp
+++ b/mlir/lib/Analysis/LoopAnalysis.cpp
@@ -24,6 +24,7 @@
#include "mlir/Analysis/AffineAnalysis.h"
#include "mlir/Analysis/AffineStructures.h"
#include "mlir/Analysis/MLFunctionMatcher.h"
+#include "mlir/Analysis/VectorAnalysis.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Statements.h"
@@ -193,9 +194,7 @@ static bool isVectorElement(LoadOrStoreOpPointer memoryOp) {
// TODO(ntv): make the following into MLIR instructions, then use isa<>.
static bool isVectorTransferReadOrWrite(const Statement &stmt) {
const auto *opStmt = cast<OperationStmt>(&stmt);
- llvm::SmallString<16> name(opStmt->getName().getStringRef());
- return name == kVectorTransferReadOpName ||
- name == kVectorTransferWriteOpName;
+ return isaVectorTransferRead(*opStmt) || isaVectorTransferWrite(*opStmt);
}
using VectorizableStmtFun =
diff --git a/mlir/lib/Analysis/VectorAnalysis.cpp b/mlir/lib/Analysis/VectorAnalysis.cpp
new file mode 100644
index 00000000000..f6bb9d4f516
--- /dev/null
+++ b/mlir/lib/Analysis/VectorAnalysis.cpp
@@ -0,0 +1,166 @@
+//===- VectorAnalysis.cpp - Analysis for Vectorization --------------------===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+
+#include "mlir/Analysis/VectorAnalysis.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/Statements.h"
+#include "mlir/Support/Functional.h"
+#include "mlir/Support/STLExtras.h"
+
+///
+/// Implements Analysis functions specific to vectors which support
+/// the vectorization and vectorization materialization passes.
+///
+
+using namespace mlir;
+
+bool mlir::isaVectorTransferRead(const OperationStmt &stmt) {
+ return stmt.getName().getStringRef().str() == kVectorTransferReadOpName;
+}
+
+bool mlir::isaVectorTransferWrite(const OperationStmt &stmt) {
+ return stmt.getName().getStringRef().str() == kVectorTransferWriteOpName;
+}
+
+Optional<SmallVector<unsigned, 4>> mlir::shapeRatio(ArrayRef<int> superShape,
+ ArrayRef<int> subShape) {
+ if (superShape.size() < subShape.size()) {
+ return Optional<SmallVector<unsigned, 4>>();
+ }
+
+ // Starting from the end, compute the integer divisors.
+ // Set the boolean `divides` if integral division is not possible.
+ std::vector<unsigned> result;
+ result.reserve(superShape.size());
+ bool divides = true;
+ auto divide = [&divides, &result](int superSize, int subSize) {
+ assert(superSize > 0 && "superSize must be > 0");
+ assert(subSize > 0 && "subSize must be > 0");
+ divides &= (superSize % subSize == 0);
+ result.push_back(superSize / subSize);
+ };
+ functional::zip(divide,
+ SmallVector<int, 8>{superShape.rbegin(), superShape.rend()},
+ SmallVector<int, 8>{subShape.rbegin(), subShape.rend()});
+
+ // If integral division does not occur, return and let the caller decide.
+ if (!divides) {
+ return Optional<SmallVector<unsigned, 4>>();
+ }
+
+ // At this point we computed the multiplicity (in reverse) for the common
+ // size. Fill with the remaining entries from the super-vector shape (still in
+ // reverse).
+ int commonSize = subShape.size();
+ std::copy(superShape.rbegin() + commonSize, superShape.rend(),
+ std::back_inserter(result));
+
+ assert(result.size() == superShape.size() &&
+ "multiplicity must be of the same size as the super-vector rank");
+
+ // Reverse again to get it back in the proper order and return.
+ return SmallVector<unsigned, 4>{result.rbegin(), result.rend()};
+}
+
+Optional<SmallVector<unsigned, 4>> mlir::shapeRatio(VectorType superVectorType,
+ VectorType subVectorType) {
+ assert(superVectorType.getElementType() == subVectorType.getElementType() &&
+ "NYI: vector types must be of the same elemental type");
+ assert(superVectorType.getElementType() ==
+ Type::getF32(superVectorType.getContext()) &&
+ "Only f32 supported for now");
+ return shapeRatio(superVectorType.getShape(), subVectorType.getShape());
+}
+
+/// Matches vector_transfer_read, vector_transfer_write and ops that return a
+/// vector type that is at least a 2-multiple of the sub-vector type size.
+/// This allows leaving other vector types in the function untouched and avoids
+/// interfering with operations on those.
+/// This is a first approximation, it can easily be extended in the future.
+/// TODO(ntv): this could all be much simpler if we added a bit that a vector
+/// type to mark that a vector is a strict super-vector but it is not strictly
+/// needed so let's avoid adding even 1 extra bit in the IR for now.
+bool mlir::matcher::operatesOnStrictSuperVectors(const OperationStmt &opStmt,
+ VectorType subVectorType) {
+ // First, extract the vector type and ditinguish between:
+ // a. ops that *must* lower a super-vector (i.e. vector_transfer_read,
+ // vector_transfer_write); and
+ // b. ops that *may* lower a super-vector (all other ops).
+ // The ops that *may* lower a super-vector only do so if the vector size is
+ // an integer multiple of the HW vector size, with multiplicity 1.
+ // The ops that *must* lower a super-vector are explicitly checked for this
+ // property.
+ /// TODO(ntv): there should be a single function for all ops to do this so we
+ /// do not have to special case. Maybe a trait, or just a method, unclear atm.
+ bool mustDivide = false;
+ VectorType superVectorType;
+ if (isaVectorTransferRead(opStmt)) {
+ superVectorType = opStmt.getResult(0)->getType().cast<VectorType>();
+ mustDivide = true;
+ } else if (isaVectorTransferWrite(opStmt)) {
+ // TODO(ntv): if vector_transfer_write had store-like semantics we could
+ // have written something similar to:
+ // auto store = storeOp->cast<StoreOp>();
+ // auto *value = store->getValueToStore();
+ superVectorType = opStmt.getOperand(0)->getType().cast<VectorType>();
+ mustDivide = true;
+ } else if (opStmt.getNumResults() == 0) {
+ assert(opStmt.dyn_cast<ReturnOp>() &&
+ "NYI: assuming only return statements can have 0 results at this "
+ "point");
+ return false;
+ } else if (opStmt.getNumResults() == 1) {
+ if (auto v = opStmt.getResult(0)->getType().dyn_cast<VectorType>()) {
+ superVectorType = v;
+ } else {
+ // Not a vector type.
+ return false;
+ }
+ } else {
+ // Not a vector_transfer and has more than 1 result, fail hard for now to
+ // wake us up when something changes.
+ assert(false && "NYI: statement has more than 1 result");
+ return false;
+ }
+
+ // Get the multiplicity.
+ auto multiplicity = shapeRatio(superVectorType, subVectorType);
+
+ // Sanity check.
+ assert((multiplicity.hasValue() || !mustDivide) &&
+ "NYI: vector_transfer instruction in which super-vector size is not an"
+ " integer multiple of sub-vector size");
+
+ // This catches cases that are not strictly necessary to have multiplicity but
+ // still aren't divisible by the sub-vector shape.
+ // This could be useful information if we wanted to reshape at the level of
+ // the vector type (but we would have to look at the compute and distinguish
+ // between parallel, reduction and possibly other cases.
+ if (!multiplicity.hasValue()) {
+ return false;
+ }
+
+ // A strict super-vector is at least 2 sub-vectors.
+ for (auto m : *multiplicity) {
+ if (m > 1) {
+ return true;
+ }
+ }
+
+ // Not a strict super-vector.
+ return false;
+}
diff --git a/mlir/lib/Transforms/Vectorization/VectorizerTestPass.cpp b/mlir/lib/Transforms/Vectorization/VectorizerTestPass.cpp
new file mode 100644
index 00000000000..dcd0a072ed3
--- /dev/null
+++ b/mlir/lib/Transforms/Vectorization/VectorizerTestPass.cpp
@@ -0,0 +1,116 @@
+//===- VectorizerTestPass.cpp - VectorizerTestPass Pass Impl ----*- C++
+//-*-====================//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+//
+// This file implements a simple testing pass for vectorization functionality.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Analysis/MLFunctionMatcher.h"
+#include "mlir/Analysis/VectorAnalysis.h"
+#include "mlir/Pass.h"
+#include "mlir/Support/LLVM.h"
+#include "mlir/Support/STLExtras.h"
+#include "mlir/Transforms/Passes.h"
+
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+
+using namespace mlir;
+
+using llvm::outs;
+using llvm::cl::desc;
+using llvm::cl::list;
+using llvm::cl::ZeroOrMore;
+
+static list<int> clTestVectorMultiplicity(
+ "vector-multiplicity", desc("Specify the HW vector size for vectorization"),
+ ZeroOrMore);
+
+#define DEBUG_TYPE "vectorizer-test"
+
+namespace {
+
+struct VectorizerTestPass : public FunctionPass {
+ VectorizerTestPass() : FunctionPass(&VectorizerTestPass::passID) {}
+
+ PassResult runOnMLFunction(MLFunction *f) override;
+ void testVectorMultiplicity(MLFunction *f);
+
+ // Thread-safe RAII contexts local to pass, BumpPtrAllocator freed on exit.
+ MLFunctionMatcherContext MLContext;
+
+ static char passID;
+};
+
+} // end anonymous namespace
+
+char VectorizerTestPass::passID = 0;
+
+void VectorizerTestPass::testVectorMultiplicity(MLFunction *f) {
+ using matcher::Op;
+ SmallVector<int, 8> shape(clTestVectorMultiplicity.begin(),
+ clTestVectorMultiplicity.end());
+ auto subVectorType = VectorType::get(shape, Type::getF32(f->getContext()));
+ // Only filter statements that operate on a strict super-vector and have one
+ // return. This makes testing easier.
+ auto filter = [subVectorType](const Statement &stmt) {
+ outs() << "\ntest: " << stmt << " ";
+ auto *opStmt = dyn_cast<OperationStmt>(&stmt);
+ if (!opStmt) {
+ return false;
+ }
+ if (!matcher::operatesOnStrictSuperVectors(*opStmt, subVectorType)) {
+ return false;
+ }
+ if (opStmt->getNumResults() != 1) {
+ return false;
+ }
+ return true;
+ };
+ auto pat = Op(filter);
+ auto matches = pat.match(f);
+ for (auto m : matches) {
+ auto *opStmt = cast<OperationStmt>(m.first);
+ // This is a unit test that only checks and prints shape ratio.
+ // As a consequence we write only Ops with a single return type for the
+ // purpose of this test. If we need to test more intricate behavior in the
+ // future we can always extend.
+ auto superVectorType = opStmt->getResult(0)->getType().cast<VectorType>();
+ auto multiplicity = shapeRatio(superVectorType, subVectorType);
+ assert(multiplicity.hasValue() && "Expected multiplicity");
+ outs() << "\nmatched: " << *opStmt << " with multiplicity: ";
+ interleaveComma(MutableArrayRef<unsigned>(*multiplicity), outs());
+ }
+}
+
+PassResult VectorizerTestPass::runOnMLFunction(MLFunction *f) {
+ if (!clTestVectorMultiplicity.empty()) {
+ testVectorMultiplicity(f);
+ }
+
+ return PassResult::Success;
+}
+
+FunctionPass *mlir::createVectorizerTestPass() {
+ return new VectorizerTestPass();
+}
+
+static PassRegistration<VectorizerTestPass>
+ pass("vectorizer-test", "Tests vectorizer standalone functionality.");
+
+#undef DEBUG_TYPE
diff --git a/mlir/lib/Transforms/Vectorize.cpp b/mlir/lib/Transforms/Vectorize.cpp
index d72353e9a87..7763c2d9471 100644
--- a/mlir/lib/Transforms/Vectorize.cpp
+++ b/mlir/lib/Transforms/Vectorize.cpp
@@ -22,6 +22,7 @@
#include "mlir/Analysis/LoopAnalysis.h"
#include "mlir/Analysis/MLFunctionMatcher.h"
+#include "mlir/Analysis/VectorAnalysis.h"
#include "mlir/IR/AffineExpr.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
diff --git a/mlir/test/Transforms/vector_utils.mlir b/mlir/test/Transforms/vector_utils.mlir
new file mode 100644
index 00000000000..08810935334
--- /dev/null
+++ b/mlir/test/Transforms/vector_utils.mlir
@@ -0,0 +1,37 @@
+// RUN: mlir-opt %s -vectorizer-test -vector-multiplicity 4 -vector-multiplicity 8 | FileCheck %s
+// RUN: mlir-opt %s -vectorizer-test -vector-multiplicity 2 -vector-multiplicity 5 -vector-multiplicity 2 | FileCheck %s -check-prefix=TEST-3x4x5x8
+
+mlfunc @vector_add_2d(%arg0 : index, %arg1 : index) -> f32 {
+ // Nothing should be matched in this first block.
+ // CHECK-NOT:matched: {{.*}} = alloc{{.*}}
+ // CHECK-NOT:matched: {{.*}} = constant 0{{.*}}
+ // CHECK-NOT:matched: {{.*}} = constant 1{{.*}}
+ %0 = alloc(%arg0, %arg1) : memref<?x?xf32>
+ %1 = alloc(%arg0, %arg1) : memref<?x?xf32>
+ %2 = alloc(%arg0, %arg1) : memref<?x?xf32>
+ %c0 = constant 0 : index
+ %cst = constant 1.000000e+00 : f32
+
+ // CHECK:matched: {{.*}} constant splat{{.*}} with multiplicity: 2, 32
+ %cst_1 = constant splat<vector<8x256xf32>, 1.000000e+00> : vector<8x256xf32>
+ // CHECK:matched: {{.*}} constant splat{{.*}} with multiplicity: 1, 3, 7, 2, 1
+ %cst_a = constant splat<vector<1x3x7x8x8xf32>, 1.000000e+00> : vector<1x3x7x8x8xf32>
+ // CHECK-NOT:matched: {{.*}} constant splat{{.*}} with multiplicity: 1, 3, 7, 1{{.*}}
+ %cst_b = constant splat<vector<1x3x7x4x4xf32>, 1.000000e+00> : vector<1x3x7x8x8xf32>
+ // TEST-3x4x5x8:matched: {{.*}} constant splat{{.*}} with multiplicity: 3, 2, 1, 4
+ %cst_c = constant splat<vector<3x4x5x8xf32>, 1.000000e+00> : vector<3x4x5x8xf32>
+ // TEST-3x4x4x8-NOT:matched: {{.*}} constant splat{{.*}} with multiplicity{{.*}}
+ %cst_d = constant splat<vector<3x4x4x8xf32>, 1.000000e+00> : vector<3x4x4x8xf32>
+ // TEST-3x4x4x8:matched: {{.*}} constant splat{{.*}} with multiplicity: 1, 1, 2, 16
+ %cst_e = constant splat<vector<1x2x10x32xf32>, 1.000000e+00> : vector<1x2x10x32xf32>
+
+ // Nothing should be matched in this last block.
+ // CHECK-NOT:matched: {{.*}} = constant 7{{.*}}
+ // CHECK-NOT:matched: {{.*}} = constant 42{{.*}}
+ // CHECK-NOT:matched: {{.*}} = load{{.*}}
+ // CHECK-NOT:matched: return {{.*}}
+ %c7 = constant 7 : index
+ %c42 = constant 42 : index
+ %9 = load %2[%c7, %c42] : memref<?x?xf32>
+ return %9 : f32
+} \ No newline at end of file
OpenPOWER on IntegriCloud