🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

1 total messages Started by Satoshi Adachi Sat, 30 Dec 1995 00:00
An inconsistency in the calling convention used by GCC on RS6000/AIX.
#6266
Author: Satoshi Adachi
Date: Sat, 30 Dec 1995 00:00
542 lines
21445 bytes
Dear Developers of GNU C compiler,

((0)) This is a bug report about GNU C compiler (version 2.7.2) built
for IBM RS6000 running on AIX 3.2.x and AIX 4.1.x.

The problem is that GNU C compiler uses a slightly different calling
convention to call some functions which take a floating point number as
the argument. Consequently, some functions in the system libraries receive
wrong argument and accordingly return a wrong result.


((1)) I found this incompatibility in the calling convention for
the system function "finite" at first. So, I will explain the problem
with this function in the below.

The following is a simple program to test the function "finite".
The function "finite" is called with the argument of the type double
having the value the minus infinite. Accordingly, the function "finite"
should return the value 0 indicating FALSE.

-------------------------------------------------------------------------------
hera[1] cat test_finite.c 
/*
 *   test_finite.c
 *
 *   S.Adachi (adachi@aa.ap.titech.ac.jp, 95/12/29)
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

extern int finite(double x);

int main(int argc,
         char** argv)
{
  unsigned char buf[] = { 0xFF, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
  double minus_infinite = *(double*)buf;

  int res = finite(minus_infinite);

  printf("%e --> finite = %s\n", minus_infinite, (res ? "TRUE" : "FALSE"));

  return 0;
}
-------------------------------------------------------------------------------

For the sake of your ease to read the above program, I will describe briefly
the IEEE floating point format for double number.

    63  62            51                                0
   +---+-------------+-----------------------------------+
   | s | 11-bit exp. |          52-bit fraction          |
   +---+-------------+-----------------------------------+

The positive (negative) infinite number is represented by

	    s == 0 (1) 
	and exponent == 2047 == B11111111111 == 0x7FF
	and fraction == 0.

At first, let us compile the above program by the native CC compiler
built by IBM and test the resultant executable.

-------------------------------------------------------------------------------
hera[2] cc test_finite.c 
hera[3] a.out
-INF --> finite = FALSE
-------------------------------------------------------------------------------

OK. This is the expected result. The IBM comlier CC compiled the program
correctly.

Then let us repeat the same test with the GNU C compiler (version 2.7.2).

-------------------------------------------------------------------------------
hera[4] rm a.out
rm: remove `a.out'? y
hera[5] gcc -Wall test_finite.c 
hera[6] a.out
-INF --> finite = TRUE
-------------------------------------------------------------------------------

This result is wrong. GNU C compiler failed in compiling the test program
correctly....


((2)) Let us inspect what is going on in the executable compiled by GCC
with the help of GDB.

-------------------------------------------------------------------------------
hera[7] rm a.out
rm: remove `a.out'? y
hera[8] gcc -g test_finite.c 
hera[9] gdb a.out
GDB is free software and you are welcome to 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.
GDB 4.15.1 (rs6000-ibm-aix3.2), Copyright 1995 Free Software Foundation, Inc...
(gdb) break main
Breakpoint 1 at 0x2a0: file test_finite.c, line 16.
(gdb) break finite
Breakpoint 2 at 0x538
(gdb) run
Starting program: /mnt/home/work/ddd/a.out 
Breakpoint 1 at 0x100004a0: file test_finite.c, line 16.
Breakpoint 2 at 0x10000738

Breakpoint 1, main (argc=1, argv=0x2ff7fc48) at test_finite.c:16
16        unsigned char buf[] = { 0xFF, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
(gdb) disassemble
Dump of assembler code for function main:
0x1000047c <main>:      mflr    r0
0x10000480 <main+4>:    st      r31,-4(r1)
0x10000484 <main+8>:    st      r0,8(r1)
0x10000488 <main+12>:   stu     r1,-88(r1)
0x1000048c <main+16>:   mr      r31,r1
0x10000490 <main+20>:   st      r3,112(r31)
0x10000494 <main+24>:   st      r4,116(r31)
0x10000498 <main+28>:   bl      0x100006c0 <__main>
0x1000049c <main+32>:   cror    31,31,31
0x100004a0 <main+36>:   cal     r0,56(r31)
0x100004a4 <main+40>:   lil     r0,255
0x100004a8 <main+44>:   stb     r0,56(r31)
0x100004ac <main+48>:   lil     r0,240
0x100004b0 <main+52>:   stb     r0,57(r31)
0x100004b4 <main+56>:   lil     r0,0
0x100004b8 <main+60>:   stb     r0,58(r31)
0x100004bc <main+64>:   lil     r0,0
0x100004c0 <main+68>:   stb     r0,59(r31)
0x100004c4 <main+72>:   lil     r0,0
0x100004c8 <main+76>:   stb     r0,60(r31)
0x100004cc <main+80>:   lil     r0,0
0x100004d0 <main+84>:   stb     r0,61(r31)
0x100004d4 <main+88>:   lil     r0,0
0x100004d8 <main+92>:   stb     r0,62(r31)
0x100004dc <main+96>:   lil     r0,0
0x100004e0 <main+100>:  stb     r0,63(r31)
0x100004e4 <main+104>:  l       r0,60(r31)
0x100004e8 <main+108>:  st      r0,68(r31)
0x100004ec <main+112>:  l       r0,56(r31)
0x100004f0 <main+116>:  st      r0,64(r31)
0x100004f4 <main+120>:  lfd     f1,64(r31)
0x100004f8 <main+124>:  bl      0x10000738 <finite>
0x100004fc <main+128>:  cror    31,31,31
0x10000500 <main+132>:  mr      r0,r3
0x10000504 <main+136>:  st      r0,72(r31)
0x10000508 <main+140>:  l       r0,72(r31)
0x1000050c <main+144>:  cmpi    1,r0,0
0x10000510 <main+148>:  beq     1,0x1000051c <main+160>
0x10000514 <main+152>:  l       r0,28(r2)
0x10000518 <main+156>:  b       0x10000520 <main+164>
0x1000051c <main+160>:  l       r0,32(r2)
0x10000520 <main+164>:  l       r3,36(r2)
0x10000524 <main+168>:  l       r4,64(r31)
0x10000528 <main+172>:  l       r5,68(r31)
0x1000052c <main+176>:  lfd     f1,64(r31)
0x10000530 <main+180>:  mr      r6,r0
0x10000534 <main+184>:  bl      0x10000774 <printf>
0x10000538 <main+188>:  l       r2,20(r1)
0x1000053c <main+192>:  lil     r3,0
0x10000540 <main+196>:  b       0x10000544 <main+200>
0x10000544 <main+200>:  l       r1,0(r1)
0x10000548 <main+204>:  l       r0,8(r1)
0x1000054c <main+208>:  mtlr    r0
0x10000550 <main+212>:  l       r31,-4(r1)
0x10000554 <main+216>:  br
0x10000558 <main+220>:  .long 0x0
0x1000055c <main+224>:  .long 0x2061
0x10000560 <main+228>:  l       r0,513(r1)
0x10000564 <main+232>:  .long 0x0
0x10000568 <main+236>:  .long 0xdc
0x1000056c <main+240>:  .long 0x46d61
0x10000570 <main+244>:  xoril   r14,r11,7936
End of assembler dump.
(gdb) continue
Continuing.

Breakpoint 2, 0x10000738 in finite ()
(gdb) disassemble
Dump of assembler code for function finite:
0x10000738 <finite>:    rlinm   r5,r3,12,21,31
0x1000073c <finite+4>:  cmpli   0,r5,2047
0x10000740 <finite+8>:  lil     r3,1
0x10000744 <finite+12>: bner    
0x10000748 <finite+16>: lil     r3,0
0x1000074c <finite+20>: br
0x10000750 <finite+24>: .long 0x0
0x10000754 <finite+28>: .long 0xc0000
0x10000758 <finite+32>: .long 0x0
0x1000075c <finite+36>: .long 0x0
0x10000760 <finite+40>: .long 0x0
0x10000764 <finite+44>: .long 0x0
0x10000768 <finite+48>: .long 0x0
0x1000076c <finite+52>: .long 0x0
0x10000770 <finite+56>: .long 0x0
End of assembler dump.
(gdb)
-------------------------------------------------------------------------------

I cannot read the assembler code for RS6000.
(M68K is the only one CPU for which I can read the assembler code...)
However, I notice immediately from the above assembler listing that
there is an inconsistency to transfer the argument from the main routine
to the function "finite".

	In the function "finite", any floating point register is not
	touched at all, even though the function takes an argument of
	the type double.

Consequently, I have to conclude that in some situation the correct calling
convention (*) for RS6000/AIX requires that if a function takes an argument
of the type double, then the argument should be passed from the calling
routine to the called routine not only through a floating point register
as is usual but also through a general register simultaneously.

Clearly, GCC does not obey this kind of calling convention as is seen in
the above assembler listing for the main routine.


((3)) Let us confirm that the above calling convention (*) is actually used,
when the program is compiled by the native compiler CC built by IBM.

-------------------------------------------------------------------------------
hera[10] rm a.out
rm: remove `a.out'? y
hera[11] cc -g test_finite.c 
hera[12] gdb a.out
GDB is free software and you are welcome to 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.
GDB 4.15.1 (rs6000-ibm-aix3.2), Copyright 1995 Free Software Foundation, Inc...
(gdb) break main
Breakpoint 1 at 0x294: file test_finite.c, line 16.
(gdb) run
Starting program: /mnt/home/work/ddd/a.out 
Breakpoint 1 at 0x10000494: file test_finite.c, line 16.

Breakpoint 1, main (argc=1, argv=0x2ff7fc48) at test_finite.c:16
16        unsigned char buf[] = { 0xFF, 0xF0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
(gdb) disassemble
Dump of assembler code for function main:
0x10000478 <main>:      mflr    r0
0x1000047c <main+4>:    st      r31,-4(r1)
0x10000480 <main+8>:    st      r0,8(r1)
0x10000484 <main+12>:   stu     r1,-128(r1)
0x10000488 <main+16>:   l       r31,8(r2)
0x1000048c <main+20>:   st      r3,152(r1)
0x10000490 <main+24>:   st      r4,156(r1)
0x10000494 <main+28>:   lil     r3,255
0x10000498 <main+32>:   stb     r3,56(r1)
0x1000049c <main+36>:   lil     r3,240
0x100004a0 <main+40>:   stb     r3,57(r1)
0x100004a4 <main+44>:   lil     r3,0
0x100004a8 <main+48>:   stb     r3,58(r1)
0x100004ac <main+52>:   stb     r3,59(r1)
0x100004b0 <main+56>:   stb     r3,60(r1)
0x100004b4 <main+60>:   stb     r3,61(r1)
0x100004b8 <main+64>:   stb     r3,62(r1)
0x100004bc <main+68>:   stb     r3,63(r1)
0x100004c0 <main+72>:   ai      r3,r1,56
0x100004c4 <main+76>:   lfd     f1,0(r3)
0x100004c8 <main+80>:   stfd    f1,64(r1)
0x100004cc <main+84>:   lfd     f1,64(r1)
0x100004d0 <main+88>:   stfd    f1,96(r1)
0x100004d4 <main+92>:   l       r3,96(r1)			(**)
0x100004d8 <main+96>:   l       r4,100(r1)			(**)
0x100004dc <main+100>:  bl      0x10000568 <finite>
0x100004e0 <main+104>:  cror    31,31,31
0x100004e4 <main+108>:  st      r3,72(r1)
0x100004e8 <main+112>:  l       r3,72(r1)
0x100004ec <main+116>:  cmpi    1,r3,0
0x100004f0 <main+120>:  beq     1,0x10000500 <main+136>
0x100004f4 <main+124>:  ai      r3,r31,20
0x100004f8 <main+128>:  st      r3,76(r1)
0x100004fc <main+132>:  b       0x10000508 <main+144>
0x10000500 <main+136>:  ai      r3,r31,28
0x10000504 <main+140>:  st      r3,76(r1)
0x10000508 <main+144>:  l       r3,76(r1)
0x1000050c <main+148>:  st      r3,80(r1)
0x10000510 <main+152>:  oril    r3,r31,0
0x10000514 <main+156>:  lfd     f1,64(r1)
0x10000518 <main+160>:  l       r6,80(r1)
0x1000051c <main+164>:  stfd    f1,96(r1)
0x10000520 <main+168>:  l       r4,96(r1)
0x10000524 <main+172>:  l       r5,100(r1)
0x10000528 <main+176>:  bl      0x100005a4 <printf>
0x1000052c <main+180>:  l       r2,20(r1)
0x10000530 <main+184>:  lil     r3,0
0x10000534 <main+188>:  l       r31,124(r1)
0x10000538 <main+192>:  l       r0,136(r1)
0x1000053c <main+196>:  mtlr    r0
0x10000540 <main+200>:  ai      r1,r1,128
0x10000544 <main+204>:  br
End of assembler dump.
(gdb) 
-------------------------------------------------------------------------------

That is right. In the main routine, the argument of the type double
to be passed to the functin "finite" is put not only in the floating point
register "f1" (length = 8 bytes) but also in the general registers "r3" and
"r4" (length = 4 bytes x 2). Please look at the instrunctions marked with (**)
in the above listing. (I guess that "l" represents "load" for RS6000.)


((4)) Furthermore, I found how the calling convention for functions which
take argument(s) of the type "double" is controled in the native environment,
namely,  the condition to require that the argument(s) for such a function
as "finite" should be prepared not only in the floating point registers
but also in the general registers.

In the sytem header file "math.h", the functions, including "finite",
which require the special calling convention are declared in the following
way:

-------------------------------------------------------------------------------
hera[13] cat /usr/include/math.h
...
#ifdef _NO_PROTO
...
extern     int      finite();
...
#else /* _NO_PROTO */                   /* Use ANSI C required prototyping */
...
#ifdef _IBMR2
...
     /* The IBMR2 wants arguments in both GPR's and FPR's
      * By not specifying a prototype of double, the compiler
      * will put the argument in both types of registers.
      */
