IsDead, IsKill

Is there anything that documents what these properties (and the other similar properties) do on the MachineOperand class?

I’m trying to debug an instruction selection issue I think. It’s hard to find documentation on what the MO properties mean.

Thanks,
Carl

Hi Carl,

Look at the comments in include/llvm/CodeGen/MachineOperand.h for the documentation of the various flags.

IsDead means that a definition is never used.
IsKill means that this is the last use of a given register.

Cheers,
-Quentin

Thanks.

I saw the header comments but it wasn’t clear to me what the difference between those concepts is?

My slightly vague understanding is IsDef means that the register specified by this operand is set by the machine instruction. So I understand that to mean the MO will override that register?

Also things like early clobber, perhaps there is another document that clarifies some of these concepts?

In an example .td file that I’m looking at, how would I tell the compiler “this will use register XX but will damage its contents so it’s value is no longer available to later instructions”?

There are two concepts that may map to what you want:

1. Tied registers: you list the operand in the ins and in the outs of the Instruction in the tablegen, and then add "$rega = $regb" to the Constraints field of Instruction. This means that the register allocation must force the two registers to be the same.
2. Early clobber: you add "@earlyclobber $reg" to the Constraints field of Instruction. This tells LLVM that the output could be written to before the input is read, so the register should not allocate $reg to any of the input registers of the instruction.

Thanks.

I saw the header comments but it wasn’t clear to me what the difference between those concepts is?

My slightly vague understanding is IsDef means that the register specified by this operand is set by the machine instruction.

Correct!

So I understand that to mean the MO will override that register?

Override in the case of a redefinition, but define otherwise.
The backends are in SSA form until register allocation (we destroy SSA during the PHIElmination pass, more precisely), so the semantic is more that the virtual register is defined since there is no redefinition in SSA.

Also things like early clobber, perhaps there is another document that clarifies some of these concepts?

Not that I know off, but I found the comment clear enough x).

So early clobber is a mean to extend the live range of a definition before the uses of the same instruction.
For instance, let say that your HW requires that the register defined by an instruction does not overlap with any of the input register, e.g., `r0 = inst r0, r1` is invalid because r0 is both a use and and def of inst, the way to tell that to the compiler is by marking the definition as early clobber.
This will effectively create an interference between the registers used by inst and its definition, such that the register allocator will not be allowed to use the same register for the definition and the rest of its operands.

In an example .td file that I’m looking at, how would I tell the compiler “this will use register XX but will damage its contents so it’s value is no longer available to later instructions”?

On top of my head, I am not sure we can precisely model exactly that.
The closest thing I can think of is using tied operand. Basically, you tie you use to a definition so it gets destroyed by this use:
op0, op1(tie:2) = inst op2, …

Here op0 would be your true definition of inst, and op1 is the “override” of op2 (op1 is tied to operand number 2).
Then you can add isDead to op1 and you have almost what you want.

This modeling is not exactly what you want, because op1 will prevent op0 to reuse the register of op2. Therefore, essentially, you artificially increase the register pressure by one locally.

For the context, the tie operands are usually used for 2-address operation, thus we don’t have this problem of a fake definition increasing the register pressure.

Hi Joshua,

Tied registers sounds like it will solve my problem, but I’m having trouble making it compile.

So I’m fixing a bug in an existing tablegen instruction. This pseudo instruction has a side effect:

  let Constraints = "@earlyclobber $reg” in
  def LDWRdPtr : Pseudo<(outs DREGS:$reg),
                        (ins PTRREGS:$ptrreg),
                        "ldw\t$reg, $ptrreg",
                        [(set i16:$reg, (load i16:$ptrreg))]>,
                 Requires<[HasSRAM]>;

The expanded hand written code loads a 16 bit value from a memory location pointed to by $ptrreg, which is one of three special “registers”.

The problem is, a side effect of the expanded code is that the pointer in $ptrreg is incremented by one. Somehow the above definition is not reflecting that so the compiler is reusing the same register as if it were unchanged, rather than reloading it or saving a copy somewhere or whatever. (Forgive me not getting the right terminology as I’m pretty new.)

