Linearscan allocator bug?

Folks,
I'm running into something which looks like a bug in linearscan allocator. Of
course I can't be 100% sure it's not some unobvious mistake on my part, so
I'd like to hear your opinion.

First, I attach two files -- LLVM asm and the asm for my target. The problem
with assembler is: on line 171 it uses register gr2, which is copied from gr6
above, on line 161. The only predecessor of this basic block is jump on line
90. The problem is that gr6 is not initialized in the interval from the
function entry till the jump.

I also attach the debug dumps from my backend.

The basic block in question is shortcirc_done.1 (line 198 in the log). It
starts with:

    %reg1060 = phi %reg1032, mbb<shortcirc_next.0.selectcont.selectcont,

The predecessor is at line 155 and the register 1032 is assigned a value on
line 140 (in shortcirc_next.0.selectcont):

    %reg1032 = move %reg103

After register allocation the code in shortcirc_done.1 is (line 334):

    %gr2 = move %gr6

the predecessor in at line 285 and the code in shortcirc_next.0.selectcont is
(line 268)

    %gr4 = move %gr2

while I'd expect destination register should be gr6.

In fact, gr6 is first used here (line 289):

        if <>0 goto %disp(label shortcirc_done.1)
  %gr2 = move %gr4
  %gr5 = move %gr1
  %gr6 = move %gr4
  %gr1 = move %gr1

So, it's possible that jump goes to shortcirc_done.1 which then uses gr6 and
gets back results.

Any ideas?

- Volodya

test.ll (2.08 KB)

test.s (4.02 KB)

log (18.5 KB)

Hi Vladimir.

Folks,
I'm running into something which looks like a bug in linearscan allocator. Of
course I can't be 100% sure it's not some unobvious mistake on my part, so
I'd like to hear your opinion.

Yes, there's a known bug in the linear scan allocator. It causes a few
of the nightly tests to fail.

I'll defer to the experts on your analysis below, but if you're close to
finding this problem, you might want to talk to Alkis who wrote linear
scan (I think).

Reid.

First, I attach two files -- LLVM asm and the asm for my target. The problem
with assembler is: on line 171 it uses register gr2, which is copied from gr6
above, on line 161. The only predecessor of this basic block is jump on line
90. The problem is that gr6 is not initialized in the interval from the
function entry till the jump.

Okay, I see the problem. You need to tell the compiler that the
conditional branches are terminators. You're getting code that looks like
this:

<LBB7> // // shortcirc_next.0.selectcont.selectcont
        gr1 = gr1;
        gr1 = gr1;
        gr5 = 0;
        gr2 - gr5;
        if <>0 goto LBB11;
* gr2 = gr4;
* gr5 = gr1;
* gr6 = gr4;
* gr1 = gr1;
        goto LBB8;

I'm guessing that those copies are inserted by the register allocator, and
in particular, that is probably where gr6 is supposed to get it's value.
If you set the isTerminator flag on your 'if <>0 goto LBB11;' things will
probably magically start working for you, as the copies will be inserted
BEFORE the branch instead of after it.

Also, if you haven't already, you might want to teach TargetInstrInfo
that '=' is a move instruction (implement isMoveInstr), so instructinos
like 'gr1 = gr1' will go away and you'll get coallescing. :slight_smile:

-Chris

I also attach the debug dumps from my backend.

The basic block in question is shortcirc_done.1 (line 198 in the log). It
starts with:

    %reg1060 = phi %reg1032, mbb<shortcirc_next.0.selectcont.selectcont,

The predecessor is at line 155 and the register 1032 is assigned a value on
line 140 (in shortcirc_next.0.selectcont):

    %reg1032 = move %reg103

After register allocation the code in shortcirc_done.1 is (line 334):

    %gr2 = move %gr6

the predecessor in at line 285 and the code in shortcirc_next.0.selectcont is
(line 268)

    %gr4 = move %gr2

while I'd expect destination register should be gr6.

In fact, gr6 is first used here (line 289):

        if <>0 goto %disp(label shortcirc_done.1)
  %gr2 = move %gr4
  %gr5 = move %gr1
  %gr6 = move %gr4
  %gr1 = move %gr1

So, it's possible that jump goes to shortcirc_done.1 which then uses gr6 and
gets back results.

Any ideas?

- Volodya

-Chris

Chris Lattner wrote:

> First, I attach two files -- LLVM asm and the asm for my target. The
> problem with assembler is: on line 171 it uses register gr2, which is
> copied from gr6 above, on line 161. The only predecessor of this basic
> block is jump on line 90. The problem is that gr6 is not initialized in
> the interval from the function entry till the jump.

