Harald van Dijk via llvm-dev
2019-Oct-15  11:16 UTC
[llvm-dev] Assertion failure "There should be at least one conflict." in register coalescing
Hi,
Please take a look at this MIR:
name: f
body: |
   bb.0:
     %0:gr64_with_sub_8bit = COPY $rax
     %1:gr32 = MOV32rr %0.sub_32bit:gr64_with_sub_8bit
     %2:gr32 = COPY %1:gr32
     %0.sub_32bit:gr64_with_sub_8bit = COPY %2:gr32
     %3:gr32 = MOV32rr %0.sub_32bit:gr64_with_sub_8bit
     %1:gr32 = MOV32rr %2:gr32
     %0.sub_32bit:gr64_with_sub_8bit = COPY %2:gr32
This is heavily reduced from real code, translated to X86 to rule out
the possibility of a bug in the target's register definitions.
When running the Simple Register Coalescing pass on this:
   llc -run-pass=simple-register-coalescing test.mir
It triggers an assert:
   llc: /home/harald/llvm/lib/CodeGen/RegisterCoalescer.cpp:2864: bool
{anonymous}::JoinVals::resolveConflicts({anonymous}::JoinVals&): Assertion
`!TaintExtent.empty() && "There should be at least one
conflict."' failed.
   Stack dump:
   0.      Program arguments: llvm/build/bin/llc
-run-pass=simple-register-coalescing testx86.mir
   1.      Running pass 'Function Pass Manager' on module
'testx86.mir'.
   2.      Running pass 'Simple Register Coalescing' on function
'@f'
    #0 0x0000563febf583f3 llvm::sys::PrintStackTrace(llvm::raw_ostream&)
/home/harald/llvm/lib/Support/Unix/Signals.inc:532:22
    #1 0x0000563febf58486 PrintStackTraceSignalHandler(void*)
/home/harald/llvm/lib/Support/Unix/Signals.inc:593:1
    #2 0x0000563febf56334 llvm::sys::RunSignalHandlers()
/home/harald/llvm/lib/Support/Signals.cpp:68:20
    #3 0x0000563febf57dac SignalHandler(int)
/home/harald/llvm/lib/Support/Unix/Signals.inc:384:1
    #4 0x00007fdc53042f40 __restore_rt
(/lib/x86_64-linux-gnu/libpthread.so.0+0x13f40)
    #5 0x00007fdc52aebed7 gsignal
/build/glibc-KRRWSm/glibc-2.29/signal/../sysdeps/unix/sysv/linux/internal-signals.h:84:10
    #6 0x00007fdc52acd535 abort
/build/glibc-KRRWSm/glibc-2.29/stdlib/abort.c:81:7
    #7 0x00007fdc52acd40f __tls_get_addr
/build/glibc-KRRWSm/glibc-2.29/intl/loadmsgcat.c:1177:9
    #8 0x00007fdc52add012 (/lib/x86_64-linux-gnu/libc.so.6+0x35012)
    #9 0x0000563feb388d39 (anonymous
namespace)::JoinVals::resolveConflicts((anonymous namespace)::JoinVals&)
/home/harald/llvm/lib/CodeGen/RegisterCoalescer.cpp:2864:5
   #10 0x0000563feb38b2ee (anonymous
namespace)::RegisterCoalescer::joinVirtRegs(llvm::CoalescerPair&)
/home/harald/llvm/lib/CodeGen/RegisterCoalescer.cpp:3323:45
   #11 0x0000563feb38bc86 (anonymous
namespace)::RegisterCoalescer::joinIntervals(llvm::CoalescerPair&)
/home/harald/llvm/lib/CodeGen/RegisterCoalescer.cpp:3417:1
   #12 0x0000563feb3853f3 (anonymous
namespace)::RegisterCoalescer::joinCopy(llvm::MachineInstr*, bool&)
/home/harald/llvm/lib/CodeGen/RegisterCoalescer.cpp:1868:7
   #13 0x0000563feb38c1cf (anonymous
namespace)::RegisterCoalescer::copyCoalesceWorkList(llvm::MutableArrayRef<llvm::MachineInstr*>)
/home/harald/llvm/lib/CodeGen/RegisterCoalescer.cpp:3501:28
   #14 0x0000563feb38d0a9 (anonymous
namespace)::RegisterCoalescer::joinAllIntervals()
/home/harald/llvm/lib/CodeGen/RegisterCoalescer.cpp:3663:30
   #15 0x0000563feb38d3f0 (anonymous
namespace)::RegisterCoalescer::runOnMachineFunction(llvm::MachineFunction&)
/home/harald/llvm/lib/CodeGen/RegisterCoalescer.cpp:3710:17
This is with current LLVM trunk (r374866), but it is not new. When
running this with the Ubuntu-provided llc 8, it segfaults. Presumably
this has assertions disabled, and would have asserted otherwise.
Is this MIR valid? Does it contain something that Simple Register
Coalescing expects to have been removed by a previous pass already?
The extra output with -debug-only=regalloc is:
   Computing live-in reg-units in ABI blocks.
   Created 0 new intervals.
   ********** INTERVALS **********
   %0 [16r,64r:2)[64r,112r:1)[112r,112d:0)  0 at 112r 1 at 64r 2 at 16r
weight:0.000000e+00
   %1 [32r,48r:1)[96r,96d:0)  0 at 96r 1 at 32r weight:0.000000e+00
   %2 [48r,112r:0)  0 at 48r weight:0.000000e+00
   %3 [80r,80d:0)  0 at 80r weight:0.000000e+00
   RegMasks:
   ********** MACHINEINSTRS **********
   # Machine code for function f: NoPHIs
   
   0B      bb.0:
   16B       %0:gr64_with_sub_8bit = COPY $rax
   32B       %1:gr32 = MOV32rr %0.sub_32bit:gr64_with_sub_8bit
   48B       %2:gr32 = COPY %1:gr32
   64B       %0.sub_32bit:gr64_with_sub_8bit = COPY %2:gr32
   80B       dead %3:gr32 = MOV32rr %0.sub_32bit:gr64_with_sub_8bit
   96B       dead %1:gr32 = MOV32rr %2:gr32
   112B      dead %0.sub_32bit:gr64_with_sub_8bit = COPY %2:gr32
   
   # End machine code for function f.
   
   ********** SIMPLE REGISTER COALESCING **********
   ********** Function: f
   ********** JOINING INTERVALS ***********
   :
   16B     %0:gr64_with_sub_8bit = COPY $rax
           Considering merging %0 with $rax
           Can only merge into reserved registers.
   48B     %2:gr32 = COPY %1:gr32
           Considering merging to GR32 with %2 in %1
                   RHS = %2 [48r,112r:0)  0 at 48r weight:0.000000e+00
                   LHS = %1 [32r,48r:1)[96r,96d:0)  0 at 96r 1 at 32r
weight:0.000000e+00
                   merge %2:0 at 48r into %1:1 at 32r --> @32r
                   interference at %1:0 at 96r
           Interference!
   64B     %0.sub_32bit:gr64_with_sub_8bit = COPY %2:gr32
           Considering merging to GR64_with_sub_8bit with %2 in %0:sub_32bit
                   RHS = %2 [48r,112r:0)  0 at 48r weight:0.000000e+00
                   LHS = %0 [16r,64r:2)[64r,112r:1)[112r,112d:0)  0 at 112r 1 at
64r 2 at 16r weight:0.000000e+00
                   merge %0:1 at 64r into %2:0 at 48r --> @48r
                   merge %0:0 at 112r into %2:0 at 48r --> @48r
                   conflict at %2:0 at 48r
                   taints local %0:2 at 16r to 64r
                   pruned %0 at 48r: [16r,48r:2)[64r,112r:1)[112r,112d:0)  0 at
112r 1 at 64r 2 at 16r
                   erased: 112r    dead %0.sub_32bit:gr64_with_sub_8bit = COPY
%2:gr32
                   erased: 64r     %0.sub_32bit:gr64_with_sub_8bit = COPY
%2:gr32
                   restoring liveness to 2 points: 64r,48r:  %0
[16r,48r:0)[48r,112d:1)  0 at 16r 1 at 48r weight:0.000000e+00
   AllocationOrder(GR64) = [ $rax $rcx $rdx $rsi $rdi $r8 $r9 $r10 $r11 $rbx
$r14 $r15 $r12 $r13 $rbp ]
   AllocationOrder(GR64_with_sub_8bit) = [ $rax $rcx $rdx $rsi $rdi $r8 $r9 $r10
$r11 $rbx $r14 $r15 $r12 $r13 $rbp ]
                   updated: 48B    %0.sub_32bit:gr64_with_sub_8bit = COPY
%1:gr32
                   updated: 96B    dead %1:gr32 = MOV32rr
%0.sub_32bit:gr64_with_sub_8bit
           Success: %2:sub_32bit -> %0
           Result = %0 [16r,48r:0)[48r,112d:1)  0 at 16r 1 at 48r
weight:0.000000e+00
   48B     %0.sub_32bit:gr64_with_sub_8bit = COPY %1:gr32
           Considering merging to GR64_with_sub_8bit with %1 in %0:sub_32bit
                   RHS = %1 [32r,48r:1)[96r,96d:0)  0 at 96r 1 at 32r
weight:0.000000e+00
                   LHS = %0 [16r,48r:0)[48r,112d:1)  0 at 16r 1 at 48r
weight:0.000000e+00
                   merge %0:1 at 48r into %1:1 at 32r --> @32r
                   conflict at %1:0 at 96r
                   taints local %0:1 at 48r to 112d
I am trying to understand what this all means, but one thing that
strikes me is that if I manually update the MIR so that the first update
has been performed already:
   name: f
   body: |
     bb.0:
       %0:gr64_with_sub_8bit = COPY $rax
       %1:gr32 = MOV32rr %0.sub_32bit:gr64_with_sub_8bit
       %0.sub_32bit:gr64_with_sub_8bit = COPY %1:gr32
       %3:gr32 = MOV32rr %0.sub_32bit:gr64_with_sub_8bit
       %1:gr32 = MOV32rr %0.sub_32bit:gr64_with_sub_8bit
the pass does run successfully. However, the recalculated intervals are
not identical to what they were thought to be after the first update:
   ********** INTERVALS **********
   %0 [16r,48r:1)[48r,80r:0)  0 at 48r 1 at 16r weight:0.000000e+00
   %1 [32r,48r:1)[80r,80d:0)  0 at 80r 1 at 32r weight:0.000000e+00
   %2 [64r,64d:0)  0 at 64r weight:0.000000e+00
Note %0's [48r,80r:0) here, which was [48r,112d) after the initial
update. The instruction at 112 was
%0.sub_32bit:gr64_with_sub_8bit = COPY %2:gr32, which had been deleted.
Could that be the problem, the interval referring to a deleted
instruction?
Cheers,
Harald van Dijk
Harald van Dijk via llvm-dev
2019-Oct-15  13:28 UTC
[llvm-dev] Assertion failure "There should be at least one conflict." in register coalescing
On 15/10/2019 12:16, Harald van Dijk via llvm-dev wrote:> restoring liveness to 2 points: 64r,48r: %0 > [16r,48r:0)[48r,112d:1) 0 at 16r 1 at 48r weight:0.000000e+00 >[...] > I am trying to understand what this all means, but one thing that > strikes me is that if I manually update the MIR so that the first update > has been performed already: > > name: f > body: | > bb.0: > %0:gr64_with_sub_8bit = COPY $rax > %1:gr32 = MOV32rr %0.sub_32bit:gr64_with_sub_8bit > %0.sub_32bit:gr64_with_sub_8bit = COPY %1:gr32 > %3:gr32 = MOV32rr %0.sub_32bit:gr64_with_sub_8bit > %1:gr32 = MOV32rr %0.sub_32bit:gr64_with_sub_8bit > > the pass does run successfully. However, the recalculated intervals are > not identical to what they were thought to be after the first update: > > ********** INTERVALS ********** > %0 [16r,48r:1)[48r,80r:0) 0 at 48r 1 at 16r weight:0.000000e+00 > %1 [32r,48r:1)[80r,80d:0) 0 at 80r 1 at 32r weight:0.000000e+00 > %2 [64r,64d:0) 0 at 64r weight:0.000000e+00 > > Note %0's [48r,80r:0) here, which was [48r,112d) after the initial > update. The instruction at 112 was > %0.sub_32bit:gr64_with_sub_8bit = COPY %2:gr32, which had been deleted. > Could that be the problem, the interval referring to a deleted > instruction?The d in 112d is Slot_Dead, which is documented as: /// Dead def kill point. Kill slot for a live range that is defined by /// the same instruction (Slot_Register or Slot_EarlyClobber), but isn't /// used anywhere. Slot_Dead, That seems very wrong: the value defined here is not dead.> Cheers, > Harald van Dijk