As described in the issue #160060, my understanding is that the current flang determines whether real(kind=16) is available based on the host’s LDBL_MANT_DIG. In my environment, when cross-compiling with an x86_64 host and an aarch64 target, the cross-compilation fails, even though real(kind=16) is available when compiling natively on aarch64. I would like to make this kind of cross-compilation possible. I would appreciate any opinions on how this could be improved.
Current behavior
At build time, flang checks whether the host’s LDBL_MANT_DIG is 113:
When cross-compiling in a situation where kind=16 is not available on the host but is available on the target, compilation fails.
It might be possible to implement this using something like clang::TargetInfo::getLongDoubleFormat, but this is not currently used in flang.
Possible approaches
Use clang::TargetInfo::getLongDoubleFormat
This is not currently used anywhere in flang, and it is unclear whether it can be used at all.
Decide availability based only on the target triple
For example, allow kind=16 on x86_64 and aarch64, and disallow it on others.
Even for native compilation, link errors could occur. My understanding is that the current implementation bases this decision on whether quadruple precision is supported via libquadmath or libm, so there are no cases where compilation succeeds but linking fails.
Which target triples should be considered as supporting kind=16?
Add a driver option to allow compiling with kind=16
For example, add an option like -ffortran-enable-kind16 to avoid compilation errors. Whether linking will succeed is not guaranteed.
In the case of cross-compilation, the compiler cannot determine whether the program will actually be executable on the target. For this reason, I personally think the second approach may be acceptable, but are there better alternatives? In particular, I am concerned about whether it is necessary to guarantee linkability at compile time. If the decision is made solely based on the target triple, compilation may succeed even when required libraries such as quadmath are not available, which could lead to link failures.
Since I am not sure what other improvements might be possible or what the ideal fix would be, I would appreciate any comments or suggestions.
I don’t have much experience with cross-compilation, so apologies in advance if the rest of this post is unhelpful. On the cmake side of things, could this be addressed by specifying a sysroot? My understanding is that one would need to do so when cross-compiling anyway, right? If check_c_source_compiles could be made to use headers from the target toolchain, would it get the correct value of LDBL_MANT_DIG?
This might solve the issue if there is only a single target to compile for. However, I think there would be problems when trying to cross-compile for multiple targets. For example, if LDBL_MANT_DIG = 113 is obtained from the sysroot headers at build time, flang would be built assuming that KIND=16 is available for all targets.
If this were to be handled correctly at build time, I think it would be necessary to check the corresponding LDBL_MANT_DIG from each target’s sysroot and store the results somewhere. I do not know how many users cross-compile for multiple architectures, but ideally there should be a mechanism to determine whether KIND=16 can be compiled at compilation time.
Yes, I also understand that a sysroot is required every time when cross-compiling.
It would probably be possible, but I am not confident that it would be correct. Also, as mentioned above, I believe that the decision about whether compilation is possible should be made at compile time rather than at build time.
Is this an issue with the architecture, the libraries available on the system or both? For instance, Is it possible that libm or libquadmath on, say, aarch64, does not have quad-precision support? In that case, would we see a link-time failure when compiling code containing REAL(16)?
Sorry, but I am trying to understand what the fundamental issue is here.
If I understand this correctly, it sounds like it would largely duplicate the work currently being done in clang/lib/Basic/Targets. It would be worth reusing since that code since it is fairly involved. However, all of it is in libclangBasic.a. flang currently does depend on it, but my understanding is that we would like to remove this dependence if possible. @kiranchandramohan , please correct me if this is not the case.
That being said, there is a very broad range of targets that are supported there. If we are not interested in supporting such a broad range, including 32-bit and 64-bit variants of each, it may be easier.
I think libquadmath always provides real(16) support, but libm might not if the version is too old.
That is why it is more complex than just examining the Target. As @s-watanabe314 observed, the hard part is being sure that the program will link.
We only know this at build time of flang-rt (for a particular target). I wonder if some cmake magic could build flang-rt with and without real(16) support and then something in the compiler driver could figure out which flang-rt to link, and pass a flag to the front end driver explaining whether our flang-rt has real(16) or not.
Last I checked this is far from the only assumption in flang that breaks cross compilation. For example see flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h.
Yes, I think the same functionality as in clang/lib/Basic/Targets is needed. If we decide not to reuse it in order to avoid depending on Clang, then I think the only option would be for Flang to implement minimal target-specific handling on its own.
However, ideally, target information should be separated from Clang and shared among Clang, Flang, and other frontends. It’s possible that the project discussed in this RFC could lead to a common implementation of Clang::TargetInfo that Flang could then reference. I haven’t been able to follow this project closely, so I’m not sure yet whether it will be available.
My understanding is that the LLVMABI project is narrowly focused on types.
If flang-rt knows about quad-precision support available on the target at build time, we could see if variable could be set at configure-time in a header file that is visible to the driver and/or frontend. This would be similar, in principle, to something like CLANG_DEFAULT_RTLIB in clang/include/clang/Config/config.h.cmake. However, flang-rt is a separate project, so this may not be possible at all. I have not looked into the feasibility of this approach.
When building flang-rt , how can we know if quad-precision support is available for the target? It seems that we can know quad-precision support for the host at build time, but we may not have enough information about the target environment.
How should the compiler driver select which flang-rt to link? My understanding is that we either have to reuse clang::TargetInfo::getLongDoubleFormat() or implement similar logic in flang. Are there any other approaches?
how can we know if quad-precision support is available for the target?
I presume it looks in the sysroot configured for that target to see if there’s libquadmath or libm support. For cross-compilation of flang-rt to work at all, cmake must understand that it shouldn’t be looking at the build system’s headers/libraries.
As I understand it, clang::TargetInfo::getLongDoubleFormat() only tells us if the CPU supports real(16). But if the libraries on the target system don’t support real(16) then fortran builtins (e.g. SIN, COS, etc) won’t work. I was thinking flang could check at link time if those libraries are available for the configured target and if so link to the version of flang-rt built with real(16) support (if available for that target). This still isn’t perfect because the system linking might not have the same libraries available as the system running, but I thought this would get us one step closer than deciding when flang is built.
My understanding is that the current flang-rt checks the value of LDBL_MANT_DIG from the build environment and for the presence of sinl in libm (flang-rt/lib/quadmath/CMakeLists.txt) . When cross-compiling, it would be ideal if there were a way to check LDBL_MANT_DIG for the target environment, but I don’t know how to do that.
Even if the target’s LDBL_MANT_DIG value is unknown, I think we could determine that quad-precision support is available if libquadmath exists. However, if only libm is present, it is difficult to determine whether quad-precision is supported.
If the *f128 functions were implemented in flang-rt/lib/quadmath/math-entries.h, it might be possible to check the glibc version, or to directly inspect whether libm provides quad-precision symbols (such as sinf128) to infer quad-precision support. But if there are only C long double symbols like sinl, I believe it is hard to determine whether quad-precision is actually supported.
In summary, can we assume quad-precision is available in any of the following cases?
libquadmath exists in the sysroot.
The target’s LDBL_MANT_DIG is 113, and sinl exists in the sysroot’s libm.
The method to check the target’s LDBL_MANT_DIG value is unknown.
sinf128 exists in the sysroot’s libm.
*f128 is not yet implemented in flang-rt.
I’m not sure how accurate this understanding is, so I would appreciate any corrections or comments if my understanding is mistaken.
I am not at all an expert in this area, but my understanding is as follows:
When building a runtime for a particular target, the llvm build system creates a new cmake invocation for that runtime build - in this case flang-rt
flang-rt/CMakeLists.txt includes FlangCommon (flang/cmake/modules/FlangCommon.cmake)
I think FlangCommon is then evaluated in the context of the new cmake process for this target’s build of flang-rt (I might be wrong here - not a cmake expert at all)
FlangCommon contains the test that sets HAVE_LDBL_MANT_DIG_113 (by compiling C source which uses the preprocessor to inspect the value of the macro).
If HAVE_LDBL_MANT_DIG is set then we can use sinl.
I think so long as FlangCommon.cmakeis in fact evaluated with build settings configured for the target (mostly using the correct sysroot so that it is including the target’s header not the host’s header) then this should produce results that are correct for the sysroot being used.
I agree that the presence of libquadmath is also sufficient to allow this support. This is currently set using the FLANG_RUNTIME_F128_MATH_LIB variable. I wonder if this could need some additional handling to allow the variable to be set to different values for different targets.
I can also see a check in flang-rt for sinf128. I agree that presence of these symbols should also be sufficient.
I think most of this hinges on whether the runtimes build has correctly configured cmake such that check_library_exists() and check_c_source_compiles() are run using the target’s sysroot rather than the host.
I believe the problem of cross-compiling with kind=16 can be divided into two main parts:
Whether to enable kind=16 when cross-building flang-rt.
How to determine if kind=16 compilation is possible when cross-compiling a Fortran program.
Regarding the first point, cross-building flang-rt, as you mentioned, it seems that CMake would need to inspect the sysroot.
For the second point, determining the feasibility of kind=16 compilation, what if the driver checks whether flang-rt contains quad-precision symbols (e.g., _FortranASinF128)? The idea would be for this information to then be passed as an option to the frontend. With this approach, native compilation would continue to work as it does currently, and cross-compilation should become possible if an appropriate runtime for the different target is available. For example, copying a flang-rt built natively on the target environment to the host should enable cross-compilation. However, this approach wouldn’t check for the presence of symbols like sinf128 from the finaly linked libm, so there’s a possibility of a linker error.
Relying on the presence of symbols in flang-rt sounds good enough to me. I think it is reasonable for there to be link errors if the cross-compilation sysroot doesn’t match the system that flang-rt is deployed on.
Yes. We may need to update the documentation to make it clear to users that they may encounter link errors if cross-compiling code that uses quad-precision arithmetic. The FAQ page may be a good place for this.
Just to be clear, are we talking about inspecting the presence of symbols in flang-rt each time flang is used? Or is it sufficient to check if flang-rt has been built with quad-precision support? I expect that in the second case, symbols such as _FortranASinF128 are guaranteed to be present.
Thank you for your feedback. I will proceed with relying on the presence of symbols in flang-rt. I will also add the possibility of link errors to the documentation.
I also recently noticed that whether the driver passes -lquadmath to the frontend is hardcoded here, and this also needs to be addressed together.
I believe it’s the first case. The idea is to check if _FortranASinF128 is included in flang-rt each time Flang is used. If quad-precision is not supported, _FortranASinF128 should not exist, and thus it would result in a compilation error. I do not intend to check what _FortranASinF128 actually links to (e.g., sinq or sinl). My apologies if I misunderstood your question.
Not checking which underlying math function libflang_rt is using (sinq or sinl in your example) seems reasonable to me.
However, do you anticipate performing this check for both native and cross-compilation? Or only when cross-compiling? I don’t know this part of the code well, and I don’t have a great deal of experience with cross-compilation, so apologies if this is obvious.
I plan to perform the check for both native and cross-compilation. I am not considering separating the process based on whether the compilation is native or cross.