...
extern     int      finite();
#else
...
extern     int      finite(double x);
#endif
#endif /* _NO_PROTO */
...
-------------------------------------------------------------------------------

Namely, even when the prototyped declarations are requested as is usual in the
ANSI environment, if the preprocessor variable "_IBMR2" is defined,
several functions including "finite" should be declared without prototype.
This is the way to control the calling convention in the native environment.

In AIX running on RS6000, the preprocessor variable "_IBMR2" is defined
and this is inherited to GCC.

Furthermore, please remark that the comment written by IBM in the above
header file. It confirms our consideration about the calling convention
in the RS6000/AIX world.


((5)) In the following, I will demonstrate that the existance of the
non-prototyped declaration for the function "finite", irrespective of
the existance of the prototyped declaration for the same function,
enforces that the special calling convention is used for "finite"
in the native IBM environment.

First, let us see the existance of the declaration of the non-prototype type
for the function "finite" surely results in the correct executable
after compilation.

-------------------------------------------------------------------------------
hera[14] cat test_finite.c | sed 's/#include <math.h>/extern int finite();/' \
	> test_finite_no_proto_1.c
hera[15] diff -c test_finite.c test_finite_no_proto_1.c 
*** test_finite.c       Sat Dec 30 20:55:59 1995
--- test_finite_no_proto_1.c       Sat Dec 30 22:27:31 1995
***************
*** 6,12 ****
  
  #include <stdio.h>
  #include <stdlib.h>
