[GISel/InstructionSelect] Assertion: Register class not set, wrong accessor?

Hello,

I am stuck with the assertion “Register class not set, wrong accessor” in InstructionSelect and would like to learn where I am doing wrong.

Let’s say I have the following gMIR after RegBankSelect

  %9:src64r(p1) = G_PTR_ADD %0:ptr64r, %8:dst32r(s32)
  %11:dst32r(s32) = G_LOAD %9:ptr64r(p1) :: (load (s32) from %ir.arrayidx, !tbaa !8, addrspace 1)

My target has src32r and dst32r register banks. The dest register bank is a subset of source register bank.

My tablegen will match G_LOAD (G_PTR_ADD %0:ptr64r, %8:dst32r(s32)), and translate it to:

  %21:src32 = COPY %8:dst32r(s32)
  %11:dst32(s32) = LOAD %0:ptr64(p1), %21:src32 :: (load (s32) from %ir.arrayidx, !tbaa !8, addrspace 1)

Note there is an unpleasant COPY, and even worse, the %8:dst32r is a RegisterBank, not a RegisterClass.

This causes InstructionSelect hitting the assertion failure “Register class not set, wrong accessor”.

I am not sure if the root cause is that on my target, the output register class/bank is a subset of input register class/bank, and I forgot to setup something to teach GISel to not emit the copy?

def v32 : RegisterClass<"x", [i32, f32], 32, ..> {}
def u32 : RegisterClass<"x", [i32, f32], 32, ..> {}

// Src32 is used as the source operand of an instruction
def Src32 : RegisterClass<"x", [i32, f32], 32, (add v32, u32)>;

// Dst32 is used as the dest of an instruction
def Dst32 : RegisterClass<"x", [i32, f32], 32, (add v32)>;

def Src32RegBank : RegisterBank<"Src32R", [Src32]>;
def Dst32RegBank : RegisterBank<"Dst32R", [Dst32]>;

def LOAD : Instr<"ld", [], (outs Dst32:$rd),
                 (ins Ptr64:$ptr, Src32:$offset)>;

In my gMIR example, I can see after GIR_BuildMI, GIR_Copy, GIR_Copy, GIR_Copy, tablegen builds the LOAD with the original operands:

  %11:dst32r(s32) = LOAD %0:src64r(p1), %8:dst32r(s32)

After GIR_ConstrainSelectedInstOperands, tablegen adds the COPY because it thinks dst32r is not compatible with src32r.
Moreover %8:dst32r(s32) is not converted it to proper RegisterClass.

  %21:src32 = COPY %8:dst32r(s32)
  %11:dst32(s32) = LOAD %0:src64(p1), %21:src32

Could someone please give some hints?
Thanks,
CY

Hi!

If I interpret your description correctly, then you want only one register bank. Basically, the register banks come into play when you have registers in different register files, and you need to move data between these files. Think of general purpose registers and vector registers.
I assume when you use just one register class, then the COPY is not generated, because there is no cross-bank move, and your problem should be solved. That register u32 can’t be used as a destination register is modeled by the Dst32 register class, and it should work fine.

After GIR_ConstrainSelectedInstOperands, tablegen adds the COPY because it thinks dst32r is not compatible with src32r .

That is the result of having 2 register banks, because with this you declare that src32r and dst32r are very different entities.

Regards,
Kai

1 Like

Hey @redstar

Hmmmm… yes it works… wow ???

I was confused by RegBank totally… because on my target, v32 and u32 are different banks physically!! When I read AArch64 and AMDGPU’s implementation, I noticed they only define few register banks, I don’t know if they are also seeing something similar?

In my case, if I complete remove Dst32RegBank and everything works!

+ // remove Dst32RegBank, only use one reg bank for 32-bit reg.
  def Src32RegBank : RegisterBank<"Src32R", [Src32]>;
- def Dst32RegBank : RegisterBank<"Dst32R", [Dst32]>;

I also noticed that I had to use larger set of RegisterClass, for example:

def Src32RegBank : RegisterBank<"Src32R", [v32, u32]>;

This doesn’t work when GISel needs to constrain Src32R (RegisterBank) to a RegisterClass, it will fail in RBI.constrainGenericRegister and cause tablegen to generate COPY and won’t pick proper RegisterClass for source operand:

GIR_ConstrainSelectedInstOperands
-> constrainSelectedInstRegOperands
   -> constrainOperandRegClass
      -> constrainRegToClass
         -> RBI.constrainGenericRegister

            // RC is Src32, RB is Src32R and only contains v32 and u32
            // RegisterClass, so RB doesn't covert RC.
            if (RB && !RB->covers(RC))
              return nullptr;

Thanks for your help :slight_smile:
CY

:grinning: Happy to help!
This section about register banks in the Generic Machine IR docs might give you some more insight.

Kai

1 Like

Thanks! The document is very helpful :slight_smile:

1 Like