srctree

Jacob Young parent 9ab6d910 4e855366
Builder: fix encoding big integers in bitcode

Closes #19543

inlinesplit
src/codegen/c.zig added: 188, removed: 116, total 72
@@ -4120,7 +4120,7 @@ fn airEquality(
 
const operand_ty = f.typeOf(bin_op.lhs);
const operand_bits = operand_ty.bitSize(zcu);
if (operand_ty.isInt(zcu) and operand_bits > 64)
if (operand_ty.isAbiInt(zcu) and operand_bits > 64)
return airCmpBuiltinCall(
f,
inst,
@@ -4474,9 +4474,9 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
const owner_decl = zcu.funcOwnerDeclPtr(extra.data.func);
const writer = f.object.writer();
try writer.writeAll("/* ");
try writer.writeAll("/* inline:");
try owner_decl.renderFullyQualifiedName(zcu, writer);
try writer.writeAll(" */ ");
try writer.writeAll(" */\n");
return lowerBlock(f, inst, @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len]));
}
 
 
src/codegen/llvm/Builder.zig added: 188, removed: 116, total 72
@@ -7339,28 +7339,36 @@ pub const Constant = enum(u32) {
.positive_integer,
.negative_integer,
=> |tag| {
const extra: *align(@alignOf(std.math.big.Limb)) Integer =
const extra: *align(@alignOf(std.math.big.Limb)) const Integer =
@ptrCast(data.builder.constant_limbs.items[item.data..][0..Integer.limbs]);
const limbs = data.builder.constant_limbs
.items[item.data + Integer.limbs ..][0..extra.limbs_len];
const bigint: std.math.big.int.Const = .{
.limbs = limbs,
.positive = tag == .positive_integer,
.positive = switch (tag) {
.positive_integer => true,
.negative_integer => false,
else => unreachable,
},
};
const ExpectedContents = extern struct {
string: [(64 * 8 / std.math.log2(10)) + 2]u8,
const expected_limbs = @divExact(512, @bitSizeOf(std.math.big.Limb));
string: [
(std.math.big.int.Const{
.limbs = &([1]std.math.big.Limb{
std.math.maxInt(std.math.big.Limb),
} ** expected_limbs),
.positive = false,
}).sizeInBaseUpperBound(10)
]u8,
limbs: [
std.math.big.int.calcToStringLimbsBufferLen(
64 / @sizeOf(std.math.big.Limb),
10,
)
std.math.big.int.calcToStringLimbsBufferLen(expected_limbs, 10)
]std.math.big.Limb,
};
var stack align(@alignOf(ExpectedContents)) =
std.heap.stackFallback(@sizeOf(ExpectedContents), data.builder.gpa);
const allocator = stack.get();
const str = bigint.toStringAlloc(allocator, 10, undefined) catch
return writer.writeAll("...");
const str = try bigint.toStringAlloc(allocator, 10, undefined);
defer allocator.free(str);
try writer.writeAll(str);
},
@@ -9464,10 +9472,38 @@ pub fn print(self: *Builder, writer: anytype) (@TypeOf(writer).Error || Allocato
try bw.flush();
}
 
fn WriterWithErrors(comptime BackingWriter: type, comptime ExtraErrors: type) type {
return struct {
backing_writer: BackingWriter,
 
pub const Error = BackingWriter.Error || ExtraErrors;
pub const Writer = std.io.Writer(*const Self, Error, write);
 
const Self = @This();
 
pub fn writer(self: *const Self) Writer {
return .{ .context = self };
}
 
pub fn write(self: *const Self, bytes: []const u8) Error!usize {
return self.backing_writer.write(bytes);
}
};
}
fn writerWithErrors(
backing_writer: anytype,
comptime ExtraErrors: type,
) WriterWithErrors(@TypeOf(backing_writer), ExtraErrors) {
return .{ .backing_writer = backing_writer };
}
 
pub fn printUnbuffered(
self: *Builder,
writer: anytype,
) (@TypeOf(writer).Error || Allocator.Error)!void {
backing_writer: anytype,
) (@TypeOf(backing_writer).Error || Allocator.Error)!void {
const writer_with_errors = writerWithErrors(backing_writer, Allocator.Error);
const writer = writer_with_errors.writer();
 
var need_newline = false;
var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };
defer metadata_formatter.map.deinit(self.gpa);
@@ -10344,12 +10380,17 @@ pub fn printUnbuffered(
const extra = self.metadataExtraData(Metadata.Enumerator, metadata_item.data);
 
const ExpectedContents = extern struct {
string: [(64 * 8 / std.math.log2(10)) + 2]u8,
const expected_limbs = @divExact(512, @bitSizeOf(std.math.big.Limb));
string: [
(std.math.big.int.Const{
.limbs = &([1]std.math.big.Limb{
std.math.maxInt(std.math.big.Limb),
} ** expected_limbs),
.positive = false,
}).sizeInBaseUpperBound(10)
]u8,
limbs: [
std.math.big.int.calcToStringLimbsBufferLen(
64 / @sizeOf(std.math.big.Limb),
10,
)
std.math.big.int.calcToStringLimbsBufferLen(expected_limbs, 10)
]std.math.big.Limb,
};
var stack align(@alignOf(ExpectedContents)) =
@@ -10375,7 +10416,9 @@ pub fn printUnbuffered(
.value = str,
.isUnsigned = switch (kind) {
.enumerator_unsigned => true,
.enumerator_signed_positive, .enumerator_signed_negative => false,
.enumerator_signed_positive,
.enumerator_signed_negative,
=> false,
else => unreachable,
},
}, writer);
@@ -13787,37 +13830,42 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
=> |tag| {
const extra: *align(@alignOf(std.math.big.Limb)) Constant.Integer =
@ptrCast(self.constant_limbs.items[data..][0..Constant.Integer.limbs]);
const limbs = self.constant_limbs
.items[data + Constant.Integer.limbs ..][0..extra.limbs_len];
const bigint: std.math.big.int.Const = .{
.limbs = limbs,
.positive = tag == .positive_integer,
.limbs = self.constant_limbs
.items[data + Constant.Integer.limbs ..][0..extra.limbs_len],
.positive = switch (tag) {
.positive_integer => true,
.negative_integer => false,
else => unreachable,
},
};
 
const bit_count = extra.type.scalarBits(self);
if (bit_count <= 64) {
const val = bigint.to(i64) catch unreachable;
const emit_val = if (tag == .positive_integer)
@shlWithOverflow(val, 1)[0]
else
(@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);
try constants_block.writeAbbrev(Constants.Integer{ .value = @bitCast(emit_val) });
} else {
const word_count = std.mem.alignForward(u24, bit_count, 64) / 64;
try record.ensureUnusedCapacity(self.gpa, word_count);
const buffer: [*]u8 = @ptrCast(record.items.ptr);
bigint.writeTwosComplement(buffer[0..(word_count * 8)], .little);
 
const signed_buffer: [*]i64 = @ptrCast(record.items.ptr);
for (signed_buffer[0..word_count], 0..) |val, i| {
signed_buffer[i] = if (val >= 0)
@shlWithOverflow(val, 1)[0]
const val: i64 = if (bit_count <= 64)
bigint.to(i64) catch unreachable
else if (bigint.to(u64)) |val|
@bitCast(val)
else |_| {
const limbs = try record.addManyAsSlice(
self.gpa,
std.math.divCeil(u24, bit_count, 64) catch unreachable,
);
bigint.writeTwosComplement(std.mem.sliceAsBytes(limbs), .little);
for (limbs) |*limb| {
const val = std.mem.littleToNative(i64, @bitCast(limb.*));
limb.* = @bitCast(if (val >= 0)
val << 1 | 0
else
(@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);
-%val << 1 | 1);
}
 
try constants_block.writeUnabbrev(5, record.items.ptr[0..word_count]);
}
try constants_block.writeUnabbrev(5, record.items);
continue;
};
try constants_block.writeAbbrev(Constants.Integer{
.value = @bitCast(if (val >= 0)
val << 1 | 0
else
-%val << 1 | 1),
});
},
.half,
.bfloat,
@@ -14186,6 +14234,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
self.metadata_items.items(.tag)[1..],
self.metadata_items.items(.data)[1..],
) |tag, data| {
record.clearRetainingCapacity();
switch (tag) {
.none => unreachable,
.file => {
@@ -14333,77 +14382,60 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
.enumerator_signed_positive,
.enumerator_signed_negative,
=> |kind| {
const positive = switch (kind) {
.enumerator_unsigned,
.enumerator_signed_positive,
=> true,
.enumerator_signed_negative => false,
else => unreachable,
};
 
const unsigned = switch (kind) {
.enumerator_unsigned => true,
.enumerator_signed_positive,
.enumerator_signed_negative,
=> false,
else => unreachable,
};
 
const extra = self.metadataExtraData(Metadata.Enumerator, data);
 
const limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len];
 
const bigint: std.math.big.int.Const = .{
.limbs = limbs,
.positive = positive,
.limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len],
.positive = switch (kind) {
.enumerator_unsigned,
.enumerator_signed_positive,
=> true,
.enumerator_signed_negative => false,
else => unreachable,
},
};
 
if (extra.bit_width <= 64) {
const val = bigint.to(i64) catch unreachable;
const emit_val = if (positive)
@shlWithOverflow(val, 1)[0]
else
(@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);
try metadata_block.writeAbbrevAdapted(MetadataBlock.Enumerator{
.flags = .{
.unsigned = unsigned,
},
.bit_width = extra.bit_width,
.name = extra.name,
.value = @bitCast(emit_val),
}, metadata_adapter);
} else {
const word_count = std.mem.alignForward(u32, extra.bit_width, 64) / 64;
try record.ensureUnusedCapacity(self.gpa, 3 + word_count);
 
const flags: MetadataBlock.Enumerator.Flags = .{
.unsigned = unsigned,
};
 
const FlagsInt = @typeInfo(MetadataBlock.Enumerator.Flags).Struct.backing_integer.?;
 
const flags_int: FlagsInt = @bitCast(flags);
 
record.appendAssumeCapacity(@intCast(flags_int));
record.appendAssumeCapacity(@intCast(extra.bit_width));
const flags: MetadataBlock.Enumerator.Flags = .{
.unsigned = switch (kind) {
.enumerator_unsigned => true,
.enumerator_signed_positive,
.enumerator_signed_negative,
=> false,
else => unreachable,
},
};
const val: i64 = if (bigint.to(i64)) |val|
val
else |_| if (bigint.to(u64)) |val|
@bitCast(val)
else |_| {
const limbs_len = std.math.divCeil(u32, extra.bit_width, 64) catch unreachable;
try record.ensureTotalCapacity(self.gpa, 3 + limbs_len);
record.appendAssumeCapacity(@as(
@typeInfo(MetadataBlock.Enumerator.Flags).Struct.backing_integer.?,
@bitCast(flags),
));
record.appendAssumeCapacity(extra.bit_width);
record.appendAssumeCapacity(metadata_adapter.getMetadataStringIndex(extra.name));
 
const buffer: [*]u8 = @ptrCast(record.items.ptr);
bigint.writeTwosComplement(buffer[0..(word_count * 8)], .little);
 
const signed_buffer: [*]i64 = @ptrCast(record.items.ptr);
for (signed_buffer[0..word_count], 0..) |val, i| {
signed_buffer[i] = if (val >= 0)
@shlWithOverflow(val, 1)[0]
const limbs = record.addManyAsSliceAssumeCapacity(limbs_len);
bigint.writeTwosComplement(std.mem.sliceAsBytes(limbs), .little);
for (limbs) |*limb| {
const val = std.mem.littleToNative(i64, @bitCast(limb.*));
limb.* = @bitCast(if (val >= 0)
val << 1 | 0
else
(@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);
-%val << 1 | 1);
}
 
try metadata_block.writeUnabbrev(
MetadataBlock.Enumerator.id,
record.items.ptr[0..(3 + word_count)],
);
}
try metadata_block.writeUnabbrev(MetadataBlock.Enumerator.id, record.items);
continue;
};
try metadata_block.writeAbbrevAdapted(MetadataBlock.Enumerator{
.flags = flags,
.bit_width = extra.bit_width,
.name = extra.name,
.value = @bitCast(if (val >= 0)
val << 1 | 0
else
-%val << 1 | 1),
}, metadata_adapter);
},
.subrange => {
const extra = self.metadataExtraData(Metadata.Subrange, data);
@@ -14491,7 +14523,6 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
}, metadata_adapter);
},
}
record.clearRetainingCapacity();
}
 
