MLIR untyped


Is it possible to represent untyped functions in MLIR.


function(variable) {
return variable + 1;

The above function is a valid representation if the type of variable was int or pointer.
When it is lowered, it might need to be separated out depending on the type of the caller parameter, but sometimes not. E.g. the above could be represented in x86 assembler with exactly the same machine level instructions if the calling param type was i64 or a pointer.

This of course gets more complex when the function has more than one parameter.
for example:
function2(value1, value2)
return value1+value2;

In the above case, when coming to actually call this function, one cannot call it with value1 being a pointer and value2 being a pointer, because you cannot add pointers together. Only pointer + offset can be added together.

So, my question is, can these sorts of functions be described in MLIR, and can one run passes over them?
For example one pass, might be, expand the function to create all the possible functions with their associated types in order to verify that all the callers can be handled. I.e. no callers with both value1 and value2 being pointers.

Kind Regards


Hey James,

One can effectively do so by introducing a placeholder type. E.g., introduce a type !my_dialect.undetermined and then one can still have functions, operations and run passes on them. Now for most ops today that would fail verification (e.g., op X verifies that it is operating on integer type, and as undetermined isn’t integer, it fails verification - rather than not failing as there is insufficient information to fail) and so it wouldn’t work well with existing ops. For your own dialect it would work fine, but wouldn’t compose as well [perhaps that is fine and you are operating on a “template”'esque level and so these define with ops in your dialect and get instantiated & specialized before lowering to other dialects].

A proposal/discussion that is similar is [RFC] Tensors with unknown element types - #24 by mehdi_amini which could have been used here to say all functions operating on tensors of unknown element & unranked size. One can still represent scalars as tensors, but could be cumbersome.

There is also another option: decoupling type verification from op verification. So that one can have operations that operate on arbitrary types and only verify supported types when it matters (e.g., why not allow std.addi to operate on type !my_dialect.banana and only verify when it matters). This is a little bit extreme, Allowing Types to have interfaces and/or traits (like ops do) is less so [I have an idea in that direction different than the one proposed, with less coupling to the type which I should try and just do a scratch work of so that holes could be poked at :slight_smile: ]

This seems to have come up a few times in different context (thinking of another internal discussion from this past week), so might be worth a ODM discussion actually.

– Jacques

Dialect types are the way to go here – and as Jacques says above, there are various proposals for making the facilities better for doing this kind of thing. When I see this come up, it is in specific contexts for lowering specific languages (vs , say, literally wanting a low level, untyped asm form).

While young, here are some links to how I have started to solve this for compiling python:

The next level of it for the above is a modeling of extern functions that are pattern overloaded/type generic. I’ve got ideas about the ir structures that would be helpful in that and am hoping to sketch some things out next week.

I’m not aware if anyone else is trying things in this area. I could definitely see us, with some further elaboration of specific cases, creating a dialect in the llvm repo with types/interfaces/transforms that could be useful in these situations.