Commit 874ae81f1b

Jacob Young <jacobly0@users.noreply.github.com>
2023-03-03 06:18:34
CBE: implement big integer literals
1 parent e7f128c
Changed files (5)
lib
std
math
src
codegen
test
behavior
lib/std/math/big/int.zig
@@ -1674,6 +1674,7 @@ pub const Mutable = struct {
 
     /// If a is positive, this passes through to truncate.
     /// If a is negative, then r is set to positive with the bit pattern ~(a - 1).
+    /// r may alias a.
     ///
     /// Asserts `r` has enough storage to store the result.
     /// The upper bound is `calcTwosCompLimbCount(a.len)`.
lib/zig.h
@@ -1360,8 +1360,8 @@ typedef   signed __int128 zig_i128;
 
 #define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo))
 #define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo))
-#define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo)
-#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo)
+#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
+#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
 #define zig_hi_u128(val) ((uint64_t)((val) >> 64))
 #define zig_lo_u128(val) ((uint64_t)((val) >>  0))
 #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;
 #define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) })
 
 #if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */
-#define zig_make_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }
-#define zig_make_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }
+#define zig_init_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }
+#define zig_init_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }
 #else /* But non-MSVC doesn't like the unprotected commas */
-#define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo)
-#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo)
+#define zig_init_u128(hi, lo) zig_make_u128(hi, lo)
+#define zig_init_i128(hi, lo) zig_make_i128(hi, lo)
 #endif
 #define zig_hi_u128(val) ((val).hi)
 #define zig_lo_u128(val) ((val).lo)
src/codegen/c/type.zig
@@ -496,6 +496,296 @@ pub const CType = extern union {
         }
     };
 
