Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions .github/workflows/slang-mode-benchmark.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
name: Slang Pool-Mode Benchmark

# Wall-clock comparison of the two compilation-dispatch modes (in-process
# rayon threads vs SOLX_SUBPROCESS worker pool) across operating systems.
# Opt-in: label a PR with ci:mode-bench, or dispatch manually.

on:
pull_request:
types: [opened, synchronize, reopened, labeled]
workflow_dispatch:

permissions:
contents: read
packages: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:

label-check:
# Checks the PR's current label set, not the triggering action (see
# slang-tests.yaml for the rationale).
if: >-
github.event_name == 'workflow_dispatch'
|| (contains(github.event.pull_request.labels.*.name, 'ci:mode-bench')
&& !github.event.pull_request.head.repo.fork)
runs-on: ubuntu-24.04
steps:
- run: 'true'

benchmark:
needs: label-check
permissions:
contents: read
packages: read
env:
CARGO_TARGET_DIR: target-slang
CARGO_INCREMENTAL: "0"
strategy:
fail-fast: false
matrix:
include:
- name: "MacOS x86"
runner: macos-15-intel
- name: "MacOS arm64"
runner: macos-15
- name: "Linux x86 gnu"
runner: ubuntu-24.04
image: ghcr.io/nomicfoundation/solx-ci-runner@sha256:cd5a37f2630fdf1898ddb2ca11f8c2ecc4d572c60011fe923b27b1625f92ba15
target: "x86_64-unknown-linux-gnu"
# rustflags: aarch64-linux links with strict single-pass bfd
# (x86-64 rust uses order-tolerant rust-lld), which cannot
# resolve the crate-graph archive order rustc emits for the
# lld/LLVM static libs. Link with the CI image's lld instead.
# Durable fix: #[link] attributes in the inkwell fork.
- name: "Linux ARM64 gnu"
runner: ubuntu-24.04-arm
image: ghcr.io/nomicfoundation/solx-ci-runner@sha256:cd5a37f2630fdf1898ddb2ca11f8c2ecc4d572c60011fe923b27b1625f92ba15
target: "aarch64-unknown-linux-gnu"
rustflags: "-C link-arg=-fuse-ld=lld -C link-arg=-B/usr/lib/llvm-21/bin"
# Windows is parked: the slang release build has never linked on
# MinGW. Two layers fixed here (cc-rs must use gcc to match the
# gcc-built LLVM tree; MLIR TypeID data symbols), the next one
# (mass undefined llvm::detail::UniqueFunctionBase/function_ref
# template instantiations) is a real porting effort. Re-add the
# entry once slang-release links on Windows:
# - name: "Windows"
# runner: windows-2025
# target: "x86_64-pc-windows-gnu"
runs-on: ${{ matrix.runner }}
container:
image: ${{ matrix.image || '' }}
name: ${{ matrix.name }}
# The LLVM "cache" is a ccache (timestamp-keyed), so every run
# recompiles LLVM with warm objects; Windows additionally cold-builds
# solx in release and needs the most headroom.
timeout-minutes: 480
steps:

- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
submodules: true
persist-credentials: false

- name: Build toolchain
uses: ./.github/actions/build-toolchain
with:
solc: 'false'

# See slang-tests.yaml (rust-lang/rust-bindgen#1760).
- name: Set bindgen target for MinGW
if: runner.os == 'Windows'
shell: bash
run: echo "BINDGEN_EXTRA_CLANG_ARGS=--target=x86_64-w64-windows-gnu" >> "${GITHUB_ENV}"

# The cargo cache must be keyed on the solx-llvm pin: Cargo.lock does
# not change when the submodule moves, and restored build-script
# outputs from an older LLVM tree poison the link (undefined lldC
# symbols).
- name: Compute solx-llvm pin
shell: bash
run: echo "LLVM_PIN=$(git rev-parse HEAD:solx-llvm)" >> "${GITHUB_ENV}"

- name: Cache cargo artifacts
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
prefix-key: slang-mode-bench-v2-${{ env.LLVM_PIN }}
workspaces: ". -> target-slang"
cache-on-failure: true

- name: Install build target
if: matrix.target != ''
uses: ./.github/actions/setup-rust
with:
target: ${{ matrix.target }}

# The runner context is not available in a workflow step's `shell:`
# (composite actions only), so the Windows msys2 build is its own step.
- name: Build solx (slang, release)
if: runner.os != 'Windows'
shell: bash
env:
RUSTFLAGS: ${{ matrix.rustflags || '' }}
run: |
${SFW_PREFIX:-} cargo fetch --locked
${SFW_PREFIX:-} cargo build-slang --release \
${{ matrix.target && format('--target {0}', matrix.target) || '' }}

