From 8263be5acc501f310292dc1167893b72927e666a Mon Sep 17 00:00:00 2001 From: Mugunthan Selvanayagam Date: Tue, 31 Mar 2026 10:17:26 +0530 Subject: [PATCH] Add support for building and testing Folly on Windows ARM64 --- .github/workflows/getdeps_windows_arm64.yml | 408 ++++++++++++++++++++ build/fbcode_builder/getdeps/builder.py | 32 +- build/fbcode_builder/getdeps/buildopts.py | 4 +- build/fbcode_builder/getdeps/platform.py | 2 +- folly/Portability.h | 8 +- folly/algorithm/simd/Movemask.h | 28 +- folly/algorithm/simd/find_first_of.h | 10 +- folly/chrono/Hardware.h | 2 +- folly/compression/Instructions.h | 24 +- folly/container/detail/F14Table.h | 8 +- folly/fibers/BoostContextCompatibility.h | 115 +++++- folly/lang/Bits.h | 17 +- folly/portability/Asm.h | 2 + 13 files changed, 626 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/getdeps_windows_arm64.yml diff --git a/.github/workflows/getdeps_windows_arm64.yml b/.github/workflows/getdeps_windows_arm64.yml new file mode 100644 index 00000000000..a6d22d050de --- /dev/null +++ b/.github/workflows/getdeps_windows_arm64.yml @@ -0,0 +1,408 @@ +# This file was @generated by getdeps.py + +name: Windows ARM64 + +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + build: + runs-on: windows-11-arm + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Export boost environment + run: "echo BOOST_ROOT=%BOOST_ROOT_1_83_0% >> %GITHUB_ENV%" + shell: cmd + - name: Fix Git config + run: > + git config --system core.longpaths true && + git config --system core.autocrlf false && + git config --system core.symlinks true + shell: cmd + - uses: actions/checkout@v6 + - id: paths + name: Query paths + run: python build/fbcode_builder/getdeps.py query-paths --recursive --src-dir=. folly >> $env:GITHUB_OUTPUT + shell: pwsh + - name: Fetch boost + if: ${{ steps.paths.outputs.boost_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests boost + - name: Fetch libsodium + if: ${{ steps.paths.outputs.libsodium_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests libsodium + - name: Fetch ninja + if: ${{ steps.paths.outputs.ninja_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests ninja + - name: Fetch cmake + if: ${{ steps.paths.outputs.cmake_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests cmake + - name: Fetch double-conversion + if: ${{ steps.paths.outputs.double-conversion_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests double-conversion + - name: Fetch fast_float + if: ${{ steps.paths.outputs.fast_float_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests fast_float + - name: Fetch fmt + if: ${{ steps.paths.outputs.fmt_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests fmt + - name: Fetch gflags + if: ${{ steps.paths.outputs.gflags_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests gflags + - name: Fetch glog + if: ${{ steps.paths.outputs.glog_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests glog + - name: Fetch googletest + if: ${{ steps.paths.outputs.googletest_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests googletest + - name: Fetch libdwarf + if: ${{ steps.paths.outputs.libdwarf_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests libdwarf + - name: Fetch lz4 + if: ${{ steps.paths.outputs.lz4_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests lz4 + - name: Fetch jom + if: ${{ steps.paths.outputs.jom_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests jom + - name: Fetch perl + if: ${{ steps.paths.outputs.perl_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests perl + - name: Fetch openssl + if: ${{ steps.paths.outputs.openssl_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests openssl + - name: Fetch snappy + if: ${{ steps.paths.outputs.snappy_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests snappy + - name: Fetch zlib + if: ${{ steps.paths.outputs.zlib_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests zlib + - name: Fetch zstd + if: ${{ steps.paths.outputs.zstd_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests zstd + - name: Fetch libevent + if: ${{ steps.paths.outputs.libevent_SOURCE }} + run: python build/fbcode_builder/getdeps.py fetch --no-tests libevent + - name: Restore boost from cache + id: restore_boost + if: ${{ steps.paths.outputs.boost_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.boost_INSTALL }} + key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install + - name: Build boost + if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests boost + - name: Save boost to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.boost_INSTALL }} + key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install + - name: Restore libsodium from cache + id: restore_libsodium + if: ${{ steps.paths.outputs.libsodium_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.libsodium_INSTALL }} + key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install + - name: Build libsodium + if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests libsodium + - name: Save libsodium to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.libsodium_INSTALL }} + key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install + - name: Restore ninja from cache + id: restore_ninja + if: ${{ steps.paths.outputs.ninja_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.ninja_INSTALL }} + key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install + - name: Build ninja + if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests ninja + - name: Save ninja to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.ninja_INSTALL }} + key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install + - name: Restore cmake from cache + id: restore_cmake + if: ${{ steps.paths.outputs.cmake_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.cmake_INSTALL }} + key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install + - name: Build cmake + if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests cmake + - name: Save cmake to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.cmake_INSTALL }} + key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install + - name: Restore double-conversion from cache + id: restore_double-conversion + if: ${{ steps.paths.outputs.double-conversion_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.double-conversion_INSTALL }} + key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install + - name: Build double-conversion + if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests double-conversion + - name: Save double-conversion to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.double-conversion_INSTALL }} + key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install + - name: Restore fast_float from cache + id: restore_fast_float + if: ${{ steps.paths.outputs.fast_float_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.fast_float_INSTALL }} + key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install + - name: Build fast_float + if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests fast_float + - name: Save fast_float to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.fast_float_INSTALL }} + key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install + - name: Restore fmt from cache + id: restore_fmt + if: ${{ steps.paths.outputs.fmt_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.fmt_INSTALL }} + key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install + - name: Build fmt + if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests fmt + - name: Save fmt to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.fmt_INSTALL }} + key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install + - name: Restore gflags from cache + id: restore_gflags + if: ${{ steps.paths.outputs.gflags_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.gflags_INSTALL }} + key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install + - name: Build gflags + if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests gflags + - name: Save gflags to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.gflags_INSTALL }} + key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install + - name: Restore glog from cache + id: restore_glog + if: ${{ steps.paths.outputs.glog_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.glog_INSTALL }} + key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install + - name: Build glog + if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests glog + - name: Save glog to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.glog_INSTALL }} + key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install + - name: Restore googletest from cache + id: restore_googletest + if: ${{ steps.paths.outputs.googletest_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.googletest_INSTALL }} + key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install + - name: Build googletest + if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests googletest + - name: Save googletest to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.googletest_INSTALL }} + key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install + - name: Restore libdwarf from cache + id: restore_libdwarf + if: ${{ steps.paths.outputs.libdwarf_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.libdwarf_INSTALL }} + key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install + - name: Build libdwarf + if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests libdwarf + - name: Save libdwarf to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.libdwarf_INSTALL }} + key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install + - name: Restore lz4 from cache + id: restore_lz4 + if: ${{ steps.paths.outputs.lz4_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.lz4_INSTALL }} + key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install + - name: Build lz4 + if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests lz4 + - name: Save lz4 to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.lz4_INSTALL }} + key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install + - name: Restore jom from cache + id: restore_jom + if: ${{ steps.paths.outputs.jom_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.jom_INSTALL }} + key: ${{ steps.paths.outputs.jom_CACHE_KEY }}-install + - name: Build jom + if: ${{ steps.paths.outputs.jom_SOURCE && ! steps.restore_jom.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests jom + - name: Save jom to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.jom_SOURCE && ! steps.restore_jom.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.jom_INSTALL }} + key: ${{ steps.paths.outputs.jom_CACHE_KEY }}-install + - name: Restore perl from cache + id: restore_perl + if: ${{ steps.paths.outputs.perl_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.perl_INSTALL }} + key: ${{ steps.paths.outputs.perl_CACHE_KEY }}-install + - name: Build perl + if: ${{ steps.paths.outputs.perl_SOURCE && ! steps.restore_perl.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests perl + - name: Save perl to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.perl_SOURCE && ! steps.restore_perl.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.perl_INSTALL }} + key: ${{ steps.paths.outputs.perl_CACHE_KEY }}-install + - name: Restore openssl from cache + id: restore_openssl + if: ${{ steps.paths.outputs.openssl_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.openssl_INSTALL }} + key: ${{ steps.paths.outputs.openssl_CACHE_KEY }}-install + - name: Build openssl + if: ${{ steps.paths.outputs.openssl_SOURCE && ! steps.restore_openssl.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests openssl + - name: Save openssl to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.openssl_SOURCE && ! steps.restore_openssl.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.openssl_INSTALL }} + key: ${{ steps.paths.outputs.openssl_CACHE_KEY }}-install + - name: Restore snappy from cache + id: restore_snappy + if: ${{ steps.paths.outputs.snappy_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.snappy_INSTALL }} + key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install + - name: Build snappy + if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests snappy + - name: Save snappy to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.snappy_INSTALL }} + key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install + - name: Restore zlib from cache + id: restore_zlib + if: ${{ steps.paths.outputs.zlib_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.zlib_INSTALL }} + key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install + - name: Build zlib + if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests zlib + - name: Save zlib to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.zlib_INSTALL }} + key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install + - name: Restore zstd from cache + id: restore_zstd + if: ${{ steps.paths.outputs.zstd_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.zstd_INSTALL }} + key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install + - name: Build zstd + if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests zstd + - name: Save zstd to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.zstd_INSTALL }} + key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install + - name: Restore libevent from cache + id: restore_libevent + if: ${{ steps.paths.outputs.libevent_SOURCE }} + uses: actions/cache/restore@v4 + with: + path: ${{ steps.paths.outputs.libevent_INSTALL }} + key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install + - name: Build libevent + if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }} + run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests libevent + - name: Save libevent to cache + uses: actions/cache/save@v4 + if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }} + with: + path: ${{ steps.paths.outputs.libevent_INSTALL }} + key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install + - name: Build folly + run: python build/fbcode_builder/getdeps.py build --src-dir=. folly + - name: Copy artifacts + run: python build/fbcode_builder/getdeps.py fixup-dyn-deps --src-dir=. folly _artifacts/windows --final-install-prefix /usr/local + - uses: actions/upload-artifact@v6 + with: + name: folly + path: _artifacts + - name: Test folly + run: python build/fbcode_builder/getdeps.py test --src-dir=. folly diff --git a/build/fbcode_builder/getdeps/builder.py b/build/fbcode_builder/getdeps/builder.py index 2e7ba2e58d8..4bcbd6b5926 100644 --- a/build/fbcode_builder/getdeps/builder.py +++ b/build/fbcode_builder/getdeps/builder.py @@ -92,7 +92,8 @@ def _get_cmd_prefix(self) -> list[str]: wrapper = os.path.join(self.build_dir, "succeed.bat") with open(wrapper, "w") as f: f.write("@echo off\n") - f.write(f'call "{vcvarsall}" amd64\n') + arch = "arm64" if self.build_opts.is_arm() else "amd64" + f.write(f'call "{vcvarsall}" {arch}\n') f.write("set ERRORLEVEL=0\n") f.write("exit /b 0\n") return [wrapper, "&&"] @@ -1421,7 +1422,7 @@ def _build(self, reconfigure: bool) -> None: # jom is compatible with nmake, adds the /j argument for parallel build make = "jom.exe" make_j_args = ["/j%s" % self.num_jobs] - args = ["VC-WIN64A-masm", "-utf-8"] + args = ["VC-WIN64-ARM", "-utf-8"] if self.build_opts.is_arm() else ["VC-WIN64A-masm", "-utf-8"] # fixes "if multiple CL.EXE write to the same .PDB file, please use /FS" extra_args = ["/FS"] elif self.build_opts.is_darwin(): @@ -1498,6 +1499,13 @@ def __init__( def _build(self, reconfigure: bool) -> None: env = self._compute_env() + if self.build_opts.is_arm(): + if not self.b2_args: + self.b2_args = [] + self.b2_args += [ + "pch=off", + "context-impl=winfib", + ] linkage: list[str] = ["static"] if self.build_opts.is_windows() or self.build_opts.shared_libs: linkage.append("shared") @@ -1517,7 +1525,10 @@ def _build(self, reconfigure: bool) -> None: if self.build_opts.is_windows(): bootstrap = os.path.join(self.src_dir, "bootstrap.bat") self._check_cmd([bootstrap] + bootstrap_args, cwd=self.src_dir, env=env) - args += ["address-model=64"] + if self.build_opts.is_arm(): + args += ["address-model=64", "architecture=arm"] + else: + args += ["address-model=64"] else: bootstrap = os.path.join(self.src_dir, "bootstrap.sh") self._check_cmd( @@ -1526,6 +1537,19 @@ def _build(self, reconfigure: bool) -> None: env=env, ) + b2_args = list(self.b2_args) + if self.build_opts.is_arm(): + ARM_EXCLUDED_LIBS = { + "coroutine", + "graph", + "graph_parallel", + "mpi", + "python", + } + b2_args = [ + arg for arg in b2_args + if not any(arg == f"--with-{lib}" for lib in ARM_EXCLUDED_LIBS) + ] b2 = os.path.join(self.src_dir, "b2") self._check_cmd( [ @@ -1535,7 +1559,7 @@ def _build(self, reconfigure: bool) -> None: "--builddir=%s" % self.build_dir, ] + args - + self.b2_args + + b2_args + [ "link=%s" % link, "runtime-link=shared", diff --git a/build/fbcode_builder/getdeps/buildopts.py b/build/fbcode_builder/getdeps/buildopts.py index 435cce5951a..659d98f55fa 100644 --- a/build/fbcode_builder/getdeps/buildopts.py +++ b/build/fbcode_builder/getdeps/buildopts.py @@ -330,7 +330,9 @@ def compute_env_for_install_dirs( "xplat/third-party/yarn/", yarn_exe, ) - node_exe = "node-win-x64.exe" if self.is_windows() else "node" + node_exe = "node" + if self.is_windows(): + node_exe = "node-win-arm64.exe" if self.is_arm() else "node-win-x64.exe" env["NODE_BIN"] = os.path.join( # pyre-fixme[6]: For 1st argument expected `LiteralString` but got # `Optional[str]`. diff --git a/build/fbcode_builder/getdeps/platform.py b/build/fbcode_builder/getdeps/platform.py index cae49d3dcb4..aa8bfde157e 100644 --- a/build/fbcode_builder/getdeps/platform.py +++ b/build/fbcode_builder/getdeps/platform.py @@ -195,7 +195,7 @@ def is_current_host_arm() -> bool: return "ARM64" in os.uname().version else: machine = platform.machine().lower() - return "arm" in machine or "aarch" in machine + return "arm" in machine or "aarch" in machine or "arm64" in machine class HostType: diff --git a/folly/Portability.h b/folly/Portability.h index d76900b0a5b..be6f7f87242 100644 --- a/folly/Portability.h +++ b/folly/Portability.h @@ -108,7 +108,7 @@ constexpr bool kHasUnalignedAccess = false; #define FOLLY_ARM 0 #endif -#if defined(__aarch64__) +#if defined(__aarch64__) || defined(_M_ARM64) #define FOLLY_AARCH64 1 #else #define FOLLY_AARCH64 0 @@ -361,7 +361,7 @@ constexpr auto kHasWeakSymbols = false; (FOLLY_SSE > major || FOLLY_SSE == major && FOLLY_SSE_MINOR >= minor) #ifndef FOLLY_NEON -#if (defined(__ARM_NEON) || defined(__ARM_NEON__)) && !defined(__CUDACC__) +#if (defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(_M_ARM64)) && !defined(__CUDACC__) #define FOLLY_NEON 1 #else #define FOLLY_NEON 0 @@ -646,6 +646,10 @@ constexpr auto kCpplibVer = 0; #if defined(__NVCC__) // For now, NVCC matches other compilers but does not offer coroutines. #define FOLLY_HAS_COROUTINES 0 +#elif defined(_MSC_VER) && defined(_M_ARM64) +// MSVC ARM64: coroutine codegen triggers unsupported relocations at link time, +// leading to build failures. Disable coroutines for this target. +#define FOLLY_HAS_COROUTINES 0 #elif defined(_WIN32) && defined(__clang__) && !defined(LLVM_COROUTINES) && \ !defined(LLVM_COROUTINES_CPP20) // LLVM and MSVC coroutines are ABI incompatible, so for the MSVC implementation diff --git a/folly/algorithm/simd/Movemask.h b/folly/algorithm/simd/Movemask.h index eec9ee1d5bc..ad30e627587 100644 --- a/folly/algorithm/simd/Movemask.h +++ b/folly/algorithm/simd/Movemask.h @@ -146,13 +146,13 @@ FOLLY_ERASE auto movemaskChars16Aarch64(uint8x16_t reg) { return std::pair{bits, std::integral_constant{}}; } -template +template FOLLY_ERASE uint64x1_t asUint64x1Aarch64(Reg reg) { - if constexpr (std::is_same_v) { + if constexpr (sizeof(Scalar) == 8) { return reg; - } else if constexpr (std::is_same_v) { + } else if constexpr (sizeof(Scalar) == 4) { return vreinterpret_u64_u32(reg); - } else if constexpr (std::is_same_v) { + } else if constexpr (sizeof(Scalar) == 2) { return vreinterpret_u64_u16(reg); } else { return vreinterpret_u64_u8(reg); @@ -164,16 +164,18 @@ FOLLY_ERASE uint64x1_t asUint64x1Aarch64(Reg reg) { template template FOLLY_ERASE auto movemask_fn::operator()(Reg reg) const { - if constexpr (std::is_same_v) { - return movemask(vmovn_u64(reg)); - } else if constexpr (std::is_same_v) { - return movemask(vmovn_u32(reg)); - } else if constexpr (std::is_same_v) { - return movemask(vmovn_u16(reg)); - } else if constexpr (std::is_same_v) { - return detail::movemaskChars16Aarch64(reg); + if constexpr (sizeof(Reg) == 16) { + if constexpr (sizeof(Scalar) == 1) { + return detail::movemaskChars16Aarch64(reg); + } else if constexpr (sizeof(Scalar) == 2) { + return movemask(vmovn_u16(reg)); + } else if constexpr (sizeof(Scalar) == 4) { + return movemask(vmovn_u32(reg)); + } else { + return movemask(vmovn_u64(reg)); + } } else { - std::uint64_t mmask = vget_lane_u64(detail::asUint64x1Aarch64(reg), 0); + std::uint64_t mmask = vget_lane_u64(detail::asUint64x1Aarch64(reg), 0); return std::pair{ mmask, std::integral_constant{}}; } diff --git a/folly/algorithm/simd/find_first_of.h b/folly/algorithm/simd/find_first_of.h index 5b099c1455d..3ddd3d2ba29 100644 --- a/folly/algorithm/simd/find_first_of.h +++ b/folly/algorithm/simd/find_first_of.h @@ -277,8 +277,8 @@ class default_vector_finder_first_op_of { vld1q_u8(reinterpret_cast(input.data() + size)); auto vmask = vdupq_n_u8(Eq ? 0 : -1); for (auto const a : alphabet_) { - auto const veq = vhaystack == vdupq_n_u8(a); - vmask = Eq ? veq | vmask : ~veq & vmask; + auto const veq = vceqq_u8(vhaystack, vdupq_n_u8(static_cast(a))); + vmask = Eq ? vorrq_u8(veq, vmask) : vbicq_u8(vmask, veq); } #endif if (auto const [word, bits] = movemask(vmask); word) { @@ -398,10 +398,12 @@ class shuffle_vector_finder_first_op_of { auto const vtable = reinterpret_cast(table); auto const vhaystack = vld1q_u8(reinterpret_cast(input.data() + size)); + auto const vhaystack_lo = vandq_u8(vhaystack, vdupq_n_u8(15)); auto vmask = vdupq_n_u8(Eq ? 0 : -1); for (size_t i = 0; i < shuffle_.rounds; ++i) { - auto const veq = vqtbl1q_u8(vtable[i], vhaystack & 15) == vhaystack; - vmask = Eq ? veq | vmask : ~veq & vmask; + auto const vshuffle = vqtbl1q_u8(vtable[i], vhaystack_lo); + auto const veq = vceqq_u8(vshuffle, vhaystack); + vmask = Eq ? vorrq_u8(veq, vmask) : vbicq_u8(vmask, veq); } #endif if (auto const [word, bits] = movemask(vmask); word) { diff --git a/folly/chrono/Hardware.h b/folly/chrono/Hardware.h index 4f41ad62480..a5f9d3afefe 100644 --- a/folly/chrono/Hardware.h +++ b/folly/chrono/Hardware.h @@ -43,7 +43,7 @@ inline std::uint64_t hardware_timestamp() { return __rdtsc(); #elif defined(__GNUC__) && (defined(__i386__) || FOLLY_X64) return __builtin_ia32_rdtsc(); -#elif FOLLY_AARCH64 && !FOLLY_MOBILE +#elif FOLLY_AARCH64 && !FOLLY_MOBILE && !defined(_MSC_VER) uint64_t cval; asm volatile("mrs %0, cntvct_el0" : "=r"(cval)); return cval; diff --git a/folly/compression/Instructions.h b/folly/compression/Instructions.h index 0e7c8937401..06af6d75397 100644 --- a/folly/compression/Instructions.h +++ b/folly/compression/Instructions.h @@ -18,7 +18,9 @@ #include -#ifdef _MSC_VER +#if defined(_MSC_VER) && defined(_M_ARM64) +#include +#elif defined(_MSC_VER) #include #endif @@ -45,15 +47,31 @@ struct Default { static std::string_view name() noexcept { return "Default"; } static bool supported(const folly::CpuId& /* cpuId */ = {}) { return true; } static FOLLY_ALWAYS_INLINE uint64_t popcount(uint64_t value) { + #if defined(_MSC_VER) && defined(_M_ARM64) + return uint64_t(__popcnt64(value)); + #else return uint64_t(__builtin_popcountll(value)); + #endif } static FOLLY_ALWAYS_INLINE int ctz(uint64_t value) { DCHECK_GT(value, 0u); - return __builtin_ctzll(value); + #if defined(_MSC_VER) && defined(_M_ARM64) + unsigned long index; + _BitScanForward64(&index, value); + return int(index); + #else + return __builtin_ctzll(value); + #endif } static FOLLY_ALWAYS_INLINE int clz(uint64_t value) { DCHECK_GT(value, 0u); - return __builtin_clzll(value); + #if defined(_MSC_VER) && defined(_M_ARM64) + unsigned long index; + _BitScanReverse64(&index, value); + return 63 - int(index); + #else + return __builtin_clzll(value); + #endif } static FOLLY_ALWAYS_INLINE uint64_t blsr(uint64_t value) { return value & (value - 1); diff --git a/folly/container/detail/F14Table.h b/folly/container/detail/F14Table.h index 69887e2ec8b..3bdd077688b 100644 --- a/folly/container/detail/F14Table.h +++ b/folly/container/detail/F14Table.h @@ -338,7 +338,13 @@ std::pair splitHashImpl(std::size_t hash) { // was 48 bytes of assembly (even after using the same multiplicand // for both steps) and this one was 27 bytes, for example. auto const kMul = 0xc4ceb9fe1a85ec53ULL; -#ifdef _WIN32 +#if defined(_MSC_VER) && defined(_M_ARM64) + // MSVC ARM64: no _mul128, no __int128. + // Use 32-bit halves to compute the 128-bit product manually. + uint64_t lo = hash * kMul; + // __umulh gives the high 64 bits of an unsigned 64x64 multiply + uint64_t hi = __umulh(hash, kMul); +#elif defined(_WIN32) && !defined(FOLLY_AARCH64) __int64 signedHi; __int64 signedLo = _mul128( static_cast<__int64>(hash), static_cast<__int64>(kMul), &signedHi); diff --git a/folly/fibers/BoostContextCompatibility.h b/folly/fibers/BoostContextCompatibility.h index 310c4d13c77..30b04251347 100644 --- a/folly/fibers/BoostContextCompatibility.h +++ b/folly/fibers/BoostContextCompatibility.h @@ -16,17 +16,127 @@ #pragma once -#include #include +#include /** * Wrappers for different versions of boost::context library * API reference for different versions * Boost 1.61: * https://github.com/boostorg/context/blob/boost-1.61.0/include/boost/context/detail/fcontext.hpp + * + * On Windows ARM64, boost::context assembly stubs (jump_fcontext / make_fcontext) + * are not currently supported, so fall back to the native Windows Fibers API. */ -#include +#if defined(_WIN32) && defined(_M_ARM64) + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +namespace folly { +namespace fibers { + +class FiberImpl { + public: + FiberImpl( + folly::Function func, + unsigned char* /*stackLimit*/, + size_t stackSize) + : func_(std::move(func)) { + fiber_ = CreateFiberEx( + stackSize, + stackSize, + FIBER_FLAG_FLOAT_SWITCH, + &FiberImpl::fiberFunc, + this); + CHECK(fiber_ != nullptr) + << "CreateFiberEx failed: " << GetLastError(); + } + + ~FiberImpl() { + if (fiber_) { + DCHECK(GetCurrentFiber() != fiber_) + << "Destroying fiber while it is active"; + DeleteFiber(std::exchange(fiber_, nullptr)); + } + } + + FiberImpl(const FiberImpl&) = delete; + FiberImpl& operator=(const FiberImpl&) = delete; + + FiberImpl(FiberImpl&& other) noexcept + : func_(std::move(other.func_)), + fiber_(std::exchange(other.fiber_, nullptr)), + mainFiber_(std::exchange(other.mainFiber_, nullptr)), + convertedThread_(std::exchange(other.convertedThread_, false)) {} + + FiberImpl& operator=(FiberImpl&& other) noexcept { + if (this != &other) { + if (fiber_) DeleteFiber(fiber_); + func_ = std::move(other.func_); + fiber_ = std::exchange(other.fiber_, nullptr); + mainFiber_ = std::exchange(other.mainFiber_, nullptr); + convertedThread_ = std::exchange(other.convertedThread_, false); + } + return *this; + } + + void activate() { + mainFiber_ = GetCurrentFiber(); + // On ARM64 Windows, GetCurrentFiber() returns a garbage low address + // (e.g. 0x1E00) when the thread has not been converted to a fiber yet. + // A real fiber handle is always above 64KB (Windows minimum allocation + // granularity), so use 0x10000 as the threshold. + if (mainFiber_ == nullptr || + mainFiber_ == INVALID_HANDLE_VALUE || + reinterpret_cast(mainFiber_) < 0x10000) { + mainFiber_ = ConvertThreadToFiber(nullptr); + CHECK(mainFiber_ != nullptr) + << "ConvertThreadToFiber failed: " << GetLastError(); + convertedThread_ = true; + } + SwitchToFiber(fiber_); + } + + + void deactivate() { + DCHECK(mainFiber_ != nullptr) << "deactivate() called before activate()"; + SwitchToFiber(std::exchange(mainFiber_, nullptr)); + if (convertedThread_) { + ConvertFiberToThread(); + convertedThread_ = false; + } + } + + void* getStackPointer() const { return nullptr; } + + private: + static VOID CALLBACK fiberFunc(LPVOID param) { + auto* self = static_cast(param); + self->func_(); + // func_() must not return in normal folly::fibers usage. + // If it does, switch back to avoid undefined behavior — but + // don't call deactivate() as mainFiber_ may already be cleared. + CHECK(false) << "FiberImpl::func_() returned unexpectedly"; + } + + folly::Function func_; + LPVOID fiber_{nullptr}; + LPVOID mainFiber_{nullptr}; + bool convertedThread_{false}; +}; + +} // namespace fibers +} // namespace folly +#else + +#include namespace folly { namespace fibers { @@ -110,3 +220,4 @@ class FiberImpl { }; } // namespace fibers } // namespace folly +#endif diff --git a/folly/lang/Bits.h b/folly/lang/Bits.h index 3043f0f3d0f..8a33c71205c 100644 --- a/folly/lang/Bits.h +++ b/folly/lang/Bits.h @@ -67,6 +67,10 @@ #include #include +#if defined(_MSC_VER) && defined(_M_ARM64) +#include +#endif + #ifdef __BMI2__ #include #endif @@ -183,14 +187,22 @@ inline constexpr T extractFirstSet(T const v) { /// Returns the number of bits which are set. template inline constexpr unsigned int popcount(T const v) { - using U0 = unsigned int; - using U1 = unsigned long int; using U2 = unsigned long long int; using detail::bits_to_unsigned; static_assert(sizeof(T) <= sizeof(U2), "over-sized type"); static_assert(std::is_integral::value, "non-integral type"); static_assert(!std::is_same::value, "bool type"); +#if defined(_MSC_VER) && defined(_M_ARM64) + // For MSVC ARM64 use __popcnt intrinsics. + return static_cast( + sizeof(T) <= sizeof(unsigned int) ? + __popcnt(bits_to_unsigned(v)) + : __popcnt64(bits_to_unsigned(v)) + ); +#else + using U0 = unsigned int; + using U1 = unsigned long int; // clang-format off return static_cast( sizeof(T) <= sizeof(U0) ? __builtin_popcount(bits_to_unsigned(v)) : @@ -198,6 +210,7 @@ inline constexpr unsigned int popcount(T const v) { sizeof(T) <= sizeof(U2) ? __builtin_popcountll(bits_to_unsigned(v)) : 0); // clang-format on +#endif } template diff --git a/folly/portability/Asm.h b/folly/portability/Asm.h index 5f850163e30..a5637bdf4a4 100644 --- a/folly/portability/Asm.h +++ b/folly/portability/Asm.h @@ -36,6 +36,8 @@ inline void asm_volatile_memory() { inline void asm_volatile_pause() { #if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) ::_mm_pause(); +#elif defined(_MSC_VER) && defined(_M_ARM64) + __yield(); #elif defined(__i386__) || FOLLY_X64 || \ (defined(__mips_isa_rev) && __mips_isa_rev > 1) asm volatile("pause");