1 //===- mlir-pdll.cpp - MLIR PDLL frontend -----------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "mlir/IR/BuiltinOps.h"
10 #include "mlir/Support/FileUtilities.h"
11 #include "mlir/Support/ToolUtilities.h"
12 #include "mlir/Tools/PDLL/AST/Context.h"
13 #include "mlir/Tools/PDLL/AST/Nodes.h"
14 #include "mlir/Tools/PDLL/CodeGen/CPPGen.h"
15 #include "mlir/Tools/PDLL/CodeGen/MLIRGen.h"
16 #include "mlir/Tools/PDLL/ODS/Context.h"
17 #include "mlir/Tools/PDLL/Parser/Parser.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/InitLLVM.h"
20 #include "llvm/Support/SourceMgr.h"
21 #include "llvm/Support/ToolOutputFile.h"
25 using namespace mlir::pdll
;
27 //===----------------------------------------------------------------------===//
29 //===----------------------------------------------------------------------===//
31 /// The desired output type.
32 enum class OutputType
{
39 processBuffer(raw_ostream
&os
, std::unique_ptr
<llvm::MemoryBuffer
> chunkBuffer
,
40 OutputType outputType
, std::vector
<std::string
> &includeDirs
,
41 bool dumpODS
, std::set
<std::string
> *includedFiles
) {
42 llvm::SourceMgr sourceMgr
;
43 sourceMgr
.setIncludeDirs(includeDirs
);
44 sourceMgr
.AddNewSourceBuffer(std::move(chunkBuffer
), SMLoc());
46 // If we are dumping ODS information, also enable documentation to ensure the
47 // summary and description information is imported as well.
48 bool enableDocumentation
= dumpODS
;
50 ods::Context odsContext
;
51 ast::Context
astContext(odsContext
);
52 FailureOr
<ast::Module
*> module
=
53 parsePDLLAST(astContext
, sourceMgr
, enableDocumentation
);
57 // Add the files that were included to the set.
59 for (unsigned i
= 1, e
= sourceMgr
.getNumBuffers(); i
< e
; ++i
) {
60 includedFiles
->insert(
61 sourceMgr
.getMemoryBuffer(i
+ 1)->getBufferIdentifier().str());
65 // Print out the ODS information if requested.
67 odsContext
.print(llvm::errs());
69 // Generate the output.
70 if (outputType
== OutputType::AST
) {
75 MLIRContext mlirContext
;
76 OwningOpRef
<ModuleOp
> pdlModule
=
77 codegenPDLLToMLIR(&mlirContext
, astContext
, sourceMgr
, **module
);
81 if (outputType
== OutputType::MLIR
) {
82 pdlModule
->print(os
, OpPrintingFlags().enableDebugInfo());
85 codegenPDLLToCPP(**module
, *pdlModule
, os
);
89 /// Create a dependency file for `-d` option.
91 /// This functionality is generally only for the benefit of the build system,
92 /// and is modeled after the same option in TableGen.
94 createDependencyFile(StringRef outputFilename
, StringRef dependencyFile
,
95 std::set
<std::string
> &includedFiles
) {
96 if (outputFilename
== "-") {
97 llvm::errs() << "error: the option -d must be used together with -o\n";
101 std::string errorMessage
;
102 std::unique_ptr
<llvm::ToolOutputFile
> outputFile
=
103 openOutputFile(dependencyFile
, &errorMessage
);
105 llvm::errs() << errorMessage
<< "\n";
109 outputFile
->os() << outputFilename
<< ":";
110 for (const auto &includeFile
: includedFiles
)
111 outputFile
->os() << ' ' << includeFile
;
112 outputFile
->os() << "\n";
117 int main(int argc
, char **argv
) {
118 // FIXME: This is necessary because we link in TableGen, which defines its
119 // options as static variables.. some of which overlap with our options.
120 llvm::cl::ResetCommandLineParser();
122 llvm::cl::opt
<std::string
> inputFilename(
123 llvm::cl::Positional
, llvm::cl::desc("<input file>"), llvm::cl::init("-"),
124 llvm::cl::value_desc("filename"));
126 llvm::cl::opt
<std::string
> outputFilename(
127 "o", llvm::cl::desc("Output filename"), llvm::cl::value_desc("filename"),
128 llvm::cl::init("-"));
130 llvm::cl::list
<std::string
> includeDirs(
131 "I", llvm::cl::desc("Directory of include files"),
132 llvm::cl::value_desc("directory"), llvm::cl::Prefix
);
134 llvm::cl::opt
<bool> dumpODS(
137 "Print out the parsed ODS information from the input file"),
138 llvm::cl::init(false));
139 llvm::cl::opt
<bool> splitInputFile(
141 llvm::cl::desc("Split the input file into pieces and process each "
142 "chunk independently"),
143 llvm::cl::init(false));
144 llvm::cl::opt
<enum OutputType
> outputType(
145 "x", llvm::cl::init(OutputType::AST
),
146 llvm::cl::desc("The type of output desired"),
147 llvm::cl::values(clEnumValN(OutputType::AST
, "ast",
148 "generate the AST for the input file"),
149 clEnumValN(OutputType::MLIR
, "mlir",
150 "generate the PDL MLIR for the input file"),
151 clEnumValN(OutputType::CPP
, "cpp",
152 "generate a C++ source file containing the "
153 "patterns for the input file")));
154 llvm::cl::opt
<std::string
> dependencyFilename(
155 "d", llvm::cl::desc("Dependency filename"),
156 llvm::cl::value_desc("filename"), llvm::cl::init(""));
157 llvm::cl::opt
<bool> writeIfChanged(
159 llvm::cl::desc("Only write to the output file if it changed"));
161 llvm::InitLLVM
y(argc
, argv
);
162 llvm::cl::ParseCommandLineOptions(argc
, argv
, "PDLL Frontend");
164 // Set up the input file.
165 std::string errorMessage
;
166 std::unique_ptr
<llvm::MemoryBuffer
> inputFile
=
167 openInputFile(inputFilename
, &errorMessage
);
169 llvm::errs() << errorMessage
<< "\n";
173 // If we are creating a dependency file, we'll also need to track what files
174 // get included during processing.
175 std::set
<std::string
> includedFilesStorage
;
176 std::set
<std::string
> *includedFiles
= nullptr;
177 if (!dependencyFilename
.empty())
178 includedFiles
= &includedFilesStorage
;
180 // The split-input-file mode is a very specific mode that slices the file
181 // up into small pieces and checks each independently.
182 std::string outputStr
;
183 llvm::raw_string_ostream
outputStrOS(outputStr
);
184 auto processFn
= [&](std::unique_ptr
<llvm::MemoryBuffer
> chunkBuffer
,
186 return processBuffer(os
, std::move(chunkBuffer
), outputType
, includeDirs
,
187 dumpODS
, includedFiles
);
189 if (failed(splitAndProcessBuffer(std::move(inputFile
), processFn
, outputStrOS
,
194 bool shouldWriteOutput
= true;
195 if (writeIfChanged
) {
196 // Only update the real output file if there are any differences. This
197 // prevents recompilation of all the files depending on it if there aren't
199 if (auto existingOrErr
=
200 llvm::MemoryBuffer::getFile(outputFilename
, /*IsText=*/true))
201 if (std::move(existingOrErr
.get())->getBuffer() == outputStrOS
.str())
202 shouldWriteOutput
= false;
205 // Populate the output file if necessary.
206 if (shouldWriteOutput
) {
207 std::unique_ptr
<llvm::ToolOutputFile
> outputFile
=
208 openOutputFile(outputFilename
, &errorMessage
);
210 llvm::errs() << errorMessage
<< "\n";
213 outputFile
->os() << outputStrOS
.str();
217 // Always write the depfile, even if the main output hasn't changed. If it's
218 // missing, Ninja considers the output dirty.
219 if (!dependencyFilename
.empty()) {
220 if (failed(createDependencyFile(outputFilename
, dependencyFilename
,
221 includedFilesStorage
)))