| author | |
| committer | |
| log | 5888446c03b1f77a031f5a8093488a6a2f6decb6 |
| tree | cb084cae72edc4c581c0ffa37cdc1130c6c2a8fc |
| parent | 7a4758ed7868096c12fec8dacba0bfd4a37fdd13 |
| parent | f33b3fc3eae54b9d1159fc5a7a69a4b0e4aceca6 |
| signature |
stage2: vectorized overflow arithmetic, integer overflow safety, left-shift overflow safety14 files changed, 1467 insertions(+), 260 deletions(-)
lib/std/builtin.zig+1-2| ... | @@ -767,8 +767,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn | ... | @@ -767,8 +767,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn |
| 767 | 767 | ||
| 768 | // Until self-hosted catches up with stage1 language features, we have a simpler | 768 | // Until self-hosted catches up with stage1 language features, we have a simpler |
| 769 | // default panic function: | 769 | // default panic function: |
| 770 | if ((builtin.zig_backend == .stage2_llvm and builtin.link_libc) or | 770 | if (builtin.zig_backend == .stage2_c or |
| 771 | builtin.zig_backend == .stage2_c or | ||
| 772 | builtin.zig_backend == .stage2_wasm or | 771 | builtin.zig_backend == .stage2_wasm or |
| 773 | builtin.zig_backend == .stage2_arm or | 772 | builtin.zig_backend == .stage2_arm or |
| 774 | builtin.zig_backend == .stage2_aarch64 or | 773 | builtin.zig_backend == .stage2_aarch64 or |
src/Sema.zig+172-59| ... | @@ -1574,6 +1574,12 @@ fn failWithErrorSetCodeMissing( | ... | @@ -1574,6 +1574,12 @@ fn failWithErrorSetCodeMissing( |
| 1574 | }); | 1574 | }); |
| 1575 | } | 1575 | } |
| 1576 | 1576 | ||
| 1577 | fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value) CompileError { | ||
| 1578 | return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{ | ||
| 1579 | int_ty.fmt(sema.mod), val.fmtValue(Type.@"comptime_int", sema.mod), | ||
| 1580 | }); | ||
| 1581 | } | ||
| 1582 | |||
| 1577 | /// We don't return a pointer to the new error note because the pointer | 1583 | /// We don't return a pointer to the new error note because the pointer |
| 1578 | /// becomes invalid when you add another one. | 1584 | /// becomes invalid when you add another one. |
| 1579 | fn errNote( | 1585 | fn errNote( |
| ... | @@ -8820,8 +8826,6 @@ fn zirShl( | ... | @@ -8820,8 +8826,6 @@ fn zirShl( |
| 8820 | return sema.addConstant(lhs_ty, val); | 8826 | return sema.addConstant(lhs_ty, val); |
| 8821 | } else lhs_src; | 8827 | } else lhs_src; |
| 8822 | 8828 | ||
| 8823 | // TODO: insert runtime safety check for shl_exact | ||
| 8824 | |||
| 8825 | const new_rhs = if (air_tag == .shl_sat) rhs: { | 8829 | const new_rhs = if (air_tag == .shl_sat) rhs: { |
| 8826 | // Limit the RHS type for saturating shl to be an integer as small as the LHS. | 8830 | // Limit the RHS type for saturating shl to be an integer as small as the LHS. |
| 8827 | if (rhs_is_comptime_int or | 8831 | if (rhs_is_comptime_int or |
| ... | @@ -8839,6 +8843,41 @@ fn zirShl( | ... | @@ -8839,6 +8843,41 @@ fn zirShl( |
| 8839 | } else rhs; | 8843 | } else rhs; |
| 8840 | 8844 | ||
| 8841 | try sema.requireRuntimeBlock(block, runtime_src); | 8845 | try sema.requireRuntimeBlock(block, runtime_src); |
| 8846 | if (block.wantSafety()) { | ||
| 8847 | const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) { | ||
| 8848 | .shl_exact => .shl_with_overflow, | ||
| 8849 | else => null, | ||
| 8850 | }; | ||
| 8851 | if (maybe_op_ov) |op_ov_tag| { | ||
| 8852 | const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty); | ||
| 8853 | const op_ov = try block.addInst(.{ | ||
| 8854 | .tag = op_ov_tag, | ||
| 8855 | .data = .{ .ty_pl = .{ | ||
| 8856 | .ty = try sema.addType(op_ov_tuple_ty), | ||
| 8857 | .payload = try sema.addExtra(Air.Bin{ | ||
| 8858 | .lhs = lhs, | ||
| 8859 | .rhs = rhs, | ||
| 8860 | }), | ||
| 8861 | } }, | ||
| 8862 | }); | ||
| 8863 | const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); | ||
| 8864 | const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector) | ||
| 8865 | try block.addInst(.{ | ||
| 8866 | .tag = .reduce, | ||
| 8867 | .data = .{ .reduce = .{ | ||
| 8868 | .operand = ov_bit, | ||
| 8869 | .operation = .Or, | ||
| 8870 | } }, | ||
| 8871 | }) | ||
| 8872 | else | ||
| 8873 | ov_bit; | ||
| 8874 | const zero_ov = try sema.addConstant(Type.@"u1", Value.zero); | ||
| 8875 | const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov); | ||
| 8876 | |||
| 8877 | try sema.addSafetyCheck(block, no_ov, .shl_overflow); | ||
| 8878 | return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty); | ||
| 8879 | } | ||
| 8880 | } | ||
| 8842 | return block.addBinOp(air_tag, lhs, new_rhs); | 8881 | return block.addBinOp(air_tag, lhs, new_rhs); |
| 8843 | } | 8882 | } |
| 8844 | 8883 | ||
| ... | @@ -9417,32 +9456,29 @@ fn zirOverflowArithmetic( | ... | @@ -9417,32 +9456,29 @@ fn zirOverflowArithmetic( |
| 9417 | const ptr = sema.resolveInst(extra.ptr); | 9456 | const ptr = sema.resolveInst(extra.ptr); |
| 9418 | 9457 | ||
| 9419 | const lhs_ty = sema.typeOf(lhs); | 9458 | const lhs_ty = sema.typeOf(lhs); |
| 9459 | const rhs_ty = sema.typeOf(rhs); | ||
| 9420 | const mod = sema.mod; | 9460 | const mod = sema.mod; |
| 9421 | const target = mod.getTarget(); | 9461 | const target = mod.getTarget(); |
| 9422 | 9462 | ||
| 9423 | // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen. | 9463 | // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen. |
| 9464 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); | ||
| 9424 | const dest_ty = lhs_ty; | 9465 | const dest_ty = lhs_ty; |
| 9425 | if (dest_ty.zigTypeTag() != .Int) { | 9466 | if (dest_ty.scalarType().zigTypeTag() != .Int) { |
| 9426 | return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty.fmt(mod)}); | 9467 | return sema.fail(block, src, "expected vector of integers or integer type, found '{}'", .{dest_ty.fmt(mod)}); |
| 9427 | } | 9468 | } |
| 9428 | 9469 | ||
| 9429 | const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); | 9470 | const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); |
| 9430 | const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); | 9471 | const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); |
| 9431 | 9472 | ||
| 9432 | const types = try sema.arena.alloc(Type, 2); | 9473 | const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty); |
| 9433 | const values = try sema.arena.alloc(Value, 2); | 9474 | const ov_ty = tuple_ty.tupleFields().types[1]; |
| 9434 | const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ | 9475 | // TODO: Remove and use `ov_ty` instead. |
| 9435 | .types = types, | 9476 | // This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`. |
| 9436 | .values = values, | 9477 | const overflowed_ty = if (dest_ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, dest_ty.vectorLen(), Type.@"bool") else Type.@"bool"; |
| 9437 | }); | ||
| 9438 | |||
| 9439 | types[0] = dest_ty; | ||
| 9440 | types[1] = Type.initTag(.u1); | ||
| 9441 | values[0] = Value.initTag(.unreachable_value); | ||
| 9442 | values[1] = Value.initTag(.unreachable_value); | ||
| 9443 | 9478 | ||
| 9444 | const result: struct { | 9479 | const result: struct { |
| 9445 | overflowed: enum { yes, no, undef }, | 9480 | /// TODO: Rename to `overflow_bit` and make of type `u1`. |
| 9481 | overflowed: Air.Inst.Ref, | ||
| 9446 | wrapped: Air.Inst.Ref, | 9482 | wrapped: Air.Inst.Ref, |
| 9447 | } = result: { | 9483 | } = result: { |
| 9448 | switch (zir_tag) { | 9484 | switch (zir_tag) { |
| ... | @@ -9452,23 +9488,24 @@ fn zirOverflowArithmetic( | ... | @@ -9452,23 +9488,24 @@ fn zirOverflowArithmetic( |
| 9452 | // Otherwise, if either of the argument is undefined, undefined is returned. | 9488 | // Otherwise, if either of the argument is undefined, undefined is returned. |
| 9453 | if (maybe_lhs_val) |lhs_val| { | 9489 | if (maybe_lhs_val) |lhs_val| { |
| 9454 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { | 9490 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { |
| 9455 | break :result .{ .overflowed = .no, .wrapped = rhs }; | 9491 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; |
| 9456 | } | 9492 | } |
| 9457 | } | 9493 | } |
| 9458 | if (maybe_rhs_val) |rhs_val| { | 9494 | if (maybe_rhs_val) |rhs_val| { |
| 9459 | if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { | 9495 | if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { |
| 9460 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9496 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9461 | } | 9497 | } |
| 9462 | } | 9498 | } |
| 9463 | if (maybe_lhs_val) |lhs_val| { | 9499 | if (maybe_lhs_val) |lhs_val| { |
| 9464 | if (maybe_rhs_val) |rhs_val| { | 9500 | if (maybe_rhs_val) |rhs_val| { |
| 9465 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 9501 | if (lhs_val.isUndef() or rhs_val.isUndef()) { |
| 9466 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | 9502 | break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; |
| 9467 | } | 9503 | } |
| 9468 | 9504 | ||
| 9469 | const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target); | 9505 | const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target); |
| 9470 | const inst = try sema.addConstant(dest_ty, result.wrapped_result); | 9506 | const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); |
| 9471 | break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; | 9507 | const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); |
| 9508 | break :result .{ .overflowed = overflowed, .wrapped = wrapped }; | ||
| 9472 | } | 9509 | } |
| 9473 | } | 9510 | } |
| 9474 | }, | 9511 | }, |
| ... | @@ -9477,17 +9514,18 @@ fn zirOverflowArithmetic( | ... | @@ -9477,17 +9514,18 @@ fn zirOverflowArithmetic( |
| 9477 | // Otherwise, if either result is undefined, both results are undefined. | 9514 | // Otherwise, if either result is undefined, both results are undefined. |
| 9478 | if (maybe_rhs_val) |rhs_val| { | 9515 | if (maybe_rhs_val) |rhs_val| { |
| 9479 | if (rhs_val.isUndef()) { | 9516 | if (rhs_val.isUndef()) { |
| 9480 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | 9517 | break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; |
| 9481 | } else if (rhs_val.compareWithZero(.eq)) { | 9518 | } else if (rhs_val.compareWithZero(.eq)) { |
| 9482 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9519 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9483 | } else if (maybe_lhs_val) |lhs_val| { | 9520 | } else if (maybe_lhs_val) |lhs_val| { |
| 9484 | if (lhs_val.isUndef()) { | 9521 | if (lhs_val.isUndef()) { |
| 9485 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | 9522 | break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; |
| 9486 | } | 9523 | } |
| 9487 | 9524 | ||
| 9488 | const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target); | 9525 | const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target); |
| 9489 | const inst = try sema.addConstant(dest_ty, result.wrapped_result); | 9526 | const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); |
| 9490 | break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; | 9527 | const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); |
| 9528 | break :result .{ .overflowed = overflowed, .wrapped = wrapped }; | ||
| 9491 | } | 9529 | } |
| 9492 | } | 9530 | } |
| 9493 | }, | 9531 | }, |
| ... | @@ -9498,9 +9536,9 @@ fn zirOverflowArithmetic( | ... | @@ -9498,9 +9536,9 @@ fn zirOverflowArithmetic( |
| 9498 | if (maybe_lhs_val) |lhs_val| { | 9536 | if (maybe_lhs_val) |lhs_val| { |
| 9499 | if (!lhs_val.isUndef()) { | 9537 | if (!lhs_val.isUndef()) { |
| 9500 | if (lhs_val.compareWithZero(.eq)) { | 9538 | if (lhs_val.compareWithZero(.eq)) { |
| 9501 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9539 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9502 | } else if (lhs_val.compare(.eq, Value.one, dest_ty, mod)) { | 9540 | } else if (lhs_val.compare(.eq, Value.one, dest_ty, mod)) { |
| 9503 | break :result .{ .overflowed = .no, .wrapped = rhs }; | 9541 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; |
| 9504 | } | 9542 | } |
| 9505 | } | 9543 | } |
| 9506 | } | 9544 | } |
| ... | @@ -9508,9 +9546,9 @@ fn zirOverflowArithmetic( | ... | @@ -9508,9 +9546,9 @@ fn zirOverflowArithmetic( |
| 9508 | if (maybe_rhs_val) |rhs_val| { | 9546 | if (maybe_rhs_val) |rhs_val| { |
| 9509 | if (!rhs_val.isUndef()) { | 9547 | if (!rhs_val.isUndef()) { |
| 9510 | if (rhs_val.compareWithZero(.eq)) { | 9548 | if (rhs_val.compareWithZero(.eq)) { |
| 9511 | break :result .{ .overflowed = .no, .wrapped = rhs }; | 9549 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; |
| 9512 | } else if (rhs_val.compare(.eq, Value.one, dest_ty, mod)) { | 9550 | } else if (rhs_val.compare(.eq, Value.one, dest_ty, mod)) { |
| 9513 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9551 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9514 | } | 9552 | } |
| 9515 | } | 9553 | } |
| 9516 | } | 9554 | } |
| ... | @@ -9518,12 +9556,13 @@ fn zirOverflowArithmetic( | ... | @@ -9518,12 +9556,13 @@ fn zirOverflowArithmetic( |
| 9518 | if (maybe_lhs_val) |lhs_val| { | 9556 | if (maybe_lhs_val) |lhs_val| { |
| 9519 | if (maybe_rhs_val) |rhs_val| { | 9557 | if (maybe_rhs_val) |rhs_val| { |
| 9520 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 9558 | if (lhs_val.isUndef() or rhs_val.isUndef()) { |
| 9521 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | 9559 | break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; |
| 9522 | } | 9560 | } |
| 9523 | 9561 | ||
| 9524 | const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target); | 9562 | const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target); |
| 9525 | const inst = try sema.addConstant(dest_ty, result.wrapped_result); | 9563 | const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); |
| 9526 | break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; | 9564 | const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); |
| 9565 | break :result .{ .overflowed = overflowed, .wrapped = wrapped }; | ||
| 9527 | } | 9566 | } |
| 9528 | } | 9567 | } |
| 9529 | }, | 9568 | }, |
| ... | @@ -9533,23 +9572,24 @@ fn zirOverflowArithmetic( | ... | @@ -9533,23 +9572,24 @@ fn zirOverflowArithmetic( |
| 9533 | // Oterhwise if either of the arguments is undefined, both results are undefined. | 9572 | // Oterhwise if either of the arguments is undefined, both results are undefined. |
| 9534 | if (maybe_lhs_val) |lhs_val| { | 9573 | if (maybe_lhs_val) |lhs_val| { |
| 9535 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { | 9574 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { |
| 9536 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9575 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9537 | } | 9576 | } |
| 9538 | } | 9577 | } |
| 9539 | if (maybe_rhs_val) |rhs_val| { | 9578 | if (maybe_rhs_val) |rhs_val| { |
| 9540 | if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { | 9579 | if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { |
| 9541 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9580 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9542 | } | 9581 | } |
| 9543 | } | 9582 | } |
| 9544 | if (maybe_lhs_val) |lhs_val| { | 9583 | if (maybe_lhs_val) |lhs_val| { |
| 9545 | if (maybe_rhs_val) |rhs_val| { | 9584 | if (maybe_rhs_val) |rhs_val| { |
| 9546 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 9585 | if (lhs_val.isUndef() or rhs_val.isUndef()) { |
| 9547 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | 9586 | break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; |
| 9548 | } | 9587 | } |
| 9549 | 9588 | ||
| 9550 | const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target); | 9589 | const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target); |
| 9551 | const inst = try sema.addConstant(dest_ty, result.wrapped_result); | 9590 | const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); |
| 9552 | break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; | 9591 | const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); |
| 9592 | break :result .{ .overflowed = overflowed, .wrapped = wrapped }; | ||
| 9553 | } | 9593 | } |
| 9554 | } | 9594 | } |
| 9555 | }, | 9595 | }, |
| ... | @@ -9577,21 +9617,40 @@ fn zirOverflowArithmetic( | ... | @@ -9577,21 +9617,40 @@ fn zirOverflowArithmetic( |
| 9577 | } }, | 9617 | } }, |
| 9578 | }); | 9618 | }); |
| 9579 | 9619 | ||
| 9580 | const wrapped = try block.addStructFieldVal(tuple, 0, dest_ty); | 9620 | const wrapped = try sema.tupleFieldValByIndex(block, src, tuple, 0, tuple_ty); |
| 9581 | try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store); | 9621 | try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store); |
| 9582 | 9622 | ||
| 9583 | const overflow_bit = try block.addStructFieldVal(tuple, 1, Type.initTag(.u1)); | 9623 | const overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty); |
| 9584 | const zero_u1 = try sema.addConstant(Type.initTag(.u1), Value.zero); | 9624 | const zero_ov_val = if (dest_ty.zigTypeTag() == .Vector) try Value.Tag.repeated.create(sema.arena, Value.zero) else Value.zero; |
| 9585 | return try block.addBinOp(.cmp_neq, overflow_bit, zero_u1); | 9625 | const zero_ov = try sema.addConstant(ov_ty, zero_ov_val); |
| 9626 | |||
| 9627 | const overflowed_inst = if (dest_ty.zigTypeTag() == .Vector) | ||
| 9628 | block.addCmpVector(overflow_bit, .zero, .neq, try sema.addType(ov_ty)) | ||
| 9629 | else | ||
| 9630 | block.addBinOp(.cmp_neq, overflow_bit, zero_ov); | ||
| 9631 | return overflowed_inst; | ||
| 9586 | }; | 9632 | }; |
| 9587 | 9633 | ||
| 9588 | try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store); | 9634 | try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store); |
| 9635 | return result.overflowed; | ||
| 9636 | } | ||
| 9589 | 9637 | ||
| 9590 | return switch (result.overflowed) { | 9638 | fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type { |
| 9591 | .yes => Air.Inst.Ref.bool_true, | 9639 | const ov_ty = if (ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, ty.vectorLen(), Type.@"u1") else Type.@"u1"; |
| 9592 | .no => Air.Inst.Ref.bool_false, | 9640 | |
| 9593 | .undef => try sema.addConstUndef(Type.bool), | 9641 | const types = try sema.arena.alloc(Type, 2); |
| 9594 | }; | 9642 | const values = try sema.arena.alloc(Value, 2); |
| 9643 | const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ | ||
| 9644 | .types = types, | ||
| 9645 | .values = values, | ||
| 9646 | }); | ||
| 9647 | |||
| 9648 | types[0] = ty; | ||
| 9649 | types[1] = ov_ty; | ||
| 9650 | values[0] = Value.initTag(.unreachable_value); | ||
| 9651 | values[1] = Value.initTag(.unreachable_value); | ||
| 9652 | |||
| 9653 | return tuple_ty; | ||
| 9595 | } | 9654 | } |
| 9596 | 9655 | ||
| 9597 | fn analyzeArithmetic( | 9656 | fn analyzeArithmetic( |
| ... | @@ -9691,10 +9750,11 @@ fn analyzeArithmetic( | ... | @@ -9691,10 +9750,11 @@ fn analyzeArithmetic( |
| 9691 | } | 9750 | } |
| 9692 | if (maybe_rhs_val) |rhs_val| { | 9751 | if (maybe_rhs_val) |rhs_val| { |
| 9693 | if (is_int) { | 9752 | if (is_int) { |
| 9694 | return sema.addConstant( | 9753 | const sum = try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target); |
| 9695 | resolved_type, | 9754 | if (!sum.intFitsInType(resolved_type, target)) { |
| 9696 | try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target), | 9755 | return sema.failWithIntegerOverflow(block, src, resolved_type, sum); |
| 9697 | ); | 9756 | } |
| 9757 | return sema.addConstant(resolved_type, sum); | ||
| 9698 | } else { | 9758 | } else { |
| 9699 | return sema.addConstant( | 9759 | return sema.addConstant( |
| 9700 | resolved_type, | 9760 | resolved_type, |
| ... | @@ -9784,10 +9844,11 @@ fn analyzeArithmetic( | ... | @@ -9784,10 +9844,11 @@ fn analyzeArithmetic( |
| 9784 | } | 9844 | } |
| 9785 | if (maybe_rhs_val) |rhs_val| { | 9845 | if (maybe_rhs_val) |rhs_val| { |
| 9786 | if (is_int) { | 9846 | if (is_int) { |
| 9787 | return sema.addConstant( | 9847 | const diff = try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target); |
| 9788 | resolved_type, | 9848 | if (!diff.intFitsInType(resolved_type, target)) { |
| 9789 | try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target), | 9849 | return sema.failWithIntegerOverflow(block, src, resolved_type, diff); |
| 9790 | ); | 9850 | } |
| 9851 | return sema.addConstant(resolved_type, diff); | ||
| 9791 | } else { | 9852 | } else { |
| 9792 | return sema.addConstant( | 9853 | return sema.addConstant( |
| 9793 | resolved_type, | 9854 | resolved_type, |
| ... | @@ -10157,10 +10218,11 @@ fn analyzeArithmetic( | ... | @@ -10157,10 +10218,11 @@ fn analyzeArithmetic( |
| 10157 | } | 10218 | } |
| 10158 | } | 10219 | } |
| 10159 | if (is_int) { | 10220 | if (is_int) { |
| 10160 | return sema.addConstant( | 10221 | const product = try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target); |
| 10161 | resolved_type, | 10222 | if (!product.intFitsInType(resolved_type, target)) { |
| 10162 | try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target), | 10223 | return sema.failWithIntegerOverflow(block, src, resolved_type, product); |
| 10163 | ); | 10224 | } |
| 10225 | return sema.addConstant(resolved_type, product); | ||
| 10164 | } else { | 10226 | } else { |
| 10165 | return sema.addConstant( | 10227 | return sema.addConstant( |
| 10166 | resolved_type, | 10228 | resolved_type, |
| ... | @@ -10448,6 +10510,45 @@ fn analyzeArithmetic( | ... | @@ -10448,6 +10510,45 @@ fn analyzeArithmetic( |
| 10448 | }; | 10510 | }; |
| 10449 | 10511 | ||
| 10450 | try sema.requireRuntimeBlock(block, rs.src); | 10512 | try sema.requireRuntimeBlock(block, rs.src); |
| 10513 | if (block.wantSafety()) { | ||
| 10514 | if (scalar_tag == .Int) { | ||
| 10515 | const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) { | ||
| 10516 | .add => .add_with_overflow, | ||
| 10517 | .sub => .sub_with_overflow, | ||
| 10518 | .mul => .mul_with_overflow, | ||
| 10519 | else => null, | ||
| 10520 | }; | ||
| 10521 | if (maybe_op_ov) |op_ov_tag| { | ||
| 10522 | const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(resolved_type); | ||
| 10523 | const op_ov = try block.addInst(.{ | ||
| 10524 | .tag = op_ov_tag, | ||
| 10525 | .data = .{ .ty_pl = .{ | ||
| 10526 | .ty = try sema.addType(op_ov_tuple_ty), | ||
| 10527 | .payload = try sema.addExtra(Air.Bin{ | ||
| 10528 | .lhs = casted_lhs, | ||
| 10529 | .rhs = casted_rhs, | ||
| 10530 | }), | ||
| 10531 | } }, | ||
| 10532 | }); | ||
| 10533 | const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); | ||
| 10534 | const any_ov_bit = if (resolved_type.zigTypeTag() == .Vector) | ||
| 10535 | try block.addInst(.{ | ||
| 10536 | .tag = .reduce, | ||
| 10537 | .data = .{ .reduce = .{ | ||
| 10538 | .operand = ov_bit, | ||
| 10539 | .operation = .Or, | ||
| 10540 | } }, | ||
| 10541 | }) | ||
| 10542 | else | ||
| 10543 | ov_bit; | ||
| 10544 | const zero_ov = try sema.addConstant(Type.@"u1", Value.zero); | ||
| 10545 | const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov); | ||
| 10546 | |||
| 10547 | try sema.addSafetyCheck(block, no_ov, .integer_overflow); | ||
| 10548 | return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty); | ||
| 10549 | } | ||
| 10550 | } | ||
| 10551 | } | ||
| 10451 | return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); | 10552 | return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); |
| 10452 | } | 10553 | } |
| 10453 | 10554 | ||
| ... | @@ -16682,6 +16783,8 @@ pub const PanicId = enum { | ... | @@ -16682,6 +16783,8 @@ pub const PanicId = enum { |
| 16682 | invalid_error_code, | 16783 | invalid_error_code, |
| 16683 | index_out_of_bounds, | 16784 | index_out_of_bounds, |
| 16684 | cast_truncated_data, | 16785 | cast_truncated_data, |
| 16786 | integer_overflow, | ||
| 16787 | shl_overflow, | ||
| 16685 | }; | 16788 | }; |
| 16686 | 16789 | ||
| 16687 | fn addSafetyCheck( | 16790 | fn addSafetyCheck( |
| ... | @@ -16805,6 +16908,8 @@ fn safetyPanic( | ... | @@ -16805,6 +16908,8 @@ fn safetyPanic( |
| 16805 | .invalid_error_code => "invalid error code", | 16908 | .invalid_error_code => "invalid error code", |
| 16806 | .index_out_of_bounds => "attempt to index out of bounds", | 16909 | .index_out_of_bounds => "attempt to index out of bounds", |
| 16807 | .cast_truncated_data => "integer cast truncated bits", | 16910 | .cast_truncated_data => "integer cast truncated bits", |
| 16911 | .integer_overflow => "integer overflow", | ||
| 16912 | .shl_overflow => "left shift overflowed bits", | ||
| 16808 | }; | 16913 | }; |
| 16809 | 16914 | ||
| 16810 | const msg_inst = msg_inst: { | 16915 | const msg_inst = msg_inst: { |
| ... | @@ -23093,6 +23198,14 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref { | ... | @@ -23093,6 +23198,14 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref { |
| 23093 | return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int)); | 23198 | return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int)); |
| 23094 | } | 23199 | } |
| 23095 | 23200 | ||
| 23201 | fn addBool(sema: *Sema, ty: Type, boolean: bool) CompileError!Air.Inst.Ref { | ||
| 23202 | return switch (ty.zigTypeTag()) { | ||
| 23203 | .Vector => sema.addConstant(ty, try Value.Tag.repeated.create(sema.arena, Value.makeBool(boolean))), | ||
| 23204 | .Bool => sema.resolveInst(if (boolean) .bool_true else .bool_false), | ||
| 23205 | else => unreachable, | ||
| 23206 | }; | ||
| 23207 | } | ||
| 23208 | |||
| 23096 | fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref { | 23209 | fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref { |
| 23097 | return sema.addConstant(ty, Value.undef); | 23210 | return sema.addConstant(ty, Value.undef); |
| 23098 | } | 23211 | } |
src/arch/aarch64/CodeGen.zig+4| ... | @@ -1901,6 +1901,10 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1901,6 +1901,10 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1901 | } | 1901 | } |
| 1902 | }; | 1902 | }; |
| 1903 | 1903 | ||
| 1904 | if (tag == .sub_with_overflow) { | ||
| 1905 | break :result MCValue{ .register_v_flag = dest.register }; | ||
| 1906 | } | ||
| 1907 | |||
| 1904 | switch (int_info.signedness) { | 1908 | switch (int_info.signedness) { |
| 1905 | .unsigned => break :result MCValue{ .register_c_flag = dest.register }, | 1909 | .unsigned => break :result MCValue{ .register_c_flag = dest.register }, |
| 1906 | .signed => break :result MCValue{ .register_v_flag = dest.register }, | 1910 | .signed => break :result MCValue{ .register_v_flag = dest.register }, |
src/arch/arm/CodeGen.zig+4| ... | @@ -1455,6 +1455,10 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1455,6 +1455,10 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1455 | } | 1455 | } |
| 1456 | }; | 1456 | }; |
| 1457 | 1457 | ||
| 1458 | if (tag == .sub_with_overflow) { | ||
| 1459 | break :result MCValue{ .register_v_flag = dest.register }; | ||
| 1460 | } | ||
| 1461 | |||
| 1458 | switch (int_info.signedness) { | 1462 | switch (int_info.signedness) { |
| 1459 | .unsigned => break :result MCValue{ .register_c_flag = dest.register }, | 1463 | .unsigned => break :result MCValue{ .register_c_flag = dest.register }, |
| 1460 | .signed => break :result MCValue{ .register_v_flag = dest.register }, | 1464 | .signed => break :result MCValue{ .register_v_flag = dest.register }, |
src/arch/wasm/CodeGen.zig+84-77| ... | @@ -1450,9 +1450,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1450,9 +1450,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1450 | .min => self.airMaxMin(inst, .min), | 1450 | .min => self.airMaxMin(inst, .min), |
| 1451 | .mul_add => self.airMulAdd(inst), | 1451 | .mul_add => self.airMulAdd(inst), |
| 1452 | 1452 | ||
| 1453 | .add_with_overflow => self.airBinOpOverflow(inst, .add), | 1453 | .add_with_overflow => self.airAddSubWithOverflow(inst, .add), |
| 1454 | .sub_with_overflow => self.airBinOpOverflow(inst, .sub), | 1454 | .sub_with_overflow => self.airAddSubWithOverflow(inst, .sub), |
| 1455 | .shl_with_overflow => self.airBinOpOverflow(inst, .shl), | 1455 | .shl_with_overflow => self.airShlWithOverflow(inst), |
| 1456 | .mul_with_overflow => self.airMulWithOverflow(inst), | 1456 | .mul_with_overflow => self.airMulWithOverflow(inst), |
| 1457 | 1457 | ||
| 1458 | .clz => self.airClz(inst), | 1458 | .clz => self.airClz(inst), |
| ... | @@ -3941,25 +3941,22 @@ fn airPtrSliceFieldPtr(self: *Self, inst: Air.Inst.Index, offset: u32) InnerErro | ... | @@ -3941,25 +3941,22 @@ fn airPtrSliceFieldPtr(self: *Self, inst: Air.Inst.Index, offset: u32) InnerErro |
| 3941 | return self.buildPointerOffset(slice_ptr, offset, .new); | 3941 | return self.buildPointerOffset(slice_ptr, offset, .new); |
| 3942 | } | 3942 | } |
| 3943 | 3943 | ||
| 3944 | fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | 3944 | fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 3945 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; | 3945 | assert(op == .add or op == .sub); |
| 3946 | |||
| 3947 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 3946 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3948 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 3947 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3949 | const lhs = try self.resolveInst(extra.lhs); | 3948 | const lhs_op = try self.resolveInst(extra.lhs); |
| 3950 | const rhs = try self.resolveInst(extra.rhs); | 3949 | const rhs_op = try self.resolveInst(extra.rhs); |
| 3951 | const lhs_ty = self.air.typeOf(extra.lhs); | 3950 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 3952 | 3951 | ||
| 3953 | if (lhs_ty.zigTypeTag() == .Vector) { | 3952 | if (lhs_ty.zigTypeTag() == .Vector) { |
| 3954 | return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); | 3953 | return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| 3955 | } | 3954 | } |
| 3956 | 3955 | ||
| 3957 | // We store the bit if it's overflowed or not in this. As it's zero-initialized | ||
| 3958 | // we only need to update it if an overflow (or underflow) occured. | ||
| 3959 | const overflow_bit = try self.allocLocal(Type.initTag(.u1)); | ||
| 3960 | const int_info = lhs_ty.intInfo(self.target); | 3956 | const int_info = lhs_ty.intInfo(self.target); |
| 3957 | const is_signed = int_info.signedness == .signed; | ||
| 3961 | const wasm_bits = toWasmBits(int_info.bits) orelse { | 3958 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 3962 | return self.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits}); | 3959 | return self.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits}); |
| 3963 | }; | 3960 | }; |
| 3964 | 3961 | ||
| 3965 | const zero = switch (wasm_bits) { | 3962 | const zero = switch (wasm_bits) { |
| ... | @@ -3967,83 +3964,93 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue | ... | @@ -3967,83 +3964,93 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 3967 | 64 => WValue{ .imm64 = 0 }, | 3964 | 64 => WValue{ .imm64 = 0 }, |
| 3968 | else => unreachable, | 3965 | else => unreachable, |
| 3969 | }; | 3966 | }; |
| 3970 | const int_max = (@as(u65, 1) << @intCast(u7, int_info.bits - @boolToInt(int_info.signedness == .signed))) - 1; | 3967 | const shift_amt = wasm_bits - int_info.bits; |
| 3971 | const int_max_wvalue = switch (wasm_bits) { | 3968 | const shift_val = switch (wasm_bits) { |
| 3972 | 32 => WValue{ .imm32 = @intCast(u32, int_max) }, | 3969 | 32 => WValue{ .imm32 = shift_amt }, |
| 3973 | 64 => WValue{ .imm64 = @intCast(u64, int_max) }, | 3970 | 64 => WValue{ .imm64 = shift_amt }, |
| 3974 | else => unreachable, | ||
| 3975 | }; | ||
| 3976 | const int_min = if (int_info.signedness == .unsigned) | ||
| 3977 | @as(i64, 0) | ||
| 3978 | else | ||
| 3979 | -@as(i64, 1) << @intCast(u6, int_info.bits - 1); | ||
| 3980 | const int_min_wvalue = switch (wasm_bits) { | ||
| 3981 | 32 => WValue{ .imm32 = @bitCast(u32, @intCast(i32, int_min)) }, | ||
| 3982 | 64 => WValue{ .imm64 = @bitCast(u64, int_min) }, | ||
| 3983 | else => unreachable, | 3971 | else => unreachable, |
| 3984 | }; | 3972 | }; |
| 3985 | 3973 | ||
| 3986 | if (int_info.signedness == .unsigned and op == .add) { | 3974 | // for signed integers, we first apply signed shifts by the difference in bits |
| 3987 | const diff = try self.binOp(int_max_wvalue, lhs, lhs_ty, .sub); | 3975 | // to get the signed value, as we store it internally as 2's complement. |
| 3988 | const cmp_res = try self.cmp(rhs, diff, lhs_ty, .gt); | 3976 | const lhs = if (wasm_bits != int_info.bits and is_signed) blk: { |
| 3989 | try self.emitWValue(cmp_res); | 3977 | const shl = try self.binOp(lhs_op, shift_val, lhs_ty, .shl); |
| 3990 | try self.addLabel(.local_set, overflow_bit.local); | 3978 | break :blk try self.binOp(shl, shift_val, lhs_ty, .shr); |
| 3991 | } else if (int_info.signedness == .unsigned and op == .sub) { | 3979 | } else lhs_op; |
| 3992 | const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt); | 3980 | const rhs = if (wasm_bits != int_info.bits and is_signed) blk: { |
| 3993 | try self.emitWValue(cmp_res); | 3981 | const shl = try self.binOp(rhs_op, shift_val, lhs_ty, .shl); |
| 3994 | try self.addLabel(.local_set, overflow_bit.local); | 3982 | break :blk try self.binOp(shl, shift_val, lhs_ty, .shr); |
| 3995 | } else if (int_info.signedness == .signed and op != .shl) { | 3983 | } else rhs_op; |
| 3996 | // for overflow, we first check if lhs is > 0 (or lhs < 0 in case of subtraction). If not, we will not overflow. | 3984 | |
| 3997 | // We first create an outer block, where we handle overflow. | 3985 | const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); |
| 3998 | // Then we create an inner block, where underflow is handled. | 3986 | const result = if (wasm_bits != int_info.bits) blk: { |
| 3999 | try self.startBlock(.block, wasm.block_empty); | 3987 | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4000 | try self.startBlock(.block, wasm.block_empty); | 3988 | } else bin_op; |
| 4001 | { | 3989 | |
| 4002 | try self.emitWValue(lhs); | 3990 | const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt; |
| 4003 | const cmp_result = try self.cmp(lhs, zero, lhs_ty, .lt); | 3991 | const overflow_bit: WValue = if (is_signed) blk: { |
| 4004 | try self.emitWValue(cmp_result); | 3992 | if (wasm_bits == int_info.bits) { |
| 3993 | const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op); | ||
| 3994 | const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt); | ||
| 3995 | break :blk try self.binOp(cmp_zero, lt, Type.u32, .xor); // result of cmp_zero and lt is always 32bit | ||
| 4005 | } | 3996 | } |
| 4006 | try self.addLabel(.br_if, 0); // break to outer block, and handle underflow | 3997 | const shl = try self.binOp(bin_op, shift_val, lhs_ty, .shl); |
| 3998 | const shr = try self.binOp(shl, shift_val, lhs_ty, .shr); | ||
| 3999 | break :blk try self.cmp(shr, bin_op, lhs_ty, .neq); | ||
| 4000 | } else if (wasm_bits == int_info.bits) | ||
| 4001 | try self.cmp(bin_op, lhs, lhs_ty, cmp_op) | ||
| 4002 | else | ||
| 4003 | try self.cmp(bin_op, result, lhs_ty, .neq); | ||
| 4007 | 4004 | ||
| 4008 | // handle overflow | 4005 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |
| 4009 | { | 4006 | try self.store(result_ptr, result, lhs_ty, 0); |
| 4010 | const diff = try self.binOp(int_max_wvalue, lhs, lhs_ty, .sub); | 4007 | const offset = @intCast(u32, lhs_ty.abiSize(self.target)); |
| 4011 | const cmp_res = try self.cmp(rhs, diff, lhs_ty, if (op == .add) .gt else .lt); | 4008 | try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); |
| 4012 | try self.emitWValue(cmp_res); | ||
| 4013 | try self.addLabel(.local_set, overflow_bit.local); | ||
| 4014 | } | ||
| 4015 | try self.addLabel(.br, 1); // break from blocks, and continue regular flow. | ||
| 4016 | try self.endBlock(); | ||
| 4017 | 4009 | ||
| 4018 | // handle underflow | 4010 | return result_ptr; |
| 4019 | { | 4011 | } |
| 4020 | const diff = try self.binOp(int_min_wvalue, lhs, lhs_ty, .sub); | 4012 | |
| 4021 | const cmp_res = try self.cmp(rhs, diff, lhs_ty, if (op == .add) .lt else .gt); | 4013 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4022 | try self.emitWValue(cmp_res); | 4014 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 4023 | try self.addLabel(.local_set, overflow_bit.local); | 4015 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 4024 | } | 4016 | const lhs = try self.resolveInst(extra.lhs); |
| 4025 | try self.endBlock(); | 4017 | const rhs = try self.resolveInst(extra.rhs); |
| 4018 | const lhs_ty = self.air.typeOf(extra.lhs); | ||
| 4019 | |||
| 4020 | if (lhs_ty.zigTypeTag() == .Vector) { | ||
| 4021 | return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); | ||
| 4026 | } | 4022 | } |
| 4027 | 4023 | ||
| 4028 | const bin_op = if (op == .shl) blk: { | 4024 | const int_info = lhs_ty.intInfo(self.target); |
| 4029 | const tmp_val = try self.binOp(lhs, rhs, lhs_ty, op); | 4025 | const is_signed = int_info.signedness == .signed; |
| 4030 | const cmp_res = try self.cmp(tmp_val, int_max_wvalue, lhs_ty, .gt); | 4026 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 4031 | try self.emitWValue(cmp_res); | 4027 | return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits}); |
| 4032 | try self.addLabel(.local_set, overflow_bit.local); | 4028 | }; |
| 4033 | 4029 | ||
| 4034 | try self.emitWValue(tmp_val); | 4030 | const shl = try self.binOp(lhs, rhs, lhs_ty, .shl); |
| 4035 | try self.emitWValue(int_max_wvalue); | 4031 | const result = if (wasm_bits != int_info.bits) blk: { |
| 4036 | switch (wasm_bits) { | 4032 | break :blk try self.wrapOperand(shl, lhs_ty); |
| 4037 | 32 => try self.addTag(.i32_and), | 4033 | } else shl; |
| 4038 | 64 => try self.addTag(.i64_and), | 4034 | |
| 4035 | const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: { | ||
| 4036 | const shift_amt = wasm_bits - int_info.bits; | ||
| 4037 | const shift_val = switch (wasm_bits) { | ||
| 4038 | 32 => WValue{ .imm32 = shift_amt }, | ||
| 4039 | 64 => WValue{ .imm64 = shift_amt }, | ||
| 4039 | else => unreachable, | 4040 | else => unreachable, |
| 4040 | } | 4041 | }; |
| 4041 | try self.addLabel(.local_set, tmp_val.local); | 4042 | |
| 4042 | break :blk tmp_val; | 4043 | const secondary_shl = try self.binOp(shl, shift_val, lhs_ty, .shl); |
| 4043 | } else try self.wrapBinOp(lhs, rhs, lhs_ty, op); | 4044 | const initial_shr = try self.binOp(secondary_shl, shift_val, lhs_ty, .shr); |
| 4045 | const shr = try self.wrapBinOp(initial_shr, rhs, lhs_ty, .shr); | ||
| 4046 | break :blk try self.cmp(lhs, shr, lhs_ty, .neq); | ||
| 4047 | } else blk: { | ||
| 4048 | const shr = try self.binOp(result, rhs, lhs_ty, .shr); | ||
| 4049 | break :blk try self.cmp(lhs, shr, lhs_ty, .neq); | ||
| 4050 | }; | ||
| 4044 | 4051 | ||
| 4045 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); | 4052 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |
| 4046 | try self.store(result_ptr, bin_op, lhs_ty, 0); | 4053 | try self.store(result_ptr, result, lhs_ty, 0); |
| 4047 | const offset = @intCast(u32, lhs_ty.abiSize(self.target)); | 4054 | const offset = @intCast(u32, lhs_ty.abiSize(self.target)); |
| 4048 | try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); | 4055 | try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); |
| 4049 | 4056 |
src/arch/x86_64/Emit.zig+1-1| ... | @@ -1896,7 +1896,7 @@ fn lowerToMrEnc( | ... | @@ -1896,7 +1896,7 @@ fn lowerToMrEnc( |
| 1896 | const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8).?; | 1896 | const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8).?; |
| 1897 | switch (reg_or_mem) { | 1897 | switch (reg_or_mem) { |
| 1898 | .register => |dst_reg| { | 1898 | .register => |dst_reg| { |
| 1899 | const encoder = try Encoder.init(code, 3); | 1899 | const encoder = try Encoder.init(code, 4); |
| 1900 | if (dst_reg.size() == 16) { | 1900 | if (dst_reg.size() == 16) { |
| 1901 | encoder.prefix16BitMode(); | 1901 | encoder.prefix16BitMode(); |
| 1902 | } | 1902 | } |
src/codegen/c.zig+118-82| ... | @@ -1766,10 +1766,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -1766,10 +1766,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1766 | 1766 | ||
| 1767 | .mul_add => try airMulAdd(f, inst), | 1767 | .mul_add => try airMulAdd(f, inst), |
| 1768 | 1768 | ||
| 1769 | .add_with_overflow => try airAddWithOverflow(f, inst), | 1769 | .add_with_overflow => try airOverflow(f, inst, "addo_"), |
| 1770 | .sub_with_overflow => try airSubWithOverflow(f, inst), | 1770 | .sub_with_overflow => try airOverflow(f, inst, "subo_"), |
| 1771 | .mul_with_overflow => try airMulWithOverflow(f, inst), | 1771 | .mul_with_overflow => try airOverflow(f, inst, "mulo_"), |
| 1772 | .shl_with_overflow => try airShlWithOverflow(f, inst), | 1772 | .shl_with_overflow => try airOverflow(f, inst, "shlo_"), |
| 1773 | 1773 | ||
| 1774 | .min => try airMinMax(f, inst, "<"), | 1774 | .min => try airMinMax(f, inst, "<"), |
| 1775 | .max => try airMinMax(f, inst, ">"), | 1775 | .max => try airMinMax(f, inst, ">"), |
| ... | @@ -2295,7 +2295,8 @@ fn airWrapOp( | ... | @@ -2295,7 +2295,8 @@ fn airWrapOp( |
| 2295 | 2295 | ||
| 2296 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | 2296 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 2297 | const inst_ty = f.air.typeOfIndex(inst); | 2297 | const inst_ty = f.air.typeOfIndex(inst); |
| 2298 | const int_info = inst_ty.intInfo(f.object.dg.module.getTarget()); | 2298 | const target = f.object.dg.module.getTarget(); |
| 2299 | const int_info = inst_ty.intInfo(target); | ||
| 2299 | const bits = int_info.bits; | 2300 | const bits = int_info.bits; |
| 2300 | 2301 | ||
| 2301 | // if it's an unsigned int with non-arbitrary bit size then we can just add | 2302 | // if it's an unsigned int with non-arbitrary bit size then we can just add |
| ... | @@ -2313,47 +2314,8 @@ fn airWrapOp( | ... | @@ -2313,47 +2314,8 @@ fn airWrapOp( |
| 2313 | return f.fail("TODO: C backend: airWrapOp for large integers", .{}); | 2314 | return f.fail("TODO: C backend: airWrapOp for large integers", .{}); |
| 2314 | } | 2315 | } |
| 2315 | 2316 | ||
| 2316 | var min_buf: [80]u8 = undefined; | ||
| 2317 | const min = switch (int_info.signedness) { | ||
| 2318 | .unsigned => "0", | ||
| 2319 | else => switch (inst_ty.tag()) { | ||
| 2320 | .c_short => "SHRT_MIN", | ||
| 2321 | .c_int => "INT_MIN", | ||
| 2322 | .c_long => "LONG_MIN", | ||
| 2323 | .c_longlong => "LLONG_MIN", | ||
| 2324 | .isize => "INTPTR_MIN", | ||
| 2325 | else => blk: { | ||
| 2326 | const val = -1 * std.math.pow(i64, 2, @intCast(i64, bits - 1)); | ||
| 2327 | break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { | ||
| 2328 | error.NoSpaceLeft => unreachable, | ||
| 2329 | }; | ||
| 2330 | }, | ||
| 2331 | }, | ||
| 2332 | }; | ||
| 2333 | |||
| 2334 | var max_buf: [80]u8 = undefined; | 2317 | var max_buf: [80]u8 = undefined; |
| 2335 | const max = switch (inst_ty.tag()) { | 2318 | const max = intMax(inst_ty, target, &max_buf); |
| 2336 | .c_short => "SHRT_MAX", | ||
| 2337 | .c_ushort => "USHRT_MAX", | ||
| 2338 | .c_int => "INT_MAX", | ||
| 2339 | .c_uint => "UINT_MAX", | ||
| 2340 | .c_long => "LONG_MAX", | ||
| 2341 | .c_ulong => "ULONG_MAX", | ||
| 2342 | .c_longlong => "LLONG_MAX", | ||
| 2343 | .c_ulonglong => "ULLONG_MAX", | ||
| 2344 | .isize => "INTPTR_MAX", | ||
| 2345 | .usize => "UINTPTR_MAX", | ||
| 2346 | else => blk: { | ||
| 2347 | const pow_bits = switch (int_info.signedness) { | ||
| 2348 | .signed => bits - 1, | ||
| 2349 | .unsigned => bits, | ||
| 2350 | }; | ||
| 2351 | const val = std.math.pow(u64, 2, pow_bits) - 1; | ||
| 2352 | break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { | ||
| 2353 | error.NoSpaceLeft => unreachable, | ||
| 2354 | }; | ||
| 2355 | }, | ||
| 2356 | }; | ||
| 2357 | 2319 | ||
| 2358 | const lhs = try f.resolveInst(bin_op.lhs); | 2320 | const lhs = try f.resolveInst(bin_op.lhs); |
| 2359 | const rhs = try f.resolveInst(bin_op.rhs); | 2321 | const rhs = try f.resolveInst(bin_op.rhs); |
| ... | @@ -2369,10 +2331,7 @@ fn airWrapOp( | ... | @@ -2369,10 +2331,7 @@ fn airWrapOp( |
| 2369 | .c_long => try w.writeAll("long"), | 2331 | .c_long => try w.writeAll("long"), |
| 2370 | .c_longlong => try w.writeAll("longlong"), | 2332 | .c_longlong => try w.writeAll("longlong"), |
| 2371 | else => { | 2333 | else => { |
| 2372 | const prefix_byte: u8 = switch (int_info.signedness) { | 2334 | const prefix_byte: u8 = signAbbrev(int_info.signedness); |
| 2373 | .signed => 'i', | ||
| 2374 | .unsigned => 'u', | ||
| 2375 | }; | ||
| 2376 | for ([_]u8{ 8, 16, 32, 64 }) |nbits| { | 2335 | for ([_]u8{ 8, 16, 32, 64 }) |nbits| { |
| 2377 | if (bits <= nbits) { | 2336 | if (bits <= nbits) { |
| 2378 | try w.print("{c}{d}", .{ prefix_byte, nbits }); | 2337 | try w.print("{c}{d}", .{ prefix_byte, nbits }); |
| ... | @@ -2390,6 +2349,9 @@ fn airWrapOp( | ... | @@ -2390,6 +2349,9 @@ fn airWrapOp( |
| 2390 | try f.writeCValue(w, rhs); | 2349 | try f.writeCValue(w, rhs); |
| 2391 | 2350 | ||
| 2392 | if (int_info.signedness == .signed) { | 2351 | if (int_info.signedness == .signed) { |
| 2352 | var min_buf: [80]u8 = undefined; | ||
| 2353 | const min = intMin(inst_ty, target, &min_buf); | ||
| 2354 | |||
| 2393 | try w.print(", {s}", .{min}); | 2355 | try w.print(", {s}", .{min}); |
| 2394 | } | 2356 | } |
| 2395 | 2357 | ||
| ... | @@ -2475,10 +2437,7 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { | ... | @@ -2475,10 +2437,7 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { |
| 2475 | .c_long => try w.writeAll("long"), | 2437 | .c_long => try w.writeAll("long"), |
| 2476 | .c_longlong => try w.writeAll("longlong"), | 2438 | .c_longlong => try w.writeAll("longlong"), |
| 2477 | else => { | 2439 | else => { |
| 2478 | const prefix_byte: u8 = switch (int_info.signedness) { | 2440 | const prefix_byte: u8 = signAbbrev(int_info.signedness); |
| 2479 | .signed => 'i', | ||
| 2480 | .unsigned => 'u', | ||
| 2481 | }; | ||
| 2482 | for ([_]u8{ 8, 16, 32, 64 }) |nbits| { | 2441 | for ([_]u8{ 8, 16, 32, 64 }) |nbits| { |
| 2483 | if (bits <= nbits) { | 2442 | if (bits <= nbits) { |
| 2484 | try w.print("{c}{d}", .{ prefix_byte, nbits }); | 2443 | try w.print("{c}{d}", .{ prefix_byte, nbits }); |
| ... | @@ -2505,28 +2464,63 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { | ... | @@ -2505,28 +2464,63 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { |
| 2505 | return ret; | 2464 | return ret; |
| 2506 | } | 2465 | } |
| 2507 | 2466 | ||
| 2508 | fn airAddWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { | 2467 | fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CValue { |
| 2509 | _ = f; | 2468 | if (f.liveness.isUnused(inst)) |
| 2510 | _ = inst; | 2469 | return CValue.none; |
| 2511 | return f.fail("TODO add with overflow", .{}); | ||
| 2512 | } | ||
| 2513 | 2470 | ||
| 2514 | fn airSubWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { | 2471 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; |
| 2515 | _ = f; | 2472 | const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2516 | _ = inst; | ||
| 2517 | return f.fail("TODO sub with overflow", .{}); | ||
| 2518 | } | ||
| 2519 | 2473 | ||
| 2520 | fn airMulWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { | 2474 | const lhs = try f.resolveInst(bin_op.lhs); |
| 2521 | _ = f; | 2475 | const rhs = try f.resolveInst(bin_op.rhs); |
| 2522 | _ = inst; | ||
| 2523 | return f.fail("TODO mul with overflow", .{}); | ||
| 2524 | } | ||
| 2525 | 2476 | ||
| 2526 | fn airShlWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { | 2477 | const inst_ty = f.air.typeOfIndex(inst); |
| 2527 | _ = f; | 2478 | const scalar_ty = f.air.typeOf(bin_op.lhs).scalarType(); |
| 2528 | _ = inst; | 2479 | const target = f.object.dg.module.getTarget(); |
| 2529 | return f.fail("TODO shl with overflow", .{}); | 2480 | const int_info = scalar_ty.intInfo(target); |
| 2481 | const w = f.object.writer(); | ||
| 2482 | const c_bits = toCIntBits(int_info.bits) orelse | ||
| 2483 | return f.fail("TODO: C backend: implement integer arithmetic larger than 128 bits", .{}); | ||
| 2484 | |||
| 2485 | var max_buf: [80]u8 = undefined; | ||
| 2486 | const max = intMax(scalar_ty, target, &max_buf); | ||
| 2487 | |||
| 2488 | const ret = try f.allocLocal(inst_ty, .Mut); | ||
| 2489 | try w.writeAll(";"); | ||
| 2490 | try f.object.indent_writer.insertNewline(); | ||
| 2491 | try f.writeCValue(w, ret); | ||
| 2492 | |||
| 2493 | switch (int_info.signedness) { | ||
| 2494 | .unsigned => { | ||
| 2495 | try w.print(".field_1 = zig_{s}u{d}(", .{ | ||
| 2496 | op_abbrev, c_bits, | ||
| 2497 | }); | ||
| 2498 | try f.writeCValue(w, lhs); | ||
| 2499 | try w.writeAll(", "); | ||
| 2500 | try f.writeCValue(w, rhs); | ||
| 2501 | try w.writeAll(", &"); | ||
| 2502 | try f.writeCValue(w, ret); | ||
| 2503 | try w.print(".field_0, {s}", .{max}); | ||
| 2504 | }, | ||
| 2505 | .signed => { | ||
| 2506 | var min_buf: [80]u8 = undefined; | ||
| 2507 | const min = intMin(scalar_ty, target, &min_buf); | ||
| 2508 | |||
| 2509 | try w.print(".field_1 = zig_{s}i{d}(", .{ | ||
| 2510 | op_abbrev, c_bits, | ||
| 2511 | }); | ||
| 2512 | try f.writeCValue(w, lhs); | ||
| 2513 | try w.writeAll(", "); | ||
| 2514 | try f.writeCValue(w, rhs); | ||
| 2515 | try w.writeAll(", &"); | ||
| 2516 | try f.writeCValue(w, ret); | ||
| 2517 | try w.print(".field_0, {s}, {s}", .{ min, max }); | ||
| 2518 | }, | ||
| 2519 | } | ||
| 2520 | |||
| 2521 | try w.writeAll(");"); | ||
| 2522 | try f.object.indent_writer.insertNewline(); | ||
| 2523 | return ret; | ||
| 2530 | } | 2524 | } |
| 2531 | 2525 | ||
| 2532 | fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { | 2526 | fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -3571,11 +3565,7 @@ fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !C | ... | @@ -3571,11 +3565,7 @@ fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !C |
| 3571 | return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); | 3565 | return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); |
| 3572 | 3566 | ||
| 3573 | try writer.print(" = zig_{s}_", .{fn_name}); | 3567 | try writer.print(" = zig_{s}_", .{fn_name}); |
| 3574 | const prefix_byte: u8 = switch (int_info.signedness) { | 3568 | try writer.print("{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); |
| 3575 | .signed => 'i', | ||
| 3576 | .unsigned => 'u', | ||
| 3577 | }; | ||
| 3578 | try writer.print("{c}{d}(", .{ prefix_byte, c_bits }); | ||
| 3579 | try f.writeCValue(writer, try f.resolveInst(operand)); | 3569 | try f.writeCValue(writer, try f.resolveInst(operand)); |
| 3580 | try writer.print(", {d});\n", .{int_info.bits}); | 3570 | try writer.print(", {d});\n", .{int_info.bits}); |
| 3581 | return local; | 3571 | return local; |
| ... | @@ -3596,11 +3586,7 @@ fn airBinOpBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u | ... | @@ -3596,11 +3586,7 @@ fn airBinOpBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u |
| 3596 | const int_info = lhs_ty.intInfo(target); | 3586 | const int_info = lhs_ty.intInfo(target); |
| 3597 | const c_bits = toCIntBits(int_info.bits) orelse | 3587 | const c_bits = toCIntBits(int_info.bits) orelse |
| 3598 | return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); | 3588 | return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); |
| 3599 | const prefix_byte: u8 = switch (int_info.signedness) { | 3589 | try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, signAbbrev(int_info.signedness), c_bits }); |
| 3600 | .signed => 'i', | ||
| 3601 | .unsigned => 'u', | ||
| 3602 | }; | ||
| 3603 | try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, prefix_byte, c_bits }); | ||
| 3604 | } else if (lhs_ty.isRuntimeFloat()) { | 3590 | } else if (lhs_ty.isRuntimeFloat()) { |
| 3605 | const c_bits = lhs_ty.floatBits(target); | 3591 | const c_bits = lhs_ty.floatBits(target); |
| 3606 | try writer.print(" = zig_{s}_f{d}", .{ fn_name, c_bits }); | 3592 | try writer.print(" = zig_{s}_f{d}", .{ fn_name, c_bits }); |
| ... | @@ -4085,3 +4071,53 @@ fn toCIntBits(zig_bits: u32) ?u32 { | ... | @@ -4085,3 +4071,53 @@ fn toCIntBits(zig_bits: u32) ?u32 { |
| 4085 | } | 4071 | } |
| 4086 | return null; | 4072 | return null; |
| 4087 | } | 4073 | } |
| 4074 | |||
| 4075 | fn signAbbrev(signedness: std.builtin.Signedness) u8 { | ||
| 4076 | return switch (signedness) { | ||
| 4077 | .signed => 'i', | ||
| 4078 | .unsigned => 'u', | ||
| 4079 | }; | ||
| 4080 | } | ||
| 4081 | |||
| 4082 | fn intMax(ty: Type, target: std.Target, buf: []u8) []const u8 { | ||
| 4083 | switch (ty.tag()) { | ||
| 4084 | .c_short => return "SHRT_MAX", | ||
| 4085 | .c_ushort => return "USHRT_MAX", | ||
| 4086 | .c_int => return "INT_MAX", | ||
| 4087 | .c_uint => return "UINT_MAX", | ||
| 4088 | .c_long => return "LONG_MAX", | ||
| 4089 | .c_ulong => return "ULONG_MAX", | ||
| 4090 | .c_longlong => return "LLONG_MAX", | ||
| 4091 | .c_ulonglong => return "ULLONG_MAX", | ||
| 4092 | else => { | ||
| 4093 | const int_info = ty.intInfo(target); | ||
| 4094 | const rhs = @intCast(u7, int_info.bits - @boolToInt(int_info.signedness == .signed)); | ||
| 4095 | const val = (@as(u128, 1) << rhs) - 1; | ||
| 4096 | // TODO make this integer literal have a suffix if necessary (such as "ull") | ||
| 4097 | return std.fmt.bufPrint(buf, "{}", .{val}) catch |err| switch (err) { | ||
| 4098 | error.NoSpaceLeft => unreachable, | ||
| 4099 | }; | ||
| 4100 | }, | ||
| 4101 | } | ||
| 4102 | } | ||
| 4103 | |||
| 4104 | fn intMin(ty: Type, target: std.Target, buf: []u8) []const u8 { | ||
| 4105 | switch (ty.tag()) { | ||
| 4106 | .c_short => return "SHRT_MIN", | ||
| 4107 | .c_int => return "INT_MIN", | ||
| 4108 | .c_long => return "LONG_MIN", | ||
| 4109 | .c_longlong => return "LLONG_MIN", | ||
| 4110 | else => { | ||
| 4111 | const int_info = ty.intInfo(target); | ||
| 4112 | assert(int_info.signedness == .signed); | ||
| 4113 | const val = v: { | ||
| 4114 | if (int_info.bits == 0) break :v 0; | ||
| 4115 | const rhs = @intCast(u7, (int_info.bits - 1)); | ||
| 4116 | break :v -(@as(i128, 1) << rhs); | ||
| 4117 | }; | ||
| 4118 | return std.fmt.bufPrint(buf, "{d}", .{val}) catch |err| switch (err) { | ||
| 4119 | error.NoSpaceLeft => unreachable, | ||
| 4120 | }; | ||
| 4121 | }, | ||
| 4122 | } | ||
| 4123 | } |
src/codegen/llvm.zig+21-6| ... | @@ -5604,14 +5604,25 @@ pub const FuncGen = struct { | ... | @@ -5604,14 +5604,25 @@ pub const FuncGen = struct { |
| 5604 | const rhs = try self.resolveInst(extra.rhs); | 5604 | const rhs = try self.resolveInst(extra.rhs); |
| 5605 | 5605 | ||
| 5606 | const lhs_ty = self.air.typeOf(extra.lhs); | 5606 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 5607 | const scalar_ty = lhs_ty.scalarType(); | ||
| 5608 | const dest_ty = self.air.typeOfIndex(inst); | ||
| 5607 | 5609 | ||
| 5608 | const intrinsic_name = if (lhs_ty.isSignedInt()) signed_intrinsic else unsigned_intrinsic; | 5610 | const intrinsic_name = if (scalar_ty.isSignedInt()) signed_intrinsic else unsigned_intrinsic; |
| 5609 | 5611 | ||
| 5610 | const llvm_lhs_ty = try self.dg.llvmType(lhs_ty); | 5612 | const llvm_lhs_ty = try self.dg.llvmType(lhs_ty); |
| 5613 | const llvm_dest_ty = try self.dg.llvmType(dest_ty); | ||
| 5614 | |||
| 5615 | const tg = self.dg.module.getTarget(); | ||
| 5611 | 5616 | ||
| 5612 | const llvm_fn = self.getIntrinsic(intrinsic_name, &.{llvm_lhs_ty}); | 5617 | const llvm_fn = self.getIntrinsic(intrinsic_name, &.{llvm_lhs_ty}); |
| 5613 | const result_struct = self.builder.buildCall(llvm_fn, &[_]*const llvm.Value{ lhs, rhs }, 2, .Fast, .Auto, ""); | 5618 | const result_struct = self.builder.buildCall(llvm_fn, &[_]*const llvm.Value{ lhs, rhs }, 2, .Fast, .Auto, ""); |
| 5614 | return result_struct; | 5619 | |
| 5620 | const result = self.builder.buildExtractValue(result_struct, 0, ""); | ||
| 5621 | const overflow_bit = self.builder.buildExtractValue(result_struct, 1, ""); | ||
| 5622 | |||
| 5623 | var ty_buf: Type.Payload.Pointer = undefined; | ||
| 5624 | const partial = self.builder.buildInsertValue(llvm_dest_ty.getUndef(), result, llvmFieldIndex(dest_ty, 0, tg, &ty_buf).?, ""); | ||
| 5625 | return self.builder.buildInsertValue(partial, overflow_bit, llvmFieldIndex(dest_ty, 1, tg, &ty_buf).?, ""); | ||
| 5615 | } | 5626 | } |
| 5616 | 5627 | ||
| 5617 | fn buildElementwiseCall( | 5628 | fn buildElementwiseCall( |
| ... | @@ -5898,26 +5909,30 @@ pub const FuncGen = struct { | ... | @@ -5898,26 +5909,30 @@ pub const FuncGen = struct { |
| 5898 | 5909 | ||
| 5899 | const lhs_ty = self.air.typeOf(extra.lhs); | 5910 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 5900 | const rhs_ty = self.air.typeOf(extra.rhs); | 5911 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 5912 | const lhs_scalar_ty = lhs_ty.scalarType(); | ||
| 5913 | const rhs_scalar_ty = rhs_ty.scalarType(); | ||
| 5914 | |||
| 5901 | const dest_ty = self.air.typeOfIndex(inst); | 5915 | const dest_ty = self.air.typeOfIndex(inst); |
| 5902 | const llvm_dest_ty = try self.dg.llvmType(dest_ty); | 5916 | const llvm_dest_ty = try self.dg.llvmType(dest_ty); |
| 5903 | 5917 | ||
| 5904 | const tg = self.dg.module.getTarget(); | 5918 | const tg = self.dg.module.getTarget(); |
| 5905 | 5919 | ||
| 5906 | const casted_rhs = if (rhs_ty.bitSize(tg) < lhs_ty.bitSize(tg)) | 5920 | const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) |
| 5907 | self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") | 5921 | self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") |
| 5908 | else | 5922 | else |
| 5909 | rhs; | 5923 | rhs; |
| 5910 | 5924 | ||
| 5911 | const result = self.builder.buildShl(lhs, casted_rhs, ""); | 5925 | const result = self.builder.buildShl(lhs, casted_rhs, ""); |
| 5912 | const reconstructed = if (lhs_ty.isSignedInt()) | 5926 | const reconstructed = if (lhs_scalar_ty.isSignedInt()) |
| 5913 | self.builder.buildAShr(result, casted_rhs, "") | 5927 | self.builder.buildAShr(result, casted_rhs, "") |
| 5914 | else | 5928 | else |
| 5915 | self.builder.buildLShr(result, casted_rhs, ""); | 5929 | self.builder.buildLShr(result, casted_rhs, ""); |
| 5916 | 5930 | ||
| 5917 | const overflow_bit = self.builder.buildICmp(.NE, lhs, reconstructed, ""); | 5931 | const overflow_bit = self.builder.buildICmp(.NE, lhs, reconstructed, ""); |
| 5918 | 5932 | ||
| 5919 | const partial = self.builder.buildInsertValue(llvm_dest_ty.getUndef(), result, 0, ""); | 5933 | var ty_buf: Type.Payload.Pointer = undefined; |
| 5920 | return self.builder.buildInsertValue(partial, overflow_bit, 1, ""); | 5934 | const partial = self.builder.buildInsertValue(llvm_dest_ty.getUndef(), result, llvmFieldIndex(dest_ty, 0, tg, &ty_buf).?, ""); |
| 5935 | return self.builder.buildInsertValue(partial, overflow_bit, llvmFieldIndex(dest_ty, 1, tg, &ty_buf).?, ""); | ||
| 5921 | } | 5936 | } |
| 5922 | 5937 | ||
| 5923 | fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 5938 | fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
src/link/C/zig.h+769| ... | @@ -165,8 +165,24 @@ | ... | @@ -165,8 +165,24 @@ |
| 165 | 165 | ||
| 166 | #define int128_t __int128 | 166 | #define int128_t __int128 |
| 167 | #define uint128_t unsigned __int128 | 167 | #define uint128_t unsigned __int128 |
| 168 | #define UINT128_MAX ((uint128_t)(0xffffffffffffffffull) | 0xffffffffffffffffull) | ||
| 168 | ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); | 169 | ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); |
| 169 | ZIG_EXTERN_C void *memset (void *, int, size_t); | 170 | ZIG_EXTERN_C void *memset (void *, int, size_t); |
| 171 | ZIG_EXTERN_C int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); | ||
| 172 | ZIG_EXTERN_C int128_t __addoti4(int128_t lhs, int128_t rhs, int *overflow); | ||
| 173 | ZIG_EXTERN_C uint64_t __uaddodi4(uint64_t lhs, uint64_t rhs, int *overflow); | ||
| 174 | ZIG_EXTERN_C uint128_t __uaddoti4(uint128_t lhs, uint128_t rhs, int *overflow); | ||
| 175 | ZIG_EXTERN_C int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow); | ||
| 176 | ZIG_EXTERN_C int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow); | ||
| 177 | ZIG_EXTERN_C int128_t __suboti4(int128_t lhs, int128_t rhs, int *overflow); | ||
| 178 | ZIG_EXTERN_C uint32_t __usubosi4(uint32_t lhs, uint32_t rhs, int *overflow); | ||
| 179 | ZIG_EXTERN_C uint64_t __usubodi4(uint64_t lhs, uint64_t rhs, int *overflow); | ||
| 180 | ZIG_EXTERN_C uint128_t __usuboti4(uint128_t lhs, uint128_t rhs, int *overflow); | ||
| 181 | ZIG_EXTERN_C int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow); | ||
| 182 | ZIG_EXTERN_C int128_t __muloti4(int128_t lhs, int128_t rhs, int *overflow); | ||
| 183 | ZIG_EXTERN_C uint64_t __umulodi4(uint64_t lhs, uint64_t rhs, int *overflow); | ||
| 184 | ZIG_EXTERN_C uint128_t __umuloti4(uint128_t lhs, uint128_t rhs, int *overflow); | ||
| 185 | |||
| 170 | 186 | ||
| 171 | static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { | 187 | static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { |
| 172 | uint8_t thresh = max - rhs; | 188 | uint8_t thresh = max - rhs; |
| ... | @@ -396,6 +412,689 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon | ... | @@ -396,6 +412,689 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon |
| 396 | return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs)); | 412 | return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs)); |
| 397 | } | 413 | } |
| 398 | 414 | ||
| 415 | static inline bool zig_addo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { | ||
| 416 | #if defined(__GNUC__) && INT8_MAX == INT_MAX | ||
| 417 | if (min == INT8_MIN && max == INT8_MAX) { | ||
| 418 | return __builtin_sadd_overflow(lhs, rhs, (int*)res); | ||
| 419 | } | ||
| 420 | #elif defined(__GNUC__) && INT8_MAX == LONG_MAX | ||
| 421 | if (min == INT8_MIN && max == INT8_MAX) { | ||
| 422 | return __builtin_saddl_overflow(lhs, rhs, (long*)res); | ||
| 423 | } | ||
| 424 | #elif defined(__GNUC__) && INT8_MAX == LLONG_MAX | ||
| 425 | if (min == INT8_MIN && max == INT8_MAX) { | ||
| 426 | return __builtin_saddll_overflow(lhs, rhs, (long long*)res); | ||
| 427 | } | ||
| 428 | #endif | ||
| 429 | int16_t big_result = (int16_t)lhs + (int16_t)rhs; | ||
| 430 | if (big_result > max) { | ||
| 431 | *res = big_result - ((int16_t)max - (int16_t)min); | ||
| 432 | return true; | ||
| 433 | } | ||
| 434 | if (big_result < min) { | ||
| 435 | *res = big_result + ((int16_t)max - (int16_t)min); | ||
| 436 | return true; | ||
| 437 | } | ||
| 438 | *res = big_result; | ||
| 439 | return false; | ||
| 440 | } | ||
| 441 | |||
| 442 | static inline bool zig_addo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { | ||
| 443 | #if defined(__GNUC__) && INT16_MAX == INT_MAX | ||
| 444 | if (min == INT16_MIN && max == INT16_MAX) { | ||
| 445 | return __builtin_sadd_overflow(lhs, rhs, (int*)res); | ||
| 446 | } | ||
| 447 | #elif defined(__GNUC__) && INT16_MAX == LONG_MAX | ||
| 448 | if (min == INT16_MIN && max == INT16_MAX) { | ||
| 449 | return __builtin_saddl_overflow(lhs, rhs, (long*)res); | ||
| 450 | } | ||
| 451 | #elif defined(__GNUC__) && INT16_MAX == LLONG_MAX | ||
| 452 | if (min == INT16_MIN && max == INT16_MAX) { | ||
| 453 | return __builtin_saddll_overflow(lhs, rhs, (long long*)res); | ||
| 454 | } | ||
| 455 | #endif | ||
| 456 | int32_t big_result = (int32_t)lhs + (int32_t)rhs; | ||
| 457 | if (big_result > max) { | ||
| 458 | *res = big_result - ((int32_t)max - (int32_t)min); | ||
| 459 | return true; | ||
| 460 | } | ||
| 461 | if (big_result < min) { | ||
| 462 | *res = big_result + ((int32_t)max - (int32_t)min); | ||
| 463 | return true; | ||
| 464 | } | ||
| 465 | *res = big_result; | ||
| 466 | return false; | ||
| 467 | } | ||
| 468 | |||
| 469 | static inline bool zig_addo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { | ||
| 470 | #if defined(__GNUC__) && INT32_MAX == INT_MAX | ||
| 471 | if (min == INT32_MIN && max == INT32_MAX) { | ||
| 472 | return __builtin_sadd_overflow(lhs, rhs, (int*)res); | ||
| 473 | } | ||
| 474 | #elif defined(__GNUC__) && INT32_MAX == LONG_MAX | ||
| 475 | if (min == INT32_MIN && max == INT32_MAX) { | ||
| 476 | return __builtin_saddl_overflow(lhs, rhs, (long*)res); | ||
| 477 | } | ||
| 478 | #elif defined(__GNUC__) && INT32_MAX == LLONG_MAX | ||
| 479 | if (min == INT32_MIN && max == INT32_MAX) { | ||
| 480 | return __builtin_saddll_overflow(lhs, rhs, (long long*)res); | ||
| 481 | } | ||
| 482 | #endif | ||
| 483 | int64_t big_result = (int64_t)lhs + (int64_t)rhs; | ||
| 484 | if (big_result > max) { | ||
| 485 | *res = big_result - ((int64_t)max - (int64_t)min); | ||
| 486 | return true; | ||
| 487 | } | ||
| 488 | if (big_result < min) { | ||
| 489 | *res = big_result + ((int64_t)max - (int64_t)min); | ||
| 490 | return true; | ||
| 491 | } | ||
| 492 | *res = big_result; | ||
| 493 | return false; | ||
| 494 | } | ||
| 495 | |||
| 496 | static inline bool zig_addo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { | ||
| 497 | bool overflow; | ||
| 498 | #if defined(__GNUC__) && INT64_MAX == INT_MAX | ||
| 499 | overflow = __builtin_sadd_overflow(lhs, rhs, (int*)res); | ||
| 500 | #elif defined(__GNUC__) && INT64_MAX == LONG_MAX | ||
| 501 | overflow = __builtin_saddl_overflow(lhs, rhs, (long*)res); | ||
| 502 | #elif defined(__GNUC__) && INT64_MAX == LLONG_MAX | ||
| 503 | overflow = __builtin_saddll_overflow(lhs, rhs, (long long*)res); | ||
| 504 | #else | ||
| 505 | int int_overflow; | ||
| 506 | *res = __addodi4(lhs, rhs, &int_overflow); | ||
| 507 | overflow = int_overflow != 0; | ||
| 508 | #endif | ||
| 509 | if (!overflow) { | ||
| 510 | if (*res > max) { | ||
| 511 | // TODO adjust the result to be the truncated bits | ||
| 512 | return true; | ||
| 513 | } else if (*res < min) { | ||
| 514 | // TODO adjust the result to be the truncated bits | ||
| 515 | return true; | ||
| 516 | } | ||
| 517 | } | ||
| 518 | return overflow; | ||
| 519 | } | ||
| 520 | |||
| 521 | static inline bool zig_addo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { | ||
| 522 | bool overflow; | ||
| 523 | #if defined(__GNUC__) && INT128_MAX == INT_MAX | ||
| 524 | overflow = __builtin_sadd_overflow(lhs, rhs, (int*)res); | ||
| 525 | #elif defined(__GNUC__) && INT128_MAX == LONG_MAX | ||
| 526 | overflow = __builtin_saddl_overflow(lhs, rhs, (long*)res); | ||
| 527 | #elif defined(__GNUC__) && INT128_MAX == LLONG_MAX | ||
| 528 | overflow = __builtin_saddll_overflow(lhs, rhs, (long long*)res); | ||
| 529 | #else | ||
| 530 | int int_overflow; | ||
| 531 | *res = __addoti4(lhs, rhs, &int_overflow); | ||
| 532 | overflow = int_overflow != 0; | ||
| 533 | #endif | ||
| 534 | if (!overflow) { | ||
| 535 | if (*res > max) { | ||
| 536 | // TODO adjust the result to be the truncated bits | ||
| 537 | return true; | ||
| 538 | } else if (*res < min) { | ||
| 539 | // TODO adjust the result to be the truncated bits | ||
| 540 | return true; | ||
| 541 | } | ||
| 542 | } | ||
| 543 | return overflow; | ||
| 544 | } | ||
| 545 | |||
| 546 | static inline bool zig_addo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { | ||
| 547 | #if defined(__GNUC__) && UINT8_MAX == UINT_MAX | ||
| 548 | if (max == UINT8_MAX) { | ||
| 549 | return __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); | ||
| 550 | } | ||
| 551 | #elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX | ||
| 552 | if (max == UINT8_MAX) { | ||
| 553 | return __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); | ||
| 554 | } | ||
| 555 | #elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX | ||
| 556 | if (max == UINT8_MAX) { | ||
| 557 | return __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 558 | } | ||
| 559 | #endif | ||
| 560 | uint16_t big_result = (uint16_t)lhs + (uint16_t)rhs; | ||
| 561 | if (big_result > max) { | ||
| 562 | *res = big_result - max - 1; | ||
| 563 | return true; | ||
| 564 | } | ||
| 565 | *res = big_result; | ||
| 566 | return false; | ||
| 567 | } | ||
| 568 | |||
| 569 | static inline uint16_t zig_addo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { | ||
| 570 | #if defined(__GNUC__) && UINT16_MAX == UINT_MAX | ||
| 571 | if (max == UINT16_MAX) { | ||
| 572 | return __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); | ||
| 573 | } | ||
| 574 | #elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX | ||
| 575 | if (max == UINT16_MAX) { | ||
| 576 | return __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); | ||
| 577 | } | ||
| 578 | #elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX | ||
| 579 | if (max == UINT16_MAX) { | ||
| 580 | return __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 581 | } | ||
| 582 | #endif | ||
| 583 | uint32_t big_result = (uint32_t)lhs + (uint32_t)rhs; | ||
| 584 | if (big_result > max) { | ||
| 585 | *res = big_result - max - 1; | ||
| 586 | return true; | ||
| 587 | } | ||
| 588 | *res = big_result; | ||
| 589 | return false; | ||
| 590 | } | ||
| 591 | |||
| 592 | static inline uint32_t zig_addo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { | ||
| 593 | #if defined(__GNUC__) && UINT32_MAX == UINT_MAX | ||
| 594 | if (max == UINT32_MAX) { | ||
| 595 | return __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); | ||
| 596 | } | ||
| 597 | #elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX | ||
| 598 | if (max == UINT32_MAX) { | ||
| 599 | return __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); | ||
| 600 | } | ||
| 601 | #elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX | ||
| 602 | if (max == UINT32_MAX) { | ||
| 603 | return __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 604 | } | ||
| 605 | #endif | ||
| 606 | uint64_t big_result = (uint64_t)lhs + (uint64_t)rhs; | ||
| 607 | if (big_result > max) { | ||
| 608 | *res = big_result - max - 1; | ||
| 609 | return true; | ||
| 610 | } | ||
| 611 | *res = big_result; | ||
| 612 | return false; | ||
| 613 | } | ||
| 614 | |||
| 615 | static inline uint64_t zig_addo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { | ||
| 616 | bool overflow; | ||
| 617 | #if defined(__GNUC__) && UINT64_MAX == UINT_MAX | ||
| 618 | overflow = __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); | ||
| 619 | #elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX | ||
| 620 | overflow = __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); | ||
| 621 | #elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX | ||
| 622 | overflow = __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 623 | #else | ||
| 624 | int int_overflow; | ||
| 625 | *res = __uaddodi4(lhs, rhs, &int_overflow); | ||
| 626 | overflow = int_overflow != 0; | ||
| 627 | #endif | ||
| 628 | if (*res > max && !overflow) { | ||
| 629 | *res -= max - 1; | ||
| 630 | return true; | ||
| 631 | } | ||
| 632 | return overflow; | ||
| 633 | } | ||
| 634 | |||
| 635 | static inline uint128_t zig_addo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { | ||
| 636 | int overflow; | ||
| 637 | *res = __uaddoti4(lhs, rhs, &overflow); | ||
| 638 | if (*res > max && overflow == 0) { | ||
| 639 | *res -= max - 1; | ||
| 640 | return true; | ||
| 641 | } | ||
| 642 | return overflow != 0; | ||
| 643 | } | ||
| 644 | |||
| 645 | static inline bool zig_subo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { | ||
| 646 | #if defined(__GNUC__) && INT8_MAX == INT_MAX | ||
| 647 | if (min == INT8_MIN && max == INT8_MAX) { | ||
| 648 | return __builtin_ssub_overflow(lhs, rhs, (int*)res); | ||
| 649 | } | ||
| 650 | #elif defined(__GNUC__) && INT8_MAX == LONG_MAX | ||
| 651 | if (min == INT8_MIN && max == INT8_MAX) { | ||
| 652 | return __builtin_ssubl_overflow(lhs, rhs, (long*)res); | ||
| 653 | } | ||
| 654 | #elif defined(__GNUC__) && INT8_MAX == LLONG_MAX | ||
| 655 | if (min == INT8_MIN && max == INT8_MAX) { | ||
| 656 | return __builtin_ssubll_overflow(lhs, rhs, (long long*)res); | ||
| 657 | } | ||
| 658 | #endif | ||
| 659 | int16_t big_result = (int16_t)lhs - (int16_t)rhs; | ||
| 660 | if (big_result > max) { | ||
| 661 | *res = big_result - ((int16_t)max - (int16_t)min); | ||
| 662 | return true; | ||
| 663 | } | ||
| 664 | if (big_result < min) { | ||
| 665 | *res = big_result + ((int16_t)max - (int16_t)min); | ||
| 666 | return true; | ||
| 667 | } | ||
| 668 | *res = big_result; | ||
| 669 | return false; | ||
| 670 | } | ||
| 671 | |||
| 672 | static inline bool zig_subo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { | ||
| 673 | #if defined(__GNUC__) && INT16_MAX == INT_MAX | ||
| 674 | if (min == INT16_MIN && max == INT16_MAX) { | ||
| 675 | return __builtin_ssub_overflow(lhs, rhs, (int*)res); | ||
| 676 | } | ||
| 677 | #elif defined(__GNUC__) && INT16_MAX == LONG_MAX | ||
| 678 | if (min == INT16_MIN && max == INT16_MAX) { | ||
| 679 | return __builtin_ssubl_overflow(lhs, rhs, (long*)res); | ||
| 680 | } | ||
| 681 | #elif defined(__GNUC__) && INT16_MAX == LLONG_MAX | ||
| 682 | if (min == INT16_MIN && max == INT16_MAX) { | ||
| 683 | return __builtin_ssubll_overflow(lhs, rhs, (long long*)res); | ||
| 684 | } | ||
| 685 | #endif | ||
| 686 | int32_t big_result = (int32_t)lhs - (int32_t)rhs; | ||
| 687 | if (big_result > max) { | ||
| 688 | *res = big_result - ((int32_t)max - (int32_t)min); | ||
| 689 | return true; | ||
| 690 | } | ||
| 691 | if (big_result < min) { | ||
| 692 | *res = big_result + ((int32_t)max - (int32_t)min); | ||
| 693 | return true; | ||
| 694 | } | ||
| 695 | *res = big_result; | ||
| 696 | return false; | ||
| 697 | } | ||
| 698 | |||
| 699 | static inline bool zig_subo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { | ||
| 700 | #if defined(__GNUC__) && INT32_MAX == INT_MAX | ||
| 701 | if (min == INT32_MIN && max == INT32_MAX) { | ||
| 702 | return __builtin_ssub_overflow(lhs, rhs, (int*)res); | ||
| 703 | } | ||
| 704 | #elif defined(__GNUC__) && INT32_MAX == LONG_MAX | ||
| 705 | if (min == INT32_MIN && max == INT32_MAX) { | ||
| 706 | return __builtin_ssubl_overflow(lhs, rhs, (long*)res); | ||
| 707 | } | ||
| 708 | #elif defined(__GNUC__) && INT32_MAX == LLONG_MAX | ||
| 709 | if (min == INT32_MIN && max == INT32_MAX) { | ||
| 710 | return __builtin_ssubll_overflow(lhs, rhs, (long long*)res); | ||
| 711 | } | ||
| 712 | #endif | ||
| 713 | int64_t big_result = (int64_t)lhs - (int64_t)rhs; | ||
| 714 | if (big_result > max) { | ||
| 715 | *res = big_result - ((int64_t)max - (int64_t)min); | ||
| 716 | return true; | ||
| 717 | } | ||
| 718 | if (big_result < min) { | ||
| 719 | *res = big_result + ((int64_t)max - (int64_t)min); | ||
| 720 | return true; | ||
| 721 | } | ||
| 722 | *res = big_result; | ||
| 723 | return false; | ||
| 724 | } | ||
| 725 | |||
| 726 | static inline bool zig_subo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { | ||
| 727 | bool overflow; | ||
| 728 | #if defined(__GNUC__) && INT64_MAX == INT_MAX | ||
| 729 | overflow = __builtin_ssub_overflow(lhs, rhs, (int*)res); | ||
| 730 | #elif defined(__GNUC__) && INT64_MAX == LONG_MAX | ||
| 731 | overflow = __builtin_ssubl_overflow(lhs, rhs, (long*)res); | ||
| 732 | #elif defined(__GNUC__) && INT64_MAX == LLONG_MAX | ||
| 733 | overflow = __builtin_ssubll_overflow(lhs, rhs, (long long*)res); | ||
| 734 | #else | ||
| 735 | int int_overflow; | ||
| 736 | *res = __subodi4(lhs, rhs, &int_overflow); | ||
| 737 | overflow = int_overflow != 0; | ||
| 738 | #endif | ||
| 739 | if (!overflow) { | ||
| 740 | if (*res > max) { | ||
| 741 | // TODO adjust the result to be the truncated bits | ||
| 742 | return true; | ||
| 743 | } else if (*res < min) { | ||
| 744 | // TODO adjust the result to be the truncated bits | ||
| 745 | return true; | ||
| 746 | } | ||
| 747 | } | ||
| 748 | return overflow; | ||
| 749 | } | ||
| 750 | |||
| 751 | static inline bool zig_subo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { | ||
| 752 | bool overflow; | ||
| 753 | #if defined(__GNUC__) && INT128_MAX == INT_MAX | ||
| 754 | overflow = __builtin_ssub_overflow(lhs, rhs, (int*)res); | ||
| 755 | #elif defined(__GNUC__) && INT128_MAX == LONG_MAX | ||
| 756 | overflow = __builtin_ssubl_overflow(lhs, rhs, (long*)res); | ||
| 757 | #elif defined(__GNUC__) && INT128_MAX == LLONG_MAX | ||
| 758 | overflow = __builtin_ssubll_overflow(lhs, rhs, (long long*)res); | ||
| 759 | #else | ||
| 760 | int int_overflow; | ||
| 761 | *res = __suboti4(lhs, rhs, &int_overflow); | ||
| 762 | overflow = int_overflow != 0; | ||
| 763 | #endif | ||
| 764 | if (!overflow) { | ||
| 765 | if (*res > max) { | ||
| 766 | // TODO adjust the result to be the truncated bits | ||
| 767 | return true; | ||
| 768 | } else if (*res < min) { | ||
| 769 | // TODO adjust the result to be the truncated bits | ||
| 770 | return true; | ||
| 771 | } | ||
| 772 | } | ||
| 773 | return overflow; | ||
| 774 | } | ||
| 775 | |||
| 776 | static inline bool zig_subo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { | ||
| 777 | #if defined(__GNUC__) && UINT8_MAX == UINT_MAX | ||
| 778 | return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); | ||
| 779 | #elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX | ||
| 780 | return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); | ||
| 781 | #elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX | ||
| 782 | return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 783 | #endif | ||
| 784 | if (rhs > lhs) { | ||
| 785 | *res = max - (rhs - lhs - 1); | ||
| 786 | return true; | ||
| 787 | } | ||
| 788 | *res = lhs - rhs; | ||
| 789 | return false; | ||
| 790 | } | ||
| 791 | |||
| 792 | static inline uint16_t zig_subo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { | ||
| 793 | #if defined(__GNUC__) && UINT16_MAX == UINT_MAX | ||
| 794 | return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); | ||
| 795 | #elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX | ||
| 796 | return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); | ||
| 797 | #elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX | ||
| 798 | return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 799 | #endif | ||
| 800 | if (rhs > lhs) { | ||
| 801 | *res = max - (rhs - lhs - 1); | ||
| 802 | return true; | ||
| 803 | } | ||
| 804 | *res = lhs - rhs; | ||
| 805 | return false; | ||
| 806 | } | ||
| 807 | |||
| 808 | static inline uint32_t zig_subo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { | ||
| 809 | if (max == UINT32_MAX) { | ||
| 810 | #if defined(__GNUC__) && UINT32_MAX == UINT_MAX | ||
| 811 | return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); | ||
| 812 | #elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX | ||
| 813 | return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); | ||
| 814 | #elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX | ||
| 815 | return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 816 | #endif | ||
| 817 | int int_overflow; | ||
| 818 | *res = __usubosi4(lhs, rhs, &int_overflow); | ||
| 819 | return int_overflow != 0; | ||
| 820 | } else { | ||
| 821 | if (rhs > lhs) { | ||
| 822 | *res = max - (rhs - lhs - 1); | ||
| 823 | return true; | ||
| 824 | } | ||
| 825 | *res = lhs - rhs; | ||
| 826 | return false; | ||
| 827 | } | ||
| 828 | } | ||
| 829 | |||
| 830 | static inline uint64_t zig_subo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { | ||
| 831 | if (max == UINT64_MAX) { | ||
| 832 | #if defined(__GNUC__) && UINT64_MAX == UINT_MAX | ||
| 833 | return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); | ||
| 834 | #elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX | ||
| 835 | return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); | ||
| 836 | #elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX | ||
| 837 | return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 838 | #else | ||
| 839 | int int_overflow; | ||
| 840 | *res = __usubodi4(lhs, rhs, &int_overflow); | ||
| 841 | return int_overflow != 0; | ||
| 842 | #endif | ||
| 843 | } else { | ||
| 844 | if (rhs > lhs) { | ||
| 845 | *res = max - (rhs - lhs - 1); | ||
| 846 | return true; | ||
| 847 | } | ||
| 848 | *res = lhs - rhs; | ||
| 849 | return false; | ||
| 850 | } | ||
| 851 | } | ||
| 852 | |||
| 853 | static inline uint128_t zig_subo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { | ||
| 854 | if (max == UINT128_MAX) { | ||
| 855 | int int_overflow; | ||
| 856 | *res = __usuboti4(lhs, rhs, &int_overflow); | ||
| 857 | return int_overflow != 0; | ||
| 858 | } else { | ||
| 859 | if (rhs > lhs) { | ||
| 860 | *res = max - (rhs - lhs - 1); | ||
| 861 | return true; | ||
| 862 | } | ||
| 863 | *res = lhs - rhs; | ||
| 864 | return false; | ||
| 865 | } | ||
| 866 | } | ||
| 867 | |||
| 868 | static inline bool zig_mulo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { | ||
| 869 | #if defined(__GNUC__) && INT8_MAX == INT_MAX | ||
| 870 | if (min == INT8_MIN && max == INT8_MAX) { | ||
| 871 | return __builtin_smul_overflow(lhs, rhs, (int*)res); | ||
| 872 | } | ||
| 873 | #elif defined(__GNUC__) && INT8_MAX == LONG_MAX | ||
| 874 | if (min == INT8_MIN && max == INT8_MAX) { | ||
| 875 | return __builtin_smull_overflow(lhs, rhs, (long*)res); | ||
| 876 | } | ||
| 877 | #elif defined(__GNUC__) && INT8_MAX == LLONG_MAX | ||
| 878 | if (min == INT8_MIN && max == INT8_MAX) { | ||
| 879 | return __builtin_smulll_overflow(lhs, rhs, (long long*)res); | ||
| 880 | } | ||
| 881 | #endif | ||
| 882 | int16_t big_result = (int16_t)lhs * (int16_t)rhs; | ||
| 883 | if (big_result > max) { | ||
| 884 | *res = big_result - ((int16_t)max - (int16_t)min); | ||
| 885 | return true; | ||
| 886 | } | ||
| 887 | if (big_result < min) { | ||
| 888 | *res = big_result + ((int16_t)max - (int16_t)min); | ||
| 889 | return true; | ||
| 890 | } | ||
| 891 | *res = big_result; | ||
| 892 | return false; | ||
| 893 | } | ||
| 894 | |||
| 895 | static inline bool zig_mulo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { | ||
| 896 | #if defined(__GNUC__) && INT16_MAX == INT_MAX | ||
| 897 | if (min == INT16_MIN && max == INT16_MAX) { | ||
| 898 | return __builtin_smul_overflow(lhs, rhs, (int*)res); | ||
| 899 | } | ||
| 900 | #elif defined(__GNUC__) && INT16_MAX == LONG_MAX | ||
| 901 | if (min == INT16_MIN && max == INT16_MAX) { | ||
| 902 | return __builtin_smull_overflow(lhs, rhs, (long*)res); | ||
| 903 | } | ||
| 904 | #elif defined(__GNUC__) && INT16_MAX == LLONG_MAX | ||
| 905 | if (min == INT16_MIN && max == INT16_MAX) { | ||
| 906 | return __builtin_smulll_overflow(lhs, rhs, (long long*)res); | ||
| 907 | } | ||
| 908 | #endif | ||
| 909 | int32_t big_result = (int32_t)lhs * (int32_t)rhs; | ||
| 910 | if (big_result > max) { | ||
| 911 | *res = big_result - ((int32_t)max - (int32_t)min); | ||
| 912 | return true; | ||
| 913 | } | ||
| 914 | if (big_result < min) { | ||
| 915 | *res = big_result + ((int32_t)max - (int32_t)min); | ||
| 916 | return true; | ||
| 917 | } | ||
| 918 | *res = big_result; | ||
| 919 | return false; | ||
| 920 | } | ||
| 921 | |||
| 922 | static inline bool zig_mulo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { | ||
| 923 | #if defined(__GNUC__) && INT32_MAX == INT_MAX | ||
| 924 | if (min == INT32_MIN && max == INT32_MAX) { | ||
| 925 | return __builtin_smul_overflow(lhs, rhs, (int*)res); | ||
| 926 | } | ||
| 927 | #elif defined(__GNUC__) && INT32_MAX == LONG_MAX | ||
| 928 | if (min == INT32_MIN && max == INT32_MAX) { | ||
| 929 | return __builtin_smull_overflow(lhs, rhs, (long*)res); | ||
| 930 | } | ||
| 931 | #elif defined(__GNUC__) && INT32_MAX == LLONG_MAX | ||
| 932 | if (min == INT32_MIN && max == INT32_MAX) { | ||
| 933 | return __builtin_smulll_overflow(lhs, rhs, (long long*)res); | ||
| 934 | } | ||
| 935 | #endif | ||
| 936 | int64_t big_result = (int64_t)lhs * (int64_t)rhs; | ||
| 937 | if (big_result > max) { | ||
| 938 | *res = big_result - ((int64_t)max - (int64_t)min); | ||
| 939 | return true; | ||
| 940 | } | ||
| 941 | if (big_result < min) { | ||
| 942 | *res = big_result + ((int64_t)max - (int64_t)min); | ||
| 943 | return true; | ||
| 944 | } | ||
| 945 | *res = big_result; | ||
| 946 | return false; | ||
| 947 | } | ||
| 948 | |||
| 949 | static inline bool zig_mulo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { | ||
| 950 | bool overflow; | ||
| 951 | #if defined(__GNUC__) && INT64_MAX == INT_MAX | ||
| 952 | overflow = __builtin_smul_overflow(lhs, rhs, (int*)res); | ||
| 953 | #elif defined(__GNUC__) && INT64_MAX == LONG_MAX | ||
| 954 | overflow = __builtin_smull_overflow(lhs, rhs, (long*)res); | ||
| 955 | #elif defined(__GNUC__) && INT64_MAX == LLONG_MAX | ||
| 956 | overflow = __builtin_smulll_overflow(lhs, rhs, (long long*)res); | ||
| 957 | #else | ||
| 958 | int int_overflow; | ||
| 959 | *res = __mulodi4(lhs, rhs, &int_overflow); | ||
| 960 | overflow = int_overflow != 0; | ||
| 961 | #endif | ||
| 962 | if (!overflow) { | ||
| 963 | if (*res > max) { | ||
| 964 | // TODO adjust the result to be the truncated bits | ||
| 965 | return true; | ||
| 966 | } else if (*res < min) { | ||
| 967 | // TODO adjust the result to be the truncated bits | ||
| 968 | return true; | ||
| 969 | } | ||
| 970 | } | ||
| 971 | return overflow; | ||
| 972 | } | ||
| 973 | |||
| 974 | static inline bool zig_mulo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { | ||
| 975 | bool overflow; | ||
| 976 | #if defined(__GNUC__) && INT128_MAX == INT_MAX | ||
| 977 | overflow = __builtin_smul_overflow(lhs, rhs, (int*)res); | ||
| 978 | #elif defined(__GNUC__) && INT128_MAX == LONG_MAX | ||
| 979 | overflow = __builtin_smull_overflow(lhs, rhs, (long*)res); | ||
| 980 | #elif defined(__GNUC__) && INT128_MAX == LLONG_MAX | ||
| 981 | overflow = __builtin_smulll_overflow(lhs, rhs, (long long*)res); | ||
| 982 | #else | ||
| 983 | int int_overflow; | ||
| 984 | *res = __muloti4(lhs, rhs, &int_overflow); | ||
| 985 | overflow = int_overflow != 0; | ||
| 986 | #endif | ||
| 987 | if (!overflow) { | ||
| 988 | if (*res > max) { | ||
| 989 | // TODO adjust the result to be the truncated bits | ||
| 990 | return true; | ||
| 991 | } else if (*res < min) { | ||
| 992 | // TODO adjust the result to be the truncated bits | ||
| 993 | return true; | ||
| 994 | } | ||
| 995 | } | ||
| 996 | return overflow; | ||
| 997 | } | ||
| 998 | |||
| 999 | static inline bool zig_mulo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { | ||
| 1000 | #if defined(__GNUC__) && UINT8_MAX == UINT_MAX | ||
| 1001 | if (max == UINT8_MAX) { | ||
| 1002 | return __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); | ||
| 1003 | } | ||
| 1004 | #elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX | ||
| 1005 | if (max == UINT8_MAX) { | ||
| 1006 | return __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); | ||
| 1007 | } | ||
| 1008 | #elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX | ||
| 1009 | if (max == UINT8_MAX) { | ||
| 1010 | return __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 1011 | } | ||
| 1012 | #endif | ||
| 1013 | uint16_t big_result = (uint16_t)lhs * (uint16_t)rhs; | ||
| 1014 | if (big_result > max) { | ||
| 1015 | *res = big_result - max - 1; | ||
| 1016 | return true; | ||
| 1017 | } | ||
| 1018 | *res = big_result; | ||
| 1019 | return false; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | static inline uint16_t zig_mulo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { | ||
| 1023 | #if defined(__GNUC__) && UINT16_MAX == UINT_MAX | ||
| 1024 | if (max == UINT16_MAX) { | ||
| 1025 | return __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); | ||
| 1026 | } | ||
| 1027 | #elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX | ||
| 1028 | if (max == UINT16_MAX) { | ||
| 1029 | return __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); | ||
| 1030 | } | ||
| 1031 | #elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX | ||
| 1032 | if (max == UINT16_MAX) { | ||
| 1033 | return __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 1034 | } | ||
| 1035 | #endif | ||
| 1036 | uint32_t big_result = (uint32_t)lhs * (uint32_t)rhs; | ||
| 1037 | if (big_result > max) { | ||
| 1038 | *res = big_result - max - 1; | ||
| 1039 | return true; | ||
| 1040 | } | ||
| 1041 | *res = big_result; | ||
| 1042 | return false; | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | static inline uint32_t zig_mulo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { | ||
| 1046 | #if defined(__GNUC__) && UINT32_MAX == UINT_MAX | ||
| 1047 | if (max == UINT32_MAX) { | ||
| 1048 | return __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); | ||
| 1049 | } | ||
| 1050 | #elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX | ||
| 1051 | if (max == UINT32_MAX) { | ||
| 1052 | return __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); | ||
| 1053 | } | ||
| 1054 | #elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX | ||
| 1055 | if (max == UINT32_MAX) { | ||
| 1056 | return __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 1057 | } | ||
| 1058 | #endif | ||
| 1059 | uint64_t big_result = (uint64_t)lhs * (uint64_t)rhs; | ||
| 1060 | if (big_result > max) { | ||
| 1061 | *res = big_result - max - 1; | ||
| 1062 | return true; | ||
| 1063 | } | ||
| 1064 | *res = big_result; | ||
| 1065 | return false; | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | static inline uint64_t zig_mulo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { | ||
| 1069 | bool overflow; | ||
| 1070 | #if defined(__GNUC__) && UINT64_MAX == UINT_MAX | ||
| 1071 | overflow = __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); | ||
| 1072 | #elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX | ||
| 1073 | overflow = __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); | ||
| 1074 | #elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX | ||
| 1075 | overflow = __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); | ||
| 1076 | #else | ||
| 1077 | int int_overflow; | ||
| 1078 | *res = __umulodi4(lhs, rhs, &int_overflow); | ||
| 1079 | overflow = int_overflow != 0; | ||
| 1080 | #endif | ||
| 1081 | if (*res > max && !overflow) { | ||
| 1082 | *res -= max - 1; | ||
| 1083 | return true; | ||
| 1084 | } | ||
| 1085 | return overflow; | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | static inline uint128_t zig_mulo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { | ||
| 1089 | int overflow; | ||
| 1090 | *res = __umuloti4(lhs, rhs, &overflow); | ||
| 1091 | if (*res > max && overflow == 0) { | ||
| 1092 | *res -= max - 1; | ||
| 1093 | return true; | ||
| 1094 | } | ||
| 1095 | return overflow != 0; | ||
| 1096 | } | ||
| 1097 | |||
| 399 | static inline float zig_bitcast_f32_u32(uint32_t arg) { | 1098 | static inline float zig_bitcast_f32_u32(uint32_t arg) { |
| 400 | float dest; | 1099 | float dest; |
| 401 | memcpy(&dest, &arg, sizeof dest); | 1100 | memcpy(&dest, &arg, sizeof dest); |
| ... | @@ -608,6 +1307,76 @@ static inline int zig_popcount_u128(uint128_t value, uint8_t zig_type_bit_width) | ... | @@ -608,6 +1307,76 @@ static inline int zig_popcount_u128(uint128_t value, uint8_t zig_type_bit_width) |
| 608 | 1307 | ||
| 609 | #define zig_popcount_i128 zig_popcount_u128 | 1308 | #define zig_popcount_i128 zig_popcount_u128 |
| 610 | 1309 | ||
| 1310 | static inline bool zig_shlo_i8(int8_t lhs, int8_t rhs, int8_t *res, uint8_t bits) { | ||
| 1311 | *res = lhs << rhs; | ||
| 1312 | if (zig_clz_i8(lhs, bits) >= rhs) return false; | ||
| 1313 | *res &= UINT8_MAX >> (8 - bits); | ||
| 1314 | return true; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | static inline bool zig_shlo_i16(int16_t lhs, int16_t rhs, int16_t *res, uint8_t bits) { | ||
| 1318 | *res = lhs << rhs; | ||
| 1319 | if (zig_clz_i16(lhs, bits) >= rhs) return false; | ||
| 1320 | *res &= UINT16_MAX >> (16 - bits); | ||
| 1321 | return true; | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | static inline bool zig_shlo_i32(int32_t lhs, int32_t rhs, int32_t *res, uint8_t bits) { | ||
| 1325 | *res = lhs << rhs; | ||
| 1326 | if (zig_clz_i32(lhs, bits) >= rhs) return false; | ||
| 1327 | *res &= UINT32_MAX >> (32 - bits); | ||
| 1328 | return true; | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | static inline bool zig_shlo_i64(int64_t lhs, int64_t rhs, int64_t *res, uint8_t bits) { | ||
| 1332 | *res = lhs << rhs; | ||
| 1333 | if (zig_clz_i64(lhs, bits) >= rhs) return false; | ||
| 1334 | *res &= UINT64_MAX >> (64 - bits); | ||
| 1335 | return true; | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | static inline bool zig_shlo_i128(int128_t lhs, int128_t rhs, int128_t *res, uint8_t bits) { | ||
| 1339 | *res = lhs << rhs; | ||
| 1340 | if (zig_clz_i128(lhs, bits) >= rhs) return false; | ||
| 1341 | *res &= UINT128_MAX >> (128 - bits); | ||
| 1342 | return true; | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | static inline bool zig_shlo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t bits) { | ||
| 1346 | *res = lhs << rhs; | ||
| 1347 | if (zig_clz_u8(lhs, bits) >= rhs) return false; | ||
| 1348 | *res &= UINT8_MAX >> (8 - bits); | ||
| 1349 | return true; | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | static inline uint16_t zig_shlo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint8_t bits) { | ||
| 1353 | *res = lhs << rhs; | ||
| 1354 | if (zig_clz_u16(lhs, bits) >= rhs) return false; | ||
| 1355 | *res &= UINT16_MAX >> (16 - bits); | ||
| 1356 | return true; | ||
| 1357 | } | ||
| 1358 | |||
| 1359 | static inline uint32_t zig_shlo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint8_t bits) { | ||
| 1360 | *res = lhs << rhs; | ||
| 1361 | if (zig_clz_u32(lhs, bits) >= rhs) return false; | ||
| 1362 | *res &= UINT32_MAX >> (32 - bits); | ||
| 1363 | return true; | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | static inline uint64_t zig_shlo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint8_t bits) { | ||
| 1367 | *res = lhs << rhs; | ||
| 1368 | if (zig_clz_u64(lhs, bits) >= rhs) return false; | ||
| 1369 | *res &= UINT64_MAX >> (64 - bits); | ||
| 1370 | return true; | ||
| 1371 | } | ||
| 1372 | |||
| 1373 | static inline uint128_t zig_shlo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint8_t bits) { | ||
| 1374 | *res = lhs << rhs; | ||
| 1375 | if (zig_clz_u128(lhs, bits) >= rhs) return false; | ||
| 1376 | *res &= UINT128_MAX >> (128 - bits); | ||
| 1377 | return true; | ||
| 1378 | } | ||
| 1379 | |||
| 611 | #define zig_sign_extend(T) \ | 1380 | #define zig_sign_extend(T) \ |
| 612 | static inline T zig_sign_extend_##T(T value, uint8_t zig_type_bit_width) { \ | 1381 | static inline T zig_sign_extend_##T(T value, uint8_t zig_type_bit_width) { \ |
| 613 | const T m = (T)1 << (T)(zig_type_bit_width - 1); \ | 1382 | const T m = (T)1 << (T)(zig_type_bit_width - 1); \ |
src/type.zig+1| ... | @@ -5999,6 +5999,7 @@ pub const Type = extern union { | ... | @@ -5999,6 +5999,7 @@ pub const Type = extern union { |
| 5999 | }; | 5999 | }; |
| 6000 | }; | 6000 | }; |
| 6001 | 6001 | ||
| 6002 | pub const @"u1" = initTag(.u1); | ||
| 6002 | pub const @"u8" = initTag(.u8); | 6003 | pub const @"u8" = initTag(.u8); |
| 6003 | pub const @"u16" = initTag(.u16); | 6004 | pub const @"u16" = initTag(.u16); |
| 6004 | pub const @"u32" = initTag(.u32); | 6005 | pub const @"u32" = initTag(.u32); |
src/value.zig+110-6| ... | @@ -1671,6 +1671,7 @@ pub const Value = extern union { | ... | @@ -1671,6 +1671,7 @@ pub const Value = extern union { |
| 1671 | } | 1671 | } |
| 1672 | 1672 | ||
| 1673 | /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. | 1673 | /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. |
| 1674 | /// Vectors are also accepted. Vector results are reduced with AND. | ||
| 1674 | pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { | 1675 | pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { |
| 1675 | switch (self.tag()) { | 1676 | switch (self.tag()) { |
| 1676 | .zero, | 1677 | .zero, |
| ... | @@ -1767,6 +1768,16 @@ pub const Value = extern union { | ... | @@ -1767,6 +1768,16 @@ pub const Value = extern union { |
| 1767 | else => unreachable, | 1768 | else => unreachable, |
| 1768 | }, | 1769 | }, |
| 1769 | 1770 | ||
| 1771 | .aggregate => { | ||
| 1772 | assert(ty.zigTypeTag() == .Vector); | ||
| 1773 | for (self.castTag(.aggregate).?.data) |elem| { | ||
| 1774 | if (!elem.intFitsInType(ty.scalarType(), target)) { | ||
| 1775 | return false; | ||
| 1776 | } | ||
| 1777 | } | ||
| 1778 | return true; | ||
| 1779 | }, | ||
| 1780 | |||
| 1770 | else => unreachable, | 1781 | else => unreachable, |
| 1771 | } | 1782 | } |
| 1772 | } | 1783 | } |
| ... | @@ -2015,7 +2026,7 @@ pub const Value = extern union { | ... | @@ -2015,7 +2026,7 @@ pub const Value = extern union { |
| 2015 | const result_data = try allocator.alloc(Value, ty.vectorLen()); | 2026 | const result_data = try allocator.alloc(Value, ty.vectorLen()); |
| 2016 | for (result_data) |*scalar, i| { | 2027 | for (result_data) |*scalar, i| { |
| 2017 | const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), mod); | 2028 | const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), mod); |
| 2018 | scalar.* = if (res_bool) Value.@"true" else Value.@"false"; | 2029 | scalar.* = makeBool(res_bool); |
| 2019 | } | 2030 | } |
| 2020 | return Value.Tag.aggregate.create(allocator, result_data); | 2031 | return Value.Tag.aggregate.create(allocator, result_data); |
| 2021 | } | 2032 | } |
| ... | @@ -2950,7 +2961,8 @@ pub const Value = extern union { | ... | @@ -2950,7 +2961,8 @@ pub const Value = extern union { |
| 2950 | } | 2961 | } |
| 2951 | 2962 | ||
| 2952 | pub const OverflowArithmeticResult = struct { | 2963 | pub const OverflowArithmeticResult = struct { |
| 2953 | overflowed: bool, | 2964 | /// TODO: Rename to `overflow_bit` and make of type `u1`. |
| 2965 | overflowed: Value, | ||
| 2954 | wrapped_result: Value, | 2966 | wrapped_result: Value, |
| 2955 | }; | 2967 | }; |
| 2956 | 2968 | ||
| ... | @@ -2960,6 +2972,29 @@ pub const Value = extern union { | ... | @@ -2960,6 +2972,29 @@ pub const Value = extern union { |
| 2960 | ty: Type, | 2972 | ty: Type, |
| 2961 | arena: Allocator, | 2973 | arena: Allocator, |
| 2962 | target: Target, | 2974 | target: Target, |
| 2975 | ) !OverflowArithmeticResult { | ||
| 2976 | if (ty.zigTypeTag() == .Vector) { | ||
| 2977 | const overflowed_data = try arena.alloc(Value, ty.vectorLen()); | ||
| 2978 | const result_data = try arena.alloc(Value, ty.vectorLen()); | ||
| 2979 | for (result_data) |*scalar, i| { | ||
| 2980 | const of_math_result = try intAddWithOverflowScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); | ||
| 2981 | overflowed_data[i] = of_math_result.overflowed; | ||
| 2982 | scalar.* = of_math_result.wrapped_result; | ||
| 2983 | } | ||
| 2984 | return OverflowArithmeticResult{ | ||
| 2985 | .overflowed = try Value.Tag.aggregate.create(arena, overflowed_data), | ||
| 2986 | .wrapped_result = try Value.Tag.aggregate.create(arena, result_data), | ||
| 2987 | }; | ||
| 2988 | } | ||
| 2989 | return intAddWithOverflowScalar(lhs, rhs, ty, arena, target); | ||
| 2990 | } | ||
| 2991 | |||
| 2992 | pub fn intAddWithOverflowScalar( | ||
| 2993 | lhs: Value, | ||
| 2994 | rhs: Value, | ||
| 2995 | ty: Type, | ||
| 2996 | arena: Allocator, | ||
| 2997 | target: Target, | ||
| 2963 | ) !OverflowArithmeticResult { | 2998 | ) !OverflowArithmeticResult { |
| 2964 | const info = ty.intInfo(target); | 2999 | const info = ty.intInfo(target); |
| 2965 | 3000 | ||
| ... | @@ -2975,7 +3010,7 @@ pub const Value = extern union { | ... | @@ -2975,7 +3010,7 @@ pub const Value = extern union { |
| 2975 | const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); | 3010 | const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); |
| 2976 | const result = try fromBigInt(arena, result_bigint.toConst()); | 3011 | const result = try fromBigInt(arena, result_bigint.toConst()); |
| 2977 | return OverflowArithmeticResult{ | 3012 | return OverflowArithmeticResult{ |
| 2978 | .overflowed = overflowed, | 3013 | .overflowed = makeBool(overflowed), |
| 2979 | .wrapped_result = result, | 3014 | .wrapped_result = result, |
| 2980 | }; | 3015 | }; |
| 2981 | } | 3016 | } |
| ... | @@ -3086,6 +3121,29 @@ pub const Value = extern union { | ... | @@ -3086,6 +3121,29 @@ pub const Value = extern union { |
| 3086 | ty: Type, | 3121 | ty: Type, |
| 3087 | arena: Allocator, | 3122 | arena: Allocator, |
| 3088 | target: Target, | 3123 | target: Target, |
| 3124 | ) !OverflowArithmeticResult { | ||
| 3125 | if (ty.zigTypeTag() == .Vector) { | ||
| 3126 | const overflowed_data = try arena.alloc(Value, ty.vectorLen()); | ||
| 3127 | const result_data = try arena.alloc(Value, ty.vectorLen()); | ||
| 3128 | for (result_data) |*scalar, i| { | ||
| 3129 | const of_math_result = try intSubWithOverflowScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); | ||
| 3130 | overflowed_data[i] = of_math_result.overflowed; | ||
| 3131 | scalar.* = of_math_result.wrapped_result; | ||
| 3132 | } | ||
| 3133 | return OverflowArithmeticResult{ | ||
| 3134 | .overflowed = try Value.Tag.aggregate.create(arena, overflowed_data), | ||
| 3135 | .wrapped_result = try Value.Tag.aggregate.create(arena, result_data), | ||
| 3136 | }; | ||
| 3137 | } | ||
| 3138 | return intSubWithOverflowScalar(lhs, rhs, ty, arena, target); | ||
| 3139 | } | ||
| 3140 | |||
| 3141 | pub fn intSubWithOverflowScalar( | ||
| 3142 | lhs: Value, | ||
| 3143 | rhs: Value, | ||
| 3144 | ty: Type, | ||
| 3145 | arena: Allocator, | ||
| 3146 | target: Target, | ||
| 3089 | ) !OverflowArithmeticResult { | 3147 | ) !OverflowArithmeticResult { |
| 3090 | const info = ty.intInfo(target); | 3148 | const info = ty.intInfo(target); |
| 3091 | 3149 | ||
| ... | @@ -3101,7 +3159,7 @@ pub const Value = extern union { | ... | @@ -3101,7 +3159,7 @@ pub const Value = extern union { |
| 3101 | const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); | 3159 | const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); |
| 3102 | const wrapped_result = try fromBigInt(arena, result_bigint.toConst()); | 3160 | const wrapped_result = try fromBigInt(arena, result_bigint.toConst()); |
| 3103 | return OverflowArithmeticResult{ | 3161 | return OverflowArithmeticResult{ |
| 3104 | .overflowed = overflowed, | 3162 | .overflowed = makeBool(overflowed), |
| 3105 | .wrapped_result = wrapped_result, | 3163 | .wrapped_result = wrapped_result, |
| 3106 | }; | 3164 | }; |
| 3107 | } | 3165 | } |
| ... | @@ -3196,6 +3254,29 @@ pub const Value = extern union { | ... | @@ -3196,6 +3254,29 @@ pub const Value = extern union { |
| 3196 | ty: Type, | 3254 | ty: Type, |
| 3197 | arena: Allocator, | 3255 | arena: Allocator, |
| 3198 | target: Target, | 3256 | target: Target, |
| 3257 | ) !OverflowArithmeticResult { | ||
| 3258 | if (ty.zigTypeTag() == .Vector) { | ||
| 3259 | const overflowed_data = try arena.alloc(Value, ty.vectorLen()); | ||
| 3260 | const result_data = try arena.alloc(Value, ty.vectorLen()); | ||
| 3261 | for (result_data) |*scalar, i| { | ||
| 3262 | const of_math_result = try intMulWithOverflowScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); | ||
| 3263 | overflowed_data[i] = of_math_result.overflowed; | ||
| 3264 | scalar.* = of_math_result.wrapped_result; | ||
| 3265 | } | ||
| 3266 | return OverflowArithmeticResult{ | ||
| 3267 | .overflowed = try Value.Tag.aggregate.create(arena, overflowed_data), | ||
| 3268 | .wrapped_result = try Value.Tag.aggregate.create(arena, result_data), | ||
| 3269 | }; | ||
| 3270 | } | ||
| 3271 | return intMulWithOverflowScalar(lhs, rhs, ty, arena, target); | ||
| 3272 | } | ||
| 3273 | |||
| 3274 | pub fn intMulWithOverflowScalar( | ||
| 3275 | lhs: Value, | ||
| 3276 | rhs: Value, | ||
| 3277 | ty: Type, | ||
| 3278 | arena: Allocator, | ||
| 3279 | target: Target, | ||
| 3199 | ) !OverflowArithmeticResult { | 3280 | ) !OverflowArithmeticResult { |
| 3200 | const info = ty.intInfo(target); | 3281 | const info = ty.intInfo(target); |
| 3201 | 3282 | ||
| ... | @@ -3220,7 +3301,7 @@ pub const Value = extern union { | ... | @@ -3220,7 +3301,7 @@ pub const Value = extern union { |
| 3220 | } | 3301 | } |
| 3221 | 3302 | ||
| 3222 | return OverflowArithmeticResult{ | 3303 | return OverflowArithmeticResult{ |
| 3223 | .overflowed = overflowed, | 3304 | .overflowed = makeBool(overflowed), |
| 3224 | .wrapped_result = try fromBigInt(arena, result_bigint.toConst()), | 3305 | .wrapped_result = try fromBigInt(arena, result_bigint.toConst()), |
| 3225 | }; | 3306 | }; |
| 3226 | } | 3307 | } |
| ... | @@ -3910,6 +3991,29 @@ pub const Value = extern union { | ... | @@ -3910,6 +3991,29 @@ pub const Value = extern union { |
| 3910 | ty: Type, | 3991 | ty: Type, |
| 3911 | allocator: Allocator, | 3992 | allocator: Allocator, |
| 3912 | target: Target, | 3993 | target: Target, |
| 3994 | ) !OverflowArithmeticResult { | ||
| 3995 | if (ty.zigTypeTag() == .Vector) { | ||
| 3996 | const overflowed_data = try allocator.alloc(Value, ty.vectorLen()); | ||
| 3997 | const result_data = try allocator.alloc(Value, ty.vectorLen()); | ||
| 3998 | for (result_data) |*scalar, i| { | ||
| 3999 | const of_math_result = try shlWithOverflowScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), allocator, target); | ||
| 4000 | overflowed_data[i] = of_math_result.overflowed; | ||
| 4001 | scalar.* = of_math_result.wrapped_result; | ||
| 4002 | } | ||
| 4003 | return OverflowArithmeticResult{ | ||
| 4004 | .overflowed = try Value.Tag.aggregate.create(allocator, overflowed_data), | ||
| 4005 | .wrapped_result = try Value.Tag.aggregate.create(allocator, result_data), | ||
| 4006 | }; | ||
| 4007 | } | ||
| 4008 | return shlWithOverflowScalar(lhs, rhs, ty, allocator, target); | ||
| 4009 | } | ||
| 4010 | |||
| 4011 | pub fn shlWithOverflowScalar( | ||
| 4012 | lhs: Value, | ||
| 4013 | rhs: Value, | ||
| 4014 | ty: Type, | ||
| 4015 | allocator: Allocator, | ||
| 4016 | target: Target, | ||
| 3913 | ) !OverflowArithmeticResult { | 4017 | ) !OverflowArithmeticResult { |
| 3914 | const info = ty.intInfo(target); | 4018 | const info = ty.intInfo(target); |
| 3915 | var lhs_space: Value.BigIntSpace = undefined; | 4019 | var lhs_space: Value.BigIntSpace = undefined; |
| ... | @@ -3930,7 +4034,7 @@ pub const Value = extern union { | ... | @@ -3930,7 +4034,7 @@ pub const Value = extern union { |
| 3930 | result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits); | 4034 | result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits); |
| 3931 | } | 4035 | } |
| 3932 | return OverflowArithmeticResult{ | 4036 | return OverflowArithmeticResult{ |
| 3933 | .overflowed = overflowed, | 4037 | .overflowed = makeBool(overflowed), |
| 3934 | .wrapped_result = try fromBigInt(allocator, result_bigint.toConst()), | 4038 | .wrapped_result = try fromBigInt(allocator, result_bigint.toConst()), |
| 3935 | }; | 4039 | }; |
| 3936 | } | 4040 | } |
test/behavior/math.zig+61-26| ... | @@ -621,24 +621,41 @@ test "128-bit multiplication" { | ... | @@ -621,24 +621,41 @@ test "128-bit multiplication" { |
| 621 | test "@addWithOverflow" { | 621 | test "@addWithOverflow" { |
| 622 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 622 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 623 | 623 | ||
| 624 | var result: u8 = undefined; | 624 | { |
| 625 | try expect(@addWithOverflow(u8, 250, 100, &result)); | 625 | var result: u8 = undefined; |
| 626 | try expect(result == 94); | 626 | try expect(@addWithOverflow(u8, 250, 100, &result)); |
| 627 | try expect(!@addWithOverflow(u8, 100, 150, &result)); | 627 | try expect(result == 94); |
| 628 | try expect(result == 250); | 628 | try expect(!@addWithOverflow(u8, 100, 150, &result)); |
| 629 | 629 | try expect(result == 250); | |
| 630 | var a: u8 = 200; | 630 | |
| 631 | var b: u8 = 99; | 631 | var a: u8 = 200; |
| 632 | try expect(@addWithOverflow(u8, a, b, &result)); | 632 | var b: u8 = 99; |
| 633 | try expect(result == 43); | 633 | try expect(@addWithOverflow(u8, a, b, &result)); |
| 634 | b = 55; | 634 | try expect(result == 43); |
| 635 | try expect(!@addWithOverflow(u8, a, b, &result)); | 635 | b = 55; |
| 636 | try expect(result == 255); | 636 | try expect(!@addWithOverflow(u8, a, b, &result)); |
| 637 | try expect(result == 255); | ||
| 638 | } | ||
| 639 | |||
| 640 | { | ||
| 641 | var a: usize = 6; | ||
| 642 | var b: usize = 6; | ||
| 643 | var res: usize = undefined; | ||
| 644 | try expect(!@addWithOverflow(usize, a, b, &res)); | ||
| 645 | try expect(res == 12); | ||
| 646 | } | ||
| 647 | |||
| 648 | { | ||
| 649 | var a: isize = -6; | ||
| 650 | var b: isize = -6; | ||
| 651 | var res: isize = undefined; | ||
| 652 | try expect(!@addWithOverflow(isize, a, b, &res)); | ||
| 653 | try expect(res == -12); | ||
| 654 | } | ||
| 637 | } | 655 | } |
| 638 | 656 | ||
| 639 | test "small int addition" { | 657 | test "small int addition" { |
| 640 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 658 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 641 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 642 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 659 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 643 | 660 | ||
| 644 | var x: u2 = 0; | 661 | var x: u2 = 0; |
| ... | @@ -886,19 +903,37 @@ test "@mulWithOverflow bitsize > 32" { | ... | @@ -886,19 +903,37 @@ test "@mulWithOverflow bitsize > 32" { |
| 886 | test "@subWithOverflow" { | 903 | test "@subWithOverflow" { |
| 887 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 904 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 888 | 905 | ||
| 889 | var result: u8 = undefined; | 906 | { |
| 890 | try expect(@subWithOverflow(u8, 1, 2, &result)); | 907 | var result: u8 = undefined; |
| 891 | try expect(result == 255); | 908 | try expect(@subWithOverflow(u8, 1, 2, &result)); |
| 892 | try expect(!@subWithOverflow(u8, 1, 1, &result)); | 909 | try expect(result == 255); |
| 893 | try expect(result == 0); | 910 | try expect(!@subWithOverflow(u8, 1, 1, &result)); |
| 911 | try expect(result == 0); | ||
| 894 | 912 | ||
| 895 | var a: u8 = 1; | 913 | var a: u8 = 1; |
| 896 | var b: u8 = 2; | 914 | var b: u8 = 2; |
| 897 | try expect(@subWithOverflow(u8, a, b, &result)); | 915 | try expect(@subWithOverflow(u8, a, b, &result)); |
| 898 | try expect(result == 255); | 916 | try expect(result == 255); |
| 899 | b = 1; | 917 | b = 1; |
| 900 | try expect(!@subWithOverflow(u8, a, b, &result)); | 918 | try expect(!@subWithOverflow(u8, a, b, &result)); |
| 901 | try expect(result == 0); | 919 | try expect(result == 0); |
| 920 | } | ||
| 921 | |||
| 922 | { | ||
| 923 | var a: usize = 6; | ||
| 924 | var b: usize = 6; | ||
| 925 | var res: usize = undefined; | ||
| 926 | try expect(!@subWithOverflow(usize, a, b, &res)); | ||
| 927 | try expect(res == 0); | ||
| 928 | } | ||
| 929 | |||
| 930 | { | ||
| 931 | var a: isize = -6; | ||
| 932 | var b: isize = -6; | ||
| 933 | var res: isize = undefined; | ||
| 934 | try expect(!@subWithOverflow(isize, a, b, &res)); | ||
| 935 | try expect(res == 0); | ||
| 936 | } | ||
| 902 | } | 937 | } |
| 903 | 938 | ||
| 904 | test "@shlWithOverflow" { | 939 | test "@shlWithOverflow" { |
test/behavior/vector.zig+120| ... | @@ -903,3 +903,123 @@ test "multiplication-assignment operator with an array operand" { | ... | @@ -903,3 +903,123 @@ test "multiplication-assignment operator with an array operand" { |
| 903 | try S.doTheTest(); | 903 | try S.doTheTest(); |
| 904 | comptime try S.doTheTest(); | 904 | comptime try S.doTheTest(); |
| 905 | } | 905 | } |
| 906 | |||
| 907 | test "@addWithOverflow" { | ||
| 908 | if (builtin.zig_backend == .stage1) { | ||
| 909 | // stage1 doesn't support vector args | ||
| 910 | return error.SkipZigTest; | ||
| 911 | } | ||
| 912 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 913 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 914 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 915 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 916 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 917 | |||
| 918 | const S = struct { | ||
| 919 | fn doTheTest() !void { | ||
| 920 | { | ||
| 921 | var result: @Vector(4, u8) = undefined; | ||
| 922 | var overflow = @addWithOverflow(@Vector(4, u8), @Vector(4, u8){ 250, 250, 250, 250 }, @Vector(4, u8){ 0, 5, 6, 10 }, &result); | ||
| 923 | var expected: @Vector(4, bool) = .{ false, false, true, true }; | ||
| 924 | try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); | ||
| 925 | } | ||
| 926 | { | ||
| 927 | var result: @Vector(4, i8) = undefined; | ||
| 928 | var overflow = @addWithOverflow(@Vector(4, i8), @Vector(4, i8){ -125, -125, 125, 125 }, @Vector(4, i8){ -3, -4, 2, 3 }, &result); | ||
| 929 | var expected: @Vector(4, bool) = .{ false, true, false, true }; | ||
| 930 | try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); | ||
| 931 | } | ||
| 932 | { | ||
| 933 | var result: @Vector(4, u1) = undefined; | ||
| 934 | var overflow = @addWithOverflow(@Vector(4, u1), @Vector(4, u1){ 0, 0, 1, 1 }, @Vector(4, u1){ 0, 1, 0, 1 }, &result); | ||
| 935 | var expected: @Vector(4, bool) = .{ false, false, false, true }; | ||
| 936 | try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); | ||
| 937 | } | ||
| 938 | { | ||
| 939 | var result: @Vector(4, u0) = undefined; | ||
| 940 | var overflow = @addWithOverflow(@Vector(4, u0), @Vector(4, u0){ 0, 0, 0, 0 }, @Vector(4, u0){ 0, 0, 0, 0 }, &result); | ||
| 941 | var expected: @Vector(4, bool) = .{ false, false, false, false }; | ||
| 942 | try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); | ||
| 943 | } | ||
| 944 | } | ||
| 945 | }; | ||
| 946 | try S.doTheTest(); | ||
| 947 | comptime try S.doTheTest(); | ||
| 948 | } | ||
| 949 | |||
| 950 | test "@subWithOverflow" { | ||
| 951 | if (builtin.zig_backend == .stage1) { | ||
| 952 | // stage1 doesn't support vector args | ||
| 953 | return error.SkipZigTest; | ||
| 954 | } | ||
| 955 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 956 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 957 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 958 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 959 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 960 | |||
| 961 | const S = struct { | ||
| 962 | fn doTheTest() !void { | ||
| 963 | { | ||
| 964 | var result: @Vector(2, u8) = undefined; | ||
| 965 | var overflow = @subWithOverflow(@Vector(2, u8), @Vector(2, u8){ 5, 5 }, @Vector(2, u8){ 5, 6 }, &result); | ||
| 966 | var expected: @Vector(2, bool) = .{ false, true }; | ||
| 967 | try expect(mem.eql(bool, &@as([2]bool, overflow), &@as([2]bool, expected))); | ||
| 968 | } | ||
| 969 | { | ||
| 970 | var result: @Vector(4, i8) = undefined; | ||
| 971 | var overflow = @subWithOverflow(@Vector(4, i8), @Vector(4, i8){ -120, -120, 120, 120 }, @Vector(4, i8){ 8, 9, -7, -8 }, &result); | ||
| 972 | var expected: @Vector(4, bool) = .{ false, true, false, true }; | ||
| 973 | try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); | ||
| 974 | } | ||
| 975 | } | ||
| 976 | }; | ||
| 977 | try S.doTheTest(); | ||
| 978 | comptime try S.doTheTest(); | ||
| 979 | } | ||
| 980 | |||
| 981 | test "@mulWithOverflow" { | ||
| 982 | if (builtin.zig_backend == .stage1) { | ||
| 983 | // stage1 doesn't support vector args | ||
| 984 | return error.SkipZigTest; | ||
| 985 | } | ||
| 986 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 987 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 988 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 989 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 990 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 991 | |||
| 992 | const S = struct { | ||
| 993 | fn doTheTest() !void { | ||
| 994 | var result: @Vector(4, u8) = undefined; | ||
| 995 | var overflow = @mulWithOverflow(@Vector(4, u8), @Vector(4, u8){ 10, 10, 10, 10 }, @Vector(4, u8){ 25, 26, 0, 30 }, &result); | ||
| 996 | var expected: @Vector(4, bool) = .{ false, true, false, true }; | ||
| 997 | try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); | ||
| 998 | } | ||
| 999 | }; | ||
| 1000 | try S.doTheTest(); | ||
| 1001 | comptime try S.doTheTest(); | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | test "@shlWithOverflow" { | ||
| 1005 | if (builtin.zig_backend == .stage1) { | ||
| 1006 | // stage1 doesn't support vector args | ||
| 1007 | return error.SkipZigTest; | ||
| 1008 | } | ||
| 1009 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1010 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1011 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1012 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 1013 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 1014 | |||
| 1015 | const S = struct { | ||
| 1016 | fn doTheTest() !void { | ||
| 1017 | var result: @Vector(4, u8) = undefined; | ||
| 1018 | var overflow = @shlWithOverflow(@Vector(4, u8), @Vector(4, u8){ 0, 1, 8, 255 }, @Vector(4, u3){ 7, 7, 7, 7 }, &result); | ||
| 1019 | var expected: @Vector(4, bool) = .{ false, false, true, true }; | ||
| 1020 | try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); | ||
| 1021 | } | ||
| 1022 | }; | ||
| 1023 | try S.doTheTest(); | ||
| 1024 | comptime try S.doTheTest(); | ||
| 1025 | } |
test/cases/recursive_fibonacci.zig+1-1| ... | @@ -20,5 +20,5 @@ fn assert(ok: bool) void { | ... | @@ -20,5 +20,5 @@ fn assert(ok: bool) void { |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | // run | 22 | // run |
| 23 | // target=arm-linux,x86_64-linux,x86_64-macos,wasm32-wasi | 23 | // target=x86_64-linux,x86_64-macos,wasm32-wasi |
| 24 | // | 24 | // |