+    pub fn toSigned(self: CType) CType {
+        return CType.initTag(switch (self.tag()) {
+            .char, .@"signed char", .@"unsigned char" => .@"signed char",
+            .short, .@"unsigned short" => .short,
+            .int, .@"unsigned int" => .int,
+            .long, .@"unsigned long" => .long,
+            .@"long long", .@"unsigned long long" => .@"long long",
+            .size_t, .ptrdiff_t => .ptrdiff_t,
+            .uint8_t, .int8_t => .int8_t,
+            .uint16_t, .int16_t => .int16_t,
+            .uint32_t, .int32_t => .int32_t,
+            .uint64_t, .int64_t => .int64_t,
+            .uintptr_t, .intptr_t => .intptr_t,
+            .zig_u128, .zig_i128 => .zig_i128,
+            .float,
+            .double,
+            .@"long double",
+            .zig_f16,
+            .zig_f32,
+            .zig_f80,
+            .zig_f128,
+            .zig_c_longdouble,
+            => |t| t,
+            else => unreachable,
+        });
+    }
+
+    pub fn toUnsigned(self: CType) CType {
+        return CType.initTag(switch (self.tag()) {
+            .char, .@"signed char", .@"unsigned char" => .@"unsigned char",
+            .short, .@"unsigned short" => .@"unsigned short",
+            .int, .@"unsigned int" => .@"unsigned int",
+            .long, .@"unsigned long" => .@"unsigned long",
+            .@"long long", .@"unsigned long long" => .@"unsigned long long",
+            .size_t, .ptrdiff_t => .size_t,
+            .uint8_t, .int8_t => .uint8_t,
+            .uint16_t, .int16_t => .uint16_t,
+            .uint32_t, .int32_t => .uint32_t,
+            .uint64_t, .int64_t => .uint64_t,
+            .uintptr_t, .intptr_t => .uintptr_t,
+            .zig_u128, .zig_i128 => .zig_u128,
+            else => unreachable,
+        });
+    }
+
+    pub fn getStandardDefineAbbrev(self: CType) ?[]const u8 {
+        return switch (self.tag()) {
+            .char => "CHAR",
+            .@"signed char" => "SCHAR",
+            .short => "SHRT",
+            .int => "INT",
+            .long => "LONG",
+            .@"long long" => "LLONG",
+            .@"unsigned char" => "UCHAR",
+            .@"unsigned short" => "USHRT",
+            .@"unsigned int" => "UINT",
+            .@"unsigned long" => "ULONG",
+            .@"unsigned long long" => "ULLONG",
+            .float => "FLT",
+            .double => "DBL",
+            .@"long double" => "LDBL",
+            .size_t => "SIZE",
+            .ptrdiff_t => "PTRDIFF",
+            .uint8_t => "UINT8",
+            .int8_t => "INT8",
+            .uint16_t => "UINT16",
+            .int16_t => "INT16",
+            .uint32_t => "UINT32",
+            .int32_t => "INT32",
+            .uint64_t => "UINT64",
+            .int64_t => "INT64",
+            .uintptr_t => "UINTPTR",
+            .intptr_t => "INTPTR",
+            else => null,
+        };
+    }
+
+    pub fn renderLiteralPrefix(self: CType, writer: anytype, kind: Kind) @TypeOf(writer).Error!void {
+        switch (self.tag()) {
+            .void => unreachable,
+            ._Bool,
+            .char,
+            .@"signed char",
+            .short,
+            .@"unsigned short",
+            .bool,
+            .size_t,
+            .ptrdiff_t,
+            .uintptr_t,
+            .intptr_t,
+            => |t| switch (kind) {
+                else => try writer.print("({s})", .{@tagName(t)}),
+                .global => {},
+            },
+            .int,
+            .long,
+            .@"long long",
+            .@"unsigned char",
+            .@"unsigned int",
+            .@"unsigned long",
+            .@"unsigned long long",
+            .float,
+            .double,
+            .@"long double",
+            => {},
+            .uint8_t,
+            .int8_t,
+            .uint16_t,
+            .int16_t,
+            .uint32_t,
+            .int32_t,
+            .uint64_t,
+            .int64_t,
+            => try writer.print("{s}_C(", .{self.getStandardDefineAbbrev().?}),
+            .zig_u128,
+            .zig_i128,
+            .zig_f16,
+            .zig_f32,
+            .zig_f64,
+            .zig_f80,
+            .zig_f128,
+            .zig_c_longdouble,
+            => |t| try writer.print("zig_{s}_{s}(", .{
+                switch (kind) {
+                    else => "make",
+                    .global => "init",
+                },
+                @tagName(t)["zig_".len..],
+            }),
+            .pointer,
+            .pointer_const,
+            .pointer_volatile,
+            .pointer_const_volatile,
+            => unreachable,
+            .array,
+            .vector,
+            => try writer.writeByte('{'),
+            .fwd_anon_struct,
+            .fwd_anon_union,
+            .fwd_struct,
+            .fwd_union,
+            .unnamed_struct,
+            .unnamed_union,
+            .packed_unnamed_struct,
+            .packed_unnamed_union,
+            .anon_struct,
+            .anon_union,
+            .@"struct",
+            .@"union",
+            .packed_struct,
+            .packed_union,
+            .function,
+            .varargs_function,
+            => unreachable,
+        }
+    }
+
+    pub fn renderLiteralSuffix(self: CType, writer: anytype) @TypeOf(writer).Error!void {
+        switch (self.tag()) {
+            .void => unreachable,
+            ._Bool => {},
+            .char,
+            .@"signed char",
+            .short,
+            .int,
+            => {},
+            .long => try writer.writeByte('l'),
+            .@"long long" => try writer.writeAll("ll"),
+            .@"unsigned char",
+            .@"unsigned short",
+            .@"unsigned int",
+            => try writer.writeByte('u'),
+            .@"unsigned long",
+            .size_t,
+            .uintptr_t,
+            => try writer.writeAll("ul"),
+            .@"unsigned long long" => try writer.writeAll("ull"),
+            .float => try writer.writeByte('f'),
+            .double => {},
+            .@"long double" => try writer.writeByte('l'),
+            .bool,
+            .ptrdiff_t,
+            .intptr_t,
+            => {},
+            .uint8_t,
+            .int8_t,
+            .uint16_t,
+            .int16_t,
+            .uint32_t,
+            .int32_t,
+            .uint64_t,
+            .int64_t,
+            .zig_u128,
+            .zig_i128,
+            .zig_f16,
+            .zig_f32,
+            .zig_f64,
+            .zig_f80,
+            .zig_f128,
+            .zig_c_longdouble,
+            => try writer.writeByte(')'),
+            .pointer,
+            .pointer_const,
+            .pointer_volatile,
+            .pointer_const_volatile,
+            => unreachable,
+            .array,
+            .vector,
+            => try writer.writeByte('}'),
+            .fwd_anon_struct,
+            .fwd_anon_union,
+            .fwd_struct,
+            .fwd_union,
+            .unnamed_struct,
+            .unnamed_union,
+            .packed_unnamed_struct,
+            .packed_unnamed_union,
+            .anon_struct,
+            .anon_union,
+            .@"struct",
+            .@"union",
+            .packed_struct,
+            .packed_union,
+            .function,
+            .varargs_function,
+            => unreachable,
+        }
+    }
+
+    pub fn byteSize(self: CType, store: Store.Set, target: Target) u64 {
+        return switch (self.tag()) {
+            .void => 0,
+            .char, .@"signed char", ._Bool, .@"unsigned char", .bool, .uint8_t, .int8_t => 1,
+            .short => target.c_type_byte_size(.short),
+            .int => target.c_type_byte_size(.int),
+            .long => target.c_type_byte_size(.long),
+            .@"long long" => target.c_type_byte_size(.longlong),
+            .@"unsigned short" => target.c_type_byte_size(.ushort),
+            .@"unsigned int" => target.c_type_byte_size(.uint),
+            .@"unsigned long" => target.c_type_byte_size(.ulong),
+            .@"unsigned long long" => target.c_type_byte_size(.ulonglong),
+            .float => target.c_type_byte_size(.float),
+            .double => target.c_type_byte_size(.double),
+            .@"long double" => target.c_type_byte_size(.longdouble),
+            .size_t,
+            .ptrdiff_t,
+            .uintptr_t,
+            .intptr_t,
+            .pointer,
+            .pointer_const,
+            .pointer_volatile,
+            .pointer_const_volatile,
+            => @divExact(target.cpu.arch.ptrBitWidth(), 8),
+            .uint16_t, .int16_t, .zig_f16 => 2,
+            .uint32_t, .int32_t, .zig_f32 => 4,
+            .uint64_t, .int64_t, .zig_f64 => 8,
+            .zig_u128, .zig_i128, .zig_f128 => 16,
+            .zig_f80 => if (target.c_type_bit_size(.longdouble) == 80)
+                target.c_type_byte_size(.longdouble)
+            else
+                16,
+            .zig_c_longdouble => target.c_type_byte_size(.longdouble),
+
+            .array,
+            .vector,
+            => {
+                const data = self.cast(Payload.Sequence).?.data;
+                return data.len * store.indexToCType(data.elem_type).byteSize(store, target);
+            },
+
+            .fwd_anon_struct,
+            .fwd_anon_union,
+            .fwd_struct,
+            .fwd_union,
+            .unnamed_struct,
+            .unnamed_union,
+            .packed_unnamed_struct,
+            .packed_unnamed_union,
+            .anon_struct,
+            .anon_union,
+            .@"struct",
+            .@"union",
+            .packed_struct,
+            .packed_union,
+            .function,
+            .varargs_function,
+            => unreachable,
+        };
+    }
+
     pub fn isPacked(self: CType) bool {
         return switch (self.tag()) {
             else => false,
@@ -787,26 +1077,26 @@ pub const CType = extern union {
             };
         }
 
-        fn tagFromIntInfo(signedness: std.builtin.Signedness, bits: u16) Tag {
-            return switch (bits) {
+        fn tagFromIntInfo(int_info: std.builtin.Type.Int) Tag {
+            return switch (int_info.bits) {
                 0 => .void,
-                1...8 => switch (signedness) {
+                1...8 => switch (int_info.signedness) {
                     .unsigned => .uint8_t,
                     .signed => .int8_t,
                 },
-                9...16 => switch (signedness) {
+                9...16 => switch (int_info.signedness) {
                     .unsigned => .uint16_t,
                     .signed => .int16_t,
                 },
-                17...32 => switch (signedness) {
+                17...32 => switch (int_info.signedness) {
                     .unsigned => .uint32_t,
                     .signed => .int32_t,
                 },
-                33...64 => switch (signedness) {
+                33...64 => switch (int_info.signedness) {
                     .unsigned => .uint64_t,
                     .signed => .int64_t,
                 },
-                65...128 => switch (signedness) {
+                65...128 => switch (int_info.signedness) {
                     .unsigned => .zig_u128,
                     .signed => .zig_i128,
                 },
@@ -945,31 +1235,27 @@ pub const CType = extern union {
                 .c_ulong => self.init(.@"unsigned long"),
                 .c_longlong => self.init(.@"long long"),
                 .c_ulonglong => self.init(.@"unsigned long long"),
-                else => {
-                    const info = ty.intInfo(target);
-                    const t = tagFromIntInfo(info.signedness, info.bits);
-                    switch (t) {
-                        .void => unreachable,
-                        else => self.init(t),
-                        .array => switch (kind) {
-                            .forward, .complete, .global => {
-                                const abi_size = ty.abiSize(target);
-                                const abi_align = ty.abiAlignment(target);
-                                self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{
-                                    .len = @divExact(abi_size, abi_align),
-                                    .elem_type = tagFromIntInfo(
-                                        .unsigned,
-                                        @intCast(u16, abi_align * 8),
-                                    ).toIndex(),
-                                } } };
-                                self.value = .{ .cty = initPayload(&self.storage.seq) };
-                            },
-                            .forward_parameter,
-                            .parameter,
-                            => try self.initArrayParameter(ty, kind, lookup),
-                            .payload => unreachable,
+                else => switch (tagFromIntInfo(ty.intInfo(target))) {
+                    .void => unreachable,
+                    else => |t| self.init(t),
+                    .array => switch (kind) {
+                        .forward, .complete, .global => {
+                            const abi_size = ty.abiSize(target);
+                            const abi_align = ty.abiAlignment(target);
+                            self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{
+                                .len = @divExact(abi_size, abi_align),
+                                .elem_type = tagFromIntInfo(.{
+                                    .signedness = .unsigned,
+                                    .bits = @intCast(u16, abi_align * 8),
+                                }).toIndex(),
+                            } } };
+                            self.value = .{ .cty = initPayload(&self.storage.seq) };
                         },
-                    }
+                        .forward_parameter,
+                        .parameter,
+                        => try self.initArrayParameter(ty, kind, lookup),
+                        .payload => unreachable,
+                    },
                 },
             } else switch (ty.zigTypeTag()) {
                 .Frame => unreachable,
src/codegen/c.zig
@@ -449,7 +449,7 @@ pub const Function = struct {
     }
 
     fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) {
-        return f.object.dg.fmtIntLiteral(ty, val);
+        return f.object.dg.fmtIntLiteral(ty, val, .Other);
     }
 
     fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 {
@@ -574,9 +574,9 @@ pub const DeclGen = struct {
             const len_val = Value.initPayload(&len_pl.base);
 
             if (location == .StaticInitializer) {
-                return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val)});
+                return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)});
             } else {
-                return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val)});
+                return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)});
             }
         }
 
