61 over-annotations dropped where the rhs unambiguously infers the declared type: 22 overflow:bool comparison binds in checked.ww, and the mem/s/sl memio.stream/io.stream/log.stdlogger triplet across 13 @test sites in logtest.ww. Sub-word res:/fullres: binds with casts or truncation are kept. Byte-identical asm in both stages, both files non-embedded.
160 lines
4.4 KiB
Plaintext
160 lines
4.4 KiB
Plaintext
// math/checked — overflow-checked integer arithmetic. Ported from
|
|
// Hare's math::checked (ref/hare/math/checked/checked.ha). add*/sub*/
|
|
// mul* return (result, overflow) with wrapping semantics. The saturating
|
|
// (clamp-on-overflow) siblings live in saturating.ww — same `checked`
|
|
// module.
|
|
//
|
|
// Subset of Hare's surface (Hare splits per type, so these are clean
|
|
// omissions, not divergences):
|
|
// - size-typed *z variants (addz/subz/mulz): no `size` type yet (#85).
|
|
// - int/uint native-width variants: ww int/uint are 64-bit (type.c),
|
|
// so they'd silently behave like the i64/u64 forms — a width
|
|
// divergence in the overflow boundary.
|
|
// - 64-bit muls (muli64/mulu64/powi64) and the muli/mulu dispatchers:
|
|
// need math::mulu64 (128-bit product), not yet exposed by lib/math.
|
|
|
|
package checked;
|
|
|
|
export fn addi8(a: i8, b: i8) (i8, bool) = {
|
|
let res: i8 = a + b;
|
|
let overflow = a < 0 == b < 0 && a < 0 != res < 0;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn addi16(a: i16, b: i16) (i16, bool) = {
|
|
let res: i16 = a + b;
|
|
let overflow = a < 0 == b < 0 && a < 0 != res < 0;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn addi32(a: i32, b: i32) (i32, bool) = {
|
|
let res: i32 = a + b;
|
|
let overflow = a < 0 == b < 0 && a < 0 != res < 0;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn addi64(a: i64, b: i64) (i64, bool) = {
|
|
let res: i64 = a + b;
|
|
let overflow = a < 0 == b < 0 && a < 0 != res < 0;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn addu8(a: u8, b: u8) (u8, bool) = {
|
|
let res: u8 = a + b;
|
|
let overflow = res < a;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn addu16(a: u16, b: u16) (u16, bool) = {
|
|
let res: u16 = a + b;
|
|
let overflow = res < a;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn addu32(a: u32, b: u32) (u32, bool) = {
|
|
let res: u32 = a + b;
|
|
let overflow = res < a;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn addu64(a: u64, b: u64) (u64, bool) = {
|
|
let res: u64 = a + b;
|
|
let overflow = res < a;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn subi8(a: i8, b: i8) (i8, bool) = {
|
|
let res: i8 = a - b;
|
|
let overflow = a < 0 != b < 0 && a < 0 != res < 0;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn subi16(a: i16, b: i16) (i16, bool) = {
|
|
let res: i16 = a - b;
|
|
let overflow = a < 0 != b < 0 && a < 0 != res < 0;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn subi32(a: i32, b: i32) (i32, bool) = {
|
|
let res: i32 = a - b;
|
|
let overflow = a < 0 != b < 0 && a < 0 != res < 0;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn subi64(a: i64, b: i64) (i64, bool) = {
|
|
let res: i64 = a - b;
|
|
let overflow = a < 0 != b < 0 && a < 0 != res < 0;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn subu8(a: u8, b: u8) (u8, bool) = {
|
|
let res: u8 = a - b;
|
|
let overflow = res > a;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn subu16(a: u16, b: u16) (u16, bool) = {
|
|
let res: u16 = a - b;
|
|
let overflow = res > a;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn subu32(a: u32, b: u32) (u32, bool) = {
|
|
let res: u32 = a - b;
|
|
let overflow = res > a;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn subu64(a: u64, b: u64) (u64, bool) = {
|
|
let res: u64 = a - b;
|
|
let overflow = res > a;
|
|
return (res, overflow);
|
|
};
|
|
|
|
// muli8: Hare compares the truncated `res` against the full-width
|
|
// product directly (checked.ha:321 `res != fullres`), relying on its
|
|
// implicit i8->int promotion. ww has no implicit integer promotion
|
|
// (cmd/wcc/check.c unify_arith errors on differing concrete types), so
|
|
// the widening cast is written explicitly. Same shape in the muls below.
|
|
export fn muli8(a: i8, b: i8) (i8, bool) = {
|
|
let fullres: int = a: int * b: int;
|
|
let res: i8 = fullres: i8;
|
|
let overflow = res: int != fullres;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn muli16(a: i16, b: i16) (i16, bool) = {
|
|
let fullres: int = a: int * b: int;
|
|
let res: i16 = fullres: i16;
|
|
let overflow = res: int != fullres;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn muli32(a: i32, b: i32) (i32, bool) = {
|
|
let fullres: i64 = a: i64 * b: i64;
|
|
let res: i32 = fullres: i32;
|
|
let overflow = res: i64 != fullres;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn mulu8(a: u8, b: u8) (u8, bool) = {
|
|
let fullres: uint = a: uint * b: uint;
|
|
let res: u8 = fullres: u8;
|
|
let overflow = res: uint != fullres;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn mulu16(a: u16, b: u16) (u16, bool) = {
|
|
let fullres: uint = a: uint * b: uint;
|
|
let res: u16 = fullres: u16;
|
|
let overflow = res: uint != fullres;
|
|
return (res, overflow);
|
|
};
|
|
|
|
export fn mulu32(a: u32, b: u32) (u32, bool) = {
|
|
let fullres: u64 = a: u64 * b: u64;
|
|
let res: u32 = fullres: u32;
|
|
let overflow = res: u64 != fullres;
|
|
return (res, overflow);
|
|
};
|