Commit 23e210c38f

Robin Voetter <robin@voetter.nl>
2022-11-27 12:51:05
spirv: (some) array and struct constants
Starts implementing constant lowering for some array and struct constants. In particular, TODO are packed structs.
1 parent bca6f29
Changed files (1)
src
codegen
src/codegen/spirv.zig
@@ -410,6 +410,44 @@ pub const DeclGen = struct {
                     .value = value,
                 });
             },
+            .Array => switch (val.tag()) {
+                .aggregate => { // todo: combine with Vector
+                    const elem_vals = val.castTag(.aggregate).?.data;
+                    const elem_ty = ty.elemType();
+                    const len = @intCast(u32, ty.arrayLenIncludingSentinel()); // TODO: limit spir-v to 32 bit arrays in a more elegant way.
+                    const constituents = try self.spv.gpa.alloc(IdRef, len);
+                    defer self.spv.gpa.free(constituents);
+                    for (elem_vals[0..len]) |elem_val, i| {
+                        constituents[i] = try self.genConstant(elem_ty, elem_val);
+                    }
+                    try section.emit(self.spv.gpa, .OpConstantComposite, .{
+                        .id_result_type = result_type_id,
+                        .id_result = result_id,
+                        .constituents = constituents,
+                    });
+                },
+                .repeated => {
+                    const elem_val = val.castTag(.repeated).?.data;
+                    const elem_ty = ty.elemType();
+                    const len = @intCast(u32, ty.arrayLenIncludingSentinel()); // TODO: limit spir-v to 32 bit arrays in a more elegant way.
+                    const constituents = try self.spv.gpa.alloc(IdRef, len);
+                    defer self.spv.gpa.free(constituents);
+
+                    const elem_val_id = try self.genConstant(elem_ty, elem_val);
+                    for (constituents[0..len]) |*elem| {
+                        elem.* = elem_val_id;
+                    }
+                    if (ty.sentinel()) |sentinel| {
+                        constituents[len] = try self.genConstant(elem_ty, sentinel);
+                    }
+                    try section.emit(self.spv.gpa, .OpConstantComposite, .{
+                        .id_result_type = result_type_id,
+                        .id_result = result_id,
+                        .constituents = constituents,
+                    });
+                },
+                else => return self.todo("array constant with tag {s}", .{@tagName(val.tag())}),
+            },
             .Vector => switch (val.tag()) {
                 .aggregate => {
                     const elem_vals = val.castTag(.aggregate).?.data;
@@ -427,20 +465,20 @@ pub const DeclGen = struct {
                         .constituents = elem_refs,
                     });
                 },
-                else => unreachable, // TODO
+                else => return self.todo("vector constant with tag {s}", .{@tagName(val.tag())}),
             },
             .Enum => {
-                var int_buffer: Value.Payload.U64 = undefined;
-                const int_val = val.enumToInt(ty, &int_buffer).toUnsignedInt(target);
-
-                var buffer: Type.Payload.Bits = undefined;
-                const int_ty = ty.intTagType(&buffer);
+                var ty_buffer: Type.Payload.Bits = undefined;
+                const int_ty = ty.intTagType(&ty_buffer);
                 const int_info = int_ty.intInfo(target);
 
                 const backing_bits = self.backingIntBits(int_info.bits) orelse {
                     return self.todo("implement composite int constants for {}", .{int_ty.fmtDebug()});
                 };
 
+                var int_buffer: Value.Payload.U64 = undefined;
+                const int_val = val.enumToInt(ty, &int_buffer).toUnsignedInt(target); // TODO: composite integer constants
+
                 const value: spec.LiteralContextDependentNumber = switch (backing_bits) {
                     1...32 => .{ .uint32 = @truncate(u32, int_val) },
                     33...64 => .{ .uint64 = int_val },
@@ -453,6 +491,48 @@ pub const DeclGen = struct {
                     .value = value,
                 });
             },
+            .Struct => {
+                const constituents = if (ty.isSimpleTupleOrAnonStruct()) blk: {
+                    const tuple = ty.tupleFields();
+                    const constituents = try self.spv.gpa.alloc(IdRef, tuple.types.len);
+                    errdefer self.spv.gpa.free(constituents);
+
+                    var member_index: usize = 0;
+                    for (tuple.types) |field_ty, i| {
+                        const field_val = tuple.values[i];
+                        if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue;
+                        constituents[member_index] = try self.genConstant(field_ty, field_val);
+                        member_index += 1;
+                    }
+
+                    break :blk constituents[0..member_index];
+                } else blk: {
+                    const struct_ty = ty.castTag(.@"struct").?.data;
+
+                    if (struct_ty.layout == .Packed) {
+                        return self.todo("packed struct constants", .{});
+                    }
+
+                    const field_vals = val.castTag(.aggregate).?.data;
+                    const constituents = try self.spv.gpa.alloc(IdRef, struct_ty.fields.count());
+                    errdefer self.spv.gpa.free(constituents);
+                    var member_index: usize = 0;
+                    for (struct_ty.fields.values()) |field, i| {
+                        if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
+                        constituents[member_index] = try self.genConstant(field.ty, field_vals[i]);
+                        member_index += 1;
+                    }
+
+                    break :blk constituents[0..member_index];
+                };
+                defer self.spv.gpa.free(constituents);
+
+                try section.emit(self.spv.gpa, .OpConstantComposite, .{
+                    .id_result_type = result_type_id,
+                    .id_result = result_id,
+                    .constituents = constituents,
+                });
+            },
             .Void => unreachable,
             .Fn => unreachable,
             else => return self.todo("constant generation of type {s}: {}", .{ @tagName(ty.zigTypeTag()), ty.fmtDebug() }),
@@ -539,9 +619,8 @@ pub const DeclGen = struct {
             },
             .Array => {
                 const elem_ty = ty.childType();
-                const total_len_u64 = ty.arrayLen() + @boolToInt(ty.sentinel() != null);
-                const total_len = std.math.cast(u32, total_len_u64) orelse {
-                    return self.fail("array type of {} elements is too large", .{total_len_u64});
+                const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse {
+                    return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()});
                 };
 
                 const payload = try self.spv.arena.create(SpvType.Payload.Array);
@@ -630,6 +709,7 @@ pub const DeclGen = struct {
                             .ty = try self.resolveType(field_ty),
                             .offset = 0,
                         };
+                        member_index += 1;
                     }
 
                     const payload = try self.spv.arena.create(SpvType.Payload.Struct);