| ... | @@ -1782,19 +1782,6 @@ const DeclGen = struct { | ... | @@ -1782,19 +1782,6 @@ const DeclGen = struct { |
| 1782 | wip.dg.gpa.free(wip.results); | 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 | /// Utility function to extract the element at a particular index in an | 1785 | /// Utility function to extract the element at a particular index in an |
| 1799 | /// input vector. This type is expected to be a vector if `wip.is_vector`, and | 1786 | /// input vector. This type is expected to be a vector if `wip.is_vector`, and |
| 1800 | /// a scalar otherwise. | 1787 | /// a scalar otherwise. |
| ... | @@ -1844,7 +1831,7 @@ const DeclGen = struct { | ... | @@ -1844,7 +1831,7 @@ const DeclGen = struct { |
| 1844 | const results = try self.gpa.alloc(IdRef, num_results); | 1831 | const results = try self.gpa.alloc(IdRef, num_results); |
| 1845 | for (results) |*result| result.* = undefined; | 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 | const scalar_ty_ref = try self.resolveType(scalar_ty, .direct); | 1835 | const scalar_ty_ref = try self.resolveType(scalar_ty, .direct); |
| 1849 | | 1836 | |
| 1850 | return .{ | 1837 | return .{ |
| ... | @@ -2198,6 +2185,7 @@ const DeclGen = struct { | ... | @@ -2198,6 +2185,7 @@ const DeclGen = struct { |
| 2198 | | 2185 | |
| 2199 | .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan), | 2186 | .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan), |
| 2200 | .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan), | 2187 | .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan), |
| | 2188 | .shl_with_overflow => try self.airShlOverflow(inst), |
| 2201 | | 2189 | |
| 2202 | .shuffle => try self.airShuffle(inst), | 2190 | .shuffle => try self.airShuffle(inst), |
| 2203 | | 2191 | |
| ... | @@ -2343,23 +2331,30 @@ const DeclGen = struct { | ... | @@ -2343,23 +2331,30 @@ const DeclGen = struct { |
| 2343 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 2331 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2344 | const lhs_id = try self.resolve(bin_op.lhs); | 2332 | const lhs_id = try self.resolve(bin_op.lhs); |
| 2345 | const rhs_id = try self.resolve(bin_op.rhs); | 2333 | const rhs_id = try self.resolve(bin_op.rhs); |
| | 2334 | |
| 2346 | const result_ty = self.typeOfIndex(inst); | 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, | 2339 | const info = try self.arithmeticTypeInfo(result_ty); |
| 2349 | // so just manually upcast it if required. | 2340 | switch (info.class) { |
| 2350 | // TODO(robin) | 2341 | .composite_integer => return self.todo("shift ops for composite integers", .{}), |
| | 2342 | .integer, .strange_integer => {}, |
| | 2343 | .float, .bool => unreachable, |
| | 2344 | } |
| 2351 | | 2345 | |
| 2352 | var wip = try self.elementWise(result_ty); | 2346 | var wip = try self.elementWise(result_ty); |
| 2353 | defer wip.deinit(); | 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 | for (0..wip.results.len) |i| { | 2348 | for (0..wip.results.len) |i| { |
| 2359 | const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i); | 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 | const shift_id = self.spv.allocId(); | 2358 | const shift_id = self.spv.allocId(); |
| 2364 | try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ | 2359 | try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ |
| 2365 | .id_result_type = wip.scalar_ty_id, | 2360 | .id_result_type = wip.scalar_ty_id, |
| ... | @@ -2368,12 +2363,13 @@ const DeclGen = struct { | ... | @@ -2368,12 +2363,13 @@ const DeclGen = struct { |
| 2368 | }); | 2363 | }); |
| 2369 | break :blk shift_id; | 2364 | break :blk shift_id; |
| 2370 | } else rhs_elem_id; | 2365 | } else rhs_elem_id; |
| | 2366 | const shift_norm_id = try self.normalizeInt(wip.scalar_ty_ref, shift_id, info); |
| 2371 | | 2367 | |
| 2372 | const args = .{ | 2368 | const args = .{ |
| 2373 | .id_result_type = wip.scalar_ty_id, | 2369 | .id_result_type = wip.scalar_ty_id, |
| 2374 | .id_result = wip.allocId(i), | 2370 | .id_result = wip.allocId(i), |
| 2375 | .base = lhs_elem_id, | 2371 | .base = lhs_norm_id, |
| 2376 | .shift = shift_id, | 2372 | .shift = shift_norm_id, |
| 2377 | }; | 2373 | }; |
| 2378 | | 2374 | |
| 2379 | if (result_ty.isSignedInt(mod)) { | 2375 | if (result_ty.isSignedInt(mod)) { |
| ... | @@ -2680,6 +2676,88 @@ const DeclGen = struct { | ... | @@ -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 | fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 2761 | fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2684 | const mod = self.module; | 2762 | const mod = self.module; |
| 2685 | if (self.liveness.isUnused(inst)) return null; | 2763 | if (self.liveness.isUnused(inst)) return null; |