CIRCT 20.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")
267 dir = ModulePort::Direction::Input;
268 else if (key == "out")
269 dir = ModulePort::Direction::Output;
270 else if (key == "inout")
271 dir = ModulePort::Direction::InOut;
272 else
273 return p.emitError(p.getCurrentLocation(), "unknown port direction '")
274 << key << "'";
275 return success();
276}
277
278static ParseResult parseInputPort(OpAsmParser &parser,
280 if (parser.parseOperand(result.ssaName, /*allowResultNumber=*/false))
281 return failure();
282 NamedAttrList attrs;
283
284 // Parse the result name.
285 bool found = false;
286 if (parseOptionalKeywordOrOptionalString(parser, result.rawName, found))
287 return failure();
288
289 // If there is only a ssa name, use it as the port name. The ssa name is
290 // always required, but if there is the optional arbitrary name, it is used as
291 // the port name and the ssa name is just used for parsing the module.
292 if (!found)
293 result.rawName =
294 parsing_util::getNameFromSSA(parser.getContext(), result.ssaName.name)
295 .str();
296
297 if (parser.parseColonType(result.type) ||
298 parser.parseOptionalAttrDict(attrs) ||
299 parser.parseOptionalLocationSpecifier(result.sourceLoc))
300 return failure();
301 result.attrs = attrs.getDictionary(parser.getContext());
302 return success();
303}
304
305static ParseResult parseOutputPort(OpAsmParser &parser,
307 // Stash the current location parser location.
308 auto irLoc = parser.getCurrentLocation();
309
310 // Parse the result name.
311 if (parser.parseKeywordOrString(&result.rawName))
312 return failure();
313
314 // Parse the results type.
315 if (parser.parseColonType(result.type))
316 return failure();
317
318 // Parse the result attributes.
319 NamedAttrList attrs;
320 if (failed(parser.parseOptionalAttrDict(attrs)))
321 return failure();
322 result.attrs = attrs.getDictionary(parser.getContext());
323
324 // Parse the result location.
325 std::optional<Location> maybeLoc;
326 if (failed(parser.parseOptionalLocationSpecifier(maybeLoc)))
327 return failure();
328 result.sourceLoc = maybeLoc ? *maybeLoc : parser.getEncodedSourceLoc(irLoc);
329
330 return success();
331}
332
333/// Parse a single argument with the following syntax:
334///
335/// output (id|string) : !type { optionalAttrDict} loc(optionalSourceLoc)`
336/// (input|inout) %ssaname : !type { optionalAttrDict} loc(optionalSourceLoc)`
337///
338static ParseResult parsePort(OpAsmParser &p,
340 NamedAttrList attrs;
341 if (parseDirection(p, result.direction))
342 return failure();
343 if (result.direction == ModulePort::Direction::Output)
344 return parseOutputPort(p, result);
345 return parseInputPort(p, result);
346}
347
348static ParseResult
349parsePortList(OpAsmParser &p,
350 SmallVectorImpl<module_like_impl::PortParse> &result) {
351 auto parseOnePort = [&]() -> ParseResult {
352 return parsePort(p, result.emplace_back());
353 };
354 return p.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren, parseOnePort,
355 " in port list");
356}
357
359 OpAsmParser &parser, SmallVectorImpl<PortParse> &args, TypeAttr &modType) {
360
361 auto *context = parser.getContext();
362
363 // Parse the port list.
364 if (parsePortList(parser, args))
365 return failure();
366
367 // Process the ssa args for the information we're looking for.
368 SmallVector<ModulePort> ports;
369 for (auto &arg : args) {
370 ports.push_back(
371 {StringAttr::get(context, arg.rawName), arg.type, arg.direction});
372 // rewrite type AFTER constructing ports. This will be used in block args.
373 if (arg.direction == ModulePort::InOut)
374 arg.type = InOutType::get(arg.type);
375 if (!arg.sourceLoc)
376 arg.sourceLoc = parser.getEncodedSourceLoc(arg.ssaName.location);
377 }
378 modType = TypeAttr::get(ModuleType::get(context, ports));
379
380 return success();
381}
382
384 if (dir == ModulePort::Direction::Input)
385 return "in";
386 if (dir == ModulePort::Direction::Output)
387 return "out";
388 if (dir == ModulePort::Direction::InOut)
389 return "inout";
390 assert(0 && "Unknown port direction");
391 abort();
392 return "unknown";
393}
395 hw::HWModuleLike op) {
397 p, op.getModuleBody(), op.getHWModuleType(), op.getAllPortAttrs(),
398 op.getAllPortLocs());
399}
400
401void module_like_impl::printModuleSignatureNew(OpAsmPrinter &p, Region &body,
402 hw::ModuleType modType,
403 ArrayRef<Attribute> portAttrs,
404 ArrayRef<Location> locAttrs) {
405 bool isExternal = body.empty();
406 SmallString<32> resultNameStr;
407 mlir::OpPrintingFlags flags;
408 unsigned curArg = 0;
409 p << '(';
410 for (auto [i, port] : llvm::enumerate(modType.getPorts())) {
411 if (i > 0)
412 p << ", ";
413 p.printKeywordOrString(directionAsString(port.dir));
414 if (port.dir == ModulePort::Direction::Output) {
415 p << " ";
416 p.printKeywordOrString(port.name);
417 } else {
418 if (!isExternal) {
419 // Get the printed format for the argument name.
420 resultNameStr.clear();
421 llvm::raw_svector_ostream tmpStream(resultNameStr);
422 p.printOperand(body.front().getArgument(curArg), tmpStream);
423 p << " " << tmpStream.str();
424 // If the name wasn't printable in a way that agreed with argName, make
425 // sure to print out an explicit argNames attribute.
426 if (tmpStream.str().drop_front() != port.name) {
427 p << " ";
428 p.printKeywordOrString(port.name);
429 }
430 } else {
431 p << " %" << port.name.getValue();
432 }
433 ++curArg;
434 }
435 p << " : ";
436 p.printType(port.type);
437 if (!portAttrs.empty())
438 if (auto attr = dyn_cast<DictionaryAttr>(portAttrs[i]))
439 p.printOptionalAttrDict(attr.getValue());
440
441 // TODO: `printOptionalLocationSpecifier` will emit aliases for locations,
442 // even if they are not printed. This will have to be fixed upstream. For
443 // now, use what was specified on the command line.
444 if (!locAttrs.empty() && flags.shouldPrintDebugInfo()) {
445 auto loc = locAttrs[i];
446 if (!isa<UnknownLoc>(loc))
447 p.printOptionalLocationSpecifier(cast<Location>(loc));
448 }
449 }
450
451 p << ')';
452}
453
454/// Get a special name to use when printing the entry block arguments of the
455/// region contained by an operation in this dialect.
457 mlir::Region &region, OpAsmSetValueNameFn setNameFn) {
458 if (region.empty())
459 return;
460 // Assign port names to the bbargs.
461 auto module = cast<HWModuleOp>(region.getParentOp());
462
463 auto *block = &region.front();
464 for (size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
465 auto name = module.getInputName(i);
466 // Let mlir deterministically convert names to valid identifiers
467 setNameFn(block->getArgument(i), name);
468 }
469}
assert(baseType &&"element must be base type")
static ParseResult parseDirection(OpAsmParser &p, ModulePort::Direction &dir)
static const char * directionAsString(ModulePort::Direction dir)
static ParseResult parsePortList(OpAsmParser &p, SmallVectorImpl< module_like_impl::PortParse > &result)
ParseResult parseOptionalKeywordOrOptionalString(OpAsmParser &p, std::string &result, bool &found)
Parse an optional keyword or string and set instance into 'result'.
static StringRef getModuleResultName(Operation *module, size_t resultNo)
static ParseResult parseInputPort(OpAsmParser &parser, module_like_impl::PortParse &result)
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:
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.