Bug 109818 - std::trunc() requires a <cmath> hack after building DJGPP
Summary: std::trunc() requires a <cmath> hack after building DJGPP
Status: RESOLVED WONTFIX
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 13.1.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-05-11 18:42 UTC by Janez Zemva
Modified: 2023-06-01 10:41 UTC (History)
1 user (show)

See Also:
Host:
Target: djgpp
Build:
Known to work:
Known to fail:
Last reconfirmed: 2023-05-12 00:00:00


Attachments
Patch to make cmath configure tests more granular (3.99 KB, patch)
2023-05-12 21:58 UTC, Jonathan Wakely
Details | Diff
Patch to make cmath configure tests more granular (2.09 KB, patch)
2023-06-01 10:41 UTC, Jonathan Wakely
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Janez Zemva 2023-05-11 18:42:29 UTC
After building gcc/DJGPP, the installation requires the following hack:

sed -i '/{ return __builtin_tanh(__x); }/a\
\
  using ::trunc;\
\
#ifndef __CORRECT_ISO_CPP_MATH_H_PROTO_FP\
  constexpr float\
  trunc(float __x)\
  { return __builtin_truncf(__x); }\
\
  constexpr long double\
  trunc(long double __x)\
  { return __builtin_truncl(__x); }\
#endif\
\
  template<typename _Tp>\
    constexpr typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value, \
                                              double>::__type\
    trunc(_Tp __x)\
    { return __builtin_trunc(__x); }' $pkgdir/usr/$_target/include/c++/$pkgver/cmath

The hack can be observed in action if you install/checkout:

https://aur.archlinux.org/packages/dosbox-gcc

If we don't apply the hack, gcc will complain about std::trunc() being unavailable (compile error), if we try to compile an application requiring std::trunc(), but not if it requires plain C trunc(). At least part of the blame probably lies with DJGPP, not with libstdc++, but libstdc++ manages to hack many other math.h functions (i.e. it seems to contain a template for the hack, that it applies to many different math.h functions, just not to trunc()). Perhaps a simple fix of adding trunc() to a list of functions, that need to be hacked, is possible. Other math.h functions, not just trunc(), may also need to be hacked into <cmath>.

Like most DJGPP-related bugs, this one is very old.
Comment 1 Jonathan Wakely 2023-05-11 20:11:49 UTC
This means djgpp is doing this:

dnl To use the obsolete 'c_std' headers use --enable-cheaders-obsolete as
dnl well as --enable-cheaders=c_std, otherwise configure will fail.

Any idea why?

As it says, the include/c_std/cmath header is obsolete, and is unmaintained and so has hardly any of the C99 <math.h> functions.

The supported include/c_global/cmath header has trunc, and all the other math functions.
Comment 2 Jonathan Wakely 2023-05-11 20:13:06 UTC
Also:

  if test $enable_cheaders = c_std ; then
    AC_MSG_WARN([the --enable-cheaders=c_std configuration is obsolete, c_global should be used instead])
    AC_MSG_WARN([if you are unable to use c_global please report a bug or inform libstdc++@gcc.gnu.org])
    if test $enable_cheaders_obsolete != yes ; then
      AC_MSG_ERROR(use --enable-cheaders-obsolete to use c_std "C" headers)
    fi
  fi

So somebody is ignoring that when configuring djgpp. I think it would be much better to figure out how to get djgpp using the new header instead.
Comment 3 Janez Zemva 2023-05-11 20:56:05 UTC
I am the "maintainer" of this dosbox-gcc aur (mostly I just borrow from other builds). I have told the DJGPP community about this issue 2 times and they shrugged it off, since they mostly compile .c source files. I don't think most of them even know of these 2 configure options you mentioned and I failed to spot them when I looked into configure. My impression is that configure itself figured it needed this obsolete header and the build also finishes just fine without having to specify anything extra, just that std::trunc() is later unavailable (which doesn't bother anyone since they are mostly C users). I experimented with various env and configure options, but found nothing that worked, so I resolved to just hack <cmath>. Now I'll take another look.
Comment 4 Jonathan Wakely 2023-05-11 21:13:19 UTC
Hmm, I don't see anything in the configure scripts that would cause c_std to be used implicitly for djgpp (or any other target). Maybe there's a local patch applied to the djgpp copy of GCC sources?

