LLVM Discussion Forums

Type Conversion

Hi.

I would like to convert from my custom dialect types to standard types.

I just want to replace all instances of my type with a different type.

I’m struggling to implement this - The doc page on TypeConverter and ConversionPattern are pretty vague.

Are there any examples anywhere of how to do this? Some pointers would be greatly appreciated.

For future reference (in case any other beginner is ever in my situation), I think the easiest way to achieve this is to use Operation::walk to walk all the ops you want to convert the type for, then call typeConverter::convertType on each operation.

I do this in my lowering pass before my call to applyPartialConversion. So far it’s worked fine - but there is likely a better way that I’m not seeing.

This doesn’t seem sound in the general case. Many ops have assumptions on the types of their operands and will trigger assertions (e.g., inside .cast<SpecificType>) when these assumptions had been violated by invasive operand type changes.

Individual ConversionPatterns can hold a reference to a TypeConverter. They are in charge of applying the type conversion in a way compatible with the generated ops. Inside, these patterns can use the converter, e.g., to infer the result types of the new op. ConversionPattern::matchAndRewrite accepts a list of operands with converted types, while the original operands remain unmodified and accessible through op->getOperands(). TypeConverter provides a separate mechanism for updating region (entry block) signatures; typically the op containing the region is in charge of applying those.

Standard to LLVM conversion is a good, albeit lengthy, example. The dialect conversion infrastructure was originally created to support this conversion.

Thank you, that does make sense. I guess for my simple case it always happens to work but could cause bugs in the future.

The issue is that I couldn’t figure out how to apply ConversionPatterns to ops that should remain legal in both the source and the target dialect during a lowering pass.

In particular, I have “call” ops from the standard dialect mixed with a bunch of ops from my own dialect. I then have a pass during which I want to lower to exclusively ops from the standard dialect.

So I added my dialect as an illegal dialect. But then, patterns are not being applied to call ops, because they are already considered legal. Is there a way to force patterns to also be applied to ops which are legal in both the source and target dialect? Ie. I only want to change the type of the call op, but keep the op itself.

Is the way to do this to use a canonicaliser pass instead of a lowering pass?

Generally this involves defining a “dynamic” legality that takes into account certain aspects of an operation, i.e. you can specify that an operation is only legal if it has certain properties. For type conversions with things like calls, this generally involves defining these operations as legal only when the types are legal: see the following example: https://github.com/llvm/llvm-project/blob/7c62c6313baebb4866dd51a095c66c7808af868b/mlir/test/lib/Transforms/TestDecomposeCallGraphTypes.cpp#L44

std.call(and func+a few others) has a special conversion pattern that you can add in that will just make sure that types are converted properly: https://github.com/llvm/llvm-project/blob/473bdaf2e81aef06514586e82e5e697ceabb7aa6/mlir/lib/Dialect/StandardOps/Transforms/FuncConversions.cpp#L18

Those two links are super helpful, thanks - that’s exactly what I need. I read about the dynamic legality in the docs but for some reason it didn’t occur to me that this is a good place to use it!

Thank you for taking the time to dig those out of the source code - I’m not familiar with the codebase so finding this kind of stuff takes me ages.