1 | //===- Parser.h - MLIR Parser Library Interface -----------------*- C++ -*-===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is contains a unified interface for parsing serialized MLIR. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #ifndef MLIR_PARSER_PARSER_H |
14 | #define MLIR_PARSER_PARSER_H |
15 | |
16 | #include "mlir/IR/AsmState.h" |
17 | #include "mlir/IR/Builders.h" |
18 | #include "mlir/IR/OwningOpRef.h" |
19 | #include <cstddef> |
20 | |
21 | namespace llvm { |
22 | class SourceMgr; |
23 | class SMDiagnostic; |
24 | class StringRef; |
25 | } // namespace llvm |
26 | |
27 | namespace mlir { |
28 | namespace detail { |
29 | |
30 | /// Given a block containing operations that have just been parsed, if the block |
31 | /// contains a single operation of `ContainerOpT` type then remove it from the |
32 | /// block and return it. If the block does not contain just that operation, |
33 | /// create a new operation instance of `ContainerOpT` and move all of the |
34 | /// operations within `parsedBlock` into the first block of the first region. |
35 | /// `ContainerOpT` is required to have a single region containing a single |
36 | /// block, and must implement the `SingleBlockImplicitTerminator` trait. |
37 | template <typename ContainerOpT> |
38 | inline OwningOpRef<ContainerOpT> constructContainerOpForParserIfNecessary( |
39 | Block *parsedBlock, MLIRContext *context, Location sourceFileLoc) { |
40 | |
41 | // Check to see if we parsed a single instance of this operation. |
42 | if (llvm::hasSingleElement(C&: *parsedBlock)) { |
43 | if (ContainerOpT op = dyn_cast<ContainerOpT>(&parsedBlock->front())) { |
44 | op->remove(); |
45 | return op; |
46 | } |
47 | } |
48 | |
49 | // If not, then build a new top-level op if a concrete operation type was |
50 | // specified. |
51 | if constexpr (std::is_same_v<ContainerOpT, Operation *>) { |
52 | (void)context; |
53 | return emitError(loc: sourceFileLoc) |
54 | << "source must contain a single top-level operation, found: " |
55 | << parsedBlock->getOperations().size(), |
56 | nullptr; |
57 | } else { |
58 | static_assert( |
59 | ContainerOpT::template hasTrait<OpTrait::OneRegion>() && |
60 | (ContainerOpT::template hasTrait<OpTrait::NoTerminator>() || |
61 | OpTrait::template hasSingleBlockImplicitTerminator< |
62 | ContainerOpT>::value), |
63 | "Expected `ContainerOpT` to have a single region with a single " |
64 | "block that has an implicit terminator or does not require one" ); |
65 | |
66 | OpBuilder builder(context); |
67 | ContainerOpT op = builder.create<ContainerOpT>(sourceFileLoc); |
68 | OwningOpRef<ContainerOpT> opRef(op); |
69 | assert(op->getNumRegions() == 1 && |
70 | llvm::hasSingleElement(op->getRegion(0)) && |
71 | "expected generated operation to have a single region with a single " |
72 | "block" ); |
73 | Block *opBlock = &op->getRegion(0).front(); |
74 | opBlock->getOperations().splice(where: opBlock->begin(), |
75 | L2&: parsedBlock->getOperations()); |
76 | |
77 | // After splicing, verify just this operation to ensure it can properly |
78 | // contain the operations inside of it. |
79 | if (failed(op.verifyInvariants())) |
80 | return OwningOpRef<ContainerOpT>(); |
81 | return opRef; |
82 | } |
83 | } |
84 | } // namespace detail |
85 | |
86 | /// This parses the file specified by the indicated SourceMgr and appends parsed |
87 | /// operations to the given block. If the block is non-empty, the operations are |
88 | /// placed before the current terminator. If parsing is successful, success is |
89 | /// returned. Otherwise, an error message is emitted through the error handler |
90 | /// registered in the context, and failure is returned. If `sourceFileLoc` is |
91 | /// non-null, it is populated with a file location representing the start of the |
92 | /// source file that is being parsed. |
93 | LogicalResult parseSourceFile(const llvm::SourceMgr &sourceMgr, Block *block, |
94 | const ParserConfig &config, |
95 | LocationAttr *sourceFileLoc = nullptr); |
96 | /// An overload with a source manager that may have references taken during the |
97 | /// parsing process, and whose lifetime can be freely extended (such that the |
98 | /// source manager is not destroyed before the parsed IR). This is useful, for |
99 | /// example, to avoid copying some large resources into the MLIRContext and |
100 | /// instead referencing the data directly from the input buffers. |
101 | LogicalResult parseSourceFile(const std::shared_ptr<llvm::SourceMgr> &sourceMgr, |
102 | Block *block, const ParserConfig &config, |
103 | LocationAttr *sourceFileLoc = nullptr); |
104 | |
105 | /// This parses the file specified by the indicated filename and appends parsed |
106 | /// operations to the given block. If the block is non-empty, the operations are |
107 | /// placed before the current terminator. If parsing is successful, success is |
108 | /// returned. Otherwise, an error message is emitted through the error handler |
109 | /// registered in the context, and failure is returned. If `sourceFileLoc` is |
110 | /// non-null, it is populated with a file location representing the start of the |
111 | /// source file that is being parsed. |
112 | LogicalResult parseSourceFile(llvm::StringRef filename, Block *block, |
113 | const ParserConfig &config, |
114 | LocationAttr *sourceFileLoc = nullptr); |
115 | |
116 | /// This parses the file specified by the indicated filename using the provided |
117 | /// SourceMgr and appends parsed operations to the given block. If the block is |
118 | /// non-empty, the operations are placed before the current terminator. If |
119 | /// parsing is successful, success is returned. Otherwise, an error message is |
120 | /// emitted through the error handler registered in the context, and failure is |
121 | /// returned. If `sourceFileLoc` is non-null, it is populated with a file |
122 | /// location representing the start of the source file that is being parsed. |
123 | LogicalResult parseSourceFile(llvm::StringRef filename, |
124 | llvm::SourceMgr &sourceMgr, Block *block, |
125 | const ParserConfig &config, |
126 | LocationAttr *sourceFileLoc = nullptr); |
127 | /// An overload with a source manager that may have references taken during the |
128 | /// parsing process, and whose lifetime can be freely extended (such that the |
129 | /// source manager is not destroyed before the parsed IR). This is useful, for |
130 | /// example, to avoid copying some large resources into the MLIRContext and |
131 | /// instead referencing the data directly from the input buffers. |
132 | LogicalResult parseSourceFile(llvm::StringRef filename, |
133 | const std::shared_ptr<llvm::SourceMgr> &sourceMgr, |
134 | Block *block, const ParserConfig &config, |
135 | LocationAttr *sourceFileLoc = nullptr); |
136 | |
137 | /// This parses the IR string and appends parsed operations to the given block. |
138 | /// If the block is non-empty, the operations are placed before the current |
139 | /// terminator. If parsing is successful, success is returned. Otherwise, an |
140 | /// error message is emitted through the error handler registered in the |
141 | /// context, and failure is returned. |
142 | /// `sourceName` is used as the file name of the source; any IR without |
143 | /// locations will get a `FileLineColLoc` location with `sourceName` as the file |
144 | /// name. If `sourceFileLoc` is non-null, it is populated with a file location |
145 | /// representing the start of the source file that is being parsed. |
146 | LogicalResult parseSourceString(llvm::StringRef sourceStr, Block *block, |
147 | const ParserConfig &config, |
148 | StringRef sourceName = "" , |
149 | LocationAttr *sourceFileLoc = nullptr); |
150 | |
151 | namespace detail { |
152 | /// The internal implementation of the templated `parseSourceFile` methods |
153 | /// below, that simply forwards to the non-templated version. |
154 | template <typename ContainerOpT, typename... ParserArgs> |
155 | inline OwningOpRef<ContainerOpT> parseSourceFile(const ParserConfig &config, |
156 | ParserArgs &&...args) { |
157 | LocationAttr sourceFileLoc; |
158 | Block block; |
159 | if (failed(parseSourceFile(std::forward<ParserArgs>(args)..., &block, config, |
160 | &sourceFileLoc))) |
161 | return OwningOpRef<ContainerOpT>(); |
162 | return detail::constructContainerOpForParserIfNecessary<ContainerOpT>( |
163 | &block, config.getContext(), sourceFileLoc); |
164 | } |
165 | } // namespace detail |
166 | |
167 | /// This parses the file specified by the indicated SourceMgr. If the source IR |
168 | /// contained a single instance of `ContainerOpT`, it is returned. Otherwise, a |
169 | /// new instance of `ContainerOpT` is constructed containing all of the parsed |
170 | /// operations. If parsing was not successful, null is returned and an error |
171 | /// message is emitted through the error handler registered in the context, and |
172 | /// failure is returned. `ContainerOpT` is required to have a single region |
173 | /// containing a single block, and must implement the |
174 | /// `SingleBlockImplicitTerminator` trait. |
175 | template <typename ContainerOpT = Operation *> |
176 | inline OwningOpRef<ContainerOpT> |
177 | parseSourceFile(const llvm::SourceMgr &sourceMgr, const ParserConfig &config) { |
178 | return detail::parseSourceFile<ContainerOpT>(config, sourceMgr); |
179 | } |
180 | /// An overload with a source manager that may have references taken during the |
181 | /// parsing process, and whose lifetime can be freely extended (such that the |
182 | /// source manager is not destroyed before the parsed IR). This is useful, for |
183 | /// example, to avoid copying some large resources into the MLIRContext and |
184 | /// instead referencing the data directly from the input buffers. |
185 | template <typename ContainerOpT = Operation *> |
186 | inline OwningOpRef<ContainerOpT> |
187 | parseSourceFile(const std::shared_ptr<llvm::SourceMgr> &sourceMgr, |
188 | const ParserConfig &config) { |
189 | return detail::parseSourceFile<ContainerOpT>(config, sourceMgr); |
190 | } |
191 | |
192 | /// This parses the file specified by the indicated filename. If the source IR |
193 | /// contained a single instance of `ContainerOpT`, it is returned. Otherwise, a |
194 | /// new instance of `ContainerOpT` is constructed containing all of the parsed |
195 | /// operations. If parsing was not successful, null is returned and an error |
196 | /// message is emitted through the error handler registered in the context, and |
197 | /// failure is returned. `ContainerOpT` is required to have a single region |
198 | /// containing a single block, and must implement the |
199 | /// `SingleBlockImplicitTerminator` trait. |
200 | template <typename ContainerOpT = Operation *> |
201 | inline OwningOpRef<ContainerOpT> parseSourceFile(StringRef filename, |
202 | const ParserConfig &config) { |
203 | return detail::parseSourceFile<ContainerOpT>(config, filename); |
204 | } |
205 | |
206 | /// This parses the file specified by the indicated filename using the provided |
207 | /// SourceMgr. If the source IR contained a single instance of `ContainerOpT`, |
208 | /// it is returned. Otherwise, a new instance of `ContainerOpT` is constructed |
209 | /// containing all of the parsed operations. If parsing was not successful, null |
210 | /// is returned and an error message is emitted through the error handler |
211 | /// registered in the context, and failure is returned. `ContainerOpT` is |
212 | /// required to have a single region containing a single block, and must |
213 | /// implement the `SingleBlockImplicitTerminator` trait. |
214 | template <typename ContainerOpT = Operation *> |
215 | inline OwningOpRef<ContainerOpT> parseSourceFile(llvm::StringRef filename, |
216 | llvm::SourceMgr &sourceMgr, |
217 | const ParserConfig &config) { |
218 | return detail::parseSourceFile<ContainerOpT>(config, filename, sourceMgr); |
219 | } |
220 | /// An overload with a source manager that may have references taken during the |
221 | /// parsing process, and whose lifetime can be freely extended (such that the |
222 | /// source manager is not destroyed before the parsed IR). This is useful, for |
223 | /// example, to avoid copying some large resources into the MLIRContext and |
224 | /// instead referencing the data directly from the input buffers. |
225 | template <typename ContainerOpT = Operation *> |
226 | inline OwningOpRef<ContainerOpT> |
227 | parseSourceFile(llvm::StringRef filename, |
228 | const std::shared_ptr<llvm::SourceMgr> &sourceMgr, |
229 | const ParserConfig &config) { |
230 | return detail::parseSourceFile<ContainerOpT>(config, filename, sourceMgr); |
231 | } |
232 | |
233 | /// This parses the provided string containing MLIR. If the source IR contained |
234 | /// a single instance of `ContainerOpT`, it is returned. Otherwise, a new |
235 | /// instance of `ContainerOpT` is constructed containing all of the parsed |
236 | /// operations. If parsing was not successful, null is returned and an error |
237 | /// message is emitted through the error handler registered in the context, and |
238 | /// failure is returned. `ContainerOpT` is required to have a single region |
239 | /// containing a single block, and must implement the |
240 | /// `SingleBlockImplicitTerminator` trait. |
241 | /// `sourceName` is used as the file name of the source; any IR without |
242 | /// locations will get a `FileLineColLoc` location with `sourceName` as the file |
243 | /// name. |
244 | template <typename ContainerOpT = Operation *> |
245 | inline OwningOpRef<ContainerOpT> parseSourceString(llvm::StringRef sourceStr, |
246 | const ParserConfig &config, |
247 | StringRef sourceName = "" ) { |
248 | LocationAttr sourceFileLoc; |
249 | Block block; |
250 | if (failed(result: parseSourceString(sourceStr, block: &block, config, sourceName, |
251 | sourceFileLoc: &sourceFileLoc))) |
252 | return OwningOpRef<ContainerOpT>(); |
253 | return detail::constructContainerOpForParserIfNecessary<ContainerOpT>( |
254 | &block, config.getContext(), sourceFileLoc); |
255 | } |
256 | |
257 | } // namespace mlir |
258 | |
259 | #endif // MLIR_PARSER_PARSER_H |
260 | |