@@ -606,7 +606,7 @@ pub const DeclGen = struct {
             try writer.writeByte(')');
         }
         switch (ptr_val.tag()) {
-            .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val)}),
+            .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val, .Other)}),
             .decl_ref_mut, .decl_ref, .variable => {
                 const decl_index = switch (ptr_val.tag()) {
                     .decl_ref => ptr_val.castTag(.decl_ref).?.data,
@@ -670,7 +670,9 @@ pub const DeclGen = struct {
                             container_ptr_ty,
                             location,
                         );
-                        try writer.print(" + {})", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)});
+                        try writer.print(" + {})", .{
+                            try dg.fmtIntLiteral(Type.usize, byte_offset_val, .Other),
+                        });
                     },
                     .end => {
                         try writer.writeAll("((");
@@ -680,7 +682,9 @@ pub const DeclGen = struct {
                             container_ptr_ty,
                             location,
                         );
-                        try writer.print(") + {})", .{try dg.fmtIntLiteral(Type.usize, Value.one)});
+                        try writer.print(") + {})", .{
+                            try dg.fmtIntLiteral(Type.usize, Value.one, .Other),
+                        });
                     },
                 }
             },
@@ -746,7 +750,7 @@ pub const DeclGen = struct {
                         return writer.writeAll("false");
                     }
                 },
