authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-09-23 01:31:38+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-10-15 13:59:22+02:00
log4ea361f6dc07826337255fdddcbdfca9b8c9a0a9
tree4f36a88e5e92846f3d763d60f5f4717efaa5eca2
parent4e22f811e746ab5771ea7355ed8dfbfcda0420c2
signaturebadge-check Signed by SSH key SHA256:CQ99aPxq+RueiL9u7z0FEki5Fm7V6T8q4PrEGmINrA4

spirv: lower pointers to zero-bit types as undef


2 files changed, 74 insertions(+), 61 deletions(-)

src/codegen/spirv.zig+73-60
......@@ -521,59 +521,6 @@ pub const DeclGen = struct {
521521 return result_id;
522522 }
523523
524 fn constructDeclRef(self: *DeclGen, ty: Type, decl_index: Decl.Index) !IdRef {
525 const mod = self.module;
526 const ty_ref = try self.resolveType(ty, .direct);
527 const ty_id = self.typeId(ty_ref);
528 const decl = mod.declPtr(decl_index);
529 const spv_decl_index = try self.resolveDecl(decl_index);
530 switch (mod.intern_pool.indexToKey(decl.val.ip_index)) {
531 .func => {
532 // TODO: Properly lower function pointers. For now we are going to hack around it and
533 // just generate an empty pointer. Function pointers are represented by a pointer to usize.
534 // TODO: Add dependency
535 return try self.spv.constNull(ty_ref);
536 },
537 .extern_func => unreachable, // TODO
538 else => {
539 const decl_id = self.spv.declPtr(spv_decl_index).result_id;
540 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
541
542 const final_storage_class = spvStorageClass(decl.@"addrspace");
543
544 const decl_ty_ref = try self.resolveType(decl.ty, .indirect);
545 const decl_ptr_ty_ref = try self.spv.ptrType(decl_ty_ref, final_storage_class);
546
547 const ptr_id = switch (final_storage_class) {
548 .Generic => blk: {
549 // Pointer should be Generic, but is actually placed in CrossWorkgroup.
550 const result_id = self.spv.allocId();
551 try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{
552 .id_result_type = self.typeId(decl_ptr_ty_ref),
553 .id_result = result_id,
554 .pointer = decl_id,
555 });
556 break :blk result_id;
557 },
558 else => decl_id,
559 };
560
561 if (decl_ptr_ty_ref != ty_ref) {
562 // Differing pointer types, insert a cast.
563 const casted_ptr_id = self.spv.allocId();
564 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
565 .id_result_type = ty_id,
566 .id_result = casted_ptr_id,
567 .operand = ptr_id,
568 });
569 return casted_ptr_id;
570 } else {
571 return ptr_id;
572 }
573 },
574 }
575 }
576
577524 /// This function generates a load for a constant in direct (ie, non-memory) representation.
578525 /// When the constant is simple, it can be generated directly using OpConstant instructions.
579526 /// When the constant is more complicated however, it needs to be constructed using multiple values. This
......@@ -796,7 +743,8 @@ pub const DeclGen = struct {
796743
797744 return try self.constructStruct(result_ty_ref, constituents.items);
798745 },
799 .vector_type, .anon_struct_type => unreachable, // TODO
746 .vector_type => unreachable, // TODO
747 .anon_struct_type => unreachable, // TODO
800748 else => unreachable,
801749 },
802750 .un => |un| {
......@@ -817,9 +765,9 @@ pub const DeclGen = struct {
817765 const result_ty_ref = try self.resolveType(ptr_ty, .direct);
818766 const mod = self.module;
819767 switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) {
820 .decl => |decl| return try self.constructDeclRef(ptr_ty, decl),
768 .decl => |decl| return try self.constantDeclRef(ptr_ty, decl),
769 .mut_decl => |decl_mut| return try self.constantDeclRef(ptr_ty, decl_mut.decl),
821770 .anon_decl => @panic("TODO"),
822 .mut_decl => |decl_mut| return try self.constructDeclRef(ptr_ty, decl_mut.decl),
823771 .int => |int| {
824772 const ptr_id = self.spv.allocId();
825773 // TODO: This can probably be an OpSpecConstantOp Bitcast, but
......@@ -846,6 +794,65 @@ pub const DeclGen = struct {
846794 }
847795 }
848796
797 fn constantDeclRef(self: *DeclGen, ty: Type, decl_index: Decl.Index) !IdRef {
798 const mod = self.module;
799 const ty_ref = try self.resolveType(ty, .direct);
800 const ty_id = self.typeId(ty_ref);
801 const decl = mod.declPtr(decl_index);
802 switch (mod.intern_pool.indexToKey(decl.val.ip_index)) {
803 .func => {
804 // TODO: Properly lower function pointers. For now we are going to hack around it and
805 // just generate an empty pointer. Function pointers are represented by a pointer to usize.
806 // TODO: Add dependency
807 return try self.spv.constNull(ty_ref);
808 },
809 .extern_func => unreachable, // TODO
810 else => {},
811 }
812
813 if (!decl.ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) {
814 // Pointer to nothing - return undefined.
815 return self.spv.constUndef(ty_ref);
816 }
817
818 const spv_decl_index = try self.resolveDecl(decl_index);
819
820 const decl_id = self.spv.declPtr(spv_decl_index).result_id;
821 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
822
823 const final_storage_class = spvStorageClass(decl.@"addrspace");
824
825 const decl_ty_ref = try self.resolveType(decl.ty, .indirect);
826 const decl_ptr_ty_ref = try self.spv.ptrType(decl_ty_ref, final_storage_class);
827
828 const ptr_id = switch (final_storage_class) {
829 .Generic => blk: {
830 // Pointer should be Generic, but is actually placed in CrossWorkgroup.
831 const result_id = self.spv.allocId();
832 try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{
833 .id_result_type = self.typeId(decl_ptr_ty_ref),
834 .id_result = result_id,
835 .pointer = decl_id,
836 });
837 break :blk result_id;
838 },
839 else => decl_id,
840 };
841
842 if (decl_ptr_ty_ref != ty_ref) {
843 // Differing pointer types, insert a cast.
844 const casted_ptr_id = self.spv.allocId();
845 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
846 .id_result_type = ty_id,
847 .id_result = casted_ptr_id,
848 .operand = ptr_id,
849 });
850 return casted_ptr_id;
851 } else {
852 return ptr_id;
853 }
854 }
855
849856 // Turn a Zig type's name into a cache reference.
850857 fn resolveTypeName(self: *DeclGen, ty: Type) !CacheString {
851858 var name = std.ArrayList(u8).init(self.gpa);
......@@ -1126,10 +1133,16 @@ pub const DeclGen = struct {
11261133
11271134 var it = struct_type.iterateRuntimeOrder(ip);
11281135 while (it.next()) |field_index| {
1129 const field_ty = struct_type.field_types.get(ip)[field_index];
1130 const field_name = ip.stringToSlice(struct_type.field_names.get(ip)[field_index]);
1131 try member_types.append(try self.resolveType(field_ty.toType(), .indirect));
1132 try member_names.append(try self.spv.resolveString(field_name));
1136 const field_ty = struct_type.field_types.get(ip)[field_index].toType();
1137 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) {
1138 // This is a zero-bit field - we only needed it for the alignment.
1139 continue;
1140 }
1141
1142 const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse
1143 try ip.getOrPutStringFmt(mod.gpa, "{d}", .{field_index});
1144 try member_types.append(try self.resolveType(field_ty, .indirect));
1145 try member_names.append(try self.spv.resolveString(ip.stringToSlice(field_name)));
11331146 }
11341147
11351148 const ty_ref = try self.spv.resolve(.{ .struct_type = .{
src/link/SpirV.zig+1-1
......@@ -217,7 +217,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
217217fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
218218 // TODO: Integrate with a hypothetical feature system
219219 const caps: []const spec.Capability = switch (target.os.tag) {
220 .opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .Float64, .GenericPointer },
220 .opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .Float64, .Float16, .GenericPointer },
221221 .glsl450 => &.{.Shader},
222222 .vulkan => &.{.Shader},
223223 else => unreachable, // TODO