diff --git a/mlir/include/mlir/Reducer/Tester.h b/mlir/include/mlir/Reducer/Tester.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Reducer/Tester.h @@ -0,0 +1,62 @@ +//===- Tester.h -------------------------------------------------*- 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 Tester class used in the MLIR Reduce tool. +// +// A Tester object is passed as an argument to the reduction passes and it is +// used to keep track of the state of the reduction throughout the multiple +// passes. The testScript and testScriptArgs specify the filename of the +// interestingness script and its respective arguments. The mostReduced module +// is used to keep track of the most reduced variant of the test case generated +// up to any given point in time. +// +// The run method allows the different reduction passes to test the interesting +// behavior of a generated variant. If a generated variant is found to be +// successful and shorter than the mostReduced module, the mostReduced module +// must be updated with the new variant. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_REDUCER_TESTER_H +#define MLIR_REDUCER_TESTER_H + +#include + +#include "mlir/IR/Module.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Program.h" + +namespace mlir { + +/// This class is used to keep track of the state of the reduction. It contains +/// a method to run the interestingness testing script on MLIR test case files +/// and provides functionality to track the most reduced test case. +class Tester { +public: + Tester(StringRef testScript, ArrayRef testScriptArgs); + + /// Runs the interestingness testing script on a MLIR test case file. + bool isInteresting(StringRef testCase); + + /// Returns the most reduced MLIR test case module. + ModuleOp getMostReduced() const { return mostReduced; } + + /// Updates the most reduced MLIR test case module. + void setMostReduced(ModuleOp t) { mostReduced = t; } + +private: + StringRef testScript; + ArrayRef testScriptArgs; + ModuleOp mostReduced; +}; + +} // end namespace mlir + +#endif \ No newline at end of file diff --git a/mlir/lib/CMakeLists.txt b/mlir/lib/CMakeLists.txt --- a/mlir/lib/CMakeLists.txt +++ b/mlir/lib/CMakeLists.txt @@ -10,8 +10,9 @@ add_subdirectory(Interfaces) add_subdirectory(Parser) add_subdirectory(Pass) +add_subdirectory(Reducer) add_subdirectory(Support) add_subdirectory(TableGen) add_subdirectory(Target) add_subdirectory(Transforms) -add_subdirectory(Translation) +add_subdirectory(Translation) \ No newline at end of file diff --git a/mlir/lib/Reducer/CMakeLists.txt b/mlir/lib/Reducer/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/lib/Reducer/CMakeLists.txt @@ -0,0 +1,7 @@ +llvm_add_library(MLIRReduce + Tester.cpp +) + +mlir_check_all_link_libraries(MLIRReduce) + +add_mlir_library_install(MLIRReduce) \ No newline at end of file diff --git a/mlir/lib/Reducer/Tester.cpp b/mlir/lib/Reducer/Tester.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/Reducer/Tester.cpp @@ -0,0 +1,56 @@ +//===- Tester.cpp ---------------------------------------------------------===// +// +// 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 Tester class used in the MLIR Reduce tool. +// +// A Tester object is passed as an argument to the reduction passes and it is +// used to keep track of the state of the reduction throughout the multiple +// passes. The testScript and testScriptArgs specify the filename of the +// interestingness script and its respective arguments. The mostReduced module +// is used to keep track of the most reduced variant of the test case generated +// up to any given point in time. +// +// The run method allows the different reduction passes to test the interesting +// behavior of a generated variant. If a generated variant is found to be +// successful and shorter than the mostReduced module, the mostReduced module +// must be updated with the new variant. +// +//===----------------------------------------------------------------------===// + +#include "mlir/Reducer/Tester.h" + +using namespace mlir; + +Tester::Tester(StringRef scriptName, ArrayRef scriptArgs) + : testScript(scriptName), testScriptArgs(scriptArgs) {} + +/// 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 +bool Tester::isInteresting(StringRef testCase) { + + std::vector testerArgs; + testerArgs.push_back(testCase); + + for (const std::string &arg : testScriptArgs) + testerArgs.push_back(arg); + + std::string errMsg; + int result = llvm::sys::ExecuteAndWait( + testScript, testerArgs, /*Env=*/None, /*Redirects=*/None, + /*SecondsToWait=*/0, /*MemoryLimit=*/0, &errMsg); + + if (result < 0) + llvm::report_fatal_error("Error running interestingness test: " + errMsg, + false); + + if (!result) + return false; + + return true; +} diff --git a/mlir/test/CMakeLists.txt b/mlir/test/CMakeLists.txt --- a/mlir/test/CMakeLists.txt +++ b/mlir/test/CMakeLists.txt @@ -38,6 +38,7 @@ mlir-edsc-builder-api-test mlir-linalg-ods-gen mlir-opt + mlir-reduce mlir-sdbm-api-test mlir-tblgen mlir-translate diff --git a/mlir/test/mlir-reduce/test.bat b/mlir/test/mlir-reduce/test.bat new file mode 100644 --- /dev/null +++ b/mlir/test/mlir-reduce/test.bat @@ -0,0 +1,4 @@ +#Replicate bug + +#Interesting behavior +exit 1 \ No newline at end of file diff --git a/mlir/test/mlir-reduce/test.sh b/mlir/test/mlir-reduce/test.sh new file mode 100755 --- /dev/null +++ b/mlir/test/mlir-reduce/test.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +#Replicate bug + +#Interesting behavior +exit 1 \ No newline at end of file diff --git a/mlir/test/mlir-reduce/testcase-linux.mlir b/mlir/test/mlir-reduce/testcase-linux.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/mlir-reduce/testcase-linux.mlir @@ -0,0 +1,15 @@ +// REQUIRES: linux +// RUN: mlir-reduce %s -test %S/test.sh + +func @simple1(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) { + cond_br %arg0, ^bb1, ^bb2 +^bb1: + br ^bb3(%arg1 : memref<2xf32>) +^bb2: + %0 = alloc() : memref<2xf32> + br ^bb3(%0 : memref<2xf32>) +^bb3(%1: memref<2xf32>): + return +} + +// ----- \ No newline at end of file diff --git a/mlir/test/mlir-reduce/testcase-windows.mlir b/mlir/test/mlir-reduce/testcase-windows.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/mlir-reduce/testcase-windows.mlir @@ -0,0 +1,15 @@ +// REQUIRES: windows +// RUN: mlir-reduce %s -test %S/test.bat + +func @simple1(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) { + cond_br %arg0, ^bb1, ^bb2 +^bb1: + br ^bb3(%arg1 : memref<2xf32>) +^bb2: + %0 = alloc() : memref<2xf32> + br ^bb3(%0 : memref<2xf32>) +^bb3(%1: memref<2xf32>): + return +} + +// ----- \ No newline at end of file diff --git a/mlir/tools/CMakeLists.txt b/mlir/tools/CMakeLists.txt --- a/mlir/tools/CMakeLists.txt +++ b/mlir/tools/CMakeLists.txt @@ -2,7 +2,8 @@ add_subdirectory(mlir-cpu-runner) add_subdirectory(mlir-linalg-ods-gen) add_subdirectory(mlir-opt) +add_subdirectory(mlir-reduce) add_subdirectory(mlir-rocm-runner) -add_subdirectory(mlir-translate) -add_subdirectory(mlir-vulkan-runner) add_subdirectory(mlir-shlib) +add_subdirectory(mlir-translate) +add_subdirectory(mlir-vulkan-runner) \ No newline at end of file diff --git a/mlir/tools/mlir-reduce/CMakeLists.txt b/mlir/tools/mlir-reduce/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/mlir/tools/mlir-reduce/CMakeLists.txt @@ -0,0 +1,41 @@ +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS) +set(LLVM_LINK_COMPONENTS + AllTargetsAsmParsers + AllTargetsCodeGens + AllTargetsDescs + AllTargetsInfos + AsmParser + Core + IRReader + Support + Target + TransformUtils + ) + +set(LIBS + ${dialect_libs} + ${conversion_libs} + ${test_libs} + MLIRAnalysis + MLIRDialect + MLIREDSC + MLIRIR + MLIRLoopAnalysis + MLIROptLib + MLIRParser + MLIRPass + MLIRReduce + MLIRSupport + MLIRTransforms + MLIRTransformUtils + ) + +add_llvm_tool(mlir-reduce + mlir-reduce.cpp + ) + +target_link_libraries(mlir-reduce PRIVATE ${LIBS}) +llvm_update_compile_flags(mlir-reduce) + +mlir_check_all_link_libraries(mlir-reduce) \ No newline at end of file diff --git a/mlir/tools/mlir-reduce/mlir-reduce.cpp b/mlir/tools/mlir-reduce/mlir-reduce.cpp new file mode 100644 --- /dev/null +++ b/mlir/tools/mlir-reduce/mlir-reduce.cpp @@ -0,0 +1,97 @@ +//===- mlir-reduce.cpp - The MLIR reducer ---------------------------------===// +// +// 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 implements the general framework of the MLIR reducer tool. It +// parses the command line arguments, parses the initial MLIR test case and sets +// up the testing environment. It outputs the most reduced test case variant +// after executing the reduction passes. +// +//===----------------------------------------------------------------------===// + +#include + +#include "mlir/InitAllDialects.h" +#include "mlir/Parser.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Reducer/Tester.h" +#include "mlir/Support/FileUtilities.h" +#include "mlir/Support/LogicalResult.h" +#include "mlir/Transforms/Passes.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/ToolOutputFile.h" + +using namespace mlir; + +static llvm::cl::opt inputFilename(llvm::cl::Positional, + llvm::cl::Required, + llvm::cl::desc("")); + +static llvm::cl::opt + testFilename("test", llvm::cl::Required, llvm::cl::desc("Testing script")); + +static llvm::cl::list + testArguments("test-args", llvm::cl::ZeroOrMore, + llvm::cl::desc("Testing script arguments")); + +static llvm::cl::opt + outputFilename("o", + llvm::cl::desc("Output filename for the reduced test case"), + llvm::cl::init("-")); + +// Parse and verify the input MLIR file. +static LogicalResult loadModule(MLIRContext &context, OwningModuleRef &module, + StringRef inputFilename) { + module = parseSourceFile(inputFilename, &context); + if (!module) + return failure(); + + return success(); +} + +int main(int argc, char **argv) { + + llvm::InitLLVM y(argc, argv); + + registerAllDialects(); + registerMLIRContextCLOptions(); + registerPassManagerCLOptions(); + + llvm::cl::ParseCommandLineOptions(argc, argv, + "MLIR test case reduction tool.\n"); + + std::string errorMessage; + + auto testscript = openInputFile(testFilename, &errorMessage); + if (!testscript) + llvm::report_fatal_error(errorMessage); + + auto output = openOutputFile(outputFilename, &errorMessage); + if (!output) + llvm::report_fatal_error(errorMessage); + + mlir::MLIRContext context; + mlir::OwningModuleRef moduleRef; + context.allowUnregisteredDialects(true); + + if (failed(loadModule(context, moduleRef, inputFilename))) + llvm::report_fatal_error("Input test case can't be parsed"); + + // Initialize test environment. + Tester test(testFilename, testArguments); + test.setMostReduced(moduleRef.get()); + + if (!test.isInteresting(inputFilename)) + llvm::report_fatal_error( + "Input test case does not exhibit interesting behavior"); + + test.getMostReduced().print(output->os()); + output->keep(); + + return 0; +} \ No newline at end of file