| ... | @@ -2167,21 +2167,20 @@ const DeclGen = struct { | ... | @@ -2167,21 +2167,20 @@ const DeclGen = struct { |
| 2167 | const air_tags = self.air.instructions.items(.tag); | 2167 | const air_tags = self.air.instructions.items(.tag); |
| 2168 | const maybe_result_id: ?IdRef = switch (air_tags[@intFromEnum(inst)]) { | 2168 | const maybe_result_id: ?IdRef = switch (air_tags[@intFromEnum(inst)]) { |
| 2169 | // zig fmt: off | 2169 | // zig fmt: off |
| 2170 | .add, .add_wrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd, true), | 2170 | .add, .add_wrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd), |
| 2171 | .sub, .sub_wrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub, true), | 2171 | .sub, .sub_wrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub), |
| 2172 | .mul, .mul_wrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul, true), | 2172 | .mul, .mul_wrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul), |
| 2173 | | 2173 | |
| 2174 | .div_float, | 2174 | .div_float, |
| 2175 | .div_float_optimized, | 2175 | .div_float_optimized, |
| 2176 | // TODO: Check that this is the right operation. | 2176 | // TODO: Check that this is the right operation. |
| 2177 | .div_trunc, | 2177 | .div_trunc, |
| 2178 | .div_trunc_optimized, | 2178 | .div_trunc_optimized, |
| 2179 | => try self.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv, false), | 2179 | => try self.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv), |
| 2180 | // TODO: Check if this is the right operation | 2180 | // TODO: Check if this is the right operation |
| 2181 | // TODO: Make airArithOp for rem not emit a mask for the LHS. | | |
| 2182 | .rem, | 2181 | .rem, |
| 2183 | .rem_optimized, | 2182 | .rem_optimized, |
| 2184 | => try self.airArithOp(inst, .OpFRem, .OpSRem, .OpSRem, false), | 2183 | => try self.airArithOp(inst, .OpFRem, .OpSRem, .OpSRem), |
| 2185 | | 2184 | |
| 2186 | .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan), | 2185 | .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan), |
| 2187 | .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan), | 2186 | .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan), |
| ... | @@ -2346,13 +2345,10 @@ const DeclGen = struct { | ... | @@ -2346,13 +2345,10 @@ const DeclGen = struct { |
| 2346 | | 2345 | |
| 2347 | var wip = try self.elementWise(result_ty); | 2346 | var wip = try self.elementWise(result_ty); |
| 2348 | defer wip.deinit(); | 2347 | defer wip.deinit(); |
| 2349 | for (0..wip.results.len) |i| { | 2348 | for (wip.results, 0..) |*result_id, i| { |
| 2350 | 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); |
| 2351 | const rhs_elem_id = try wip.elementAt(shift_ty, rhs_id, i); | 2350 | const rhs_elem_id = try wip.elementAt(shift_ty, rhs_id, i); |
| 2352 | | 2351 | |
| 2353 | // TODO: Can we omit normalizing lhs? | | |
| 2354 | const lhs_norm_id = try self.normalizeInt(wip.scalar_ty_ref, lhs_elem_id, info); | | |
| 2355 | | | |
| 2356 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, | 2352 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, |
| 2357 | // so just manually upcast it if required. | 2353 | // so just manually upcast it if required. |
| 2358 | const shift_id = if (scalar_shift_ty_ref != wip.scalar_ty_ref) blk: { | 2354 | const shift_id = if (scalar_shift_ty_ref != wip.scalar_ty_ref) blk: { |
| ... | @@ -2364,13 +2360,13 @@ const DeclGen = struct { | ... | @@ -2364,13 +2360,13 @@ const DeclGen = struct { |
| 2364 | }); | 2360 | }); |
| 2365 | break :blk shift_id; | 2361 | break :blk shift_id; |
| 2366 | } else rhs_elem_id; | 2362 | } else rhs_elem_id; |
| 2367 | const shift_norm_id = try self.normalizeInt(wip.scalar_ty_ref, shift_id, info); | | |
| 2368 | | 2363 | |
| | 2364 | const value_id = self.spv.allocId(); |
| 2369 | const args = .{ | 2365 | const args = .{ |
| 2370 | .id_result_type = wip.scalar_ty_id, | 2366 | .id_result_type = wip.scalar_ty_id, |
| 2371 | .id_result = wip.allocId(i), | 2367 | .id_result = value_id, |
| 2372 | .base = lhs_norm_id, | 2368 | .base = lhs_elem_id, |
| 2373 | .shift = shift_norm_id, | 2369 | .shift = shift_id, |
| 2374 | }; | 2370 | }; |
| 2375 | | 2371 | |
| 2376 | if (result_ty.isSignedInt(mod)) { | 2372 | if (result_ty.isSignedInt(mod)) { |
| ... | @@ -2378,6 +2374,8 @@ const DeclGen = struct { | ... | @@ -2378,6 +2374,8 @@ const DeclGen = struct { |
| 2378 | } else { | 2374 | } else { |
| 2379 | try self.func.body.emit(self.spv.gpa, unsigned, args); | 2375 | try self.func.body.emit(self.spv.gpa, unsigned, args); |
| 2380 | } | 2376 | } |
| | 2377 | |
| | 2378 | result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, info); |
| 2381 | } | 2379 | } |
| 2382 | return try wip.finalize(); | 2380 | return try wip.finalize(); |
| 2383 | } | 2381 | } |
| ... | @@ -2435,47 +2433,52 @@ const DeclGen = struct { | ... | @@ -2435,47 +2433,52 @@ const DeclGen = struct { |
| 2435 | return result_id; | 2433 | return result_id; |
| 2436 | } | 2434 | } |
| 2437 | | 2435 | |
| 2438 | /// This function canonicalizes a "strange" integer value: | 2436 | /// This function normalizes values to a canonical representation |
| 2439 | /// For unsigned integers, the value is masked so that only the relevant bits can contain | 2437 | /// after some arithmetic operation. This mostly consists of wrapping |
| 2440 | /// non-zeros. | 2438 | /// behavior for strange integers: |
| 2441 | /// For signed integers, the value is also sign extended. | 2439 | /// - Unsigned integers are bitwise masked with a mask that only passes |
| 2442 | fn normalizeInt(self: *DeclGen, ty_ref: CacheRef, value_id: IdRef, info: ArithmeticTypeInfo) !IdRef { | 2440 | /// the valid bits through. |
| 2443 | assert(info.class != .composite_integer); // TODO | 2441 | /// - Signed integers are also sign extended if they are negative. |
| 2444 | if (info.bits == info.backing_bits) { | 2442 | /// All other values are returned unmodified (this makes strange integer |
| 2445 | return value_id; | 2443 | /// wrapping easier to use in generic operations). |
| 2446 | } | 2444 | fn normalize(self: *DeclGen, ty_ref: CacheRef, value_id: IdRef, info: ArithmeticTypeInfo) !IdRef { |
| 2447 | | 2445 | switch (info.class) { |
| 2448 | switch (info.signedness) { | 2446 | .integer, .bool, .float => return value_id, |
| 2449 | .unsigned => { | 2447 | .composite_integer => unreachable, // TODO |
| 2450 | const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1; | 2448 | .strange_integer => { |
| 2451 | const result_id = self.spv.allocId(); | 2449 | switch (info.signedness) { |
| 2452 | const mask_id = try self.constInt(ty_ref, mask_value); | 2450 | .unsigned => { |
| 2453 | try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{ | 2451 | const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1; |
| 2454 | .id_result_type = self.typeId(ty_ref), | 2452 | const result_id = self.spv.allocId(); |
| 2455 | .id_result = result_id, | 2453 | const mask_id = try self.constInt(ty_ref, mask_value); |
| 2456 | .operand_1 = value_id, | 2454 | try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{ |
| 2457 | .operand_2 = mask_id, | 2455 | .id_result_type = self.typeId(ty_ref), |
| 2458 | }); | 2456 | .id_result = result_id, |
| 2459 | return result_id; | 2457 | .operand_1 = value_id, |
| 2460 | }, | 2458 | .operand_2 = mask_id, |
| 2461 | .signed => { | 2459 | }); |
| 2462 | // Shift left and right so that we can copy the sight bit that way. | 2460 | return result_id; |
| 2463 | const shift_amt_id = try self.constInt(ty_ref, info.backing_bits - info.bits); | 2461 | }, |
| 2464 | const left_id = self.spv.allocId(); | 2462 | .signed => { |
| 2465 | try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ | 2463 | // Shift left and right so that we can copy the sight bit that way. |
| 2466 | .id_result_type = self.typeId(ty_ref), | 2464 | const shift_amt_id = try self.constInt(ty_ref, info.backing_bits - info.bits); |
| 2467 | .id_result = left_id, | 2465 | const left_id = self.spv.allocId(); |
| 2468 | .base = value_id, | 2466 | try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ |
| 2469 | .shift = shift_amt_id, | 2467 | .id_result_type = self.typeId(ty_ref), |
| 2470 | }); | 2468 | .id_result = left_id, |
| 2471 | const right_id = self.spv.allocId(); | 2469 | .base = value_id, |
| 2472 | try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{ | 2470 | .shift = shift_amt_id, |
| 2473 | .id_result_type = self.typeId(ty_ref), | 2471 | }); |
| 2474 | .id_result = right_id, | 2472 | const right_id = self.spv.allocId(); |
| 2475 | .base = left_id, | 2473 | try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{ |
| 2476 | .shift = shift_amt_id, | 2474 | .id_result_type = self.typeId(ty_ref), |
| 2477 | }); | 2475 | .id_result = right_id, |
| 2478 | return right_id; | 2476 | .base = left_id, |
| | 2477 | .shift = shift_amt_id, |
| | 2478 | }); |
| | 2479 | return right_id; |
| | 2480 | }, |
| | 2481 | } |
| 2479 | }, | 2482 | }, |
| 2480 | } | 2483 | } |
| 2481 | } | 2484 | } |
| ... | @@ -2486,8 +2489,6 @@ const DeclGen = struct { | ... | @@ -2486,8 +2489,6 @@ const DeclGen = struct { |
| 2486 | comptime fop: Opcode, | 2489 | comptime fop: Opcode, |
| 2487 | comptime sop: Opcode, | 2490 | comptime sop: Opcode, |
| 2488 | comptime uop: Opcode, | 2491 | comptime uop: Opcode, |
| 2489 | /// true if this operation holds under modular arithmetic. | | |
| 2490 | comptime modular: bool, | | |
| 2491 | ) !?IdRef { | 2492 | ) !?IdRef { |
| 2492 | if (self.liveness.isUnused(inst)) return null; | 2493 | if (self.liveness.isUnused(inst)) return null; |
| 2493 | | 2494 | |
| ... | @@ -2501,7 +2502,7 @@ const DeclGen = struct { | ... | @@ -2501,7 +2502,7 @@ const DeclGen = struct { |
| 2501 | assert(self.typeOf(bin_op.lhs).eql(ty, self.module)); | 2502 | assert(self.typeOf(bin_op.lhs).eql(ty, self.module)); |
| 2502 | assert(self.typeOf(bin_op.rhs).eql(ty, self.module)); | 2503 | assert(self.typeOf(bin_op.rhs).eql(ty, self.module)); |
| 2503 | | 2504 | |
| 2504 | return try self.arithOp(ty, lhs_id, rhs_id, fop, sop, uop, modular); | 2505 | return try self.arithOp(ty, lhs_id, rhs_id, fop, sop, uop); |
| 2505 | } | 2506 | } |
| 2506 | | 2507 | |
| 2507 | fn arithOp( | 2508 | fn arithOp( |
| ... | @@ -2512,8 +2513,6 @@ const DeclGen = struct { | ... | @@ -2512,8 +2513,6 @@ const DeclGen = struct { |
| 2512 | comptime fop: Opcode, | 2513 | comptime fop: Opcode, |
| 2513 | comptime sop: Opcode, | 2514 | comptime sop: Opcode, |
| 2514 | comptime uop: Opcode, | 2515 | comptime uop: Opcode, |
| 2515 | /// true if this operation holds under modular arithmetic. | | |
| 2516 | comptime modular: bool, | | |
| 2517 | ) !IdRef { | 2516 | ) !IdRef { |
| 2518 | // Binary operations are generally applicable to both scalar and vector operations | 2517 | // Binary operations are generally applicable to both scalar and vector operations |
| 2519 | // in SPIR-V, but int and float versions of operations require different opcodes. | 2518 | // in SPIR-V, but int and float versions of operations require different opcodes. |
| ... | @@ -2533,25 +2532,16 @@ const DeclGen = struct { | ... | @@ -2533,25 +2532,16 @@ const DeclGen = struct { |
| 2533 | | 2532 | |
| 2534 | var wip = try self.elementWise(ty); | 2533 | var wip = try self.elementWise(ty); |
| 2535 | defer wip.deinit(); | 2534 | defer wip.deinit(); |
| 2536 | for (0..wip.results.len) |i| { | 2535 | for (wip.results, 0..) |*result_id, i| { |
| 2537 | const lhs_elem_id = try wip.elementAt(ty, lhs_id, i); | 2536 | const lhs_elem_id = try wip.elementAt(ty, lhs_id, i); |
| 2538 | const rhs_elem_id = try wip.elementAt(ty, rhs_id, i); | 2537 | const rhs_elem_id = try wip.elementAt(ty, rhs_id, i); |
| 2539 | | 2538 | |
| 2540 | const lhs_norm_id = if (modular and info.class == .strange_integer) | 2539 | const value_id = self.spv.allocId(); |
| 2541 | try self.normalizeInt(wip.scalar_ty_ref, lhs_elem_id, info) | | |
| 2542 | else | | |
| 2543 | lhs_elem_id; | | |
| 2544 | | | |
| 2545 | const rhs_norm_id = if (modular and info.class == .strange_integer) | | |
| 2546 | try self.normalizeInt(wip.scalar_ty_ref, rhs_elem_id, info) | | |
| 2547 | else | | |
| 2548 | rhs_elem_id; | | |
| 2549 | | | |
| 2550 | const operands = .{ | 2540 | const operands = .{ |
| 2551 | .id_result_type = wip.scalar_ty_id, | 2541 | .id_result_type = wip.scalar_ty_id, |
| 2552 | .id_result = wip.allocId(i), | 2542 | .id_result = value_id, |
| 2553 | .operand_1 = lhs_norm_id, | 2543 | .operand_1 = lhs_elem_id, |
| 2554 | .operand_2 = rhs_norm_id, | 2544 | .operand_2 = rhs_elem_id, |
| 2555 | }; | 2545 | }; |
| 2556 | | 2546 | |
| 2557 | switch (opcode_index) { | 2547 | switch (opcode_index) { |
| ... | @@ -2563,6 +2553,7 @@ const DeclGen = struct { | ... | @@ -2563,6 +2553,7 @@ const DeclGen = struct { |
| 2563 | | 2553 | |
| 2564 | // TODO: Trap on overflow? Probably going to be annoying. | 2554 | // TODO: Trap on overflow? Probably going to be annoying. |
| 2565 | // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. | 2555 | // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. |
| | 2556 | result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, info); |
| 2566 | } | 2557 | } |
| 2567 | | 2558 | |
| 2568 | return try wip.finalize(); | 2559 | return try wip.finalize(); |
| ... | @@ -2599,24 +2590,22 @@ const DeclGen = struct { | ... | @@ -2599,24 +2590,22 @@ const DeclGen = struct { |
| 2599 | defer wip_result.deinit(); | 2590 | defer wip_result.deinit(); |
| 2600 | var wip_ov = try self.elementWise(ov_ty); | 2591 | var wip_ov = try self.elementWise(ov_ty); |
| 2601 | defer wip_ov.deinit(); | 2592 | defer wip_ov.deinit(); |
| 2602 | for (wip_result.results, wip_ov.results, 0..) |*value_id, *ov_id, i| { | 2593 | for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { |
| 2603 | const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); | 2594 | const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); |
| 2604 | const rhs_elem_id = try wip_result.elementAt(operand_ty, rhs, i); | 2595 | const rhs_elem_id = try wip_result.elementAt(operand_ty, rhs, i); |
| 2605 | | 2596 | |
| 2606 | // Normalize both so that we can properly check for overflow | 2597 | // Normalize both so that we can properly check for overflow |
| 2607 | const lhs_norm_id = try self.normalizeInt(wip_result.scalar_ty_ref, lhs_elem_id, info); | 2598 | const value_id = self.spv.allocId(); |
| 2608 | const rhs_norm_id = try self.normalizeInt(wip_result.scalar_ty_ref, rhs_elem_id, info); | | |
| 2609 | const op_result_id = self.spv.allocId(); | | |
| 2610 | | 2599 | |
| 2611 | try self.func.body.emit(self.spv.gpa, add, .{ | 2600 | try self.func.body.emit(self.spv.gpa, add, .{ |
| 2612 | .id_result_type = wip_result.scalar_ty_id, | 2601 | .id_result_type = wip_result.scalar_ty_id, |
| 2613 | .id_result = op_result_id, | 2602 | .id_result = value_id, |
| 2614 | .operand_1 = lhs_norm_id, | 2603 | .operand_1 = lhs_elem_id, |
| 2615 | .operand_2 = rhs_norm_id, | 2604 | .operand_2 = rhs_elem_id, |
| 2616 | }); | 2605 | }); |
| 2617 | | 2606 | |
| 2618 | // Normalize the result so that the comparisons go well | 2607 | // Normalize the result so that the comparisons go well |
| 2619 | value_id.* = try self.normalizeInt(wip_result.scalar_ty_ref, op_result_id, info); | 2608 | result_id.* = try self.normalize(wip_result.scalar_ty_ref, value_id, info); |
| 2620 | | 2609 | |
| 2621 | const overflowed_id = switch (info.signedness) { | 2610 | const overflowed_id = switch (info.signedness) { |
| 2622 | .unsigned => blk: { | 2611 | .unsigned => blk: { |
| ... | @@ -2626,8 +2615,8 @@ const DeclGen = struct { | ... | @@ -2626,8 +2615,8 @@ const DeclGen = struct { |
| 2626 | try self.func.body.emit(self.spv.gpa, ucmp, .{ | 2615 | try self.func.body.emit(self.spv.gpa, ucmp, .{ |
| 2627 | .id_result_type = self.typeId(bool_ty_ref), | 2616 | .id_result_type = self.typeId(bool_ty_ref), |
| 2628 | .id_result = overflowed_id, | 2617 | .id_result = overflowed_id, |
| 2629 | .operand_1 = value_id.*, | 2618 | .operand_1 = result_id.*, |
| 2630 | .operand_2 = lhs_norm_id, | 2619 | .operand_2 = lhs_elem_id, |
| 2631 | }); | 2620 | }); |
| 2632 | break :blk overflowed_id; | 2621 | break :blk overflowed_id; |
| 2633 | }, | 2622 | }, |
| ... | @@ -2654,7 +2643,7 @@ const DeclGen = struct { | ... | @@ -2654,7 +2643,7 @@ const DeclGen = struct { |
| 2654 | try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ | 2643 | try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ |
| 2655 | .id_result_type = self.typeId(bool_ty_ref), | 2644 | .id_result_type = self.typeId(bool_ty_ref), |
| 2656 | .id_result = rhs_lt_zero_id, | 2645 | .id_result = rhs_lt_zero_id, |
| 2657 | .operand_1 = rhs_norm_id, | 2646 | .operand_1 = rhs_elem_id, |
| 2658 | .operand_2 = zero_id, | 2647 | .operand_2 = zero_id, |
| 2659 | }); | 2648 | }); |
| 2660 | | 2649 | |
| ... | @@ -2662,8 +2651,8 @@ const DeclGen = struct { | ... | @@ -2662,8 +2651,8 @@ const DeclGen = struct { |
| 2662 | try self.func.body.emit(self.spv.gpa, scmp, .{ | 2651 | try self.func.body.emit(self.spv.gpa, scmp, .{ |
| 2663 | .id_result_type = self.typeId(bool_ty_ref), | 2652 | .id_result_type = self.typeId(bool_ty_ref), |
| 2664 | .id_result = value_gt_lhs_id, | 2653 | .id_result = value_gt_lhs_id, |
| 2665 | .operand_1 = lhs_norm_id, | 2654 | .operand_1 = lhs_elem_id, |
| 2666 | .operand_2 = value_id.*, | 2655 | .operand_2 = result_id.*, |
| 2667 | }); | 2656 | }); |
| 2668 | | 2657 | |
| 2669 | const overflowed_id = self.spv.allocId(); | 2658 | const overflowed_id = self.spv.allocId(); |
| ... | @@ -2715,13 +2704,10 @@ const DeclGen = struct { | ... | @@ -2715,13 +2704,10 @@ const DeclGen = struct { |
| 2715 | defer wip_result.deinit(); | 2704 | defer wip_result.deinit(); |
| 2716 | var wip_ov = try self.elementWise(ov_ty); | 2705 | var wip_ov = try self.elementWise(ov_ty); |
| 2717 | defer wip_ov.deinit(); | 2706 | defer wip_ov.deinit(); |
| 2718 | for (0..wip_result.results.len, wip_ov.results) |i, *ov_id| { | 2707 | for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { |
| 2719 | const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); | 2708 | const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); |
| 2720 | const rhs_elem_id = try wip_result.elementAt(shift_ty, rhs, i); | 2709 | const rhs_elem_id = try wip_result.elementAt(shift_ty, rhs, i); |
| 2721 | | 2710 | |
| 2722 | // Normalize both so that we can shift back and check if the result is the same. | | |
| 2723 | const lhs_norm_id = try self.normalizeInt(wip_result.scalar_ty_ref, lhs_elem_id, info); | | |
| 2724 | | | |
| 2725 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, | 2711 | // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, |
| 2726 | // so just manually upcast it if required. | 2712 | // so just manually upcast it if required. |
| 2727 | const shift_id = if (scalar_shift_ty_ref != wip_result.scalar_ty_ref) blk: { | 2713 | const shift_id = if (scalar_shift_ty_ref != wip_result.scalar_ty_ref) blk: { |
| ... | @@ -2733,29 +2719,41 @@ const DeclGen = struct { | ... | @@ -2733,29 +2719,41 @@ const DeclGen = struct { |
| 2733 | }); | 2719 | }); |
| 2734 | break :blk shift_id; | 2720 | break :blk shift_id; |
| 2735 | } else rhs_elem_id; | 2721 | } else rhs_elem_id; |
| 2736 | const shift_norm_id = try self.normalizeInt(wip_result.scalar_ty_ref, shift_id, info); | | |
| 2737 | | 2722 | |
| | 2723 | const value_id = self.spv.allocId(); |
| 2738 | try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ | 2724 | try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ |
| 2739 | .id_result_type = wip_result.scalar_ty_id, | 2725 | .id_result_type = wip_result.scalar_ty_id, |
| 2740 | .id_result = wip_result.allocId(i), | 2726 | .id_result = value_id, |
| 2741 | .base = lhs_norm_id, | 2727 | .base = lhs_elem_id, |
| 2742 | .shift = shift_norm_id, | 2728 | .shift = shift_id, |
| 2743 | }); | 2729 | }); |
| | 2730 | result_id.* = try self.normalize(wip_result.scalar_ty_ref, value_id, info); |
| 2744 | | 2731 | |
| 2745 | // To check if overflow happened, just check if the right-shifted result is the same value. | | |
| 2746 | const right_shift_id = self.spv.allocId(); | 2732 | const right_shift_id = self.spv.allocId(); |
| 2747 | try self.func.body.emit(self.spv.gpa, .OpShiftRightLogical, .{ | 2733 | switch (info.signedness) { |
| 2748 | .id_result_type = wip_result.scalar_ty_id, | 2734 | .signed => { |
| 2749 | .id_result = right_shift_id, | 2735 | try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{ |
| 2750 | .base = try self.normalizeInt(wip_result.scalar_ty_ref, wip_result.results[i], info), | 2736 | .id_result_type = wip_result.scalar_ty_id, |
| 2751 | .shift = shift_norm_id, | 2737 | .id_result = right_shift_id, |
| 2752 | }); | 2738 | .base = result_id.*, |
| | 2739 | .shift = shift_id, |
| | 2740 | }); |
| | 2741 | }, |
| | 2742 | .unsigned => { |
| | 2743 | try self.func.body.emit(self.spv.gpa, .OpShiftRightLogical, .{ |
| | 2744 | .id_result_type = wip_result.scalar_ty_id, |
| | 2745 | .id_result = right_shift_id, |
| | 2746 | .base = result_id.*, |
| | 2747 | .shift = shift_id, |
| | 2748 | }); |
| | 2749 | }, |
| | 2750 | } |
| 2753 | | 2751 | |
| 2754 | const overflowed_id = self.spv.allocId(); | 2752 | const overflowed_id = self.spv.allocId(); |
| 2755 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ | 2753 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ |
| 2756 | .id_result_type = self.typeId(bool_ty_ref), | 2754 | .id_result_type = self.typeId(bool_ty_ref), |
| 2757 | .id_result = overflowed_id, | 2755 | .id_result = overflowed_id, |
| 2758 | .operand_1 = lhs_norm_id, | 2756 | .operand_1 = lhs_elem_id, |
| 2759 | .operand_2 = right_shift_id, | 2757 | .operand_2 = right_shift_id, |
| 2760 | }); | 2758 | }); |
| 2761 | | 2759 | |
| ... | @@ -3113,14 +3111,7 @@ const DeclGen = struct { | ... | @@ -3113,14 +3111,7 @@ const DeclGen = struct { |
| 3113 | .neq => .OpLogicalNotEqual, | 3111 | .neq => .OpLogicalNotEqual, |
| 3114 | else => unreachable, | 3112 | else => unreachable, |
| 3115 | }, | 3113 | }, |
| 3116 | .strange_integer => sign: { | 3114 | .integer, .strange_integer => info.signedness, |
| 3117 | const op_ty_ref = try self.resolveType(op_ty, .direct); | | |
| 3118 | // Mask operands before performing comparison. | | |
| 3119 | cmp_lhs_id = try self.normalizeInt(op_ty_ref, cmp_lhs_id, info); | | |
| 3120 | cmp_rhs_id = try self.normalizeInt(op_ty_ref, cmp_rhs_id, info); | | |
| 3121 | break :sign info.signedness; | | |
| 3122 | }, | | |
| 3123 | .integer => info.signedness, | | |
| 3124 | }; | 3115 | }; |
| 3125 | | 3116 | |
| 3126 | break :opcode switch (signedness) { | 3117 | break :opcode switch (signedness) { |
| ... | @@ -3252,18 +3243,13 @@ const DeclGen = struct { | ... | @@ -3252,18 +3243,13 @@ const DeclGen = struct { |
| 3252 | const operand_id = try self.resolve(ty_op.operand); | 3243 | const operand_id = try self.resolve(ty_op.operand); |
| 3253 | const src_ty = self.typeOf(ty_op.operand); | 3244 | const src_ty = self.typeOf(ty_op.operand); |
| 3254 | const dst_ty = self.typeOfIndex(inst); | 3245 | const dst_ty = self.typeOfIndex(inst); |
| 3255 | const src_ty_ref = try self.resolveType(src_ty, .direct); | | |
| 3256 | const dst_ty_ref = try self.resolveType(dst_ty, .direct); | 3246 | const dst_ty_ref = try self.resolveType(dst_ty, .direct); |
| 3257 | | 3247 | |
| 3258 | const src_info = try self.arithmeticTypeInfo(src_ty); | 3248 | const src_info = try self.arithmeticTypeInfo(src_ty); |
| 3259 | const dst_info = try self.arithmeticTypeInfo(dst_ty); | 3249 | const dst_info = try self.arithmeticTypeInfo(dst_ty); |
| 3260 | | 3250 | |
| 3261 | // While intcast promises that the value already fits, the upper bits of a | | |
| 3262 | // strange integer may contain garbage. Therefore, mask/sign extend it before. | | |
| 3263 | const src_id = try self.normalizeInt(src_ty_ref, operand_id, src_info); | | |
| 3264 | | | |
| 3265 | if (src_info.backing_bits == dst_info.backing_bits) { | 3251 | if (src_info.backing_bits == dst_info.backing_bits) { |
| 3266 | return src_id; | 3252 | return operand_id; |
| 3267 | } | 3253 | } |
| 3268 | | 3254 | |
| 3269 | const result_id = self.spv.allocId(); | 3255 | const result_id = self.spv.allocId(); |
| ... | @@ -3271,14 +3257,23 @@ const DeclGen = struct { | ... | @@ -3271,14 +3257,23 @@ const DeclGen = struct { |
| 3271 | .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{ | 3257 | .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{ |
| 3272 | .id_result_type = self.typeId(dst_ty_ref), | 3258 | .id_result_type = self.typeId(dst_ty_ref), |
| 3273 | .id_result = result_id, | 3259 | .id_result = result_id, |
| 3274 | .signed_value = src_id, | 3260 | .signed_value = operand_id, |
| 3275 | }), | 3261 | }), |
| 3276 | .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ | 3262 | .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ |
| 3277 | .id_result_type = self.typeId(dst_ty_ref), | 3263 | .id_result_type = self.typeId(dst_ty_ref), |
| 3278 | .id_result = result_id, | 3264 | .id_result = result_id, |
| 3279 | .unsigned_value = src_id, | 3265 | .unsigned_value = operand_id, |
| 3280 | }), | 3266 | }), |
| 3281 | } | 3267 | } |
| | 3268 | |
| | 3269 | // Make sure to normalize the result if shrinking. |
| | 3270 | // Because strange ints are sign extended in their backing |
| | 3271 | // type, we don't need to normalize when growing the type. The |
| | 3272 | // representation is already the same. |
| | 3273 | if (dst_info.bits < src_info.bits) { |
| | 3274 | return try self.normalize(dst_ty_ref, result_id, dst_info); |
| | 3275 | } |
| | 3276 | |
| 3282 | return result_id; | 3277 | return result_id; |
| 3283 | } | 3278 | } |
| 3284 | | 3279 | |