[Mlir-commits] [mlir] a33b647 - Revert "Refactor the architecture of mlir-reduce"
Mehdi Amini
llvmlistbot at llvm.org
Wed Apr 14 12:26:17 PDT 2021
Author: Mehdi Amini
Date: 2021-04-14T19:25:43Z
New Revision: a33b6471001aa8381a152c9ff49442e79d6198f8
URL: https://github.com/llvm/llvm-project/commit/a33b6471001aa8381a152c9ff49442e79d6198f8
DIFF: https://github.com/llvm/llvm-project/commit/a33b6471001aa8381a152c9ff49442e79d6198f8.diff
LOG: Revert "Refactor the architecture of mlir-reduce"
This reverts commit a32846b1d0147f30f6dde4bfec453cd681937005.
The build is broken with -DBUILD_SHARED_LIBS=ON:
tools/mlir/lib/Reducer/CMakeFiles/obj.MLIRReduce.dir/Tester.cpp.o: In function `mlir::Tester::isInteresting(mlir::ModuleOp) const':
Tester.cpp:(.text._ZNK4mlir6Tester13isInterestingENS_8ModuleOpE+0xa8): undefined reference to `mlir::OpPrintingFlags::OpPrintingFlags()'
Tester.cpp:(.text._ZNK4mlir6Tester13isInterestingENS_8ModuleOpE+0xc6): undefined reference to `mlir::Operation::print(llvm::raw_ostream&, mlir::OpPrintingFlags)'
Added:
mlir/include/mlir/Reducer/ReductionTreeUtils.h
mlir/tools/mlir-reduce/Passes/OpReducer.cpp
mlir/tools/mlir-reduce/ReductionTreeUtils.cpp
Modified:
mlir/include/mlir/Reducer/Passes/OpReducer.h
mlir/include/mlir/Reducer/ReductionNode.h
mlir/include/mlir/Reducer/ReductionTreePass.h
mlir/include/mlir/Reducer/Tester.h
mlir/lib/Reducer/Tester.cpp
mlir/tools/mlir-reduce/CMakeLists.txt
mlir/tools/mlir-reduce/OptReductionPass.cpp
mlir/tools/mlir-reduce/ReductionNode.cpp
mlir/tools/mlir-reduce/mlir-reduce.cpp
Removed:
mlir/tools/mlir-reduce/ReductionTreePass.cpp
################################################################################
diff --git a/mlir/include/mlir/Reducer/Passes/OpReducer.h b/mlir/include/mlir/Reducer/Passes/OpReducer.h
index 6e48d41187356..b16b5c0d763a9 100644
--- a/mlir/include/mlir/Reducer/Passes/OpReducer.h
+++ b/mlir/include/mlir/Reducer/Passes/OpReducer.h
@@ -15,52 +15,65 @@
#ifndef MLIR_REDUCER_PASSES_OPREDUCER_H
#define MLIR_REDUCER_PASSES_OPREDUCER_H
-#include <limits>
-
+#include "mlir/IR/Region.h"
#include "mlir/Reducer/ReductionNode.h"
+#include "mlir/Reducer/ReductionTreeUtils.h"
#include "mlir/Reducer/Tester.h"
namespace mlir {
-class OpReducer {
+class OpReducerImpl {
public:
- virtual ~OpReducer() = default;
- /// According to rangeToKeep, try to reduce the given module. We implicitly
- /// number each interesting operation and rangeToKeep indicates that if an
- /// operation's number falls into certain range, then we will not try to
- /// reduce that operation.
- virtual void reduce(ModuleOp module,
- ArrayRef<ReductionNode::Range> rangeToKeep) = 0;
- /// Return the number of certain kind of operations that we would like to
- /// reduce. This can be used to build a range map to exclude uninterested
- /// operations.
- virtual int getNumTargetOps(ModuleOp module) const = 0;
+ OpReducerImpl(
+ llvm::function_ref<std::vector<Operation *>(ModuleOp)> getSpecificOps);
+
+ /// Return the name of this reducer class.
+ StringRef getName();
+
+ /// Return the initial transformSpace containing the transformable indices.
+ std::vector<bool> initTransformSpace(ModuleOp module);
+
+ /// Generate variants by removing OpType operations from the module in the
+ /// parent and link the variants as childs in the Reduction Tree Pass.
+ void generateVariants(ReductionNode *parent, const Tester &test,
+ int numVariants);
+
+ /// Generate variants by removing OpType operations from the module in the
+ /// parent and link the variants as childs in the Reduction Tree Pass. The
+ /// transform argument defines the function used to remove the OpTpye
+ /// operations in range of indexed OpType operations.
+ void generateVariants(ReductionNode *parent, const Tester &test,
+ int numVariants,
+ llvm::function_ref<void(ModuleOp, int, int)> transform);
+
+private:
+ llvm::function_ref<std::vector<Operation *>(ModuleOp)> getSpecificOps;
};
-/// Reducer is a helper class to remove potential uninteresting operations from
-/// module.
+/// The OpReducer class defines a variant generator method that produces
+/// multiple variants by eliminating
diff erent OpType operations from the
+/// parent module.
template <typename OpType>
-class Reducer : public OpReducer {
+class OpReducer {
public:
- ~Reducer() override = default;
+ OpReducer() : impl(new OpReducerImpl(getSpecificOps)) {}
- int getNumTargetOps(ModuleOp module) const override {
- return std::distance(module.getOps<OpType>().begin(),
- module.getOps<OpType>().end());
+ /// Returns the vector of pointer to the OpType operations in the module.
+ static std::vector<Operation *> getSpecificOps(ModuleOp module) {
+ std::vector<Operation *> ops;
+ for (auto op : module.getOps<OpType>()) {
+ ops.push_back(op);
+ }
+ return ops;
}
- void reduce(ModuleOp module,
- ArrayRef<ReductionNode::Range> rangeToKeep) override {
+ /// Deletes the OpType operations in the module in the specified index.
+ static void deleteOps(ModuleOp module, int start, int end) {
std::vector<Operation *> opsToRemove;
- size_t keepIndex = 0;
- for (auto op : enumerate(module.getOps<OpType>())) {
+ for (auto op : enumerate(getSpecificOps(module))) {
int index = op.index();
- if (keepIndex < rangeToKeep.size() &&
- index == rangeToKeep[keepIndex].second)
- ++keepIndex;
- if (keepIndex == rangeToKeep.size() ||
- index < rangeToKeep[keepIndex].first)
+ if (index >= start && index < end)
opsToRemove.push_back(op.value());
}
@@ -69,6 +82,24 @@ class Reducer : public OpReducer {
o->erase();
}
}
+
+ /// Return the name of this reducer class.
+ StringRef getName() { return impl->getName(); }
+
+ /// Return the initial transformSpace containing the transformable indices.
+ std::vector<bool> initTransformSpace(ModuleOp module) {
+ return impl->initTransformSpace(module);
+ }
+
+ /// Generate variants by removing OpType operations from the module in the
+ /// parent and link the variants as childs in the Reduction Tree Pass.
+ void generateVariants(ReductionNode *parent, const Tester &test,
+ int numVariants) {
+ impl->generateVariants(parent, test, numVariants, deleteOps);
+ }
+
+private:
+ std::unique_ptr<OpReducerImpl> impl;
};
} // end namespace mlir
diff --git a/mlir/include/mlir/Reducer/ReductionNode.h b/mlir/include/mlir/Reducer/ReductionNode.h
index 6364157b9040b..79758b0330545 100644
--- a/mlir/include/mlir/Reducer/ReductionNode.h
+++ b/mlir/include/mlir/Reducer/ReductionNode.h
@@ -17,129 +17,82 @@
#ifndef MLIR_REDUCER_REDUCTIONNODE_H
#define MLIR_REDUCER_REDUCTIONNODE_H
-#include <queue>
#include <vector>
#include "mlir/Reducer/Tester.h"
-#include "llvm/Support/Allocator.h"
#include "llvm/Support/ToolOutputFile.h"
namespace mlir {
-/// Defines the traversal method options to be used in the reduction tree
-/// traversal.
-enum TraversalMode { SinglePath, Backtrack, MultiPath };
-
-/// This class defines the ReductionNode which is used to generate variant and
-/// keep track of the necessary metadata for the reduction pass. The nodes are
-/// linked together in a reduction tree structure which defines the relationship
-/// between all the
diff erent generated variants.
+/// This class defines the ReductionNode which is used to wrap the module of
+/// a generated variant and keep track of the necessary metadata for the
+/// reduction pass. The nodes are linked together in a reduction tree structure
+/// which defines the relationship between all the
diff erent generated variants.
class ReductionNode {
public:
- template <TraversalMode mode>
- class iterator;
+ ReductionNode(ModuleOp module, ReductionNode *parent);
+
+ ReductionNode(ModuleOp module, ReductionNode *parent,
+ std::vector<bool> transformSpace);
- using Range = std::pair<int, int>;
+ /// Calculates and initializes the size and interesting values of the node.
+ void measureAndTest(const Tester &test);
- ReductionNode(ReductionNode *parent, std::vector<Range> range,
- llvm::SpecificBumpPtrAllocator<ReductionNode> &allocator);
+ /// Returns the module.
+ ModuleOp getModule() const { return module; }
- ReductionNode *getParent() const;
+ /// Returns true if the size and interestingness have been calculated.
+ bool isEvaluated() const;
- size_t getSize() const;
+ /// Returns the size in bytes of the module.
+ int getSize() const;
/// Returns true if the module exhibits the interesting behavior.
- Tester::Interestingness isInteresting() const;
+ bool isInteresting() const;
+
+ /// Returns the pointer to a child variant by index.
+ ReductionNode *getVariant(unsigned long index) const;
- std::vector<Range> getRanges() const;
+ /// Returns the number of child variants.
+ int variantsSize() const;
- std::vector<ReductionNode *> &getVariants();
+ /// Returns true if the vector containing the child variants is empty.
+ bool variantsEmpty() const;
- /// Split the ranges and generate new variants.
- std::vector<ReductionNode *> generateNewVariants();
+ /// Sort the child variants and remove the uninteresting ones.
+ void organizeVariants(const Tester &test);
- /// Update the interestingness result from tester.
- void update(std::pair<Tester::Interestingness, size_t> result);
+ /// Returns the number of child variants.
+ int transformSpaceSize();
+
+ /// Returns a vector indicating the transformed indices as true.
+ const std::vector<bool> getTransformSpace();
private:
- /// A custom BFS iterator. The
diff erence between
- /// llvm/ADT/BreadthFirstIterator.h is the graph we're exploring is dynamic.
- /// We may explore more neighbors at certain node if we didn't find interested
- /// event. As a result, we defer pushing adjacent nodes until poping the last
- /// visited node. The graph exploration strategy will be put in
- /// getNeighbors().
- ///
- /// Subclass BaseIterator and implement traversal strategy in getNeighbors().
- template <typename T>
- class BaseIterator {
- public:
- BaseIterator(ReductionNode *node) { visitQueue.push(node); }
- BaseIterator(const BaseIterator &) = default;
- BaseIterator() = default;
-
- static BaseIterator end() { return BaseIterator(); }
-
- bool operator==(const BaseIterator &i) {
- return visitQueue == i.visitQueue;
- }
- bool operator!=(const BaseIterator &i) { return !(*this == i); }
-
- BaseIterator &operator++() {
- ReductionNode *top = visitQueue.front();
- visitQueue.pop();
- std::vector<ReductionNode *> neighbors = getNeighbors(top);
- for (ReductionNode *node : neighbors)
- visitQueue.push(node);
- return *this;
- }
-
- BaseIterator operator++(int) {
- BaseIterator tmp = *this;
- ++*this;
- return tmp;
- }
-
- ReductionNode &operator*() const { return *(visitQueue.front()); }
- ReductionNode *operator->() const { return visitQueue.front(); }
-
- protected:
- std::vector<ReductionNode *> getNeighbors(ReductionNode *node) {
- return static_cast<T *>(this)->getNeighbors(node);
- }
-
- private:
- std::queue<ReductionNode *> visitQueue;
- };
-
- /// The size of module after applying the range constraints.
- size_t size;
-
- /// This is true if the module has been evaluated and it exhibits the
- /// interesting behavior.
- Tester::Interestingness interesting;
-
- ReductionNode *parent;
-
- /// We will only keep the operation with index falls into the ranges.
- /// For example, number each function in a certain module and then we will
- /// remove the functions with index outside the ranges and see if the
- /// resulting module is still interesting.
- std::vector<Range> ranges;
-
- /// This points to the child variants that were created using this node as a
- /// starting point.
- std::vector<ReductionNode *> variants;
-
- llvm::SpecificBumpPtrAllocator<ReductionNode> &allocator;
-};
+ /// Link a child variant node.
+ void linkVariant(ReductionNode *newVariant);
+
+ // This is the MLIR module of this variant.
+ ModuleOp module;
+
+ // This is true if the module has been evaluated and it exhibits the
+ // interesting behavior.
+ bool interesting;
+
+ // This indicates the number of characters in the printed module if the module
+ // has been evaluated.
+ int size;
+
+ // This indicates if the module has been evaluated (measured and tested).
+ bool evaluated;
+
+ // Indicates the indices in the node that have been transformed in previous
+ // levels of the reduction tree.
+ std::vector<bool> transformSpace;
-// Specialized iterator for SinglePath traversal
-template <>
-class ReductionNode::iterator<SinglePath>
- : public BaseIterator<iterator<SinglePath>> {
- friend BaseIterator<iterator<SinglePath>>;
- using BaseIterator::BaseIterator;
- std::vector<ReductionNode *> getNeighbors(ReductionNode *node);
+ // This points to the child variants that were created using this node as a
+ // starting point.
+ std::vector<std::unique_ptr<ReductionNode>> variants;
};
} // end namespace mlir
diff --git a/mlir/include/mlir/Reducer/ReductionTreePass.h b/mlir/include/mlir/Reducer/ReductionTreePass.h
index 8f1269e4ae29b..be13191dbd384 100644
--- a/mlir/include/mlir/Reducer/ReductionTreePass.h
+++ b/mlir/include/mlir/Reducer/ReductionTreePass.h
@@ -22,40 +22,131 @@
#include "PassDetail.h"
#include "ReductionNode.h"
#include "mlir/Reducer/Passes/OpReducer.h"
+#include "mlir/Reducer/ReductionTreeUtils.h"
#include "mlir/Reducer/Tester.h"
#define DEBUG_TYPE "mlir-reduce"
namespace mlir {
+// Defines the traversal method options to be used in the reduction tree
+/// traversal.
+enum TraversalMode { SinglePath, Backtrack, MultiPath };
+
/// This class defines the Reduction Tree Pass. It provides a framework to
/// to implement a reduction pass using a tree structure to keep track of the
/// generated reduced variants.
-class ReductionTreePass : public ReductionTreeBase<ReductionTreePass> {
+template <typename Reducer, TraversalMode mode>
+class ReductionTreePass
+ : public ReductionTreeBase<ReductionTreePass<Reducer, mode>> {
public:
ReductionTreePass(const ReductionTreePass &pass)
- : ReductionTreeBase<ReductionTreePass>(pass), opType(pass.opType),
- mode(pass.mode), test(pass.test) {}
+ : ReductionTreeBase<ReductionTreePass<Reducer, mode>>(pass),
+ root(new ReductionNode(pass.root->getModule().clone(), nullptr)),
+ test(pass.test) {}
- ReductionTreePass(StringRef opType, TraversalMode mode, const Tester &test)
- : opType(opType), mode(mode), test(test) {}
+ ReductionTreePass(const Tester &test) : test(test) {}
/// Runs the pass instance in the pass pipeline.
- void runOnOperation() override;
+ void runOnOperation() override {
+ ModuleOp module = this->getOperation();
+ Reducer reducer;
+ std::vector<bool> transformSpace = reducer.initTransformSpace(module);
+ ReductionNode *reduced;
-private:
- template <typename IteratorType>
- ModuleOp findOptimal(ModuleOp module, std::unique_ptr<OpReducer> reducer,
- ReductionNode *node);
+ this->root =
+ std::make_unique<ReductionNode>(module, nullptr, transformSpace);
+
+ root->measureAndTest(test);
- /// The name of operation that we will try to remove.
- StringRef opType;
+ LLVM_DEBUG(llvm::dbgs() << "\nReduction Tree Pass: " << reducer.getName(););
+ switch (mode) {
+ case SinglePath:
+ LLVM_DEBUG(llvm::dbgs() << " (Single Path)\n";);
+ reduced = singlePathTraversal();
+ break;
+ default:
+ llvm::report_fatal_error("Traversal method not currently supported.");
+ }
+
+ ReductionTreeUtils::updateGoldenModule(module,
+ reduced->getModule().clone());
+ }
+
+private:
+ // Points to the root node in this reduction tree.
+ std::unique_ptr<ReductionNode> root;
- TraversalMode mode;
+ // This object defines the variant generation at each level of the reduction
+ // tree.
+ Reducer reducer;
- /// This is used to test the interesting behavior of the reduction nodes in
- /// the tree.
+ // This is used to test the interesting behavior of the reduction nodes in the
+ // tree.
const Tester &test;
+
+ /// Traverse the most reduced path in the reduction tree by generating the
+ /// variants at each level using the Reducer parameter's generateVariants
+ /// function. Stops when no new successful variants can be created at the
+ /// current level.
+ ReductionNode *singlePathTraversal() {
+ ReductionNode *currNode = root.get();
+ ReductionNode *smallestNode = currNode;
+ int tSpaceSize = currNode->transformSpaceSize();
+ std::vector<int> path;
+
+ ReductionTreeUtils::updateSmallestNode(currNode, smallestNode, path);
+
+ LLVM_DEBUG(llvm::dbgs() << "\nGenerating 1 variant: applying the ");
+ LLVM_DEBUG(llvm::dbgs() << "transformation to the entire module\n");
+
+ reducer.generateVariants(currNode, test, 1);
+ LLVM_DEBUG(llvm::dbgs() << "Testing\n");
+ currNode->organizeVariants(test);
+
+ if (!currNode->variantsEmpty())
+ return currNode->getVariant(0);
+
+ while (tSpaceSize != 1) {
+ ReductionTreeUtils::updateSmallestNode(currNode, smallestNode, path);
+
+ LLVM_DEBUG(llvm::dbgs() << "\nGenerating 2 variants: applying the ");
+ LLVM_DEBUG(llvm::dbgs() << "transformation to two
diff erent sections ");
+ LLVM_DEBUG(llvm::dbgs() << "of transformable indices\n");
+
+ reducer.generateVariants(currNode, test, 2);
+ LLVM_DEBUG(llvm::dbgs() << "Testing\n");
+ currNode->organizeVariants(test);
+
+ if (currNode->variantsEmpty())
+ break;
+
+ currNode = currNode->getVariant(0);
+ tSpaceSize = currNode->transformSpaceSize();
+ path.push_back(0);
+ }
+
+ if (tSpaceSize == 1) {
+ ReductionTreeUtils::updateSmallestNode(currNode, smallestNode, path);
+
+ LLVM_DEBUG(llvm::dbgs() << "\nGenerating 1 variants: applying the ");
+ LLVM_DEBUG(llvm::dbgs() << "transformation to the only transformable");
+ LLVM_DEBUG(llvm::dbgs() << "index\n");
+
+ reducer.generateVariants(currNode, test, 1);
+ LLVM_DEBUG(llvm::dbgs() << "Testing\n");
+ currNode->organizeVariants(test);
+
+ if (!currNode->variantsEmpty()) {
+ currNode = currNode->getVariant(0);
+ path.push_back(0);
+
+ ReductionTreeUtils::updateSmallestNode(currNode, smallestNode, path);
+ }
+ }
+
+ return currNode;
+ }
};
} // end namespace mlir
diff --git a/mlir/include/mlir/Reducer/ReductionTreeUtils.h b/mlir/include/mlir/Reducer/ReductionTreeUtils.h
new file mode 100644
index 0000000000000..cb938e2e4765a
--- /dev/null
+++ b/mlir/include/mlir/Reducer/ReductionTreeUtils.h
@@ -0,0 +1,53 @@
+//===- ReductionTreeUtils.h - Reduction Tree utilities ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the Reduction Tree Utilities. It defines pass independent
+// methods that help in the reduction passes of the MLIR Reduce tool.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_REDUCER_REDUCTIONTREEUTILS_H
+#define MLIR_REDUCER_REDUCTIONTREEUTILS_H
+
+#include <tuple>
+
+#include "PassDetail.h"
+#include "ReductionNode.h"
+#include "mlir/Reducer/Tester.h"
+#include "llvm/Support/Debug.h"
+
+namespace mlir {
+
+// Defines the utilities for the implementation of custom reduction
+// passes using the ReductionTreePass framework.
+namespace ReductionTreeUtils {
+
+/// Update the golden module's content with that of the reduced module.
+void updateGoldenModule(ModuleOp &golden, ModuleOp reduced);
+
+/// Update the smallest node traversed so far in the reduction tree and
+/// print the debugging information for the currNode being traversed.
+void updateSmallestNode(ReductionNode *currNode, ReductionNode *&smallestNode,
+ std::vector<int> path);
+
+/// Create a transform space index vector based on the specified number of
+/// indices.
+std::vector<bool> createTransformSpace(ModuleOp module, int numIndices);
+
+/// Create the specified number of variants by applying the transform method
+/// to
diff erent ranges of indices in the parent module. The isDeletion boolean
+/// specifies if the transformation is the deletion of indices.
+void createVariants(ReductionNode *parent, const Tester &test, int numVariants,
+ llvm::function_ref<void(ModuleOp, int, int)> transform,
+ bool isDeletion);
+
+} // namespace ReductionTreeUtils
+
+} // end namespace mlir
+
+#endif
diff --git a/mlir/include/mlir/Reducer/Tester.h b/mlir/include/mlir/Reducer/Tester.h
index 5969d63eaee2c..b54bb94e11b95 100644
--- a/mlir/include/mlir/Reducer/Tester.h
+++ b/mlir/include/mlir/Reducer/Tester.h
@@ -32,21 +32,12 @@ namespace mlir {
/// case file.
class Tester {
public:
- enum class Interestingness {
- True,
- False,
- Untested,
- };
-
Tester(StringRef testScript, ArrayRef<std::string> testScriptArgs);
/// Runs the interestingness testing script on a MLIR test case file. Returns
/// true if the interesting behavior is present in the test case or false
/// otherwise.
- std::pair<Interestingness, size_t> isInteresting(ModuleOp module) const;
-
- /// Return whether the file in the given path is interesting.
- Interestingness isInteresting(StringRef testCase) const;
+ bool isInteresting(StringRef testCase) const;
private:
StringRef testScript;
diff --git a/mlir/lib/Reducer/Tester.cpp b/mlir/lib/Reducer/Tester.cpp
index c0d4862481016..3ca0e93935865 100644
--- a/mlir/lib/Reducer/Tester.cpp
+++ b/mlir/lib/Reducer/Tester.cpp
@@ -16,40 +16,15 @@
#include "mlir/Reducer/Tester.h"
-#include "llvm/Support/ToolOutputFile.h"
-
using namespace mlir;
Tester::Tester(StringRef scriptName, ArrayRef<std::string> scriptArgs)
: testScript(scriptName), testScriptArgs(scriptArgs) {}
-std::pair<Tester::Interestingness, size_t>
-Tester::isInteresting(ModuleOp module) const {
- SmallString<128> filepath;
- int fd;
-
- // Print module to temporary file.
- std::error_code ec =
- llvm::sys::fs::createTemporaryFile("mlir-reduce", "mlir", fd, filepath);
-
- if (ec)
- llvm::report_fatal_error("Error making unique filename: " + ec.message());
-
- llvm::ToolOutputFile out(filepath, fd);
- module.print(out.os());
- out.os().close();
-
- if (out.os().has_error())
- llvm::report_fatal_error("Error emitting the IR to file '" + filepath);
-
- size_t size = out.os().tell();
- return std::make_pair(isInteresting(filepath), size);
-}
-
/// Runs the interestingness testing script on a MLIR test case file. Returns
/// true if the interesting behavior is present in the test case or false
/// otherwise.
-Tester::Interestingness Tester::isInteresting(StringRef testCase) const {
+bool Tester::isInteresting(StringRef testCase) const {
std::vector<StringRef> testerArgs;
testerArgs.push_back(testCase);
@@ -69,7 +44,7 @@ Tester::Interestingness Tester::isInteresting(StringRef testCase) const {
false);
if (!result)
- return Interestingness::False;
+ return false;
- return Interestingness::True;
+ return true;
}
diff --git a/mlir/tools/mlir-reduce/CMakeLists.txt b/mlir/tools/mlir-reduce/CMakeLists.txt
index 162306e1a72b4..958c2c94cc684 100644
--- a/mlir/tools/mlir-reduce/CMakeLists.txt
+++ b/mlir/tools/mlir-reduce/CMakeLists.txt
@@ -45,8 +45,9 @@ set(LIBS
add_llvm_tool(mlir-reduce
OptReductionPass.cpp
+ Passes/OpReducer.cpp
ReductionNode.cpp
- ReductionTreePass.cpp
+ ReductionTreeUtils.cpp
mlir-reduce.cpp
ADDITIONAL_HEADER_DIRS
diff --git a/mlir/tools/mlir-reduce/OptReductionPass.cpp b/mlir/tools/mlir-reduce/OptReductionPass.cpp
index 97b9b3e3aa372..2ad55e948618d 100644
--- a/mlir/tools/mlir-reduce/OptReductionPass.cpp
+++ b/mlir/tools/mlir-reduce/OptReductionPass.cpp
@@ -36,25 +36,21 @@ void OptReductionPass::runOnOperation() {
PassManager pmTransform(context);
pmTransform.addPass(std::move(optPass));
- std::pair<Tester::Interestingness, int> original = test.isInteresting(module);
-
if (failed(pmTransform.run(moduleVariant)))
return;
- std::pair<Tester::Interestingness, int> reduced =
- test.isInteresting(moduleVariant);
+ ReductionNode original(module, nullptr);
+ original.measureAndTest(test);
+
+ ReductionNode reduced(moduleVariant, nullptr);
+ reduced.measureAndTest(test);
- if (reduced.first == Tester::Interestingness::True &&
- reduced.second < original.second) {
- module.getBody()->clear();
- module.getBody()->getOperations().splice(
- module.getBody()->begin(), moduleVariant.getBody()->getOperations());
+ if (reduced.isInteresting() && reduced.getSize() < original.getSize()) {
+ ReductionTreeUtils::updateGoldenModule(module, reduced.getModule().clone());
LLVM_DEBUG(llvm::dbgs() << "\nSuccessful Transformed version\n\n");
} else {
LLVM_DEBUG(llvm::dbgs() << "\nUnsuccessful Transformed version\n\n");
}
- moduleVariant->destroy();
-
LLVM_DEBUG(llvm::dbgs() << "Pass Complete\n\n");
}
diff --git a/mlir/tools/mlir-reduce/Passes/OpReducer.cpp b/mlir/tools/mlir-reduce/Passes/OpReducer.cpp
new file mode 100644
index 0000000000000..8455b3831c421
--- /dev/null
+++ b/mlir/tools/mlir-reduce/Passes/OpReducer.cpp
@@ -0,0 +1,41 @@
+//===- OpReducer.cpp - Operation Reducer ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the OpReducer class. It defines a variant generator method
+// with the purpose of producing
diff erent variants by eliminating a
+// parameterizable type of operations from the parent module.
+//
+//===----------------------------------------------------------------------===//
+#include "mlir/Reducer/Passes/OpReducer.h"
+
+using namespace mlir;
+
+OpReducerImpl::OpReducerImpl(
+ llvm::function_ref<std::vector<Operation *>(ModuleOp)> getSpecificOps)
+ : getSpecificOps(getSpecificOps) {}
+
+/// Return the name of this reducer class.
+StringRef OpReducerImpl::getName() {
+ return StringRef("High Level Operation Reduction");
+}
+
+/// Return the initial transformSpace containing the transformable indices.
+std::vector<bool> OpReducerImpl::initTransformSpace(ModuleOp module) {
+ auto ops = getSpecificOps(module);
+ int numOps = std::distance(ops.begin(), ops.end());
+ return ReductionTreeUtils::createTransformSpace(module, numOps);
+}
+
+/// Generate variants by removing opType operations from the module in the
+/// parent and link the variants as childs in the Reduction Tree Pass.
+void OpReducerImpl::generateVariants(
+ ReductionNode *parent, const Tester &test, int numVariants,
+ llvm::function_ref<void(ModuleOp, int, int)> transform) {
+ ReductionTreeUtils::createVariants(parent, test, numVariants, transform,
+ true);
+}
diff --git a/mlir/tools/mlir-reduce/ReductionNode.cpp b/mlir/tools/mlir-reduce/ReductionNode.cpp
index a8e4af8c88223..bd4ef51786ec7 100644
--- a/mlir/tools/mlir-reduce/ReductionNode.cpp
+++ b/mlir/tools/mlir-reduce/ReductionNode.cpp
@@ -15,138 +15,116 @@
//===----------------------------------------------------------------------===//
#include "mlir/Reducer/ReductionNode.h"
-#include "llvm/ADT/STLExtras.h"
-
-#include <algorithm>
-#include <limits>
using namespace mlir;
-ReductionNode::ReductionNode(
- ReductionNode *parent, std::vector<Range> ranges,
- llvm::SpecificBumpPtrAllocator<ReductionNode> &allocator)
- : size(std::numeric_limits<size_t>::max()),
- interesting(Tester::Interestingness::Untested),
- /// Root node will have the parent pointer point to themselves.
- parent(parent == nullptr ? this : parent), ranges(ranges),
- allocator(allocator) {}
+/// Sets up the metadata and links the node to its parent.
+ReductionNode::ReductionNode(ModuleOp module, ReductionNode *parent)
+ : module(module), evaluated(false) {
-/// Returns the size in bytes of the module.
-size_t ReductionNode::getSize() const { return size; }
+ if (parent != nullptr)
+ parent->linkVariant(this);
+}
-ReductionNode *ReductionNode::getParent() const { return parent; }
+ReductionNode::ReductionNode(ModuleOp module, ReductionNode *parent,
+ std::vector<bool> transformSpace)
+ : module(module), evaluated(false), transformSpace(transformSpace) {
-/// Returns true if the module exhibits the interesting behavior.
-Tester::Interestingness ReductionNode::isInteresting() const {
- return interesting;
+ if (parent != nullptr)
+ parent->linkVariant(this);
}
-std::vector<ReductionNode::Range> ReductionNode::getRanges() const {
- return ranges;
+/// Calculates and updates the size and interesting values of the module.
+void ReductionNode::measureAndTest(const Tester &test) {
+ SmallString<128> filepath;
+ int fd;
+
+ // Print module to temporary file.
+ std::error_code ec =
+ llvm::sys::fs::createTemporaryFile("mlir-reduce", "mlir", fd, filepath);
+
+ if (ec)
+ llvm::report_fatal_error("Error making unique filename: " + ec.message());
+
+ llvm::ToolOutputFile out(filepath, fd);
+ module.print(out.os());
+ out.os().close();
+
+ if (out.os().has_error())
+ llvm::report_fatal_error("Error emitting bitcode to file '" + filepath);
+
+ size = out.os().tell();
+ interesting = test.isInteresting(filepath);
+ evaluated = true;
}
-std::vector<ReductionNode *> &ReductionNode::getVariants() { return variants; }
-
-#include <iostream>
-
-/// If we haven't explored any variants from this node, we will create N
-/// variants, N is the length of `ranges` if N > 1. Otherwise, we will split the
-/// max element in `ranges` and create 2 new variants for each call.
-std::vector<ReductionNode *> ReductionNode::generateNewVariants() {
- std::vector<ReductionNode *> newNodes;
-
- // If we haven't created new variant, then we can create varients by removing
- // each of them respectively. For example, given {{1, 3}, {4, 9}}, we can
- // produce variants with range {{1, 3}} and {{4, 9}}.
- if (variants.size() == 0 && ranges.size() != 1) {
- for (const Range &range : ranges) {
- std::vector<Range> subRanges = ranges;
- llvm::erase_value(subRanges, range);
- ReductionNode *newNode = allocator.Allocate();
- new (newNode) ReductionNode(this, subRanges, allocator);
- newNodes.push_back(newNode);
- variants.push_back(newNode);
- }
+/// Returns true if the size and interestingness have been calculated.
+bool ReductionNode::isEvaluated() const { return evaluated; }
- return newNodes;
- }
+/// Returns the size in bytes of the module.
+int ReductionNode::getSize() const { return size; }
- // At here, we have created the type of variants mentioned above. We would
- // like to split the max range into 2 to create 2 new variants. Continue on
- // the above example, we split the range {4, 9} into {4, 6}, {6, 9}, and
- // create two variants with range {{1, 3}, {4, 6}} and {{1, 3}, {6, 9}}. The
- // result ranges vector will be {{1, 3}, {4, 6}, {6, 9}}.
- auto maxElement = std::max_element(
- ranges.begin(), ranges.end(), [](const Range &lhs, const Range &rhs) {
- return (lhs.second - lhs.first) > (rhs.second - rhs.first);
- });
+/// Returns true if the module exhibits the interesting behavior.
+bool ReductionNode::isInteresting() const { return interesting; }
- // We can't split range with lenght 1, which means we can't produce new
- // variant.
- if (maxElement->second - maxElement->first == 1)
- return {};
-
- auto createNewNode = [this](const std::vector<Range> &ranges) {
- ReductionNode *newNode = allocator.Allocate();
- new (newNode) ReductionNode(this, ranges, allocator);
- return newNode;
- };
-
- Range maxRange = *maxElement;
- std::vector<Range> subRanges = ranges;
- auto subRangesIter = subRanges.begin() + (maxElement - ranges.begin());
- int half = (maxRange.first + maxRange.second) / 2;
- *subRangesIter = std::make_pair(maxRange.first, half);
- newNodes.push_back(createNewNode(subRanges));
- *subRangesIter = std::make_pair(half, maxRange.second);
- newNodes.push_back(createNewNode(subRanges));
-
- variants.insert(variants.end(), newNodes.begin(), newNodes.end());
- auto it = ranges.insert(maxElement, std::make_pair(half, maxRange.second));
- it = ranges.insert(it, std::make_pair(maxRange.first, half));
- // Remove the range that has been split.
- ranges.erase(it + 2);
-
- return newNodes;
+/// Returns the pointers to the child variants.
+ReductionNode *ReductionNode::getVariant(unsigned long index) const {
+ if (index < variants.size())
+ return variants[index].get();
+
+ return nullptr;
}
-void ReductionNode::update(std::pair<Tester::Interestingness, size_t> result) {
- std::tie(interesting, size) = result;
+/// Returns the number of child variants.
+int ReductionNode::variantsSize() const { return variants.size(); }
+
+/// Returns true if the child variants vector is empty.
+bool ReductionNode::variantsEmpty() const { return variants.empty(); }
+
+/// Link a child variant node.
+void ReductionNode::linkVariant(ReductionNode *newVariant) {
+ std::unique_ptr<ReductionNode> ptrVariant(newVariant);
+ variants.push_back(std::move(ptrVariant));
}
-std::vector<ReductionNode *>
-ReductionNode::iterator<SinglePath>::getNeighbors(ReductionNode *node) {
- // Single Path: Traverses the smallest successful variant at each level until
- // no new successful variants can be created at that level.
- llvm::ArrayRef<ReductionNode *> variantsFromParent =
- node->getParent()->getVariants();
-
- // The parent node created several variants and they may be waiting for
- // examing interestingness. In Single Path approach, we will select the
- // smallest variant to continue our exploration. Thus we should wait until the
- // last variant to be examed then do the following traversal decision.
- if (!llvm::all_of(variantsFromParent, [](ReductionNode *node) {
- return node->isInteresting() != Tester::Interestingness::Untested;
- })) {
- return {};
- }
+/// Sort the child variants and remove the uninteresting ones.
+void ReductionNode::organizeVariants(const Tester &test) {
+ // Ensure all variants are evaluated.
+ for (auto &var : variants)
+ if (!var->isEvaluated())
+ var->measureAndTest(test);
- ReductionNode *smallest = nullptr;
- for (ReductionNode *node : variantsFromParent) {
- if (node->isInteresting() != Tester::Interestingness::True)
- continue;
- if (smallest == nullptr || node->getSize() < smallest->getSize())
- smallest = node;
- }
+ // Sort variants by interestingness and size.
+ llvm::array_pod_sort(
+ variants.begin(), variants.end(), [](const auto *lhs, const auto *rhs) {
+ if (lhs->get()->isInteresting() && !rhs->get()->isInteresting())
+ return 0;
+
+ if (!lhs->get()->isInteresting() && rhs->get()->isInteresting())
+ return 1;
- if (smallest != nullptr) {
- // We got a smallest one, keep traversing from this node.
- node = smallest;
- } else {
- // None of these variants is interesting, let the parent node to generate
- // more variants.
- node = node->getParent();
+ return (lhs->get()->getSize(), rhs->get()->getSize());
+ });
+
+ int interestingCount = 0;
+ for (auto &var : variants) {
+ if (var->isInteresting()) {
+ ++interestingCount;
+ } else {
+ break;
+ }
}
- return node->generateNewVariants();
+ // Remove uninteresting variants.
+ variants.resize(interestingCount);
+}
+
+/// Returns the number of non transformed indices.
+int ReductionNode::transformSpaceSize() {
+ return std::count(transformSpace.begin(), transformSpace.end(), false);
+}
+
+/// Returns a vector of the transformable indices in the Module.
+const std::vector<bool> ReductionNode::getTransformSpace() {
+ return transformSpace;
}
diff --git a/mlir/tools/mlir-reduce/ReductionTreePass.cpp b/mlir/tools/mlir-reduce/ReductionTreePass.cpp
deleted file mode 100644
index 6dbf783d2e6ff..0000000000000
--- a/mlir/tools/mlir-reduce/ReductionTreePass.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-//===- ReductionTreePass.cpp - ReductionTreePass Implementation -----------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines the Reduction Tree Pass class. It provides a framework for
-// the implementation of
diff erent reduction passes in the MLIR Reduce tool. It
-// allows for custom specification of the variant generation behavior. It
-// implements methods that define the
diff erent possible traversals of the
-// reduction tree.
-//
-//===----------------------------------------------------------------------===//
-
-#include "mlir/Reducer/ReductionTreePass.h"
-
-#include "llvm/Support/Allocator.h"
-
-using namespace mlir;
-
-static std::unique_ptr<OpReducer> getOpReducer(llvm::StringRef opType) {
- if (opType == ModuleOp::getOperationName())
- return std::make_unique<Reducer<ModuleOp>>();
- else if (opType == FuncOp::getOperationName())
- return std::make_unique<Reducer<FuncOp>>();
- llvm_unreachable("Now only supports two built-in ops");
-}
-
-void ReductionTreePass::runOnOperation() {
- ModuleOp module = this->getOperation();
- std::unique_ptr<OpReducer> reducer = getOpReducer(opType);
- std::vector<std::pair<int, int>> ranges = {
- {0, reducer->getNumTargetOps(module)}};
-
- llvm::SpecificBumpPtrAllocator<ReductionNode> allocator;
-
- ReductionNode *root = allocator.Allocate();
- new (root) ReductionNode(nullptr, ranges, allocator);
-
- ModuleOp golden = module;
- switch (mode) {
- case TraversalMode::SinglePath:
- golden = findOptimal<ReductionNode::iterator<TraversalMode::SinglePath>>(
- module, std::move(reducer), root);
- break;
- default:
- llvm_unreachable("Unsupported mode");
- }
-
- if (golden != module) {
- module.getBody()->clear();
- module.getBody()->getOperations().splice(module.getBody()->begin(),
- golden.getBody()->getOperations());
- golden->destroy();
- }
-}
-
-template <typename IteratorType>
-ModuleOp ReductionTreePass::findOptimal(ModuleOp module,
- std::unique_ptr<OpReducer> reducer,
- ReductionNode *root) {
- std::pair<Tester::Interestingness, size_t> initStatus =
- test.isInteresting(module);
- root->update(initStatus);
-
- ReductionNode *smallestNode = root;
- ModuleOp golden = module;
-
- IteratorType iter(root);
-
- while (iter != IteratorType::end()) {
- ModuleOp cloneModule = module.clone();
-
- ReductionNode ¤tNode = *iter;
- reducer->reduce(cloneModule, currentNode.getRanges());
-
- std::pair<Tester::Interestingness, size_t> result =
- test.isInteresting(cloneModule);
- currentNode.update(result);
-
- if (result.first == Tester::Interestingness::True &&
- result.second < smallestNode->getSize()) {
- smallestNode = ¤tNode;
- golden = cloneModule;
- } else {
- cloneModule->destroy();
- }
-
- ++iter;
- }
-
- return golden;
-}
diff --git a/mlir/tools/mlir-reduce/ReductionTreeUtils.cpp b/mlir/tools/mlir-reduce/ReductionTreeUtils.cpp
new file mode 100644
index 0000000000000..820c19a4f6673
--- /dev/null
+++ b/mlir/tools/mlir-reduce/ReductionTreeUtils.cpp
@@ -0,0 +1,159 @@
+//===- ReductionTreeUtils.cpp - Reduction Tree Utilities ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the Reduction Tree Utilities. It defines pass independent
+// methods that help in a reduction pass of the MLIR Reduce tool.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Reducer/ReductionTreeUtils.h"
+
+#define DEBUG_TYPE "mlir-reduce"
+
+using namespace mlir;
+
+/// Update the golden module's content with that of the reduced module.
+void ReductionTreeUtils::updateGoldenModule(ModuleOp &golden,
+ ModuleOp reduced) {
+ golden.getBody()->clear();
+
+ golden.getBody()->getOperations().splice(golden.getBody()->begin(),
+ reduced.getBody()->getOperations());
+}
+
+/// Update the smallest node traversed so far in the reduction tree and
+/// print the debugging information for the currNode being traversed.
+void ReductionTreeUtils::updateSmallestNode(ReductionNode *currNode,
+ ReductionNode *&smallestNode,
+ std::vector<int> path) {
+ LLVM_DEBUG(llvm::dbgs() << "\nTree Path: root");
+ #ifndef NDEBUG
+ for (int nodeIndex : path)
+ LLVM_DEBUG(llvm::dbgs() << " -> " << nodeIndex);
+ #endif
+
+ LLVM_DEBUG(llvm::dbgs() << "\nSize (chars): " << currNode->getSize());
+ if (currNode->getSize() < smallestNode->getSize()) {
+ LLVM_DEBUG(llvm::dbgs() << " - new smallest node!");
+ smallestNode = currNode;
+ }
+}
+
+/// Create a transform space index vector based on the specified number of
+/// indices.
+std::vector<bool> ReductionTreeUtils::createTransformSpace(ModuleOp module,
+ int numIndices) {
+ std::vector<bool> transformSpace;
+ for (int i = 0; i < numIndices; ++i)
+ transformSpace.push_back(false);
+
+ return transformSpace;
+}
+
+/// Translate section start and end into a vector of ranges specifying the
+/// section in the non transformed indices in the transform space.
+static std::vector<std::tuple<int, int>> getRanges(std::vector<bool> tSpace,
+ int start, int end) {
+ std::vector<std::tuple<int, int>> ranges;
+ int rangeStart = 0;
+ int rangeEnd = 0;
+ bool inside = false;
+ int transformableCount = 0;
+
+ for (auto element : llvm::enumerate(tSpace)) {
+ int index = element.index();
+ bool value = element.value();
+
+ if (start <= transformableCount && transformableCount < end) {
+ if (!value && !inside) {
+ inside = true;
+ rangeStart = index;
+ }
+ if (value && inside) {
+ rangeEnd = index;
+ ranges.push_back(std::make_tuple(rangeStart, rangeEnd));
+ inside = false;
+ }
+ }
+
+ if (!value)
+ transformableCount++;
+
+ if (transformableCount == end && inside) {
+ ranges.push_back(std::make_tuple(rangeStart, index + 1));
+ inside = false;
+ break;
+ }
+ }
+
+ return ranges;
+}
+
+/// Create the specified number of variants by applying the transform method
+/// to
diff erent ranges of indices in the parent module. The isDeletion boolean
+/// specifies if the transformation is the deletion of indices.
+void ReductionTreeUtils::createVariants(
+ ReductionNode *parent, const Tester &test, int numVariants,
+ llvm::function_ref<void(ModuleOp, int, int)> transform, bool isDeletion) {
+ std::vector<bool> newTSpace;
+ ModuleOp module = parent->getModule();
+
+ std::vector<bool> parentTSpace = parent->getTransformSpace();
+ int indexCount = parent->transformSpaceSize();
+ std::vector<std::tuple<int, int>> ranges;
+
+ // No new variants can be created.
+ if (indexCount == 0)
+ return;
+
+ // Create a single variant by transforming the unique index.
+ if (indexCount == 1) {
+ ModuleOp variantModule = module.clone();
+ if (isDeletion) {
+ transform(variantModule, 0, 1);
+ } else {
+ ranges = getRanges(parentTSpace, 0, parentTSpace.size());
+ transform(variantModule, std::get<0>(ranges[0]), std::get<1>(ranges[0]));
+ }
+
+ new ReductionNode(variantModule, parent, newTSpace);
+
+ return;
+ }
+
+ // Create the specified number of variants.
+ for (int i = 0; i < numVariants; ++i) {
+ ModuleOp variantModule = module.clone();
+ newTSpace = parent->getTransformSpace();
+ int sectionSize = indexCount / numVariants;
+ int sectionStart = sectionSize * i;
+ int sectionEnd = sectionSize * (i + 1);
+
+ if (i == numVariants - 1)
+ sectionEnd = indexCount;
+
+ if (isDeletion)
+ transform(variantModule, sectionStart, sectionEnd);
+
+ ranges = getRanges(parentTSpace, sectionStart, sectionEnd);
+
+ for (auto range : ranges) {
+ int rangeStart = std::get<0>(range);
+ int rangeEnd = std::get<1>(range);
+
+ for (int x = rangeStart; x < rangeEnd; ++x)
+ newTSpace[x] = true;
+
+ if (!isDeletion)
+ transform(variantModule, rangeStart, rangeEnd);
+ }
+
+ // Create Reduction Node in the Reduction tree
+ new ReductionNode(variantModule, parent, newTSpace);
+ }
+}
diff --git a/mlir/tools/mlir-reduce/mlir-reduce.cpp b/mlir/tools/mlir-reduce/mlir-reduce.cpp
index 7df1dc155d38f..d995683bb30c2 100644
--- a/mlir/tools/mlir-reduce/mlir-reduce.cpp
+++ b/mlir/tools/mlir-reduce/mlir-reduce.cpp
@@ -103,7 +103,7 @@ int main(int argc, char **argv) {
// Initialize test environment.
const Tester test(testFilename, testArguments);
- if (test.isInteresting(inputFilename) != Tester::Interestingness::True)
+ if (!test.isInteresting(inputFilename))
llvm::report_fatal_error(
"Input test case does not exhibit interesting behavior");
@@ -118,10 +118,11 @@ int main(int argc, char **argv) {
} else if (passTestSpecifier == "function-reducer") {
- // Reduction tree pass with Reducer variant generation and single path
+ // Reduction tree pass with OpReducer variant generation and single path
// traversal.
- pm.addPass(std::make_unique<ReductionTreePass>(
- FuncOp::getOperationName(), TraversalMode::SinglePath, test));
+ pm.addPass(
+ std::make_unique<ReductionTreePass<OpReducer<FuncOp>, SinglePath>>(
+ test));
}
ModuleOp m = moduleRef.get().clone();
More information about the Mlir-commits
mailing list