authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-04-05 02:48:00-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-06 12:53:09-07:00
log4e8553660405a063d8abd335a600714af121aed9
treec4522378f427ddb62c6eb4c638d55c10312d4beb
parent9ab6d9106787e76ecba817b40c804be38aa6cfe7

Builder: fix encoding big integers in bitcode

Closes #19543

3 files changed, 185 insertions(+), 113 deletions(-)

src/codegen/c.zig+3-3
......@@ -4120,7 +4120,7 @@ fn airEquality(
41204120
41214121 const operand_ty = f.typeOf(bin_op.lhs);
41224122 const operand_bits = operand_ty.bitSize(zcu);
4123 if (operand_ty.isInt(zcu) and operand_bits > 64)
4123 if (operand_ty.isAbiInt(zcu) and operand_bits > 64)
41244124 return airCmpBuiltinCall(
41254125 f,
41264126 inst,
......@@ -4474,9 +4474,9 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
44744474 const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
44754475 const owner_decl = zcu.funcOwnerDeclPtr(extra.data.func);
44764476 const writer = f.object.writer();
4477 try writer.writeAll("/* ");
4477 try writer.writeAll("/* inline:");
44784478 try owner_decl.renderFullyQualifiedName(zcu, writer);
4479 try writer.writeAll(" */ ");
4479 try writer.writeAll(" */\n");
44804480 return lowerBlock(f, inst, @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len]));
44814481}
44824482
src/codegen/llvm/Builder.zig+141-110
......@@ -7339,28 +7339,36 @@ pub const Constant = enum(u32) {
73397339 .positive_integer,
73407340 .negative_integer,
73417341 => |tag| {
7342 const extra: *align(@alignOf(std.math.big.Limb)) Integer =
7342 const extra: *align(@alignOf(std.math.big.Limb)) const Integer =
73437343 @ptrCast(data.builder.constant_limbs.items[item.data..][0..Integer.limbs]);
73447344 const limbs = data.builder.constant_limbs
73457345 .items[item.data + Integer.limbs ..][0..extra.limbs_len];
73467346 const bigint: std.math.big.int.Const = .{
73477347 .limbs = limbs,
7348 .positive = tag == .positive_integer,
7348 .positive = switch (tag) {
7349 .positive_integer => true,
7350 .negative_integer => false,
7351 else => unreachable,
7352 },
73497353 };
73507354 const ExpectedContents = extern struct {
7351 string: [(64 * 8 / std.math.log2(10)) + 2]u8,
7355 const expected_limbs = @divExact(512, @bitSizeOf(std.math.big.Limb));
7356 string: [
7357 (std.math.big.int.Const{
7358 .limbs = &([1]std.math.big.Limb{
7359 std.math.maxInt(std.math.big.Limb),
7360 } ** expected_limbs),
7361 .positive = false,
7362 }).sizeInBaseUpperBound(10)
7363 ]u8,
73527364 limbs: [
7353 std.math.big.int.calcToStringLimbsBufferLen(
7354 64 / @sizeOf(std.math.big.Limb),
7355 10,
7356 )
7365 std.math.big.int.calcToStringLimbsBufferLen(expected_limbs, 10)
73577366 ]std.math.big.Limb,
73587367 };
73597368 var stack align(@alignOf(ExpectedContents)) =
73607369 std.heap.stackFallback(@sizeOf(ExpectedContents), data.builder.gpa);
73617370 const allocator = stack.get();
7362 const str = bigint.toStringAlloc(allocator, 10, undefined) catch
7363 return writer.writeAll("...");
7371 const str = try bigint.toStringAlloc(allocator, 10, undefined);
73647372 defer allocator.free(str);
73657373 try writer.writeAll(str);
73667374 },
......@@ -9464,10 +9472,38 @@ pub fn print(self: *Builder, writer: anytype) (@TypeOf(writer).Error || Allocato
94649472 try bw.flush();
94659473}
94669474
9475fn WriterWithErrors(comptime BackingWriter: type, comptime ExtraErrors: type) type {
9476 return struct {
9477 backing_writer: BackingWriter,
9478
9479 pub const Error = BackingWriter.Error || ExtraErrors;
9480 pub const Writer = std.io.Writer(*const Self, Error, write);
9481
9482 const Self = @This();
9483
9484 pub fn writer(self: *const Self) Writer {
9485 return .{ .context = self };
9486 }
9487
9488 pub fn write(self: *const Self, bytes: []const u8) Error!usize {
9489 return self.backing_writer.write(bytes);
9490 }
9491 };
9492}
9493fn writerWithErrors(
9494 backing_writer: anytype,
9495 comptime ExtraErrors: type,
9496) WriterWithErrors(@TypeOf(backing_writer), ExtraErrors) {
9497 return .{ .backing_writer = backing_writer };
9498}
9499
94679500pub fn printUnbuffered(
94689501 self: *Builder,
9469 writer: anytype,
9470) (@TypeOf(writer).Error || Allocator.Error)!void {
9502 backing_writer: anytype,
9503) (@TypeOf(backing_writer).Error || Allocator.Error)!void {
9504 const writer_with_errors = writerWithErrors(backing_writer, Allocator.Error);
9505 const writer = writer_with_errors.writer();
9506
94719507 var need_newline = false;
94729508 var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };
94739509 defer metadata_formatter.map.deinit(self.gpa);
......@@ -10344,12 +10380,17 @@ pub fn printUnbuffered(
1034410380 const extra = self.metadataExtraData(Metadata.Enumerator, metadata_item.data);
1034510381
1034610382 const ExpectedContents = extern struct {
10347 string: [(64 * 8 / std.math.log2(10)) + 2]u8,
10383 const expected_limbs = @divExact(512, @bitSizeOf(std.math.big.Limb));
10384 string: [
10385 (std.math.big.int.Const{
10386 .limbs = &([1]std.math.big.Limb{
10387 std.math.maxInt(std.math.big.Limb),
10388 } ** expected_limbs),
10389 .positive = false,
10390 }).sizeInBaseUpperBound(10)
10391 ]u8,
1034810392 limbs: [
10349 std.math.big.int.calcToStringLimbsBufferLen(
10350 64 / @sizeOf(std.math.big.Limb),
10351 10,
10352 )
10393 std.math.big.int.calcToStringLimbsBufferLen(expected_limbs, 10)
1035310394 ]std.math.big.Limb,
1035410395 };
1035510396 var stack align(@alignOf(ExpectedContents)) =
......@@ -10375,7 +10416,9 @@ pub fn printUnbuffered(
1037510416 .value = str,
1037610417 .isUnsigned = switch (kind) {
1037710418 .enumerator_unsigned => true,
10378 .enumerator_signed_positive, .enumerator_signed_negative => false,
10419 .enumerator_signed_positive,
10420 .enumerator_signed_negative,
10421 => false,
1037910422 else => unreachable,
1038010423 },
1038110424 }, writer);
......@@ -13787,37 +13830,42 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1378713830 => |tag| {
1378813831 const extra: *align(@alignOf(std.math.big.Limb)) Constant.Integer =
1378913832 @ptrCast(self.constant_limbs.items[data..][0..Constant.Integer.limbs]);
13790 const limbs = self.constant_limbs
13791 .items[data + Constant.Integer.limbs ..][0..extra.limbs_len];
1379213833 const bigint: std.math.big.int.Const = .{
13793 .limbs = limbs,
13794 .positive = tag == .positive_integer,
13834 .limbs = self.constant_limbs
13835 .items[data + Constant.Integer.limbs ..][0..extra.limbs_len],
13836 .positive = switch (tag) {
13837 .positive_integer => true,
13838 .negative_integer => false,
13839 else => unreachable,
13840 },
1379513841 };
13796
1379713842 const bit_count = extra.type.scalarBits(self);
13798 if (bit_count <= 64) {
13799 const val = bigint.to(i64) catch unreachable;
13800 const emit_val = if (tag == .positive_integer)
13801 @shlWithOverflow(val, 1)[0]
13802 else
13803 (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);
13804 try constants_block.writeAbbrev(Constants.Integer{ .value = @bitCast(emit_val) });
13805 } else {
13806 const word_count = std.mem.alignForward(u24, bit_count, 64) / 64;
13807 try record.ensureUnusedCapacity(self.gpa, word_count);
13808 const buffer: [*]u8 = @ptrCast(record.items.ptr);
13809 bigint.writeTwosComplement(buffer[0..(word_count * 8)], .little);
13810
13811 const signed_buffer: [*]i64 = @ptrCast(record.items.ptr);
13812 for (signed_buffer[0..word_count], 0..) |val, i| {
13813 signed_buffer[i] = if (val >= 0)
13814 @shlWithOverflow(val, 1)[0]
13843 const val: i64 = if (bit_count <= 64)
13844 bigint.to(i64) catch unreachable
13845 else if (bigint.to(u64)) |val|
13846 @bitCast(val)
13847 else |_| {
13848 const limbs = try record.addManyAsSlice(
13849 self.gpa,
13850 std.math.divCeil(u24, bit_count, 64) catch unreachable,
13851 );
13852 bigint.writeTwosComplement(std.mem.sliceAsBytes(limbs), .little);
13853 for (limbs) |*limb| {
13854 const val = std.mem.littleToNative(i64, @bitCast(limb.*));
13855 limb.* = @bitCast(if (val >= 0)
13856 val << 1 | 0
1381513857 else
13816 (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);
13858 -%val << 1 | 1);
1381713859 }
13818
13819 try constants_block.writeUnabbrev(5, record.items.ptr[0..word_count]);
13820 }
13860 try constants_block.writeUnabbrev(5, record.items);
13861 continue;
13862 };
13863 try constants_block.writeAbbrev(Constants.Integer{
13864 .value = @bitCast(if (val >= 0)
13865 val << 1 | 0
13866 else
13867 -%val << 1 | 1),
13868 });
1382113869 },
1382213870 .half,
1382313871 .bfloat,
......@@ -14186,6 +14234,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1418614234 self.metadata_items.items(.tag)[1..],
1418714235 self.metadata_items.items(.data)[1..],
1418814236 ) |tag, data| {
14237 record.clearRetainingCapacity();
1418914238 switch (tag) {
1419014239 .none => unreachable,
1419114240 .file => {
......@@ -14333,77 +14382,60 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1433314382 .enumerator_signed_positive,
1433414383 .enumerator_signed_negative,
1433514384 => |kind| {
14336 const positive = switch (kind) {
14337 .enumerator_unsigned,
14338 .enumerator_signed_positive,
14339 => true,
14340 .enumerator_signed_negative => false,
14341 else => unreachable,
14342 };
14343
14344 const unsigned = switch (kind) {
14345 .enumerator_unsigned => true,
14346 .enumerator_signed_positive,
14347 .enumerator_signed_negative,
14348 => false,
14349 else => unreachable,
14350 };
14351
1435214385 const extra = self.metadataExtraData(Metadata.Enumerator, data);
14353
14354 const limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len];
14355
1435614386 const bigint: std.math.big.int.Const = .{
14357 .limbs = limbs,
14358 .positive = positive,
14387 .limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len],
14388 .positive = switch (kind) {
14389 .enumerator_unsigned,
14390 .enumerator_signed_positive,
14391 => true,
14392 .enumerator_signed_negative => false,
14393 else => unreachable,
14394 },
1435914395 };
14360
14361 if (extra.bit_width <= 64) {
14362 const val = bigint.to(i64) catch unreachable;
14363 const emit_val = if (positive)
14364 @shlWithOverflow(val, 1)[0]
14365 else
14366 (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);
14367 try metadata_block.writeAbbrevAdapted(MetadataBlock.Enumerator{
14368 .flags = .{
14369 .unsigned = unsigned,
14370 },
14371 .bit_width = extra.bit_width,
14372 .name = extra.name,
14373 .value = @bitCast(emit_val),
14374 }, metadata_adapter);
14375 } else {
14376 const word_count = std.mem.alignForward(u32, extra.bit_width, 64) / 64;
14377 try record.ensureUnusedCapacity(self.gpa, 3 + word_count);
14378
14379 const flags: MetadataBlock.Enumerator.Flags = .{
14380 .unsigned = unsigned,
14381 };
14382
14383 const FlagsInt = @typeInfo(MetadataBlock.Enumerator.Flags).Struct.backing_integer.?;
14384
14385 const flags_int: FlagsInt = @bitCast(flags);
14386
14387 record.appendAssumeCapacity(@intCast(flags_int));
14388 record.appendAssumeCapacity(@intCast(extra.bit_width));
14396 const flags: MetadataBlock.Enumerator.Flags = .{
14397 .unsigned = switch (kind) {
14398 .enumerator_unsigned => true,
14399 .enumerator_signed_positive,
14400 .enumerator_signed_negative,
14401 => false,
14402 else => unreachable,
14403 },
14404 };
14405 const val: i64 = if (bigint.to(i64)) |val|
14406 val
14407 else |_| if (bigint.to(u64)) |val|
14408 @bitCast(val)
14409 else |_| {
14410 const limbs_len = std.math.divCeil(u32, extra.bit_width, 64) catch unreachable;
14411 try record.ensureTotalCapacity(self.gpa, 3 + limbs_len);
14412 record.appendAssumeCapacity(@as(
14413 @typeInfo(MetadataBlock.Enumerator.Flags).Struct.backing_integer.?,
14414 @bitCast(flags),
14415 ));
14416 record.appendAssumeCapacity(extra.bit_width);
1438914417 record.appendAssumeCapacity(metadata_adapter.getMetadataStringIndex(extra.name));
14390
14391 const buffer: [*]u8 = @ptrCast(record.items.ptr);
14392 bigint.writeTwosComplement(buffer[0..(word_count * 8)], .little);
14393
14394 const signed_buffer: [*]i64 = @ptrCast(record.items.ptr);
14395 for (signed_buffer[0..word_count], 0..) |val, i| {
14396 signed_buffer[i] = if (val >= 0)
14397 @shlWithOverflow(val, 1)[0]
14418 const limbs = record.addManyAsSliceAssumeCapacity(limbs_len);
14419 bigint.writeTwosComplement(std.mem.sliceAsBytes(limbs), .little);
14420 for (limbs) |*limb| {
14421 const val = std.mem.littleToNative(i64, @bitCast(limb.*));
14422 limb.* = @bitCast(if (val >= 0)
14423 val << 1 | 0
1439814424 else
14399 (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);
14425 -%val << 1 | 1);
1440014426 }
14401
14402 try metadata_block.writeUnabbrev(
14403 MetadataBlock.Enumerator.id,
14404 record.items.ptr[0..(3 + word_count)],
14405 );
14406 }
14427 try metadata_block.writeUnabbrev(MetadataBlock.Enumerator.id, record.items);
14428 continue;
14429 };
14430 try metadata_block.writeAbbrevAdapted(MetadataBlock.Enumerator{
14431 .flags = flags,
14432 .bit_width = extra.bit_width,
14433 .name = extra.name,
14434 .value = @bitCast(if (val >= 0)
14435 val << 1 | 0
14436 else
14437 -%val << 1 | 1),
14438 }, metadata_adapter);
1440714439 },
1440814440 .subrange => {
1440914441 const extra = self.metadataExtraData(Metadata.Subrange, data);
......@@ -14491,7 +14523,6 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1449114523 }, metadata_adapter);
1449214524 },
1449314525 }
14494 record.clearRetainingCapacity();
1449514526 }
1449614527
1449714528 // Write named metadata
......@@ -14615,7 +14646,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1461514646 }
1461614647
1461714648 pub fn getOffsetValueIndex(adapter: @This(), value: Value) u32 {
14618 return @subWithOverflow(adapter.offset(), adapter.getValueIndex(value))[0];
14649 return adapter.offset() -% adapter.getValueIndex(value);
1461914650 }
1462014651
1462114652 pub fn getOffsetValueSignedIndex(adapter: @This(), value: Value) i32 {
test/behavior/enum.zig+41
......@@ -1263,3 +1263,44 @@ test "matching captures causes enum equivalence" {
12631263 comptime assert(@TypeOf(a) == @TypeOf(b));
12641264 try expect(@intFromEnum(a) == @intFromEnum(b));
12651265}
1266
1267test "large enum field values" {
1268 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1269
1270 {
1271 const E = enum(u64) { min = std.math.minInt(u64), max = std.math.maxInt(u64) };
1272 var e: E = .min;
1273 try expect(e == .min);
1274 try expect(@intFromEnum(e) == std.math.minInt(u64));
1275 e = .max;
1276 try expect(e == .max);
1277 try expect(@intFromEnum(e) == std.math.maxInt(u64));
1278 }
1279 {
1280 const E = enum(i64) { min = std.math.minInt(i64), max = std.math.maxInt(i64) };
1281 var e: E = .min;
1282 try expect(e == .min);
1283 try expect(@intFromEnum(e) == std.math.minInt(i64));
1284 e = .max;
1285 try expect(e == .max);
1286 try expect(@intFromEnum(e) == std.math.maxInt(i64));
1287 }
1288 {
1289 const E = enum(u128) { min = std.math.minInt(u128), max = std.math.maxInt(u128) };
1290 var e: E = .min;
1291 try expect(e == .min);
1292 try expect(@intFromEnum(e) == std.math.minInt(u128));
1293 e = .max;
1294 try expect(e == .max);
1295 try expect(@intFromEnum(e) == std.math.maxInt(u128));
1296 }
1297 {
1298 const E = enum(i128) { min = std.math.minInt(i128), max = std.math.maxInt(i128) };
1299 var e: E = .min;
1300 try expect(e == .min);
1301 try expect(@intFromEnum(e) == std.math.minInt(i128));
1302 e = .max;
1303 try expect(e == .max);
1304 try expect(@intFromEnum(e) == std.math.maxInt(i128));
1305 }
1306}