blob: 4238bb8b8b143ab14d4a838dafff0275488bb970 [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;
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -070097 SourceMgr fileSourceMgr;
98 fileSourceMgr.AddNewSourceBuffer(std::move(buffer), SMLoc());
99
100 // Tracks offset of subbuffer into original buffer.
101 const char *fileOffset =
102 fileSourceMgr.getMemoryBuffer(fileSourceMgr.getMainFileID())
103 ->getBufferStart();
104
105 // Create error checker that uses the helper function to relate the reported
106 // error to the file being parsed.
107 SMDiagnosticHandlerTy checker = [&](SMDiagnostic err) {
108 const auto &sourceMgr = *err.getSourceMgr();
109 const char *bufferStart =
110 sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID())->getBufferStart();
111
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700112 StringRef line = err.getLineContents();
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700113 size_t offset = err.getLoc().getPointer() - bufferStart;
114 SMLoc loc = SMLoc::getFromPointer(fileOffset + offset);
115
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700116 // Extract expected substring using regex and check simple containment in
117 // error message.
118 llvm::Regex expected("expected-error {{(.*)}}");
119 SmallVector<StringRef, 2> matches;
120 bool matched = expected.match(line, &matches);
121 if (matches.size() != 2) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700122 fileSourceMgr.PrintMessage(
123 loc, SourceMgr::DK_Error,
124 "unexpected error: " + err.getMessage());
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700125 failed = true;
126 return;
127 }
128
129 matched = err.getMessage().contains(matches[1]);
130 if (!matched) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700131 const char checkPrefix[] = "expected-error {{";
132 loc = SMLoc::getFromPointer(fileOffset + offset + line.find(checkPrefix) -
133 err.getColumnNo() + strlen(checkPrefix));
134 fileSourceMgr.PrintMessage(loc, SourceMgr::DK_Error,
135 "\"" + err.getMessage() +
136 "\" did not contain expected substring \"" +
137 matches[1] + "\"");
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700138 failed = true;
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700139 return;
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700140 }
141 };
142
143 for (auto& subbuffer : sourceBuffers) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700144 SourceMgr sourceMgr;
145 // Tell sourceMgr about this buffer, which is what the parser will pick up.
146 sourceMgr.AddNewSourceBuffer(MemoryBuffer::getMemBufferCopy(subbuffer),
147 SMLoc());
148
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700149 int expectedCount = subbuffer.count("expected-error");
150 if (expectedCount > 1) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700151 size_t expectedOffset = subbuffer.find("expected-error");
152 expectedOffset = subbuffer.find("expected-error", expectedOffset);
153 SMLoc loc = SMLoc::getFromPointer(fileOffset + expectedOffset);
154 fileSourceMgr.PrintMessage(loc, SourceMgr::DK_Error,
155 "too many errors expected: unable to verify "
156 "more than one error per group");
157 fileOffset += subbuffer.size() + strlen(marker);
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700158 failed = true;
159 continue;
160 }
161
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700162 // Parse the input file.
163 MLIRContext context;
164 std::unique_ptr<Module> module(
165 parseSourceFile(sourceMgr, &context, checker));
166 bool parsed = module != nullptr;
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700167 if (parsed && expectedCount != 0) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700168 llvm::Regex expected(".*expected-error {{(.*)}}");
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700169 SmallVector<StringRef, 2> matches;
170 expected.match(subbuffer, &matches);
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700171
172 // Highlight expected-error clause of unexpectedly passing test case.
173 size_t expectedOffset = subbuffer.find("expected-error");
174 size_t endOffset = matches[0].size();
175 SMLoc loc = SMLoc::getFromPointer(fileOffset + expectedOffset);
176 SMRange range(loc, SMLoc::getFromPointer(fileOffset + endOffset));
177 fileSourceMgr.PrintMessage(
178 loc, SourceMgr::DK_Error,
179 "expected error \"" + matches[1] + "\" was not produced", range);
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700180 failed = true;
181 }
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700182
183 fileOffset += subbuffer.size() + strlen(marker);
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700184 }
185
186 return !failed;
Jacques Pienaarbae40512018-06-24 09:10:36 -0700187}
188
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700189int main(int argc, char **argv) {
Chris Lattnere2259872018-06-21 15:22:42 -0700190 InitLLVM x(argc, argv);
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700191
Chris Lattnere2259872018-06-21 15:22:42 -0700192 cl::ParseCommandLineOptions(argc, argv, "MLIR modular optimizer driver\n");
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700193
Chris Lattnere79379a2018-06-22 10:39:19 -0700194 // Set up the input file.
195 auto fileOrErr = MemoryBuffer::getFileOrSTDIN(inputFilename);
196 if (std::error_code error = fileOrErr.getError()) {
197 llvm::errs() << argv[0] << ": could not open input file '" << inputFilename
198 << "': " << error.message() << "\n";
199 return 1;
200 }
201
Jacques Pienaarbae40512018-06-24 09:10:36 -0700202 if (checkParserErrors)
203 return !splitMemoryBufferForErrorChecking(std::move(*fileOrErr));
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700204
205 // Error reporter that simply prints the errors reported.
206 SMDiagnosticHandlerTy errorReporter = [](llvm::SMDiagnostic err) {
207 const auto& sourceMgr = *err.getSourceMgr();
208 sourceMgr.PrintMessage(err.getLoc(), err.getKind(), err.getMessage());
209 };
210 return !parseAndPrintMemoryBuffer(std::move(*fileOrErr), errorReporter);
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700211}