authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2022-11-28 18:12:03+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-04-09 01:51:50+02:00
log0c2526b18ec21d8155b070022d3b3b9069303744
tree880e4e26bf2b703b6d6de83bcda64c8b3bba69ec
parente443b1bed7ccce45bf039a304fa8fa271f1faa0b
signaturelock-open Commit is signed but in an unrecognized format.

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.

4 files changed, 68 insertions(+), 42 deletions(-)

src/codegen/spirv.zig+32-35
...@@ -585,9 +585,10 @@ pub const DeclGen = struct {...@@ -585,9 +585,10 @@ pub const DeclGen = struct {
585 switch (ty.zigTypeTag()) {585 switch (ty.zigTypeTag()) {
586 .Void, .NoReturn => return try self.spv.resolveType(SpvType.initTag(.void)),586 .Void, .NoReturn => return try self.spv.resolveType(SpvType.initTag(.void)),
587 .Bool => {587 .Bool => {
588 // TODO: SPIR-V booleans are opaque. For local variables this is fine, but for structs588 // SPIR-V booleans are opaque, which is fine for operations, but they cant be stored.
589 // members we want to use integer types instead.589 // This function returns the *stored* type, for values directly we convert this into a bool when
590 return try self.spv.resolveType(SpvType.initTag(.bool));590 // it is loaded, and convert it back to this type when stored.
591 return try self.intType(.unsigned, 1);
591 },592 },
592 .Int => {593 .Int => {
593 const int_info = ty.intInfo(target);594 const int_info = ty.intInfo(target);
...@@ -627,7 +628,6 @@ pub const DeclGen = struct {...@@ -627,7 +628,6 @@ pub const DeclGen = struct {
627 payload.* = .{628 payload.* = .{
628 .element_type = try self.resolveType(elem_ty),629 .element_type = try self.resolveType(elem_ty),
629 .length = total_len,630 .length = total_len,
630 .array_stride = @intCast(u32, ty.abiSize(target)),
631 };631 };
632 return try self.spv.resolveType(SpvType.initPayload(&payload.base));632 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
633 },633 },
...@@ -654,29 +654,18 @@ pub const DeclGen = struct {...@@ -654,29 +654,18 @@ pub const DeclGen = struct {
654 ptr_payload.* = .{654 ptr_payload.* = .{
655 .storage_class = spirvStorageClass(ptr_info.@"addrspace"),655 .storage_class = spirvStorageClass(ptr_info.@"addrspace"),
656 .child_type = try self.resolveType(ptr_info.pointee_type),656 .child_type = try self.resolveType(ptr_info.pointee_type),
657 // TODO: ???
658 .array_stride = 0,
659 // Note: only available in Kernels!657 // Note: only available in Kernels!
660 .alignment = ty.ptrAlignment(target) * 8,658 .alignment = ty.ptrAlignment(target) * 8,
661 .max_byte_offset = null,
662 };659 };
663 const spv_ptr_ty = try self.spv.resolveType(SpvType.initPayload(&ptr_payload.base));660 const ptr_ty_id = try self.spv.resolveType(SpvType.initPayload(&ptr_payload.base));
664661
665 if (ptr_info.size != .Slice) {662 if (ptr_info.size != .Slice) {
666 return spv_ptr_ty;663 return ptr_ty_id;
667 }664 }
668665
669 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
670 const ptr_ty = ty.slicePtrFieldType(&buf);
671 const len_ty = Type.usize;
672
673 const ptr_size = ptr_ty.abiSize(target);
674 const len_align = len_ty.abiAlignment(target);
675 const len_offset = std.mem.alignForwardGeneric(u64, ptr_size, len_align);
676
677 return try self.simpleStructType(&.{666 return try self.simpleStructType(&.{
678 .{ .ty = spv_ptr_ty, .offset = 0 },667 .{ .ty = ptr_ty_id, .name = "ptr" },
679 .{ .ty = try self.sizeType(), .offset = @intCast(u32, len_offset) },668 .{ .ty = try self.sizeType(), .name = "len" },
680 });669 });
681 },670 },
682 .Vector => {671 .Vector => {
...@@ -700,18 +689,15 @@ pub const DeclGen = struct {...@@ -700,18 +689,15 @@ pub const DeclGen = struct {
700 if (ty.isSimpleTupleOrAnonStruct()) {689 if (ty.isSimpleTupleOrAnonStruct()) {
701 const tuple = ty.tupleFields();690 const tuple = ty.tupleFields();
702 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, tuple.types.len);691 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, tuple.types.len);
703 var member_index: usize = 0;692 var member_index: u32 = 0;
704 for (tuple.types) |field_ty, i| {693 for (tuple.types) |field_ty, i| {
705 const field_val = tuple.values[i];694 const field_val = tuple.values[i];
706 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue;695 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBitsIgnoreComptime()) continue;
707
708 members[member_index] = .{696 members[member_index] = .{
709 .ty = try self.resolveType(field_ty),697 .ty = try self.resolveType(field_ty),
710 .offset = 0,
711 };698 };
712 member_index += 1;699 member_index += 1;
713 }700 }
714
715 const payload = try self.spv.arena.create(SpvType.Payload.Struct);701 const payload = try self.spv.arena.create(SpvType.Payload.Struct);
716 payload.* = .{702 payload.* = .{
717 .members = members[0..member_index],703 .members = members[0..member_index],
...@@ -727,18 +713,23 @@ pub const DeclGen = struct {...@@ -727,18 +713,23 @@ pub const DeclGen = struct {
727713
728 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count());714 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count());
729 var member_index: usize = 0;715 var member_index: usize = 0;
730 for (struct_ty.fields.values()) |field| {716 for (struct_ty.fields.values()) |field, i| {
731 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;717 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
732718
733 members[member_index] = .{719 members[member_index] = .{
734 .ty = try self.resolveType(field.ty),720 .ty = try self.resolveType(field.ty),
735 .offset = field.offset,721 .name = struct_ty.fields.keys()[i],
736 };722 };
723 member_index += 1;
737 }724 }
738725
726 const name = try struct_ty.getFullyQualifiedName(self.module);
727 defer self.module.gpa.free(name);
728
739 const payload = try self.spv.arena.create(SpvType.Payload.Struct);729 const payload = try self.spv.arena.create(SpvType.Payload.Struct);
740 payload.* = .{730 payload.* = .{
741 .members = members[0..member_index],731 .members = members[0..member_index],
732 .name = try self.spv.arena.dupe(u8, name),
742 };733 };
743 return try self.spv.resolveType(SpvType.initPayload(&payload.base));734 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
744 },735 },
...@@ -808,6 +799,14 @@ pub const DeclGen = struct {...@@ -808,6 +799,14 @@ pub const DeclGen = struct {
808 // Append the actual code into the functions section.799 // Append the actual code into the functions section.
809 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});800 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});
810 try self.spv.addFunction(self.func);801 try self.spv.addFunction(self.func);
802
803 const fqn = try decl.getFullyQualifiedName(self.module);
804 defer self.module.gpa.free(fqn);
805
806 try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{
807 .target = result_id.toRef(),
808 .name = fqn,
809 });
811 } else {810 } else {
812 // TODO811 // TODO
813 // return self.todo("generate decl type {}", .{decl.ty.zigTypeTag()});812 // return self.todo("generate decl type {}", .{decl.ty.zigTypeTag()});
...@@ -1053,8 +1052,6 @@ pub const DeclGen = struct {...@@ -1053,8 +1052,6 @@ pub const DeclGen = struct {
1053 fn airOverflowArithOp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {1052 fn airOverflowArithOp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
1054 if (self.liveness.isUnused(inst)) return null;1053 if (self.liveness.isUnused(inst)) return null;
10551054
1056 const target = self.getTarget();
1057
1058 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1055 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1059 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;1056 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1060 const lhs = try self.resolve(extra.lhs);1057 const lhs = try self.resolve(extra.lhs);
...@@ -1082,8 +1079,8 @@ pub const DeclGen = struct {...@@ -1082,8 +1079,8 @@ pub const DeclGen = struct {
1082 // It is almost the same as the zig one, except that the fields must be the same type1079 // It is almost the same as the zig one, except that the fields must be the same type
1083 // and they must be unsigned.1080 // and they must be unsigned.
1084 const overflow_result_ty = try self.simpleStructTypeId(&.{1081 const overflow_result_ty = try self.simpleStructTypeId(&.{
1085 .{ .ty = overflow_member_ty, .offset = 0 },1082 .{ .ty = overflow_member_ty, .name = "res" },
1086 .{ .ty = overflow_member_ty, .offset = @intCast(u32, operand_ty.abiSize(target)) },1083 .{ .ty = overflow_member_ty, .name = "ov" },
1087 });1084 });
1088 const result_id = self.spv.allocId();1085 const result_id = self.spv.allocId();
1089 try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{1086 try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{
...@@ -1098,7 +1095,7 @@ pub const DeclGen = struct {...@@ -1098,7 +1095,7 @@ pub const DeclGen = struct {
1098 // Now convert the SPIR-V flavor result into a Zig-flavor result.1095 // Now convert the SPIR-V flavor result into a Zig-flavor result.
1099 // First, extract the two fields.1096 // First, extract the two fields.
1100 const unsigned_result = try self.extractField(overflow_member_ty_id, op_result_id, 0);1097 const unsigned_result = try self.extractField(overflow_member_ty_id, op_result_id, 0);
1101 const overflow = try self.extractField(overflow_member_ty_id, op_result_id, 0);1098 const overflow = try self.extractField(overflow_member_ty_id, op_result_id, 1);
11021099
1103 // We need to convert the results to the types that Zig expects here.1100 // We need to convert the results to the types that Zig expects here.
1104 // The `result` is the same type except unsigned, so we can just bitcast that.1101 // The `result` is the same type except unsigned, so we can just bitcast that.
...@@ -1190,8 +1187,9 @@ pub const DeclGen = struct {...@@ -1190,8 +1187,9 @@ pub const DeclGen = struct {
1190 .float => 0,1187 .float => 0,
1191 .bool => 1,1188 .bool => 1,
1192 .strange_integer => blk: {1189 .strange_integer => blk: {
1193 lhs_id = try self.maskStrangeInt(result_type_id, lhs_id, info.bits);1190 const op_ty_id = try self.resolveTypeId(op_ty);
1194 rhs_id = try self.maskStrangeInt(result_type_id, rhs_id, info.bits);1191 lhs_id = try self.maskStrangeInt(op_ty_id, lhs_id, info.bits);
1192 rhs_id = try self.maskStrangeInt(op_ty_id, rhs_id, info.bits);
1195 break :blk switch (info.signedness) {1193 break :blk switch (info.signedness) {
1196 .signed => @as(usize, 1),1194 .signed => @as(usize, 1),
1197 .unsigned => @as(usize, 2),1195 .unsigned => @as(usize, 2),
...@@ -1437,7 +1435,7 @@ pub const DeclGen = struct {...@@ -1437,7 +1435,7 @@ pub const DeclGen = struct {
1437 else => {1435 else => {
1438 const field_index_id = self.spv.allocId();1436 const field_index_id = self.spv.allocId();
1439 const u32_ty_id = self.spv.typeResultId(try self.intType(.unsigned, 32));1437 const u32_ty_id = self.spv.typeResultId(try self.intType(.unsigned, 32));
1440 try self.func.body.emit(self.spv.gpa, .OpConstant, .{1438 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpConstant, .{
1441 .id_result_type = u32_ty_id,1439 .id_result_type = u32_ty_id,
1442 .id_result = field_index_id,1440 .id_result = field_index_id,
1443 .value = .{ .uint32 = field_index },1441 .value = .{ .uint32 = field_index },
...@@ -1632,7 +1630,6 @@ pub const DeclGen = struct {...@@ -1632,7 +1630,6 @@ pub const DeclGen = struct {
1632 }1630 }
16331631
1634 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {1632 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {
1635 if (self.liveness.isUnused(inst)) return;
1636 const operand = self.air.instructions.items(.data)[inst].un_op;1633 const operand = self.air.instructions.items(.data)[inst].un_op;
1637 const operand_ty = self.air.typeOf(operand);1634 const operand_ty = self.air.typeOf(operand);
1638 if (operand_ty.hasRuntimeBits()) {1635 if (operand_ty.hasRuntimeBits()) {
src/codegen/spirv/Module.zig+27
...@@ -437,8 +437,16 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {...@@ -437,8 +437,16 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
437}437}
438438
439fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct) !void {439fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct) !void {
440 const debug_names = &self.sections.debug_names;
440 const annotations = &self.sections.annotations;441 const annotations = &self.sections.annotations;
441442
443 if (info.name.len != 0) {
444 try debug_names.emit(self.gpa, .OpName, .{
445 .target = target,
446 .name = info.name,
447 });
448 }
449
442 // Decorations for the struct type itself.450 // Decorations for the struct type itself.
443 if (info.decorations.block)451 if (info.decorations.block)
444 try annotations.decorate(self.gpa, target, .Block);452 try annotations.decorate(self.gpa, target, .Block);
...@@ -457,6 +465,25 @@ fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct...@@ -457,6 +465,25 @@ fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct
457 for (info.members, 0..) |member, i| {465 for (info.members, 0..) |member, i| {
458 const d = member.decorations;466 const d = member.decorations;
459 const index = @intCast(Word, i);467 const index = @intCast(Word, i);
468
469 if (member.name.len != 0) {
470 try debug_names.emit(self.gpa, .OpMemberName, .{
471 .type = target,
472 .member = index,
473 .name = member.name,
474 });
475 }
476
477 switch (member.offset) {
478 .none => {},
479 else => try annotations.decorateMember(
480 self.gpa,
481 target,
482 index,
483 .{ .Offset = .{ .byte_offset = @enumToInt(member.offset) } },
484 ),
485 }
486
460 switch (d.matrix_layout) {487 switch (d.matrix_layout) {
461 .row_major => try annotations.decorateMember(self.gpa, target, index, .RowMajor),488 .row_major => try annotations.decorateMember(self.gpa, target, index, .RowMajor),
462 .col_major => try annotations.decorateMember(self.gpa, target, index, .ColMajor),489 .col_major => try annotations.decorateMember(self.gpa, target, index, .ColMajor),
src/codegen/spirv/type.zig+8-6
...@@ -429,13 +429,13 @@ pub const Type = extern union {...@@ -429,13 +429,13 @@ pub const Type = extern union {
429 element_type: Ref,429 element_type: Ref,
430 /// Type has the 'ArrayStride' decoration.430 /// Type has the 'ArrayStride' decoration.
431 /// If zero, no stride is present.431 /// If zero, no stride is present.
432 array_stride: u32,432 array_stride: u32 = 0,
433 };433 };
434434
435 pub const Struct = struct {435 pub const Struct = struct {
436 base: Payload = .{ .tag = .@"struct" },436 base: Payload = .{ .tag = .@"struct" },
437 // TODO: name
438 members: []Member,437 members: []Member,
438 name: []const u8 = "",
439 decorations: StructDecorations = .{},439 decorations: StructDecorations = .{},
440440
441 /// Extra information for decorations, packed for efficiency. Fields are stored sequentially by441 /// Extra information for decorations, packed for efficiency. Fields are stored sequentially by
...@@ -444,11 +444,13 @@ pub const Type = extern union {...@@ -444,11 +444,13 @@ pub const Type = extern union {
444444
445 pub const Member = struct {445 pub const Member = struct {
446 ty: Ref,446 ty: Ref,
447 offset: u32,447 name: []const u8 = "",
448 // TODO: name448 offset: MemberOffset = .none,
449 decorations: MemberDecorations = .{},449 decorations: MemberDecorations = .{},
450 };450 };
451451
452 pub const MemberOffset = enum(u32) { none = 0xFFFF_FFFF, _ };
453
452 pub const StructDecorations = packed struct {454 pub const StructDecorations = packed struct {
453 /// Type has the 'Block' decoration.455 /// Type has the 'Block' decoration.
454 block: bool = false,456 block: bool = false,
...@@ -544,11 +546,11 @@ pub const Type = extern union {...@@ -544,11 +546,11 @@ pub const Type = extern union {
544 /// Type has the 'ArrayStride' decoration.546 /// Type has the 'ArrayStride' decoration.
545 /// This is valid for pointers to elements of an array.547 /// This is valid for pointers to elements of an array.
546 /// If zero, no stride is present.548 /// If zero, no stride is present.
547 array_stride: u32,549 array_stride: u32 = 0,
548 /// Type has the 'Alignment' decoration.550 /// Type has the 'Alignment' decoration.
549 alignment: ?u32,551 alignment: ?u32,
550 /// Type has the 'MaxByteOffset' decoration.552 /// Type has the 'MaxByteOffset' decoration.
551 max_byte_offset: ?u32,553 max_byte_offset: ?u32 = null,
552 };554 };
553555
554 pub const Function = struct {556 pub const Function = struct {
src/link/SpirV.zig+1-1
...@@ -244,7 +244,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No...@@ -244,7 +244,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
244fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {244fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
245 // TODO: Integrate with a hypothetical feature system245 // TODO: Integrate with a hypothetical feature system
246 const caps: []const spec.Capability = switch (target.os.tag) {246 const caps: []const spec.Capability = switch (target.os.tag) {
247 .opencl => &.{ .Kernel, .Addresses },247 .opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .GenericPointer },
248 .glsl450 => &.{.Shader},248 .glsl450 => &.{.Shader},
249 .vulkan => &.{.Shader},249 .vulkan => &.{.Shader},
250 else => unreachable, // TODO250 else => unreachable, // TODO