Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Value: use interned values for min and max int for types #694

Merged
merged 2 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (and wcharMax) is to avoid Compilation depending on Value but can be changed if you want

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it better this way.

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,
};
Loading