diff --git a/mlir/include/mlir/Support/Tester.h b/mlir/include/mlir/Support/Tester.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Support/Tester.h @@ -0,0 +1,53 @@ +//===- 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 testing environment. It provides functionality to run +// the interestingness scripts on the MLIR test cases and keep track of the most +// reduced test case variant. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_MLIRREDUCE_TESTER_H +#define LLVM_TOOLS_MLIRREDUCE_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 defines the testing environment. 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, + const llvm::ArrayRef &testScriptArgs); + + /// Runs the interestingness testing script on a MLIR test case file + int run(StringRef testCase); + + /// Returns the most reduced MLIR test case module + ModuleOp getTestCase() const { return testCase; } + + /// Updates the most reduced MLIR test case module + void setTestCase(ModuleOp t) { testCase = std::move(t); } + +private: + StringRef testScript; + const llvm::ArrayRef &testScriptArgs; + ModuleOp testCase; +}; + +} // end namespace mlir + +#endif \ No newline at end of file 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.sh b/mlir/test/mlir-reduce/test.sh new file mode 100755 --- /dev/null +++ b/mlir/test/mlir-reduce/test.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +mlir-opt $1 | grep 'NO BUG' &> /dev/null + +if [ $? == 0 ]; then + exit 0 +else + exit 1 + #Interesting behavior +fi diff --git a/mlir/test/mlir-reduce/testcase.mlir b/mlir/test/mlir-reduce/testcase.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/mlir-reduce/testcase.mlir @@ -0,0 +1,14 @@ +// RUN: mlir-reduce %s -test %p/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 +} + +// ----- 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 + MLIRSupport + MLIRTransforms + MLIRTransformUtils + ) + +add_llvm_tool(mlir-reduce + mlir-reduce.cpp + Tester.cpp + ) + +target_link_libraries(mlir-reduce PRIVATE ${LIBS}) +llvm_update_compile_flags(mlir-reduce) + +mlir_check_all_link_libraries(mlir-reduce) diff --git a/mlir/tools/mlir-reduce/Tester.cpp b/mlir/tools/mlir-reduce/Tester.cpp new file mode 100644 --- /dev/null +++ b/mlir/tools/mlir-reduce/Tester.cpp @@ -0,0 +1,48 @@ +//===- 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 testing environment. It provides functionality to run +// the interestingness scripts on the MLIR test cases and keep track of the most +// reduced test case variant. +// +//===----------------------------------------------------------------------===// + +#include "mlir/Support/Tester.h" + +using namespace mlir; + +Tester::Tester(StringRef scriptName, + const llvm::ArrayRef &scriptArgs) + : testScript(scriptName), testScriptArgs(scriptArgs) {} + +/// Runs the interestingness testing script on a MLIR test case file +int Tester::run(StringRef testCase) { + + std::vector testerArgs; + testerArgs.push_back(testCase); + + for (const std::string &arg : testScriptArgs) + testerArgs.push_back(arg); + + testerArgs.push_back(testCase); + + std::string errMsg; + int result = llvm::sys::ExecuteAndWait( + testScript, testerArgs, /*Env=*/None, /*Redirects=*/None, + /*SecondsToWait=*/0, /*MemoryLimit=*/0, &errMsg); + + if (result < 0) { + llvm::Error ec = llvm::make_error( + "Error running interesting-ness test: " + errMsg, + llvm::inconvertibleErrorCode()); + llvm::errs() << toString(std::move(ec)); + exit(1); + } + + return result; +} 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,125 @@ +//===- 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/IR/Verifier.h" +#include "mlir/InitAllDialects.h" +#include "mlir/Parser.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.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" + +#include "mlir/Support/Tester.h" + +using namespace mlir; + +static llvm::cl::opt + inputFilename(llvm::cl::Positional, llvm::cl::Required, + llvm::cl::desc("Input MLIR test case")); + +static llvm::cl::opt + testFilename("test", llvm::cl::Required, + llvm::cl::desc("Oracle testing script")); + +static llvm::cl::list testArguments( + "test-args", llvm::cl::ZeroOrMore, + llvm::cl::desc("Arguments passed to the oracle testing script")); + +static llvm::cl::opt + outputFilename("o", llvm::cl::desc("Reduced test case"), + llvm::cl::init("-")); + +// Parse the input MLIR file +static LogicalResult loadMLIR(MLIRContext &context, OwningModuleRef &module, + StringRef inputFilename) { + if (!inputFilename.endswith(".mlir")) + return failure(); + + module = parseSourceFile(inputFilename, &context); + if (!module || failed(verify(*module))) + return failure(); + + return success(); +} + +// Parse the input MLIR file and apply pre-processing optimization passes +static LogicalResult loadAndProcessMLIR(MLIRContext &context, + OwningModuleRef &module, + StringRef inputFilename) { + if (failed(loadMLIR(context, module, inputFilename))) + return failure(); + + mlir::PassManager pm(&context); + mlir::OpPassManager &optPM = pm.nest(); + optPM.addPass(mlir::createCanonicalizerPass()); + + 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::errs() << errorMessage << "\n"; + exit(1); + } + + auto output = openOutputFile(outputFilename, &errorMessage); + if (!output) { + llvm::errs() << errorMessage << "\n"; + exit(1); + } + + mlir::MLIRContext context; + mlir::OwningModuleRef moduleRef; + context.allowUnregisteredDialects(true); + + if (failed(loadAndProcessMLIR(context, moduleRef, inputFilename))) { + llvm::errs() << "Initial test case can't be parsed and processed\n "; + exit(1); + } + + // Initialize test environment + Tester test(testFilename, testArguments); + test.setTestCase(moduleRef.get()); + + if (!test.run(inputFilename)) { + llvm::errs() + << "Initial test case does not exhibit the interesting behavior\n " + "No reduction to be performed\n"; + exit(1); + } + + moduleRef.get().print(output->os()); + output->keep(); + + return 0; +} \ No newline at end of file