Looking at similar definitions, I can see what looks like Tied registers.

Here are three very similar definitions that looks like they use the Tied registers concept…

// Indirect loads (with postincrement or predecrement).
let mayLoad = 1,
hasSideEffects = 0,
Constraints = "$ptrreg = $base_wb,@earlyclobber $reg" in
{
  def LDRdPtrPi : FSTLD<0,
                        0b01,
                        (outs GPR8:$reg, PTRREGS:$base_wb),
                        (ins LDSTPtrReg:$ptrreg),
                        "ld\t$reg, $ptrreg+",
                        >,
                  Requires<[HasSRAM]>;

  // LDW Rd+1:Rd, P+
  // Expands to:
  // ld Rd, P+
  // ld Rd+1, P+
  def LDWRdPtrPi : Pseudo<(outs DREGS:$reg, PTRREGS:$base_wb),
                          (ins PTRREGS:$ptrreg),
                          "ldw\t$reg, $ptrreg+",
                          >,
                   Requires<[HasSRAM]>;

  def LDRdPtrPd : FSTLD<0,
                        0b10,
                        (outs GPR8:$reg, PTRREGS:$base_wb),
                        (ins LDSTPtrReg:$ptrreg),
                        "ld\t$reg, -$ptrreg",
                        >,
                  Requires<[HasSRAM]>;

So on that basis, I tried to modify my definition to match but it’s not compiling…

I tried this:
  // LDW Rd+1:Rd, P
  //
  // Expands to:
  // ld Rd, P+
  // ld Rd+1, P
  let Constraints = "$ptrreg = $base_wb,@earlyclobber $reg" in
  def LDWRdPtr : Pseudo<(outs DREGS:$reg, PTRREGS:$base_wb),
                        (ins PTRREGS:$ptrreg),
                        "ldw\t$reg, $ptrreg",
                        [(set i16:$reg, (load i16:$ptrreg))]>,
                 Requires<[HasSRAM]>;

And I now when I run ninja I get the compile error:

...
FAILED: lib/Target/AVR/AVRGenInstrInfo.inc.tmp
cd /Users/carlpeto/llvm/llvm2/llvm-avr/build/llvm-patch2 && /Users/carlpeto/llvm/llvm2/llvm-avr/build/llvm-patch2/bin/llvm-tblgen -gen-instr-info -I /Users/carlpeto/llvm/llvm2/llvm-avr/lib/Target/AVR -I /Users/carlpeto/llvm/llvm2/llvm-avr/include -I /Users/carlpeto/llvm/llvm2/llvm-avr/lib/Target /Users/carlpeto/llvm/llvm2/llvm-avr/lib/Target/AVR/AVR.td -o lib/Target/AVR/AVRGenInstrInfo.inc.tmp -d lib/Target/AVR/AVRGenInstrInfo.inc.d
LDWRdPtr: (set i16:{ *:[i16] }:$reg, (load:{} i16:{ *:[i16] }:$ptrreg))
Included from /Users/carlpeto/llvm/llvm2/llvm-avr/lib/Target/AVR/AVR.td:34:
/Users/carlpeto/llvm/llvm2/llvm-avr/lib/Target/AVR/AVRInstrInfo.td:1157:3: error: In LDWRdPtr: Operand $base_wb does not appear in the instruction pattern
  def LDWRdPtr : Pseudo<(outs DREGS:$reg, PTRREGS:$base_wb),
  ^

Can anyone suggest what I’m doing wrong?

Cheers,
Carl

Hi Joshua,

Tied registers sounds like it will solve my problem, but I’m having trouble making it compile.

So I’m fixing a bug in an existing tablegen instruction. This pseudo instruction has a side effect:

let Constraints = "@earlyclobber $reg” in
def LDWRdPtr : Pseudo<(outs DREGS:$reg),
                       (ins PTRREGS:$ptrreg),
                       "ldw\t$reg, $ptrreg",
                       [(set i16:$reg, (load i16:$ptrreg))]>,
                Requires<[HasSRAM]>;

The expanded hand written code loads a 16 bit value from a memory location pointed to by $ptrreg, which is one of three special “registers”.

The problem is, a side effect of the expanded code is that the pointer in $ptrreg is incremented by one. Somehow the above definition is not reflecting that so the compiler is reusing the same register as if it were unchanged, rather than reloading it or saving a copy somewhere or whatever. (Forgive me not getting the right terminology as I’m pretty new.)

Looking at similar definitions, I can see what looks like Tied registers.

Here are three very similar definitions that looks like they use the Tied registers concept…

// Indirect loads (with postincrement or predecrement).
let mayLoad = 1,
hasSideEffects = 0,
Constraints = "$ptrreg = $base_wb,@earlyclobber $reg" in
{
def LDRdPtrPi : FSTLD<0,
                       0b01,
                       (outs GPR8:$reg, PTRREGS:$base_wb),
                       (ins LDSTPtrReg:$ptrreg),
                       "ld\t$reg, $ptrreg+",
                       >,
                 Requires<[HasSRAM]>;

// LDW Rd+1:Rd, P+
// Expands to:
// ld Rd, P+
// ld Rd+1, P+
def LDWRdPtrPi : Pseudo<(outs DREGS:$reg, PTRREGS:$base_wb),
                         (ins PTRREGS:$ptrreg),
                         "ldw\t$reg, $ptrreg+",
                         >,
                  Requires<[HasSRAM]>;

def LDRdPtrPd : FSTLD<0,
                       0b10,
                       (outs GPR8:$reg, PTRREGS:$base_wb),
                       (ins LDSTPtrReg:$ptrreg),
                       "ld\t$reg, -$ptrreg",
                       >,
                 Requires<[HasSRAM]>;

So on that basis, I tried to modify my definition to match but it’s not compiling…

I tried this:
// LDW Rd+1:Rd, P
//
// Expands to:
// ld Rd, P+
// ld Rd+1, P
let Constraints = "$ptrreg = $base_wb,@earlyclobber $reg" in
def LDWRdPtr : Pseudo<(outs DREGS:$reg, PTRREGS:$base_wb),
                       (ins PTRREGS:$ptrreg),
                       "ldw\t$reg, $ptrreg",
                       [(set i16:$reg, (load i16:$ptrreg))]>,
                Requires<[HasSRAM]>;

And I now when I run ninja I get the compile error:

...
FAILED: lib/Target/AVR/AVRGenInstrInfo.inc.tmp
cd /Users/carlpeto/llvm/llvm2/llvm-avr/build/llvm-patch2 && /Users/carlpeto/llvm/llvm2/llvm-avr/build/llvm-patch2/bin/llvm-tblgen -gen-instr-info -I /Users/carlpeto/llvm/llvm2/llvm-avr/lib/Target/AVR -I /Users/carlpeto/llvm/llvm2/llvm-avr/include -I /Users/carlpeto/llvm/llvm2/llvm-avr/lib/Target /Users/carlpeto/llvm/llvm2/llvm-avr/lib/Target/AVR/AVR.td -o lib/Target/AVR/AVRGenInstrInfo.inc.tmp -d lib/Target/AVR/AVRGenInstrInfo.inc.d
LDWRdPtr: (set i16:{ *:[i16] }:$reg, (load:{} i16:{ *:[i16] }:$ptrreg))
Included from /Users/carlpeto/llvm/llvm2/llvm-avr/lib/Target/AVR/AVR.td:34:
/Users/carlpeto/llvm/llvm2/llvm-avr/lib/Target/AVR/AVRInstrInfo.td:1157:3: error: In LDWRdPtr: Operand $base_wb does not appear in the instruction pattern
def LDWRdPtr : Pseudo<(outs DREGS:$reg, PTRREGS:$base_wb),
^

I am guessing it complains about the pattern (what appears between ) that doesn’t reference $base_wb.