saturating arithmetic supports integers only

This commit is contained in:
Andrew Kelley 2021-09-28 20:27:28 -07:00
parent cf90cb7218
commit 51a40f9a66
4 changed files with 22 additions and 31 deletions

View file

@ -130,7 +130,7 @@ pub const Inst = struct {
/// it shifts out any bits that disagree with the resultant sign bit. /// it shifts out any bits that disagree with the resultant sign bit.
/// Uses the `bin_op` field. /// Uses the `bin_op` field.
shl_exact, shl_exact,
/// Shift left saturating. `<<|` /// Saturating integer shift left. `<<|`
/// Uses the `bin_op` field. /// Uses the `bin_op` field.
shl_sat, shl_sat,
/// Bitwise XOR. `^` /// Bitwise XOR. `^`

View file

@ -6380,7 +6380,7 @@ fn analyzeArithmetic(
} else break :rs .{ .src = rhs_src, .air_tag = .addwrap }; } else break :rs .{ .src = rhs_src, .air_tag = .addwrap };
}, },
.add_sat => { .add_sat => {
// For both integers and floats: // Integers only; floats are checked above.
// If either of the operands are zero, then the other operand is returned. // If either of the operands are zero, then the other operand is returned.
// If either of the operands are undefined, the result is undefined. // If either of the operands are undefined, the result is undefined.
if (maybe_lhs_val) |lhs_val| { if (maybe_lhs_val) |lhs_val| {
@ -6398,7 +6398,7 @@ fn analyzeArithmetic(
if (maybe_lhs_val) |lhs_val| { if (maybe_lhs_val) |lhs_val| {
return sema.addConstant( return sema.addConstant(
scalar_type, scalar_type,
try lhs_val.numberAddSat(rhs_val, scalar_type, sema.arena, target), try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target),
); );
} else break :rs .{ .src = lhs_src, .air_tag = .add_sat }; } else break :rs .{ .src = lhs_src, .air_tag = .add_sat };
} else break :rs .{ .src = rhs_src, .air_tag = .add_sat }; } else break :rs .{ .src = rhs_src, .air_tag = .add_sat };
@ -6471,7 +6471,7 @@ fn analyzeArithmetic(
} else break :rs .{ .src = lhs_src, .air_tag = .subwrap }; } else break :rs .{ .src = lhs_src, .air_tag = .subwrap };
}, },
.sub_sat => { .sub_sat => {
// For both integers and floats: // Integers only; floats are checked above.
// If the RHS is zero, result is LHS. // If the RHS is zero, result is LHS.
// If either of the operands are undefined, result is undefined. // If either of the operands are undefined, result is undefined.
if (maybe_rhs_val) |rhs_val| { if (maybe_rhs_val) |rhs_val| {
@ -6489,7 +6489,7 @@ fn analyzeArithmetic(
if (maybe_rhs_val) |rhs_val| { if (maybe_rhs_val) |rhs_val| {
return sema.addConstant( return sema.addConstant(
scalar_type, scalar_type,
try lhs_val.numberSubSat(rhs_val, scalar_type, sema.arena, target), try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target),
); );
} else break :rs .{ .src = rhs_src, .air_tag = .sub_sat }; } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };
} else break :rs .{ .src = lhs_src, .air_tag = .sub_sat }; } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
@ -6647,7 +6647,7 @@ fn analyzeArithmetic(
} else break :rs .{ .src = rhs_src, .air_tag = .mulwrap }; } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap };
}, },
.mul_sat => { .mul_sat => {
// For both integers and floats: // Integers only; floats are checked above.
// If either of the operands are zero, result is zero. // If either of the operands are zero, result is zero.
// If either of the operands are one, result is the other operand. // If either of the operands are one, result is the other operand.
// If either of the operands are undefined, result is undefined. // If either of the operands are undefined, result is undefined.
@ -6677,7 +6677,7 @@ fn analyzeArithmetic(
} }
return sema.addConstant( return sema.addConstant(
scalar_type, scalar_type,
try lhs_val.numberMulSat(rhs_val, scalar_type, sema.arena, target), try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target),
); );
} else break :rs .{ .src = lhs_src, .air_tag = .mul_sat }; } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };
} else break :rs .{ .src = rhs_src, .air_tag = .mul_sat }; } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat };
@ -7931,7 +7931,7 @@ fn analyzeRet(
fn floatOpAllowed(tag: Zir.Inst.Tag) bool { fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
// extend this swich as additional operators are implemented // extend this swich as additional operators are implemented
return switch (tag) { return switch (tag) {
.add, .add_sat, .sub, .sub_sat, .mul, .mul_sat, .div, .mod, .rem, .mod_rem => true, .add, .sub, .mul, .div, .mod, .rem, .mod_rem => true,
else => false, else => false,
}; };
} }

