authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-16 20:40:57-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-05-16 20:40:57-04:00
log5888446c03b1f77a031f5a8093488a6a2f6decb6
treecb084cae72edc4c581c0ffa37cdc1130c6c2a8fc
parent7a4758ed7868096c12fec8dacba0bfd4a37fdd13
parentf33b3fc3eae54b9d1159fc5a7a69a4b0e4aceca6
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11316 from wsengir/stage2-overflow-safety

stage2: vectorized overflow arithmetic, integer overflow safety, left-shift overflow safety

14 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
767767
768768 // Until self-hosted catches up with stage1 language features, we have a simpler
769769 // default panic function:
770 if ((builtin.zig_backend == .stage2_llvm and builtin.link_libc) or
771 builtin.zig_backend == .stage2_c or
770 if (builtin.zig_backend == .stage2_c or
772771 builtin.zig_backend == .stage2_wasm or
773772 builtin.zig_backend == .stage2_arm or
774773 builtin.zig_backend == .stage2_aarch64 or
src/Sema.zig+172-59
......@@ -1574,6 +1574,12 @@ fn failWithErrorSetCodeMissing(
15741574 });
15751575}
15761576
1577fn 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
15771583/// We don't return a pointer to the new error note because the pointer
15781584/// becomes invalid when you add another one.
15791585fn errNote(
......@@ -8820,8 +8826,6 @@ fn zirShl(
88208826 return sema.addConstant(lhs_ty, val);
88218827 } else lhs_src;
88228828
8823 // TODO: insert runtime safety check for shl_exact
8824
88258829 const new_rhs = if (air_tag == .shl_sat) rhs: {
88268830 // Limit the RHS type for saturating shl to be an integer as small as the LHS.
88278831 if (rhs_is_comptime_int or
......@@ -8839,6 +8843,41 @@ fn zirShl(
88398843 } else rhs;
88408844
88418845 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 }
88428881 return block.addBinOp(air_tag, lhs, new_rhs);
88438882}
88448883
......@@ -9417,32 +9456,29 @@ fn zirOverflowArithmetic(
94179456 const ptr = sema.resolveInst(extra.ptr);
94189457
94199458 const lhs_ty = sema.typeOf(lhs);
9459 const rhs_ty = sema.typeOf(rhs);
94209460 const mod = sema.mod;
94219461 const target = mod.getTarget();
94229462
94239463 // 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);
94249465 const dest_ty = lhs_ty;
9425 if (dest_ty.zigTypeTag() != .Int) {
9426 return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty.fmt(mod)});
9466 if (dest_ty.scalarType().zigTypeTag() != .Int) {
9467 return sema.fail(block, src, "expected vector of integers or integer type, found '{}'", .{dest_ty.fmt(mod)});
94279468 }
94289469
94299470 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
94309471 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
94319472
9432 const types = try sema.arena.alloc(Type, 2);
9433 const values = try sema.arena.alloc(Value, 2);
9434 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
9435 .types = types,
9436 .values = values,
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);
9473 const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty);
9474 const ov_ty = tuple_ty.tupleFields().types[1];
9475 // TODO: Remove and use `ov_ty` instead.
9476 // This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`.
9477 const overflowed_ty = if (dest_ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, dest_ty.vectorLen(), Type.@"bool") else Type.@"bool";
94439478
94449479 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,
94469482 wrapped: Air.Inst.Ref,
94479483 } = result: {
94489484 switch (zir_tag) {
......@@ -9452,23 +9488,24 @@ fn zirOverflowArithmetic(
94529488 // Otherwise, if either of the argument is undefined, undefined is returned.
94539489 if (maybe_lhs_val) |lhs_val| {
94549490 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 };
94569492 }
94579493 }
94589494 if (maybe_rhs_val) |rhs_val| {
94599495 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 };
94619497 }
94629498 }
94639499 if (maybe_lhs_val) |lhs_val| {
94649500 if (maybe_rhs_val) |rhs_val| {
94659501 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) };
94679503 }
94689504
94699505 const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target);
9470 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
9471 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
9506 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9507 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9508 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
94729509 }
94739510 }
94749511 },
......@@ -9477,17 +9514,18 @@ fn zirOverflowArithmetic(
94779514 // Otherwise, if either result is undefined, both results are undefined.
94789515 if (maybe_rhs_val) |rhs_val| {
94799516 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) };
94819518 } 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 };
94839520 } else if (maybe_lhs_val) |lhs_val| {
94849521 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) };
94869523 }
94879524
94889525 const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target);
9489 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
9490 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
9526 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9527 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9528 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
94919529 }
94929530 }
94939531 },
......@@ -9498,9 +9536,9 @@ fn zirOverflowArithmetic(
94989536 if (maybe_lhs_val) |lhs_val| {
94999537 if (!lhs_val.isUndef()) {
95009538 if (lhs_val.compareWithZero(.eq)) {
9501 break :result .{ .overflowed = .no, .wrapped = lhs };
9539 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
95029540 } 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 };
95049542 }
95059543 }
95069544 }
......@@ -9508,9 +9546,9 @@ fn zirOverflowArithmetic(
95089546 if (maybe_rhs_val) |rhs_val| {
95099547 if (!rhs_val.isUndef()) {
95109548 if (rhs_val.compareWithZero(.eq)) {
9511 break :result .{ .overflowed = .no, .wrapped = rhs };
9549 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
95129550 } 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 };
95149552 }
95159553 }
95169554 }
......@@ -9518,12 +9556,13 @@ fn zirOverflowArithmetic(
95189556 if (maybe_lhs_val) |lhs_val| {
95199557 if (maybe_rhs_val) |rhs_val| {
95209558 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) };
95229560 }
95239561
95249562 const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target);
9525 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
9526 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
9563 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9564 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9565 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
95279566 }
95289567 }
95299568 },
......@@ -9533,23 +9572,24 @@ fn zirOverflowArithmetic(
95339572 // Oterhwise if either of the arguments is undefined, both results are undefined.
95349573 if (maybe_lhs_val) |lhs_val| {
95359574 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 };
95379576 }
95389577 }
95399578 if (maybe_rhs_val) |rhs_val| {
95409579 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 };
95429581 }
95439582 }
95449583 if (maybe_lhs_val) |lhs_val| {
95459584 if (maybe_rhs_val) |rhs_val| {
95469585 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) };
95489587 }
95499588
95509589 const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target);
9551 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
9552 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
9590 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9591 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9592 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
95539593 }
95549594 }
95559595 },
......@@ -9577,21 +9617,40 @@ fn zirOverflowArithmetic(
95779617 } },
95789618 });
95799619
9580 const wrapped = try block.addStructFieldVal(tuple, 0, dest_ty);
9620 const wrapped = try sema.tupleFieldValByIndex(block, src, tuple, 0, tuple_ty);
95819621 try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store);
95829622
9583 const overflow_bit = try block.addStructFieldVal(tuple, 1, Type.initTag(.u1));
9584 const zero_u1 = try sema.addConstant(Type.initTag(.u1), Value.zero);
9585 return try block.addBinOp(.cmp_neq, overflow_bit, zero_u1);
9623 const overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty);
9624 const zero_ov_val = if (dest_ty.zigTypeTag() == .Vector) try Value.Tag.repeated.create(sema.arena, Value.zero) else Value.zero;
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;
95869632 };
95879633
95889634 try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store);
9635 return result.overflowed;
9636}
95899637
9590 return switch (result.overflowed) {
9591 .yes => Air.Inst.Ref.bool_true,
9592 .no => Air.Inst.Ref.bool_false,
9593 .undef => try sema.addConstUndef(Type.bool),
9594 };
9638fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type {
9639 const ov_ty = if (ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, ty.vectorLen(), Type.@"u1") else Type.@"u1";
9640
9641 const types = try sema.arena.alloc(Type, 2);
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;
95959654}
95969655
95979656fn analyzeArithmetic(
......@@ -9691,10 +9750,11 @@ fn analyzeArithmetic(
96919750 }
96929751 if (maybe_rhs_val) |rhs_val| {
96939752 if (is_int) {
9694 return sema.addConstant(
9695 resolved_type,
9696 try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target),
9697 );
9753 const sum = try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target);
9754 if (!sum.intFitsInType(resolved_type, target)) {
9755 return sema.failWithIntegerOverflow(block, src, resolved_type, sum);
9756 }
9757 return sema.addConstant(resolved_type, sum);
96989758 } else {
96999759 return sema.addConstant(
97009760 resolved_type,
......@@ -9784,10 +9844,11 @@ fn analyzeArithmetic(
97849844 }
97859845 if (maybe_rhs_val) |rhs_val| {
97869846 if (is_int) {
9787 return sema.addConstant(
9788 resolved_type,
9789 try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target),
9790 );
9847 const diff = try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target);
9848 if (!diff.intFitsInType(resolved_type, target)) {
9849 return sema.failWithIntegerOverflow(block, src, resolved_type, diff);
9850 }
9851 return sema.addConstant(resolved_type, diff);
97919852 } else {
97929853 return sema.addConstant(
97939854 resolved_type,
......@@ -10157,10 +10218,11 @@ fn analyzeArithmetic(
1015710218 }
1015810219 }
1015910220 if (is_int) {
10160 return sema.addConstant(
10161 resolved_type,
10162 try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target),
10163 );
10221 const product = try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target);
10222 if (!product.intFitsInType(resolved_type, target)) {
10223 return sema.failWithIntegerOverflow(block, src, resolved_type, product);
10224 }
10225 return sema.addConstant(resolved_type, product);
1016410226 } else {
1016510227 return sema.addConstant(
1016610228 resolved_type,
......@@ -10448,6 +10510,45 @@ fn analyzeArithmetic(
1044810510 };
1044910511
1045010512 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 }
1045110552 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
1045210553}
1045310554
......@@ -16682,6 +16783,8 @@ pub const PanicId = enum {
1668216783 invalid_error_code,
1668316784 index_out_of_bounds,
1668416785 cast_truncated_data,
16786 integer_overflow,
16787 shl_overflow,
1668516788};
1668616789
1668716790fn addSafetyCheck(
......@@ -16805,6 +16908,8 @@ fn safetyPanic(
1680516908 .invalid_error_code => "invalid error code",
1680616909 .index_out_of_bounds => "attempt to index out of bounds",
1680716910 .cast_truncated_data => "integer cast truncated bits",
16911 .integer_overflow => "integer overflow",
16912 .shl_overflow => "left shift overflowed bits",
1680816913 };
1680916914
1681016915 const msg_inst = msg_inst: {
......@@ -23093,6 +23198,14 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
2309323198 return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int));
2309423199}
2309523200
23201fn 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
2309623209fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
2309723210 return sema.addConstant(ty, Value.undef);
2309823211}
src/arch/aarch64/CodeGen.zig+4
......@@ -1901,6 +1901,10 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
19011901 }
19021902 };
19031903
1904 if (tag == .sub_with_overflow) {
1905 break :result MCValue{ .register_v_flag = dest.register };
1906 }
1907
19041908 switch (int_info.signedness) {
19051909 .unsigned => break :result MCValue{ .register_c_flag = dest.register },
19061910 .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 {
14551455 }
14561456 };
14571457
1458 if (tag == .sub_with_overflow) {
1459 break :result MCValue{ .register_v_flag = dest.register };
1460 }
1461
14581462 switch (int_info.signedness) {
14591463 .unsigned => break :result MCValue{ .register_c_flag = dest.register },
14601464 .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 {
14501450 .min => self.airMaxMin(inst, .min),
14511451 .mul_add => self.airMulAdd(inst),
14521452
1453 .add_with_overflow => self.airBinOpOverflow(inst, .add),
1454 .sub_with_overflow => self.airBinOpOverflow(inst, .sub),
1455 .shl_with_overflow => self.airBinOpOverflow(inst, .shl),
1453 .add_with_overflow => self.airAddSubWithOverflow(inst, .add),
1454 .sub_with_overflow => self.airAddSubWithOverflow(inst, .sub),
1455 .shl_with_overflow => self.airShlWithOverflow(inst),
14561456 .mul_with_overflow => self.airMulWithOverflow(inst),
14571457
14581458 .clz => self.airClz(inst),
......@@ -3941,25 +3941,22 @@ fn airPtrSliceFieldPtr(self: *Self, inst: Air.Inst.Index, offset: u32) InnerErro
39413941 return self.buildPointerOffset(slice_ptr, offset, .new);
39423942}
39433943
3944fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
3945 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3946
3944fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
3945 assert(op == .add or op == .sub);
39473946 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
39483947 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
3949 const lhs = try self.resolveInst(extra.lhs);
3950 const rhs = try self.resolveInst(extra.rhs);
3948 const lhs_op = try self.resolveInst(extra.lhs);
3949 const rhs_op = try self.resolveInst(extra.rhs);
39513950 const lhs_ty = self.air.typeOf(extra.lhs);
39523951
39533952 if (lhs_ty.zigTypeTag() == .Vector) {
39543953 return self.fail("TODO: Implement overflow arithmetic for vectors", .{});
39553954 }
39563955
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));
39603956 const int_info = lhs_ty.intInfo(self.target);
3957 const is_signed = int_info.signedness == .signed;
39613958 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});
39633960 };
39643961
39653962 const zero = switch (wasm_bits) {
......@@ -3967,83 +3964,93 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue
39673964 64 => WValue{ .imm64 = 0 },
39683965 else => unreachable,
39693966 };
3970 const int_max = (@as(u65, 1) << @intCast(u7, int_info.bits - @boolToInt(int_info.signedness == .signed))) - 1;
3971 const int_max_wvalue = switch (wasm_bits) {
3972 32 => WValue{ .imm32 = @intCast(u32, int_max) },
3973 64 => WValue{ .imm64 = @intCast(u64, int_max) },
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) },
3967 const shift_amt = wasm_bits - int_info.bits;
3968 const shift_val = switch (wasm_bits) {
3969 32 => WValue{ .imm32 = shift_amt },
3970 64 => WValue{ .imm64 = shift_amt },
39833971 else => unreachable,
39843972 };
39853973
3986 if (int_info.signedness == .unsigned and op == .add) {
3987 const diff = try self.binOp(int_max_wvalue, lhs, lhs_ty, .sub);
3988 const cmp_res = try self.cmp(rhs, diff, lhs_ty, .gt);
3989 try self.emitWValue(cmp_res);
3990 try self.addLabel(.local_set, overflow_bit.local);
3991 } else if (int_info.signedness == .unsigned and op == .sub) {
3992 const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt);
3993 try self.emitWValue(cmp_res);
3994 try self.addLabel(.local_set, overflow_bit.local);
3995 } else if (int_info.signedness == .signed and op != .shl) {
3996 // for overflow, we first check if lhs is > 0 (or lhs < 0 in case of subtraction). If not, we will not overflow.
3997 // We first create an outer block, where we handle overflow.
3998 // Then we create an inner block, where underflow is handled.
3999 try self.startBlock(.block, wasm.block_empty);
4000 try self.startBlock(.block, wasm.block_empty);
4001 {
4002 try self.emitWValue(lhs);
4003 const cmp_result = try self.cmp(lhs, zero, lhs_ty, .lt);
4004 try self.emitWValue(cmp_result);
3974 // for signed integers, we first apply signed shifts by the difference in bits
3975 // to get the signed value, as we store it internally as 2's complement.
3976 const lhs = if (wasm_bits != int_info.bits and is_signed) blk: {
3977 const shl = try self.binOp(lhs_op, shift_val, lhs_ty, .shl);
3978 break :blk try self.binOp(shl, shift_val, lhs_ty, .shr);
3979 } else lhs_op;
3980 const rhs = if (wasm_bits != int_info.bits and is_signed) blk: {
3981 const shl = try self.binOp(rhs_op, shift_val, lhs_ty, .shl);
3982 break :blk try self.binOp(shl, shift_val, lhs_ty, .shr);
3983 } else rhs_op;
3984
3985 const bin_op = try self.binOp(lhs, rhs, lhs_ty, op);
3986 const result = if (wasm_bits != int_info.bits) blk: {
3987 break :blk try self.wrapOperand(bin_op, lhs_ty);
3988 } else bin_op;
3989
3990 const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt;
3991 const overflow_bit: WValue = if (is_signed) blk: {
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
40053996 }
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);
40074004
4008 // handle overflow
4009 {
4010 const diff = try self.binOp(int_max_wvalue, lhs, lhs_ty, .sub);
4011 const cmp_res = try self.cmp(rhs, diff, lhs_ty, if (op == .add) .gt else .lt);
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();
4005 const result_ptr = try self.allocStack(self.air.typeOfIndex(inst));
4006 try self.store(result_ptr, result, lhs_ty, 0);
4007 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
4008 try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset);
40174009
4018 // handle underflow
4019 {
4020 const diff = try self.binOp(int_min_wvalue, lhs, lhs_ty, .sub);
4021 const cmp_res = try self.cmp(rhs, diff, lhs_ty, if (op == .add) .lt else .gt);
4022 try self.emitWValue(cmp_res);
4023 try self.addLabel(.local_set, overflow_bit.local);
4024 }
4025 try self.endBlock();
4010 return result_ptr;
4011}
4012
4013fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
4014 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4015 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
4016 const lhs = try self.resolveInst(extra.lhs);
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", .{});
40264022 }
40274023
4028 const bin_op = if (op == .shl) blk: {
4029 const tmp_val = try self.binOp(lhs, rhs, lhs_ty, op);
4030 const cmp_res = try self.cmp(tmp_val, int_max_wvalue, lhs_ty, .gt);
4031 try self.emitWValue(cmp_res);
4032 try self.addLabel(.local_set, overflow_bit.local);
4024 const int_info = lhs_ty.intInfo(self.target);
4025 const is_signed = int_info.signedness == .signed;
4026 const wasm_bits = toWasmBits(int_info.bits) orelse {
4027 return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
4028 };
40334029
4034 try self.emitWValue(tmp_val);
4035 try self.emitWValue(int_max_wvalue);
4036 switch (wasm_bits) {
4037 32 => try self.addTag(.i32_and),
4038 64 => try self.addTag(.i64_and),
4030 const shl = try self.binOp(lhs, rhs, lhs_ty, .shl);
4031 const result = if (wasm_bits != int_info.bits) blk: {
4032 break :blk try self.wrapOperand(shl, lhs_ty);
4033 } else shl;
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 },
40394040 else => unreachable,
4040 }
4041 try self.addLabel(.local_set, tmp_val.local);
4042 break :blk tmp_val;
4043 } else try self.wrapBinOp(lhs, rhs, lhs_ty, op);
4041 };
4042
4043 const secondary_shl = try self.binOp(shl, shift_val, lhs_ty, .shl);
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 };
40444051
40454052 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);
40474054 const offset = @intCast(u32, lhs_ty.abiSize(self.target));
40484055 try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset);
40494056
src/arch/x86_64/Emit.zig+1-1
......@@ -1896,7 +1896,7 @@ fn lowerToMrEnc(
18961896 const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8).?;
18971897 switch (reg_or_mem) {
18981898 .register => |dst_reg| {
1899 const encoder = try Encoder.init(code, 3);
1899 const encoder = try Encoder.init(code, 4);
19001900 if (dst_reg.size() == 16) {
19011901 encoder.prefix16BitMode();
19021902 }
src/codegen/c.zig+118-82
......@@ -1766,10 +1766,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
17661766
17671767 .mul_add => try airMulAdd(f, inst),
17681768
1769 .add_with_overflow => try airAddWithOverflow(f, inst),
1770 .sub_with_overflow => try airSubWithOverflow(f, inst),
1771 .mul_with_overflow => try airMulWithOverflow(f, inst),
1772 .shl_with_overflow => try airShlWithOverflow(f, inst),
1769 .add_with_overflow => try airOverflow(f, inst, "addo_"),
1770 .sub_with_overflow => try airOverflow(f, inst, "subo_"),
1771 .mul_with_overflow => try airOverflow(f, inst, "mulo_"),
1772 .shl_with_overflow => try airOverflow(f, inst, "shlo_"),
17731773
17741774 .min => try airMinMax(f, inst, "<"),
17751775 .max => try airMinMax(f, inst, ">"),
......@@ -2295,7 +2295,8 @@ fn airWrapOp(
22952295
22962296 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
22972297 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);
22992300 const bits = int_info.bits;
23002301
23012302 // if it's an unsigned int with non-arbitrary bit size then we can just add
......@@ -2313,47 +2314,8 @@ fn airWrapOp(
23132314 return f.fail("TODO: C backend: airWrapOp for large integers", .{});
23142315 }
23152316
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
23342317 var max_buf: [80]u8 = undefined;
2335 const max = switch (inst_ty.tag()) {
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 };
2318 const max = intMax(inst_ty, target, &max_buf);
23572319
23582320 const lhs = try f.resolveInst(bin_op.lhs);
23592321 const rhs = try f.resolveInst(bin_op.rhs);
......@@ -2369,10 +2331,7 @@ fn airWrapOp(
23692331 .c_long => try w.writeAll("long"),
23702332 .c_longlong => try w.writeAll("longlong"),
23712333 else => {
2372 const prefix_byte: u8 = switch (int_info.signedness) {
2373 .signed => 'i',
2374 .unsigned => 'u',
2375 };
2334 const prefix_byte: u8 = signAbbrev(int_info.signedness);
23762335 for ([_]u8{ 8, 16, 32, 64 }) |nbits| {
23772336 if (bits <= nbits) {
23782337 try w.print("{c}{d}", .{ prefix_byte, nbits });
......@@ -2390,6 +2349,9 @@ fn airWrapOp(
23902349 try f.writeCValue(w, rhs);
23912350
23922351 if (int_info.signedness == .signed) {
2352 var min_buf: [80]u8 = undefined;
2353 const min = intMin(inst_ty, target, &min_buf);
2354
23932355 try w.print(", {s}", .{min});
23942356 }
23952357
......@@ -2475,10 +2437,7 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue {
24752437 .c_long => try w.writeAll("long"),
24762438 .c_longlong => try w.writeAll("longlong"),
24772439 else => {
2478 const prefix_byte: u8 = switch (int_info.signedness) {
2479 .signed => 'i',
2480 .unsigned => 'u',
2481 };
2440 const prefix_byte: u8 = signAbbrev(int_info.signedness);
24822441 for ([_]u8{ 8, 16, 32, 64 }) |nbits| {
24832442 if (bits <= nbits) {
24842443 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 {
25052464 return ret;
25062465}
25072466
2508fn airAddWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
2509 _ = f;
2510 _ = inst;
2511 return f.fail("TODO add with overflow", .{});
2512}
2467fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CValue {
2468 if (f.liveness.isUnused(inst))
2469 return CValue.none;
25132470
2514fn airSubWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
2515 _ = f;
2516 _ = inst;
2517 return f.fail("TODO sub with overflow", .{});
2518}
2471 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
2472 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
25192473
2520fn airMulWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
2521 _ = f;
2522 _ = inst;
2523 return f.fail("TODO mul with overflow", .{});
2524}
2474 const lhs = try f.resolveInst(bin_op.lhs);
2475 const rhs = try f.resolveInst(bin_op.rhs);
25252476
2526fn airShlWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
2527 _ = f;
2528 _ = inst;
2529 return f.fail("TODO shl with overflow", .{});
2477 const inst_ty = f.air.typeOfIndex(inst);
2478 const scalar_ty = f.air.typeOf(bin_op.lhs).scalarType();
2479 const target = f.object.dg.module.getTarget();
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;
25302524}
25312525
25322526fn 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
35713565 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
35723566
35733567 try writer.print(" = zig_{s}_", .{fn_name});
3574 const prefix_byte: u8 = switch (int_info.signedness) {
3575 .signed => 'i',
3576 .unsigned => 'u',
3577 };
3578 try writer.print("{c}{d}(", .{ prefix_byte, c_bits });
3568 try writer.print("{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits });
35793569 try f.writeCValue(writer, try f.resolveInst(operand));
35803570 try writer.print(", {d});\n", .{int_info.bits});
35813571 return local;
......@@ -3596,11 +3586,7 @@ fn airBinOpBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u
35963586 const int_info = lhs_ty.intInfo(target);
35973587 const c_bits = toCIntBits(int_info.bits) orelse
35983588 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
3599 const prefix_byte: u8 = switch (int_info.signedness) {
3600 .signed => 'i',
3601 .unsigned => 'u',
3602 };
3603 try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, prefix_byte, c_bits });
3589 try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, signAbbrev(int_info.signedness), c_bits });
36043590 } else if (lhs_ty.isRuntimeFloat()) {
36053591 const c_bits = lhs_ty.floatBits(target);
36063592 try writer.print(" = zig_{s}_f{d}", .{ fn_name, c_bits });
......@@ -4085,3 +4071,53 @@ fn toCIntBits(zig_bits: u32) ?u32 {
40854071 }
40864072 return null;
40874073}
4074
4075fn signAbbrev(signedness: std.builtin.Signedness) u8 {
4076 return switch (signedness) {
4077 .signed => 'i',
4078 .unsigned => 'u',
4079 };
4080}
4081
4082fn 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
4104fn 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 {
56045604 const rhs = try self.resolveInst(extra.rhs);
56055605
56065606 const lhs_ty = self.air.typeOf(extra.lhs);
5607 const scalar_ty = lhs_ty.scalarType();
5608 const dest_ty = self.air.typeOfIndex(inst);
56075609
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;
56095611
56105612 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();
56115616
56125617 const llvm_fn = self.getIntrinsic(intrinsic_name, &.{llvm_lhs_ty});
56135618 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).?, "");
56155626 }
56165627
56175628 fn buildElementwiseCall(
......@@ -5898,26 +5909,30 @@ pub const FuncGen = struct {
58985909
58995910 const lhs_ty = self.air.typeOf(extra.lhs);
59005911 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
59015915 const dest_ty = self.air.typeOfIndex(inst);
59025916 const llvm_dest_ty = try self.dg.llvmType(dest_ty);
59035917
59045918 const tg = self.dg.module.getTarget();
59055919
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))
59075921 self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "")
59085922 else
59095923 rhs;
59105924
59115925 const result = self.builder.buildShl(lhs, casted_rhs, "");
5912 const reconstructed = if (lhs_ty.isSignedInt())
5926 const reconstructed = if (lhs_scalar_ty.isSignedInt())
59135927 self.builder.buildAShr(result, casted_rhs, "")
59145928 else
59155929 self.builder.buildLShr(result, casted_rhs, "");
59165930
59175931 const overflow_bit = self.builder.buildICmp(.NE, lhs, reconstructed, "");
59185932
5919 const partial = self.builder.buildInsertValue(llvm_dest_ty.getUndef(), result, 0, "");
5920 return self.builder.buildInsertValue(partial, overflow_bit, 1, "");
5933 var ty_buf: Type.Payload.Pointer = undefined;
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).?, "");
59215936 }
59225937
59235938 fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
src/link/C/zig.h+769
......@@ -165,8 +165,24 @@
165165
166166#define int128_t __int128
167167#define uint128_t unsigned __int128
168#define UINT128_MAX ((uint128_t)(0xffffffffffffffffull) | 0xffffffffffffffffull)
168169ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);
169170ZIG_EXTERN_C void *memset (void *, int, size_t);
171ZIG_EXTERN_C int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow);
172ZIG_EXTERN_C int128_t __addoti4(int128_t lhs, int128_t rhs, int *overflow);
173ZIG_EXTERN_C uint64_t __uaddodi4(uint64_t lhs, uint64_t rhs, int *overflow);
174ZIG_EXTERN_C uint128_t __uaddoti4(uint128_t lhs, uint128_t rhs, int *overflow);
175ZIG_EXTERN_C int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow);
176ZIG_EXTERN_C int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow);
177ZIG_EXTERN_C int128_t __suboti4(int128_t lhs, int128_t rhs, int *overflow);
178ZIG_EXTERN_C uint32_t __usubosi4(uint32_t lhs, uint32_t rhs, int *overflow);
179ZIG_EXTERN_C uint64_t __usubodi4(uint64_t lhs, uint64_t rhs, int *overflow);
180ZIG_EXTERN_C uint128_t __usuboti4(uint128_t lhs, uint128_t rhs, int *overflow);
181ZIG_EXTERN_C int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow);
182ZIG_EXTERN_C int128_t __muloti4(int128_t lhs, int128_t rhs, int *overflow);
183ZIG_EXTERN_C uint64_t __umulodi4(uint64_t lhs, uint64_t rhs, int *overflow);
184ZIG_EXTERN_C uint128_t __umuloti4(uint128_t lhs, uint128_t rhs, int *overflow);
185
170186
171187static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {
172188 uint8_t thresh = max - rhs;
......@@ -396,6 +412,689 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon
396412 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));
397413}
398414
415static 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
442static 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
469static 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
496static 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
521static 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
546static 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
569static 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
592static 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
615static 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
635static 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
645static 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
672static 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
699static 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
726static 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
751static 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
776static 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
792static 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
808static 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
830static 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
853static 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
868static 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
895static 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
922static 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
949static 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
974static 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
999static 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
1022static 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
1045static 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
1068static 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
1088static 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
3991098static inline float zig_bitcast_f32_u32(uint32_t arg) {
4001099 float dest;
4011100 memcpy(&dest, &arg, sizeof dest);
......@@ -608,6 +1307,76 @@ static inline int zig_popcount_u128(uint128_t value, uint8_t zig_type_bit_width)
6081307
6091308#define zig_popcount_i128 zig_popcount_u128
6101309
1310static 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
1317static 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
1324static 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
1331static 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
1338static 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
1345static 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
1352static 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
1359static 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
1366static 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
1373static 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
6111380#define zig_sign_extend(T) \
6121381 static inline T zig_sign_extend_##T(T value, uint8_t zig_type_bit_width) { \
6131382 const T m = (T)1 << (T)(zig_type_bit_width - 1); \
src/type.zig+1
......@@ -5999,6 +5999,7 @@ pub const Type = extern union {
59995999 };
60006000 };
60016001
6002 pub const @"u1" = initTag(.u1);
60026003 pub const @"u8" = initTag(.u8);
60036004 pub const @"u16" = initTag(.u16);
60046005 pub const @"u32" = initTag(.u32);
src/value.zig+110-6
......@@ -1671,6 +1671,7 @@ pub const Value = extern union {
16711671 }
16721672
16731673 /// 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.
16741675 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
16751676 switch (self.tag()) {
16761677 .zero,
......@@ -1767,6 +1768,16 @@ pub const Value = extern union {
17671768 else => unreachable,
17681769 },
17691770
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
17701781 else => unreachable,
17711782 }
17721783 }
......@@ -2015,7 +2026,7 @@ pub const Value = extern union {
20152026 const result_data = try allocator.alloc(Value, ty.vectorLen());
20162027 for (result_data) |*scalar, i| {
20172028 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);
20192030 }
20202031 return Value.Tag.aggregate.create(allocator, result_data);
20212032 }
......@@ -2950,7 +2961,8 @@ pub const Value = extern union {
29502961 }
29512962
29522963 pub const OverflowArithmeticResult = struct {
2953 overflowed: bool,
2964 /// TODO: Rename to `overflow_bit` and make of type `u1`.
2965 overflowed: Value,
29542966 wrapped_result: Value,
29552967 };
29562968
......@@ -2960,6 +2972,29 @@ pub const Value = extern union {
29602972 ty: Type,
29612973 arena: Allocator,
29622974 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,
29632998 ) !OverflowArithmeticResult {
29642999 const info = ty.intInfo(target);
29653000
......@@ -2975,7 +3010,7 @@ pub const Value = extern union {
29753010 const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
29763011 const result = try fromBigInt(arena, result_bigint.toConst());
29773012 return OverflowArithmeticResult{
2978 .overflowed = overflowed,
3013 .overflowed = makeBool(overflowed),
29793014 .wrapped_result = result,
29803015 };
29813016 }
......@@ -3086,6 +3121,29 @@ pub const Value = extern union {
30863121 ty: Type,
30873122 arena: Allocator,
30883123 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,
30893147 ) !OverflowArithmeticResult {
30903148 const info = ty.intInfo(target);
30913149
......@@ -3101,7 +3159,7 @@ pub const Value = extern union {
31013159 const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
31023160 const wrapped_result = try fromBigInt(arena, result_bigint.toConst());
31033161 return OverflowArithmeticResult{
3104 .overflowed = overflowed,
3162 .overflowed = makeBool(overflowed),
31053163 .wrapped_result = wrapped_result,
31063164 };
31073165 }
......@@ -3196,6 +3254,29 @@ pub const Value = extern union {
31963254 ty: Type,
31973255 arena: Allocator,
31983256 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,
31993280 ) !OverflowArithmeticResult {
32003281 const info = ty.intInfo(target);
32013282
......@@ -3220,7 +3301,7 @@ pub const Value = extern union {
32203301 }
32213302
32223303 return OverflowArithmeticResult{
3223 .overflowed = overflowed,
3304 .overflowed = makeBool(overflowed),
32243305 .wrapped_result = try fromBigInt(arena, result_bigint.toConst()),
32253306 };
32263307 }
......@@ -3910,6 +3991,29 @@ pub const Value = extern union {
39103991 ty: Type,
39113992 allocator: Allocator,
39123993 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,
39134017 ) !OverflowArithmeticResult {
39144018 const info = ty.intInfo(target);
39154019 var lhs_space: Value.BigIntSpace = undefined;
......@@ -3930,7 +4034,7 @@ pub const Value = extern union {
39304034 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
39314035 }
39324036 return OverflowArithmeticResult{
3933 .overflowed = overflowed,
4037 .overflowed = makeBool(overflowed),
39344038 .wrapped_result = try fromBigInt(allocator, result_bigint.toConst()),
39354039 };
39364040 }
test/behavior/math.zig+61-26
......@@ -621,24 +621,41 @@ test "128-bit multiplication" {
621621test "@addWithOverflow" {
622622 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
623623
624 var result: u8 = undefined;
625 try expect(@addWithOverflow(u8, 250, 100, &result));
626 try expect(result == 94);
627 try expect(!@addWithOverflow(u8, 100, 150, &result));
628 try expect(result == 250);
629
630 var a: u8 = 200;
631 var b: u8 = 99;
632 try expect(@addWithOverflow(u8, a, b, &result));
633 try expect(result == 43);
634 b = 55;
635 try expect(!@addWithOverflow(u8, a, b, &result));
636 try expect(result == 255);
624 {
625 var result: u8 = undefined;
626 try expect(@addWithOverflow(u8, 250, 100, &result));
627 try expect(result == 94);
628 try expect(!@addWithOverflow(u8, 100, 150, &result));
629 try expect(result == 250);
630
631 var a: u8 = 200;
632 var b: u8 = 99;
633 try expect(@addWithOverflow(u8, a, b, &result));
634 try expect(result == 43);
635 b = 55;
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 }
637655}
638656
639657test "small int addition" {
640658 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
641 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
642659 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
643660
644661 var x: u2 = 0;
......@@ -886,19 +903,37 @@ test "@mulWithOverflow bitsize > 32" {
886903test "@subWithOverflow" {
887904 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
888905
889 var result: u8 = undefined;
890 try expect(@subWithOverflow(u8, 1, 2, &result));
891 try expect(result == 255);
892 try expect(!@subWithOverflow(u8, 1, 1, &result));
893 try expect(result == 0);
906 {
907 var result: u8 = undefined;
908 try expect(@subWithOverflow(u8, 1, 2, &result));
909 try expect(result == 255);
910 try expect(!@subWithOverflow(u8, 1, 1, &result));
911 try expect(result == 0);
894912
895 var a: u8 = 1;
896 var b: u8 = 2;
897 try expect(@subWithOverflow(u8, a, b, &result));
898 try expect(result == 255);
899 b = 1;
900 try expect(!@subWithOverflow(u8, a, b, &result));
901 try expect(result == 0);
913 var a: u8 = 1;
914 var b: u8 = 2;
915 try expect(@subWithOverflow(u8, a, b, &result));
916 try expect(result == 255);
917 b = 1;
918 try expect(!@subWithOverflow(u8, a, b, &result));
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 }
902937}
903938
904939test "@shlWithOverflow" {
test/behavior/vector.zig+120
......@@ -903,3 +903,123 @@ test "multiplication-assignment operator with an array operand" {
903903 try S.doTheTest();
904904 comptime try S.doTheTest();
905905}
906
907test "@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
950test "@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
981test "@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
1004test "@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 {
2020}
2121
2222// run
23// target=arm-linux,x86_64-linux,x86_64-macos,wasm32-wasi
23// target=x86_64-linux,x86_64-macos,wasm32-wasi
2424//