optimization/7069: Instruction scheduling for some CPUs is very wrong with -O2; gcc generates bad code
mark@moxienet.com
mark@moxienet.com
Tue Jun 18 10:01:00 GMT 2002
>Number: 7069
>Category: optimization
>Synopsis: Instruction scheduling for some CPUs is very wrong with -O2; gcc generates bad code
>Confidential: no
>Severity: critical
>Priority: medium
>Responsible: unassigned
>State: open
>Class: wrong-code
>Submitter-Id: net
>Arrival-Date: Tue Jun 18 09:16:02 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator: Mark Mentovai
>Release: 3.1
>Organization:
>Environment:
System: SunOS s7 5.8 Generic_108528-15 sun4u sparc SUNW,UltraAX-i2
Architecture: sun4
Sun Netra T1 200, 500MHz UltraSPARC IIe, 1GB, 2x18GB
Sun Solaris 8 7/01 MU 7
host: sparc-sun-solaris2.8
build: sparc-sun-solaris2.8
target: sparc-sun-solaris2.8
configured with: ../gcc-3.1/configure --enable-shared --with-gnu-as --with-gnu-ld --enable-threads --with-cpu=ultrasparc --enable-libgcj --with-system-zlib
Also exhibited on:
System: Linux localhost 2.4.18 #5 SMP Wed May 8 11:52:37 EDT 2002 sparc64 unknown
Architecture: sparc64
Sun Enterprise 220R, 450MHz UltraSPARC II, 1GB, 2x18GB
Debian GNU/Linux 2.2, Linux 2.4.18 (sparc64)
host: sparc-sun-linux-gnu
build: sparc-sun-linux-gnu
target: sparc-sun-linux-gnu
configured with: ../gcc-3.1/configure --enable-shared --with-gnu-as --with-gnu-ld --enable-threads --enable-libgcj --with-system-zlib sparc-sun-linux-gnu
>Description:
Instruction scheduling for some CPUs is very wrong with -O2; gcc generates bad code. The optimized code is incorrect, it functions differently from non-optimized code, and various levels of optimization may produce code that functions differently as well.
Given the C code gcc-bug-20020618.c:
--
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char **argv) {
char c[4]={222,173,190,239};
int *p;
p=(int *)&c;
printf("p = 0x%08x\n",*p);
exit(0);
return 0;
}
--
GCC schedules the load of *p for the call to printf before the stores of c[0..3]; *p thus has an incorrect value.
This occurs with -O2 and higher (but not -Os) when instruction scheduling for ultrasparc, cypress, or tsc701 is selected with -mcpu or -mtune. Default scheduling for all SPARC architectures appears to be unaffected, as does implementation-specific scheduling for implementations other than ultrasparc, cypress, and tsc701. I have not tested non-SPARC architectures.
Note that cypress is the default implementation selected at configure time in the absence of --with-cpu.
As one example, the assembly source output on Solaris from "gcc -mcpu=ultrasparc -O2 gcc-bug-20020618.c -S -o gcc-bug-20020618,ultrasparc-O2.s":
--
.file "gcc-bug-20020618.c"
.section .rodata.str1.8,"aMS",@progbits,1
.align 8
.LLC0:
.asciz "*p = 0x%08x\n"
.section ".text"
.align 4
.align 32
.global main
.type main,#function
.proc 04
main:
!#PROLOGUE# 0
save %sp, -120, %sp
!#PROLOGUE# 1
mov -34, %o2
mov -83, %o3
ld [%fp-24], %o1
sethi %hi(.LLC0), %o0
stb %o2, [%fp-24]
mov -66, %o2
or %o0, %lo(.LLC0), %o0
stb %o3, [%fp-23]
mov -17, %o3
stb %o2, [%fp-22]
call printf, 0
stb %o3, [%fp-21]
call exit, 0
mov 0, %o0
nop
.LLfe1:
.size main,.LLfe1-main
.ident "GCC: (GNU) 3.1"
--
As you can see, "ld [%fp-24], %o1" is scheduled in advance of any stb instruction to [%fp-24]..[%fp-21].
This bug has been observed on SPARC running gcc 3.1 under both SunOS 5.8 and Linux 2.4.18. gcc 3.0.4 does not exhibit this bug.
C source included rather than preprocessed code, as it is more relevant in this case, and this test case does not depend on anything esoteric. The expected output is "*p = 0xdeadbeef" on big-endian platforms.
>How-To-Repeat:
Compile and run gcc-bug-20020618.c with -O2 or higher and ultrasparc or cypress as the selected CPU with -mcpu, or select instruction scheduling only with -mtune. The expected output is "*p = 0xdeadbeef" as seen when building with -O1 or when using default instruction scheduling parameters (selecting -mcpu=v7 or -mcpu=v9).
On Solaris:
> gcc -mcpu=ultrasparc -O1 gcc-bug-20020618.c -o gcc-bug-20020618,ultrasparc-O1 && ./gcc-bug-20020618,ultrasparc-O1
*p = 0xdeadbeef
> gcc -mcpu=ultrasparc -O2 gcc-bug-20020618.c -o gcc-bug-20020618,ultrasparc-O2 && ./gcc-bug-20020618,ultrasparc-O2
*p = 0x00000004
> gcc -mcpu=ultrasparc -O3 gcc-bug-20020618.c -o gcc-bug-20020618,ultrasparc-O3 && ./gcc-bug-20020618,ultrasparc-O3
*p = 0x00000004
> gcc -mcpu=v9 -O3 gcc-bug-20020618.c -o gcc-bug-20020618,v9-O3 && ./gcc-bug-20020618,v9-O3
*p = 0xdeadbeef
> gcc -mcpu=cypress -O1 gcc-bug-20020618.c -o gcc-bug-20020618,cypress-O1 && ./gcc-bug-20020618,cypress-O1
*p = 0xdeadbeef
> gcc -mcpu=cypress -O2 gcc-bug-20020618.c -o gcc-bug-20020618,cypress-O2 && ./gcc-bug-20020618,cypress-O2
*p = 0xdeadbe04
> gcc -mcpu=cypress -O3 gcc-bug-20020618.c -o gcc-bug-20020618,cypress-O3 && ./gcc-bug-20020618,cypress-O3
*p = 0x00ad00ef
> gcc -mcpu=v7 -O3 gcc-bug-20020618.c -o gcc-bug-20020618,v7-O3 && ./gcc-bug-20020618,v7-O3
*p = 0xdeadbeef
On Linux:
> gcc -mcpu=ultrasparc -O1 gcc-bug-20020618.c -o gcc-bug-20020618,ultrasparc-O1 && ./gcc-bug-20020618,ultrasparc-O1
*p = 0xdeadbeef
> gcc -mcpu=ultrasparc -O2 gcc-bug-20020618.c -o gcc-bug-20020618,ultrasparc-O2 && ./gcc-bug-20020618,ultrasparc-O2
*p = 0x00000000
> gcc -mcpu=ultrasparc -O3 gcc-bug-20020618.c -o gcc-bug-20020618,ultrasparc-O3 && ./gcc-bug-20020618,ultrasparc-O3
*p = 0x00000000
> gcc -mcpu=v9 -O3 gcc-bug-20020618.c -o gcc-bug-20020618,v9-O3 && ./gcc-bug-20020618,v9-O3
*p = 0xdeadbeef
> gcc -mcpu=cypress -O1 gcc-bug-20020618.c -o gcc-bug-20020618,cypress-O1 && ./gcc-bug-20020618,cypress-O1
*p = 0xdeadbeef
> gcc -mcpu=cypress -O2 gcc-bug-20020618.c -o gcc-bug-20020618,cypress-O2 && ./gcc-bug-20020618,cypress-O2
*p = 0xdeadbe00
> gcc -mcpu=cypress -O3 gcc-bug-20020618.c -o gcc-bug-20020618,cypress-O3 && ./gcc-bug-20020618,cypress-O3
*p = 0x000000ef
> gcc -mcpu=v7 -O3 gcc-bug-20020618.c -o gcc-bug-20020618,v7-O3 && ./gcc-bug-20020618,v7-O3
*p = 0xdeadbeef
>Fix:
No fix.
Workaround: Do not use implementation-specific instruction scheduling, use "-mcpu=v9" or "-mcpu=v7" instead of "-mcpu=ultrasparc" and "-mcpu=cypress". Alternatively, allow implementation-specific scheduling but use -O1 instead of -O2 or higher.
As cypress is the default CPU unless gcc is configured otherwise, users may wish to explicitly select another CPU by setting CC to something like "gcc -mcpu=v7", or by configuring gcc to use another CPU by default.
>Release-Note:
>Audit-Trail:
>Unformatted:
----gnatsweb-attachment----
Content-Type: text/plain; name="gcc-bug-20020618.c"
Content-Disposition: inline; filename="gcc-bug-20020618.c"
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char **argv) {
char c[4]={222,173,190,239};
int *p;
p=(int *)&c;
printf("*p = 0x%08x\n",*p);
exit(0);
return 0;
}
More information about the Gcc-bugs
mailing list