Commit 0c2526b18e

Robin Voetter <robin@voetter.nl>
2022-11-28 18:12:03
spirv: some fixes and improvements
- Adds the Int8. Int16, Int64 and GenericPointer capabilities. TODO: This should integrate with the feature system. - Default some struct fields of SPIR-V types so that we dont need to type them all the time. - Store struct field name's in SPIR-V types, and generate the OpMemberName decoration if they are non-null. - Also add the field names to the actual SPIR-V types. - Generate OpName for functions.
1 parent e443b1b
Changed files (4)
src/codegen/spirv/Module.zig
@@ -437,8 +437,16 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
 }
 
 fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct) !void {
+    const debug_names = &self.sections.debug_names;
     const annotations = &self.sections.annotations;
 
+    if (info.name.len != 0) {
+        try debug_names.emit(self.gpa, .OpName, .{
+            .target = target,
+            .name = info.name,
+        });
+    }
+
     // Decorations for the struct type itself.
     if (info.decorations.block)
         try annotations.decorate(self.gpa, target, .Block);
@@ -457,6 +465,25 @@ fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct
     for (info.members, 0..) |member, i| {
         const d = member.decorations;
         const index = @intCast(Word, i);
+
+        if (member.name.len != 0) {
+            try debug_names.emit(self.gpa, .OpMemberName, .{
+                .type = target,
+                .member = index,
+                .name = member.name,
+            });
+        }
+
+        switch (member.offset) {
+            .none => {},
+            else => try annotations.decorateMember(
+                self.gpa,
+                target,
+                index,
+                .{ .Offset = .{ .byte_offset = @enumToInt(member.offset) } },
+            ),
+        }
+
         switch (d.matrix_layout) {
             .row_major => try annotations.decorateMember(self.gpa, target, index, .RowMajor),
             .col_major => try annotations.decorateMember(self.gpa, target, index, .ColMajor),
src/codegen/spirv/type.zig
@@ -429,13 +429,13 @@ pub const Type = extern union {
             element_type: Ref,
             /// Type has the 'ArrayStride' decoration.
             /// If zero, no stride is present.
-            array_stride: u32,
+            array_stride: u32 = 0,
         };
 
         pub const Struct = struct {
             base: Payload = .{ .tag = .@"struct" },
-            // TODO: name
             members: []Member,
+            name: []const u8 = "",
             decorations: StructDecorations = .{},
 
             /// Extra information for decorations, packed for efficiency. Fields are stored sequentially by
@@ -444,11 +444,13 @@ pub const Type = extern union {
 
             pub const Member = struct {
                 ty: Ref,
-                offset: u32,
-                // TODO: name
+                name: []const u8 = "",
+                offset: MemberOffset = .none,
                 decorations: MemberDecorations = .{},
             };
 
+            pub const MemberOffset = enum(u32) { none = 0xFFFF_FFFF, _ };
+
             pub const StructDecorations = packed struct {
                 /// Type has the 'Block' decoration.
                 block: bool = false,
@@ -544,11 +546,11 @@ pub const Type = extern union {
             /// Type has the 'ArrayStride' decoration.
             /// This is valid for pointers to elements of an array.
             /// If zero, no stride is present.
-            array_stride: u32,
+            array_stride: u32 = 0,
             /// Type has the 'Alignment' decoration.
             alignment: ?u32,
             /// Type has the 'MaxByteOffset' decoration.
-            max_byte_offset: ?u32,
+            max_byte_offset: ?u32 = null,
         };
 
         pub const Function = struct {
src/codegen/spirv.zig
@@ -585,9 +585,10 @@ pub const DeclGen = struct {
         switch (ty.zigTypeTag()) {
             .Void, .NoReturn => return try self.spv.resolveType(SpvType.initTag(.void)),
             .Bool => {
-                // TODO: SPIR-V booleans are opaque. For local variables this is fine, but for structs
-                // members we want to use integer types instead.
-                return try self.spv.resolveType(SpvType.initTag(.bool));
+                // SPIR-V booleans are opaque, which is fine for operations, but they cant be stored.
+                // This function returns the *stored* type, for values directly we convert this into a bool when
+                // it is loaded, and convert it back to this type when stored.
+                return try self.intType(.unsigned, 1);
             },
             .Int => {
                 const int_info = ty.intInfo(target);
@@ -627,7 +628,6 @@ pub const DeclGen = struct {
                 payload.* = .{
                     .element_type = try self.resolveType(elem_ty),
                     .length = total_len,
-                    .array_stride = @intCast(u32, ty.abiSize(target)),
                 };
                 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
             },
@@ -654,29 +654,18 @@ pub const DeclGen = struct {
                 ptr_payload.* = .{
                     .storage_class = spirvStorageClass(ptr_info.@"addrspace"),
                     .child_type = try self.resolveType(ptr_info.pointee_type),
-                    // TODO: ???
-                    .array_stride = 0,
                     // Note: only available in Kernels!
                     .alignment = ty.ptrAlignment(target) * 8,
-                    .max_byte_offset = null,
                 };
-                const spv_ptr_ty = try self.spv.resolveType(SpvType.initPayload(&ptr_payload.base));
+                const ptr_ty_id = try self.spv.resolveType(SpvType.initPayload(&ptr_payload.base));
 
                 if (ptr_info.size != .Slice) {
-                    return spv_ptr_ty;
+                    return ptr_ty_id;
                 }
 
-                var buf: Type.SlicePtrFieldTypeBuffer = undefined;
-                const ptr_ty = ty.slicePtrFieldType(&buf);
-                const len_ty = Type.usize;
-
-                const ptr_size = ptr_ty.abiSize(target);
-                const len_align = len_ty.abiAlignment(target);
-                const len_offset = std.mem.alignForwardGeneric(u64, ptr_size, len_align);
-
                 return try self.simpleStructType(&.{
-                    .{ .ty = spv_ptr_ty, .offset = 0 },
-                    .{ .ty = try self.sizeType(), .offset = @intCast(u32, len_offset) },
+                    .{ .ty = ptr_ty_id, .name = "ptr" },
+                    .{ .ty = try self.sizeType(), .name = "len" },
                 });
             },
             .Vector => {
@@ -700,18 +689,15 @@ pub const DeclGen = struct {
                 if (ty.isSimpleTupleOrAnonStruct()) {
                     const tuple = ty.tupleFields();
                     const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, tuple.types.len);
-                    var member_index: usize = 0;
+                    var member_index: u32 = 0;
                     for (tuple.types) |field_ty, i| {
                         const field_val = tuple.values[i];
-                        if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue;
-
+                        if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBitsIgnoreComptime()) continue;
                         members[member_index] = .{
                             .ty = try self.resolveType(field_ty),
-                            .offset = 0,
                         };
                         member_index += 1;
                     }
-
                     const payload = try self.spv.arena.create(SpvType.Payload.Struct);
                     payload.* = .{
                         .members = members[0..member_index],
@@ -727,18 +713,23 @@ pub const DeclGen = struct {
 
                 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count());
                 var member_index: usize = 0;
-                for (struct_ty.fields.values()) |field| {
+                for (struct_ty.fields.values()) |field, i| {
                     if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
 
                     members[member_index] = .{
                         .ty = try self.resolveType(field.ty),
-                        .offset = field.offset,
+                        .name = struct_ty.fields.keys()[i],
                     };
+                    member_index += 1;
                 }
 
+                const name = try struct_ty.getFullyQualifiedName(self.module);
+                defer self.module.gpa.free(name);
+
                 const payload = try self.spv.arena.create(SpvType.Payload.Struct);
                 payload.* = .{
                     .members = members[0..member_index],
+                    .name = try self.spv.arena.dupe(u8, name),
                 };
                 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
             },
@@ -808,6 +799,14 @@ pub const DeclGen = struct {
             // Append the actual code into the functions section.
             try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});
             try self.spv.addFunction(self.func);
+
+            const fqn = try decl.getFullyQualifiedName(self.module);
+            defer self.module.gpa.free(fqn);
+
+            try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{
+                .target = result_id.toRef(),
+                .name = fqn,
+            });
         } else {
             // TODO
             // return self.todo("generate decl type {}", .{decl.ty.zigTypeTag()});
@@ -1053,8 +1052,6 @@ pub const DeclGen = struct {
     fn airOverflowArithOp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
         if (self.liveness.isUnused(inst)) return null;
 
-        const target = self.getTarget();
-
         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
         const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
         const lhs = try self.resolve(extra.lhs);
@@ -1082,8 +1079,8 @@ pub const DeclGen = struct {
             // It is almost the same as the zig one, except that the fields must be the same type
             // and they must be unsigned.
             const overflow_result_ty = try self.simpleStructTypeId(&.{
-                .{ .ty = overflow_member_ty, .offset = 0 },
-                .{ .ty = overflow_member_ty, .offset = @intCast(u32, operand_ty.abiSize(target)) },
+                .{ .ty = overflow_member_ty, .name = "res" },
+                .{ .ty = overflow_member_ty, .name = "ov" },
             });
             const result_id = self.spv.allocId();
             try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{
@@ -1098,7 +1095,7 @@ pub const DeclGen = struct {
         // Now convert the SPIR-V flavor result into a Zig-flavor result.
         // First, extract the two fields.
         const unsigned_result = try self.extractField(overflow_member_ty_id, op_result_id, 0);
-        const overflow = try self.extractField(overflow_member_ty_id, op_result_id, 0);
+        const overflow = try self.extractField(overflow_member_ty_id, op_result_id, 1);
 
         // We need to convert the results to the types that Zig expects here.
         // The `result` is the same type except unsigned, so we can just bitcast that.
@@ -1190,8 +1187,9 @@ pub const DeclGen = struct {
             .float => 0,
             .bool => 1,
             .strange_integer => blk: {
-                lhs_id = try self.maskStrangeInt(result_type_id, lhs_id, info.bits);
-                rhs_id = try self.maskStrangeInt(result_type_id, rhs_id, info.bits);
+                const op_ty_id = try self.resolveTypeId(op_ty);
+                lhs_id = try self.maskStrangeInt(op_ty_id, lhs_id, info.bits);
+                rhs_id = try self.maskStrangeInt(op_ty_id, rhs_id, info.bits);
                 break :blk switch (info.signedness) {
                     .signed => @as(usize, 1),
                     .unsigned => @as(usize, 2),
@@ -1437,7 +1435,7 @@ pub const DeclGen = struct {
                 else => {
                     const field_index_id = self.spv.allocId();
                     const u32_ty_id = self.spv.typeResultId(try self.intType(.unsigned, 32));
-                    try self.func.body.emit(self.spv.gpa, .OpConstant, .{
+                    try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpConstant, .{
                         .id_result_type = u32_ty_id,
                         .id_result = field_index_id,
                         .value = .{ .uint32 = field_index },
@@ -1632,7 +1630,6 @@ pub const DeclGen = struct {
     }
 
     fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {
-        if (self.liveness.isUnused(inst)) return;
         const operand = self.air.instructions.items(.data)[inst].un_op;
         const operand_ty = self.air.typeOf(operand);
         if (operand_ty.hasRuntimeBits()) {
src/link/SpirV.zig
@@ -244,7 +244,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
 fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
     // TODO: Integrate with a hypothetical feature system
     const caps: []const spec.Capability = switch (target.os.tag) {
-        .opencl => &.{ .Kernel, .Addresses },
+        .opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .GenericPointer },
         .glsl450 => &.{.Shader},
         .vulkan => &.{.Shader},
         else => unreachable, // TODO