Variadic operands using the C API

Is it possible to construct and pass operands of type Variadic or even VariadicOfVariadic via the C API? I have found no reference to them in the C API.

To make the scenario more concrete, I would like to construct a cf.switch statement using the C API. According to ControlFlowOps.td the necessary arguments are:

let arguments = (ins
    AnyInteger:$flag,
    Variadic<AnyType>:$defaultOperands,
    VariadicOfVariadic<AnyType, "case_operand_segments">:$caseOperands,
    OptionalAttr<AnyIntElementsAttr>:$case_values,
    DenseI32ArrayAttr:$case_operand_segments
  );
let successors = (successor
  AnySuccessor:$defaultDestination,
  VariadicSuccessor<AnySuccessor>:$caseDestinations
);

May anyone give me some guidance on work with these arguments? Is there any example in the codebase where the VariadicOfVariadic argument is constructed via the C API? Is this even possible, or do I have to extend the MLIR API myself to create a cf.switch operation?

Under the hood, all operands are stored in a single list. All the ‘variadic’ flavors are syntactic sugar to accessing segments of that list. A segment (or a list of segments for VariadicOfVariadic, but there can be only one) is associated with the named definition in ODS. The lengths of each portion is stored in the operand|result_segment_sizes attribute of DenseI64ArrayAttr type. You can see this by printing the IR in the generic form.

To construct such an op from The C API, one needs to provide the flattened list of operands and the operand|result_segment_sizes attributes that identify the relevant segments through the state object supplied to mlirOperationCreate. It may be useful to have C API that makes it easier to construct the state object, but I don’t think we want C API for specific operations.

Thanks @ftynse for the comprehensive answer. I found the two different attributes operand_segment_sizes and result_segment_sizes in the MLIR Code. And they indeed appear in the generic pretty-printing, e.g, here.

A segment (or a list of segments for VariadicOfVariadic , but there can be only one) is associated with the named definition in ODS.

What can be only one? Do you mean each operation can only have a single VariadicOfVariadic argument?

The lengths of each portion is stored in the operand|result_segment_sizes attribute of DenseI64ArrayAttr type.

Originally, I was left wondering how you would store lengths of each single subrange of a VariadicOfVariadic operand. However, I found the solution in the MLIR code. You have to specify another attribute that contains the single lengths of the VariadicOfVariadic.

For any future readers, here an example

func.func @main() -> () {
  ^start:
    %flag = arith.constant 1 : i32
    %a = arith.constant 10 : i32
    %b = arith.constant 11 : i32
    cf.switch %flag : i32, [
      default: ^end,
      1: ^bb0(%a : i32),
      2: ^bb1(%a, %b : i32, i32)
    ]

  ^bb0(%a0: i32):
    func.return

  ^bb1(%a1: i32, %b1: i32):
    func.return

  ^end():
    func.return
}

can be turned into the following generic IR by running mlir-opt -mlir-print-op-generic ./sample.mlir

"builtin.module"() ({
  "func.func"() ({
    %0 = "arith.constant"() {value = 1 : i32} : () -> i32
    %1 = "arith.constant"() {value = 10 : i32} : () -> i32
    %2 = "arith.constant"() {value = 11 : i32} : () -> i32
    "cf.switch"(%0, %1, %1, %2)[^bb3, ^bb1, ^bb2] {case_operand_segments = dense<[1, 2]> : tensor<2xi32>, case_values = dense<[1, 2]> : vector<2xi32>, operand_segment_sizes = dense<[1, 0, 3]> : vector<3xi32>} : (i32, i32, i32, i32) -> ()
  ^bb1(%3: i32):  // pred: ^bb0
    "func.return"() : () -> ()
  ^bb2(%4: i32, %5: i32):  // pred: ^bb0
    "func.return"() : () -> ()
  ^bb3:  // pred: ^bb0
    "func.return"() : () -> ()
  }) {function_type = () -> (), sym_name = "main"} : () -> ()
}) : () -> ()

My bad, it seems to have been updated (or implemented differently) since the discussion. It uses as a second attribute as the rest of your comment describes.