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:
- The
offload_kernelmacro lowered tostd::unimplemented!, which made it incompatible withno_stddevice code. - The build system attempted to link files even in cases (like the
HostMetadataoffload pass) where no codegen or object files were produced, causing unnecessary work or potential errors. - Test coverage and organization for offload scenarios (especially mixing
stdandno_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
stdandno_stdcontexts. - 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
stdandno_stdcombinations 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 usesstdand the device usesno_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
stdandno_stduse. 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
stdhost andno_stddevice). This ensures changes are robust and maintainable.