cbe: fix InternPool regressions

This commit is contained in:
Jacob Young
2023-05-29 11:32:12 -04:00
committed by Andrew Kelley
parent a702af062b
commit 5580a69d71
2 changed files with 164 additions and 275 deletions

View File

@@ -707,8 +707,11 @@ pub const Decl = struct {
return TypedValue{ .ty = decl.ty, .val = decl.val };
}
pub fn internValue(decl: Decl, mod: *Module) Allocator.Error!InternPool.Index {
return decl.val.intern(decl.ty, mod);
pub fn internValue(decl: *Decl, mod: *Module) Allocator.Error!InternPool.Index {
assert(decl.has_tv);
const ip_index = try decl.val.intern(decl.ty, mod);
decl.val = ip_index.toValue();
return ip_index;
}
pub fn isFunction(decl: Decl, mod: *const Module) !bool {

View File

@@ -560,7 +560,7 @@ pub const DeclGen = struct {
// them). The analysis until now should ensure that the C function
// pointers are compatible. If they are not, then there is a bug
// somewhere and we should let the C compiler tell us about it.
const need_typecast = if (ty.castPtrToFn(mod)) |_| false else !ty.eql(decl.ty, mod);
const need_typecast = if (ty.castPtrToFn(mod)) |_| false else !ty.childType(mod).eql(decl.ty, mod);
if (need_typecast) {
try writer.writeAll("((");
try dg.renderType(writer, ty);
@@ -581,6 +581,7 @@ pub const DeclGen = struct {
) error{ OutOfMemory, AnalysisFail }!void {
const mod = dg.module;
const ptr_ty = mod.intern_pool.typeOf(ptr_val).toType();
const ptr_cty = try dg.typeToIndex(ptr_ty, .complete);
const ptr = mod.intern_pool.indexToKey(ptr_val).ptr;
switch (ptr.addr) {
.decl, .mut_decl => try dg.renderDeclValue(
@@ -598,22 +599,66 @@ pub const DeclGen = struct {
try dg.fmtIntLiteral(Type.usize, int.toValue(), .Other),
}),
.eu_payload, .opt_payload => |base| {
const base_ty = mod.intern_pool.typeOf(base).toType().childType(mod);
const ptr_base_ty = mod.intern_pool.typeOf(base).toType();
const base_ty = ptr_base_ty.childType(mod);
// Ensure complete type definition is visible before accessing fields.
_ = try dg.typeToIndex(base_ty, .complete);
const payload_ty = switch (ptr.addr) {
.eu_payload => base_ty.errorUnionPayload(mod),
.opt_payload => base_ty.optionalChild(mod),
else => unreachable,
};
const ptr_payload_ty = try mod.adjustPtrTypeChild(ptr_base_ty, payload_ty);
const ptr_payload_cty = try dg.typeToIndex(ptr_payload_ty, .complete);
if (ptr_cty != ptr_payload_cty) {
try writer.writeByte('(');
try dg.renderCType(writer, ptr_cty);
try writer.writeByte(')');
}
try writer.writeAll("&(");
try dg.renderParentPtr(writer, base, location);
try writer.writeAll(")->payload");
},
.elem => |elem| {
const ptr_base_ty = mod.intern_pool.typeOf(elem.base).toType();
const elem_ty = ptr_base_ty.elemType2(mod);
const ptr_elem_ty = try mod.adjustPtrTypeChild(ptr_base_ty, elem_ty);
const ptr_elem_cty = try dg.typeToIndex(ptr_elem_ty, .complete);
if (ptr_cty != ptr_elem_cty) {
try writer.writeByte('(');
try dg.renderCType(writer, ptr_cty);
try writer.writeByte(')');
}
try writer.writeAll("&(");
if (mod.intern_pool.indexToKey(ptr_base_ty.toIntern()).ptr_type.size == .One)
try writer.writeByte('*');
try dg.renderParentPtr(writer, elem.base, location);
try writer.print(")[{d}]", .{elem.index});
},
.field => |field| {
const base_ty = mod.intern_pool.typeOf(field.base).toType().childType(mod);
const ptr_base_ty = mod.intern_pool.typeOf(field.base).toType();
const base_ty = ptr_base_ty.childType(mod);
// Ensure complete type definition is visible before accessing fields.
_ = try dg.typeToIndex(base_ty, .complete);
const field_ty = switch (mod.intern_pool.indexToKey(base_ty.toIntern())) {
.anon_struct_type, .struct_type, .union_type => base_ty.structFieldType(field.index, mod),
.ptr_type => |ptr_type| switch (ptr_type.size) {
.One, .Many, .C => unreachable,
.Slice => switch (field.index) {
Value.slice_ptr_index => base_ty.slicePtrFieldType(mod),
Value.slice_len_index => Type.usize,
else => unreachable,
},
},
else => unreachable,
};
const ptr_field_ty = try mod.adjustPtrTypeChild(ptr_base_ty, field_ty);
const ptr_field_cty = try dg.typeToIndex(ptr_field_ty, .complete);
if (ptr_cty != ptr_field_cty) {
try writer.writeByte('(');
try dg.renderCType(writer, ptr_cty);
try writer.writeByte(')');
}
switch (fieldLocation(base_ty, ptr_ty, @intCast(u32, field.index), mod)) {
.begin => try dg.renderParentPtr(writer, field.base, location),
.field => |name| {
@@ -861,234 +906,6 @@ pub const DeclGen = struct {
unreachable;
}
if (val.ip_index == .none) switch (ty.zigTypeTag(mod)) {
.Array, .Vector => {
if (location == .FunctionArgument) {
try writer.writeByte('(');
try dg.renderType(writer, ty);
try writer.writeByte(')');
}
// First try specific tag representations for more efficiency.
switch (val.toIntern()) {
.undef => {
const ai = ty.arrayInfo(mod);
try writer.writeByte('{');
if (ai.sentinel) |s| {
try dg.renderValue(writer, ai.elem_type, s, initializer_type);
} else {
try writer.writeByte('0');
}
try writer.writeByte('}');
return;
},
.empty_struct => {
const ai = ty.arrayInfo(mod);
try writer.writeByte('{');
if (ai.sentinel) |s| {
try dg.renderValue(writer, ai.elem_type, s, initializer_type);
} else {
try writer.writeByte('0');
}
try writer.writeByte('}');
return;
},
else => {},
}
// Fall back to generic implementation.
// MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal
const max_string_initializer_len = 65535;
const ai = ty.arrayInfo(mod);
if (ai.elem_type.eql(Type.u8, mod)) {
if (ai.len <= max_string_initializer_len) {
var literal = stringLiteral(writer);
try literal.start();
var index: usize = 0;
while (index < ai.len) : (index += 1) {
const elem_val = try val.elemValue(mod, index);
const elem_val_u8 = if (elem_val.isUndef(mod)) undefPattern(u8) else @intCast(u8, elem_val.toUnsignedInt(mod));
try literal.writeChar(elem_val_u8);
}
if (ai.sentinel) |s| {
const s_u8 = @intCast(u8, s.toUnsignedInt(mod));
if (s_u8 != 0) try literal.writeChar(s_u8);
}
try literal.end();
} else {
try writer.writeByte('{');
var index: usize = 0;
while (index < ai.len) : (index += 1) {
if (index != 0) try writer.writeByte(',');
const elem_val = try val.elemValue(mod, index);
const elem_val_u8 = if (elem_val.isUndef(mod)) undefPattern(u8) else @intCast(u8, elem_val.toUnsignedInt(mod));
try writer.print("'\\x{x}'", .{elem_val_u8});
}
if (ai.sentinel) |s| {
if (index != 0) try writer.writeByte(',');
try dg.renderValue(writer, ai.elem_type, s, initializer_type);
}
try writer.writeByte('}');
}
} else {
try writer.writeByte('{');
var index: usize = 0;
while (index < ai.len) : (index += 1) {
if (index != 0) try writer.writeByte(',');
const elem_val = try val.elemValue(mod, index);
try dg.renderValue(writer, ai.elem_type, elem_val, initializer_type);
}
if (ai.sentinel) |s| {
if (index != 0) try writer.writeByte(',');
try dg.renderValue(writer, ai.elem_type, s, initializer_type);
}
try writer.writeByte('}');
}
},
.Struct => switch (ty.containerLayout(mod)) {
.Auto, .Extern => {
const field_vals = val.castTag(.aggregate).?.data;
if (!location.isInitializer()) {
try writer.writeByte('(');
try dg.renderType(writer, ty);
try writer.writeByte(')');
}
try writer.writeByte('{');
var empty = true;
for (field_vals, 0..) |field_val, field_i| {
if (ty.structFieldIsComptime(field_i, mod)) continue;
const field_ty = ty.structFieldType(field_i, mod);
if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
if (!empty) try writer.writeByte(',');
try dg.renderValue(writer, field_ty, field_val, initializer_type);
empty = false;
}
try writer.writeByte('}');
},
.Packed => {
const field_vals = val.castTag(.aggregate).?.data;
const int_info = ty.intInfo(mod);
const bits = Type.smallestUnsignedBits(int_info.bits - 1);
const bit_offset_ty = try mod.intType(.unsigned, bits);
var bit_offset: u64 = 0;
var eff_num_fields: usize = 0;
for (0..field_vals.len) |field_i| {
if (ty.structFieldIsComptime(field_i, mod)) continue;
const field_ty = ty.structFieldType(field_i, mod);
if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
eff_num_fields += 1;
}
if (eff_num_fields == 0) {
try writer.writeByte('(');
try dg.renderValue(writer, ty, Value.undef, initializer_type);
try writer.writeByte(')');
} else if (ty.bitSize(mod) > 64) {
// zig_or_u128(zig_or_u128(zig_shl_u128(a, a_off), zig_shl_u128(b, b_off)), zig_shl_u128(c, c_off))
var num_or = eff_num_fields - 1;
while (num_or > 0) : (num_or -= 1) {
try writer.writeAll("zig_or_");
try dg.renderTypeForBuiltinFnName(writer, ty);
try writer.writeByte('(');
}
var eff_index: usize = 0;
var needs_closing_paren = false;
for (field_vals, 0..) |field_val, field_i| {
if (ty.structFieldIsComptime(field_i, mod)) continue;
const field_ty = ty.structFieldType(field_i, mod);
if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
const cast_context = IntCastContext{ .value = .{ .value = field_val } };
if (bit_offset != 0) {
try writer.writeAll("zig_shl_");
try dg.renderTypeForBuiltinFnName(writer, ty);
try writer.writeByte('(');
try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument);
try writer.writeAll(", ");
const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset);
try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
try writer.writeByte(')');
} else {
try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument);
}
if (needs_closing_paren) try writer.writeByte(')');
if (eff_index != eff_num_fields - 1) try writer.writeAll(", ");
bit_offset += field_ty.bitSize(mod);
needs_closing_paren = true;
eff_index += 1;
}
} else {
try writer.writeByte('(');
// a << a_off | b << b_off | c << c_off
var empty = true;
for (field_vals, 0..) |field_val, field_i| {
if (ty.structFieldIsComptime(field_i, mod)) continue;
const field_ty = ty.structFieldType(field_i, mod);
if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
if (!empty) try writer.writeAll(" | ");
try writer.writeByte('(');
try dg.renderType(writer, ty);
try writer.writeByte(')');
if (bit_offset != 0) {
try dg.renderValue(writer, field_ty, field_val, .Other);
try writer.writeAll(" << ");
const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset);
try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
} else {
try dg.renderValue(writer, field_ty, field_val, .Other);
}
bit_offset += field_ty.bitSize(mod);
empty = false;
}
try writer.writeByte(')');
}
},
},
.Frame,
.AnyFrame,
=> |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{
@tagName(tag),
}),
.Float,
.Union,
.Optional,
.ErrorUnion,
.ErrorSet,
.Int,
.Enum,
.Bool,
.Pointer,
=> unreachable, // handled below
.Type,
.Void,
.NoReturn,
.ComptimeFloat,
.ComptimeInt,
.Undefined,
.Null,
.Opaque,
.EnumLiteral,
.Fn,
=> unreachable, // comptime-only types
};
switch (mod.intern_pool.indexToKey(val.ip_index)) {
// types, not values
.int_type,
@@ -1144,10 +961,24 @@ pub const DeclGen = struct {
.error_union => |error_union| {
const payload_ty = ty.errorUnionPayload(mod);
const error_ty = ty.errorUnionSet(mod);
const error_val = if (val.errorUnionIsPayload(mod)) try mod.intValue(Type.err_int, 0) else val;
if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
return dg.renderValue(writer, Type.err_int, error_val, location);
switch (error_union.val) {
.err_name => |err_name| return dg.renderValue(
writer,
error_ty,
(try mod.intern(.{ .err = .{
.ty = error_ty.toIntern(),
.name = err_name,
} })).toValue(),
location,
),
.payload => return dg.renderValue(
writer,
Type.err_int,
try mod.intValue(Type.err_int, 0),
location,
),
}
}
if (!location.isInitializer()) {
@@ -1156,15 +987,34 @@ pub const DeclGen = struct {
try writer.writeByte(')');
}
const payload_val = switch (error_union.val) {
.err_name => try mod.intern(.{ .undef = payload_ty.ip_index }),
.payload => |payload| payload,
}.toValue();
try writer.writeAll("{ .payload = ");
try dg.renderValue(writer, payload_ty, payload_val, initializer_type);
try dg.renderValue(
writer,
payload_ty,
switch (error_union.val) {
.err_name => try mod.intern(.{ .undef = payload_ty.ip_index }),
.payload => |payload| payload,
}.toValue(),
initializer_type,
);
try writer.writeAll(", .error = ");
try dg.renderValue(writer, error_ty, error_val, initializer_type);
switch (error_union.val) {
.err_name => |err_name| try dg.renderValue(
writer,
error_ty,
(try mod.intern(.{ .err = .{
.ty = error_ty.toIntern(),
.name = err_name,
} })).toValue(),
location,
),
.payload => try dg.renderValue(
writer,
Type.err_int,
try mod.intValue(Type.err_int, 0),
location,
),
}
try writer.writeAll(" }");
},
.enum_tag => {
@@ -1272,30 +1122,42 @@ pub const DeclGen = struct {
}
try writer.writeByte('{');
}
const ptr_location = switch (ptr.len) {
.none => location,
else => initializer_type,
};
const ptr_ty = switch (ptr.len) {
.none => ty,
else => ty.slicePtrFieldType(mod),
};
const ptr_val = switch (ptr.len) {
.none => val,
else => val.slicePtr(mod),
};
switch (ptr.addr) {
.decl, .mut_decl => try dg.renderDeclValue(
writer,
ty,
val,
ptr_ty,
ptr_val,
switch (ptr.addr) {
.decl => |decl| decl,
.mut_decl => |mut_decl| mut_decl.decl,
else => unreachable,
},
location,
ptr_location,
),
.int => |int| {
try writer.writeAll("((");
try dg.renderType(writer, ty);
try dg.renderType(writer, ptr_ty);
try writer.print("){x})", .{
try dg.fmtIntLiteral(Type.usize, int.toValue(), .Other),
try dg.fmtIntLiteral(Type.usize, int.toValue(), ptr_location),
});
},
.eu_payload,
.opt_payload,
.elem,
.field,
=> try dg.renderParentPtr(writer, val.ip_index, location),
=> try dg.renderParentPtr(writer, ptr_val.ip_index, ptr_location),
.comptime_field => unreachable,
}
if (ptr.len != .none) {
@@ -1311,10 +1173,19 @@ pub const DeclGen = struct {
if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod))
return dg.renderValue(writer, Type.bool, is_null_val, location);
if (ty.optionalReprIsPayload(mod)) switch (opt.val) {
.none => return writer.writeByte('0'),
else => |payload| return dg.renderValue(writer, payload_ty, payload.toValue(), location),
};
if (ty.optionalReprIsPayload(mod)) return dg.renderValue(
writer,
payload_ty,
switch (opt.val) {
.none => switch (payload_ty.zigTypeTag(mod)) {
.ErrorSet => try mod.intValue(Type.err_int, 0),
.Pointer => try mod.getCoerced(val, payload_ty),
else => unreachable,
},
else => |payload| payload.toValue(),
},
location,
);
if (!location.isInitializer()) {
try writer.writeByte('(');
@@ -2535,7 +2406,7 @@ pub fn genErrDecls(o: *Object) !void {
try writer.writeAll("enum {\n");
o.indent_writer.pushIndent();
var max_name_len: usize = 0;
for (mod.error_name_list.items, 0..) |name, value| {
for (mod.error_name_list.items[1..], 1..) |name, value| {
max_name_len = std.math.max(name.len, max_name_len);
const err_val = try mod.intern(.{ .err = .{
.ty = .anyerror_type,
@@ -2562,21 +2433,21 @@ pub fn genErrDecls(o: *Object) !void {
.child = .u8_type,
.sentinel = .zero_u8,
});
var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name };
const name_val = Value.initPayload(&name_pl.base);
const name_val = try mod.intern(.{ .aggregate = .{
.ty = name_ty.toIntern(),
.storage = .{ .bytes = name },
} });
try writer.writeAll("static ");
try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, Const, 0, .complete);
try writer.writeAll(" = ");
try o.dg.renderValue(writer, name_ty, name_val, .StaticInitializer);
try o.dg.renderValue(writer, name_ty, name_val.toValue(), .StaticInitializer);
try writer.writeAll(";\n");
}
const name_array_ty = try mod.arrayType(.{
.len = mod.error_name_list.items.len,
.child = .slice_const_u8_sentinel_0_type,
.sentinel = .zero_u8,
});
try writer.writeAll("static ");
@@ -2588,7 +2459,7 @@ pub fn genErrDecls(o: *Object) !void {
const len_val = try mod.intValue(Type.usize, name.len);
try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{
fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other),
fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val, .StaticInitializer),
});
}
try writer.writeAll("};\n");
@@ -2642,10 +2513,10 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
.child = .u8_type,
.sentinel = .zero_u8,
});
var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name };
const name_val = Value.initPayload(&name_pl.base);
const name_val = try mod.intern(.{ .aggregate = .{
.ty = name_ty.toIntern(),
.storage = .{ .bytes = name },
} });
const len_val = try mod.intValue(Type.usize, name.len);
try w.print(" case {}: {{\n static ", .{
@@ -2653,7 +2524,7 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
});
try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete);
try w.writeAll(" = ");
try o.dg.renderValue(w, name_ty, name_val, .Initializer);
try o.dg.renderValue(w, name_ty, name_val.toValue(), .Initializer);
try w.writeAll(";\n return (");
try o.dg.renderType(w, name_slice_ty);
try w.print("){{{}, {}}};\n", .{
@@ -2789,7 +2660,7 @@ pub fn genDecl(o: *Object) !void {
const mod = o.dg.module;
const decl = o.dg.decl.?;
const decl_c_value = .{ .decl = o.dg.decl_index.unwrap().? };
const tv: TypedValue = .{ .ty = decl.ty, .val = decl.val };
const tv: TypedValue = .{ .ty = decl.ty, .val = (try decl.internValue(mod)).toValue() };
if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) return;
if (tv.val.getExternFunc(mod)) |_| {
@@ -4771,6 +4642,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll(") ");
try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false);
try writer.writeByte('\n');
// We don't need to use `genBodyResolveState` for the else block, because this instruction is
// noreturn so must terminate a body, therefore we don't need to leave `value_map` or
@@ -5165,7 +5037,7 @@ fn airIsNull(
TypedValue{ .ty = Type.bool, .val = Value.true }
else if (optional_ty.isPtrLikeOptional(mod))
// operand is a regular pointer, test `operand !=/== NULL`
TypedValue{ .ty = optional_ty, .val = try mod.nullValue(optional_ty) }
TypedValue{ .ty = optional_ty, .val = try mod.getCoerced(Value.null, optional_ty) }
else if (payload_ty.zigTypeTag(mod) == .ErrorSet)
TypedValue{ .ty = Type.err_int, .val = try mod.intValue(Type.err_int, 0) }
else if (payload_ty.isSlice(mod) and optional_ty.optionalReprIsPayload(mod)) rhs: {
@@ -5778,7 +5650,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
try reap(f, inst, &.{ty_op.operand});
try f.writeCValueDeref(writer, operand);
try writer.writeAll(".error = ");
try f.object.dg.renderValue(writer, error_ty, try mod.intValue(error_ty, 0), .Other);
try f.object.dg.renderValue(writer, Type.err_int, try mod.intValue(Type.err_int, 0), .Other);
try writer.writeAll(";\n");
// Then return the payload pointer (only if it is used)
@@ -6760,27 +6632,41 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll(" = ");
try f.object.dg.renderValue(writer, scalar_ty, switch (reduce.operation) {
.Or, .Xor, .Add => try mod.intValue(scalar_ty, 0),
.Or, .Xor => switch (scalar_ty.zigTypeTag(mod)) {
.Bool => Value.false,
.Int => try mod.intValue(scalar_ty, 0),
else => unreachable,
},
.And => switch (scalar_ty.zigTypeTag(mod)) {
.Bool => try mod.intValue(Type.comptime_int, 1),
else => switch (scalar_ty.intInfo(mod).signedness) {
.Bool => Value.true,
.Int => switch (scalar_ty.intInfo(mod).signedness) {
.unsigned => try scalar_ty.maxIntScalar(mod, scalar_ty),
.signed => try mod.intValue(scalar_ty, -1),
},
else => unreachable,
},
.Add => switch (scalar_ty.zigTypeTag(mod)) {
.Int => try mod.intValue(scalar_ty, 0),
.Float => try mod.floatValue(scalar_ty, 0.0),
else => unreachable,
},
.Mul => switch (scalar_ty.zigTypeTag(mod)) {
.Int => try mod.intValue(scalar_ty, 1),
.Float => try mod.floatValue(scalar_ty, 1.0),
else => unreachable,
},
.Min => switch (scalar_ty.zigTypeTag(mod)) {
.Bool => Value.one_comptime_int,
.Bool => Value.true,
.Int => try scalar_ty.maxIntScalar(mod, scalar_ty),
.Float => try mod.floatValue(scalar_ty, std.math.nan_f128),
else => unreachable,
},
.Max => switch (scalar_ty.zigTypeTag(mod)) {
.Bool => try mod.intValue(scalar_ty, 0),
.Int => try scalar_ty.minInt(mod, scalar_ty),
.Bool => Value.false,
.Int => try scalar_ty.minIntScalar(mod, scalar_ty),
.Float => try mod.floatValue(scalar_ty, std.math.nan_f128),
else => unreachable,
},
.Mul => try mod.intValue(Type.comptime_int, 1),
}, .Initializer);
try writer.writeAll(";\n");