authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-05-01 20:17:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-10 20:40:03-07:00
logc7e84ddb722df0403dd6ae8283820c02efc77e50
tree2911e2d4cdae1e311709feb2f05a91698c95ab42
parentcac60a05a7c253e197b26b7d707103feefb850f9

InternPool: flesh out some of the implementations

* hashing * equality * encoding

1 files changed, 358 insertions(+), 37 deletions(-)

src/InternPool.zig+358-37
......@@ -3,6 +3,9 @@
33map: std.AutoArrayHashMapUnmanaged(void, void) = .{},
44items: std.MultiArrayList(Item) = .{},
55extra: std.ArrayListUnmanaged(u32) = .{},
6/// On 32-bit systems, this array is ignored and extra is used for everything.
7/// On 64-bit systems, this array is used for big integers and associated metadata.
8limbs: std.ArrayListUnmanaged(u64) = .{},
69
710const std = @import("std");
811const Allocator = std.mem.Allocator;
......@@ -91,19 +94,43 @@ pub const Key = union(enum) {
9194 }
9295
9396 pub fn hashWithHasher(key: Key, hasher: *std.hash.Wyhash) void {
97 const KeyTag = @typeInfo(Key).Union.tag_type.?;
98 const key_tag: KeyTag = key;
99 std.hash.autoHash(hasher, key_tag);
94100 switch (key) {
95 .int_type => |int_type| {
96 std.hash.autoHash(hasher, int_type);
101 inline .int_type,
102 .ptr_type,
103 .array_type,
104 .vector_type,
105 .optional_type,
106 .error_union_type,
107 .simple_type,
108 .simple_value,
109 .extern_func,
110 => |info| std.hash.autoHash(hasher, info),
111
112 .int => |int| {
113 std.hash.autoHash(hasher, int.ty);
114 std.hash.autoHash(hasher, int.big_int.positive);
115 for (int.big_int.limbs) |limb| std.hash.autoHash(hasher, limb);
97116 },
98 .array_type => |array_type| {
99 std.hash.autoHash(hasher, array_type);
117
118 .enum_tag => |enum_tag| {
119 std.hash.autoHash(hasher, enum_tag.ty);
120 std.hash.autoHash(hasher, enum_tag.tag.positive);
121 for (enum_tag.tag.limbs) |limb| std.hash.autoHash(hasher, limb);
122 },
123
124 .struct_type => |struct_type| {
125 if (struct_type.fields_len != 0) {
126 @panic("TODO");
127 }
100128 },
101 else => @panic("TODO"),
102129 }
103130 }
104131
105132 pub fn eql(a: Key, b: Key) bool {
106 const KeyTag = std.meta.Tag(Key);
133 const KeyTag = @typeInfo(Key).Union.tag_type.?;
107134 const a_tag: KeyTag = a;
108135 const b_tag: KeyTag = b;
109136 if (a_tag != b_tag) return false;
......@@ -112,11 +139,62 @@ pub const Key = union(enum) {
112139 const b_info = b.int_type;
113140 return std.meta.eql(a_info, b_info);
114141 },
142 .ptr_type => |a_info| {
143 const b_info = b.ptr_type;
144 return std.meta.eql(a_info, b_info);
145 },
115146 .array_type => |a_info| {
116147 const b_info = b.array_type;
117148 return std.meta.eql(a_info, b_info);
118149 },
119 else => @panic("TODO"),
150 .vector_type => |a_info| {
151 const b_info = b.vector_type;
152 return std.meta.eql(a_info, b_info);
153 },
154 .optional_type => |a_info| {
155 const b_info = b.optional_type;
156 return std.meta.eql(a_info, b_info);
157 },
158 .error_union_type => |a_info| {
159 const b_info = b.error_union_type;
160 return std.meta.eql(a_info, b_info);
161 },
162 .simple_type => |a_info| {
163 const b_info = b.simple_type;
164 return a_info == b_info;
165 },
166 .simple_value => |a_info| {
167 const b_info = b.simple_value;
168 return a_info == b_info;
169 },
170 .extern_func => |a_info| {
171 const b_info = b.extern_func;
172 return std.meta.eql(a_info, b_info);
173 },
174
175 .int => |a_info| {
176 const b_info = b.int;
177 _ = a_info;
178 _ = b_info;
179 @panic("TODO");
180 },
181
182 .enum_tag => |a_info| {
183 const b_info = b.enum_tag;
184 _ = a_info;
185 _ = b_info;
186 @panic("TODO");
187 },
188
189 .struct_type => |a_info| {
190 const b_info = b.struct_type;
191
192 // TODO: remove this special case for empty_struct
193 if (a_info.fields_len == 0 and b_info.fields_len == 0)
194 return true;
195
196 @panic("TODO");
197 },
120198 }
121199 }
122200
......@@ -491,27 +569,65 @@ pub const Tag = enum(u8) {
491569 /// An integer type.
492570 /// data is number of bits
493571 type_int_unsigned,
494 /// An array type.
495 /// data is payload to Array.
572 /// An array type that has no sentinel and whose length fits in 32 bits.
573 /// data is payload to Vector.
496574 type_array,
575 /// A vector type.
576 /// data is payload to Vector.
577 type_vector,
578 /// A pointer type along with all its bells and whistles.
579 /// data is payload to Pointer.
580 type_pointer,
581 /// An optional type.
582 /// data is the child type.
583 type_optional,
584 /// An error union type.
585 /// data is payload to ErrorUnion.
586 type_error_union,
587 /// Represents the data that an enum declaration provides, when the fields
588 /// are auto-numbered, and there are no declarations.
589 /// data is payload index to `EnumSimple`.
590 type_enum_simple,
591
497592 /// A type that can be represented with only an enum tag.
498593 /// data is SimpleType enum value.
499594 simple_type,
500595 /// A value that can be represented with only an enum tag.
501596 /// data is SimpleValue enum value.
502597 simple_value,
503 /// An unsigned integer value that can be represented by u32.
598 /// The SimpleType and SimpleValue enums are exposed via the InternPool API using
599 /// SimpleType and SimpleValue as the Key data themselves.
600 /// This tag is for miscellaneous types and values that can be represented with
601 /// only an enum tag, but will be presented via the API with a different Key.
602 /// data is SimpleInternal enum value.
603 simple_internal,
604 /// Type: u32
504605 /// data is integer value
505 int_u32,
506 /// An unsigned integer value that can be represented by i32.
606 int_small_u32,
607 /// Type: i32
507608 /// data is integer value bitcasted to u32.
508 int_i32,
509 /// A positive integer value that does not fit in 32 bits.
510 /// data is a extra index to BigInt.
511 int_big_positive,
512 /// A negative integer value that does not fit in 32 bits.
513 /// data is a extra index to BigInt.
514 int_big_negative,
609 int_small_i32,
610 /// A usize that fits in 32 bits.
611 /// data is integer value.
612 int_small_usize,
613 /// A comptime_int that fits in a u32.
614 /// data is integer value.
615 int_small_comptime_unsigned,
616 /// A comptime_int that fits in an i32.
617 /// data is integer value bitcasted to u32.
618 int_small_comptime_signed,
619 /// A positive integer value.
620 /// data is a limbs index to Int.
621 int_positive,
622 /// A negative integer value.
623 /// data is a limbs index to Int.
624 int_negative,
625 /// An enum tag identified by a positive integer value.
626 /// data is a limbs index to Int.
627 enum_tag_positive,
628 /// An enum tag identified by a negative integer value.
629 /// data is a limbs index to Int.
630 enum_tag_negative,
515631 /// A float value that can be represented by f32.
516632 /// data is float value bitcasted to u32.
517633 float_f32,
......@@ -525,10 +641,6 @@ pub const Tag = enum(u8) {
525641 extern_func,
526642 /// A regular function.
527643 func,
528 /// Represents the data that an enum declaration provides, when the fields
529 /// are auto-numbered, and there are no declarations.
530 /// data is payload index to `EnumSimple`.
531 enum_simple,
532644};
533645
534646/// Having `SimpleType` and `SimpleValue` in separate enums makes it easier to
......@@ -593,11 +705,43 @@ pub const SimpleValue = enum(u32) {
593705 generic_poison,
594706};
595707
596pub const Array = struct {
708pub const SimpleInternal = enum(u32) {
709 /// This is the empty struct type. Note that empty_struct value is exposed
710 /// via SimpleValue.
711 type_empty_struct,
712};
713
714pub const Pointer = struct {
715 child: Index,
716 sentinel: Index,
717 flags: Flags,
718
719 pub const Flags = packed struct(u32) {
720 alignment: u16,
721 is_const: bool,
722 is_volatile: bool,
723 is_allowzero: bool,
724 size: Size,
725 address_space: AddressSpace,
726 _: u7 = undefined,
727 };
728
729 pub const Size = std.builtin.Type.Pointer.Size;
730 pub const AddressSpace = std.builtin.AddressSpace;
731};
732
733/// Used for non-sentineled arrays that have length fitting in u32, as well as
734/// vectors.
735pub const Vector = struct {
597736 len: u32,
598737 child: Index,
599738};
600739
740pub const ErrorUnion = struct {
741 error_set_type: Index,
742 payload_type: Index,
743};
744
601745/// Trailing:
602746/// 0. field name: null-terminated string index for each fields_len; declaration order
603747pub const EnumSimple = struct {
......@@ -612,6 +756,12 @@ pub const EnumSimple = struct {
612756 fields_len: u32,
613757};
614758
759/// Trailing: Limb for every limbs_len
760pub const Int = struct {
761 ty: Index,
762 limbs_len: u32,
763};
764
615765pub fn init(ip: *InternPool, gpa: Allocator) !void {
616766 assert(ip.items.len == 0);
617767
......@@ -619,6 +769,7 @@ pub fn init(ip: *InternPool, gpa: Allocator) !void {
619769 try ip.items.ensureUnusedCapacity(gpa, static_keys.len);
620770 try ip.map.ensureUnusedCapacity(gpa, static_keys.len);
621771 try ip.extra.ensureUnusedCapacity(gpa, static_keys.len);
772 try ip.limbs.ensureUnusedCapacity(gpa, 2);
622773
623774 // This inserts all the statically-known values into the intern pool in the
624775 // order expected.
......@@ -635,6 +786,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
635786 ip.map.deinit(gpa);
636787 ip.items.deinit(gpa);
637788 ip.extra.deinit(gpa);
789 ip.limbs.deinit(gpa);
638790 ip.* = undefined;
639791}
640792
......@@ -655,7 +807,7 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
655807 },
656808 },
657809 .type_array => {
658 const array_info = ip.extraData(Array, data);
810 const array_info = ip.extraData(Vector, data);
659811 return .{ .array_type = .{
660812 .len = array_info.len,
661813 .child = array_info.child,
......@@ -675,58 +827,226 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
675827 if (gop.found_existing) {
676828 return @intToEnum(Index, gop.index);
677829 }
830 try ip.items.ensureUnusedCapacity(gpa, 1);
678831 switch (key) {
679832 .int_type => |int_type| {
680833 const t: Tag = switch (int_type.signedness) {
681834 .signed => .type_int_signed,
682835 .unsigned => .type_int_unsigned,
683836 };
684 try ip.items.append(gpa, .{
837 ip.items.appendAssumeCapacity(.{
685838 .tag = t,
686839 .data = int_type.bits,
687840 });
688841 },
842 .ptr_type => |ptr_type| {
843 // TODO introduce more pointer encodings
844 ip.items.appendAssumeCapacity(.{
845 .tag = .type_pointer,
846 .data = try ip.addExtra(gpa, Pointer{
847 .child = ptr_type.elem_type,
848 .sentinel = ptr_type.sentinel,
849 .flags = .{
850 .alignment = ptr_type.alignment,
851 .is_const = ptr_type.is_const,
852 .is_volatile = ptr_type.is_volatile,
853 .is_allowzero = ptr_type.is_allowzero,
854 .size = ptr_type.size,
855 .address_space = ptr_type.address_space,
856 },
857 }),
858 });
859 },
689860 .array_type => |array_type| {
690861 const len = @intCast(u32, array_type.len); // TODO have a big_array encoding
691862 assert(array_type.sentinel == .none); // TODO have a sentinel_array encoding
692 try ip.items.append(gpa, .{
863 ip.items.appendAssumeCapacity(.{
693864 .tag = .type_array,
694 .data = try ip.addExtra(gpa, Array{
865 .data = try ip.addExtra(gpa, Vector{
695866 .len = len,
696867 .child = array_type.child,
697868 }),
698869 });
699870 },
700 else => @panic("TODO"),
871 .vector_type => |vector_type| {
872 ip.items.appendAssumeCapacity(.{
873 .tag = .type_vector,
874 .data = try ip.addExtra(gpa, Vector{
875 .len = vector_type.len,
876 .child = vector_type.child,
877 }),
878 });
879 },
880 .optional_type => |optional_type| {
881 ip.items.appendAssumeCapacity(.{
882 .tag = .type_optional,
883 .data = @enumToInt(optional_type.payload_type),
884 });
885 },
886 .error_union_type => |error_union_type| {
887 ip.items.appendAssumeCapacity(.{
888 .tag = .type_error_union,
889 .data = try ip.addExtra(gpa, ErrorUnion{
890 .error_set_type = error_union_type.error_set_type,
891 .payload_type = error_union_type.payload_type,
892 }),
893 });
894 },
895 .simple_type => |simple_type| {
896 ip.items.appendAssumeCapacity(.{
897 .tag = .simple_type,
898 .data = @enumToInt(simple_type),
899 });
900 },
901 .simple_value => |simple_value| {
902 ip.items.appendAssumeCapacity(.{
903 .tag = .simple_value,
904 .data = @enumToInt(simple_value),
905 });
906 },
907 .extern_func => @panic("TODO"),
908
909 .int => |int| b: {
910 switch (int.ty) {
911 .u32_type => {
912 if (int.big_int.fits(u32)) {
913 ip.items.appendAssumeCapacity(.{
914 .tag = .int_small_u32,
915 .data = int.big_int.to(u32) catch unreachable,
916 });
917 break :b;
918 }
919 },
920 .i32_type => {
921 if (int.big_int.fits(i32)) {
922 ip.items.appendAssumeCapacity(.{
923 .tag = .int_small_i32,
924 .data = @bitCast(u32, int.big_int.to(i32) catch unreachable),
925 });
926 break :b;
927 }
928 },
929 .usize_type => {
930 if (int.big_int.fits(u32)) {
931 ip.items.appendAssumeCapacity(.{
932 .tag = .int_small_usize,
933 .data = int.big_int.to(u32) catch unreachable,
934 });
935 break :b;
936 }
937 },
938 .comptime_int_type => {
939 if (int.big_int.fits(u32)) {
940 ip.items.appendAssumeCapacity(.{
941 .tag = .int_small_comptime_unsigned,
942 .data = int.big_int.to(u32) catch unreachable,
943 });
944 break :b;
945 }
946 if (int.big_int.fits(i32)) {
947 ip.items.appendAssumeCapacity(.{
948 .tag = .int_small_comptime_signed,
949 .data = @bitCast(u32, int.big_int.to(i32) catch unreachable),
950 });
951 break :b;
952 }
953 },
954 else => {},
955 }
956
957 const tag: Tag = if (int.big_int.positive) .int_positive else .int_negative;
958 try addInt(ip, gpa, int.ty, tag, int.big_int.limbs);
959 },
960
961 .enum_tag => |enum_tag| {
962 const tag: Tag = if (enum_tag.tag.positive) .enum_tag_positive else .enum_tag_negative;
963 try addInt(ip, gpa, enum_tag.ty, tag, enum_tag.tag.limbs);
964 },
965
966 .struct_type => |struct_type| {
967 if (struct_type.fields_len != 0) {
968 @panic("TODO"); // handle structs other than empty_struct
969 }
970 ip.items.appendAssumeCapacity(.{
971 .tag = .simple_internal,
972 .data = @enumToInt(SimpleInternal.type_empty_struct),
973 });
974 },
701975 }
702976 return @intToEnum(Index, ip.items.len - 1);
703977}
704978
705pub fn tag(ip: InternPool, index: Index) Tag {
706 const tags = ip.items.items(.tag);
707 return tags[@enumToInt(index)];
979fn addInt(ip: *InternPool, gpa: Allocator, ty: Index, tag: Tag, limbs: []const usize) !void {
980 const limbs_len = @intCast(u32, limbs.len);
981 try ip.reserveLimbs(gpa, @typeInfo(Int).Struct.fields.len + limbs_len);
982 ip.items.appendAssumeCapacity(.{
983 .tag = tag,
984 .data = ip.addLimbsExtraAssumeCapacity(Int{
985 .ty = ty,
986 .limbs_len = limbs_len,
987 }),
988 });
989 ip.addLimbsAssumeCapacity(limbs);
708990}
709991
710992fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 {
711 const fields = std.meta.fields(@TypeOf(extra));
993 const fields = @typeInfo(@TypeOf(extra)).Struct.fields;
712994 try ip.extra.ensureUnusedCapacity(gpa, fields.len);
713995 return ip.addExtraAssumeCapacity(extra);
714996}
715997
716998fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
717 const fields = std.meta.fields(@TypeOf(extra));
718999 const result = @intCast(u32, ip.extra.items.len);
719 inline for (fields) |field| {
1000 inline for (@typeInfo(@TypeOf(extra)).Struct.fields) |field| {
7201001 ip.extra.appendAssumeCapacity(switch (field.type) {
7211002 u32 => @field(extra, field.name),
7221003 Index => @enumToInt(@field(extra, field.name)),
7231004 i32 => @bitCast(u32, @field(extra, field.name)),
724 else => @compileError("bad field type"),
1005 Pointer.Flags => @bitCast(u32, @field(extra, field.name)),
1006 else => @compileError("bad field type: " ++ @typeName(field.type)),
7251007 });
7261008 }
7271009 return result;
7281010}
7291011
1012fn reserveLimbs(ip: *InternPool, gpa: Allocator, n: usize) !void {
1013 switch (@sizeOf(usize)) {
1014 @sizeOf(u32) => try ip.extra.ensureUnusedCapacity(gpa, n),
1015 @sizeOf(u64) => try ip.limbs.ensureUnusedCapacity(gpa, n),
1016 else => @compileError("unsupported host"),
1017 }
1018}
1019
1020fn addLimbsExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
1021 switch (@sizeOf(usize)) {
1022 @sizeOf(u32) => return addExtraAssumeCapacity(ip, extra),
1023 @sizeOf(u64) => {},
1024 else => @compileError("unsupported host"),
1025 }
1026 const result = @intCast(u32, ip.extra.items.len);
1027 inline for (@typeInfo(@TypeOf(extra)).Struct.fields, 0..) |field, i| {
1028 const new: u32 = switch (field.type) {
1029 u32 => @field(extra, field.name),
1030 Index => @enumToInt(@field(extra, field.name)),
1031 else => @compileError("bad field type: " ++ @typeName(field.type)),
1032 };
1033 if (i % 2 == 0) {
1034 ip.limbs.appendAssumeCapacity(new);
1035 } else {
1036 ip.limbs.items[ip.limbs.items.len - 1] |= @as(u64, new) << 32;
1037 }
1038 }
1039 return result;
1040}
1041
1042fn addLimbsAssumeCapacity(ip: *InternPool, limbs: []const usize) void {
1043 switch (@sizeOf(usize)) {
1044 @sizeOf(u32) => ip.extra.appendSliceAssumeCapacity(limbs),
1045 @sizeOf(u64) => ip.limbs.appendSliceAssumeCapacity(limbs),
1046 else => @compileError("unsupported host"),
1047 }
1048}
1049
7301050fn extraData(ip: InternPool, comptime T: type, index: usize) T {
7311051 const fields = std.meta.fields(T);
7321052 var i: usize = index;
......@@ -736,7 +1056,8 @@ fn extraData(ip: InternPool, comptime T: type, index: usize) T {
7361056 u32 => ip.extra.items[i],
7371057 Index => @intToEnum(Index, ip.extra.items[i]),
7381058 i32 => @bitCast(i32, ip.extra.items[i]),
739 else => @compileError("bad field type"),
1059 Pointer.Flags => @bitCast(Pointer.Flags, ip.extra.items[i]),
1060 else => @compileError("bad field type: " ++ @typeName(field.type)),
7401061 };
7411062 i += 1;
7421063 }