CIRCT  19.0.0git
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 
10 #include "circt/Dialect/HW/HWOps.h"
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 
18 using namespace circt;
19 using 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 ///
29 static ParseResult
30 parseFunctionResultList(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.
72 static 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 
85 static 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 
98 void 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.
247 ParseResult 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 
262 static 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 if (key == "inout")
272  else
273  return p.emitError(p.getCurrentLocation(), "unknown port direction '")
274  << key << "'";
275  return success();
276 }
277 
278 static ParseResult parseInputPort(OpAsmParser &parser,
279  module_like_impl::PortParse &result) {
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 
305 static ParseResult parseOutputPort(OpAsmParser &parser,
306  module_like_impl::PortParse &result) {
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 ///
338 static ParseResult parsePort(OpAsmParser &p,
339  module_like_impl::PortParse &result) {
340  NamedAttrList attrs;
341  if (parseDirection(p, result.direction))
342  return failure();
344  return parseOutputPort(p, result);
345  return parseInputPort(p, result);
346 }
347 
348 static ParseResult
349 parsePortList(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 
383 static const char *directionAsString(ModulePort::Direction dir) {
384  if (dir == ModulePort::Direction::Input)
385  return "in";
387  return "out";
388  if (dir == ModulePort::Direction::InOut)
389  return "inout";
390  assert(0 && "Unknown port direction");
391  abort();
392  return "unknown";
393 }
394 
396  HWModuleLike op) {
397 
398  Region &body = op.getModuleBody();
399  bool isExternal = body.empty();
400  SmallString<32> resultNameStr;
401  mlir::OpPrintingFlags flags;
402  unsigned curArg = 0;
403 
404  auto modType = op.getHWModuleType();
405  auto portAttrs = op.getAllPortAttrs();
406  auto locAttrs = op.getAllPortLocs();
407 
408  p << '(';
409  for (auto [i, port] : llvm::enumerate(modType.getPorts())) {
410  if (i > 0)
411  p << ", ";
412  p.printKeywordOrString(directionAsString(port.dir));
413  if (port.dir == ModulePort::Direction::Output) {
414  p << " ";
415  p.printKeywordOrString(port.name);
416  } else {
417  if (!isExternal) {
418  // Get the printed format for the argument name.
419  resultNameStr.clear();
420  llvm::raw_svector_ostream tmpStream(resultNameStr);
421  p.printOperand(body.front().getArgument(curArg), tmpStream);
422  p << " " << tmpStream.str();
423  // If the name wasn't printable in a way that agreed with argName, make
424  // sure to print out an explicit argNames attribute.
425  if (tmpStream.str().drop_front() != port.name) {
426  p << " ";
427  p.printKeywordOrString(port.name);
428  }
429  } else {
430  p << " %" << port.name.getValue();
431  }
432  ++curArg;
433  }
434  p << " : ";
435  p.printType(port.type);
436  if (!portAttrs.empty())
437  if (auto attr = dyn_cast<DictionaryAttr>(portAttrs[i]))
438  p.printOptionalAttrDict(attr.getValue());
439 
440  // TODO: `printOptionalLocationSpecifier` will emit aliases for locations,
441  // even if they are not printed. This will have to be fixed upstream. For
442  // now, use what was specified on the command line.
443  if (flags.shouldPrintDebugInfo()) {
444  auto loc = locAttrs[i];
445  if (!isa<UnknownLoc>(loc))
446  p.printOptionalLocationSpecifier(cast<Location>(loc));
447  }
448  }
449 
450  p << ')';
451 }
assert(baseType &&"element must be base type")
@ Input
Definition: HW.h:35
@ Output
Definition: HW.h:35
@ InOut
Definition: HW.h:35
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.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
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 printModuleSignatureNew(OpAsmPrinter &p, HWModuleLike op)
static StringAttr getNameFromSSA(MLIRContext *context, StringRef name)
Get a name from an SSA value string, if said value name is not a number.
Definition: ParsingUtils.h:25
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21