Skip to content

Changes to allow different toolchains for host and guest - #1962

Open
simongdavies wants to merge 2 commits into
mainfrom
simongdavies-split-rust-toolchains
Open

simongdavies wants to merge 2 commits into
mainfrom
simongdavies-split-rust-toolchains

Conversation

@simongdavies

Copy link
Copy Markdown
Member

Enables different toolchains for host and guest.

Host - 1.99.1
Guest - 1.94.1

No change to MSRV.

Minor changes to resolve warnings from move to 1.99

Host move to 1.99.1
Guest fixed at 1.94.1 until hyperlight target is upstreamed

Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 15:46
@simongdavies simongdavies added the kind/dependencies For PRs that update dependencies or related components label Oct 20, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The guest debug-information regression remains unresolved, and cross-platform CI, Nix, and container changes need build validation.

1 open issue
What changed in this PR

Separates Hyperlight’s host and guest Rust toolchains without changing the minimum supported Rust version.

Changes:

  • Pins host builds to Rust 1.99.1 and guest builds to 1.94.1.
  • Updates CI, Nix, containers, and contributor documentation for separate toolchains.
  • Resolves compiler and lint warnings.
File Description
src/​tests/​rust_guests/​rust-toolchain.toml Pins Rust guest toolchain.
src/​hyperlight_testing/​src/​tracing_subscriber.rs Removes redundant formatting borrows.
src/​hyperlight_host/​tests/​sandbox_host_tests.rs Removes obsolete import.
src/​hyperlight_host/​build.rs Removes redundant formatting borrow.
src/​hyperlight_guest_capi/​rust-toolchain.toml Pins C API guest toolchain.
src/​hyperlight_component_util/​src/​util.rs Removes redundant formatting borrow.
src/​hyperlight_component_util/​src/​rtypes.rs Iterates map keys directly.
src/​hyperlight_component_util/​src/​emit.rs Iterates map values directly.
rust-toolchain.toml Pins host toolchain and components.
flake.nix Supports separate toolchain selection.
CONTRIBUTING.md Documents guest toolchain selection.
.github/​workflows/​ValidatePullRequest.yml Installs both toolchains.
.github/​workflows/​RustNightly.yml Updates setup and guest cache keys.
.github/​workflows/​RegenSnapshotGoldens.yml Installs both toolchains.
.github/​workflows/​PrimeCaches.yml Installs both toolchains.
.github/​workflows/​dep_update_guest_locks.yml Installs both toolchains.
.github/​workflows/​dep_run_examples.yml Installs both toolchains.
.github/​workflows/​dep_miri.yml Uses file-based toolchain setup.
.github/​workflows/​dep_fuzzing.yml Installs both toolchains.
.github/​workflows/​dep_code_checks.yml Updates setup and guest cache keys.
.github/​workflows/​dep_build_test.yml Installs both toolchains.
.github/​workflows/​dep_build_guests.yml Supports guest pins and older commits.
.github/​workflows/​dep_benchmarks.yml Installs both toolchains.
.github/​workflows/​CreateRelease.yml Installs both toolchains.
.github/​workflows/​CreateDevcontainerImage.yml Reads and passes both toolchain versions.
.github/​workflows/​Coverage.yml Installs both toolchains.
.github/​workflows/​copilot-setup-steps.yml Consolidates toolchain setup.
.github/​workflows/​CargoPublish.yml Installs both toolchains.
.github/​workflows/​CargoAudit.yml Uses file-based toolchain selection.
.devcontainer/​Dockerfile Installs separate host and guest toolchains.

Review effort: Balanced

Comment thread .github/workflows/dep_build_guests.yml Outdated
@hyperlight-gh-bot

This comment has been minimized.

Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
@hyperlight-gh-bot

Copy link
Copy Markdown

Benchmark Results

Measured commit: 225c7b5ec274
Baseline commit: f0934723ebf0

