Instruction Selection sanity check

Hi,

I am working on a new back-end for LLVM. This architecture has two register types, data(A) and accumulator(B).

A registers are i32 where as B registers are i64. This is causing me some headaches, as far as I can tell, it’s not really possible to mix the two using tablegen?

In the hardware, every instruction can either take an A register or a B register, in tablegen (as far as I can understand) this is not possible.

I ended up creating instructions like

MOV32ri (register immediate)
MOV32rr (register register)
MOV64rr, MOV64ri etc.

I’ve done this for essentially every instruction. This kind of works, but there are issues.

It results in unneeded copies between A registers and B registers. If a value is in an A register and the other is in a B, LLVM will do a copy between registers to make sure both registers are the same “type”(same bank) before doing the operation.

Is there any way around this? Also is it really necessary to define a different instruction for each register type (even though it’s the same instruction in hardware, with the same encoding etc).

Another issue is that when multiplying, the result must always be stored in a B (accumulator register). So I did some custom lowering for the MUL node, and got the instruction to always write the result to a B register.

The problem is that later on LLVM will move it from a B register to an A register which means the result getting truncated (64 bit → 32 bit). I am also unsure how to deal with this.

I just want to confirm that I’m doing this somewhat correctly and there isn’t a much more obvious and better way of doing this.

Kind Regards,

Jonathan

Hi,

I am working on a new back-end for LLVM. This architecture has two register
types, data(A) and accumulator(B).

A registers are i32 where as B registers are i64. This is causing me some
headaches, as far as I can tell, it's not really possible to mix the two
using tablegen?

In the hardware, every instruction can either take an A register or a B
register, in tablegen (as far as I can understand) this is not possible.

I ended up creating instructions like

MOV32ri (register immediate)
MOV32rr (register register)
MOV64rr, MOV64ri etc.

I've done this for essentially every instruction. This kind of works, but
there are issues.

It results in unneeded copies between A registers and B registers. If a
value is in an A register and the other is in a B, LLVM will do a copy
between registers to make sure both registers are the same "type"(same
bank) before doing the operation.

Is it legal to have to mix register classes in an instruction? For example,
ADD AReg, BReg?

If so you will need add more instruction variants e.g.
ADD3264rr, ADD6432rr.

Is there any way around this? Also is it really necessary to define a
different instruction for each register type (even though it's the same
instruction in hardware, with the same encoding etc).

There is not really a good way around this, but it is not so bad if
you use multiclasses.

Another issue is that when multiplying, the result must always be stored in
a B (accumulator register). So I did some custom lowering for the MUL node,
and got the instruction to always write the result to a B register.

How are you defining your MUL instruction? If you define the MUL
instruction in tablegen with a B register as destination register,
then it should always output to a B register.

-Tom

> Hi,
>
> I am working on a new back-end for LLVM. This architecture has two

register

> types, data(A) and accumulator(B).
>
> A registers are i32 where as B registers are i64. This is causing me

some

> headaches, as far as I can tell, it's not really possible to mix the two
> using tablegen?
>
> In the hardware, every instruction can either take an A register or a B
> register, in tablegen (as far as I can understand) this is not possible.
>
> I ended up creating instructions like
>
> MOV32ri (register immediate)
> MOV32rr (register register)
> MOV64rr, MOV64ri etc.
>
> I've done this for essentially every instruction. This kind of works,

but

> there are issues.
>
> It results in unneeded copies between A registers and B registers. If a
> value is in an A register and the other is in a B, LLVM will do a copy
> between registers to make sure both registers are the same "type"(same
> bank) before doing the operation.
>

Is it legal to have to mix register classes in an instruction? For

example,

ADD AReg, BReg?

If so you will need add more instruction variants e.g.
ADD3264rr, ADD6432rr.

This is what I attempted to do. I get errors such as:

"Type inference contradiction found, merging 'i32' into 'i64' "

Which is where I got the idea that mixing types in the tablegen
instructions was not allowed.

