Hello, I am the developer of the Jule programming language.
First of all, thank you for this project.
Clang is the recommended C++ backend compiler for Jule.
On Windows, llvm-mingw is the recommended toolchain.
I recently released Jule version 0.2.0. Until now, I had not encountered any issues with this toolchain. However, starting with this release, I began using thread-local variables. Their primary purpose is to manage coroutine state across worker threads and to store a pointer to the runtime object associated with the current thread.
I did not notice any problems until the release process, because during development I was using debug builds and therefore did not observe issues caused by optimizations. For builds, I use the compilation commands generated by julec (the official Jule compiler). When producing a production build, julec enables several optimization flags, and one of these clearly triggers the problem.
For Windows builds, I use a Windows 11 virtual machine running under VMware Fusion. The primary architecture is ARM64 (AArch64). For ARM64 builds, I use the default clang++. On the same virtual machine, I also build for Windows AMD64 (x86_64), using x86_64-w64-mingw32-clang++. The compilation command is otherwise identical.
These are the exact compilation commands I am using:
windows-arm64
clang++ -static -Wno-everything --std=c++20 -fwrapv -ffloat-store -fno-fast-math -fno-rounding-math -ffp-contract=fast -O3 -flto=thin -DNDEBUG -fomit-frame-pointer -fno-strict-aliasing -o ./bin/julec.exe ir.cpp -lws2_32 -lshell32 -liphlpapi -lsynchronization
windows-amd64
x86_64-w64-mingw32-clang++ -static -Wno-everything --std=c++20 -fwrapv -ffloat-store -fno-fast-math -fno-rounding-math -ffp-contract=fast -O3 -flto=thin -DNDEBUG -fomit-frame-pointer -fno-strict-aliasing -o ./bin/julec.exe ir.cpp -lws2_32 -lshell32 -liphlpapi -lsynchronization
Compiling with these commands consistently results in the following linker error:
ld.lld: error: undefined symbol: thread-local initialization routine for __jule_ct
>>> referenced by ir.cpp
>>> C:/Users/mertc/AppData/Local/Temp/ir-e624c2.o
clang-22: error: linker command failed with exit code 1 (use -v to see invocation)
The symbol __jule_ct referenced in the error is a thread_local variable that holds a pointer to the thread's runtime object. Its type is __jule_Ptr, which is a smart pointer implementing reference counting. This type is not trivially copyable, trivially constructible, or trivially destructible.
I investigated the issue further, and it appears to be related to the interaction between the __jule_Ptr type and LTO. If I avoid using __jule_Ptr and instead use a fully trivial type (for example, a raw pointer), LTO does not cause any problems. However, when LTO is enabled, LTO appears to eliminate or fail to preserve the TLS initialization routine generated for the __jule_Ptr thread-local variable, which then results in the linker error shown above.
If -flto=thin is removed from the compilation commands above, the problem is resolved. Any use of LTO, whether thin or full, triggers this issue.
ir.cpp is a C++ IR file generated by julec. However, the issue is actually unrelated to this, the real problem is the __jule_ct variable defined on the Jule's C++ API side.
__jule_ct is defined here: api/async.hpp
It looks like this:
inline thread_local __jule_Ptr<__jule_thread> __jule_ct;
To determine when this issue was introduced, I tested older llvm-mingw releases. Based on my tests, llvm-mingw-20240502-ucrt-aarch64 (LLVM 18.1.5) behaves as expected and links successfully with LTO enabled. However, llvm-mingw-20240518-ucrt-aarch64 (LLVM 18.1.6) consistently exhibits the linker failure described above. This suggests that the issue may have been introduced between LLVM 18.1.5 and 18.1.6.
clang++ --version for 18.1.6:
clang version 18.1.6 (https://github.com/llvm/llvm-project.git 1118c2e05e67a36ed8ca250524525cdb66a55256)
Target: aarch64-w64-windows-gnu
Thread model: posix
InstalledDir: C:/llvm/bin
clang++ --version for 18.1.5:
clang version 18.1.5 (https://github.com/llvm/llvm-project.git 617a15a9eac96088ae5e9134248d8236e34b91b1)
Target: aarch64-w64-windows-gnu
Thread model: posix
InstalledDir: C:/llvm/bin
The problem is still present in the latest release and pre-release.
Since I am working on Windows ARM64, I am using this toolchain:
llvm-mingw-20260127-ucrt-aarch64.
Reproduction
I attempted to create a small standalone reproducible example, but I was not able to isolate the issue in a minimal form. I've prepared a PowerShell script. This script downloads the source code of the jule0.2.0 release along with the IR file used for the production build, and then attempts to compile it using clang++. I hope this will make reproducing the issue easier.
By default, it downloads the IR file built for ARM64. If you're testing on an AMD64 system, you can change the windows-arm64.cpp part of the IR file name to windows-amd64.cpp.
After performing the required downloads, it runs a build attempt for you. If the reproduction is successful, you should then be able to debug the downloaded source code locally as you wish.
This is the script:
echo "Downloading jule0.2.0 source code"
curl.exe -L -o jule0.2.0.zip "https://github.com/julelang/jule/archive/refs/tags/jule0.2.0.zip"
echo "Extracting..."
Expand-Archive -Path "jule0.2.0.zip" -DestinationPath ".\"
echo "cd jule-jule0.2.0"
cd jule-jule0.2.0
echo "mkdir bin"
mkdir bin
echo "Downloading C++ IR of jule0.2.0"
curl -o ir.cpp https://raw.githubusercontent.com/julelang/julec-ir/e4134d89f34588e9fb5cc5698f27b0471918e057/src/windows-arm64.cpp
echo "Compiling..."
clang++ -static -Wno-everything --std=c++20 -fwrapv -ffloat-store -fno-fast-math -fno-rounding-math -ffp-contract=fast -O3 -flto=thin -DNDEBUG -fomit-frame-pointer -fno-strict-aliasing -o ./bin/julec.exe ir.cpp -lws2_32 -lshell32 -liphlpapi -lsynchronization
Additional
Potential issues that may point to the same problem:
Hello, I am the developer of the Jule programming language.
First of all, thank you for this project.
Clang is the recommended C++ backend compiler for Jule.
On Windows, llvm-mingw is the recommended toolchain.
I recently released Jule version 0.2.0. Until now, I had not encountered any issues with this toolchain. However, starting with this release, I began using thread-local variables. Their primary purpose is to manage coroutine state across worker threads and to store a pointer to the runtime object associated with the current thread.
I did not notice any problems until the release process, because during development I was using debug builds and therefore did not observe issues caused by optimizations. For builds, I use the compilation commands generated by
julec(the official Jule compiler). When producing a production build,julecenables several optimization flags, and one of these clearly triggers the problem.For Windows builds, I use a Windows 11 virtual machine running under VMware Fusion. The primary architecture is ARM64 (AArch64). For ARM64 builds, I use the default
clang++. On the same virtual machine, I also build for Windows AMD64 (x86_64), usingx86_64-w64-mingw32-clang++. The compilation command is otherwise identical.These are the exact compilation commands I am using:
windows-arm64
windows-amd64
Compiling with these commands consistently results in the following linker error:
The symbol
__jule_ctreferenced in the error is athread_localvariable that holds a pointer to the thread's runtime object. Its type is__jule_Ptr, which is a smart pointer implementing reference counting. This type is not trivially copyable, trivially constructible, or trivially destructible.I investigated the issue further, and it appears to be related to the interaction between the
__jule_Ptrtype and LTO. If I avoid using__jule_Ptrand instead use a fully trivial type (for example, a raw pointer), LTO does not cause any problems. However, when LTO is enabled, LTO appears to eliminate or fail to preserve the TLS initialization routine generated for the__jule_Ptrthread-local variable, which then results in the linker error shown above.If
-flto=thinis removed from the compilation commands above, the problem is resolved. Any use of LTO, whetherthinorfull, triggers this issue.ir.cppis a C++ IR file generated byjulec. However, the issue is actually unrelated to this, the real problem is the__jule_ctvariable defined on the Jule's C++ API side.__jule_ctis defined here:api/async.hppIt looks like this:
To determine when this issue was introduced, I tested older llvm-mingw releases. Based on my tests,
llvm-mingw-20240502-ucrt-aarch64(LLVM 18.1.5) behaves as expected and links successfully with LTO enabled. However,llvm-mingw-20240518-ucrt-aarch64(LLVM 18.1.6) consistently exhibits the linker failure described above. This suggests that the issue may have been introduced between LLVM 18.1.5 and 18.1.6.clang++ --versionfor 18.1.6:clang++ --versionfor 18.1.5:The problem is still present in the latest release and pre-release.
Since I am working on Windows ARM64, I am using this toolchain:
llvm-mingw-20260127-ucrt-aarch64.Reproduction
I attempted to create a small standalone reproducible example, but I was not able to isolate the issue in a minimal form. I've prepared a PowerShell script. This script downloads the source code of the
jule0.2.0release along with the IR file used for the production build, and then attempts to compile it usingclang++. I hope this will make reproducing the issue easier.By default, it downloads the IR file built for ARM64. If you're testing on an AMD64 system, you can change the
windows-arm64.cpppart of the IR file name towindows-amd64.cpp.After performing the required downloads, it runs a build attempt for you. If the reproduction is successful, you should then be able to debug the downloaded source code locally as you wish.
This is the script:
Additional
Potential issues that may point to the same problem: