Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

namespace std does not contain functions in <cmath> #607

Open
Ravenwater opened this issue Mar 26, 2020 · 10 comments
Open

namespace std does not contain functions in <cmath> #607

Ravenwater opened this issue Mar 26, 2020 · 10 comments

Comments

@Ravenwater
Copy link

Built the toolchain as indicated in the README, but math functions in the std namespace cannot be found.

test program:

#include <iostream>
#include <iomanip>
#include <cmath>

int main(int argc, char* argv[])
try {
        std::cout << std::setprecision(21);
        std::cout << std::nextafter(1.0f, 1.1f) << std::endl;
}
catch(const char* msg) {
        std::cerr << msg << std::endl;
}
catch(...) {
        std::cerr << "caught unexpected exception" << std::endl;
}

When compiled yields the following failure finding functions in cmath:

$ riscv64-unknown-elf-g++ main.cpp
main.cpp: In function 'int main(int, char**)':
main.cpp:8:20: error: 'nextafter' is not a member of 'std'; did you mean 'nextafter'?
    8 |  std::cout << std::nextafter(1.0f, 1.1f) << std::endl;
      |                    ^~~~~~~~~
In file included from /opt/riscv/riscv64-unknown-elf/include/c++/9.2.0/cmath:45,
                 from main.cpp:2:
/opt/riscv/riscv64-unknown-elf/include/math.h:298:15: note: 'nextafter' declared here
  298 | extern double nextafter (double, double);
      |               ^~~~~~~~~

Any guidance of what went wrong in the configuration of the build?

@jim-wilson
Copy link
Collaborator

Looks like a newlib related problem.

libstdc++ has a configure test to check to see if <math.h> supports ISO C99 TR1, and the configure check fails because many (all?) long double functions are missing, e.g. acoshl, fminl, truncl. So libstdc++ does not enable the TR1 support, which means no nextafter function in std.

Looking a little further, I see that the long double functions are missing because riscv*-elf targets make long double 128-bits, and newlib is missing 128-bit long double support. If we defined long double to 64-bits, using the same format as double, which is what other targets like arm does, then this would work. But changing the size of long double is an ABI break, and currently not supported. Fixing newlib to have 128-bit long double support is likely a major project.

There is a proposal to define a new embedded abi for RISC-V, and one of the changes in that proposal is to make long double 64-bits for embedded work instead of 128-bits, but it could be a few yeas before this work is finished, so no help in the short term.

On the positive side, newlib does have nextafter and nextafterf, so if you just call nextafter instead of std::nextafter that will work. Though maybe with slightly different semantics, as I don't know the difference between the C library nextafter and the C++ library nextafter. E.g. maybe the latter can throw exceptions and the former just sets an exception flag or errno of something.

@Ravenwater
Copy link
Author

Ravenwater commented Mar 27, 2020

@jim-wilson we encountered this problem during the RISC-V porting of our tensor algebra run-time that does offer 128-bit long double support on IBM POWER systems. That is to say that we have an interest to solve this problem. We need to get over the short term hump, so we will use compiler guards to stay clear of this failure.

For the 'proper' fix, we can help solve the configure check fail. Are there any other folks interested in creating a RISC-V chip that supports 128-bit IEEE floats?

@jim-wilson
Copy link
Collaborator

In the embedded space, there are a lot of people that care about code size, and a 128-bit long double causes trouble because a call to printf on a soft-float machine needs 3 soft-float libraries (float, double, long double) whereas it only needs 2 for our competitors.

For linux, a 128-bit long double is the right thing to do. I see occasional comments about q support but I haven't seen any serious implementation attempts. The assembler supports the q instructions, but gcc does not. GCC just calls libgcc soft-float routines for long double arithmetic. I don't know about llvm. Looks like qemu doesn't support the q instructions either. The ecosystem support for q is very limited at this time.

@fwsGonzo
Copy link

fwsGonzo commented May 21, 2020

Newlib has a define that lets you typedef long double to 64-bits, this shouldn't be an issue unless you really need 80-bits+ precision. 128-bits is imo completely ridiculous but maybe you have a specific need for it.

The global define is LDBL_EQ_DBL, and if you have your own build system just throw that in there.

@Ravenwater
Copy link
Author

@fwsGonzo for the RISC-V we do not need extended precision and certainly not 128-bits long doubles, although we do support these in our tensor algebra run-time for the cloud-native HPC market.

