Fix C++ name-mangling issue with caml_state on Cygwin#14220
Fix C++ name-mangling issue with caml_state on Cygwin#14220dra27 merged 3 commits intoocaml:trunkfrom
caml_state on Cygwin#14220Conversation
kit-ty-kate
left a comment
There was a problem hiding this comment.
After encountering some resistance (#14224), i finally managed to build opam (which includes mccs – a C++ solver – by default) with this PR. Thanks a lot!
|
Between @kit-ty-kate test and the shortness of the patch that looks like a correct fix from a distance. @dra27 , do you have an opinion on the patch? |
This causes name-mangling issues when linking. Use an intermediate function to access the caml state.
db8da96 to
d17093d
Compare
|
Just checking something, but I expect it's only going to affect the title. What confused me when the original PR was opened is that it's not directly to do with TLS. The Cygwin port currently defines I'm trying something at the moment which will either lead to a (similarly simple) alternative or to my immediate approval of this PR 😊 |
|
Right, investigations complete - this definitely is not the way to fix this. ocaml-mccs can be fixed directly with this: diff --git a/src/context_flags.ml b/src/context_flags.ml
--- a/src/context_flags.ml
+++ b/src/context_flags.ml
@@ -28,7 +28,8 @@ let cxxflags =
(ifc useCOIN ["-DUSECOIN"]) @
(ifc useCLP ["-DUSECLP"]) @
(ifc useCBC ["-DUSECBC"]) @
- (ifc useSYM ["-DUSESYM"])
+ (ifc useSYM ["-DUSESYM"]) @
+ (ifc (Config.system = "cygwin") ["-fno-extern-tls-init"])
in
"(" ^ String.concat " " flags ^ ")"and/or the compiler can be fixed with: diff --git a/runtime/caml/misc.h b/runtime/caml/misc.h
--- a/runtime/caml/misc.h
+++ b/runtime/caml/misc.h
@@ -166,7 +166,7 @@ CAMLdeprecated_typedef(addr, char *);
#endif
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L || \
- defined(__cplusplus)
+ defined(__cplusplus) && !defined(__CYGWIN__)
#define CAMLthread_local thread_local
#else
#define CAMLthread_local _Thread_localThe ocaml-mccs fix/workaround changes the compilation of its C++ files (which don't use TLS themselves) to force GCC to omit the helper functions. The underlying reason is that the C++11's I don't think it matters - the only publicly visible TLS symbol in the runtime is |
|
Thanks for the investigation! I'm perfectly happy to wait for it to be fixed in the compiler |
|
To expand on the options:
|
|
I'm not convinced that |
I should perhaps have emphasised that more - by publicly I mean not guarded by |
|
I amended the present PR with the second suggestion of David. For the record, the initial implementation of @MisterDA did the following: diff --git a/configure.ac b/configure.ac
index 412eec0b616..9fe8670528c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1391,7 +1391,7 @@ AS_IF([! $cc_supports_atomic],
# macOS and MinGW-w64 have problems with thread local storage accessed from DLLs
AS_CASE([$target],
- [*-apple-darwin*|*-w64-mingw32*|*-pc-windows], [],
+ [*-apple-darwin*|*-w64-mingw32*|*-pc-windows|*-pc-cygwin], [],
[AC_DEFINE([HAS_FULL_THREAD_VARIABLES], [1])]
) |
|
I re-tested this branch with its new content just to be sure and it still fixes the issue. Thanks! |
configure.ac
Outdated
|
|
||
| AS_CASE([$target], | ||
| [*-apple-darwin*|*-w64-mingw32*|*-pc-windows], [], | ||
| [*-apple-darwin*|*-w64-mingw32*|*-pc-windows|*-pc-cygwin], [], |
There was a problem hiding this comment.
The changes to configure are meant to have gone, right?
|
The changes to |
|
Oops, my mistake, sorry for the time wasted on your side. I'll get rid of the configure changes and rebase. |
f4d18c7 to
69584d1
Compare
69584d1 to
bedd677
Compare
|
(I got it wrong again by reverting the configure.ac change but not the configure change! But this should be okay now.) |
No problem at all! All looks to good to me, although I'm not sure where it all sits with approvals now 😊 |
kit-ty-kate
left a comment
There was a problem hiding this comment.
The build is still successful
|
(This needs a green-approval from Florian or David, and then we can just merge and cherry-pick in 5.4.) |
|
Naive question, from reading various discussions, I have the impression that one source of the issue is that C23 |
| so prefer _Thread_local on Cygwin systems. */ | ||
| #define CAMLthread_local thread_local | ||
| #else | ||
| #define CAMLthread_local _Thread_local |
There was a problem hiding this comment.
However, using _Thread_local for C++ compiler on cygwin seems like a very reasonable way to avoid semantics difference between C and C++ thread_local to me.
Doesn't sound naïve to me! It's GCC and Clang 10+, apparently (@MisterDA?) |
I'm not aware of a change of semantics of
which is why I favored the new spelling. As we noticed C and C++ differ in the semantics of this keyword, see for instance llvm/llvm-project#70107.
Perhaps it's just wrong to use the C++ |
You're on the right track ;)
For internal C code it doesn't matter if we use Now what's happening when processing the header with a C++ compiler? #ifdef __cplusplus
extern "C" _Thread_local int x;
#endif
int f() {
return x;
}$ /opt/homebrew/opt/gcc@15/bin/g++-15 -Wall -c test.cpp
test.cpp:2:12: error: '_Thread_local' does not name a type; did you mean 'thread_local'?
2 | extern "C" _Thread_local int x;
| ^~~~~~~~~~~~~
| thread_local
test.cpp: In function 'int f()':
test.cpp:6:12: error: 'x' was not declared in this scope
6 | return x;
| ^
$ /opt/homebrew/opt/llvm@21/bin/clang++ -Wall -c test.cpp # successTo summarise, and I hope I got everything right, we could:
I have to admit this is a bit hand-wavy. |
|
What would go wrong if we used |
|
(Ah, silly question, the answer is in the code: |
|
It looks like all of gcc, g++, clang and clang++ support |
Not for MSVC, but I don't think we should abandon #if defined(__STDC_VERSION__) || defined(__clang__)
#define CAMLthread_local _Thread_local
#elif defined(_MSC_VER)
#define __declsplec(thread)
#else
#define CAMLthread_local __thread
#endif |
|
The current misc.h definition does not use |
Yes, see C11 Threads in Visual Studio 2022 version 17.8 Preview 2 and
The blogpost reads:
and I'm not sure what to make of that. Can we use either |
|
If the following example correctly models the situation, then we can observe that the same code is generated between C and C++ compilers to read and access a thread local variable. Here's an example for x86_64 with latest MSVC, Clang, and GCC: https://godbolt.org/z/bqnEhsqz6. #if defined(__STDC_VERSION__) || defined(__clang__)
#define CAMLthread_local _Thread_local
#elif defined(_MSC_VER)
#define CAMLthread_local __declspec(thread)
#else
#define CAMLthread_local __thread
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_MSC_VER) && defined(__cplusplus)
[[msvc::no_tls_guard]]
#endif
extern CAMLthread_local int x;
#ifdef __cplusplus
}
#endif
void set_x(int i) {
x = i;
}
int get_x() {
return x;
} |
This PR is my attempt to cherry-pick the part of @MisterDA's #13777 that I suppose are required to fix #13541 again -- the compatibility of OCaml C headers with C++ compilers, which is broken on Cygwin on the current 5.4 branch.
(I kept the Changes entry in 5.4 as we intend to include this in 5.4, if merged.)
I have not tested that the patch suffices to fix the issue reported by @kit-ty-kate in #13541, so anyone able to test this without too much work is warmly welcome.