Skip to content

Commit 44f5287

Browse files
committed
fixes
1 parent 90cf470 commit 44f5287

19 files changed

+987
-469
lines changed

crates/rustc_codegen_spirv/src/linker/array_stride_fixer.rs

Lines changed: 477 additions & 465 deletions
Large diffs are not rendered by default.

crates/rustc_codegen_spirv/src/linker/duplicates.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,14 @@ fn gather_names(debug_names: &[Instruction]) -> FxHashMap<Word, String> {
117117
.collect()
118118
}
119119

120-
fn make_dedupe_key(
120+
fn make_dedupe_key_with_array_context(
121121
inst: &Instruction,
122122
unresolved_forward_pointers: &FxHashSet<Word>,
123123
annotations: &FxHashMap<Word, Vec<u32>>,
124124
names: &FxHashMap<Word, String>,
125+
array_contexts: Option<
126+
&FxHashMap<Word, crate::linker::array_stride_fixer::ArrayStorageContext>,
127+
>,
125128
) -> Vec<u32> {
126129
let mut data = vec![inst.class.opcode as u32];
127130

@@ -169,6 +172,38 @@ fn make_dedupe_key(
169172
}
170173
}
171174

175+
// For array types, include storage class context in the key to prevent
176+
// inappropriate deduplication between different storage class contexts
177+
if let Some(result_id) = inst.result_id {
178+
if matches!(inst.class.opcode, Op::TypeArray | Op::TypeRuntimeArray) {
179+
if let Some(contexts) = array_contexts {
180+
if let Some(context) = contexts.get(&result_id) {
181+
// Include usage pattern in the key so arrays with different contexts won't deduplicate
182+
let usage_pattern_discriminant = match context.usage_pattern {
183+
crate::linker::array_stride_fixer::ArrayUsagePattern::LayoutRequired => {
184+
1u32
185+
}
186+
crate::linker::array_stride_fixer::ArrayUsagePattern::LayoutForbidden => {
187+
2u32
188+
}
189+
crate::linker::array_stride_fixer::ArrayUsagePattern::MixedUsage => 3u32,
190+
crate::linker::array_stride_fixer::ArrayUsagePattern::Unused => 4u32,
191+
};
192+
data.push(usage_pattern_discriminant);
193+
194+
// Also include the specific storage classes for fine-grained differentiation
195+
let mut storage_classes: Vec<u32> = context
196+
.storage_classes
197+
.iter()
198+
.map(|sc| *sc as u32)
199+
.collect();
200+
storage_classes.sort(); // Ensure deterministic ordering
201+
data.extend(storage_classes);
202+
}
203+
}
204+
}
205+
}
206+
172207
data
173208
}
174209

@@ -185,6 +220,15 @@ fn rewrite_inst_with_rules(inst: &mut Instruction, rules: &FxHashMap<u32, u32>)
185220
}
186221