> Is there any way around this? Also is it really necessary to define a
> different instruction for each register type (even though it's the same
> instruction in hardware, with the same encoding etc).
>

There is not really a good way around this, but it is not so bad if
you use multiclasses.

> Another issue is that when multiplying, the result must always be

stored in

> a B (accumulator register). So I did some custom lowering for the MUL

node,

> and got the instruction to always write the result to a B register.
>

How are you defining your MUL instruction? If you define the MUL
instruction in tablegen with a B register as destination register,
then it should always output to a B register.

I did do that, and that is indeed what happens. My C++ lowering code, quite
literally, just replaces ISD::MUL nodes with my target specific MUL node. I
leave the pattern empty in the tablegen description of the instruction to
avoid the previously described error. Is this the correct way of solving
this problem?

-Tom

> The problem is that later on LLVM will move it from a B register to an A
> register which means the result getting truncated (64 bit -> 32 bit). I

am

> also unsure how to deal with this.
>
> I just want to confirm that I'm doing this somewhat correctly and there
> isn't a much more obvious and better way of doing this.
>
> Kind Regards,
>
> Jonathan

> _______________________________________________
> LLVM Developers mailing list
> LLVMdev@cs.uiuc.edu http://llvm.cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev

Thanks for the help!

>
> > Hi,
> >
> > I am working on a new back-end for LLVM. This architecture has two
register
> > types, data(A) and accumulator(B).
> >
> > A registers are i32 where as B registers are i64. This is causing me
some
> > headaches, as far as I can tell, it's not really possible to mix the two
> > using tablegen?
> >
> > In the hardware, every instruction can either take an A register or a B
> > register, in tablegen (as far as I can understand) this is not possible.
> >
> > I ended up creating instructions like
> >
> > MOV32ri (register immediate)
> > MOV32rr (register register)
> > MOV64rr, MOV64ri etc.
> >
> > I've done this for essentially every instruction. This kind of works,
but
> > there are issues.
> >
> > It results in unneeded copies between A registers and B registers. If a
> > value is in an A register and the other is in a B, LLVM will do a copy
> > between registers to make sure both registers are the same "type"(same
> > bank) before doing the operation.
> >
>
> Is it legal to have to mix register classes in an instruction? For
example,
> ADD AReg, BReg?
>
> If so you will need add more instruction variants e.g.
> ADD3264rr, ADD6432rr.

This is what I attempted to do. I get errors such as:

"Type inference contradiction found, merging 'i32' into 'i64' "

Which is where I got the idea that mixing types in the tablegen
instructions was not allowed.

This error comes from the instruction patterns. You need to use types in
the patterns to match the register classes that are being used.

Since add requires inputs of the same type, your pattern would have to
be something like:

def : Pat <
  (add i32:$src0, i32:$src1)
  (ADD3264rr $src0, (i64 (MOV_B_A $src1))

;

> > Is there any way around this? Also is it really necessary to define a
> > different instruction for each register type (even though it's the same
> > instruction in hardware, with the same encoding etc).
> >
>
> There is not really a good way around this, but it is not so bad if
> you use multiclasses.
>
> > Another issue is that when multiplying, the result must always be
stored in
> > a B (accumulator register). So I did some custom lowering for the MUL
node,
> > and got the instruction to always write the result to a B register.
> >
>
> How are you defining your MUL instruction? If you define the MUL
> instruction in tablegen with a B register as destination register,
> then it should always output to a B register.
>

I did do that, and that is indeed what happens. My C++ lowering code, quite
literally, just replaces ISD::MUL nodes with my target specific MUL node. I
leave the pattern empty in the tablegen description of the instruction to
avoid the previously described error. Is this the correct way of solving
this problem?

You can still use tablegen patterns to match target specific nodes. You
just need to define them in your tablegen file.
See lib/Target/R600/AMDGPUInstrInfo.td for examples.

I'm still not sure why it would try using an A register when the MUL instruction
is defined with a B register. Do your A and B register classes overlap?

-Tom