# CC/CXX pinned to gcc: cc-rs otherwise picks msys2 clang for the
# mlir-sys/solx-mlir C++ glue, whose MLIR TypeID data symbols don't
# resolve against the gcc-built LLVM tree (undefined .refptr at link).
- name: Build solx (slang, release, Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
env:
CC: gcc
CXX: g++
run: |
${SFW_PREFIX:-} cargo fetch --locked
${SFW_PREFIX:-} cargo build-slang --release \
${{ matrix.target && format('--target {0}', matrix.target) || '' }}

- name: Run benchmark
shell: bash
run: |
PY=$(command -v python3 || command -v python)
EXE=${{ runner.os == 'Windows' && '.exe' || '' }}
SOLX=target-slang/${{ matrix.target && format('{0}/', matrix.target) || '' }}release/solx${EXE}
"${PY}" tests/benchmark/pool-modes/gen_corpus.py "${RUNNER_TEMP}/pool-modes-corpus"
echo "cores: $("${PY}" -c 'import os; print(os.cpu_count())')"
for corpus in tiny-clean tiny-overflow big-clean big-overflow mixed; do
"${PY}" tests/benchmark/pool-modes/bench.py \
--solx "${SOLX}" --corpus "${RUNNER_TEMP}/pool-modes-corpus/${corpus}" --pairs 5
done
23 changes: 23 additions & 0 deletions solx-codegen-evm/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//!
//! The build script for `solx-codegen-evm`.
//!

fn main() {
println!("cargo:rerun-if-env-changed=LLVM_SYS_211_PREFIX");

let prefix = std::env::var("LLVM_SYS_211_PREFIX")
.expect("LLVM_SYS_211_PREFIX must be set — point it to the solx-llvm build output");

let lib_path = std::path::PathBuf::from(&prefix).join("lib");
println!("cargo:rustc-link-search=native={}", lib_path.display());

// LLD C API — LLVMAssembleEVM/LLVMLinkEVM and friends, referenced by
// inkwell's memory_buffer. The directives must come from this crate
// (the one that owns the inkwell dependency): strict single-pass
// linkers (bfd on aarch64-linux and mingw) resolve archives in
// command-line order, and directives emitted by an unrelated crate
// (solx-mlir) can land before the inkwell rlib that needs them.
println!("cargo:rustc-link-lib=static=lldC");
println!("cargo:rustc-link-lib=static=lldCommon");
println!("cargo:rustc-link-lib=static=lldELF");
}
40 changes: 33 additions & 7 deletions solx-codegen-evm/src/codegen/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,18 +171,44 @@ impl<'ctx> Context<'ctx> {
) -> anyhow::Result<EVMBuild> {
let contract_path = self.module.get_name().to_str().expect("Always valid");

let diagnostics = crate::diagnostics::Capture::install(self.llvm);

// Per-unit codegen parameters ride the module as flags. The presence
// guards keep the first pass's values through the size-fallback
// recursion, whose settings do not carry them.
if let Some(size) = self.optimizer.settings().spill_area_size()
&& self.module().get_flag("evm-stack-region-size").is_none()
{
self.module().add_basic_value_flag(
"evm-stack-region-offset",
inkwell::module::FlagBehavior::Error,
self.llvm.i64_type().const_int(self.memory_guard, false),
);
self.module().add_basic_value_flag(
"evm-stack-region-size",
inkwell::module::FlagBehavior::Error,
self.llvm.i64_type().const_int(size, false),
);
}
if let Some(size) = self.optimizer.settings().metadata_size
&& self.module().get_flag("evm-metadata-size").is_none()
{
self.module().add_basic_value_flag(
"evm-metadata-size",
inkwell::module::FlagBehavior::Error,
self.llvm.i64_type().const_int(size, false),
);
}

let run_init_verify = profiler.start_evm_translation_unit(
contract_path,
self.code_segment,
"InitVerify",
self.optimizer.settings(),
);
let spill_area_size = self.optimizer.settings().spill_area_size();
let target_machine = TargetMachine::new(
self.optimizer.settings(),
self.llvm_options.as_slice(),
spill_area_size.map(|size| (self.memory_guard, size)),
)?;
let target_machine =
TargetMachine::new(self.optimizer.settings(), self.llvm_options.as_slice())?;
target_machine.set_target_data(self.module());
target_machine.set_asm_verbosity(true);

Expand Down Expand Up @@ -270,6 +296,7 @@ impl<'ctx> Context<'ctx> {
inkwell::targets::FileType::Assembly,
)
.map_err(|error| anyhow::anyhow!("assembly emitting: {error}"))?;
diagnostics.check(is_size_fallback)?;

if let Some(output_config) = self.output_config.as_ref() {
let assembly_text = String::from_utf8_lossy(assembly_buffer.as_slice());
Expand Down Expand Up @@ -317,6 +344,7 @@ impl<'ctx> Context<'ctx> {
})?;
(bytecode_buffer, None)
};
diagnostics.check(is_size_fallback)?;
run_emit_bytecode.borrow_mut().finish();

let immutables = match self.code_segment {
Expand All @@ -333,8 +361,6 @@ impl<'ctx> Context<'ctx> {
let bytecode_size = bytecode_buffer.as_slice().len();
if bytecode_size > bytecode_size_limit {
if needs_size_fallback {
crate::codegen::IS_SIZE_FALLBACK
.store(true, std::sync::atomic::Ordering::Relaxed);
let mut size_fallback_settings = OptimizerSettings::size();
size_fallback_settings.metadata_size = self.optimizer.settings().metadata_size;
self.optimizer = Optimizer::new(size_fallback_settings);
Expand Down
5 changes: 0 additions & 5 deletions solx-codegen-evm/src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ pub mod profiler;
pub mod warning;

use std::collections::BTreeMap;
use std::sync::atomic::AtomicBool;

use self::context::Context;

Expand All @@ -37,10 +36,6 @@ pub fn append_metadata(
.map_err(|error| anyhow::anyhow!("bytecode metadata appending error: {error}"))
}

/// Whether the size fallback is activated during the compilation.
/// Only set once, as we're only compiling one traslation unit in a process.
pub static IS_SIZE_FALLBACK: AtomicBool = AtomicBool::new(false);

///
/// Assembles the main buffer and its dependencies from `bytecode_buffers`.
///
Expand Down
Loading
Loading