blob: 67a090c726cfa35199b51da5ace31028cf18ac5c [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
Jacques Pienaar7b829702018-07-03 13:24:09 -070047enum OptResult { OptSuccess, OptFailure };
48
Chris Lattnere2259872018-06-21 15:22:42 -070049/// Open the specified output file and return it, exiting if there is any I/O or
50/// other errors.
51static std::unique_ptr<ToolOutputFile> getOutputStream() {
52 std::error_code error;
53 auto result = make_unique<ToolOutputFile>(outputFilename, error,
54 sys::fs::F_None);
55 if (error) {
56 llvm::errs() << error.message() << '\n';
57 exit(1);
58 }
59
60 return result;
61}
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -070062
Jacques Pienaarbae40512018-06-24 09:10:36 -070063/// Parses the memory buffer and, if successfully parsed, prints the parsed
Jacques Pienaar7b829702018-07-03 13:24:09 -070064/// output.
65OptResult parseAndPrintMemoryBuffer(std::unique_ptr<MemoryBuffer> buffer) {
Jacques Pienaarbae40512018-06-24 09:10:36 -070066 // Tell sourceMgr about this buffer, which is what the parser will pick up.
67 SourceMgr sourceMgr;
68 sourceMgr.AddNewSourceBuffer(std::move(buffer), SMLoc());
69
Jacques Pienaar9c411be2018-06-24 19:17:35 -070070 // Parse the input file.
Jacques Pienaarbae40512018-06-24 09:10:36 -070071 MLIRContext context;
Jacques Pienaar7b829702018-07-03 13:24:09 -070072 std::unique_ptr<Module> module(parseSourceFile(sourceMgr, &context));
73 if (!module)
74 return OptFailure;
Jacques Pienaarbae40512018-06-24 09:10:36 -070075
76 // Print the output.
77 auto output = getOutputStream();
78 module->print(output->os());
79 output->keep();
80
Jacques Pienaar7b829702018-07-03 13:24:09 -070081 return OptSuccess;
Jacques Pienaarbae40512018-06-24 09:10:36 -070082}
83
84/// Split the memory buffer into multiple buffers using the marker -----.
Jacques Pienaar7b829702018-07-03 13:24:09 -070085OptResult
86splitMemoryBufferForErrorChecking(std::unique_ptr<MemoryBuffer> buffer) {
Jacques Pienaarbae40512018-06-24 09:10:36 -070087 const char marker[] = "-----";
88 SmallVector<StringRef, 2> sourceBuffers;
89 buffer->getBuffer().split(sourceBuffers, marker);
Jacques Pienaarbae40512018-06-24 09:10:36 -070090
Jacques Pienaarca4c4a02018-06-25 08:10:46 -070091 // Error reporter that verifies error reports matches expected error
92 // substring.
93 // TODO: Only checking for error cases below. Could be expanded to other kinds
94 // of diagnostics.
95 // TODO: Enable specifying errors on different lines (@-1).
96 // TODO: Currently only checking if substring matches, enable regex checking.
Jacques Pienaar7b829702018-07-03 13:24:09 -070097 OptResult opt_result = OptSuccess;
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -070098 SourceMgr fileSourceMgr;
99 fileSourceMgr.AddNewSourceBuffer(std::move(buffer), SMLoc());
100
101 // Tracks offset of subbuffer into original buffer.
102 const char *fileOffset =
103 fileSourceMgr.getMemoryBuffer(fileSourceMgr.getMainFileID())
104 ->getBufferStart();
105
106 // Create error checker that uses the helper function to relate the reported
107 // error to the file being parsed.
Jacques Pienaar7b829702018-07-03 13:24:09 -0700108 SMDiagnosticHandlerTy checker = [&](const SMDiagnostic &err) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700109 const auto &sourceMgr = *err.getSourceMgr();
110 const char *bufferStart =
111 sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID())->getBufferStart();
112
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700113 StringRef line = err.getLineContents();
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700114 size_t offset = err.getLoc().getPointer() - bufferStart;
115 SMLoc loc = SMLoc::getFromPointer(fileOffset + offset);
116
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700117 // Extract expected substring using regex and check simple containment in
118 // error message.
119 llvm::Regex expected("expected-error {{(.*)}}");
120 SmallVector<StringRef, 2> matches;
121 bool matched = expected.match(line, &matches);
122 if (matches.size() != 2) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700123 fileSourceMgr.PrintMessage(
124 loc, SourceMgr::DK_Error,
125 "unexpected error: " + err.getMessage());
Jacques Pienaar7b829702018-07-03 13:24:09 -0700126 opt_result = OptFailure;
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700127 return;
128 }
129
130 matched = err.getMessage().contains(matches[1]);
131 if (!matched) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700132 const char checkPrefix[] = "expected-error {{";
133 loc = SMLoc::getFromPointer(fileOffset + offset + line.find(checkPrefix) -
134 err.getColumnNo() + strlen(checkPrefix));
Jacques Pienaar7b829702018-07-03 13:24:09 -0700135 fileSourceMgr.PrintMessage(
136 loc, SourceMgr::DK_Error,
137 "\"" + err.getMessage() + "\" did not contain expected substring \"" +
138 matches[1] + "\"");
139 opt_result = OptFailure;
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700140 return;
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700141 }
142 };
143
144 for (auto& subbuffer : sourceBuffers) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700145 SourceMgr sourceMgr;
146 // Tell sourceMgr about this buffer, which is what the parser will pick up.
147 sourceMgr.AddNewSourceBuffer(MemoryBuffer::getMemBufferCopy(subbuffer),
148 SMLoc());
149
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700150 int expectedCount = subbuffer.count("expected-error");
151 if (expectedCount > 1) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700152 size_t expectedOffset = subbuffer.find("expected-error");
153 expectedOffset = subbuffer.find("expected-error", expectedOffset);
154 SMLoc loc = SMLoc::getFromPointer(fileOffset + expectedOffset);
155 fileSourceMgr.PrintMessage(loc, SourceMgr::DK_Error,
156 "too many errors expected: unable to verify "
157 "more than one error per group");
158 fileOffset += subbuffer.size() + strlen(marker);
Jacques Pienaar7b829702018-07-03 13:24:09 -0700159 opt_result = OptFailure;
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700160 continue;
161 }
162
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700163 // Parse the input file.
164 MLIRContext context;
165 std::unique_ptr<Module> module(
166 parseSourceFile(sourceMgr, &context, checker));
167 bool parsed = module != nullptr;
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700168 if (parsed && expectedCount != 0) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700169 llvm::Regex expected(".*expected-error {{(.*)}}");
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700170 SmallVector<StringRef, 2> matches;
171 expected.match(subbuffer, &matches);
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700172
173 // Highlight expected-error clause of unexpectedly passing test case.
174 size_t expectedOffset = subbuffer.find("expected-error");
175 size_t endOffset = matches[0].size();
176 SMLoc loc = SMLoc::getFromPointer(fileOffset + expectedOffset);
177 SMRange range(loc, SMLoc::getFromPointer(fileOffset + endOffset));
178 fileSourceMgr.PrintMessage(
179 loc, SourceMgr::DK_Error,
180 "expected error \"" + matches[1] + "\" was not produced", range);
Jacques Pienaar7b829702018-07-03 13:24:09 -0700181 opt_result = OptFailure;
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700182 }
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700183
184 fileOffset += subbuffer.size() + strlen(marker);
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700185 }
186
Jacques Pienaar7b829702018-07-03 13:24:09 -0700187 return opt_result;
Jacques Pienaarbae40512018-06-24 09:10:36 -0700188}
189
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700190int main(int argc, char **argv) {
Chris Lattnere2259872018-06-21 15:22:42 -0700191 InitLLVM x(argc, argv);
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700192
Chris Lattnere2259872018-06-21 15:22:42 -0700193 cl::ParseCommandLineOptions(argc, argv, "MLIR modular optimizer driver\n");
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700194
Chris Lattnere79379a2018-06-22 10:39:19 -0700195 // Set up the input file.
196 auto fileOrErr = MemoryBuffer::getFileOrSTDIN(inputFilename);
197 if (std::error_code error = fileOrErr.getError()) {
198 llvm::errs() << argv[0] << ": could not open input file '" << inputFilename
199 << "': " << error.message() << "\n";
200 return 1;
201 }
202
Jacques Pienaarbae40512018-06-24 09:10:36 -0700203 if (checkParserErrors)
Jacques Pienaar7b829702018-07-03 13:24:09 -0700204 return splitMemoryBufferForErrorChecking(std::move(*fileOrErr));
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700205
Jacques Pienaar7b829702018-07-03 13:24:09 -0700206 return parseAndPrintMemoryBuffer(std::move(*fileOrErr));
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -0700207}