From 1d84eb4a64f58ab03d30c101e96208f8d551420e Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 16 May 2025 00:21:32 +0200 Subject: [PATCH 1/3] CI: Add native Windows arm64 job/package --- .github/actions/1-setup/action.yml | 10 ++++++++-- .github/actions/5-install/action.yml | 5 +++++ .github/actions/merge-windows/action.yml | 1 + .github/workflows/main.yml | 10 ++++++++++ CMakeLists.txt | 13 ++++++++++--- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/.github/actions/1-setup/action.yml b/.github/actions/1-setup/action.yml index de1155d760..0624547b51 100644 --- a/.github/actions/1-setup/action.yml +++ b/.github/actions/1-setup/action.yml @@ -68,8 +68,12 @@ runs: run: | set -eux cd .. + suffix='win64' + if [[ '${{ inputs.arch }}' == arm64 ]]; then + suffix='woa64' + fi curl -fL --retry 3 --max-time 300 -o clang.exe \ - https://github.com/llvm/llvm-project/releases/download/llvmorg-21.1.8/LLVM-21.1.8-win64.exe + https://github.com/llvm/llvm-project/releases/download/llvmorg-21.1.8/LLVM-21.1.8-$suffix.exe ./clang.exe //S # double-slash for bash rm clang.exe # C:\Program Files\LLVM\bin should already be in PATH @@ -169,7 +173,9 @@ runs: cd .. url='https://curl.se/windows/latest.cgi?p=win64-mingw.zip' - if [[ '${{ inputs.arch }}' == x86 ]]; then + if [[ '${{ inputs.arch }}' == arm64 ]]; then + url='https://curl.se/windows/latest.cgi?p=win64a-mingw.zip' + elif [[ '${{ inputs.arch }}' == x86 ]]; then # this is the latest *official* 32-bit build url='https://curl.se/windows/dl-8.15.0_5/curl-8.15.0_5-win32-mingw.zip' fi diff --git a/.github/actions/5-install/action.yml b/.github/actions/5-install/action.yml index 143dc7c4b1..f4e3bd439f 100644 --- a/.github/actions/5-install/action.yml +++ b/.github/actions/5-install/action.yml @@ -50,6 +50,11 @@ runs: cp libcurl/ldc2/* install/bin/ + if [[ '${{ inputs.arch }}' == arm64 ]]; then + echo "No MinGW-based libraries available for arm64 yet." + exit 0 + fi + curl -fL --retry 3 --max-time 60 -o mingw-w64-libs.7z \ https://github.com/ldc-developers/mingw-w64-libs/releases/download/v8.0.0/mingw-w64-libs-v8.0.0.7z mkdir mingw-w64-libs diff --git a/.github/actions/merge-windows/action.yml b/.github/actions/merge-windows/action.yml index da3173bdbf..bba113bdec 100644 --- a/.github/actions/merge-windows/action.yml +++ b/.github/actions/merge-windows/action.yml @@ -66,6 +66,7 @@ runs: set PATH=%CD%\ldc2-multilib\lib32;%PATH% ldc2-multilib\bin\ldc2 -link-defaultlib-shared -m32 -run hello.d || exit /b + # TODO # preliminary arm64 cross-compilation support - name: Set VSDIR env variable shell: bash diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a025ea8715..08b0eb08e6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -126,6 +126,16 @@ jobs: -DEXTRA_CXXFLAGS=-flto=full with_pgo: true + - job_name: Windows arm64 + os: windows-11-arm + arch: arm64 + base_cmake_flags: >- + -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded + #extra_cmake_flags: >- + # "-DD_COMPILER_FLAGS=-O -flto=full -defaultlib=phobos2-ldc-lto,druntime-ldc-lto" + # -DEXTRA_CXXFLAGS=-flto=full + #with_pgo: true + - job_name: Windows x86 os: windows-2025 arch: x86 diff --git a/CMakeLists.txt b/CMakeLists.txt index eee41a4feb..e0e63459ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,7 +175,10 @@ if(NOT MSVC_IDE) endif() if(MSVC) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) + if($ENV{VSCMD_ARG_TGT_ARCH} STREQUAL "arm64") + message(STATUS "Let D host compiler output arm64 object files") + append("-mtriple=aarch64-windows-msvc" DFLAGS_BASE) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) message(STATUS "Let D host compiler output 64-bit object files") append("-m64" DFLAGS_BASE) else() @@ -280,7 +283,9 @@ endif() if(MSVC) separate_arguments(LLVM_LDFLAGS WINDOWS_COMMAND "${LLVM_LDFLAGS}") if(NOT MSVC_IDE) # apparently not needed for VS (and spaces in path are problematic) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) + if($ENV{VSCMD_ARG_TGT_ARCH} STREQUAL "arm64") + list(APPEND LLVM_LDFLAGS "$ENV{VSINSTALLDIR}DIA SDK\\lib\\arm64\\diaguids.lib") + elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND LLVM_LDFLAGS "$ENV{VSINSTALLDIR}DIA SDK\\lib\\amd64\\diaguids.lib") else() list(APPEND LLVM_LDFLAGS "$ENV{VSINSTALLDIR}DIA SDK\\lib\\diaguids.lib") @@ -878,7 +883,9 @@ if (LDC_INSTALL_LLVM_RUNTIME_LIBS) copy_compilerrt_lib("libclang_rt.xray-profiling${compilerrt_suffix}.a" "libldc_rt.xray-profiling.a" FALSE) elseif(WIN32) set(compilerrt_arch_suffix "x86_64") - if(CMAKE_SIZEOF_VOID_P EQUAL 4) + if($ENV{VSCMD_ARG_TGT_ARCH} STREQUAL "arm64") + set(compilerrt_arch_suffix "aarch64") + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) set(compilerrt_arch_suffix "i386") endif() if(LDC_LLVM_VER LESS 2000) From 049086aa3c612461aa83c5d1114fa371f7c583af Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Fri, 16 May 2025 01:28:49 +0200 Subject: [PATCH 2/3] Overcome longdouble_soft problem on WoA64 hosts That 'soft' implementation (of an 80-bit x87 value used for compile-time reals on Windows MSVC x86[_64] hosts, if their native `real` is a 64-bit double with an LDC host compiler) is still based on x87 inline asm, so cannot be used in a WoA64 compiler binary. So use a 64-bit native `real` for compile-time reals on WoA64 hosts. --- dmd/main.d | 7 +++++-- dmd/root/ctfloat.d | 14 +++++++++----- dmd/root/longdouble.d | 5 ++++- dmd/root/longdouble.h | 6 +++--- gen/ctfloat.cpp | 4 ++-- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/dmd/main.d b/dmd/main.d index 9afc17605f..f80c80ce13 100644 --- a/dmd/main.d +++ b/dmd/main.d @@ -449,8 +449,11 @@ else reconcileLinkRunLib(params, files.length, target.obj_ext); version(CRuntime_Microsoft) { - import dmd.root.longdouble; - initFPU(); + version(AArch64) { /* no longdouble_soft support on arm64 host */ } else + { + import dmd.root.longdouble; + initFPU(); + } } import dmd.root.ctfloat : CTFloat; CTFloat.initialize(); diff --git a/dmd/root/ctfloat.d b/dmd/root/ctfloat.d index 2996d7cd60..4c674f4ebd 100644 --- a/dmd/root/ctfloat.d +++ b/dmd/root/ctfloat.d @@ -27,13 +27,17 @@ private { version(CRuntime_DigitalMars) __gshared extern (C) extern const(char)* __locale_decpoint; - version(CRuntime_Microsoft) extern (C++) + version(CRuntime_Microsoft) { - public import dmd.root.longdouble : longdouble_soft, ld_sprint; + version(AArch64) { /* 64-bit real_t */ } else + { + version = MSVC_X87; + import dmd.root.longdouble : longdouble_soft, ld_sprint; version (IN_LLVM) {} else { - import dmd.root.strtold; + import dmd.root.strtold; } + } } } @@ -193,7 +197,7 @@ extern (C++) struct CTFloat // the implementation of longdouble for MSVC is a struct, so mangling // doesn't match with the C++ header. // add a wrapper just for isSNaN as this is the only function called from C++ - version(CRuntime_Microsoft) static if (is(real_t == real)) + version(MSVC_X87) static if (is(real_t == real)) pure @trusted static bool isSNaN(longdouble_soft ld) { @@ -239,7 +243,7 @@ extern (C++) struct CTFloat @system static int sprint(char* str, size_t size, char fmt, real_t x) { - version(CRuntime_Microsoft) + version(MSVC_X87) { auto len = cast(int) ld_sprint(str, size, fmt, longdouble_soft(x)); } diff --git a/dmd/root/longdouble.d b/dmd/root/longdouble.d index 0bbd27a39e..563e4c5036 100644 --- a/dmd/root/longdouble.d +++ b/dmd/root/longdouble.d @@ -13,7 +13,9 @@ module dmd.root.longdouble; version (CRuntime_Microsoft) { - static if (real.sizeof > 8) + version (AArch64) + alias longdouble = real; // 64-bit + else static if (real.sizeof > 8) alias longdouble = real; else alias longdouble = longdouble_soft; @@ -24,6 +26,7 @@ else // longdouble_soft needed when building the backend with // Visual C or the frontend with LDC on Windows version (CRuntime_Microsoft): +version (AArch64) { /* cannot use x87 inline asm on arm64 host */ } else: extern (C++): nothrow: @nogc: diff --git a/dmd/root/longdouble.h b/dmd/root/longdouble.h index 150b9f55e1..4a3db75422 100644 --- a/dmd/root/longdouble.h +++ b/dmd/root/longdouble.h @@ -11,7 +11,7 @@ #pragma once -#if !_MSC_VER // has native 10 byte doubles +#if !defined(_MSC_VER) || defined(_M_ARM64) #include typedef long double longdouble; typedef volatile long double volatile_longdouble; @@ -48,7 +48,7 @@ inline size_t ld_sprint(char* str, size_t size, int fmt, longdouble x) #undef snprintf #endif -#else +#else // defined(_MSC_VER) && !defined(_M_ARM64) #include #include @@ -264,4 +264,4 @@ typedef longdouble_soft longdouble; // is not required. typedef longdouble_soft volatile_longdouble; -#endif // !_MSC_VER +#endif // defined(_MSC_VER) && !defined(_M_ARM64) diff --git a/gen/ctfloat.cpp b/gen/ctfloat.cpp index ef033c018d..dd411277fa 100644 --- a/gen/ctfloat.cpp +++ b/gen/ctfloat.cpp @@ -42,8 +42,8 @@ void CTFloat::initialize() { if (apSemantics) return; -#ifdef _MSC_VER - // MSVC hosts use dmd.root.longdouble (80-bit x87) +#if defined(_MSC_VER) && !defined(_M_ARM64) + // MSVC x86[_64] hosts use dmd.root.longdouble (80-bit x87) apSemantics = &APFloat::x87DoubleExtended(); #else static_assert(std::numeric_limits::is_specialized, From e87872be99a94abd5fdd27c85189f4ecd64ffa3b Mon Sep 17 00:00:00 2001 From: Martin Kinkelin Date: Sat, 31 Jan 2026 04:55:51 +0100 Subject: [PATCH 3/3] [troubleshoot] --- .github/actions/1-setup/action.yml | 1 + .github/actions/helper-build-ldc/action.yml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.github/actions/1-setup/action.yml b/.github/actions/1-setup/action.yml index 0624547b51..996059afee 100644 --- a/.github/actions/1-setup/action.yml +++ b/.github/actions/1-setup/action.yml @@ -77,6 +77,7 @@ runs: ./clang.exe //S # double-slash for bash rm clang.exe # C:\Program Files\LLVM\bin should already be in PATH + where clang-cl clang-cl --version - name: Download & extract LDC-flavoured LLVM # into ../llvm diff --git a/.github/actions/helper-build-ldc/action.yml b/.github/actions/helper-build-ldc/action.yml index badb77ade5..c5749d7f5b 100644 --- a/.github/actions/helper-build-ldc/action.yml +++ b/.github/actions/helper-build-ldc/action.yml @@ -46,8 +46,11 @@ runs: - if: runner.os == 'Windows' shell: cmd run: | + where clang-cl call "%LDC_VSDIR%\Common7\Tools\VsDevCmd.bat" -arch=${{ inputs.arch }} || exit /b echo on + ${{ inputs.arch == 'arm64' && 'set PATH=C:\Program Files\LLVM\bin;%PATH%' || '' }} + where clang-cl cd .. || exit /b set installDir=%CD%\install mkdir "${{ inputs.build_dir }}" || exit /b