I very much would like to get over this hump, but I need a little bit more context than "just throw that in there", :-) Would you be able to guide me through this solution?

@fwsGonzo
Copy link

fwsGonzo commented May 21, 2020

I haven't looked at the riscv-gnu-toolchain makefiles, but if you can find a place to append some compiler arguments to newlib (or maybe even globally) then simply adding:

-DLDBL_EQ_DBL

would make newlib treat long double as a 64-bit FP type. With D-extension you should be fine then.

https://github.com/eblot/newlib/blob/master/newlib/configure.in#L529

I'm not so good at reading ancient configure scripts but it seems like theres no configure option to do this so you would have to edit the final configure files or some kind of cache to override this. Should be simple enough with a modern build system. At the very least, there are many ways to solve this problem. I'm not so sure if my idea to just add the global define to compiler arguments will work.

@Ravenwater
Copy link
Author

Thanks for the additional information, let's give this a whirl.

@TommyMurphyTM1234
Copy link
Collaborator

-DLDBL_EQ_DBL

I think you meant -D_LDBL_EQ_DBL?

I'm not so sure if my idea to just add the global define to compiler arguments will work.

It doesn't seem to - I tried this (I also tried with LDBL_EQ_DBL first but I'm pretty sure that that is incorrect) and still got the same problem as in the original post above:

git clone --recursive https://github.com/riscv-collab/riscv-gnu-toolchain
cd riscv-gnu-toolchain
./configure --prefix=`pwd`/installed-tools
date > build.log && CFLAGS_FOR_TARGET_EXTRA=-D_LDBL_EQ_DBL make 2>&1 | tee -a build.log && date >> build.log
// create main.cpp as per the first post
 ./riscv64-unknown-elf-g++ main.cpp -D_LDBL_EQ_DBL
main.cpp: In function 'int main(int, char**)':
main.cpp:8:27: error: 'nextafter' is not a member of 'std'; did you mean 'nextafter'?
    8 |         std::cout << std::nextafter(1.0f, 1.1f) << std::endl;
      |                           ^~~~~~~~~
In file included from /home/tommy/Downloads/riscv-gnu-toolchain/installed-tools/riscv64-unknown-elf/include/c++/12.2.0/cmath:45,
                 from main.cpp:3:
/home/tommy/Downloads/riscv-gnu-toolchain/installed-tools/riscv64-unknown-elf/include/math.h:308:15: note: 'nextafter' declared here
  308 | extern double nextafter (double, double);
      |               ^~~~~~~~~

@tomhepworth
Copy link

-DLDBL_EQ_DBL

I think you meant -D_LDBL_EQ_DBL?

I'm not so sure if my idea to just add the global define to compiler arguments will work.

It doesn't seem to - I tried this (I also tried with LDBL_EQ_DBL first but I'm pretty sure that that is incorrect) and still got the same problem as in the original post above:

git clone --recursive https://github.com/riscv-collab/riscv-gnu-toolchain
cd riscv-gnu-toolchain
./configure --prefix=`pwd`/installed-tools
date > build.log && CFLAGS_FOR_TARGET_EXTRA=-D_LDBL_EQ_DBL make 2>&1 | tee -a build.log && date >> build.log
// create main.cpp as per the first post
 ./riscv64-unknown-elf-g++ main.cpp -D_LDBL_EQ_DBL
main.cpp: In function 'int main(int, char**)':
main.cpp:8:27: error: 'nextafter' is not a member of 'std'; did you mean 'nextafter'?
    8 |         std::cout << std::nextafter(1.0f, 1.1f) << std::endl;
      |                           ^~~~~~~~~
In file included from /home/tommy/Downloads/riscv-gnu-toolchain/installed-tools/riscv64-unknown-elf/include/c++/12.2.0/cmath:45,
                 from main.cpp:3:
/home/tommy/Downloads/riscv-gnu-toolchain/installed-tools/riscv64-unknown-elf/include/math.h:308:15: note: 'nextafter' declared here
  308 | extern double nextafter (double, double);
      |               ^~~~~~~~~

Did you ever manage to solve this? Currently experiencing the same issue

@TommyMurphyTM1234
Copy link
Collaborator

TommyMurphyTM1234 commented Jul 6, 2023

Unfortunately nothing has changed and the earlier analysis by @jim-wilson still stands as far as I know.

The issue should probably really be logged upstream in the newlib project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants