InternPool: add ptr-to-int value

Also modify coercion in Sema to be InternPool-aware by calling
getCoerced.

The unnecessary comptime logic in mod.intValue is deleted too
This commit is contained in:
Andrew Kelley 2023-05-08 11:51:32 -07:00
parent fd674d95be
commit 68b95a39b1
4 changed files with 211 additions and 70 deletions

View File

@ -55,6 +55,7 @@ pub const Key = union(enum) {
lib_name: u32,
},
int: Key.Int,
ptr: Key.Ptr,
enum_tag: struct {
ty: Index,
tag: BigIntConst,
@ -140,6 +141,16 @@ pub const Key = union(enum) {
};
};
pub const Ptr = struct {
ty: Index,
addr: Addr,
pub const Addr = union(enum) {
decl: DeclIndex,
int: Index,
};
};
pub fn hash32(key: Key) u32 {
return @truncate(u32, key.hash64());
}
@ -176,6 +187,16 @@ pub const Key = union(enum) {
for (big_int.limbs) |limb| std.hash.autoHash(hasher, limb);
},
.ptr => |ptr| {
std.hash.autoHash(hasher, ptr.ty);
// Int-to-ptr pointers are hashed separately than decl-referencing pointers.
// This is sound due to pointer province rules.
switch (ptr.addr) {
.int => |int| std.hash.autoHash(hasher, int),
.decl => @panic("TODO"),
}
},
.enum_tag => |enum_tag| {
std.hash.autoHash(hasher, enum_tag.ty);
std.hash.autoHash(hasher, enum_tag.tag.positive);
@ -237,8 +258,30 @@ pub const Key = union(enum) {
return std.meta.eql(a_info, b_info);
},
.ptr => |a_info| {
const b_info = b.ptr;
if (a_info.ty != b_info.ty)
return false;
return switch (a_info.addr) {
.int => |a_int| switch (b_info.addr) {
.int => |b_int| a_int == b_int,
.decl => false,
},
.decl => |a_decl| switch (b_info.addr) {
.int => false,
.decl => |b_decl| a_decl == b_decl,
},
};
},
.int => |a_info| {
const b_info = b.int;
if (a_info.ty != b_info.ty)
return false;
return switch (a_info.storage) {
.u64 => |aa| switch (b_info.storage) {
.u64 => |bb| aa == bb,
@ -298,9 +341,11 @@ pub const Key = union(enum) {
.union_type,
=> return .type_type,
.int => |x| return x.ty,
.extern_func => |x| return x.ty,
.enum_tag => |x| return x.ty,
inline .ptr,
.int,
.extern_func,
.enum_tag,
=> |x| return x.ty,
.simple_value => |s| switch (s) {
.undefined => return .undefined_type,
@ -724,6 +769,9 @@ pub const Tag = enum(u8) {
/// only an enum tag, but will be presented via the API with a different Key.
/// data is SimpleInternal enum value.
simple_internal,
/// A pointer to an integer value.
/// data is extra index of PtrInt, which contains the type and address.
ptr_int,
/// Type: u8
/// data is integer value
int_u8,
@ -897,16 +945,13 @@ pub const Array = struct {
child: Index,
sentinel: Index,
pub const Length = packed struct(u64) {
len0: u32,
len1: u32,
};
pub const Length = PackedU64;
pub fn getLength(a: Array) u64 {
return @bitCast(u64, Length{
.len0 = a.len0,
.len1 = a.len1,
});
return (PackedU64{
.a = a.len0,
.b = a.len1,
}).get();
}
};
@ -929,6 +974,24 @@ pub const EnumSimple = struct {
fields_len: u32,
};
pub const PackedU64 = packed struct(u64) {
a: u32,
b: u32,
pub fn get(x: PackedU64) u64 {
return @bitCast(u64, x);
}
pub fn init(x: u64) PackedU64 {
return @bitCast(PackedU64, x);
}
};
pub const PtrInt = struct {
ty: Index,
addr: Index,
};
/// Trailing: Limb for every limbs_len
pub const Int = struct {
ty: Index,
@ -1066,6 +1129,13 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
.fields_len = 0,
} },
},
.ptr_int => {
const info = ip.extraData(PtrInt, data);
return .{ .ptr = .{
.ty = info.ty,
.addr = .{ .int = info.addr },
} };
},
.int_u8 => .{ .int = .{
.ty = .u8_type,
.storage = .{ .u64 = data },
@ -1188,12 +1258,12 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
}
}
const length = @bitCast(Array.Length, array_type.len);
const length = Array.Length.init(array_type.len);
ip.items.appendAssumeCapacity(.{
.tag = .type_array_big,
.data = try ip.addExtra(gpa, Array{
.len0 = length.len0,
.len1 = length.len1,
.len0 = length.a,
.len1 = length.b,
.child = array_type.child,
.sentinel = array_type.sentinel,
}),
@ -1237,6 +1307,20 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
},
.extern_func => @panic("TODO"),
.ptr => |ptr| switch (ptr.addr) {
.decl => @panic("TODO"),
.int => |int| {
assert(ptr.ty != .none);
ip.items.appendAssumeCapacity(.{
.tag = .ptr_int,
.data = try ip.addExtra(gpa, PtrInt{
.ty = ptr.ty,
.addr = int,
}),
});
},
},
.int => |int| b: {
switch (int.ty) {
.none => unreachable,
@ -1620,38 +1704,43 @@ pub fn slicePtrType(ip: InternPool, i: Index) Index {
}
}
/// Given an existing integer value, returns the same numerical value but with
/// the supplied type.
pub fn getCoercedInt(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Allocator.Error!Index {
const key = ip.indexToKey(val);
// The key cannot be passed directly to `get`, otherwise in the case of
// big_int storage, the limbs would be invalidated before they are read.
// Here we pre-reserve the limbs to ensure that the logic in `addInt` will
// not use an invalidated limbs pointer.
switch (key.int.storage) {
.u64 => |x| return ip.get(gpa, .{ .int = .{
.ty = new_ty,
.storage = .{ .u64 = x },
} }),
.i64 => |x| return ip.get(gpa, .{ .int = .{
.ty = new_ty,
.storage = .{ .i64 = x },
} }),
/// Given an existing value, returns the same value but with the supplied type.
/// Only some combinations are allowed:
/// * int to int
pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Allocator.Error!Index {
switch (ip.indexToKey(val)) {
.int => |int| {
// The key cannot be passed directly to `get`, otherwise in the case of
// big_int storage, the limbs would be invalidated before they are read.
// Here we pre-reserve the limbs to ensure that the logic in `addInt` will
// not use an invalidated limbs pointer.
switch (int.storage) {
.u64 => |x| return ip.get(gpa, .{ .int = .{
.ty = new_ty,
.storage = .{ .u64 = x },
} }),
.i64 => |x| return ip.get(gpa, .{ .int = .{
.ty = new_ty,
.storage = .{ .i64 = x },
} }),
.big_int => |big_int| {
const positive = big_int.positive;
const limbs = ip.limbsSliceToIndex(big_int.limbs);
// This line invalidates the limbs slice, but the indexes computed in the
// previous line are still correct.
try reserveLimbs(ip, gpa, @typeInfo(Int).Struct.fields.len + big_int.limbs.len);
return ip.get(gpa, .{ .int = .{
.ty = new_ty,
.storage = .{ .big_int = .{
.limbs = ip.limbsIndexToSlice(limbs),
.positive = positive,
} },
} });
.big_int => |big_int| {
const positive = big_int.positive;
const limbs = ip.limbsSliceToIndex(big_int.limbs);
// This line invalidates the limbs slice, but the indexes computed in the
// previous line are still correct.
try reserveLimbs(ip, gpa, @typeInfo(Int).Struct.fields.len + big_int.limbs.len);
return ip.get(gpa, .{ .int = .{
.ty = new_ty,
.storage = .{ .big_int = .{
.limbs = ip.limbsIndexToSlice(limbs),
.positive = positive,
} },
} });
},
}
},
else => unreachable,
}
}
@ -1708,6 +1797,7 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {
.simple_type => 0,
.simple_value => 0,
.simple_internal => 0,
.ptr_int => @sizeOf(PtrInt),
.int_u8 => 0,
.int_u16 => 0,
.int_u32 => 0,

View File

@ -6887,17 +6887,23 @@ pub fn singleConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
return ptrType(mod, .{ .elem_type = child_type.ip_index, .is_const = true });
}
pub fn ptrIntValue(mod: *Module, ty: Type, x: u64) Allocator.Error!Value {
assert(ty.zigTypeTag(mod) == .Pointer);
const i = try intern(mod, .{ .ptr = .{
.ty = ty.ip_index,
.addr = .{ .int = try intern(mod, .{ .int = .{
.ty = ty.ip_index,
.storage = .{ .u64 = x },
} }) },
} });
return i.toValue();
}
pub fn intValue(mod: *Module, ty: Type, x: anytype) Allocator.Error!Value {
if (std.debug.runtime_safety) {
// TODO: decide if this also works for ABI int types like enums
const tag = ty.zigTypeTag(mod);
assert(tag == .Int or tag == .ComptimeInt);
}
if (@TypeOf(x) == comptime_int) {
if (comptime std.math.cast(u64, x)) |casted| return intValue_u64(mod, ty, casted);
if (comptime std.math.cast(i64, x)) |casted| return intValue_i64(mod, ty, casted);
@compileError("Out-of-range comptime_int passed to Module.intValue");
}
if (std.math.cast(u64, x)) |casted| return intValue_u64(mod, ty, casted);
if (std.math.cast(i64, x)) |casted| return intValue_i64(mod, ty, casted);
var limbs_buffer: [4]usize = undefined;

View File

@ -15096,7 +15096,7 @@ fn analyzePtrArithmetic(
.ptr_sub => addr - elem_size * offset_int,
else => unreachable,
};
const new_ptr_val = try mod.intValue(new_ptr_ty, new_addr);
const new_ptr_val = try mod.ptrIntValue(new_ptr_ty, new_addr);
return sema.addConstant(new_ptr_ty, new_ptr_val);
}
if (air_tag == .ptr_sub) {
@ -19931,7 +19931,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
if (addr != 0 and ptr_align != 0 and addr % ptr_align != 0)
return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)});
return sema.addConstant(ptr_ty, try mod.intValue(ptr_ty, addr));
return sema.addConstant(ptr_ty, try mod.ptrIntValue(ptr_ty, addr));
}
try sema.requireRuntimeBlock(block, src, operand_src);
@ -25640,8 +25640,13 @@ fn coerceExtra(
var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
if (in_memory_result == .ok) {
if (maybe_inst_val) |val| {
// Keep the comptime Value representation; take the new type.
return sema.addConstant(dest_ty, val);
if (val.ip_index == .none or val.ip_index == .null_value) {
// Keep the comptime Value representation; take the new type.
return sema.addConstant(dest_ty, val);
} else {
const new_val = try mod.intern_pool.getCoerced(mod.gpa, val.ip_index, dest_ty.ip_index);
return sema.addConstant(dest_ty, new_val.toValue());
}
}
try sema.requireRuntimeBlock(block, inst_src, null);
return block.addBitCast(dest_ty, inst);
@ -26014,7 +26019,7 @@ fn coerceExtra(
if (!opts.report_err) return error.NotCoercible;
return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) });
}
const new_val = try mod.intern_pool.getCoercedInt(sema.gpa, val.ip_index, dest_ty.ip_index);
const new_val = try mod.intern_pool.getCoerced(sema.gpa, val.ip_index, dest_ty.ip_index);
return try sema.addConstant(dest_ty, new_val.toValue());
}
if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {
@ -31673,10 +31678,13 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
// values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.enum_tag => unreachable, // it's a value, not a type
.ptr => unreachable,
.enum_tag => unreachable,
},
};
}
@ -33193,10 +33201,13 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
// values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.enum_tag => unreachable, // it's a value, not a type
.ptr => unreachable,
.enum_tag => unreachable,
},
}
}
@ -33253,7 +33264,14 @@ pub fn addConstant(sema: *Sema, ty: Type, val: Value) SemaError!Air.Inst.Ref {
const result = Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1));
// This assertion can be removed when the `ty` parameter is removed from
// this function thanks to the InternPool transition being complete.
assert(Type.eql(sema.typeOf(result), ty, sema.mod));
if (std.debug.runtime_safety) {
const val_ty = sema.typeOf(result);
if (!Type.eql(val_ty, ty, sema.mod)) {
std.debug.panic("addConstant type mismatch: '{}' vs '{}'\n", .{
ty.fmt(sema.mod), val_ty.fmt(sema.mod),
});
}
}
return result;
}
const ty_inst = try sema.addType(ty);
@ -33752,10 +33770,13 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
// values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.enum_tag => unreachable, // it's a value, not a type
.ptr => unreachable,
.enum_tag => unreachable,
},
};
}

