| ... | @@ -1760,6 +1760,92 @@ const DeclGen = struct { | ... | @@ -1760,6 +1760,92 @@ const DeclGen = struct { |
| 1760 | return union_layout; | 1760 | return union_layout; |
| 1761 | } | 1761 | } |
| 1762 | | 1762 | |
| | 1763 | /// This structure is used as helper for element-wise operations. It is intended |
| | 1764 | /// to be used with both vectors and single elements. |
| | 1765 | const WipElementWise = struct { |
| | 1766 | dg: *DeclGen, |
| | 1767 | result_ty: Type, |
| | 1768 | /// Always in direct representation. |
| | 1769 | result_ty_ref: CacheRef, |
| | 1770 | scalar_ty: Type, |
| | 1771 | /// Always in direct representation. |
| | 1772 | scalar_ty_ref: CacheRef, |
| | 1773 | scalar_ty_id: IdRef, |
| | 1774 | /// True if the input is actually a vector type. |
| | 1775 | is_vector: bool, |
| | 1776 | /// The element-wise operation should fill these results before calling finalize(). |
| | 1777 | /// These should all be in **direct** representation! `finalize()` will convert |
| | 1778 | /// them to indirect if required. |
| | 1779 | results: []IdRef, |
| | 1780 | |
| | 1781 | fn deinit(wip: *WipElementWise) void { |
| | 1782 | wip.dg.gpa.free(wip.results); |
| | 1783 | } |
| | 1784 | |
| | 1785 | /// Utility function to extract the element at a particular index in an |
| | 1786 | /// input vector. This type is expected to be a vector if `wip.is_vector`, and |
| | 1787 | /// a scalar otherwise. |
| | 1788 | fn elementAt(wip: WipElementWise, ty: Type, value: IdRef, index: usize) !IdRef { |
| | 1789 | const mod = wip.dg.module; |
| | 1790 | if (wip.is_vector) { |
| | 1791 | assert(ty.isVector(mod)); |
| | 1792 | return try wip.dg.extractField(ty, value, @intCast(index)); |
| | 1793 | } else { |
| | 1794 | assert(!ty.isVector(mod)); |
| | 1795 | assert(index == 0); |
| | 1796 | return value; |
| | 1797 | } |
| | 1798 | } |
| | 1799 | |
| | 1800 | /// Turns the results of this WipElementWise into a result. This can either |
| | 1801 | /// be a vector or single element, depending on `result_ty`. |
| | 1802 | /// After calling this function, this WIP is no longer usable. |
| | 1803 | /// Results is in `direct` representation. |
| | 1804 | fn finalize(wip: *WipElementWise) !IdRef { |
| | 1805 | if (wip.is_vector) { |
| | 1806 | // Convert all the constituents to indirect, as required for the array. |
| | 1807 | for (wip.results) |*result| { |
| | 1808 | result.* = try wip.dg.convertToIndirect(wip.scalar_ty, result.*); |
| | 1809 | } |
| | 1810 | return try wip.dg.constructArray(wip.result_ty, wip.results); |
| | 1811 | } else { |
| | 1812 | return wip.results[0]; |
| | 1813 | } |
| | 1814 | } |
| | 1815 | |
| | 1816 | /// Allocate a result id at a particular index, and return it. |
| | 1817 | fn allocId(wip: *WipElementWise, index: usize) IdRef { |
| | 1818 | assert(wip.is_vector or index == 0); |
| | 1819 | wip.results[index] = wip.dg.spv.allocId(); |
| | 1820 | return wip.results[index]; |
| | 1821 | } |
| | 1822 | }; |
| | 1823 | |
| | 1824 | /// Create a new element-wise operation. |
| | 1825 | fn elementWise(self: *DeclGen, result_ty: Type) !WipElementWise { |
| | 1826 | const mod = self.module; |
| | 1827 | // For now, this operation also reasons in terms of `.direct` representation. |
| | 1828 | const result_ty_ref = try self.resolveType(result_ty, .direct); |
| | 1829 | const is_vector = result_ty.isVector(mod); |
| | 1830 | const num_results = if (is_vector) result_ty.vectorLen(mod) else 1; |
| | 1831 | const results = try self.gpa.alloc(IdRef, num_results); |
| | 1832 | for (results) |*result| result.* = undefined; |
| | 1833 | |
| | 1834 | const scalar_ty = if (is_vector) result_ty.childType(mod) else result_ty; |
| | 1835 | const scalar_ty_ref = try self.resolveType(scalar_ty, .direct); |
| | 1836 | |
| | 1837 | return .{ |
| | 1838 | .dg = self, |
| | 1839 | .result_ty = result_ty, |
| | 1840 | .result_ty_ref = result_ty_ref, |
| | 1841 | .scalar_ty = scalar_ty, |
| | 1842 | .scalar_ty_ref = scalar_ty_ref, |
| | 1843 | .scalar_ty_id = self.typeId(scalar_ty_ref), |
| | 1844 | .is_vector = is_vector, |
| | 1845 | .results = results, |
| | 1846 | }; |
| | 1847 | } |
| | 1848 | |
| 1763 | /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure. | 1849 | /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure. |
| 1764 | /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry- | 1850 | /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry- |
| 1765 | /// points. The test executor will then be able to invoke these to run the tests. | 1851 | /// points. The test executor will then be able to invoke these to run the tests. |
| ... | @@ -2214,34 +2300,17 @@ const DeclGen = struct { | ... | @@ -2214,34 +2300,17 @@ const DeclGen = struct { |
| 2214 | } | 2300 | } |
| 2215 | | 2301 | |
| 2216 | fn binOpSimple(self: *DeclGen, ty: Type, lhs_id: IdRef, rhs_id: IdRef, comptime opcode: Opcode) !IdRef { | 2302 | fn binOpSimple(self: *DeclGen, ty: Type, lhs_id: IdRef, rhs_id: IdRef, comptime opcode: Opcode) !IdRef { |
| 2217 | const mod = self.module; | 2303 | var wip = try self.elementWise(ty); |
| 2218 | | 2304 | defer wip.deinit(); |
| 2219 | if (ty.isVector(mod)) { | 2305 | for (0..wip.results.len) |i| { |
| 2220 | const child_ty = ty.childType(mod); | 2306 | try self.func.body.emit(self.spv.gpa, opcode, .{ |
| 2221 | const vector_len = ty.vectorLen(mod); | 2307 | .id_result_type = wip.scalar_ty_id, |
| 2222 | | 2308 | .id_result = wip.allocId(i), |
| 2223 | const constituents = try self.gpa.alloc(IdRef, vector_len); | 2309 | .operand_1 = try wip.elementAt(ty, lhs_id, i), |
| 2224 | defer self.gpa.free(constituents); | 2310 | .operand_2 = try wip.elementAt(ty, rhs_id, i), |
| 2225 | | 2311 | }); |
| 2226 | for (constituents, 0..) |*constituent, i| { | | |
| 2227 | const lhs_index_id = try self.extractField(child_ty, lhs_id, @intCast(i)); | | |
| 2228 | const rhs_index_id = try self.extractField(child_ty, rhs_id, @intCast(i)); | | |
| 2229 | const result_id = try self.binOpSimple(child_ty, lhs_index_id, rhs_index_id, opcode); | | |
| 2230 | constituent.* = try self.convertToIndirect(child_ty, result_id); | | |
| 2231 | } | | |
| 2232 | | | |
| 2233 | return try self.constructArray(ty, constituents); | | |
| 2234 | } | 2312 | } |
| 2235 | | 2313 | return try wip.finalize(); |
| 2236 | const result_id = self.spv.allocId(); | | |
| 2237 | const result_type_id = try self.resolveTypeId(ty); | | |
| 2238 | try self.func.body.emit(self.spv.gpa, opcode, .{ | | |
| 2239 | .id_result_type = result_type_id, | | |
| 2240 | .id_result = result_id, | | |
| 2241 | .operand_1 = lhs_id, | | |
| 2242 | .operand_2 = rhs_id, | | |
| 2243 | }); | | |
| 2244 | return result_id; | | |
| 2245 | } | 2314 | } |
| 2246 | | 2315 | |
| 2247 | fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef { | 2316 | fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef { |