| ... | ... | @@ -629,6 +629,16 @@ const DeclGen = struct { |
| 629 | 629 | return self.backingIntBits(ty) == null; |
| 630 | 630 | } |
| 631 | 631 | |
| 632 | /// Checks whether the type can be directly translated to SPIR-V vectors |
| 633 | fn isVector(self: *DeclGen, ty: Type) bool { |
| 634 | const mod = self.module; |
| 635 | if (ty.zigTypeTag(mod) != .Vector) return false; |
| 636 | const elem_ty = ty.childType(mod); |
| 637 | const len = ty.vectorLen(mod); |
| 638 | const is_scalar = elem_ty.isNumeric(mod) or elem_ty.toIntern() == .bool_type; |
| 639 | return is_scalar and len > 1 and len <= 4; |
| 640 | } |
| 641 | |
| 632 | 642 | fn arithmeticTypeInfo(self: *DeclGen, ty: Type) ArithmeticTypeInfo { |
| 633 | 643 | const mod = self.module; |
| 634 | 644 | const target = self.getTarget(); |
| ... | ... | @@ -694,6 +704,24 @@ const DeclGen = struct { |
| 694 | 704 | /// This function, unlike SpvModule.constInt, takes care to bitcast |
| 695 | 705 | /// the value to an unsigned int first for Kernels. |
| 696 | 706 | fn constInt(self: *DeclGen, ty_ref: CacheRef, value: anytype) !IdRef { |
| 707 | switch (self.spv.cache.lookup(ty_ref)) { |
| 708 | .vector_type => |vec_type| { |
| 709 | const elem_ids = try self.gpa.alloc(IdRef, vec_type.component_count); |
| 710 | defer self.gpa.free(elem_ids); |
| 711 | const int_value = try self.constInt(vec_type.component_type, value); |
| 712 | @memset(elem_ids, int_value); |
| 713 | |
| 714 | const constituents_id = self.spv.allocId(); |
| 715 | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ |
| 716 | .id_result_type = self.typeId(ty_ref), |
| 717 | .id_result = constituents_id, |
| 718 | .constituents = elem_ids, |
| 719 | }); |
| 720 | return constituents_id; |
| 721 | }, |
| 722 | else => {}, |
| 723 | } |
| 724 | |
| 697 | 725 | if (value < 0) { |
| 698 | 726 | const ty = self.spv.cache.lookup(ty_ref).int_type; |
| 699 | 727 | // Manually truncate the value so that the resulting value |
| ... | ... | @@ -711,6 +739,24 @@ const DeclGen = struct { |
| 711 | 739 | |
| 712 | 740 | /// Emits a float constant |
| 713 | 741 | fn constFloat(self: *DeclGen, ty_ref: CacheRef, value: f128) !IdRef { |
| 742 | switch (self.spv.cache.lookup(ty_ref)) { |
| 743 | .vector_type => |vec_type| { |
| 744 | const elem_ids = try self.gpa.alloc(IdRef, vec_type.component_count); |
| 745 | defer self.gpa.free(elem_ids); |
| 746 | const int_value = try self.constFloat(vec_type.component_type, value); |
| 747 | @memset(elem_ids, int_value); |
| 748 | |
| 749 | const constituents_id = self.spv.allocId(); |
| 750 | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ |
| 751 | .id_result_type = self.typeId(ty_ref), |
| 752 | .id_result = constituents_id, |
| 753 | .constituents = elem_ids, |
| 754 | }); |
| 755 | return constituents_id; |
| 756 | }, |
| 757 | else => {}, |
| 758 | } |
| 759 | |
| 714 | 760 | const ty = self.spv.cache.lookup(ty_ref).float_type; |
| 715 | 761 | return switch (ty.bits) { |
| 716 | 762 | 16 => try self.spv.resolveId(.{ .float = .{ .ty = ty_ref, .value = .{ .float16 = @floatCast(value) } } }), |
| ... | ... | @@ -726,9 +772,9 @@ const DeclGen = struct { |
| 726 | 772 | /// if the parameters are in indirect representation, then the result is too. |
| 727 | 773 | fn constructComposite(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef { |
| 728 | 774 | const constituents_id = self.spv.allocId(); |
| 729 | | const type_id = try self.resolveTypeId(ty); |
| 775 | const type_id = try self.resolveType(ty, .direct); |
| 730 | 776 | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ |
| 731 | | .id_result_type = type_id, |
| 777 | .id_result_type = self.typeId(type_id), |
| 732 | 778 | .id_result = constituents_id, |
| 733 | 779 | .constituents = constituents, |
| 734 | 780 | }); |
| ... | ... | @@ -901,19 +947,19 @@ const DeclGen = struct { |
| 901 | 947 | .bytes => |bytes| { |
| 902 | 948 | // TODO: This is really space inefficient, perhaps there is a better |
| 903 | 949 | // way to do it? |
| 904 | | for (bytes, 0..) |byte, i| { |
| 905 | | constituents[i] = try self.constInt(elem_ty_ref, byte); |
| 950 | for (constituents, bytes) |*constituent, byte| { |
| 951 | constituent.* = try self.constInt(elem_ty_ref, byte); |
| 906 | 952 | } |
| 907 | 953 | }, |
| 908 | 954 | .elems => |elems| { |
| 909 | | for (0..@as(usize, @intCast(array_type.len))) |i| { |
| 910 | | constituents[i] = try self.constant(elem_ty, Value.fromInterned(elems[i]), .indirect); |
| 955 | for (constituents, elems) |*constituent, elem| { |
| 956 | constituent.* = try self.constant(elem_ty, Value.fromInterned(elem), .indirect); |
| 911 | 957 | } |
| 912 | 958 | }, |
| 913 | 959 | .repeated_elem => |elem| { |
| 914 | 960 | const val_id = try self.constant(elem_ty, Value.fromInterned(elem), .indirect); |
| 915 | | for (0..@as(usize, @intCast(array_type.len))) |i| { |
| 916 | | constituents[i] = val_id; |
| 961 | for (constituents) |*constituent| { |
| 962 | constituent.* = val_id; |
| 917 | 963 | } |
| 918 | 964 | }, |
| 919 | 965 | } |
| ... | ... | @@ -1448,12 +1494,11 @@ const DeclGen = struct { |
| 1448 | 1494 | const elem_ty = ty.childType(mod); |
| 1449 | 1495 | const elem_ty_ref = try self.resolveType(elem_ty, .indirect); |
| 1450 | 1496 | const len = ty.vectorLen(mod); |
| 1451 | | const is_scalar = elem_ty.isNumeric(mod) or elem_ty.toIntern() == .bool_type; |
| 1452 | 1497 | |
| 1453 | | const ty_ref = if (is_scalar and len > 1 and len <= 4) |
| 1454 | | try self.spv.vectorType(ty.vectorLen(mod), elem_ty_ref) |
| 1498 | const ty_ref = if (self.isVector(ty)) |
| 1499 | try self.spv.vectorType(len, elem_ty_ref) |
| 1455 | 1500 | else |
| 1456 | | try self.spv.arrayType(ty.vectorLen(mod), elem_ty_ref); |
| 1501 | try self.spv.arrayType(len, elem_ty_ref); |
| 1457 | 1502 | |
| 1458 | 1503 | try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); |
| 1459 | 1504 | return ty_ref; |
| ... | ... | @@ -1752,18 +1797,16 @@ const DeclGen = struct { |
| 1752 | 1797 | } |
| 1753 | 1798 | |
| 1754 | 1799 | /// This structure is used as helper for element-wise operations. It is intended |
| 1755 | | /// to be used with both vectors and single elements. |
| 1800 | /// to be used with vectors, fake vectors (arrays) and single elements. |
| 1756 | 1801 | const WipElementWise = struct { |
| 1757 | 1802 | dg: *DeclGen, |
| 1758 | 1803 | result_ty: Type, |
| 1804 | ty: Type, |
| 1759 | 1805 | /// Always in direct representation. |
| 1760 | | result_ty_ref: CacheRef, |
| 1761 | | scalar_ty: Type, |
| 1762 | | /// Always in direct representation. |
| 1763 | | scalar_ty_ref: CacheRef, |
| 1764 | | scalar_ty_id: IdRef, |
| 1765 | | /// True if the input is actually a vector type. |
| 1766 | | is_vector: bool, |
| 1806 | ty_ref: CacheRef, |
| 1807 | ty_id: IdRef, |
| 1808 | /// True if the input is an array type. |
| 1809 | is_array: bool, |
| 1767 | 1810 | /// The element-wise operation should fill these results before calling finalize(). |
| 1768 | 1811 | /// These should all be in **direct** representation! `finalize()` will convert |
| 1769 | 1812 | /// them to indirect if required. |
| ... | ... | @@ -1774,29 +1817,28 @@ const DeclGen = struct { |
| 1774 | 1817 | } |
| 1775 | 1818 | |
| 1776 | 1819 | /// Utility function to extract the element at a particular index in an |
| 1777 | | /// input vector. This type is expected to be a vector if `wip.is_vector`, and |
| 1778 | | /// a scalar otherwise. |
| 1820 | /// input array. This type is expected to be a fake vector (array) if `wip.is_array`, and |
| 1821 | /// a vector or scalar otherwise. |
| 1779 | 1822 | fn elementAt(wip: WipElementWise, ty: Type, value: IdRef, index: usize) !IdRef { |
| 1780 | 1823 | const mod = wip.dg.module; |
| 1781 | | if (wip.is_vector) { |
| 1824 | if (wip.is_array) { |
| 1782 | 1825 | assert(ty.isVector(mod)); |
| 1783 | 1826 | return try wip.dg.extractField(ty.childType(mod), value, @intCast(index)); |
| 1784 | 1827 | } else { |
| 1785 | | assert(!ty.isVector(mod)); |
| 1786 | 1828 | assert(index == 0); |
| 1787 | 1829 | return value; |
| 1788 | 1830 | } |
| 1789 | 1831 | } |
| 1790 | 1832 | |
| 1791 | | /// Turns the results of this WipElementWise into a result. This can either |
| 1792 | | /// be a vector or single element, depending on `result_ty`. |
| 1833 | /// Turns the results of this WipElementWise into a result. This can be |
| 1834 | /// vectors, fake vectors (arrays) and single elements, depending on `result_ty`. |
| 1793 | 1835 | /// After calling this function, this WIP is no longer usable. |
| 1794 | 1836 | /// Results is in `direct` representation. |
| 1795 | 1837 | fn finalize(wip: *WipElementWise) !IdRef { |
| 1796 | | if (wip.is_vector) { |
| 1838 | if (wip.is_array) { |
| 1797 | 1839 | // Convert all the constituents to indirect, as required for the array. |
| 1798 | 1840 | for (wip.results) |*result| { |
| 1799 | | result.* = try wip.dg.convertToIndirect(wip.scalar_ty, result.*); |
| 1841 | result.* = try wip.dg.convertToIndirect(wip.ty, result.*); |
| 1800 | 1842 | } |
| 1801 | 1843 | return try wip.dg.constructComposite(wip.result_ty, wip.results); |
| 1802 | 1844 | } else { |
| ... | ... | @@ -1806,33 +1848,30 @@ const DeclGen = struct { |
| 1806 | 1848 | |
| 1807 | 1849 | /// Allocate a result id at a particular index, and return it. |
| 1808 | 1850 | fn allocId(wip: *WipElementWise, index: usize) IdRef { |
| 1809 | | assert(wip.is_vector or index == 0); |
| 1851 | assert(wip.is_array or index == 0); |
| 1810 | 1852 | wip.results[index] = wip.dg.spv.allocId(); |
| 1811 | 1853 | return wip.results[index]; |
| 1812 | 1854 | } |
| 1813 | 1855 | }; |
| 1814 | 1856 | |
| 1815 | 1857 | /// Create a new element-wise operation. |
| 1816 | | fn elementWise(self: *DeclGen, result_ty: Type) !WipElementWise { |
| 1858 | fn elementWise(self: *DeclGen, result_ty: Type, force_element_wise: bool) !WipElementWise { |
| 1817 | 1859 | const mod = self.module; |
| 1818 | | // For now, this operation also reasons in terms of `.direct` representation. |
| 1819 | | const result_ty_ref = try self.resolveType(result_ty, .direct); |
| 1820 | | const is_vector = result_ty.isVector(mod); |
| 1821 | | const num_results = if (is_vector) result_ty.vectorLen(mod) else 1; |
| 1860 | const is_array = result_ty.isVector(mod) and (!self.isVector(result_ty) or force_element_wise); |
| 1861 | const num_results = if (is_array) result_ty.vectorLen(mod) else 1; |
| 1822 | 1862 | const results = try self.gpa.alloc(IdRef, num_results); |
| 1823 | | for (results) |*result| result.* = undefined; |
| 1863 | @memset(results, undefined); |
| 1824 | 1864 | |
| 1825 | | const scalar_ty = result_ty.scalarType(mod); |
| 1826 | | const scalar_ty_ref = try self.resolveType(scalar_ty, .direct); |
| 1865 | const ty = if (is_array) result_ty.scalarType(mod) else result_ty; |
| 1866 | const ty_ref = try self.resolveType(ty, .direct); |
| 1827 | 1867 | |
| 1828 | 1868 | return .{ |
| 1829 | 1869 | .dg = self, |
| 1830 | 1870 | .result_ty = result_ty, |
| 1831 | | .result_ty_ref = result_ty_ref, |
| 1832 | | .scalar_ty = scalar_ty, |
| 1833 | | .scalar_ty_ref = scalar_ty_ref, |
| 1834 | | .scalar_ty_id = self.typeId(scalar_ty_ref), |
| 1835 | | .is_vector = is_vector, |
| 1871 | .ty = ty, |
| 1872 | .ty_ref = ty_ref, |
| 1873 | .ty_id = self.typeId(ty_ref), |
| 1874 | .is_array = is_array, |
| 1836 | 1875 | .results = results, |
| 1837 | 1876 | }; |
| 1838 | 1877 | } |
| ... | ... | @@ -2312,11 +2351,11 @@ const DeclGen = struct { |
| 2312 | 2351 | } |
| 2313 | 2352 | |
| 2314 | 2353 | fn binOpSimple(self: *DeclGen, ty: Type, lhs_id: IdRef, rhs_id: IdRef, comptime opcode: Opcode) !IdRef { |
| 2315 | | var wip = try self.elementWise(ty); |
| 2354 | var wip = try self.elementWise(ty, false); |
| 2316 | 2355 | defer wip.deinit(); |
| 2317 | 2356 | for (0..wip.results.len) |i| { |
| 2318 | 2357 | try self.func.body.emit(self.spv.gpa, opcode, .{ |
| 2319 | | .id_result_type = wip.scalar_ty_id, |
| 2358 | .id_result_type = wip.ty_id, |
| 2320 | 2359 | .id_result = wip.allocId(i), |
| 2321 | 2360 | .operand_1 = try wip.elementAt(ty, lhs_id, i), |
| 2322 | 2361 | .operand_2 = try wip.elementAt(ty, rhs_id, i), |
| ... | ... | @@ -2345,7 +2384,7 @@ const DeclGen = struct { |
| 2345 | 2384 | |
| 2346 | 2385 | const result_ty = self.typeOfIndex(inst); |
| 2347 | 2386 | const shift_ty = self.typeOf(bin_op.rhs); |
| 2348 | | const scalar_shift_ty_ref = try self.resolveType(shift_ty.scalarType(mod), .direct); |
| 2387 | const shift_ty_ref = try self.resolveType(shift_ty, .direct); |
| 2349 | 2388 | |
| 2350 | 2389 | const info = self.arithmeticTypeInfo(result_ty); |
| 2351 | 2390 | switch (info.class) { |
| ... | ... | @@ -2354,7 +2393,7 @@ const DeclGen = struct { |
| 2354 | 2393 | .float, .bool => unreachable, |
| 2355 | 2394 | } |
| 2356 | 2395 | |
| 2357 | | var wip = try self.elementWise(result_ty); |
| 2396 | var wip = try self.elementWise(result_ty, false); |
| 2358 | 2397 | defer wip.deinit(); |
| 2359 | 2398 | for (wip.results, 0..) |*result_id, i| { |
| 2360 | 2399 | const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i); |
| ... | ... | @@ -2362,10 +2401,10 @@ const DeclGen = struct { |
| 2362 | 2401 | |
| 2363 | 2402 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, |
| 2364 | 2403 | // so just manually upcast it if required. |
| 2365 | | const shift_id = if (scalar_shift_ty_ref != wip.scalar_ty_ref) blk: { |
| 2404 | const shift_id = if (shift_ty_ref != wip.ty_ref) blk: { |
| 2366 | 2405 | const shift_id = self.spv.allocId(); |
| 2367 | 2406 | try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ |
| 2368 | | .id_result_type = wip.scalar_ty_id, |
| 2407 | .id_result_type = wip.ty_id, |
| 2369 | 2408 | .id_result = shift_id, |
| 2370 | 2409 | .unsigned_value = rhs_elem_id, |
| 2371 | 2410 | }); |
| ... | ... | @@ -2374,7 +2413,7 @@ const DeclGen = struct { |
| 2374 | 2413 | |
| 2375 | 2414 | const value_id = self.spv.allocId(); |
| 2376 | 2415 | const args = .{ |
| 2377 | | .id_result_type = wip.scalar_ty_id, |
| 2416 | .id_result_type = wip.ty_id, |
| 2378 | 2417 | .id_result = value_id, |
| 2379 | 2418 | .base = lhs_elem_id, |
| 2380 | 2419 | .shift = shift_id, |
| ... | ... | @@ -2386,7 +2425,7 @@ const DeclGen = struct { |
| 2386 | 2425 | try self.func.body.emit(self.spv.gpa, unsigned, args); |
| 2387 | 2426 | } |
| 2388 | 2427 | |
| 2389 | | result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, info); |
| 2428 | result_id.* = try self.normalize(wip.ty_ref, value_id, info); |
| 2390 | 2429 | } |
| 2391 | 2430 | return try wip.finalize(); |
| 2392 | 2431 | } |
| ... | ... | @@ -2405,14 +2444,14 @@ const DeclGen = struct { |
| 2405 | 2444 | fn minMax(self: *DeclGen, result_ty: Type, op: std.math.CompareOperator, lhs_id: IdRef, rhs_id: IdRef) !IdRef { |
| 2406 | 2445 | const info = self.arithmeticTypeInfo(result_ty); |
| 2407 | 2446 | |
| 2408 | | var wip = try self.elementWise(result_ty); |
| 2447 | var wip = try self.elementWise(result_ty, true); |
| 2409 | 2448 | defer wip.deinit(); |
| 2410 | 2449 | for (wip.results, 0..) |*result_id, i| { |
| 2411 | 2450 | const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i); |
| 2412 | 2451 | const rhs_elem_id = try wip.elementAt(result_ty, rhs_id, i); |
| 2413 | 2452 | |
| 2414 | 2453 | // TODO: Use fmin for OpenCL |
| 2415 | | const cmp_id = try self.cmp(op, Type.bool, wip.scalar_ty, lhs_elem_id, rhs_elem_id); |
| 2454 | const cmp_id = try self.cmp(op, Type.bool, wip.ty, lhs_elem_id, rhs_elem_id); |
| 2416 | 2455 | const selection_id = switch (info.class) { |
| 2417 | 2456 | .float => blk: { |
| 2418 | 2457 | // cmp uses OpFOrd. When we have 0 [<>] nan this returns false, |
| ... | ... | @@ -2440,7 +2479,7 @@ const DeclGen = struct { |
| 2440 | 2479 | |
| 2441 | 2480 | result_id.* = self.spv.allocId(); |
| 2442 | 2481 | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ |
| 2443 | | .id_result_type = wip.scalar_ty_id, |
| 2482 | .id_result_type = wip.ty_id, |
| 2444 | 2483 | .id_result = result_id.*, |
| 2445 | 2484 | .condition = selection_id, |
| 2446 | 2485 | .object_1 = lhs_elem_id, |
| ... | ... | @@ -2545,7 +2584,7 @@ const DeclGen = struct { |
| 2545 | 2584 | .bool => unreachable, |
| 2546 | 2585 | }; |
| 2547 | 2586 | |
| 2548 | | var wip = try self.elementWise(ty); |
| 2587 | var wip = try self.elementWise(ty, false); |
| 2549 | 2588 | defer wip.deinit(); |
| 2550 | 2589 | for (wip.results, 0..) |*result_id, i| { |
| 2551 | 2590 | const lhs_elem_id = try wip.elementAt(ty, lhs_id, i); |
| ... | ... | @@ -2553,7 +2592,7 @@ const DeclGen = struct { |
| 2553 | 2592 | |
| 2554 | 2593 | const value_id = self.spv.allocId(); |
| 2555 | 2594 | const operands = .{ |
| 2556 | | .id_result_type = wip.scalar_ty_id, |
| 2595 | .id_result_type = wip.ty_id, |
| 2557 | 2596 | .id_result = value_id, |
| 2558 | 2597 | .operand_1 = lhs_elem_id, |
| 2559 | 2598 | .operand_2 = rhs_elem_id, |
| ... | ... | @@ -2568,7 +2607,7 @@ const DeclGen = struct { |
| 2568 | 2607 | |
| 2569 | 2608 | // TODO: Trap on overflow? Probably going to be annoying. |
| 2570 | 2609 | // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. |
| 2571 | | result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, info); |
| 2610 | result_id.* = try self.normalize(wip.ty_ref, value_id, info); |
| 2572 | 2611 | } |
| 2573 | 2612 | |
| 2574 | 2613 | return try wip.finalize(); |
| ... | ... | @@ -2582,12 +2621,12 @@ const DeclGen = struct { |
| 2582 | 2621 | const operand_id = try self.resolve(ty_op.operand); |
| 2583 | 2622 | // Note: operand_ty may be signed, while ty is always unsigned! |
| 2584 | 2623 | const operand_ty = self.typeOf(ty_op.operand); |
| 2585 | | const ty = self.typeOfIndex(inst); |
| 2586 | | const info = self.arithmeticTypeInfo(ty); |
| 2624 | const result_ty = self.typeOfIndex(inst); |
| 2625 | const info = self.arithmeticTypeInfo(result_ty); |
| 2587 | 2626 | const operand_scalar_ty = operand_ty.scalarType(mod); |
| 2588 | 2627 | const operand_scalar_ty_ref = try self.resolveType(operand_scalar_ty, .direct); |
| 2589 | 2628 | |
| 2590 | | var wip = try self.elementWise(ty); |
| 2629 | var wip = try self.elementWise(result_ty, true); |
| 2591 | 2630 | defer wip.deinit(); |
| 2592 | 2631 | |
| 2593 | 2632 | const zero_id = switch (info.class) { |
| ... | ... | @@ -2615,7 +2654,7 @@ const DeclGen = struct { |
| 2615 | 2654 | .composite_integer => unreachable, // TODO |
| 2616 | 2655 | .bool => unreachable, |
| 2617 | 2656 | } |
| 2618 | | const neg_norm_id = try self.normalize(wip.scalar_ty_ref, neg_id, info); |
| 2657 | const neg_norm_id = try self.normalize(wip.ty_ref, neg_id, info); |
| 2619 | 2658 | |
| 2620 | 2659 | const gt_zero_id = try self.cmp(.gt, Type.bool, operand_scalar_ty, elem_id, zero_id); |
| 2621 | 2660 | const abs_id = self.spv.allocId(); |
| ... | ... | @@ -2627,7 +2666,7 @@ const DeclGen = struct { |
| 2627 | 2666 | .object_2 = neg_norm_id, |
| 2628 | 2667 | }); |
| 2629 | 2668 | // For Shader, we may need to cast from signed to unsigned here. |
| 2630 | | result_id.* = try self.bitCast(wip.scalar_ty, operand_scalar_ty, abs_id); |
| 2669 | result_id.* = try self.bitCast(wip.ty, operand_scalar_ty, abs_id); |
| 2631 | 2670 | } |
| 2632 | 2671 | return try wip.finalize(); |
| 2633 | 2672 | } |
| ... | ... | @@ -2641,6 +2680,7 @@ const DeclGen = struct { |
| 2641 | 2680 | ) !?IdRef { |
| 2642 | 2681 | if (self.liveness.isUnused(inst)) return null; |
| 2643 | 2682 | |
| 2683 | const mod = self.module; |
| 2644 | 2684 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2645 | 2685 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2646 | 2686 | const lhs = try self.resolve(extra.lhs); |
| ... | ... | @@ -2651,6 +2691,10 @@ const DeclGen = struct { |
| 2651 | 2691 | const ov_ty = result_ty.structFieldType(1, self.module); |
| 2652 | 2692 | |
| 2653 | 2693 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 2694 | const cmp_ty_ref = if (self.isVector(operand_ty)) |
| 2695 | try self.spv.vectorType(operand_ty.vectorLen(mod), bool_ty_ref) |
| 2696 | else |
| 2697 | bool_ty_ref; |
| 2654 | 2698 | |
| 2655 | 2699 | const info = self.arithmeticTypeInfo(operand_ty); |
| 2656 | 2700 | switch (info.class) { |
| ... | ... | @@ -2659,9 +2703,9 @@ const DeclGen = struct { |
| 2659 | 2703 | .float, .bool => unreachable, |
| 2660 | 2704 | } |
| 2661 | 2705 | |
| 2662 | | var wip_result = try self.elementWise(operand_ty); |
| 2706 | var wip_result = try self.elementWise(operand_ty, false); |
| 2663 | 2707 | defer wip_result.deinit(); |
| 2664 | | var wip_ov = try self.elementWise(ov_ty); |
| 2708 | var wip_ov = try self.elementWise(ov_ty, false); |
| 2665 | 2709 | defer wip_ov.deinit(); |
| 2666 | 2710 | for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { |
| 2667 | 2711 | const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); |
| ... | ... | @@ -2671,14 +2715,14 @@ const DeclGen = struct { |
| 2671 | 2715 | const value_id = self.spv.allocId(); |
| 2672 | 2716 | |
| 2673 | 2717 | try self.func.body.emit(self.spv.gpa, add, .{ |
| 2674 | | .id_result_type = wip_result.scalar_ty_id, |
| 2718 | .id_result_type = wip_result.ty_id, |
| 2675 | 2719 | .id_result = value_id, |
| 2676 | 2720 | .operand_1 = lhs_elem_id, |
| 2677 | 2721 | .operand_2 = rhs_elem_id, |
| 2678 | 2722 | }); |
| 2679 | 2723 | |
| 2680 | 2724 | // Normalize the result so that the comparisons go well |
| 2681 | | result_id.* = try self.normalize(wip_result.scalar_ty_ref, value_id, info); |
| 2725 | result_id.* = try self.normalize(wip_result.ty_ref, value_id, info); |
| 2682 | 2726 | |
| 2683 | 2727 | const overflowed_id = switch (info.signedness) { |
| 2684 | 2728 | .unsigned => blk: { |
| ... | ... | @@ -2686,7 +2730,7 @@ const DeclGen = struct { |
| 2686 | 2730 | // For subtraction the conditions need to be swapped. |
| 2687 | 2731 | const overflowed_id = self.spv.allocId(); |
| 2688 | 2732 | try self.func.body.emit(self.spv.gpa, ucmp, .{ |
| 2689 | | .id_result_type = self.typeId(bool_ty_ref), |
| 2733 | .id_result_type = self.typeId(cmp_ty_ref), |
| 2690 | 2734 | .id_result = overflowed_id, |
| 2691 | 2735 | .operand_1 = result_id.*, |
| 2692 | 2736 | .operand_2 = lhs_elem_id, |
| ... | ... | @@ -2712,9 +2756,9 @@ const DeclGen = struct { |
| 2712 | 2756 | // = (rhs < 0) == (lhs > value) |
| 2713 | 2757 | |
| 2714 | 2758 | const rhs_lt_zero_id = self.spv.allocId(); |
| 2715 | | const zero_id = try self.constInt(wip_result.scalar_ty_ref, 0); |
| 2759 | const zero_id = try self.constInt(wip_result.ty_ref, 0); |
| 2716 | 2760 | try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ |
| 2717 | | .id_result_type = self.typeId(bool_ty_ref), |
| 2761 | .id_result_type = self.typeId(cmp_ty_ref), |
| 2718 | 2762 | .id_result = rhs_lt_zero_id, |
| 2719 | 2763 | .operand_1 = rhs_elem_id, |
| 2720 | 2764 | .operand_2 = zero_id, |
| ... | ... | @@ -2722,7 +2766,7 @@ const DeclGen = struct { |
| 2722 | 2766 | |
| 2723 | 2767 | const value_gt_lhs_id = self.spv.allocId(); |
| 2724 | 2768 | try self.func.body.emit(self.spv.gpa, scmp, .{ |
| 2725 | | .id_result_type = self.typeId(bool_ty_ref), |
| 2769 | .id_result_type = self.typeId(cmp_ty_ref), |
| 2726 | 2770 | .id_result = value_gt_lhs_id, |
| 2727 | 2771 | .operand_1 = lhs_elem_id, |
| 2728 | 2772 | .operand_2 = result_id.*, |
| ... | ... | @@ -2730,7 +2774,7 @@ const DeclGen = struct { |
| 2730 | 2774 | |
| 2731 | 2775 | const overflowed_id = self.spv.allocId(); |
| 2732 | 2776 | try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{ |
| 2733 | | .id_result_type = self.typeId(bool_ty_ref), |
| 2777 | .id_result_type = self.typeId(cmp_ty_ref), |
| 2734 | 2778 | .id_result = overflowed_id, |
| 2735 | 2779 | .operand_1 = rhs_lt_zero_id, |
| 2736 | 2780 | .operand_2 = value_gt_lhs_id, |
| ... | ... | @@ -2739,7 +2783,7 @@ const DeclGen = struct { |
| 2739 | 2783 | }, |
| 2740 | 2784 | }; |
| 2741 | 2785 | |
| 2742 | | ov_id.* = try self.intFromBool(wip_ov.scalar_ty_ref, overflowed_id); |
| 2786 | ov_id.* = try self.intFromBool(wip_ov.ty_ref, overflowed_id); |
| 2743 | 2787 | } |
| 2744 | 2788 | |
| 2745 | 2789 | return try self.constructComposite( |
| ... | ... | @@ -2759,11 +2803,15 @@ const DeclGen = struct { |
| 2759 | 2803 | const result_ty = self.typeOfIndex(inst); |
| 2760 | 2804 | const operand_ty = self.typeOf(extra.lhs); |
| 2761 | 2805 | const shift_ty = self.typeOf(extra.rhs); |
| 2762 | | const scalar_shift_ty_ref = try self.resolveType(shift_ty.scalarType(mod), .direct); |
| 2806 | const shift_ty_ref = try self.resolveType(shift_ty, .direct); |
| 2763 | 2807 | |
| 2764 | 2808 | const ov_ty = result_ty.structFieldType(1, self.module); |
| 2765 | 2809 | |
| 2766 | 2810 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 2811 | const cmp_ty_ref = if (self.isVector(operand_ty)) |
| 2812 | try self.spv.vectorType(operand_ty.vectorLen(mod), bool_ty_ref) |
| 2813 | else |
| 2814 | bool_ty_ref; |
| 2767 | 2815 | |
| 2768 | 2816 | const info = self.arithmeticTypeInfo(operand_ty); |
| 2769 | 2817 | switch (info.class) { |
| ... | ... | @@ -2772,9 +2820,9 @@ const DeclGen = struct { |
| 2772 | 2820 | .float, .bool => unreachable, |
| 2773 | 2821 | } |
| 2774 | 2822 | |
| 2775 | | var wip_result = try self.elementWise(operand_ty); |
| 2823 | var wip_result = try self.elementWise(operand_ty, false); |
| 2776 | 2824 | defer wip_result.deinit(); |
| 2777 | | var wip_ov = try self.elementWise(ov_ty); |
| 2825 | var wip_ov = try self.elementWise(ov_ty, false); |
| 2778 | 2826 | defer wip_ov.deinit(); |
| 2779 | 2827 | for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { |
| 2780 | 2828 | const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); |
| ... | ... | @@ -2782,10 +2830,10 @@ const DeclGen = struct { |
| 2782 | 2830 | |
| 2783 | 2831 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, |
| 2784 | 2832 | // so just manually upcast it if required. |
| 2785 | | const shift_id = if (scalar_shift_ty_ref != wip_result.scalar_ty_ref) blk: { |
| 2833 | const shift_id = if (shift_ty_ref != wip_result.ty_ref) blk: { |
| 2786 | 2834 | const shift_id = self.spv.allocId(); |
| 2787 | 2835 | try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ |
| 2788 | | .id_result_type = wip_result.scalar_ty_id, |
| 2836 | .id_result_type = wip_result.ty_id, |
| 2789 | 2837 | .id_result = shift_id, |
| 2790 | 2838 | .unsigned_value = rhs_elem_id, |
| 2791 | 2839 | }); |
| ... | ... | @@ -2794,18 +2842,18 @@ const DeclGen = struct { |
| 2794 | 2842 | |
| 2795 | 2843 | const value_id = self.spv.allocId(); |
| 2796 | 2844 | try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ |
| 2797 | | .id_result_type = wip_result.scalar_ty_id, |
| 2845 | .id_result_type = wip_result.ty_id, |
| 2798 | 2846 | .id_result = value_id, |
| 2799 | 2847 | .base = lhs_elem_id, |
| 2800 | 2848 | .shift = shift_id, |
| 2801 | 2849 | }); |
| 2802 | | result_id.* = try self.normalize(wip_result.scalar_ty_ref, value_id, info); |
| 2850 | result_id.* = try self.normalize(wip_result.ty_ref, value_id, info); |
| 2803 | 2851 | |
| 2804 | 2852 | const right_shift_id = self.spv.allocId(); |
| 2805 | 2853 | switch (info.signedness) { |
| 2806 | 2854 | .signed => { |
| 2807 | 2855 | try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{ |
| 2808 | | .id_result_type = wip_result.scalar_ty_id, |
| 2856 | .id_result_type = wip_result.ty_id, |
| 2809 | 2857 | .id_result = right_shift_id, |
| 2810 | 2858 | .base = result_id.*, |
| 2811 | 2859 | .shift = shift_id, |
| ... | ... | @@ -2813,7 +2861,7 @@ const DeclGen = struct { |
| 2813 | 2861 | }, |
| 2814 | 2862 | .unsigned => { |
| 2815 | 2863 | try self.func.body.emit(self.spv.gpa, .OpShiftRightLogical, .{ |
| 2816 | | .id_result_type = wip_result.scalar_ty_id, |
| 2864 | .id_result_type = wip_result.ty_id, |
| 2817 | 2865 | .id_result = right_shift_id, |
| 2818 | 2866 | .base = result_id.*, |
| 2819 | 2867 | .shift = shift_id, |
| ... | ... | @@ -2823,13 +2871,13 @@ const DeclGen = struct { |
| 2823 | 2871 | |
| 2824 | 2872 | const overflowed_id = self.spv.allocId(); |
| 2825 | 2873 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ |
| 2826 | | .id_result_type = self.typeId(bool_ty_ref), |
| 2874 | .id_result_type = self.typeId(cmp_ty_ref), |
| 2827 | 2875 | .id_result = overflowed_id, |
| 2828 | 2876 | .operand_1 = lhs_elem_id, |
| 2829 | 2877 | .operand_2 = right_shift_id, |
| 2830 | 2878 | }); |
| 2831 | 2879 | |
| 2832 | | ov_id.* = try self.intFromBool(wip_ov.scalar_ty_ref, overflowed_id); |
| 2880 | ov_id.* = try self.intFromBool(wip_ov.ty_ref, overflowed_id); |
| 2833 | 2881 | } |
| 2834 | 2882 | |
| 2835 | 2883 | return try self.constructComposite( |
| ... | ... | @@ -2853,19 +2901,19 @@ const DeclGen = struct { |
| 2853 | 2901 | const info = self.arithmeticTypeInfo(ty); |
| 2854 | 2902 | assert(info.class == .float); // .mul_add is only emitted for floats |
| 2855 | 2903 | |
| 2856 | | var wip = try self.elementWise(ty); |
| 2904 | var wip = try self.elementWise(ty, false); |
| 2857 | 2905 | defer wip.deinit(); |
| 2858 | 2906 | for (0..wip.results.len) |i| { |
| 2859 | 2907 | const mul_result = self.spv.allocId(); |
| 2860 | 2908 | try self.func.body.emit(self.spv.gpa, .OpFMul, .{ |
| 2861 | | .id_result_type = wip.scalar_ty_id, |
| 2909 | .id_result_type = wip.ty_id, |
| 2862 | 2910 | .id_result = mul_result, |
| 2863 | 2911 | .operand_1 = try wip.elementAt(ty, mulend1, i), |
| 2864 | 2912 | .operand_2 = try wip.elementAt(ty, mulend2, i), |
| 2865 | 2913 | }); |
| 2866 | 2914 | |
| 2867 | 2915 | try self.func.body.emit(self.spv.gpa, .OpFAdd, .{ |
| 2868 | | .id_result_type = wip.scalar_ty_id, |
| 2916 | .id_result_type = wip.ty_id, |
| 2869 | 2917 | .id_result = wip.allocId(i), |
| 2870 | 2918 | .operand_1 = mul_result, |
| 2871 | 2919 | .operand_2 = try wip.elementAt(ty, addend, i), |
| ... | ... | @@ -2879,11 +2927,9 @@ const DeclGen = struct { |
| 2879 | 2927 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 2880 | 2928 | const operand_id = try self.resolve(ty_op.operand); |
| 2881 | 2929 | const result_ty = self.typeOfIndex(inst); |
| 2882 | | var wip = try self.elementWise(result_ty); |
| 2930 | var wip = try self.elementWise(result_ty, true); |
| 2883 | 2931 | defer wip.deinit(); |
| 2884 | | for (wip.results) |*result_id| { |
| 2885 | | result_id.* = operand_id; |
| 2886 | | } |
| 2932 | @memset(wip.results, operand_id); |
| 2887 | 2933 | return try wip.finalize(); |
| 2888 | 2934 | } |
| 2889 | 2935 | |
| ... | ... | @@ -2965,20 +3011,20 @@ const DeclGen = struct { |
| 2965 | 3011 | |
| 2966 | 3012 | const ty = self.typeOfIndex(inst); |
| 2967 | 3013 | |
| 2968 | | var wip = try self.elementWise(ty); |
| 3014 | var wip = try self.elementWise(ty, true); |
| 2969 | 3015 | defer wip.deinit(); |
| 2970 | 3016 | for (wip.results, 0..) |*result_id, i| { |
| 2971 | 3017 | const elem = try mask.elemValue(mod, i); |
| 2972 | 3018 | if (elem.isUndef(mod)) { |
| 2973 | | result_id.* = try self.spv.constUndef(wip.scalar_ty_ref); |
| 3019 | result_id.* = try self.spv.constUndef(wip.ty_ref); |
| 2974 | 3020 | continue; |
| 2975 | 3021 | } |
| 2976 | 3022 | |
| 2977 | 3023 | const index = elem.toSignedInt(mod); |
| 2978 | 3024 | if (index >= 0) { |
| 2979 | | result_id.* = try self.extractField(wip.scalar_ty, a, @intCast(index)); |
| 3025 | result_id.* = try self.extractField(wip.ty, a, @intCast(index)); |
| 2980 | 3026 | } else { |
| 2981 | | result_id.* = try self.extractField(wip.scalar_ty, b, @intCast(~index)); |
| 3027 | result_id.* = try self.extractField(wip.ty, b, @intCast(~index)); |
| 2982 | 3028 | } |
| 2983 | 3029 | } |
| 2984 | 3030 | return try wip.finalize(); |
| ... | ... | @@ -3188,7 +3234,7 @@ const DeclGen = struct { |
| 3188 | 3234 | return result_id; |
| 3189 | 3235 | }, |
| 3190 | 3236 | .Vector => { |
| 3191 | | var wip = try self.elementWise(result_ty); |
| 3237 | var wip = try self.elementWise(result_ty, true); |
| 3192 | 3238 | defer wip.deinit(); |
| 3193 | 3239 | const scalar_ty = ty.scalarType(mod); |
| 3194 | 3240 | for (wip.results, 0..) |*result_id, i| { |
| ... | ... | @@ -3374,19 +3420,19 @@ const DeclGen = struct { |
| 3374 | 3420 | return operand_id; |
| 3375 | 3421 | } |
| 3376 | 3422 | |
| 3377 | | var wip = try self.elementWise(dst_ty); |
| 3423 | var wip = try self.elementWise(dst_ty, false); |
| 3378 | 3424 | defer wip.deinit(); |
| 3379 | 3425 | for (wip.results, 0..) |*result_id, i| { |
| 3380 | 3426 | const elem_id = try wip.elementAt(src_ty, operand_id, i); |
| 3381 | 3427 | const value_id = self.spv.allocId(); |
| 3382 | 3428 | switch (dst_info.signedness) { |
| 3383 | 3429 | .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{ |
| 3384 | | .id_result_type = wip.scalar_ty_id, |
| 3430 | .id_result_type = wip.ty_id, |
| 3385 | 3431 | .id_result = value_id, |
| 3386 | 3432 | .signed_value = elem_id, |
| 3387 | 3433 | }), |
| 3388 | 3434 | .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ |
| 3389 | | .id_result_type = wip.scalar_ty_id, |
| 3435 | .id_result_type = wip.ty_id, |
| 3390 | 3436 | .id_result = value_id, |
| 3391 | 3437 | .unsigned_value = elem_id, |
| 3392 | 3438 | }), |
| ... | ... | @@ -3397,7 +3443,7 @@ const DeclGen = struct { |
| 3397 | 3443 | // type, we don't need to normalize when growing the type. The |
| 3398 | 3444 | // representation is already the same. |
| 3399 | 3445 | if (dst_info.bits < src_info.bits) { |
| 3400 | | result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, dst_info); |
| 3446 | result_id.* = try self.normalize(wip.ty_ref, value_id, dst_info); |
| 3401 | 3447 | } else { |
| 3402 | 3448 | result_id.* = value_id; |
| 3403 | 3449 | } |
| ... | ... | @@ -3482,11 +3528,11 @@ const DeclGen = struct { |
| 3482 | 3528 | const operand_id = try self.resolve(un_op); |
| 3483 | 3529 | const result_ty = self.typeOfIndex(inst); |
| 3484 | 3530 | |
| 3485 | | var wip = try self.elementWise(result_ty); |
| 3531 | var wip = try self.elementWise(result_ty, false); |
| 3486 | 3532 | defer wip.deinit(); |
| 3487 | 3533 | for (wip.results, 0..) |*result_id, i| { |
| 3488 | 3534 | const elem_id = try wip.elementAt(Type.bool, operand_id, i); |
| 3489 | | result_id.* = try self.intFromBool(wip.scalar_ty_ref, elem_id); |
| 3535 | result_id.* = try self.intFromBool(wip.ty_ref, elem_id); |
| 3490 | 3536 | } |
| 3491 | 3537 | return try wip.finalize(); |
| 3492 | 3538 | } |
| ... | ... | @@ -3515,12 +3561,12 @@ const DeclGen = struct { |
| 3515 | 3561 | const result_ty = self.typeOfIndex(inst); |
| 3516 | 3562 | const info = self.arithmeticTypeInfo(result_ty); |
| 3517 | 3563 | |
| 3518 | | var wip = try self.elementWise(result_ty); |
| 3564 | var wip = try self.elementWise(result_ty, false); |
| 3519 | 3565 | defer wip.deinit(); |
| 3520 | 3566 | |
| 3521 | 3567 | for (0..wip.results.len) |i| { |
| 3522 | 3568 | const args = .{ |
| 3523 | | .id_result_type = wip.scalar_ty_id, |
| 3569 | .id_result_type = wip.ty_id, |
| 3524 | 3570 | .id_result = wip.allocId(i), |
| 3525 | 3571 | .operand = try wip.elementAt(result_ty, operand_id, i), |
| 3526 | 3572 | }; |
| ... | ... | @@ -3563,10 +3609,7 @@ const DeclGen = struct { |
| 3563 | 3609 | // Convert the pointer-to-array to a pointer to the first element. |
| 3564 | 3610 | try self.accessChain(elem_ptr_ty_ref, array_ptr_id, &.{0}); |
| 3565 | 3611 | |
| 3566 | | return try self.constructComposite( |
| 3567 | | slice_ty, |
| 3568 | | &.{ elem_ptr_id, len_id }, |
| 3569 | | ); |
| 3612 | return try self.constructComposite(slice_ty, &.{ elem_ptr_id, len_id }); |
| 3570 | 3613 | } |
| 3571 | 3614 | |
| 3572 | 3615 | fn airSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -3580,10 +3623,7 @@ const DeclGen = struct { |
| 3580 | 3623 | |
| 3581 | 3624 | // Note: Types should not need to be converted to direct, these types |
| 3582 | 3625 | // dont need to be converted. |
| 3583 | | return try self.constructComposite( |
| 3584 | | slice_ty, |
| 3585 | | &.{ ptr_id, len_id }, |
| 3586 | | ); |
| 3626 | return try self.constructComposite(slice_ty, &.{ ptr_id, len_id }); |
| 3587 | 3627 | } |
| 3588 | 3628 | |
| 3589 | 3629 | fn airAggregateInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -3641,9 +3681,9 @@ const DeclGen = struct { |
| 3641 | 3681 | const elem_ids = try self.gpa.alloc(IdRef, n_elems); |
| 3642 | 3682 | defer self.gpa.free(elem_ids); |
| 3643 | 3683 | |
| 3644 | | for (elements, 0..) |element, i| { |
| 3684 | for (elements, elem_ids) |element, *elem_id| { |
| 3645 | 3685 | const id = try self.resolve(element); |
| 3646 | | elem_ids[i] = try self.convertToIndirect(result_ty.childType(mod), id); |
| 3686 | elem_id.* = try self.convertToIndirect(result_ty.childType(mod), id); |
| 3647 | 3687 | } |
| 3648 | 3688 | |
| 3649 | 3689 | return try self.constructComposite(result_ty, elem_ids); |
| ... | ... | @@ -3654,9 +3694,9 @@ const DeclGen = struct { |
| 3654 | 3694 | const elem_ids = try self.gpa.alloc(IdRef, n_elems); |
| 3655 | 3695 | defer self.gpa.free(elem_ids); |
| 3656 | 3696 | |
| 3657 | | for (elements, 0..) |element, i| { |
| 3697 | for (elements, elem_ids) |element, *elem_id| { |
| 3658 | 3698 | const id = try self.resolve(element); |
| 3659 | | elem_ids[i] = try self.convertToIndirect(array_info.elem_type, id); |
| 3699 | elem_id.* = try self.convertToIndirect(array_info.elem_type, id); |
| 3660 | 3700 | } |
| 3661 | 3701 | |
| 3662 | 3702 | if (array_info.sentinel) |sentinel_val| { |