kvm / amd (Linux) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 761.08 ns (➖ 1.01x faster)
vec_bytes 583.70 ns (➖ 1.01x slower)
373.95 µs (➖ 1.00x slower)

payload_allocation

slot_pool_segmented
262144 523.63 ns (➖ 1.04x faster)
65536 142.83 ns (➖ 1.00x faster)

sandboxes

create_initialized_and_drop
medium 79.88 ms (➖ 1.07x faster)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
7.73 ns (➖ 1.02x faster) 7.68 ns (➖ 1.02x faster) 7.73 ns (➖ 1.03x faster)

snapshot_files

load_snapshot_unverified
small 93.93 µs (➖ 1.01x slower)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 7.12 µs (➖ 1.01x faster) 6.99 µs (➖ 1.00x faster)
65536 2.04 µs (➖ 1.07x slower) 1.96 µs (➖ 1.06x faster)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 6.33 µs (➖ 1.06x faster) 6.15 µs (➖ 1.02x faster)
8192 1.04 µs (➖ 1.01x slower) 1.03 µs (➖ 1.01x slower)
262144 27.40 µs (➖ 1.01x faster)
kvm / intel (Linux) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 682.66 ns (➖ 1.01x slower)
vec_bytes 496.98 ns (➖ 1.01x faster)
660.91 µs (➖ 1.01x slower)

payload_allocation

slot_pool_segmented
262144 502.30 ns (➖ 1.01x slower)
65536 136.62 ns (➖ 1.02x slower)

sandboxes

create_initialized_and_drop
medium 74.32 ms (➖ 1.04x faster)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
6.88 ns (➖ 1.02x slower) 6.84 ns (➖ 1.01x slower) 6.87 ns (➖ 1.01x slower)

snapshot_files

load_snapshot_unverified
small 45.02 µs (➖ 1.03x faster)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 7.73 µs (➖ 1.03x slower) 7.69 µs (➖ 1.02x slower)
65536 2.18 µs (➖ 1.02x slower) 2.15 µs (➖ 1.01x slower)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 7.29 µs (➖ 1.01x slower) 7.27 µs (➖ 1.01x slower)
8192 749.40 ns (➖ 1.02x slower) 733.10 ns (➖ 1.04x slower)
262144 29.70 µs (➖ 1.03x faster)
mshv3 / amd (Linux) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 950.96 ns (➖ 1.02x faster)
vec_bytes 732.95 ns (➖ 1.03x slower)
441.95 µs (➖ 1.15x slower)

payload_allocation

slot_pool_segmented
262144 715.04 ns (➖ 1.01x faster)
65536 190.65 ns (➖ 1.01x slower)

sandboxes

create_initialized_and_drop
medium 65.52 ms (➖ 1.09x slower)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
9.71 ns (➖ 1.00x faster) 9.76 ns (➖ 1.00x slower) 9.71 ns (➖ 1.03x faster)

snapshot_files

load_snapshot_unverified
small 86.86 µs (➖ 1.02x faster)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 10.27 µs (➖ 1.06x slower) 9.55 µs (➖ 1.01x faster)
65536 2.43 µs (➖ 1.01x faster) 2.22 µs (➖ 1.01x faster)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 8.64 µs (➖ 1.12x faster) 8.27 µs (➖ 1.18x faster)
8192 1.25 µs (➖ 1.02x faster) 1.26 µs (➖ 1.02x slower)
262144 36.72 µs (➖ 1.05x faster)
mshv3 / intel (Linux) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 919.37 ns (➖ 1.02x slower)
vec_bytes 656.50 ns (➖ 1.00x slower)
678.65 µs (➖ 1.05x faster)

payload_allocation

slot_pool_segmented
262144 631.13 ns (➖ 1.00x faster)
65536 172.61 ns (➖ 1.00x faster)

sandboxes

create_initialized_and_drop
medium 76.60 ms (➖ 1.12x faster)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
9.58 ns (➖ 1.05x slower) 8.99 ns (➖ 1.00x slower) 9.06 ns (➖ 1.00x faster)

