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(...@@ -4120,7 +4120,7 @@ fn airEquality(
41204120
4121 const operand_ty = f.typeOf(bin_op.lhs);4121 const operand_ty = f.typeOf(bin_op.lhs);
4122 const operand_bits = operand_ty.bitSize(zcu);4122 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)
4124 return airCmpBuiltinCall(4124 return airCmpBuiltinCall(
4125 f,4125 f,
4126 inst,4126 inst,
...@@ -4474,9 +4474,9 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4474,9 +4474,9 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
4474 const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload);4474 const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
4475 const owner_decl = zcu.funcOwnerDeclPtr(extra.data.func);4475 const owner_decl = zcu.funcOwnerDeclPtr(extra.data.func);
4476 const writer = f.object.writer();4476 const writer = f.object.writer();
4477 try writer.writeAll("/* ");4477 try writer.writeAll("/* inline:");
4478 try owner_decl.renderFullyQualifiedName(zcu, writer);4478 try owner_decl.renderFullyQualifiedName(zcu, writer);
4479 try writer.writeAll(" */ ");4479 try writer.writeAll(" */\n");
4480 return lowerBlock(f, inst, @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len]));4480 return lowerBlock(f, inst, @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len]));
4481}4481}
44824482
src/codegen/llvm/Builder.zig+141-110
...@@ -7339,28 +7339,36 @@ pub const Constant = enum(u32) {...@@ -7339,28 +7339,36 @@ pub const Constant = enum(u32) {
7339 .positive_integer,7339 .positive_integer,
7340 .negative_integer,7340 .negative_integer,
7341 => |tag| {7341 => |tag| {
7342 const extra: *align(@alignOf(std.math.big.Limb)) Integer =7342 const extra: *align(@alignOf(std.math.big.Limb)) const Integer =
7343 @ptrCast(data.builder.constant_limbs.items[item.data..][0..Integer.limbs]);7343 @ptrCast(data.builder.constant_limbs.items[item.data..][0..Integer.limbs]);
7344 const limbs = data.builder.constant_limbs7344 const limbs = data.builder.constant_limbs
7345 .items[item.data + Integer.limbs ..][0..extra.limbs_len];7345 .items[item.data + Integer.limbs ..][0..extra.limbs_len];
7346 const bigint: std.math.big.int.Const = .{7346 const bigint: std.math.big.int.Const = .{
7347 .limbs = limbs,7347 .limbs = limbs,
7348 .positive = tag == .positive_integer,7348 .positive = switch (tag) {
7349 .positive_integer => true,
7350 .negative_integer => false,
7351 else => unreachable,
7352 },
7349 };7353 };
7350 const ExpectedContents = extern struct {7354 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,
7352 limbs: [7364 limbs: [
7353 std.math.big.int.calcToStringLimbsBufferLen(7365 std.math.big.int.calcToStringLimbsBufferLen(expected_limbs, 10)
7354 64 / @sizeOf(std.math.big.Limb),
7355 10,
7356 )
7357 ]std.math.big.Limb,7366 ]std.math.big.Limb,
7358 };7367 };
7359 var stack align(@alignOf(ExpectedContents)) =7368 var stack align(@alignOf(ExpectedContents)) =
7360 std.heap.stackFallback(@sizeOf(ExpectedContents), data.builder.gpa);7369 std.heap.stackFallback(@sizeOf(ExpectedContents), data.builder.gpa);
7361 const allocator = stack.get();7370 const allocator = stack.get();
7362 const str = bigint.toStringAlloc(allocator, 10, undefined) catch7371 const str = try bigint.toStringAlloc(allocator, 10, undefined);
7363 return writer.writeAll("...");
7364 defer allocator.free(str);7372 defer allocator.free(str);
7365 try writer.writeAll(str);7373 try writer.writeAll(str);
7366 },7374 },
...@@ -9464,10 +9472,38 @@ pub fn print(self: *Builder, writer: anytype) (@TypeOf(writer).Error || Allocato...@@ -9464,10 +9472,38 @@ pub fn print(self: *Builder, writer: anytype) (@TypeOf(writer).Error || Allocato
9464 try bw.flush();9472 try bw.flush();
9465}9473}
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
9467pub fn printUnbuffered(9500pub fn printUnbuffered(
9468 self: *Builder,9501 self: *Builder,
9469 writer: anytype,9502 backing_writer: anytype,
9470) (@TypeOf(writer).Error || Allocator.Error)!void {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
9471 var need_newline = false;9507 var need_newline = false;
9472 var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };9508 var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };
9473 defer metadata_formatter.map.deinit(self.gpa);9509 defer metadata_formatter.map.deinit(self.gpa);
...@@ -10344,12 +10380,17 @@ pub fn printUnbuffered(...@@ -10344,12 +10380,17 @@ pub fn printUnbuffered(
10344 const extra = self.metadataExtraData(Metadata.Enumerator, metadata_item.data);10380 const extra = self.metadataExtraData(Metadata.Enumerator, metadata_item.data);
1034510381
10346 const ExpectedContents = extern struct {10382 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,
10348 limbs: [10392 limbs: [
10349 std.math.big.int.calcToStringLimbsBufferLen(10393 std.math.big.int.calcToStringLimbsBufferLen(expected_limbs, 10)
10350 64 / @sizeOf(std.math.big.Limb),
10351 10,
10352 )
10353 ]std.math.big.Limb,10394 ]std.math.big.Limb,
10354 };10395 };
10355 var stack align(@alignOf(ExpectedContents)) =10396 var stack align(@alignOf(ExpectedContents)) =
...@@ -10375,7 +10416,9 @@ pub fn printUnbuffered(...@@ -10375,7 +10416,9 @@ pub fn printUnbuffered(
10375 .value = str,10416 .value = str,
10376 .isUnsigned = switch (kind) {10417 .isUnsigned = switch (kind) {
10377 .enumerator_unsigned => true,10418 .enumerator_unsigned => true,
10378 .enumerator_signed_positive, .enumerator_signed_negative => false,10419 .enumerator_signed_positive,
10420 .enumerator_signed_negative,
10421 => false,
10379 else => unreachable,10422 else => unreachable,
10380 },10423 },
10381 }, writer);10424 }, writer);
...@@ -13787,37 +13830,42 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co...@@ -13787,37 +13830,42 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
13787 => |tag| {13830 => |tag| {
13788 const extra: *align(@alignOf(std.math.big.Limb)) Constant.Integer =13831 const extra: *align(@alignOf(std.math.big.Limb)) Constant.Integer =
13789 @ptrCast(self.constant_limbs.items[data..][0..Constant.Integer.limbs]);13832 @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];
13792 const bigint: std.math.big.int.Const = .{13833 const bigint: std.math.big.int.Const = .{
13793 .limbs = limbs,13834 .limbs = self.constant_limbs
13794 .positive = tag == .positive_integer,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 },
13795 };13841 };
13796
13797 const bit_count = extra.type.scalarBits(self);13842 const bit_count = extra.type.scalarBits(self);
13798 if (bit_count <= 64) {13843 const val: i64 = if (bit_count <= 64)
13799 const val = bigint.to(i64) catch unreachable;13844 bigint.to(i64) catch unreachable
13800 const emit_val = if (tag == .positive_integer)13845 else if (bigint.to(u64)) |val|
13801 @shlWithOverflow(val, 1)[0]13846 @bitCast(val)
13802 else13847 else |_| {
13803 (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);13848 const limbs = try record.addManyAsSlice(
13804 try constants_block.writeAbbrev(Constants.Integer{ .value = @bitCast(emit_val) });13849 self.gpa,
13805 } else {13850 std.math.divCeil(u24, bit_count, 64) catch unreachable,
13806 const word_count = std.mem.alignForward(u24, bit_count, 64) / 64;13851 );
13807 try record.ensureUnusedCapacity(self.gpa, word_count);13852 bigint.writeTwosComplement(std.mem.sliceAsBytes(limbs), .little);
13808 const buffer: [*]u8 = @ptrCast(record.items.ptr);13853 for (limbs) |*limb| {
13809 bigint.writeTwosComplement(buffer[0..(word_count * 8)], .little);13854 const val = std.mem.littleToNative(i64, @bitCast(limb.*));
1381013855 limb.* = @bitCast(if (val >= 0)
13811 const signed_buffer: [*]i64 = @ptrCast(record.items.ptr);13856 val << 1 | 0
13812 for (signed_buffer[0..word_count], 0..) |val, i| {
13813 signed_buffer[i] = if (val >= 0)
13814 @shlWithOverflow(val, 1)[0]
13815 else13857 else
13816 (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);13858 -%val << 1 | 1);
13817 }13859 }
1381813860 try constants_block.writeUnabbrev(5, record.items);
13819 try constants_block.writeUnabbrev(5, record.items.ptr[0..word_count]);13861 continue;
13820 }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 });
13821 },13869 },
13822 .half,13870 .half,
13823 .bfloat,13871 .bfloat,
...@@ -14186,6 +14234,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co...@@ -14186,6 +14234,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
14186 self.metadata_items.items(.tag)[1..],14234 self.metadata_items.items(.tag)[1..],
14187 self.metadata_items.items(.data)[1..],14235 self.metadata_items.items(.data)[1..],
14188 ) |tag, data| {14236 ) |tag, data| {
14237 record.clearRetainingCapacity();
14189 switch (tag) {14238 switch (tag) {
14190 .none => unreachable,14239 .none => unreachable,
14191 .file => {14240 .file => {
...@@ -14333,77 +14382,60 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co...@@ -14333,77 +14382,60 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
14333 .enumerator_signed_positive,14382 .enumerator_signed_positive,
14334 .enumerator_signed_negative,14383 .enumerator_signed_negative,
14335 => |kind| {14384 => |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
14352 const extra = self.metadataExtraData(Metadata.Enumerator, data);14385 const extra = self.metadataExtraData(Metadata.Enumerator, data);
14353
14354 const limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len];
14355
14356 const bigint: std.math.big.int.Const = .{14386 const bigint: std.math.big.int.Const = .{
14357 .limbs = limbs,14387 .limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len],
14358 .positive = positive,14388 .positive = switch (kind) {
14389 .enumerator_unsigned,
14390 .enumerator_signed_positive,
14391 => true,
14392 .enumerator_signed_negative => false,
14393 else => unreachable,
14394 },
14359 };14395 };
1436014396 const flags: MetadataBlock.Enumerator.Flags = .{
14361 if (extra.bit_width <= 64) {14397 .unsigned = switch (kind) {
14362 const val = bigint.to(i64) catch unreachable;14398 .enumerator_unsigned => true,
14363 const emit_val = if (positive)14399 .enumerator_signed_positive,
14364 @shlWithOverflow(val, 1)[0]14400 .enumerator_signed_negative,
14365 else14401 => false,
14366 (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);14402 else => unreachable,
14367 try metadata_block.writeAbbrevAdapted(MetadataBlock.Enumerator{14403 },
14368 .flags = .{14404 };
14369 .unsigned = unsigned,14405 const val: i64 = if (bigint.to(i64)) |val|
14370 },14406 val
14371 .bit_width = extra.bit_width,14407 else |_| if (bigint.to(u64)) |val|
14372 .name = extra.name,14408 @bitCast(val)
14373 .value = @bitCast(emit_val),14409 else |_| {
14374 }, metadata_adapter);14410 const limbs_len = std.math.divCeil(u32, extra.bit_width, 64) catch unreachable;
14375 } else {14411 try record.ensureTotalCapacity(self.gpa, 3 + limbs_len);
14376 const word_count = std.mem.alignForward(u32, extra.bit_width, 64) / 64;14412 record.appendAssumeCapacity(@as(
14377 try record.ensureUnusedCapacity(self.gpa, 3 + word_count);14413 @typeInfo(MetadataBlock.Enumerator.Flags).Struct.backing_integer.?,
1437814414 @bitCast(flags),
14379 const flags: MetadataBlock.Enumerator.Flags = .{14415 ));
14380 .unsigned = unsigned,14416 record.appendAssumeCapacity(extra.bit_width);
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));
14389 record.appendAssumeCapacity(metadata_adapter.getMetadataStringIndex(extra.name));14417 record.appendAssumeCapacity(metadata_adapter.getMetadataStringIndex(extra.name));
1439014418 const limbs = record.addManyAsSliceAssumeCapacity(limbs_len);
14391 const buffer: [*]u8 = @ptrCast(record.items.ptr);14419 bigint.writeTwosComplement(std.mem.sliceAsBytes(limbs), .little);
14392 bigint.writeTwosComplement(buffer[0..(word_count * 8)], .little);14420 for (limbs) |*limb| {
1439314421 const val = std.mem.littleToNative(i64, @bitCast(limb.*));
14394 const signed_buffer: [*]i64 = @ptrCast(record.items.ptr);14422 limb.* = @bitCast(if (val >= 0)
14395 for (signed_buffer[0..word_count], 0..) |val, i| {14423 val << 1 | 0
14396 signed_buffer[i] = if (val >= 0)
14397 @shlWithOverflow(val, 1)[0]
14398 else14424 else
14399 (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);14425 -%val << 1 | 1);
14400 }14426 }
1440114427 try metadata_block.writeUnabbrev(MetadataBlock.Enumerator.id, record.items);
14402 try metadata_block.writeUnabbrev(14428 continue;
14403 MetadataBlock.Enumerator.id,14429 };
14404 record.items.ptr[0..(3 + word_count)],14430 try metadata_block.writeAbbrevAdapted(MetadataBlock.Enumerator{
14405 );14431 .flags = flags,
14406 }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);
14407 },14439 },
14408 .subrange => {14440 .subrange => {
14409 const extra = self.metadataExtraData(Metadata.Subrange, data);14441 const extra = self.metadataExtraData(Metadata.Subrange, data);
...@@ -14491,7 +14523,6 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co...@@ -14491,7 +14523,6 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
14491 }, metadata_adapter);14523 }, metadata_adapter);
14492 },14524 },
14493 }14525 }
14494 record.clearRetainingCapacity();
14495 }14526 }
1449614527
14497 // Write named metadata14528 // Write named metadata
...@@ -14615,7 +14646,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co...@@ -14615,7 +14646,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
14615 }14646 }
1461614647
14617 pub fn getOffsetValueIndex(adapter: @This(), value: Value) u32 {14648 pub fn getOffsetValueIndex(adapter: @This(), value: Value) u32 {
14618 return @subWithOverflow(adapter.offset(), adapter.getValueIndex(value))[0];14649 return adapter.offset() -% adapter.getValueIndex(value);
14619 }14650 }
1462014651
14621 pub fn getOffsetValueSignedIndex(adapter: @This(), value: Value) i32 {14652 pub fn getOffsetValueSignedIndex(adapter: @This(), value: Value) i32 {
test/behavior/enum.zig+41
...@@ -1263,3 +1263,44 @@ test "matching captures causes enum equivalence" {...@@ -1263,3 +1263,44 @@ test "matching captures causes enum equivalence" {
1263 comptime assert(@TypeOf(a) == @TypeOf(b));1263 comptime assert(@TypeOf(a) == @TypeOf(b));
1264 try expect(@intFromEnum(a) == @intFromEnum(b));1264 try expect(@intFromEnum(a) == @intFromEnum(b));
1265}1265}
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}