diff --git a/.ci/cmake_ci_build.sh b/.ci/cmake_ci_build.sh new file mode 100755 index 000000000..db1efef91 --- /dev/null +++ b/.ci/cmake_ci_build.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +set -euo pipefail + +if [ "$#" -lt 1 ]; then + echo "usage: $0 [--] [cmake configure args...]" >&2 + exit 2 +fi + +BUILD_DIR=$1 +shift + +if [ "${1:-}" = "--" ]; then + shift +fi + +SOURCE_DIR=${DTVM_CI_SOURCE_DIR:-.} +USE_NINJA=${DTVM_CI_USE_NINJA:-0} +USE_SCCACHE=${DTVM_CI_USE_SCCACHE:-0} +DRY_RUN=${DTVM_CI_DRY_RUN:-0} +BUILD_JOBS=${DTVM_CI_BUILD_JOBS:-16} + +GENERATOR_ARGS=() +GENERATOR_NAME=default +if [ "$USE_NINJA" = "1" ]; then + GENERATOR_ARGS=(-G Ninja) + GENERATOR_NAME=Ninja +fi + +LAUNCHER_ARGS=() +LAUNCHER_NAME=none +if [ "$USE_SCCACHE" = "1" ]; then + LAUNCHER_ARGS=( + -DCMAKE_C_COMPILER_LAUNCHER=sccache + -DCMAKE_CXX_COMPILER_LAUNCHER=sccache + ) + LAUNCHER_NAME=sccache +fi + +CONFIGURE_CMD=( + cmake + -S "$SOURCE_DIR" + -B "$BUILD_DIR" + "${GENERATOR_ARGS[@]}" + "${LAUNCHER_ARGS[@]}" + "$@" +) +BUILD_CMD=(cmake --build "$BUILD_DIR" -j "$BUILD_JOBS") + +echo "DTVM CI CMake source dir: ${SOURCE_DIR}" +echo "DTVM CI CMake generator: ${GENERATOR_NAME}" +echo "DTVM CI CMake compiler launcher: ${LAUNCHER_NAME}" + +printf '+' +printf ' %q' "${CONFIGURE_CMD[@]}" +printf '\n' + +if [ "$DRY_RUN" = "1" ]; then + printf '+' + printf ' %q' "${BUILD_CMD[@]}" + printf '\n' + exit 0 +fi + +if [ "$USE_SCCACHE" = "1" ] && ! command -v sccache >/dev/null 2>&1; then + echo "DTVM_CI_USE_SCCACHE=1 but sccache is not in PATH" >&2 + exit 1 +fi + +"${CONFIGURE_CMD[@]}" + +printf '+' +printf ' %q' "${BUILD_CMD[@]}" +printf '\n' +"${BUILD_CMD[@]}" diff --git a/.ci/print_sccache_stats.py b/.ci/print_sccache_stats.py new file mode 100644 index 000000000..d10841a92 --- /dev/null +++ b/.ci/print_sccache_stats.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + +import argparse +import json +import numbers +import shutil +import subprocess +import sys + + +def sum_numbers(value): + if isinstance(value, bool): + return 0 + if isinstance(value, numbers.Number): + return int(value) + if isinstance(value, dict): + return sum(sum_numbers(item) for item in value.values()) + if isinstance(value, list): + return sum(sum_numbers(item) for item in value) + return 0 + + +def count_bucket(stats, name): + value = stats.get(name, {}) + if isinstance(value, dict) and "counts" in value: + value = value["counts"] + return sum_numbers(value) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--require-requests", action="store_true") + parser.add_argument("--verbose", action="store_true") + args = parser.parse_args() + + if shutil.which("sccache") is None: + print("sccache is not in PATH", file=sys.stderr) + return 1 if args.require_requests else 0 + + if args.verbose: + subprocess.run(["sccache", "--show-stats"], check=False) + + stats_json = subprocess.check_output( + ["sccache", "--show-stats", "--stats-format=json"], + text=True, + ) + data = json.loads(stats_json) + stats = data.get("stats", {}) + compile_requests = int(stats.get("compile_requests") or 0) + cache_hits = count_bucket(stats, "cache_hits") + cache_misses = count_bucket(stats, "cache_misses") + + print(f"sccache compile_requests={compile_requests}") + print(f"sccache aggregate_cache_hits={cache_hits}") + print(f"sccache aggregate_cache_misses={cache_misses}") + + if args.require_requests and compile_requests <= 0: + print("sccache did not observe compiler requests", file=sys.stderr) + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.ci/run_test_suite.sh b/.ci/run_test_suite.sh index c0b45e430..dd253b0c4 100644 --- a/.ci/run_test_suite.sh +++ b/.ci/run_test_suite.sh @@ -21,6 +21,19 @@ set -e +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) + +print_sccache_stats() { + if [ "${DTVM_CI_USE_SCCACHE:-0}" = "1" ] && command -v sccache >/dev/null 2>&1; then + if [ -n "${1:-}" ]; then + echo "sccache stats checkpoint: $1" + fi + python3 "$SCRIPT_DIR/print_sccache_stats.py" || true + fi +} + +trap print_sccache_stats EXIT + # Convert INPUT_FORMAT to lowercase for case-insensitive comparison INPUT_FORMAT=${INPUT_FORMAT,,} @@ -114,14 +127,28 @@ export PATH=$PATH:$PWD/build CMAKE_OPTIONS_ORIGIN="$CMAKE_OPTIONS" if [[ ${INPUT_FORMAT} == "evm" ]]; then - ./tools/easm2bytecode.sh ./tests/evm_asm ./tests/evm_asm - ./tools/solc_batch_compile.sh + if [ "${DTVM_CI_DRY_RUN:-0}" = "1" ]; then + echo "+ ./tools/easm2bytecode.sh ./tests/evm_asm ./tests/evm_asm" + echo "+ ./tools/solc_batch_compile.sh" + else + ./tools/easm2bytecode.sh ./tests/evm_asm ./tests/evm_asm + ./tools/solc_batch_compile.sh + fi fi for STACK_TYPE in ${STACK_TYPES[@]}; do - rm -rf build - cmake -S . -B build $CMAKE_OPTIONS_ORIGIN $STACK_TYPE - cmake --build build -j 16 + if [ "${DTVM_CI_DRY_RUN:-0}" = "1" ]; then + echo "+ rm -rf build" + else + rm -rf build + fi + "$SCRIPT_DIR/cmake_ci_build.sh" build -- $CMAKE_OPTIONS_ORIGIN $STACK_TYPE + if [[ $TestSuite == "benchmarksuite" ]]; then + print_sccache_stats "after DTVM build" + fi + if [ "${DTVM_CI_DRY_RUN:-0}" = "1" ]; then + continue + fi case $TestSuite in "microsuite") @@ -203,8 +230,7 @@ for STACK_TYPE in ${STACK_TYPES[@]}; do fi fi cd "$EVMONE_DIR" - cmake -S . -B build -DEVMONE_TESTING=ON -DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TARGET" - cmake --build build --parallel 16 + "$SCRIPT_DIR/cmake_ci_build.sh" build -- -DEVMONE_TESTING=ON -DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TARGET" EVMONE_STATETEST_BIN="$PWD/build/bin/evmone-statetest" cd "$WORKSPACE_ROOT" @@ -305,9 +331,9 @@ for STACK_TYPE in ${STACK_TYPES[@]}; do cp ../tools/check_performance_regression.py ./ if [ ! -f "build/bin/evmone-bench" ]; then - cmake -S . -B build -DEVMONE_TESTING=ON -DCMAKE_BUILD_TYPE=Release - cmake --build build --parallel -j 16 + "$SCRIPT_DIR/cmake_ci_build.sh" build -- -DEVMONE_TESTING=ON -DCMAKE_BUILD_TYPE=Release fi + print_sccache_stats "before benchmarks" BASELINE_CACHE=${BENCHMARK_BASELINE_CACHE:-} diff --git a/.github/workflows/dtvm_evm_test_x86.yml b/.github/workflows/dtvm_evm_test_x86.yml index 70d8e8d71..b2815fbd0 100644 --- a/.github/workflows/dtvm_evm_test_x86.yml +++ b/.github/workflows/dtvm_evm_test_x86.yml @@ -18,11 +18,16 @@ permissions: # Shared FetchContent cache root for all container jobs. The hook in # CMakeLists.txt (commit 96707a2 lines 8-18) picks this up as the base -# dir for FetchContent populations. Each container job adds an -# `actions/cache` step keyed on `hashFiles('third_party/AddDeps.cmake')` -# to persist this dir across CI runs. +# dir for FetchContent populations. The cache key includes the generator +# namespace because CMake writes generator-specific FetchContent subbuilds. env: FETCHCONTENT_BASE_DIR: /github/home/.fetchcontent + SCCACHE_GHA_ENABLED: "true" + SCCACHE_GHA_VERSION: dtvm-ci-sccache-v2-gcc11-llvm15-ninja + SCCACHE_BASEDIRS: ${{ github.workspace }} + SCCACHE_IDLE_TIMEOUT: "0" + DTVM_CI_USE_NINJA: "1" + DTVM_CI_USE_SCCACHE: "1" jobs: build_test_evm_interpreter_x86_ctest: @@ -39,10 +44,12 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Clone asmjit run: | git clone https://github.com/asmjit/asmjit.git @@ -78,10 +85,12 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Build and Test run: | echo "current home is $HOME" @@ -114,10 +123,12 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Build and Test run: | echo "current home is $HOME" @@ -161,10 +172,12 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Build and Test run: | echo "current home is $HOME" @@ -197,10 +210,12 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Build and Test run: | echo "current home is $HOME" @@ -234,10 +249,12 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Build and Test run: | echo "current home is $HOME" @@ -271,10 +288,19 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} + - name: Cache Hunter + uses: actions/cache@v4 + with: + path: /github/home/.hunter + key: ${{ runner.os }}-hunter-evmone-v1-${{ hashFiles('.ci/run_test_suite.sh', '.github/workflows/dtvm_evm_test_x86.yml') }} + restore-keys: | + ${{ runner.os }}-hunter-evmone-v1- - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Build and Test env: LLVM_SYS_150_PREFIX: /opt/llvm15 @@ -306,7 +332,7 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Cache Hunter uses: actions/cache@v4 with: @@ -317,6 +343,8 @@ jobs: - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Build and Test env: LLVM_SYS_150_PREFIX: /opt/llvm15 @@ -352,10 +380,12 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Build and Test run: | echo "current home is $HOME" @@ -399,7 +429,15 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} + + - name: Cache Hunter + uses: actions/cache@v4 + with: + path: /github/home/.hunter + key: ${{ runner.os }}-hunter-evmone-v1-${{ hashFiles('.ci/run_test_suite.sh', '.github/workflows/dtvm_evm_test_x86.yml') }} + restore-keys: | + ${{ runner.os }}-hunter-evmone-v1- - name: Setup git safe directory run: | @@ -409,6 +447,8 @@ jobs: - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 # Cache the BUILT baseline libdtvmapi.so (a deterministic function of # base.sha), not the bench output JSON (a function of source + host + @@ -435,6 +475,9 @@ jobs: git checkout ${{ github.base_ref }} cmake -S . -B build \ + -G Ninja \ + -DCMAKE_C_COMPILER_LAUNCHER=sccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=sccache \ -DCMAKE_BUILD_TYPE=Release \ -DZEN_ENABLE_SINGLEPASS_JIT=OFF \ -DZEN_ENABLE_MULTIPASS_JIT=ON \ diff --git a/.github/workflows/dtvm_wasm_test_x86.yml b/.github/workflows/dtvm_wasm_test_x86.yml index 0eda995d8..b27c5f700 100644 --- a/.github/workflows/dtvm_wasm_test_x86.yml +++ b/.github/workflows/dtvm_wasm_test_x86.yml @@ -18,11 +18,16 @@ permissions: # Shared FetchContent cache root for all container jobs. The hook in # CMakeLists.txt (commit 96707a2 lines 8-18) picks this up as the base -# dir for FetchContent populations. Each container job adds an -# `actions/cache` step keyed on `hashFiles('third_party/AddDeps.cmake')` -# to persist this dir across CI runs. +# dir for FetchContent populations. The cache key includes the generator +# namespace because CMake writes generator-specific FetchContent subbuilds. env: FETCHCONTENT_BASE_DIR: /github/home/.fetchcontent + SCCACHE_GHA_ENABLED: "true" + SCCACHE_GHA_VERSION: dtvm-ci-sccache-v2-gcc11-llvm15-ninja + SCCACHE_BASEDIRS: ${{ github.workspace }} + SCCACHE_IDLE_TIMEOUT: "0" + DTVM_CI_USE_NINJA: "1" + DTVM_CI_USE_SCCACHE: "1" jobs: build_test_interp_on_x86: @@ -39,10 +44,12 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Test Git clone run: | git clone https://github.com/asmjit/asmjit.git @@ -86,10 +93,12 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Test Git clone run: | git clone https://github.com/asmjit/asmjit.git @@ -133,10 +142,12 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Test Git clone run: | git clone https://github.com/asmjit/asmjit.git @@ -180,10 +191,12 @@ jobs: uses: actions/cache@v4 with: path: /github/home/.fetchcontent - key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + key: ${{ runner.os }}-fc-${{ github.workflow }}-ninja-v2-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check + - name: Setup sccache + uses: mozilla-actions/sccache-action@v0.0.10 - name: Test Git clone run: | git clone https://github.com/asmjit/asmjit.git @@ -202,7 +215,16 @@ jobs: git apply ../spec.patch cd $CUR_PROJECT - cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DZEN_ENABLE_DWASM=ON -DZEN_ENABLE_SINGLEPASS_JIT=ON -DZEN_ENABLE_MULTIPASS_JIT=ON -DZEN_ENABLE_CHECKED_ARITHMETIC=ON -DZEN_ENABLE_CPU_EXCEPTION=OFF -DZEN_ENABLE_EVMABI_TEST=ON -DZEN_ENABLE_BUILTIN_LIBC=OFF -DZEN_ENABLE_BUILTIN_ENV=OFF - cmake --build build -j7 + DTVM_CI_BUILD_JOBS=7 .ci/cmake_ci_build.sh build -- \ + -DCMAKE_BUILD_TYPE=Debug \ + -DZEN_ENABLE_DWASM=ON \ + -DZEN_ENABLE_SINGLEPASS_JIT=ON \ + -DZEN_ENABLE_MULTIPASS_JIT=ON \ + -DZEN_ENABLE_CHECKED_ARITHMETIC=ON \ + -DZEN_ENABLE_CPU_EXCEPTION=OFF \ + -DZEN_ENABLE_EVMABI_TEST=ON \ + -DZEN_ENABLE_BUILTIN_LIBC=OFF \ + -DZEN_ENABLE_BUILTIN_ENV=OFF + python3 .ci/print_sccache_stats.py || true # use dtvm to test evm abi wasm files # ./build/dtvm -m 2 -f call counter.wasm