Skip to content

Commit ad8c6f7

Browse files
committed
Fix zero-sized Array GEPs
Previously GEP on zero-sized arrays, would fail. This change fixes arrays to instead emit runtime arrays. I do not know if this will lead to any runtime cost, but it fixes all the compile errors.
1 parent 1932353 commit ad8c6f7

16 files changed

+144
-6
lines changed

crates/rustc_codegen_spirv/src/abi.rs

-3
Original file line numberDiff line numberDiff line change
@@ -649,9 +649,6 @@ fn trans_aggregate<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>
649649
element: element_type,
650650
}
651651
.def(span, cx)
652-
} else if count == 0 {
653-
// spir-v doesn't support zero-sized arrays
654-
create_zst(cx, span, ty)
655652
} else {
656653
let count_const = cx.constant_u32(span, count as u32);
657654
let element_spv = cx.lookup_type(element_type);

crates/rustc_codegen_spirv/src/builder/builder_methods.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
406406
let ptr = ptr.strip_ptrcasts();
407407
let mut leaf_ty = match self.lookup_type(ptr.ty) {
408408
SpirvType::Pointer { pointee } => pointee,
409-
other => self.fatal(format!("non-pointer type: {other:?}")),
409+
SpirvType::Adt { .. } => return None,
410+
other => self.fatal(format!("adjust_pointer for non-pointer type: {other:?}")),
410411
};
411412

412413
// FIXME(eddyb) this isn't efficient, `recover_access_chain_from_offset`
@@ -528,8 +529,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
528529
let stride = ty_kind.sizeof(self)?;
529530
ty_size = MaybeSized::Sized(stride);
530531

531-
indices.push((offset.bytes() / stride.bytes()).try_into().ok()?);
532-
offset = Size::from_bytes(offset.bytes() % stride.bytes());
532+
if stride.bytes() == 0 {
533+
indices.push(0);
534+
offset = Size::from_bytes(0);
535+
} else {
536+
indices.push((offset.bytes() / stride.bytes()).try_into().ok()?);
537+
offset = Size::from_bytes(offset.bytes() % stride.bytes());
538+
}
533539
}
534540
_ => return None,
535541
}
@@ -566,6 +572,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
566572
.iter()
567573
.map(|index| {
568574
result_pointee_type = match self.lookup_type(result_pointee_type) {
575+
SpirvType::Array { count, element }
576+
if self.builder.lookup_const_u64(count) == Some(0) =>
577+
{
578+
self.fatal(format!(
579+
"evaluation of constant value failed: cannot index into [{}; 0]",
580+
self.debug_type(element)
581+
))
582+
}
569583
SpirvType::Array { element, .. } | SpirvType::RuntimeArray { element } => {
570584
element
571585
}

crates/rustc_codegen_spirv/src/codegen_cx/constant.rs

+3
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,9 @@ impl<'tcx> CodegenCx<'tcx> {
549549
}
550550
self.constant_composite(ty, values.into_iter())
551551
}
552+
SpirvType::Array { count, .. } if self.builder.lookup_const_u64(count) == Some(0) => {
553+
self.undef(ty)
554+
}
552555
SpirvType::Array { element, count } => {
553556
let count = self.builder.lookup_const_u64(count).unwrap() as usize;
554557
let values = (0..count).map(|_| {

crates/rustc_codegen_spirv/src/spirv_type.rs

+3
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,9 @@ impl SpirvType<'_> {
368368
.bytes(),
369369
)
370370
.expect("alignof: Vectors must have power-of-2 size"),
371+
Self::Array { count, .. } if cx.builder.lookup_const_u64(count) == Some(0) => {
372+
Align::from_bytes(0).unwrap()
373+
}
371374
Self::Array { element, .. }
372375
| Self::RuntimeArray { element }
373376
| Self::Matrix { element, .. } => cx.lookup_type(element).alignof(cx),

tests/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@ the resulting binary with spirv-val). Because of this, there might be some stran
2121
the point isn't to make a fully functional shader every time (that would take an annoying amount of
2222
effort), but rather validate that specific parts of the compiler are doing their job correctly
2323
(either succeeding as they should, or erroring as they should).
24+
25+
For more docs on compiletests, check the [rustc docs](https://rustc-dev-guide.rust-lang.org/tests/compiletest.html).

tests/ui/lang/core/array/array_0.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![allow(unconditional_panic)]
2+
3+
// build-fail
4+
#![cfg_attr(target_arch = "spirv", no_std)]
5+
use spirv_std::spirv;
6+
7+
#[spirv(compute(threads(1, 1, 1)))]
8+
pub fn compute() {
9+
let array = [0; 0];
10+
let x = array[0];
11+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: evaluation of constant value failed: cannot index into [i32; 0]
2+
--> $DIR/array_0.rs:10:13
3+
|
4+
10 | let x = array[0];
5+
| ^^^^^^^^
6+
7+
error: aborting due to 1 previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// build-fail
2+
#![allow(unconditional_panic)]
3+
4+
#![cfg_attr(target_arch = "spirv", no_std)]
5+
use spirv_std::spirv;
6+
7+
// note that &mut [usize; 0] will cause an even worse panic
8+
#[spirv(compute(threads(1, 1, 1)))]
9+
pub fn compute(m: [usize; 0]) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: entry point parameter type not yet supported (`[usize; 0]` has size `0`)
2+
--> $DIR/array_entry_param.rs:9:19
3+
|
4+
9 | pub fn compute(m: [usize; 0]) {}
5+
| ^^^^^^^^^^
6+
7+
error: aborting due to 1 previous error
8+
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// build-pass
2+
#![cfg_attr(target_arch = "spirv", no_std)]
3+
use spirv_std::spirv;
4+
5+
#[spirv(compute(threads(1, 1, 1)))]
6+
pub fn compute() {
7+
let mut array = [(); 0];
8+
for i in 0..array.len() {
9+
array[i] = ();
10+
}
11+
let () = array[0];
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: this operation will panic at runtime
2+
--> $DIR/array_zst_0.rs:11:14
3+
|
4+
11 | let () = array[0];
5+
| ^^^^^^^^ index out of bounds: the length is 0 but the index is 0
6+
|
7+
= note: `#[deny(unconditional_panic)]` on by default
8+
9+
error: aborting due to 1 previous error
10+

tests/ui/lang/core/array/gep0.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// build-fail
2+
3+
#![cfg_attr(target_arch = "spirv", no_std)]
4+
use spirv_std::spirv;
5+
6+
fn example<const N: usize>() {
7+
let mut array = [0; N];
8+
for i in 0..N {
9+
array[i] += i;
10+
}
11+
}
12+
13+
#[spirv(compute(threads(1, 1, 1)))]
14+
pub fn compute() {
15+
example::<0>();
16+
}

tests/ui/lang/core/array/gep0.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: evaluation of constant value failed: cannot index into [u32; 0]
2+
--> $DIR/gep0.rs:9:9
3+
|
4+
9 | array[i] += i;
5+
| ^^^^^^^^^^^^^
6+
7+
error: aborting due to 1 previous error
8+
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// build-pass
2+
// compile-flags: -C llvm-args=--disassemble-entry=compute
3+
#![allow(unconditional_panic)]
4+
5+
#![cfg_attr(target_arch = "spirv", no_std)]
6+
use spirv_std::spirv;
7+
8+
#[spirv(compute(threads(1, 1, 1)))]
9+
pub fn compute() {
10+
let mut array = [0; 0];
11+
// writes to an array compile fine although they should be a panic.
12+
// (&mut array)[0] = 1; // this fails to compile, but it seems that below is
13+
// optimized out, and I'm not sure where.
14+
// GEP is not being hit, and neither is any load/store.
15+
array[0] = 1;
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
%1 = OpFunction %2 None %3
2+
%4 = OpLabel
3+
OpLine %5 15 4
4+
%6 = OpULessThan %7 %8 %8
5+
OpNoLine
6+
OpSelectionMerge %9 None
7+
OpBranchConditional %6 %10 %11
8+
%10 = OpLabel
9+
OpBranch %9
10+
%11 = OpLabel
11+
OpReturn
12+
%9 = OpLabel
13+
OpReturn
14+
OpFunctionEnd
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![cfg_attr(target_arch = "spirv", no_std)]
2+
use spirv_std::spirv;
3+
4+
#[spirv(compute(threads(1, 1, 1)))]
5+
pub fn compute() {
6+
let array = [0; 0];
7+
}

0 commit comments

Comments
 (0)