snapshot_files

load_snapshot_unverified
small 44.51 µs (➖ 1.01x faster)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 7.92 µs (➖ 1.00x slower) 7.89 µs (➖ 1.01x slower)
65536 2.27 µs (➖ 1.00x faster) 2.26 µs (➖ 1.00x slower)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 7.57 µs (➖ 1.01x slower) 7.46 µs (➖ 1.02x faster)
8192 894.29 ns (➖ 1.02x faster) 898.47 ns (➖ 1.01x slower)
262144 39.52 µs (➖ 1.01x slower)
hyperv-ws2025 / amd (Windows) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 1.17 µs (➖ 1.01x faster)
vec_bytes 786.88 ns (➖ 1.02x faster)
1.96 ms (➖ 1.03x faster)

payload_allocation

slot_pool_segmented
262144 801.32 ns (➖ 1.01x slower)
65536 238.44 ns (➖ 1.00x slower)

sandboxes

create_initialized_and_drop
medium 86.92 ms (➖ 1.10x slower)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
12.06 ns (➖ 1.19x slower) 10.47 ns (➖ 1.01x slower) 10.47 ns (➖ 1.06x slower)

snapshot_files

load_snapshot_unverified
small 644.84 µs (➖ 1.13x faster)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 9.79 µs (➖ 1.03x slower) 8.95 µs (➖ 1.02x faster)
65536 2.45 µs (➖ 1.03x slower) 2.76 µs (➖ 1.18x slower)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 8.86 µs (➖ 1.04x faster) 8.72 µs (➖ 1.00x faster)
8192 1.31 µs (➖ 1.00x slower) 1.27 µs (➖ 1.03x slower)
262144 40.64 µs (➖ 1.04x faster)
hyperv-ws2025 / intel (Windows) (➖ stable)

No benchmark improved or regressed.

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 1.18 µs (➖ 1.06x faster)
vec_bytes 810.18 ns (➖ 1.04x faster)
3.02 ms (➖ 1.05x faster)

payload_allocation

slot_pool_segmented
262144 757.42 ns (➖ 1.02x slower)
65536 222.50 ns (➖ 1.01x slower)

sandboxes

create_initialized_and_drop
medium 118.00 ms (➖ 1.08x slower)

slot_pool

alloc_dealloc_1500 alloc_dealloc_4096 alloc_dealloc_128
9.80 ns (➖ 1.03x faster) 9.77 ns (➖ 1.02x slower) 9.99 ns (➖ 1.04x slower)

snapshot_files

load_snapshot_unverified
small 586.88 µs (➖ 1.02x slower)

virtq_readonly

slot_pool_segmented_fragmented slot_pool_segmented
262144 9.04 µs (➖ 1.14x slower) 7.86 µs (➖ 1.02x faster)
65536 2.61 µs (➖ 1.11x slower) 2.30 µs (➖ 1.03x faster)

virtq_readwrite

slot_pool_segmented_fragmented slot_pool_segmented
65536 8.42 µs (➖ 1.00x faster) 9.17 µs (➖ 1.05x faster)
8192 1.08 µs (➖ 1.03x slower) 1.07 µs (➖ 1.01x slower)
262144 40.56 µs (➖ 1.09x faster)

Reported by cargo ci bench-report --candidate run:39489269058 --config-file bench_report.toml.

submodules: true

- uses: hyperlight-dev/ci-setup-workflow@2f4142ba17cf573af44fc1e1f1ffc743daded5b3 # v1.10.0
- uses: hyperlight-dev/ci-setup-workflow@4b7107a0f24c523f503c861ddeb8180d3a23e7f7 # v2.1.0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The v2 setup action calls actions-rust-lang/setup-rust-toolchain, which writes CARGO_PROFILE_DEV_DEBUG=0 to GITHUB_ENV when it is unset:

if [[ ! -v CARGO_PROFILE_DEV_DEBUG ]]; then
  echo "CARGO_PROFILE_DEV_DEBUG=0" >> $GITHUB_ENV
