| ... | @@ -9417,32 +9417,29 @@ fn zirOverflowArithmetic( | ... | @@ -9417,32 +9417,29 @@ fn zirOverflowArithmetic( |
| 9417 | const ptr = sema.resolveInst(extra.ptr); | 9417 | const ptr = sema.resolveInst(extra.ptr); |
| 9418 | | 9418 | |
| 9419 | const lhs_ty = sema.typeOf(lhs); | 9419 | const lhs_ty = sema.typeOf(lhs); |
| | 9420 | const rhs_ty = sema.typeOf(rhs); |
| 9420 | const mod = sema.mod; | 9421 | const mod = sema.mod; |
| 9421 | const target = mod.getTarget(); | 9422 | const target = mod.getTarget(); |
| 9422 | | 9423 | |
| 9423 | // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen. | 9424 | // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen. |
| | 9425 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 9424 | const dest_ty = lhs_ty; | 9426 | const dest_ty = lhs_ty; |
| 9425 | if (dest_ty.zigTypeTag() != .Int) { | 9427 | if (dest_ty.scalarType().zigTypeTag() != .Int) { |
| 9426 | return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty.fmt(mod)}); | 9428 | return sema.fail(block, src, "expected vector of integers or integer type, found '{}'", .{dest_ty.fmt(mod)}); |
| 9427 | } | 9429 | } |
| 9428 | | 9430 | |
| 9429 | const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); | 9431 | const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); |
| 9430 | const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); | 9432 | const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); |
| 9431 | | 9433 | |
| 9432 | const types = try sema.arena.alloc(Type, 2); | 9434 | const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty); |
| 9433 | const values = try sema.arena.alloc(Value, 2); | 9435 | const ov_ty = tuple_ty.tupleFields().types[1]; |
| 9434 | const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ | 9436 | // TODO: Remove and use `ov_ty` instead. |
| 9435 | .types = types, | 9437 | // This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`. |
| 9436 | .values = values, | 9438 | 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 | | 9439 | |
| 9444 | const result: struct { | 9440 | const result: struct { |
| 9445 | overflowed: enum { yes, no, undef }, | 9441 | /// TODO: Rename to `overflow_bit` and make of type `u1`. |
| | 9442 | overflowed: Air.Inst.Ref, |
| 9446 | wrapped: Air.Inst.Ref, | 9443 | wrapped: Air.Inst.Ref, |
| 9447 | } = result: { | 9444 | } = result: { |
| 9448 | switch (zir_tag) { | 9445 | switch (zir_tag) { |
| ... | @@ -9452,23 +9449,24 @@ fn zirOverflowArithmetic( | ... | @@ -9452,23 +9449,24 @@ fn zirOverflowArithmetic( |
| 9452 | // Otherwise, if either of the argument is undefined, undefined is returned. | 9449 | // Otherwise, if either of the argument is undefined, undefined is returned. |
| 9453 | if (maybe_lhs_val) |lhs_val| { | 9450 | if (maybe_lhs_val) |lhs_val| { |
| 9454 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { | 9451 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { |
| 9455 | break :result .{ .overflowed = .no, .wrapped = rhs }; | 9452 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; |
| 9456 | } | 9453 | } |
| 9457 | } | 9454 | } |
| 9458 | if (maybe_rhs_val) |rhs_val| { | 9455 | if (maybe_rhs_val) |rhs_val| { |
| 9459 | if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { | 9456 | if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { |
| 9460 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9457 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9461 | } | 9458 | } |
| 9462 | } | 9459 | } |
| 9463 | if (maybe_lhs_val) |lhs_val| { | 9460 | if (maybe_lhs_val) |lhs_val| { |
| 9464 | if (maybe_rhs_val) |rhs_val| { | 9461 | if (maybe_rhs_val) |rhs_val| { |
| 9465 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 9462 | if (lhs_val.isUndef() or rhs_val.isUndef()) { |
| 9466 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | 9463 | break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; |
| 9467 | } | 9464 | } |
| 9468 | | 9465 | |
| 9469 | const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target); | 9466 | const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target); |
| 9470 | const inst = try sema.addConstant(dest_ty, result.wrapped_result); | 9467 | const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); |
| 9471 | break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; | 9468 | const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); |
| | 9469 | break :result .{ .overflowed = overflowed, .wrapped = wrapped }; |
| 9472 | } | 9470 | } |
| 9473 | } | 9471 | } |
| 9474 | }, | 9472 | }, |
| ... | @@ -9477,17 +9475,18 @@ fn zirOverflowArithmetic( | ... | @@ -9477,17 +9475,18 @@ fn zirOverflowArithmetic( |
| 9477 | // Otherwise, if either result is undefined, both results are undefined. | 9475 | // Otherwise, if either result is undefined, both results are undefined. |
| 9478 | if (maybe_rhs_val) |rhs_val| { | 9476 | if (maybe_rhs_val) |rhs_val| { |
| 9479 | if (rhs_val.isUndef()) { | 9477 | if (rhs_val.isUndef()) { |
| 9480 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | 9478 | break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; |
| 9481 | } else if (rhs_val.compareWithZero(.eq)) { | 9479 | } else if (rhs_val.compareWithZero(.eq)) { |
| 9482 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9480 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9483 | } else if (maybe_lhs_val) |lhs_val| { | 9481 | } else if (maybe_lhs_val) |lhs_val| { |
| 9484 | if (lhs_val.isUndef()) { | 9482 | if (lhs_val.isUndef()) { |
| 9485 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | 9483 | break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; |
| 9486 | } | 9484 | } |
| 9487 | | 9485 | |
| 9488 | const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target); | 9486 | const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target); |
| 9489 | const inst = try sema.addConstant(dest_ty, result.wrapped_result); | 9487 | const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); |
| 9490 | break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; | 9488 | const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); |
| | 9489 | break :result .{ .overflowed = overflowed, .wrapped = wrapped }; |
| 9491 | } | 9490 | } |
| 9492 | } | 9491 | } |
| 9493 | }, | 9492 | }, |
| ... | @@ -9498,9 +9497,9 @@ fn zirOverflowArithmetic( | ... | @@ -9498,9 +9497,9 @@ fn zirOverflowArithmetic( |
| 9498 | if (maybe_lhs_val) |lhs_val| { | 9497 | if (maybe_lhs_val) |lhs_val| { |
| 9499 | if (!lhs_val.isUndef()) { | 9498 | if (!lhs_val.isUndef()) { |
| 9500 | if (lhs_val.compareWithZero(.eq)) { | 9499 | if (lhs_val.compareWithZero(.eq)) { |
| 9501 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9500 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9502 | } else if (lhs_val.compare(.eq, Value.one, dest_ty, mod)) { | 9501 | } else if (lhs_val.compare(.eq, Value.one, dest_ty, mod)) { |
| 9503 | break :result .{ .overflowed = .no, .wrapped = rhs }; | 9502 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; |
| 9504 | } | 9503 | } |
| 9505 | } | 9504 | } |
| 9506 | } | 9505 | } |
| ... | @@ -9508,9 +9507,9 @@ fn zirOverflowArithmetic( | ... | @@ -9508,9 +9507,9 @@ fn zirOverflowArithmetic( |
| 9508 | if (maybe_rhs_val) |rhs_val| { | 9507 | if (maybe_rhs_val) |rhs_val| { |
| 9509 | if (!rhs_val.isUndef()) { | 9508 | if (!rhs_val.isUndef()) { |
| 9510 | if (rhs_val.compareWithZero(.eq)) { | 9509 | if (rhs_val.compareWithZero(.eq)) { |
| 9511 | break :result .{ .overflowed = .no, .wrapped = rhs }; | 9510 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; |
| 9512 | } else if (rhs_val.compare(.eq, Value.one, dest_ty, mod)) { | 9511 | } else if (rhs_val.compare(.eq, Value.one, dest_ty, mod)) { |
| 9513 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9512 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9514 | } | 9513 | } |
| 9515 | } | 9514 | } |
| 9516 | } | 9515 | } |
| ... | @@ -9518,12 +9517,13 @@ fn zirOverflowArithmetic( | ... | @@ -9518,12 +9517,13 @@ fn zirOverflowArithmetic( |
| 9518 | if (maybe_lhs_val) |lhs_val| { | 9517 | if (maybe_lhs_val) |lhs_val| { |
| 9519 | if (maybe_rhs_val) |rhs_val| { | 9518 | if (maybe_rhs_val) |rhs_val| { |
| 9520 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 9519 | if (lhs_val.isUndef() or rhs_val.isUndef()) { |
| 9521 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | 9520 | break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; |
| 9522 | } | 9521 | } |
| 9523 | | 9522 | |
| 9524 | const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target); | 9523 | const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target); |
| 9525 | const inst = try sema.addConstant(dest_ty, result.wrapped_result); | 9524 | const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); |
| 9526 | break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; | 9525 | const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); |
| | 9526 | break :result .{ .overflowed = overflowed, .wrapped = wrapped }; |
| 9527 | } | 9527 | } |
| 9528 | } | 9528 | } |
| 9529 | }, | 9529 | }, |
| ... | @@ -9533,23 +9533,24 @@ fn zirOverflowArithmetic( | ... | @@ -9533,23 +9533,24 @@ fn zirOverflowArithmetic( |
| 9533 | // Oterhwise if either of the arguments is undefined, both results are undefined. | 9533 | // Oterhwise if either of the arguments is undefined, both results are undefined. |
| 9534 | if (maybe_lhs_val) |lhs_val| { | 9534 | if (maybe_lhs_val) |lhs_val| { |
| 9535 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { | 9535 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { |
| 9536 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9536 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9537 | } | 9537 | } |
| 9538 | } | 9538 | } |
| 9539 | if (maybe_rhs_val) |rhs_val| { | 9539 | if (maybe_rhs_val) |rhs_val| { |
| 9540 | if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { | 9540 | if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { |
| 9541 | break :result .{ .overflowed = .no, .wrapped = lhs }; | 9541 | break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; |
| 9542 | } | 9542 | } |
| 9543 | } | 9543 | } |
| 9544 | if (maybe_lhs_val) |lhs_val| { | 9544 | if (maybe_lhs_val) |lhs_val| { |
| 9545 | if (maybe_rhs_val) |rhs_val| { | 9545 | if (maybe_rhs_val) |rhs_val| { |
| 9546 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 9546 | if (lhs_val.isUndef() or rhs_val.isUndef()) { |
| 9547 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | 9547 | break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; |
| 9548 | } | 9548 | } |
| 9549 | | 9549 | |
| 9550 | const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target); | 9550 | const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target); |
| 9551 | const inst = try sema.addConstant(dest_ty, result.wrapped_result); | 9551 | const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); |
| 9552 | break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; | 9552 | const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); |
| | 9553 | break :result .{ .overflowed = overflowed, .wrapped = wrapped }; |
| 9553 | } | 9554 | } |
| 9554 | } | 9555 | } |
| 9555 | }, | 9556 | }, |
| ... | @@ -9577,21 +9578,40 @@ fn zirOverflowArithmetic( | ... | @@ -9577,21 +9578,40 @@ fn zirOverflowArithmetic( |
| 9577 | } }, | 9578 | } }, |
| 9578 | }); | 9579 | }); |
| 9579 | | 9580 | |
| 9580 | const wrapped = try block.addStructFieldVal(tuple, 0, dest_ty); | 9581 | const wrapped = try sema.tupleFieldValByIndex(block, src, tuple, 0, tuple_ty); |
| 9581 | try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store); | 9582 | try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store); |
| 9582 | | 9583 | |
| 9583 | const overflow_bit = try block.addStructFieldVal(tuple, 1, Type.initTag(.u1)); | 9584 | const overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty); |
| 9584 | const zero_u1 = try sema.addConstant(Type.initTag(.u1), Value.zero); | 9585 | 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); | 9586 | const zero_ov = try sema.addConstant(ov_ty, zero_ov_val); |
| | 9587 | |
| | 9588 | const overflowed_inst = if (dest_ty.zigTypeTag() == .Vector) |
| | 9589 | block.addCmpVector(overflow_bit, .zero, .neq, try sema.addType(ov_ty)) |
| | 9590 | else |
| | 9591 | block.addBinOp(.cmp_neq, overflow_bit, zero_ov); |
| | 9592 | return overflowed_inst; |
| 9586 | }; | 9593 | }; |
| 9587 | | 9594 | |
| 9588 | try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store); | 9595 | try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store); |
| | 9596 | return result.overflowed; |
| | 9597 | } |
| 9589 | | 9598 | |
| 9590 | return switch (result.overflowed) { | 9599 | fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type { |
| 9591 | .yes => Air.Inst.Ref.bool_true, | 9600 | 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, | 9601 | |
| 9593 | .undef => try sema.addConstUndef(Type.bool), | 9602 | const types = try sema.arena.alloc(Type, 2); |
| 9594 | }; | 9603 | const values = try sema.arena.alloc(Value, 2); |
| | 9604 | const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ |
| | 9605 | .types = types, |
| | 9606 | .values = values, |
| | 9607 | }); |
| | 9608 | |
| | 9609 | types[0] = ty; |
| | 9610 | types[1] = ov_ty; |
| | 9611 | values[0] = Value.initTag(.unreachable_value); |
| | 9612 | values[1] = Value.initTag(.unreachable_value); |
| | 9613 | |
| | 9614 | return tuple_ty; |
| 9595 | } | 9615 | } |
| 9596 | | 9616 | |
| 9597 | fn analyzeArithmetic( | 9617 | fn analyzeArithmetic( |
| ... | @@ -23093,6 +23113,14 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref { | ... | @@ -23093,6 +23113,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)); | 23113 | return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int)); |
| 23094 | } | 23114 | } |
| 23095 | | 23115 | |
| | 23116 | fn addBool(sema: *Sema, ty: Type, boolean: bool) CompileError!Air.Inst.Ref { |
| | 23117 | return switch (ty.zigTypeTag()) { |
| | 23118 | .Vector => sema.addConstant(ty, try Value.Tag.repeated.create(sema.arena, Value.makeBool(boolean))), |
| | 23119 | .Bool => sema.resolveInst(if (boolean) .bool_true else .bool_false), |
| | 23120 | else => unreachable, |
| | 23121 | }; |
| | 23122 | } |
| | 23123 | |
| 23096 | fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref { | 23124 | fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref { |
| 23097 | return sema.addConstant(ty, Value.undef); | 23125 | return sema.addConstant(ty, Value.undef); |
| 23098 | } | 23126 | } |