From 4638120f43d02f2107e04ce86980aaacc77e2a83 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Sat, 24 Jan 2026 13:41:38 -0800 Subject: [PATCH 01/13] Update rspirv/spirv to sdk-update branch with newer SPIR-V opcodes The published rspirv 0.12.0+sdk-1.3.268.0 doesn't include newer SPIR-V opcodes like UntypedPointersKHR, TypeUntypedPointerKHR, UntypedVariableKHR, and ConditionalEntryPointINTEL that are required by the spirv-tools crate. This patches rspirv and spirv to use the gfx-rs/rspirv sdk-update branch which includes these opcodes (version 0.12.0+sdk-1.4.335.0). --- Cargo.lock | 10 ++++------ Cargo.toml | 6 ++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 620864b458..0854f0b436 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2690,9 +2690,8 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "rspirv" -version = "0.12.0+sdk-1.3.268.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cf3a93856b6e5946537278df0d3075596371b1950ccff012f02b0f7eafec8d" +version = "0.12.0+sdk-1.4.335.0" +source = "git+https://github.com/gfx-rs/rspirv?branch=sdk-update#6a8d3db93205311ffb156793727df8f348bd3099" dependencies = [ "rustc-hash", "spirv", @@ -3113,9 +3112,8 @@ dependencies = [ [[package]] name = "spirv" -version = "0.3.0+sdk-1.3.268.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +version = "0.3.0+sdk-1.4.335.0" +source = "git+https://github.com/gfx-rs/rspirv?branch=sdk-update#6a8d3db93205311ffb156793727df8f348bd3099" dependencies = [ "bitflags 2.10.0", "serde", diff --git a/Cargo.toml b/Cargo.toml index 1eb4422bb0..6eecc5da9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,12 @@ glam = { version = ">=0.30.8", default-features = false } libm = { version = "0.2.5", default-features = false } bytemuck = { version = "1.23", features = ["derive"] } +# Patch rspirv/spirv to use newer versions with additional SPIR-V opcodes needed by spirv-tools. +# TODO: Once a newer rspirv is published that includes these opcodes, switch to a crates.io version. +[patch.crates-io] +rspirv = { git = "https://github.com/gfx-rs/rspirv", branch = "sdk-update" } +spirv = { git = "https://github.com/gfx-rs/rspirv", branch = "sdk-update", package = "spirv" } + # Enable incremental by default in release mode. [profile.release] incremental = true From 1bb9de47bd3588bac450bdb39d284c73e010b9cb Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Sat, 24 Jan 2026 13:42:08 -0800 Subject: [PATCH 02/13] Switch spirv-tools to Rust-GPU/SPIRV-Tools git dependency Switch from crates.io spirv-tools 0.13.0 to the Rust-GPU/SPIRV-Tools repository via git dependency. This provides access to the new pure-Rust SPIR-V validator and egglog-based optimizer. When using as a git dependency, set SPIRV_HEADERS_PATH to point to a local SPIRV-Headers checkout since the submodule isn't available. --- Cargo.lock | 774 +++++++++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 2 +- 2 files changed, 725 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0854f0b436..25539e05df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,7 +141,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -152,7 +152,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -167,6 +167,24 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arc-swap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d03449bb8ca2cc2ef70869af31463d1ae5ccc8fa3e334b307203fbf815207e" +dependencies = [ + "rustversion", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -253,12 +271,30 @@ dependencies = [ "serde_core", ] +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + [[package]] name = "block" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "block2" version = "0.5.1" @@ -297,7 +333,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -429,6 +465,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "num-traits", +] + [[package]] name = "clap" version = "4.5.53" @@ -457,10 +502,10 @@ version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -670,6 +715,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -679,6 +733,28 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -698,6 +774,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -710,12 +795,68 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + [[package]] name = "cursor-icon" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "derive_more" version = "0.99.20" @@ -726,7 +867,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.111", ] [[package]] @@ -775,6 +916,16 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -810,7 +961,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -831,6 +982,21 @@ dependencies = [ "litrs", ] +[[package]] +name = "dot-generator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aaac7ada45f71873ebce336491d1c1bc4a7c8042c7cea978168ad59e805b871" +dependencies = [ + "dot-structures", +] + +[[package]] +name = "dot-structures" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cfcded997a93eb31edd639361fa33fd229a8784e953b37d71035fe3890b7b" + [[package]] name = "downcast-rs" version = "1.2.1" @@ -843,6 +1009,171 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "egglog" +version = "1.0.0" +source = "git+https://github.com/egraphs-good/egglog.git#57fdb7dfa7bef87d057a98ed691df87e0b8fdeb2" +dependencies = [ + "chrono", + "clap", + "csv", + "dyn-clone", + "egglog-add-primitive", + "egglog-ast", + "egglog-bridge", + "egglog-core-relations", + "egglog-numeric-id", + "egglog-reports", + "egraph-serialize", + "env_logger", + "hashbrown 0.16.1", + "im-rc", + "indexmap 2.12.1", + "log", + "mimalloc", + "num", + "ordered-float 5.1.0", + "rayon", + "rustc-hash 2.1.1", + "serde_json", + "thiserror 2.0.17", + "web-time", +] + +[[package]] +name = "egglog-add-primitive" +version = "1.0.0" +source = "git+https://github.com/egraphs-good/egglog.git#57fdb7dfa7bef87d057a98ed691df87e0b8fdeb2" +dependencies = [ + "quote", + "syn 2.0.111", +] + +[[package]] +name = "egglog-ast" +version = "1.0.0" +source = "git+https://github.com/egraphs-good/egglog.git#57fdb7dfa7bef87d057a98ed691df87e0b8fdeb2" +dependencies = [ + "ordered-float 5.1.0", +] + +[[package]] +name = "egglog-bridge" +version = "1.0.0" +source = "git+https://github.com/egraphs-good/egglog.git#57fdb7dfa7bef87d057a98ed691df87e0b8fdeb2" +dependencies = [ + "anyhow", + "dyn-clone", + "egglog-core-relations", + "egglog-numeric-id", + "egglog-reports", + "egglog-union-find", + "hashbrown 0.16.1", + "indexmap 2.12.1", + "log", + "num-rational", + "once_cell", + "ordered-float 5.1.0", + "petgraph", + "rayon", + "smallvec", + "thiserror 2.0.17", + "web-time", +] + +[[package]] +name = "egglog-concurrency" +version = "1.0.0" +source = "git+https://github.com/egraphs-good/egglog.git#57fdb7dfa7bef87d057a98ed691df87e0b8fdeb2" +dependencies = [ + "arc-swap", + "egglog-numeric-id", + "rayon", + "smallvec", +] + +[[package]] +name = "egglog-core-relations" +version = "1.0.0" +source = "git+https://github.com/egraphs-good/egglog.git#57fdb7dfa7bef87d057a98ed691df87e0b8fdeb2" +dependencies = [ + "anyhow", + "bumpalo", + "crossbeam", + "crossbeam-queue", + "dashmap", + "dyn-clone", + "egglog-concurrency", + "egglog-numeric-id", + "egglog-reports", + "egglog-union-find", + "fixedbitset", + "hashbrown 0.16.1", + "indexmap 2.12.1", + "log", + "num", + "once_cell", + "petgraph", + "rand 0.9.2", + "rayon", + "rustc-hash 2.1.1", + "smallvec", + "thiserror 2.0.17", + "web-time", +] + +[[package]] +name = "egglog-numeric-id" +version = "1.0.0" +source = "git+https://github.com/egraphs-good/egglog.git#57fdb7dfa7bef87d057a98ed691df87e0b8fdeb2" +dependencies = [ + "rayon", +] + +[[package]] +name = "egglog-reports" +version = "1.0.0" +source = "git+https://github.com/egraphs-good/egglog.git#57fdb7dfa7bef87d057a98ed691df87e0b8fdeb2" +dependencies = [ + "clap", + "hashbrown 0.16.1", + "indexmap 2.12.1", + "rustc-hash 2.1.1", + "serde", + "serde_json", + "web-time", +] + +[[package]] +name = "egglog-union-find" +version = "1.0.0" +source = "git+https://github.com/egraphs-good/egglog.git#57fdb7dfa7bef87d057a98ed691df87e0b8fdeb2" +dependencies = [ + "crossbeam", + "egglog-concurrency", + "egglog-numeric-id", +] + +[[package]] +name = "egraph-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0977732fb537ace6f8c15ce160ebdda78b6502b4866d3b904e4fe752e2be4702" +dependencies = [ + "graphviz-rust", + "indexmap 2.12.1", + "once_cell", + "ordered-float 5.1.0", + "serde", + "serde_json", +] + [[package]] name = "either" version = "1.15.0" @@ -906,7 +1237,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1052,7 +1383,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1135,7 +1466,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1168,6 +1499,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "gethostname" version = "1.1.0" @@ -1325,6 +1666,22 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "graphviz-rust" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db134cb611668917cabf340af9a39518426f9a10827b4cedcb4cdcf84443f6d0" +dependencies = [ + "dot-generator", + "dot-structures", + "into-attr", + "into-attr-derive", + "pest", + "pest_derive", + "rand 0.9.2", + "tempfile", +] + [[package]] name = "half" version = "2.7.1" @@ -1343,6 +1700,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" @@ -1361,9 +1724,19 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", + "serde", + "serde_core", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -1493,6 +1866,20 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "im-rc" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" +dependencies = [ + "bitmaps", + "rand_core 0.6.4", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1553,6 +1940,28 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "969ee3fc68ec2e88eb21434ce4d9b7e1600d1ce92ff974560a6c4a304f5124b9" +[[package]] +name = "into-attr" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18b48c537e49a709e678caec3753a7dba6854661a1eaa27675024283b3f8b376" +dependencies = [ + "dot-structures", +] + +[[package]] +name = "into-attr-derive" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecac7c1ae6cd2c6a3a64d1061a8bdc7f52ff62c26a831a2301e54c1b5d70d5b1" +dependencies = [ + "dot-generator", + "dot-structures", + "into-attr", + "quote", + "syn 1.0.109", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -1604,7 +2013,7 @@ checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1726,6 +2135,16 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +[[package]] +name = "libmimalloc-sys" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "libredox" version = "0.1.11" @@ -1839,6 +2258,15 @@ dependencies = [ "paste", ] +[[package]] +name = "mimalloc" +version = "0.1.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8" +dependencies = [ + "libmimalloc-sys", +] + [[package]] name = "minifb" version = "0.28.0" @@ -1896,7 +2324,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1935,7 +2363,7 @@ dependencies = [ "num-traits", "once_cell", "petgraph", - "rustc-hash", + "rustc-hash 1.1.0", "spirv", "thiserror 2.0.17", "unicode-ident", @@ -2014,7 +2442,71 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", ] [[package]] @@ -2056,7 +2548,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2338,6 +2830,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" dependencies = [ "num-traits", + "rand 0.8.5", + "serde", ] [[package]] @@ -2395,6 +2889,49 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "pest_meta" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" +dependencies = [ + "pest", + "sha2", +] + [[package]] name = "petgraph" version = "0.8.3" @@ -2404,6 +2941,7 @@ dependencies = [ "fixedbitset", "hashbrown 0.15.5", "indexmap 2.12.1", + "serde", ] [[package]] @@ -2429,7 +2967,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2488,6 +3026,15 @@ dependencies = [ "zerovec", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "presser" version = "0.3.1" @@ -2552,6 +3099,63 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core 0.6.4", + "serde", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "serde", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "range-alloc" version = "0.1.4" @@ -2693,7 +3297,7 @@ name = "rspirv" version = "0.12.0+sdk-1.4.335.0" source = "git+https://github.com/gfx-rs/rspirv?branch=sdk-update#6a8d3db93205311ffb156793727df8f348bd3099" dependencies = [ - "rustc-hash", + "rustc-hash 1.1.0", "spirv", ] @@ -2716,6 +3320,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_codegen_spirv" version = "0.9.0" @@ -2803,7 +3413,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2952,7 +3562,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2961,6 +3571,7 @@ version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ + "indexmap 2.12.1", "itoa", "memchr", "ryu", @@ -2986,6 +3597,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -3023,6 +3645,16 @@ dependencies = [ "spirv-std", ] +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + [[package]] name = "sky-shader" version = "0.0.0" @@ -3104,7 +3736,7 @@ dependencies = [ "itertools 0.10.5", "lazy_static", "longest-increasing-subsequence", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", "smallvec", @@ -3157,7 +3789,7 @@ dependencies = [ "proc-macro2", "quote", "spirv-std-types", - "syn", + "syn 2.0.111", ] [[package]] @@ -3166,22 +3798,41 @@ version = "0.9.0" [[package]] name = "spirv-tools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863f14733a4ecb68c0bde9ea9b93bc58085a4172e76c8cefe7c810c940f02131" +version = "0.1.0" +source = "git+https://github.com/Rust-GPU/SPIRV-Tools#a6840f810b2feb5511d979ca81c703fe9b7913cd" dependencies = [ - "memchr", - "spirv-tools-sys", - "tempfile", + "rspirv", + "spirv-tools-core", + "spirv-tools-opt", ] [[package]] -name = "spirv-tools-sys" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219df977b2dd5a34a3529a7f7d2be12727abd87e4545abd0d54edd4fa2cfe5a8" +name = "spirv-tools-core" +version = "0.1.0" +source = "git+https://github.com/Rust-GPU/SPIRV-Tools#a6840f810b2feb5511d979ca81c703fe9b7913cd" dependencies = [ - "cc", + "bitflags 2.10.0", + "heck 0.4.1", + "libc", + "once_cell", + "rspirv", + "serde", + "serde_json", + "spirv", + "thiserror 1.0.69", +] + +[[package]] +name = "spirv-tools-opt" +version = "0.1.0" +source = "git+https://github.com/Rust-GPU/SPIRV-Tools#a6840f810b2feb5511d979ca81c703fe9b7913cd" +dependencies = [ + "arbitrary", + "clap", + "egglog", + "rspirv", + "spirv-tools-core", + "thiserror 1.0.69", ] [[package]] @@ -3223,10 +3874,21 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ - "heck", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ "proc-macro2", "quote", - "syn", + "unicode-ident", ] [[package]] @@ -3248,7 +3910,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3271,7 +3933,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3342,7 +4004,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3353,7 +4015,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3515,7 +4177,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3604,6 +4266,18 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -3703,10 +4377,10 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef06db404cbaed87cb25fd2ca3a62502af485f43383c9641ffcf1479d02fffd" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3789,7 +4463,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.111", "wasm-bindgen-shared", ] @@ -4087,7 +4761,7 @@ dependencies = [ "portable-atomic", "profiling", "raw-window-handle 0.6.2", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", "thiserror 2.0.17", "wgpu-core-deps-apple", @@ -4209,7 +4883,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4249,7 +4923,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4260,7 +4934,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4681,7 +5355,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", "synstructure", ] @@ -4702,7 +5376,7 @@ checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4722,7 +5396,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", "synstructure", ] @@ -4756,5 +5430,5 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] diff --git a/Cargo.toml b/Cargo.toml index 6eecc5da9f..be9f4df1ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ spirv-builder = { path = "./crates/spirv-builder", version = "=0.9.0", default-f spirv-std = { path = "./crates/spirv-std", version = "=0.9.0" } spirv-std-types = { path = "./crates/spirv-std/shared", version = "=0.9.0" } spirv-std-macros = { path = "./crates/spirv-std/macros", version = "=0.9.0" } -spirv-tools = { version = "0.13.0", default-features = false } +spirv-tools = { git = "https://github.com/Rust-GPU/SPIRV-Tools", default-features = false } rustc_codegen_spirv = { path = "./crates/rustc_codegen_spirv", version = "=0.9.0", default-features = false } rustc_codegen_spirv-types = { path = "./crates/rustc_codegen_spirv-types", version = "=0.9.0" } From e20ef27b79bf97c175d164ffb02ceb0c830b7bc2 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Sat, 24 Jan 2026 14:55:32 -0800 Subject: [PATCH 03/13] =?UTF-8?q?Update=20for=20rspirv=20sdk-update=20bran?= =?UTF-8?q?ch=20API=20changes=20Adapt=20to=20changes=20in=20rspirv=200.12.?= =?UTF-8?q?0+sdk-1.4.335.0:=20-=20Replace=20`rspirv::grammar::reflect::is?= =?UTF-8?q?=5Ftype(op)`=20with=20`op.is=5Ftype()`=20-=20Replace=20`rspirv:?= =?UTF-8?q?:grammar::reflect::is=5Fconstant(op)`=20with=20`op.is=5Fconstan?= =?UTF-8?q?t()`=20-=20Update=20`type=5Ffloat=5Fid`=20to=20include=20FPEnco?= =?UTF-8?q?ding=20parameter=20(None)=20-=20Change=20`MemorySemantics::NONE?= =?UTF-8?q?`=20to=20`MemorySemantics::RELAXED`=20-=20Change=20`Decoration:?= =?UTF-8?q?:UserTypeGOOGLE`=20to=20`Decoration::UserSemantic`=20-=20Update?= =?UTF-8?q?=20ExecutionMode=20NV=20suffixes=20to=20EXT/KHR=20(OutputLinesN?= =?UTF-8?q?V=20=E2=86=92=20OutputLinesEXT,=20etc.)=20-=20Add=20aliases=20f?= =?UTF-8?q?or=20DemoteToHelperInvocationEXT=20and=20IsHelperInvocationEXT?= =?UTF-8?q?=20-=20Update=20ImageOperands=20and=20MemorySemantics=20KHR=20c?= =?UTF-8?q?onstants=20(now=20unified)=20-=20Add=20stubs=20for=20new=20oper?= =?UTF-8?q?and=20kinds=20(RawAccessChainOperands,=20CooperativeMatrixReduc?= =?UTF-8?q?e,=20etc.)=20-=20Update=20renamed=20ops=20(FinalizeNodePayloads?= =?UTF-8?q?AMDX=20=E2=86=92=20EnqueueNodePayloadsAMDX,=20etc.)=20-=20Add?= =?UTF-8?q?=20catch-all=20for=20new=20ops=20in=20spirv=5Ftype=5Fconstraint?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/builder/builder_methods.rs | 2 +- .../src/builder/spirv_asm.rs | 64 +++++++++++++++---- .../src/custom_decorations.rs | 11 ++-- .../rustc_codegen_spirv/src/linker/inline.rs | 4 +- .../src/linker/spirt_passes/diagnostics.rs | 2 +- .../src/linker/spirt_passes/mod.rs | 2 +- crates/rustc_codegen_spirv/src/spirv_type.rs | 2 +- .../src/spirv_type_constraints.rs | 6 +- crates/rustc_codegen_spirv/src/symbols.rs | 8 +-- 9 files changed, 73 insertions(+), 28 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index 840a206174..82c79b0bcf 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -341,7 +341,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn ordering_to_semantics_def(&mut self, ordering: AtomicOrdering) -> SpirvValue { let mut invalid_seq_cst = false; let semantics = match ordering { - AtomicOrdering::Relaxed => MemorySemantics::NONE, + AtomicOrdering::Relaxed => MemorySemantics::RELAXED, AtomicOrdering::Acquire => MemorySemantics::MAKE_VISIBLE | MemorySemantics::ACQUIRE, AtomicOrdering::Release => MemorySemantics::MAKE_AVAILABLE | MemorySemantics::RELEASE, AtomicOrdering::AcqRel => { diff --git a/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs b/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs index 894c5a8d02..6b8c0677af 100644 --- a/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs +++ b/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs @@ -32,9 +32,23 @@ pub struct InstructionTable { impl InstructionTable { pub fn new() -> Self { - let table = rspirv::grammar::CoreInstructionTable::iter() + let mut table: FxHashMap<_, _> = rspirv::grammar::CoreInstructionTable::iter() .map(|inst| (inst.opname, inst)) .collect(); + + // Add aliases for EXT/KHR instructions whose suffixes were removed + for inst in rspirv::grammar::CoreInstructionTable::iter() { + match inst.opname { + "DemoteToHelperInvocation" => { + table.insert("DemoteToHelperInvocationEXT", inst); + } + "IsHelperInvocation" => { + table.insert("IsHelperInvocationEXT", inst); + } + _ => {} + } + } + Self { table } } } @@ -1529,6 +1543,32 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> { Ok(x) => inst.operands.push(dr::Operand::StoreCacheControl(x)), Err(()) => self.err(format!("unknown StoreCacheControl {word}")), }, + // New operand kinds added in newer SPIR-V versions - not yet supported in inline asm + (OperandKind::RawAccessChainOperands, Some(word)) => { + self.err(format!("RawAccessChainOperands not yet supported: {word}")); + } + (OperandKind::CooperativeMatrixReduce, Some(word)) => { + self.err(format!("CooperativeMatrixReduce not yet supported: {word}")); + } + (OperandKind::TensorClampMode, Some(word)) => { + self.err(format!("TensorClampMode not yet supported: {word}")); + } + (OperandKind::TensorAddressingOperands, Some(word)) => { + self.err(format!("TensorAddressingOperands not yet supported: {word}")); + } + (OperandKind::FPEncoding, Some(word)) => { + self.err(format!("FPEncoding not yet supported: {word}")); + } + (OperandKind::NamedMaximumNumberOfRegisters, Some(word)) => { + self.err(format!("NamedMaximumNumberOfRegisters not yet supported: {word}")); + } + (OperandKind::MatrixMultiplyAccumulateOperands, Some(word)) => { + self.err(format!("MatrixMultiplyAccumulateOperands not yet supported: {word}")); + } + // Catch-all for any other new operand kinds + (kind, Some(word)) => { + self.err(format!("unsupported operand kind {kind:?}: {word}")); + } (kind, None) => match token { Token::Word(_) => bug!(), Token::String(_) => { @@ -1565,14 +1605,14 @@ pub const IMAGE_OPERANDS: &[(&str, ImageOperands)] = &[ ("MakeTexelAvailable", ImageOperands::MAKE_TEXEL_AVAILABLE), ( "MakeTexelAvailableKHR", - ImageOperands::MAKE_TEXEL_AVAILABLE_KHR, + ImageOperands::MAKE_TEXEL_AVAILABLE, ), ("MakeTexelVisible", ImageOperands::MAKE_TEXEL_VISIBLE), - ("MakeTexelVisibleKHR", ImageOperands::MAKE_TEXEL_VISIBLE_KHR), + ("MakeTexelVisibleKHR", ImageOperands::MAKE_TEXEL_VISIBLE), ("NonPrivateTexel", ImageOperands::NON_PRIVATE_TEXEL), - ("NonPrivateTexelKHR", ImageOperands::NON_PRIVATE_TEXEL_KHR), + ("NonPrivateTexelKHR", ImageOperands::NON_PRIVATE_TEXEL), ("VolatileTexel", ImageOperands::VOLATILE_TEXEL), - ("VolatileTexelKHR", ImageOperands::VOLATILE_TEXEL_KHR), + ("VolatileTexelKHR", ImageOperands::VOLATILE_TEXEL), ("SignExtend", ImageOperands::SIGN_EXTEND), ("ZeroExtend", ImageOperands::ZERO_EXTEND), ]; @@ -1610,7 +1650,7 @@ pub const FUNCTION_CONTROL: &[(&str, FunctionControl)] = &[ ]; pub const MEMORY_SEMANTICS: &[(&str, MemorySemantics)] = &[ ("Relaxed", MemorySemantics::RELAXED), - ("None", MemorySemantics::NONE), + ("None", MemorySemantics::RELAXED), ("Acquire", MemorySemantics::ACQUIRE), ("Release", MemorySemantics::RELEASE), ("AcquireRelease", MemorySemantics::ACQUIRE_RELEASE), @@ -1631,11 +1671,11 @@ pub const MEMORY_SEMANTICS: &[(&str, MemorySemantics)] = &[ ), ("ImageMemory", MemorySemantics::IMAGE_MEMORY), ("OutputMemory", MemorySemantics::OUTPUT_MEMORY), - ("OutputMemoryKHR", MemorySemantics::OUTPUT_MEMORY_KHR), + ("OutputMemoryKHR", MemorySemantics::OUTPUT_MEMORY), ("MakeAvailable", MemorySemantics::MAKE_AVAILABLE), - ("MakeAvailableKHR", MemorySemantics::MAKE_AVAILABLE_KHR), + ("MakeAvailableKHR", MemorySemantics::MAKE_AVAILABLE), ("MakeVisible", MemorySemantics::MAKE_VISIBLE), - ("MakeVisibleKHR", MemorySemantics::MAKE_VISIBLE_KHR), + ("MakeVisibleKHR", MemorySemantics::MAKE_VISIBLE), ("Volatile", MemorySemantics::VOLATILE), ]; pub const MEMORY_ACCESS: &[(&str, MemoryAccess)] = &[ @@ -1646,17 +1686,17 @@ pub const MEMORY_ACCESS: &[(&str, MemoryAccess)] = &[ ("MakePointerAvailable", MemoryAccess::MAKE_POINTER_AVAILABLE), ( "MakePointerAvailableKHR", - MemoryAccess::MAKE_POINTER_AVAILABLE_KHR, + MemoryAccess::MAKE_POINTER_AVAILABLE, ), ("MakePointerVisible", MemoryAccess::MAKE_POINTER_VISIBLE), ( "MakePointerVisibleKHR", - MemoryAccess::MAKE_POINTER_VISIBLE_KHR, + MemoryAccess::MAKE_POINTER_VISIBLE, ), ("NonPrivatePointer", MemoryAccess::NON_PRIVATE_POINTER), ( "NonPrivatePointerKHR", - MemoryAccess::NON_PRIVATE_POINTER_KHR, + MemoryAccess::NON_PRIVATE_POINTER, ), ]; pub const KERNEL_PROFILING_INFO: &[(&str, KernelProfilingInfo)] = &[ diff --git a/crates/rustc_codegen_spirv/src/custom_decorations.rs b/crates/rustc_codegen_spirv/src/custom_decorations.rs index 1027df9aed..c8efd57c95 100644 --- a/crates/rustc_codegen_spirv/src/custom_decorations.rs +++ b/crates/rustc_codegen_spirv/src/custom_decorations.rs @@ -18,7 +18,7 @@ use std::{fmt, iter, slice, str}; /// Decorations not native to SPIR-V require some form of encoding into existing /// SPIR-V constructs, for which we use `OpDecorateString` with decoration type -/// `UserTypeGOOGLE` and some encoded Rust value as the decoration string. +/// `UserSemantic` and some encoded Rust value as the decoration string. /// /// Each decoration type has to implement this trait, and use a different /// `ENCODING_PREFIX` from any other decoration type, to disambiguate them. @@ -27,7 +27,10 @@ use std::{fmt, iter, slice, str}; /// ideally as soon as they're no longer needed, because no other tools /// processing the SPIR-V would understand them correctly. /// -/// TODO: uses `non_semantic` instead of piggybacking off of `UserTypeGOOGLE` +/// Custom decorations are encoded using `OpDecorateString` with `UserSemantic`, +/// which is a standard SPIR-V decoration that doesn't require any extension. +/// +/// TODO: consider using `non_semantic` instead for even cleaner separation /// pub trait CustomDecoration<'a>: Sized { const ENCODING_PREFIX: &'static str; @@ -45,7 +48,7 @@ pub trait CustomDecoration<'a>: Sized { None, vec![ Operand::IdRef(id), - Operand::Decoration(Decoration::UserTypeGOOGLE), + Operand::Decoration(Decoration::UserSemantic), Operand::LiteralString(encoded), ], ) @@ -53,7 +56,7 @@ pub trait CustomDecoration<'a>: Sized { fn try_decode_from_inst(inst: &Instruction) -> Option<(Word, LazilyDecoded<'_, Self>)> { if inst.class.opcode == Op::DecorateString - && inst.operands[1].unwrap_decoration() == Decoration::UserTypeGOOGLE + && inst.operands[1].unwrap_decoration() == Decoration::UserSemantic { let id = inst.operands[0].unwrap_id_ref(); let prefixed_encoded = inst.operands[2].unwrap_literal_string(); diff --git a/crates/rustc_codegen_spirv/src/linker/inline.rs b/crates/rustc_codegen_spirv/src/linker/inline.rs index 0ab19bd40f..edb31c485d 100644 --- a/crates/rustc_codegen_spirv/src/linker/inline.rs +++ b/crates/rustc_codegen_spirv/src/linker/inline.rs @@ -298,8 +298,8 @@ impl LegalGlobal { let global = match inst.class.opcode { Op::TypePointer => Self::TypePointer(inst.operands[0].unwrap_storage_class()), Op::Variable => Self::Variable, - op if rspirv::grammar::reflect::is_type(op) => Self::TypeNonPointer, - op if rspirv::grammar::reflect::is_constant(op) => Self::Const, + op if op.is_type() => Self::TypeNonPointer, + op if op.is_constant() => Self::Const, // FIXME(eddyb) should this be `unreachable!()`? _ => continue, diff --git a/crates/rustc_codegen_spirv/src/linker/spirt_passes/diagnostics.rs b/crates/rustc_codegen_spirv/src/linker/spirt_passes/diagnostics.rs index 3951a7f8c0..11cdb6ccb8 100644 --- a/crates/rustc_codegen_spirv/src/linker/spirt_passes/diagnostics.rs +++ b/crates/rustc_codegen_spirv/src/linker/spirt_passes/diagnostics.rs @@ -128,7 +128,7 @@ fn try_decode_custom_decoration<'a, D: CustomDecoration<'a>>( }; let str_imms = spv_inst .imms - .strip_prefix(&[spv::Imm::Short(wk.Decoration, wk.UserTypeGOOGLE)])?; + .strip_prefix(&[spv::Imm::Short(wk.Decoration, wk.UserSemantic)])?; decode_spv_lit_str_with(str_imms, |prefixed_encoded| { let encoded = prefixed_encoded.strip_prefix(D::ENCODING_PREFIX)?; diff --git a/crates/rustc_codegen_spirv/src/linker/spirt_passes/mod.rs b/crates/rustc_codegen_spirv/src/linker/spirt_passes/mod.rs index f41dc5aa4d..2f78875be4 100644 --- a/crates/rustc_codegen_spirv/src/linker/spirt_passes/mod.rs +++ b/crates/rustc_codegen_spirv/src/linker/spirt_passes/mod.rs @@ -113,7 +113,7 @@ def_spv_spec_with_extra_well_known! { MemoryAccess, ], decoration: u32 = [ - UserTypeGOOGLE, + UserSemantic, MatrixStride, ], storage_class: u32 = [ diff --git a/crates/rustc_codegen_spirv/src/spirv_type.rs b/crates/rustc_codegen_spirv/src/spirv_type.rs index 3213b13129..6bc31b27e7 100644 --- a/crates/rustc_codegen_spirv/src/spirv_type.rs +++ b/crates/rustc_codegen_spirv/src/spirv_type.rs @@ -104,7 +104,7 @@ impl SpirvType<'_> { Self::Void => cx.emit_global().type_void_id(id), Self::Bool => cx.emit_global().type_bool_id(id), Self::Integer(width, signed) => cx.emit_global().type_int_id(id, width, signed as u32), - Self::Float(width) => cx.emit_global().type_float_id(id, width), + Self::Float(width) => cx.emit_global().type_float_id(id, width, None), Self::Adt { def_id: _, align: _, diff --git a/crates/rustc_codegen_spirv/src/spirv_type_constraints.rs b/crates/rustc_codegen_spirv/src/spirv_type_constraints.rs index c838fc4b54..9a60d50435 100644 --- a/crates/rustc_codegen_spirv/src/spirv_type_constraints.rs +++ b/crates/rustc_codegen_spirv/src/spirv_type_constraints.rs @@ -1096,9 +1096,9 @@ pub fn instruction_signatures(op: Op) -> Option<&'static [InstSig<'static>]> { | Op::ImageBlockMatchSSDQCOM | Op::ImageBlockMatchSADQCOM => reserved!(SPV_QCOM_image_processing), // SPV_AMDX_shader_enqueue - Op::FinalizeNodePayloadsAMDX + Op::EnqueueNodePayloadsAMDX | Op::FinishWritingNodePayloadAMDX - | Op::InitializeNodePayloadsAMDX => reserved!(SPV_AMDX_shader_enqueue), + | Op::AllocateNodePayloadsAMDX => reserved!(SPV_AMDX_shader_enqueue), // SPV_NV_displacement_micromap Op::FetchMicroTriangleVertexPositionNV | Op::FetchMicroTriangleVertexBarycentricNV => { reserved!(SPV_NV_displacement_micromap) @@ -1124,6 +1124,8 @@ pub fn instruction_signatures(op: Op) -> Option<&'static [InstSig<'static>]> { | Op::SpecConstantCompositeContinuedINTEL | Op::ControlBarrierArriveINTEL | Op::ControlBarrierWaitINTEL => reserved!(unknown_extension_INTEL), + // Catch-all for any new ops added in newer SPIR-V versions + _ => return None, } None diff --git a/crates/rustc_codegen_spirv/src/symbols.rs b/crates/rustc_codegen_spirv/src/symbols.rs index 7660f37b5a..3160e54bcc 100644 --- a/crates/rustc_codegen_spirv/src/symbols.rs +++ b/crates/rustc_codegen_spirv/src/symbols.rs @@ -271,10 +271,10 @@ const EXECUTION_MODES: &[(&str, ExecutionMode, ExecutionModeExtraDim)] = { ("rounding_mode_rte", RoundingModeRTE, Value), ("rounding_mode_rtz", RoundingModeRTZ, Value), ("stencil_ref_replacing_ext", StencilRefReplacingEXT, None), - ("output_lines_nv", OutputLinesNV, None), - ("output_primitives_nv", OutputPrimitivesNV, Value), - ("derivative_group_quads_nv", DerivativeGroupQuadsNV, None), - ("output_triangles_nv", OutputTrianglesNV, None), + ("output_lines_nv", OutputLinesEXT, None), + ("output_primitives_nv", OutputPrimitivesEXT, Value), + ("derivative_group_quads_nv", DerivativeGroupQuadsKHR, None), + ("output_triangles_nv", OutputTrianglesEXT, None), ("output_lines_ext", ExecutionMode::OutputLinesEXT, None), ( "output_triangles_ext", From 3e1f37b37c3e5608af93314c86080dfc9cd3ea55 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Sat, 24 Jan 2026 14:55:44 -0800 Subject: [PATCH 04/13] Add rich validation error messages with source spans Improve spirv-val error messages by looking up source locations from SrcLocDecoration annotations: - Add validation_err.rs module to parse validation errors and look up source spans from the SPIR-V module's custom SrcLocDecoration - Strip SrcLocDecoration and ZombieDecoration before validation (these are invalid on functions) but keep the original module for span lookup - Add --preserve-debug-decorations flag to optionally keep decorations - Add SrcLocDecoration to entry point interface variables so validation errors for conflicting locations show the relevant source spans - Keep decorations in linker output (they're stripped after validation) For location conflict errors, the error message now shows: - The specific variable causing the conflict with its source location - The conflicting variable with its source location - A help message explaining why the conflict occurs (e.g., Mat4 uses 4 locations) --- .../src/codegen_cx/entry.rs | 9 + .../rustc_codegen_spirv/src/codegen_cx/mod.rs | 12 + crates/rustc_codegen_spirv/src/lib.rs | 1 + crates/rustc_codegen_spirv/src/link.rs | 40 ++- crates/rustc_codegen_spirv/src/linker/mod.rs | 13 +- .../rustc_codegen_spirv/src/validation_err.rs | 315 ++++++++++++++++++ 6 files changed, 375 insertions(+), 15 deletions(-) create mode 100644 crates/rustc_codegen_spirv/src/validation_err.rs diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs index e78175785e..720bf0ba81 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/entry.rs @@ -6,6 +6,7 @@ use crate::abi::ConvSpirvType; use crate::attr::{AggregatedSpirvAttributes, Entry, Spanned, SpecConstant}; use crate::builder::Builder; use crate::builder_spirv::{SpirvFunctionCursor, SpirvValue, SpirvValueExt}; +use crate::custom_decorations::{CustomDecoration, SrcLocDecoration}; use crate::spirv_type::SpirvType; use rspirv::dr::Operand; use rspirv::spirv::{ @@ -988,6 +989,14 @@ impl<'tcx> CodegenCx<'tcx> { self.emit_global() .variable(var_ptr_spirv_type, Some(var), storage_class, None); + // Add SrcLocDecoration to the variable for rich error messages. + let src_loc_inst = SrcLocDecoration::from_rustc_span(hir_param.span, &self.builder) + .map(|src_loc| src_loc.encode_to_inst(var)); + self.emit_global() + .module_mut() + .annotations + .extend(src_loc_inst); + // Record this `OpVariable` as needing to be added (if applicable), // to the *Interface* operands of the `OpEntryPoint` instruction. if self.emit_global().version().unwrap() > (1, 3) { diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs index d36e789b1c..3fefc0ed12 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs @@ -313,6 +313,10 @@ pub struct CodegenArgs { pub run_spirv_opt: bool, + /// Preserve internal debug decorations (SrcLocDecoration, ZombieDecoration) in the output. + /// These are normally stripped after validation. Enable for debugging or testing. + pub preserve_debug_decorations: bool, + /// All options pertinent to `rustc_codegen_spirv::linker` specifically. // // FIXME(eddyb) should these be handled as `-C linker-args="..."` instead? @@ -384,6 +388,12 @@ impl CodegenArgs { "disables running spirv-opt on the final output", ); + opts.optflag( + "", + "preserve-debug-decorations", + "preserve internal debug decorations (SrcLocDecoration, ZombieDecoration) in the output", + ); + opts.optflag( "", "preserve-bindings", @@ -652,6 +662,8 @@ impl CodegenArgs { run_spirv_opt, + preserve_debug_decorations: matches.opt_present("preserve-debug-decorations"), + linker_opts, // NOTE(eddyb) these are debugging options that used to be env vars diff --git a/crates/rustc_codegen_spirv/src/lib.rs b/crates/rustc_codegen_spirv/src/lib.rs index 3e57d8648d..c844c818e3 100644 --- a/crates/rustc_codegen_spirv/src/lib.rs +++ b/crates/rustc_codegen_spirv/src/lib.rs @@ -138,6 +138,7 @@ mod spirv_type_constraints; mod symbols; mod target; mod target_feature; +mod validation_err; use builder::Builder; use codegen_cx::CodegenCx; diff --git a/crates/rustc_codegen_spirv/src/link.rs b/crates/rustc_codegen_spirv/src/link.rs index 038d307930..8fef464722 100644 --- a/crates/rustc_codegen_spirv/src/link.rs +++ b/crates/rustc_codegen_spirv/src/link.rs @@ -253,8 +253,27 @@ fn post_link_single_module( out_filename: &Path, dump_prefix: Option<&OsStr>, ) { - cg_args.do_disassemble(&module); - let spv_binary = module.assemble(); + use crate::custom_decorations::{CustomDecoration, SrcLocDecoration, ZombieDecoration}; + + // Strip internal debug decorations (SrcLocDecoration, ZombieDecoration) BEFORE + // disassembly/validation/optimization unless preserve_debug_decorations is set. + // These decorations are only valid on certain targets (variables), but rust-gpu + // also places them on functions for span tracking which is invalid SPIR-V. + // Keep the original module for span lookup in error messages. + let (module_for_output, original_module_for_spans) = if cg_args.preserve_debug_decorations { + // When preserving decorations, use the same module for both + (module.clone(), module) + } else { + let mut stripped = module.clone(); + SrcLocDecoration::remove_all(&mut stripped); + ZombieDecoration::remove_all(&mut stripped); + (stripped, module) + }; + + // Disassemble the stripped module (unless preserving decorations) + cg_args.do_disassemble(&module_for_output); + let spv_binary = module_for_output.assemble(); + let original_binary_for_spans = original_module_for_spans.assemble(); if let Some(dir) = &cg_args.dump_post_link { // FIXME(eddyb) rename `filename` with `file_path` to make this less confusing. @@ -305,7 +324,7 @@ fn post_link_single_module( }; if cg_args.run_spirv_val { - do_spirv_val(sess, &spv_binary, out_filename, val_options); + do_spirv_val(sess, &spv_binary, &original_binary_for_spans, out_filename, val_options); } { @@ -393,18 +412,25 @@ fn do_spirv_opt( fn do_spirv_val( sess: &Session, spv_binary: &[u32], + original_binary_for_spans: &[u32], filename: &Path, options: spirv_tools::val::ValidatorOptions, ) { + use crate::validation_err::ValidationErrorContext; use spirv_tools::val::{self, Validator}; let validator = val::create(sess.target.options.env.parse().ok()); if let Err(e) = validator.validate(spv_binary, Some(options)) { - let mut err = sess.dcx().struct_err(e.to_string()); - err.note("spirv-val failed"); - err.note(format!("module `{}`", filename.display())); - err.emit(); + // Parse the ORIGINAL binary (with SrcLocDecoration) to look up source spans + // Note: We validate the stripped binary, but look up spans in the original + let module = with_rspirv_loader(|loader| { + rspirv::binary::parse_words(original_binary_for_spans, loader) + }) + .ok(); + + let mut ctx = ValidationErrorContext::new(sess, module.as_ref(), filename); + ctx.emit_error(&e); } } diff --git a/crates/rustc_codegen_spirv/src/linker/mod.rs b/crates/rustc_codegen_spirv/src/linker/mod.rs index b6e3b9a05f..c74ea25871 100644 --- a/crates/rustc_codegen_spirv/src/linker/mod.rs +++ b/crates/rustc_codegen_spirv/src/linker/mod.rs @@ -19,7 +19,7 @@ mod zombies; use std::borrow::Cow; use crate::codegen_cx::{ModuleOutputType, SpirvMetadata}; -use crate::custom_decorations::{CustomDecoration, SrcLocDecoration, ZombieDecoration}; +// Note: SrcLocDecoration and ZombieDecoration are stripped in link.rs after validation use crate::custom_insts; use either::Either; use rspirv::binary::Assemble; @@ -768,13 +768,10 @@ pub fn link( output.header.as_mut().unwrap().bound = simple_passes::compact_ids(output); }; - // FIXME(eddyb) convert these into actual `OpLine`s with a SPIR-T pass, - // but that'd require keeping the modules in SPIR-T form (once lowered), - // and never loading them back into `rspirv` once lifted back to SPIR-V. - SrcLocDecoration::remove_all(output); - - // FIXME(eddyb) might make more sense to rewrite these away on SPIR-T. - ZombieDecoration::remove_all(output); + // NOTE: We keep SrcLocDecoration and ZombieDecoration in the module + // so that they can be used for rich error messages during validation. + // These are encoded as OpDecorateString with UserSemantic, which is + // valid SPIR-V and should not cause validation failures. } Ok(output) diff --git a/crates/rustc_codegen_spirv/src/validation_err.rs b/crates/rustc_codegen_spirv/src/validation_err.rs new file mode 100644 index 0000000000..69d1841e19 --- /dev/null +++ b/crates/rustc_codegen_spirv/src/validation_err.rs @@ -0,0 +1,315 @@ +//! Rich error reporting for SPIR-V validation errors. +//! +//! This module provides utilities for converting SPIR-V validation errors into +//! user-friendly rustc diagnostics with source spans and helpful hints. + +use std::path::Path; + +use rspirv::dr::Module; +use rspirv::spirv::{Decoration, Op}; +use rustc_session::Session; +use spirv_tools::ValidationError; + +use crate::custom_decorations::SpanRegenerator; + +/// Context for generating rich validation error messages. +pub struct ValidationErrorContext<'a> { + sess: &'a Session, + module: Option<&'a Module>, + span_regen: Option>, + filename: &'a Path, +} + +impl<'a> ValidationErrorContext<'a> { + /// Creates a new validation error context. + pub fn new( + sess: &'a Session, + module: Option<&'a Module>, + filename: &'a Path, + ) -> Self { + let span_regen = module.map(|m| SpanRegenerator::new(sess.source_map(), m)); + Self { + sess, + module, + span_regen, + filename, + } + } + + /// Emits a rich diagnostic for the given validation error. + pub fn emit_error(&mut self, error: &spirv_tools::val::ValidatorError) { + match &error.validation_error { + ValidationError::EntryPointInterfaceLocationConflict { + first_var, + second_var, + location, + component, + storage_class, + .. + } => { + self.emit_location_conflict( + u32::from(*first_var), + u32::from(*second_var), + *location, + *component, + *storage_class, + ); + } + _ => { + // Fall back to generic error message + self.emit_generic_error(error); + } + } + } + + /// Emits a generic validation error without rich formatting. + fn emit_generic_error(&self, error: &spirv_tools::val::ValidatorError) { + let mut err = self.sess.dcx().struct_err(error.to_string()); + err.note("spirv-val failed"); + err.note(format!("module `{}`", self.filename.display())); + err.emit(); + } + + fn emit_location_conflict( + &mut self, + first_var_id: u32, + second_var_id: u32, + location: u32, + component: u32, + storage_class: rspirv::spirv::StorageClass, + ) { + let first_span = self.id_to_span(first_var_id); + let second_span = self.id_to_span(second_var_id); + + let first_name = self.get_name(first_var_id); + let second_name = self.get_name(second_var_id); + + let first_var_start_location = self.get_location_decoration(first_var_id); + let first_var_type_info = self.get_variable_type_info(first_var_id); + + let first_name_fallback = format!("%{}", first_var_id); + let second_name_fallback = format!("%{}", second_var_id); + let first_name_display = first_name.as_deref().unwrap_or(&first_name_fallback); + let second_name_display = second_name.as_deref().unwrap_or(&second_name_fallback); + + if let (Some(span1), Some(span2)) = (first_span, second_span) { + // Rich error with source spans + let mut err = self.sess.dcx().struct_span_err( + span2, + format!( + "{:?} variable `{}` at location {} conflicts with another variable", + storage_class, second_name_display, location + ), + ); + + err.span_note( + span1, + format!( + "variable `{}` already uses location {} component {}", + first_name_display, location, component + ), + ); + + // Add hint if the first variable spans multiple locations + if let Some(start_loc) = first_var_start_location { + if start_loc != location { + err.help(self.format_multi_location_hint( + first_name_display, + start_loc, + first_var_type_info.as_ref(), + )); + } + } + + err.note("spirv-val failed"); + err.note(format!("module `{}`", self.filename.display())); + err.emit(); + } else { + // Fall back to non-span error + let mut err = self.sess.dcx().struct_err(format!( + "{:?} variable `{}` at location {} conflicts with `{}`", + storage_class, second_name_display, location, first_name_display + )); + err.note("spirv-val failed"); + err.note(format!("module `{}`", self.filename.display())); + err.note(format!( + "variables `{}` and `{}` both use location {} component {}", + first_name_display, second_name_display, location, component + )); + err.emit(); + } + } + + /// Looks up the rustc Span for a SPIR-V ID. + fn id_to_span(&mut self, id: u32) -> Option { + self.span_regen.as_mut().and_then(|sr| { + sr.src_loc_for_id(id) + .and_then(|src_loc| sr.src_loc_to_rustc(src_loc)) + }) + } + + /// Gets the name of a SPIR-V ID from OpName. + fn get_name(&self, id: u32) -> Option { + self.module.and_then(|m| { + m.debug_names.iter().find_map(|inst| { + if inst.class.opcode == Op::Name && inst.operands.first()?.unwrap_id_ref() == id { + Some(inst.operands.get(1)?.unwrap_literal_string().to_string()) + } else { + None + } + }) + }) + } + + /// Gets the Location decoration value for a variable. + fn get_location_decoration(&self, var_id: u32) -> Option { + self.module.and_then(|m| { + m.annotations.iter().find_map(|inst| { + if inst.class.opcode == Op::Decorate + && inst.operands.first()?.unwrap_id_ref() == var_id + && inst.operands.get(1)?.unwrap_decoration() == Decoration::Location + { + Some(inst.operands.get(2)?.unwrap_literal_bit32()) + } else { + None + } + }) + }) + } + + /// Gets type information for a variable (type name and location count). + fn get_variable_type_info(&self, var_id: u32) -> Option { + let m = self.module?; + + // Find the OpVariable + let var_inst = m.types_global_values.iter().find(|inst| { + inst.class.opcode == Op::Variable && inst.result_id == Some(var_id) + })?; + + // Get the pointer type, then the pointee type + let ptr_type_id = var_inst.result_type?; + let ptr_type = m + .types_global_values + .iter() + .find(|inst| inst.result_id == Some(ptr_type_id))?; + let pointee_type_id = ptr_type.operands.get(1)?.unwrap_id_ref(); + + // Look up the type name + let type_name = m.debug_names.iter().find_map(|inst| { + if inst.class.opcode == Op::Name + && inst.operands.first()?.unwrap_id_ref() == pointee_type_id + { + Some(inst.operands.get(1)?.unwrap_literal_string().to_string()) + } else { + None + } + }); + + // Compute location count + let location_count = Self::count_type_locations(m, pointee_type_id); + + Some(TypeInfo { + name: type_name, + location_count, + }) + } + + /// Counts the number of interface locations consumed by a type. + fn count_type_locations(m: &Module, type_id: u32) -> Option { + let type_inst = m + .types_global_values + .iter() + .find(|inst| inst.result_id == Some(type_id))?; + + match type_inst.class.opcode { + // Scalars and vectors use 1 location + Op::TypeBool | Op::TypeInt | Op::TypeFloat | Op::TypeVector => Some(1), + + // Matrices use 1 location per column + Op::TypeMatrix => { + let column_count = type_inst.operands.get(1)?.unwrap_literal_bit32(); + Some(column_count) + } + + // Arrays: element_locations * array_length + Op::TypeArray => { + let element_type_id = type_inst.operands.first()?.unwrap_id_ref(); + let length_id = type_inst.operands.get(1)?.unwrap_id_ref(); + let length_inst = m + .types_global_values + .iter() + .find(|inst| inst.result_id == Some(length_id))?; + let length = length_inst.operands.first()?.unwrap_literal_bit32(); + let element_locs = Self::count_type_locations(m, element_type_id)?; + Some(element_locs * length) + } + + // Structs: sum of member locations + Op::TypeStruct => { + let mut total = 0u32; + for operand in &type_inst.operands { + let member_type_id = operand.unwrap_id_ref(); + total += Self::count_type_locations(m, member_type_id)?; + } + Some(total) + } + + _ => None, + } + } + + fn format_multi_location_hint( + &self, + var_name: &str, + start_loc: u32, + type_info: Option<&TypeInfo>, + ) -> String { + let (type_name, location_count) = type_info + .map(|ti| (ti.name.as_deref(), ti.location_count)) + .unwrap_or((None, None)); + + match (type_name, location_count) { + (Some(name), Some(count)) => { + format!( + "`{}` is at location {} but type `{}` consumes {} locations ({}–{})", + var_name, + start_loc, + name, + count, + start_loc, + start_loc + count - 1 + ) + } + (Some(name), None) => { + format!( + "`{}` is at location {} but type `{}` consumes multiple locations", + var_name, start_loc, name + ) + } + (None, Some(count)) => { + format!( + "`{}` is at location {} but its type consumes {} locations ({}–{})", + var_name, + start_loc, + count, + start_loc, + start_loc + count - 1 + ) + } + (None, None) => { + format!( + "`{}` is at location {} but its type consumes multiple locations", + var_name, start_loc + ) + } + } + } +} + +/// Information about a SPIR-V type for error reporting. +struct TypeInfo { + /// The type name from OpName, if available. + name: Option, + /// The number of interface locations consumed by this type. + location_count: Option, +} From 978c8720530ca6e3403b619055cf1b1a1e7d4dca Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Sat, 24 Jan 2026 14:55:54 -0800 Subject: [PATCH 05/13] =?UTF-8?q?Update=20feature=20docs=20and=20test=20ex?= =?UTF-8?q?pectations=20for=20pure=20Rust=20spirv-tools=20Update=20Cargo.t?= =?UTF-8?q?oml=20feature=20documentation:=20-=20The=20use-compiled-tools?= =?UTF-8?q?=20and=20use-installed-tools=20features=20are=20now=20no-ops=20?= =?UTF-8?q?=20=20since=20we=20use=20a=20pure=20Rust=20implementation=20of?= =?UTF-8?q?=20spirv-tools=20-=20Keep=20features=20for=20backward=20compati?= =?UTF-8?q?bility=20with=20existing=20build=20configurations=20-=20Fix=20t?= =?UTF-8?q?ypo:=20rustc=5Fcogeden=5Fspirv=20=E2=86=92=20rustc=5Fcodegen=5F?= =?UTF-8?q?spirv=20Update=20test=20expectations:=20-=20explicit=5Foverlap.?= =?UTF-8?q?stderr:=20Now=20shows=20rich=20error=20with=20source=20spans=20?= =?UTF-8?q?-=20mesh=5Fshader.stderr:=20NV=20suffixes=20updated=20to=20EXT?= =?UTF-8?q?=20(OutputPrimitivesNV=20=E2=86=92=20OutputPrimitivesEXT,=20etc?= =?UTF-8?q?.)=20-=20member=5Fref=5Farg-broken:=20Updated=20line=20numbers?= =?UTF-8?q?=20and=20add=20normalizer=20for=20new=20error=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/rustc_codegen_spirv/Cargo.toml | 17 ++++------------- tests/compiletests/Cargo.toml | 7 +++---- .../ui/lang/core/ref/member_ref_arg-broken.rs | 1 + .../lang/core/ref/member_ref_arg-broken.stderr | 9 ++++----- .../location_assignment/explicit_overlap.stderr | 13 +++++++++++-- .../location_assignment/mesh_shader.stderr | 8 ++++---- 6 files changed, 27 insertions(+), 28 deletions(-) diff --git a/crates/rustc_codegen_spirv/Cargo.toml b/crates/rustc_codegen_spirv/Cargo.toml index 2357ae1f7f..b0e6907c39 100644 --- a/crates/rustc_codegen_spirv/Cargo.toml +++ b/crates/rustc_codegen_spirv/Cargo.toml @@ -12,20 +12,11 @@ repository.workspace = true crate-type = ["dylib"] [features] -# By default, the use-compiled-tools is enabled, as doesn't require additional -# setup steps for the user. This does however mean that you will need to disable -# default features and explicitly enable `use-installed-tools` if you are using -# this in an environment with spirv-tools in PATH, and you don't want to take -# the compile time cost -default = ["use-compiled-tools"] -# If enabled, uses spirv-tools binaries installed in PATH, instead of -# compiling and linking the spirv-tools C++ code -use-installed-tools = ["spirv-tools/use-installed-tools"] -# If enabled will compile and link the C++ code for the spirv tools, the compiled -# version is preferred if both this and `use-installed-tools` are enabled -use-compiled-tools = ["spirv-tools/use-compiled-tools"] +# These features are no-ops kept for compatibility with existing build configurations. +use-installed-tools = [] +use-compiled-tools = [] # If enabled, this will not check whether the current rustc version is set to the -# appropriate channel. rustc_cogeden_spirv requires a specific nightly version, +# appropriate channel. rustc_codegen_spirv requires a specific nightly version, # and will likely produce compile errors when built against a different toolchain. # Enable this feature to be able to experiment with other versions. skip-toolchain-check = [] diff --git a/tests/compiletests/Cargo.toml b/tests/compiletests/Cargo.toml index 8b88c9211b..6e30fca0a2 100644 --- a/tests/compiletests/Cargo.toml +++ b/tests/compiletests/Cargo.toml @@ -7,11 +7,10 @@ edition.workspace = true license.workspace = true repository.workspace = true -# See rustc_codegen_spirv/Cargo.toml for details on these features [features] -default = ["use-compiled-tools"] -use-installed-tools = ["rustc_codegen_spirv/use-installed-tools"] -use-compiled-tools = ["rustc_codegen_spirv/use-compiled-tools"] +# These features are no-ops kept for compatibility with existing build configurations. +use-installed-tools = [] +use-compiled-tools = [] [dependencies] compiletest = { version = "0.11.2", package = "compiletest_rs" } diff --git a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs b/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs index e0f13873ba..7ce9736ff3 100644 --- a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs +++ b/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs @@ -5,6 +5,7 @@ // build-fail // normalize-stderr-test "ref/member_ref_arg-broken\.[^`]*" -> "ref/member_ref_arg-broken" // normalize-stderr-test "38\[%38\]" -> "$$ID[%$$ID]" +// normalize-stderr-test "'38'" -> "'$$ID[%$$ID]'" use spirv_std::spirv; diff --git a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr b/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr index 4a41bfb462..1e6443319b 100644 --- a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr +++ b/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr @@ -1,5 +1,5 @@ warning: `#[inline(never)]` function `member_ref_arg_broken::f` has been inlined - --> $DIR/member_ref_arg-broken.rs:20:4 + --> $DIR/member_ref_arg-broken.rs:21:4 | LL | fn f(x: &u32) -> u32 { | ^ @@ -8,7 +8,7 @@ LL | fn f(x: &u32) -> u32 { = note: called from `member_ref_arg_broken::main` warning: `#[inline(never)]` function `member_ref_arg_broken::g` has been inlined - --> $DIR/member_ref_arg-broken.rs:25:4 + --> $DIR/member_ref_arg-broken.rs:26:4 | LL | fn g(xy: (&u32, &u32)) -> (u32, u32) { | ^ @@ -17,7 +17,7 @@ LL | fn g(xy: (&u32, &u32)) -> (u32, u32) { = note: called from `member_ref_arg_broken::main` warning: `#[inline(never)]` function `member_ref_arg_broken::h` has been inlined - --> $DIR/member_ref_arg-broken.rs:30:4 + --> $DIR/member_ref_arg-broken.rs:31:4 | LL | fn h(xyz: (&u32, &u32, &u32)) -> (u32, u32, u32) { | ^ @@ -26,7 +26,7 @@ LL | fn h(xyz: (&u32, &u32, &u32)) -> (u32, u32, u32) { = note: called from `member_ref_arg_broken::main` warning: `#[inline(never)]` function `member_ref_arg_broken::h_newtyped` has been inlined - --> $DIR/member_ref_arg-broken.rs:41:4 + --> $DIR/member_ref_arg-broken.rs:42:4 | LL | fn h_newtyped(xyz: ((&u32, &u32, &u32),)) -> (u32, u32, u32) { | ^^^^^^^^^^ @@ -35,7 +35,6 @@ LL | fn h_newtyped(xyz: ((&u32, &u32, &u32),)) -> (u32, u32, u32) { = note: called from `member_ref_arg_broken::main` error: error:0:0 - OpLoad Pointer '$ID[%$ID]' is not a logical pointer. - %39 = OpLoad %uint %38 | = note: spirv-val failed = note: module `$TEST_BUILD_DIR/lang/core/ref/member_ref_arg-broken` diff --git a/tests/compiletests/ui/spirv-attr/location_assignment/explicit_overlap.stderr b/tests/compiletests/ui/spirv-attr/location_assignment/explicit_overlap.stderr index dbde616b1c..9cbed5c5e6 100644 --- a/tests/compiletests/ui/spirv-attr/location_assignment/explicit_overlap.stderr +++ b/tests/compiletests/ui/spirv-attr/location_assignment/explicit_overlap.stderr @@ -41,9 +41,18 @@ OpStore %26 %14 OpNoLine OpReturn OpFunctionEnd -error: error:0:0 - [VUID-StandaloneSpirv-OpEntryPoint-08722] Entry-point has conflicting output location assignment at location 1, component 0 - OpEntryPoint Vertex %1 "main" %out1 %out2 +error: Output variable `out2` at location 1 conflicts with another variable + --> $DIR/explicit_overlap.rs:24:53 | +LL | pub fn main(#[spirv(location = 0)] out1: &mut Mat4, #[spirv(location = 1)] out2: &mut Vec2) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: variable `out1` already uses location 1 component 0 + --> $DIR/explicit_overlap.rs:24:13 + | +LL | pub fn main(#[spirv(location = 0)] out1: &mut Mat4, #[spirv(location = 1)] out2: &mut Vec2) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: `out1` is at location 0 but type `spirv_std::glam::Mat4` consumes 4 locations (0–3) = note: spirv-val failed = note: module `` diff --git a/tests/compiletests/ui/spirv-attr/location_assignment/mesh_shader.stderr b/tests/compiletests/ui/spirv-attr/location_assignment/mesh_shader.stderr index c6073a1bcb..d10e813f20 100644 --- a/tests/compiletests/ui/spirv-attr/location_assignment/mesh_shader.stderr +++ b/tests/compiletests/ui/spirv-attr/location_assignment/mesh_shader.stderr @@ -5,8 +5,8 @@ OpMemoryModel Logical Simple OpEntryPoint MeshEXT %1 "main" %2 %3 %4 %5 %6 %7 OpExecutionMode %1 LocalSize 1 1 1 OpExecutionMode %1 OutputVertices 9 -OpExecutionMode %1 OutputPrimitivesNV 3 -OpExecutionMode %1 OutputTrianglesNV +OpExecutionMode %1 OutputPrimitivesEXT 3 +OpExecutionMode %1 OutputTrianglesEXT OpName %16 "core::ops::Range" OpMemberName %16 0 "start" OpMemberName %16 1 "end" @@ -23,9 +23,9 @@ OpDecorate %3 Location 0 OpDecorate %4 Location 1 OpDecorate %5 BuiltIn PrimitiveTriangleIndicesEXT OpDecorate %6 Location 2 -OpDecorate %6 PerPrimitiveNV +OpDecorate %6 PerPrimitiveEXT OpDecorate %7 Location 3 -OpDecorate %7 PerPrimitiveNV +OpDecorate %7 PerPrimitiveEXT %17 = OpTypeFloat 32 %18 = OpTypeVector %17 4 %19 = OpTypeInt 32 0 From 2fbee153d655d8df2c77fde70bd11929179f2638 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Sat, 24 Jan 2026 15:17:18 -0800 Subject: [PATCH 06/13] Pass Module directly to do_spirv_val instead of re-parsing binary Avoids assembling the original module to a binary and then parsing it back. The validation error context can now use the Module directly for span lookups. --- crates/rustc_codegen_spirv/src/link.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/link.rs b/crates/rustc_codegen_spirv/src/link.rs index 8fef464722..58936a46e8 100644 --- a/crates/rustc_codegen_spirv/src/link.rs +++ b/crates/rustc_codegen_spirv/src/link.rs @@ -273,7 +273,6 @@ fn post_link_single_module( // Disassemble the stripped module (unless preserving decorations) cg_args.do_disassemble(&module_for_output); let spv_binary = module_for_output.assemble(); - let original_binary_for_spans = original_module_for_spans.assemble(); if let Some(dir) = &cg_args.dump_post_link { // FIXME(eddyb) rename `filename` with `file_path` to make this less confusing. @@ -324,7 +323,7 @@ fn post_link_single_module( }; if cg_args.run_spirv_val { - do_spirv_val(sess, &spv_binary, &original_binary_for_spans, out_filename, val_options); + do_spirv_val(sess, &spv_binary, &original_module_for_spans, out_filename, val_options); } { @@ -412,7 +411,7 @@ fn do_spirv_opt( fn do_spirv_val( sess: &Session, spv_binary: &[u32], - original_binary_for_spans: &[u32], + original_module_for_spans: &Module, filename: &Path, options: spirv_tools::val::ValidatorOptions, ) { @@ -422,14 +421,9 @@ fn do_spirv_val( let validator = val::create(sess.target.options.env.parse().ok()); if let Err(e) = validator.validate(spv_binary, Some(options)) { - // Parse the ORIGINAL binary (with SrcLocDecoration) to look up source spans + // Use the ORIGINAL module (with SrcLocDecoration) to look up source spans // Note: We validate the stripped binary, but look up spans in the original - let module = with_rspirv_loader(|loader| { - rspirv::binary::parse_words(original_binary_for_spans, loader) - }) - .ok(); - - let mut ctx = ValidationErrorContext::new(sess, module.as_ref(), filename); + let mut ctx = ValidationErrorContext::new(sess, Some(original_module_for_spans), filename); ctx.emit_error(&e); } } From 3d1a5803e69df3105e3bdfda5444c3aa1f474dc1 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Sat, 24 Jan 2026 15:17:27 -0800 Subject: [PATCH 07/13] Add rich error handling for NotALogicalPointer validation errors Provides helpful diagnostics when SPIR-V validation fails due to using a pointer from an invalid source (like OpCompositeExtract) with memory operations (OpLoad/OpStore) in logical addressing mode. The error now shows: - Which instruction cannot use the pointer - What produced the invalid pointer - Explanation of logical addressing mode restrictions - The relevant SPIR-V instructions for context --- .../rustc_codegen_spirv/src/validation_err.rs | 120 ++++++++++++++++++ .../core/ref/member_ref_arg-broken.stderr | 7 +- 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/crates/rustc_codegen_spirv/src/validation_err.rs b/crates/rustc_codegen_spirv/src/validation_err.rs index 69d1841e19..e8458a8aa2 100644 --- a/crates/rustc_codegen_spirv/src/validation_err.rs +++ b/crates/rustc_codegen_spirv/src/validation_err.rs @@ -55,6 +55,13 @@ impl<'a> ValidationErrorContext<'a> { *storage_class, ); } + ValidationError::NotALogicalPointer { + instruction, + pointer, + source_opcode, + } => { + self.emit_not_a_logical_pointer(*instruction, u32::from(*pointer), *source_opcode); + } _ => { // Fall back to generic error message self.emit_generic_error(error); @@ -140,6 +147,119 @@ impl<'a> ValidationErrorContext<'a> { } } + fn emit_not_a_logical_pointer(&mut self, instruction: Op, pointer_id: u32, source_opcode: Op) { + // Try to find a useful span - first the pointer, then the instruction using it, + // then trace back through the def chain + let span = self.id_to_span(pointer_id).or_else(|| { + // Try to find the load/store instruction that uses this pointer + self.find_instruction_using_pointer(pointer_id, instruction) + .and_then(|inst_id| self.id_to_span(inst_id)) + }); + + let pointer_name = self.get_name(pointer_id); + let pointer_name_fallback = format!("%{}", pointer_id); + let pointer_name_display = pointer_name.as_deref().unwrap_or(&pointer_name_fallback); + + // Get SPIR-V context showing the relevant instructions + let spirv_context = self.get_spirv_context_for_pointer(pointer_id, source_opcode); + + let message = format!( + "Op{:?} cannot use pointer `{}` because it was produced by Op{:?}", + instruction, pointer_name_display, source_opcode + ); + + let mut err = if let Some(span) = span { + self.sess.dcx().struct_span_err(span, message) + } else { + self.sess.dcx().struct_err(message) + }; + + err.note(format!( + "in SPIR-V's logical addressing mode, pointers for Op{:?} must come from \ + specific instructions like OpVariable, OpAccessChain, or OpFunctionParameter", + instruction + )); + err.help(format!( + "Op{:?} cannot produce pointers valid for memory operations in logical addressing", + source_opcode + )); + + // Show SPIR-V context if available + if let Some(context) = spirv_context { + err.note(format!("generated SPIR-V:\n{}", context)); + } + + err.note("spirv-val failed"); + err.note(format!("module `{}`", self.filename.display())); + err.emit(); + } + + /// Finds an instruction that uses the given pointer ID with the specified opcode. + fn find_instruction_using_pointer(&self, pointer_id: u32, opcode: Op) -> Option { + let m = self.module?; + for func in &m.functions { + for block in &func.blocks { + for inst in &block.instructions { + if inst.class.opcode == opcode { + // Check if any operand references our pointer + for operand in &inst.operands { + if let rspirv::dr::Operand::IdRef(id) = operand { + if *id == pointer_id { + return inst.result_id; + } + } + } + } + } + } + } + None + } + + /// Gets SPIR-V context showing the pointer-producing instruction and its use. + fn get_spirv_context_for_pointer(&self, pointer_id: u32, source_opcode: Op) -> Option { + let m = self.module?; + + // Find the instruction that produced the pointer + let mut context_lines = Vec::new(); + + for func in &m.functions { + for block in &func.blocks { + for inst in &block.instructions { + // Found the instruction that produced the pointer + if inst.result_id == Some(pointer_id) && inst.class.opcode == source_opcode { + context_lines.push(format!(" %{} = Op{:?} ...", pointer_id, source_opcode)); + } + // Found instructions using the pointer + if matches!(inst.class.opcode, Op::Load | Op::Store) { + for operand in &inst.operands { + if let rspirv::dr::Operand::IdRef(id) = operand { + if *id == pointer_id { + let result = inst + .result_id + .map(|r| format!("%{} = ", r)) + .unwrap_or_default(); + context_lines.push(format!( + " -> {}Op{:?} %{} ...", + result, + inst.class.opcode, + pointer_id + )); + } + } + } + } + } + } + } + + if context_lines.is_empty() { + None + } else { + Some(context_lines.join("\n")) + } + } + /// Looks up the rustc Span for a SPIR-V ID. fn id_to_span(&mut self, id: u32) -> Option { self.span_regen.as_mut().and_then(|sr| { diff --git a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr b/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr index 1e6443319b..b04aaa5fe9 100644 --- a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr +++ b/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr @@ -34,8 +34,13 @@ LL | fn h_newtyped(xyz: ((&u32, &u32, &u32),)) -> (u32, u32, u32) { = note: inlining was required due to illegal parameter type = note: called from `member_ref_arg_broken::main` -error: error:0:0 - OpLoad Pointer '$ID[%$ID]' is not a logical pointer. +error: OpLoad cannot use pointer `%38` because it was produced by OpCompositeExtract | + = note: in SPIR-V's logical addressing mode, pointers for OpLoad must come from specific instructions like OpVariable, OpAccessChain, or OpFunctionParameter + = help: OpCompositeExtract cannot produce pointers valid for memory operations in logical addressing + = note: generated SPIR-V: + %38 = OpCompositeExtract ... + -> %39 = OpLoad %38 ... = note: spirv-val failed = note: module `$TEST_BUILD_DIR/lang/core/ref/member_ref_arg-broken` From 0c6ac641e8ad4ecfe78dee5f90696ea624c5c443 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Sat, 24 Jan 2026 17:30:25 -0800 Subject: [PATCH 08/13] Remove redundant 'spirv-val failed' note from validation errors The error context already makes it clear this is a validation error. --- crates/rustc_codegen_spirv/src/validation_err.rs | 4 ---- .../ui/lang/core/ref/member_ref_arg-broken.stderr | 1 - .../ui/spirv-attr/location_assignment/explicit_overlap.stderr | 1 - 3 files changed, 6 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/validation_err.rs b/crates/rustc_codegen_spirv/src/validation_err.rs index e8458a8aa2..bb39f23236 100644 --- a/crates/rustc_codegen_spirv/src/validation_err.rs +++ b/crates/rustc_codegen_spirv/src/validation_err.rs @@ -72,7 +72,6 @@ impl<'a> ValidationErrorContext<'a> { /// Emits a generic validation error without rich formatting. fn emit_generic_error(&self, error: &spirv_tools::val::ValidatorError) { let mut err = self.sess.dcx().struct_err(error.to_string()); - err.note("spirv-val failed"); err.note(format!("module `{}`", self.filename.display())); err.emit(); } @@ -128,7 +127,6 @@ impl<'a> ValidationErrorContext<'a> { } } - err.note("spirv-val failed"); err.note(format!("module `{}`", self.filename.display())); err.emit(); } else { @@ -137,7 +135,6 @@ impl<'a> ValidationErrorContext<'a> { "{:?} variable `{}` at location {} conflicts with `{}`", storage_class, second_name_display, location, first_name_display )); - err.note("spirv-val failed"); err.note(format!("module `{}`", self.filename.display())); err.note(format!( "variables `{}` and `{}` both use location {} component {}", @@ -189,7 +186,6 @@ impl<'a> ValidationErrorContext<'a> { err.note(format!("generated SPIR-V:\n{}", context)); } - err.note("spirv-val failed"); err.note(format!("module `{}`", self.filename.display())); err.emit(); } diff --git a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr b/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr index b04aaa5fe9..049001ea7d 100644 --- a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr +++ b/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr @@ -41,7 +41,6 @@ error: OpLoad cannot use pointer `%38` because it was produced by OpCompositeExt = note: generated SPIR-V: %38 = OpCompositeExtract ... -> %39 = OpLoad %38 ... - = note: spirv-val failed = note: module `$TEST_BUILD_DIR/lang/core/ref/member_ref_arg-broken` error: aborting due to 1 previous error; 4 warnings emitted diff --git a/tests/compiletests/ui/spirv-attr/location_assignment/explicit_overlap.stderr b/tests/compiletests/ui/spirv-attr/location_assignment/explicit_overlap.stderr index 9cbed5c5e6..9fe59bf734 100644 --- a/tests/compiletests/ui/spirv-attr/location_assignment/explicit_overlap.stderr +++ b/tests/compiletests/ui/spirv-attr/location_assignment/explicit_overlap.stderr @@ -53,7 +53,6 @@ note: variable `out1` already uses location 1 component 0 LL | pub fn main(#[spirv(location = 0)] out1: &mut Mat4, #[spirv(location = 1)] out2: &mut Vec2) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: `out1` is at location 0 but type `spirv_std::glam::Mat4` consumes 4 locations (0–3) - = note: spirv-val failed = note: module `` error: aborting due to 1 previous error From 50f5d7f55dd7d744b10c63e7fc1d9fcc12b66110 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Sat, 24 Jan 2026 17:41:09 -0800 Subject: [PATCH 09/13] Add rich error handling for additional validation errors Add rich error handlers with helpful hints for: - MissingInstructionCapability / MissingOperandCapability - MissingDescriptorSetDecoration / MissingBindingDecoration - InvalidBlockLayout - InvalidBuiltInType These handlers provide user-friendly rustc diagnostics with source spans (when available) and actionable hints for fixing common issues. --- .../rustc_codegen_spirv/src/validation_err.rs | 161 +++++++++++++++++- 1 file changed, 160 insertions(+), 1 deletion(-) diff --git a/crates/rustc_codegen_spirv/src/validation_err.rs b/crates/rustc_codegen_spirv/src/validation_err.rs index bb39f23236..6177bf00c3 100644 --- a/crates/rustc_codegen_spirv/src/validation_err.rs +++ b/crates/rustc_codegen_spirv/src/validation_err.rs @@ -6,7 +6,7 @@ use std::path::Path; use rspirv::dr::Module; -use rspirv::spirv::{Decoration, Op}; +use rspirv::spirv::{BuiltIn, Capability, Decoration, Op}; use rustc_session::Session; use spirv_tools::ValidationError; @@ -62,6 +62,42 @@ impl<'a> ValidationErrorContext<'a> { } => { self.emit_not_a_logical_pointer(*instruction, u32::from(*pointer), *source_opcode); } + ValidationError::MissingInstructionCapability { + opcode, + required_capability, + } => { + self.emit_missing_capability(*opcode, *required_capability, None); + } + ValidationError::MissingOperandCapability { + opcode, + operand_index, + required_capability, + } => { + self.emit_missing_capability(*opcode, *required_capability, Some(*operand_index)); + } + ValidationError::MissingDescriptorSetDecoration { variable } => { + self.emit_missing_decoration( + u32::from(*variable), + "DescriptorSet", + "#[spirv(descriptor_set = N)]", + ); + } + ValidationError::MissingBindingDecoration { variable } => { + self.emit_missing_decoration( + u32::from(*variable), + "Binding", + "#[spirv(binding = N)]", + ); + } + ValidationError::InvalidBlockLayout { + struct_type, + reason, + } => { + self.emit_invalid_block_layout(u32::from(*struct_type), reason); + } + ValidationError::InvalidBuiltInType { builtin, expected } => { + self.emit_invalid_builtin_type(*builtin, expected); + } _ => { // Fall back to generic error message self.emit_generic_error(error); @@ -144,6 +180,96 @@ impl<'a> ValidationErrorContext<'a> { } } + fn emit_invalid_block_layout(&mut self, struct_type_id: u32, reason: &str) { + let span = self.id_to_span(struct_type_id); + let type_name = self.get_name(struct_type_id); + let type_name_fallback = format!("%{}", struct_type_id); + let type_name_display = type_name.as_deref().unwrap_or(&type_name_fallback); + + let message = format!( + "struct `{}` has invalid block layout: {}", + type_name_display, reason + ); + + let mut err = if let Some(span) = span { + self.sess.dcx().struct_span_err(span, message) + } else { + self.sess.dcx().struct_err(message) + }; + + err.help("ensure struct members are properly aligned according to std140/std430 layout rules"); + err.note(format!("module `{}`", self.filename.display())); + err.emit(); + } + + fn emit_invalid_builtin_type(&self, builtin: BuiltIn, expected: &str) { + let mut err = self.sess.dcx().struct_err(format!( + "BuiltIn {:?} has incorrect type, expected {}", + builtin, expected + )); + + err.help(format!( + "variables with `#[spirv(builtin = \"{:?}\")]` must have the correct type", + builtin + )); + err.note(format!("module `{}`", self.filename.display())); + err.emit(); + } + + fn emit_missing_decoration(&mut self, var_id: u32, decoration_name: &str, hint: &str) { + let span = self.id_to_span(var_id); + let var_name = self.get_name(var_id); + let var_name_fallback = format!("%{}", var_id); + let var_name_display = var_name.as_deref().unwrap_or(&var_name_fallback); + + let message = format!( + "resource variable `{}` is missing {} decoration", + var_name_display, decoration_name + ); + + let mut err = if let Some(span) = span { + self.sess.dcx().struct_span_err(span, message) + } else { + self.sess.dcx().struct_err(message) + }; + + err.help(format!("add `{}` to the variable", hint)); + err.note(format!("module `{}`", self.filename.display())); + err.emit(); + } + + fn emit_missing_capability( + &mut self, + opcode: Op, + capability: Capability, + operand_index: Option, + ) { + // Try to find an instruction with this opcode to get a span + let span = self.find_instruction_span_by_opcode(opcode); + + let message = if let Some(idx) = operand_index { + format!( + "operand {} of Op{:?} requires capability {:?}", + idx, opcode, capability + ) + } else { + format!("Op{:?} requires capability {:?}", opcode, capability) + }; + + let mut err = if let Some(span) = span { + self.sess.dcx().struct_span_err(span, message) + } else { + self.sess.dcx().struct_err(message) + }; + + err.help(format!( + "add `#[spirv(capability({:?}))]` to your entry point function", + capability + )); + err.note(format!("module `{}`", self.filename.display())); + err.emit(); + } + fn emit_not_a_logical_pointer(&mut self, instruction: Op, pointer_id: u32, source_opcode: Op) { // Try to find a useful span - first the pointer, then the instruction using it, // then trace back through the def chain @@ -190,6 +316,39 @@ impl<'a> ValidationErrorContext<'a> { err.emit(); } + /// Finds the first instruction with the given opcode and returns its span. + fn find_instruction_span_by_opcode(&mut self, opcode: Op) -> Option { + let m = self.module?; + + // Search in functions first (most common case) + for func in &m.functions { + for block in &func.blocks { + for inst in &block.instructions { + if inst.class.opcode == opcode { + if let Some(id) = inst.result_id { + if let Some(span) = self.id_to_span(id) { + return Some(span); + } + } + } + } + } + } + + // Search in types/globals + for inst in &m.types_global_values { + if inst.class.opcode == opcode { + if let Some(id) = inst.result_id { + if let Some(span) = self.id_to_span(id) { + return Some(span); + } + } + } + } + + None + } + /// Finds an instruction that uses the given pointer ID with the specified opcode. fn find_instruction_using_pointer(&self, pointer_id: u32, opcode: Op) -> Option { let m = self.module?; From b1fe43d87cf3714b94a6c96be960663b41486cd8 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Tue, 27 Jan 2026 08:57:43 -0800 Subject: [PATCH 10/13] Remove replacements and comments no longer needed --- crates/rustc_codegen_spirv/src/linker/mod.rs | 1 - tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/linker/mod.rs b/crates/rustc_codegen_spirv/src/linker/mod.rs index c74ea25871..eaed8e2143 100644 --- a/crates/rustc_codegen_spirv/src/linker/mod.rs +++ b/crates/rustc_codegen_spirv/src/linker/mod.rs @@ -19,7 +19,6 @@ mod zombies; use std::borrow::Cow; use crate::codegen_cx::{ModuleOutputType, SpirvMetadata}; -// Note: SrcLocDecoration and ZombieDecoration are stripped in link.rs after validation use crate::custom_insts; use either::Either; use rspirv::binary::Assemble; diff --git a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs b/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs index 7ce9736ff3..d29e92a080 100644 --- a/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs +++ b/tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.rs @@ -4,8 +4,6 @@ // build-fail // normalize-stderr-test "ref/member_ref_arg-broken\.[^`]*" -> "ref/member_ref_arg-broken" -// normalize-stderr-test "38\[%38\]" -> "$$ID[%$$ID]" -// normalize-stderr-test "'38'" -> "'$$ID[%$$ID]'" use spirv_std::spirv; From 8c353b61e2668585cffb03ec041a81a8290af917 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Tue, 27 Jan 2026 09:01:44 -0800 Subject: [PATCH 11/13] rustfmt --- .../src/builder/spirv_asm.rs | 27 +++++++++---------- crates/rustc_codegen_spirv/src/link.rs | 8 +++++- .../rustc_codegen_spirv/src/validation_err.rs | 26 +++++++++--------- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs b/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs index 6b8c0677af..b06a52186e 100644 --- a/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs +++ b/crates/rustc_codegen_spirv/src/builder/spirv_asm.rs @@ -1554,16 +1554,22 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> { self.err(format!("TensorClampMode not yet supported: {word}")); } (OperandKind::TensorAddressingOperands, Some(word)) => { - self.err(format!("TensorAddressingOperands not yet supported: {word}")); + self.err(format!( + "TensorAddressingOperands not yet supported: {word}" + )); } (OperandKind::FPEncoding, Some(word)) => { self.err(format!("FPEncoding not yet supported: {word}")); } (OperandKind::NamedMaximumNumberOfRegisters, Some(word)) => { - self.err(format!("NamedMaximumNumberOfRegisters not yet supported: {word}")); + self.err(format!( + "NamedMaximumNumberOfRegisters not yet supported: {word}" + )); } (OperandKind::MatrixMultiplyAccumulateOperands, Some(word)) => { - self.err(format!("MatrixMultiplyAccumulateOperands not yet supported: {word}")); + self.err(format!( + "MatrixMultiplyAccumulateOperands not yet supported: {word}" + )); } // Catch-all for any other new operand kinds (kind, Some(word)) => { @@ -1603,10 +1609,7 @@ pub const IMAGE_OPERANDS: &[(&str, ImageOperands)] = &[ ("Sample", ImageOperands::SAMPLE), ("MinLod", ImageOperands::MIN_LOD), ("MakeTexelAvailable", ImageOperands::MAKE_TEXEL_AVAILABLE), - ( - "MakeTexelAvailableKHR", - ImageOperands::MAKE_TEXEL_AVAILABLE, - ), + ("MakeTexelAvailableKHR", ImageOperands::MAKE_TEXEL_AVAILABLE), ("MakeTexelVisible", ImageOperands::MAKE_TEXEL_VISIBLE), ("MakeTexelVisibleKHR", ImageOperands::MAKE_TEXEL_VISIBLE), ("NonPrivateTexel", ImageOperands::NON_PRIVATE_TEXEL), @@ -1689,15 +1692,9 @@ pub const MEMORY_ACCESS: &[(&str, MemoryAccess)] = &[ MemoryAccess::MAKE_POINTER_AVAILABLE, ), ("MakePointerVisible", MemoryAccess::MAKE_POINTER_VISIBLE), - ( - "MakePointerVisibleKHR", - MemoryAccess::MAKE_POINTER_VISIBLE, - ), + ("MakePointerVisibleKHR", MemoryAccess::MAKE_POINTER_VISIBLE), ("NonPrivatePointer", MemoryAccess::NON_PRIVATE_POINTER), - ( - "NonPrivatePointerKHR", - MemoryAccess::NON_PRIVATE_POINTER, - ), + ("NonPrivatePointerKHR", MemoryAccess::NON_PRIVATE_POINTER), ]; pub const KERNEL_PROFILING_INFO: &[(&str, KernelProfilingInfo)] = &[ ("None", KernelProfilingInfo::NONE), diff --git a/crates/rustc_codegen_spirv/src/link.rs b/crates/rustc_codegen_spirv/src/link.rs index 58936a46e8..635764008f 100644 --- a/crates/rustc_codegen_spirv/src/link.rs +++ b/crates/rustc_codegen_spirv/src/link.rs @@ -323,7 +323,13 @@ fn post_link_single_module( }; if cg_args.run_spirv_val { - do_spirv_val(sess, &spv_binary, &original_module_for_spans, out_filename, val_options); + do_spirv_val( + sess, + &spv_binary, + &original_module_for_spans, + out_filename, + val_options, + ); } { diff --git a/crates/rustc_codegen_spirv/src/validation_err.rs b/crates/rustc_codegen_spirv/src/validation_err.rs index 6177bf00c3..6bf569d116 100644 --- a/crates/rustc_codegen_spirv/src/validation_err.rs +++ b/crates/rustc_codegen_spirv/src/validation_err.rs @@ -22,11 +22,7 @@ pub struct ValidationErrorContext<'a> { impl<'a> ValidationErrorContext<'a> { /// Creates a new validation error context. - pub fn new( - sess: &'a Session, - module: Option<&'a Module>, - filename: &'a Path, - ) -> Self { + pub fn new(sess: &'a Session, module: Option<&'a Module>, filename: &'a Path) -> Self { let span_regen = module.map(|m| SpanRegenerator::new(sess.source_map(), m)); Self { sess, @@ -197,7 +193,9 @@ impl<'a> ValidationErrorContext<'a> { self.sess.dcx().struct_err(message) }; - err.help("ensure struct members are properly aligned according to std140/std430 layout rules"); + err.help( + "ensure struct members are properly aligned according to std140/std430 layout rules", + ); err.note(format!("module `{}`", self.filename.display())); err.emit(); } @@ -383,7 +381,10 @@ impl<'a> ValidationErrorContext<'a> { for inst in &block.instructions { // Found the instruction that produced the pointer if inst.result_id == Some(pointer_id) && inst.class.opcode == source_opcode { - context_lines.push(format!(" %{} = Op{:?} ...", pointer_id, source_opcode)); + context_lines.push(format!( + " %{} = Op{:?} ...", + pointer_id, source_opcode + )); } // Found instructions using the pointer if matches!(inst.class.opcode, Op::Load | Op::Store) { @@ -396,9 +397,7 @@ impl<'a> ValidationErrorContext<'a> { .unwrap_or_default(); context_lines.push(format!( " -> {}Op{:?} %{} ...", - result, - inst.class.opcode, - pointer_id + result, inst.class.opcode, pointer_id )); } } @@ -457,9 +456,10 @@ impl<'a> ValidationErrorContext<'a> { let m = self.module?; // Find the OpVariable - let var_inst = m.types_global_values.iter().find(|inst| { - inst.class.opcode == Op::Variable && inst.result_id == Some(var_id) - })?; + let var_inst = m + .types_global_values + .iter() + .find(|inst| inst.class.opcode == Op::Variable && inst.result_id == Some(var_id))?; // Get the pointer type, then the pointee type let ptr_type_id = var_inst.result_type?; From c4bd6df9e2e878e845d4faef41ce09336a9d23cf Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Tue, 27 Jan 2026 09:35:34 -0800 Subject: [PATCH 12/13] Update SPIRV-Tools to include vendored grammar and license fix --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25539e05df..d7170e7c42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,7 +141,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -152,7 +152,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1237,7 +1237,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2324,7 +2324,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3413,7 +3413,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3799,7 +3799,7 @@ version = "0.9.0" [[package]] name = "spirv-tools" version = "0.1.0" -source = "git+https://github.com/Rust-GPU/SPIRV-Tools#a6840f810b2feb5511d979ca81c703fe9b7913cd" +source = "git+https://github.com/Rust-GPU/SPIRV-Tools#e0e30b37ecd011ad25142444eba9925f312aecd7" dependencies = [ "rspirv", "spirv-tools-core", @@ -3809,7 +3809,7 @@ dependencies = [ [[package]] name = "spirv-tools-core" version = "0.1.0" -source = "git+https://github.com/Rust-GPU/SPIRV-Tools#a6840f810b2feb5511d979ca81c703fe9b7913cd" +source = "git+https://github.com/Rust-GPU/SPIRV-Tools#e0e30b37ecd011ad25142444eba9925f312aecd7" dependencies = [ "bitflags 2.10.0", "heck 0.4.1", @@ -3825,7 +3825,7 @@ dependencies = [ [[package]] name = "spirv-tools-opt" version = "0.1.0" -source = "git+https://github.com/Rust-GPU/SPIRV-Tools#a6840f810b2feb5511d979ca81c703fe9b7913cd" +source = "git+https://github.com/Rust-GPU/SPIRV-Tools#e0e30b37ecd011ad25142444eba9925f312aecd7" dependencies = [ "arbitrary", "clap", @@ -3933,7 +3933,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4883,7 +4883,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] From 43cffc7591970f148356846ae4f65f52d91e3bb3 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Tue, 27 Jan 2026 13:11:20 -0800 Subject: [PATCH 13/13] Allow MPL-2.0 license for egglog transitive deps --- deny.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deny.toml b/deny.toml index e4feda2e21..c7f40964c6 100644 --- a/deny.toml +++ b/deny.toml @@ -52,6 +52,10 @@ exceptions = [ # https://tldrlegal.com/license/creative-commons-cc0-1.0-universal { allow = ["CC0-1.0"], name = "notify" }, { allow = ["CC0-1.0"], name = "hexf-parse" }, + # MPL-2.0+ crates pulled in by egglog via im-rc + { allow = ["MPL-2.0"], name = "bitmaps" }, + { allow = ["MPL-2.0"], name = "im-rc" }, + { allow = ["MPL-2.0"], name = "sized-chunks" }, ] # See note in encoding_rs's readme! This clarification is copied directly from there.