fi

This job has no override. just tar-static-lib builds debug/libhyperlight_guest_capi.a with --profile dev, so the published debug archive has no debug info. Other jobs lose backtrace line numbers too. Could we set CARGO_PROFILE_DEV_DEBUG once in ci-setup-workflow before both setup steps, and drop the three per-workflow overrides? Otherwise add it to this workflow's env.

// even though the warning is a false positive here. We suppress it because the ABI contract is
// the important thing. We also allow `unknown_lints` so older guest toolchains can still build
// even though they do not know this lint's name yet.
#![allow(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a false positive. The lint compares the whole signature with eq, and that includes safety (source). write builds a slice from a caller pointer and clock_gettime writes through one, so they should be unsafe extern "C" fn with # Safety docs. memory.rs already does this for malloc:

#[unsafe(no_mangle)]
unsafe extern "C" fn write(fd: c_int, buf: *const c_void, count: usize) -> isize {

Then the module-wide allow can go. The unknown_lints allow also hides typos in other lint names in this module.

return tomllib.load(source)["toolchain"]["channel"]
guest = channel("src/tests/rust_guests/rust-toolchain.toml")
if guest != channel("src/hyperlight_guest_capi/rust-toolchain.toml"):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check runs only on pushes to main. A PR that bumps one guest toolchain file passes CI, the C API builds with a different rustc, and this workflow fails after merge. Could the same check run in dep_code_checks.yml?

.clang_arg(format!("-I{}", include_dir.display()))
.clang_arg("-nostdlibinc")
// Preserve size_t in runtime function declarations for bindgen.
.clang_arg("-fno-builtin")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flag makes sense and matches line 88. It does change public bindings though. Without it clang types builtins like strlen and memcpy with unsigned long, so bindgen emitted c_ulong. With it they keep size_t and become usize. These are re-exported as hyperlight_guest_bin::libc, so guest code like let n: u64 = unsafe { libc::strlen(p) }; stops compiling. Worth a breaking CHANGELOG entry?

- uses: hyperlight-dev/ci-setup-workflow@4b7107a0f24c523f503c861ddeb8180d3a23e7f7 # v2.1.0
with:
rust-toolchain: "1.94"
additional-toolchain-directory: src/tests/rust_guests

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This job only runs root cargo build, so it doesn't need the guest toolchain. Same for other host-only and artifact-consuming jobs. Could we pass additional-toolchain-directory only where guests or the C API are built?

@@ -0,0 +1,6 @@
[toolchain]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hyperlight-guest-capi is still a root workspace member, so the toolchain depends on the working directory. cargo build -p hyperlight-guest-capi and rust-analyzer from the root use 1.99.1, and both toolchains share target/. Would it make sense to exclude it from the root workspace like src/tests/rust_guests? Could be a follow-up.

Comment thread .devcontainer/Dockerfile
&& rustup target add x86_64-unknown-linux-gnu \
&& rustup target add x86_64-unknown-linux-musl \
&& rustup target add x86_64-unknown-none \
&& rustup component add clippy rustfmt --toolchain ${RUST_TOOLCHAIN} \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: rustup default installs clippy and rustfmt with the default profile, so this line looks redundant. rustfmt in the toolchain files also seems unused since fmt runs on nightly.

Comment thread CONTRIBUTING.md

The root `rust-toolchain.toml` pins the rust version for host builds and tools.

Rust guests and the C API use Rust 1.94.1, pinned in

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the hard-coded version will go stale on the next bump. Maybe just point at the toolchain files.

Comment thread flake.nix
case "+$requested" in
${clauses}
install) exit 0; ;;
+1.99.1) base="${toolchains.stable.rust}"; ;;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the stable channel dated 2026-10-15 guaranteed to be 1.99.1? Naming the toolchain attribute "1.99.1" would avoid this special case.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/dependencies For PRs that update dependencies or related components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants