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