Skip to content

Commit c3a9b9f

Browse files
author
Sylvester Hesp
authored
Migration from register_attr to register_tool (#926)
* Accept `#[rust_gpu::spirv()]` attributes rather than `#[spirv()]` in backend * Implemented `#[spirv(..)]` proc macro attribute for all platforms that conditionally translates to `#[rust_gpu::spirv()]` based on platform * Changed `SpirvBuilder` to always apply `register_tool(rust_gpu)` attribute to shader crates * Updated docs * Added changelog
1 parent 0811739 commit c3a9b9f

File tree

201 files changed

+432
-251
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

201 files changed

+432
-251
lines changed

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# `rust-gpu` Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Changed 🛠️
11+
12+
- 🚨BREAKING🚨 Migrated from `register_attr` to `register_tool`. [More information](docs/src/migration-to-register-tool.md).
13+
14+
## [0.4.0-alpha.15]
15+
16+
### Added ⭐
17+
18+
- Build-time check for nightly toolchain version to provide user-friendly error messages.
19+
20+
### Changed 🛠️
21+
22+
- Updated rust toolchain to `nightly-2022-08-29`.

crates/rustc_codegen_spirv/src/symbols.rs

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::attr::{Entry, ExecutionModeExtra, IntrinsicType, SpirvAttribute};
22
use crate::builder::libm_intrinsics;
33
use rspirv::spirv::{BuiltIn, ExecutionMode, ExecutionModel, StorageClass};
4-
use rustc_ast::ast::{Attribute, Lit, LitIntType, LitKind, NestedMetaItem};
4+
use rustc_ast::ast::{AttrKind, Attribute, Lit, LitIntType, LitKind, NestedMetaItem};
55
use rustc_data_structures::fx::FxHashMap;
66
use rustc_span::symbol::{Ident, Symbol};
77
use rustc_span::Span;
@@ -16,6 +16,7 @@ pub struct Symbols {
1616
// Used by `is_blocklisted_fn`.
1717
pub fmt_decimal: Symbol,
1818

19+
pub rust_gpu: Symbol,
1920
pub spirv: Symbol,
2021
pub spirv_std: Symbol,
2122
pub libm: Symbol,
@@ -373,6 +374,7 @@ impl Symbols {
373374
Self {
374375
fmt_decimal: Symbol::intern("fmt_decimal"),
375376

377+
rust_gpu: Symbol::intern("rust_gpu"),
376378
spirv: Symbol::intern("spirv"),
377379
spirv_std: Symbol::intern("spirv_std"),
378380
libm: Symbol::intern("libm"),
@@ -411,20 +413,44 @@ pub(crate) fn parse_attrs_for_checking<'a>(
411413
attrs: &'a [Attribute],
412414
) -> impl Iterator<Item = Result<(Span, SpirvAttribute), ParseAttrError>> + 'a {
413415
attrs.iter().flat_map(move |attr| {
414-
let (whole_attr_error, args) = if !attr.has_name(sym.spirv) {
415-
// Use an empty vec here to return empty
416-
(None, Vec::new())
417-
} else if let Some(args) = attr.meta_item_list() {
418-
(None, args)
419-
} else {
420-
(
421-
Some(Err((
422-
attr.span,
423-
"#[spirv(..)] attribute must have at least one argument".to_string(),
424-
))),
425-
Vec::new(),
426-
)
416+
let (whole_attr_error, args) = match attr.kind {
417+
AttrKind::Normal(ref normal) => {
418+
// #[...]
419+
let s = &normal.item.path.segments;
420+
if s.len() > 1 && s[0].ident.name == sym.rust_gpu {
421+
// #[rust_gpu ...]
422+
if s.len() != 2 || s[1].ident.name != sym.spirv {
423+
// #[rust_gpu::...] but not #[rust_gpu::spirv]
424+
(
425+
Some(Err((
426+
attr.span,
427+
"unknown `rust_gpu` attribute, expected `rust_gpu::spirv`"
428+
.to_string(),
429+
))),
430+
Vec::new(),
431+
)
432+
} else if let Some(args) = attr.meta_item_list() {
433+
// #[rust_gpu::spirv(...)]
434+
(None, args)
435+
} else {
436+
// #[rust_gpu::spirv]
437+
(
438+
Some(Err((
439+
attr.span,
440+
"#[rust_gpu::spirv(..)] attribute must have at least one argument"
441+
.to_string(),
442+
))),
443+
Vec::new(),
444+
)
445+
}
446+
} else {
447+
// #[...] but not #[rust_gpu ...]
448+
(None, Vec::new())
449+
}
450+
}
451+
AttrKind::DocComment(..) => (None, Vec::new()), // doccomment
427452
};
453+
428454
whole_attr_error
429455
.into_iter()
430456
.chain(args.into_iter().map(move |ref arg| {

crates/spirv-builder/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,8 @@ fn invoke_rustc(builder: &SpirvBuilder) -> Result<PathBuf, SpirvBuilderError> {
424424
// the default until https://github.com/rust-lang/rust/pull/93969).
425425
"-Zbinary-dep-depinfo".to_string(),
426426
"-Csymbol-mangling-version=v0".to_string(),
427+
"-Zcrate-attr=feature(register_tool)".to_string(),
428+
"-Zcrate-attr=register_tool(rust_gpu)".to_string(),
427429
];
428430

429431
let mut llvm_args = vec![];

crates/spirv-std/macros/src/lib.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ use proc_macro2::{Delimiter, Group, Ident, Span, TokenTree};
7777

7878
use syn::{punctuated::Punctuated, spanned::Spanned, ItemFn, Token};
7979

80-
use quote::ToTokens;
80+
use quote::{quote, ToTokens};
8181
use std::fmt::Write;
8282

8383
/// A macro for creating SPIR-V `OpTypeImage` types. Always produces a
@@ -138,9 +138,16 @@ pub fn Image(item: TokenStream) -> TokenStream {
138138
output.into()
139139
}
140140

141+
/// Replaces all (nested) occurrences of the `#[spirv(..)]` attribute with
142+
/// `#[cfg_attr(target_arch="spirv", rust_gpu::spirv(..))]`.
141143
#[proc_macro_attribute]
142-
pub fn spirv(_attr: TokenStream, item: TokenStream) -> TokenStream {
143-
let mut tokens = Vec::new();
144+
pub fn spirv(attr: TokenStream, item: TokenStream) -> TokenStream {
145+
let mut tokens: Vec<TokenTree> = Vec::new();
146+
147+
// prepend with #[rust_gpu::spirv(..)]
148+
let attr: proc_macro2::TokenStream = attr.into();
149+
tokens.extend(quote! { #[cfg_attr(target_arch="spirv", rust_gpu::spirv(#attr))] });
150+
144151
let item: proc_macro2::TokenStream = item.into();
145152
for tt in item {
146153
match tt {
@@ -153,7 +160,11 @@ pub fn spirv(_attr: TokenStream, item: TokenStream) -> TokenStream {
153160
&& matches!(group.stream().into_iter().next(), Some(TokenTree::Ident(ident)) if ident == "spirv")
154161
&& matches!(sub_tokens.last(), Some(TokenTree::Punct(p)) if p.as_char() == '#') =>
155162
{
156-
sub_tokens.pop();
163+
// group matches [spirv ...]
164+
let inner = group.stream(); // group stream doesn't include the brackets
165+
sub_tokens.extend(
166+
quote! { [cfg_attr(target_arch="spirv", rust_gpu::#inner)] },
167+
);
157168
}
158169
_ => sub_tokens.push(tt),
159170
}

crates/spirv-std/shared/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Small shared crate, to share definitions between `spirv-std`
22
//! and `spirv-std-macros`.
3+
34
#![no_std]
45

56
pub mod image_params;

crates/spirv-std/src/byte_addressable_buffer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl<'a> ByteAddressableBuffer<'a> {
4545
/// transmute)
4646
pub unsafe fn load<T>(&self, byte_index: u32) -> T {
4747
if byte_index + mem::size_of::<T>() as u32 > self.data.len() as u32 {
48-
panic!("Index out of range")
48+
panic!("Index out of range");
4949
}
5050
buffer_load_intrinsic(self.data, byte_index)
5151
}
@@ -71,7 +71,7 @@ impl<'a> ByteAddressableBuffer<'a> {
7171
/// transmute)
7272
pub unsafe fn store<T>(&mut self, byte_index: u32, value: T) {
7373
if byte_index + mem::size_of::<T>() as u32 > self.data.len() as u32 {
74-
panic!("Index out of range")
74+
panic!("Index out of range");
7575
}
7676
buffer_store_intrinsic(self.data, byte_index, value);
7777
}

crates/spirv-std/src/lib.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
asm_experimental_arch,
77
core_intrinsics,
88
lang_items,
9-
register_attr,
109
repr_simd,
11-
),
12-
register_attr(spirv)
10+
)
1311
)]
1412
// BEGIN - Embark standard lints v0.4
1513
// do not change or add/remove here, but one can add exceptions after this section
@@ -93,8 +91,9 @@
9391
//! Core functions, traits, and more that make up a "standard library" for SPIR-V for use in
9492
//! rust-gpu.
9593
96-
#[cfg_attr(not(target_arch = "spirv"), macro_use)]
94+
#[macro_use]
9795
pub extern crate spirv_std_macros as macros;
96+
pub use macros::spirv;
9897

9998
pub mod arch;
10099
pub mod byte_addressable_buffer;

docs/src/attributes.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
rust-gpu introduces a number of SPIR-V related attributes to express behavior specific to SPIR-V not exposed in the base rust language.
44

5+
Before you'll able to use these attributes, make sure you import the attribute from the `spirv-std` crate:
6+
7+
```rust
8+
use spirv_std::spirv;
9+
```
10+
511
There are a few different categories of attributes:
612

713
## Entry points
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Migration to `register_tool`
2+
3+
This document applies to [PR#926](https://github.com/EmbarkStudios/rust-gpu/pull/926)
4+
5+
## What happened
6+
7+
In a [recent nightly Rust update](https://github.com/rust-lang/rust/commit/76dd5c58a011bb734ad5b8e96fc560374893bc8f), the `register_attr` feature was removed in favor of `register_tool`. Unfortunately, rust-gpu made use of this feature to register the `spirv` attribute.
8+
9+
## What does this mean for you as a shader maintainer
10+
11+
You'll need to import the `spirv` proc macro attribute from `spirv-std` in order for the rust compiler:
12+
13+
```rust
14+
use spirv_std::spirv;
15+
```
16+
17+
If your shader code already contains this line but is conditionally only included for non-spirv builds, like so:
18+
19+
```rust
20+
#[cfg(not(target_arch = "spirv"))]
21+
use spirv_std::spirv;
22+
```
23+
24+
please remove the conditional attribute.
25+
26+
For this macro attribute to work correctly, it is important that `spirv` is visible in the global score and you use it like you used it before: `#[spirv(..)]`. An attempt to scope the attribute (such as `#[spirv_std::spirv(..)]`) will confuse the macro and likely fail.
27+
28+
You'll also need to remove the `feature(register_attr)` and `register_attr(spirv)` attributes from your shader crates. If you're building using `SpirvBuilder`, you don't need to do anything else; the new `register_tool` is applied automatically. If not, you'll need to include these attributes instead:
29+
30+
```rust
31+
#![feature(register_tool)]
32+
#![register_tool(rust_gpu)]
33+
```
34+
35+
That's it. Your shaders should now compile like before.
36+
37+
## Technical Background
38+
39+
Unfortunately, since the new Rust nightly toolchain in September 2022, `register_attr(spirv)` can no longer be used to register a global `spirv` attribute. Without this registration, the compiler would simply complain about `spirv` being an unknown attribute. However, the alternative, `register_tool`, requires us to scope the attribute in a namespace. For instance, as we've chosen the `rust_gpu` namespace, this would mean that you'd need to start writing `#[rust_gpu::spirv(..)]` instead, which would be quite tedious and would break a lot of code. And it's not possible to `use` a name from a tool namespace to bring it into scope.
40+
41+
Instead, we opted to implement a proc macro attribute called `spirv` instead[^1]. This macro attribute scans the item it is applied to, and translates any `#[spirv(..)]` it finds into `#[rust_gpu::spirv(..)]` which will be subsequently handled by the codegen backend. Because it is now a proc macro attribute exported from `spirv_std`, you need to do `use spirv_std::spirv` to make it globally visible in your crate. ***Note that we recommend using the `spirv` proc macro attribute itself rather than the `rust_gpu::spirv` attribute it translates to, as the latter is subject to change.***
42+
43+
We've also added the `feature(register_tool)` and `register_tool(rust_gpu)` crate attributes by default when compiling through `SpirvBuilder`. This will silence any error that you would otherwise get for applying a `rust_gpu` scoped attribute.
44+
45+
[^1]: This is not entirely true. In reality, the `spirv` proc macro attribute already existed, but only for non-spirv builds. It was used to turn the `#[spirv(..)]` attribute into a no-op. The proc macro is now used on all platforms, and it emits `#[cfg_attr(target_arch="spirv", rust_gpu::spirv(..))]` for each usage of `#[spirv(..)]`.

examples/shaders/compute-shader/src/lib.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
#![cfg_attr(
2-
target_arch = "spirv",
3-
feature(register_attr),
4-
register_attr(spirv),
5-
no_std
6-
)]
1+
#![cfg_attr(target_arch = "spirv", no_std)]
72
// HACK(eddyb) can't easily see warnings otherwise from `spirv-builder` builds.
83
#![deny(warnings)]
94

105
use glam::UVec3;
11-
use spirv_std::glam;
12-
#[cfg(not(target_arch = "spirv"))]
13-
use spirv_std::macros::spirv;
6+
use spirv_std::{glam, spirv};
147

158
// Adapted from the wgpu hello-compute example
169

examples/shaders/mouse-shader/src/lib.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,17 @@
1-
#![cfg_attr(
2-
target_arch = "spirv",
3-
no_std,
4-
feature(register_attr),
5-
register_attr(spirv)
6-
)]
1+
#![cfg_attr(target_arch = "spirv", no_std)]
72
// HACK(eddyb) can't easily see warnings otherwise from `spirv-builder` builds.
83
#![deny(warnings)]
94

105
use core::f32::consts::PI;
116
use glam::{const_vec4, vec2, vec3, Mat2, Vec2, Vec3, Vec4, Vec4Swizzles};
127
use shared::*;
8+
use spirv_std::spirv;
139

1410
// Note: This cfg is incorrect on its surface, it really should be "are we compiling with std", but
1511
// we tie #[no_std] above to the same condition, so it's fine.
1612
#[cfg(target_arch = "spirv")]
1713
use spirv_std::num_traits::Float;
1814

19-
#[cfg(not(target_arch = "spirv"))]
20-
use spirv_std::macros::spirv;
21-
2215
trait Shape: Copy {
2316
/// Distances indicate where the point is in relation to the shape:
2417
/// * negative distance: the point is "inside" the shape

examples/shaders/reduce/src/lib.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
1-
#![cfg_attr(
2-
target_arch = "spirv",
3-
no_std,
4-
feature(register_attr),
5-
register_attr(spirv)
6-
)]
1+
#![cfg_attr(target_arch = "spirv", no_std)]
72
#![allow(clippy::too_many_arguments, clippy::missing_safety_doc)]
83
// HACK(eddyb) can't easily see warnings otherwise from `spirv-builder` builds.
94
#![deny(warnings)]
105
use spirv_std::glam::UVec3;
11-
#[cfg(not(target_arch = "spirv"))]
12-
use spirv_std::macros::spirv;
136
#[cfg(target_arch = "spirv")]
147
use spirv_std::memory::Scope;
8+
use spirv_std::spirv;
159

1610
#[doc(alias = "OpGroupNonUniformIAdd")]
1711
#[cfg(target_arch = "spirv")]

examples/shaders/simplest-shader/src/lib.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
#![cfg_attr(
2-
target_arch = "spirv",
3-
no_std,
4-
feature(register_attr),
5-
register_attr(spirv)
6-
)]
1+
#![cfg_attr(target_arch = "spirv", no_std)]
72
// HACK(eddyb) can't easily see warnings otherwise from `spirv-builder` builds.
83
#![deny(warnings)]
94

10-
#[cfg(not(target_arch = "spirv"))]
11-
use spirv_std::macros::spirv;
12-
135
use shared::glam::{vec4, Vec4};
6+
use spirv_std::spirv;
147

158
#[spirv(fragment)]
169
pub fn main_fs(output: &mut Vec4) {

examples/shaders/sky-shader/src/lib.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
//! Ported to Rust from <https://github.com/Tw1ddle/Sky-Shader/blob/master/src/shaders/glsl/sky.fragment>
22
3-
#![cfg_attr(
4-
target_arch = "spirv",
5-
no_std,
6-
feature(register_attr, lang_items),
7-
register_attr(spirv)
8-
)]
3+
#![cfg_attr(target_arch = "spirv", no_std)]
94
// HACK(eddyb) can't easily see warnings otherwise from `spirv-builder` builds.
105
#![deny(warnings)]
116

12-
#[cfg(not(target_arch = "spirv"))]
13-
use spirv_std::macros::spirv;
14-
157
use core::f32::consts::PI;
168
use glam::{const_vec3, vec2, vec3, Vec2, Vec3, Vec4};
179
use shared::*;
10+
use spirv_std::spirv;
1811

1912
// Note: This cfg is incorrect on its surface, it really should be "are we compiling with std", but
2013
// we tie #[no_std] above to the same condition, so it's fine.

tests/src/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,7 @@ impl Runner {
123123
"--crate-type dylib",
124124
"-Zunstable-options",
125125
"-Zcrate-attr=no_std",
126-
"-Zcrate-attr=feature(register_attr,asm_const,asm_experimental_arch)",
127-
"-Zcrate-attr=register_attr(spirv)",
126+
"-Zcrate-attr=feature(asm_const,asm_experimental_arch)",
128127
]
129128
.join(" ")
130129
}
@@ -333,6 +332,8 @@ fn rust_flags(codegen_backend_path: &Path) -> String {
333332
"-Cembed-bitcode=no",
334333
&format!("-Ctarget-feature=+{}", target_features.join(",+")),
335334
"-Csymbol-mangling-version=v0",
335+
"-Zcrate-attr=feature(register_tool)",
336+
"-Zcrate-attr=register_tool(rust_gpu)",
336337
]
337338
.join(" ")
338339
}

tests/ui/arch/all.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use spirv_std::spirv;
2+
13
// build-pass
24

35
#[spirv(fragment)]

0 commit comments

Comments
 (0)