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 {
585585 switch (ty.zigTypeTag()) {
586586 .Void, .NoReturn => return try self.spv.resolveType(SpvType.initTag(.void)),
587587 .Bool => {
588 // TODO: SPIR-V booleans are opaque. For local variables this is fine, but for structs
589 // members we want to use integer types instead.
590 return try self.spv.resolveType(SpvType.initTag(.bool));
588 // SPIR-V booleans are opaque, which is fine for operations, but they cant be stored.
589 // This function returns the *stored* type, for values directly we convert this into a bool when
590 // it is loaded, and convert it back to this type when stored.
591 return try self.intType(.unsigned, 1);
591592 },
592593 .Int => {
593594 const int_info = ty.intInfo(target);
......@@ -627,7 +628,6 @@ pub const DeclGen = struct {
627628 payload.* = .{
628629 .element_type = try self.resolveType(elem_ty),
629630 .length = total_len,
630 .array_stride = @intCast(u32, ty.abiSize(target)),
631631 };
632632 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
633633 },
......@@ -654,29 +654,18 @@ pub const DeclGen = struct {
654654 ptr_payload.* = .{
655655 .storage_class = spirvStorageClass(ptr_info.@"addrspace"),
656656 .child_type = try self.resolveType(ptr_info.pointee_type),
657 // TODO: ???
658 .array_stride = 0,
659657 // Note: only available in Kernels!
660658 .alignment = ty.ptrAlignment(target) * 8,
661 .max_byte_offset = null,
662659 };
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
665662 if (ptr_info.size != .Slice) {
666 return spv_ptr_ty;
663 return ptr_ty_id;
667664 }
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
677666 return try self.simpleStructType(&.{
678 .{ .ty = spv_ptr_ty, .offset = 0 },
679 .{ .ty = try self.sizeType(), .offset = @intCast(u32, len_offset) },
667 .{ .ty = ptr_ty_id, .name = "ptr" },
668 .{ .ty = try self.sizeType(), .name = "len" },
680669 });
681670 },
682671 .Vector => {
......@@ -700,18 +689,15 @@ pub const DeclGen = struct {
700689 if (ty.isSimpleTupleOrAnonStruct()) {
701690 const tuple = ty.tupleFields();
702691 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;
704693 for (tuple.types) |field_ty, i| {
705694 const field_val = tuple.values[i];
706 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue;
707
695 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBitsIgnoreComptime()) continue;
708696 members[member_index] = .{
709697 .ty = try self.resolveType(field_ty),
710 .offset = 0,
711698 };
712699 member_index += 1;
713700 }
714
715701 const payload = try self.spv.arena.create(SpvType.Payload.Struct);
716702 payload.* = .{
717703 .members = members[0..member_index],
......@@ -727,18 +713,23 @@ pub const DeclGen = struct {
727713
728714 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count());
729715 var member_index: usize = 0;
730 for (struct_ty.fields.values()) |field| {
716 for (struct_ty.fields.values()) |field, i| {
731717 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
732718
733719 members[member_index] = .{
734720 .ty = try self.resolveType(field.ty),
735 .offset = field.offset,
721 .name = struct_ty.fields.keys()[i],
736722 };
723 member_index += 1;
737724 }
738725
726 const name = try struct_ty.getFullyQualifiedName(self.module);
727 defer self.module.gpa.free(name);
728
739729 const payload = try self.spv.arena.create(SpvType.Payload.Struct);
740730 payload.* = .{
741731 .members = members[0..member_index],
732 .name = try self.spv.arena.dupe(u8, name),
742733 };
743734 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
744735 },
......@@ -808,6 +799,14 @@ pub const DeclGen = struct {
808799 // Append the actual code into the functions section.
809800 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});
810801 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 });
811810 } else {
812811 // TODO
813812 // return self.todo("generate decl type {}", .{decl.ty.zigTypeTag()});
......@@ -1053,8 +1052,6 @@ pub const DeclGen = struct {
10531052 fn airOverflowArithOp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
10541053 if (self.liveness.isUnused(inst)) return null;
10551054
1056 const target = self.getTarget();
1057
10581055 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
10591056 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
10601057 const lhs = try self.resolve(extra.lhs);
......@@ -1082,8 +1079,8 @@ pub const DeclGen = struct {
10821079 // It is almost the same as the zig one, except that the fields must be the same type
10831080 // and they must be unsigned.
10841081 const overflow_result_ty = try self.simpleStructTypeId(&.{
1085 .{ .ty = overflow_member_ty, .offset = 0 },
1086 .{ .ty = overflow_member_ty, .offset = @intCast(u32, operand_ty.abiSize(target)) },
1082 .{ .ty = overflow_member_ty, .name = "res" },
1083 .{ .ty = overflow_member_ty, .name = "ov" },
10871084 });
10881085 const result_id = self.spv.allocId();
10891086 try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{
......@@ -1098,7 +1095,7 @@ pub const DeclGen = struct {
10981095 // Now convert the SPIR-V flavor result into a Zig-flavor result.
10991096 // First, extract the two fields.
11001097 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
11031100 // We need to convert the results to the types that Zig expects here.
11041101 // The `result` is the same type except unsigned, so we can just bitcast that.
......@@ -1190,8 +1187,9 @@ pub const DeclGen = struct {
11901187 .float => 0,
11911188 .bool => 1,
11921189 .strange_integer => blk: {
1193 lhs_id = try self.maskStrangeInt(result_type_id, lhs_id, info.bits);
1194 rhs_id = try self.maskStrangeInt(result_type_id, rhs_id, info.bits);
1190 const op_ty_id = try self.resolveTypeId(op_ty);
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);
11951193 break :blk switch (info.signedness) {
11961194 .signed => @as(usize, 1),
11971195 .unsigned => @as(usize, 2),
......@@ -1437,7 +1435,7 @@ pub const DeclGen = struct {
14371435 else => {
14381436 const field_index_id = self.spv.allocId();
14391437 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, .{
14411439 .id_result_type = u32_ty_id,
14421440 .id_result = field_index_id,
14431441 .value = .{ .uint32 = field_index },
......@@ -1632,7 +1630,6 @@ pub const DeclGen = struct {
16321630 }
16331631
16341632 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {
1635 if (self.liveness.isUnused(inst)) return;
16361633 const operand = self.air.instructions.items(.data)[inst].un_op;
16371634 const operand_ty = self.air.typeOf(operand);
16381635 if (operand_ty.hasRuntimeBits()) {
src/codegen/spirv/Module.zig+27
......@@ -437,8 +437,16 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
437437}
438438
439439fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct) !void {
440 const debug_names = &self.sections.debug_names;
440441 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
442450 // Decorations for the struct type itself.
443451 if (info.decorations.block)
444452 try annotations.decorate(self.gpa, target, .Block);
......@@ -457,6 +465,25 @@ fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct
457465 for (info.members, 0..) |member, i| {
458466 const d = member.decorations;
459467 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
460487 switch (d.matrix_layout) {
461488 .row_major => try annotations.decorateMember(self.gpa, target, index, .RowMajor),
462489 .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 {
429429 element_type: Ref,
430430 /// Type has the 'ArrayStride' decoration.
431431 /// If zero, no stride is present.
432 array_stride: u32,
432 array_stride: u32 = 0,
433433 };
434434
435435 pub const Struct = struct {
436436 base: Payload = .{ .tag = .@"struct" },
437 // TODO: name
438437 members: []Member,
438 name: []const u8 = "",
439439 decorations: StructDecorations = .{},
440440
441441 /// Extra information for decorations, packed for efficiency. Fields are stored sequentially by
......@@ -444,11 +444,13 @@ pub const Type = extern union {
444444
445445 pub const Member = struct {
446446 ty: Ref,
447 offset: u32,
448 // TODO: name
447 name: []const u8 = "",
448 offset: MemberOffset = .none,
449449 decorations: MemberDecorations = .{},
450450 };
451451
452 pub const MemberOffset = enum(u32) { none = 0xFFFF_FFFF, _ };
453
452454 pub const StructDecorations = packed struct {
453455 /// Type has the 'Block' decoration.
454456 block: bool = false,
......@@ -544,11 +546,11 @@ pub const Type = extern union {
544546 /// Type has the 'ArrayStride' decoration.
545547 /// This is valid for pointers to elements of an array.
546548 /// If zero, no stride is present.
547 array_stride: u32,
549 array_stride: u32 = 0,
548550 /// Type has the 'Alignment' decoration.
549551 alignment: ?u32,
550552 /// Type has the 'MaxByteOffset' decoration.
551 max_byte_offset: ?u32,
553 max_byte_offset: ?u32 = null,
552554 };
553555
554556 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
244244fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
245245 // TODO: Integrate with a hypothetical feature system
246246 const caps: []const spec.Capability = switch (target.os.tag) {
247 .opencl => &.{ .Kernel, .Addresses },
247 .opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .GenericPointer },
248248 .glsl450 => &.{.Shader},
249249 .vulkan => &.{.Shader},
250250 else => unreachable, // TODO