Op assemblyFormat and inferred operands Type

Hi,

I am having some issues to make the assemblyFormat property work correctly for my custom op, and I am having a couple of questions about it.

Here is my code:

def GOM_LoadOp : GOM_Op<"load", []> {
    let summary = "Load data from memory";
    let arguments = (ins GOM_PointerType:$ptr);
    let results = (outs GOM_Type:$result);
    let assemblyFormat = "$ptr attr-dict `:` type($result)";
}

GOM_PointerType is defined as such:

def GOM_Float : TypeAlias<AnyTypeOf<[F16, F32]>, "floating point">;

def GOM_PointerType :
    DialectType<GOM_Dialect, CPred<"$_self.isa<PointerType>()">,
                "GOM pointer type">;

def GOM_Type : AnyTypeOf<[GOM_Float, GOM_PointerType]>;

And the C++ definition is:

class PointerType : public mlir::Type::TypeBase<PointerType, mlir::Type,
                                               detail::PointerTypeStorage> {
public:
  /// Inherit some necessary constructors from 'TypeBase'.
  using Base::Base;

  static PointerType get(Type pointeeType);
  Type getPointeeType() const;
};

I am using assemblyFormt to simplify an instruction such as:

%1 = "gom.load"(%0) : (!gom.ptr<f32>) -> f32

into:

%1 = gom.load %0 : f32

But unfortunately my LoadOp TableGen definition fails to compile:

error: type of operand #0, named 'ptr', is not buildable and a buildable type cannot be inferred
$ptr attr-dict `:` type($result)

I was able to solve It by doing this instead:

def GOM_LoadOp : GOM_Op<"load", 
    [TypesMatchWith<"result type matches ipointee type of 'ptr'",
                     "ptr", "result",
                     "$_self.cast<PointerType>().getPointeeType()">]> {
    let summary = "Load data from memory";
    let arguments = (ins GOM_PointerType:$ptr);
    let results = (outs GOM_Type:$result);
    let assemblyFormat = "$ptr attr-dict `:` type($ptr)";
}

But it’s not exactly what I wanted, here the format is:

%1 = gom.load %0 : !gom.ptr<f32>

I looked more at the code, and found the error is triggered in OpFormatParser::verifyOperands (llvm-project/OpFormatGen.cpp at main · llvm/llvm-project · GitHub).
The function starts with;

// Check that all of the operands are within the format, and their types can
// be inferred.

That’s what I don’t understand, why do we need to infer the operands types ? I assume this is used by the parser. Since the operands definition were already parsed, I would have thoughts the operands types would be known already. I am missing something ?

MLIR assembly format in general does not assume that operands are already parsed and instead operations should have a self-contained format that parses in isolation of the surrounding context.

This happens in practice either when there are multiple blocks (which can be in any order) and an operand is defined in another block, or when using graph regions.
I don’t think it can happen in an isolated CFG region with a single block, but isolated regions aren’t that common I think and also we don’t define the assembly format for an op with an assumption on the region they are included in.