-                .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteralLoc(ty, val, location)}),
+                .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val, location)}),
                 .Float => {
                     const bits = ty.floatBits(target);
                     var int_pl = Type.Payload.Bits{ .base = .{ .tag = .int_signed }, .data = bits };
@@ -780,11 +784,11 @@ pub const DeclGen = struct {
                     var buf: Type.SlicePtrFieldTypeBuffer = undefined;
                     const ptr_ty = ty.slicePtrFieldType(&buf);
                     try dg.renderType(writer, ptr_ty);
-                    return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val)});
+                    return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val, .Other)});
                 } else {
                     try writer.writeAll("((");
                     try dg.renderType(writer, ty);
-                    return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)});
+                    return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)});
                 },
                 .Optional => {
                     var opt_buf: Type.Payload.ElemType = undefined;
@@ -831,7 +835,7 @@ pub const DeclGen = struct {
 
                         return writer.writeByte('}');
                     },
-                    .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef)}),
+                    .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef, .Other)}),
                 },
                 .Union => {
                     if (!location.isInitializer()) {
@@ -854,7 +858,7 @@ pub const DeclGen = struct {
                         if (!field.ty.hasRuntimeBits()) continue;
                         try dg.renderValue(writer, field.ty, val, initializer_type);
                         break;
-                    } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)});
+                    } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef, .Other)});
                     if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}');
                     return writer.writeByte('}');
                 },
@@ -868,7 +872,7 @@ pub const DeclGen = struct {
                     try writer.writeAll("{ .payload = ");
                     try dg.renderValue(writer, ty.errorUnionPayload(), val, initializer_type);
                     return writer.print(", .error = {x} }}", .{
-                        try dg.fmtIntLiteral(ty.errorUnionSet(), val),
+                        try dg.fmtIntLiteral(ty.errorUnionSet(), val, .Other),
                     });
                 },
                 .Array, .Vector => {
@@ -927,7 +931,7 @@ pub const DeclGen = struct {
                 .decl_ref_mut,
                 .decl_ref,
                 => try dg.renderParentPtr(writer, val, ty, location),
-                else => try writer.print("{}", .{try dg.fmtIntLiteralLoc(ty, val, location)}),
+                else => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, location)}),
             },
             .Float => {
                 const bits = ty.floatBits(target);
@@ -1020,7 +1024,7 @@ pub const DeclGen = struct {
                     try writer.writeAll(", ");
                     empty = false;
                 }
-                try writer.print("{x}", .{try dg.fmtIntLiteralLoc(int_ty, int_val, location)});
+                try writer.print("{x}", .{try dg.fmtIntLiteral(int_ty, int_val, location)});
                 if (!empty) try writer.writeByte(')');
                 return;
             },
