diff --git a/src/InternPool.zig b/src/InternPool.zig index 79506c4404..a16ebe6487 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -506,7 +506,12 @@ pub const Key = union(enum) { pub const Aggregate = struct { ty: Index, - fields: []const Index, + storage: Storage, + + pub const Storage = union(enum) { + elems: []const Index, + repeated_elem: Index, + }; }; pub fn hash32(key: Key) u32 { @@ -578,7 +583,14 @@ pub const Key = union(enum) { .aggregate => |aggregate| { std.hash.autoHash(hasher, aggregate.ty); - for (aggregate.fields) |field| std.hash.autoHash(hasher, field); + std.hash.autoHash(hasher, @as( + @typeInfo(Key.Aggregate.Storage).Union.tag_type.?, + aggregate.storage, + )); + switch (aggregate.storage) { + .elems => |elems| for (elems) |elem| std.hash.autoHash(hasher, elem), + .repeated_elem => |elem| std.hash.autoHash(hasher, elem), + } }, .error_set_type => |error_set_type| { @@ -756,7 +768,14 @@ pub const Key = union(enum) { .aggregate => |a_info| { const b_info = b.aggregate; if (a_info.ty != b_info.ty) return false; - return std.mem.eql(Index, a_info.fields, b_info.fields); + + const StorageTag = @typeInfo(Key.Aggregate.Storage).Union.tag_type.?; + if (@as(StorageTag, a_info.storage) != @as(StorageTag, b_info.storage)) return false; + + return switch (a_info.storage) { + .elems => |a_elems| std.mem.eql(Index, a_elems, b_info.storage.elems), + .repeated_elem => |a_elem| a_elem == b_info.storage.repeated_elem, + }; }, .anon_struct_type => |a_info| { const b_info = b.anon_struct_type; @@ -1407,6 +1426,9 @@ pub const Tag = enum(u8) { /// An instance of a struct, array, or vector. /// data is extra index to `Aggregate`. aggregate, + /// An instance of an array or vector with every element being the same value. + /// data is extra index to `Repeated`. + repeated, }; /// Trailing: @@ -1446,6 +1468,13 @@ pub const Aggregate = struct { ty: Index, }; +pub const Repeated = struct { + /// The type of the aggregate. + ty: Index, + /// The value of every element. + elem_val: Index, +}; + /// Trailing: /// 0. type: Index for each fields_len /// 1. value: Index for each fields_len @@ -2049,13 +2078,13 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { // as the tuple case below). .struct_type => .{ .aggregate = .{ .ty = ty, - .fields = &.{}, + .storage = .{ .elems = &.{} }, } }, // There is only one possible value precisely due to the // fact that this values slice is fully populated! .anon_struct_type => |anon_struct_type| .{ .aggregate = .{ .ty = ty, - .fields = anon_struct_type.values, + .storage = .{ .elems = anon_struct_type.values }, } }, else => unreachable, }; @@ -2066,7 +2095,14 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { const fields = @ptrCast([]const Index, ip.extra.items[extra.end..][0..len]); return .{ .aggregate = .{ .ty = extra.data.ty, - .fields = fields, + .storage = .{ .elems = fields }, + } }; + }, + .repeated => { + const extra = ip.extraData(Repeated, data); + return .{ .aggregate = .{ + .ty = extra.ty, + .storage = .{ .repeated_elem = extra.elem_val }, } }; }, .union_value => .{ .un = ip.extraData(Key.Union, data) }, @@ -2663,10 +2699,18 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { .aggregate => |aggregate| { assert(aggregate.ty != .none); - for (aggregate.fields) |elem| assert(elem != .none); - assert(aggregate.fields.len == ip.aggregateTypeLen(aggregate.ty)); + const aggregate_len = ip.aggregateTypeLen(aggregate.ty); + switch (aggregate.storage) { + .elems => |elems| { + assert(elems.len == aggregate_len); + for (elems) |elem| assert(elem != .none); + }, + .repeated_elem => |elem| { + assert(elem != .none); + }, + } - if (aggregate.fields.len == 0) { + if (aggregate_len == 0) { ip.items.appendAssumeCapacity(.{ .tag = .only_possible_value, .data = @enumToInt(aggregate.ty), @@ -2676,7 +2720,12 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { switch (ip.indexToKey(aggregate.ty)) { .anon_struct_type => |anon_struct_type| { - if (std.mem.eql(Index, anon_struct_type.values, aggregate.fields)) { + if (switch (aggregate.storage) { + .elems => |elems| std.mem.eql(Index, anon_struct_type.values, elems), + .repeated_elem => |elem| for (anon_struct_type.values) |value| { + if (value != elem) break false; + } else true, + }) { // This encoding works thanks to the fact that, as we just verified, // the type itself contains a slice of values that can be provided // in the aggregate fields. @@ -2690,9 +2739,33 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { else => {}, } + if (switch (aggregate.storage) { + .elems => |elems| for (elems[1..]) |elem| { + if (elem != elems[0]) break false; + } else true, + .repeated_elem => true, + }) { + try ip.extra.ensureUnusedCapacity( + gpa, + @typeInfo(Repeated).Struct.fields.len, + ); + + ip.items.appendAssumeCapacity(.{ + .tag = .repeated, + .data = ip.addExtraAssumeCapacity(Repeated{ + .ty = aggregate.ty, + .elem_val = switch (aggregate.storage) { + .elems => |elems| elems[0], + .repeated_elem => |elem| elem, + }, + }), + }); + return @intToEnum(Index, ip.items.len - 1); + } + try ip.extra.ensureUnusedCapacity( gpa, - @typeInfo(Aggregate).Struct.fields.len + aggregate.fields.len, + @typeInfo(Aggregate).Struct.fields.len + aggregate_len, ); ip.items.appendAssumeCapacity(.{ @@ -2701,7 +2774,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { .ty = aggregate.ty, }), }); - ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, aggregate.fields)); + ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, aggregate.storage.elems)); }, .un => |un| { @@ -3417,6 +3490,7 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void { const fields_len = @intCast(u32, ip.aggregateTypeLen(info.ty)); break :b @sizeOf(Aggregate) + (@sizeOf(u32) * fields_len); }, + .repeated => @sizeOf(Repeated), .float_f16 => 0, .float_f32 => 0, diff --git a/src/Sema.zig b/src/Sema.zig index be505d74a3..bf4d13824a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12671,7 +12671,7 @@ fn analyzeTupleCat( const runtime_src = opt_runtime_src orelse { const tuple_val = try mod.intern(.{ .aggregate = .{ .ty = tuple_ty, - .fields = values, + .storage = .{ .elems = values }, } }); return sema.addConstant(tuple_ty.toType(), tuple_val.toValue()); }; @@ -12989,7 +12989,7 @@ fn analyzeTupleMul( const runtime_src = opt_runtime_src orelse { const tuple_val = try mod.intern(.{ .aggregate = .{ .ty = tuple_ty, - .fields = values, + .storage = .{ .elems = values }, } }); return sema.addConstant(tuple_ty.toType(), tuple_val.toValue()); }; @@ -18227,7 +18227,7 @@ fn zirStructInitAnon( const runtime_index = opt_runtime_index orelse { const tuple_val = try mod.intern(.{ .aggregate = .{ .ty = tuple_ty, - .fields = values, + .storage = .{ .elems = values }, } }); return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref); }; @@ -18442,7 +18442,7 @@ fn zirArrayInitAnon( const runtime_src = opt_runtime_src orelse { const tuple_val = try mod.intern(.{ .aggregate = .{ .ty = tuple_ty, - .fields = values, + .storage = .{ .elems = values }, } }); return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref); }; @@ -29176,7 +29176,7 @@ fn coerceTupleToTuple( tuple_ty, (try mod.intern(.{ .aggregate = .{ .ty = tuple_ty.ip_index, - .fields = field_vals, + .storage = .{ .elems = field_vals }, } })).toValue(), ); } @@ -33085,7 +33085,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { // one-possible-value in type.zig. const empty = try mod.intern(.{ .aggregate = .{ .ty = ty.ip_index, - .fields = &.{}, + .storage = .{ .elems = &.{} }, } }); return empty.toValue(); }, @@ -33098,7 +33098,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { // therefore has one possible value. return (try mod.intern(.{ .aggregate = .{ .ty = ty.ip_index, - .fields = tuple.values, + .storage = .{ .elems = tuple.values }, } })).toValue(); }, diff --git a/src/type.zig b/src/type.zig index 4e90cbd34d..019557c5e0 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2738,7 +2738,7 @@ pub const Type = struct { // one-possible-value logic in Sema.zig. const empty = try mod.intern(.{ .aggregate = .{ .ty = ty.ip_index, - .fields = &.{}, + .storage = .{ .elems = &.{} }, } }); return empty.toValue(); }, @@ -2751,7 +2751,7 @@ pub const Type = struct { // therefore has one possible value. return (try mod.intern(.{ .aggregate = .{ .ty = ty.ip_index, - .fields = tuple.values, + .storage = .{ .elems = tuple.values }, } })).toValue(); }, diff --git a/src/value.zig b/src/value.zig index 4408d10231..db79fa3fe6 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2665,7 +2665,10 @@ pub const Value = struct { else => unreachable, }, else => return switch (mod.intern_pool.indexToKey(val.ip_index)) { - .aggregate => |aggregate| aggregate.fields[index].toValue(), + .aggregate => |aggregate| switch (aggregate.storage) { + .elems => |elems| elems[index], + .repeated_elem => |elem| elem, + }.toValue(), else => unreachable, }, } @@ -2753,8 +2756,11 @@ pub const Value = struct { else => switch (mod.intern_pool.indexToKey(val.ip_index)) { .undef => return true, .simple_value => |v| if (v == .undefined) return true, - .aggregate => |aggregate| for (aggregate.fields) |field| { - if (try anyUndef(field.toValue(), mod)) return true; + .aggregate => |aggregate| switch (aggregate.storage) { + .elems => |elems| for (elems) |elem| { + if (try anyUndef(elem.toValue(), mod)) return true; + }, + .repeated_elem => |elem| if (try anyUndef(elem.toValue(), mod)) return true, }, else => {}, },