| ... | ... | @@ -2582,103 +2582,108 @@ const DeclGen = struct { |
| 2582 | 2582 | const lhs = try self.resolve(extra.lhs); |
| 2583 | 2583 | const rhs = try self.resolve(extra.rhs); |
| 2584 | 2584 | |
| 2585 | | const operand_ty = self.typeOf(extra.lhs); |
| 2586 | 2585 | const result_ty = self.typeOfIndex(inst); |
| 2586 | const operand_ty = self.typeOf(extra.lhs); |
| 2587 | const ov_ty = result_ty.structFieldType(1, self.module); |
| 2588 | |
| 2589 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 2587 | 2590 | |
| 2588 | 2591 | const info = try self.arithmeticTypeInfo(operand_ty); |
| 2589 | 2592 | switch (info.class) { |
| 2590 | 2593 | .composite_integer => return self.todo("overflow ops for composite integers", .{}), |
| 2591 | | .strange_integer => return self.todo("overflow ops for strange integers", .{}), |
| 2592 | | .integer => {}, |
| 2594 | .strange_integer, .integer => {}, |
| 2593 | 2595 | .float, .bool => unreachable, |
| 2594 | 2596 | } |
| 2595 | 2597 | |
| 2596 | | // The operand type must be the same as the result type in SPIR-V, which |
| 2597 | | // is the same as in Zig. |
| 2598 | | const operand_ty_ref = try self.resolveType(operand_ty, .direct); |
| 2599 | | const operand_ty_id = self.typeId(operand_ty_ref); |
| 2598 | var wip_result = try self.elementWise(operand_ty); |
| 2599 | defer wip_result.deinit(); |
| 2600 | var wip_ov = try self.elementWise(ov_ty); |
| 2601 | defer wip_ov.deinit(); |
| 2602 | for (wip_result.results, wip_ov.results, 0..) |*value_id, *ov_id, i| { |
| 2603 | const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); |
| 2604 | const rhs_elem_id = try wip_result.elementAt(operand_ty, rhs, i); |
| 2600 | 2605 | |
| 2601 | | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 2606 | // Normalize both so that we can properly check for overflow |
| 2607 | const lhs_norm_id = try self.normalizeInt(wip_result.scalar_ty_ref, lhs_elem_id, info); |
| 2608 | const rhs_norm_id = try self.normalizeInt(wip_result.scalar_ty_ref, rhs_elem_id, info); |
| 2609 | const op_result_id = self.spv.allocId(); |
| 2602 | 2610 | |
| 2603 | | const ov_ty = result_ty.structFieldType(1, self.module); |
| 2604 | | // Note: result is stored in a struct, so indirect representation. |
| 2605 | | const ov_ty_ref = try self.resolveType(ov_ty, .indirect); |
| 2606 | | |
| 2607 | | // TODO: Operations other than addition. |
| 2608 | | const value_id = self.spv.allocId(); |
| 2609 | | try self.func.body.emit(self.spv.gpa, add, .{ |
| 2610 | | .id_result_type = operand_ty_id, |
| 2611 | | .id_result = value_id, |
| 2612 | | .operand_1 = lhs, |
| 2613 | | .operand_2 = rhs, |
| 2614 | | }); |
| 2611 | try self.func.body.emit(self.spv.gpa, add, .{ |
| 2612 | .id_result_type = wip_result.scalar_ty_id, |
| 2613 | .id_result = op_result_id, |
| 2614 | .operand_1 = lhs_norm_id, |
| 2615 | .operand_2 = rhs_norm_id, |
| 2616 | }); |
| 2615 | 2617 | |
| 2616 | | const overflowed_id = switch (info.signedness) { |
| 2617 | | .unsigned => blk: { |
| 2618 | | // Overflow happened if the result is smaller than either of the operands. It doesn't matter which. |
| 2619 | | // For subtraction the conditions need to be swapped. |
| 2620 | | const overflowed_id = self.spv.allocId(); |
| 2621 | | try self.func.body.emit(self.spv.gpa, ucmp, .{ |
| 2622 | | .id_result_type = self.typeId(bool_ty_ref), |
| 2623 | | .id_result = overflowed_id, |
| 2624 | | .operand_1 = value_id, |
| 2625 | | .operand_2 = lhs, |
| 2626 | | }); |
| 2627 | | break :blk overflowed_id; |
| 2628 | | }, |
| 2629 | | .signed => blk: { |
| 2630 | | // lhs - rhs |
| 2631 | | // For addition, overflow happened if: |
| 2632 | | // - rhs is negative and value > lhs |
| 2633 | | // - rhs is positive and value < lhs |
| 2634 | | // This can be shortened to: |
| 2635 | | // (rhs < 0 and value > lhs) or (rhs >= 0 and value <= lhs) |
| 2636 | | // = (rhs < 0) == (value > lhs) |
| 2637 | | // = (rhs < 0) == (lhs < value) |
| 2638 | | // Note that signed overflow is also wrapping in spir-v. |
| 2639 | | // For subtraction, overflow happened if: |
| 2640 | | // - rhs is negative and value < lhs |
| 2641 | | // - rhs is positive and value > lhs |
| 2642 | | // This can be shortened to: |
| 2643 | | // (rhs < 0 and value < lhs) or (rhs >= 0 and value >= lhs) |
| 2644 | | // = (rhs < 0) == (value < lhs) |
| 2645 | | // = (rhs < 0) == (lhs > value) |
| 2646 | | |
| 2647 | | const rhs_lt_zero_id = self.spv.allocId(); |
| 2648 | | const zero_id = try self.constInt(operand_ty_ref, 0); |
| 2649 | | try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ |
| 2650 | | .id_result_type = self.typeId(bool_ty_ref), |
| 2651 | | .id_result = rhs_lt_zero_id, |
| 2652 | | .operand_1 = rhs, |
| 2653 | | .operand_2 = zero_id, |
| 2654 | | }); |
| 2618 | // Normalize the result so that the comparisons go well |
| 2619 | value_id.* = try self.normalizeInt(wip_result.scalar_ty_ref, op_result_id, info); |
| 2620 | |
| 2621 | const overflowed_id = switch (info.signedness) { |
| 2622 | .unsigned => blk: { |
| 2623 | // Overflow happened if the result is smaller than either of the operands. It doesn't matter which. |
| 2624 | // For subtraction the conditions need to be swapped. |
| 2625 | const overflowed_id = self.spv.allocId(); |
| 2626 | try self.func.body.emit(self.spv.gpa, ucmp, .{ |
| 2627 | .id_result_type = self.typeId(bool_ty_ref), |
| 2628 | .id_result = overflowed_id, |
| 2629 | .operand_1 = value_id.*, |
| 2630 | .operand_2 = lhs_norm_id, |
| 2631 | }); |
| 2632 | break :blk overflowed_id; |
| 2633 | }, |
| 2634 | .signed => blk: { |
| 2635 | // lhs - rhs |
| 2636 | // For addition, overflow happened if: |
| 2637 | // - rhs is negative and value > lhs |
| 2638 | // - rhs is positive and value < lhs |
| 2639 | // This can be shortened to: |
| 2640 | // (rhs < 0 and value > lhs) or (rhs >= 0 and value <= lhs) |
| 2641 | // = (rhs < 0) == (value > lhs) |
| 2642 | // = (rhs < 0) == (lhs < value) |
| 2643 | // Note that signed overflow is also wrapping in spir-v. |
| 2644 | // For subtraction, overflow happened if: |
| 2645 | // - rhs is negative and value < lhs |
| 2646 | // - rhs is positive and value > lhs |
| 2647 | // This can be shortened to: |
| 2648 | // (rhs < 0 and value < lhs) or (rhs >= 0 and value >= lhs) |
| 2649 | // = (rhs < 0) == (value < lhs) |
| 2650 | // = (rhs < 0) == (lhs > value) |
| 2651 | |
| 2652 | const rhs_lt_zero_id = self.spv.allocId(); |
| 2653 | const zero_id = try self.constInt(wip_result.scalar_ty_ref, 0); |
| 2654 | try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ |
| 2655 | .id_result_type = self.typeId(bool_ty_ref), |
| 2656 | .id_result = rhs_lt_zero_id, |
| 2657 | .operand_1 = rhs_norm_id, |
| 2658 | .operand_2 = zero_id, |
| 2659 | }); |
| 2655 | 2660 | |
| 2656 | | const value_gt_lhs_id = self.spv.allocId(); |
| 2657 | | try self.func.body.emit(self.spv.gpa, scmp, .{ |
| 2658 | | .id_result_type = self.typeId(bool_ty_ref), |
| 2659 | | .id_result = value_gt_lhs_id, |
| 2660 | | .operand_1 = lhs, |
| 2661 | | .operand_2 = value_id, |
| 2662 | | }); |
| 2661 | const value_gt_lhs_id = self.spv.allocId(); |
| 2662 | try self.func.body.emit(self.spv.gpa, scmp, .{ |
| 2663 | .id_result_type = self.typeId(bool_ty_ref), |
| 2664 | .id_result = value_gt_lhs_id, |
| 2665 | .operand_1 = lhs_norm_id, |
| 2666 | .operand_2 = value_id.*, |
| 2667 | }); |
| 2663 | 2668 | |
| 2664 | | const overflowed_id = self.spv.allocId(); |
| 2665 | | try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{ |
| 2666 | | .id_result_type = self.typeId(bool_ty_ref), |
| 2667 | | .id_result = overflowed_id, |
| 2668 | | .operand_1 = rhs_lt_zero_id, |
| 2669 | | .operand_2 = value_gt_lhs_id, |
| 2670 | | }); |
| 2671 | | break :blk overflowed_id; |
| 2672 | | }, |
| 2673 | | }; |
| 2669 | const overflowed_id = self.spv.allocId(); |
| 2670 | try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{ |
| 2671 | .id_result_type = self.typeId(bool_ty_ref), |
| 2672 | .id_result = overflowed_id, |
| 2673 | .operand_1 = rhs_lt_zero_id, |
| 2674 | .operand_2 = value_gt_lhs_id, |
| 2675 | }); |
| 2676 | break :blk overflowed_id; |
| 2677 | }, |
| 2678 | }; |
| 2679 | |
| 2680 | ov_id.* = try self.intFromBool(wip_ov.scalar_ty_ref, overflowed_id); |
| 2681 | } |
| 2674 | 2682 | |
| 2675 | | // Construct the struct that Zig wants as result. |
| 2676 | | // The value should already be the correct type. |
| 2677 | | const ov_id = try self.intFromBool(ov_ty_ref, overflowed_id); |
| 2678 | 2683 | return try self.constructStruct( |
| 2679 | 2684 | result_ty, |
| 2680 | 2685 | &.{ operand_ty, ov_ty }, |
| 2681 | | &.{ value_id, ov_id }, |
| 2686 | &.{ try wip_result.finalize(), try wip_ov.finalize() }, |
| 2682 | 2687 | ); |
| 2683 | 2688 | } |
| 2684 | 2689 | |