@@ -1069,7 +1073,7 @@ pub const DeclGen = struct {
                 .int_u64, .one => {
                     try writer.writeAll("((");
                     try dg.renderType(writer, ty);
-                    return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)});
+                    return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)});
                 },
                 .field_ptr,
                 .elem_ptr,
@@ -1889,11 +1893,11 @@ pub const DeclGen = struct {
                 const int_info = ty.intInfo(target);
                 if (int_info.signedness == .signed) {
                     const min_val = try ty.minInt(stack.get(), target);
-                    try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, min_val)});
+                    try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, min_val, .Other)});
                 }
 
                 const max_val = try ty.maxInt(stack.get(), target);
-                try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val)});
+                try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val, .Other)});
             },
             .Bits => {
                 var bits_pl = Value.Payload.U64{
@@ -1901,7 +1905,7 @@ pub const DeclGen = struct {
                     .data = ty.bitSize(target),
                 };
                 const bits_val = Value.initPayload(&bits_pl.base);
-                try writer.print(", {}", .{try dg.fmtIntLiteral(Type.u8, bits_val)});
+                try writer.print(", {}", .{try dg.fmtIntLiteral(Type.u8, bits_val, .Other)});
             },
         }
     }
@@ -1910,30 +1914,21 @@ pub const DeclGen = struct {
         dg: *DeclGen,
         ty: Type,
         val: Value,
+        loc: ValueRenderLocation,
     ) !std.fmt.Formatter(formatIntLiteral) {
-        const int_info = ty.intInfo(dg.module.getTarget());
-        const c_bits = toCIntBits(int_info.bits);
-        if (c_bits == null or c_bits.? > 128)
-            return dg.fail("TODO implement integer constants larger than 128 bits", .{});
+        const kind: CType.Kind = switch (loc) {
+            .FunctionArgument => .parameter,
+            .Initializer, .Other => .complete,
+            .StaticInitializer => .global,
+        };
         return std.fmt.Formatter(formatIntLiteral){ .data = .{
-            .ty = ty,
+            .dg = dg,
+            .int_info = ty.intInfo(dg.module.getTarget()),
+            .kind = kind,
+            .cty = try dg.typeToCType(ty, kind),
             .val = val,
-            .mod = dg.module,
         } };
     }
-
-    fn fmtIntLiteralLoc(
-        dg: *DeclGen,
-        ty: Type,
-        val: Value,
-        location: ValueRenderLocation, // TODO: Instead add this as optional arg to fmtIntLiteral
-    ) !std.fmt.Formatter(formatIntLiteral) {
-        const int_info = ty.intInfo(dg.module.getTarget());
-        const c_bits = toCIntBits(int_info.bits);
-        if (c_bits == null or c_bits.? > 128)
-            return dg.fail("TODO implement integer constants larger than 128 bits", .{});
-        return std.fmt.Formatter(formatIntLiteral){ .data = .{ .ty = ty, .val = val, .mod = dg.module, .location = location } };
-    }
 };
 
 const CTypeFix = enum { prefix, suffix };
@@ -2450,7 +2445,7 @@ pub fn genErrDecls(o: *Object) !void {
         const len_val = Value.initPayload(&len_pl.base);
 
         try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{
-            fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val),
+            fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other),
         });
     }
     try writer.writeAll("};\n");
@@ -2501,7 +2496,10 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
                 var int_pl: Value.Payload.U64 = undefined;
                 const int_val = tag_val.enumToInt(enum_ty, &int_pl);
 
-                var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len };
+                var name_ty_pl = Type.Payload.Len{
+                    .base = .{ .tag = .array_u8_sentinel_0 },
+                    .data = name.len,
+                };
                 const name_ty = Type.initPayload(&name_ty_pl.base);
 
                 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 {
                 var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len };
                 const len_val = Value.initPayload(&len_pl.base);
 
-                try w.print("  case {}: {{\n   static ", .{try o.dg.fmtIntLiteral(enum_ty, int_val)});
+                try w.print("  case {}: {{\n   static ", .{
+                    try o.dg.fmtIntLiteral(enum_ty, int_val, .Other),
+                });
                 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 w.writeAll(";\n   return (");
                 try o.dg.renderType(w, name_slice_ty);
                 try w.print("){{{}, {}}};\n", .{
-                    fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val),
+                    fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other),
                 });
 
                 try w.writeAll("  }\n");
@@ -2535,7 +2535,12 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
 
             const fwd_decl_writer = o.dg.fwd_decl.writer();
             try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)});
