This commit is contained in:
Dylan Conway
2026-02-03 00:34:00 -08:00
parent 5d4b1821f3
commit f1b243257a
9 changed files with 94 additions and 45 deletions

View File

@@ -470,19 +470,30 @@ function getBuildCommand(target, options, label) {
*/
function getWindowsArm64CrossFlags(target) {
if (target.os === "windows" && target.arch === "aarch64") {
return " --toolchain windows-aarch64 -DSKIP_CODEGEN=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl";
return " --toolchain windows-aarch64";
}
return "";
}
/**
* @param {Platform} platform
* @param {PipelineOptions} options
* @returns {Step}
*/
function getBuildCppStep(platform, options) {
const { os, arch } = platform;
const command = getBuildCommand(platform, options);
const crossFlags = getWindowsArm64CrossFlags(platform);
// Build commands for C++ dependencies and bun
const buildCommands = [`${command}${crossFlags} --target bun`, `${command}${crossFlags} --target dependencies`];
// Cross-compiling Windows ARM64 from x64 requires Rust ARM64 target
if (os === "windows" && arch === "aarch64") {
buildCommands.unshift("rustup target add aarch64-pc-windows-msvc");
}
return {
key: `${getTargetKey(platform)}-build-cpp`,
label: `${getTargetLabel(platform)} - build-cpp`,
@@ -496,7 +507,7 @@ function getBuildCppStep(platform, options) {
// We used to build the C++ dependencies and bun in separate steps.
// However, as long as the zig build takes longer than both sequentially,
// it's cheaper to run them in the same step. Can be revisited in the future.
command: [`${command}${crossFlags} --target bun`, `${command}${crossFlags} --target dependencies`],
command: buildCommands,
};
}

View File

@@ -7,6 +7,13 @@ register_repository(
4f4f5ef8ebc6e23cbf393428f0ab1b526773f7ac
)
set(BORINGSSL_CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF)
# Disable ASM on Windows ARM64 to avoid mixing non-ARM object files into ARM64 libs
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
list(APPEND BORINGSSL_CMAKE_ARGS -DOPENSSL_NO_ASM=1)
endif()
register_cmake_command(
TARGET
boringssl
@@ -15,7 +22,7 @@ register_cmake_command(
ssl
decrepit
ARGS
-DBUILD_SHARED_LIBS=OFF
${BORINGSSL_CMAKE_ARGS}
INCLUDES
include
)

View File

@@ -1457,6 +1457,8 @@ if(NOT BUN_CPP_ONLY)
# ==856230==See https://github.com/google/sanitizers/issues/856 for possible workarounds.
# the linked issue refers to very old kernels but this still happens to us on modern ones.
# disabling ASLR to run the binary works around it
# Skip post-build test/features when cross-compiling (can't run the target binary on the host)
if(NOT CMAKE_CROSSCOMPILING)
set(TEST_BUN_COMMAND_BASE ${BUILD_PATH}/${bunExe} --revision)
set(TEST_BUN_COMMAND_ENV_WRAP
${CMAKE_COMMAND} -E env BUN_DEBUG_QUIET_LOGS=1)
@@ -1505,6 +1507,7 @@ if(NOT BUN_CPP_ONLY)
${BUILD_PATH}/features.json
)
endif()
endif() # NOT CMAKE_CROSSCOMPILING
if(CMAKE_HOST_APPLE AND bunStrip)
register_command(
@@ -1551,7 +1554,10 @@ if(NOT BUN_CPP_ONLY)
string(REPLACE bun ${bunTriplet} bunPath ${bun})
endif()
set(bunFiles ${bunExe} features.json)
set(bunFiles ${bunExe})
if(NOT CMAKE_CROSSCOMPILING)
list(APPEND bunFiles features.json)
endif()
if(WIN32)
list(APPEND bunFiles ${bun}.pdb)
elseif(APPLE)

View File

@@ -41,6 +41,7 @@ register_cmake_command(
# spawn a processes to compress instead of using the library.
-DENABLE_ZLIB=OFF
-DHAVE_ZLIB_H=ON
-DENABLE_CNG=OFF
-DCMAKE_C_FLAGS="-I${VENDOR_PATH}/zlib"
LIB_PATH
libarchive

View File

@@ -26,6 +26,12 @@ if(RELEASE)
list(APPEND LOLHTML_BUILD_ARGS --release)
endif()
# Cross-compilation: tell cargo to target ARM64
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
list(APPEND LOLHTML_BUILD_ARGS --target aarch64-pc-windows-msvc)
set(LOLHTML_LIBRARY ${LOLHTML_BUILD_PATH}/aarch64-pc-windows-msvc/${LOLHTML_BUILD_TYPE}/${CMAKE_STATIC_LIBRARY_PREFIX}lolhtml${CMAKE_STATIC_LIBRARY_SUFFIX})
endif()
# Windows requires unwind tables, apparently.
if (NOT WIN32)
# The encoded escape sequences are intentional. They're how you delimit multiple arguments in a single environment variable.
@@ -51,7 +57,12 @@ if(WIN32)
if(MSVC_VERSIONS)
list(GET MSVC_VERSIONS -1 MSVC_LATEST) # Get the latest version
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64")
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/HostARM64/arm64/link.exe")
# Use Hostx64/arm64 for cross-compilation from x64, fall back to native
if(EXISTS "${MSVC_LATEST}/bin/Hostx64/arm64/link.exe")
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/Hostx64/arm64/link.exe")
else()
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/HostARM64/arm64/link.exe")
endif()
set(CARGO_LINKER_VAR "CARGO_TARGET_AARCH64_PC_WINDOWS_MSVC_LINKER")
else()
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/Hostx64/x64/link.exe")

View File

@@ -4,17 +4,35 @@ set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER_WORKS ON)
set(CMAKE_CXX_COMPILER_WORKS ON)
# Force ARM64 architecture ID - this is what CMake uses to determine /machine: flag
set(MSVC_C_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
set(MSVC_CXX_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
# Prevent CMake from trying to link ARM64 executables during try_compile tests.
# The x64 host can't link ARM64 executables (missing ARM64 CRT/system libs).
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# CMake 4.0+ policy CMP0197 controls how MSVC machine type flags are handled
set(CMAKE_POLICY_DEFAULT_CMP0197 NEW CACHE INTERNAL "")
# The rest only applies when building on Windows (C++ and link steps).
# The Zig step runs on Linux and only needs CMAKE_SYSTEM_NAME/PROCESSOR above.
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
# Clear any inherited static linker flags that might have wrong machine types
set(CMAKE_STATIC_LINKER_FLAGS "" CACHE STRING "" FORCE)
# Set compiler target for ARM64 cross-compilation
set(CMAKE_C_COMPILER_TARGET aarch64-pc-windows-msvc CACHE STRING "" FORCE)
set(CMAKE_CXX_COMPILER_TARGET aarch64-pc-windows-msvc CACHE STRING "" FORCE)
# Use wrapper script for llvm-lib that strips /machine:x64 flags
# This works around CMake 4.1.0 bug where both ARM64 and x64 machine flags are added
get_filename_component(_TOOLCHAIN_DIR "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY)
set(CMAKE_AR "${_TOOLCHAIN_DIR}/scripts/llvm-lib-wrapper.bat" CACHE FILEPATH "" FORCE)
# ARM64 has lock-free atomics (highway's FindAtomics check can't run ARM64 test binary on x64)
set(ATOMICS_LOCK_FREE_INSTRUCTIONS TRUE CACHE BOOL "" FORCE)
set(HAVE_CXX_ATOMICS_WITHOUT_LIB TRUE CACHE BOOL "" FORCE)
set(HAVE_CXX_ATOMICS64_WITHOUT_LIB TRUE CACHE BOOL "" FORCE)
# Force ARM64 architecture ID - this is what CMake uses to determine /machine: flag
set(MSVC_C_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
set(MSVC_CXX_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
# CMake 4.0+ policy CMP0197 controls how MSVC machine type flags are handled
set(CMAKE_POLICY_DEFAULT_CMP0197 NEW CACHE INTERNAL "")
# Clear any inherited static linker flags that might have wrong machine types
set(CMAKE_STATIC_LINKER_FLAGS "" CACHE STRING "" FORCE)
# Use wrapper script for llvm-lib that strips /machine:x64 flags
# This works around CMake 4.1.0 bug where both ARM64 and x64 machine flags are added
get_filename_component(_TOOLCHAIN_DIR "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY)
set(CMAKE_AR "${_TOOLCHAIN_DIR}/scripts/llvm-lib-wrapper.bat" CACHE FILEPATH "" FORCE)
endif()

View File

@@ -259,3 +259,19 @@ file(RENAME ${CACHE_PATH}/bun-webkit ${WEBKIT_PATH})
if(APPLE)
file(REMOVE_RECURSE ${WEBKIT_INCLUDE_PATH}/unicode)
endif()
# Patch simde neon.h for clang-cl ARM64 compatibility.
# The MSVC workaround uses intrinsics that clang-cl doesn't provide.
# TODO: Remove once WebKit is rebuilt with the upstream fix.
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
set(SIMDE_NEON_H "${WEBKIT_INCLUDE_PATH}/wtf/simde/arm/neon.h")
if(EXISTS "${SIMDE_NEON_H}")
file(READ "${SIMDE_NEON_H}" SIMDE_CONTENT)
string(REPLACE
"(defined _MSC_VER) && (defined SIMDE_ARM_NEON_A64V8_NATIVE)"
"(defined _MSC_VER) && !defined(__clang__) && (defined SIMDE_ARM_NEON_A64V8_NATIVE)"
SIMDE_CONTENT "${SIMDE_CONTENT}")
file(WRITE "${SIMDE_NEON_H}" "${SIMDE_CONTENT}")
message(STATUS "Patched simde/arm/neon.h for clang-cl ARM64 compatibility")
endif()
endif()

View File

@@ -92,21 +92,9 @@ async function build(args) {
generateOptions["--toolchain"] = toolchainPath;
}
// Windows ARM64: automatically set required options
// Windows ARM64: log detection (compiler is selected by CMake/toolchain)
if (isWindowsARM64) {
// Use clang-cl instead of MSVC cl.exe for proper ARM64 flag support
if (!generateOptions["-DCMAKE_C_COMPILER"]) {
generateOptions["-DCMAKE_C_COMPILER"] = "clang-cl";
}
if (!generateOptions["-DCMAKE_CXX_COMPILER"]) {
generateOptions["-DCMAKE_CXX_COMPILER"] = "clang-cl";
}
// Skip codegen by default since x64 bun crashes under WoW64 emulation
// Can be overridden with -DSKIP_CODEGEN=OFF once ARM64 bun is available
if (!generateOptions["-DSKIP_CODEGEN"]) {
generateOptions["-DSKIP_CODEGEN"] = "ON";
}
console.log("Windows ARM64 detected: using clang-cl and SKIP_CODEGEN=ON");
console.log("Windows ARM64 detected");
}
const generateArgs = Object.entries(generateOptions).flatMap(([flag, value]) =>

View File

@@ -81,19 +81,6 @@ size_t IndexOfAnyCharImpl(const uint8_t* HWY_RESTRICT text, size_t text_len, con
} else {
ASSERT(chars_len <= 16);
// Use FixedTag to preload search characters into fixed-size vectors.
// ScalableTag vectors (SVE) are sizeless and cannot be stored in arrays.
// FixedTag gives us a known compile-time size that can be stored in arrays,
// then ResizeBitCast converts back to scalable vectors in the inner loop.
static constexpr size_t kMaxPreloadedChars = 16;
const hn::FixedTag<uint8_t, 16> d_fixed;
using VecFixed = hn::Vec<decltype(d_fixed)>;
VecFixed char_vecs[kMaxPreloadedChars];
const size_t num_chars_to_preload = std::min(chars_len, kMaxPreloadedChars);
for (size_t c = 0; c < num_chars_to_preload; ++c) {
char_vecs[c] = hn::Set(d_fixed, chars[c]);
}
const size_t simd_text_len = text_len - (text_len % N);
size_t i = 0;
@@ -101,8 +88,12 @@ size_t IndexOfAnyCharImpl(const uint8_t* HWY_RESTRICT text, size_t text_len, con
const auto text_vec = hn::LoadN(d, text + i, N);
auto found_mask = hn::MaskFalse(d);
for (size_t c = 0; c < num_chars_to_preload; ++c) {
found_mask = hn::Or(found_mask, hn::Eq(text_vec, hn::ResizeBitCast(d, char_vecs[c])));
// Broadcast and compare each search character individually.
// Cannot preload into a Vec array because SVE types are sizeless.
// hn::Set is a single broadcast instruction; the compiler will
// hoist these loop-invariant broadcasts out of the outer loop.
for (size_t c = 0; c < chars_len; ++c) {
found_mask = hn::Or(found_mask, hn::Eq(text_vec, hn::Set(d, chars[c])));
}
const intptr_t pos = hn::FindFirstTrue(d, found_mask);