authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-05-29 23:54:09+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-05-30 19:43:37+02:00
log0552a8b11f973fc9621971e2130c25ad3a4af0ad
tree5d9fd2ed5cdc4a8a00b1047dfb0721145d752a2c
parentfcb422585c1a9e91933ff998417eb8682a4ffbcc
signaturelock-open Commit is signed but in an unrecognized format.

spirv: translate remaining types


5 files changed, 295 insertions(+), 1556 deletions(-)

src/codegen/spirv.zig+122-459
...@@ -22,11 +22,10 @@ const IdResultType = spec.IdResultType;...@@ -22,11 +22,10 @@ const IdResultType = spec.IdResultType;
22const StorageClass = spec.StorageClass;22const StorageClass = spec.StorageClass;
2323
24const SpvModule = @import("spirv/Module.zig");24const SpvModule = @import("spirv/Module.zig");
25const SpvCacheRef = SpvModule.TypeConstantCache.Ref;25const CacheRef = SpvModule.CacheRef;
26const SpvCacheString = SpvModule.TypeConstantCache.String;26const CacheString = SpvModule.CacheString;
2727
28const SpvSection = @import("spirv/Section.zig");28const SpvSection = @import("spirv/Section.zig");
29const SpvType = @import("spirv/type.zig").Type;
30const SpvAssembler = @import("spirv/Assembler.zig");29const SpvAssembler = @import("spirv/Assembler.zig");
3130
32const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef);31const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef);
...@@ -380,74 +379,23 @@ pub const DeclGen = struct {...@@ -380,74 +379,23 @@ pub const DeclGen = struct {
380 };379 };
381 }380 }
382381
383 fn genConstInt(self: *DeclGen, ty_ref: SpvType.Ref, result_id: IdRef, value: anytype) !void {382 /// Emits a bool constant in a particular representation.
384 const ty = self.spv.typeRefType(ty_ref);
385 const ty_id = self.typeId(ty_ref);
386
387 const Lit = spec.LiteralContextDependentNumber;
388 const literal = switch (ty.intSignedness()) {
389 .signed => switch (ty.intFloatBits()) {
390 1...32 => Lit{ .int32 = @intCast(i32, value) },
391 33...64 => Lit{ .int64 = @intCast(i64, value) },
392 else => unreachable, // TODO: composite integer literals
393 },
394 .unsigned => switch (ty.intFloatBits()) {
395 1...32 => Lit{ .uint32 = @intCast(u32, value) },
396 33...64 => Lit{ .uint64 = @intCast(u64, value) },
397 else => unreachable,
398 },
399 };
400
401 try self.spv.emitConstant(ty_id, result_id, literal);
402 }
403
404 fn constInt(self: *DeclGen, ty_ref: SpvType.Ref, value: anytype) !IdRef {
405 const result_id = self.spv.allocId();
406 try self.genConstInt(ty_ref, result_id, value);
407 return result_id;
408 }
409
410 fn constUndef(self: *DeclGen, ty_ref: SpvType.Ref) !IdRef {
411 const result_id = self.spv.allocId();
412 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpUndef, .{
413 .id_result_type = self.typeId(ty_ref),
414 .id_result = result_id,
415 });
416 return result_id;
417 }
418
419 fn constNull(self: *DeclGen, ty_ref: SpvType.Ref) !IdRef {
420 const result_id = self.spv.allocId();
421 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpConstantNull, .{
422 .id_result_type = self.typeId(ty_ref),
423 .id_result = result_id,
424 });
425 return result_id;
426 }
427
428 fn constBool(self: *DeclGen, value: bool, repr: Repr) !IdRef {383 fn constBool(self: *DeclGen, value: bool, repr: Repr) !IdRef {
429 switch (repr) {384 switch (repr) {
430 .indirect => {385 .indirect => {
431 const int_ty_ref = try self.intType(.unsigned, 1);386 const int_ty_ref = try self.intType(.unsigned, 1);
432 return self.constInt(int_ty_ref, @boolToInt(value));387 return self.spv.constInt(int_ty_ref, @boolToInt(value));
433 },388 },
434 .direct => {389 .direct => {
435 const bool_ty_ref = try self.resolveType(Type.bool, .direct);390 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
436 const result_id = self.spv.allocId();391 return self.spv.constBool(bool_ty_ref, value);
437 const operands = .{ .id_result_type = self.typeId(bool_ty_ref), .id_result = result_id };
438 if (value) {
439 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpConstantTrue, operands);
440 } else {
441 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpConstantFalse, operands);
442 }
443 return result_id;
444 },392 },
445 }393 }
446 }394 }
447395
448 /// Construct a struct at runtime.396 /// Construct a struct at runtime.
449 /// result_ty_ref must be a struct type.397 /// result_ty_ref must be a struct type.
450 fn constructStruct(self: *DeclGen, result_ty_ref: SpvType.Ref, constituents: []const IdRef) !IdRef {398 fn constructStruct(self: *DeclGen, result_ty_ref: CacheRef, constituents: []const IdRef) !IdRef {
451 // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which'399 // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which'
452 // operands are not constant.400 // operands are not constant.
453 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349401 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349
...@@ -456,11 +404,13 @@ pub const DeclGen = struct {...@@ -456,11 +404,13 @@ pub const DeclGen = struct {
456 const ptr_composite_id = try self.alloc(result_ty_ref, null);404 const ptr_composite_id = try self.alloc(result_ty_ref, null);
457 // Note: using 32-bit ints here because usize crashes the translator as well405 // Note: using 32-bit ints here because usize crashes the translator as well
458 const index_ty_ref = try self.intType(.unsigned, 32);406 const index_ty_ref = try self.intType(.unsigned, 32);
459 const spv_composite_ty = self.spv.typeRefType(result_ty_ref);407
460 const members = spv_composite_ty.payload(.@"struct").members;408 const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).struct_type;
461 for (constituents, members, 0..) |constitent_id, member, index| {409 const member_types = spv_composite_ty.member_types;
462 const index_id = try self.constInt(index_ty_ref, index);410
463 const ptr_member_ty_ref = try self.spv.ptrType(member.ty, .Generic, 0);411 for (constituents, member_types, 0..) |constitent_id, member_ty_ref, index| {
412 const index_id = try self.spv.constInt(index_ty_ref, index);
413 const ptr_member_ty_ref = try self.spv.ptrType(member_ty_ref, .Generic);
464 const ptr_id = try self.accessChain(ptr_member_ty_ref, ptr_composite_id, &.{index_id});414 const ptr_id = try self.accessChain(ptr_member_ty_ref, ptr_composite_id, &.{index_id});
465 try self.func.body.emit(self.spv.gpa, .OpStore, .{415 try self.func.body.emit(self.spv.gpa, .OpStore, .{
466 .pointer = ptr_id,416 .pointer = ptr_id,
...@@ -481,11 +431,11 @@ pub const DeclGen = struct {...@@ -481,11 +431,11 @@ pub const DeclGen = struct {
481431
482 dg: *DeclGen,432 dg: *DeclGen,
483 /// Cached reference of the u32 type.433 /// Cached reference of the u32 type.
484 u32_ty_ref: SpvType.Ref,434 u32_ty_ref: CacheRef,
485 /// Cached type id of the u32 type.435 /// Cached type id of the u32 type.
486 u32_ty_id: IdRef,436 u32_ty_id: IdRef,
487 /// The members of the resulting structure type437 /// The members of the resulting structure type
488 members: std.ArrayList(SpvType.Payload.Struct.Member),438 members: std.ArrayList(CacheRef),
489 /// The initializers of each of the members.439 /// The initializers of each of the members.
490 initializers: std.ArrayList(IdRef),440 initializers: std.ArrayList(IdRef),
491 /// The current size of the structure. Includes441 /// The current size of the structure. Includes
...@@ -519,7 +469,7 @@ pub const DeclGen = struct {...@@ -519,7 +469,7 @@ pub const DeclGen = struct {
519 const result_id = self.dg.spv.allocId();469 const result_id = self.dg.spv.allocId();
520 // TODO: Integrate with caching mechanism470 // TODO: Integrate with caching mechanism
521 try self.dg.spv.emitConstant(self.u32_ty_id, result_id, .{ .uint32 = word });471 try self.dg.spv.emitConstant(self.u32_ty_id, result_id, .{ .uint32 = word });
522 try self.members.append(.{ .ty = self.u32_ty_ref });472 try self.members.append(self.u32_ty_ref);
523 try self.initializers.append(result_id);473 try self.initializers.append(result_id);
524474
525 self.partial_word.len = 0;475 self.partial_word.len = 0;
...@@ -555,7 +505,7 @@ pub const DeclGen = struct {...@@ -555,7 +505,7 @@ pub const DeclGen = struct {
555 }505 }
556 }506 }
557507
558 fn addPtr(self: *@This(), ptr_ty_ref: SpvType.Ref, ptr_id: IdRef) !void {508 fn addPtr(self: *@This(), ptr_ty_ref: CacheRef, ptr_id: IdRef) !void {
559 // TODO: Double check pointer sizes here.509 // TODO: Double check pointer sizes here.
560 // shared pointers might be u32...510 // shared pointers might be u32...
561 const target = self.dg.getTarget();511 const target = self.dg.getTarget();
...@@ -563,12 +513,12 @@ pub const DeclGen = struct {...@@ -563,12 +513,12 @@ pub const DeclGen = struct {
563 if (self.size % width != 0) {513 if (self.size % width != 0) {
564 return self.dg.todo("misaligned pointer constants", .{});514 return self.dg.todo("misaligned pointer constants", .{});
565 }515 }
566 try self.members.append(.{ .ty = ptr_ty_ref });516 try self.members.append(ptr_ty_ref);
567 try self.initializers.append(ptr_id);517 try self.initializers.append(ptr_id);
568 self.size += width;518 self.size += width;
569 }519 }
570520
571 fn addNullPtr(self: *@This(), ptr_ty_ref: SpvType.Ref) !void {521 fn addNullPtr(self: *@This(), ptr_ty_ref: CacheRef) !void {
572 const result_id = self.dg.spv.allocId();522 const result_id = self.dg.spv.allocId();
573 try self.dg.spv.sections.types_globals_constants.emit(self.dg.spv.gpa, .OpConstantNull, .{523 try self.dg.spv.sections.types_globals_constants.emit(self.dg.spv.gpa, .OpConstantNull, .{
574 .id_result_type = self.dg.typeId(ptr_ty_ref),524 .id_result_type = self.dg.typeId(ptr_ty_ref),
...@@ -931,7 +881,7 @@ pub const DeclGen = struct {...@@ -931,7 +881,7 @@ pub const DeclGen = struct {
931 const section = &self.spv.globals.section;881 const section = &self.spv.globals.section;
932882
933 const ty_ref = try self.resolveType(ty, .indirect);883 const ty_ref = try self.resolveType(ty, .indirect);
934 const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, 0);884 const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class);
935885
936 // const target = self.getTarget();886 // const target = self.getTarget();
937887
...@@ -960,7 +910,7 @@ pub const DeclGen = struct {...@@ -960,7 +910,7 @@ pub const DeclGen = struct {
960 .dg = self,910 .dg = self,
961 .u32_ty_ref = u32_ty_ref,911 .u32_ty_ref = u32_ty_ref,
962 .u32_ty_id = self.typeId(u32_ty_ref),912 .u32_ty_id = self.typeId(u32_ty_ref),
963 .members = std.ArrayList(SpvType.Payload.Struct.Member).init(self.gpa),913 .members = std.ArrayList(CacheRef).init(self.gpa),
964 .initializers = std.ArrayList(IdRef).init(self.gpa),914 .initializers = std.ArrayList(IdRef).init(self.gpa),
965 .decl_deps = std.AutoArrayHashMap(SpvModule.Decl.Index, void).init(self.gpa),915 .decl_deps = std.AutoArrayHashMap(SpvModule.Decl.Index, void).init(self.gpa),
966 };916 };
...@@ -972,8 +922,10 @@ pub const DeclGen = struct {...@@ -972,8 +922,10 @@ pub const DeclGen = struct {
972 try icl.lower(ty, val);922 try icl.lower(ty, val);
973 try icl.flush();923 try icl.flush();
974924
975 const constant_struct_ty_ref = try self.spv.simpleStructType(icl.members.items);925 const constant_struct_ty_ref = try self.spv.resolve(.{ .struct_type = .{
976 const ptr_constant_struct_ty_ref = try self.spv.ptrType(constant_struct_ty_ref, storage_class, 0);926 .member_types = icl.members.items,
927 } });
928 const ptr_constant_struct_ty_ref = try self.spv.ptrType(constant_struct_ty_ref, storage_class);
977929
978 const constant_struct_id = self.spv.allocId();930 const constant_struct_id = self.spv.allocId();
979 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{931 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
...@@ -1007,7 +959,7 @@ pub const DeclGen = struct {...@@ -1007,7 +959,7 @@ pub const DeclGen = struct {
1007 });959 });
1008960
1009 if (cast_to_generic) {961 if (cast_to_generic) {
1010 const generic_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic, 0);962 const generic_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic);
1011 try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{963 try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{
1012 .id_result_type = self.typeId(generic_ptr_ty_ref),964 .id_result_type = self.typeId(generic_ptr_ty_ref),
1013 .id_result = result_id,965 .id_result = result_id,
...@@ -1044,9 +996,9 @@ pub const DeclGen = struct {...@@ -1044,9 +996,9 @@ pub const DeclGen = struct {
1044 switch (ty.zigTypeTag()) {996 switch (ty.zigTypeTag()) {
1045 .Int => {997 .Int => {
1046 if (ty.isSignedInt()) {998 if (ty.isSignedInt()) {
1047 return try self.constInt(result_ty_ref, val.toSignedInt(target));999 return try self.spv.constInt(result_ty_ref, val.toSignedInt(target));
1048 } else {1000 } else {
1049 return try self.constInt(result_ty_ref, val.toUnsignedInt(target));1001 return try self.spv.constInt(result_ty_ref, val.toUnsignedInt(target));
1050 }1002 }
1051 },1003 },
1052 .Bool => switch (repr) {1004 .Bool => switch (repr) {
...@@ -1060,7 +1012,7 @@ pub const DeclGen = struct {...@@ -1060,7 +1012,7 @@ pub const DeclGen = struct {
1060 }1012 }
1061 return result_id;1013 return result_id;
1062 },1014 },
1063 .indirect => return try self.constInt(result_ty_ref, @boolToInt(val.toBool())),1015 .indirect => return try self.spv.constInt(result_ty_ref, @boolToInt(val.toBool())),
1064 },1016 },
1065 .Float => {1017 .Float => {
1066 const result_id = self.spv.allocId();1018 const result_id = self.spv.allocId();
...@@ -1084,7 +1036,7 @@ pub const DeclGen = struct {...@@ -1084,7 +1036,7 @@ pub const DeclGen = struct {
1084 else => unreachable,1036 else => unreachable,
1085 };1037 };
10861038
1087 return try self.constInt(result_ty_ref, value);1039 return try self.spv.constInt(result_ty_ref, value);
1088 },1040 },
1089 .ErrorUnion => {1041 .ErrorUnion => {
1090 const payload_ty = ty.errorUnionPayload();1042 const payload_ty = ty.errorUnionPayload();
...@@ -1143,45 +1095,31 @@ pub const DeclGen = struct {...@@ -1143,45 +1095,31 @@ pub const DeclGen = struct {
1143 /// Turn a Zig type into a SPIR-V Type, and return its type result-id.1095 /// Turn a Zig type into a SPIR-V Type, and return its type result-id.
1144 fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType {1096 fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType {
1145 const type_ref = try self.resolveType(ty, .direct);1097 const type_ref = try self.resolveType(ty, .direct);
1146 return self.typeId(type_ref);1098 return self.spv.resultId(type_ref);
1147 }1099 }
11481100
1149 fn typeId(self: *DeclGen, ty_ref: SpvType.Ref) IdRef {1101 fn typeId(self: *DeclGen, ty_ref: CacheRef) IdRef {
1150 return self.spv.typeId(ty_ref);1102 return self.spv.resultId(ty_ref);
1151 }1103 }
11521104
1153 /// Create an integer type suitable for storing at least 'bits' bits.1105 /// Create an integer type suitable for storing at least 'bits' bits.
1154 fn intType(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !SpvType.Ref {1106 /// The integer type that is returned by this function is the type that is used to perform
1107 /// actual operations (as well as store) a Zig type of a particular number of bits. To create
1108 /// a type with an exact size, use SpvModule.intType.
1109 fn intType(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !CacheRef {
1155 const backing_bits = self.backingIntBits(bits) orelse {1110 const backing_bits = self.backingIntBits(bits) orelse {
1156 // TODO: Integers too big for any native type are represented as "composite integers":1111 // TODO: Integers too big for any native type are represented as "composite integers":
1157 // An array of largestSupportedIntBits.1112 // An array of largestSupportedIntBits.
1158 return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits });1113 return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits });
1159 };1114 };
11601115 return self.spv.intType(signedness, backing_bits);
1161 return try self.spv.resolveType(try SpvType.int(self.spv.arena, signedness, backing_bits));
1162 }
1163
1164 fn intType2(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !SpvCacheRef {
1165 const backing_bits = self.backingIntBits(bits) orelse {
1166 // TODO: Integers too big for any native type are represented as "composite integers":
1167 // An array of largestSupportedIntBits.
1168 return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits });
1169 };
1170 return try self.spv.resolve(.{ .int_type = .{
1171 .signedness = signedness,
1172 .bits = backing_bits,
1173 } });
1174 }1116 }
11751117
1176 /// Create an integer type that represents 'usize'.1118 /// Create an integer type that represents 'usize'.
1177 fn sizeType(self: *DeclGen) !SpvType.Ref {1119 fn sizeType(self: *DeclGen) !CacheRef {
1178 return try self.intType(.unsigned, self.getTarget().ptrBitWidth());1120 return try self.intType(.unsigned, self.getTarget().ptrBitWidth());
1179 }1121 }
11801122
1181 fn sizeType2(self: *DeclGen) !SpvCacheRef {
1182 return try self.intType2(.unsigned, self.getTarget().ptrBitWidth());
1183 }
1184
1185 /// Generate a union type, optionally with a known field. If the tag alignment is greater1123 /// Generate a union type, optionally with a known field. If the tag alignment is greater
1186 /// than that of the payload, a regular union (non-packed, with both tag and payload), will1124 /// than that of the payload, a regular union (non-packed, with both tag and payload), will
1187 /// be generated as follows:1125 /// be generated as follows:
...@@ -1204,7 +1142,7 @@ pub const DeclGen = struct {...@@ -1204,7 +1142,7 @@ pub const DeclGen = struct {
1204 /// If any of the fields' size is 0, it will be omitted.1142 /// If any of the fields' size is 0, it will be omitted.
1205 /// NOTE: When the active field is set to something other than the most aligned field, the1143 /// NOTE: When the active field is set to something other than the most aligned field, the
1206 /// resulting struct will be *underaligned*.1144 /// resulting struct will be *underaligned*.
1207 fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !SpvType.Ref {1145 fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef {
1208 const target = self.getTarget();1146 const target = self.getTarget();
1209 const layout = ty.unionGetLayout(target);1147 const layout = ty.unionGetLayout(target);
1210 const union_ty = ty.cast(Type.Payload.Union).?.data;1148 const union_ty = ty.cast(Type.Payload.Union).?.data;
...@@ -1218,7 +1156,8 @@ pub const DeclGen = struct {...@@ -1218,7 +1156,8 @@ pub const DeclGen = struct {
1218 return try self.resolveType(union_ty.tag_ty, .indirect);1156 return try self.resolveType(union_ty.tag_ty, .indirect);
1219 }1157 }
12201158
1221 var members = std.BoundedArray(SpvType.Payload.Struct.Member, 4){};1159 var member_types = std.BoundedArray(CacheRef, 4){};
1160 var member_names = std.BoundedArray(CacheString, 4){};
12221161
1223 const has_tag = layout.tag_size != 0;1162 const has_tag = layout.tag_size != 0;
1224 const tag_first = layout.tag_align >= layout.payload_align;1163 const tag_first = layout.tag_align >= layout.payload_align;
...@@ -1226,82 +1165,6 @@ pub const DeclGen = struct {...@@ -1226,82 +1165,6 @@ pub const DeclGen = struct {
12261165
1227 if (has_tag and tag_first) {1166 if (has_tag and tag_first) {
1228 const tag_ty_ref = try self.resolveType(union_ty.tag_ty, .indirect);1167 const tag_ty_ref = try self.resolveType(union_ty.tag_ty, .indirect);
1229 members.appendAssumeCapacity(.{ .name = "tag", .ty = tag_ty_ref });
1230 }
1231
1232 const active_field = maybe_active_field orelse layout.most_aligned_field;
1233 const active_field_ty = union_ty.fields.values()[active_field].ty;
1234
1235 const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime()) blk: {
1236 const active_payload_ty_ref = try self.resolveType(active_field_ty, .indirect);
1237 members.appendAssumeCapacity(.{ .name = "payload", .ty = active_payload_ty_ref });
1238 break :blk active_field_ty.abiSize(target);
1239 } else 0;
1240
1241 const payload_padding_len = layout.payload_size - active_field_size;
1242 if (payload_padding_len != 0) {
1243 const payload_padding_ty_ref = try self.spv.arrayType(@intCast(u32, payload_padding_len), u8_ty_ref);
1244 members.appendAssumeCapacity(.{ .name = "padding_payload", .ty = payload_padding_ty_ref });
1245 }
1246
1247 if (has_tag and !tag_first) {
1248 const tag_ty_ref = try self.resolveType(union_ty.tag_ty, .indirect);
1249 members.appendAssumeCapacity(.{ .name = "tag", .ty = tag_ty_ref });
1250 }
1251
1252 if (layout.padding != 0) {
1253 const padding_ty_ref = try self.spv.arrayType(layout.padding, u8_ty_ref);
1254 members.appendAssumeCapacity(.{ .name = "padding", .ty = padding_ty_ref });
1255 }
1256
1257 return try self.spv.simpleStructType(members.slice());
1258 }
1259
1260 /// Generate a union type, optionally with a known field. If the tag alignment is greater
1261 /// than that of the payload, a regular union (non-packed, with both tag and payload), will
1262 /// be generated as follows:
1263 /// If the active field is known:
1264 /// struct {
1265 /// tag: TagType,
1266 /// payload: ActivePayloadType,
1267 /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8,
1268 /// padding: [padding_size]u8,
1269 /// }
1270 /// If the payload alignment is greater than that of the tag:
1271 /// struct {
1272 /// payload: ActivePayloadType,
1273 /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8,
1274 /// tag: TagType,
1275 /// padding: [padding_size]u8,
1276 /// }
1277 /// If the active payload is unknown, it will default back to the most aligned field. This is
1278 /// to make sure that the overal struct has the correct alignment in spir-v.
1279 /// If any of the fields' size is 0, it will be omitted.
1280 /// NOTE: When the active field is set to something other than the most aligned field, the
1281 /// resulting struct will be *underaligned*.
1282 fn resolveUnionType2(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !SpvCacheRef {
1283 const target = self.getTarget();
1284 const layout = ty.unionGetLayout(target);
1285 const union_ty = ty.cast(Type.Payload.Union).?.data;
1286
1287 if (union_ty.layout == .Packed) {
1288 return self.todo("packed union types", .{});
1289 }
1290
1291 if (layout.payload_size == 0) {
1292 // No payload, so represent this as just the tag type.
1293 return try self.resolveType2(union_ty.tag_ty, .indirect);
1294 }
1295
1296 var member_types = std.BoundedArray(SpvCacheRef, 4){};
1297 var member_names = std.BoundedArray(SpvCacheString, 4){};
1298
1299 const has_tag = layout.tag_size != 0;
1300 const tag_first = layout.tag_align >= layout.payload_align;
1301 const u8_ty_ref = try self.intType2(.unsigned, 8); // TODO: What if Int8Type is not enabled?
1302
1303 if (has_tag and tag_first) {
1304 const tag_ty_ref = try self.resolveType2(union_ty.tag_ty, .indirect);
1305 member_types.appendAssumeCapacity(tag_ty_ref);1168 member_types.appendAssumeCapacity(tag_ty_ref);
1306 member_names.appendAssumeCapacity(try self.spv.resolveString("tag"));1169 member_names.appendAssumeCapacity(try self.spv.resolveString("tag"));
1307 }1170 }
...@@ -1310,7 +1173,7 @@ pub const DeclGen = struct {...@@ -1310,7 +1173,7 @@ pub const DeclGen = struct {
1310 const active_field_ty = union_ty.fields.values()[active_field].ty;1173 const active_field_ty = union_ty.fields.values()[active_field].ty;
13111174
1312 const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime()) blk: {1175 const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime()) blk: {
1313 const active_payload_ty_ref = try self.resolveType2(active_field_ty, .indirect);1176 const active_payload_ty_ref = try self.resolveType(active_field_ty, .indirect);
1314 member_types.appendAssumeCapacity(active_payload_ty_ref);1177 member_types.appendAssumeCapacity(active_payload_ty_ref);
1315 member_names.appendAssumeCapacity(try self.spv.resolveString("payload"));1178 member_names.appendAssumeCapacity(try self.spv.resolveString("payload"));
1316 break :blk active_field_ty.abiSize(target);1179 break :blk active_field_ty.abiSize(target);
...@@ -1318,19 +1181,19 @@ pub const DeclGen = struct {...@@ -1318,19 +1181,19 @@ pub const DeclGen = struct {
13181181
1319 const payload_padding_len = layout.payload_size - active_field_size;1182 const payload_padding_len = layout.payload_size - active_field_size;
1320 if (payload_padding_len != 0) {1183 if (payload_padding_len != 0) {
1321 const payload_padding_ty_ref = try self.spv.arrayType2(@intCast(u32, payload_padding_len), u8_ty_ref);1184 const payload_padding_ty_ref = try self.spv.arrayType(@intCast(u32, payload_padding_len), u8_ty_ref);
1322 member_types.appendAssumeCapacity(payload_padding_ty_ref);1185 member_types.appendAssumeCapacity(payload_padding_ty_ref);
1323 member_names.appendAssumeCapacity(try self.spv.resolveString("payload_padding"));1186 member_names.appendAssumeCapacity(try self.spv.resolveString("payload_padding"));
1324 }1187 }
13251188
1326 if (has_tag and !tag_first) {1189 if (has_tag and !tag_first) {
1327 const tag_ty_ref = try self.resolveType2(union_ty.tag_ty, .indirect);1190 const tag_ty_ref = try self.resolveType(union_ty.tag_ty, .indirect);
1328 member_types.appendAssumeCapacity(tag_ty_ref);1191 member_types.appendAssumeCapacity(tag_ty_ref);
1329 member_names.appendAssumeCapacity(try self.spv.resolveString("tag"));1192 member_names.appendAssumeCapacity(try self.spv.resolveString("tag"));
1330 }1193 }
13311194
1332 if (layout.padding != 0) {1195 if (layout.padding != 0) {
1333 const padding_ty_ref = try self.spv.arrayType2(layout.padding, u8_ty_ref);1196 const padding_ty_ref = try self.spv.arrayType(layout.padding, u8_ty_ref);
1334 member_types.appendAssumeCapacity(padding_ty_ref);1197 member_types.appendAssumeCapacity(padding_ty_ref);
1335 member_names.appendAssumeCapacity(try self.spv.resolveString("padding"));1198 member_names.appendAssumeCapacity(try self.spv.resolveString("padding"));
1336 }1199 }
...@@ -1341,22 +1204,24 @@ pub const DeclGen = struct {...@@ -1341,22 +1204,24 @@ pub const DeclGen = struct {
1341 } });1204 } });
1342 }1205 }
13431206
1344 fn resolveType2(self: *DeclGen, ty: Type, repr: Repr) Error!SpvCacheRef {1207 /// Turn a Zig type into a SPIR-V Type, and return a reference to it.
1208 fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!CacheRef {
1209 log.debug("resolveType: ty = {}", .{ty.fmt(self.module)});
1345 const target = self.getTarget();1210 const target = self.getTarget();
1346 switch (ty.zigTypeTag()) {1211 switch (ty.zigTypeTag()) {
1347 .Void, .NoReturn => return try self.spv.resolve(.void_type),1212 .Void, .NoReturn => return try self.spv.resolve(.void_type),
1348 .Bool => switch (repr) {1213 .Bool => switch (repr) {
1349 .direct => return try self.spv.resolve(.bool_type),1214 .direct => return try self.spv.resolve(.bool_type),
1350 .indirect => return try self.intType2(.unsigned, 1),1215 .indirect => return try self.intType(.unsigned, 1),
1351 },1216 },
1352 .Int => {1217 .Int => {
1353 const int_info = ty.intInfo(target);1218 const int_info = ty.intInfo(target);
1354 return try self.intType2(int_info.signedness, int_info.bits);1219 return try self.intType(int_info.signedness, int_info.bits);
1355 },1220 },
1356 .Enum => {1221 .Enum => {
1357 var buffer: Type.Payload.Bits = undefined;1222 var buffer: Type.Payload.Bits = undefined;
1358 const tag_ty = ty.intTagType(&buffer);1223 const tag_ty = ty.intTagType(&buffer);
1359 return self.resolveType2(tag_ty, repr);1224 return self.resolveType(tag_ty, repr);
1360 },1225 },
1361 .Float => {1226 .Float => {
1362 // We can (and want) not really emulate floating points with other floating point types like with the integer types,1227 // We can (and want) not really emulate floating points with other floating point types like with the integer types,
...@@ -1378,11 +1243,11 @@ pub const DeclGen = struct {...@@ -1378,11 +1243,11 @@ pub const DeclGen = struct {
1378 },1243 },
1379 .Array => {1244 .Array => {
1380 const elem_ty = ty.childType();1245 const elem_ty = ty.childType();
1381 const elem_ty_ref = try self.resolveType2(elem_ty, .direct);1246 const elem_ty_ref = try self.resolveType(elem_ty, .direct);
1382 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse {1247 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse {
1383 return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()});1248 return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()});
1384 };1249 };
1385 return self.spv.arrayType2(total_len, elem_ty_ref);1250 return self.spv.arrayType(total_len, elem_ty_ref);
1386 },1251 },
1387 .Fn => switch (repr) {1252 .Fn => switch (repr) {
1388 .direct => {1253 .direct => {
...@@ -1390,12 +1255,12 @@ pub const DeclGen = struct {...@@ -1390,12 +1255,12 @@ pub const DeclGen = struct {
1390 if (ty.fnIsVarArgs())1255 if (ty.fnIsVarArgs())
1391 return self.fail("VarArgs functions are unsupported for SPIR-V", .{});1256 return self.fail("VarArgs functions are unsupported for SPIR-V", .{});
13921257
1393 const param_ty_refs = try self.gpa.alloc(SpvCacheRef, ty.fnParamLen());1258 const param_ty_refs = try self.gpa.alloc(CacheRef, ty.fnParamLen());
1394 defer self.gpa.free(param_ty_refs);1259 defer self.gpa.free(param_ty_refs);
1395 for (param_ty_refs, 0..) |*param_type, i| {1260 for (param_ty_refs, 0..) |*param_type, i| {
1396 param_type.* = try self.resolveType2(ty.fnParamType(i), .direct);1261 param_type.* = try self.resolveType(ty.fnParamType(i), .direct);
1397 }1262 }
1398 const return_ty_ref = try self.resolveType2(ty.fnReturnType(), .direct);1263 const return_ty_ref = try self.resolveType(ty.fnReturnType(), .direct);
13991264
1400 return try self.spv.resolve(.{ .function_type = .{1265 return try self.spv.resolve(.{ .function_type = .{
1401 .return_type = return_ty_ref,1266 .return_type = return_ty_ref,
...@@ -1405,14 +1270,14 @@ pub const DeclGen = struct {...@@ -1405,14 +1270,14 @@ pub const DeclGen = struct {
1405 .indirect => {1270 .indirect => {
1406 // TODO: Represent function pointers properly.1271 // TODO: Represent function pointers properly.
1407 // For now, just use an usize type.1272 // For now, just use an usize type.
1408 return try self.sizeType2();1273 return try self.sizeType();
1409 },1274 },
1410 },1275 },
1411 .Pointer => {1276 .Pointer => {
1412 const ptr_info = ty.ptrInfo().data;1277 const ptr_info = ty.ptrInfo().data;
14131278
1414 const storage_class = spvStorageClass(ptr_info.@"addrspace");1279 const storage_class = spvStorageClass(ptr_info.@"addrspace");
1415 const child_ty_ref = try self.resolveType2(ptr_info.pointee_type, .indirect);1280 const child_ty_ref = try self.resolveType(ptr_info.pointee_type, .indirect);
1416 const ptr_ty_ref = try self.spv.resolve(.{ .ptr_type = .{1281 const ptr_ty_ref = try self.spv.resolve(.{ .ptr_type = .{
1417 .storage_class = storage_class,1282 .storage_class = storage_class,
1418 .child_type = child_ty_ref,1283 .child_type = child_ty_ref,
...@@ -1420,7 +1285,15 @@ pub const DeclGen = struct {...@@ -1420,7 +1285,15 @@ pub const DeclGen = struct {
1420 if (ptr_info.size != .Slice) {1285 if (ptr_info.size != .Slice) {
1421 return ptr_ty_ref;1286 return ptr_ty_ref;
1422 }1287 }
1423 unreachable; // TODO1288
1289 const size_ty_ref = try self.sizeType();
1290 return self.spv.resolve(.{ .struct_type = .{
1291 .member_types = &.{ ptr_ty_ref, size_ty_ref },
1292 .member_names = &.{
1293 try self.spv.resolveString("ptr"),
1294 try self.spv.resolveString("len"),
1295 },
1296 } });
1424 },1297 },
1425 .Vector => {1298 .Vector => {
1426 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations1299 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations
...@@ -1433,33 +1306,47 @@ pub const DeclGen = struct {...@@ -1433,33 +1306,47 @@ pub const DeclGen = struct {
1433 // TODO: Properly verify sizes and child type.1306 // TODO: Properly verify sizes and child type.
14341307
1435 return try self.spv.resolve(.{ .vector_type = .{1308 return try self.spv.resolve(.{ .vector_type = .{
1436 .component_type = try self.resolveType2(ty.elemType(), repr),1309 .component_type = try self.resolveType(ty.elemType(), repr),
1437 .component_count = @intCast(u32, ty.vectorLen()),1310 .component_count = @intCast(u32, ty.vectorLen()),
1438 } });1311 } });
1439 },1312 },
1440 .Struct => {1313 .Struct => {
1441 if (ty.isSimpleTupleOrAnonStruct()) {1314 if (ty.isSimpleTupleOrAnonStruct()) {
1442 unreachable; // TODO1315 const tuple = ty.tupleFields();
1316 const member_types = try self.gpa.alloc(CacheRef, tuple.types.len);
1317 defer self.gpa.free(member_types);
1318
1319 var member_index: usize = 0;
1320 for (tuple.types, 0..) |field_ty, i| {
1321 const field_val = tuple.values[i];
1322 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue;
1323
1324 member_types[member_index] = try self.resolveType(field_ty, .indirect);
1325 member_index += 1;
1326 }
1327
1328 return try self.spv.resolve(.{ .struct_type = .{
1329 .member_types = member_types[0..member_index],
1330 } });
1443 }1331 }
14441332
1445 const struct_ty = ty.castTag(.@"struct").?.data;1333 const struct_ty = ty.castTag(.@"struct").?.data;
14461334
1447 if (struct_ty.layout == .Packed) {1335 if (struct_ty.layout == .Packed) {
1448 return try self.resolveType2(struct_ty.backing_int_ty, .direct);1336 return try self.resolveType(struct_ty.backing_int_ty, .direct);
1449 }1337 }
14501338
1451 const member_types = try self.gpa.alloc(SpvCacheRef, struct_ty.fields.count());1339 const member_types = try self.gpa.alloc(CacheRef, struct_ty.fields.count());
1452 defer self.gpa.free(member_types);1340 defer self.gpa.free(member_types);
14531341
1454 const member_names = try self.gpa.alloc(SpvCacheString, struct_ty.fields.count());1342 const member_names = try self.gpa.alloc(CacheString, struct_ty.fields.count());
1455 defer self.gpa.free(member_names);1343 defer self.gpa.free(member_names);
14561344
1457 // const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count());
1458 var member_index: usize = 0;1345 var member_index: usize = 0;
1459 for (struct_ty.fields.values(), 0..) |field, i| {1346 for (struct_ty.fields.values(), 0..) |field, i| {
1460 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;1347 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
14611348
1462 member_types[member_index] = try self.resolveType2(field.ty, .indirect);1349 member_types[member_index] = try self.resolveType(field.ty, .indirect);
1463 member_names[member_index] = try self.spv.resolveString(struct_ty.fields.keys()[i]);1350 member_names[member_index] = try self.spv.resolveString(struct_ty.fields.keys()[i]);
1464 member_index += 1;1351 member_index += 1;
1465 }1352 }
...@@ -1480,16 +1367,16 @@ pub const DeclGen = struct {...@@ -1480,16 +1367,16 @@ pub const DeclGen = struct {
1480 // Just use a bool.1367 // Just use a bool.
1481 // Note: Always generate the bool with indirect format, to save on some sanity1368 // Note: Always generate the bool with indirect format, to save on some sanity
1482 // Perform the conversion to a direct bool when the field is extracted.1369 // Perform the conversion to a direct bool when the field is extracted.
1483 return try self.resolveType2(Type.bool, .indirect);1370 return try self.resolveType(Type.bool, .indirect);
1484 }1371 }
14851372
1486 const payload_ty_ref = try self.resolveType2(payload_ty, .indirect);1373 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
1487 if (ty.optionalReprIsPayload()) {1374 if (ty.optionalReprIsPayload()) {
1488 // Optional is actually a pointer or a slice.1375 // Optional is actually a pointer or a slice.
1489 return payload_ty_ref;1376 return payload_ty_ref;
1490 }1377 }
14911378
1492 const bool_ty_ref = try self.resolveType2(Type.bool, .indirect);1379 const bool_ty_ref = try self.resolveType(Type.bool, .indirect);
14931380
1494 return try self.spv.resolve(.{ .struct_type = .{1381 return try self.spv.resolve(.{ .struct_type = .{
1495 .member_types = &.{ payload_ty_ref, bool_ty_ref },1382 .member_types = &.{ payload_ty_ref, bool_ty_ref },
...@@ -1499,21 +1386,21 @@ pub const DeclGen = struct {...@@ -1499,21 +1386,21 @@ pub const DeclGen = struct {
1499 },1386 },
1500 } });1387 } });
1501 },1388 },
1502 .Union => return try self.resolveUnionType2(ty, null),1389 .Union => return try self.resolveUnionType(ty, null),
1503 .ErrorSet => return try self.intType2(.unsigned, 16),1390 .ErrorSet => return try self.intType(.unsigned, 16),
1504 .ErrorUnion => {1391 .ErrorUnion => {
1505 const payload_ty = ty.errorUnionPayload();1392 const payload_ty = ty.errorUnionPayload();
1506 const error_ty_ref = try self.resolveType2(Type.anyerror, .indirect);1393 const error_ty_ref = try self.resolveType(Type.anyerror, .indirect);
15071394
1508 const eu_layout = self.errorUnionLayout(payload_ty);1395 const eu_layout = self.errorUnionLayout(payload_ty);
1509 if (!eu_layout.payload_has_bits) {1396 if (!eu_layout.payload_has_bits) {
1510 return error_ty_ref;1397 return error_ty_ref;
1511 }1398 }
15121399
1513 const payload_ty_ref = try self.resolveType2(payload_ty, .indirect);1400 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
15141401
1515 var member_types: [2]SpvCacheRef = undefined;1402 var member_types: [2]CacheRef = undefined;
1516 var member_names: [2]SpvCacheString = undefined;1403 var member_names: [2]CacheString = undefined;
1517 if (eu_layout.error_first) {1404 if (eu_layout.error_first) {
1518 // Put the error first1405 // Put the error first
1519 member_types = .{ error_ty_ref, payload_ty_ref };1406 member_types = .{ error_ty_ref, payload_ty_ref };
...@@ -1550,226 +1437,6 @@ pub const DeclGen = struct {...@@ -1550,226 +1437,6 @@ pub const DeclGen = struct {
1550 }1437 }
1551 }1438 }
15521439
1553 /// Turn a Zig type into a SPIR-V Type, and return a reference to it.
1554 fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!SpvType.Ref {
1555 log.debug("resolveType: ty = {}", .{ty.fmt(self.module)});
1556 _ = try self.resolveType2(ty, repr);
1557 const target = self.getTarget();
1558 switch (ty.zigTypeTag()) {
1559 .Void, .NoReturn => return try self.spv.resolveType(SpvType.initTag(.void)),
1560 .Bool => switch (repr) {
1561 .direct => return try self.spv.resolveType(SpvType.initTag(.bool)),
1562 // SPIR-V booleans are opaque, which is fine for operations, but they cant be stored.
1563 // This function returns the *stored* type, for values directly we convert this into a bool when
1564 // it is loaded, and convert it back to this type when stored.
1565 .indirect => return try self.intType(.unsigned, 1),
1566 },
1567 .Int => {
1568 const int_info = ty.intInfo(target);
1569 return try self.intType(int_info.signedness, int_info.bits);
1570 },
1571 .Enum => {
1572 var buffer: Type.Payload.Bits = undefined;
1573 const tag_ty = ty.intTagType(&buffer);
1574 return self.resolveType(tag_ty, repr);
1575 },
1576 .Float => {
1577 // We can (and want) not really emulate floating points with other floating point types like with the integer types,
1578 // so if the float is not supported, just return an error.
1579 const bits = ty.floatBits(target);
1580 const supported = switch (bits) {
1581 16 => Target.spirv.featureSetHas(target.cpu.features, .Float16),
1582 // 32-bit floats are always supported (see spec, 2.16.1, Data rules).
1583 32 => true,
1584 64 => Target.spirv.featureSetHas(target.cpu.features, .Float64),
1585 else => false,
1586 };
1587
1588 if (!supported) {
1589 return self.fail("Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits});
1590 }
1591
1592 return try self.spv.resolveType(SpvType.float(bits));
1593 },
1594 .Array => {
1595 const elem_ty = ty.childType();
1596 const elem_ty_ref = try self.resolveType(elem_ty, .indirect);
1597 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse {
1598 return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()});
1599 };
1600 return try self.spv.arrayType(total_len, elem_ty_ref);
1601 },
1602 .Fn => switch (repr) {
1603 .direct => {
1604 // TODO: Put this somewhere in Sema.zig
1605 if (ty.fnIsVarArgs())
1606 return self.fail("VarArgs functions are unsupported for SPIR-V", .{});
1607
1608 // TODO: Parameter passing convention etc.
1609
1610 const param_types = try self.spv.arena.alloc(SpvType.Ref, ty.fnParamLen());
1611 for (param_types, 0..) |*param, i| {
1612 param.* = try self.resolveType(ty.fnParamType(i), .direct);
1613 }
1614
1615 const return_type = try self.resolveType(ty.fnReturnType(), .direct);
1616
1617 const payload = try self.spv.arena.create(SpvType.Payload.Function);
1618 payload.* = .{ .return_type = return_type, .parameters = param_types };
1619 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
1620 },
1621 .indirect => {
1622 // TODO: Represent function pointers properly.
1623 // For now, just use an usize type.
1624 return try self.sizeType();
1625 },
1626 },
1627 .Pointer => {
1628 const ptr_info = ty.ptrInfo().data;
1629
1630 const storage_class = spvStorageClass(ptr_info.@"addrspace");
1631 const child_ty_ref = try self.resolveType(ptr_info.pointee_type, .indirect);
1632 const ptr_ty_ref = try self.spv.ptrType(child_ty_ref, storage_class, 0);
1633
1634 if (ptr_info.size != .Slice) {
1635 return ptr_ty_ref;
1636 }
1637
1638 return try self.spv.simpleStructType(&.{
1639 .{ .ty = ptr_ty_ref, .name = "ptr" },
1640 .{ .ty = try self.sizeType(), .name = "len" },
1641 });
1642 },
1643 .Vector => {
1644 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations
1645 // which work on them), so simply use those.
1646 // Note: SPIR-V vectors only support bools, ints and floats, so pointer vectors need to be supported another way.
1647 // "composite integers" (larger than the largest supported native type) can probably be represented by an array of vectors.
1648 // TODO: The SPIR-V spec mentions that vector sizes may be quite restricted! look into which we can use, and whether OpTypeVector
1649 // is adequate at all for this.
1650
1651 // TODO: Properly verify sizes and child type.
1652
1653 const payload = try self.spv.arena.create(SpvType.Payload.Vector);
1654 payload.* = .{
1655 .component_type = try self.resolveType(ty.elemType(), repr),
1656 .component_count = @intCast(u32, ty.vectorLen()),
1657 };
1658 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
1659 },
1660 .Struct => {
1661 if (ty.isSimpleTupleOrAnonStruct()) {
1662 const tuple = ty.tupleFields();
1663 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, tuple.types.len);
1664 var member_index: u32 = 0;
1665 for (tuple.types, 0..) |field_ty, i| {
1666 const field_val = tuple.values[i];
1667 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBitsIgnoreComptime()) continue;
1668 members[member_index] = .{
1669 .ty = try self.resolveType(field_ty, .indirect),
1670 };
1671 member_index += 1;
1672 }
1673 const payload = try self.spv.arena.create(SpvType.Payload.Struct);
1674 payload.* = .{
1675 .members = members[0..member_index],
1676 };
1677 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
1678 }
1679
1680 const struct_ty = ty.castTag(.@"struct").?.data;
1681
1682 if (struct_ty.layout == .Packed) {
1683 return try self.resolveType(struct_ty.backing_int_ty, .indirect);
1684 }
1685
1686 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count());
1687 var member_index: usize = 0;
1688 for (struct_ty.fields.values(), 0..) |field, i| {
1689 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
1690
1691 members[member_index] = .{
1692 .ty = try self.resolveType(field.ty, .indirect),
1693 .name = struct_ty.fields.keys()[i],
1694 };
1695 member_index += 1;
1696 }
1697
1698 const name = try struct_ty.getFullyQualifiedName(self.module);
1699 defer self.module.gpa.free(name);
1700
1701 const payload = try self.spv.arena.create(SpvType.Payload.Struct);
1702 payload.* = .{
1703 .members = members[0..member_index],
1704 .name = try self.spv.arena.dupe(u8, name),
1705 };
1706 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
1707 },
1708 .Optional => {
1709 var buf: Type.Payload.ElemType = undefined;
1710 const payload_ty = ty.optionalChild(&buf);
1711 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
1712 // Just use a bool.
1713 // Note: Always generate the bool with indirect format, to save on some sanity
1714 // Perform the converison to a direct bool when the field is extracted.
1715 return try self.resolveType(Type.bool, .indirect);
1716 }
1717
1718 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
1719 if (ty.optionalReprIsPayload()) {
1720 // Optional is actually a pointer or a slice.
1721 return payload_ty_ref;
1722 }
1723
1724 const bool_ty_ref = try self.resolveType(Type.bool, .indirect);
1725
1726 // its an actual optional
1727 return try self.spv.simpleStructType(&.{
1728 .{ .ty = payload_ty_ref, .name = "payload" },
1729 .{ .ty = bool_ty_ref, .name = "valid" },
1730 });
1731 },
1732 .Union => return try self.resolveUnionType(ty, null),
1733 .ErrorSet => return try self.intType(.unsigned, 16),
1734 .ErrorUnion => {
1735 const payload_ty = ty.errorUnionPayload();
1736 const error_ty_ref = try self.resolveType(Type.anyerror, .indirect);
1737
1738 const eu_layout = self.errorUnionLayout(payload_ty);
1739 if (!eu_layout.payload_has_bits) {
1740 return error_ty_ref;
1741 }
1742
1743 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
1744
1745 var members = std.BoundedArray(SpvType.Payload.Struct.Member, 2){};
1746 if (eu_layout.error_first) {
1747 // Put the error first
1748 members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" });
1749 members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" });
1750 // TODO: ABI padding?
1751 } else {
1752 // Put the payload first.
1753 members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" });
1754 members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" });
1755 // TODO: ABI padding?
1756 }
1757
1758 return try self.spv.simpleStructType(members.slice());
1759 },
1760
1761 .Null,
1762 .Undefined,
1763 .EnumLiteral,
1764 .ComptimeFloat,
1765 .ComptimeInt,
1766 .Type,
1767 => unreachable, // Must be comptime.
1768
1769 else => |tag| return self.todo("Implement zig type '{}'", .{tag}),
1770 }
1771 }
1772
1773 fn spvStorageClass(as: std.builtin.AddressSpace) StorageClass {1440 fn spvStorageClass(as: std.builtin.AddressSpace) StorageClass {
1774 return switch (as) {1441 return switch (as) {
1775 .generic => .Generic,1442 .generic => .Generic,
...@@ -1839,17 +1506,13 @@ pub const DeclGen = struct {...@@ -1839,17 +1506,13 @@ pub const DeclGen = struct {
1839 /// the name of an error in the text executor.1506 /// the name of an error in the text executor.
1840 fn generateTestEntryPoint(self: *DeclGen, name: []const u8, spv_test_decl_index: SpvModule.Decl.Index) !void {1507 fn generateTestEntryPoint(self: *DeclGen, name: []const u8, spv_test_decl_index: SpvModule.Decl.Index) !void {
1841 const anyerror_ty_ref = try self.resolveType(Type.anyerror, .direct);1508 const anyerror_ty_ref = try self.resolveType(Type.anyerror, .direct);
1842 const ptr_anyerror_ty_ref = try self.spv.ptrType(anyerror_ty_ref, .CrossWorkgroup, 0);1509 const ptr_anyerror_ty_ref = try self.spv.ptrType(anyerror_ty_ref, .CrossWorkgroup);
1843 const void_ty_ref = try self.resolveType(Type.void, .direct);1510 const void_ty_ref = try self.resolveType(Type.void, .direct);
18441511
1845 const kernel_proto_ty_ref = blk: {1512 const kernel_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{
1846 const proto_payload = try self.spv.arena.create(SpvType.Payload.Function);1513 .return_type = void_ty_ref,
1847 proto_payload.* = .{1514 .parameters = &.{ptr_anyerror_ty_ref},
1848 .return_type = void_ty_ref,1515 } });
1849 .parameters = try self.spv.arena.dupe(SpvType.Ref, &.{ptr_anyerror_ty_ref}),
1850 };
1851 break :blk try self.spv.resolveType(SpvType.initPayload(&proto_payload.base));
1852 };
18531516
1854 const test_id = self.spv.declPtr(spv_test_decl_index).result_id;1517 const test_id = self.spv.declPtr(spv_test_decl_index).result_id;
18551518
...@@ -1983,9 +1646,9 @@ pub const DeclGen = struct {...@@ -1983,9 +1646,9 @@ pub const DeclGen = struct {
1983 }1646 }
1984 }1647 }
19851648
1986 fn boolToInt(self: *DeclGen, result_ty_ref: SpvType.Ref, condition_id: IdRef) !IdRef {1649 fn boolToInt(self: *DeclGen, result_ty_ref: CacheRef, condition_id: IdRef) !IdRef {
1987 const zero_id = try self.constInt(result_ty_ref, 0);1650 const zero_id = try self.spv.constInt(result_ty_ref, 0);
1988 const one_id = try self.constInt(result_ty_ref, 1);1651 const one_id = try self.spv.constInt(result_ty_ref, 1);
1989 const result_id = self.spv.allocId();1652 const result_id = self.spv.allocId();
1990 try self.func.body.emit(self.spv.gpa, .OpSelect, .{1653 try self.func.body.emit(self.spv.gpa, .OpSelect, .{
1991 .id_result_type = self.typeId(result_ty_ref),1654 .id_result_type = self.typeId(result_ty_ref),
...@@ -2004,7 +1667,7 @@ pub const DeclGen = struct {...@@ -2004,7 +1667,7 @@ pub const DeclGen = struct {
2004 .Bool => blk: {1667 .Bool => blk: {
2005 const direct_bool_ty_ref = try self.resolveType(ty, .direct);1668 const direct_bool_ty_ref = try self.resolveType(ty, .direct);
2006 const indirect_bool_ty_ref = try self.resolveType(ty, .indirect);1669 const indirect_bool_ty_ref = try self.resolveType(ty, .indirect);
2007 const zero_id = try self.constInt(indirect_bool_ty_ref, 0);1670 const zero_id = try self.spv.constInt(indirect_bool_ty_ref, 0);
2008 const result_id = self.spv.allocId();1671 const result_id = self.spv.allocId();
2009 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{1672 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{
2010 .id_result_type = self.typeId(direct_bool_ty_ref),1673 .id_result_type = self.typeId(direct_bool_ty_ref),
...@@ -2242,10 +1905,10 @@ pub const DeclGen = struct {...@@ -2242,10 +1905,10 @@ pub const DeclGen = struct {
2242 return result_id;1905 return result_id;
2243 }1906 }
22441907
2245 fn maskStrangeInt(self: *DeclGen, ty_ref: SpvType.Ref, value_id: IdRef, bits: u16) !IdRef {1908 fn maskStrangeInt(self: *DeclGen, ty_ref: CacheRef, value_id: IdRef, bits: u16) !IdRef {
2246 const mask_value = if (bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @intCast(u6, bits)) - 1;1909 const mask_value = if (bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @intCast(u6, bits)) - 1;
2247 const result_id = self.spv.allocId();1910 const result_id = self.spv.allocId();
2248 const mask_id = try self.constInt(ty_ref, mask_value);1911 const mask_id = try self.spv.constInt(ty_ref, mask_value);
2249 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{1912 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{
2250 .id_result_type = self.typeId(ty_ref),1913 .id_result_type = self.typeId(ty_ref),
2251 .id_result = result_id,1914 .id_result = result_id,
...@@ -2384,7 +2047,7 @@ pub const DeclGen = struct {...@@ -2384,7 +2047,7 @@ pub const DeclGen = struct {
2384 // Note that signed overflow is also wrapping in spir-v.2047 // Note that signed overflow is also wrapping in spir-v.
23852048
2386 const rhs_lt_zero_id = self.spv.allocId();2049 const rhs_lt_zero_id = self.spv.allocId();
2387 const zero_id = try self.constInt(operand_ty_ref, 0);2050 const zero_id = try self.spv.constInt(operand_ty_ref, 0);
2388 try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{2051 try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{
2389 .id_result_type = self.typeId(bool_ty_ref),2052 .id_result_type = self.typeId(bool_ty_ref),
2390 .id_result = rhs_lt_zero_id,2053 .id_result = rhs_lt_zero_id,
...@@ -2463,7 +2126,7 @@ pub const DeclGen = struct {...@@ -2463,7 +2126,7 @@ pub const DeclGen = struct {
2463 /// is the latter and PtrAccessChain is the former.2126 /// is the latter and PtrAccessChain is the former.
2464 fn accessChain(2127 fn accessChain(
2465 self: *DeclGen,2128 self: *DeclGen,
2466 result_ty_ref: SpvType.Ref,2129 result_ty_ref: CacheRef,
2467 base: IdRef,2130 base: IdRef,
2468 indexes: []const IdRef,2131 indexes: []const IdRef,
2469 ) !IdRef {2132 ) !IdRef {
...@@ -2479,7 +2142,7 @@ pub const DeclGen = struct {...@@ -2479,7 +2142,7 @@ pub const DeclGen = struct {
24792142
2480 fn ptrAccessChain(2143 fn ptrAccessChain(
2481 self: *DeclGen,2144 self: *DeclGen,
2482 result_ty_ref: SpvType.Ref,2145 result_ty_ref: CacheRef,
2483 base: IdRef,2146 base: IdRef,
2484 element: IdRef,2147 element: IdRef,
2485 indexes: []const IdRef,2148 indexes: []const IdRef,
...@@ -2854,7 +2517,7 @@ pub const DeclGen = struct {...@@ -2854,7 +2517,7 @@ pub const DeclGen = struct {
2854 // Construct new pointer type for the resulting pointer2517 // Construct new pointer type for the resulting pointer
2855 const elem_ty = ptr_ty.elemType2(); // use elemType() so that we get T for *[N]T.2518 const elem_ty = ptr_ty.elemType2(); // use elemType() so that we get T for *[N]T.
2856 const elem_ty_ref = try self.resolveType(elem_ty, .direct);2519 const elem_ty_ref = try self.resolveType(elem_ty, .direct);
2857 const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, spvStorageClass(ptr_ty.ptrAddressSpace()), 0);2520 const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, spvStorageClass(ptr_ty.ptrAddressSpace()));
2858 if (ptr_ty.isSinglePointer()) {2521 if (ptr_ty.isSinglePointer()) {
2859 // Pointer-to-array. In this case, the resulting pointer is not of the same type2522 // Pointer-to-array. In this case, the resulting pointer is not of the same type
2860 // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain.2523 // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain.
...@@ -2970,7 +2633,7 @@ pub const DeclGen = struct {...@@ -2970,7 +2633,7 @@ pub const DeclGen = struct {
2970 fn makePointerConstant(2633 fn makePointerConstant(
2971 self: *DeclGen,2634 self: *DeclGen,
2972 section: *SpvSection,2635 section: *SpvSection,
2973 ptr_ty_ref: SpvType.Ref,2636 ptr_ty_ref: CacheRef,
2974 ptr_id: IdRef,2637 ptr_id: IdRef,
2975 ) !IdRef {2638 ) !IdRef {
2976 const result_id = self.spv.allocId();2639 const result_id = self.spv.allocId();
...@@ -2988,11 +2651,11 @@ pub const DeclGen = struct {...@@ -2988,11 +2651,11 @@ pub const DeclGen = struct {
2988 // placed in the Function address space.2651 // placed in the Function address space.
2989 fn alloc(2652 fn alloc(
2990 self: *DeclGen,2653 self: *DeclGen,
2991 ty_ref: SpvType.Ref,2654 ty_ref: CacheRef,
2992 initializer: ?IdRef,2655 initializer: ?IdRef,
2993 ) !IdRef {2656 ) !IdRef {
2994 const fn_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Function, 0);2657 const fn_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Function);
2995 const general_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic, 0);2658 const general_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic);
29962659
2997 // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to2660 // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to
2998 // directly generate them into func.prologue instead of the body.2661 // directly generate them into func.prologue instead of the body.
...@@ -3146,7 +2809,7 @@ pub const DeclGen = struct {...@@ -3146,7 +2809,7 @@ pub const DeclGen = struct {
31462809
3147 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;2810 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
3148 if (val_is_undef) {2811 if (val_is_undef) {
3149 const undef = try self.constUndef(ptr_ty_ref);2812 const undef = try self.spv.constUndef(ptr_ty_ref);
3150 try self.store(ptr_ty, ptr, undef);2813 try self.store(ptr_ty, ptr, undef);
3151 } else {2814 } else {
3152 try self.store(ptr_ty, ptr, value);2815 try self.store(ptr_ty, ptr, value);
...@@ -3217,7 +2880,7 @@ pub const DeclGen = struct {...@@ -3217,7 +2880,7 @@ pub const DeclGen = struct {
3217 else2880 else
3218 err_union_id;2881 err_union_id;
32192882
3220 const zero_id = try self.constInt(err_ty_ref, 0);2883 const zero_id = try self.spv.constInt(err_ty_ref, 0);
3221 const is_err_id = self.spv.allocId();2884 const is_err_id = self.spv.allocId();
3222 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{2885 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{
3223 .id_result_type = self.typeId(bool_ty_ref),2886 .id_result_type = self.typeId(bool_ty_ref),
...@@ -3266,7 +2929,7 @@ pub const DeclGen = struct {...@@ -3266,7 +2929,7 @@ pub const DeclGen = struct {
32662929
3267 if (err_union_ty.errorUnionSet().errorSetIsEmpty()) {2930 if (err_union_ty.errorUnionSet().errorSetIsEmpty()) {
3268 // No error possible, so just return undefined.2931 // No error possible, so just return undefined.
3269 return try self.constUndef(err_ty_ref);2932 return try self.spv.constUndef(err_ty_ref);
3270 }2933 }
32712934
3272 const payload_ty = err_union_ty.errorUnionPayload();2935 const payload_ty = err_union_ty.errorUnionPayload();
...@@ -3295,7 +2958,7 @@ pub const DeclGen = struct {...@@ -3295,7 +2958,7 @@ pub const DeclGen = struct {
32952958
3296 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);2959 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
3297 var members = std.BoundedArray(IdRef, 2){};2960 var members = std.BoundedArray(IdRef, 2){};
3298 const payload_id = try self.constUndef(payload_ty_ref);2961 const payload_id = try self.spv.constUndef(payload_ty_ref);
3299 if (eu_layout.error_first) {2962 if (eu_layout.error_first) {
3300 members.appendAssumeCapacity(operand_id);2963 members.appendAssumeCapacity(operand_id);
3301 members.appendAssumeCapacity(payload_id);2964 members.appendAssumeCapacity(payload_id);
...@@ -3337,7 +3000,7 @@ pub const DeclGen = struct {...@@ -3337,7 +3000,7 @@ pub const DeclGen = struct {
3337 operand_id;3000 operand_id;
33383001
3339 const payload_ty_ref = try self.resolveType(ptr_ty, .direct);3002 const payload_ty_ref = try self.resolveType(ptr_ty, .direct);
3340 const null_id = try self.constNull(payload_ty_ref);3003 const null_id = try self.spv.constNull(payload_ty_ref);
3341 const result_id = self.spv.allocId();3004 const result_id = self.spv.allocId();
3342 const operands = .{3005 const operands = .{
3343 .id_result_type = self.typeId(bool_ty_ref),3006 .id_result_type = self.typeId(bool_ty_ref),
src/codegen/spirv/Assembler.zig+36-134
...@@ -11,7 +11,8 @@ const IdRef = spec.IdRef;...@@ -11,7 +11,8 @@ const IdRef = spec.IdRef;
11const IdResult = spec.IdResult;11const IdResult = spec.IdResult;
1212
13const SpvModule = @import("Module.zig");13const SpvModule = @import("Module.zig");
14const SpvType = @import("type.zig").Type;14const CacheRef = SpvModule.CacheRef;
15const CacheKey = SpvModule.CacheKey;
1516
16/// Represents a token in the assembly template.17/// Represents a token in the assembly template.
17const Token = struct {18const Token = struct {
...@@ -126,7 +127,7 @@ const AsmValue = union(enum) {...@@ -126,7 +127,7 @@ const AsmValue = union(enum) {
126 value: IdRef,127 value: IdRef,
127128
128 /// This result-value represents a type registered into the module's type system.129 /// This result-value represents a type registered into the module's type system.
129 ty: SpvType.Ref,130 ty: CacheRef,
130131
131 /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue132 /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue
132 /// is of a variant that allows the result to be obtained (not an unresolved133 /// is of a variant that allows the result to be obtained (not an unresolved
...@@ -135,7 +136,7 @@ const AsmValue = union(enum) {...@@ -135,7 +136,7 @@ const AsmValue = union(enum) {
135 return switch (self) {136 return switch (self) {
136 .just_declared, .unresolved_forward_reference => unreachable,137 .just_declared, .unresolved_forward_reference => unreachable,
137 .value => |result| result,138 .value => |result| result,
138 .ty => |ref| spv.typeId(ref),139 .ty => |ref| spv.resultId(ref),
139 };140 };
140 }141 }
141};142};
...@@ -267,9 +268,9 @@ fn processInstruction(self: *Assembler) !void {...@@ -267,9 +268,9 @@ fn processInstruction(self: *Assembler) !void {
267/// refers to the result.268/// refers to the result.
268fn processTypeInstruction(self: *Assembler) !AsmValue {269fn processTypeInstruction(self: *Assembler) !AsmValue {
269 const operands = self.inst.operands.items;270 const operands = self.inst.operands.items;
270 const ty = switch (self.inst.opcode) {271 const ref = switch (self.inst.opcode) {
271 .OpTypeVoid => SpvType.initTag(.void),272 .OpTypeVoid => try self.spv.resolve(.void_type),
272 .OpTypeBool => SpvType.initTag(.bool),273 .OpTypeBool => try self.spv.resolve(.bool_type),
273 .OpTypeInt => blk: {274 .OpTypeInt => blk: {
274 const signedness: std.builtin.Signedness = switch (operands[2].literal32) {275 const signedness: std.builtin.Signedness = switch (operands[2].literal32) {
275 0 => .unsigned,276 0 => .unsigned,
...@@ -282,7 +283,7 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {...@@ -282,7 +283,7 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {
282 const width = std.math.cast(u16, operands[1].literal32) orelse {283 const width = std.math.cast(u16, operands[1].literal32) orelse {
283 return self.fail(0, "int type of {} bits is too large", .{operands[1].literal32});284 return self.fail(0, "int type of {} bits is too large", .{operands[1].literal32});
284 };285 };
285 break :blk try SpvType.int(self.spv.arena, signedness, width);286 break :blk try self.spv.intType(signedness, width);
286 },287 },
287 .OpTypeFloat => blk: {288 .OpTypeFloat => blk: {
288 const bits = operands[1].literal32;289 const bits = operands[1].literal32;
...@@ -292,136 +293,36 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {...@@ -292,136 +293,36 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {
292 return self.fail(0, "{} is not a valid bit count for floats (expected 16, 32 or 64)", .{bits});293 return self.fail(0, "{} is not a valid bit count for floats (expected 16, 32 or 64)", .{bits});
293 },294 },
294 }295 }
295 break :blk SpvType.float(@intCast(u16, bits));296 break :blk try self.spv.resolve(.{ .float_type = .{ .bits = @intCast(u16, bits) } });
296 },
297 .OpTypeVector => blk: {
298 const payload = try self.spv.arena.create(SpvType.Payload.Vector);
299 payload.* = .{
300 .component_type = try self.resolveTypeRef(operands[1].ref_id),
301 .component_count = operands[2].literal32,
302 };
303 break :blk SpvType.initPayload(&payload.base);
304 },
305 .OpTypeMatrix => blk: {
306 const payload = try self.spv.arena.create(SpvType.Payload.Matrix);
307 payload.* = .{
308 .column_type = try self.resolveTypeRef(operands[1].ref_id),
309 .column_count = operands[2].literal32,
310 };
311 break :blk SpvType.initPayload(&payload.base);
312 },
313 .OpTypeImage => blk: {
314 const payload = try self.spv.arena.create(SpvType.Payload.Image);
315 payload.* = .{
316 .sampled_type = try self.resolveTypeRef(operands[1].ref_id),
317 .dim = @intToEnum(spec.Dim, operands[2].value),
318 .depth = switch (operands[3].literal32) {
319 0 => .no,
320 1 => .yes,
321 2 => .maybe,
322 else => {
323 return self.fail(0, "'{}' is not a valid image depth (expected 0, 1 or 2)", .{operands[3].literal32});
324 },
325 },
326 .arrayed = switch (operands[4].literal32) {
327 0 => false,
328 1 => true,
329 else => {
330 return self.fail(0, "'{}' is not a valid image arrayed-ness (expected 0 or 1)", .{operands[4].literal32});
331 },
332 },
333 .multisampled = switch (operands[5].literal32) {
334 0 => false,
335 1 => true,
336 else => {
337 return self.fail(0, "'{}' is not a valid image multisampled-ness (expected 0 or 1)", .{operands[5].literal32});
338 },
339 },
340 .sampled = switch (operands[6].literal32) {
341 0 => .known_at_runtime,
342 1 => .with_sampler,
343 2 => .without_sampler,
344 else => {
345 return self.fail(0, "'{}' is not a valid image sampled-ness (expected 0, 1 or 2)", .{operands[6].literal32});
346 },
347 },
348 .format = @intToEnum(spec.ImageFormat, operands[7].value),
349 .access_qualifier = if (operands.len > 8)
350 @intToEnum(spec.AccessQualifier, operands[8].value)
351 else
352 null,
353 };
354 break :blk SpvType.initPayload(&payload.base);
355 },
356 .OpTypeSampler => SpvType.initTag(.sampler),
357 .OpTypeSampledImage => blk: {
358 const payload = try self.spv.arena.create(SpvType.Payload.SampledImage);
359 payload.* = .{
360 .image_type = try self.resolveTypeRef(operands[1].ref_id),
361 };
362 break :blk SpvType.initPayload(&payload.base);
363 },297 },
298 .OpTypeVector => try self.spv.resolve(.{ .vector_type = .{
299 .component_type = try self.resolveTypeRef(operands[1].ref_id),
300 .component_count = operands[2].literal32,
301 } }),
364 .OpTypeArray => {302 .OpTypeArray => {
365 // TODO: The length of an OpTypeArray is determined by a constant (which may be a spec constant),303 // TODO: The length of an OpTypeArray is determined by a constant (which may be a spec constant),
366 // and so some consideration must be taken when entering this in the type system.304 // and so some consideration must be taken when entering this in the type system.
367 return self.todo("process OpTypeArray", .{});305 return self.todo("process OpTypeArray", .{});
368 },306 },
369 .OpTypeRuntimeArray => blk: {307 .OpTypePointer => try self.spv.ptrType(
370 const payload = try self.spv.arena.create(SpvType.Payload.RuntimeArray);308 try self.resolveTypeRef(operands[2].ref_id),
371 payload.* = .{309 @intToEnum(spec.StorageClass, operands[1].value),
372 .element_type = try self.resolveTypeRef(operands[1].ref_id),310 ),
373 // TODO: Fetch array stride from decorations.
374 .array_stride = 0,
375 };
376 break :blk SpvType.initPayload(&payload.base);
377 },
378 .OpTypeOpaque => blk: {
379 const payload = try self.spv.arena.create(SpvType.Payload.Opaque);
380 const name_offset = operands[1].string;
381 payload.* = .{
382 .name = std.mem.sliceTo(self.inst.string_bytes.items[name_offset..], 0),
383 };
384 break :blk SpvType.initPayload(&payload.base);
385 },
386 .OpTypePointer => blk: {
387 const payload = try self.spv.arena.create(SpvType.Payload.Pointer);
388 payload.* = .{
389 .storage_class = @intToEnum(spec.StorageClass, operands[1].value),
390 .child_type = try self.resolveTypeRef(operands[2].ref_id),
391 // TODO: Fetch decorations
392 };
393 break :blk SpvType.initPayload(&payload.base);
394 },
395 .OpTypeFunction => blk: {311 .OpTypeFunction => blk: {
396 const param_operands = operands[2..];312 const param_operands = operands[2..];
397 const param_types = try self.spv.arena.alloc(SpvType.Ref, param_operands.len);313 const param_types = try self.spv.gpa.alloc(CacheRef, param_operands.len);
314 defer self.spv.gpa.free(param_types);
398 for (param_types, 0..) |*param, i| {315 for (param_types, 0..) |*param, i| {
399 param.* = try self.resolveTypeRef(param_operands[i].ref_id);316 param.* = try self.resolveTypeRef(param_operands[i].ref_id);
400 }317 }
401 const payload = try self.spv.arena.create(SpvType.Payload.Function);318 break :blk try self.spv.resolve(.{ .function_type = .{
402 payload.* = .{
403 .return_type = try self.resolveTypeRef(operands[1].ref_id),319 .return_type = try self.resolveTypeRef(operands[1].ref_id),
404 .parameters = param_types,320 .parameters = param_types,
405 };321 } });
406 break :blk SpvType.initPayload(&payload.base);
407 },322 },
408 .OpTypeEvent => SpvType.initTag(.event),
409 .OpTypeDeviceEvent => SpvType.initTag(.device_event),
410 .OpTypeReserveId => SpvType.initTag(.reserve_id),
411 .OpTypeQueue => SpvType.initTag(.queue),
412 .OpTypePipe => blk: {
413 const payload = try self.spv.arena.create(SpvType.Payload.Pipe);
414 payload.* = .{
415 .qualifier = @intToEnum(spec.AccessQualifier, operands[1].value),
416 };
417 break :blk SpvType.initPayload(&payload.base);
418 },
419 .OpTypePipeStorage => SpvType.initTag(.pipe_storage),
420 .OpTypeNamedBarrier => SpvType.initTag(.named_barrier),
421 else => return self.todo("process type instruction {s}", .{@tagName(self.inst.opcode)}),323 else => return self.todo("process type instruction {s}", .{@tagName(self.inst.opcode)}),
422 };324 };
423325
424 const ref = try self.spv.resolveType(ty);
425 return AsmValue{ .ty = ref };326 return AsmValue{ .ty = ref };
426}327}
427328
...@@ -528,7 +429,7 @@ fn resolveRef(self: *Assembler, ref: AsmValue.Ref) !AsmValue {...@@ -528,7 +429,7 @@ fn resolveRef(self: *Assembler, ref: AsmValue.Ref) !AsmValue {
528}429}
529430
530/// Resolve a value reference as type.431/// Resolve a value reference as type.
531fn resolveTypeRef(self: *Assembler, ref: AsmValue.Ref) !SpvType.Ref {432fn resolveTypeRef(self: *Assembler, ref: AsmValue.Ref) !CacheRef {
532 const value = try self.resolveRef(ref);433 const value = try self.resolveRef(ref);
533 switch (value) {434 switch (value) {
534 .just_declared, .unresolved_forward_reference => unreachable,435 .just_declared, .unresolved_forward_reference => unreachable,
...@@ -761,19 +662,20 @@ fn parseContextDependentNumber(self: *Assembler) !void {...@@ -761,19 +662,20 @@ fn parseContextDependentNumber(self: *Assembler) !void {
761662
762 const tok = self.currentToken();663 const tok = self.currentToken();
763 const result_type_ref = try self.resolveTypeRef(self.inst.operands.items[0].ref_id);664 const result_type_ref = try self.resolveTypeRef(self.inst.operands.items[0].ref_id);
764 const result_type = self.spv.type_cache.keys()[@enumToInt(result_type_ref)];665 const result_type = self.spv.cache.lookup(result_type_ref);
765 if (result_type.isInt()) {666 switch (result_type) {
766 try self.parseContextDependentInt(result_type.intSignedness(), result_type.intFloatBits());667 .int_type => |int| {
767 } else if (result_type.isFloat()) {668 try self.parseContextDependentInt(int.signedness, int.bits);
768 const width = result_type.intFloatBits();669 },
769 switch (width) {670 .float_type => |float| {
770 16 => try self.parseContextDependentFloat(16),671 switch (float.bits) {
771 32 => try self.parseContextDependentFloat(32),672 16 => try self.parseContextDependentFloat(16),
772 64 => try self.parseContextDependentFloat(64),673 32 => try self.parseContextDependentFloat(32),
773 else => return self.fail(tok.start, "cannot parse {}-bit float literal", .{width}),674 64 => try self.parseContextDependentFloat(64),
774 }675 else => return self.fail(tok.start, "cannot parse {}-bit float literal", .{float.bits}),
775 } else {676 }
776 return self.fail(tok.start, "cannot parse literal constant {s}", .{@tagName(result_type.tag())});677 },
678 else => return self.fail(tok.start, "cannot parse literal constant", .{}),
777 }679 }
778}680}
779681
src/codegen/spirv/Module.zig+51-396
...@@ -20,12 +20,13 @@ const IdResult = spec.IdResult;...@@ -20,12 +20,13 @@ const IdResult = spec.IdResult;
20const IdResultType = spec.IdResultType;20const IdResultType = spec.IdResultType;
2121
22const Section = @import("Section.zig");22const Section = @import("Section.zig");
23const Type = @import("type.zig").Type;
24pub const TypeConstantCache = @import("TypeConstantCache.zig");
2523
26const TypeCache = std.ArrayHashMapUnmanaged(Type, IdResultType, Type.ShallowHashContext32, true);24const Cache = @import("TypeConstantCache.zig");
25pub const CacheKey = Cache.Key;
26pub const CacheRef = Cache.Ref;
27pub const CacheString = Cache.String;
2728
28/// This structure represents a function that is in-progress of being emitted.29/// This structure represents a function that isc in-progress of being emitted.
29/// Commonly, the contents of this structure will be merged with the appropriate30/// Commonly, the contents of this structure will be merged with the appropriate
30/// sections of the module and re-used. Note that the SPIR-V module system makes31/// sections of the module and re-used. Note that the SPIR-V module system makes
31/// no attempt of compacting result-id's, so any Fn instance should ultimately32/// no attempt of compacting result-id's, so any Fn instance should ultimately
...@@ -130,7 +131,7 @@ sections: struct {...@@ -130,7 +131,7 @@ sections: struct {
130 /// From this section, OpLine and OpNoLine is allowed.131 /// From this section, OpLine and OpNoLine is allowed.
131 /// According to the SPIR-V documentation, this section normally132 /// According to the SPIR-V documentation, this section normally
132 /// also holds type and constant instructions. These are managed133 /// also holds type and constant instructions. These are managed
133 /// via the tc_cache instead, which is the sole structure that134 /// via the cache instead, which is the sole structure that
134 /// manages that section. These will be inserted between this and135 /// manages that section. These will be inserted between this and
135 /// the previous section when emitting the final binary.136 /// the previous section when emitting the final binary.
136 /// TODO: Do we need this section? Globals are also managed with another mechanism.137 /// TODO: Do we need this section? Globals are also managed with another mechanism.
...@@ -152,10 +153,9 @@ next_result_id: Word,...@@ -152,10 +153,9 @@ next_result_id: Word,
152/// just the ones for OpLine. Note that OpLine needs the result of OpString, and not that of OpSource.153/// just the ones for OpLine. Note that OpLine needs the result of OpString, and not that of OpSource.
153source_file_names: std.StringHashMapUnmanaged(IdRef) = .{},154source_file_names: std.StringHashMapUnmanaged(IdRef) = .{},
154155
155type_cache: TypeCache = .{},
156/// SPIR-V type- and constant cache. This structure is used to store information about these in a more156/// SPIR-V type- and constant cache. This structure is used to store information about these in a more
157/// efficient manner.157/// efficient manner.
158tc_cache: TypeConstantCache = .{},158cache: Cache = .{},
159159
160/// Set of Decls, referred to by Decl.Index.160/// Set of Decls, referred to by Decl.Index.
161decls: std.ArrayListUnmanaged(Decl) = .{},161decls: std.ArrayListUnmanaged(Decl) = .{},
...@@ -196,7 +196,7 @@ pub fn deinit(self: *Module) void {...@@ -196,7 +196,7 @@ pub fn deinit(self: *Module) void {
196 self.sections.functions.deinit(self.gpa);196 self.sections.functions.deinit(self.gpa);
197197
198 self.source_file_names.deinit(self.gpa);198 self.source_file_names.deinit(self.gpa);
199 self.tc_cache.deinit(self);199 self.cache.deinit(self);
200200
201 self.decls.deinit(self.gpa);201 self.decls.deinit(self.gpa);
202 self.decl_deps.deinit(self.gpa);202 self.decl_deps.deinit(self.gpa);
...@@ -223,20 +223,20 @@ pub fn idBound(self: Module) Word {...@@ -223,20 +223,20 @@ pub fn idBound(self: Module) Word {
223 return self.next_result_id;223 return self.next_result_id;
224}224}
225225
226pub fn resolve(self: *Module, key: TypeConstantCache.Key) !TypeConstantCache.Ref {226pub fn resolve(self: *Module, key: CacheKey) !CacheRef {
227 return self.tc_cache.resolve(self, key);227 return self.cache.resolve(self, key);
228}228}
229229
230pub fn resultId(self: *Module, ref: TypeConstantCache.Ref) IdResult {230pub fn resultId(self: *const Module, ref: CacheRef) IdResult {
231 return self.tc_cache.resultId(ref);231 return self.cache.resultId(ref);
232}232}
233233
234pub fn resolveId(self: *Module, key: TypeConstantCache.Key) !IdResult {234pub fn resolveId(self: *Module, key: CacheKey) !IdResult {
235 return self.resultId(try self.resolve(key));235 return self.resultId(try self.resolve(key));
236}236}
237237
238pub fn resolveString(self: *Module, str: []const u8) !TypeConstantCache.String {238pub fn resolveString(self: *Module, str: []const u8) !CacheString {
239 return try self.tc_cache.addString(self, str);239 return try self.cache.addString(self, str);
240}240}
241241
242fn orderGlobalsInto(242fn orderGlobalsInto(
...@@ -350,7 +350,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void {...@@ -350,7 +350,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void {
350 var entry_points = try self.entryPoints();350 var entry_points = try self.entryPoints();
351 defer entry_points.deinit(self.gpa);351 defer entry_points.deinit(self.gpa);
352352
353 var types_constants = try self.tc_cache.materialize(self);353 var types_constants = try self.cache.materialize(self);
354 defer types_constants.deinit(self.gpa);354 defer types_constants.deinit(self.gpa);
355355
356 // Note: needs to be kept in order according to section 2.3!356 // Note: needs to be kept in order according to section 2.3!
...@@ -364,6 +364,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void {...@@ -364,6 +364,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void {
364 self.sections.debug_names.toWords(),364 self.sections.debug_names.toWords(),
365 self.sections.annotations.toWords(),365 self.sections.annotations.toWords(),
366 types_constants.toWords(),366 types_constants.toWords(),
367 self.sections.types_globals_constants.toWords(),
367 self.sections.globals.toWords(),368 self.sections.globals.toWords(),
368 globals.toWords(),369 globals.toWords(),
369 self.sections.functions.toWords(),370 self.sections.functions.toWords(),
...@@ -416,364 +417,14 @@ pub fn resolveSourceFileName(self: *Module, decl: *ZigDecl) !IdRef {...@@ -416,364 +417,14 @@ pub fn resolveSourceFileName(self: *Module, decl: *ZigDecl) !IdRef {
416 return result.value_ptr.*;417 return result.value_ptr.*;
417}418}
418419
419/// Fetch a result-id for a spir-v type. This function deduplicates the type as appropriate,420pub fn intType(self: *Module, signedness: std.builtin.Signedness, bits: u16) !CacheRef {
420/// and returns a cached version if that exists.421 return try self.resolve(.{ .int_type = .{
421/// Note: This function does not attempt to perform any validation on the type.422 .signedness = signedness,
422/// The type is emitted in a shallow fashion; any child types should already423 .bits = bits,
423/// be emitted at this point.424 } });
424pub fn resolveType(self: *Module, ty: Type) !Type.Ref {
425 const result = try self.type_cache.getOrPut(self.gpa, ty);
426 const index = @intToEnum(Type.Ref, result.index);
427
428 if (!result.found_existing) {
429 const ref = try self.emitType(ty);
430 self.type_cache.values()[result.index] = ref;
431 }
432
433 return index;
434}
435
436pub fn resolveTypeId(self: *Module, ty: Type) !IdResultType {
437 const ty_ref = try self.resolveType(ty);
438 return self.typeId(ty_ref);
439}
440
441pub fn typeRefType(self: Module, ty_ref: Type.Ref) Type {
442 return self.type_cache.keys()[@enumToInt(ty_ref)];
443}
444
445/// Get the result-id of a particular type, by reference. Asserts type_ref is valid.
446pub fn typeId(self: Module, ty_ref: Type.Ref) IdResultType {
447 return self.type_cache.values()[@enumToInt(ty_ref)];
448}
449
450/// Unconditionally emit a spir-v type into the appropriate section.
451/// Note: If this function is called with a type that is already generated, it may yield an invalid module
452/// as non-pointer non-aggregrate types must me unique!
453/// Note: This function does not attempt to perform any validation on the type.
454/// The type is emitted in a shallow fashion; any child types should already
455/// be emitted at this point.
456pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
457 const result_id = self.allocId();
458 const ref_id = result_id;
459 const types = &self.sections.types_globals_constants;
460 const debug_names = &self.sections.debug_names;
461 const result_id_operand = .{ .id_result = result_id };
462
463 switch (ty.tag()) {
464 .void => {
465 try types.emit(self.gpa, .OpTypeVoid, result_id_operand);
466 try debug_names.emit(self.gpa, .OpName, .{
467 .target = result_id,
468 .name = "void",
469 });
470 },
471 .bool => {
472 try types.emit(self.gpa, .OpTypeBool, result_id_operand);
473 try debug_names.emit(self.gpa, .OpName, .{
474 .target = result_id,
475 .name = "bool",
476 });
477 },
478 .u8,
479 .u16,
480 .u32,
481 .u64,
482 .i8,
483 .i16,
484 .i32,
485 .i64,
486 .int,
487 => {
488 // TODO: Kernels do not support OpTypeInt that is signed. We can probably
489 // can get rid of the signedness all together, in Shaders also.
490 const bits = ty.intFloatBits();
491 const signedness: spec.LiteralInteger = switch (ty.intSignedness()) {
492 .unsigned => 0,
493 .signed => 1,
494 };
495
496 try types.emit(self.gpa, .OpTypeInt, .{
497 .id_result = result_id,
498 .width = bits,
499 .signedness = signedness,
500 });
501
502 const ui: []const u8 = switch (signedness) {
503 0 => "u",
504 1 => "i",
505 else => unreachable,
506 };
507 const name = try std.fmt.allocPrint(self.gpa, "{s}{}", .{ ui, bits });
508 defer self.gpa.free(name);
509
510 try debug_names.emit(self.gpa, .OpName, .{
511 .target = result_id,
512 .name = name,
513 });
514 },
515 .f16, .f32, .f64 => {
516 const bits = ty.intFloatBits();
517 try types.emit(self.gpa, .OpTypeFloat, .{
518 .id_result = result_id,
519 .width = bits,
520 });
521
522 const name = try std.fmt.allocPrint(self.gpa, "f{}", .{bits});
523 defer self.gpa.free(name);
524 try debug_names.emit(self.gpa, .OpName, .{
525 .target = result_id,
526 .name = name,
527 });
528 },
529 .vector => try types.emit(self.gpa, .OpTypeVector, .{
530 .id_result = result_id,
531 .component_type = self.typeId(ty.childType()),
532 .component_count = ty.payload(.vector).component_count,
533 }),
534 .matrix => try types.emit(self.gpa, .OpTypeMatrix, .{
535 .id_result = result_id,
536 .column_type = self.typeId(ty.childType()),
537 .column_count = ty.payload(.matrix).column_count,
538 }),
539 .image => {
540 const info = ty.payload(.image);
541 try types.emit(self.gpa, .OpTypeImage, .{
542 .id_result = result_id,
543 .sampled_type = self.typeId(ty.childType()),
544 .dim = info.dim,
545 .depth = @enumToInt(info.depth),
546 .arrayed = @boolToInt(info.arrayed),
547 .ms = @boolToInt(info.multisampled),
548 .sampled = @enumToInt(info.sampled),
549 .image_format = info.format,
550 .access_qualifier = info.access_qualifier,
551 });
552 },
553 .sampler => try types.emit(self.gpa, .OpTypeSampler, result_id_operand),
554 .sampled_image => try types.emit(self.gpa, .OpTypeSampledImage, .{
555 .id_result = result_id,
556 .image_type = self.typeId(ty.childType()),
557 }),
558 .array => {
559 const info = ty.payload(.array);
560 assert(info.length != 0);
561
562 const size_type = Type.initTag(.u32);
563 const size_type_id = try self.resolveTypeId(size_type);
564 const length_id = self.allocId();
565 try self.emitConstant(size_type_id, length_id, .{ .uint32 = info.length });
566
567 try types.emit(self.gpa, .OpTypeArray, .{
568 .id_result = result_id,
569 .element_type = self.typeId(ty.childType()),
570 .length = length_id,
571 });
572 if (info.array_stride != 0) {
573 try self.decorate(ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });
574 }
575 },
576 .runtime_array => {
577 const info = ty.payload(.runtime_array);
578 try types.emit(self.gpa, .OpTypeRuntimeArray, .{
579 .id_result = result_id,
580 .element_type = self.typeId(ty.childType()),
581 });
582 if (info.array_stride != 0) {
583 try self.decorate(ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });
584 }
585 },
586 .@"struct" => {
587 const info = ty.payload(.@"struct");
588 try types.emitRaw(self.gpa, .OpTypeStruct, 1 + info.members.len);
589 types.writeOperand(IdResult, result_id);
590 for (info.members) |member| {
591 types.writeOperand(IdRef, self.typeId(member.ty));
592 }
593 try self.decorateStruct(ref_id, info);
594 },
595 .@"opaque" => try types.emit(self.gpa, .OpTypeOpaque, .{
596 .id_result = result_id,
597 .literal_string = ty.payload(.@"opaque").name,
598 }),
599 .pointer => {
600 const info = ty.payload(.pointer);
601 try types.emit(self.gpa, .OpTypePointer, .{
602 .id_result = result_id,
603 .storage_class = info.storage_class,
604 .type = self.typeId(ty.childType()),
605 });
606 if (info.array_stride != 0) {
607 try self.decorate(ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });
608 }
609 if (info.alignment != 0) {
610 try self.decorate(ref_id, .{ .Alignment = .{ .alignment = info.alignment } });
611 }
612 if (info.max_byte_offset) |max_byte_offset| {
613 try self.decorate(ref_id, .{ .MaxByteOffset = .{ .max_byte_offset = max_byte_offset } });
614 }
615 },
616 .function => {
617 const info = ty.payload(.function);
618 try types.emitRaw(self.gpa, .OpTypeFunction, 2 + info.parameters.len);
619 types.writeOperand(IdResult, result_id);
620 types.writeOperand(IdRef, self.typeId(info.return_type));
621 for (info.parameters) |parameter_type| {
622 types.writeOperand(IdRef, self.typeId(parameter_type));
623 }
624 },
625 .event => try types.emit(self.gpa, .OpTypeEvent, result_id_operand),
626 .device_event => try types.emit(self.gpa, .OpTypeDeviceEvent, result_id_operand),
627 .reserve_id => try types.emit(self.gpa, .OpTypeReserveId, result_id_operand),
628 .queue => try types.emit(self.gpa, .OpTypeQueue, result_id_operand),
629 .pipe => try types.emit(self.gpa, .OpTypePipe, .{
630 .id_result = result_id,
631 .qualifier = ty.payload(.pipe).qualifier,
632 }),
633 .pipe_storage => try types.emit(self.gpa, .OpTypePipeStorage, result_id_operand),
634 .named_barrier => try types.emit(self.gpa, .OpTypeNamedBarrier, result_id_operand),
635 }
636
637 return result_id;
638}
639
640fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct) !void {
641 const debug_names = &self.sections.debug_names;
642
643 if (info.name.len != 0) {
644 try debug_names.emit(self.gpa, .OpName, .{
645 .target = target,
646 .name = info.name,
647 });
648 }
649
650 // Decorations for the struct type itself.
651 if (info.decorations.block)
652 try self.decorate(target, .Block);
653 if (info.decorations.buffer_block)
654 try self.decorate(target, .BufferBlock);
655 if (info.decorations.glsl_shared)
656 try self.decorate(target, .GLSLShared);
657 if (info.decorations.glsl_packed)
658 try self.decorate(target, .GLSLPacked);
659 if (info.decorations.c_packed)
660 try self.decorate(target, .CPacked);
661
662 // Decorations for the struct members.
663 const extra = info.member_decoration_extra;
664 var extra_i: u32 = 0;
665 for (info.members, 0..) |member, i| {
666 const d = member.decorations;
667 const index = @intCast(Word, i);
668
669 if (member.name.len != 0) {
670 try debug_names.emit(self.gpa, .OpMemberName, .{
671 .type = target,
672 .member = index,
673 .name = member.name,
674 });
675 }
676
677 switch (member.offset) {
678 .none => {},
679 else => try self.decorateMember(
680 target,
681 index,
682 .{ .Offset = .{ .byte_offset = @enumToInt(member.offset) } },
683 ),
684 }
685
686 switch (d.matrix_layout) {
687 .row_major => try self.decorateMember(target, index, .RowMajor),
688 .col_major => try self.decorateMember(target, index, .ColMajor),
689 .none => {},
690 }
691 if (d.matrix_layout != .none) {
692 try self.decorateMember(target, index, .{
693 .MatrixStride = .{ .matrix_stride = extra[extra_i] },
694 });
695 extra_i += 1;
696 }
697
698 if (d.no_perspective)
699 try self.decorateMember(target, index, .NoPerspective);
700 if (d.flat)
701 try self.decorateMember(target, index, .Flat);
702 if (d.patch)
703 try self.decorateMember(target, index, .Patch);
704 if (d.centroid)
705 try self.decorateMember(target, index, .Centroid);
706 if (d.sample)
707 try self.decorateMember(target, index, .Sample);
708 if (d.invariant)
709 try self.decorateMember(target, index, .Invariant);
710 if (d.@"volatile")
711 try self.decorateMember(target, index, .Volatile);
712 if (d.coherent)
713 try self.decorateMember(target, index, .Coherent);
714 if (d.non_writable)
715 try self.decorateMember(target, index, .NonWritable);
716 if (d.non_readable)
717 try self.decorateMember(target, index, .NonReadable);
718
719 if (d.builtin) {
720 try self.decorateMember(target, index, .{
721 .BuiltIn = .{ .built_in = @intToEnum(spec.BuiltIn, extra[extra_i]) },
722 });
723 extra_i += 1;
724 }
725 if (d.stream) {
726 try self.decorateMember(target, index, .{
727 .Stream = .{ .stream_number = extra[extra_i] },
728 });
729 extra_i += 1;
730 }
731 if (d.location) {
732 try self.decorateMember(target, index, .{
733 .Location = .{ .location = extra[extra_i] },
734 });
735 extra_i += 1;
736 }
737 if (d.component) {
738 try self.decorateMember(target, index, .{
739 .Component = .{ .component = extra[extra_i] },
740 });
741 extra_i += 1;
742 }
743 if (d.xfb_buffer) {
744 try self.decorateMember(target, index, .{
745 .XfbBuffer = .{ .xfb_buffer_number = extra[extra_i] },
746 });
747 extra_i += 1;
748 }
749 if (d.xfb_stride) {
750 try self.decorateMember(target, index, .{
751 .XfbStride = .{ .xfb_stride = extra[extra_i] },
752 });
753 extra_i += 1;
754 }
755 if (d.user_semantic) {
756 const len = extra[extra_i];
757 extra_i += 1;
758 const semantic = @ptrCast([*]const u8, &extra[extra_i])[0..len];
759 try self.decorateMember(target, index, .{
760 .UserSemantic = .{ .semantic = semantic },
761 });
762 extra_i += std.math.divCeil(u32, extra_i, @sizeOf(u32)) catch unreachable;
763 }
764 }
765}
766
767pub fn simpleStructType(self: *Module, members: []const Type.Payload.Struct.Member) !Type.Ref {
768 const payload = try self.arena.create(Type.Payload.Struct);
769 payload.* = .{
770 .members = try self.arena.dupe(Type.Payload.Struct.Member, members),
771 .decorations = .{},
772 };
773 return try self.resolveType(Type.initPayload(&payload.base));
774}425}
775426
776pub fn arrayType2(self: *Module, len: u32, elem_ty_ref: TypeConstantCache.Ref) !TypeConstantCache.Ref {427pub fn arrayType(self: *Module, len: u32, elem_ty_ref: CacheRef) !CacheRef {
777 const len_ty_ref = try self.resolve(.{ .int_type = .{428 const len_ty_ref = try self.resolve(.{ .int_type = .{
778 .signedness = .unsigned,429 .signedness = .unsigned,
779 .bits = 32,430 .bits = 32,
...@@ -788,41 +439,45 @@ pub fn arrayType2(self: *Module, len: u32, elem_ty_ref: TypeConstantCache.Ref) !...@@ -788,41 +439,45 @@ pub fn arrayType2(self: *Module, len: u32, elem_ty_ref: TypeConstantCache.Ref) !
788 } });439 } });
789}440}
790441
791pub fn arrayType(self: *Module, len: u32, ty: Type.Ref) !Type.Ref {
792 const payload = try self.arena.create(Type.Payload.Array);
793 payload.* = .{
794 .element_type = ty,
795 .length = len,
796 };
797 return try self.resolveType(Type.initPayload(&payload.base));
798}
799
800pub fn ptrType(442pub fn ptrType(
801 self: *Module,443 self: *Module,
802 child: Type.Ref,444 child: CacheRef,
803 storage_class: spec.StorageClass,445 storage_class: spec.StorageClass,
804 alignment: u32,446) !CacheRef {
805) !Type.Ref {447 return try self.resolve(.{ .ptr_type = .{
806 const ptr_payload = try self.arena.create(Type.Payload.Pointer);
807 ptr_payload.* = .{
808 .storage_class = storage_class,448 .storage_class = storage_class,
809 .child_type = child,449 .child_type = child,
810 .alignment = alignment,450 } });
811 };451}
812 return try self.resolveType(Type.initPayload(&ptr_payload.base));452
453pub fn constInt(self: *Module, ty_ref: CacheRef, value: anytype) !IdRef {
454 const ty = self.cache.lookup(ty_ref).int_type;
455 const Value = Cache.Key.Int.Value;
456 return try self.resolveId(.{ .int = .{
457 .ty = ty_ref,
458 .value = switch (ty.signedness) {
459 .signed => Value{ .int64 = @intCast(i64, value) },
460 .unsigned => Value{ .uint64 = @intCast(u64, value) },
461 },
462 } });
463}
464
465pub fn constUndef(self: *Module, ty_ref: CacheRef) !IdRef {
466 return try self.resolveId(.{ .undef = .{ .ty = ty_ref } });
467}
468
469pub fn constNull(self: *Module, ty_ref: CacheRef) !IdRef {
470 return try self.resolveId(.{ .null = .{ .ty = ty_ref } });
813}471}
814472
815pub fn changePtrStorageClass(self: *Module, ptr_ty_ref: Type.Ref, new_storage_class: spec.StorageClass) !Type.Ref {473pub fn constBool(self: *Module, ty_ref: CacheRef, value: bool) !IdRef {
816 const payload = try self.arena.create(Type.Payload.Pointer);474 return try self.resolveId(.{ .bool = .{ .ty = ty_ref, .value = value } });
817 payload.* = self.typeRefType(ptr_ty_ref).payload(.pointer).*;
818 payload.storage_class = new_storage_class;
819 return try self.resolveType(Type.initPayload(&payload.base));
820}475}
821476
822pub fn constComposite(self: *Module, ty_ref: Type.Ref, members: []const IdRef) !IdRef {477pub fn constComposite(self: *Module, ty_ref: CacheRef, members: []const IdRef) !IdRef {
823 const result_id = self.allocId();478 const result_id = self.allocId();
824 try self.sections.types_globals_constants.emit(self.gpa, .OpSpecConstantComposite, .{479 try self.sections.types_globals_constants.emit(self.gpa, .OpSpecConstantComposite, .{
825 .id_result_type = self.typeId(ty_ref),480 .id_result_type = self.resultId(ty_ref),
826 .id_result = result_id,481 .id_result = result_id,
827 .constituents = members,482 .constituents = members,
828 });483 });
src/codegen/spirv/TypeConstantCache.zig+86
...@@ -111,6 +111,18 @@ const Tag = enum {...@@ -111,6 +111,18 @@ const Tag = enum {
111 /// Value of type f64111 /// Value of type f64
112 /// data is payload to Float16112 /// data is payload to Float16
113 float64,113 float64,
114 /// Undefined value
115 /// data is type
116 undef,
117 /// Null value
118 /// data is type
119 null,
120 /// Bool value that is true
121 /// data is (bool) type
122 bool_true,
123 /// Bool value that is false
124 /// data is (bool) type
125 bool_false,
114126
115 const SimpleType = enum { void, bool };127 const SimpleType = enum { void, bool };
116128
...@@ -227,6 +239,9 @@ pub const Key = union(enum) {...@@ -227,6 +239,9 @@ pub const Key = union(enum) {
227 // -- values239 // -- values
228 int: Int,240 int: Int,
229 float: Float,241 float: Float,
242 undef: Undef,
243 null: Null,
244 bool: Bool,
230245
231 pub const IntType = std.builtin.Type.Int;246 pub const IntType = std.builtin.Type.Int;
232 pub const FloatType = std.builtin.Type.Float;247 pub const FloatType = std.builtin.Type.Float;
...@@ -323,6 +338,19 @@ pub const Key = union(enum) {...@@ -323,6 +338,19 @@ pub const Key = union(enum) {
323 };338 };
324 };339 };
325340
341 pub const Undef = struct {
342 ty: Ref,
343 };
344
345 pub const Null = struct {
346 ty: Ref,
347 };
348
349 pub const Bool = struct {
350 ty: Ref,
351 value: bool,
352 };
353
326 fn hash(self: Key) u32 {354 fn hash(self: Key) u32 {
327 var hasher = std.hash.Wyhash.init(0);355 var hasher = std.hash.Wyhash.init(0);
328 switch (self) {356 switch (self) {
...@@ -539,6 +567,32 @@ fn emit(...@@ -539,6 +567,32 @@ fn emit(
539 .value = lit,567 .value = lit,
540 });568 });
541 },569 },
570 .undef => |undef| {
571 try section.emit(spv.gpa, .OpUndef, .{
572 .id_result_type = self.resultId(undef.ty),
573 .id_result = result_id,
574 });
575 },
576 .null => |null_info| {
577 try section.emit(spv.gpa, .OpConstantNull, .{
578 .id_result_type = self.resultId(null_info.ty),
579 .id_result = result_id,
580 });
581 },
582 .bool => |bool_info| switch (bool_info.value) {
583 true => {
584 try section.emit(spv.gpa, .OpConstantTrue, .{
585 .id_result_type = self.resultId(bool_info.ty),
586 .id_result = result_id,
587 });
588 },
589 false => {
590 try section.emit(spv.gpa, .OpConstantFalse, .{
591 .id_result_type = self.resultId(bool_info.ty),
592 .id_result = result_id,
593 });
594 },
595 },
542 }596 }
543}597}
544598
...@@ -713,6 +767,24 @@ pub fn resolve(self: *Self, spv: *Module, key: Key) !Ref {...@@ -713,6 +767,24 @@ pub fn resolve(self: *Self, spv: *Module, key: Key) !Ref {
713 },767 },
714 else => unreachable,768 else => unreachable,
715 },769 },
770 .undef => |undef| .{
771 .tag = .undef,
772 .result_id = result_id,
773 .data = @enumToInt(undef.ty),
774 },
775 .null => |null_info| .{
776 .tag = .null,
777 .result_id = result_id,
778 .data = @enumToInt(null_info.ty),
779 },
780 .bool => |bool_info| .{
781 .tag = switch (bool_info.value) {
782 true => Tag.bool_true,
783 false => Tag.bool_false,
784 },
785 .result_id = result_id,
786 .data = @enumToInt(bool_info.ty),
787 },
716 };788 };
717 try self.items.append(spv.gpa, item);789 try self.items.append(spv.gpa, item);
718790
...@@ -850,6 +922,20 @@ pub fn lookup(self: *const Self, ref: Ref) Key {...@@ -850,6 +922,20 @@ pub fn lookup(self: *const Self, ref: Ref) Key {
850 .value = .{ .uint64 = payload.decode() },922 .value = .{ .uint64 = payload.decode() },
851 } };923 } };
852 },924 },
925 .undef => .{ .undef = .{
926 .ty = @intToEnum(Ref, data),
927 } },
928 .null => .{ .null = .{
929 .ty = @intToEnum(Ref, data),
930 } },
931 .bool_true => .{ .bool = .{
932 .ty = @intToEnum(Ref, data),
933 .value = true,
934 } },
935 .bool_false => .{ .bool = .{
936 .ty = @intToEnum(Ref, data),
937 .value = false,
938 } },
853 };939 };
854}940}
855941
src/codegen/spirv/type.zig deleted-567
...@@ -1,567 +0,0 @@
1//! This module models a SPIR-V Type. These are distinct from Zig types, with some types
2//! which are not representable by Zig directly.
3
4const std = @import("std");
5const assert = std.debug.assert;
6const Signedness = std.builtin.Signedness;
7const Allocator = std.mem.Allocator;
8
9const spec = @import("spec.zig");
10
11pub const Type = extern union {
12 tag_if_small_enough: Tag,
13 ptr_otherwise: *Payload,
14
15 /// A reference to another SPIR-V type.
16 pub const Ref = enum(u32) { _ };
17
18 pub fn initTag(comptime small_tag: Tag) Type {
19 comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
20 return .{ .tag_if_small_enough = small_tag };
21 }
22
23 pub fn initPayload(pl: *Payload) Type {
24 assert(@enumToInt(pl.tag) >= Tag.no_payload_count);
25 return .{ .ptr_otherwise = pl };
26 }
27
28 pub fn int(arena: Allocator, signedness: Signedness, bits: u16) !Type {
29 const bits_and_signedness = switch (signedness) {
30 .signed => -@as(i32, bits),
31 .unsigned => @as(i32, bits),
32 };
33
34 return switch (bits_and_signedness) {
35 8 => initTag(.u8),
36 16 => initTag(.u16),
37 32 => initTag(.u32),
38 64 => initTag(.u64),
39 -8 => initTag(.i8),
40 -16 => initTag(.i16),
41 -32 => initTag(.i32),
42 -64 => initTag(.i64),
43 else => {
44 const int_payload = try arena.create(Payload.Int);
45 int_payload.* = .{
46 .width = bits,
47 .signedness = signedness,
48 };
49 return initPayload(&int_payload.base);
50 },
51 };
52 }
53
54 pub fn float(bits: u16) Type {
55 return switch (bits) {
56 16 => initTag(.f16),
57 32 => initTag(.f32),
58 64 => initTag(.f64),
59 else => unreachable, // Enable more types if required.
60 };
61 }
62
63 pub fn tag(self: Type) Tag {
64 if (@enumToInt(self.tag_if_small_enough) < Tag.no_payload_count) {
65 return self.tag_if_small_enough;
66 } else {
67 return self.ptr_otherwise.tag;
68 }
69 }
70
71 pub fn castTag(self: Type, comptime t: Tag) ?*t.Type() {
72 if (@enumToInt(self.tag_if_small_enough) < Tag.no_payload_count)
73 return null;
74
75 if (self.ptr_otherwise.tag == t)
76 return self.payload(t);
77
78 return null;
79 }
80
81 /// Access the payload of a type directly.
82 pub fn payload(self: Type, comptime t: Tag) *t.Type() {
83 assert(self.tag() == t);
84 return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise);
85 }
86
87 /// Perform a shallow equality test, comparing two types while assuming that any child types
88 /// are equal only if their references are equal.
89 pub fn eqlShallow(a: Type, b: Type) bool {
90 if (a.tag_if_small_enough == b.tag_if_small_enough)
91 return true;
92
93 const tag_a = a.tag();
94 const tag_b = b.tag();
95 if (tag_a != tag_b)
96 return false;
97
98 inline for (@typeInfo(Tag).Enum.fields) |field| {
99 const t = @field(Tag, field.name);
100 if (t == tag_a) {
101 return eqlPayloads(t, a, b);
102 }
103 }
104
105 unreachable;
106 }
107
108 /// Compare the payload of two compatible tags, given that we already know the tag of both types.
109 fn eqlPayloads(comptime t: Tag, a: Type, b: Type) bool {
110 switch (t) {
111 .void,
112 .bool,
113 .sampler,
114 .event,
115 .device_event,
116 .reserve_id,
117 .queue,
118 .pipe_storage,
119 .named_barrier,
120 .u8,
121 .u16,
122 .u32,
123 .u64,
124 .i8,
125 .i16,
126 .i32,
127 .i64,
128 .f16,
129 .f32,
130 .f64,
131 => return true,
132 .int,
133 .vector,
134 .matrix,
135 .sampled_image,
136 .array,
137 .runtime_array,
138 .@"opaque",
139 .pointer,
140 .pipe,
141 .image,
142 => return std.meta.eql(a.payload(t).*, b.payload(t).*),
143 .@"struct" => {
144 const struct_a = a.payload(.@"struct");
145 const struct_b = b.payload(.@"struct");
146 if (struct_a.members.len != struct_b.members.len)
147 return false;
148 for (struct_a.members, 0..) |mem_a, i| {
149 if (!std.meta.eql(mem_a, struct_b.members[i]))
150 return false;
151 }
152 return true;
153 },
154 .function => {
155 const fn_a = a.payload(.function);
156 const fn_b = b.payload(.function);
157 if (fn_a.return_type != fn_b.return_type)
158 return false;
159 return std.mem.eql(Ref, fn_a.parameters, fn_b.parameters);
160 },
161 }
162 }
163
164 /// Perform a shallow hash, which hashes the reference value of child types instead of recursing.
165 pub fn hashShallow(self: Type) u64 {
166 var hasher = std.hash.Wyhash.init(0);
167 const t = self.tag();
168 std.hash.autoHash(&hasher, t);
169
170 inline for (@typeInfo(Tag).Enum.fields) |field| {
171 if (@field(Tag, field.name) == t) {
172 switch (@field(Tag, field.name)) {
173 .void,
174 .bool,
175 .sampler,
176 .event,
177 .device_event,
178 .reserve_id,
179 .queue,
180 .pipe_storage,
181 .named_barrier,
182 .u8,
183 .u16,
184 .u32,
185 .u64,
186 .i8,
187 .i16,
188 .i32,
189 .i64,
190 .f16,
191 .f32,
192 .f64,
193 => {},
194 else => self.hashPayload(@field(Tag, field.name), &hasher),
195 }
196 }
197 }
198
199 return hasher.final();
200 }
201
202 /// Perform a shallow hash, given that we know the tag of the field ahead of time.
203 fn hashPayload(self: Type, comptime t: Tag, hasher: *std.hash.Wyhash) void {
204 const fields = @typeInfo(t.Type()).Struct.fields;
205 const pl = self.payload(t);
206 comptime assert(std.mem.eql(u8, fields[0].name, "base"));
207 inline for (fields[1..]) |field| { // Skip the 'base' field.
208 std.hash.autoHashStrat(hasher, @field(pl, field.name), .DeepRecursive);
209 }
210 }
211
212 /// Hash context that hashes and compares types in a shallow fashion, useful for type caches.
213 pub const ShallowHashContext32 = struct {
214 pub fn hash(self: @This(), t: Type) u32 {
215 _ = self;
216 return @truncate(u32, t.hashShallow());
217 }
218 pub fn eql(self: @This(), a: Type, b: Type, b_index: usize) bool {
219 _ = self;
220 _ = b_index;
221 return a.eqlShallow(b);
222 }
223 };
224
225 /// Return the reference to any child type. Asserts the type is one of:
226 /// - Vectors
227 /// - Matrices
228 /// - Images
229 /// - SampledImages,
230 /// - Arrays
231 /// - RuntimeArrays
232 /// - Pointers
233 pub fn childType(self: Type) Ref {
234 return switch (self.tag()) {
235 .vector => self.payload(.vector).component_type,
236 .matrix => self.payload(.matrix).column_type,
237 .image => self.payload(.image).sampled_type,
238 .sampled_image => self.payload(.sampled_image).image_type,
239 .array => self.payload(.array).element_type,
240 .runtime_array => self.payload(.runtime_array).element_type,
241 .pointer => self.payload(.pointer).child_type,
242 else => unreachable,
243 };
244 }
245
246 pub fn isInt(self: Type) bool {
247 return switch (self.tag()) {
248 .u8,
249 .u16,
250 .u32,
251 .u64,
252 .i8,
253 .i16,
254 .i32,
255 .i64,
256 .int,
257 => true,
258 else => false,
259 };
260 }
261
262 pub fn isFloat(self: Type) bool {
263 return switch (self.tag()) {
264 .f16, .f32, .f64 => true,
265 else => false,
266 };
267 }
268
269 /// Returns the number of bits that make up an int or float type.
270 /// Asserts type is either int or float.
271 pub fn intFloatBits(self: Type) u16 {
272 return switch (self.tag()) {
273 .u8, .i8 => 8,
274 .u16, .i16, .f16 => 16,
275 .u32, .i32, .f32 => 32,
276 .u64, .i64, .f64 => 64,
277 .int => self.payload(.int).width,
278 else => unreachable,
279 };
280 }
281
282 /// Returns the signedness of an integer type.
283 /// Asserts that the type is an int.
284 pub fn intSignedness(self: Type) Signedness {
285 return switch (self.tag()) {
286 .u8, .u16, .u32, .u64 => .unsigned,
287 .i8, .i16, .i32, .i64 => .signed,
288 .int => self.payload(.int).signedness,
289 else => unreachable,
290 };
291 }
292
293 pub const Tag = enum(usize) {
294 void,
295 bool,
296 sampler,
297 event,
298 device_event,
299 reserve_id,
300 queue,
301 pipe_storage,
302 named_barrier,
303 u8,
304 u16,
305 u32,
306 u64,
307 i8,
308 i16,
309 i32,
310 i64,
311 f16,
312 f32,
313 f64,
314
315 // After this, the tag requires a payload.
316 int,
317 vector,
318 matrix,
319 image,
320 sampled_image,
321 array,
322 runtime_array,
323 @"struct",
324 @"opaque",
325 pointer,
326 function,
327 pipe,
328
329 pub const last_no_payload_tag = Tag.f64;
330 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
331
332 pub fn Type(comptime t: Tag) type {
333 return switch (t) {
334 .void,
335 .bool,
336 .sampler,
337 .event,
338 .device_event,
339 .reserve_id,
340 .queue,
341 .pipe_storage,
342 .named_barrier,
343 .u8,
344 .u16,
345 .u32,
346 .u64,
347 .i8,
348 .i16,
349 .i32,
350 .i64,
351 .f16,
352 .f32,
353 .f64,
354 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
355 .int => Payload.Int,
356 .vector => Payload.Vector,
357 .matrix => Payload.Matrix,
358 .image => Payload.Image,
359 .sampled_image => Payload.SampledImage,
360 .array => Payload.Array,
361 .runtime_array => Payload.RuntimeArray,
362 .@"struct" => Payload.Struct,
363 .@"opaque" => Payload.Opaque,
364 .pointer => Payload.Pointer,
365 .function => Payload.Function,
366 .pipe => Payload.Pipe,
367 };
368 }
369 };
370
371 pub const Payload = struct {
372 tag: Tag,
373
374 pub const Int = struct {
375 base: Payload = .{ .tag = .int },
376 width: u16,
377 signedness: Signedness,
378 };
379
380 pub const Vector = struct {
381 base: Payload = .{ .tag = .vector },
382 component_type: Ref,
383 component_count: u32,
384 };
385
386 pub const Matrix = struct {
387 base: Payload = .{ .tag = .matrix },
388 column_type: Ref,
389 column_count: u32,
390 };
391
392 pub const Image = struct {
393 base: Payload = .{ .tag = .image },
394 sampled_type: Ref,
395 dim: spec.Dim,
396 depth: enum(u2) {
397 no = 0,
398 yes = 1,
399 maybe = 2,
400 },
401 arrayed: bool,
402 multisampled: bool,
403 sampled: enum(u2) {
404 known_at_runtime = 0,
405 with_sampler = 1,
406 without_sampler = 2,
407 },
408 format: spec.ImageFormat,
409 access_qualifier: ?spec.AccessQualifier,
410 };
411
412 pub const SampledImage = struct {
413 base: Payload = .{ .tag = .sampled_image },
414 image_type: Ref,
415 };
416
417 pub const Array = struct {
418 base: Payload = .{ .tag = .array },
419 element_type: Ref,
420 /// Note: Must be emitted as constant, not as literal!
421 length: u32,
422 /// Type has the 'ArrayStride' decoration.
423 /// If zero, no stride is present.
424 array_stride: u32 = 0,
425 };
426
427 pub const RuntimeArray = struct {
428 base: Payload = .{ .tag = .runtime_array },
429 element_type: Ref,
430 /// Type has the 'ArrayStride' decoration.
431 /// If zero, no stride is present.
432 array_stride: u32 = 0,
433 };
434
435 pub const Struct = struct {
436 base: Payload = .{ .tag = .@"struct" },
437 members: []Member,
438 name: []const u8 = "",
439 decorations: StructDecorations = .{},
440
441 /// Extra information for decorations, packed for efficiency. Fields are stored sequentially by
442 /// order of the `members` slice and `MemberDecorations` struct.
443 member_decoration_extra: []u32 = &.{},
444
445 pub const Member = struct {
446 ty: Ref,
447 name: []const u8 = "",
448 offset: MemberOffset = .none,
449 decorations: MemberDecorations = .{},
450 };
451
452 pub const MemberOffset = enum(u32) { none = 0xFFFF_FFFF, _ };
453
454 pub const StructDecorations = packed struct {
455 /// Type has the 'Block' decoration.
456 block: bool = false,
457 /// Type has the 'BufferBlock' decoration.
458 buffer_block: bool = false,
459 /// Type has the 'GLSLShared' decoration.
460 glsl_shared: bool = false,
461 /// Type has the 'GLSLPacked' decoration.
462 glsl_packed: bool = false,
463 /// Type has the 'CPacked' decoration.
464 c_packed: bool = false,
465 };
466
467 pub const MemberDecorations = packed struct {
468 /// Matrix layout for (arrays of) matrices. If this field is not .none,
469 /// then there is also an extra field containing the matrix stride corresponding
470 /// to the 'MatrixStride' decoration.
471 matrix_layout: enum(u2) {
472 /// Member has the 'RowMajor' decoration. The member type
473 /// must be a matrix or an array of matrices.
474 row_major,
475 /// Member has the 'ColMajor' decoration. The member type
476 /// must be a matrix or an array of matrices.
477 col_major,
478 /// Member is not a matrix or array of matrices.
479 none,
480 } = .none,
481
482 // Regular decorations, these do not imply extra fields.
483
484 /// Member has the 'NoPerspective' decoration.
485 no_perspective: bool = false,
486 /// Member has the 'Flat' decoration.
487 flat: bool = false,
488 /// Member has the 'Patch' decoration.
489 patch: bool = false,
490 /// Member has the 'Centroid' decoration.
491 centroid: bool = false,
492 /// Member has the 'Sample' decoration.
493 sample: bool = false,
494 /// Member has the 'Invariant' decoration.
495 /// Note: requires parent struct to have 'Block'.
496 invariant: bool = false,
497 /// Member has the 'Volatile' decoration.
498 @"volatile": bool = false,
499 /// Member has the 'Coherent' decoration.
500 coherent: bool = false,
501 /// Member has the 'NonWritable' decoration.
502 non_writable: bool = false,
503 /// Member has the 'NonReadable' decoration.
504 non_readable: bool = false,
505
506 // The following decorations all imply extra field(s).
507
508 /// Member has the 'BuiltIn' decoration.
509 /// This decoration has an extra field of type `spec.BuiltIn`.
510 /// Note: If any member of a struct has the BuiltIn decoration, all members must have one.
511 /// Note: Each builtin may only be reachable once for a particular entry point.
512 /// Note: The member type may be constrained by a particular built-in, defined in the client API specification.
513 builtin: bool = false,
514 /// Member has the 'Stream' decoration.
515 /// This member has an extra field of type `u32`.
516 stream: bool = false,
517 /// Member has the 'Location' decoration.
518 /// This member has an extra field of type `u32`.
519 location: bool = false,
520 /// Member has the 'Component' decoration.
521 /// This member has an extra field of type `u32`.
522 component: bool = false,
523 /// Member has the 'XfbBuffer' decoration.
524 /// This member has an extra field of type `u32`.
525 xfb_buffer: bool = false,
526 /// Member has the 'XfbStride' decoration.
527 /// This member has an extra field of type `u32`.
528 xfb_stride: bool = false,
529 /// Member has the 'UserSemantic' decoration.
530 /// This member has an extra field of type `[]u8`, which is encoded
531 /// by an `u32` containing the number of chars exactly, and then the string padded to
532 /// a multiple of 4 bytes with zeroes.
533 user_semantic: bool = false,
534 };
535 };
536
537 pub const Opaque = struct {
538 base: Payload = .{ .tag = .@"opaque" },
539 name: []u8,
540 };
541
542 pub const Pointer = struct {
543 base: Payload = .{ .tag = .pointer },
544 storage_class: spec.StorageClass,
545 child_type: Ref,
546 /// Type has the 'ArrayStride' decoration.
547 /// This is valid for pointers to elements of an array.
548 /// If zero, no stride is present.
549 array_stride: u32 = 0,
550 /// If nonzero, type has the 'Alignment' decoration.
551 alignment: u32 = 0,
552 /// Type has the 'MaxByteOffset' decoration.
553 max_byte_offset: ?u32 = null,
554 };
555
556 pub const Function = struct {
557 base: Payload = .{ .tag = .function },
558 return_type: Ref,
559 parameters: []Ref,
560 };
561
562 pub const Pipe = struct {
563 base: Payload = .{ .tag = .pipe },
564 qualifier: spec.AccessQualifier,
565 };
566 };
567};