Bug 51364 - C++ interoperability with ISO-C extension DFP types requires explicit typedefs.
Summary: C++ interoperability with ISO-C extension DFP types requires explicit typedefs.
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: unknown
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-11-30 18:42 UTC by Ryan S. Arnold
Modified: 2024-03-26 02:17 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments
Program to show that gcc doesn't generate good code size (11.53 KB, application/zip)
2011-12-19 02:25 UTC, Domingo Alvarez
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Ryan S. Arnold 2011-11-30 18:42:52 UTC
The following code requires explicit typedefs for _Decimal[32|64|128] in C++ programs in order to be able to use the ISO-C extension DFP types.

#include <errno.h>
#include <decimal>
#include <stdio.h>
#include <dfp/stdlib.h>

int main()
{
  char buffer[] = "0.0";
  _Decimal64 x = -1.0DD;
  errno = 0;
  x = strtod64(buffer, NULL);
  printf("%0.16De\n", x);
  return 0;
} 

/opt/at4.0/bin/g++ -g -isystem/opt/at4.0/include/c++/4.5.4/decimal -D__STDC_WANT_DEC_FP__ strtod.cpp -ldfp -o d
In file included from strtod.cpp:9:0:
/opt/at4.0/include/dfp/stdlib.h:37:8: error: '_Decimal32' does not name a type
/opt/at4.0/include/dfp/stdlib.h:42:8: error: '_Decimal64' does not name a type
/opt/at4.0/include/dfp/stdlib.h:47:8: error: '_Decimal128' does not name a type
...

This is solved be declaring the following prior to the inclusion of <dfp/stdlib.h> (which is where the _Decimal types are first encountered):

typedef float _Decimal32 __attribute__((mode(SD)));
typedef float _Decimal64 __attribute__((mode(DD)));
typedef float _Decimal128 __attribute__((mode(TD)));

Should these typedefs be implicitly defined when using the std::decimal namespace?
Comment 1 Jonathan Wakely 2011-11-30 18:57:38 UTC
n2732 says they should be defined in <float.h>

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2732.pdf
Comment 2 Janis Johnson 2011-11-30 21:33:46 UTC
The definitions that n2732 wants to add are typedefs to the C++ classes, but interoperability with C requires the typedefs using modes that Ryan shows.  I never got a good answer from the authors of the C++ technical report about how to provide C/C++ interoperability.

I'm not sure what dfp/stdlib.h is, but perhaps it should define typedefs if __cplusplus is defined.
Comment 3 Ryan S. Arnold 2011-11-30 21:40:47 UTC
(In reply to comment #2)
> The definitions that n2732 wants to add are typedefs to the C++ classes, but
> interoperability with C requires the typedefs using modes that Ryan shows.  I
> never got a good answer from the authors of the C++ technical report about how
> to provide C/C++ interoperability.
> 
> I'm not sure what dfp/stdlib.h is, but perhaps it should define typedefs if
> __cplusplus is defined.

The spec defines the following to be included in float.h for C-compatibility.

3.4.2 Header <float.h> synopsis  
      // C-compatibility convenience typedefs:  
      typedef std::decimal::decimal32  _Decimal32;  
      typedef std::decimal::decimal64  _Decimal64;  
      typedef std::decimal::decimal128 _Decimal128;

Adding these to the example code solves the issue as well.  If you feel these don't belong in float.h as the spec defines then I could put them in each of the dfp/ headers under the __cplusplus guard.
Comment 4 Janis Johnson 2011-11-30 21:44:19 UTC
If following the spec works, then by all means do that.  It's been quite a long time since I've thought about decimal float support and my memory is a bit hazy.
Comment 5 Ryan S. Arnold 2011-12-09 16:03:04 UTC
As a temporary fix I've added the following to libdfp:

http://www.eglibc.org/cgi-bin/viewvc.cgi/libdfp/trunk/dfp/float.h?view=markup

This adds <dfp/float.h>, which does #include_next <float.h>, and then checks to see #ifdef __cplusplus and #ifndef _Decimal64.  If both pass, then it means that the compiler has not yet added _Decimal[32|64|128] compatibility support to float.h and it defines them itself.
Comment 6 Domingo Alvarez 2011-12-17 21:05:30 UTC
I did a patch to sqlite3 to compile using _Decimal32 and it worked although the database format isn't the default for a reason that I still don't know, but when I tried to use it frrom c++ I got this message about missing _Decimal64, adding the typedef from decimal::decimal* to _Decimal* make it work.

One question if someone can answer is the original sqlite3.exe compiled with mingw 4.6.1 is about 500KB but when compiled with _Decimal64 it grows to 3MB 6 times bigger is this normal or it's because the decimal implementation is still in the early stages of development ?

Thanks for any help !
Comment 7 Janis Johnson 2011-12-18 01:32:43 UTC
An executable with decimal float support is very big because the runtime support is in static libraries, not in shared libraries (DLLs).  That will probably change if it ever gets widespread use.
Comment 8 Domingo Alvarez 2011-12-18 23:30:43 UTC
(In reply to comment #7)
> An executable with decimal float support is very big because the runtime
> support is in static libraries, not in shared libraries (DLLs).  That will
> probably change if it ever gets widespread use.

Thanks for the answer but there is something strange because the jump from a small executable to a big one +2.5MB is a bit strange, in one test program consisting of compiling the printf.c of sqlite3 after some defines to allow it compile outside sqlite3 source tree, the executable using _Decimal64 and _Decimal128 is 2.49MB and if I remove a call to "isnan" the size go down to 480KB and if I use _Decimal64 in place of _Decimal128 it goes down to 228KB.

With other program that only do some calculations with _Decimal64 and one use of _Decimal128 result in an executable of 135KB if I add a call to "isnan" there is no change at all on executable size.

So I got lost on understando the logic that makes gcc add 2MB of static code.

By the results of the second program it seems that it's possible to use _Decimal64 _Decimal128 and have a reasonable executable size, but suddenly it jumps to 2.5MB.

I tested lua 5.1.4 as well and it goes from using double from 150KB to using _Decimal64 to 2.48MB and I tried to cut code where _Math is done on numbers but without get any executable reduction.

So resuming I think there is possibility to use _Decimal64 without load 2.5MB it only needs some adjusts to the way gcc is generating code.
Comment 9 Jonathan Wakely 2011-12-19 00:21:36 UTC
There's nothing strange - the runtime code is in static libraries, so all the code for doing I/O must be linked into the executable if you use e.g. printf.  Using isnan probably doesn't require linking to a library function, so doesn't pull in all the code.
Comment 10 Domingo Alvarez 2011-12-19 02:25:16 UTC
Created attachment 26131 [details]
Program to show that gcc doesn't generate good code size

Here is a program and a batch file that call gcc to generate executables with different features to show that gcc doesn't generate good code size when it probably can with _Decimal*.

There is two source files main.c and printf.c the smallest executable is generated with main.c alone, the biggest executable is generated with main.c and printf.c using _Decimal128 mixed with _Decimal128 and using isnan.

gcc -O2 main.c -o smallest.exe -> 175KB
gcc -O2 -DWITH_DEC128 -DWITH_ISNAN -DWITH_MPRINTF main.c printf.c -o biggest.exe -> 2.606KB
gcc -O2 -DWITH_MPRINTF main.c printf.c -o optimus.exe -> 279KB
gcc -O2 -DWITH_DEC128 -DWITH_MPRINTF main.c printf.c -o good.exe -> 503KB

They make several mixes to show how gcc is generating code sizes to basically the same use of features.

My hope is that this will help analyze and find why this happen.
The answers to this till now don't seem to be valid/consistent.
Comment 11 Janis Johnson 2011-12-19 20:36:52 UTC
The large code size isn't from the generated code, it's from the runtime support in static libraries; compile main.c with -c and look at the size of main.o.  You might compare it to similar code using double and long double compiled with and without -msoft-float.

With limited exceptions (I know of only one) decimal floating-point arithmetic must be supported through software emulation.  GCC does this with libraries that were written to be fast and accurate rather than small.
Comment 12 Ryan S. Arnold 2012-01-18 20:00:08 UTC
Returning to the bug report at hand...

In my version of float.h I have the following (which aligns with the typedef definitions in the std::decimal namespace).  This works just fine for _Decimal* and decimal* compatibility:

typedef float _Decimal32 __attribute__((mode(SD)));
typedef float _Decimal64 __attribute__((mode(DD)));
typedef float _Decimal128 __attribute__((mode(TD)));

But the TR indicates that the following compatibility types are to be provided instead so I attempted to replace what I have with these:

3.4.2 Header <float.h> synopsis  
      // C-compatibility convenience typedefs:  
      typedef std::decimal::decimal32  _Decimal32;  
      typedef std::decimal::decimal64  _Decimal64;  
      typedef std::decimal::decimal128 _Decimal128;

I've found that when this is provided in float.h (my own overloaded float.h) I get the following warning:

warning: format '%Df' expects type '_Decimal64', but argument 2 has type '_Decimal64'

And more importantly, the following error:

../libdfp-decimal/dfp/decimal/decimal:241:32: error: no matching function for call to 'std::decimal::decimal32::__setval(_Decimal32&)'
/opt/at4.0/lib/gcc/powerpc64-linux/4.5.4/../../../../include/c++/4.5.4/decimal/decimal:304:10: note: candidate is: void std::decimal::decimal32::__setval(std::decimal::decimal32::__decfloat32)

Where the error identified code is doing the following:


  template <class charT, class traits>
    inline std::basic_istream<charT, traits> &
      operator>>(std::basic_istream<charT, traits> & is, decimal32 & d)
      {
        char buf[CHAR_MAX];
        memset(buf, '\0', CHAR_MAX);
        is.read(buf,CHAR_MAX);
--->    d.__setval(strtod32(buf, NULL));
        return is;
      }
(Note: This error isn't encountered when _Decimal32 is typedefed to float _Decimal32 __attribute__((mode(SD)));).

This is how I'd expect to be able to set the actual value in the decimal32 number (coming out of a routine that returns a C-style _Decimal32) but this obviously isn't correct since _Decimal32 is recognized by the C++ compiler as a std::decimal::decimal32 typedef and is not type compatible with std::decimal:decimal32::__decfloat32.

Interestingly, adding the following to the system decimal/decimal std::decimal::decimal32 class definition makes the error go away:

operator __decfloat32() { return __val; }

I presume what is happening is that _Decimal32 is recognized as std::decimal::decimal32 (due to the typedef) and then the __decfloat32 operator is applied to it and it then magically matches the __setval() prototype and the error is silenced.  The problem is, it doesn't work, i.e., the assignment never makes it way into __val correctly.

So I'm not sure what to make of the TR's requirement.  It seems that the typedef float _Decimal32 __attribute__((mode(SD))); method is the correct one.