Skip to content

Commit d569771

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 561d2eb commit d569771

16 files changed

+146
-6
lines changed

crates/rustc_codegen_spirv/src/abi.rs

-3
Original file line numberDiff line numberDiff line change
@@ -745,9 +745,6 @@ fn trans_aggregate<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>
745745
element: element_type,
746746
}
747747
.def(span, cx)
748-
} else if count == 0 {
749-
// spir-v doesn't support zero-sized arrays
750-
create_zst(cx, span, ty)
751748
} else {
752749
let count_const = cx.constant_u32(span, count as u32);
753750
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
@@ -409,7 +409,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
409409
let ptr = ptr.strip_ptrcasts();
410410
let mut leaf_ty = match self.lookup_type(ptr.ty) {
411411
SpirvType::Pointer { pointee } => pointee,
412-
other => self.fatal(format!("non-pointer type: {other:?}")),
412+
SpirvType::Adt { .. } => return None,
413+
other => self.fatal(format!("adjust_pointer for non-pointer type: {other:?}")),
413414
};
414415

415416
// FIXME(eddyb) this isn't efficient, `recover_access_chain_from_offset`
@@ -531,8 +532,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
531532
let stride = ty_kind.sizeof(self)?;
532533
ty_size = MaybeSized::Sized(stride);
533534

534-
indices.push((offset.bytes() / stride.bytes()).try_into().ok()?);
535-
offset = Size::from_bytes(offset.bytes() % stride.bytes());
535+
if stride.bytes() == 0 {
536+
indices.push(0);
537+
offset = Size::from_bytes(0);
538+
} else {
539+
indices.push((offset.bytes() / stride.bytes()).try_into().ok()?);
540+
offset = Size::from_bytes(offset.bytes() % stride.bytes());
541+
}
536542
}
537543
_ => return None,
538544
}
@@ -569,6 +575,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
569575
.iter()
570576
.map(|index| {
571577
result_pointee_type = match self.lookup_type(result_pointee_type) {
578+
SpirvType::Array { count, element }
579+
if self.builder.lookup_const_scalar(count) == Some(0) =>
580+
{
581+
self.fatal(format!(
582+
"evaluation of constant value failed: cannot index into [{}; 0]",
583+
self.debug_type(element)
584+
))
585+
}
572586
SpirvType::Array { element, .. } | SpirvType::RuntimeArray { element } => {
573587
element
574588
}

crates/rustc_codegen_spirv/src/codegen_cx/constant.rs

+5
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,11 @@ impl<'tcx> CodegenCx<'tcx> {
480480
}
481481
self.constant_composite(ty, values.into_iter())
482482
}
483+
SpirvType::Array { count, .. }
484+
if self.builder.lookup_const_scalar(count) == Some(0) =>
485+
{
486+
self.undef(ty)
487+
}
483488
SpirvType::Array { element, count } => {
484489
let count = self.builder.lookup_const_scalar(count).unwrap() as usize;
485490
let values = (0..count).map(|_| {

crates/rustc_codegen_spirv/src/spirv_type.rs

+3
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ impl SpirvType<'_> {
373373
.bytes(),
374374
)
375375
.expect("alignof: Vectors must have power-of-2 size"),
376+
Self::Array { count, .. } if cx.builder.lookup_const_scalar(count) == Some(0) => {
377+
Align::from_bytes(0).unwrap()
378+
}
376379
Self::Array { element, .. }
377380
| Self::RuntimeArray { element }
378381
| 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)