We have been discussing lowering the mhlo dialect, which uses signed and unsigned integer types to model signedness, to linalg and the standard dialect, which uses signless integer types and models signdness semantics via different operations.
As a first step in this direction, I propose to add a cast operation that allows to cast unsigned and signed integer types to the signless integer type.
%signless = signedness_cast(%signed) : si8 to i8
%signed = signedness_cast(%signless) : i8 to si8
%unsigned = signedness_cast(%signless) : i8 to ui8
It should also be allowed to cast between tensors of corresponding types.
%signless = signedness_cast(%signed) : tensor<32xsi8> to tensor<32xi8>
%signless = signedness_cast(%signed) : memref<32xsi8> to memref<32xi8>
A cast is hence valid if it does not modify the shape and casts from a signed/unsigned type to its signless counterpart. I would not allow direct casts between signed and unsigned but I am also not strongly opposed to allowing this.
I would place this cast in the standard dialect, as it also works on scalars. If there is a strong desire, I can also add three separate ops for scalars, tensors and memref.
With this cast operation added, we can gradually lower mhlo based IR to linalg by inserting signedness_cast
operations to bridge between dialects. As a pseudo-code example, we could get
%res = mhlo.add %0, %1 : tensor<42xui32>
could be lowered to something like
%sl0 = signedness_cast %0 : tensor<42xui32> to tensor<42xi32>
%sl1 = signedness_cast %1 : tensor<42xui32> to tensor<42xi32>
%0 = linalg.init_tensor [42] : tensor<42xi32>
%res = linalg.generic {
indexing_maps = [#map, #map, #map],
iterator_types = ["parallel"] }
ins(%si0, %si1 : tensor<42xi32>, tensor<42xi32>)
outs(%0 : tensor<42xi32>) {
^bb0(%arg2: i32, %arg3: i32, %arg4: i32): // no predecessors
%2 = addi %arg2, %arg3 : i32
linalg.yield %2 : i32
} -> tensor<42xi32>
%res = signedness_cast %res : tensor<42xi32> to tensor<42xui32>