authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-28 20:27:28-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-28 20:27:28-07:00
log51a40f9a666085b53353f65f017bcf9bf18daaa3
tree64b9886e75ebaf65e59d66b218c411b15c677561
parentcf90cb7218d1baa56586477bab50e88fdb6be0bb

saturating arithmetic supports integers only


4 files changed, 22 insertions(+), 31 deletions(-)

src/Air.zig+1-1
...@@ -130,7 +130,7 @@ pub const Inst = struct {...@@ -130,7 +130,7 @@ pub const Inst = struct {
130 /// it shifts out any bits that disagree with the resultant sign bit.130 /// it shifts out any bits that disagree with the resultant sign bit.
131 /// Uses the `bin_op` field.131 /// Uses the `bin_op` field.
132 shl_exact,132 shl_exact,
133 /// Shift left saturating. `<<|`133 /// Saturating integer shift left. `<<|`
134 /// Uses the `bin_op` field.134 /// Uses the `bin_op` field.
135 shl_sat,135 shl_sat,
136 /// Bitwise XOR. `^`136 /// Bitwise XOR. `^`
src/Sema.zig+7-7
...@@ -6380,7 +6380,7 @@ fn analyzeArithmetic(...@@ -6380,7 +6380,7 @@ fn analyzeArithmetic(
6380 } else break :rs .{ .src = rhs_src, .air_tag = .addwrap };6380 } else break :rs .{ .src = rhs_src, .air_tag = .addwrap };
6381 },6381 },
6382 .add_sat => {6382 .add_sat => {
6383 // For both integers and floats:6383 // Integers only; floats are checked above.
6384 // If either of the operands are zero, then the other operand is returned.6384 // If either of the operands are zero, then the other operand is returned.
6385 // If either of the operands are undefined, the result is undefined.6385 // If either of the operands are undefined, the result is undefined.
6386 if (maybe_lhs_val) |lhs_val| {6386 if (maybe_lhs_val) |lhs_val| {
...@@ -6398,7 +6398,7 @@ fn analyzeArithmetic(...@@ -6398,7 +6398,7 @@ fn analyzeArithmetic(
6398 if (maybe_lhs_val) |lhs_val| {6398 if (maybe_lhs_val) |lhs_val| {
6399 return sema.addConstant(6399 return sema.addConstant(
6400 scalar_type,6400 scalar_type,
6401 try lhs_val.numberAddSat(rhs_val, scalar_type, sema.arena, target),6401 try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target),
6402 );6402 );
6403 } else break :rs .{ .src = lhs_src, .air_tag = .add_sat };6403 } else break :rs .{ .src = lhs_src, .air_tag = .add_sat };
6404 } else break :rs .{ .src = rhs_src, .air_tag = .add_sat };6404 } else break :rs .{ .src = rhs_src, .air_tag = .add_sat };
...@@ -6471,7 +6471,7 @@ fn analyzeArithmetic(...@@ -6471,7 +6471,7 @@ fn analyzeArithmetic(
6471 } else break :rs .{ .src = lhs_src, .air_tag = .subwrap };6471 } else break :rs .{ .src = lhs_src, .air_tag = .subwrap };
6472 },6472 },
6473 .sub_sat => {6473 .sub_sat => {
6474 // For both integers and floats:6474 // Integers only; floats are checked above.
6475 // If the RHS is zero, result is LHS.6475 // If the RHS is zero, result is LHS.
6476 // If either of the operands are undefined, result is undefined.6476 // If either of the operands are undefined, result is undefined.
6477 if (maybe_rhs_val) |rhs_val| {6477 if (maybe_rhs_val) |rhs_val| {
...@@ -6489,7 +6489,7 @@ fn analyzeArithmetic(...@@ -6489,7 +6489,7 @@ fn analyzeArithmetic(
6489 if (maybe_rhs_val) |rhs_val| {6489 if (maybe_rhs_val) |rhs_val| {
6490 return sema.addConstant(6490 return sema.addConstant(
6491 scalar_type,6491 scalar_type,
6492 try lhs_val.numberSubSat(rhs_val, scalar_type, sema.arena, target),6492 try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target),
6493 );6493 );
6494 } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };6494 } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };
6495 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };6495 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
...@@ -6647,7 +6647,7 @@ fn analyzeArithmetic(...@@ -6647,7 +6647,7 @@ fn analyzeArithmetic(
6647 } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap };6647 } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap };
6648 },6648 },
6649 .mul_sat => {6649 .mul_sat => {
6650 // For both integers and floats:6650 // Integers only; floats are checked above.
6651 // If either of the operands are zero, result is zero.6651 // If either of the operands are zero, result is zero.
6652 // If either of the operands are one, result is the other operand.6652 // If either of the operands are one, result is the other operand.
6653 // If either of the operands are undefined, result is undefined.6653 // If either of the operands are undefined, result is undefined.
...@@ -6677,7 +6677,7 @@ fn analyzeArithmetic(...@@ -6677,7 +6677,7 @@ fn analyzeArithmetic(
6677 }6677 }
6678 return sema.addConstant(6678 return sema.addConstant(
6679 scalar_type,6679 scalar_type,
6680 try lhs_val.numberMulSat(rhs_val, scalar_type, sema.arena, target),6680 try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target),
6681 );6681 );
6682 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };6682 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };
6683 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat };6683 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat };
...@@ -7931,7 +7931,7 @@ fn analyzeRet(...@@ -7931,7 +7931,7 @@ fn analyzeRet(
7931fn floatOpAllowed(tag: Zir.Inst.Tag) bool {7931fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
7932 // extend this swich as additional operators are implemented7932 // extend this swich as additional operators are implemented
7933 return switch (tag) {7933 return switch (tag) {
7934 .add, .add_sat, .sub, .sub_sat, .mul, .mul_sat, .div, .mod, .rem, .mod_rem => true,7934 .add, .sub, .mul, .div, .mod, .rem, .mod_rem => true,
7935 else => false,7935 else => false,
7936 };7936 };
7937}7937}
src/value.zig+12-21
...@@ -1588,20 +1588,17 @@ pub const Value = extern union {...@@ -1588,20 +1588,17 @@ pub const Value = extern union {
1588 return result;1588 return result;
1589 }1589 }
15901590
1591 /// Supports both floats and ints; handles undefined.1591 /// Supports integers only; asserts neither operand is undefined.
1592 pub fn numberAddSat(1592 pub fn intAddSat(
1593 lhs: Value,1593 lhs: Value,
1594 rhs: Value,1594 rhs: Value,
1595 ty: Type,1595 ty: Type,
1596 arena: *Allocator,1596 arena: *Allocator,
1597 target: Target,1597 target: Target,
1598 ) !Value {1598 ) !Value {
1599 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);1599 assert(!lhs.isUndef());
1600 assert(!rhs.isUndef());
16001601
1601 if (ty.isAnyFloat()) {
1602 // TODO: handle outside float range
1603 return floatAdd(lhs, rhs, ty, arena);
1604 }
1605 const result = try intAdd(lhs, rhs, arena);1602 const result = try intAdd(lhs, rhs, arena);
16061603
1607 const max = try ty.maxInt(arena, target);1604 const max = try ty.maxInt(arena, target);
...@@ -1645,20 +1642,17 @@ pub const Value = extern union {...@@ -1645,20 +1642,17 @@ pub const Value = extern union {
1645 return result;1642 return result;
1646 }1643 }
16471644
1648 /// Supports both floats and ints; handles undefined.1645 /// Supports integers only; asserts neither operand is undefined.
1649 pub fn numberSubSat(1646 pub fn intSubSat(
1650 lhs: Value,1647 lhs: Value,
1651 rhs: Value,1648 rhs: Value,
1652 ty: Type,1649 ty: Type,
1653 arena: *Allocator,1650 arena: *Allocator,
1654 target: Target,1651 target: Target,
1655 ) !Value {1652 ) !Value {
1656 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);1653 assert(!lhs.isUndef());
1654 assert(!rhs.isUndef());
16571655
1658 if (ty.isAnyFloat()) {
1659 // TODO: handle outside float range
1660 return floatSub(lhs, rhs, ty, arena);
1661 }
1662 const result = try intSub(lhs, rhs, arena);1656 const result = try intSub(lhs, rhs, arena);
16631657
1664 const max = try ty.maxInt(arena, target);1658 const max = try ty.maxInt(arena, target);
...@@ -1702,20 +1696,17 @@ pub const Value = extern union {...@@ -1702,20 +1696,17 @@ pub const Value = extern union {
1702 return result;1696 return result;
1703 }1697 }
17041698
1705 /// Supports both floats and ints; handles undefined.1699 /// Supports integers only; asserts neither operand is undefined.
1706 pub fn numberMulSat(1700 pub fn intMulSat(
1707 lhs: Value,1701 lhs: Value,
1708 rhs: Value,1702 rhs: Value,
1709 ty: Type,1703 ty: Type,
1710 arena: *Allocator,1704 arena: *Allocator,
1711 target: Target,1705 target: Target,
1712 ) !Value {1706 ) !Value {
1713 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);1707 assert(!lhs.isUndef());
1708 assert(!rhs.isUndef());
17141709
1715 if (ty.isAnyFloat()) {
1716 // TODO: handle outside float range
1717 return floatMul(lhs, rhs, ty, arena);
1718 }
1719 const result = try intMul(lhs, rhs, arena);1710 const result = try intMul(lhs, rhs, arena);
17201711
1721 const max = try ty.maxInt(arena, target);1712 const max = try ty.maxInt(arena, target);
test/compile_errors.zig+2-2
...@@ -8859,9 +8859,9 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -8859,9 +8859,9 @@ pub fn addCases(ctx: *TestContext) !void {
8859 "tmp.zig:3:12: note: crosses namespace boundary here",8859 "tmp.zig:3:12: note: crosses namespace boundary here",
8860 });8860 });
88618861
8862 ctx.objErrStage1("Issue #9619: saturating arithmetic builtins should fail to compile when given floats",8862 ctx.objErrStage1("saturating arithmetic does not allow floats",
8863 \\pub fn main() !void {8863 \\pub fn main() !void {
8864 \\ _ = @addWithSaturation(@as(f32, 1.0), @as(f32, 1.0));8864 \\ _ = @as(f32, 1.0) +| @as(f32, 1.0);
8865 \\}8865 \\}
8866 , &[_][]const u8{8866 , &[_][]const u8{
8867 "error: invalid operands to binary expression: 'f32' and 'f32'",8867 "error: invalid operands to binary expression: 'f32' and 'f32'",