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;
2222const StorageClass = spec.StorageClass;
2323
2424const SpvModule = @import("spirv/Module.zig");
25const SpvRef = SpvModule.TypeConstantCache.Ref;
25const SpvCacheRef = SpvModule.TypeConstantCache.Ref;
26const SpvCacheString = SpvModule.TypeConstantCache.String;
2627
2728const SpvSection = @import("spirv/Section.zig");
2829const SpvType = @import("spirv/type.zig").Type;
......@@ -1160,7 +1161,7 @@ pub const DeclGen = struct {
11601161 return try self.spv.resolveType(try SpvType.int(self.spv.arena, signedness, backing_bits));
11611162 }
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 {
11641165 const backing_bits = self.backingIntBits(bits) orelse {
11651166 // TODO: Integers too big for any native type are represented as "composite integers":
11661167 // An array of largestSupportedIntBits.
......@@ -1177,7 +1178,7 @@ pub const DeclGen = struct {
11771178 return try self.intType(.unsigned, self.getTarget().ptrBitWidth());
11781179 }
11791180
1180 fn sizeType2(self: *DeclGen) !SpvRef {
1181 fn sizeType2(self: *DeclGen) !SpvCacheRef {
11811182 return try self.intType2(.unsigned, self.getTarget().ptrBitWidth());
11821183 }
11831184
......@@ -1256,7 +1257,91 @@ pub const DeclGen = struct {
12561257 return try self.spv.simpleStructType(members.slice());
12571258 }
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 {
12601345 const target = self.getTarget();
12611346 switch (ty.zigTypeTag()) {
12621347 .Void, .NoReturn => return try self.spv.resolve(.void_type),
......@@ -1297,15 +1382,7 @@ pub const DeclGen = struct {
12971382 const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse {
12981383 return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()});
12991384 };
1300 const len_ty_ref = try self.intType2(.unsigned, 32);
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 } });
1385 return self.spv.arrayType2(total_len, elem_ty_ref);
13091386 },
13101387 .Fn => switch (repr) {
13111388 .direct => {
......@@ -1313,7 +1390,7 @@ pub const DeclGen = struct {
13131390 if (ty.fnIsVarArgs())
13141391 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());
13171394 defer self.gpa.free(param_ty_refs);
13181395 for (param_ty_refs, 0..) |*param_type, i| {
13191396 param_type.* = try self.resolveType2(ty.fnParamType(i), .direct);
......@@ -1360,8 +1437,116 @@ pub const DeclGen = struct {
13601437 .component_count = @intCast(u32, ty.vectorLen()),
13611438 } });
13621439 },
1440 .Struct => {
1441 if (ty.isSimpleTupleOrAnonStruct()) {
1442 unreachable; // TODO
1443 }
13631444
1364 else => unreachable, // TODO
1445 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}),
13651550 }
13661551 }
13671552
src/codegen/spirv/Module.zig+19
......@@ -235,6 +235,10 @@ pub fn resolveId(self: *Module, key: TypeConstantCache.Key) !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);
240}
241
238242fn orderGlobalsInto(
239243 self: *Module,
240244 decl_index: Decl.Index,
......@@ -769,6 +773,21 @@ pub fn simpleStructType(self: *Module, members: []const Type.Payload.Struct.Memb
769773 return try self.resolveType(Type.initPayload(&payload.base));
770774}
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
772791pub fn arrayType(self: *Module, len: u32, ty: Type.Ref) !Type.Ref {
773792 const payload = try self.arena.create(Type.Payload.Array);
774793 payload.* = .{
src/codegen/spirv/TypeConstantCache.zig+8-8
......@@ -263,7 +263,7 @@ pub const Key = union(enum) {
263263 pub const StructType = struct {
264264 // TODO: Decorations.
265265 /// The name of the structure. Can be `.none`.
266 name: String,
266 name: String = .none,
267267 /// The type of each member.
268268 member_types: []const Ref,
269269 /// Name for each member. May be omitted.
......@@ -922,14 +922,14 @@ pub const String = enum(u32) {
922922 self: *const Self,
923923
924924 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];
926926 const b = std.mem.sliceTo(ctx.self.string_bytes.items[offset..], 0);
927927 return std.mem.eql(u8, a, b);
928928 }
929929
930930 pub fn hash(ctx: @This(), a: []const u8) u32 {
931931 _ = ctx;
932 const hasher = std.hash.Wyhash.init(0);
932 var hasher = std.hash.Wyhash.init(0);
933933 hasher.update(a);
934934 return @truncate(u32, hasher.final());
935935 }
......@@ -937,16 +937,16 @@ pub const String = enum(u32) {
937937};
938938
939939/// 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 {
941941 assert(std.mem.indexOfScalar(u8, str, 0) == null);
942942 const adapter = String.Adapter{ .self = self };
943943 const entry = try self.strings.getOrPutAdapted(spv.gpa, str, adapter);
944944 if (!entry.found_existing) {
945945 const offset = self.string_bytes.items.len;
946 try self.string_bytes.ensureUnusedCapacity(1 + str.len);
947 self.string_bytes.appendAssumeCapacity(str);
948 self.string_bytes.append(0);
949 entry.value_ptr.* = offset;
946 try self.string_bytes.ensureUnusedCapacity(spv.gpa, 1 + str.len);
947 self.string_bytes.appendSliceAssumeCapacity(str);
948 self.string_bytes.appendAssumeCapacity(0);
949 entry.value_ptr.* = @intCast(u32, offset);
950950 }
951951
952952 return @intToEnum(String, entry.index);