View file

@ -1588,20 +1588,17 @@ pub const Value = extern union {
return result; return result;
} }
/// Supports both floats and ints; handles undefined. /// Supports integers only; asserts neither operand is undefined.
pub fn numberAddSat( pub fn intAddSat(
lhs: Value, lhs: Value,
rhs: Value, rhs: Value,
ty: Type, ty: Type,
arena: *Allocator, arena: *Allocator,
target: Target, target: Target,
) !Value { ) !Value {
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); assert(!lhs.isUndef());
assert(!rhs.isUndef());
if (ty.isAnyFloat()) {
// TODO: handle outside float range
return floatAdd(lhs, rhs, ty, arena);
}
const result = try intAdd(lhs, rhs, arena); const result = try intAdd(lhs, rhs, arena);
const max = try ty.maxInt(arena, target); const max = try ty.maxInt(arena, target);
@ -1645,20 +1642,17 @@ pub const Value = extern union {
return result; return result;
} }
/// Supports both floats and ints; handles undefined. /// Supports integers only; asserts neither operand is undefined.
pub fn numberSubSat( pub fn intSubSat(
lhs: Value, lhs: Value,
rhs: Value, rhs: Value,
ty: Type, ty: Type,
arena: *Allocator, arena: *Allocator,
target: Target, target: Target,
) !Value { ) !Value {
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); assert(!lhs.isUndef());
assert(!rhs.isUndef());
if (ty.isAnyFloat()) {
// TODO: handle outside float range
return floatSub(lhs, rhs, ty, arena);
}
const result = try intSub(lhs, rhs, arena); const result = try intSub(lhs, rhs, arena);
const max = try ty.maxInt(arena, target); const max = try ty.maxInt(arena, target);
@ -1702,20 +1696,17 @@ pub const Value = extern union {
return result; return result;
} }
/// Supports both floats and ints; handles undefined. /// Supports integers only; asserts neither operand is undefined.
pub fn numberMulSat( pub fn intMulSat(
lhs: Value, lhs: Value,
rhs: Value, rhs: Value,
ty: Type, ty: Type,
arena: *Allocator, arena: *Allocator,
target: Target, target: Target,
) !Value { ) !Value {
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); assert(!lhs.isUndef());
assert(!rhs.isUndef());
if (ty.isAnyFloat()) {
// TODO: handle outside float range
return floatMul(lhs, rhs, ty, arena);
}
const result = try intMul(lhs, rhs, arena); const result = try intMul(lhs, rhs, arena);
const max = try ty.maxInt(arena, target); const max = try ty.maxInt(arena, target);

View file

@ -8859,9 +8859,9 @@ pub fn addCases(ctx: *TestContext) !void {
"tmp.zig:3:12: note: crosses namespace boundary here", "tmp.zig:3:12: note: crosses namespace boundary here",
}); });
ctx.objErrStage1("Issue #9619: saturating arithmetic builtins should fail to compile when given floats", ctx.objErrStage1("saturating arithmetic does not allow floats",
\\pub fn main() !void { \\pub fn main() !void {
\\ _ = @addWithSaturation(@as(f32, 1.0), @as(f32, 1.0)); \\ _ = @as(f32, 1.0) +| @as(f32, 1.0);
\\} \\}
, &[_][]const u8{ , &[_][]const u8{
"error: invalid operands to binary expression: 'f32' and 'f32'", "error: invalid operands to binary expression: 'f32' and 'f32'",