! #include <math.h>
  
  extern int finite(double x);
  
--- 6,12 ----
  
should be able to overcome by some way.  #include <stdio.h>
  #include <stdlib.h>
! extern int finite();
  
  extern int finite(double x);
  
hera[16] cc test_finite_no_proto_1.c 
hera[17] a.out
-INF --> finite = FALSE
-------------------------------------------------------------------------------

OK. The executable "a.out" prints the right answer.

Next, repeat the same test after the prototyped declaration is deleted
and the non-prototyped declaration is remained.

-------------------------------------------------------------------------------
hera[18] cat test_finite_no_proto_1.c | sed '/extern int finite(double x);/d' \
	> test_finite_no_proto_2.c
hera[19] diff -c test_finite.c test_finite_no_proto_2.c 
*** test_finite.c       Sat Dec 30 20:55:59 1995
--- test_finite_no_proto_2.c       Sat Dec 30 22:31:56 1995
***************
*** 6,14 ****
  
  #include <stdio.h>
  #include <stdlib.h>
! #include <math.h>
  
- extern int finite(double x);
  
  int main(int argc,
         char** argv)
--- 6,13 ----
  
  #include <stdio.h>
  #include <stdlib.h>
! extern int finite();
  
  
  int main(int argc,
         char** argv)
