[Binding] Generate signless operands for arithmetic operations
Closed this issue · 6 comments
The current LLVM/MLIR ecosystem only accepts signless integers as the inputs and outputs for arithmetic operations, and lets specific operations do the interpretation (e.g., arith.divsi
vs arith.divui
). The rationale behind using the signless integers instead of signed/unsigned integers (si
/ui
) can be found here.
Thus, based on this design, the following code is not allowed in MLIR.
module {
func @main() {
%a = arith.constant 1 : ui32
%b = arith.constant 2 : ui32
%1 = arith.muli %a, %b : ui32
return
}
}
The verifier throws the following error:
loc("../test/test.mlir":3:14): error: 'arith.constant' op integer return type must be signless
Error can't load file ../test/test.mlir
This issue seems to come up when we upgrade to LLVM 14 that adds more type checking facilities in the verifier. The error does not only appear in arith.constant
but also in arith.addi
, arith.andi
, and arith.maxsi
, etc., so it requires our frontend compiler to map integers to signless types but also find a way to keep the signedness.
I can think of two ways to do that:
- Add CastOp for each operand and result. It preserves the signedness for each operand, but it is very inelegant since we need to add lots of
cast
before and after each operation, and finally the codegen may generate something likeunsigned a = (unsigned) ai
for cast operations, which is totally redundant.
module {
func @main() {
%a = arith.constant 1 : i32
%au = builtin.unrealized_conversion_cast %a : i32 to ui32
%b = arith.constant 2 : i32
%bu = builtin.unrealized_conversion_cast %b : i32 to ui32
%ai = builtin.unrealized_conversion_cast %au : ui32 to i32
%bi = builtin.unrealized_conversion_cast %bu : ui32 to i32
%ci = arith.muli %ai, %bi : i32
%c = builtin.unrealized_conversion_cast %ci : i32 to ui32
return
}
}
- Following the paradigm provided by LLVM, we use signless integers all the time, but how to pass the unsigned types from the frontend to the backend becomes the problem.
module {
func @main() {
%a = arith.constant 1 : i32
%b = arith.constant 2 : i32
%c = arith.muli %a, %b : i32
return
}
}
Do you have any comments on this? @zhangzhiru @zzzDavid @hecmay
Signed and unsigned semantics are added to MLIR in D72533. SPIRV had the same need to model signed and unsigned arithmetic operations, their solution is to build in-house integer Add/Mul operations. For example:
spv.func @i8_const() -> () "None" {
// CHECK: spv.Constant 0 : i8
%0 = spv.Constant 0 : i8
// CHECK: spv.Constant -1 : i8
%1 = spv.Constant 255 : i8
// CHECK: spv.Constant 0 : si8
%2 = spv.Constant 0 : si8
// CHECK: spv.Constant 127 : si8
%3 = spv.Constant 127 : si8
// CHECK: spv.Constant -128 : si8
%4 = spv.Constant -128 : si8
// CHECK: spv.Constant 0 : i8
%5 = spv.Constant 0 : ui8
// CHECK: spv.Constant -1 : i8
%6 = spv.Constant 255 : ui8
%10 = spv.IAdd %0, %1: i8
%11 = spv.IAdd %2, %3: si8
%12 = spv.IAdd %3, %4: si8
%13 = spv.IAdd %5, %6: ui8
spv.Return
}
Following the paradigm provided by LLVM, we use signless integers all the time, but how to pass the unsigned types from the frontend to the backend becomes the problem.
Can we use signless and attach attributes to pass signed/unsigned type information to HLS backend? I think building our own integer operations may be unnecessary
I adopted @zzzDavid 's advice. My current solution is attaching an unsigned
attribute to those operations involving unsigned integers, and unsigned/signed types will not appear in intermediate programs. All the integers are represented in signless type i<bit>
.
Specifically, for operations, a unit attribute is simply attached, like
%5 = memref.alloc() {name = "D", unsigned} : memref<32x32xi12>
But for function arguments, I will generate extra_itypes
and extra_otypes
in the frontend. Each character represents a type. u
is unsigned, s
is signed, and _
is other types. So the following signature means %arg0
is signed integer, %arg1
is unsigned, and the return type is also unsigned.
func @top(%arg0: memref<32x32xi12>, %arg1: memref<32x32xi12>) -> memref<32x32xi12> attributes {extra_itypes = "us", extra_otypes = "u"}
I will push the fix later and close this issue.
Let's discuss a bit more about this in the next meeting.