| ... | ... | @@ -28,6 +28,9 @@ map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, |
| 28 | 28 | items: std.MultiArrayList(Item) = .{}, |
| 29 | 29 | extra: std.ArrayListUnmanaged(u32) = .{}, |
| 30 | 30 | |
| 31 | string_bytes: std.ArrayListUnmanaged(u8) = .{}, |
| 32 | strings: std.AutoArrayHashMapUnmanaged(void, u32) = .{}, |
| 33 | |
| 31 | 34 | const Item = struct { |
| 32 | 35 | tag: Tag, |
| 33 | 36 | /// The result-id that this item uses. |
| ... | ... | @@ -74,6 +77,10 @@ const Tag = enum { |
| 74 | 77 | /// Simple structure type that does not have any decorations. |
| 75 | 78 | /// data is payload to SimpleStructType |
| 76 | 79 | type_struct_simple, |
| 80 | /// Simple structure type that does not have any decorations, but does |
| 81 | /// have member names trailing. |
| 82 | /// data is payload to SimpleStructType |
| 83 | type_struct_simple_with_member_names, |
| 77 | 84 | |
| 78 | 85 | // -- Values |
| 79 | 86 | /// Value of type u8 |
| ... | ... | @@ -124,7 +131,10 @@ const Tag = enum { |
| 124 | 131 | |
| 125 | 132 | /// Trailing: |
| 126 | 133 | /// - [members_len]Ref: Member types. |
| 134 | /// - [members_len]String: Member names, -- ONLY if the tag is type_struct_simple_with_member_names |
| 127 | 135 | const SimpleStructType = struct { |
| 136 | /// (optional) The name of the struct. |
| 137 | name: String, |
| 128 | 138 | /// Number of members that this struct has. |
| 129 | 139 | members_len: u32, |
| 130 | 140 | }; |
| ... | ... | @@ -252,8 +262,16 @@ pub const Key = union(enum) { |
| 252 | 262 | |
| 253 | 263 | pub const StructType = struct { |
| 254 | 264 | // TODO: Decorations. |
| 265 | /// The name of the structure. Can be `.none`. |
| 266 | name: String, |
| 255 | 267 | /// The type of each member. |
| 256 | 268 | member_types: []const Ref, |
| 269 | /// Name for each member. May be omitted. |
| 270 | member_names: ?[]const String = null, |
| 271 | |
| 272 | fn memberNames(self: @This()) []const String { |
| 273 | return if (self.member_names) |member_names| member_names else &.{}; |
| 274 | } |
| 257 | 275 | }; |
| 258 | 276 | |
| 259 | 277 | pub const Int = struct { |
| ... | ... | @@ -323,9 +341,13 @@ pub const Key = union(enum) { |
| 323 | 341 | } |
| 324 | 342 | }, |
| 325 | 343 | .struct_type => |struct_type| { |
| 344 | std.hash.autoHash(&hasher, struct_type.name); |
| 326 | 345 | for (struct_type.member_types) |member_type| { |
| 327 | 346 | std.hash.autoHash(&hasher, member_type); |
| 328 | 347 | } |
| 348 | for (struct_type.memberNames()) |member_name| { |
| 349 | std.hash.autoHash(&hasher, member_name); |
| 350 | } |
| 329 | 351 | }, |
| 330 | 352 | inline else => |key| std.hash.autoHash(&hasher, key), |
| 331 | 353 | } |
| ... | ... | @@ -347,7 +369,9 @@ pub const Key = union(enum) { |
| 347 | 369 | }, |
| 348 | 370 | .struct_type => |a_struct| { |
| 349 | 371 | const b_struct = b.struct_type; |
| 350 | | return std.mem.eql(Ref, a_struct.member_types, b_struct.member_types); |
| 372 | return a_struct.name == b_struct.name and |
| 373 | std.mem.eql(Ref, a_struct.member_types, b_struct.member_types) and |
| 374 | std.mem.eql(String, a_struct.memberNames(), b_struct.memberNames()); |
| 351 | 375 | }, |
| 352 | 376 | // TODO: Unroll? |
| 353 | 377 | else => std.meta.eql(a, b), |
| ... | ... | @@ -381,6 +405,8 @@ pub fn deinit(self: *Self, spv: *const Module) void { |
| 381 | 405 | self.map.deinit(spv.gpa); |
| 382 | 406 | self.items.deinit(spv.gpa); |
| 383 | 407 | self.extra.deinit(spv.gpa); |
| 408 | self.string_bytes.deinit(spv.gpa); |
| 409 | self.strings.deinit(spv.gpa); |
| 384 | 410 | } |
| 385 | 411 | |
| 386 | 412 | /// Actually materialize the database into spir-v instructions. |
| ... | ... | @@ -475,6 +501,14 @@ fn emit( |
| 475 | 501 | for (struct_type.member_types) |member_type| { |
| 476 | 502 | section.writeOperand(IdResult, self.resultId(member_type)); |
| 477 | 503 | } |
| 504 | if (self.getString(struct_type.name)) |name| { |
| 505 | try spv.debugName(result_id, "{s}", .{name}); |
| 506 | } |
| 507 | for (struct_type.memberNames(), 0..) |member_name, i| { |
| 508 | if (self.getString(member_name)) |name| { |
| 509 | try spv.memberDebugName(result_id, @intCast(u32, i), "{s}", .{name}); |
| 510 | } |
| 511 | } |
| 478 | 512 | // TODO: Decorations? |
| 479 | 513 | }, |
| 480 | 514 | .int => |int| { |
| ... | ... | @@ -589,15 +623,25 @@ pub fn resolve(self: *Self, spv: *Module, key: Key) !Ref { |
| 589 | 623 | }, |
| 590 | 624 | .struct_type => |struct_type| blk: { |
| 591 | 625 | const extra = try self.addExtra(spv, Tag.SimpleStructType{ |
| 626 | .name = struct_type.name, |
| 592 | 627 | .members_len = @intCast(u32, struct_type.member_types.len), |
| 593 | 628 | }); |
| 594 | 629 | try self.extra.appendSlice(spv.gpa, @ptrCast([]const u32, struct_type.member_types)); |
| 595 | 630 | |
| 596 | | break :blk Item{ |
| 597 | | .tag = .type_struct_simple, |
| 598 | | .result_id = result_id, |
| 599 | | .data = extra, |
| 600 | | }; |
| 631 | if (struct_type.member_names) |member_names| { |
| 632 | try self.extra.appendSlice(spv.gpa, @ptrCast([]const u32, member_names)); |
| 633 | break :blk Item{ |
| 634 | .tag = .type_struct_simple_with_member_names, |
| 635 | .result_id = result_id, |
| 636 | .data = extra, |
| 637 | }; |
| 638 | } else { |
| 639 | break :blk Item{ |
| 640 | .tag = .type_struct_simple, |
| 641 | .result_id = result_id, |
| 642 | .data = extra, |
| 643 | }; |
| 644 | } |
| 601 | 645 | }, |
| 602 | 646 | .int => |int| blk: { |
| 603 | 647 | const int_type = self.lookup(int.ty).int_type; |
| ... | ... | @@ -739,7 +783,22 @@ pub fn lookup(self: *const Self, ref: Ref) Key { |
| 739 | 783 | const member_types = @ptrCast([]const Ref, self.extra.items[payload.trail..][0..payload.data.members_len]); |
| 740 | 784 | return .{ |
| 741 | 785 | .struct_type = .{ |
| 786 | .name = payload.data.name, |
| 742 | 787 | .member_types = member_types, |
| 788 | .member_names = null, |
| 789 | }, |
| 790 | }; |
| 791 | }, |
| 792 | .type_struct_simple_with_member_names => { |
| 793 | const payload = self.extraDataTrail(Tag.SimpleStructType, data); |
| 794 | const trailing = self.extra.items[payload.trail..]; |
| 795 | const member_types = @ptrCast([]const Ref, trailing[0..payload.data.members_len]); |
| 796 | const member_names = @ptrCast([]const String, trailing[payload.data.members_len..][0..payload.data.members_len]); |
| 797 | return .{ |
| 798 | .struct_type = .{ |
| 799 | .name = payload.data.name, |
| 800 | .member_types = member_types, |
| 801 | .member_names = member_names, |
| 743 | 802 | }, |
| 744 | 803 | }; |
| 745 | 804 | }, |
| ... | ... | @@ -822,6 +881,7 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) !u32 { |
| 822 | 881 | i32 => @bitCast(u32, field_val), |
| 823 | 882 | Ref => @enumToInt(field_val), |
| 824 | 883 | StorageClass => @enumToInt(field_val), |
| 884 | String => @enumToInt(field_val), |
| 825 | 885 | else => @compileError("Invalid type: " ++ @typeName(field.type)), |
| 826 | 886 | }; |
| 827 | 887 | self.extra.appendAssumeCapacity(word); |
| ... | ... | @@ -843,6 +903,7 @@ fn extraDataTrail(self: Self, comptime T: type, offset: u32) struct { data: T, t |
| 843 | 903 | i32 => @bitCast(i32, word), |
| 844 | 904 | Ref => @intToEnum(Ref, word), |
| 845 | 905 | StorageClass => @intToEnum(StorageClass, word), |
| 906 | String => @intToEnum(String, word), |
| 846 | 907 | else => @compileError("Invalid type: " ++ @typeName(field.type)), |
| 847 | 908 | }; |
| 848 | 909 | } |
| ... | ... | @@ -851,3 +912,49 @@ fn extraDataTrail(self: Self, comptime T: type, offset: u32) struct { data: T, t |
| 851 | 912 | .trail = offset + @intCast(u32, fields.len), |
| 852 | 913 | }; |
| 853 | 914 | } |
| 915 | |
| 916 | /// Represents a reference to some null-terminated string. |
| 917 | pub const String = enum(u32) { |
| 918 | none = std.math.maxInt(u32), |
| 919 | _, |
| 920 | |
| 921 | pub const Adapter = struct { |
| 922 | self: *const Self, |
| 923 | |
| 924 | pub fn eql(ctx: @This(), a: []const u8, _: void, b_index: usize) bool { |
| 925 | const offset = ctx.self.string_map.values()[b_index]; |
| 926 | const b = std.mem.sliceTo(ctx.self.string_bytes.items[offset..], 0); |
| 927 | return std.mem.eql(u8, a, b); |
| 928 | } |
| 929 | |
| 930 | pub fn hash(ctx: @This(), a: []const u8) u32 { |
| 931 | _ = ctx; |
| 932 | const hasher = std.hash.Wyhash.init(0); |
| 933 | hasher.update(a); |
| 934 | return @truncate(u32, hasher.final()); |
| 935 | } |
| 936 | }; |
| 937 | }; |
| 938 | |
| 939 | /// Add a string to the cache. Must not contain any 0 values. |
| 940 | pub fn addString(self: *Self, spv: *Module, str: []const u8) String { |
| 941 | assert(std.mem.indexOfScalar(u8, str, 0) == null); |
| 942 | const adapter = String.Adapter{ .self = self }; |
| 943 | const entry = try self.strings.getOrPutAdapted(spv.gpa, str, adapter); |
| 944 | if (!entry.found_existing) { |
| 945 | 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; |
| 950 | } |
| 951 | |
| 952 | return @intToEnum(String, entry.index); |
| 953 | } |
| 954 | |
| 955 | pub fn getString(self: *const Self, ref: String) ?[]const u8 { |
| 956 | return switch (ref) { |
| 957 | .none => null, |
| 958 | else => std.mem.sliceTo(self.string_bytes.items[self.strings.values()[@enumToInt(ref)]..], 0), |
| 959 | }; |
| 960 | } |