Confusion in LLVM backend's target description file

What is the difference in the behavior of the following pieces of code? What does i32 specify in the first code? What’s the procedure if I want to declare one of my operands as an immediate value of 12 bits?

def ADDrr : InstTOY<(outs GRRegs:$dst),
(ins GRRegs:$src1, GRRegs:$src2),
"add $dst, $src1,z$src2",
[(set i32:$dst, (add i32:$src1, i32:$src2))]>;
def ADDrr : InstTOY<(outs GRRegs:$dst),
(ins GRRegs:$src1, GRRegs:$src2),
"add $dst, $src1,z$src2",
[(set GRRegs:$dst, (add GRRegs:$src1, GRRegs:$src2))]>;

i32 is a type
GRRegs is a register class

Does i32 specify that $src2 will be a register of type i32, or a constant/immediate? :face_with_spiral_eyes:

The last line specifies the pattern for the selection DAG. Using i32 makes sure that the operand has the specified type - it works as a predicate.
Why would you need this? When you define the register class you specify a (possible empty) list of types this register can hold. For example, a floating point register may hold f32 or f64 values. You may then want to specify the type to limit instruction selection to that type.

To define an 12bit immediate operand, you can begin with:

let OperandType = "OPERAND_IMMEDIATE" in
  def i12imm  : Operand<i2>;

Most likely, you also want a predicate to use for the instruction selection. Both can be combined, e.g.

class ImmediateOp<ValueType vt> : Operand<vt> {
  let OperandType = "OPERAND_IMMEDIATE";

def i12imm : ImmediateOp<12>, IntImmLeaf<i12, [{
  uint64_t Val = Imm.getZExtValue();
  return isUInt<12>(Val);

Then you should be able to define:

def ADDri : InstTOY<(outs GRRegs:$dst),
(ins GRRegs:$src1, i12imm:$src2),
"add $dst, $src1,z$src2",
[(set GRRegs:$dst, (add GRRegs:$src1, i12imm:$src2))]>;

(Disclaimer: I have not run this, so there can be errors in it.)


1 Like