refactor InternPool KeyAdapter

This commit is contained in:
Techatrix 2023-01-14 19:17:06 +01:00
parent 04d281340c
commit 1456bfa1c6
2 changed files with 191 additions and 196 deletions

View File

@ -705,7 +705,7 @@ pub fn interpret(
const value_namespace = interpreter.ip.indexToKey(value.val).getNamespace();
if (value_namespace == .none) return error.InvalidBuiltin;
const name = interpreter.ip.indexToKey(field_name.val).bytes.data; // TODO add checks
const name = interpreter.ip.indexToKey(field_name.val).bytes; // TODO add checks
const decls = interpreter.namespaces.items(.decls)[@enumToInt(value_namespace)];
const has_decl = decls.contains(name);
@ -764,7 +764,7 @@ pub fn interpret(
.interpreter = interpreter,
.node_idx = node_idx,
.ty = string_literal_type,
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = .{ .data = str } }), // TODO
.val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = str }), // TODO
};
// TODO: Add type casting, sentinel

View File

@ -10,26 +10,12 @@ const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const KeyAdapter = struct {
intern_pool: *const InternPool,
pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool {
_ = b_void;
return ctx.intern_pool.indexToKey(@intToEnum(Index, b_map_index)).eql(a);
}
pub fn hash(ctx: @This(), a: Key) u32 {
_ = ctx;
return a.hash();
}
};
pub const Int = struct {
pub const Int = packed struct {
signedness: std.builtin.Signedness,
bits: u16,
};
pub const Pointer = struct {
pub const Pointer = packed struct {
elem_type: Index,
sentinel: Index = .none,
alignment: u16 = 0,
@ -40,7 +26,7 @@ pub const Pointer = struct {
address_space: std.builtin.AddressSpace = .generic,
};
pub const Array = struct {
pub const Array = packed struct {
// TODO support big int
len: u32,
child: Index,
@ -62,19 +48,15 @@ pub const Struct = struct {
};
};
pub const Optional = struct {
pub const Optional = packed struct {
payload_type: Index,
};
pub const ErrorUnion = struct {
pub const ErrorUnion = packed struct {
error_set_type: Index,
payload_type: Index,
};
// pub const Error = struct {
// name: []const u8,
// };
pub const ErrorSet = struct {
/// must be sorted
names: []const []const u8,
@ -104,11 +86,11 @@ pub const Fn = struct {
return_type: Index,
args: []const Param,
pub const Param = struct {
pub const Param = packed struct {
arg_type: Index,
is_comptime: bool = false,
is_generic: bool = false,
is_noalias: bool = false,
arg_type: Index,
};
};
@ -127,31 +109,27 @@ pub const Union = struct {
pub const Tuple = struct {
types: []const Index,
/// unreachable_value elements are used to indicate runtime-known.
/// Index.none elements are used to indicate runtime-known.
values: []const Index,
};
pub const Vector = struct {
pub const Vector = packed struct {
// TODO support big int
len: u32,
child: Index,
};
pub const AnyFrame = struct {
pub const AnyFrame = packed struct {
child: Index,
};
pub const BigInt = std.math.big.int.Const;
pub const Bytes = struct {
data: []const u8,
};
pub const Bytes = []const u8;
pub const Aggregate = struct {
data: []const Index,
};
pub const Aggregate = []const Index;
pub const UnionValue = struct {
pub const UnionValue = packed struct {
tag: Index,
val: Index,
};
@ -165,14 +143,13 @@ pub const Key = union(enum) {
struct_type: Struct,
optional_type: Optional,
error_union_type: ErrorUnion,
// error_type: Error,
error_set_type: ErrorSet,
enum_type: Enum,
function_type: Fn,
union_type: Union,
tuple_type: Tuple,
vector_type: Vector,
anyframe_t_type: AnyFrame,
anyframe_type: AnyFrame,
int_u64_value: u64,
int_i64_value: i64,
@ -182,10 +159,8 @@ pub const Key = union(enum) {
float_64_value: f64,
float_80_value: f80,
float_128_value: f128,
// type_value: Index,
bytes: Bytes,
// one_pointer: Index,
aggregate: Aggregate,
union_value: UnionValue,
@ -193,98 +168,10 @@ pub const Key = union(enum) {
// error
// error union
pub fn hash(key: Key) u32 {
var hasher = std.hash.Wyhash.init(0);
std.hash.autoHash(&hasher, std.meta.activeTag(key));
switch (key) {
.float_16_value => |f| std.hash.autoHash(&hasher, @bitCast(u16, f)),
.float_32_value => |f| std.hash.autoHash(&hasher, @bitCast(u32, f)),
.float_64_value => |f| std.hash.autoHash(&hasher, @bitCast(u64, f)),
.float_80_value => |f| std.hash.autoHash(&hasher, @bitCast(u80, f)),
.float_128_value => |f| std.hash.autoHash(&hasher, @bitCast(u128, f)),
inline else => |info| std.hash.autoHashStrat(&hasher, info, .Deep), // TODO sad stage1 noises :(
}
return @truncate(u32, hasher.final());
}
pub fn eql(a: Key, b: Key) bool {
const KeyTag = std.meta.Tag(Key);
const a_tag: KeyTag = a;
const b_tag: KeyTag = b;
if (a_tag != b_tag) return false;
return switch (a) {
.struct_type => |struct_info| {
if (struct_info.layout != b.struct_type.layout) return false;
if (struct_info.fields.len != b.struct_type.fields.len) return false;
for (struct_info.fields) |field, i| {
if (!std.meta.eql(field, b.struct_type.fields[i])) return false;
}
return true;
},
// .error_type => |error_info| std.mem.eql(u8, error_info.name, b.error_type.name),
.error_set_type => |error_set_info| {
if (error_set_info.names.len != b.error_set_type.names.len) return false;
for (error_set_info.names) |a_name, i| {
const b_name = b.error_set_type.names[i];
if (!std.mem.eql(u8, a_name, b_name)) return false;
}
return true;
},
.enum_type => |enum_info| {
if (enum_info.tag_type != b.enum_type.tag_type) return false;
if (enum_info.tag_type_infered != b.enum_type.tag_type_infered) return false;
if (enum_info.fields.len != b.enum_type.fields.len) return false;
@panic("TODO: implement field equality check");
},
.function_type => |function_info| {
if (function_info.calling_convention != b.function_type.calling_convention) return false;
if (function_info.alignment != b.function_type.alignment) return false;
if (function_info.is_generic != b.function_type.is_generic) return false;
if (function_info.is_var_args != b.function_type.is_var_args) return false;
if (function_info.return_type != b.function_type.return_type) return false;
if (function_info.args.len != b.function_type.args.len) return false;
for (function_info.args) |arg, i| {
if (!std.meta.eql(arg, b.function_type.args[i])) return false;
}
return true;
},
.union_type => |union_info| {
if (union_info.tag_type != b.union_type.tag_type) return false;
if (union_info.layout != b.union_type.layout) return false;
if (union_info.fields.len != b.union_type.fields.len) return false;
for (union_info.fields) |field, i| {
if (!std.meta.eql(field, b.union_type.fields[i])) return false;
}
return true;
},
.tuple_type => |tuple_info| {
std.debug.assert(tuple_info.types.len == tuple_info.values.len);
std.debug.assert(b.tuple_type.types.len == b.tuple_type.values.len);
if (tuple_info.types.len != b.tuple_type.types.len) return false;
for (tuple_info.types) |ty, i| {
if (ty != b.tuple_type.types[i]) return false;
}
for (tuple_info.values) |val, i| {
if (val != b.tuple_type.values[i]) return false;
}
return true;
},
.bytes => |bytes| std.mem.eql(u8, bytes.data, b.bytes.data),
.aggregate => |aggregate| {
if (aggregate.data.len != b.aggregate.data.len) return false;
for (aggregate.data) |ty, i| {
if (ty != b.aggregate.data[i]) return false;
}
return true;
},
else => std.meta.eql(a, b),
};
}
pub fn tag(key: Key) Tag {
return switch (key) {
.simple => .simple,
.int_type => |int_info| switch (int_info.signedness) {
.signed => .type_int_signed,
.unsigned => .type_int_unsigned,
@ -294,14 +181,13 @@ pub const Key = union(enum) {
.struct_type => .type_struct,
.optional_type => .type_optional,
.error_union_type => .type_error_union,
// .error_type => .type_error,
.error_set_type => .type_error_set,
.enum_type => .type_enum,
.function_type => .type_function,
.union_type => .type_union,
.tuple_type => .type_tuple,
.vector_type => .type_vector,
.anyframe_t_type => .type_anyframe_t,
.anyframe_type => .type_anyframe,
.int_u64_value => |int| if (int <= std.math.maxInt(u32)) .int_u32 else .int_u64,
.int_i64_value => |int| if (std.math.maxInt(i32) <= int and int <= std.math.maxInt(i32)) .int_i32 else .int_i64,
@ -311,10 +197,8 @@ pub const Key = union(enum) {
.float_64_value => .float_f64,
.float_80_value => .float_f80,
.float_128_value => .float_f128,
// .type_value => .type,
.bytes => .bytes,
// .one_pointer => .one_pointer,
.aggregate => .aggregate,
.union_value => .union_value,
};
@ -378,7 +262,7 @@ pub const Key = union(enum) {
.union_type => .Union,
.tuple_type => .Struct, // TODO this correct?
.vector_type => .Vector,
.anyframe_t_type => .AnyFrame,
.anyframe_type => .AnyFrame,
.int_u64_value,
.int_i64_value,
@ -500,7 +384,7 @@ pub const Key = union(enum) {
.array_type => |array_info| array_info.child,
.optional_type => |optional_info| optional_info.payload_type,
.vector_type => |vector_info| vector_info.child,
.anyframe_t_type => |anyframe_t_info| anyframe_t_info.child,
.anyframe_type => |anyframe_info| anyframe_info.child,
else => unreachable,
};
}
@ -717,7 +601,7 @@ pub const Key = union(enum) {
vector_info.child.fmtType(ip),
});
},
.anyframe_t_type => |anyframe_info| {
.anyframe_type => |anyframe_info| {
try writer.writeAll("anyframe->");
try printType(anyframe_info.child, ip, writer);
},
@ -813,7 +697,7 @@ pub const Key = union(enum) {
.union_type,
.tuple_type,
.vector_type,
.anyframe_t_type,
.anyframe_type,
=> unreachable,
.int_u64_value => |int| try std.fmt.formatIntValue(int, "", .{}, writer),
@ -826,19 +710,19 @@ pub const Key = union(enum) {
.float_128_value => |float| try writer.print("{d}", .{@floatCast(f64, float)}),
// .type_value => |tty| tty.fmtType(ip),
.bytes => |data| try writer.print("\"{}\"", .{std.zig.fmtEscapes(data.data)}),
.bytes => |bytes| try writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)}),
// .one_pointer => unreachable,
.aggregate => |aggregate| {
const struct_info = ip.indexToKey(ty).struct_type;
std.debug.assert(aggregate.data.len == struct_info.fields.len);
std.debug.assert(aggregate.len == struct_info.fields.len);
try writer.writeAll(".{");
var i: u32 = 0;
while (i < aggregate.data.len) : (i += 1) {
while (i < aggregate.len) : (i += 1) {
if (i != 0) try writer.writeAll(", ");
try writer.print(".{s} = ", .{struct_info.fields[i].name});
try printValue(aggregate.data[i], struct_info.fields[i].ty, ip, writer);
try printValue(aggregate[i], struct_info.fields[i].ty, ip, writer);
}
try writer.writeByte('}');
},
@ -917,9 +801,6 @@ pub const Tag = enum(u8) {
/// An error union type.
/// data is payload to ErrorUnion.
type_error_union,
/// An error type.
/// data is payload to Error.
type_error,
/// An error set type.
/// data is payload to ErrorSet.
type_error_set,
@ -940,7 +821,7 @@ pub const Tag = enum(u8) {
type_vector,
/// An anyframe->T type.
/// data is index to type
type_anyframe_t,
type_anyframe,
/// An unsigned integer value that can be represented by u32.
/// data is integer value
@ -975,16 +856,10 @@ pub const Tag = enum(u8) {
/// A float value that can be represented by f128.
/// data is payload to f128.
float_f128,
// /// A type value.
// /// data is Index.
// type,
/// A byte sequence value.
/// data is payload to data begin and length.
bytes,
// /// A single pointer value.
// /// data is index to value.
// one_pointer,
/// A aggregate (struct) value.
/// data is index to Aggregate.
aggregate,
@ -1039,12 +914,12 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
// TODO deinit fields
}
pub fn indexToKey(ip: InternPool, index: Index) Key {
const item = ip.items.get(@enumToInt(index));
const data = item.data;
return switch (item.tag) {
.simple => .{ .simple = @intToEnum(Simple, data) },
.type_int_signed => .{ .int_type = .{
.signedness = .signed,
.bits = @intCast(u16, data),
@ -1057,9 +932,8 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
.type_array => .{ .array_type = ip.extraData(Array, data) },
.type_struct => .{ .struct_type = ip.extraData(Struct, data) },
.type_optional => .{ .optional_type = .{ .payload_type = @intToEnum(Index, data) } },
.type_anyframe_t => .{ .anyframe_t_type = .{ .child = @intToEnum(Index, data) } },
.type_anyframe => .{ .anyframe_type = .{ .child = @intToEnum(Index, data) } },
.type_error_union => .{ .error_union_type = ip.extraData(ErrorUnion, data) },
// .type_error => .{ .error_type = ip.extraData(Error, data) },
.type_error_set => .{ .error_set_type = ip.extraData(ErrorSet, data) },
.type_enum => .{ .enum_type = ip.extraData(Enum, data) },
.type_function => .{ .function_type = ip.extraData(Fn, data) },
@ -1071,59 +945,51 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
.int_i32 => .{ .int_i64_value = @bitCast(i32, data) },
.int_u64 => .{ .int_u64_value = ip.extraData(u64, data) },
.int_i64 => .{ .int_i64_value = ip.extraData(i64, data) },
.int_big_positive => unreachable,
.int_big_negative => unreachable,
.int_big_positive => .{ .int_big_value = .{
.positive = true,
.limbs = ip.extraData([]const std.math.big.Limb, data),
} },
.int_big_negative => .{ .int_big_value = .{
.positive = true,
.limbs = ip.extraData([]const std.math.big.Limb, data),
} },
.float_f16 => .{ .float_16_value = @bitCast(f16, @intCast(u16, data)) },
.float_f32 => .{ .float_32_value = @bitCast(f32, data) },
.float_f64 => .{ .float_64_value = ip.extraData(f64, data) },
.float_f80 => .{ .float_80_value = ip.extraData(f80, data) },
.float_f128 => .{ .float_128_value = ip.extraData(f128, data) },
// .type => .{ .type_value = @intToEnum(Index, data) },
.bytes => unreachable, // TODO
// .one_pointer => .{ .one_pointer = @intToEnum(Index, data) },
else => @panic("TODO"),
.bytes => .{ .bytes = ip.extraData([]const u8, data) },
.aggregate => .{ .aggregate = ip.extraData(Aggregate, data) },
.union_value => .{ .union_value = ip.extraData(UnionValue, data) },
};
}
pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
const adapter: KeyAdapter = .{ .intern_pool = ip };
const adapter: KeyAdapter = .{ .ip = ip };
const gop = try ip.map.getOrPutAdapted(gpa, key, adapter);
if (gop.found_existing) return @intToEnum(Index, gop.index);
const item: Item = switch (key) {
.simple => |simple| .{ .tag = .simple, .data = @enumToInt(simple) },
.int_type => |int_ty| .{
.tag = switch (int_ty.signedness) {
.signed => .type_int_signed,
.unsigned => .type_int_unsigned,
},
.data = int_ty.bits,
},
.optional_type => |optional_ty| .{ .tag = .type_optional, .data = @enumToInt(optional_ty.payload_type) },
.anyframe_t_type => |anyframe_t| .{ .tag = .type_anyframe_t, .data = @enumToInt(anyframe_t.child) },
.int_u64_value => |int_val| if (int_val <= std.math.maxInt(u32)) .{
.tag = .int_u32,
.data = @intCast(u32, int_val),
} else .{
.tag = .int_u64,
.data = try ip.addExtra(gpa, int_val),
},
.int_i64_value => |int_val| if (std.math.maxInt(i32) <= int_val and int_val <= std.math.maxInt(i32)) .{
.tag = .int_i32,
.data = @bitCast(u32, @intCast(u32, int_val)),
} else .{
.tag = .int_i64,
.data = try ip.addExtra(gpa, int_val),
},
.float_16_value => |float_val| .{ .tag = .float_f16, .data = @bitCast(u16, float_val) },
.float_32_value => |float_val| .{ .tag = .float_f32, .data = @bitCast(u32, float_val) },
// .type_value => |ty| .{ .tag = .type, .data = @enumToInt(ty) },
.bytes => unreachable, // TODO
// .one_pointer => |val| .{ .tag = .one_pointer, .data = @enumToInt(val) },
inline else => |data| .{ .tag = key.tag(), .data = try ip.addExtra(gpa, data) }, // TODO sad stage1 noises :(
const tag: Tag = key.tag();
const data: u32 = switch (key) {
.simple => |simple| @enumToInt(simple),
.int_type => |int_ty| int_ty.bits,
.optional_type => |optional_ty| @enumToInt(optional_ty.payload_type),
.anyframe_type => |anyframe_ty| @enumToInt(anyframe_ty.child),
.int_u64_value => |int_val| if (tag == .int_u32) @intCast(u32, int_val) else try ip.addExtra(gpa, int_val),
.int_i64_value => |int_val| if (tag == .int_i32) @bitCast(u32, @intCast(u32, int_val)) else try ip.addExtra(gpa, int_val),
.int_big_value => |big_int_val| try ip.addExtra(gpa, big_int_val.limbs),
.float_16_value => |float_val| @bitCast(u16, float_val),
.float_32_value => |float_val| @bitCast(u32, float_val),
inline else => |data| try ip.addExtra(gpa, data), // TODO sad stage1 noises :(
};
try ip.items.append(gpa, item);
try ip.items.append(gpa, .{
.tag = tag,
.data = data,
});
return @intToEnum(Index, ip.items.len - 1);
}
@ -1142,6 +1008,135 @@ fn extraData(ip: InternPool, comptime T: type, index: usize) T {
return std.mem.bytesToValue(T, bytes);
}
const KeyAdapter = struct {
ip: *const InternPool,
pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool {
_ = b_void;
return deepEql(a, ctx.ip.indexToKey(@intToEnum(Index, b_map_index)));
}
pub fn hash(ctx: @This(), a: Key) u32 {
_ = ctx;
var hasher = std.hash.Wyhash.init(0);
deepHash(&hasher, a);
return @truncate(u32, hasher.final());
}
};
fn deepEql(a: anytype, b: @TypeOf(a)) bool {
const T = @TypeOf(a);
switch (@typeInfo(T)) {
.Struct => |info| {
if (info.layout == .Packed) {
return std.mem.eql(u8, std.mem.asBytes(&a), std.mem.asBytes(&b));
}
inline for (info.fields) |field_info| {
if (!deepEql(@field(a, field_info.name), @field(b, field_info.name))) return false;
}
return true;
},
.Union => |info| {
const UnionTag = info.tag_type.?;
const tag_a = std.meta.activeTag(a);
const tag_b = std.meta.activeTag(b);
if (tag_a != tag_b) return false;
inline for (info.fields) |field_info| {
if (@field(UnionTag, field_info.name) == tag_a) {
return deepEql(@field(a, field_info.name), @field(b, field_info.name));
}
}
return false;
},
.Pointer => |info| {
if (info.size != .Slice) {
@compileError("cannot compare non slice pointer type " ++ @typeName(T));
}
if (@typeInfo(info.child) == .Int) {
return std.mem.eql(info.child, a, b);
}
if (a.len != b.len) return false;
var i: usize = 0;
while (i < a.len) : (i += 1) {
if (!deepEql(a[i], b[i])) return false;
}
return true;
},
.Enum, .Int => return a == b,
else => unreachable,
}
}
fn deepHash(hasher: anytype, key: anytype) void {
const Inner = @TypeOf(key);
switch (@typeInfo(Inner)) {
.Int => {
if (comptime std.meta.trait.hasUniqueRepresentation(Inner)) {
hasher.update(std.mem.asBytes(&key));
} else {
const byte_size = comptime std.math.divCeil(comptime_int, @bitSizeOf(Inner), 8) catch unreachable;
hasher.update(std.mem.asBytes(&key)[0..byte_size]);
}
},
.Bool => deepHash(hasher, @boolToInt(key)),
.Enum => deepHash(hasher, @enumToInt(key)),
.Float => |info| deepHash(hasher, switch (info.bits) {
16 => @bitCast(u16, key),
32 => @bitCast(u32, key),
64 => @bitCast(u64, key),
80 => @bitCast(u80, key),
128 => @bitCast(u128, key),
else => unreachable,
}),
.Pointer => |info| {
if (info.size != .Slice) {
@compileError("");
}
if (comptime std.meta.trait.hasUniqueRepresentation(info.child)) {
hasher.update(std.mem.sliceAsBytes(key));
} else {
for (key) |item| {
deepHash(hasher, item);
}
}
},
.Struct => |info| {
if (info.layout == .Packed) {
hasher.update(std.mem.asBytes(&key));
} else {
inline for (info.fields) |field| {
deepHash(hasher, @field(key, field.name));
}
}
},
.Union => |info| {
const TagType = info.tag_type.?;
const tag = std.meta.activeTag(key);
deepHash(hasher, tag);
inline for (info.fields) |field| {
if (@field(TagType, field.name) == tag) {
deepHash(hasher, @field(key, field.name));
break;
}
}
},
else => unreachable,
}
}
// ---------------------------------------------
// UTILITY
// ---------------------------------------------
@ -2601,8 +2596,8 @@ test "anyframe type" {
const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } });
const bool_type = try ip.get(gpa, .{ .simple = .bool });
const @"anyframe->i32" = try ip.get(gpa, Key{ .anyframe_t_type = .{ .child = i32_type } });
const @"anyframe->bool" = try ip.get(gpa, Key{ .anyframe_t_type = .{ .child = bool_type } });
const @"anyframe->i32" = try ip.get(gpa, Key{ .anyframe_type = .{ .child = i32_type } });
const @"anyframe->bool" = try ip.get(gpa, Key{ .anyframe_type = .{ .child = bool_type } });
try testExpectFmtType(&ip, @"anyframe->i32", "anyframe->i32");
try testExpectFmtType(&ip, @"anyframe->bool", "anyframe->bool");