CIRCT 24.0.0git
Loading...
Searching...
No Matches
ModuleImplementation.cpp
Go to the documentation of this file.
1//===- ModuleImplementation.cpp - Utilities for module-like ops -----------===//
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
11#include "circt/Support/LLVM.h"
13
14#include "mlir/IR/Builders.h"
15#include "mlir/IR/DialectImplementation.h"
16#include "mlir/Interfaces/FunctionImplementation.h"
17
18using namespace circt;
19using namespace circt::hw;
20
21/// Parse a function result list.
22///
23/// function-result-list ::= function-result-list-parens
24/// function-result-list-parens ::= `(` `)`
25/// | `(` function-result-list-no-parens `)`
26/// function-result-list-no-parens ::= function-result (`,` function-result)*
27/// function-result ::= (percent-identifier `:`) type attribute-dict?
28///
29static ParseResult
30parseFunctionResultList(OpAsmParser &parser,
31 SmallVectorImpl<Attribute> &resultNames,
32 SmallVectorImpl<Type> &resultTypes,
33 SmallVectorImpl<DictionaryAttr> &resultAttrs,
34 SmallVectorImpl<Attribute> &resultLocs) {
35
36 auto parseElt = [&]() -> ParseResult {
37 // Stash the current location parser location.
38 auto irLoc = parser.getCurrentLocation();
39
40 // Parse the result name.
41 std::string portName;
42 if (parser.parseKeywordOrString(&portName))
43 return failure();
44 resultNames.push_back(StringAttr::get(parser.getContext(), portName));
45
46 // Parse the results type.
47 resultTypes.emplace_back();
48 if (parser.parseColonType(resultTypes.back()))
49 return failure();
50
51 // Parse the result attributes.
52 NamedAttrList attrs;
53 if (failed(parser.parseOptionalAttrDict(attrs)))
54 return failure();
55 resultAttrs.push_back(attrs.getDictionary(parser.getContext()));
56
57 // Parse the result location.
58 std::optional<Location> maybeLoc;
59 if (failed(parser.parseOptionalLocationSpecifier(maybeLoc)))
60 return failure();
61 Location loc = maybeLoc ? *maybeLoc : parser.getEncodedSourceLoc(irLoc);
62 resultLocs.push_back(loc);
63
64 return success();
65 };
66
67 return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
68 parseElt);
69}
70
71/// Return the port name for the specified argument or result.
72static StringRef getModuleArgumentName(Operation *module, size_t argNo) {
73 if (auto mod = dyn_cast<HWModuleLike>(module)) {
74 if (argNo < mod.getNumInputPorts())
75 return mod.getInputName(argNo);
76 return StringRef();
77 }
78 auto argNames = module->getAttrOfType<ArrayAttr>("argNames");
79 // Tolerate malformed IR here to enable debug printing etc.
80 if (argNames && argNo < argNames.size())
81 return cast<StringAttr>(argNames[argNo]).getValue();
82 return StringRef();
83}
84
85static StringRef getModuleResultName(Operation *module, size_t resultNo) {
86 if (auto mod = dyn_cast<HWModuleLike>(module)) {
87 if (resultNo < mod.getNumOutputPorts())
88 return mod.getOutputName(resultNo);
89 return StringRef();
90 }
91 auto resultNames = module->getAttrOfType<ArrayAttr>("resultNames");
92 // Tolerate malformed IR here to enable debug printing etc.
93 if (resultNames && resultNo < resultNames.size())
94 return cast<StringAttr>(resultNames[resultNo]).getValue();
95 return StringRef();
96}
97
98void module_like_impl::printModuleSignature(OpAsmPrinter &p, Operation *op,
99 ArrayRef<Type> argTypes,
100 bool isVariadic,
101 ArrayRef<Type> resultTypes,
102 bool &needArgNamesAttr) {
103 using namespace mlir::function_interface_impl;
104
105 Region &body = op->getRegion(0);
106 bool isExternal = body.empty();
107 SmallString<32> resultNameStr;
108 mlir::OpPrintingFlags flags;
109
110 // Handle either old FunctionOpInterface modules or new-style hwmodulelike
111 // This whole thing should be split up into two functions, but the delta is
112 // so small, we are leaving this for now.
113 auto modOp = dyn_cast<hw::HWModuleLike>(op);
114 auto funcOp = dyn_cast<mlir::FunctionOpInterface>(op);
115 SmallVector<Attribute> inputAttrs, outputAttrs;
116 if (funcOp) {
117 if (auto args = funcOp.getAllArgAttrs())
118 for (auto a : args.getValue())
119 inputAttrs.push_back(a);
120 inputAttrs.resize(funcOp.getNumArguments());
121 if (auto results = funcOp.getAllResultAttrs())
122 for (auto a : results.getValue())
123 outputAttrs.push_back(a);
124 outputAttrs.resize(funcOp.getNumResults());
125 } else {
126 inputAttrs = modOp.getAllInputAttrs();
127 outputAttrs = modOp.getAllOutputAttrs();
128 }
129
130 p << '(';
131 for (unsigned i = 0, e = argTypes.size(); i < e; ++i) {
132 if (i > 0)
133 p << ", ";
134
135 auto argName = modOp ? modOp.getInputName(i) : getModuleArgumentName(op, i);
136 if (!isExternal) {
137 // Get the printed format for the argument name.
138 resultNameStr.clear();
139 llvm::raw_svector_ostream tmpStream(resultNameStr);
140 p.printOperand(body.front().getArgument(i), tmpStream);
141 // If the name wasn't printable in a way that agreed with argName, make
142 // sure to print out an explicit argNames attribute.
143 if (tmpStream.str().drop_front() != argName)
144 needArgNamesAttr = true;
145
146 p << tmpStream.str() << ": ";
147 } else if (!argName.empty()) {
148 p << '%' << argName << ": ";
149 }
150
151 p.printType(argTypes[i]);
152 auto inputAttr = inputAttrs[i];
153 p.printOptionalAttrDict(inputAttr
154 ? cast<DictionaryAttr>(inputAttr).getValue()
155 : ArrayRef<NamedAttribute>());
156
157 // TODO: `printOptionalLocationSpecifier` will emit aliases for locations,
158 // even if they are not printed. This will have to be fixed upstream. For
159 // now, use what was specified on the command line.
160 if (flags.shouldPrintDebugInfo()) {
161 auto loc = modOp.getInputLoc(i);
162 if (!isa<UnknownLoc>(loc))
163 p.printOptionalLocationSpecifier(loc);
164 }
165 }
166
167 if (isVariadic) {
168 if (!argTypes.empty())
169 p << ", ";
170 p << "...";
171 }
172
173 p << ')';
174
175 // We print result types specially since we support named arguments.
176 if (!resultTypes.empty()) {
177 p << " -> (";
178 for (size_t i = 0, e = resultTypes.size(); i < e; ++i) {
179 if (i != 0)
180 p << ", ";
181 p.printKeywordOrString(getModuleResultName(op, i));
182 p << ": ";
183 p.printType(resultTypes[i]);
184 auto outputAttr = outputAttrs[i];
185 p.printOptionalAttrDict(outputAttr
186 ? cast<DictionaryAttr>(outputAttr).getValue()
187 : ArrayRef<NamedAttribute>());
188
189 // TODO: `printOptionalLocationSpecifier` will emit aliases for locations,
190 // even if they are not printed. This will have to be fixed upstream. For
191 // now, use what was specified on the command line.
192 if (flags.shouldPrintDebugInfo()) {
193 auto loc = modOp.getOutputLoc(i);
194 if (!isa<UnknownLoc>(loc))
195 p.printOptionalLocationSpecifier(loc);
196 }
197 }
198 p << ')';
199 }
200}
201
203 OpAsmParser &parser, bool &isVariadic,
204 SmallVectorImpl<OpAsmParser::Argument> &args,
205 SmallVectorImpl<Attribute> &argNames, SmallVectorImpl<Attribute> &argLocs,
206 SmallVectorImpl<Attribute> &resultNames,
207 SmallVectorImpl<DictionaryAttr> &resultAttrs,
208 SmallVectorImpl<Attribute> &resultLocs, TypeAttr &type) {
209
210 using namespace mlir::function_interface_impl;
211 auto *context = parser.getContext();
212
213 // Parse the argument list.
214 if (parser.parseArgumentList(args, OpAsmParser::Delimiter::Paren,
215 /*allowType=*/true, /*allowAttrs=*/true))
216 return failure();
217
218 // Parse the result list.
219 SmallVector<Type> resultTypes;
220 if (succeeded(parser.parseOptionalArrow()))
221 if (failed(parseFunctionResultList(parser, resultNames, resultTypes,
222 resultAttrs, resultLocs)))
223 return failure();
224
225 // Process the ssa args for the information we're looking for.
226 SmallVector<Type> argTypes;
227 for (auto &arg : args) {
228 argNames.push_back(parsing_util::getNameFromSSA(context, arg.ssaName.name));
229 argTypes.push_back(arg.type);
230 if (!arg.sourceLoc)
231 arg.sourceLoc = parser.getEncodedSourceLoc(arg.ssaName.location);
232 argLocs.push_back(*arg.sourceLoc);
233 }
234
235 type = TypeAttr::get(FunctionType::get(context, argTypes, resultTypes));
236
237 return success();
238}
239
240////////////////////////////////////////////////////////////////////////////////
241// New Style
242////////////////////////////////////////////////////////////////////////////////
243
244/// Parse an optional keyword or string and set instance into 'result'.`
245/// Returns failure on a parse issue, but not on not finding the string. 'found'
246/// indicates whether the optional value exists.
247ParseResult parseOptionalKeywordOrOptionalString(OpAsmParser &p,
248 std::string &result,
249 bool &found) {
250 StringRef keyword;
251 if (succeeded(p.parseOptionalKeyword(&keyword))) {
252 result = keyword.str();
253 found = true;
254 return success();
255 }
256
257 if (succeeded(p.parseOptionalString(&result)))
258 found = true;
259 return success();
260}
261
262static ParseResult parseDirection(OpAsmParser &p, ModulePort::Direction &dir) {
263 StringRef key;
264 if (failed(p.parseKeyword(&key)))
265 return p.emitError(p.getCurrentLocation(), "expected port direction");
266 if (key == "in")
268 else if (key == "out")
270 else
271 return p.emitError(p.getCurrentLocation(), "unknown port direction '")
272 << key << "'";
273 return success();
274}
275
276static ParseResult parseInputPort(OpAsmParser &parser,
278 if (parser.parseOperand(result.ssaName, /*allowResultNumber=*/false))
279 return failure();
280 NamedAttrList attrs;
281
282 // Parse the result name.
283 bool found = false;
284 if (parseOptionalKeywordOrOptionalString(parser, result.rawName, found))
285 return failure();
286
287 // If there is only a ssa name, use it as the port name. The ssa name is
288 // always required, but if there is the optional arbitrary name, it is used as
289 // the port name and the ssa name is just used for parsing the module.
290 if (!found)
291 result.rawName =
292 parsing_util::getNameFromSSA(parser.getContext(), result.ssaName.name)
293 .str();
294
295 if (parser.parseColonType(result.type) ||
296 parser.parseOptionalAttrDict(attrs) ||
297 parser.parseOptionalLocationSpecifier(result.sourceLoc))
298 return failure();
299 result.attrs = attrs.getDictionary(parser.getContext());
300 return success();
301}
302
303static ParseResult parseOutputPort(OpAsmParser &parser,
305 // Stash the current location parser location.
306 auto irLoc = parser.getCurrentLocation();
307
308 // Parse the result name.
309 if (parser.parseKeywordOrString(&result.rawName))
310 return failure();
311
312 // Parse the results type.
313 if (parser.parseColonType(result.type))
314 return failure();
315
316 // Parse the result attributes.
317 NamedAttrList attrs;
318 if (failed(parser.parseOptionalAttrDict(attrs)))
319 return failure();
320 result.attrs = attrs.getDictionary(parser.getContext());
321
322 // Parse the result location.
323 std::optional<Location> maybeLoc;
324 if (failed(parser.parseOptionalLocationSpecifier(maybeLoc)))
325 return failure();
326 result.sourceLoc = maybeLoc ? *maybeLoc : parser.getEncodedSourceLoc(irLoc);
327
328 return success();
329}
330
331/// Parse a single argument with the following syntax:
332///
333/// output (id|string) : !type { optionalAttrDict} loc(optionalSourceLoc)`
334/// (input|inout) %ssaname : !type { optionalAttrDict} loc(optionalSourceLoc)`
335///
336static ParseResult parsePort(OpAsmParser &p,
338 NamedAttrList attrs;
339 if (parseDirection(p, result.direction))
340 return failure();
342 return parseOutputPort(p, result);
343 return parseInputPort(p, result);
344}
345
346static ParseResult
347parsePortList(OpAsmParser &p,
348 SmallVectorImpl<module_like_impl::PortParse> &result) {
349 auto parseOnePort = [&]() -> ParseResult {
350 return parsePort(p, result.emplace_back());
351 };
352 return p.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren, parseOnePort,
353 " in port list");
354}
355
357 OpAsmParser &parser, SmallVectorImpl<PortParse> &args, TypeAttr &modType) {
358
359 auto *context = parser.getContext();
360
361 // Parse the port list.
362 if (parsePortList(parser, args))
363 return failure();
364
365 // Process the ssa args for the information we're looking for.
366 SmallVector<ModulePort> ports;
367 for (auto &arg : args) {
368 ports.push_back(
369 {StringAttr::get(context, arg.rawName), arg.type, arg.direction});
370 // rewrite type AFTER constructing ports. This will be used in block args.
371 if (!arg.sourceLoc)
372 arg.sourceLoc = parser.getEncodedSourceLoc(arg.ssaName.location);
373 }
374 modType = TypeAttr::get(ModuleType::get(context, ports));
375
376 return success();
377}
378
381 return "in";
383 return "out";
384 assert(0 && "Unknown port direction");
385 abort();
386 return "unknown";
387}
389 hw::HWModuleLike op) {
391 p, op.getModuleBody(), op.getHWModuleType(), op.getAllPortAttrs(),
392 op.getAllPortLocs());
393}
394
395void module_like_impl::printModuleSignatureNew(OpAsmPrinter &p, Region &body,
396 hw::ModuleType modType,
397 ArrayRef<Attribute> portAttrs,
398 ArrayRef<Location> locAttrs) {
399 bool isExternal = body.empty();
400 SmallString<32> resultNameStr;
401 mlir::OpPrintingFlags flags;
402 unsigned curArg = 0;
403 p << '(';
404 for (auto [i, port] : llvm::enumerate(modType.getPorts())) {
405 if (i > 0)
406 p << ", ";
407 p.printKeywordOrString(directionAsString(port.dir));
408 if (port.dir == ModulePort::Direction::Output) {
409 p << " ";
410 p.printKeywordOrString(port.name);
411 } else {
412 if (!isExternal) {
413 // Get the printed format for the argument name.
414 resultNameStr.clear();
415 llvm::raw_svector_ostream tmpStream(resultNameStr);
416 p.printOperand(body.front().getArgument(curArg), tmpStream);
417 p << " " << tmpStream.str();
418 // If the name wasn't printable in a way that agreed with argName, make
419 // sure to print out an explicit argNames attribute.
420 if (tmpStream.str().drop_front() != port.name) {
421 p << " ";
422 p.printKeywordOrString(port.name);
423 }
424 } else {
425 p << " %" << port.name.getValue();
426 }
427 ++curArg;
428 }
429 p << " : ";
430 p.printType(port.type);
431 if (!portAttrs.empty())
432 if (auto attr = dyn_cast<DictionaryAttr>(portAttrs[i]))
433 p.printOptionalAttrDict(attr.getValue());
434
435 // TODO: `printOptionalLocationSpecifier` will emit aliases for locations,
436 // even if they are not printed. This will have to be fixed upstream. For
437 // now, use what was specified on the command line.
438 if (!locAttrs.empty() && flags.shouldPrintDebugInfo()) {
439 auto loc = locAttrs[i];
440 if (!isa<UnknownLoc>(loc))
441 p.printOptionalLocationSpecifier(cast<Location>(loc));
442 }
443 }
444
445 p << ')';
446}
447
448/// Get a special name to use when printing the entry block arguments of the
449/// region contained by an operation in this dialect.
451 mlir::Region &region, OpAsmSetValueNameFn setNameFn) {
452 if (region.empty())
453 return;
454 // Assign port names to the bbargs.
455 auto module = cast<HWModuleOp>(region.getParentOp());
456
457 auto *block = &region.front();
458 for (size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
459 auto name = module.getInputName(i);
460 // Let mlir deterministically convert names to valid identifiers
461 setNameFn(block->getArgument(i), name);
462 }
463}
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static const char * directionAsString(ModulePort::Direction dir)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
static ParseResult parsePortList(OpAsmParser &p, SmallVectorImpl< module_like_impl::PortParse > &result)
return success()
static StringRef getModuleResultName(Operation *module, size_t resultNo)
static ParseResult parseOutputPort(OpAsmParser &parser, module_like_impl::PortParse &result)
static ParseResult parsePort(OpAsmParser &p, module_like_impl::PortParse &result)
Parse a single argument with the following syntax:
NamedAttrList attrs
static StringRef getModuleArgumentName(Operation *module, size_t argNo)
Return the port name for the specified argument or result.
static ParseResult parseFunctionResultList(OpAsmParser &parser, SmallVectorImpl< Attribute > &resultNames, SmallVectorImpl< Type > &resultTypes, SmallVectorImpl< DictionaryAttr > &resultAttrs, SmallVectorImpl< Attribute > &resultLocs)
Parse a function result list.
void printModuleSignature(OpAsmPrinter &p, Operation *op, ArrayRef< Type > argTypes, bool isVariadic, ArrayRef< Type > resultTypes, bool &needArgNamesAttr)
Print a module signature with named results.
ParseResult parseModuleSignature(OpAsmParser &parser, SmallVectorImpl< PortParse > &args, TypeAttr &modType)
New Style parsing.
ParseResult parseModuleFunctionSignature(OpAsmParser &parser, bool &isVariadic, SmallVectorImpl< OpAsmParser::Argument > &args, SmallVectorImpl< Attribute > &argNames, SmallVectorImpl< Attribute > &argLocs, SmallVectorImpl< Attribute > &resultNames, SmallVectorImpl< DictionaryAttr > &resultAttrs, SmallVectorImpl< Attribute > &resultLocs, TypeAttr &type)
This is a variant of mlir::parseFunctionSignature that allows names on result arguments.
void getAsmBlockArgumentNamesImpl(mlir::Region &region, OpAsmSetValueNameFn setNameFn)
Get a special name to use when printing the entry block arguments of the region contained by an opera...
void printModuleSignatureNew(OpAsmPrinter &p, Region &body, hw::ModuleType modType, ArrayRef< Attribute > portAttrs, ArrayRef< Location > locAttrs)
static StringAttr getNameFromSSA(MLIRContext *context, StringRef name)
Get a name from an SSA value string, if said value name is not a number.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:193