| ... | ... | @@ -1782,6 +1782,19 @@ 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 | |
| 1785 | 1798 | /// Utility function to extract the element at a particular index in an |
| 1786 | 1799 | /// input vector. This type is expected to be a vector if `wip.is_vector`, and |
| 1787 | 1800 | /// a scalar otherwise. |
| ... | ... | @@ -1789,7 +1802,7 @@ const DeclGen = struct { |
| 1789 | 1802 | const mod = wip.dg.module; |
| 1790 | 1803 | if (wip.is_vector) { |
| 1791 | 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 | 1806 | } else { |
| 1794 | 1807 | assert(!ty.isVector(mod)); |
| 1795 | 1808 | assert(index == 0); |
| ... | ... | @@ -2331,36 +2344,45 @@ const DeclGen = struct { |
| 2331 | 2344 | const lhs_id = try self.resolve(bin_op.lhs); |
| 2332 | 2345 | const rhs_id = try self.resolve(bin_op.rhs); |
| 2333 | 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 | 2348 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, |
| 2339 | 2349 | // so just manually upcast it if required. |
| 2340 | | const shift_ty_ref = try self.resolveType(self.typeOf(bin_op.rhs), .direct); |
| 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 | // TODO(robin) |
| 2350 | 2351 | |
| 2351 | | const args = .{ |
| 2352 | | .id_result_type = self.typeId(result_ty_ref), |
| 2353 | | .id_result = result_id, |
| 2354 | | .base = lhs_id, |
| 2355 | | .shift = shift_id, |
| 2356 | | }; |
| 2352 | var wip = try self.elementWise(result_ty); |
| 2353 | defer wip.deinit(); |
| 2357 | 2354 | |
| 2358 | | if (result_ty.isSignedInt(mod)) { |
| 2359 | | try self.func.body.emit(self.spv.gpa, signed, args); |
| 2360 | | } else { |
| 2361 | | try self.func.body.emit(self.spv.gpa, unsigned, args); |
| 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| { |
| 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 | 2388 | fn airMinMax(self: *DeclGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !?IdRef { |
| ... | ... | @@ -2483,35 +2505,14 @@ const DeclGen = struct { |
| 2483 | 2505 | fn arithOp( |
| 2484 | 2506 | self: *DeclGen, |
| 2485 | 2507 | ty: Type, |
| 2486 | | lhs_id_: IdRef, |
| 2487 | | rhs_id_: IdRef, |
| 2508 | lhs_id: IdRef, |
| 2509 | rhs_id: IdRef, |
| 2488 | 2510 | comptime fop: Opcode, |
| 2489 | 2511 | comptime sop: Opcode, |
| 2490 | 2512 | comptime uop: Opcode, |
| 2491 | 2513 | /// true if this operation holds under modular arithmetic. |
| 2492 | 2514 | comptime modular: bool, |
| 2493 | 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 | 2516 | // Binary operations are generally applicable to both scalar and vector operations |
| 2516 | 2517 | // in SPIR-V, but int and float versions of operations require different opcodes. |
| 2517 | 2518 | const info = try self.arithmeticTypeInfo(ty); |
| ... | ... | @@ -2520,17 +2521,7 @@ const DeclGen = struct { |
| 2520 | 2521 | .composite_integer => { |
| 2521 | 2522 | return self.todo("binary operations for composite integers", .{}); |
| 2522 | 2523 | }, |
| 2523 | | .strange_integer => blk: { |
| 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) { |
| 2524 | .integer, .strange_integer => switch (info.signedness) { |
| 2534 | 2525 | .signed => @as(usize, 1), |
| 2535 | 2526 | .unsigned => @as(usize, 2), |
| 2536 | 2527 | }, |
| ... | ... | @@ -2538,24 +2529,41 @@ const DeclGen = struct { |
| 2538 | 2529 | .bool => unreachable, |
| 2539 | 2530 | }; |
| 2540 | 2531 | |
| 2541 | | const result_id = self.spv.allocId(); |
| 2542 | | const operands = .{ |
| 2543 | | .id_result_type = self.typeId(result_ty_ref), |
| 2544 | | .id_result = result_id, |
| 2545 | | .operand_1 = lhs_id, |
| 2546 | | .operand_2 = rhs_id, |
| 2547 | | }; |
| 2532 | var wip = try self.elementWise(ty); |
| 2533 | defer wip.deinit(); |
| 2534 | for (0..wip.results.len) |i| { |
| 2535 | const lhs_elem_id = try wip.elementAt(ty, lhs_id, i); |
| 2536 | const rhs_elem_id = try wip.elementAt(ty, rhs_id, i); |
| 2548 | 2537 | |
| 2549 | | switch (opcode_index) { |
| 2550 | | 0 => try self.func.body.emit(self.spv.gpa, fop, operands), |
| 2551 | | 1 => try self.func.body.emit(self.spv.gpa, sop, operands), |
| 2552 | | 2 => try self.func.body.emit(self.spv.gpa, uop, operands), |
| 2553 | | else => unreachable, |
| 2538 | const lhs_norm_id = if (modular and info.class == .strange_integer) |
| 2539 | try self.normalizeInt(wip.scalar_ty_ref, lhs_elem_id, info) |
| 2540 | else |
| 2541 | lhs_elem_id; |
| 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 | 2569 | fn airAddSubOverflow( |