| ... | @@ -1782,6 +1782,19 @@ const DeclGen = struct { | ... | @@ -1782,6 +1782,19 @@ 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 | |
| 1785 | /// Utility function to extract the element at a particular index in an | 1798 | /// 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 | 1799 | /// input vector. This type is expected to be a vector if `wip.is_vector`, and |
| 1787 | /// a scalar otherwise. | 1800 | /// a scalar otherwise. |
| ... | @@ -1789,7 +1802,7 @@ const DeclGen = struct { | ... | @@ -1789,7 +1802,7 @@ const DeclGen = struct { |
| 1789 | const mod = wip.dg.module; | 1802 | const mod = wip.dg.module; |
| 1790 | if (wip.is_vector) { | 1803 | if (wip.is_vector) { |
| 1791 | assert(ty.isVector(mod)); | 1804 | assert(ty.isVector(mod)); |
| 1792 | return try wip.dg.extractField(ty, value, @intCast(index)); | 1805 | return try wip.dg.extractField(ty.childType(mod), value, @intCast(index)); |
| 1793 | } else { | 1806 | } else { |
| 1794 | assert(!ty.isVector(mod)); | 1807 | assert(!ty.isVector(mod)); |
| 1795 | assert(index == 0); | 1808 | assert(index == 0); |
| ... | @@ -2331,36 +2344,45 @@ const DeclGen = struct { | ... | @@ -2331,36 +2344,45 @@ const DeclGen = struct { |
| 2331 | const lhs_id = try self.resolve(bin_op.lhs); | 2344 | const lhs_id = try self.resolve(bin_op.lhs); |
| 2332 | const rhs_id = try self.resolve(bin_op.rhs); | 2345 | const rhs_id = try self.resolve(bin_op.rhs); |
| 2333 | const result_ty = self.typeOfIndex(inst); | 2346 | const result_ty = self.typeOfIndex(inst); |
| 2334 | const result_ty_ref = try self.resolveType(result_ty, .direct); | | |
| 2335 | | | |
| 2336 | const result_id = self.spv.allocId(); | | |
| 2337 | | 2347 | |
| 2338 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, | 2348 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, |
| 2339 | // so just manually upcast it if required. | 2349 | // so just manually upcast it if required. |
| 2340 | const shift_ty_ref = try self.resolveType(self.typeOf(bin_op.rhs), .direct); | 2350 | // TODO(robin) |
| 2341 | const shift_id = if (shift_ty_ref != result_ty_ref) blk: { | | |
| 2342 | const shift_id = self.spv.allocId(); | | |
| 2343 | try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ | | |
| 2344 | .id_result_type = self.typeId(result_ty_ref), | | |
| 2345 | .id_result = shift_id, | | |
| 2346 | .unsigned_value = rhs_id, | | |
| 2347 | }); | | |
| 2348 | break :blk shift_id; | | |
| 2349 | } else rhs_id; | | |
| 2350 | | 2351 | |
| 2351 | const args = .{ | 2352 | var wip = try self.elementWise(result_ty); |
| 2352 | .id_result_type = self.typeId(result_ty_ref), | 2353 | defer wip.deinit(); |
| 2353 | .id_result = result_id, | | |
| 2354 | .base = lhs_id, | | |
| 2355 | .shift = shift_id, | | |
| 2356 | }; | | |
| 2357 | | 2354 | |
| 2358 | if (result_ty.isSignedInt(mod)) { | 2355 | const shift_ty = wip.scalarType(self.typeOf(bin_op.rhs)); |
| 2359 | try self.func.body.emit(self.spv.gpa, signed, args); | 2356 | const shift_ty_ref = try self.resolveType(shift_ty, .direct); |
| 2360 | } else { | 2357 | |
| 2361 | try self.func.body.emit(self.spv.gpa, unsigned, args); | 2358 | for (0..wip.results.len) |i| { |
| | 2359 | 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); |
| | 2361 | |
| | 2362 | const shift_id = if (shift_ty_ref != wip.result_ty_ref) blk: { |
| | 2363 | const shift_id = self.spv.allocId(); |
| | 2364 | try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ |
| | 2365 | .id_result_type = wip.scalar_ty_id, |
| | 2366 | .id_result = shift_id, |
| | 2367 | .unsigned_value = rhs_elem_id, |
| | 2368 | }); |
| | 2369 | break :blk shift_id; |
| | 2370 | } else rhs_elem_id; |
| | 2371 | |
| | 2372 | const args = .{ |
| | 2373 | .id_result_type = wip.scalar_ty_id, |
| | 2374 | .id_result = wip.allocId(i), |
| | 2375 | .base = lhs_elem_id, |
| | 2376 | .shift = shift_id, |
| | 2377 | }; |
| | 2378 | |
| | 2379 | if (result_ty.isSignedInt(mod)) { |
| | 2380 | try self.func.body.emit(self.spv.gpa, signed, args); |
| | 2381 | } else { |
| | 2382 | try self.func.body.emit(self.spv.gpa, unsigned, args); |
| | 2383 | } |
| 2362 | } | 2384 | } |
| 2363 | return result_id; | 2385 | return try wip.finalize(); |
| 2364 | } | 2386 | } |
| 2365 | | 2387 | |
| 2366 | fn airMinMax(self: *DeclGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !?IdRef { | 2388 | fn airMinMax(self: *DeclGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !?IdRef { |
| ... | @@ -2483,35 +2505,14 @@ const DeclGen = struct { | ... | @@ -2483,35 +2505,14 @@ const DeclGen = struct { |
| 2483 | fn arithOp( | 2505 | fn arithOp( |
| 2484 | self: *DeclGen, | 2506 | self: *DeclGen, |
| 2485 | ty: Type, | 2507 | ty: Type, |
| 2486 | lhs_id_: IdRef, | 2508 | lhs_id: IdRef, |
| 2487 | rhs_id_: IdRef, | 2509 | rhs_id: IdRef, |
| 2488 | comptime fop: Opcode, | 2510 | comptime fop: Opcode, |
| 2489 | comptime sop: Opcode, | 2511 | comptime sop: Opcode, |
| 2490 | comptime uop: Opcode, | 2512 | comptime uop: Opcode, |
| 2491 | /// true if this operation holds under modular arithmetic. | 2513 | /// true if this operation holds under modular arithmetic. |
| 2492 | comptime modular: bool, | 2514 | comptime modular: bool, |
| 2493 | ) !IdRef { | 2515 | ) !IdRef { |
| 2494 | var rhs_id = rhs_id_; | | |
| 2495 | var lhs_id = lhs_id_; | | |
| 2496 | | | |
| 2497 | const mod = self.module; | | |
| 2498 | const result_ty_ref = try self.resolveType(ty, .direct); | | |
| 2499 | | | |
| 2500 | if (ty.isVector(mod)) { | | |
| 2501 | const child_ty = ty.childType(mod); | | |
| 2502 | const vector_len = ty.vectorLen(mod); | | |
| 2503 | const constituents = try self.gpa.alloc(IdRef, vector_len); | | |
| 2504 | defer self.gpa.free(constituents); | | |
| 2505 | | | |
| 2506 | for (constituents, 0..) |*constituent, i| { | | |
| 2507 | const lhs_index_id = try self.extractField(child_ty, lhs_id, @intCast(i)); | | |
| 2508 | const rhs_index_id = try self.extractField(child_ty, rhs_id, @intCast(i)); | | |
| 2509 | constituent.* = try self.arithOp(child_ty, lhs_index_id, rhs_index_id, fop, sop, uop, modular); | | |
| 2510 | } | | |
| 2511 | | | |
| 2512 | return self.constructArray(ty, constituents); | | |
| 2513 | } | | |
| 2514 | | | |
| 2515 | // Binary operations are generally applicable to both scalar and vector operations | 2516 | // Binary operations are generally applicable to both scalar and vector operations |
| 2516 | // in SPIR-V, but int and float versions of operations require different opcodes. | 2517 | // in SPIR-V, but int and float versions of operations require different opcodes. |
| 2517 | const info = try self.arithmeticTypeInfo(ty); | 2518 | const info = try self.arithmeticTypeInfo(ty); |
| ... | @@ -2520,17 +2521,7 @@ const DeclGen = struct { | ... | @@ -2520,17 +2521,7 @@ const DeclGen = struct { |
| 2520 | .composite_integer => { | 2521 | .composite_integer => { |
| 2521 | return self.todo("binary operations for composite integers", .{}); | 2522 | return self.todo("binary operations for composite integers", .{}); |
| 2522 | }, | 2523 | }, |
| 2523 | .strange_integer => blk: { | 2524 | .integer, .strange_integer => switch (info.signedness) { |
| 2524 | if (!modular) { | | |
| 2525 | lhs_id = try self.normalizeInt(result_ty_ref, lhs_id, info); | | |
| 2526 | rhs_id = try self.normalizeInt(result_ty_ref, rhs_id, info); | | |
| 2527 | } | | |
| 2528 | break :blk switch (info.signedness) { | | |
| 2529 | .signed => @as(usize, 1), | | |
| 2530 | .unsigned => @as(usize, 2), | | |
| 2531 | }; | | |
| 2532 | }, | | |
| 2533 | .integer => switch (info.signedness) { | | |
| 2534 | .signed => @as(usize, 1), | 2525 | .signed => @as(usize, 1), |
| 2535 | .unsigned => @as(usize, 2), | 2526 | .unsigned => @as(usize, 2), |
| 2536 | }, | 2527 | }, |
| ... | @@ -2538,24 +2529,41 @@ const DeclGen = struct { | ... | @@ -2538,24 +2529,41 @@ const DeclGen = struct { |
| 2538 | .bool => unreachable, | 2529 | .bool => unreachable, |
| 2539 | }; | 2530 | }; |
| 2540 | | 2531 | |
| 2541 | const result_id = self.spv.allocId(); | 2532 | var wip = try self.elementWise(ty); |
| 2542 | const operands = .{ | 2533 | defer wip.deinit(); |
| 2543 | .id_result_type = self.typeId(result_ty_ref), | 2534 | for (0..wip.results.len) |i| { |
| 2544 | .id_result = result_id, | 2535 | const lhs_elem_id = try wip.elementAt(ty, lhs_id, i); |
| 2545 | .operand_1 = lhs_id, | 2536 | const rhs_elem_id = try wip.elementAt(ty, rhs_id, i); |
| 2546 | .operand_2 = rhs_id, | | |
| 2547 | }; | | |
| 2548 | | 2537 | |
| 2549 | switch (opcode_index) { | 2538 | const lhs_norm_id = if (modular and info.class == .strange_integer) |
| 2550 | 0 => try self.func.body.emit(self.spv.gpa, fop, operands), | 2539 | try self.normalizeInt(wip.scalar_ty_ref, lhs_elem_id, info) |
| 2551 | 1 => try self.func.body.emit(self.spv.gpa, sop, operands), | 2540 | else |
| 2552 | 2 => try self.func.body.emit(self.spv.gpa, uop, operands), | 2541 | lhs_elem_id; |
| 2553 | else => unreachable, | 2542 | |
| | 2543 | const rhs_norm_id = if (modular and info.class == .strange_integer) |
| | 2544 | try self.normalizeInt(wip.scalar_ty_ref, rhs_elem_id, info) |
| | 2545 | else |
| | 2546 | rhs_elem_id; |
| | 2547 | |
| | 2548 | const operands = .{ |
| | 2549 | .id_result_type = wip.scalar_ty_id, |
| | 2550 | .id_result = wip.allocId(i), |
| | 2551 | .operand_1 = lhs_norm_id, |
| | 2552 | .operand_2 = rhs_norm_id, |
| | 2553 | }; |
| | 2554 | |
| | 2555 | switch (opcode_index) { |
| | 2556 | 0 => try self.func.body.emit(self.spv.gpa, fop, operands), |
| | 2557 | 1 => try self.func.body.emit(self.spv.gpa, sop, operands), |
| | 2558 | 2 => try self.func.body.emit(self.spv.gpa, uop, operands), |
| | 2559 | else => unreachable, |
| | 2560 | } |
| | 2561 | |
| | 2562 | // TODO: Trap on overflow? Probably going to be annoying. |
| | 2563 | // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. |
| 2554 | } | 2564 | } |
| 2555 | // TODO: Trap on overflow? Probably going to be annoying. | | |
| 2556 | // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. | | |
| 2557 | | 2565 | |
| 2558 | return result_id; | 2566 | return try wip.finalize(); |
| 2559 | } | 2567 | } |
| 2560 | | 2568 | |
| 2561 | fn airAddSubOverflow( | 2569 | fn airAddSubOverflow( |