diff --git a/src/aro/Compilation.zig b/src/aro/Compilation.zig index 7ca4cb99..f1401676 100644 --- a/src/aro/Compilation.zig +++ b/src/aro/Compilation.zig @@ -994,13 +994,28 @@ fn generateVaListType(comp: *Compilation) !Type { fn generateIntMax(comp: *const Compilation, w: anytype, name: []const u8, ty: Type) !void { const bit_count: u8 = @intCast(ty.sizeof(comp).? * 8); const unsigned = ty.isUnsignedInt(comp); - const max = if (bit_count == 128) - @as(u128, if (unsigned) std.math.maxInt(u128) else std.math.maxInt(u128)) - else - ty.maxInt(comp); + const max: u128 = switch (bit_count) { + 8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8), + 16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16), + 32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32), + 64 => if (unsigned) std.math.maxInt(u64) else std.math.maxInt(i64), + 128 => if (unsigned) std.math.maxInt(u128) else std.math.maxInt(i128), + else => unreachable, + }; try w.print("#define __{s}_MAX__ {d}{s}\n", .{ name, max, ty.intValueSuffix(comp) }); } +/// Largest value that can be stored in wchar_t +pub fn wcharMax(comp: *const Compilation) u32 { + const unsigned = comp.types.wchar.isUnsignedInt(comp); + return switch (comp.types.wchar.bitSizeof(comp).?) { + 8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8), + 16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16), + 32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32), + else => unreachable, + }; +} + fn generateExactWidthIntMax(comp: *const Compilation, w: anytype, specifier: Type.Specifier) !void { var ty = Type{ .specifier = specifier }; const bit_count: u8 = @intCast(ty.sizeof(comp).? * 8); diff --git a/src/aro/Parser.zig b/src/aro/Parser.zig index f7996b3b..0b200a34 100644 --- a/src/aro/Parser.zig +++ b/src/aro/Parser.zig @@ -2814,14 +2814,12 @@ fn enumerator(p: *Parser, e: *Enumerator) Error!?EnumFieldAndNode { if (err_start == p.comp.diagnostics.list.items.len) { // only do these warnings if we didn't already warn about overflow or non-representable values if (e.res.val.compare(.lt, Value.zero, p.comp)) { - const min_int = (Type{ .specifier = .int }).minInt(p.comp); - const min_val = try Value.int(min_int, p.comp); + const min_val = try Value.minInt(Type.int, p.comp); if (e.res.val.compare(.lt, min_val, p.comp)) { try p.errStr(.enumerator_too_small, name_tok, try e.res.str(p)); } } else { - const max_int = (Type{ .specifier = .int }).maxInt(p.comp); - const max_val = try Value.int(max_int, p.comp); + const max_val = try Value.maxInt(Type.int, p.comp); if (e.res.val.compare(.gt, max_val, p.comp)) { try p.errStr(.enumerator_too_large, name_tok, try e.res.str(p)); } @@ -6024,8 +6022,8 @@ pub const Result = struct { } fn intFitsInType(res: Result, p: *Parser, ty: Type) !bool { - const max_int = try Value.int(ty.maxInt(p.comp), p.comp); - const min_int = try Value.int(ty.minInt(p.comp), p.comp); + const max_int = try Value.maxInt(ty, p.comp); + const min_int = try Value.minInt(ty, p.comp); return res.val.compare(.lte, max_int, p.comp) and (res.ty.isUnsignedInt(p.comp) or res.val.compare(.gte, min_int, p.comp)); } @@ -8371,8 +8369,10 @@ fn fixedSizeInt(p: *Parser, base: u8, buf: []const u8, suffix: NumberSuffix, tok if (!p.in_macro) try p.value_map.put(res.node, res.val); return res; } + const interned_val = try Value.int(val, p.comp); if (suffix.isSignedInteger()) { - if (val > p.comp.types.intmax.maxInt(p.comp)) { + const max_int = try Value.maxInt(p.comp.types.intmax, p.comp); + if (interned_val.compare(.gt, max_int, p.comp)) { try p.errTok(.implicitly_unsigned_literal, tok_i); } } @@ -8400,8 +8400,8 @@ fn fixedSizeInt(p: *Parser, base: u8, buf: []const u8, suffix: NumberSuffix, tok for (specs) |spec| { res.ty = Type{ .specifier = spec }; if (res.ty.compareIntegerRanks(suffix_ty, p.comp).compare(.lt)) continue; - const max_int = res.ty.maxInt(p.comp); - if (val <= max_int) break; + const max_int = try Value.maxInt(res.ty, p.comp); + if (interned_val.compare(.lte, max_int, p.comp)) break; } else { res.ty = .{ .specifier = .ulong_long }; } diff --git a/src/aro/Type.zig b/src/aro/Type.zig index 949bd3a9..0ff08ee8 100644 --- a/src/aro/Type.zig +++ b/src/aro/Type.zig @@ -959,31 +959,6 @@ pub fn hasField(ty: Type, name: StringId) bool { return false; } -// TODO handle bitints -pub fn minInt(ty: Type, comp: *const Compilation) i64 { - std.debug.assert(ty.isInt()); - if (ty.isUnsignedInt(comp)) return 0; - return switch (ty.sizeof(comp).?) { - 1 => std.math.minInt(i8), - 2 => std.math.minInt(i16), - 4 => std.math.minInt(i32), - 8 => std.math.minInt(i64), - else => unreachable, - }; -} - -// TODO handle bitints -pub fn maxInt(ty: Type, comp: *const Compilation) u64 { - std.debug.assert(ty.isInt()); - return switch (ty.sizeof(comp).?) { - 1 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u8)) else std.math.maxInt(i8), - 2 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u16)) else std.math.maxInt(i16), - 4 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u32)) else std.math.maxInt(i32), - 8 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u64)) else std.math.maxInt(i64), - else => unreachable, - }; -} - const TypeSizeOrder = enum { lt, gt, diff --git a/src/aro/Value.zig b/src/aro/Value.zig index d703eb49..34025fc1 100644 --- a/src/aro/Value.zig +++ b/src/aro/Value.zig @@ -676,11 +676,11 @@ pub fn rem(lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !Value { const signedness = ty.signedness(comp); if (signedness == .signed) { - var spaces: [3]BigIntSpace = undefined; - const min_val = BigIntMutable.init(&spaces[0].limbs, ty.minInt(comp)).toConst(); - const negative = BigIntMutable.init(&spaces[1].limbs, -1).toConst(); - const big_one = BigIntMutable.init(&spaces[2].limbs, 1).toConst(); - if (lhs_bigint.eql(min_val) and rhs_bigint.eql(negative)) { + var spaces: [2]BigIntSpace = undefined; + const min_val = try Value.minInt(ty, comp); + const negative = BigIntMutable.init(&spaces[0].limbs, -1).toConst(); + const big_one = BigIntMutable.init(&spaces[1].limbs, 1).toConst(); + if (lhs.compare(.eq, min_val, comp) and rhs_bigint.eql(negative)) { return .{}; } else if (rhs_bigint.order(big_one).compare(.lt)) { // lhs - @divTrunc(lhs, rhs) * rhs @@ -798,9 +798,9 @@ pub fn shl(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b const bits: usize = @intCast(ty.bitSizeof(comp).?); if (shift > bits) { if (lhs_bigint.positive) { - res.* = try intern(comp, .{ .int = .{ .u64 = ty.maxInt(comp) } }); + res.* = try Value.maxInt(ty, comp); } else { - res.* = try intern(comp, .{ .int = .{ .i64 = ty.minInt(comp) } }); + res.* = try Value.minInt(ty, comp); } return true; } @@ -890,6 +890,42 @@ pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *cons return lhs_bigint.order(rhs_bigint).compare(op); } +fn twosCompIntLimit(limit: std.math.big.int.TwosCompIntLimit, ty: Type, comp: *Compilation) !Value { + const signedness = ty.signedness(comp); + if (limit == .min and signedness == .unsigned) return Value.zero; + const mag_bits: usize = @intCast(ty.bitSizeof(comp).?); + switch (mag_bits) { + inline 8, 16, 32, 64 => |bits| { + if (limit == .min) return Value.int(@as(i64, std.math.minInt(std.meta.Int(.signed, bits))), comp); + return switch (signedness) { + inline else => |sign| Value.int(std.math.maxInt(std.meta.Int(sign, bits)), comp), + }; + }, + else => {}, + } + + const sign_bits = @intFromBool(signedness == .signed); + const total_bits = mag_bits + sign_bits; + + const limbs = try comp.gpa.alloc( + std.math.big.Limb, + std.math.big.int.calcTwosCompLimbCount(total_bits), + ); + defer comp.gpa.free(limbs); + + var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined }; + result_bigint.setTwosCompIntLimit(limit, signedness, mag_bits); + return Value.intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } }); +} + +pub fn minInt(ty: Type, comp: *Compilation) !Value { + return twosCompIntLimit(.min, ty, comp); +} + +pub fn maxInt(ty: Type, comp: *Compilation) !Value { + return twosCompIntLimit(.max, ty, comp); +} + pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w).Error!void { if (ty.is(.bool)) { return w.writeAll(if (v.isZero(comp)) "false" else "true"); diff --git a/src/aro/text_literal.zig b/src/aro/text_literal.zig index 1c5d5929..d9f6b2a8 100644 --- a/src/aro/text_literal.zig +++ b/src/aro/text_literal.zig @@ -71,7 +71,7 @@ pub const Kind = enum { pub fn maxCodepoint(kind: Kind, comp: *const Compilation) u21 { return @intCast(switch (kind) { .char => std.math.maxInt(u7), - .wide => @min(0x10FFFF, comp.types.wchar.maxInt(comp)), + .wide => @min(0x10FFFF, comp.wcharMax()), .utf_8 => std.math.maxInt(u7), .utf_16 => std.math.maxInt(u16), .utf_32 => 0x10FFFF, @@ -83,7 +83,7 @@ pub const Kind = enum { pub fn maxInt(kind: Kind, comp: *const Compilation) u32 { return @intCast(switch (kind) { .char, .utf_8 => std.math.maxInt(u8), - .wide => comp.types.wchar.maxInt(comp), + .wide => comp.wcharMax(), .utf_16 => std.math.maxInt(u16), .utf_32 => std.math.maxInt(u32), .unterminated => unreachable, diff --git a/test/cases/_BitInt min max.c b/test/cases/_BitInt min max.c new file mode 100644 index 00000000..1f36a9d3 --- /dev/null +++ b/test/cases/_BitInt min max.c @@ -0,0 +1,7 @@ +//aro-args -std=c23 +_Static_assert(-10 % -444444444444444442051616WB != 0, ""); + +enum E: _BitInt(512) { + A = -6703903964971298549787012499102923063739682910296196688861780721860882015036773488400937149083451713845015929093243025426876941405973284973216824503042048WB, + B = 6703903964971298549787012499102923063739682910296196688861780721860882015036773488400937149083451713845015929093243025426876941405973284973216824503042047WB, +};