blob: 33de538919d62b9afd0e95771f8dd091966f2dee [file] [log] [blame]
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -07001//===- mlir-opt.cpp - MLIR Optimizer Driver -------------------------------===//
2//
3// Copyright 2019 The MLIR Authors.
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16// =============================================================================
17//
18// This is a command line utility that parses an MLIR file, runs an optimization
19// pass, then prints the result back out. It is designed to support unit
20// testing.
21//
22//===----------------------------------------------------------------------===//
23
Chris Lattnerf7e22732018-06-22 22:03:48 -070024#include "mlir/IR/MLIRContext.h"
Chris Lattnere2259872018-06-21 15:22:42 -070025#include "mlir/IR/Module.h"
Chris Lattnere79379a2018-06-22 10:39:19 -070026#include "mlir/Parser.h"
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -070027#include "llvm/Support/CommandLine.h"
Chris Lattnere79379a2018-06-22 10:39:19 -070028#include "llvm/Support/SourceMgr.h"
Chris Lattnere2259872018-06-21 15:22:42 -070029#include "llvm/Support/FileUtilities.h"
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -070030#include "llvm/Support/InitLLVM.h"
Jacques Pienaarca4c4a02018-06-25 08:10:46 -070031#include "llvm/Support/Regex.h"
Chris Lattnere2259872018-06-21 15:22:42 -070032#include "llvm/Support/ToolOutputFile.h"
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -070033using namespace mlir;
Chris Lattnere2259872018-06-21 15:22:42 -070034using namespace llvm;
35
36static cl::opt<std::string>
37inputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));
38
39static cl::opt<std::string>
40outputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"),
41 cl::init("-"));
42
Jacques Pienaarbae40512018-06-24 09:10:36 -070043static cl::opt<bool>
44checkParserErrors("check-parser-errors", cl::desc("Check for parser errors"),
45 cl::init(false));
Chris Lattnere2259872018-06-21 15:22:42 -070046
47/// Open the specified output file and return it, exiting if there is any I/O or
48/// other errors.
49static std::unique_ptr<ToolOutputFile> getOutputStream() {
50 std::error_code error;
51 auto result = make_unique<ToolOutputFile>(outputFilename, error,
52 sys::fs::F_None);
53 if (error) {
54 llvm::errs() << error.message() << '\n';
55 exit(1);
56 }
57
58 return result;
59}
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -070060
Jacques Pienaarbae40512018-06-24 09:10:36 -070061/// Parses the memory buffer and, if successfully parsed, prints the parsed
62/// output. Returns whether parsing succeeded.
Jacques Pienaarca4c4a02018-06-25 08:10:46 -070063bool parseAndPrintMemoryBuffer(std::unique_ptr<MemoryBuffer> buffer,
64 const SMDiagnosticHandlerTy& errorReporter) {
Jacques Pienaarbae40512018-06-24 09:10:36 -070065 // Tell sourceMgr about this buffer, which is what the parser will pick up.
66 SourceMgr sourceMgr;
67 sourceMgr.AddNewSourceBuffer(std::move(buffer), SMLoc());
68
Jacques Pienaar9c411be2018-06-24 19:17:35 -070069 // Parse the input file.
Jacques Pienaarbae40512018-06-24 09:10:36 -070070 MLIRContext context;
Jacques Pienaar9c411be2018-06-24 19:17:35 -070071 std::unique_ptr<Module> module(
72 parseSourceFile(sourceMgr, &context, errorReporter));
Jacques Pienaarbae40512018-06-24 09:10:36 -070073 if (!module) return false;
74
75 // Print the output.
76 auto output = getOutputStream();
77 module->print(output->os());
78 output->keep();
79
80 // Success.
81 return true;
82}
83
84/// Split the memory buffer into multiple buffers using the marker -----.
85bool splitMemoryBufferForErrorChecking(std::unique_ptr<MemoryBuffer> buffer) {
86 const char marker[] = "-----";
87 SmallVector<StringRef, 2> sourceBuffers;
88 buffer->getBuffer().split(sourceBuffers, marker);
Jacques Pienaarbae40512018-06-24 09:10:36 -070089
Jacques Pienaarca4c4a02018-06-25 08:10:46 -070090 // Error reporter that verifies error reports matches expected error
91 // substring.
92 // TODO: Only checking for error cases below. Could be expanded to other kinds
93 // of diagnostics.
94 // TODO: Enable specifying errors on different lines (@-1).
95 // TODO: Currently only checking if substring matches, enable regex checking.
96 bool failed = false;
97 SMDiagnosticHandlerTy errorChecker = [&failed](llvm::SMDiagnostic err) {
98 StringRef line = err.getLineContents();
99 // Extract expected substring using regex and check simple containment in
100 // error message.
101 llvm::Regex expected("expected-error {{(.*)}}");
102 SmallVector<StringRef, 2> matches;
103 bool matched = expected.match(line, &matches);
104 if (matches.size() != 2) {
105 const auto& sourceMgr = *err.getSourceMgr();
106 sourceMgr.PrintMessage(err.getLoc(), SourceMgr::DK_Error,
107 "unexpected error: " + err.getMessage());
108 failed = true;
109 return;
110 }
111
112 matched = err.getMessage().contains(matches[1]);
113 if (!matched) {
114 llvm::errs() << "Expected error substring (" << matches[1]
115 << ") not found in error `" << err.getMessage() << "`.\n";
116 failed = true;
117 }
118 };
119
120 for (auto& subbuffer : sourceBuffers) {
121 int expectedCount = subbuffer.count("expected-error");
122 if (expectedCount > 1) {
123 llvm::errs() << "Unable to verify more than 1 error per group.\n";
124 failed = true;
125 continue;
126 }
127
128 bool parsed = parseAndPrintMemoryBuffer(
129 MemoryBuffer::getMemBufferCopy(subbuffer), errorChecker);
130 if (parsed && expectedCount != 0) {
131 llvm::Regex expected("expected-error {{(.*)}}");
132 SmallVector<StringRef, 2> matches;
133 expected.match(subbuffer, &matches);
134 llvm::errs() << "Expected an error (" << matches[1]
135 << ") but no error reported.\n";
136 failed = true;
137 }
138 }
139
140 return !failed;
Jacques Pienaarbae40512018-06-24 09:10:36 -0700141}
142
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700143int main(int argc, char **argv) {
Chris Lattnere2259872018-06-21 15:22:42 -0700144 InitLLVM x(argc, argv);
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700145
Chris Lattnere2259872018-06-21 15:22:42 -0700146 cl::ParseCommandLineOptions(argc, argv, "MLIR modular optimizer driver\n");
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700147
Chris Lattnere79379a2018-06-22 10:39:19 -0700148 // Set up the input file.
149 auto fileOrErr = MemoryBuffer::getFileOrSTDIN(inputFilename);
150 if (std::error_code error = fileOrErr.getError()) {
151 llvm::errs() << argv[0] << ": could not open input file '" << inputFilename
152 << "': " << error.message() << "\n";
153 return 1;
154 }
155
Jacques Pienaarbae40512018-06-24 09:10:36 -0700156 if (checkParserErrors)
157 return !splitMemoryBufferForErrorChecking(std::move(*fileOrErr));
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700158
159 // Error reporter that simply prints the errors reported.
160 SMDiagnosticHandlerTy errorReporter = [](llvm::SMDiagnostic err) {
161 const auto& sourceMgr = *err.getSourceMgr();
162 sourceMgr.PrintMessage(err.getLoc(), err.getKind(), err.getMessage());
163 };
164 return !parseAndPrintMemoryBuffer(std::move(*fileOrErr), errorReporter);
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700165}