187222
pub fn remove_duplicate_types(module: &mut Module) {
223+
remove_duplicate_types_with_array_context(module, None);
224+
}
225+
226+
pub fn remove_duplicate_types_with_array_context(
227+
module: &mut Module,
228+
array_contexts: Option<
229+
&FxHashMap<Word, crate::linker::array_stride_fixer::ArrayStorageContext>,
230+
>,
231+
) {
188232
// Keep in mind, this algorithm requires forward type references to not exist - i.e. it's a valid spir-v module.
189233

190234
// When a duplicate type is encountered, then this is a map from the deleted ID, to the new, deduplicated ID.
@@ -222,7 +266,13 @@ pub fn remove_duplicate_types(module: &mut Module) {
222266
// all_inst_iter_mut pass below. However, the code is a lil bit cleaner this way I guess.
223267
rewrite_inst_with_rules(inst, &rewrite_rules);
224268

225-
let key = make_dedupe_key(inst, &unresolved_forward_pointers, &annotations, &names);
269+
let key = make_dedupe_key_with_array_context(
270+
inst,
271+
&unresolved_forward_pointers,
272+
&annotations,
273+
&names,
274+
array_contexts,
275+
);
226276

227277
match key_to_result_id.entry(key) {
228278
hash_map::Entry::Vacant(entry) => {

crates/rustc_codegen_spirv/src/linker/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,10 +356,10 @@ pub fn link(
356356
});
357357
}
358358

359-
// Fix ArrayStride decorations for arrays in storage classes where newer SPIR-V versions forbid explicit layouts
359+
// Fix ArrayStride decorations (after storage classes are resolved to avoid conflicts)
360360
{
361361
let _timer = sess.timer("fix_array_stride_decorations");
362-
array_stride_fixer::fix_array_stride_decorations(&mut output);
362+
array_stride_fixer::fix_array_stride_decorations_with_deduplication(&mut output, false);
363363
}
364364

365365
// NOTE(eddyb) with SPIR-T, we can do `mem2reg` before inlining, too!
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Test that ArrayStride decorations are kept for function storage in SPIR-V 1.3
2+
3+
// build-pass
4+
// compile-flags: -C llvm-args=--disassemble-globals
5+
// normalize-stderr-test "OpLine .*\n" -> ""
6+
// normalize-stderr-test "OpSource .*\n" -> ""
7+
// normalize-stderr-test "\S*/lib/rustlib/" -> "$SYSROOT/lib/rustlib/"
8+
// only-spv1.3
9+
use spirv_std::spirv;
10+
11+
#[spirv(compute(threads(1)))]
12+
pub fn main(
13+
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] output: &mut [u32; 1],
14+
) {
15+
// Function storage in SPIR-V 1.3 should keep ArrayStride decorations
16+
let mut function_var: [u32; 256] = [0; 256];
17+
function_var[0] = 42;
18+
function_var[1] = function_var[0] + 1;
19+
// Force the array to be used by writing to output
20+
output[0] = function_var[1];
21+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
OpCapability Shader
2+
OpCapability Float64
3+
OpCapability Int64
4+
OpCapability Int16
5+
OpCapability Int8
6+
OpCapability ShaderClockKHR
7+
OpExtension "SPV_KHR_shader_clock"
8+
OpMemoryModel Logical Simple
9+
OpEntryPoint GLCompute %1 "main"
10+
OpExecutionMode %1 LocalSize 1 1 1
11+
%2 = OpString "$OPSTRING_FILENAME/function_storage_spirv13_kept.rs"
12+
OpDecorate %4 ArrayStride 4
13+
OpDecorate %5 Block
14+
OpMemberDecorate %5 0 Offset 0
15+
OpDecorate %3 Binding 0
16+
OpDecorate %3 DescriptorSet 0
17+
%6 = OpTypeVoid
18+
%7 = OpTypeFunction %6
19+
%8 = OpTypeInt 32 0
20+
%9 = OpConstant %8 1
21+
%4 = OpTypeArray %8 %9
22+
%10 = OpTypePointer StorageBuffer %4
23+
%5 = OpTypeStruct %4
24+
%11 = OpTypePointer StorageBuffer %5
25+
%3 = OpVariable %11 StorageBuffer
26+
%12 = OpConstant %8 0
27+
%13 = OpTypeBool
28+
%14 = OpConstant %8 256
29+
%15 = OpConstant %8 42
30+
%16 = OpTypePointer StorageBuffer %8
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Test that mixed storage class usage results in proper ArrayStride handling
2+
3+
// compile-flags: -C llvm-args=--disassemble-globals
4+
// only-vulkan1.1
5+
// normalize-stderr-test "OpLine .*\n" -> ""
6+
// normalize-stderr-test "OpSource .*\n" -> ""
7+
// normalize-stderr-test "\S*/lib/rustlib/" -> "$SYSROOT/lib/rustlib/"
8+
use spirv_std::spirv;
9+
10+
#[spirv(compute(threads(64)))]
11+
pub fn main(
12+
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] storage_data: &mut [u32; 256],
13+
#[spirv(workgroup)] workgroup_data: &mut [u32; 256],
14+
) {
15+
// Both variables use the same array type [u32; 256] but in different storage classes:
16+
// - storage_data is in StorageBuffer (requires ArrayStride)
17+
// - workgroup_data is in Workgroup (forbids ArrayStride in SPIR-V 1.4+)
18+
19+
storage_data[0] = 42;
20+
workgroup_data[0] = storage_data[0];
21+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
OpCapability Shader
2+
OpCapability Float64
3+
OpCapability Int64
4+
OpCapability Int16
5+
OpCapability Int8
6+
OpCapability ShaderClockKHR
7+
OpCapability VulkanMemoryModel
8+
OpExtension "SPV_KHR_shader_clock"
9+
OpExtension "SPV_KHR_vulkan_memory_model"
10+
OpMemoryModel Logical Vulkan
11+
OpEntryPoint GLCompute %1 "main"
12+
OpExecutionMode %1 LocalSize 64 1 1
13+
%2 = OpString "$OPSTRING_FILENAME/mixed_storage_classes.rs"
14+
OpName %4 "workgroup_data"
15+
OpDecorate %5 ArrayStride 4
16+
OpDecorate %6 Block
17+
OpMemberDecorate %6 0 Offset 0
18+
OpDecorate %3 Binding 0
19+
OpDecorate %3 DescriptorSet 0
20+
%7 = OpTypeVoid
21+
%8 = OpTypeFunction %7
22+
%9 = OpTypeInt 32 0
23+
%10 = OpConstant %9 256
24+
%5 = OpTypeArray %9 %10
25+
%11 = OpTypePointer StorageBuffer %5
26+
%6 = OpTypeStruct %5
27+
%12 = OpTypePointer StorageBuffer %6
28+
%3 = OpVariable %12 StorageBuffer
29+
%13 = OpConstant %9 0
30+
%14 = OpTypeBool
31+
%15 = OpTypePointer StorageBuffer %9
32+
%16 = OpConstant %9 42
33+
%17 = OpTypePointer Workgroup %9
34+
%18 = OpTypeArray %9 %10
35+
%19 = OpTypePointer Workgroup %18
36+
%4 = OpVariable %19 Workgroup
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Test that ArrayStride decorations are removed from nested structs with arrays in Function storage class
2+
3+
// build-pass
4+
// compile-flags: -C llvm-args=--disassemble-globals
5+
// only-vulkan1.2
6+
// normalize-stderr-test "OpLine .*\n" -> ""
7+
// normalize-stderr-test "OpSource .*\n" -> ""
8+
// normalize-stderr-test "\S*/lib/rustlib/" -> "$SYSROOT/lib/rustlib/"
9+
use spirv_std::spirv;
10+
11+
#[derive(Copy, Clone)]
12+
struct InnerStruct {
13+
data: [f32; 4],
14+
}
15+
16+
#[derive(Copy, Clone)]
17+
struct OuterStruct {
18+
inner: InnerStruct,
19+
}
20+
21+
#[spirv(compute(threads(1)))]
22+
pub fn main(
23+
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] output: &mut [f32; 1],
24+
) {
25+
// Function-local variables with nested structs containing arrays
26+
// Should have ArrayStride removed in SPIR-V 1.4+
27+
let mut function_var = OuterStruct {
28+
inner: InnerStruct { data: [0.0; 4] },
29+
};
30+
function_var.inner.data[0] = 42.0;
31+
function_var.inner.data[1] = function_var.inner.data[0] + 1.0;
32+
// Force usage to prevent optimization
33+
output[0] = function_var.inner.data[1];
34+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
OpCapability Shader
2+
OpCapability Float64
3+
OpCapability Int64
4+
OpCapability Int16
5+
OpCapability Int8
6+
OpCapability ShaderClockKHR
7+
OpCapability VulkanMemoryModel
8+
OpExtension "SPV_KHR_shader_clock"
9+
OpMemoryModel Logical Vulkan
10+
OpEntryPoint GLCompute %1 "main" %2
11+
OpExecutionMode %1 LocalSize 1 1 1
12+
%3 = OpString "$OPSTRING_FILENAME/nested_structs_function_storage.rs"
13+
OpName %4 "InnerStruct"
14+
OpMemberName %4 0 "data"
15+
OpName %5 "OuterStruct"
16+
OpMemberName %5 0 "inner"
17+
OpDecorate %6 ArrayStride 4
18+
OpDecorate %7 Block
19+
OpMemberDecorate %7 0 Offset 0
20+
OpDecorate %2 Binding 0
21+
OpDecorate %2 DescriptorSet 0
22+
OpMemberDecorate %4 0 Offset 0
23+
OpMemberDecorate %5 0 Offset 0
24+
%8 = OpTypeFloat 32
25+
%9 = OpTypeInt 32 0
26+
%10 = OpConstant %9 1
27+
%6 = OpTypeArray %8 %10
28+
%7 = OpTypeStruct %6
29+
%11 = OpTypePointer StorageBuffer %7
30+
%12 = OpTypeVoid
31+
%13 = OpTypeFunction %12
32+
%14 = OpTypePointer StorageBuffer %6
33+
%2 = OpVariable %11 StorageBuffer
34+
%15 = OpConstant %9 0
35+
%16 = OpConstant %9 4
36+
%17 = OpTypeArray %8 %16
37+
%4 = OpTypeStruct %17
38+
%5 = OpTypeStruct %4
39+
%18 = OpConstant %8 0
40+
%19 = OpConstantComposite %17 %18 %18 %18 %18
41+
%20 = OpUndef %5
42+
%21 = OpTypeBool
43+
%22 = OpConstant %8 1109917696
44+
%23 = OpConstant %8 1065353216
45+
%24 = OpTypePointer StorageBuffer %8
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Test that ArrayStride decorations are removed from private storage in SPIR-V 1.4
2+
3+
// build-pass
4+
// compile-flags: -C llvm-args=--disassemble-globals
5+
// normalize-stderr-test "OpLine .*\n" -> ""
6+
// normalize-stderr-test "OpSource .*\n" -> ""
7+
// normalize-stderr-test "\S*/lib/rustlib/" -> "$SYSROOT/lib/rustlib/"
8+
// only-spv1.4
9+
use spirv_std::spirv;
10+
11+
// Helper function to create an array in private storage
12+
fn create_private_array() -> [u32; 4] {
13+
[0, 1, 2, 3]
14+
}
15+
16+
#[spirv(compute(threads(1)))]
17+
pub fn main() {
18+
// This creates a private storage array in SPIR-V 1.4+
19+
// ArrayStride decorations should be removed
20+
let mut private_array = create_private_array();
21+
private_array[0] = 42;
22+
}

0 commit comments

Comments
 (0)