Defining an Operation in an attribute

Is it possible to define an operation - like scf.for - as a parameter in an attribute? I see parameters as complex types such as AffineMap, so it appears possible.

When I try adding Operation as a parameter, my first problem I encounter is attempting to unwrap() the parameter for the CAPI. The Operation type unwraps as a pointer, and I could “fix” this by defining the parameter as type Operation *. When I do my error goes away but the idiom seem broken to me - perhaps I’m wrong.

Will the whole “Operation as an attribute parameter” even work? If I have an attribute parameter of type Operation, will it accept any operation type such as scf.for, affine.if, math.abs, etc.? Do attribute parameters even allow this functionality? I ask now so I don’t spend time going down a fruitless path.

My purpose is to allow an attribute to have a parameter that includes a piece of code that describes the layout of the data structure. Perhaps there is a better way to accomplish my goal. Suggestions are always welcome.

John

I’m can’t tell you what is not possible, but I will point to an existing case similar to what you describe: [mlir] Add a shape function library op · llvm/llvm-project@e534cee · GitHub

In this case, the attribute is a well defined symbol representing a function, so any pointers are avoided.

I doubt that pointing to an Operation * is safe, and if it theoretically is, I suspect that not all serialization/deserialization logic would handle this as it seems different from what I know of current IR doing and I could easily imagine some unimplemented cases. Possibly an Attribute encoding an AbstractOperation would work, but then this would not provide the entire functionality you need.

Attributes can contain any arbitrary piece of data but there are contrains from the design:

  • the attribute must be hashable
  • the attribute will be stored and owned by the context
  • attributes are immutable

The last part is the main issue for storing an operation as an attribute : just changing any of the operand is a mutation and this happens when you replace the producers without explicitly touching an attribute ever.

So this sounds like the job for a wrapper class, say StaticOp.

Attributes cannot refer to operations. Attributes (and types) are immutable, uniqued, and immortal (cannot be deallocated) objects, and operations can be deallocated. You have a few choices here:

  • Operations to be referenced by their results from operands of other operations. The using operation can use both the attribute and the operands, but the attribute cannot refer to the operand or values. Check out how affine.apply works as an example.
  • You can put a symbol on the operation and then refer to the symbol from an attribute. We do this in the CIRCT project for various things, and it works. The major problem with this is that renaming a symbol becomes very difficult, because you have to recursively rebuild an attribute in order to rename the symbol.
  • You can have other ad-hoc strings or user-defined attributes that are symbol-like, if MLIR symbols don’t work. We do this in CIRCT because the symbol scoping rules aren’t compatible with some of our needs.

-Chris

I’m not totally sure what you’re aiming for, but another option would be a region on your operation that describes some computation that it is doing and could contain arbitrary other operations. linalg.generic is a good example of this, where the region body describes the computation done in the body of the loop nest. There are also reduction operations in various dialects that are described similarly.

1 Like