Writing OpBuilders in mlir

Hi Folks:
I am bit confused on syntax/semantics of op builders and any help on this is appreciated.
So, if i have an op defined as

def ConstOp : MyDialect<"constant", [ConstantLike]> {
   let results = (outs MyDialect_MemrefType);
   let arguments = (in ElementsAttr:$value);
   // This part is probably not right totally.
   let builder = [ OpBuilder<(ins "DenseElementsAttr":$value), [{
                               build($_builder, $_state, ?Type?, value); }]>            
                 ];
};

Then as I understand I can invoke the builder to construct ConstOp op as something like -

 const void *val ; /// points to all the initial values for dataAttribute
  std::vector<std::int64_t> dims = {4}; // just for example
  mlirType = builder.getF32Type(); //just for example
  mlir::RankedTensorType rankedType =  
             mlir::RankedTensorType::get(dims, mlirType);
  auto dataAttribute = mlir::DenseElementsAttr::get(rankedType, val);
builder.create<MyDialect::ConstOp>(builder.getUnknownLoc(),  returnType, dataAttribute)

My question - i am not following what the correspondence is between what builder.create<..> sends and what OpBuilder build expects ? Is there a cleaner better way for me to do this and construct ConstOp correctly?
Thanks a lot
Javed

Yeah unfortunately the create API is pretty confusing and confounds most IDEs. You can see how it works in the source though. The arguments after the location are forwarded to the build method along with the builder and operation state.

But in this case the build method you’re invoking via create in your C++ snippet is not the one created by your builder field in your tablegen. That’s invoking one of the default build methods that is generated. Pulling from the generated code for std.constant, these are all the build methods generated

  static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, Attribute value);
  static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, Attribute value, Type type);
  static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::mlir::Type resultType0, ::mlir::Attribute value);
  static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::mlir::TypeRange resultTypes, ::mlir::Attribute value);
  static void build(::mlir::OpBuilder &, ::mlir::OperationState &odsState, ::mlir::TypeRange resultTypes, ::mlir::ValueRange operands, ::llvm::ArrayRef<::mlir::NamedAttribute> attributes = {});

and here is the tablegen:

  let builders = [
    OpBuilder<(ins "Attribute":$value), [{ build($_builder, $_state, value.getType(), value); }]>,
    OpBuilder<(ins "Attribute":$value, "Type":$type), [{ build($_builder, $_state, type, value); }]>,
  ];

The custom builder method syntax is providing those same “trailing args” that you see in the create method. The ones other than the builder and operation state. For constant, the type is usually the same as the type of its attribute, so you can just derive it from that, and the body of the method (the second argument to the OpBuilder dag) is just invoking one of the default build methods.

Thank you . That was useful.
You mentioned

the build method you’re invoking via create in your C++ snippet is not the one created by your builder field in your tablegen

Where did i go wrong ?

Are you able to locate the build methods generated in the .h.inc file? You can just compare the signatures - the compiler errors would also tell you where the mismatches are.

1 Like

You didn’t do anything wrong :slight_smile: There are default builders and the way you’re invoking create there is going to a default builder. You don’t need to have any builders set in the op unless you want to customize. To invoke the custom builder you’ve got there, you’d want to pass only a location and an DenseElementsAttr to create.