// Write named metadata
@@ -14615,7 +14646,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
}
 
pub fn getOffsetValueIndex(adapter: @This(), value: Value) u32 {
return @subWithOverflow(adapter.offset(), adapter.getValueIndex(value))[0];
return adapter.offset() -% adapter.getValueIndex(value);
}
 
pub fn getOffsetValueSignedIndex(adapter: @This(), value: Value) i32 {
 
test/behavior/enum.zig added: 188, removed: 116, total 72
@@ -1263,3 +1263,44 @@ test "matching captures causes enum equivalence" {
comptime assert(@TypeOf(a) == @TypeOf(b));
try expect(@intFromEnum(a) == @intFromEnum(b));
}
 
test "large enum field values" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
 
{
const E = enum(u64) { min = std.math.minInt(u64), max = std.math.maxInt(u64) };
var e: E = .min;
try expect(e == .min);
try expect(@intFromEnum(e) == std.math.minInt(u64));
e = .max;
try expect(e == .max);
try expect(@intFromEnum(e) == std.math.maxInt(u64));
}
{
const E = enum(i64) { min = std.math.minInt(i64), max = std.math.maxInt(i64) };
var e: E = .min;
try expect(e == .min);
try expect(@intFromEnum(e) == std.math.minInt(i64));
e = .max;
try expect(e == .max);
try expect(@intFromEnum(e) == std.math.maxInt(i64));
}
{
const E = enum(u128) { min = std.math.minInt(u128), max = std.math.maxInt(u128) };
var e: E = .min;
try expect(e == .min);
try expect(@intFromEnum(e) == std.math.minInt(u128));
e = .max;
try expect(e == .max);
try expect(@intFromEnum(e) == std.math.maxInt(u128));
}
{
const E = enum(i128) { min = std.math.minInt(i128), max = std.math.maxInt(i128) };
var e: E = .min;
try expect(e == .min);
try expect(@intFromEnum(e) == std.math.minInt(i128));
e = .max;
try expect(e == .max);
try expect(@intFromEnum(e) == std.math.maxInt(i128));
}
}