I’m bringing up an old idea I had because at the first GlobalISel sync up there was a question about whether this idea had been explored before, and my answer was that we seemed to converge on FP types ad-hoc. I’d like to revisit this one more time to hear thoughts before we invest time into a particular direction.
GlobalISel was initially proposed circa 2015. It was a simpler time, when the phrase “could you pass me that 16 bit floating point value?” clearly meant it was IEEE-754 fp16. As a result,
LLT didn’t make the distinction between integer and floating-point types. The type of the operation was inferred from the other properties of the value: size, vector/scalar and the opcode of the instruction, e.g.
Extending LLT seems a natural thing to do, but doing so when we have years of development operating on plain scalars/vectors is going to be a long and error prone task. Not infeasible, but certainly arduous. The main issues I see are:
- We need to be much stricter about how we deal with vreg values in general. No longer is it possible to look through copies or propagate values without checking each time that we don’t violate the type semantics. Of course we’ll have the verifier to do this, but I’m sure we’ll have lots of places that need fixing.
- Related to the above, operations which previously didn’t need to care about types, such as merges, will have additional checks needed to propagate types to new values. This is additional cost for no benefit.
- Transitioning is expensive. We can mitigate the cost by trying to work backwards from the selector and adding support for FP types incrementally, but it’s still a large amount of work.
We can keep the current design of GISel mostly unchanged by making changes to a specifier set of operations that truly care about FP types. This shouldn’t be anywhere close to as risky of a change as FP types as it’s truly a more incremental extension rather than a complete overhaul of the MIR.
The simplest idea is to add an immediate value to all FP operations as the first input operand. This is effectively just an enum that specifies the particular FP format. Without some special handling in MIR printing this will look ugly but it should do the job. We’ll need to fix up places that deal with FP types, but these will be simple mechanical changes and, most importantly, errors will result in compile failures vs miscompiles. So FP operations will look like this:
%res:_(s16) = G_FMUL 5, %op1, %op2
5 is some encoding of the type format, or
bf16 if we implement some pretty printing.
Another route may be a complete new
MachineOperand kind that specially encodes this, which should be fairly cheap I think, but I haven’t looked into this option.
Finally, I’m assuming that adding a field to
MachineInstr is out of the question due to memory impacts, but someone can correct me if I’m wrong.