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;
2222const StorageClass = spec.StorageClass;
2323
2424const SpvModule = @import("spirv/Module.zig");
25const SpvCacheRef = SpvModule.TypeConstantCache.Ref;
26const SpvCacheString = SpvModule.TypeConstantCache.String;
25const CacheRef = SpvModule.CacheRef;
26const CacheString = SpvModule.CacheString;
2727
2828const SpvSection = @import("spirv/Section.zig");
29const SpvType = @import("spirv/type.zig").Type;
3029const SpvAssembler = @import("spirv/Assembler.zig");
3130
3231const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef);
......@@ -380,74 +379,23 @@ pub const DeclGen = struct {
380379 };
381380 }
382381
383 fn genConstInt(self: *DeclGen, ty_ref: SpvType.Ref, result_id: IdRef, value: anytype) !void {
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
382 /// Emits a bool constant in a particular representation.
428383 fn constBool(self: *DeclGen, value: bool, repr: Repr) !IdRef {
429384 switch (repr) {
430385 .indirect => {
431386 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));
433388 },
434389 .direct => {
435390 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
436 const result_id = self.spv.allocId();
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;
391 return self.spv.constBool(bool_ty_ref, value);
444392 },
445393 }
446394 }
447395
448396 /// Construct a struct at runtime.
449397 /// 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 {
451399 // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which'
452400 // operands are not constant.
453401 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349
......@@ -456,11 +404,13 @@ pub const DeclGen = struct {
456404 const ptr_composite_id = try self.alloc(result_ty_ref, null);
457405 // Note: using 32-bit ints here because usize crashes the translator as well
458406 const index_ty_ref = try self.intType(.unsigned, 32);
459 const spv_composite_ty = self.spv.typeRefType(result_ty_ref);
460 const members = spv_composite_ty.payload(.@"struct").members;
461 for (constituents, members, 0..) |constitent_id, member, index| {
462 const index_id = try self.constInt(index_ty_ref, index);
463 const ptr_member_ty_ref = try self.spv.ptrType(member.ty, .Generic, 0);
407
408 const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).struct_type;
409 const member_types = spv_composite_ty.member_types;
410
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);
464414 const ptr_id = try self.accessChain(ptr_member_ty_ref, ptr_composite_id, &.{index_id});
465415 try self.func.body.emit(self.spv.gpa, .OpStore, .{
466416 .pointer = ptr_id,
......@@ -481,11 +431,11 @@ pub const DeclGen = struct {
481431
482432 dg: *DeclGen,
483433 /// Cached reference of the u32 type.
484 u32_ty_ref: SpvType.Ref,
434 u32_ty_ref: CacheRef,
485435 /// Cached type id of the u32 type.
486436 u32_ty_id: IdRef,
487437 /// The members of the resulting structure type
488 members: std.ArrayList(SpvType.Payload.Struct.Member),
438 members: std.ArrayList(CacheRef),
489439 /// The initializers of each of the members.
490440 initializers: std.ArrayList(IdRef),
491441 /// The current size of the structure. Includes
......@@ -519,7 +469,7 @@ pub const DeclGen = struct {
519469 const result_id = self.dg.spv.allocId();
520470 // TODO: Integrate with caching mechanism
521471 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);
523473 try self.initializers.append(result_id);
524474
525475 self.partial_word.len = 0;
......@@ -555,7 +505,7 @@ pub const DeclGen = struct {
555505 }
556506 }
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 {
559509 // TODO: Double check pointer sizes here.
560510 // shared pointers might be u32...
561511 const target = self.dg.getTarget();
......@@ -563,12 +513,12 @@ pub const DeclGen = struct {
563513 if (self.size % width != 0) {
564514 return self.dg.todo("misaligned pointer constants", .{});
565515 }
566 try self.members.append(.{ .ty = ptr_ty_ref });
516 try self.members.append(ptr_ty_ref);
567517 try self.initializers.append(ptr_id);
568518 self.size += width;
569519 }
570520
571 fn addNullPtr(self: *@This(), ptr_ty_ref: SpvType.Ref) !void {
521 fn addNullPtr(self: *@This(), ptr_ty_ref: CacheRef) !void {
572522 const result_id = self.dg.spv.allocId();
573523 try self.dg.spv.sections.types_globals_constants.emit(self.dg.spv.gpa, .OpConstantNull, .{
574524 .id_result_type = self.dg.typeId(ptr_ty_ref),
......@@ -931,7 +881,7 @@ pub const DeclGen = struct {
931881 const section = &self.spv.globals.section;
932882
933883 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
936886 // const target = self.getTarget();
937887
......@@ -960,7 +910,7 @@ pub const DeclGen = struct {
960910 .dg = self,
961911 .u32_ty_ref = u32_ty_ref,
962912 .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),
964914 .initializers = std.ArrayList(IdRef).init(self.gpa),
965915 .decl_deps = std.AutoArrayHashMap(SpvModule.Decl.Index, void).init(self.gpa),
966916 };
......@@ -972,8 +922,10 @@ pub const DeclGen = struct {
972922 try icl.lower(ty, val);
973923 try icl.flush();
974924
975 const constant_struct_ty_ref = try self.spv.simpleStructType(icl.members.items);
976 const ptr_constant_struct_ty_ref = try self.spv.ptrType(constant_struct_ty_ref, storage_class, 0);
925 const constant_struct_ty_ref = try self.spv.resolve(.{ .struct_type = .{
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
978930 const constant_struct_id = self.spv.allocId();
979931 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
......@@ -1007,7 +959,7 @@ pub const DeclGen = struct {
1007959 });
1008960
1009961 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);
1011963 try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{
1012964 .id_result_type = self.typeId(generic_ptr_ty_ref),
1013965 .id_result = result_id,
......@@ -1044,9 +996,9 @@ pub const DeclGen = struct {
1044996 switch (ty.zigTypeTag()) {
1045997 .Int => {
1046998 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));
10481000 } else {
1049 return try self.constInt(result_ty_ref, val.toUnsignedInt(target));
1001 return try self.spv.constInt(result_ty_ref, val.toUnsignedInt(target));
10501002 }
10511003 },
10521004 .Bool => switch (repr) {
......@@ -1060,7 +1012,7 @@ pub const DeclGen = struct {
10601012 }
10611013 return result_id;
10621014 },
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())),
10641016 },
10651017 .Float => {
10661018 const result_id = self.spv.allocId();
......@@ -1084,7 +1036,7 @@ pub const DeclGen = struct {
10841036 else => unreachable,
10851037 };
10861038
1087 return try self.constInt(result_ty_ref, value);
1039 return try self.spv.constInt(result_ty_ref, value);
10881040 },
10891041 .ErrorUnion => {
10901042 const payload_ty = ty.errorUnionPayload();
......@@ -1143,45 +1095,31 @@ pub const DeclGen = struct {
11431095 /// Turn a Zig type into a SPIR-V Type, and return its type result-id.
11441096 fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType {
11451097 const type_ref = try self.resolveType(ty, .direct);
1146 return self.typeId(type_ref);
1098 return self.spv.resultId(type_ref);
11471099 }
11481100
1149 fn typeId(self: *DeclGen, ty_ref: SpvType.Ref) IdRef {
1150 return self.spv.typeId(ty_ref);
1101 fn typeId(self: *DeclGen, ty_ref: CacheRef) IdRef {
1102 return self.spv.resultId(ty_ref);
11511103 }
11521104
11531105 /// 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 {
11551110 const backing_bits = self.backingIntBits(bits) orelse {
11561111 // TODO: Integers too big for any native type are represented as "composite integers":
11571112 // An array of largestSupportedIntBits.
11581113 return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits });
11591114 };
1160
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 } });
1115 return self.spv.intType(signedness, backing_bits);
11741116 }
11751117
11761118 /// Create an integer type that represents 'usize'.
1177 fn sizeType(self: *DeclGen) !SpvType.Ref {
1119 fn sizeType(self: *DeclGen) !CacheRef {
11781120 return try self.intType(.unsigned, self.getTarget().ptrBitWidth());
11791121 }
11801122
1181 fn sizeType2(self: *DeclGen) !SpvCacheRef {
1182 return try self.intType2(.unsigned, self.getTarget().ptrBitWidth());
1183 }
1184
11851123 /// Generate a union type, optionally with a known field. If the tag alignment is greater
11861124 /// than that of the payload, a regular union (non-packed, with both tag and payload), will
11871125 /// be generated as follows:
......@@ -1204,7 +1142,7 @@ pub const DeclGen = struct {
12041142 /// If any of the fields' size is 0, it will be omitted.
12051143 /// NOTE: When the active field is set to something other than the most aligned field, the
12061144 /// 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 {
12081146 const target = self.getTarget();
12091147 const layout = ty.unionGetLayout(target);
12101148 const union_ty = ty.cast(Type.Payload.Union).?.data;
......@@ -1218,7 +1156,8 @@ pub const DeclGen = struct {
12181156 return try self.resolveType(union_ty.tag_ty, .indirect);
12191157 }
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
12231162 const has_tag = layout.tag_size != 0;
12241163 const tag_first = layout.tag_align >= layout.payload_align;
......@@ -1226,82 +1165,6 @@ pub const DeclGen = struct {
12261165
12271166 if (has_tag and tag_first) {
12281167 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);
13051168 member_types.appendAssumeCapacity(tag_ty_ref);
13061169 member_names.appendAssumeCapacity(try self.spv.resolveString("tag"));
13071170 }
......@@ -1310,7 +1173,7 @@ pub const DeclGen = struct {
13101173 const active_field_ty = union_ty.fields.values()[active_field].ty;
13111174
13121175 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);
13141177 member_types.appendAssumeCapacity(active_payload_ty_ref);
13151178 member_names.appendAssumeCapacity(try self.spv.resolveString("payload"));
13161179 break :blk active_field_ty.abiSize(target);
......@@ -1318,19 +1181,19 @@ pub const DeclGen = struct {
13181181
13191182 const payload_padding_len = layout.payload_size - active_field_size;
13201183 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);
13221185 member_types.appendAssumeCapacity(payload_padding_ty_ref);
13231186 member_names.appendAssumeCapacity(try self.spv.resolveString("payload_padding"));
13241187 }
13251188
13261189 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);
13281191 member_types.appendAssumeCapacity(tag_ty_ref);
13291192 member_names.appendAssumeCapacity(try self.spv.resolveString("tag"));
13301193 }
13311194
13321195 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);
13341197 member_types.appendAssumeCapacity(padding_ty_ref);
13351198 member_names.appendAssumeCapacity(try self.spv.resolveString("padding"));
13361199 }
......@@ -1341,22 +1204,24 @@ pub const DeclGen = struct {
13411204 } });
13421205 }
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)});
13451210 const target = self.getTarget();
13461211 switch (ty.zigTypeTag()) {
13471212 .Void, .NoReturn => return try self.spv.resolve(.void_type),
13481213 .Bool => switch (repr) {
13491214 .direct => return try self.spv.resolve(.bool_type),
1350 .indirect => return try self.intType2(.unsigned, 1),
1215 .indirect => return try self.intType(.unsigned, 1),
13511216 },
13521217 .Int => {
13531218 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);
13551220 },
13561221 .Enum => {
13571222 var buffer: Type.Payload.Bits = undefined;
13581223 const tag_ty = ty.intTagType(&buffer);
1359 return self.resolveType2(tag_ty, repr);
1224 return self.resolveType(tag_ty, repr);
13601225 },
13611226 .Float => {
13621227 // 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 {
13781243 },
13791244 .Array => {
13801245 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);
13821247 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse {
13831248 return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()});
13841249 };
1385 return self.spv.arrayType2(total_len, elem_ty_ref);
1250 return self.spv.arrayType(total_len, elem_ty_ref);
13861251 },
13871252 .Fn => switch (repr) {
13881253 .direct => {
......@@ -1390,12 +1255,12 @@ pub const DeclGen = struct {
13901255 if (ty.fnIsVarArgs())
13911256 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());
13941259 defer self.gpa.free(param_ty_refs);
13951260 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);
13971262 }
1398 const return_ty_ref = try self.resolveType2(ty.fnReturnType(), .direct);
1263 const return_ty_ref = try self.resolveType(ty.fnReturnType(), .direct);
13991264
14001265 return try self.spv.resolve(.{ .function_type = .{
14011266 .return_type = return_ty_ref,
......@@ -1405,14 +1270,14 @@ pub const DeclGen = struct {
14051270 .indirect => {
14061271 // TODO: Represent function pointers properly.
14071272 // For now, just use an usize type.
1408 return try self.sizeType2();
1273 return try self.sizeType();
14091274 },
14101275 },
14111276 .Pointer => {
14121277 const ptr_info = ty.ptrInfo().data;
14131278
14141279 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);
14161281 const ptr_ty_ref = try self.spv.resolve(.{ .ptr_type = .{
14171282 .storage_class = storage_class,
14181283 .child_type = child_ty_ref,
......@@ -1420,7 +1285,15 @@ pub const DeclGen = struct {
14201285 if (ptr_info.size != .Slice) {
14211286 return ptr_ty_ref;
14221287 }
1423 unreachable; // TODO
1288
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 } });
14241297 },
14251298 .Vector => {
14261299 // 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 {
14331306 // TODO: Properly verify sizes and child type.
14341307
14351308 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),
14371310 .component_count = @intCast(u32, ty.vectorLen()),
14381311 } });
14391312 },
14401313 .Struct => {
14411314 if (ty.isSimpleTupleOrAnonStruct()) {
1442 unreachable; // TODO
1315 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 } });
14431331 }
14441332
14451333 const struct_ty = ty.castTag(.@"struct").?.data;
14461334
14471335 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);
14491337 }
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());
14521340 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());
14551343 defer self.gpa.free(member_names);
14561344
1457 // const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count());
14581345 var member_index: usize = 0;
14591346 for (struct_ty.fields.values(), 0..) |field, i| {
14601347 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);
14631350 member_names[member_index] = try self.spv.resolveString(struct_ty.fields.keys()[i]);
14641351 member_index += 1;
14651352 }
......@@ -1480,16 +1367,16 @@ pub const DeclGen = struct {
14801367 // Just use a bool.
14811368 // Note: Always generate the bool with indirect format, to save on some sanity
14821369 // 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);
14841371 }
14851372
1486 const payload_ty_ref = try self.resolveType2(payload_ty, .indirect);
1373 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
14871374 if (ty.optionalReprIsPayload()) {
14881375 // Optional is actually a pointer or a slice.
14891376 return payload_ty_ref;
14901377 }
14911378
1492 const bool_ty_ref = try self.resolveType2(Type.bool, .indirect);
1379 const bool_ty_ref = try self.resolveType(Type.bool, .indirect);
14931380
14941381 return try self.spv.resolve(.{ .struct_type = .{
14951382 .member_types = &.{ payload_ty_ref, bool_ty_ref },
......@@ -1499,21 +1386,21 @@ pub const DeclGen = struct {
14991386 },
15001387 } });
15011388 },
1502 .Union => return try self.resolveUnionType2(ty, null),
1503 .ErrorSet => return try self.intType2(.unsigned, 16),
1389 .Union => return try self.resolveUnionType(ty, null),
1390 .ErrorSet => return try self.intType(.unsigned, 16),
15041391 .ErrorUnion => {
15051392 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
15081395 const eu_layout = self.errorUnionLayout(payload_ty);
15091396 if (!eu_layout.payload_has_bits) {
15101397 return error_ty_ref;
15111398 }
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;
1516 var member_names: [2]SpvCacheString = undefined;
1402 var member_types: [2]CacheRef = undefined;
1403 var member_names: [2]CacheString = undefined;
15171404 if (eu_layout.error_first) {
15181405 // Put the error first
15191406 member_types = .{ error_ty_ref, payload_ty_ref };
......@@ -1550,226 +1437,6 @@ pub const DeclGen = struct {
15501437 }
15511438 }
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
17731440 fn spvStorageClass(as: std.builtin.AddressSpace) StorageClass {
17741441 return switch (as) {
17751442 .generic => .Generic,
......@@ -1839,17 +1506,13 @@ pub const DeclGen = struct {
18391506 /// the name of an error in the text executor.
18401507 fn generateTestEntryPoint(self: *DeclGen, name: []const u8, spv_test_decl_index: SpvModule.Decl.Index) !void {
18411508 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);
18431510 const void_ty_ref = try self.resolveType(Type.void, .direct);
18441511
1845 const kernel_proto_ty_ref = blk: {
1846 const proto_payload = try self.spv.arena.create(SpvType.Payload.Function);
1847 proto_payload.* = .{
1848 .return_type = void_ty_ref,
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 };
1512 const kernel_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{
1513 .return_type = void_ty_ref,
1514 .parameters = &.{ptr_anyerror_ty_ref},
1515 } });
18531516
18541517 const test_id = self.spv.declPtr(spv_test_decl_index).result_id;
18551518
......@@ -1983,9 +1646,9 @@ pub const DeclGen = struct {
19831646 }
19841647 }
19851648
1986 fn boolToInt(self: *DeclGen, result_ty_ref: SpvType.Ref, condition_id: IdRef) !IdRef {
1987 const zero_id = try self.constInt(result_ty_ref, 0);
1988 const one_id = try self.constInt(result_ty_ref, 1);
1649 fn boolToInt(self: *DeclGen, result_ty_ref: CacheRef, condition_id: IdRef) !IdRef {
1650 const zero_id = try self.spv.constInt(result_ty_ref, 0);
1651 const one_id = try self.spv.constInt(result_ty_ref, 1);
19891652 const result_id = self.spv.allocId();
19901653 try self.func.body.emit(self.spv.gpa, .OpSelect, .{
19911654 .id_result_type = self.typeId(result_ty_ref),
......@@ -2004,7 +1667,7 @@ pub const DeclGen = struct {
20041667 .Bool => blk: {
20051668 const direct_bool_ty_ref = try self.resolveType(ty, .direct);
20061669 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);
20081671 const result_id = self.spv.allocId();
20091672 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{
20101673 .id_result_type = self.typeId(direct_bool_ty_ref),
......@@ -2242,10 +1905,10 @@ pub const DeclGen = struct {
22421905 return result_id;
22431906 }
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 {
22461909 const mask_value = if (bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @intCast(u6, bits)) - 1;
22471910 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);
22491912 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{
22501913 .id_result_type = self.typeId(ty_ref),
22511914 .id_result = result_id,
......@@ -2384,7 +2047,7 @@ pub const DeclGen = struct {
23842047 // Note that signed overflow is also wrapping in spir-v.
23852048
23862049 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);
23882051 try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{
23892052 .id_result_type = self.typeId(bool_ty_ref),
23902053 .id_result = rhs_lt_zero_id,
......@@ -2463,7 +2126,7 @@ pub const DeclGen = struct {
24632126 /// is the latter and PtrAccessChain is the former.
24642127 fn accessChain(
24652128 self: *DeclGen,
2466 result_ty_ref: SpvType.Ref,
2129 result_ty_ref: CacheRef,
24672130 base: IdRef,
24682131 indexes: []const IdRef,
24692132 ) !IdRef {
......@@ -2479,7 +2142,7 @@ pub const DeclGen = struct {
24792142
24802143 fn ptrAccessChain(
24812144 self: *DeclGen,
2482 result_ty_ref: SpvType.Ref,
2145 result_ty_ref: CacheRef,
24832146 base: IdRef,
24842147 element: IdRef,
24852148 indexes: []const IdRef,
......@@ -2854,7 +2517,7 @@ pub const DeclGen = struct {
28542517 // Construct new pointer type for the resulting pointer
28552518 const elem_ty = ptr_ty.elemType2(); // use elemType() so that we get T for *[N]T.
28562519 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()));
28582521 if (ptr_ty.isSinglePointer()) {
28592522 // Pointer-to-array. In this case, the resulting pointer is not of the same type
28602523 // 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 {
29702633 fn makePointerConstant(
29712634 self: *DeclGen,
29722635 section: *SpvSection,
2973 ptr_ty_ref: SpvType.Ref,
2636 ptr_ty_ref: CacheRef,
29742637 ptr_id: IdRef,
29752638 ) !IdRef {
29762639 const result_id = self.spv.allocId();
......@@ -2988,11 +2651,11 @@ pub const DeclGen = struct {
29882651 // placed in the Function address space.
29892652 fn alloc(
29902653 self: *DeclGen,
2991 ty_ref: SpvType.Ref,
2654 ty_ref: CacheRef,
29922655 initializer: ?IdRef,
29932656 ) !IdRef {
2994 const fn_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Function, 0);
2995 const general_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic, 0);
2657 const fn_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Function);
2658 const general_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic);
29962659
29972660 // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to
29982661 // directly generate them into func.prologue instead of the body.
......@@ -3146,7 +2809,7 @@ pub const DeclGen = struct {
31462809
31472810 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
31482811 if (val_is_undef) {
3149 const undef = try self.constUndef(ptr_ty_ref);
2812 const undef = try self.spv.constUndef(ptr_ty_ref);
31502813 try self.store(ptr_ty, ptr, undef);
31512814 } else {
31522815 try self.store(ptr_ty, ptr, value);
......@@ -3217,7 +2880,7 @@ pub const DeclGen = struct {
32172880 else
32182881 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);
32212884 const is_err_id = self.spv.allocId();
32222885 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{
32232886 .id_result_type = self.typeId(bool_ty_ref),
......@@ -3266,7 +2929,7 @@ pub const DeclGen = struct {
32662929
32672930 if (err_union_ty.errorUnionSet().errorSetIsEmpty()) {
32682931 // No error possible, so just return undefined.
3269 return try self.constUndef(err_ty_ref);
2932 return try self.spv.constUndef(err_ty_ref);
32702933 }
32712934
32722935 const payload_ty = err_union_ty.errorUnionPayload();
......@@ -3295,7 +2958,7 @@ pub const DeclGen = struct {
32952958
32962959 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
32972960 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);
32992962 if (eu_layout.error_first) {
33002963 members.appendAssumeCapacity(operand_id);
33012964 members.appendAssumeCapacity(payload_id);
......@@ -3337,7 +3000,7 @@ pub const DeclGen = struct {
33373000 operand_id;
33383001
33393002 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);
33413004 const result_id = self.spv.allocId();
33423005 const operands = .{
33433006 .id_result_type = self.typeId(bool_ty_ref),
src/codegen/spirv/Assembler.zig+36-134
......@@ -11,7 +11,8 @@ const IdRef = spec.IdRef;
1111const IdResult = spec.IdResult;
1212
1313const SpvModule = @import("Module.zig");
14const SpvType = @import("type.zig").Type;
14const CacheRef = SpvModule.CacheRef;
15const CacheKey = SpvModule.CacheKey;
1516
1617/// Represents a token in the assembly template.
1718const Token = struct {
......@@ -126,7 +127,7 @@ const AsmValue = union(enum) {
126127 value: IdRef,
127128
128129 /// This result-value represents a type registered into the module's type system.
129 ty: SpvType.Ref,
130 ty: CacheRef,
130131
131132 /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue
132133 /// is of a variant that allows the result to be obtained (not an unresolved
......@@ -135,7 +136,7 @@ const AsmValue = union(enum) {
135136 return switch (self) {
136137 .just_declared, .unresolved_forward_reference => unreachable,
137138 .value => |result| result,
138 .ty => |ref| spv.typeId(ref),
139 .ty => |ref| spv.resultId(ref),
139140 };
140141 }
141142};
......@@ -267,9 +268,9 @@ fn processInstruction(self: *Assembler) !void {
267268/// refers to the result.
268269fn processTypeInstruction(self: *Assembler) !AsmValue {
269270 const operands = self.inst.operands.items;
270 const ty = switch (self.inst.opcode) {
271 .OpTypeVoid => SpvType.initTag(.void),
272 .OpTypeBool => SpvType.initTag(.bool),
271 const ref = switch (self.inst.opcode) {
272 .OpTypeVoid => try self.spv.resolve(.void_type),
273 .OpTypeBool => try self.spv.resolve(.bool_type),
273274 .OpTypeInt => blk: {
274275 const signedness: std.builtin.Signedness = switch (operands[2].literal32) {
275276 0 => .unsigned,
......@@ -282,7 +283,7 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {
282283 const width = std.math.cast(u16, operands[1].literal32) orelse {
283284 return self.fail(0, "int type of {} bits is too large", .{operands[1].literal32});
284285 };
285 break :blk try SpvType.int(self.spv.arena, signedness, width);
286 break :blk try self.spv.intType(signedness, width);
286287 },
287288 .OpTypeFloat => blk: {
288289 const bits = operands[1].literal32;
......@@ -292,136 +293,36 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {
292293 return self.fail(0, "{} is not a valid bit count for floats (expected 16, 32 or 64)", .{bits});
293294 },
294295 }
295 break :blk SpvType.float(@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);
296 break :blk try self.spv.resolve(.{ .float_type = .{ .bits = @intCast(u16, bits) } });
363297 },
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 } }),
364302 .OpTypeArray => {
365303 // TODO: The length of an OpTypeArray is determined by a constant (which may be a spec constant),
366304 // and so some consideration must be taken when entering this in the type system.
367305 return self.todo("process OpTypeArray", .{});
368306 },
369 .OpTypeRuntimeArray => blk: {
370 const payload = try self.spv.arena.create(SpvType.Payload.RuntimeArray);
371 payload.* = .{
372 .element_type = try self.resolveTypeRef(operands[1].ref_id),
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 },
307 .OpTypePointer => try self.spv.ptrType(
308 try self.resolveTypeRef(operands[2].ref_id),
309 @intToEnum(spec.StorageClass, operands[1].value),
310 ),
395311 .OpTypeFunction => blk: {
396312 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);
398315 for (param_types, 0..) |*param, i| {
399316 param.* = try self.resolveTypeRef(param_operands[i].ref_id);
400317 }
401 const payload = try self.spv.arena.create(SpvType.Payload.Function);
402 payload.* = .{
318 break :blk try self.spv.resolve(.{ .function_type = .{
403319 .return_type = try self.resolveTypeRef(operands[1].ref_id),
404320 .parameters = param_types,
405 };
406 break :blk SpvType.initPayload(&payload.base);
321 } });
407322 },
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),
421323 else => return self.todo("process type instruction {s}", .{@tagName(self.inst.opcode)}),
422324 };
423325
424 const ref = try self.spv.resolveType(ty);
425326 return AsmValue{ .ty = ref };
426327}
427328
......@@ -528,7 +429,7 @@ fn resolveRef(self: *Assembler, ref: AsmValue.Ref) !AsmValue {
528429}
529430
530431/// Resolve a value reference as type.
531fn resolveTypeRef(self: *Assembler, ref: AsmValue.Ref) !SpvType.Ref {
432fn resolveTypeRef(self: *Assembler, ref: AsmValue.Ref) !CacheRef {
532433 const value = try self.resolveRef(ref);
533434 switch (value) {
534435 .just_declared, .unresolved_forward_reference => unreachable,
......@@ -761,19 +662,20 @@ fn parseContextDependentNumber(self: *Assembler) !void {
761662
762663 const tok = self.currentToken();
763664 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)];
765 if (result_type.isInt()) {
766 try self.parseContextDependentInt(result_type.intSignedness(), result_type.intFloatBits());
767 } else if (result_type.isFloat()) {
768 const width = result_type.intFloatBits();
769 switch (width) {
770 16 => try self.parseContextDependentFloat(16),
771 32 => try self.parseContextDependentFloat(32),
772 64 => try self.parseContextDependentFloat(64),
773 else => return self.fail(tok.start, "cannot parse {}-bit float literal", .{width}),
774 }
775 } else {
776 return self.fail(tok.start, "cannot parse literal constant {s}", .{@tagName(result_type.tag())});
665 const result_type = self.spv.cache.lookup(result_type_ref);
666 switch (result_type) {
667 .int_type => |int| {
668 try self.parseContextDependentInt(int.signedness, int.bits);
669 },
670 .float_type => |float| {
671 switch (float.bits) {
672 16 => try self.parseContextDependentFloat(16),
673 32 => try self.parseContextDependentFloat(32),
674 64 => try self.parseContextDependentFloat(64),
675 else => return self.fail(tok.start, "cannot parse {}-bit float literal", .{float.bits}),
676 }
677 },
678 else => return self.fail(tok.start, "cannot parse literal constant", .{}),
777679 }
778680}
779681
src/codegen/spirv/Module.zig+51-396
......@@ -20,12 +20,13 @@ const IdResult = spec.IdResult;
2020const IdResultType = spec.IdResultType;
2121
2222const 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.
2930/// Commonly, the contents of this structure will be merged with the appropriate
3031/// sections of the module and re-used. Note that the SPIR-V module system makes
3132/// no attempt of compacting result-id's, so any Fn instance should ultimately
......@@ -130,7 +131,7 @@ sections: struct {
130131 /// From this section, OpLine and OpNoLine is allowed.
131132 /// According to the SPIR-V documentation, this section normally
132133 /// also holds type and constant instructions. These are managed
133 /// via the tc_cache instead, which is the sole structure that
134 /// via the cache instead, which is the sole structure that
134135 /// manages that section. These will be inserted between this and
135136 /// the previous section when emitting the final binary.
136137 /// TODO: Do we need this section? Globals are also managed with another mechanism.
......@@ -152,10 +153,9 @@ next_result_id: Word,
152153/// just the ones for OpLine. Note that OpLine needs the result of OpString, and not that of OpSource.
153154source_file_names: std.StringHashMapUnmanaged(IdRef) = .{},
154155
155type_cache: TypeCache = .{},
156156/// SPIR-V type- and constant cache. This structure is used to store information about these in a more
157157/// efficient manner.
158tc_cache: TypeConstantCache = .{},
158cache: Cache = .{},
159159
160160/// Set of Decls, referred to by Decl.Index.
161161decls: std.ArrayListUnmanaged(Decl) = .{},
......@@ -196,7 +196,7 @@ pub fn deinit(self: *Module) void {
196196 self.sections.functions.deinit(self.gpa);
197197
198198 self.source_file_names.deinit(self.gpa);
199 self.tc_cache.deinit(self);
199 self.cache.deinit(self);
200200
201201 self.decls.deinit(self.gpa);
202202 self.decl_deps.deinit(self.gpa);
......@@ -223,20 +223,20 @@ pub fn idBound(self: Module) Word {
223223 return self.next_result_id;
224224}
225225
226pub fn resolve(self: *Module, key: TypeConstantCache.Key) !TypeConstantCache.Ref {
227 return self.tc_cache.resolve(self, key);
226pub fn resolve(self: *Module, key: CacheKey) !CacheRef {
227 return self.cache.resolve(self, key);
228228}
229229
230pub fn resultId(self: *Module, ref: TypeConstantCache.Ref) IdResult {
231 return self.tc_cache.resultId(ref);
230pub fn resultId(self: *const Module, ref: CacheRef) IdResult {
231 return self.cache.resultId(ref);
232232}
233233
234pub fn resolveId(self: *Module, key: TypeConstantCache.Key) !IdResult {
234pub fn resolveId(self: *Module, key: CacheKey) !IdResult {
235235 return self.resultId(try self.resolve(key));
236236}
237237
238pub fn resolveString(self: *Module, str: []const u8) !TypeConstantCache.String {
239 return try self.tc_cache.addString(self, str);
238pub fn resolveString(self: *Module, str: []const u8) !CacheString {
239 return try self.cache.addString(self, str);
240240}
241241
242242fn orderGlobalsInto(
......@@ -350,7 +350,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void {
350350 var entry_points = try self.entryPoints();
351351 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);
354354 defer types_constants.deinit(self.gpa);
355355
356356 // 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 {
364364 self.sections.debug_names.toWords(),
365365 self.sections.annotations.toWords(),
366366 types_constants.toWords(),
367 self.sections.types_globals_constants.toWords(),
367368 self.sections.globals.toWords(),
368369 globals.toWords(),
369370 self.sections.functions.toWords(),
......@@ -416,364 +417,14 @@ pub fn resolveSourceFileName(self: *Module, decl: *ZigDecl) !IdRef {
416417 return result.value_ptr.*;
417418}
418419
419/// Fetch a result-id for a spir-v type. This function deduplicates the type as appropriate,
420/// and returns a cached version if that exists.
421/// Note: This function does not attempt to perform any validation on the type.
422/// The type is emitted in a shallow fashion; any child types should already
423/// be emitted at this point.
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));
420pub fn intType(self: *Module, signedness: std.builtin.Signedness, bits: u16) !CacheRef {
421 return try self.resolve(.{ .int_type = .{
422 .signedness = signedness,
423 .bits = bits,
424 } });
774425}
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 {
777428 const len_ty_ref = try self.resolve(.{ .int_type = .{
778429 .signedness = .unsigned,
779430 .bits = 32,
......@@ -788,41 +439,45 @@ pub fn arrayType2(self: *Module, len: u32, elem_ty_ref: TypeConstantCache.Ref) !
788439 } });
789440}
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
800442pub fn ptrType(
801443 self: *Module,
802 child: Type.Ref,
444 child: CacheRef,
803445 storage_class: spec.StorageClass,
804 alignment: u32,
805) !Type.Ref {
806 const ptr_payload = try self.arena.create(Type.Payload.Pointer);
807 ptr_payload.* = .{
446) !CacheRef {
447 return try self.resolve(.{ .ptr_type = .{
808448 .storage_class = storage_class,
809449 .child_type = child,
810 .alignment = alignment,
811 };
812 return try self.resolveType(Type.initPayload(&ptr_payload.base));
450 } });
451}
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 } });
813471}
814472
815pub fn changePtrStorageClass(self: *Module, ptr_ty_ref: Type.Ref, new_storage_class: spec.StorageClass) !Type.Ref {
816 const payload = try self.arena.create(Type.Payload.Pointer);
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));
473pub fn constBool(self: *Module, ty_ref: CacheRef, value: bool) !IdRef {
474 return try self.resolveId(.{ .bool = .{ .ty = ty_ref, .value = value } });
820475}
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 {
823478 const result_id = self.allocId();
824479 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),
826481 .id_result = result_id,
827482 .constituents = members,
828483 });
src/codegen/spirv/TypeConstantCache.zig+86
......@@ -111,6 +111,18 @@ const Tag = enum {
111111 /// Value of type f64
112112 /// data is payload to Float16
113113 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
115127 const SimpleType = enum { void, bool };
116128
......@@ -227,6 +239,9 @@ pub const Key = union(enum) {
227239 // -- values
228240 int: Int,
229241 float: Float,
242 undef: Undef,
243 null: Null,
244 bool: Bool,
230245
231246 pub const IntType = std.builtin.Type.Int;
232247 pub const FloatType = std.builtin.Type.Float;
......@@ -323,6 +338,19 @@ pub const Key = union(enum) {
323338 };
324339 };
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
326354 fn hash(self: Key) u32 {
327355 var hasher = std.hash.Wyhash.init(0);
328356 switch (self) {
......@@ -539,6 +567,32 @@ fn emit(
539567 .value = lit,
540568 });
541569 },
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 },
542596 }
543597}
544598
......@@ -713,6 +767,24 @@ pub fn resolve(self: *Self, spv: *Module, key: Key) !Ref {
713767 },
714768 else => unreachable,
715769 },
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 },
716788 };
717789 try self.items.append(spv.gpa, item);
718790
......@@ -850,6 +922,20 @@ pub fn lookup(self: *const Self, ref: Ref) Key {
850922 .value = .{ .uint64 = payload.decode() },
851923 } };
852924 },
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 } },
853939 };
854940}
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};