Declarative asm format custom directive on attr-dict

Hi all-

Is there any way to get a custom directive to operate on attr-dict? I’m looking to do (more or less) this:

  let assemblyFormat = [{
    $foo `(` $inputs `)` custom<ResultNames>( attr-dict ) `:` type( $results )

I’m trying to do the same thing as @clattner a while back but while using the declarative asm format. (TL;DR: looking to save the original value names and persisting through OpAsmPrinter renames using attr-dict.) I think this would be the simplest way to do it but I’m open to suggestions.

It was simple enough to do, if y’all think this is the right way to go:

Alternatively, for parsing we could just pass in result in the generated parsing code (not to be confused with $results) to have some symmetry with the printing.

Do you have an example of how you would use your posted patch (May be a good test case to put there)? I assume one reason why you want to pass the attribute dictionary to the custom directive is to elide the additional name attributes?

Yes I do, but it’s a bit complex for a test case. I’ll write a test if you agree that this is a reasonable thing to do.

The use case is embedded in this PR. My use case is exactly the same as Chris’. Value names are being used as wire names so are sometimes important for correctness. The relevant code is:

 let assemblyFormat = [{  $instanceName $moduleName `(` $inputs `)` custom<ResultNames>( attr-dict ) `:` functional-type($inputs, results) }];
// Code is messy -- just a prototype

/// This function parses the attr-dict looking for embedded result value names.
/// If it doesn't find any, it will set them based on the the result value names
/// in the asm.
ParseResult parseResultNames(OpAsmParser &p, NamedAttrList &attrDict) {
  MLIRContext *ctxt = p.getBuilder().getContext();
  if (p.parseOptionalAttrDict(attrDict))
    return failure();

  for (size_t i = 0, e = p.getNumResults(); i < e; ++i) {
    SmallString<16> id = llvm::formatv("rtl.rname{0}", i);
    Identifier resultNameKey = Identifier::get(id, ctxt);
    if (attrDict.getNamed(resultNameKey))

    StringAttr resultName = StringAttr::get(p.getResultName(i).first, ctxt);
    attrDict.push_back(NamedAttribute(resultNameKey, resultName));
  return success();

/// Suggest to the asm printer to use the result name embedded in our attributes.
void RTLInstanceOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
  NamedAttrList attrs = getAttrs();
  for (auto it = attrs.begin(), e = attrs.end(); it < e; ++it) {
    if (it->first.strref().startswith("rtl.rname")) {
      auto numberStr = it->first.strref().substr(9);
      size_t resultIdx;
      if (!numberStr.getAsInteger<size_t>(10, resultIdx))

/// Print the result names only where they do not match the names the
/// printer is going to use. Elide the rest.
void printResultNames(OpAsmPrinter &p, RTLInstanceOp *op) {
  SmallVector<StringRef, 8> elideFields = {"instanceName", "moduleName"};

  NamedAttrList attrs = op->getAttrs();
  for (auto it = attrs.begin(), e = attrs.end(); it < e; ++it) {
    StringRef attrName = it->first.strref();
    if (attrName.startswith("rtl.rname")) {
      auto numberStr = attrName.substr(9);
      size_t resultIdx;
      if (numberStr.getAsInteger<size_t>(10, resultIdx))

      // This code ain't pretty but it works.
      SmallString<32> resultNameStr;
      llvm::raw_svector_ostream tmpStream(resultNameStr);
      p.printOperand(op->getResult(resultIdx), tmpStream);
      auto expectedName = it->second.dyn_cast<StringAttr>();
      if (expectedName &&
          tmpStream.str().drop_front() == expectedName.getValue()) {
  p.printOptionalAttrDict(op->getAttrs(), elideFields);

In printing, we need to elide some of the attributes based on dynamic information. In parsing, we also need to add attributes based on information which (AFAICT) is only available in DialogAsmParser.