authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-01-20 23:57:19+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-02-04 19:09:28+01:00
log54ec9365498635aa127ff13dfbdd3942890b53d0
treea6df4f0650bc8972746fa4c1636ee55af4143fef
parentb67d983abda198c69fbcde68a961e0e8b92b7939
signaturebadge-check Signed by SSH key SHA256:ZS52FNyUv2WUXvO4njmVaFVO46RHojFuOrxRc4LuKzg

spirv: wrap strange its before instead of after operation

Wrapping strange integers before an operation was initially done as an attempt to minimize the amount of normalizations required: This way, there would not be a normalization necessary between two modular operations. This was a premature optimization, since the resulting logic is more complicated than naive way of wrapping the result after the operation. This commit updates handling of strange integers to do wrapping after each operation. It also seems slightly more efficient in terms of size of generated code, as it reduces the size of the behavior tests binary by about 1%.

1 files changed, 114 insertions(+), 119 deletions(-)

src/codegen/spirv.zig+114-119
...@@ -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: off2169 // 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),
21732173
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 operation2180 // 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),
21852184
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 {
23462345
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);
23522351
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);
23682363
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 };
23752371
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 }
24372435
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 contain2437 /// 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); // TODO2441 /// - 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 {
24472445 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;
24932494
...@@ -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));
25032504
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 }
25062507
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 operations2517 // 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 {
25332532
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);
25392538
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 };
25562546
2557 switch (opcode_index) {2547 switch (opcode_index) {
...@@ -2563,6 +2553,7 @@ const DeclGen = struct {...@@ -2563,6 +2553,7 @@ const DeclGen = struct {
25632553
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 }
25672558
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);
26052596
2606 // Normalize both so that we can properly check for overflow2597 // 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();
26102599
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 });
26172606
2618 // Normalize the result so that the comparisons go well2607 // 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);
26202609
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 });
26602649
...@@ -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 });
26682657
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);
27212710
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);
27372722
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);
27442731
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 }
27532751
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 });
27612759
...@@ -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 };
31253116
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);
32573247
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);
32603250
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 }
32683254
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 }
32843279