| author | |
| committer | |
| log | 51a40f9a666085b53353f65f017bcf9bf18daaa3 |
| tree | 64b9886e75ebaf65e59d66b218c411b15c677561 |
| parent | cf90cb7218d1baa56586477bab50e88fdb6be0bb |
4 files changed, 22 insertions(+), 31 deletions(-)
src/Air.zig+1-1| ... | ... | @@ -130,7 +130,7 @@ pub const Inst = struct { |
| 130 | 130 | /// it shifts out any bits that disagree with the resultant sign bit. |
| 131 | 131 | /// Uses the `bin_op` field. |
| 132 | 132 | shl_exact, |
| 133 | /// Shift left saturating. `<<|` | |
| 133 | /// Saturating integer shift left. `<<|` | |
| 134 | 134 | /// Uses the `bin_op` field. |
| 135 | 135 | shl_sat, |
| 136 | 136 | /// Bitwise XOR. `^` |
src/Sema.zig+7-7| ... | ... | @@ -6380,7 +6380,7 @@ fn analyzeArithmetic( |
| 6380 | 6380 | } else break :rs .{ .src = rhs_src, .air_tag = .addwrap }; |
| 6381 | 6381 | }, |
| 6382 | 6382 | .add_sat => { |
| 6383 | // For both integers and floats: | |
| 6383 | // Integers only; floats are checked above. | |
| 6384 | 6384 | // If either of the operands are zero, then the other operand is returned. |
| 6385 | 6385 | // If either of the operands are undefined, the result is undefined. |
| 6386 | 6386 | if (maybe_lhs_val) |lhs_val| { |
| ... | ... | @@ -6398,7 +6398,7 @@ fn analyzeArithmetic( |
| 6398 | 6398 | if (maybe_lhs_val) |lhs_val| { |
| 6399 | 6399 | return sema.addConstant( |
| 6400 | 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 | 6403 | } else break :rs .{ .src = lhs_src, .air_tag = .add_sat }; |
| 6404 | 6404 | } else break :rs .{ .src = rhs_src, .air_tag = .add_sat }; |
| ... | ... | @@ -6471,7 +6471,7 @@ fn analyzeArithmetic( |
| 6471 | 6471 | } else break :rs .{ .src = lhs_src, .air_tag = .subwrap }; |
| 6472 | 6472 | }, |
| 6473 | 6473 | .sub_sat => { |
| 6474 | // For both integers and floats: | |
| 6474 | // Integers only; floats are checked above. | |
| 6475 | 6475 | // If the RHS is zero, result is LHS. |
| 6476 | 6476 | // If either of the operands are undefined, result is undefined. |
| 6477 | 6477 | if (maybe_rhs_val) |rhs_val| { |
| ... | ... | @@ -6489,7 +6489,7 @@ fn analyzeArithmetic( |
| 6489 | 6489 | if (maybe_rhs_val) |rhs_val| { |
| 6490 | 6490 | return sema.addConstant( |
| 6491 | 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 | 6494 | } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat }; |
| 6495 | 6495 | } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat }; |
| ... | ... | @@ -6647,7 +6647,7 @@ fn analyzeArithmetic( |
| 6647 | 6647 | } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap }; |
| 6648 | 6648 | }, |
| 6649 | 6649 | .mul_sat => { |
| 6650 | // For both integers and floats: | |
| 6650 | // Integers only; floats are checked above. | |
| 6651 | 6651 | // If either of the operands are zero, result is zero. |
| 6652 | 6652 | // If either of the operands are one, result is the other operand. |
| 6653 | 6653 | // If either of the operands are undefined, result is undefined. |
| ... | ... | @@ -6677,7 +6677,7 @@ fn analyzeArithmetic( |
| 6677 | 6677 | } |
| 6678 | 6678 | return sema.addConstant( |
| 6679 | 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 | 6682 | } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat }; |
| 6683 | 6683 | } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat }; |
| ... | ... | @@ -7931,7 +7931,7 @@ fn analyzeRet( |
| 7931 | 7931 | fn floatOpAllowed(tag: Zir.Inst.Tag) bool { |
| 7932 | 7932 | // extend this swich as additional operators are implemented |
| 7933 | 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 | 7935 | else => false, |
| 7936 | 7936 | }; |
| 7937 | 7937 | } |
src/value.zig+12-21| ... | ... | @@ -1588,20 +1588,17 @@ pub const Value = extern union { |
| 1588 | 1588 | return result; |
| 1589 | 1589 | } |
| 1590 | 1590 | |
| 1591 | /// Supports both floats and ints; handles undefined. | |
| 1592 | pub fn numberAddSat( | |
| 1591 | /// Supports integers only; asserts neither operand is undefined. | |
| 1592 | pub fn intAddSat( | |
| 1593 | 1593 | lhs: Value, |
| 1594 | 1594 | rhs: Value, |
| 1595 | 1595 | ty: Type, |
| 1596 | 1596 | arena: *Allocator, |
| 1597 | 1597 | target: Target, |
| 1598 | 1598 | ) !Value { |
| 1599 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1599 | assert(!lhs.isUndef()); | |
| 1600 | assert(!rhs.isUndef()); | |
| 1600 | 1601 | |
| 1601 | if (ty.isAnyFloat()) { | |
| 1602 | // TODO: handle outside float range | |
| 1603 | return floatAdd(lhs, rhs, ty, arena); | |
| 1604 | } | |
| 1605 | 1602 | const result = try intAdd(lhs, rhs, arena); |
| 1606 | 1603 | |
| 1607 | 1604 | const max = try ty.maxInt(arena, target); |
| ... | ... | @@ -1645,20 +1642,17 @@ pub const Value = extern union { |
| 1645 | 1642 | return result; |
| 1646 | 1643 | } |
| 1647 | 1644 | |
| 1648 | /// Supports both floats and ints; handles undefined. | |
| 1649 | pub fn numberSubSat( | |
| 1645 | /// Supports integers only; asserts neither operand is undefined. | |
| 1646 | pub fn intSubSat( | |
| 1650 | 1647 | lhs: Value, |
| 1651 | 1648 | rhs: Value, |
| 1652 | 1649 | ty: Type, |
| 1653 | 1650 | arena: *Allocator, |
| 1654 | 1651 | target: Target, |
| 1655 | 1652 | ) !Value { |
| 1656 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1653 | assert(!lhs.isUndef()); | |
| 1654 | assert(!rhs.isUndef()); | |
| 1657 | 1655 | |
| 1658 | if (ty.isAnyFloat()) { | |
| 1659 | // TODO: handle outside float range | |
| 1660 | return floatSub(lhs, rhs, ty, arena); | |
| 1661 | } | |
| 1662 | 1656 | const result = try intSub(lhs, rhs, arena); |
| 1663 | 1657 | |
| 1664 | 1658 | const max = try ty.maxInt(arena, target); |
| ... | ... | @@ -1702,20 +1696,17 @@ pub const Value = extern union { |
| 1702 | 1696 | return result; |
| 1703 | 1697 | } |
| 1704 | 1698 | |
| 1705 | /// Supports both floats and ints; handles undefined. | |
| 1706 | pub fn numberMulSat( | |
| 1699 | /// Supports integers only; asserts neither operand is undefined. | |
| 1700 | pub fn intMulSat( | |
| 1707 | 1701 | lhs: Value, |
| 1708 | 1702 | rhs: Value, |
| 1709 | 1703 | ty: Type, |
| 1710 | 1704 | arena: *Allocator, |
| 1711 | 1705 | target: Target, |
| 1712 | 1706 | ) !Value { |
| 1713 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1707 | assert(!lhs.isUndef()); | |
| 1708 | assert(!rhs.isUndef()); | |
| 1714 | 1709 | |
| 1715 | if (ty.isAnyFloat()) { | |
| 1716 | // TODO: handle outside float range | |
| 1717 | return floatMul(lhs, rhs, ty, arena); | |
| 1718 | } | |
| 1719 | 1710 | const result = try intMul(lhs, rhs, arena); |
| 1720 | 1711 | |
| 1721 | 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 | 8859 | "tmp.zig:3:12: note: crosses namespace boundary here", |
| 8860 | 8860 | }); |
| 8861 | 8861 | |
| 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 | 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 | 8866 | , &[_][]const u8{ |
| 8867 | 8867 | "error: invalid operands to binary expression: 'f32' and 'f32'", |