Iām not sure what this means: do you want a type that describe an integer using a width and a DictAttr? You can easily define such a type, but itās fully disconnected from the builtin IntegerType.
This isnāt possible, look at the implementation:
I am also looking for the answer to the same question. I thought to ask on this existing thread instead of creating a new one:
I created an integer type in my own dialect āfooā (as described here
The question is how to tell the framework that āfoo::IntegerTypeā is really an integer type like āmlir::IntegerTypeā
Currently, the attribute parser complains when I try to move an integer constant to a variable of my integer type (i.e. foo::IntegerType) using a constant operation I added in the foo dialect. So, for the following mlir input:
%c1 = foo.constant 5 : !foo.myint<3>
I get the following error:
error: integer literal not valid for specified type
Can someone please guide on how I can add my own integer-type such that the framework understands it like the built-in integer type?
In addition to what @mehdi_amini said above, the general premise is as follows. If you want everything to treat a type as an IntegerType, the type must be theIntegerType. There are generalizations possible via interfaces, but none implemented so far. Out of curiosity, what kind of integer type do you have that is not covered by built-in types?
Not the one asking, but from a front-end POV Iāve always wanted signed and unsigned index types or removing si* and ui* (forcing users to use actual metadata to propagate signedness information).
Weāre generalizing over IntegerType and FloatType .
The signature Iāve always had running around - though Iāve seem equivalent ones, is
ScalarTypeInterface {
/// If this type has a consistent static bitwidth, return it. This allows the `.getScalarBitwidth()` method to work.
std::optional<int64_t> getStaticBitwidth();
/// If the bitwidth of this type is data layout dependent, look up that width
/// in the data layout and return it if it is found. Otherwise, fall back to
/// getStaticBitwidth().
std::optional<int64_t> getBitwidth(DataLayout& dl) {
return getStaticBitwidth();
}
}
This would include things like pointers, as well as any newtypes of integer (ex. āIāve got a weird float, and I donāt want to get it through APFloat - I just need to be able to pass it around to my custom intrinsics and load/store itā).
Itās not clear to me why you canāt just cast/bitcast to i64 before using the load/store if these are not working on your type.
In general these kind of generalization have pretty limited uses, because while interfaces are useful to introspect information about an entity, the transformations are limited (how do you fold a binary arithmetic operation over a couple of custom integers?).
From where Iām standing, thereās often a desire for stronger type information in IRs and the ability to conjure up a newtype/wrapper struct/⦠around what might be a bag of bits post-lowering (or maybe it isnāt - maybe youāve got a target that knows about !my_machine.experimental_8_bit_thing ).
And while you canāt really do arithmetic on these things ⦠thatās partly also the point?
(To give an example, def char : TypeWrapper<$dialect, "char", I8> (or, perhaps I32) is a very sensible thin wrapper around a byte that represents a character, not an integer, and so shouldnāt be treated as an integer semantically. And that sort of type should work with memref.load, arith.bitcast, etc. ⦠so that all the standard tooling knows whatās going on even if it doesnāt know what a char is, exactly.)
Bitcasts can also make program transformations more awkward - perhaps you want different handling for a tensor<⦠x i8> and a tensor<⦠x [my byte-length type]> at the function boundary.
I meant more like, if we have signed and unsigned integer types in MLIR then we should likely have signed and unsigned index types. With the alternative being only having signless versions of ints and index.
I personally would favor removal of si* and ui*, as most upstream operations only work on signless.
I donāt quite see why considering the history and the reason behind these existing: superficial consistency is not valuable here without clear use-cases.
However, upon further checking they are being used by SPIR-V. Iād argue that SPIR-V should have itās own int type, as they are the main user. But I wonāt as Iām not going to spend time on that.
But you donāt have provided any argument in this direction: I asked ādoes it really hurt to keep them here?ā and your answer was āWhat Iām arguing for is consistencyā.
Now that Iām saying this point is artificial and there is no āconsistencyā to have here, youāre back to removal without answering the question: why?
Now that Iām saying this point is artificial and there is no āconsistencyā to have here, youāre back to removal without answering the question: why?
True.
To me, the consistency argument applies to the front-end argument.
si*/ui* are here for convenience for downstream which may use āfrontend-like semanticsā
If si* and ui* are there for downstream front-ends, then, sindex and uindex should be there under the same logic, as it will be beneficial for downstreams.
Now, I recognize that would be adding dead churm to the codebase and is not something we want, which is why I favor removal.
To explicitly answer:
does it really hurt to keep them here?
No. But, I think we shouldnāt have something we donāt plan to support ops on. Maybe in a previous era, when extending the type system was more difficult the decision made sense, now it feels mostly historical.
That is a speculation on your side: we implemented it and deployed it for specific frontends where the need for signed/unsigned index hasnāt showed up. If someone demonstrate a strong need for these that would be a different story, but in the meantime it just seems āout-of-thin-airā to me here.
Why? We decided differently when these were added, and youāre again not really making a strong case for this position.