hera[20] cc test_finite_no_proto_2.c 
hera[21] a.out
-INF --> finite = FALSE
-------------------------------------------------------------------------------

OK. This is the right answer.

Finally, modify the source code to use only the prototyped declaration
for "finite" and not to have the non-prototyped declaration for it
and repeat the same test.

-------------------------------------------------------------------------------
hera[22] cat test_finite.c | sed '/#include <math.h>/d' > test_finite_proto.c
hera[23] diff -c test_finite.c test_finite_proto.c 
*** test_finite.c       Sat Dec 30 20:55:59 1995
--- test_finite_proto.c Sat Dec 30 22:38:45 1995
***************
*** 6,12 ****
  
  #include <stdio.h>
  #include <stdlib.h>
- #include <math.h>
  
  extern int finite(double x);
  
--- 6,11 ----
hera[24] cc test_finite_proto.c 
hera[25] a.out
-INF --> finite = TRUE
-------------------------------------------------------------------------------

This is the wrong answer. So, the absence of the non-prototyped declaration
for "finite" in the source file results in the incorrect executable in which
the argument of the type double to be passed to "finite" is not copied to
the general registers.

Furthermore, I have checked that the explanation in this section is actually
correct by inspecting the assmebler code generated from the above various
source files with the help of GDB. However, I will omit the assembler code
listings in order to trim this report to be short.