Okay, I see the problem. You need to tell the compiler that the
conditional branches are terminators. You're getting code that looks like
this:

<LBB7> // // shortcirc_next.0.selectcont.selectcont
        gr1 = gr1;
        gr1 = gr1;
        gr5 = 0;
        gr2 - gr5;
        if <>0 goto LBB11;
* gr2 = gr4;
* gr5 = gr1;
* gr6 = gr4;
* gr1 = gr1;
        goto LBB8;

I'm guessing that those copies are inserted by the register allocator, and
in particular, that is probably where gr6 is supposed to get it's value.
If you set the isTerminator flag on your 'if <>0 goto LBB11;' things will
probably magically start working for you, as the copies will be inserted
BEFORE the branch instead of after it.

Hmm.. this is what I have in td file already:

let isTerminator = 1 in
    def GOTO : Unknown<"goto">;
    def IFEQ : Unknown<"if =0 goto">;
    def IFNEQ : Unknown<"if <>0 goto">;
    .....

Should this work?

Also, if you haven't already, you might want to teach TargetInstrInfo
that '=' is a move instruction (implement isMoveInstr), so instructinos
like 'gr1 = gr1' will go away and you'll get coallescing. :slight_smile:

BTW, is it possible to set some instruction flag, instead of overriding a
function? Something like:

let isMove = 1 in
      def MOVE :.....

?

- Volodya

Hmm.. this is what I have in td file already:

let isTerminator = 1 in
    def GOTO : Unknown<"goto">;
    def IFEQ : Unknown<"if =0 goto">;
    def IFNEQ : Unknown<"if <>0 goto">;
    .....

Should this work?

Nope, but try this:

let isTerminator = 1 in {
     def GOTO : Unknown<"goto">;
     def IFEQ : Unknown<"if =0 goto">;
     def IFNEQ : Unknown<"if <>0 goto">;
     .....
}

... aka add {}

Also, if you do 'tblgen target.td' it will spit out all of the records
to stdout so you can visually inspect them.

> Also, if you haven't already, you might want to teach TargetInstrInfo
> that '=' is a move instruction (implement isMoveInstr), so instructinos
> like 'gr1 = gr1' will go away and you'll get coallescing. :slight_smile:

BTW, is it possible to set some instruction flag, instead of overriding a
function? Something like:

let isMove = 1 in
      def MOVE :.....

That would make sense, but no not currently. :slight_smile: If you wanted to teach
it how to do that (similar to the isTerminator functionality), it would be
a great patch. :slight_smile:

-Chris

Chris Lattner wrote:

> let isTerminator = 1 in
> def GOTO : Unknown<"goto">;
> def IFEQ : Unknown<"if =0 goto">;
> def IFNEQ : Unknown<"if <>0 goto">;
> .....
>
> Should this work?

Nope, but try this:

let isTerminator = 1 in {
     def GOTO : Unknown<"goto">;
     def IFEQ : Unknown<"if =0 goto">;
     def IFNEQ : Unknown<"if <>0 goto">;
     .....
}

... aka add {}

Uhm :wink: This change seems to fix things.

Also, if you do 'tblgen target.td' it will spit out all of the records
to stdout so you can visually inspect them.

> > Also, if you haven't already, you might want to teach TargetInstrInfo
> > that '=' is a move instruction (implement isMoveInstr), so instructinos
> > like 'gr1 = gr1' will go away and you'll get coallescing. :slight_smile:
>
> BTW, is it possible to set some instruction flag, instead of overriding a
> function? Something like:
>
> let isMove = 1 in
> def MOVE :.....

That would make sense, but no not currently. :slight_smile: If you wanted to teach
it how to do that (similar to the isTerminator functionality), it would be
a great patch. :slight_smile:

I'll probably look at the code.

- Volodya

> BTW, is it possible to set some instruction flag, instead of overriding a
> function? Something like:
>
> let isMove = 1 in
> def MOVE :.....

That would make sense, but no not currently. :slight_smile: If you wanted to teach
it how to do that (similar to the isTerminator functionality), it would be
a great patch. :slight_smile:

Actually, I just realized why this isn't possible: isMoveInstr is a
predicate that returns three things: the boolean and the two registers
being moved.

The idea is that a move does not necessarily have to be a simple: 'X = Y'
type of operation. On RISC machines, it might be an 'or' with the zero
register. In particular, the method takes an instance of a MachineInstr
because some instances of a particular opcode might be moves and some
might not be.

Sorry for leading you astray!

-Chris