Skip to content

Commit

Permalink
Value: use interned values for min and max int for types
Browse files Browse the repository at this point in the history
This allows _BitInt's to use min/max int
  • Loading branch information
ehaas authored Apr 29, 2024
1 parent 026fd88 commit f230f45
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 47 deletions.
23 changes: 19 additions & 4 deletions src/aro/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
18 changes: 9 additions & 9 deletions src/aro/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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 };
}
Expand Down
25 changes: 0 additions & 25 deletions src/aro/Type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
50 changes: 43 additions & 7 deletions src/aro/Value.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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");
Expand Down
4 changes: 2 additions & 2 deletions src/aro/text_literal.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions test/cases/_BitInt min max.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//aro-args -std=c23
_Static_assert(-10 % -444444444444444442051616WB != 0, "");

enum E: _BitInt(512) {
A = -6703903964971298549787012499102923063739682910296196688861780721860882015036773488400937149083451713845015929093243025426876941405973284973216824503042048WB,
B = 6703903964971298549787012499102923063739682910296196688861780721860882015036773488400937149083451713845015929093243025426876941405973284973216824503042047WB,
};

0 comments on commit f230f45

Please sign in to comment.