MLIR already allows dialect author to customize the printing/parsing of their IR with “custom assembly”. This allows to make the IR more readable in the context of a specific dialect, even though it can make it harder for someone unfamiliar with the dialect to read it. This exists to various in affine
, linalg
, or the tf
(tensorflow) dialect.
This can be fairly intrusive, for example the tf_executor
dialect has an island
operation that wraps a region:
%0 = tf_executor.island {
%1 = "tf.Const"() {value = dense<1> : tensor<i32>} : () -> tensor<i32>
tf_executor.yield %1
}
However for the special case when the nested region contains a single operation, we print it as a one-liner:
%output, %control = tf_executor.island wraps "tf.Const"() {value = dense<1> : tensor<i32>} : () -> tensor<i32>
Similarly we’re looking into pattern-matching MHLO reduction to collapse the region as a one liner, right now MLIR displays:
%106 = "mhlo.reduce"(%105, %93) ( {
^bb0(%arg14: tensor<f32>, %arg15: tensor<f32>): // no predecessors
%107 = mhlo.add %arg14, %arg15 : tensor<f32>
"mhlo.return"(%107) : (tensor<f32>) -> ()
}) {dimensions = dense<0> : tensor<1xi64>} : (tensor<512x128xf32>, tensor<f32>) -> tensor<128xf32>
when XLA HLO displays much nicely as:
reduce(f32[512,128] input, f32[] 0), dimensions={0}, to_apply=add
When developing a closed system like TensorFlow or XLA with MLIR, it makes sense to allow the author of such closed system to have a syntax that will make the most sense in the context of this system, even though it can raise the bar for someone unfamiliar with the dialect.
In any case, MLIR keeps the “escape-hatch” of the generic printer which is always available.
When MLIR started, most of the IR was written with operations in the standard dialect. It was considered that it wouldn’t be nice to have to repeat std.
in front of every single operation in MLIR, since it was the main dialect it should be pretty obvious. With the same logic, when working on a “closed” TensorFlow IR, I have the same motivation to not repeat the dialect prefix either!
XLA developers enjoy reading reduce(f32[512,128] input, f32[] 0), dimensions={0}, to_apply=add
and I’d like MLIR to allow the same power to compiler authors who adopt MLIR: we shouldn’t force them to chose between a subpar experience there or not using MLIR at all!
The patch in ⚙ D107236 Add a new interface allowing to set a default dialect to be used for printing/parsing regions makes the default prefix (currently std
) configurable.
This will allow a tfg.graph
(which models GraphDef 1:1 and only allows TensorFlow operation to be expressed without repeating tfg.
in front of every single operation:
tfg.graph #tf_type.version<producer = 42, min_consumer = 33> {
%placeholder, %ctl = Placeholder : () -> (tensor<*xi32>)
%AddV2, %ctl_0 = AddV2(%placeholder, %placeholder_1) : (tensor<*xi32>, tensor<*xi32>) -> (tensor<*xi32>)
%placeholder_1, %ctl_2 = Placeholder : () -> (tensor<*xi32>)
}
I don’t anticipate any change for dialects in-tree right now with this new feature: they aren’t the target here.
The only candidate might be the SPIRV dialect which is already “closed” (no other ops than SPIRV are allowed in a SPIRV module). It could look like:
spv.module Logical GLSL450 {
func @do_nothing() -> () "None" {
Return
}
EntryPoint "GLCompute" @do_nothing
ExecutionMode @do_nothing "ContractionOff"
}
Note: I’m not proposing to change the SPIRV dialect here, just for the MLIR parser/printer to have an interface allowing other compilers to have slightly more control here, so that we’re not facing side-by-side comparisons between something like MHLO and XLA-HLO where MLIR is unnecessarily more verbose.
I also believe that this extra customization is much less intrusive and less confusing for users than anything we already allow (and do) with the custom assembly.
Something else that can be done as part of this transition, is to remove the hard-coded std.
dialect dispatch in the parser, making the std.
dialect (for what remains there still for now) on equal foot with all the other dialects.