CBE: implement big integer literals

This commit is contained in:
Jacob Young
2023-03-03 00:18:34 -05:00
parent e7f128c205
commit 874ae81f1b
5 changed files with 502 additions and 221 deletions

View File

@@ -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)`.

View File

@@ -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)

View File

@@ -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" });
}
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('-');
}
}
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;
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);
// 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;
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;
if (use_twos_comp) {
var twos_comp = BigInt.Mutable{
.limbs = &twos_comp_limbs,
.positive = 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);
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],
};
twos_comp.convertToTwosComplement(int, .signed, int_info.bits);
limbs = twos_comp.limbs;
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);
}
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(", ");
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(')'),
}
try data.cty.renderLiteralSuffix(writer);
}
fn isByRef(ty: Type) bool {

View File

@@ -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,

View File

@@ -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;