-            try o.dg.renderFunctionSignature(fwd_decl_writer, fn_decl_index, .forward, .{ .string = fn_name });
+            try o.dg.renderFunctionSignature(
+                fwd_decl_writer,
+                fn_decl_index,
+                .forward,
+                .{ .string = fn_name },
+            );
             try fwd_decl_writer.writeAll(";\n");
 
             try w.print("static zig_{s} ", .{@tagName(key)});
@@ -7177,30 +7182,33 @@ fn undefPattern(comptime IntType: type) IntType {
     return @bitCast(IntType, @as(UnsignedType, (1 << (int_info.bits | 1)) / 3));
 }
 
-const FormatIntLiteralContext = struct { ty: Type, val: Value, mod: *Module, location: ?ValueRenderLocation = null };
+const FormatIntLiteralContext = struct {
+    dg: *DeclGen,
+    int_info: std.builtin.Type.Int,
+    kind: CType.Kind,
+    cty: CType,
+    val: Value,
+};
 fn formatIntLiteral(
     data: FormatIntLiteralContext,
     comptime fmt: []const u8,
     options: std.fmt.FormatOptions,
     writer: anytype,
 ) @TypeOf(writer).Error!void {
-    const target = data.mod.getTarget();
-    const int_info = data.ty.intInfo(target);
+    const target = data.dg.module.getTarget();
 
     const ExpectedContents = struct {
         const base = 10;
-        const limbs_count_128 = BigInt.calcTwosCompLimbCount(128);
-        const expected_needed_limbs_count = BigInt.calcToStringLimbsBufferLen(limbs_count_128, base);
-        const worst_case_int = BigInt.Const{
-            .limbs = &([1]BigIntLimb{std.math.maxInt(BigIntLimb)} ** expected_needed_limbs_count),
-            .positive = false,
-        };
+        const bits = 128;
+        const limbs_count = BigInt.calcTwosCompLimbCount(bits);
 
-        undef_limbs: [limbs_count_128]BigIntLimb,
-        wrap_limbs: [limbs_count_128]BigIntLimb,
+        undef_limbs: [limbs_count]BigIntLimb,
+        wrap_limbs: [limbs_count]BigIntLimb,
+        to_string_buf: [bits]u8,
+        to_string_limbs: [BigInt.calcToStringLimbsBufferLen(limbs_count, base)]BigIntLimb,
     };
     var stack align(@alignOf(ExpectedContents)) =
-        std.heap.stackFallback(@sizeOf(ExpectedContents), data.mod.gpa);
+        std.heap.stackFallback(@sizeOf(ExpectedContents), data.dg.gpa);
     const allocator = stack.get();
 
     var undef_limbs: []BigIntLimb = &.{};
@@ -7208,7 +7216,7 @@ fn formatIntLiteral(
 
     var int_buf: Value.BigIntSpace = undefined;
     const int = if (data.val.isUndefDeep()) blk: {
-        undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(int_info.bits));
+        undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits));
         std.mem.set(BigIntLimb, undef_limbs, undefPattern(BigIntLimb));
 
         var undef_int = BigInt.Mutable{
@@ -7216,163 +7224,150 @@ fn formatIntLiteral(
             .len = undef_limbs.len,
             .positive = true,
         };
-        undef_int.truncate(undef_int.toConst(), int_info.signedness, int_info.bits);
+        undef_int.truncate(undef_int.toConst(), data.int_info.signedness, data.int_info.bits);
         break :blk undef_int.toConst();
     } else data.val.toBigInt(&int_buf, target);
-    assert(int.fitsInTwosComp(int_info.signedness, int_info.bits));
+    assert(int.fitsInTwosComp(data.int_info.signedness, data.int_info.bits));
 
-    const c_bits = toCIntBits(int_info.bits) orelse unreachable;
+    const c_bits = @intCast(usize, data.cty.byteSize(data.dg.ctypes.set, target) * 8);
     var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined;
     const one = BigInt.Mutable.init(&one_limbs, 1).toConst();
 
