authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-05-29 20:45:54+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-05-30 19:43:37+02:00
logfcb422585c1a9e91933ff998417eb8682a4ffbcc
tree46075cad0ad13ec49c3489ec0b64541b83ba8502
parent112acb1bda04c16bf1d25f7fbbe9855c35725347
signaturelock-open Commit is signed but in an unrecognized format.

spirv: translate remaining types


3 files changed, 227 insertions(+), 23 deletions(-)

src/codegen/spirv.zig+200-15
...@@ -22,7 +22,8 @@ const IdResultType = spec.IdResultType;...@@ -22,7 +22,8 @@ const IdResultType = spec.IdResultType;
22const StorageClass = spec.StorageClass;22const StorageClass = spec.StorageClass;
2323
24const SpvModule = @import("spirv/Module.zig");24const SpvModule = @import("spirv/Module.zig");
25const SpvRef = SpvModule.TypeConstantCache.Ref;25const SpvCacheRef = SpvModule.TypeConstantCache.Ref;
26const SpvCacheString = SpvModule.TypeConstantCache.String;
2627
27const SpvSection = @import("spirv/Section.zig");28const SpvSection = @import("spirv/Section.zig");
28const SpvType = @import("spirv/type.zig").Type;29const SpvType = @import("spirv/type.zig").Type;
...@@ -1160,7 +1161,7 @@ pub const DeclGen = struct {...@@ -1160,7 +1161,7 @@ pub const DeclGen = struct {
1160 return try self.spv.resolveType(try SpvType.int(self.spv.arena, signedness, backing_bits));1161 return try self.spv.resolveType(try SpvType.int(self.spv.arena, signedness, backing_bits));
1161 }1162 }
11621163
1163 fn intType2(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !SpvRef {1164 fn intType2(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !SpvCacheRef {
1164 const backing_bits = self.backingIntBits(bits) orelse {1165 const backing_bits = self.backingIntBits(bits) orelse {
1165 // TODO: Integers too big for any native type are represented as "composite integers":1166 // TODO: Integers too big for any native type are represented as "composite integers":
1166 // An array of largestSupportedIntBits.1167 // An array of largestSupportedIntBits.
...@@ -1177,7 +1178,7 @@ pub const DeclGen = struct {...@@ -1177,7 +1178,7 @@ pub const DeclGen = struct {
1177 return try self.intType(.unsigned, self.getTarget().ptrBitWidth());1178 return try self.intType(.unsigned, self.getTarget().ptrBitWidth());
1178 }1179 }
11791180
1180 fn sizeType2(self: *DeclGen) !SpvRef {1181 fn sizeType2(self: *DeclGen) !SpvCacheRef {
1181 return try self.intType2(.unsigned, self.getTarget().ptrBitWidth());1182 return try self.intType2(.unsigned, self.getTarget().ptrBitWidth());
1182 }1183 }
11831184
...@@ -1256,7 +1257,91 @@ pub const DeclGen = struct {...@@ -1256,7 +1257,91 @@ pub const DeclGen = struct {
1256 return try self.spv.simpleStructType(members.slice());1257 return try self.spv.simpleStructType(members.slice());
1257 }1258 }
12581259
1259 fn resolveType2(self: *DeclGen, ty: Type, repr: Repr) !SpvRef {1260 /// Generate a union type, optionally with a known field. If the tag alignment is greater
1261 /// than that of the payload, a regular union (non-packed, with both tag and payload), will
1262 /// be generated as follows:
1263 /// If the active field is known:
1264 /// struct {
1265 /// tag: TagType,
1266 /// payload: ActivePayloadType,
1267 /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8,
1268 /// padding: [padding_size]u8,
1269 /// }
1270 /// If the payload alignment is greater than that of the tag:
1271 /// struct {
1272 /// payload: ActivePayloadType,
1273 /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8,
1274 /// tag: TagType,
1275 /// padding: [padding_size]u8,
1276 /// }
1277 /// If the active payload is unknown, it will default back to the most aligned field. This is
1278 /// to make sure that the overal struct has the correct alignment in spir-v.
1279 /// If any of the fields' size is 0, it will be omitted.
1280 /// NOTE: When the active field is set to something other than the most aligned field, the
1281 /// resulting struct will be *underaligned*.
1282 fn resolveUnionType2(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !SpvCacheRef {
1283 const target = self.getTarget();
1284 const layout = ty.unionGetLayout(target);
1285 const union_ty = ty.cast(Type.Payload.Union).?.data;
1286
1287 if (union_ty.layout == .Packed) {
1288 return self.todo("packed union types", .{});
1289 }
1290
1291 if (layout.payload_size == 0) {
1292 // No payload, so represent this as just the tag type.
1293 return try self.resolveType2(union_ty.tag_ty, .indirect);
1294 }
1295
1296 var member_types = std.BoundedArray(SpvCacheRef, 4){};
1297 var member_names = std.BoundedArray(SpvCacheString, 4){};
1298
1299 const has_tag = layout.tag_size != 0;
1300 const tag_first = layout.tag_align >= layout.payload_align;
1301 const u8_ty_ref = try self.intType2(.unsigned, 8); // TODO: What if Int8Type is not enabled?
1302
1303 if (has_tag and tag_first) {
1304 const tag_ty_ref = try self.resolveType2(union_ty.tag_ty, .indirect);
1305 member_types.appendAssumeCapacity(tag_ty_ref);
1306 member_names.appendAssumeCapacity(try self.spv.resolveString("tag"));
1307 }
1308
1309 const active_field = maybe_active_field orelse layout.most_aligned_field;
1310 const active_field_ty = union_ty.fields.values()[active_field].ty;
1311
1312 const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime()) blk: {
1313 const active_payload_ty_ref = try self.resolveType2(active_field_ty, .indirect);
1314 member_types.appendAssumeCapacity(active_payload_ty_ref);
1315 member_names.appendAssumeCapacity(try self.spv.resolveString("payload"));
1316 break :blk active_field_ty.abiSize(target);
1317 } else 0;
1318
1319 const payload_padding_len = layout.payload_size - active_field_size;
1320 if (payload_padding_len != 0) {
1321 const payload_padding_ty_ref = try self.spv.arrayType2(@intCast(u32, payload_padding_len), u8_ty_ref);
1322 member_types.appendAssumeCapacity(payload_padding_ty_ref);
1323 member_names.appendAssumeCapacity(try self.spv.resolveString("payload_padding"));
1324 }
1325
1326 if (has_tag and !tag_first) {
1327 const tag_ty_ref = try self.resolveType2(union_ty.tag_ty, .indirect);
1328 member_types.appendAssumeCapacity(tag_ty_ref);
1329 member_names.appendAssumeCapacity(try self.spv.resolveString("tag"));
1330 }
1331
1332 if (layout.padding != 0) {
1333 const padding_ty_ref = try self.spv.arrayType2(layout.padding, u8_ty_ref);
1334 member_types.appendAssumeCapacity(padding_ty_ref);
1335 member_names.appendAssumeCapacity(try self.spv.resolveString("padding"));
1336 }
1337
1338 return try self.spv.resolve(.{ .struct_type = .{
1339 .member_types = member_types.slice(),
1340 .member_names = member_names.slice(),
1341 } });
1342 }
1343
1344 fn resolveType2(self: *DeclGen, ty: Type, repr: Repr) Error!SpvCacheRef {
1260 const target = self.getTarget();1345 const target = self.getTarget();
1261 switch (ty.zigTypeTag()) {1346 switch (ty.zigTypeTag()) {
1262 .Void, .NoReturn => return try self.spv.resolve(.void_type),1347 .Void, .NoReturn => return try self.spv.resolve(.void_type),
...@@ -1297,15 +1382,7 @@ pub const DeclGen = struct {...@@ -1297,15 +1382,7 @@ pub const DeclGen = struct {
1297 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse {1382 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse {
1298 return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()});1383 return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()});
1299 };1384 };
1300 const len_ty_ref = try self.intType2(.unsigned, 32);1385 return self.spv.arrayType2(total_len, elem_ty_ref);
1301 const len_ref = try self.spv.resolve(.{ .int = .{
1302 .ty = len_ty_ref,
1303 .value = .{ .uint64 = total_len },
1304 } });
1305 return try self.spv.resolve(.{ .array_type = .{
1306 .element_type = elem_ty_ref,
1307 .length = len_ref,
1308 } });
1309 },1386 },
1310 .Fn => switch (repr) {1387 .Fn => switch (repr) {
1311 .direct => {1388 .direct => {
...@@ -1313,7 +1390,7 @@ pub const DeclGen = struct {...@@ -1313,7 +1390,7 @@ pub const DeclGen = struct {
1313 if (ty.fnIsVarArgs())1390 if (ty.fnIsVarArgs())
1314 return self.fail("VarArgs functions are unsupported for SPIR-V", .{});1391 return self.fail("VarArgs functions are unsupported for SPIR-V", .{});
13151392
1316 const param_ty_refs = try self.gpa.alloc(SpvRef, ty.fnParamLen());1393 const param_ty_refs = try self.gpa.alloc(SpvCacheRef, ty.fnParamLen());
1317 defer self.gpa.free(param_ty_refs);1394 defer self.gpa.free(param_ty_refs);
1318 for (param_ty_refs, 0..) |*param_type, i| {1395 for (param_ty_refs, 0..) |*param_type, i| {
1319 param_type.* = try self.resolveType2(ty.fnParamType(i), .direct);1396 param_type.* = try self.resolveType2(ty.fnParamType(i), .direct);
...@@ -1360,8 +1437,116 @@ pub const DeclGen = struct {...@@ -1360,8 +1437,116 @@ pub const DeclGen = struct {
1360 .component_count = @intCast(u32, ty.vectorLen()),1437 .component_count = @intCast(u32, ty.vectorLen()),
1361 } });1438 } });
1362 },1439 },
1440 .Struct => {
1441 if (ty.isSimpleTupleOrAnonStruct()) {
1442 unreachable; // TODO
1443 }
13631444
1364 else => unreachable, // TODO1445 const struct_ty = ty.castTag(.@"struct").?.data;
1446
1447 if (struct_ty.layout == .Packed) {
1448 return try self.resolveType2(struct_ty.backing_int_ty, .direct);
1449 }
1450
1451 const member_types = try self.gpa.alloc(SpvCacheRef, struct_ty.fields.count());
1452 defer self.gpa.free(member_types);
1453
1454 const member_names = try self.gpa.alloc(SpvCacheString, struct_ty.fields.count());
1455 defer self.gpa.free(member_names);
1456
1457 // const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count());
1458 var member_index: usize = 0;
1459 for (struct_ty.fields.values(), 0..) |field, i| {
1460 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
1461
1462 member_types[member_index] = try self.resolveType2(field.ty, .indirect);
1463 member_names[member_index] = try self.spv.resolveString(struct_ty.fields.keys()[i]);
1464 member_index += 1;
1465 }
1466
1467 const name = try struct_ty.getFullyQualifiedName(self.module);
1468 defer self.module.gpa.free(name);
1469
1470 return try self.spv.resolve(.{ .struct_type = .{
1471 .name = try self.spv.resolveString(name),
1472 .member_types = member_types[0..member_index],
1473 .member_names = member_names[0..member_index],
1474 } });
1475 },
1476 .Optional => {
1477 var buf: Type.Payload.ElemType = undefined;
1478 const payload_ty = ty.optionalChild(&buf);
1479 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
1480 // Just use a bool.
1481 // Note: Always generate the bool with indirect format, to save on some sanity
1482 // Perform the conversion to a direct bool when the field is extracted.
1483 return try self.resolveType2(Type.bool, .indirect);
1484 }
1485
1486 const payload_ty_ref = try self.resolveType2(payload_ty, .indirect);
1487 if (ty.optionalReprIsPayload()) {
1488 // Optional is actually a pointer or a slice.
1489 return payload_ty_ref;
1490 }
1491
1492 const bool_ty_ref = try self.resolveType2(Type.bool, .indirect);
1493
1494 return try self.spv.resolve(.{ .struct_type = .{
1495 .member_types = &.{ payload_ty_ref, bool_ty_ref },
1496 .member_names = &.{
1497 try self.spv.resolveString("payload"),
1498 try self.spv.resolveString("valid"),
1499 },
1500 } });
1501 },
1502 .Union => return try self.resolveUnionType2(ty, null),
1503 .ErrorSet => return try self.intType2(.unsigned, 16),
1504 .ErrorUnion => {
1505 const payload_ty = ty.errorUnionPayload();
1506 const error_ty_ref = try self.resolveType2(Type.anyerror, .indirect);
1507
1508 const eu_layout = self.errorUnionLayout(payload_ty);
1509 if (!eu_layout.payload_has_bits) {
1510 return error_ty_ref;
1511 }
1512
1513 const payload_ty_ref = try self.resolveType2(payload_ty, .indirect);
1514
1515 var member_types: [2]SpvCacheRef = undefined;
1516 var member_names: [2]SpvCacheString = undefined;
1517 if (eu_layout.error_first) {
1518 // Put the error first
1519 member_types = .{ error_ty_ref, payload_ty_ref };
1520 member_names = .{
1521 try self.spv.resolveString("error"),
1522 try self.spv.resolveString("payload"),
1523 };
1524 // TODO: ABI padding?
1525 } else {
1526 // Put the payload first.
1527 member_types = .{ payload_ty_ref, error_ty_ref };
1528 member_names = .{
1529 try self.spv.resolveString("payload"),
1530 try self.spv.resolveString("error"),
1531 };
1532 // TODO: ABI padding?
1533 }
1534
1535 return try self.spv.resolve(.{ .struct_type = .{
1536 .member_types = &member_types,
1537 .member_names = &member_names,
1538 } });
1539 },
1540
1541 .Null,
1542 .Undefined,
1543 .EnumLiteral,
1544 .ComptimeFloat,
1545 .ComptimeInt,
1546 .Type,
1547 => unreachable, // Must be comptime.
1548
1549 else => |tag| return self.todo("Implement zig type '{}'", .{tag}),
1365 }1550 }
1366 }1551 }
13671552
src/codegen/spirv/Module.zig+19
...@@ -235,6 +235,10 @@ pub fn resolveId(self: *Module, key: TypeConstantCache.Key) !IdResult {...@@ -235,6 +235,10 @@ pub fn resolveId(self: *Module, key: TypeConstantCache.Key) !IdResult {
235 return self.resultId(try self.resolve(key));235 return self.resultId(try self.resolve(key));
236}236}
237237
238pub fn resolveString(self: *Module, str: []const u8) !TypeConstantCache.String {
239 return try self.tc_cache.addString(self, str);
240}
241
238fn orderGlobalsInto(242fn orderGlobalsInto(
239 self: *Module,243 self: *Module,
240 decl_index: Decl.Index,244 decl_index: Decl.Index,
...@@ -769,6 +773,21 @@ pub fn simpleStructType(self: *Module, members: []const Type.Payload.Struct.Memb...@@ -769,6 +773,21 @@ pub fn simpleStructType(self: *Module, members: []const Type.Payload.Struct.Memb
769 return try self.resolveType(Type.initPayload(&payload.base));773 return try self.resolveType(Type.initPayload(&payload.base));
770}774}
771775
776pub fn arrayType2(self: *Module, len: u32, elem_ty_ref: TypeConstantCache.Ref) !TypeConstantCache.Ref {
777 const len_ty_ref = try self.resolve(.{ .int_type = .{
778 .signedness = .unsigned,
779 .bits = 32,
780 } });
781 const len_ref = try self.resolve(.{ .int = .{
782 .ty = len_ty_ref,
783 .value = .{ .uint64 = len },
784 } });
785 return try self.resolve(.{ .array_type = .{
786 .element_type = elem_ty_ref,
787 .length = len_ref,
788 } });
789}
790
772pub fn arrayType(self: *Module, len: u32, ty: Type.Ref) !Type.Ref {791pub fn arrayType(self: *Module, len: u32, ty: Type.Ref) !Type.Ref {
773 const payload = try self.arena.create(Type.Payload.Array);792 const payload = try self.arena.create(Type.Payload.Array);
774 payload.* = .{793 payload.* = .{
src/codegen/spirv/TypeConstantCache.zig+8-8
...@@ -263,7 +263,7 @@ pub const Key = union(enum) {...@@ -263,7 +263,7 @@ pub const Key = union(enum) {
263 pub const StructType = struct {263 pub const StructType = struct {
264 // TODO: Decorations.264 // TODO: Decorations.
265 /// The name of the structure. Can be `.none`.265 /// The name of the structure. Can be `.none`.
266 name: String,266 name: String = .none,
267 /// The type of each member.267 /// The type of each member.
268 member_types: []const Ref,268 member_types: []const Ref,
269 /// Name for each member. May be omitted.269 /// Name for each member. May be omitted.
...@@ -922,14 +922,14 @@ pub const String = enum(u32) {...@@ -922,14 +922,14 @@ pub const String = enum(u32) {
922 self: *const Self,922 self: *const Self,
923923
924 pub fn eql(ctx: @This(), a: []const u8, _: void, b_index: usize) bool {924 pub fn eql(ctx: @This(), a: []const u8, _: void, b_index: usize) bool {
925 const offset = ctx.self.string_map.values()[b_index];925 const offset = ctx.self.strings.values()[b_index];
926 const b = std.mem.sliceTo(ctx.self.string_bytes.items[offset..], 0);926 const b = std.mem.sliceTo(ctx.self.string_bytes.items[offset..], 0);
927 return std.mem.eql(u8, a, b);927 return std.mem.eql(u8, a, b);
928 }928 }
929929
930 pub fn hash(ctx: @This(), a: []const u8) u32 {930 pub fn hash(ctx: @This(), a: []const u8) u32 {
931 _ = ctx;931 _ = ctx;
932 const hasher = std.hash.Wyhash.init(0);932 var hasher = std.hash.Wyhash.init(0);
933 hasher.update(a);933 hasher.update(a);
934 return @truncate(u32, hasher.final());934 return @truncate(u32, hasher.final());
935 }935 }
...@@ -937,16 +937,16 @@ pub const String = enum(u32) {...@@ -937,16 +937,16 @@ pub const String = enum(u32) {
937};937};
938938
939/// Add a string to the cache. Must not contain any 0 values.939/// Add a string to the cache. Must not contain any 0 values.
940pub fn addString(self: *Self, spv: *Module, str: []const u8) String {940pub fn addString(self: *Self, spv: *Module, str: []const u8) !String {
941 assert(std.mem.indexOfScalar(u8, str, 0) == null);941 assert(std.mem.indexOfScalar(u8, str, 0) == null);
942 const adapter = String.Adapter{ .self = self };942 const adapter = String.Adapter{ .self = self };
943 const entry = try self.strings.getOrPutAdapted(spv.gpa, str, adapter);943 const entry = try self.strings.getOrPutAdapted(spv.gpa, str, adapter);
944 if (!entry.found_existing) {944 if (!entry.found_existing) {
945 const offset = self.string_bytes.items.len;945 const offset = self.string_bytes.items.len;
946 try self.string_bytes.ensureUnusedCapacity(1 + str.len);946 try self.string_bytes.ensureUnusedCapacity(spv.gpa, 1 + str.len);
947 self.string_bytes.appendAssumeCapacity(str);947 self.string_bytes.appendSliceAssumeCapacity(str);
948 self.string_bytes.append(0);948 self.string_bytes.appendAssumeCapacity(0);
949 entry.value_ptr.* = offset;949 entry.value_ptr.* = @intCast(u32, offset);
950 }950 }
951951
952 return @intToEnum(String, entry.index);952 return @intToEnum(String, entry.index);