To reduce verbosity in the resulting assembly, AsmPrinter
can generate aliases for frequently used types and attributes.
For example, !my_dialect.type<a=3,b=4,c=5,d=tuple,e=another_type>
and #my_dialect.attr<a=3>
can be aliased to !my_dialect_type
and #my_dialect_attr
, simplifying further references.
To enable this, the owning dialect of these types/attributes can define an interface to hook into the AsmPrinter
. This is effective only when the assembly is not printed in generic form.
// OpAsmDialectInterface is defined in // https://github.com/llvm/llvm-project/blob/91ab10e8d6c256d841da1a1a1b47c334e08d95b9/mlir/include/mlir/IR/OpImplementation.h#L1738 struct MyDialectOpAsmDialectInterface : public OpAsmDialectInterface { public: using OpAsmDialectInterface::OpAsmDialectInterface; AliasResult getAlias(Type type, raw_ostream& os) const override { if (mlir::isa<MyType>(type)) { os << "my_dialect_type"; // Could return OverridableAlias when // allowing other dialect to override the alias. // // Other dialects are allowed to provide alias for // type/attribute not owned by them // but the final result would depend on the registration order // of these dialects in the MLIRContext return AliasResult::FinalAlias; } return AliasResult::NoAlias; } AliasResult getAlias(Attribute attr, raw_ostream& os) const override { if (mlir::isa<MyAttribute>(attr)) { os << "my_dialect_attr"; return AliasResult::FinalAlias; } return AliasResult::NoAlias; } }; void MyDialect::initialize() { // register the interface to the dialect addInterface<MyDialectOpAsmDialectInterface>(); }
getAlias
provides an alias with a trailing digit, AsmPrinter
appends an underscore to avoid conflicts with autogenerated IDs.getAlias
, a number is appended to the alias to avoid conflicts.An Operation
can suggest the SSA name prefix using OpAsmOpInterface
.
For example, arith.constant
will suggest a name like %c42_i32
for its result:
include "mlir/IR/OpAsmInterface.td" def Arith_ConstantOp : Op<Arith_Dialect, "constant", [ConstantLike, Pure, DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>]> { ... }
And the corresponding method:
// from https://github.com/llvm/llvm-project/blob/5ce271ef74dd3325993c827f496e460ced41af11/mlir/lib/Dialect/Arith/IR/ArithOps.cpp#L184 void arith::ConstantOp::getAsmResultNames( function_ref<void(Value, StringRef)> setNameFn) { auto type = getType(); if (auto intCst = llvm::dyn_cast<IntegerAttr>(getValue())) { auto intType = llvm::dyn_cast<IntegerType>(type); // Sugar i1 constants with 'true' and 'false'. if (intType && intType.getWidth() == 1) return setNameFn(getResult(), (intCst.getInt() ? "true" : "false")); // Otherwise, build a complex name with the value and type. SmallString<32> specialNameBuffer; llvm::raw_svector_ostream specialName(specialNameBuffer); specialName << 'c' << intCst.getValue(); if (intType) specialName << '_' << type; setNameFn(getResult(), specialName.str()); } else { setNameFn(getResult(), "cst"); } }
Similarly, an Operation
can suggest the name for its block arguments using getAsmBlockArgumentNames
method in OpAsmOpInterface
.
For custom block names, OpAsmOpInterface
has a method getAsmBlockNames
so that the operation can suggest a custom prefix instead of a generic ^bb0
.
An Operation
can indicate that the nested region in it has a default dialect prefix, and the operations in the region could elide the dialect prefix.
For example, in a func.func
op all func
prefix could be omitted:
include "mlir/IR/OpAsmInterface.td" def FuncOp : Func_Op<"func", [ OpAsmOpInterface ... ]> { let extraClassDeclaration = [{ /// Allow the dialect prefix to be omitted. static StringRef getDefaultDialect() { return "func"; } }]; }
func.func @main() { // actually func.call call @another() }