unlock unlimited code runs for $79, limited time →
NOSSLETTER.techLIVE
back to OSSpedia
api-designtesting-strategybuild-systems

Diverse offload fixes

Project: rust-lang/rust | Language: Rust | Concepts: api-design, testing-strategy, build-systems | Difficulty: mid
Link: https://github.com/rust-lang/rust/pull/161968

The problem

The offload feature in the Rust compiler had two main issues:

  1. The offload_kernel macro lowered to std::unimplemented!, which made it incompatible with no_std device code.
  2. The build system attempted to link files even in cases (like the HostMetadata offload pass) where no codegen or object files were produced, causing unnecessary work or potential errors.
  3. Test coverage and organization for offload scenarios (especially mixing std and no_std) was lacking.

Why it's non-trivial

A naive fix might just switch from std to core or skip linking in some cases, but:

  • The macro lowering must be careful to always use APIs available in both std and no_std contexts.
  • The build system must correctly detect when linking is unnecessary, without breaking other offload or incremental compilation workflows.
  • Test changes must ensure coverage for both std and no_std combinations and keep tests organized for maintainability.

The approach

1. Lowering to core instead of std in the macro

-                [sym::std, sym::unimplemented].map(|s| Ident::new(s, span)).to_vec(),
+                [sym::core, sym::unimplemented].map(|s| Ident::new(s, span)).to_vec(),

What this does:
Changes the macro expansion to use core::unimplemented! instead of std::unimplemented!. This allows the macro to work in no_std environments, which is required for device code that can't depend on the standard library.

2. Skipping linking in the HostMetadata offload pass

+        // The `HostMetadata` offload pass only writes the kernel manifest.
+        // Codegen was already skipped so there are no files to link.
+        if sess
+            .opts
+            .unstable_opts
+            .offload
+            .iter()
+            .any(|o| matches!(o, config::Offload::HostMetadata(_)))
+        {
+            return;
+        }

What this does:
Before attempting to link, the build system checks if the offload mode is HostMetadata. If so, it returns early, skipping linking entirely since there are no object files to link. This prevents unnecessary or erroneous linker invocations.

3. Test reorganization and new test for std/no_std combinations

  • Moved existing generic manifest offload tests into a subfolder for better organization.
  • Added a new test (host-std-device-nostd) that verifies offload works when the host uses std and the device uses no_std.

Example from the new test:

#![feature(gpu_offload, rustc_attrs)]
#![allow(internal_features)]
#![cfg_attr(device, no_std)]
#![cfg_attr(device, no_main)]

#[cfg(device)]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

#[rustc_offload_kernel]
fn kernel() {}

#[cfg(not(device))]
fn main() {
    core::offload::offload! {
        kernel = kernel,
        args = (),
    }
    println!("Hello from Host with std");
}

What this does:
Ensures that the offload mechanism works when mixing std and no_std in the same project, and that the macro and build changes above are effective.

4. Updating UI tests to use the new macro

-    core::intrinsics::offload::<_, _, ()>(kernel, [1, 1, 1], [1, 1, 1], 0, -1, (0.0f32,));
+    core::offload::offload! { kernel = kernel, args = (0.0f32,) }

What this does:
Switches tests to use the new macro-based API for offload, which is now compatible with both std and no_std contexts.

From the review thread

[review] Sa4dUs: this is not necessary if we compile the host with std, but ig it's still better to use core

[review] ZuseZ4: yes, there isn't really a reason to prevent offload to work in no-std code.

Takeaways

  • Prefer core over std in macros and libraries intended for both std and no_std use. This maximizes portability and avoids unnecessary dependencies.
  • Guard build steps with precise feature checks to avoid unnecessary or erroneous work. For example, skip linking if no object files are produced for a given build mode.
  • Organize and expand tests to cover all relevant feature combinations, especially when supporting mixed environments (like std host and no_std device). This ensures changes are robust and maintainable.