It might set C_INCLUDE_DIR explicitly, or adjust the default chosen for GLIBCXX_ENABLE_CHEADERS in libstdc++-v3/acinclude.m4
Comment 5 Jonathan Wakely 2023-05-11 21:14:30 UTC
And the $build-dir/$target/libstdc++-v3/config.log file should show the output from this:

  AC_MSG_NOTICE("C" header strategy set to $enable_cheaders)

That would confirm if it's being set to c_std
Comment 6 Janez Zemva 2023-05-11 21:27:34 UTC
There are a lot of patches and I never went over all of them. I'll take another look and update my bug report, if necessary (I think you're probably right). But, should anyone else want to, the build script is accessible to everyone.
Comment 7 Jonathan Wakely 2023-05-11 22:17:48 UTC
Some of the libstdc++ changes in https://aur.archlinux.org/cgit/aur.git/tree/gcc-djgpp.diff?h=dosbox-gcc would be great to upstream.
Comment 8 Janez Zemva 2023-05-12 05:21:42 UTC
I took a look and I believe the c_global is already being selected and I believe the hack I presented should perhaps be applied to libstdc++-v3/include/c_global/cmath - the hack template is in there. As for the other hacks/patches - I am not the author of any of them. The author(s) never bothered to report any bugs and I doubt they ever will. The patches are very old and they change incrementally, whenever something in gcc changes.
Comment 9 Janez Zemva 2023-05-12 06:05:58 UTC
I'd also like to mention, that the hack is already present in the c_global cmath file, but is perhaps in a wrong section of the file, i.e. it is in the section protected by:

#if defined(__STDCPP_FLOAT64_T__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)

while I can see _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 is defined in c++config.h, I don't see __STDCPP_FLOAT64_T__ defined anywhere.
Comment 10 Jonathan Wakely 2023-05-12 08:07:44 UTC
(In reply to Janez Zemva from comment #8)
> I took a look and I believe the c_global is already being selected and I
> believe the hack I presented should perhaps be applied to
> libstdc++-v3/include/c_global/cmath 

No, absolutely not, std::trunc is already defined in that file, see line 2023:

  using ::trunc;
  using ::truncf;
  using ::truncl;

And then at line 2642:

#ifndef __CORRECT_ISO_CPP11_MATH_H_PROTO_FP
  constexpr float
  trunc(float __x)
  { return __builtin_truncf(__x); }

  constexpr long double
  trunc(long double __x)
  { return __builtin_truncl(__x); }
#endif

#ifndef __CORRECT_ISO_CPP11_MATH_H_PROTO_INT
  template<typename _Tp>
    constexpr typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value, 
                                              double>::__type
    trunc(_Tp __x)
    { return __builtin_trunc(__x); }
#endif


(In reply to Janez Zemva from comment #9)
> I'd also like to mention, that the hack is already present in the c_global
> cmath file, but is perhaps in a wrong section of the file, i.e. it is in the
> section protected by:
> 
> #if defined(__STDCPP_FLOAT64_T__) &&
> defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
> 
> while I can see _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 is defined in c++config.h,
> I don't see __STDCPP_FLOAT64_T__ defined anywhere.

No, you're looking at the definition for trunc(_Float64) which is a completely different overload.
Comment 11 Jonathan Wakely 2023-05-12 08:15:03 UTC
If djgpp is already using c_global/cmath then your change is completely wrong.

The problem is more likely to be that this macro is not defined for djgpp:

#ifdef _GLIBCXX_USE_C99_MATH_TR1

And if djgpp doesn't have a full C99 math library then that would explain why you don't have std::trunc (or any of the other C99 math functions).

Adding ad-hoc declarations for a single function in arbitrary places is certainly not going to happen. You should figure out why the existing declarations aren't being used and fix *that*, not just propose ad-hoc kluges without understanding the context.

At this point there is no clear description of the problem or the status quo for djgpp. "This change is needed" is not sufficient, sorry.
Comment 12 Janez Zemva 2023-05-12 10:14:30 UTC
I doubt DJGPP will change their C library, at least this workaround exists now.
Comment 13 Jonathan Wakely 2023-05-12 10:16:38 UTC
The workaround is wrong and will not be accepted upstream.
Comment 14 Janez Zemva 2023-05-12 10:23:32 UTC
Certainly, but users will still be able to make use of the kludge.
Comment 15 Jonathan Wakely 2023-05-12 10:48:45 UTC
The reason that std::trunc is not provided is because (In reply to Jonathan Wakely from comment #11)
> The problem is more likely to be that this macro is not defined for djgpp:
> 
> #ifdef _GLIBCXX_USE_C99_MATH_TR1
> 
> And if djgpp doesn't have a full C99 math library then that would explain
> why you don't have std::trunc (or any of the other C99 math functions).

I've confirmed this is the problem. The configure test that defines that macro fails because these are missing:

config.cc7:9: error: 'double_t' does not name a type; did you mean 'double'?
config.cc8:27: error: 'float_t' does not name a type; did you mean 'float'?
config.cc1:19: error: 'acoshl' was not declared in this scope; did you mean 'acoshf'?
config.cc4:19: error: 'asinhl' was not declared in this scope; did you mean 'asinhf'?
config.cc7:19: error: 'atanhl' was not declared in this scope; did you mean 'atanhf'?
config.cc0:19: error: 'cbrtl' was not declared in this scope; did you mean 'cbrtf'?
config.cc3:19: error: 'copysignl' was not declared in this scope; did you mean 'copysignf'?
config.cc6:19: error: 'erfl' was not declared in this scope; did you mean 'erff'?
config.cc9:19: error: 'erfcl' was not declared in this scope; did you mean 'erfcf'?
config.cc1:19: error: 'exp2f' was not declared in this scope; did you mean 'expf'?
config.cc2:19: error: 'exp2l' was not declared in this scope; did you mean 'exp2'?
config.cc5:19: error: 'expm1l' was not declared in this scope; did you mean 'expm1f'?
config.cc6:19: error: 'fdim' was not declared in this scope
config.cc7:19: error: 'fdimf' was not declared in this scope
config.cc8:19: error: 'fdiml' was not declared in this scope
config.cc9:19: error: 'fma' was not declared in this scope
config.cc0:19: error: 'fmaf' was not declared in this scope
config.cc1:19: error: 'fmal' was not declared in this scope
config.cc2:19: error: 'fmax' was not declared in this scope
config.cc3:19: error: 'fmaxf' was not declared in this scope
config.cc4:19: error: 'fmaxl' was not declared in this scope
config.cc5:19: error: 'fmin' was not declared in this scope
config.cc6:19: error: 'fminf' was not declared in this scope
config.cc7:19: error: 'fminl' was not declared in this scope
config.cc0:19: error: 'hypotl' was not declared in this scope; did you mean 'hypotf'?
config.cc3:19: error: 'ilogbl' was not declared in this scope; did you mean 'ilogbf'?
config.cc6:19: error: 'lgammal' was not declared in this scope; did you mean 'lgammaf'?
config.cc7:19: error: 'log1pl' was not declared in this scope; did you mean 'log1pf'?
config.cc0:19: error: 'log2l' was not declared in this scope; did you mean 'log2f'?
config.cc3:19: error: 'logbl' was not declared in this scope; did you mean 'logbf'?
config.cc3:19: error: 'nearbyint' was not declared in this scope
config.cc4:19: error: 'nearbyintf' was not declared in this scope
config.cc5:19: error: 'nearbyintl' was not declared in this scope
config.cc8:19: error: 'nextafterl' was not declared in this scope; did you mean 'nextafterf'?
config.cc9:19: error: 'nexttoward' was not declared in this scope
config.cc0:19: error: 'nexttowardf' was not declared in this scope
config.cc1:19: error: 'nexttowardl' was not declared in this scope
config.cc4:19: error: 'remainderl' was not declared in this scope; did you mean 'remainderf'?
config.cc5:19: error: 'remquo' was not declared in this scope
config.cc6:19: error: 'remquof' was not declared in this scope
config.cc7:19: error: 'remquol' was not declared in this scope
config.cc4:19: error: 'scalbln' was not declared in this scope; did you mean 'scalbn'?
config.cc5:19: error: 'scalblnf' was not declared in this scope; did you mean 'scalbnf'?
config.cc6:19: error: 'scalblnl' was not declared in this scope; did you mean 'scalbnf'?
config.cc9:19: error: 'scalbnl' was not declared in this scope; did you mean 'scalbnf'?
config.cc0:19: error: 'tgamma' was not declared in this scope; did you mean 'lgamma'?
config.cc1:19: error: 'tgammaf' was not declared in this scope; did you mean 'lgammaf'?
config.cc2:19: error: 'tgammal' was not declared in this scope; did you mean 'lgammaf'?

A proper fix would be to split up the configure test for USE_C99_MATH_TR1 to be more fine-grained, so that we use the subset of functions that are supported, instead of all or nothing.
Comment 16 Jonathan Wakely 2023-05-12 10:55:42 UTC
Most of the missing functions are the long double variants. We could certainly have a separate macro for long double math, which might help several targets.

The other missing ones are fdim, fma, fmin, fmax, nearbyint, nexttoward, remoquo, scalbln and tgamma, which are missing for all of float, double and long double. We could have a separate check for those.
Comment 17 Janez Zemva 2023-05-12 16:35:38 UTC
Perhaps missing functionality could also be auto-generated? I am a big fan of generative programming.
Comment 18 Jonathan Wakely 2023-05-12 16:45:10 UTC
No, the functionality belongs in the C library
Comment 19 Jonathan Wakely 2023-05-12 21:21:16 UTC
(In reply to Jonathan Wakely from comment #15)
> A proper fix would be to split up the configure test for USE_C99_MATH_TR1 to
> be more fine-grained, so that we use the subset of functions that are
> supported, instead of all or nothing.

Ugh, the set of functions defined by djgpp's <math.h> seems really arbitraty.

exp2 only has the double form, not float or long double.
expm1 has double and float forms, not long double.
trunc has all three forms.

Even within a group like the Gamma functions, lgamma and lgammaf are present (but no lgammal) but none of tgamma, tgammaf and tgammal are present.

So there's no clear way to split them up into groups that can be tested together, it ends up just being incredibly djgpp-specific (and we might as well just manually add #ifndef __DJGPP__ in very specific places).
Comment 20 Jonathan Wakely 2023-05-12 21:58:02 UTC
Created attachment 55070 [details]
Patch to make cmath configure tests more granular

Something like this might work, but it's horrible.
Comment 21 Jonathan Wakely 2023-05-12 22:01:54 UTC
Oh, checking FLT_EVAL_METHOD doesn't work, because that's defined in <float.h> not <math.h>. So another djgpp-specific "NO_FLOAT_TYPES" macro will be needed. Sigh.
Comment 22 Janez Zemva 2023-05-12 23:17:19 UTC
They are very sloppy, so I doubt even what they provide is working 100%. This is why I suggested the generative approach. gcc has many built-in functions, surely a rudimentary <cmath> could be cobbled out of them.
Comment 23 Jonathan Wakely 2023-05-12 23:43:23 UTC
No, the built-in functions just call the functions defined in libm. GCC doesn't implement them.

Look at the code for a call to __builtin_acosh:
https://godbolt.org/z/dPf46sKxx
Comment 24 Janez Zemva 2023-05-13 01:19:22 UTC
I'll go libm shopping them, I know just the thing to try out first:

https://www.ibiblio.org/pub/micro/pc-stuff/freedos/files/devel/libs/libm/
Comment 25 Jonathan Wakely 2023-05-13 09:55:37 UTC
I think the simplest solution for this bug is just use <math.h> and call trunc instead of trying to use std::trunc. If you use the <math.h> functions in the global namespace then you get exactly the set that is supported by djgpp, not the incomplete set that gets imported into namespace std. It's not ideal, but there's only so much we can do when the underlying <math.h> is incomplete.
Comment 26 Janez Zemva 2023-05-13 13:01:09 UTC
I am a c++ user, so I don't like using c header files if at all possible. I am pleased with how things are: I now compile/link a replacement libm and replace the sloppy djgpp header files, but I haven't tested this arrangement yet. Maybe it works.
Comment 27 Janez Zemva 2023-05-14 12:26:29 UTC
forcing glibcxx_cv_c99_math_tr1=yes solved this issue for me. The C99 compliance test is really strict, even openlibm fails the test (it fails to define float_t and double_t, but this can be hacked around).
Comment 28 Jonathan Wakely 2023-05-14 19:56:22 UTC
(In reply to Janez Zemva from comment #26)
> I am a c++ user, so I don't like using c header files if at all possible.

<math.h> is a C++ header file, if you doubt that, you can check the path where it's included from. You'll see that libstdc++ installs its own <math.h> in the same location as <cmath>.

If you prefer, include <cmath>, you'll get the same contents. What I'm saying is to use ::trunc not std::trunc, because that is present whether or not libstdc++ adds the math functions to namespace std.
Comment 29 Jonathan Wakely 2023-05-14 20:02:44 UTC
(In reply to Janez Zemva from comment #27)
> forcing glibcxx_cv_c99_math_tr1=yes solved this issue for me. The C99
> compliance test is really strict, even openlibm fails the test (it fails to
> define float_t and double_t, but this can be hacked around).

It's precisely as strict as it needs to be: it checks that every name that will be subject to a using-declaration is present. Because otherwise compiling <cmath> will give errors.

But supporting openlibm would be nice, so I'll separate the checks for those typedefs.
Comment 30 GCC Commits 2023-05-31 20:20:16 UTC
The master branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:49f59826c66bcaa3531429381b4aed944c332e5b

commit r14-1454-g49f59826c66bcaa3531429381b4aed944c332e5b
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed May 31 12:22:06 2023 +0100

    libstdc++: Add separate autoconf macro for std::float_t and std::double_t [PR109818]
    
    This should make it possible to use openlibm with djgpp (and other
    targets with missing C99 <math.h> functions). The <math.h> from openlibm
    provides all the functions, but not the float_t and double_t typedefs.
    By separating the autoconf checks for the functionsand the typedefs, we
    don't disable support for all the functions just because those typedefs
    are not present.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/109818
            * acinclude.m4 (GLIBCXX_ENABLE_C99): Add separate check for
            float_t and double_t and define HAVE_C99_FLT_EVAL_TYPES.
            * config.h.in: Regenerate.
            * configure: Regenerate.
            * include/c_global/cmath (float_t, double_t): Guard using new
            _GLIBCXX_HAVE_C99_FLT_EVAL_TYPES macro.
Comment 31 Jonathan Wakely 2023-05-31 20:24:08 UTC
Using openlibm should work now.

I'm inclined to close this as WONTFIX, since the problem is in djgpp.

I don't think it's worth doing more work here. If there is interest in making <cmath> work better for djgpp then improving djgpp's <math.h> would be the place to start.
Comment 32 Janez Zemva 2023-05-31 23:03:42 UTC
I resolved this issue by porting openlibm over to djgpp (some hacks and typedef float float_t;, ... were necessary). The fix on the side of gcc might have been a more thorough analysis of what is available in math.h and then making a suitable <cmath> based on that analysis. For me, this issue is resolved, but the problem of how gcc handles non-compliant standard c library implementations will probably pop up again.
Comment 33 Jonathan Wakely 2023-06-01 10:41:26 UTC
Created attachment 55232 [details]
Patch to make cmath configure tests more granular

Rebased patch. Attaching it here as I don't intend to keep it in my local git repo.