Commit 2d52fc762d

Robin Voetter <robin@voetter.nl>
2023-10-08 10:54:41
spirv: handle zero-sized arrays
1 parent d8b5917
Changed files (2)
src
codegen
test
behavior
src/codegen/spirv.zig
@@ -1008,8 +1008,7 @@ const DeclGen = struct {
             .func => {
                 // TODO: Properly lower function pointers. For now we are going to hack around it and
                 // just generate an empty pointer. Function pointers are represented by a pointer to usize.
-                // TODO: Add dependency
-                return try self.spv.constNull(ty_ref);
+                return try self.spv.constUndef(ty_ref);
             },
             .extern_func => unreachable, // TODO
             else => {},
@@ -1253,6 +1252,18 @@ const DeclGen = struct {
                 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(mod)) orelse {
                     return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(mod)});
                 };
+                if (!ty.hasRuntimeBitsIgnoreComptime(mod)) {
+                    // The size of the array would be 0, but that is not allowed in SPIR-V.
+                    // This path can be reached for example when there is a slicing of a pointer
+                    // that produces a zero-length array. In all cases where this type can be generated,
+                    // we should be in an indirect path (direct uses of this type should be filtered out in Sema).
+                    assert(repr == .indirect);
+
+                    return try self.spv.resolve(.{ .opaque_type = .{
+                        .name = try self.spv.resolveString("zero-sized array"),
+                    } });
+                }
+
                 const ty_ref = try self.spv.arrayType(total_len, elem_ty_ref);
                 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
                 return ty_ref;
@@ -2742,22 +2753,23 @@ const DeclGen = struct {
         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
         const array_ptr_ty = self.typeOf(ty_op.operand);
         const array_ty = array_ptr_ty.childType(mod);
-        const elem_ty = array_ptr_ty.elemType2(mod); // use elemType() so that we get T for *[N]T.
-        const elem_ty_ref = try self.resolveType(elem_ty, .indirect);
-        const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, spvStorageClass(array_ptr_ty.ptrAddressSpace(mod)));
         const slice_ty = self.typeOfIndex(inst);
+        const elem_ptr_ty = slice_ty.slicePtrFieldType(mod);
+
+        const elem_ptr_ty_ref = try self.resolveType(elem_ptr_ty, .direct);
         const slice_ty_ref = try self.resolveType(slice_ty, .direct);
         const size_ty_ref = try self.sizeType();
 
         const array_ptr_id = try self.resolve(ty_op.operand);
         const len_id = try self.constInt(size_ty_ref, array_ty.arrayLen(mod));
 
-        if (!array_ty.hasRuntimeBitsIgnoreComptime(mod)) {
-            unreachable; // TODO
-        }
+        const elem_ptr_id = if (!array_ty.hasRuntimeBitsIgnoreComptime(mod))
+            // Note: The pointer is something like *opaque{}, so we need to bitcast it to the element type.
+            try self.bitCast(elem_ptr_ty, array_ptr_ty, array_ptr_id)
+        else
+            // Convert the pointer-to-array to a pointer to the first element.
+            try self.accessChain(elem_ptr_ty_ref, array_ptr_id, &.{0});
 
-        // Convert the pointer-to-array to a pointer to the first element.
-        const elem_ptr_id = try self.accessChain(elem_ptr_ty_ref, array_ptr_id, &.{0});
         return try self.constructStruct(slice_ty_ref, &.{ elem_ptr_id, len_id });
     }
 
@@ -2916,8 +2928,10 @@ const DeclGen = struct {
         const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
         const ptr_ty = self.typeOf(bin_op.lhs);
         const elem_ty = ptr_ty.childType(mod);
-        // TODO: Make this return a null ptr or something
-        if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) return null;
+        if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) {
+            const ptr_ty_ref = try self.resolveType(ptr_ty, .direct);
+            return try self.spv.constUndef(ptr_ty_ref);
+        }
 
         const ptr_id = try self.resolve(bin_op.lhs);
         const index_id = try self.resolve(bin_op.rhs);
test/behavior/slice.zig
@@ -64,7 +64,6 @@ test "comptime slice of undefined pointer of length 0" {
 
 test "implicitly cast array of size 0 to slice" {
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
 
     var msg = [_]u8{};
     try assertLenIsZero(&msg);
@@ -172,7 +171,6 @@ test "comptime pointer cast array and then slice" {
 test "slicing zero length array" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
 
     const s1 = ""[0..];
     const s2 = ([_]u32{})[0..];
@@ -583,7 +581,6 @@ test "slice pointer-to-array null terminated" {
 
 test "slice pointer-to-array zero length" {
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
 
     comptime {
         {
@@ -793,8 +790,6 @@ test "global slice field access" {
 }
 
 test "slice of void" {
-    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
-
     var n: usize = 10;
     var arr: [12]void = undefined;
     const slice = @as([]void, &arr)[0..n];
@@ -802,8 +797,6 @@ test "slice of void" {
 }
 
 test "slice with dereferenced value" {
-    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
-
     var a: usize = 0;
     var idx: *usize = &a;
     _ = blk: {
@@ -819,7 +812,6 @@ test "slice with dereferenced value" {
 
 test "empty slice ptr is non null" {
     if (builtin.zig_backend == .stage2_aarch64 and builtin.os.tag == .macos) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
 
     {
         const empty_slice: []u8 = &[_]u8{};