Replacing a result value with multiple values (Error)

Hi.

I’m trying to write a lowering pass that converts an op returning a single value to multiple ops. The result of the original op will need to be replaced by all the new values.

Here is an example:

%1 = foo() : MultipleIntegersType

%2 = bar(%1)

Should get lowered to:

%1 = constant 42 : Integer
%2 = constant 56 : Integer

%3 = addi(%1, %2) : Integer

Notice that the operation “foo” with custom type “MultipleIntegersType” got lowered to multiple ops.

I wrote a custom type conversion:

typeConverter.addConversion(
      [](MultipleIntegersType t, SmallVectorImpl<Type>& result) -> llvm::Optional<LogicalResult> {
        // omitted: place correct number of integers into result here
        return success();
      });

Also in pseudo-code, here is how I wrote the lowering pattern for foo:

auto intOne = rewriter.create<IntegerOp>(...);
auto intTwo = rewriter.create<IntegerOp>(...);

rewriter.replaceOp(originalOp, ValueRange{intOne, intTwo});

However, this doesn’t seem to work: I always get some variation of “wrong number of results” on the rewriter.replaceOp line. This seems counter-intuitive to me - why hasn’t the type converter been applied, which would result in the result type being multiple integers?

Is there any way to achieve what I want? I realise I could just pack the values into a struct and lower the MultipleIntegersType into a single struct, but I’d like to avoid that if possible.

There is currently no support for one-to-many value replacement in the dialect conversion infrastructure.

Only partial support is available in block argument conversion, where it is supported by the type converter providing a way to pack multiple values back into one:

^bb0(%arg0: !compound-type):
  "use"(%arg0)

gets converted into

^bb0(%arg0: !primitive-type, %arg1: !primitive-type):
  %0 = "op-generated-by-type-converter"(%arg0, %arg1) 
      : (!primitive-type, !primitive-type) -> !compund-type

This mechanism could be potentially extended to op results, but that would likely require breaking API changes and will take time.

In the meantime, you can introduce packing and extraction ops for your types in the conversion process, and then canonicalize them away in a separate step by replacing

%0 = "pack"(%1, %2)
%3 = "get"(%0) {position=0}
%4 = "get"(%0) {position=1}
"use"(%3)
"use"(%4)

with

"use"(%1)
"use"(%2)

Thank you, this is a great solution!