| 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,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( |
| 7931 | fn floatOpAllowed(tag: Zir.Inst.Tag) bool { | 7931 | fn floatOpAllowed(tag: Zir.Inst.Tag) bool { |
| 7932 | // extend this swich as additional operators are implemented | 7932 | // 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 | } |
| 1590 | 1590 | ||
| 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()); | ||
| 1600 | 1601 | ||
| 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); |
| 1606 | 1603 | ||
| 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 | } |
| 1647 | 1644 | ||
| 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()); | ||
| 1657 | 1655 | ||
| 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); |
| 1663 | 1657 | ||
| 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 | } |
| 1704 | 1698 | ||
| 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()); | ||
| 1714 | 1709 | ||
| 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); |
| 1720 | 1711 | ||
| 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 | }); |
| 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 | \\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'", |