Consider the following line of the attached test case, where a is double with value 4294967296.0. double b = (unsigned int)((unsigned long long int)a % 4294967296ULL); Then (unsigned long long int)a % 4294967296ULL should be equal to 0ULL, and therefore b should be 0.0. However, when compiled with -O1 -march=pentium4, b gets the wrong value of 4294967296.0 (the same as a). Note that the error only occurs with -march=pentium4. To compile the testcase: gcc -std=c99 -O1 -march=pentium4 convert-bug.i -o convert-bug gcc -v: Using built-in specs. Target: i686-pc-linux-gnu Configured with: ../gcc/configure --enable-checking=release --prefix=/home/jdemeyer/local CC=gcc-4.1.2 Thread model: posix gcc version 4.4.0 20080916 (experimental) (GCC)
Created attachment 16339 [details] Testcase
This works correctly for i386-darwin. In fact I tried -std=c99 -O1 -mfpmath=387 -march=pentium4 -mno-sse too.
In fact, with -mno-sse the problem disappears for me too. I *do* get the problem with gcc -std=c99 -O1 -march=i386 -msse2 -mfpmath=387
Still works correctly on i386-darwin but that might be because PIC is turned on by default.
Confirmed on i686-linux with -std=c99 -O -msse2 -mfpmath=387 -march=i386. Fails since GCC 3.4 at least.
cprop_hardreg pass is propagating DImode ax register, wrongly bypassing DI-SI conversion. Before cprop_hardreg, we have: (call_insn/u:HI 27 88 28 4 pr37544.c:9 (set (reg:DI 0 ax) (call (mem:QI (symbol_ref:SI ("__fixunsdfdi") [flags 0x41]) [0 S1 A8]) (const_int 16 [0x10]))) 687 {*call_value_0} (expr_list:REG_UNUSED (reg:SI 1 dx) (expr_list:REG_EH_REGION (const_int -1 [0xffffffff]) (nil))) (expr_list:REG_DEP_TRUE (use (mem:DF (plus:SI (reg/f:SI 7 sp) (scratch:SI)) [0 S8 A8])) (nil))) (insn 95 28 104 4 pr37544.c:9 (set (reg:SI 21 xmm0) (reg:SI 0 ax [78])) 41 {*movsi_1} (expr_list:REG_DEAD (reg:SI 0 ax [78]) (nil))) (insn 104 95 105 4 pr37544.c:9 (set (mem/c:DI (plus:SI (reg/f:SI 6 bp) (const_int -8032 [0xffffe0a0])) [0 S8 A64]) (reg:DI 21 xmm0)) 63 {*movdi_2} (expr_list:REG_DEAD (reg:DI 21 xmm0) (nil))) (insn 105 104 89 4 pr37544.c:9 (set (reg/v:DF 8 st [orig:65 b ] [65]) (float:DF (mem/c:DI (plus:SI (reg/f:SI 6 bp) (const_int -8032 [0xffffe0a0])) [0 S8 A64]))) 179 {*floatdidf2_i387} (nil)) and after cprop_hardreg: (call_insn/u:HI 27 88 28 4 pr37544.c:9 (set (reg:DI 0 ax) (call (mem:QI (symbol_ref:SI ("__fixunsdfdi") [flags 0x41]) [0 S1 A8]) (const_int 16 [0x10]))) 687 {*call_value_0} (expr_list:REG_UNUSED (reg:SI 1 dx) (expr_list:REG_EH_REGION (const_int -1 [0xffffffff]) (nil))) (expr_list:REG_DEP_TRUE (use (mem:DF (plus:SI (reg/f:SI 7 sp) (scratch:SI)) [0 S8 A8])) (nil))) (insn 95 28 104 4 pr37544.c:9 (set (reg:SI 21 xmm0) (reg:SI 0 ax [78])) 41 {*movsi_1} (expr_list:REG_DEAD (reg:SI 0 ax [78]) (nil))) (insn 104 95 105 4 pr37544.c:9 (set (mem/c:DI (plus:SI (reg/f:SI 6 bp) (const_int -8032 [0xffffe0a0])) [0 S8 A64]) (reg:DI 0 ax [21])) 63 {*movdi_2} (expr_list:REG_DEAD (reg:DI 21 xmm0) (nil))) (insn 105 104 89 4 pr37544.c:9 (set (reg/v:DF 8 st [orig:65 b ] [65]) (float:DF (mem/c:DI (plus:SI (reg/f:SI 6 bp) (const_int -8032 [0xffffe0a0])) [0 S8 A64]))) 179 {*floatdidf2_i387} (nil)) Please note, how DImode ax reg propagates into (insn 104), bypassing SImode conversion in (insn 95).
(In reply to comment #5) > Confirmed on i686-linux with -std=c99 -O -msse2 -mfpmath=387 -march=i386. Does not fail for x86_64-linux target with -m32 on x86_64-linux _and_ i686-linux hosts.
The problem is in regrename.c, function maybe_mode_change. We enter the function with: orig_mode = DImode copy_mode = SImode new_mode = DImode At the beginning of maybe_mode_change() we have: if (orig_mode == new_mode) return gen_rtx_raw_REG (new_mode, regno); we simply discard the change to a narrower mode.
4.2 and 4.3 work OK.
I'm testing following patch: Index: regrename.c =================================================================== --- regrename.c (revision 140409) +++ regrename.c (working copy) @@ -1314,6 +1314,9 @@ enum machine_mode new_mode, unsigned int regno, unsigned int copy_regno ATTRIBUTE_UNUSED) { + if (GET_MODE_SIZE (copy_mode) < GET_MODE_SIZE (new_mode)) + return NULL_RTX; + if (orig_mode == new_mode) return gen_rtx_raw_REG (new_mode, regno); else if (mode_change_ok (orig_mode, new_mode, regno))
Patch at http://gcc.gnu.org/ml/gcc-patches/2008-09/msg01221.html
Subject: Bug 37544 Author: uros Date: Thu Sep 18 10:17:42 2008 New Revision: 140446 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=140446 Log: PR rtl-optimization/37544 * regrename.c (maybe_mode_change): Exit early when copy_mode is narrower than orig_mode and narrower than new_mode. testsuite/ChangeLog: PR rtl-optimization/37544 * gcc.dg/pr37544.c: New test. Added: trunk/gcc/testsuite/gcc.dg/pr37544.c Modified: trunk/gcc/ChangeLog trunk/gcc/regrename.c trunk/gcc/testsuite/ChangeLog
Fixed for 4.4, latent on 4.3 and 4.2. BTW: This problem was uncovered by the patch that fixed PR target/13958 [1]. [1] http://gcc.gnu.org/ml/gcc-patches/2008-03/msg01295.html
Subject: Bug 37544 Author: uros Date: Fri Sep 19 08:01:07 2008 New Revision: 140484 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=140484 Log: PR rtl-optimization/37544 * regrename.c (maybe_mode_change): Exit early when copy_mode is narrower than orig_mode and narrower than new_mode. testsuite/ChangeLog: PR rtl-optimization/37544 * gcc.dg/pr37544.c: New test. Added: branches/gcc-4_3-branch/gcc/testsuite/gcc.dg/pr37544.c Modified: branches/gcc-4_3-branch/gcc/ChangeLog branches/gcc-4_3-branch/gcc/regrename.c branches/gcc-4_3-branch/gcc/testsuite/ChangeLog
Subject: Bug 37544 Author: uros Date: Fri Sep 19 10:04:46 2008 New Revision: 140487 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=140487 Log: PR rtl-optimization/37544 * regrename.c (maybe_mode_change): Exit early when copy_mode is narrower than orig_mode and narrower than new_mode. testsuite/ChangeLog: PR rtl-optimization/37544 * gcc.dg/pr37544.c: New test. Added: branches/gcc-4_2-branch/gcc/testsuite/gcc.dg/pr37544.c - copied, changed from r140484, branches/gcc-4_3-branch/gcc/testsuite/gcc.dg/pr37544.c Modified: branches/gcc-4_2-branch/gcc/ChangeLog branches/gcc-4_2-branch/gcc/regrename.c branches/gcc-4_2-branch/gcc/testsuite/ChangeLog
Fixed everywhere.