-    const wrap_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits));
-    defer allocator.free(wrap_limbs);
-    var wrap = BigInt.Mutable{ .limbs = wrap_limbs, .len = undefined, .positive = undefined };
-    if (wrap.addWrap(int, one, int_info.signedness, c_bits) or
-        int_info.signedness == .signed and wrap.subWrap(int, one, int_info.signedness, c_bits))
-    {
-        const abbrev = switch (data.ty.tag()) {
-            .c_short, .c_ushort => "SHRT",
-            .c_int, .c_uint => "INT",
-            .c_long, .c_ulong => "LONG",
-            .c_longlong, .c_ulonglong => "LLONG",
-            .isize, .usize => "INTPTR",
-            else => return writer.print("zig_{s}Int_{c}{d}", .{
-                if (int.positive) "max" else "min", signAbbrev(int_info.signedness), c_bits,
+    var wrap = BigInt.Mutable{
+        .limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)),
+        .len = undefined,
+        .positive = undefined,
+    };
+    defer allocator.free(wrap.limbs);
+    if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or
+        data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits))
+        return writer.print("{s}_{s}", .{
+            data.cty.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{
+                if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits,
             }),
-        };
-        if (int_info.signedness == .unsigned) try writer.writeByte('U');
-        return writer.print("{s}_{s}", .{ abbrev, if (int.positive) "MAX" else "MIN" });
-    }
-
-    var use_twos_comp = false;
-    if (!int.positive) {
-        if (c_bits > 64) {
-            // TODO: Can this be done for decimal literals as well?
-            if (fmt.len == 1 and fmt[0] != 'd') {
-                use_twos_comp = true;
-            } else {
-                // TODO: Use fmtIntLiteral for 0?
-                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 });
-            }
-        } else {
-            try writer.writeByte('-');
-        }
-    }
+            if (int.positive) "MAX" else "MIN",
+        });
 
