Bug 71976 - insn-combiner deletes a live 64-bit shift
Summary: insn-combiner deletes a live 64-bit shift
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: rtl-optimization (show other bugs)
Version: 6.1.1
: P3 normal
Target Milestone: 5.5
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
: 49884 (view as bug list)
Depends on:
Blocks:
 
Reported: 2016-07-22 18:12 UTC by Georg-Johann Lay
Modified: 2021-08-16 23:43 UTC (History)
1 user (show)

See Also:
Host:
Target: avr
Build:
Known to work: 5.5.0, 6.2.0, 7.1.0
Known to fail: 4.9.2, 5.2.1, 6.1.1
Last reconfirmed: 2016-07-25 00:00:00


Attachments
bug-combin.c: C test case (295 bytes, text/plain)
2016-07-22 18:12 UTC, Georg-Johann Lay
Details
bug-combin.c.242r.ud_dce (4.66 KB, text/plain)
2016-07-22 18:13 UTC, Georg-Johann Lay
Details
bug-combin.c.243r.combine (3.90 KB, text/plain)
2016-07-22 18:42 UTC, Georg-Johann Lay
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Georg-Johann Lay 2016-07-22 18:12:07 UTC
Created attachment 38953 [details]
bug-combin.c: C test case

In the following test case, combine.c removes an 64-bit shift and generates wrong code.

== Compile ==

$ avr-gcc -mmcu=atmega8 bug-combin.c -S -dp -Os -fdump-rtl-combine-details -fdump-rtl-ud_dce

== configure ==

Target: avr
Configured with: ../../gcc.gnu.org/gcc-6-branch/configure --target=avr --prefix=/local/gnu/install/gcc-6-avr-mingw32 --host=i386-mingw32 --build=x86_64-linux-gnu --enable-languages=c,c++ --disable-nls --disable-shared --enable-lto --with-dwarf2 --with-gnu-ld --with-gnu-as
Thread model: single
gcc version 6.1.1 20160627 (GCC)
Comment 1 Georg-Johann Lay 2016-07-22 18:13:40 UTC
Created attachment 38954 [details]
bug-combin.c.242r.ud_dce
Comment 2 Georg-Johann Lay 2016-07-22 18:29:38 UTC
Bugzille does not allow me to attach the .combine dump (for reference).

...anyway the relevant part of the dump is:

In .242r.ud_dce there is the following right shift insn:

(insn 51 50 52 2 (set (reg:QI 16 r16)
        (const_int 40 [0x28])) bug-combin.c:29 71 {movqi_insn}
     (nil))
(insn 52 51 61 2 (set (reg:DI 18 r18)
        (ashiftrt:DI (reg:DI 18 r18)
            (reg:QI 16 r16))) bug-combin.c:29 1416 {ashrdi3_insn}
     (expr_list:REG_DEAD (reg:QI 16 r16)
        (nil)))

Insn combine tries to macht the combination of these two insns:

Trying 51 -> 52:
Failed to match this instruction:
(set (reg:DI 18 r18)
    (reg:DI 18 r18))
allowing combination of insns 51 and 52
original costs 4 + 32 = 36
replacement cost 4
deferring deletion of insn with uid = 51.
modifying insn i3    52: r18:DI=r18:DI
deferring rescan insn with uid = 52.

So the combination is wrong; for some reason the shift is transformed to a no-op move and then removed...
Comment 3 Georg-Johann Lay 2016-07-22 18:42:36 UTC
Created attachment 38955 [details]
bug-combin.c.243r.combine
Comment 4 Andrew Pinski 2016-07-23 04:27:22 UTC
(In reply to Georg-Johann Lay from comment #2)
> Bugzille does not allow me to attach the .combine dump (for reference).
> 
> ...anyway the relevant part of the dump is:
> 
> In .242r.ud_dce there is the following right shift insn:
> 
> (insn 51 50 52 2 (set (reg:QI 16 r16)
>         (const_int 40 [0x28])) bug-combin.c:29 71 {movqi_insn}
>      (nil))
> (insn 52 51 61 2 (set (reg:DI 18 r18)
>         (ashiftrt:DI (reg:DI 18 r18)
>             (reg:QI 16 r16))) bug-combin.c:29 1416 {ashrdi3_insn}
>      (expr_list:REG_DEAD (reg:QI 16 r16)
>         (nil)))
> 
> Insn combine tries to macht the combination of these two insns:
> 
> Trying 51 -> 52:
> Failed to match this instruction:
> (set (reg:DI 18 r18)
>     (reg:DI 18 r18))
> allowing combination of insns 51 and 52
> original costs 4 + 32 = 36
> replacement cost 4
> deferring deletion of insn with uid = 51.
> modifying insn i3    52: r18:DI=r18:DI
> deferring rescan insn with uid = 52.
> 
> So the combination is wrong; for some reason the shift is transformed to a
> no-op move and then removed...

Actually the combination is ok (correct in a weird way) and here is why:

this is r15 = 40; r18 = r18 << r16; So r18 << 40 which is undefined as 40 > 16 so GCC decides why not just provide r18 here.
Comment 5 Andrew Pinski 2016-07-23 04:33:05 UTC
(In reply to Andrew Pinski from comment #4)
> this is r15 = 40; r18 = r18 << r16; So r18 << 40 which is undefined as 40 >
> 16 so GCC decides why not just provide r18 here.

Actually I take that back. I missed r18 was DI mode here and not QI mode :).
Comment 6 Georg-Johann Lay 2016-07-25 09:33:52 UTC
This also happens on current trunk r238700 from today (2016-07-25).

After substitution from
       (ashiftrt:DI (reg:DI 18 r18)
       		    (reg:QI 16 r16))
to
	(ashiftrt:DI (reg:DI 18 r18)
       		     (const_int 40))

combine calls
 
simplify_shift_const_1 (code=ASHIFTRT, result_mode=DImode, varop=0x7ffff734ee70, orig_count=40) at ../../../gcc.gnu.org/trunk/gcc/combine.c:10181

(gdb) p varop
$95 = (rtx) 0x7ffff734ee70
(gdb) pr
(reg:DI 18 r18)

And then there is this piece of code:

      /* An arithmetic right shift of a quantity known to be -1 or 0
	 is a no-op.  */
      if (code == ASHIFTRT
	  && (num_sign_bit_copies (varop, shift_mode)
	      == GET_MODE_PRECISION (shift_mode)))
	{
	  count = 0;
	  break;
	}

How can it conclude the quantity to be shifted (reg:DI 18) is always 0 or -1?
This is simply not the case.
Comment 7 Georg-Johann Lay 2016-07-25 10:37:46 UTC
...hmmm this place might be correct.  combine defines

#define RTL_HOOKS_REG_NUM_SIGN_BIT_COPIES  reg_num_sign_bit_copies_for_combine

and this function comes up with

reg_num_sign_bit_copies_for_combine (x=0x7ffff734ee70, mode=DImode, known_x=0x0, known_mode=VOIDmode, known_ret=0, result=0x7fffffff8fb0) at ../../../gcc.gnu.org/trunk/gcc/combine.c:9928

(gdb) p x
$112 = (const_rtx) 0x7ffff734ee70
(gdb) pr
(reg:DI 18 r18)

and get_last_value (x) returns 0.

Before combine we have

(insn 43 31 44 2 (set (reg:QI 18 r18)
        (const_int 0 [0])) bug-combin.c:29 56 {movqi_insn}
     (nil))
(insn 44 43 45 2 (set (reg:QI 19 r19 [+1 ])
        (const_int 0 [0])) bug-combin.c:29 56 {movqi_insn}
     (nil))
(insn 45 44 46 2 (set (reg:QI 20 r20 [+2 ])
        (const_int 0 [0])) bug-combin.c:29 56 {movqi_insn}
     (nil))
(insn 46 45 47 2 (set (reg:QI 21 r21 [+3 ])
        (const_int 0 [0])) bug-combin.c:29 56 {movqi_insn}
     (nil))
(insn 47 46 48 2 (set (reg:QI 22 r22 [+4 ])
        (const_int 0 [0])) bug-combin.c:29 56 {movqi_insn}
     (nil))
(insn 48 47 49 2 (set (reg:QI 23 r23 [+5 ])
        (reg:QI 65 [ _3+5 ])) bug-combin.c:29 56 {movqi_insn}
     (nil))
(insn 49 48 50 2 (set (reg:QI 24 r24 [+6 ])
        (reg:QI 66 [ _3+6 ])) bug-combin.c:29 56 {movqi_insn}
     (nil))
(insn 50 49 51 2 (set (reg:QI 25 r25 [+7 ])
        (reg:QI 67 [ _3+7 ])) bug-combin.c:29 56 {movqi_insn}
     (nil))
(insn 51 50 52 2 (set (reg:QI 16 r16)
        (const_int 40 [0x28])) bug-combin.c:29 56 {movqi_insn}
     (nil))
(insn 52 51 61 2 (set (reg:DI 18 r18)
        (ashiftrt:DI (reg:DI 18 r18)
            (reg:QI 16 r16))) bug-combin.c:29 1417 {ashrdi3_insn}
     (expr_list:REG_DEAD (reg:QI 16 r16)
        (nil)))

Note that we have hard reg DI 18 in insn 52 and a set of hard reg QI 18 in insn 43!

get_last_value is called for DI 18 bit only looks at the LSB byte in QI 18 and hence returns 0 which is a bug.
Comment 8 Georg-Johann Lay 2016-07-29 12:34:53 UTC
Author: gjl
Date: Fri Jul 29 12:34:21 2016
New Revision: 238863

URL: https://gcc.gnu.org/viewcvs?rev=238863&root=gcc&view=rev
Log:
gcc/
	PR rtl-optimization/71976
	* combine.c (get_last_value): Return 0 if the argument for which
	the function is called has a wider mode than the recorded value.

Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/combine.c
Comment 9 Georg-Johann Lay 2016-08-08 10:06:46 UTC
Author: gjl
Date: Mon Aug  8 10:06:15 2016
New Revision: 239238

URL: https://gcc.gnu.org/viewcvs?rev=239238&root=gcc&view=rev
Log:
	Backport from 2016-07-29 trunk r238863.
	PR rtl-optimization/71976
	* combine.c (get_last_value): Return 0 if the argument for which
	the function is called has a wider mode than the recorded value.


Modified:
    branches/gcc-6-branch/gcc/ChangeLog
    branches/gcc-6-branch/gcc/combine.c
Comment 10 Georg-Johann Lay 2016-08-08 10:14:26 UTC
Author: gjl
Date: Mon Aug  8 10:13:54 2016
New Revision: 239239

URL: https://gcc.gnu.org/viewcvs?rev=239239&root=gcc&view=rev
Log:
	Backport from 2016-07-29 trunk r238863.
	PR rtl-optimization/71976
	* combine.c (get_last_value): Return 0 if the argument for which
	the function is called has a wider mode than the recorded value.


Modified:
    branches/gcc-5-branch/gcc/ChangeLog
    branches/gcc-5-branch/gcc/combine.c
Comment 11 Georg-Johann Lay 2016-08-08 15:42:37 UTC
Fixed in 5.5 and 6.2+.
Comment 12 Clay McClure 2018-10-16 07:42:05 UTC
Georg, was this issue specific to the avr target, or could it affect other targets as well? If so, could you please give me a hint as to how I could write a failing test case on, say, x86_64?
Comment 13 Andrew Pinski 2021-08-16 23:43:47 UTC
*** Bug 49884 has been marked as a duplicate of this bug. ***