Encoding instructions with inconsistent formats

I’m attempting to implement codegen support for the AVR ST/LD family of instructions.

The binary encoding is not particularly consistent – take a look at this table of variants of LD, along with their machine code representation:

load 8 bits from pointer register X into general purpose Rd

ld Rd, X 1001 000d dddd 1100
ld Rd, X+ 1001 000d dddd 1101 (load with postincrement)
ld Rd, -X 1001 000d dddd 1110 (load with predecrement)

ld Rd, Y 1000 000d dddd 1000
ld Rd, Y+ 1001 000d dddd 1001
ld Rd, -Y 1001 000d dddd 1010

ld Rd, Z 1000 000d dddd 0000
ld Rd, Z+ 1001 000d dddd 0001
ld Rd, -Z 1001 000d dddd 0010

^

Note this one inconsistent bit

One way to solve this solution would be to to describe them in InstrInfo.td as seperate instructions. Note that R27R26 is a pointer register defined in AVRRegisterInfo.td, and ‘X’ is an alias for this.

let Uses = [R27R26],
canFoldAsLoad = 1,
isReMaterializable = 1 in
def LDRdX :   FSTLDPtrReg<0b0,
                          (outs GPR8:$reg),
                          (ins),
                          "ld\t$reg, X",
                          [(set GPR8:$reg, (load R27R26))]>;
def LDRdY : FSTLDPtrReg<0b0,
// ...

When I do this, however, I get errors that the pointer register is an invalid operand type.

Another solution might be to have a generic ‘LD’ pseudo instruction, which is later lowered into one of the 9 variants, which each have the correct encoding. This is a messy, hacky solution, which also bloats the code a fair bit more.

One last solution is to ‘make up a pattern’ for the inconsistent bit that fits.
It is:

Inconsistent bit = (postinc OR predec) OR IsPtrXReg

Where ‘postinc’ and ‘predec’ is 1 if the respective mode is used, and 0 otherwise, and IsPtrXReg is 1 if the pointer register is X.

I have tried to assign this bit using this expression with macros in AVRInstrInfo.td, but I couldn’t get it to compile (!or is not defined, and I don’t think macros will solve this situation regardless).

Hi Dylan,

One way to view those instructions is that the extra bit is actually
part of how the register is encoded. You could probably create a new
Operand subclass with a custom EncoderMethod that gets assigned to
both the dddd and the odd field.

Something like:

def BasicMemXYZ : Operand<i16> {
  let MIOperandInfo = (ops XYZReg); // Sorry, don't know what you've called it
  let CustomEncoder = "encodeXYZForBasicMem";
}

Associate that operand with 5 bits in the instruction instead of 4 and
let the C++ set the final bit as appropriate.

Cheers.

Tim.

I can’t get it to work with pattern matching. My operand is defined like so:

def LDSTPtrReg : Operand<i16>
{
    let MIOperandInfo = (ops PTRREGS);
    let EncoderMethod = "encodeLDSTPtrReg";
}

I am able to use it in the place of PTRREGS in the definition of the LD instruction, but if I use it in an instruction matching pattern, compilation fails with the error “Unknown leaf kind: LDSTPtrReg:i16:$ptrreg”.

Hi Dylan,

I am able to use it in the place of PTRREGS in the definition of the LD
instruction, but if I use it in an instruction matching pattern, compilation
fails with the error "Unknown leaf kind: LDSTPtrReg:i16:$ptrreg".

I think it should work if you just use "i16:$ptrreg" in the pattern
rather than specifying the new operand type: "(set GPR8:$reg, (load
i16:$ptrreg))".

Cheers.

Tim.