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)
Created attachment 38954 [details] bug-combin.c.242r.ud_dce
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...
Created attachment 38955 [details] bug-combin.c.243r.combine
(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.
(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 :).
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.
...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.
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
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
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
Fixed in 5.5 and 6.2+.
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?
*** Bug 49884 has been marked as a duplicate of this bug. ***