Finally, contrast to the native C compiler built by IBM, the GNU C compiler
does not support such a control of the calling convention by the declaration
of a function (as is expected). I have confirmed this statement by a simmilar
test.


((6)) You may have been noticed by other users of GNU C compiler about
the problem which I reported in this email. Anyway, this is a serious
problem for the heavy users of GNU C compiler on RS6000.

I met an unexpected trouble when I am debuging one of my large simulation
programs in physics. My efforts to debug the program finally produced this
report. I had to spend several hours for that purpose...

After I understood that the problem originated in the inconsistency in the
calling convention used by GCC, I first expected that GCC might prepare
some compilation flag to control the calling convention which GCC uses.
So, I read the manual of GCC. However, I cannot find such a switch for GCC...

I believe that this problem about the inconsistency in the calling convention
used in GCC for RS6000/AIX should be overcome by some way.

There seem to be several ways to overcome this problem. They are:

	(Plan 1) Change the calling convention used by GCC to ALWAYS preare
		 the floating point argument passed to a function like "finite"
		 in the general registers in addition to the floating point
		 register.

	(Plan 2) Modify GCC to mimic completely IBM CC. Namely, add a control
		 of the calling convention by the function declaration.

	(Plan 3) Do not modify GCC and prepare the system functions which use
		 originally strange calling convention, such as "finite", in
		 the library libgcc.a to use the "normal" present calling
		 convention.

	(Plan 4) Do not modify neither GCC nor libgcc.a, and wait for the
		 development of "glibc" and annource to evryone to use "glibc"
		 and to dispose all the system libraries.

Each plan has its own merit and demerit.

(Plan 1) is simple and remove from us the necessity to worry about whether
a new system function uses the strange calling convention. However, the
performance of the executable compiled with the new calling convention
is surely reduced.

(Plan 2) also fix the problem. This plan does not reduce the performance of
the compiled executables. However, the "fixinclude" step should be preapred
very carefully for RS6000/AIX in order to guarantee the "correct" declaration
for the functions which use the strang calling convention. Furthermore, the
strange calling convention becomes widely spreaded including the newly compiled
libraries and programs with GCC.

(Plan 3) This may be the best plan in real world. However, someone shold make
the list of the system routines which use the strange calling convention and
write the source code for the functions. Furhtermore, the list should be
maintained carefully from one version of AIX to another.

(Plan 4) This plan may be too lazy and too radical.


Anyway, I hope that you will fix this problem in some way.

Thank you very much for your efforts to develop and maintain such a nice
and free compiler. Without GCC, I cannot study physics!

Sincerely yours,

Satoshi Adachi
(adachi@wisdom.aa.ap.titech.ac.jp)

Departemnt of Applied Physics
Tokyo Institute of Technology
Oookayama 2-12-1, Meguro
Tokyo 152, JAPAN
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads