| ... | ... | @@ -1782,19 +1782,6 @@ const DeclGen = struct { |
| 1782 | 1782 | wip.dg.gpa.free(wip.results); |
| 1783 | 1783 | } |
| 1784 | 1784 | |
| 1785 | | /// Return the scalar type of an input vector. This type is expected to be a vector |
| 1786 | | /// if `wip.is_vector`, and a scalar otherwise. |
| 1787 | | fn scalarType(wip: WipElementWise, ty: Type) Type { |
| 1788 | | const mod = wip.dg.module; |
| 1789 | | if (wip.is_vector) { |
| 1790 | | assert(ty.isVector(mod)); |
| 1791 | | return ty.childType(mod); |
| 1792 | | } else { |
| 1793 | | assert(!ty.isVector(mod)); |
| 1794 | | return ty; |
| 1795 | | } |
| 1796 | | } |
| 1797 | | |
| 1798 | 1785 | /// Utility function to extract the element at a particular index in an |
| 1799 | 1786 | /// input vector. This type is expected to be a vector if `wip.is_vector`, and |
| 1800 | 1787 | /// a scalar otherwise. |
| ... | ... | @@ -1844,7 +1831,7 @@ const DeclGen = struct { |
| 1844 | 1831 | const results = try self.gpa.alloc(IdRef, num_results); |
| 1845 | 1832 | for (results) |*result| result.* = undefined; |
| 1846 | 1833 | |
| 1847 | | const scalar_ty = if (is_vector) result_ty.childType(mod) else result_ty; |
| 1834 | const scalar_ty = result_ty.scalarType(mod); |
| 1848 | 1835 | const scalar_ty_ref = try self.resolveType(scalar_ty, .direct); |
| 1849 | 1836 | |
| 1850 | 1837 | return .{ |
| ... | ... | @@ -2198,6 +2185,7 @@ const DeclGen = struct { |
| 2198 | 2185 | |
| 2199 | 2186 | .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan), |
| 2200 | 2187 | .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan), |
| 2188 | .shl_with_overflow => try self.airShlOverflow(inst), |
| 2201 | 2189 | |
| 2202 | 2190 | .shuffle => try self.airShuffle(inst), |
| 2203 | 2191 | |
| ... | ... | @@ -2343,23 +2331,30 @@ const DeclGen = struct { |
| 2343 | 2331 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2344 | 2332 | const lhs_id = try self.resolve(bin_op.lhs); |
| 2345 | 2333 | const rhs_id = try self.resolve(bin_op.rhs); |
| 2334 | |
| 2346 | 2335 | const result_ty = self.typeOfIndex(inst); |
| 2336 | const shift_ty = self.typeOf(bin_op.rhs); |
| 2337 | const scalar_shift_ty_ref = try self.resolveType(shift_ty.scalarType(mod), .direct); |
| 2347 | 2338 | |
| 2348 | | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, |
| 2349 | | // so just manually upcast it if required. |
| 2350 | | // TODO(robin) |
| 2339 | const info = try self.arithmeticTypeInfo(result_ty); |
| 2340 | switch (info.class) { |
| 2341 | .composite_integer => return self.todo("shift ops for composite integers", .{}), |
| 2342 | .integer, .strange_integer => {}, |
| 2343 | .float, .bool => unreachable, |
| 2344 | } |
| 2351 | 2345 | |
| 2352 | 2346 | var wip = try self.elementWise(result_ty); |
| 2353 | 2347 | defer wip.deinit(); |
| 2354 | | |
| 2355 | | const shift_ty = wip.scalarType(self.typeOf(bin_op.rhs)); |
| 2356 | | const shift_ty_ref = try self.resolveType(shift_ty, .direct); |
| 2357 | | |
| 2358 | 2348 | for (0..wip.results.len) |i| { |
| 2359 | 2349 | const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i); |
| 2360 | | const rhs_elem_id = try wip.elementAt(result_ty, rhs_id, i); |
| 2350 | const rhs_elem_id = try wip.elementAt(shift_ty, rhs_id, i); |
| 2351 | |
| 2352 | // TODO: Can we omit normalizing lhs? |
| 2353 | const lhs_norm_id = try self.normalizeInt(wip.scalar_ty_ref, lhs_elem_id, info); |
| 2361 | 2354 | |
| 2362 | | const shift_id = if (shift_ty_ref != wip.result_ty_ref) blk: { |
| 2355 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, |
| 2356 | // so just manually upcast it if required. |
| 2357 | const shift_id = if (scalar_shift_ty_ref != wip.scalar_ty_ref) blk: { |
| 2363 | 2358 | const shift_id = self.spv.allocId(); |
| 2364 | 2359 | try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ |
| 2365 | 2360 | .id_result_type = wip.scalar_ty_id, |
| ... | ... | @@ -2368,12 +2363,13 @@ const DeclGen = struct { |
| 2368 | 2363 | }); |
| 2369 | 2364 | break :blk shift_id; |
| 2370 | 2365 | } else rhs_elem_id; |
| 2366 | const shift_norm_id = try self.normalizeInt(wip.scalar_ty_ref, shift_id, info); |
| 2371 | 2367 | |
| 2372 | 2368 | const args = .{ |
| 2373 | 2369 | .id_result_type = wip.scalar_ty_id, |
| 2374 | 2370 | .id_result = wip.allocId(i), |
| 2375 | | .base = lhs_elem_id, |
| 2376 | | .shift = shift_id, |
| 2371 | .base = lhs_norm_id, |
| 2372 | .shift = shift_norm_id, |
| 2377 | 2373 | }; |
| 2378 | 2374 | |
| 2379 | 2375 | if (result_ty.isSignedInt(mod)) { |
| ... | ... | @@ -2680,6 +2676,88 @@ const DeclGen = struct { |
| 2680 | 2676 | ); |
| 2681 | 2677 | } |
| 2682 | 2678 | |
| 2679 | fn airShlOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2680 | if (self.liveness.isUnused(inst)) return null; |
| 2681 | const mod = self.module; |
| 2682 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2683 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2684 | const lhs = try self.resolve(extra.lhs); |
| 2685 | const rhs = try self.resolve(extra.rhs); |
| 2686 | |
| 2687 | const result_ty = self.typeOfIndex(inst); |
| 2688 | const operand_ty = self.typeOf(extra.lhs); |
| 2689 | const shift_ty = self.typeOf(extra.rhs); |
| 2690 | const scalar_shift_ty_ref = try self.resolveType(shift_ty.scalarType(mod), .direct); |
| 2691 | |
| 2692 | const ov_ty = result_ty.structFieldType(1, self.module); |
| 2693 | |
| 2694 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 2695 | |
| 2696 | const info = try self.arithmeticTypeInfo(operand_ty); |
| 2697 | switch (info.class) { |
| 2698 | .composite_integer => return self.todo("overflow shift for composite integers", .{}), |
| 2699 | .integer, .strange_integer => {}, |
| 2700 | .float, .bool => unreachable, |
| 2701 | } |
| 2702 | |
| 2703 | var wip_result = try self.elementWise(operand_ty); |
| 2704 | defer wip_result.deinit(); |
| 2705 | var wip_ov = try self.elementWise(ov_ty); |
| 2706 | defer wip_ov.deinit(); |
| 2707 | for (0..wip_result.results.len, wip_ov.results) |i, *ov_id| { |
| 2708 | const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); |
| 2709 | const rhs_elem_id = try wip_result.elementAt(shift_ty, rhs, i); |
| 2710 | |
| 2711 | // Normalize both so that we can shift back and check if the result is the same. |
| 2712 | const lhs_norm_id = try self.normalizeInt(wip_result.scalar_ty_ref, lhs_elem_id, info); |
| 2713 | |
| 2714 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, |
| 2715 | // so just manually upcast it if required. |
| 2716 | const shift_id = if (scalar_shift_ty_ref != wip_result.scalar_ty_ref) blk: { |
| 2717 | const shift_id = self.spv.allocId(); |
| 2718 | try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ |
| 2719 | .id_result_type = wip_result.scalar_ty_id, |
| 2720 | .id_result = shift_id, |
| 2721 | .unsigned_value = rhs_elem_id, |
| 2722 | }); |
| 2723 | break :blk shift_id; |
| 2724 | } else rhs_elem_id; |
| 2725 | const shift_norm_id = try self.normalizeInt(wip_result.scalar_ty_ref, shift_id, info); |
| 2726 | |
| 2727 | try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ |
| 2728 | .id_result_type = wip_result.scalar_ty_id, |
| 2729 | .id_result = wip_result.allocId(i), |
| 2730 | .base = lhs_norm_id, |
| 2731 | .shift = shift_norm_id, |
| 2732 | }); |
| 2733 | |
| 2734 | // To check if overflow happened, just check if the right-shifted result is the same value. |
| 2735 | const right_shift_id = self.spv.allocId(); |
| 2736 | try self.func.body.emit(self.spv.gpa, .OpShiftRightLogical, .{ |
| 2737 | .id_result_type = wip_result.scalar_ty_id, |
| 2738 | .id_result = right_shift_id, |
| 2739 | .base = try self.normalizeInt(wip_result.scalar_ty_ref, wip_result.results[i], info), |
| 2740 | .shift = shift_norm_id, |
| 2741 | }); |
| 2742 | |
| 2743 | const overflowed_id = self.spv.allocId(); |
| 2744 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ |
| 2745 | .id_result_type = self.typeId(bool_ty_ref), |
| 2746 | .id_result = overflowed_id, |
| 2747 | .operand_1 = lhs_norm_id, |
| 2748 | .operand_2 = right_shift_id, |
| 2749 | }); |
| 2750 | |
| 2751 | ov_id.* = try self.intFromBool(wip_ov.scalar_ty_ref, overflowed_id); |
| 2752 | } |
| 2753 | |
| 2754 | return try self.constructStruct( |
| 2755 | result_ty, |
| 2756 | &.{ operand_ty, ov_ty }, |
| 2757 | &.{ try wip_result.finalize(), try wip_ov.finalize() }, |
| 2758 | ); |
| 2759 | } |
| 2760 | |
| 2683 | 2761 | fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2684 | 2762 | const mod = self.module; |
| 2685 | 2763 | if (self.liveness.isUnused(inst)) return null; |