diff --git a/Makefile b/Makefile index 46f003bf52..40abccd2fc 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ TESTABLE_APPS := bash coreutils curl git grep lmbench sed tinycc APP ?= $(TESTABLE_APPS) # -------- Phonies ------------------------------------------------------------- -.PHONY: all preflight dirs print-config check-build libtirpc gnulib zlib openssl libcxx merge-base-sysroot merge-sysroot lmbench bash nginx coreutils cpython git curl grep sed gcc binutils postgres clean clean-all rebuild-libs rebuild-sysroot test install-bash install-nginx install-git install-curl install-grep install-sed install-lmbench install-coreutils install-gcc install-binutils install +.PHONY: all preflight dirs print-config check-build libtirpc gnulib zlib openssl libcxx merge-base-sysroot merge-sysroot lmbench bash nginx coreutils cpython git curl grep sed gcc binutils clang postgres clean clean-all rebuild-libs rebuild-sysroot install-bash install-nginx install-git install-curl install-grep install-sed install-lmbench install-coreutils install-gcc install-binutils install-clang install all: preflight libtirpc gnulib merge-sysroot lmbench bash @@ -79,24 +79,20 @@ check-build: done clean: - @if [[ -z "$(strip $(APP))" ]]; then \ - echo "ERROR: no apps selected; set APP to one or more of: $(TESTABLE_APPS)"; \ - exit 1; \ - fi + @# Per-app clean scripts (optional — skips apps without clean.sh) @for app in $(APP); do \ case " $(TESTABLE_APPS) " in \ *" $$app "*) ;; \ - *) echo "ERROR: unsupported clean app '$$app'; supported apps: $(TESTABLE_APPS)"; exit 1 ;; \ + *) continue ;; \ esac; \ if [[ -x '$(APPS_ROOT)/'"$$app"'/clean.sh' ]]; then \ '$(APPS_ROOT)/'"$$app"'/clean.sh' "$$app"; \ - else \ - echo "[SKIP] $$app: missing $(APPS_ROOT)/$$app/clean.sh"; \ fi; \ done + @# Infrastructure: stamps, sysroot, overlay, toolchain env -rm -rf '$(APPS_OVERLAY)' '$(MERGED_SYSROOT)' '$(APPS_BIN_DIR)' '$(APPS_LIB_DIR)' '$(TOOL_ENV)' - -rm -f '$(LIBTIRPC_STAMP)' '$(GNULIB_STAMP)' '$(ZLIB_STAMP)' '$(OPENSSL_STAMP)' - -rm -f '$(MERGE_BASE_STAMP)' '$(MERGE_TIRPC_STAMP)' '$(MERGE_GNULIB_STAMP)' '$(MERGE_ZLIB_STAMP)' '$(MERGE_OPENSSL_STAMP)' '$(MERGE_ALL_STAMP)' + -rm -f '$(LIBTIRPC_STAMP)' '$(GNULIB_STAMP)' '$(ZLIB_STAMP)' '$(OPENSSL_STAMP)' '$(LIBCXX_STAMP)' + -rm -f '$(MERGE_BASE_STAMP)' '$(MERGE_TIRPC_STAMP)' '$(MERGE_GNULIB_STAMP)' '$(MERGE_ZLIB_STAMP)' '$(MERGE_OPENSSL_STAMP)' '$(MERGE_LIBCXX_STAMP)' '$(MERGE_ALL_STAMP)' print-config: @echo "LIND_WASM_ROOT=$(LIND_WASM_ROOT)" @@ -329,6 +325,14 @@ binutils: $(MERGE_ZLIB_STAMP) . '$(TOOL_ENV)' JOBS='$(JOBS)' '$(APPS_ROOT)/binutils/compile_binutils.sh' +# ---------------- clang (WASM build) ------------------------------------------- +# Uses llvm-project/compile_clang.sh to cross-compile clang and lld as +# wasm32-wasi binaries. Requires libc++ in the merged sysroot (C++ source). +# Stages artifacts under build/clang/usr/local/bin. +clang: $(MERGE_LIBCXX_STAMP) + . '$(TOOL_ENV)' + JOBS='$(JOBS)' '$(APPS_ROOT)/llvm-project/compile_clang.sh' + rebuild-libs: rm -f '$(LIBTIRPC_STAMP)' '$(GNULIB_STAMP)' '$(ZLIB_STAMP)' '$(OPENSSL_STAMP)' '$(LIBCXX_STAMP)' \ '$(MERGE_TIRPC_STAMP)' '$(MERGE_GNULIB_STAMP)' '$(MERGE_ZLIB_STAMP)' '$(MERGE_OPENSSL_STAMP)' '$(MERGE_LIBCXX_STAMP)' '$(MERGE_ALL_STAMP)' @@ -376,4 +380,7 @@ install-gcc: install-binutils: '$(APPS_ROOT)/scripts/post_install.sh' '$(LINDFS_ROOT)' '$(APPS_BUILD)' binutils -install: install-bash install-nginx install-git install-curl install-grep install-sed install-lmbench install-coreutils install-gcc install-binutils +install-clang: + '$(APPS_ROOT)/scripts/post_install.sh' '$(LINDFS_ROOT)' '$(APPS_BUILD)' clang + +install: install-bash install-nginx install-git install-curl install-grep install-sed install-lmbench install-coreutils install-gcc install-binutils install-clang diff --git a/llvm-project/Toolchain-WASI-LLVM.cmake.in b/llvm-project/Toolchain-WASI-LLVM.cmake.in new file mode 100644 index 0000000000..abfd647a1f --- /dev/null +++ b/llvm-project/Toolchain-WASI-LLVM.cmake.in @@ -0,0 +1,56 @@ +# Auto-generated CMake toolchain for cross-compiling LLVM to wasm32-wasi +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR wasm32) + +# Compiler and tools +set(CMAKE_C_COMPILER "@CLANG@") +set(CMAKE_CXX_COMPILER "@CLANGXX@") +set(CMAKE_AR "@AR@") +set(CMAKE_NM "@NM@") +set(CMAKE_RANLIB "@RANLIB@") +set(CMAKE_SYSROOT "@BASE_SYSROOT@") +set(CMAKE_LINKER "@LD@") + +# Target configuration +set(CMAKE_C_COMPILER_TARGET wasm32-unknown-wasi) +set(CMAKE_CXX_COMPILER_TARGET wasm32-unknown-wasi) + +# Force CMake to accept compilers without try-run (can't run wasm on host) +set(CMAKE_C_COMPILER_WORKS TRUE) +set(CMAKE_CXX_COMPILER_WORKS TRUE) +set(CMAKE_EXECUTABLE_SUFFIX ".wasm") + +# WASM C flags — use -Os to minimize binary size (clang is huge) +# Use CACHE FORCE so nothing can override these (not even a stale CMakeCache) +# WASI uses glibc headers — define __linux__ so LLVM uses , +# Linux-style struct fields, etc. (WASI is not Linux, but its glibc +# sysroot matches the Linux ABI for these purposes.) +set(CMAKE_C_FLAGS "-Os -pthread -matomics -mbulk-memory -fno-exceptions -fno-unwind-tables -D__linux__" CACHE STRING "" FORCE) + +# WASM C++ flags — use libc++, no exceptions/RTTI/unwind to reduce binary size +set(CMAKE_CXX_FLAGS "-Os -pthread -matomics -mbulk-memory -fno-exceptions -fno-unwind-tables -fno-rtti -D__linux__ -nostdinc++ -isystem $LIBCXX_INCLUDE -include $SCRIPT_DIR/fix_std_maxmin.h" CACHE STRING "" FORCE) + +# Linker flags +set(CMAKE_EXE_LINKER_FLAGS "\ + -L$MERGED_SYSROOT/lib/wasm32-wasi \ + -L$MERGED_SYSROOT/usr/lib/wasm32-wasi \ + -Wl,--export=__stack_pointer,--export=__stack_low \ + -Wl,--import-memory,--export-memory \ + -Wl,--max-memory=67108864 \ + -lm " CACHE STRING "" FORCE) + +# Don't pass -rpath to the linker +set(CMAKE_SKIP_RPATH TRUE) +set(CMAKE_SKIP_INSTALL_RPATH TRUE) +set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) + +# Prevent try-run errors +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# Skip old toolchain warnings +set(LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN ON CACHE BOOL "" FORCE) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/llvm-project/compile_clang.sh b/llvm-project/compile_clang.sh new file mode 100755 index 0000000000..582af64bc2 --- /dev/null +++ b/llvm-project/compile_clang.sh @@ -0,0 +1,368 @@ +#!/usr/bin/env bash +set -euo pipefail + +############################################################################### +# Clang/LLD WASI build helper for lind-wasm-apps +# +# Cross-compiles LLVM 18.1.8 (clang + lld) to wasm32-wasi so that the +# compiler and linker can run inside the Lind sandbox. The resulting tools +# target x86_64-unknown-linux-gnu (X86 backend only). +# +# High-level strategy: +# 1. Build native LLVM tools (llvm-tblgen, clang-tblgen, etc.) — required +# because CMake can't run wasm binaries during the build +# 2. Generate CMake toolchain file for wasm32-wasi cross-compilation +# 3. Configure cross-build with CMake +# 4. Build clang and lld +# 5. Post-process each binary: wasm-opt (asyncify) + lind-boot (precompile) +# +# Based on Alice's work on the alice-clang branch in lind-wasm. +# +# Prerequisites: +# - Run 'make preflight' and ensure libc++ is in the merged sysroot +# (run 'make libcxx merge-sysroot' or let the Makefile handle it) +# - cmake >= 3.20 and ninja (or make) must be on PATH +############################################################################### + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +APPS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +LLVM_SRC="$SCRIPT_DIR/llvm" + +APPS_BUILD="$APPS_ROOT/build" +MERGED_SYSROOT="$APPS_BUILD/sysroot_merged" +STAGE_DIR="$APPS_BUILD/clang/usr/local/bin" +TOOL_ENV="$APPS_BUILD/.toolchain.env" + +if [[ -z "${LIND_WASM_ROOT:-}" ]]; then + LIND_WASM_ROOT="$(cd "$APPS_ROOT/.." && pwd)" +fi + +WASM_OPT="${WASM_OPT:-$LIND_WASM_ROOT/tools/binaryen/bin/wasm-opt}" +LIND_BOOT="${LIND_BOOT:-$LIND_WASM_ROOT/build/lind-boot}" + +JOBS="${JOBS:-$(nproc 2>/dev/null || getconf _NPROCESSORS_ONLN || echo 4)}" +CMAKE="${CMAKE:-cmake}" + +# ---------------------------------------------------------------------- +# 1) Load toolchain from Makefile preflight +# ---------------------------------------------------------------------- +if [[ -r "$TOOL_ENV" ]]; then + # shellcheck disable=SC1090 + . "$TOOL_ENV" +else + echo "[clang] ERROR: missing toolchain env '$TOOL_ENV' (run 'make preflight' first)" >&2 + exit 1 +fi + +: "${CLANG:?missing CLANG in $TOOL_ENV}" +: "${AR:?missing AR in $TOOL_ENV}" +: "${RANLIB:?missing RANLIB in $TOOL_ENV}" +: "${NM:?missing NM in $TOOL_ENV}" + +LLVM_BIN_DIR="$(dirname "$CLANG")" +CLANGXX="$LLVM_BIN_DIR/clang++" +if [[ ! -x "$CLANGXX" ]]; then + echo "[clang] WARN: clang++ not found; creating symlink to clang" + ln -sf "$CLANG" "$CLANGXX" +fi + +# Sanity checks +if [[ ! -d "$LLVM_SRC" ]]; then + echo "[clang] ERROR: LLVM source dir not found at '$LLVM_SRC'" >&2 + exit 1 +fi +if [[ ! -d "$SCRIPT_DIR/clang" ]]; then + echo "[clang] ERROR: clang source dir not found at '$SCRIPT_DIR/clang'" >&2 + exit 1 +fi +if [[ ! -d "$MERGED_SYSROOT" ]]; then + echo "[clang] ERROR: merged sysroot '$MERGED_SYSROOT' not found. Run 'make merge-sysroot' first." >&2 + exit 1 +fi +if [[ ! -f "$MERGED_SYSROOT/lib/wasm32-wasi/libc++.a" ]]; then + echo "[clang] ERROR: libc++.a not found in merged sysroot. Run 'make libcxx merge-sysroot' first." >&2 + exit 1 +fi +if ! command -v "$CMAKE" &>/dev/null; then + echo "[clang] ERROR: cmake not found on PATH" >&2 + exit 1 +fi + +mkdir -p "$STAGE_DIR" + +LIBCXX_INCLUDE="$MERGED_SYSROOT/include/wasm32-wasi/c++/v1" + +echo "[clang] using CLANG = $CLANG" +echo "[clang] using CLANGXX = $CLANGXX" +echo "[clang] using AR = $AR" +echo "[clang] using LD = $LD" +echo "[clang] LIND_WASM_ROOT = $LIND_WASM_ROOT" +echo "[clang] merged sysroot = $MERGED_SYSROOT" +echo "[clang] libc++ headers = $LIBCXX_INCLUDE" +echo "[clang] stage dir = $STAGE_DIR" +echo + +# ---------------------------------------------------------------------- +# 2) Build native LLVM tools (tablegen etc.) +# +# CMake cannot run wasm binaries during the build, so we need native +# versions of llvm-tblgen, clang-tblgen, llvm-min-tblgen, and +# clang-tidy-confusable-chars-gen for the cross-build. +# ---------------------------------------------------------------------- +NATIVE_BUILD="$APPS_BUILD/llvm-native-build" + +if [[ ! -x "$NATIVE_BUILD/bin/llvm-tblgen" ]] || \ + [[ ! -x "$NATIVE_BUILD/bin/clang-tblgen" ]]; then + echo "[clang] building native LLVM tools (tablegen etc.)…" + mkdir -p "$NATIVE_BUILD" + + "$CMAKE" -B "$NATIVE_BUILD" -S "$LLVM_SRC" \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_ENABLE_PROJECTS="clang;lld" \ + -DLLVM_INCLUDE_TESTS=OFF \ + -DLLVM_BUILD_TESTS=OFF \ + -DLLVM_INCLUDE_BENCHMARKS=OFF \ + -DLLVM_INCLUDE_EXAMPLES=OFF \ + -DLLVM_TARGETS_TO_BUILD="X86" + + "$CMAKE" --build "$NATIVE_BUILD" --target \ + llvm-tblgen clang-tblgen llvm-min-tblgen \ + -j"$JOBS" + + echo "[clang] native tools built at $NATIVE_BUILD/bin/" +else + echo "[clang] native LLVM tools already built; skipping." +fi + +# ---------------------------------------------------------------------- +# 3) Build compiler-rt +# ---------------------------------------------------------------------- +echo "[clang] building compiler-rt…" + +"$SCRIPT_DIR/compile_compiler-rt.sh" + +# ---------------------------------------------------------------------- +# 4) Generate CMake toolchain file for wasm32-wasi cross-compilation +# ---------------------------------------------------------------------- +mkdir -p "$BUILD_DIR" + +TOOLCHAIN_FILE="$BUILD_DIR/Toolchain-WASI-LLVM.cmake" +sed -e "s|@CLANG@|$CLANG|g" \ + -e "s|@CLANGXX@|$CLANGXX|g" \ + -e "s|@AR@|$AR|g" \ + -e "s|@NM@|$NM|g" \ + -e "s|@LD@|$LD|g" \ + -e "s|@RANLIB@|$RANLIB|g" \ + -e "s|@BASE_SYSROOT@|$BASE_SYSROOT|g" \ + "$SCRIPT_DIR/Toolchain-WASI.cmake.in" > "$TOOLCHAIN_FILE" + +echo "[clang] generated toolchain file: $TOOLCHAIN_FILE" + +# ---------------------------------------------------------------------- +# 5) Configure cross-build +# ---------------------------------------------------------------------- +echo "[clang] configuring cross-build…" + +"$CMAKE" -B "$CROSS_BUILD" -S "$LLVM_SRC" \ + -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_ENABLE_PROJECTS="clang;lld" \ + -DLLVM_HOST_TRIPLE="wasm32-unknown-wasi" \ + -DLLVM_DEFAULT_TARGET_TRIPLE="x86_64-unknown-linux-gnu" \ + -DLLVM_TARGETS_TO_BUILD="X86" \ + -DLLVM_NATIVE_TOOL_DIR="$NATIVE_BUILD/bin" \ + -DLLVM_ENABLE_THREADS=OFF \ + -DLLVM_ENABLE_PIC=OFF \ + -DLLVM_ENABLE_LIBCXX=ON \ + -DLLVM_BUILD_SHARED_LIBS=OFF \ + -DLLVM_BUILD_TOOLS=OFF \ + -DLLVM_BUILD_TESTS=OFF \ + -DLLVM_INCLUDE_TESTS=OFF \ + -DLLVM_INCLUDE_EXAMPLES=OFF \ + -DLLVM_INCLUDE_BENCHMARKS=OFF \ + -DLLVM_INCLUDE_GOOGLETEST=OFF \ + -DLLVM_TOOL_CLANG_BUILD=ON \ + -DLLVM_TOOL_LLD_BUILD=ON \ + -DLLVM_TOOL_LLI_BUILD=OFF \ + -DLLVM_TOOL_LLVM_JITLINK_EXECUTOR_BUILD=OFF \ + -DLLD_ENABLE_TARGETS="ELF" \ + -DCLANG_INCLUDE_TESTS=OFF \ + -DLLD_INCLUDE_TESTS=OFF \ + -DCMAKE_SKIP_RPATH=ON \ + -DCMAKE_SKIP_INSTALL_RPATH=ON \ + -DCMAKE_C_STANDARD_LIBRARIES="-lc -lcompiler_rt" \ + -DCMAKE_CXX_STANDARD_LIBRARIES="-lc++ -lc++abi -lc -lcompiler_rt" \ + -DHAVE_LIBRT=0 \ + -DHAVE_LIBATOMIC=1 \ + -DHAVE_CXX_ATOMICS_WITHOUT_LIB=ON \ + -DHAVE_CXX_ATOMICS64_WITHOUT_LIB=ON \ + -DHAVE_CXX_ATOMICS_WITH_LIB=OFF \ + -DHAVE_CXX_ATOMICS64_WITH_LIB=OFF + +# ---------------------------------------------------------------------- +# 6) Build clang and lld +# ---------------------------------------------------------------------- +echo "[clang] building clang…" +"$CMAKE" --build "$CROSS_BUILD" --target clang -j"$JOBS" + +echo "[clang] building lld…" +"$CMAKE" --build "$CROSS_BUILD" --target lld -j"$JOBS" + +echo "[clang] installing core resource headers…" +"$CMAKE" --build "$CROSS_BUILD" --target install-core-resource-headers -j"$JOBS" + +# ---------------------------------------------------------------------- +# 7) Post-processing helper +# ---------------------------------------------------------------------- +post_process_binary() { + local NAME="$1" + local SRC_BIN="$2" + local USE_OS="${3:-false}" # use -Os instead of -O2 + + if [[ ! -f "$SRC_BIN" ]]; then + echo "[clang] ERROR: $NAME binary not produced at '$SRC_BIN'" >&2 + return 1 + fi + + local WASM_FILE="$SCRIPT_DIR/${NAME}.wasm" + local OPT_WASM="$SCRIPT_DIR/${NAME}.opt.wasm" + cp "$SRC_BIN" "$WASM_FILE" + echo "[clang] $NAME binary size: $(du -h "$WASM_FILE" | cut -f1)" + + # --- wasm-opt --- + if [[ ! -x "$WASM_OPT" ]]; then + echo "[clang] ERROR: wasm-opt not found at '$WASM_OPT'; exiting." >&2 + exit 1 + fi + + local OPT_FLAGS=() + if [[ "$USE_OS" == "true" ]]; then + OPT_FLAGS+=(-Os) + else + OPT_FLAGS+=(-O2) + fi + + # Build asyncify removelist for this binary + local ASYNCIFY_IGNORE="$SCRIPT_DIR/${NAME}_asyncify_ignore.txt" + generate_asyncify_ignorelist "$NAME" "$ASYNCIFY_IGNORE" + local IGNORE_COUNT + IGNORE_COUNT=$(wc -l < "$ASYNCIFY_IGNORE") + echo "[clang] asyncify removelist for $NAME: $IGNORE_COUNT wildcard patterns" + + echo "[clang] running wasm-opt on $NAME (epoch-injection + asyncify + ${OPT_FLAGS[*]})…" + "$WASM_OPT" --epoch-injection --asyncify \ + --pass-arg=asyncify-removelist@@"$ASYNCIFY_IGNORE" \ + --debuginfo "${OPT_FLAGS[@]}" \ + "$WASM_FILE" -o "$OPT_WASM" + + if [[ ! -f "$OPT_WASM" ]]; then + echo "[clang] ERROR: failed to generate '$OPT_WASM'; exiting." >&2 + exit 1 + fi + + # --- lind-boot --precompile --- + if [[ ! -x "$LIND_BOOT" ]]; then + echo "[clang] ERROR: lind-boot not found at '$LIND_BOOT'" >&2 + exit 1 + fi + + echo "[clang] generating cwasm for $NAME via lind-boot --precompile…" + "$LIND_BOOT" --precompile "$OPT_WASM" + + local OPT_CWASM="$SCRIPT_DIR/${NAME}.opt.cwasm" + if [[ ! -f "$OPT_CWASM" ]]; then + echo "[clang] ERROR: precompile produced no .cwasm for $NAME" >&2 + exit 1 + fi + + cp "$OPT_CWASM" "$STAGE_DIR/$NAME" + echo "[clang] $NAME staged as $STAGE_DIR/$NAME (precompiled)" +} + +# ---------------------------------------------------------------------- +# 8) Asyncify ignore list generator +# +# Like GCC's cc1, clang is a massive C++ binary with auto-generated +# code (TableGen instruction selection, pattern matchers) and +# template-heavy libraries that explode under asyncify. These are +# all pure computation with no I/O or yield points. +# ---------------------------------------------------------------------- +generate_asyncify_ignorelist() { + local NAME="$1" + local OUTFILE="$2" + + # The WASM name section contains a mix of demangled C++ names and + # mangled _Z* names. All C++ functions in clang/LLVM are pure + # computation — I/O only happens through C runtime functions (glibc + # wrappers like __lind_make_syscall, read, write, etc.) which don't + # have C++ mangled names. + # + # Exclude ALL C++ functions from asyncify. This is safe because: + # - Asyncify only needs to instrument functions that transitively + # reach yield points (I/O syscalls) + # - All yield points go through C runtime stubs (no _Z prefix, + # no C++ demangled names with ::) + # - The C stubs are small and asyncify handles them fine + # + # We match both mangled (_Z*) and demangled (*::*) C++ names, + # plus common LLVM/Clang class names as a safety net. + cat > "$OUTFILE" << 'PATTERNS' +_Z* +*::* +*(anonymous namespace)* +PATTERNS +} + +# ---------------------------------------------------------------------- +# 9) Locate and post-process binaries +# ---------------------------------------------------------------------- +# clang binary — CMake produces bin/clang-18.wasm (or similar) +CLANG_BIN="" +for candidate in \ + "$CROSS_BUILD/bin/clang-18.wasm" \ + "$CROSS_BUILD/bin/clang-18" \ + "$CROSS_BUILD/bin/clang.wasm" \ + "$CROSS_BUILD/bin/clang"; do + if [[ -f "$candidate" ]]; then + CLANG_BIN="$candidate" + break + fi +done + +if [[ -z "$CLANG_BIN" ]]; then + echo "[clang] ERROR: clang binary not found in $CROSS_BUILD/bin/" >&2 + echo "[clang] Contents of $CROSS_BUILD/bin/:" + ls -la "$CROSS_BUILD/bin/" || true + exit 1 +fi + +# lld binary +LLD_BIN="" +for candidate in \ + "$CROSS_BUILD/bin/lld.wasm" \ + "$CROSS_BUILD/bin/lld"; do + if [[ -f "$candidate" ]]; then + LLD_BIN="$candidate" + break + fi +done + +if [[ -z "$LLD_BIN" ]]; then + echo "[clang] ERROR: lld binary not found in $CROSS_BUILD/bin/" >&2 + echo "[clang] Contents of $CROSS_BUILD/bin/:" + ls -la "$CROSS_BUILD/bin/" || true + exit 1 +fi + +echo "[clang] found clang at: $CLANG_BIN" +echo "[clang] found lld at: $LLD_BIN" + +# clang is enormous — use -Os like GCC's cc1 +post_process_binary "clang" "$CLANG_BIN" "true" +# lld is smaller — -Os is still safer +post_process_binary "lld" "$LLD_BIN" "true" + +echo +echo "[clang] build complete. Outputs under:" +echo " $STAGE_DIR" +ls -lh "$STAGE_DIR" || true diff --git a/llvm-project/compile_compiler-rt.sh b/llvm-project/compile_compiler-rt.sh new file mode 100755 index 0000000000..d97946437c --- /dev/null +++ b/llvm-project/compile_compiler-rt.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +set -euo pipefail + +############################################################################### +# compiler-rt WASI build helper for lind-wasm-apps +# +# Cross-compiles LLVM compiler-rt builtins to wasm32-wasi using the toolchain +# detected by the top-level Makefile preflight target. Installs static runtime +# libraries to the sysroot overlay so merge-sysroot can pick them up. +# +# Prerequisites: +# - Run 'make preflight' first +# - cmake >= 3.20 must be on PATH +############################################################################### + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +APPS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +LLVM_SRC="$SCRIPT_DIR" + +APPS_BUILD="$APPS_ROOT/build" +APPS_OVERLAY="$APPS_BUILD/sysroot_overlay" +TOOL_ENV="$APPS_BUILD/.toolchain.env" + +if [[ -z "${LIND_WASM_ROOT:-}" ]]; then + LIND_WASM_ROOT="$(cd "$APPS_ROOT/.." && pwd)" +fi + +BASE_SYSROOT="${BASE_SYSROOT:-$LIND_WASM_ROOT/src/glibc/sysroot}" +JOBS="${JOBS:-$(nproc 2>/dev/null || getconf _NPROCESSORS_ONLN || echo 4)}" + +# ---------------------------------------------------------------------- +# 1) Load toolchain from Makefile preflight +# ---------------------------------------------------------------------- +if [[ -r "$TOOL_ENV" ]]; then + # shellcheck disable=SC1090 + . "$TOOL_ENV" +else + echo "[compiler-rt] ERROR: missing toolchain env '$TOOL_ENV' (run 'make preflight' first)" >&2 + exit 1 +fi + +: "${CLANG:?missing CLANG in $TOOL_ENV}" +: "${AR:?missing AR in $TOOL_ENV}" +: "${RANLIB:?missing RANLIB in $TOOL_ENV}" +: "${NM:?missing NM in $TOOL_ENV}" + +LLVM_BIN_DIR="$(dirname "$CLANG")" +CLANGXX="$LLVM_BIN_DIR/clang++" +if [[ ! -x "$CLANGXX" ]]; then + echo "[compiler-rt] WARN: clang++ not found at '$CLANGXX'; creating symlink to clang" + ln -sf "$CLANG" "$CLANGXX" +fi + +# Sanity checks +if [[ ! -d "$LLVM_SRC/compiler-rt" ]]; then + echo "[compiler-rt] ERROR: compiler-rt source not found at '$LLVM_SRC/compiler-rt'" >&2 + exit 1 +fi + +if [[ ! -r "$BASE_SYSROOT/include/wasm32-wasi/stdio.h" ]]; then + echo "[compiler-rt] ERROR: base sysroot headers missing at '$BASE_SYSROOT'" >&2 + exit 1 +fi + +CMAKE="${CMAKE:-cmake}" +if ! command -v "$CMAKE" &>/dev/null; then + echo "[compiler-rt] ERROR: cmake not found on PATH" >&2 + exit 1 +fi + +BUILD_DIR="$APPS_BUILD/compiler-rt-build" +INSTALL_DIR="$APPS_BUILD/compiler-rt-install" + +echo "[compiler-rt] using CLANG = $CLANG" +echo "[compiler-rt] using CLANGXX = $CLANGXX" +echo "[compiler-rt] using AR = $AR" +echo "[compiler-rt] using NM = $NM" +echo "[compiler-rt] using RANLIB = $RANLIB" +echo "[compiler-rt] LIND_WASM_ROOT = $LIND_WASM_ROOT" +echo "[compiler-rt] base sysroot = $BASE_SYSROOT" +echo "[compiler-rt] build dir = $BUILD_DIR" +echo "[compiler-rt] install dir = $INSTALL_DIR" +echo + +mkdir -p "$BUILD_DIR" + +# ---------------------------------------------------------------------- +# 2) Generate CMake toolchain file for wasm32-wasi cross-compilation +# ---------------------------------------------------------------------- +TOOLCHAIN_FILE="$BUILD_DIR/Toolchain-WASI.cmake" +sed -e "s|@CLANG@|$CLANG|g" \ + -e "s|@CLANGXX@|$CLANGXX|g" \ + -e "s|@AR@|$AR|g" \ + -e "s|@NM@|$NM|g" \ + -e "s|@RANLIB@|$RANLIB|g" \ + -e "s|@BASE_SYSROOT@|$BASE_SYSROOT|g" \ + "$SCRIPT_DIR/Toolchain-WASI.cmake.in" > "$TOOLCHAIN_FILE" + +echo "[compiler-rt] generated toolchain file: $TOOLCHAIN_FILE" + +# ---------------------------------------------------------------------- +# 3) Configure with CMake +# ---------------------------------------------------------------------- +echo "[compiler-rt] configuring…" + +"$CMAKE" -B "$BUILD_DIR" -S "$LLVM_SRC/compiler-rt" \ + -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" \ + -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \ + -DCMAKE_C_COMPILER_WORKS=1 \ + -DCMAKE_CXX_COMPILER_WORKS=1 \ + -DCOMPILER_RT_BUILD_BUILTINS=ON \ + -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \ + -DCOMPILER_RT_INCLUDE_TESTS=OFF \ + -DCOMPILER_RT_BUILD_SANITIZERS=OFF \ + -DCOMPILER_RT_BUILD_XRAY=OFF \ + -DCOMPILER_RT_BUILD_LIBFUZZER=OFF \ + -DCOMPILER_RT_BUILD_PROFILE=OFF \ + -DCOMPILER_RT_BUILD_MEMPROF=OFF + +# ---------------------------------------------------------------------- +# 4) Build and install +# ---------------------------------------------------------------------- +echo "[compiler-rt] building…" +"$CMAKE" --build "$BUILD_DIR" --target install -j"$JOBS" + +# ---------------------------------------------------------------------- +# 5) Copy libraries to sysroot overlay +# ---------------------------------------------------------------------- +echo "[compiler-rt] installing to sysroot overlay…" + +mkdir -p "$APPS_OVERLAY/usr/lib/wasm32-wasi" + +find "$INSTALL_DIR" -type f \( -name 'libclang_rt.builtins*.a' -o -name 'libcompiler_rt*.a' \) \ + -exec cp -v {} "$APPS_OVERLAY/usr/lib/wasm32-wasi/" \; + +echo +echo "[compiler-rt] build complete. Outputs:" +echo " libs: $APPS_OVERLAY/usr/lib/wasm32-wasi/" +ls -lh "$APPS_OVERLAY/usr/lib/wasm32-wasi/" | grep -E 'clang_rt|compiler_rt' || true diff --git a/llvm-project/fix_std_maxmin.h b/llvm-project/fix_std_maxmin.h new file mode 100644 index 0000000000..1cbea48f69 --- /dev/null +++ b/llvm-project/fix_std_maxmin.h @@ -0,0 +1,45 @@ +#ifndef FIX_STD_MAXMIN_H +#define FIX_STD_MAXMIN_H + +// Workaround for wasm32 where size_type (unsigned int) and size_t +// (unsigned long) are different C++ types but the same width (32-bit). +// std::min/std::max template deduction fails because it can't unify +// the two types into a single _Tp. This doesn't happen on x86_64 +// where size_t = unsigned long (64-bit) and size_type = size_t. +// +// Fix: provide mixed-type overloads that use std::common_type to +// resolve the return type. The !is_same SFINAE guard ensures +// these only activate when the standard single-type overloads can't +// deduce, avoiding ambiguity. +// +// Force-included via -include before all other headers. + +#include + +namespace std { + +template ::value && is_arithmetic::value && + !is_same::value>::type> +inline constexpr typename common_type::type +min(const T& a, const U& b) { + using CT = typename common_type::type; + return static_cast(b) < static_cast(a) + ? static_cast(b) : static_cast(a); +} + +template ::value && is_arithmetic::value && + !is_same::value>::type> +inline constexpr typename common_type::type +max(const T& a, const U& b) { + using CT = typename common_type::type; + return static_cast(a) < static_cast(b) + ? static_cast(b) : static_cast(a); +} + +} // namespace std + +#endif // FIX_STD_MAXMIN_H