authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2024-07-18 18:01:06+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2024-07-18 18:01:06+02:00
log56d535dd24a16bcd827b54d151d6858a2c3ec3b5
tree75e1206499fd0d8cc614b218ad8f3c1595c9a29e
parentdc3176d6287955d2ecdaaa26f46f5577d5316d36

stage2-wasm: improve @shlWithOverflow for <= 128 bits

Additionally fixed a bug for shr on signed big ints

2 files changed, 63 insertions(+), 67 deletions(-)

src/arch/wasm/CodeGen.zig+23-21
......@@ -2669,8 +2669,14 @@ fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) Inner
26692669 .signed => return func.callIntrinsic("__udivti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
26702670 .unsigned => return func.callIntrinsic("__divti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
26712671 },
2672 .rem => return func.callIntrinsic("__umodti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2673 .shr => return func.callIntrinsic("__lshrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2672 .rem => switch (int_info.signedness) {
2673 .signed => return func.callIntrinsic("__modti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2674 .unsigned => return func.callIntrinsic("__umodti3", &.{ ty.toIntern(), ty.toIntern() }, ty, &.{ lhs, rhs }),
2675 },
2676 .shr => switch (int_info.signedness) {
2677 .signed => return func.callIntrinsic("__ashrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2678 .unsigned => return func.callIntrinsic("__lshrti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
2679 },
26742680 .shl => return func.callIntrinsic("__ashlti3", &.{ ty.toIntern(), .i32_type }, ty, &.{ lhs, rhs }),
26752681 .@"and", .@"or", .xor => {
26762682 const result = try func.allocStack(ty);
......@@ -6055,14 +6061,14 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
60556061
60566062 const lhs = try func.resolveInst(extra.lhs);
60576063 const rhs = try func.resolveInst(extra.rhs);
6058 const lhs_ty = func.typeOf(extra.lhs);
6064 const ty = func.typeOf(extra.lhs);
60596065 const rhs_ty = func.typeOf(extra.rhs);
60606066
6061 if (lhs_ty.zigTypeTag(mod) == .Vector) {
6067 if (ty.zigTypeTag(mod) == .Vector) {
60626068 return func.fail("TODO: Implement overflow arithmetic for vectors", .{});
60636069 }
60646070
6065 const int_info = lhs_ty.intInfo(mod);
6071 const int_info = ty.intInfo(mod);
60666072 const wasm_bits = toWasmBits(int_info.bits) orelse {
60676073 return func.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
60686074 };
......@@ -6070,32 +6076,28 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
60706076 // Ensure rhs is coerced to lhs as they must have the same WebAssembly types
60716077 // before we can perform any binary operation.
60726078 const rhs_wasm_bits = toWasmBits(rhs_ty.intInfo(mod).bits).?;
6073 const rhs_final = if (wasm_bits != rhs_wasm_bits) blk: {
6074 const rhs_casted = try func.intcast(rhs, rhs_ty, lhs_ty);
6075 break :blk try rhs_casted.toLocal(func, lhs_ty);
6079 // If wasm_bits == 128, compiler-rt expects i32 for shift
6080 const rhs_final = if (wasm_bits != rhs_wasm_bits and wasm_bits == 64) blk: {
6081 const rhs_casted = try func.intcast(rhs, rhs_ty, ty);
6082 break :blk try rhs_casted.toLocal(func, ty);
60766083 } else rhs;
60776084
6078 var shl = try (try func.binOp(lhs, rhs_final, lhs_ty, .shl)).toLocal(func, lhs_ty);
6085 var shl = try (try func.wrapBinOp(lhs, rhs_final, ty, .shl)).toLocal(func, ty);
60796086 defer shl.free(func);
6080 var result = if (wasm_bits != int_info.bits) blk: {
6081 break :blk try (try func.wrapOperand(shl, lhs_ty)).toLocal(func, lhs_ty);
6082 } else shl;
6083 defer result.free(func); // it's a no-op to free the same local twice (when wasm_bits == int_info.bits)
60846087
60856088 const overflow_bit = blk: {
6086 try func.emitWValue(lhs);
6087 const shr = try func.binOp(result, rhs_final, lhs_ty, .shr);
6088 break :blk try func.cmp(.stack, shr, lhs_ty, .neq);
6089 const shr = try func.binOp(shl, rhs_final, ty, .shr);
6090 break :blk try func.cmp(shr, lhs, ty, .neq);
60896091 };
60906092 var overflow_local = try overflow_bit.toLocal(func, Type.u1);
60916093 defer overflow_local.free(func);
60926094
6093 const result_ptr = try func.allocStack(func.typeOfIndex(inst));
6094 try func.store(result_ptr, result, lhs_ty, 0);
6095 const offset = @as(u32, @intCast(lhs_ty.abiSize(pt)));
6096 try func.store(result_ptr, overflow_local, Type.u1, offset);
6095 const result = try func.allocStack(func.typeOfIndex(inst));
6096 const offset: u32 = @intCast(ty.abiSize(pt));
6097 try func.store(result, shl, ty, 0);
6098 try func.store(result, overflow_local, Type.u1, offset);
60976099
6098 return func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
6100 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
60996101}
61006102
61016103fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
test/behavior/math.zig+40-46
......@@ -1345,62 +1345,56 @@ test "@subWithOverflow > 64 bits" {
13451345 try testSubWithOverflow(i128, maxInt(i128), -2, minInt(i128) + 1, 1);
13461346}
13471347
1348fn testShlWithOverflow(comptime T: type, a: T, b: math.Log2Int(T), shl: T, bit: u1) !void {
1349 const ov = @shlWithOverflow(a, b);
1350 try expect(ov[0] == shl);
1351 try expect(ov[1] == bit);
1352}
1353
13481354test "@shlWithOverflow" {
13491355 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13501356 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
13511357 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
13521358 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
13531359
1354 {
1355 var a: u4 = 2;
1356 _ = &a;
1357 var b: u2 = 1;
1358 var ov = @shlWithOverflow(a, b);
1359 try expect(ov[0] == 4);
1360 try expect(ov[1] == 0);
1360 try testShlWithOverflow(u4, 2, 1, 4, 0);
1361 try testShlWithOverflow(u4, 2, 3, 0, 1);
13611362
1362 b = 3;
1363 ov = @shlWithOverflow(a, b);
1364 try expect(ov[0] == 0);
1365 try expect(ov[1] == 1);
1366 }
1363 try testShlWithOverflow(i9, 127, 1, 254, 0);
1364 try testShlWithOverflow(i9, 127, 2, -4, 1);
13671365
1368 {
1369 var a: i9 = 127;
1370 _ = &a;
1371 var b: u4 = 1;
1372 var ov = @shlWithOverflow(a, b);
1373 try expect(ov[0] == 254);
1374 try expect(ov[1] == 0);
1366 try testShlWithOverflow(u16, 0b0010111111111111, 3, 0b0111111111111000, 1);
1367 try testShlWithOverflow(u16, 0b0010111111111111, 2, 0b1011111111111100, 0);
13751368
1376 b = 2;
1377 ov = @shlWithOverflow(a, b);
1378 try expect(ov[0] == -4);
1379 try expect(ov[1] == 1);
1380 }
1369 try testShlWithOverflow(u16, 0b0000_0000_0000_0011, 15, 0b1000_0000_0000_0000, 1);
1370 try testShlWithOverflow(u16, 0b0000_0000_0000_0011, 14, 0b1100_0000_0000_0000, 0);
1371}
13811372
1382 {
1383 const ov = @shlWithOverflow(@as(u16, 0b0010111111111111), 3);
1384 try expect(ov[0] == 0b0111111111111000);
1385 try expect(ov[1] == 1);
1386 }
1387 {
1388 const ov = @shlWithOverflow(@as(u16, 0b0010111111111111), 2);
1389 try expect(ov[0] == 0b1011111111111100);
1390 try expect(ov[1] == 0);
1391 }
1392 {
1393 var a: u16 = 0b0000_0000_0000_0011;
1394 _ = &a;
1395 var b: u4 = 15;
1396 var ov = @shlWithOverflow(a, b);
1397 try expect(ov[0] == 0b1000_0000_0000_0000);
1398 try expect(ov[1] == 1);
1399 b = 14;
1400 ov = @shlWithOverflow(a, b);
1401 try expect(ov[0] == 0b1100_0000_0000_0000);
1402 try expect(ov[1] == 0);
1403 }
1373test "@shlWithOverflow > 64 bits" {
1374 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1375 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1376 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1377 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1378
1379 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 7, 0x0_8000_0000_0000_0000, 0);
1380 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 8, 0x1_0000_0000_0000_0000, 0);
1381 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 9, 0, 1);
1382 try testShlWithOverflow(u65, 0x0_0100_0000_0000_0000, 10, 0, 1);
1383
1384 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 6, 0x4000_0000_0000_0000_0000000000000000, 0);
1385 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 7, 0x8000_0000_0000_0000_0000000000000000, 0);
1386 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 8, 0, 1);
1387 try testShlWithOverflow(u128, 0x0100_0000_0000_0000_0000000000000000, 9, 0, 1);
1388
1389 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 7, 0x0_8000_0000_0000_0000, 0);
1390 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 8, minInt(i65), 1);
1391 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 9, 0, 1);
1392 try testShlWithOverflow(i65, 0x0_0100_0000_0000_0000, 10, 0, 1);
1393
1394 try testShlWithOverflow(i128, 0x0100_0000_0000_0000_0000000000000000, 6, 0x4000_0000_0000_0000_0000000000000000, 0);
1395 try testShlWithOverflow(i128, 0x0100_0000_0000_0000_0000000000000000, 7, minInt(i128), 1);
1396 try testShlWithOverflow(i128, 0x0100_0000_0000_0000_0000000000000000, 8, 0, 1);
1397 try testShlWithOverflow(i128, 0x0100_0000_0000_0000_0000000000000000, 9, 0, 1);
14041398}
14051399
14061400test "overflow arithmetic with u0 values" {