authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-03-03 00:18:34-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-03-05 02:59:01-05:00
log874ae81f1b2ae76cea6f5c79203f4baa68263163
tree685e469d6064e1c07f0ae8fa14f284fc6d9f5927
parente7f128c2051b086cdb1c03da041745b560bbaa3e

CBE: implement big integer literals


5 files changed, 504 insertions(+), 223 deletions(-)

lib/std/math/big/int.zig+1
......@@ -1674,6 +1674,7 @@ pub const Mutable = struct {
16741674
16751675 /// If a is positive, this passes through to truncate.
16761676 /// If a is negative, then r is set to positive with the bit pattern ~(a - 1).
1677 /// r may alias a.
16771678 ///
16781679 /// Asserts `r` has enough storage to store the result.
16791680 /// The upper bound is `calcTwosCompLimbCount(a.len)`.
lib/zig.h+6-6
......@@ -1360,8 +1360,8 @@ typedef signed __int128 zig_i128;
13601360
13611361#define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo))
13621362#define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo))
1363#define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo)
1364#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo)
1363#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
1364#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
13651365#define zig_hi_u128(val) ((uint64_t)((val) >> 64))
13661366#define zig_lo_u128(val) ((uint64_t)((val) >> 0))
13671367#define zig_hi_i128(val) (( int64_t)((val) >> 64))
......@@ -1391,11 +1391,11 @@ typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128;
13911391#define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) })
13921392
13931393#if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */
1394#define zig_make_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1395#define zig_make_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1394#define zig_init_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }
1395#define zig_init_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }
13961396#else /* But non-MSVC doesn't like the unprotected commas */
1397#define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo)
1398#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo)
1397#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
1398#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
13991399#endif
14001400#define zig_hi_u128(val) ((val).hi)
14011401#define zig_lo_u128(val) ((val).lo)
src/codegen/c.zig+180-185
......@@ -449,7 +449,7 @@ pub const Function = struct {
449449 }
450450
451451 fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) {
452 return f.object.dg.fmtIntLiteral(ty, val);
452 return f.object.dg.fmtIntLiteral(ty, val, .Other);
453453 }
454454
455455 fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 {
......@@ -574,9 +574,9 @@ pub const DeclGen = struct {
574574 const len_val = Value.initPayload(&len_pl.base);
575575
576576 if (location == .StaticInitializer) {
577 return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val)});
577 return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)});
578578 } else {
579 return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val)});
579 return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)});
580580 }
581581 }
582582
......@@ -606,7 +606,7 @@ pub const DeclGen = struct {
606606 try writer.writeByte(')');
607607 }
608608 switch (ptr_val.tag()) {
609 .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val)}),
609 .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val, .Other)}),
610610 .decl_ref_mut, .decl_ref, .variable => {
611611 const decl_index = switch (ptr_val.tag()) {
612612 .decl_ref => ptr_val.castTag(.decl_ref).?.data,
......@@ -670,7 +670,9 @@ pub const DeclGen = struct {
670670 container_ptr_ty,
671671 location,
672672 );
673 try writer.print(" + {})", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)});
673 try writer.print(" + {})", .{
674 try dg.fmtIntLiteral(Type.usize, byte_offset_val, .Other),
675 });
674676 },
675677 .end => {
676678 try writer.writeAll("((");
......@@ -680,7 +682,9 @@ pub const DeclGen = struct {
680682 container_ptr_ty,
681683 location,
682684 );
683 try writer.print(") + {})", .{try dg.fmtIntLiteral(Type.usize, Value.one)});
685 try writer.print(") + {})", .{
686 try dg.fmtIntLiteral(Type.usize, Value.one, .Other),
687 });
684688 },
685689 }
686690 },
......@@ -746,7 +750,7 @@ pub const DeclGen = struct {
746750 return writer.writeAll("false");
747751 }
748752 },
749 .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteralLoc(ty, val, location)}),
753 .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val, location)}),
750754 .Float => {
751755 const bits = ty.floatBits(target);
752756 var int_pl = Type.Payload.Bits{ .base = .{ .tag = .int_signed }, .data = bits };
......@@ -780,11 +784,11 @@ pub const DeclGen = struct {
780784 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
781785 const ptr_ty = ty.slicePtrFieldType(&buf);
782786 try dg.renderType(writer, ptr_ty);
783 return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val)});
787 return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val, .Other)});
784788 } else {
785789 try writer.writeAll("((");
786790 try dg.renderType(writer, ty);
787 return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)});
791 return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)});
788792 },
789793 .Optional => {
790794 var opt_buf: Type.Payload.ElemType = undefined;
......@@ -831,7 +835,7 @@ pub const DeclGen = struct {
831835
832836 return writer.writeByte('}');
833837 },
834 .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef)}),
838 .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef, .Other)}),
835839 },
836840 .Union => {
837841 if (!location.isInitializer()) {
......@@ -854,7 +858,7 @@ pub const DeclGen = struct {
854858 if (!field.ty.hasRuntimeBits()) continue;
855859 try dg.renderValue(writer, field.ty, val, initializer_type);
856860 break;
857 } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)});
861 } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef, .Other)});
858862 if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}');
859863 return writer.writeByte('}');
860864 },
......@@ -868,7 +872,7 @@ pub const DeclGen = struct {
868872 try writer.writeAll("{ .payload = ");
869873 try dg.renderValue(writer, ty.errorUnionPayload(), val, initializer_type);
870874 return writer.print(", .error = {x} }}", .{
871 try dg.fmtIntLiteral(ty.errorUnionSet(), val),
875 try dg.fmtIntLiteral(ty.errorUnionSet(), val, .Other),
872876 });
873877 },
874878 .Array, .Vector => {
......@@ -927,7 +931,7 @@ pub const DeclGen = struct {
927931 .decl_ref_mut,
928932 .decl_ref,
929933 => try dg.renderParentPtr(writer, val, ty, location),
930 else => try writer.print("{}", .{try dg.fmtIntLiteralLoc(ty, val, location)}),
934 else => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, location)}),
931935 },
932936 .Float => {
933937 const bits = ty.floatBits(target);
......@@ -1020,7 +1024,7 @@ pub const DeclGen = struct {
10201024 try writer.writeAll(", ");
10211025 empty = false;
10221026 }
1023 try writer.print("{x}", .{try dg.fmtIntLiteralLoc(int_ty, int_val, location)});
1027 try writer.print("{x}", .{try dg.fmtIntLiteral(int_ty, int_val, location)});
10241028 if (!empty) try writer.writeByte(')');
10251029 return;
10261030 },
......@@ -1069,7 +1073,7 @@ pub const DeclGen = struct {
10691073 .int_u64, .one => {
10701074 try writer.writeAll("((");
10711075 try dg.renderType(writer, ty);
1072 return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)});
1076 return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)});
10731077 },
10741078 .field_ptr,
10751079 .elem_ptr,
......@@ -1889,11 +1893,11 @@ pub const DeclGen = struct {
18891893 const int_info = ty.intInfo(target);
18901894 if (int_info.signedness == .signed) {
18911895 const min_val = try ty.minInt(stack.get(), target);
1892 try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, min_val)});
1896 try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, min_val, .Other)});
18931897 }
18941898
18951899 const max_val = try ty.maxInt(stack.get(), target);
1896 try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val)});
1900 try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val, .Other)});
18971901 },
18981902 .Bits => {
18991903 var bits_pl = Value.Payload.U64{
......@@ -1901,7 +1905,7 @@ pub const DeclGen = struct {
19011905 .data = ty.bitSize(target),
19021906 };
19031907 const bits_val = Value.initPayload(&bits_pl.base);
1904 try writer.print(", {}", .{try dg.fmtIntLiteral(Type.u8, bits_val)});
1908 try writer.print(", {}", .{try dg.fmtIntLiteral(Type.u8, bits_val, .Other)});
19051909 },
19061910 }
19071911 }
......@@ -1910,30 +1914,21 @@ pub const DeclGen = struct {
19101914 dg: *DeclGen,
19111915 ty: Type,
19121916 val: Value,
1917 loc: ValueRenderLocation,
19131918 ) !std.fmt.Formatter(formatIntLiteral) {
1914 const int_info = ty.intInfo(dg.module.getTarget());
1915 const c_bits = toCIntBits(int_info.bits);
1916 if (c_bits == null or c_bits.? > 128)
1917 return dg.fail("TODO implement integer constants larger than 128 bits", .{});
1919 const kind: CType.Kind = switch (loc) {
1920 .FunctionArgument => .parameter,
1921 .Initializer, .Other => .complete,
1922 .StaticInitializer => .global,
1923 };
19181924 return std.fmt.Formatter(formatIntLiteral){ .data = .{
1919 .ty = ty,
1925 .dg = dg,
1926 .int_info = ty.intInfo(dg.module.getTarget()),
1927 .kind = kind,
1928 .cty = try dg.typeToCType(ty, kind),
19201929 .val = val,
1921 .mod = dg.module,
19221930 } };
19231931 }
1924
1925 fn fmtIntLiteralLoc(
1926 dg: *DeclGen,
1927 ty: Type,
1928 val: Value,
1929 location: ValueRenderLocation, // TODO: Instead add this as optional arg to fmtIntLiteral
1930 ) !std.fmt.Formatter(formatIntLiteral) {
1931 const int_info = ty.intInfo(dg.module.getTarget());
1932 const c_bits = toCIntBits(int_info.bits);
1933 if (c_bits == null or c_bits.? > 128)
1934 return dg.fail("TODO implement integer constants larger than 128 bits", .{});
1935 return std.fmt.Formatter(formatIntLiteral){ .data = .{ .ty = ty, .val = val, .mod = dg.module, .location = location } };
1936 }
19371932};
19381933
19391934const CTypeFix = enum { prefix, suffix };
......@@ -2450,7 +2445,7 @@ pub fn genErrDecls(o: *Object) !void {
24502445 const len_val = Value.initPayload(&len_pl.base);
24512446
24522447 try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{
2453 fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val),
2448 fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other),
24542449 });
24552450 }
24562451 try writer.writeAll("};\n");
......@@ -2501,7 +2496,10 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
25012496 var int_pl: Value.Payload.U64 = undefined;
25022497 const int_val = tag_val.enumToInt(enum_ty, &int_pl);
25032498
2504 var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len };
2499 var name_ty_pl = Type.Payload.Len{
2500 .base = .{ .tag = .array_u8_sentinel_0 },
2501 .data = name.len,
2502 };
25052503 const name_ty = Type.initPayload(&name_ty_pl.base);
25062504
25072505 var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name };
......@@ -2510,14 +2508,16 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
25102508 var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len };
25112509 const len_val = Value.initPayload(&len_pl.base);
25122510
2513 try w.print(" case {}: {{\n static ", .{try o.dg.fmtIntLiteral(enum_ty, int_val)});
2511 try w.print(" case {}: {{\n static ", .{
2512 try o.dg.fmtIntLiteral(enum_ty, int_val, .Other),
2513 });
25142514 try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete);
25152515 try w.writeAll(" = ");
25162516 try o.dg.renderValue(w, name_ty, name_val, .Initializer);
25172517 try w.writeAll(";\n return (");
25182518 try o.dg.renderType(w, name_slice_ty);
25192519 try w.print("){{{}, {}}};\n", .{
2520 fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val),
2520 fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other),
25212521 });
25222522
25232523 try w.writeAll(" }\n");
......@@ -2535,7 +2535,12 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
25352535
25362536 const fwd_decl_writer = o.dg.fwd_decl.writer();
25372537 try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)});
2538 try o.dg.renderFunctionSignature(fwd_decl_writer, fn_decl_index, .forward, .{ .string = fn_name });
2538 try o.dg.renderFunctionSignature(
2539 fwd_decl_writer,
2540 fn_decl_index,
2541 .forward,
2542 .{ .string = fn_name },
2543 );
25392544 try fwd_decl_writer.writeAll(";\n");
25402545
25412546 try w.print("static zig_{s} ", .{@tagName(key)});
......@@ -7177,30 +7182,33 @@ fn undefPattern(comptime IntType: type) IntType {
71777182 return @bitCast(IntType, @as(UnsignedType, (1 << (int_info.bits | 1)) / 3));
71787183}
71797184
7180const FormatIntLiteralContext = struct { ty: Type, val: Value, mod: *Module, location: ?ValueRenderLocation = null };
7185const FormatIntLiteralContext = struct {
7186 dg: *DeclGen,
7187 int_info: std.builtin.Type.Int,
7188 kind: CType.Kind,
7189 cty: CType,
7190 val: Value,
7191};
71817192fn formatIntLiteral(
71827193 data: FormatIntLiteralContext,
71837194 comptime fmt: []const u8,
71847195 options: std.fmt.FormatOptions,
71857196 writer: anytype,
71867197) @TypeOf(writer).Error!void {
7187 const target = data.mod.getTarget();
7188 const int_info = data.ty.intInfo(target);
7198 const target = data.dg.module.getTarget();
71897199
71907200 const ExpectedContents = struct {
71917201 const base = 10;
7192 const limbs_count_128 = BigInt.calcTwosCompLimbCount(128);
7193 const expected_needed_limbs_count = BigInt.calcToStringLimbsBufferLen(limbs_count_128, base);
7194 const worst_case_int = BigInt.Const{
7195 .limbs = &([1]BigIntLimb{std.math.maxInt(BigIntLimb)} ** expected_needed_limbs_count),
7196 .positive = false,
7197 };
7202 const bits = 128;
7203 const limbs_count = BigInt.calcTwosCompLimbCount(bits);
71987204
7199 undef_limbs: [limbs_count_128]BigIntLimb,
7200 wrap_limbs: [limbs_count_128]BigIntLimb,
7205 undef_limbs: [limbs_count]BigIntLimb,
7206 wrap_limbs: [limbs_count]BigIntLimb,
7207 to_string_buf: [bits]u8,
7208 to_string_limbs: [BigInt.calcToStringLimbsBufferLen(limbs_count, base)]BigIntLimb,
72017209 };
72027210 var stack align(@alignOf(ExpectedContents)) =
7203 std.heap.stackFallback(@sizeOf(ExpectedContents), data.mod.gpa);
7211 std.heap.stackFallback(@sizeOf(ExpectedContents), data.dg.gpa);
72047212 const allocator = stack.get();
72057213
72067214 var undef_limbs: []BigIntLimb = &.{};
......@@ -7208,7 +7216,7 @@ fn formatIntLiteral(
72087216
72097217 var int_buf: Value.BigIntSpace = undefined;
72107218 const int = if (data.val.isUndefDeep()) blk: {
7211 undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(int_info.bits));
7219 undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits));
72127220 std.mem.set(BigIntLimb, undef_limbs, undefPattern(BigIntLimb));
72137221
72147222 var undef_int = BigInt.Mutable{
......@@ -7216,163 +7224,150 @@ fn formatIntLiteral(
72167224 .len = undef_limbs.len,
72177225 .positive = true,
72187226 };
7219 undef_int.truncate(undef_int.toConst(), int_info.signedness, int_info.bits);
7227 undef_int.truncate(undef_int.toConst(), data.int_info.signedness, data.int_info.bits);
72207228 break :blk undef_int.toConst();
72217229 } else data.val.toBigInt(&int_buf, target);
7222 assert(int.fitsInTwosComp(int_info.signedness, int_info.bits));
7230 assert(int.fitsInTwosComp(data.int_info.signedness, data.int_info.bits));
72237231
7224 const c_bits = toCIntBits(int_info.bits) orelse unreachable;
7232 const c_bits = @intCast(usize, data.cty.byteSize(data.dg.ctypes.set, target) * 8);
72257233 var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined;
72267234 const one = BigInt.Mutable.init(&one_limbs, 1).toConst();
72277235
7228 const wrap_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits));
7229 defer allocator.free(wrap_limbs);
7230 var wrap = BigInt.Mutable{ .limbs = wrap_limbs, .len = undefined, .positive = undefined };
7231 if (wrap.addWrap(int, one, int_info.signedness, c_bits) or
7232 int_info.signedness == .signed and wrap.subWrap(int, one, int_info.signedness, c_bits))
7233 {
7234 const abbrev = switch (data.ty.tag()) {
7235 .c_short, .c_ushort => "SHRT",
7236 .c_int, .c_uint => "INT",
7237 .c_long, .c_ulong => "LONG",
7238 .c_longlong, .c_ulonglong => "LLONG",
7239 .isize, .usize => "INTPTR",
7240 else => return writer.print("zig_{s}Int_{c}{d}", .{
7241 if (int.positive) "max" else "min", signAbbrev(int_info.signedness), c_bits,
7236 var wrap = BigInt.Mutable{
7237 .limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)),
7238 .len = undefined,
7239 .positive = undefined,
7240 };
7241 defer allocator.free(wrap.limbs);
7242 if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or
7243 data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits))
7244 return writer.print("{s}_{s}", .{
7245 data.cty.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{
7246 if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits,
72427247 }),
7243 };
7244 if (int_info.signedness == .unsigned) try writer.writeByte('U');
7245 return writer.print("{s}_{s}", .{ abbrev, if (int.positive) "MAX" else "MIN" });
7246 }
7247
7248 var use_twos_comp = false;
7249 if (!int.positive) {
7250 if (c_bits > 64) {
7251 // TODO: Can this be done for decimal literals as well?
7252 if (fmt.len == 1 and fmt[0] != 'd') {
7253 use_twos_comp = true;
7254 } else {
7255 // TODO: Use fmtIntLiteral for 0?
7256 try writer.print("zig_sub_{c}{d}(zig_make_{c}{d}(0, 0), ", .{ signAbbrev(int_info.signedness), c_bits, signAbbrev(int_info.signedness), c_bits });
7257 }
7258 } else {
7259 try writer.writeByte('-');
7260 }
7261 }
7248 if (int.positive) "MAX" else "MIN",
7249 });
72627250
7263 switch (data.ty.tag()) {
7264 .c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {},
7265 else => {
7266 if (int_info.bits <= 64) {
7267 try writer.print("{s}INT{d}_C(", .{ switch (int_info.signedness) {
7268 .signed => "",
7269 .unsigned => "U",
7270 }, c_bits });
7271 } else if (data.location != null and data.location.? == .StaticInitializer) {
7272 // MSVC treats casting the struct initializer as not constant (C2099), so an alternate form is used in global initializers
7273 try writer.print("zig_make_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits });
7274 } else {
7275 try writer.print("zig_make_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits });
7276 }
7251 const c_limb_info: struct {
7252 cty: CType,
7253 count: usize,
7254 endian: std.builtin.Endian,
7255 homogeneous: bool,
7256 } = switch (data.cty.tag()) {
7257 else => .{
7258 .cty = CType.initTag(.void),
7259 .count = 1,
7260 .endian = .Little,
7261 .homogeneous = true,
72777262 },
7278 }
7263 .zig_u128, .zig_i128 => .{
7264 .cty = CType.initTag(.uint64_t),
7265 .count = 2,
7266 .endian = .Big,
7267 .homogeneous = false,
7268 },
7269 .array => info: {
7270 const array_data = data.cty.castTag(.array).?.data;
7271 break :info .{
7272 .cty = data.dg.indexToCType(array_data.elem_type),
7273 .count = @intCast(usize, array_data.len),
7274 .endian = target.cpu.arch.endian(),
7275 .homogeneous = true,
7276 };
7277 },
7278 };
7279 if (c_limb_info.count == 1) {
7280 if (!int.positive) try writer.writeByte('-');
7281 try data.cty.renderLiteralPrefix(writer, data.kind);
72797282
7280 const limbs_count_64 = @divExact(64, @bitSizeOf(BigIntLimb));
7281 if (c_bits <= 64) {
7282 var base: u8 = undefined;
7283 var case: std.fmt.Case = undefined;
7284 switch (fmt.len) {
7285 0 => base = 10,
7283 const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) {
7284 0 => .{ .base = 10 },
72867285 1 => switch (fmt[0]) {
7287 'b' => {
7288 base = 2;
7286 'b' => style: {
72897287 try writer.writeAll("0b");
7288 break :style .{ .base = 2 };
72907289 },
7291 'o' => {
7292 base = 8;
7290 'o' => style: {
72937291 try writer.writeByte('0');
7292 break :style .{ .base = 8 };
72947293 },
7295 'd' => base = 10,
7296 'x' => {
7297 base = 16;
7298 case = .lower;
7299 try writer.writeAll("0x");
7300 },
7301 'X' => {
7302 base = 16;
7303 case = .upper;
7294 'd' => .{ .base = 10 },
7295 'x', 'X' => |base| style: {
73047296 try writer.writeAll("0x");
7297 break :style .{ .base = 16, .case = switch (base) {
7298 'x' => .lower,
7299 'X' => .upper,
7300 else => unreachable,
7301 } };
73057302 },
73067303 else => @compileError("Invalid fmt: " ++ fmt),
73077304 },
73087305 else => @compileError("Invalid fmt: " ++ fmt),
7309 }
7306 };
73107307
7311 var str: [64]u8 = undefined;
7312 var limbs_buf: [BigInt.calcToStringLimbsBufferLen(limbs_count_64, 10)]BigIntLimb = undefined;
7313 try writer.writeAll(str[0..int.abs().toString(&str, base, case, &limbs_buf)]);
7308 const string = try int.abs().toStringAlloc(allocator, style.base, style.case);
7309 defer allocator.free(string);
7310 try writer.writeAll(string);
73147311 } else {
7315 assert(c_bits == 128);
7316 const split = std.math.min(int.limbs.len, limbs_count_64);
7317 var twos_comp_limbs: [BigInt.calcTwosCompLimbCount(128)]BigIntLimb = undefined;
7318
7319 // Adding a negation in the C code before the doesn't work in all cases:
7320 // - struct versions would require an extra zig_sub_ call to negate, which wouldn't work in constant expressions
7321 // - negating the f80 int representation (i128) doesn't make sense
7322 // Instead we write out the literal as a negative number in twos complement
7323 var limbs = int.limbs;
7324
7325 if (use_twos_comp) {
7326 var twos_comp = BigInt.Mutable{
7327 .limbs = &twos_comp_limbs,
7328 .positive = undefined,
7312 try data.cty.renderLiteralPrefix(writer, data.kind);
7313 wrap.convertToTwosComplement(int, .unsigned, data.int_info.bits);
7314 std.mem.set(BigIntLimb, wrap.limbs[wrap.len..], 0);
7315 wrap.len = wrap.limbs.len;
7316 const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count);
7317
7318 var c_limb_int_info = std.builtin.Type.Int{
7319 .signedness = undefined,
7320 .bits = @intCast(u16, @divExact(c_bits, c_limb_info.count)),
7321 };
7322 var c_limb_cty: CType = undefined;
7323
7324 var limb_offset: usize = 0;
7325 const most_significant_limb_i = wrap.len - limbs_per_c_limb;
7326 while (limb_offset < wrap.len) : (limb_offset += limbs_per_c_limb) {
7327 const limb_i = switch (c_limb_info.endian) {
7328 .Little => limb_offset,
7329 .Big => most_significant_limb_i - limb_offset,
7330 };
7331 var c_limb_mut = BigInt.Mutable{
7332 .limbs = wrap.limbs[limb_i..][0..limbs_per_c_limb],
73297333 .len = undefined,
7334 .positive = true,
73307335 };
7336 c_limb_mut.normalize(limbs_per_c_limb);
73317337
7332 twos_comp.convertToTwosComplement(int, .signed, int_info.bits);
7333 limbs = twos_comp.limbs;
7334 }
7335
7336 var upper_pl = Value.Payload.BigInt{
7337 .base = .{ .tag = .int_big_positive },
7338 .data = limbs[split..],
7339 };
7340 const upper_val = Value.initPayload(&upper_pl.base);
7341 try formatIntLiteral(.{
7342 .ty = switch (int_info.signedness) {
7343 .unsigned => Type.u64,
7344 .signed => if (use_twos_comp) Type.u64 else Type.i64,
7345 },
7346 .val = upper_val,
7347 .mod = data.mod,
7348 }, fmt, options, writer);
7349
7350 try writer.writeAll(", ");
7338 if (limb_i == most_significant_limb_i and
7339 !c_limb_info.homogeneous and data.int_info.signedness == .signed)
7340 {
7341 // most significant limb is actually signed
7342 c_limb_int_info.signedness = .signed;
7343 c_limb_cty = c_limb_info.cty.toSigned();
7344
7345 c_limb_mut.positive = wrap.positive;
7346 c_limb_mut.convertToTwosComplement(
7347 c_limb_mut.toConst(),
7348 .signed,
7349 data.int_info.bits - limb_i * @bitSizeOf(BigIntLimb),
7350 );
7351 } else {
7352 c_limb_int_info.signedness = .unsigned;
7353 c_limb_cty = c_limb_info.cty;
7354 }
7355 var c_limb_val_pl = Value.Payload.BigInt{
7356 .base = .{ .tag = if (c_limb_mut.positive) .int_big_positive else .int_big_negative },
7357 .data = c_limb_mut.limbs[0..c_limb_mut.len],
7358 };
73517359
7352 var lower_pl = Value.Payload.BigInt{
7353 .base = .{ .tag = .int_big_positive },
7354 .data = limbs[0..split],
7355 };
7356 const lower_val = Value.initPayload(&lower_pl.base);
7357 try formatIntLiteral(.{
7358 .ty = Type.u64,
7359 .val = lower_val,
7360 .mod = data.mod,
7361 }, fmt, options, writer);
7362
7363 if (!int.positive and c_bits > 64 and !use_twos_comp) try writer.writeByte(')');
7364 return writer.writeByte(')');
7365 }
7366
7367 switch (data.ty.tag()) {
7368 .c_short, .c_ushort, .c_int => {},
7369 .c_uint => try writer.writeAll("u"),
7370 .c_long => try writer.writeAll("l"),
7371 .c_ulong => try writer.writeAll("ul"),
7372 .c_longlong => try writer.writeAll("ll"),
7373 .c_ulonglong => try writer.writeAll("ull"),
7374 else => try writer.writeByte(')'),
7360 if (limb_offset > 0) try writer.writeAll(", ");
7361 try formatIntLiteral(.{
7362 .dg = data.dg,
7363 .int_info = c_limb_int_info,
7364 .kind = data.kind,
7365 .cty = c_limb_cty,
7366 .val = Value.initPayload(&c_limb_val_pl.base),
7367 }, fmt, options, writer);
7368 }
73757369 }
7370 try data.cty.renderLiteralSuffix(writer);
73767371}
73777372
73787373fn isByRef(ty: Type) bool {
src/codegen/c/type.zig+317-31
......@@ -496,6 +496,296 @@ pub const CType = extern union {
496496 }
497497 };
498498
499 pub fn toSigned(self: CType) CType {
500 return CType.initTag(switch (self.tag()) {
501 .char, .@"signed char", .@"unsigned char" => .@"signed char",
502 .short, .@"unsigned short" => .short,
503 .int, .@"unsigned int" => .int,
504 .long, .@"unsigned long" => .long,
505 .@"long long", .@"unsigned long long" => .@"long long",
506 .size_t, .ptrdiff_t => .ptrdiff_t,
507 .uint8_t, .int8_t => .int8_t,
508 .uint16_t, .int16_t => .int16_t,
509 .uint32_t, .int32_t => .int32_t,
510 .uint64_t, .int64_t => .int64_t,
511 .uintptr_t, .intptr_t => .intptr_t,
512 .zig_u128, .zig_i128 => .zig_i128,
513 .float,
514 .double,
515 .@"long double",
516 .zig_f16,
517 .zig_f32,
518 .zig_f80,
519 .zig_f128,
520 .zig_c_longdouble,
521 => |t| t,
522 else => unreachable,
523 });
524 }
525
526 pub fn toUnsigned(self: CType) CType {
527 return CType.initTag(switch (self.tag()) {
528 .char, .@"signed char", .@"unsigned char" => .@"unsigned char",
529 .short, .@"unsigned short" => .@"unsigned short",
530 .int, .@"unsigned int" => .@"unsigned int",
531 .long, .@"unsigned long" => .@"unsigned long",
532 .@"long long", .@"unsigned long long" => .@"unsigned long long",
533 .size_t, .ptrdiff_t => .size_t,
534 .uint8_t, .int8_t => .uint8_t,
535 .uint16_t, .int16_t => .uint16_t,
536 .uint32_t, .int32_t => .uint32_t,
537 .uint64_t, .int64_t => .uint64_t,
538 .uintptr_t, .intptr_t => .uintptr_t,
539 .zig_u128, .zig_i128 => .zig_u128,
540 else => unreachable,
541 });
542 }
543
544 pub fn getStandardDefineAbbrev(self: CType) ?[]const u8 {
545 return switch (self.tag()) {
546 .char => "CHAR",
547 .@"signed char" => "SCHAR",
548 .short => "SHRT",
549 .int => "INT",
550 .long => "LONG",
551 .@"long long" => "LLONG",
552 .@"unsigned char" => "UCHAR",
553 .@"unsigned short" => "USHRT",
554 .@"unsigned int" => "UINT",
555 .@"unsigned long" => "ULONG",
556 .@"unsigned long long" => "ULLONG",
557 .float => "FLT",
558 .double => "DBL",
559 .@"long double" => "LDBL",
560 .size_t => "SIZE",
561 .ptrdiff_t => "PTRDIFF",
562 .uint8_t => "UINT8",
563 .int8_t => "INT8",
564 .uint16_t => "UINT16",
565 .int16_t => "INT16",
566 .uint32_t => "UINT32",
567 .int32_t => "INT32",
568 .uint64_t => "UINT64",
569 .int64_t => "INT64",
570 .uintptr_t => "UINTPTR",
571 .intptr_t => "INTPTR",
572 else => null,
573 };
574 }
575
576 pub fn renderLiteralPrefix(self: CType, writer: anytype, kind: Kind) @TypeOf(writer).Error!void {
577 switch (self.tag()) {
578 .void => unreachable,
579 ._Bool,
580 .char,
581 .@"signed char",
582 .short,
583 .@"unsigned short",
584 .bool,
585 .size_t,
586 .ptrdiff_t,
587 .uintptr_t,
588 .intptr_t,
589 => |t| switch (kind) {
590 else => try writer.print("({s})", .{@tagName(t)}),
591 .global => {},
592 },
593 .int,
594 .long,
595 .@"long long",
596 .@"unsigned char",
597 .@"unsigned int",
598 .@"unsigned long",
599 .@"unsigned long long",
600 .float,
601 .double,
602 .@"long double",
603 => {},
604 .uint8_t,
605 .int8_t,
606 .uint16_t,
607 .int16_t,
608 .uint32_t,
609 .int32_t,
610 .uint64_t,
611 .int64_t,
612 => try writer.print("{s}_C(", .{self.getStandardDefineAbbrev().?}),
613 .zig_u128,
614 .zig_i128,
615 .zig_f16,
616 .zig_f32,
617 .zig_f64,
618 .zig_f80,
619 .zig_f128,
620 .zig_c_longdouble,
621 => |t| try writer.print("zig_{s}_{s}(", .{
622 switch (kind) {
623 else => "make",
624 .global => "init",
625 },
626 @tagName(t)["zig_".len..],
627 }),
628 .pointer,
629 .pointer_const,
630 .pointer_volatile,
631 .pointer_const_volatile,
632 => unreachable,
633 .array,
634 .vector,
635 => try writer.writeByte('{'),
636 .fwd_anon_struct,
637 .fwd_anon_union,
638 .fwd_struct,
639 .fwd_union,
640 .unnamed_struct,
641 .unnamed_union,
642 .packed_unnamed_struct,
643 .packed_unnamed_union,
644 .anon_struct,
645 .anon_union,
646 .@"struct",
647 .@"union",
648 .packed_struct,
649 .packed_union,
650 .function,
651 .varargs_function,
652 => unreachable,
653 }
654 }
655
656 pub fn renderLiteralSuffix(self: CType, writer: anytype) @TypeOf(writer).Error!void {
657 switch (self.tag()) {
658 .void => unreachable,
659 ._Bool => {},
660 .char,
661 .@"signed char",
662 .short,
663 .int,
664 => {},
665 .long => try writer.writeByte('l'),
666 .@"long long" => try writer.writeAll("ll"),
667 .@"unsigned char",
668 .@"unsigned short",
669 .@"unsigned int",
670 => try writer.writeByte('u'),
671 .@"unsigned long",
672 .size_t,
673 .uintptr_t,
674 => try writer.writeAll("ul"),
675 .@"unsigned long long" => try writer.writeAll("ull"),
676 .float => try writer.writeByte('f'),
677 .double => {},
678 .@"long double" => try writer.writeByte('l'),
679 .bool,
680 .ptrdiff_t,
681 .intptr_t,
682 => {},
683 .uint8_t,
684 .int8_t,
685 .uint16_t,
686 .int16_t,
687 .uint32_t,
688 .int32_t,
689 .uint64_t,
690 .int64_t,
691 .zig_u128,
692 .zig_i128,
693 .zig_f16,
694 .zig_f32,
695 .zig_f64,
696 .zig_f80,
697 .zig_f128,
698 .zig_c_longdouble,
699 => try writer.writeByte(')'),
700 .pointer,
701 .pointer_const,
702 .pointer_volatile,
703 .pointer_const_volatile,
704 => unreachable,
705 .array,
706 .vector,
707 => try writer.writeByte('}'),
708 .fwd_anon_struct,
709 .fwd_anon_union,
710 .fwd_struct,
711 .fwd_union,
712 .unnamed_struct,
713 .unnamed_union,
714 .packed_unnamed_struct,
715 .packed_unnamed_union,
716 .anon_struct,
717 .anon_union,
718 .@"struct",
719 .@"union",
720 .packed_struct,
721 .packed_union,
722 .function,
723 .varargs_function,
724 => unreachable,
725 }
726 }
727
728 pub fn byteSize(self: CType, store: Store.Set, target: Target) u64 {
729 return switch (self.tag()) {
730 .void => 0,
731 .char, .@"signed char", ._Bool, .@"unsigned char", .bool, .uint8_t, .int8_t => 1,
732 .short => target.c_type_byte_size(.short),
733 .int => target.c_type_byte_size(.int),
734 .long => target.c_type_byte_size(.long),
735 .@"long long" => target.c_type_byte_size(.longlong),
736 .@"unsigned short" => target.c_type_byte_size(.ushort),
737 .@"unsigned int" => target.c_type_byte_size(.uint),
738 .@"unsigned long" => target.c_type_byte_size(.ulong),
739 .@"unsigned long long" => target.c_type_byte_size(.ulonglong),
740 .float => target.c_type_byte_size(.float),
741 .double => target.c_type_byte_size(.double),
742 .@"long double" => target.c_type_byte_size(.longdouble),
743 .size_t,
744 .ptrdiff_t,
745 .uintptr_t,
746 .intptr_t,
747 .pointer,
748 .pointer_const,
749 .pointer_volatile,
750 .pointer_const_volatile,
751 => @divExact(target.cpu.arch.ptrBitWidth(), 8),
752 .uint16_t, .int16_t, .zig_f16 => 2,
753 .uint32_t, .int32_t, .zig_f32 => 4,
754 .uint64_t, .int64_t, .zig_f64 => 8,
755 .zig_u128, .zig_i128, .zig_f128 => 16,
756 .zig_f80 => if (target.c_type_bit_size(.longdouble) == 80)
757 target.c_type_byte_size(.longdouble)
758 else
759 16,
760 .zig_c_longdouble => target.c_type_byte_size(.longdouble),
761
762 .array,
763 .vector,
764 => {
765 const data = self.cast(Payload.Sequence).?.data;
766 return data.len * store.indexToCType(data.elem_type).byteSize(store, target);
767 },
768
769 .fwd_anon_struct,
770 .fwd_anon_union,
771 .fwd_struct,
772 .fwd_union,
773 .unnamed_struct,
774 .unnamed_union,
775 .packed_unnamed_struct,
776 .packed_unnamed_union,
777 .anon_struct,
778 .anon_union,
779 .@"struct",
780 .@"union",
781 .packed_struct,
782 .packed_union,
783 .function,
784 .varargs_function,
785 => unreachable,
786 };
787 }
788
499789 pub fn isPacked(self: CType) bool {
500790 return switch (self.tag()) {
501791 else => false,
......@@ -787,26 +1077,26 @@ pub const CType = extern union {
7871077 };
7881078 }
7891079
790 fn tagFromIntInfo(signedness: std.builtin.Signedness, bits: u16) Tag {
791 return switch (bits) {
1080 fn tagFromIntInfo(int_info: std.builtin.Type.Int) Tag {
1081 return switch (int_info.bits) {
7921082 0 => .void,
793 1...8 => switch (signedness) {
1083 1...8 => switch (int_info.signedness) {
7941084 .unsigned => .uint8_t,
7951085 .signed => .int8_t,
7961086 },
797 9...16 => switch (signedness) {
1087 9...16 => switch (int_info.signedness) {
7981088 .unsigned => .uint16_t,
7991089 .signed => .int16_t,
8001090 },
801 17...32 => switch (signedness) {
1091 17...32 => switch (int_info.signedness) {
8021092 .unsigned => .uint32_t,
8031093 .signed => .int32_t,
8041094 },
805 33...64 => switch (signedness) {
1095 33...64 => switch (int_info.signedness) {
8061096 .unsigned => .uint64_t,
8071097 .signed => .int64_t,
8081098 },
809 65...128 => switch (signedness) {
1099 65...128 => switch (int_info.signedness) {
8101100 .unsigned => .zig_u128,
8111101 .signed => .zig_i128,
8121102 },
......@@ -945,31 +1235,27 @@ pub const CType = extern union {
9451235 .c_ulong => self.init(.@"unsigned long"),
9461236 .c_longlong => self.init(.@"long long"),
9471237 .c_ulonglong => self.init(.@"unsigned long long"),
948 else => {
949 const info = ty.intInfo(target);
950 const t = tagFromIntInfo(info.signedness, info.bits);
951 switch (t) {
952 .void => unreachable,
953 else => self.init(t),
954 .array => switch (kind) {
955 .forward, .complete, .global => {
956 const abi_size = ty.abiSize(target);
957 const abi_align = ty.abiAlignment(target);
958 self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{
959 .len = @divExact(abi_size, abi_align),
960 .elem_type = tagFromIntInfo(
961 .unsigned,
962 @intCast(u16, abi_align * 8),
963 ).toIndex(),
964 } } };
965 self.value = .{ .cty = initPayload(&self.storage.seq) };
966 },
967 .forward_parameter,
968 .parameter,
969 => try self.initArrayParameter(ty, kind, lookup),
970 .payload => unreachable,
1238 else => switch (tagFromIntInfo(ty.intInfo(target))) {
1239 .void => unreachable,
1240 else => |t| self.init(t),
1241 .array => switch (kind) {
1242 .forward, .complete, .global => {
1243 const abi_size = ty.abiSize(target);
1244 const abi_align = ty.abiAlignment(target);
1245 self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{
1246 .len = @divExact(abi_size, abi_align),
1247 .elem_type = tagFromIntInfo(.{
1248 .signedness = .unsigned,
1249 .bits = @intCast(u16, abi_align * 8),
1250 }).toIndex(),
1251 } } };
1252 self.value = .{ .cty = initPayload(&self.storage.seq) };
9711253 },
972 }
1254 .forward_parameter,
1255 .parameter,
1256 => try self.initArrayParameter(ty, kind, lookup),
1257 .payload => unreachable,
1258 },
9731259 },
9741260 } else switch (ty.zigTypeTag()) {
9751261 .Frame => unreachable,
test/behavior/bitcast.zig-1
......@@ -368,7 +368,6 @@ test "comptime @bitCast packed struct to int and back" {
368368}
369369
370370test "comptime bitcast with fields following f80" {
371 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
372371 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
373372 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
374373 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;