-    switch (data.ty.tag()) {
-        .c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {},
-        else => {
-            if (int_info.bits <= 64) {
-                try writer.print("{s}INT{d}_C(", .{ switch (int_info.signedness) {
-                    .signed => "",
-                    .unsigned => "U",
-                }, c_bits });
-            } else if (data.location != null and data.location.? == .StaticInitializer) {
-                // MSVC treats casting the struct initializer as not constant (C2099), so an alternate form is used in global initializers
-                try writer.print("zig_make_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits });
-            } else {
-                try writer.print("zig_make_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits });
-            }
+    const c_limb_info: struct {
+        cty: CType,
+        count: usize,
+        endian: std.builtin.Endian,
+        homogeneous: bool,
+    } = switch (data.cty.tag()) {
+        else => .{
+            .cty = CType.initTag(.void),
+            .count = 1,
+            .endian = .Little,
+            .homogeneous = true,
         },
-    }
+        .zig_u128, .zig_i128 => .{
+            .cty = CType.initTag(.uint64_t),
+            .count = 2,
+            .endian = .Big,
+            .homogeneous = false,
+        },
+        .array => info: {
+            const array_data = data.cty.castTag(.array).?.data;
+            break :info .{
+                .cty = data.dg.indexToCType(array_data.elem_type),
+                .count = @intCast(usize, array_data.len),
+                .endian = target.cpu.arch.endian(),
+                .homogeneous = true,
+            };
+        },
+    };
+    if (c_limb_info.count == 1) {
+        if (!int.positive) try writer.writeByte('-');
+        try data.cty.renderLiteralPrefix(writer, data.kind);
 
-    const limbs_count_64 = @divExact(64, @bitSizeOf(BigIntLimb));
-    if (c_bits <= 64) {
-        var base: u8 = undefined;
-        var case: std.fmt.Case = undefined;
-        switch (fmt.len) {
-            0 => base = 10,
+        const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) {
+            0 => .{ .base = 10 },
             1 => switch (fmt[0]) {
-                'b' => {
-                    base = 2;
+                'b' => style: {
                     try writer.writeAll("0b");
+                    break :style .{ .base = 2 };
                 },
-                'o' => {
-                    base = 8;
+                'o' => style: {
                     try writer.writeByte('0');
+                    break :style .{ .base = 8 };
                 },
-                'd' => base = 10,
-                'x' => {
-                    base = 16;
-                    case = .lower;
-                    try writer.writeAll("0x");
-                },
-                'X' => {
-                    base = 16;
-                    case = .upper;
+                'd' => .{ .base = 10 },
+                'x', 'X' => |base| style: {
                     try writer.writeAll("0x");
+                    break :style .{ .base = 16, .case = switch (base) {
+                        'x' => .lower,
+                        'X' => .upper,
+                        else => unreachable,
+                    } };
                 },
                 else => @compileError("Invalid fmt: " ++ fmt),
             },
             else => @compileError("Invalid fmt: " ++ fmt),
-        }
+        };
 
-        var str: [64]u8 = undefined;
-        var limbs_buf: [BigInt.calcToStringLimbsBufferLen(limbs_count_64, 10)]BigIntLimb = undefined;
-        try writer.writeAll(str[0..int.abs().toString(&str, base, case, &limbs_buf)]);
+        const string = try int.abs().toStringAlloc(allocator, style.base, style.case);
+        defer allocator.free(string);
+        try writer.writeAll(string);
     } else {
-        assert(c_bits == 128);
-        const split = std.math.min(int.limbs.len, limbs_count_64);
-        var twos_comp_limbs: [BigInt.calcTwosCompLimbCount(128)]BigIntLimb = undefined;
-
-        // Adding a negation in the C code before the doesn't work in all cases:
-        // - struct versions would require an extra zig_sub_ call to negate, which wouldn't work in constant expressions
-        // - negating the f80 int representation (i128) doesn't make sense
-        // Instead we write out the literal as a negative number in twos complement
-        var limbs = int.limbs;
-
-        if (use_twos_comp) {
-            var twos_comp = BigInt.Mutable{
-                .limbs = &twos_comp_limbs,
-                .positive = undefined,
+        try data.cty.renderLiteralPrefix(writer, data.kind);
+        wrap.convertToTwosComplement(int, .unsigned, data.int_info.bits);
+        std.mem.set(BigIntLimb, wrap.limbs[wrap.len..], 0);
+        wrap.len = wrap.limbs.len;
+        const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count);
+
+        var c_limb_int_info = std.builtin.Type.Int{
+            .signedness = undefined,
+            .bits = @intCast(u16, @divExact(c_bits, c_limb_info.count)),
+        };
+        var c_limb_cty: CType = undefined;
+
+        var limb_offset: usize = 0;
+        const most_significant_limb_i = wrap.len - limbs_per_c_limb;
+        while (limb_offset < wrap.len) : (limb_offset += limbs_per_c_limb) {
+            const limb_i = switch (c_limb_info.endian) {
+                .Little => limb_offset,
+                .Big => most_significant_limb_i - limb_offset,
+            };
+            var c_limb_mut = BigInt.Mutable{
+                .limbs = wrap.limbs[limb_i..][0..limbs_per_c_limb],
                 .len = undefined,
+                .positive = true,
             };
+            c_limb_mut.normalize(limbs_per_c_limb);
 
-            twos_comp.convertToTwosComplement(int, .signed, int_info.bits);
-            limbs = twos_comp.limbs;
-        }
-
-        var upper_pl = Value.Payload.BigInt{
-            .base = .{ .tag = .int_big_positive },
-            .data = limbs[split..],
-        };
-        const upper_val = Value.initPayload(&upper_pl.base);
-        try formatIntLiteral(.{
-            .ty = switch (int_info.signedness) {
-                .unsigned => Type.u64,
-                .signed => if (use_twos_comp) Type.u64 else Type.i64,
-            },
-            .val = upper_val,
-            .mod = data.mod,
-        }, fmt, options, writer);
-
-        try writer.writeAll(", ");
+            if (limb_i == most_significant_limb_i and
+                !c_limb_info.homogeneous and data.int_info.signedness == .signed)
+            {
+                // most significant limb is actually signed
+                c_limb_int_info.signedness = .signed;
+                c_limb_cty = c_limb_info.cty.toSigned();
+
+                c_limb_mut.positive = wrap.positive;
+                c_limb_mut.convertToTwosComplement(
+                    c_limb_mut.toConst(),
+                    .signed,
+                    data.int_info.bits - limb_i * @bitSizeOf(BigIntLimb),
+                );
+            } else {
+                c_limb_int_info.signedness = .unsigned;
+                c_limb_cty = c_limb_info.cty;
+            }
+            var c_limb_val_pl = Value.Payload.BigInt{
+                .base = .{ .tag = if (c_limb_mut.positive) .int_big_positive else .int_big_negative },
+                .data = c_limb_mut.limbs[0..c_limb_mut.len],
+            };
 
-        var lower_pl = Value.Payload.BigInt{
-            .base = .{ .tag = .int_big_positive },
-            .data = limbs[0..split],
-        };
-        const lower_val = Value.initPayload(&lower_pl.base);
-        try formatIntLiteral(.{
-            .ty = Type.u64,
-            .val = lower_val,
-            .mod = data.mod,
-        }, fmt, options, writer);
-
-        if (!int.positive and c_bits > 64 and !use_twos_comp) try writer.writeByte(')');
-        return writer.writeByte(')');
-    }
-
-    switch (data.ty.tag()) {
-        .c_short, .c_ushort, .c_int => {},
-        .c_uint => try writer.writeAll("u"),
-        .c_long => try writer.writeAll("l"),
-        .c_ulong => try writer.writeAll("ul"),
-        .c_longlong => try writer.writeAll("ll"),
-        .c_ulonglong => try writer.writeAll("ull"),
-        else => try writer.writeByte(')'),
+            if (limb_offset > 0) try writer.writeAll(", ");
+            try formatIntLiteral(.{
+                .dg = data.dg,
+                .int_info = c_limb_int_info,
+                .kind = data.kind,
+                .cty = c_limb_cty,
+                .val = Value.initPayload(&c_limb_val_pl.base),
+            }, fmt, options, writer);
+        }
     }
+    try data.cty.renderLiteralSuffix(writer);
 }
 
 fn isByRef(ty: Type) bool {
test/behavior/bitcast.zig
@@ -368,7 +368,6 @@ test "comptime @bitCast packed struct to int and back" {
 }
 
 test "comptime bitcast with fields following f80" {
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;