Skip to content
Merged
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
45 changes: 44 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ jobs:
uses: actions/checkout@v4

- name: Install build tools
run: sudo apt-get update && sudo apt-get install -y build-essential
# wabt provides wasm-validate, which we use to confirm every
# example we claim works compiles to a spec-conformant module.
run: sudo apt-get update && sudo apt-get install -y build-essential wabt

- name: Build
run: make -C compiler
Expand All @@ -37,6 +39,35 @@ jobs:
- name: Bench
run: make -C compiler bench

- name: Validate emitted wasm against examples
# Compile every example marked "yes" in examples/README.md and
# confirm wasm-validate accepts the output. Catches codegen bugs
# that pass our byte-level unit tests but produce structurally
# invalid modules (e.g. wrong locals declaration, missing END).
run: |
set -euxo pipefail
mkdir -p /tmp/wasm-out
for src in examples/counter-mvp.cv; do
out=/tmp/wasm-out/$(basename "$src" .cv).wasm
./compiler/build/cleavec --emit-wasm "$src" -o "$out"
wasm-validate "$out"
done

- name: Determinism check (compile twice, diff)
# Same source through the full pipeline must produce
# byte-identical wasm across two runs. Guards against
# nondeterminism leaking in from HashMap iteration order,
# pointer addresses, build timestamps, etc.
run: |
set -euxo pipefail
./compiler/build/cleavec --emit-wasm examples/counter-mvp.cv -o /tmp/det-a.wasm
./compiler/build/cleavec --emit-wasm examples/counter-mvp.cv -o /tmp/det-b.wasm
if ! cmp -s /tmp/det-a.wasm /tmp/det-b.wasm; then
echo "codegen is not deterministic: two compilations differ" >&2
cmp /tmp/det-a.wasm /tmp/det-b.wasm || true
exit 1
fi

runtime:
name: Runtime · Build · Test
runs-on: ubuntu-latest
Expand All @@ -55,7 +86,13 @@ jobs:
run: make -C compiler

- name: Install Rust toolchain
# `clippy` component is added so the lint step below has access
# to it. `dtolnay/rust-toolchain@stable` follows whatever the
# latest stable rustc is; MSRV is pinned in runtime/Cargo.toml
# (rust-version = "1.95") so a downgrade attempt fails fast.
uses: dtolnay/rust-toolchain@stable
with:
components: clippy

- name: Cache cargo registry + build
uses: actions/cache@v4
Expand All @@ -68,6 +105,12 @@ jobs:
restore-keys: |
${{ runner.os }}-cargo-

- name: Lint runtime (clippy, deny warnings)
# Run lint before build to fail fast on style / correctness
# issues before paying the full Wasmtime + Cranelift compile.
working-directory: runtime
run: cargo clippy --all-targets --release -- -D warnings

- name: Build runtime
working-directory: runtime
run: cargo build --release
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ Prose references a version as `v0.X.Y`; headings stay bare `[0.X.Y]`.

### Added

- CI gains four lower-cost gates:
- **`wasm-validate` on emitted modules.** The compiler job installs wabt, compiles every example marked "yes" in `examples/README.md`, and runs `wasm-validate` on the output. Catches structurally invalid modules that pass our byte-level codegen unit tests.
- **Determinism check.** The compiler job compiles `examples/counter-mvp.cv` twice and `cmp -s` the outputs; non-zero exit on any difference. Guards against nondeterminism from HashMap iteration order, pointer addresses, build timestamps.
- **`cargo clippy --all-targets --release -- -D warnings`** as the first runtime step. Runs before the full Wasmtime + Cranelift compile so style and correctness lints fail fast.
- **MSRV pin** in `runtime/Cargo.toml`: `rust-version = "1.95"`. Locks the floor after the v0.3 bump (revm 40 + ruint 1.18 need rustc >= 1.91).
- One existing clippy lint fixed inline (`runtime/src/main.rs::hex_decode` now uses `s.len().is_multiple_of(2)` instead of `s.len() % 2 != 0`).
- Codegen for `let` bindings (`compiler/src/codegen.c`). The v0.3 codegen rejected `let` with a diagnostic; this lifts the restriction. The fn-body emitter pre-scans the top-level block to count let statements, allocates one WASM local per binding at index `n_params + let_index`, emits a single locals declaration group of N i64s, and lowers each `let name = expr` to `<expr>; local.set <idx>`. Reads of let-bound names go through the existing `local.get` path; assignments to let names route to `local.set`. `find_local` now scans most-recent-first so shadowing matches user expectation. Closes #44.
- 7 codegen tests (`codegen_test.c`): locals declaration byte sequence, `local.set 0` / `local.get 0` opcodes, post-param local index, three-let single-group declaration, let referencing earlier let, reassignment lowering, regression guard that fn with no lets still emits a zero local-groups byte.
- `codegen_let_heavy` bench case: 8 let bindings + arithmetic chain.
Expand Down
5 changes: 5 additions & 0 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
name = "cleave-runtime"
version = "0.3.0"
edition = "2021"
# MSRV. We bumped from 1.87 -> 1.95 mid-v0.3 because revm 40 + ruint 1.18
# require rustc >= 1.91. Lock the floor here so CI fails fast if anyone
# tries to downgrade. Older stable releases will not build the runtime
# crate; that's a deliberate consequence of pinning to the modern REVM.
rust-version = "1.95"
license = "Apache-2.0"
description = "Wasmtime-backed execution engine for Cleave-compiled WASM modules."
repository = "https://github.com/cleave-lang/cleave"
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ fn load_bytecode(spec: &str) -> Result<Vec<u8>> {
}

fn hex_decode(s: &str) -> Result<Vec<u8>> {
if s.len() % 2 != 0 {
if !s.len().is_multiple_of(2) {
return Err(anyhow!("hex string has odd length"));
}
let mut out = Vec::with_capacity(s.len() / 2);
Expand Down
Loading