diff --git a/.github/workflows/_bazel.yml b/.github/workflows/_bazel.yml index ed0c81d1ce..b9f62e5c7c 100644 --- a/.github/workflows/_bazel.yml +++ b/.github/workflows/_bazel.yml @@ -131,16 +131,24 @@ jobs: run: | exec bazel test ${{ inputs.bazel-args }} \ --platforms=@toolchains_llvm//platforms:linux-aarch64 \ + //compile/test:cross_compile_aarch64_compiler_rt_test \ //compile/test:cross_compile_aarch64_test \ //compile/test:cross_compile_aarch64_unwind_test working-directory: ${{ inputs.bazel-path }} - - name: Cross-compile test (x86_64 -> aarch64/${{ inputs.bazel-mode }}) + - name: Cross-compile test libunwind (x86_64 -> aarch64/${{ inputs.bazel-mode }}) run: | exec bazel test ${{ inputs.bazel-args }} \ --platforms=@toolchains_llvm//platforms:linux-aarch64 \ --@toolchains_llvm//toolchain/config:libunwind=False \ //compile/test:cross_compile_aarch64_no_unwind_test working-directory: ${{ inputs.bazel-path }} + - name: Cross-compile test compiler-rt (x86_64 -> aarch64/${{ inputs.bazel-mode }}) + run: | + exec bazel test ${{ inputs.bazel-args }} \ + --platforms=@toolchains_llvm//platforms:linux-aarch64 \ + --@toolchains_llvm//toolchain/config:compiler-rt=False \ + //compile/test:cross_compile_aarch64_no_compiler_rt_test + working-directory: ${{ inputs.bazel-path }} xcompile-arm-to-x86: if: inputs.action == 'test' @@ -164,13 +172,21 @@ jobs: run: | exec bazel test ${{ inputs.bazel-args }} \ --platforms=@toolchains_llvm//platforms:linux-x86_64 \ + //compile/test:cross_compile_x86_64_compiler_rt_test \ //compile/test:cross_compile_x86_64_test \ //compile/test:cross_compile_x86_64_unwind_test working-directory: ${{ inputs.bazel-path }} - - name: Cross-compile test (x86_64 -> x86_64/${{ inputs.bazel-mode }}) + - name: Cross-compile test libunwind (aarch64 -> x86_64/${{ inputs.bazel-mode }}) run: | exec bazel test ${{ inputs.bazel-args }} \ --platforms=@toolchains_llvm//platforms:linux-x86_64 \ --@toolchains_llvm//toolchain/config:libunwind=False \ //compile/test:cross_compile_x86_64_no_unwind_test working-directory: ${{ inputs.bazel-path }} + - name: Cross-compile test compiler-rt (aarch64 -> x86_64/${{ inputs.bazel-mode }}) + run: | + exec bazel test ${{ inputs.bazel-args }} \ + --platforms=@toolchains_llvm//platforms:linux-x86_64 \ + --@toolchains_llvm//toolchain/config:compiler-rt=False \ + //compile/test:cross_compile_x86_64_no_compiler_rt_test + working-directory: ${{ inputs.bazel-path }} diff --git a/bazel/compile/README.md b/bazel/compile/README.md index e9cc84335a..6c2f48b513 100644 --- a/bazel/compile/README.md +++ b/bazel/compile/README.md @@ -201,3 +201,36 @@ bazel test //compile/test:cross_compile_x86_64_no_unwind_test \ --platforms=@toolchains_llvm//platforms:linux-x86_64 \ --@toolchains_llvm//toolchain/config:libunwind=False ``` + +### Compiler-rt tests + +The compiler-rt tests verify that `libclang_rt.builtins.a` (compiler-rt) is **statically +linked** into the `test_compiler_rt` binary. They use `llvm-nm` to check for a defined +`__divti3` symbol (a 128-bit integer division function that is provided by compiler-rt +builtins and referenced by `__int128` division) and `llvm-readelf` to confirm there is no +`libgcc_s` dynamic dependency. + +```bash +# Test aarch64 cross-compilation with compiler-rt statically linked +bazel test //compile/test:cross_compile_aarch64_compiler_rt_test \ + --platforms=@toolchains_llvm//platforms:linux-aarch64 + +# Test x86_64 cross-compilation with compiler-rt statically linked +bazel test //compile/test:cross_compile_x86_64_compiler_rt_test \ + --platforms=@toolchains_llvm//platforms:linux-x86_64 +``` + +**Negative compiler-rt tests** verify that compiler-rt builtins are *not* statically +linked when `--@toolchains_llvm//toolchain/config:compiler-rt=False` is passed: + +```bash +# Verify compiler-rt is NOT linked for aarch64 when disabled +bazel test //compile/test:cross_compile_aarch64_no_compiler_rt_test \ + --platforms=@toolchains_llvm//platforms:linux-aarch64 \ + --@toolchains_llvm//toolchain/config:compiler-rt=False + +# Verify compiler-rt is NOT linked for x86_64 when disabled +bazel test //compile/test:cross_compile_x86_64_no_compiler_rt_test \ + --platforms=@toolchains_llvm//platforms:linux-x86_64 \ + --@toolchains_llvm//toolchain/config:compiler-rt=False +``` diff --git a/bazel/compile/test/BUILD.bazel b/bazel/compile/test/BUILD.bazel index 085f4356cb..4825487c06 100644 --- a/bazel/compile/test/BUILD.bazel +++ b/bazel/compile/test/BUILD.bazel @@ -157,3 +157,111 @@ sh_test( }, tags = ["manual"], ) + +# C++ compiler-rt builtins binary — exercises __int128 division to reference +# the __divti3 symbol provided by libclang_rt.builtins.a (compiler-rt). +# Build for aarch64: +# bazel build //compile/test:test_compiler_rt --platforms=@toolchains_llvm//platforms:linux-aarch64 +# Build for x86_64: +# bazel build //compile/test:test_compiler_rt --platforms=@toolchains_llvm//platforms:linux-x86_64 +cc_binary( + name = "test_compiler_rt", + srcs = ["test_compiler_rt.cc"], + copts = ["-Werror"], +) + +# Cross-compilation compiler-rt tests. +# These tests verify that the test_compiler_rt binary has libclang_rt.builtins.a +# statically linked by checking for the defined __divti3 symbol (via llvm-nm) +# and the absence of libgcc_s in dynamic NEEDED entries (via llvm-readelf). +# +# Run with: +# bazel test //compile/test:cross_compile_aarch64_compiler_rt_test \ +# --platforms=@toolchains_llvm//platforms:linux-aarch64 +sh_test( + name = "cross_compile_aarch64_compiler_rt_test", + size = "small", + srcs = ["check_compiler_rt.sh"], + data = [ + ":test_compiler_rt", + "@llvm_toolchain_llvm//:nm", + "@llvm_toolchain_llvm//:readelf", + ], + env = { + "BINARY": "$(rootpath :test_compiler_rt)", + "EXPECT_COMPILER_RT": "true", + "EXPECTED_ARCH": "AArch64", + "LLVM_NM": "$(rootpath @llvm_toolchain_llvm//:nm)", + "LLVM_READELF": "$(rootpath @llvm_toolchain_llvm//:readelf)", + }, + tags = ["manual"], +) + +# Run with: +# bazel test //compile/test:cross_compile_x86_64_compiler_rt_test \ +# --platforms=@toolchains_llvm//platforms:linux-x86_64 +sh_test( + name = "cross_compile_x86_64_compiler_rt_test", + size = "small", + srcs = ["check_compiler_rt.sh"], + data = [ + ":test_compiler_rt", + "@llvm_toolchain_llvm//:nm", + "@llvm_toolchain_llvm//:readelf", + ], + env = { + "BINARY": "$(rootpath :test_compiler_rt)", + "EXPECT_COMPILER_RT": "true", + "EXPECTED_ARCH": "X86-64", + "LLVM_NM": "$(rootpath @llvm_toolchain_llvm//:nm)", + "LLVM_READELF": "$(rootpath @llvm_toolchain_llvm//:readelf)", + }, + tags = ["manual"], +) + +# Negative compiler-rt tests — verify that compiler-rt builtins are NOT +# statically linked when the flag is disabled. Run with: +# bazel test //compile/test:cross_compile_aarch64_no_compiler_rt_test \ +# --platforms=@toolchains_llvm//platforms:linux-aarch64 \ +# --@toolchains_llvm//toolchain/config:compiler-rt=False +sh_test( + name = "cross_compile_aarch64_no_compiler_rt_test", + size = "small", + srcs = ["check_compiler_rt.sh"], + data = [ + ":test_compiler_rt", + "@llvm_toolchain_llvm//:nm", + "@llvm_toolchain_llvm//:readelf", + ], + env = { + "BINARY": "$(rootpath :test_compiler_rt)", + "EXPECT_COMPILER_RT": "false", + "EXPECTED_ARCH": "AArch64", + "LLVM_NM": "$(rootpath @llvm_toolchain_llvm//:nm)", + "LLVM_READELF": "$(rootpath @llvm_toolchain_llvm//:readelf)", + }, + tags = ["manual"], +) + +# Run with: +# bazel test //compile/test:cross_compile_x86_64_no_compiler_rt_test \ +# --platforms=@toolchains_llvm//platforms:linux-x86_64 \ +# --@toolchains_llvm//toolchain/config:compiler-rt=False +sh_test( + name = "cross_compile_x86_64_no_compiler_rt_test", + size = "small", + srcs = ["check_compiler_rt.sh"], + data = [ + ":test_compiler_rt", + "@llvm_toolchain_llvm//:nm", + "@llvm_toolchain_llvm//:readelf", + ], + env = { + "BINARY": "$(rootpath :test_compiler_rt)", + "EXPECT_COMPILER_RT": "false", + "EXPECTED_ARCH": "X86-64", + "LLVM_NM": "$(rootpath @llvm_toolchain_llvm//:nm)", + "LLVM_READELF": "$(rootpath @llvm_toolchain_llvm//:readelf)", + }, + tags = ["manual"], +) diff --git a/bazel/compile/test/check_compiler_rt.sh b/bazel/compile/test/check_compiler_rt.sh new file mode 100755 index 0000000000..1857de74d9 --- /dev/null +++ b/bazel/compile/test/check_compiler_rt.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# Checks that a cross-compiled binary has (or does not have) compiler-rt builtins statically linked. +# +# Required environment variables: +# BINARY - path to the compiled binary +# EXPECTED_ARCH - expected ELF architecture (e.g. AArch64, X86-64) +# EXPECT_COMPILER_RT - "true" if libclang_rt.builtins.a should be statically linked, "false" otherwise +# LLVM_NM - path to llvm-nm binary +# LLVM_READELF - path to llvm-readelf binary +set -euo pipefail + +: "${BINARY:?BINARY must be set to the path of the compiled binary}" +: "${EXPECTED_ARCH:?EXPECTED_ARCH must be set to the expected architecture (e.g. AArch64, X86-64)}" +: "${EXPECT_COMPILER_RT:?EXPECT_COMPILER_RT must be set to 'true' or 'false'}" +: "${LLVM_NM:?LLVM_NM must be set to the path of llvm-nm}" +: "${LLVM_READELF:?LLVM_READELF must be set to the path of llvm-readelf}" + +# Check architecture using llvm-readelf -h +READELF_OUTPUT="$("${LLVM_READELF}" -h "${BINARY}")" +echo "llvm-readelf -h output:" +echo "${READELF_OUTPUT}" + +if echo "${READELF_OUTPUT}" | grep -q "${EXPECTED_ARCH}"; then + echo "PASS: binary is ${EXPECTED_ARCH}" +else + echo "FAIL: expected ${EXPECTED_ARCH} in readelf output" + exit 1 +fi + +# Check for __divti3 defined (T/t) symbol via llvm-nm. +# __divti3 is the 128-bit signed integer division function provided by +# compiler-rt builtins (libclang_rt.builtins.a). Its presence as a defined +# symbol proves that compiler-rt was statically linked. +NM_OUTPUT="$("${LLVM_NM}" "${BINARY}")" +echo "llvm-nm output (filtered):" +echo "${NM_OUTPUT}" | grep -i "__divti3" || echo "(no __divti3 symbols found)" + +COMPILER_RT_DEFINED="false" +if grep -qE '[Tt]\s+__divti3' <<< "${NM_OUTPUT}"; then + COMPILER_RT_DEFINED="true" +fi + +# Check for libgcc_s in dynamic NEEDED entries via llvm-readelf -d +DYNAMIC_OUTPUT="$("${LLVM_READELF}" -d "${BINARY}")" +echo "llvm-readelf -d output:" +echo "${DYNAMIC_OUTPUT}" + +HAS_LIBGCC_S="false" +if echo "${DYNAMIC_OUTPUT}" | grep -q "libgcc_s"; then + HAS_LIBGCC_S="true" +fi + +if [[ "${EXPECT_COMPILER_RT}" = "true" ]]; then + if [ "${COMPILER_RT_DEFINED}" = "true" ]; then + echo "PASS: __divti3 is defined (statically linked from libclang_rt.builtins.a)" + else + echo "FAIL: expected __divti3 to be a defined (T/t) symbol, but it was not found" + exit 1 + fi + if [[ "${HAS_LIBGCC_S}" = "false" ]]; then + echo "PASS: no libgcc_s in dynamic NEEDED entries" + else + echo "FAIL: found libgcc_s in dynamic NEEDED entries (binary is using libgcc fallback)" + exit 1 + fi +else + if [[ "${COMPILER_RT_DEFINED}" = "false" ]]; then + echo "PASS: __divti3 is not a defined (T/t) symbol (compiler-rt not statically linked)" + else + echo "FAIL: expected __divti3 to NOT be a defined (T/t) symbol, but it was found" + exit 1 + fi +fi diff --git a/bazel/compile/test/test_compiler_rt.cc b/bazel/compile/test/test_compiler_rt.cc new file mode 100755 index 0000000000..7745c419cb --- /dev/null +++ b/bazel/compile/test/test_compiler_rt.cc @@ -0,0 +1,13 @@ +#include + +// Exercise compiler-rt builtins by performing 128-bit integer division. +// The __divti3 symbol (128-bit signed division) is provided by +// libclang_rt.builtins.a (compiler-rt) or libgcc on platforms that do not +// have a native 128-bit division instruction (e.g. aarch64, x86_64). +int main() { + volatile __int128 a = static_cast<__int128>(1000000000) * 1000000000; + volatile __int128 b = 7; + volatile __int128 result = a / b; + std::cout << "result: " << static_cast(result) << std::endl; + return 0; +} diff --git a/bazel/toolchains_llvm.bzl b/bazel/toolchains_llvm.bzl index a327239024..30964001ee 100644 --- a/bazel/toolchains_llvm.bzl +++ b/bazel/toolchains_llvm.bzl @@ -6,6 +6,9 @@ def setup_llvm_toolchain(llvm_version = None): compatibility_proxy_repo() llvm_toolchain( name = "llvm_toolchain", + libclang_rt = { + "@libcxx_libs_aarch64//:lib/libclang_rt.builtins.a": "linux/libclang_rt.builtins-aarch64.a", + }, llvm_version = llvm_version or VERSIONS["llvm"], cxx_cross_lib = { "linux-aarch64": "@libcxx_libs_aarch64",