TableGen: (backend) Why bit patterns are stored in reverse?

I came across this piece of code:

field bits<32> Inst;
bits<7> Opcode = 0;
let Inst{6-0} = Opcode;

In the above, the actual value of Opcode comes from an inheriting class. However, my question is, why are we storing the bit pattern for opcode in reverse?

source code

I’d wonder if doing it backwards actually changes the final value or not.

I think the TableGen statement is looking at the opcode field with bits numbered from 0 from the right, how binary encodings are often presented in an instruction set manual.

1 Like

It does:

class Foo<int Opcode> {
    field bits<4> Inst;
    let Inst{3-0} = Opcode;

class ReverseFoo<int Opcode> {
    field bits<4> Inst;
    let Inst{0-3} = Opcode;

def AFoo: Foo<10> {}
def AReverseFoo: ReverseFoo<10> {}
------------- Classes -----------------
class Foo<int Foo:Opcode = ?> {
  field bits<4> Inst = { !cast<bits<4>>(Foo:Opcode){3}, !cast<bits<4>>(Foo:Opcode){2}, !cast<bits<4>>(Foo:Opcode){1}, !cast<bits<4>>(Foo:Opcode){0} };
class ReverseFoo<int ReverseFoo:Opcode = ?> {
  field bits<4> Inst = { !cast<bits<4>>(ReverseFoo:Opcode){0}, !cast<bits<4>>(ReverseFoo:Opcode){1}, !cast<bits<4>>(ReverseFoo:Opcode){2}, !cast<bits<4>>(ReverseFoo:Opcode){3} };
------------- Defs -----------------
def AFoo {	// Foo
  field bits<4> Inst = { 1, 0, 1, 0 };
def AReverseFoo {	// ReverseFoo
  field bits<4> Inst = { 0, 1, 0, 1 };

The convention to do high bit (most significant) to low bit (least significant) likely comes from the Architecture manuals, where the most significant bit is usually on the left of the diagrams.

(and numbers in general, but specifically for encoding instructions)


Now I’m curious where we use the LSB → MSB ordering in llvm, if at all.

Searching [^\s]+\{\d+-\d+\} in *.td, at a glance nothing uses LSB->MSB order. Probably the {} range is handled generically so we had no reason to enforce an order on it.

And here is a randomly selected bit of the Arm documentation with one of these encoding diagrams, if you haven’t seen one before: Documentation – Arm Developer

Thanks to some sleuthing by @rofirrim , it turns out that we do use this in llvm in the PowerPC backend.

For example if you look up the instruction WRTEEI in this document Write MSR External Enable [Immediate] page 368 you’ll see that the encoding diagram is written with bit 0 on the left, and bit 31 on the right.

This is defined in llvm/lib/Target/PowerPC/ this way:

def WRTEEI: I<31, (outs), (ins i1imm:$E), "wrteei $E", IIC_SprMTMSR>,
              Requires<[IsBookE]> {
  bits<1> E;

  let Inst{16} = E;
  let Inst{21-30} = 163;

If the diagrams had 31 on the left and 0 on the right, it would be natural to write it as 30-21 instead.

I don’t think it was added for that purpose, it probably exists because these { } ranges are used in more places than encodings e.g. loops where you want to count up or down.

1 Like