I found that following program loops infinitely on SPARC if it is optimized. % uname -a SunOS mule 5.8 Generic_108528-29 sun4u sparc SUNW,Ultra-80 % cat tst.i typedef unsigned int size_t; extern int printf(const char *, ...); typedef unsigned char uint8_t; typedef unsigned int uint32_t; typedef unsigned int uint_t; typedef char *caddr_t; typedef int greg_t; typedef greg_t gregset_t[19]; struct rwindow { greg_t rw_local[8]; greg_t rw_in[8]; }; typedef struct gwindows { int wbcnt; greg_t *spbuf[31]; struct rwindow wbuf[31]; } gwindows_t; struct fpu { union { uint32_t fpu_regs[32]; double fpu_dregs[16]; } fpu_fr; struct fq *fpu_q; uint32_t fpu_fsr; uint8_t fpu_qcnt; uint8_t fpu_q_entrysize; uint8_t fpu_en; }; typedef struct fpu fpregset_t; typedef struct { unsigned int xrs_id; caddr_t xrs_ptr; } xrs_t; typedef struct { gregset_t gregs; gwindows_t *gwins; fpregset_t fpregs; xrs_t xrs; long filler[19]; } mcontext_t; typedef struct { unsigned int __sigbits[4]; } sigset_t; typedef struct sigaltstack { void *ss_sp; size_t ss_size; int ss_flags; } stack_t; typedef struct ucontext ucontext_t; struct ucontext { uint_t uc_flags; ucontext_t *uc_link; sigset_t uc_sigmask; stack_t uc_stack; mcontext_t uc_mcontext; long uc_filler[23]; }; extern int getcontext(ucontext_t *); extern int setcontext(const ucontext_t *); int flag; ucontext_t cont; int pad[100]; typedef void (*fun_t)(int); fun_t p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12; void h(int v) { printf("%d\n", v); } void f(void) { flag = 1; setcontext(&cont); } static int g(void) { int ret; flag = 0; getcontext(&cont); ret = flag; if (ret == 0) { printf("first flag=%d\n", flag); p0 = p1 = p2 = p3 = p4 = p5 = p6 = p7 = p8 = h; f(); p0(ret); p1(ret); p2(ret); p3(ret); p4(ret); p5(ret); p6(ret); p7(ret); p8(ret); } else { printf("second flag=%d\n", flag); } return ret; } int main(int argc, char **argv) { g(); return 0; } % gcc-4.0 -v -g -O1 tst.i Using built-in specs. Target: sparc-sun-solaris2.8 Configured with: ../gcc/configure --prefix=/home/akr/g/gcc-4.0 --enable-languages=c --disable-nls Thread model: posix gcc version 4.0.1 20050618 (prerelease) /home/akr/g/gcc-4.0/libexec/gcc/sparc-sun-solaris2.8/4.0.1/cc1 -fpreprocessed tst.i -quiet -dumpbase tst.i -mcpu=v7 -auxbase tst -g -O1 -version -o /var/tmp//ccINkNlP.s GNU C version 4.0.1 20050618 (prerelease) (sparc-sun-solaris2.8) compiled by GNU C version 4.0.1 20050618 (prerelease). GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 /usr/ccs/bin/as -V -Qy -s -xarch=v8 -o /var/tmp//ccwL3YLE.o /var/tmp//ccINkNlP.s /usr/ccs/bin/as: Sun WorkShop 6 2003/12/18 Compiler Common 6.0 Patch 114802-02 /home/akr/g/gcc-4.0/libexec/gcc/sparc-sun-solaris2.8/4.0.1/collect2 -V -Y P,/usr/ccs/lib:/usr/lib -Qy /home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/crt1.o /home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/crti.o /usr/ccs/lib/values-Xa.o /home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/crtbegin.o -L/home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1 -L/usr/ccs/bin -L/usr/ccs/lib -L/home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/../../.. /var/tmp//ccwL3YLE.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh -lc /home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/crtend.o /home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/crtn.o ld: Software Generation Utilities - Solaris Link Editors: 5.8-1.285 % ./a.out first flag=0 first flag=0 first flag=0 first flag=0 first flag=0 first flag=0 first flag=0 first flag=0 first flag=0 first flag=0 ... But it terminates as follows if it is not optimized. % gcc-4.0 -v -g -o a.out.nonopt tst.i Using built-in specs. Target: sparc-sun-solaris2.8 Configured with: ../gcc/configure --prefix=/home/akr/g/gcc-4.0 --enable-languages=c --disable-nls Thread model: posix gcc version 4.0.1 20050618 (prerelease) /home/akr/g/gcc-4.0/libexec/gcc/sparc-sun-solaris2.8/4.0.1/cc1 -fpreprocessed tst.i -quiet -dumpbase tst.i -mcpu=v7 -auxbase tst -g -version -o /var/tmp//ccbUpN08.s GNU C version 4.0.1 20050618 (prerelease) (sparc-sun-solaris2.8) compiled by GNU C version 4.0.1 20050618 (prerelease). GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 /usr/ccs/bin/as -V -Qy -s -xarch=v8 -o /var/tmp//ccMsWIyd.o /var/tmp//ccbUpN08.s /usr/ccs/bin/as: Sun WorkShop 6 2003/12/18 Compiler Common 6.0 Patch 114802-02 /home/akr/g/gcc-4.0/libexec/gcc/sparc-sun-solaris2.8/4.0.1/collect2 -V -Y P,/usr/ccs/lib:/usr/lib -Qy -o a.out.nonopt /home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/crt1.o /home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/crti.o /usr/ccs/lib/values-Xa.o /home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/crtbegin.o -L/home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1 -L/usr/ccs/bin -L/usr/ccs/lib -L/home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/../../.. /var/tmp//ccMsWIyd.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh -lc /home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/crtend.o /home/akr/g/gcc-4.0/lib/gcc/sparc-sun-solaris2.8/4.0.1/crtn.o ld: Software Generation Utilities - Solaris Link Editors: 5.8-1.285 % ./a.out.nonopt first flag=0 second flag=1 ------------------------------------------------------------------------------- I investigated the problem as follows. | % gdb a.out | GNU gdb 6.3 | Copyright 2004 Free Software Foundation, Inc. | GDB is free software, covered by the GNU General Public License, and you are | welcome to change it and/or distribute copies of it under certain conditions. | Type "show copying" to see the conditions. | There is absolutely no warranty for GDB. Type "show warranty" for details. | This GDB was configured as "sparc-sun-solaris2.8"... | (gdb) disassemble g | Dump of assembler code for function g: | 0x000108ec <g+0>: save %sp, -112, %sp | 0x000108f0 <g+4>: sethi %hi(0x20c00), %l0 | 0x000108f4 <g+8>: clr [ %l0 + 0x228 ] ! 0x20e28 <flag> | 0x000108f8 <g+12>: sethi %hi(0x20c00), %o0 | 0x000108fc <g+16>: call 0x20b84 <getcontext> | 0x00010900 <g+20>: or %o0, 0x240, %o0 ! 0x20e40 <cont> | 0x00010904 <g+24>: ld [ %l0 + 0x228 ], %i0 It seems that the code assumes %l0 is preserved across the call to getcontext. | 0x00010908 <g+28>: cmp %i0, 0 | 0x0001090c <g+32>: bne,a 0x109ec <g+256> | 0x00010910 <g+36>: sethi %hi(0x10800), %o0 | 0x00010914 <g+40>: sethi %hi(0x10800), %o0 | 0x00010918 <g+44>: or %o0, 0x2b8, %o0 ! 0x10ab8 <_lib_version+16> | 0x0001091c <g+48>: call 0x20b6c <printf> | 0x00010920 <g+52>: clr %o1 | 0x00010924 <g+56>: sethi %hi(0x21000), %i5 | 0x00010928 <g+60>: sethi %hi(0x10800), %g1 | 0x0001092c <g+64>: or %g1, 0xac, %g1 ! 0x108ac <h> | 0x00010930 <g+68>: st %g1, [ %i5 + 0x20 ] | 0x00010934 <g+72>: sethi %hi(0x21000), %l7 | 0x00010938 <g+76>: st %g1, [ %l7 + 0x1c ] ! 0x2101c <p7> | 0x0001093c <g+80>: sethi %hi(0x21000), %l6 | 0x00010940 <g+84>: st %g1, [ %l6 + 0x18 ] ! 0x21018 <p6> | 0x00010944 <g+88>: sethi %hi(0x21000), %l5 | 0x00010948 <g+92>: st %g1, [ %l5 + 0x14 ] ! 0x21014 <p5> | 0x0001094c <g+96>: sethi %hi(0x21000), %l4 | 0x00010950 <g+100>: st %g1, [ %l4 + 0x10 ] ! 0x21010 <p4> | 0x00010954 <g+104>: sethi %hi(0x21000), %l3 | 0x00010958 <g+108>: st %g1, [ %l3 + 0xc ] ! 0x2100c <p3> | 0x0001095c <g+112>: sethi %hi(0x21000), %l2 | 0x00010960 <g+116>: st %g1, [ %l2 + 8 ] ! 0x21008 <p2> | 0x00010964 <g+120>: sethi %hi(0x21000), %l1 | 0x00010968 <g+124>: st %g1, [ %l1 + 4 ] ! 0x21004 <p1> | 0x0001096c <g+128>: sethi %hi(0x21000), %l0 | 0x00010970 <g+132>: call 0x108c8 <f> | 0x00010974 <g+136>: st %g1, [ %l0 ] | 0x00010978 <g+140>: ld [ %l0 ], %g1 | 0x0001097c <g+144>: call %g1 | 0x00010980 <g+148>: clr %o0 | ---Type <return> to continue, or q <return> to quit--- | 0x00010984 <g+152>: ld [ %l1 + 4 ], %g1 | 0x00010988 <g+156>: call %g1 | 0x0001098c <g+160>: clr %o0 | 0x00010990 <g+164>: ld [ %l2 + 8 ], %g1 | 0x00010994 <g+168>: call %g1 | 0x00010998 <g+172>: clr %o0 | 0x0001099c <g+176>: ld [ %l3 + 0xc ], %g1 | 0x000109a0 <g+180>: call %g1 | 0x000109a4 <g+184>: clr %o0 | 0x000109a8 <g+188>: ld [ %l4 + 0x10 ], %g1 | 0x000109ac <g+192>: call %g1 | 0x000109b0 <g+196>: clr %o0 | 0x000109b4 <g+200>: ld [ %l5 + 0x14 ], %g1 | 0x000109b8 <g+204>: call %g1 | 0x000109bc <g+208>: clr %o0 | 0x000109c0 <g+212>: ld [ %l6 + 0x18 ], %g1 | 0x000109c4 <g+216>: call %g1 | 0x000109c8 <g+220>: clr %o0 | 0x000109cc <g+224>: ld [ %l7 + 0x1c ], %g1 | 0x000109d0 <g+228>: call %g1 | 0x000109d4 <g+232>: clr %o0 | 0x000109d8 <g+236>: ld [ %i5 + 0x20 ], %g1 | 0x000109dc <g+240>: call %g1 | 0x000109e0 <g+244>: clr %o0 | 0x000109e4 <g+248>: ret | 0x000109e8 <g+252>: restore | 0x000109ec <g+256>: or %o0, 0x2c8, %o0 | 0x000109f0 <g+260>: call 0x20b6c <printf> | 0x000109f4 <g+264>: mov %i0, %o1 | 0x000109f8 <g+268>: ret | 0x000109fc <g+272>: restore | End of assembler dump. | (gdb) break *0x000108f4 | Breakpoint 1 at 0x108f4: file tst.i, line 83. | (gdb) break *0x00010904 | Breakpoint 2 at 0x10904: file tst.i, line 85. Two breakpoints are set before and after getcontext call. | (gdb) display/i $pc | (gdb) run | Starting program: /home/akr/z/a.out | | Breakpoint 1, 0x000108f4 in g () at tst.i:83 | 83 flag = 0; | 1: x/i $pc 0x108f4 <g+8>: clr [ %l0 + 0x228 ] ! 0x20e28 <flag> | (gdb) p $l0 | $1 = 134144 | (gdb) x $l0 + 0x228 | 0x20e28 <flag>: 0x00000000 %l0 is 134144 before getcontext is called. It is hi bits of the address of flag. It is used to clear flag. | (gdb) c | Continuing. | | Breakpoint 2, g () at tst.i:85 | 85 ret = flag; | 1: x/i $pc 0x10904 <g+24>: ld [ %l0 + 0x228 ], %i0 | (gdb) p $l0 | $2 = 134144 | (gdb) x $l0 + 0x228 | 0x20e28 <flag>: 0x00000000 %l0 is still 134144 after first return from getcontext. It is used to reference flag. | (gdb) c | Continuing. | first flag=0 | | Breakpoint 2, g () at tst.i:85 | 85 ret = flag; | 1: x/i $pc 0x10904 <g+24>: ld [ %l0 + 0x228 ], %i0 | (gdb) p $l0 | $3 = 135168 | (gdb) x $l0 + 0x228 | 0x21228: 0x00000000 %l0 is changed to 135168 after second return from getcontext. It is intended to reference flag but actually reference other address. The address accessed is 135168 + 0x228 which value is zero. | (gdb) x 134144 + 0x228 | 0x20e28 <flag>: 0x00000001 The value of flag is one. | (gdb) p $i0 | $4 = 0 | (gdb) si | 86 if (ret == 0) { | 1: x/i $pc 0x10908 <g+28>: cmp %i0, 0 | (gdb) p $i0 | $5 = 0 | (gdb) The condition doesn't work as intended because the compared value is different from flag. I think this is similar to GCC Bug 21957. Note that the source program is follows. | % cat tst.c | #include <stdlib.h> | #include <stdio.h> | #include <ucontext.h> | | int flag; | ucontext_t cont; | int pad[100]; | typedef void (*fun_t)(int); | fun_t p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12; | | void h(int v) | { | printf("%d\n", v); | } | | void f(void) | { | flag = 1; | setcontext(&cont); | } | | static int | g(void) | { | int ret; | | flag = 0; | getcontext(&cont); | ret = flag; | if (ret == 0) { | printf("first flag=%d\n", flag); | p0 = p1 = p2 = p3 = p4 = p5 = p6 = p7 = p8 = h; | f(); | p0(ret); p1(ret); p2(ret); p3(ret); p4(ret); p5(ret); p6(ret); p7(ret); p8(ret); | } | else { | printf("second flag=%d\n", flag); | } | return ret; | } | | int main(int argc, char **argv) | { | g(); | return 0; | }
GCC is emmitting correct asm for a normal function, why should getcontext be different?
It's because getcontext is similar to setjmp. Since setjmp/getcontext doesn't save registers in register window, modifications to the registers between first setjmp/getcontext return and longjmp/setcontext call is retained for second return from setjmp/getcontext via longjmp/setcontext. It seems that gcc knows setjmp is special but doesn't know about getcontext. Note that getcontext is defined by Single Unix Specification.
Does this work correctly with say Sun's compiler?
Confirmed. This happens to work with 2.95.3 so we have a regression.
> Does this work correctly with say Sun's compiler? Yes it does.
Although I have no Sun's Compiler, I found some pragmas about header file in SunOS: #pragma unknown_control_flow(setjmp) #pragma unknown_control_flow(_setjmp) #pragma unknown_control_flow(sigsetjmp) #pragma unknown_control_flow(getcontext) So I guess Sun's Compiler can know getcontext is special.
This is not a front-end bug. But I still think it accidentally worked in 2.95.3 or any compiler really because why do we have to treat function special.
> This is not a front-end bug. I disagree, the front-end should automatically recognize the function.
Subject: Re: [3.4/4.0/4.1 Regression] register window not preserved after getcontext call On Jun 20, 2005, at 1:46 PM, ebotcazou at gcc dot gnu dot org wrote: > > ------- Additional Comments From ebotcazou at gcc dot gnu dot org > 2005-06-20 17:46 ------- >> This is not a front-end bug. > > I disagree, the front-end should automatically recognize the function. But the place where setjmp is recognized is not in the front-end. -- Pinski
Regardless of *where* getcontext() should be recognized, it's clear that the compiler should be aware that it has special behavior. This is a wrong-code regression on a primary platform with no non-default options in use; upgraded to P1.
> Regardless of *where* getcontext() should be recognized, it's clear that the > compiler should be aware that it has special behavior. All right, I'll try to do something along these lines.
*** Bug 21957 has been marked as a duplicate of this bug. ***
Subject: Bug 22127 Author: ebotcazou Date: Thu Nov 10 16:58:56 2005 New Revision: 106739 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=106739 Log: PR middle-end/22127 * calls.c (special_function_p): Set ECF_RETURNS_TWICE for getcontext. Added: trunk/gcc/testsuite/gcc.dg/sparc-getcontext-1.c Modified: trunk/gcc/ChangeLog trunk/gcc/calls.c trunk/gcc/testsuite/ChangeLog
Subject: Bug 22127 Author: ebotcazou Date: Thu Nov 10 17:00:41 2005 New Revision: 106740 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=106740 Log: PR middle-end/22127 * calls.c (special_function_p): Set ECF_RETURNS_TWICE for getcontext. Added: branches/gcc-4_0-branch/gcc/testsuite/gcc.dg/sparc-getcontext-1.c Modified: branches/gcc-4_0-branch/gcc/ChangeLog branches/gcc-4_0-branch/gcc/calls.c branches/gcc-4_0-branch/gcc/testsuite/ChangeLog
See http://gcc.gnu.org/ml/gcc-patches/2005-11/msg00663.html. Fixed in 4.0.3 and up.