blob: 80baee16f55d0a82976c6706f90ea5e50bc87d8e [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 Lattnere2259872018-06-21 15:22:42 -070028#include "llvm/Support/FileUtilities.h"
Chris Lattnerc0c5e0f2018-06-21 09:49:33 -070029#include "llvm/Support/InitLLVM.h"
Jacques Pienaarca4c4a02018-06-25 08:10:46 -070030#include "llvm/Support/Regex.h"
Jacques Pienaar39ffa102018-07-07 19:12:22 -070031#include "llvm/Support/SourceMgr.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
Jacques Pienaar39ffa102018-07-07 19:12:22 -0700101 // Record the expected errors's position, substring and whether it was seen.
102 struct ExpectedError {
103 int lineNo;
104 StringRef substring;
105 SMLoc fileLoc;
106 bool matched;
107 };
108
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700109 // Tracks offset of subbuffer into original buffer.
110 const char *fileOffset =
111 fileSourceMgr.getMemoryBuffer(fileSourceMgr.getMainFileID())
112 ->getBufferStart();
113
Jacques Pienaar39ffa102018-07-07 19:12:22 -0700114 for (auto &subbuffer : sourceBuffers) {
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700115 SourceMgr sourceMgr;
116 // Tell sourceMgr about this buffer, which is what the parser will pick up.
117 sourceMgr.AddNewSourceBuffer(MemoryBuffer::getMemBufferCopy(subbuffer),
118 SMLoc());
119
Jacques Pienaar39ffa102018-07-07 19:12:22 -0700120 // Extracing the expected errors.
James Molloy61a656c2018-07-22 15:45:24 -0700121 llvm::Regex expected("expected-error(@[+-][0-9]+)? *{{(.*)}}");
Jacques Pienaar39ffa102018-07-07 19:12:22 -0700122 SmallVector<ExpectedError, 2> expectedErrors;
123 SmallVector<StringRef, 100> lines;
124 subbuffer.split(lines, '\n');
125 size_t bufOffset = 0;
126 for (int lineNo = 0; lineNo < lines.size(); ++lineNo) {
127 SmallVector<StringRef, 3> matches;
128 if (expected.match(lines[lineNo], &matches)) {
129 // Point to the start of expected-error.
130 SMLoc errorStart =
131 SMLoc::getFromPointer(fileOffset + bufOffset +
132 lines[lineNo].size() - matches[2].size() - 2);
133 ExpectedError expErr{lineNo + 1, matches[2], errorStart, false};
134 int offset;
135 if (!matches[1].empty() &&
136 !matches[1].drop_front().getAsInteger(0, offset)) {
137 expErr.lineNo += offset;
138 }
139 expectedErrors.push_back(expErr);
140 }
141 bufOffset += lines[lineNo].size() + 1;
Jacques Pienaarca4c4a02018-06-25 08:10:46 -0700142 }
143
Jacques Pienaar39ffa102018-07-07 19:12:22 -0700144 // Error checker that verifies reported error was expected.
145 auto checker = [&](const SMDiagnostic &err) {
146 for (auto &e : expectedErrors) {
147 if (err.getLineNo() == e.lineNo &&
148 err.getMessage().contains(e.substring)) {
149 e.matched = true;
150 return;
151 }
152 }
153 // Report error if no match found.
154 const auto &sourceMgr = *err.getSourceMgr();
155 const char *bufferStart =
156 sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID())
157 ->getBufferStart();
158
159 size_t offset = err.getLoc().getPointer() - bufferStart;
160 SMLoc loc = SMLoc::getFromPointer(fileOffset + offset);
161 fileSourceMgr.PrintMessage(loc, SourceMgr::DK_Error,
162 "unexpected error: " + err.getMessage());
163 opt_result = OptFailure;
164 };
165
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700166 // Parse the input file.
167 MLIRContext context;
168 std::unique_ptr<Module> module(
169 parseSourceFile(sourceMgr, &context, checker));
Jacques Pienaarb2ddbb62018-06-26 08:56:55 -0700170
Jacques Pienaar39ffa102018-07-07 19:12:22 -0700171 // Verify that all expected errors were seen.
172 for (auto err : expectedErrors) {
173 if (!err.matched) {
174 SMRange range(err.fileLoc,
175 SMLoc::getFromPointer(err.fileLoc.getPointer() +
176 err.substring.size()));
177 fileSourceMgr.PrintMessage(
178 err.fileLoc, SourceMgr::DK_Error,
179 "expected error \"" + err.substring + "\" was not produced", range);
180 opt_result = OptFailure;
181 }
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}