View File

@ -142,11 +142,12 @@ pub const Type = struct {
.var_args_param => unreachable,
},
.extern_func,
.int,
.enum_tag,
.simple_value,
=> unreachable, // it's a value, not a type
// values, not types
.extern_func => unreachable,
.int => unreachable,
.ptr => unreachable,
.enum_tag => unreachable,
.simple_value => unreachable,
},
}
}
@ -1576,6 +1577,7 @@ pub const Type = struct {
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.ptr => unreachable,
.enum_tag => unreachable,
},
}
@ -1842,10 +1844,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
// values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.enum_tag => unreachable, // it's a value, not a type
.ptr => unreachable,
.enum_tag => unreachable,
},
}
}
@ -1950,10 +1955,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
// values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.enum_tag => unreachable, // it's a value, not a type
.ptr => unreachable,
.enum_tag => unreachable,
},
};
}
@ -2348,10 +2356,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
// values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.enum_tag => unreachable, // it's a value, not a type
.ptr => unreachable,
.enum_tag => unreachable,
},
}
}
@ -2759,10 +2770,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
// values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.enum_tag => unreachable, // it's a value, not a type
.ptr => unreachable,
.enum_tag => unreachable,
},
}
}
@ -2926,10 +2940,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
// values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.enum_tag => unreachable, // it's a value, not a type
.ptr => unreachable,
.enum_tag => unreachable,
};
const strat: AbiAlignmentAdvancedStrat = if (opt_sema) |sema| .{ .sema = sema } else .eager;
@ -3780,9 +3797,12 @@ pub const Type = struct {
.simple_type => unreachable, // handled via Index enum tag above
.struct_type => @panic("TODO"),
.union_type => unreachable,
// values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.ptr => unreachable,
.enum_tag => unreachable,
},
};
@ -4152,10 +4172,13 @@ pub const Type = struct {
},
.struct_type => @panic("TODO"),
.union_type => @panic("TODO"),
// values, not types
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.enum_tag => unreachable, // it's a value, not a type
.ptr => unreachable,
.enum_tag => unreachable,
},
};
}
@ -4319,6 +4342,7 @@ pub const Type = struct {
.simple_value => unreachable,
.extern_func => unreachable,
.int => unreachable,
.ptr => unreachable,
.enum_tag => unreachable, // it's a value, not a type
},
};