authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-04-09 00:12:10+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-04-09 00:15:33+02:00
log773def30c24f739647112e8221fdf398b5ef12e7
treea61f7045dff9aa5d66eba3a0510ecb030a659439
parent5b5f828019331fc7714d39b760e12b488bfd577f

stage2-wasm: fix bug in big endian limb64 + tiny fixes


3 files changed, 100 insertions(+), 38 deletions(-)

lib/compiler_rt/limb64.zig+96-25
......@@ -33,6 +33,24 @@ fn limbCount(bits: u16) u16 {
3333 return @divExact(std.zig.target.intByteSize(&builtin.target, bits), 8);
3434}
3535
36fn varLimbs(ptr: [*]u64, bits: u16) []u64 {
37 const limb_cnt = usedLimbCount(bits);
38 const true_limb_cnt = limbCount(bits);
39 return switch (endian) {
40 .little => ptr[0..limb_cnt],
41 .big => ptr[true_limb_cnt - limb_cnt .. true_limb_cnt],
42 };
43}
44
45fn constLimbs(ptr: [*]const u64, bits: u16) []const u64 {
46 const limb_cnt = usedLimbCount(bits);
47 const true_limb_cnt = limbCount(bits);
48 return switch (endian) {
49 .little => ptr[0..limb_cnt],
50 .big => ptr[true_limb_cnt - limb_cnt .. true_limb_cnt],
51 };
52}
53
3654fn fixLastLimb(out_ptr: [*]u64, is_signed: bool, bits: u16) void {
3755 const limb_cnt = usedLimbCount(bits);
3856 const true_limb_cnt = limbCount(bits);
......@@ -77,9 +95,9 @@ comptime {
7795
7896fn __addo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool {
7997 const limb_cnt = usedLimbCount(bits);
80 const out = out_ptr[0..limb_cnt];
81 const a = a_ptr[0..limb_cnt];
82 const b = b_ptr[0..limb_cnt];
98 const out = varLimbs(out_ptr, bits);
99 const a = constLimbs(a_ptr, bits);
100 const b = constLimbs(b_ptr, bits);
83101
84102 var carry: u1 = 0;
85103 var i: usize = 0;
......@@ -143,6 +161,9 @@ test __addo_limb64 {
143161 try test__addo_limb64(i64, maxInt(i64), 1, .{ minInt(i64), true });
144162 try test__addo_limb64(i65, maxInt(i65), 1, .{ minInt(i65), true });
145163 try test__addo_limb64(i255, -3, 2, .{ -1, false });
164
165 try test__addo_limb64(u150, maxInt(u150), 2, .{ 1, true });
166 try test__addo_limb64(i150, -3, 2, .{ -1, false });
146167}
147168
148169comptime {
......@@ -151,9 +172,9 @@ comptime {
151172
152173fn __subo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool {
153174 const limb_cnt = usedLimbCount(bits);
154 const out = out_ptr[0..limb_cnt];
155 const a = a_ptr[0..limb_cnt];
156 const b = b_ptr[0..limb_cnt];
175 const out = varLimbs(out_ptr, bits);
176 const a = constLimbs(a_ptr, bits);
177 const b = constLimbs(b_ptr, bits);
157178
158179 var borrow: u1 = 0;
159180 var i: usize = 0;
......@@ -216,6 +237,9 @@ test __subo_limb64 {
216237 try test__subo_limb64(i64, minInt(i64), 1, .{ maxInt(i64), true });
217238 try test__subo_limb64(i65, minInt(i65), 1, .{ maxInt(i65), true });
218239 try test__subo_limb64(i255, -1, 2, .{ -3, false });
240
241 try test__subo_limb64(u150, 2, maxInt(u150), .{ 3, true });
242 try test__subo_limb64(i150, -3, 2, .{ -5, false });
219243}
220244
221245comptime {
......@@ -227,8 +251,8 @@ comptime {
227251// a > b -> 1
228252fn __cmp_limb64(a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) i8 {
229253 const limb_cnt = usedLimbCount(bits);
230 const a = a_ptr[0..limb_cnt];
231 const b = b_ptr[0..limb_cnt];
254 const a = constLimbs(a_ptr, bits);
255 const b = constLimbs(b_ptr, bits);
232256
233257 var i: usize = 0;
234258 if (is_signed) {
......@@ -284,6 +308,9 @@ test __cmp_limb64 {
284308 try test__cmp_limb64(i255, -3, 2, -1);
285309 try test__cmp_limb64(i255, -5, -5, 0);
286310 try test__cmp_limb64(i255, 2, -3, 1);
311
312 try test__cmp_limb64(u150, maxInt(u150) - 5, maxInt(u150) - 5, 0);
313 try test__cmp_limb64(i150, minInt(i150), -5, -1);
287314}
288315
289316comptime {
......@@ -324,6 +351,9 @@ test __and_limb64 {
324351 try test__and_limb64(i64, -1, 2, 2);
325352 try test__and_limb64(i65, minInt(i65), -1, minInt(i65));
326353 try test__and_limb64(i255, -1, 2, 2);
354
355 try test__and_limb64(u150, maxInt(u150), 7, 7);
356 try test__and_limb64(i150, -2, 3, 2);
327357}
328358
329359comptime {
......@@ -364,6 +394,9 @@ test __or_limb64 {
364394 try test__or_limb64(i64, -1, 2, -1);
365395 try test__or_limb64(i65, minInt(i65), 1, minInt(i65) + 1);
366396 try test__or_limb64(i255, -3, 2, -1);
397
398 try test__or_limb64(u150, maxInt(u150) - 1, 3, maxInt(u150));
399 try test__or_limb64(i150, -2, 3, -1);
367400}
368401
369402comptime {
......@@ -404,6 +437,9 @@ test __xor_limb64 {
404437 try test__xor_limb64(i64, -1, 2, -3);
405438 try test__xor_limb64(i65, minInt(i65), -1, maxInt(i65));
406439 try test__xor_limb64(i255, -3, 2, -1);
440
441 try test__xor_limb64(u150, maxInt(u150) - 1, 3, maxInt(u150) - 2);
442 try test__xor_limb64(i150, -2, 3, -3);
407443}
408444
409445comptime {
......@@ -412,8 +448,8 @@ comptime {
412448
413449fn __not_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void {
414450 const limb_cnt = usedLimbCount(bits);
415 const out = out_ptr[0..limb_cnt];
416 const a = a_ptr[0..limb_cnt];
451 const out = varLimbs(out_ptr, bits);
452 const a = constLimbs(a_ptr, bits);
417453
418454 var i: usize = 0;
419455 while (i < limb_cnt - 1) : (i += 1) {
......@@ -450,6 +486,9 @@ test __not_limb64 {
450486 try test__not_limb64(i64, -1, 0);
451487 try test__not_limb64(i65, minInt(i65), maxInt(i65));
452488 try test__not_limb64(i255, -3, 2);
489
490 try test__not_limb64(u150, maxInt(u150), 0);
491 try test__not_limb64(i150, maxInt(i150), minInt(i150));
453492}
454493
455494comptime {
......@@ -458,8 +497,8 @@ comptime {
458497
459498fn __shlo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) bool {
460499 const limb_cnt = usedLimbCount(bits);
461 const out = out_ptr[0..limb_cnt];
462 const a = a_ptr[0..limb_cnt];
500 const out = varLimbs(out_ptr, bits);
501 const a = constLimbs(a_ptr, bits);
463502
464503 assert(shift < bits);
465504
......@@ -541,6 +580,9 @@ test __shlo_limb64 {
541580 try test__shlo_limb64(i633, -1 << 299, 333, .{ -1 << 632, false });
542581 try test__shlo_limb64(i633, -1 << 300, 333, .{ 0, true });
543582 try test__shlo_limb64(i633, -1 << 298, 333, .{ -1 << 631, false });
583
584 try test__shlo_limb64(u150, maxInt(u150), 1, .{ maxInt(u150) - 1, true });
585 try test__shlo_limb64(i150, -3, 1, .{ -6, false });
544586}
545587
546588comptime {
......@@ -549,8 +591,8 @@ comptime {
549591
550592fn __shr_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) void {
551593 const limb_cnt = usedLimbCount(bits);
552 const out = out_ptr[0..limb_cnt];
553 const a = a_ptr[0..limb_cnt];
594 const out = varLimbs(out_ptr, bits);
595 const a = constLimbs(a_ptr, bits);
554596
555597 assert(shift < bits);
556598
......@@ -572,6 +614,8 @@ fn __shr_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: boo
572614 carry = if (bit_shift != 0) (limb << @intCast(64 - bit_shift)) else 0;
573615 }
574616 }
617
618 fixLastLimb(out_ptr, is_signed, bits);
575619}
576620
577621fn test__shr_limb64(comptime T: type, a: T, shift: u16, expected: T) !void {
......@@ -609,6 +653,9 @@ test __shr_limb64 {
609653 try test__shr_limb64(i633, -1 << 333, 333, -1);
610654 try test__shr_limb64(i633, -1 << 334, 333, -2);
611655 try test__shr_limb64(i633, -1 << 332, 333, -1);
656
657 try test__shr_limb64(u150, maxInt(u150), 1, maxInt(u149));
658 try test__shr_limb64(i150, -3, 1, -2);
612659}
613660
614661comptime {
......@@ -617,7 +664,7 @@ comptime {
617664
618665fn __clz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 {
619666 const limb_cnt = usedLimbCount(bits);
620 const a = a_ptr[0..limb_cnt];
667 const a = constLimbs(a_ptr, bits);
621668
622669 var res: u16 = 0;
623670 var i: usize = 0;
......@@ -667,6 +714,9 @@ test __clz_limb64 {
667714 try test__clz_limb64(i65, 1 << 32, 32);
668715 try test__clz_limb64(i128, 0, 128);
669716 try test__clz_limb64(i255, 1 << 130, 124);
717
718 try test__clz_limb64(u150, 1 << 31, 118);
719 try test__clz_limb64(i150, maxInt(u65) - 1, 85);
670720}
671721
672722comptime {
......@@ -675,7 +725,7 @@ comptime {
675725
676726fn __ctz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 {
677727 const limb_cnt = usedLimbCount(bits);
678 const a = a_ptr[0..limb_cnt];
728 const a = constLimbs(a_ptr, bits);
679729
680730 var res: u16 = 0;
681731 var i: usize = 0;
......@@ -720,6 +770,9 @@ test __ctz_limb64 {
720770 try test__ctz_limb64(i65, 0, 65);
721771 try test__ctz_limb64(i128, -1 << 73, 73);
722772 try test__ctz_limb64(i255, 1 << 130, 130);
773
774 try test__ctz_limb64(u150, 1 << 101, 101);
775 try test__ctz_limb64(i150, -1 << 74, 74);
723776}
724777
725778comptime {
......@@ -728,7 +781,7 @@ comptime {
728781
729782fn __popcount_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 {
730783 const limb_cnt = usedLimbCount(bits);
731 const a = a_ptr[0..limb_cnt];
784 const a = constLimbs(a_ptr, bits);
732785
733786 var res: u16 = 0;
734787 var i: usize = 0;
......@@ -766,6 +819,9 @@ test __popcount_limb64 {
766819 try test__popcount_limb64(i65, -1, 65);
767820 try test__popcount_limb64(i128, -1 << 7, 121);
768821 try test__popcount_limb64(i255, -1 << 200, 55);
822
823 try test__popcount_limb64(u150, (1 << 149) | (1 << 65) | 1, 3);
824 try test__popcount_limb64(i150, -1 << 7, 143);
769825}
770826
771827comptime {
......@@ -774,8 +830,8 @@ comptime {
774830
775831fn __bitreverse_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void {
776832 const limb_cnt = usedLimbCount(bits);
777 const out = out_ptr[0..limb_cnt];
778 const a = a_ptr[0..limb_cnt];
833 const out = varLimbs(out_ptr, bits);
834 const a = constLimbs(a_ptr, bits);
779835
780836 var i: usize = 0;
781837 while (i < limb_cnt) : (i += 1) {
......@@ -813,6 +869,9 @@ test __bitreverse_limb64 {
813869 try test__bitreverse_limb64(i65, minInt(i65), 1);
814870 try test__bitreverse_limb64(i128, 1 << 63, 1 << 64);
815871 try test__bitreverse_limb64(i255, 1 << 130, 1 << 124);
872
873 try test__bitreverse_limb64(u150, 1 << 9, 1 << 140);
874 try test__bitreverse_limb64(i150, minInt(i150), 1);
816875}
817876
818877comptime {
......@@ -821,8 +880,8 @@ comptime {
821880
822881fn __byteswap_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void {
823882 const limb_cnt = usedLimbCount(bits);
824 const out = out_ptr[0..limb_cnt];
825 const a = a_ptr[0..limb_cnt];
883 const out = varLimbs(out_ptr, bits);
884 const a = constLimbs(a_ptr, bits);
826885
827886 assert(bits % 8 == 0);
828887
......@@ -862,6 +921,9 @@ test __byteswap_limb64 {
862921 try test__byteswap_limb64(i72, -1, -1);
863922 try test__byteswap_limb64(i128, 1 << 56, 1 << 64);
864923 try test__byteswap_limb64(i248, minInt(i248), 128);
924
925 try test__byteswap_limb64(u152, 1, 1 << 144);
926 try test__byteswap_limb64(i152, 1 << 56, 1 << 88);
865927}
866928
867929comptime {
......@@ -881,15 +943,19 @@ inline fn add3(x: *[3]u64, start: usize, v0: u64) void {
881943
882944fn mulwide(a: u64, b: u64) [2]u64 {
883945 const muldXi = @import("mulXi3.zig").muldXi;
884 return @bitCast(muldXi(u64, a, b));
946 const limbs: [2]u64 = @bitCast(muldXi(u64, a, b));
947 return switch (endian) {
948 .little => limbs,
949 .big => .{ limbs[1], limbs[0] },
950 };
885951}
886952
887953fn __mulo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool {
888954 const limb_cnt = usedLimbCount(bits);
889955
890 const out = out_ptr[0..limb_cnt];
891 const a = a_ptr[0..limb_cnt];
892 const b = b_ptr[0..limb_cnt];
956 const out = varLimbs(out_ptr, bits);
957 const a = constLimbs(a_ptr, bits);
958 const b = constLimbs(b_ptr, bits);
893959
894960 @memset(out, 0);
895961
......@@ -1002,6 +1068,9 @@ test __mulo_limb64 {
10021068 try test__mulo_limb64(i200, 1 << 100, 1 << 99, .{ minInt(i200), true });
10031069 try test__mulo_limb64(i200, maxInt(i200), maxInt(i200), .{ 1, true });
10041070 try test__mulo_limb64(i200, minInt(i200), minInt(i200), .{ 0, true });
1071
1072 try test__mulo_limb64(u150, maxInt(u150), 2, .{ maxInt(u150) - 1, true });
1073 try test__mulo_limb64(i150, maxInt(i150), 2, .{ -2, true });
10051074}
10061075
10071076comptime {
......@@ -1052,4 +1121,6 @@ test __abs_limb64 {
10521121 try test__abs_limb64(i200, -1 << 198, 1 << 198);
10531122 try test__abs_limb64(i255, -5, 5);
10541123 try test__abs_limb64(i255, minInt(i255), 1 << 254);
1124
1125 try test__abs_limb64(i150, -40, 40);
10551126}
src/codegen/wasm/CodeGen.zig+3-13
......@@ -1320,6 +1320,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
13201320 .mod,
13211321 .max,
13221322 .min,
1323 .div_exact,
13231324 .div_trunc,
13241325 .div_floor,
13251326 => |tag| {
......@@ -1345,6 +1346,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
13451346 .mod => try cg.floatMod(float_ty, lhs, rhs),
13461347 .max => try cg.floatMax(float_ty, lhs, rhs),
13471348 .min => try cg.floatMin(float_ty, lhs, rhs),
1349 .div_exact => try cg.floatDiv(float_ty, lhs, rhs),
13481350 .div_trunc => try cg.floatDivTrunc(float_ty, lhs, rhs),
13491351 .div_floor => try cg.floatDivFloor(float_ty, lhs, rhs),
13501352 else => unreachable,
......@@ -1362,6 +1364,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
13621364 .mod => try cg.intMod(int_ty, lhs, rhs),
13631365 .max => try cg.intMax(int_ty, lhs, rhs),
13641366 .min => try cg.intMin(int_ty, lhs, rhs),
1367 .div_exact => try cg.intDiv(int_ty, lhs, rhs),
13651368 .div_trunc => try cg.intDiv(int_ty, lhs, rhs),
13661369 .div_floor => try cg.intDivFloor(int_ty, lhs, rhs),
13671370 else => unreachable,
......@@ -1385,19 +1388,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
13851388 const result = try cg.floatDiv(.fromType(cg, ty), lhs, rhs);
13861389 try cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
13871390 },
1388 .div_exact => {
1389 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1390 const lhs = try cg.resolveInst(bin_op.lhs);
1391 const rhs = try cg.resolveInst(bin_op.rhs);
1392 const ty = cg.typeOfIndex(inst);
1393
1394 if (ty.zigTypeTag(zcu) == .vector) {
1395 return cg.fail("TODO: implement AIR op: div_exact for vectors", .{});
1396 }
1397
1398 const result = try cg.intDiv(.fromType(cg, ty), lhs, rhs);
1399 try cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
1400 },
14011391 .abs => {
14021392 const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
14031393 const operand = try cg.resolveInst(ty_op.operand);
test/behavior/saturating_arithmetic.zig+1
......@@ -326,6 +326,7 @@ test "saturating shift-left large rhs" {
326326 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
327327 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
328328 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
329 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
329330
330331 {
331332 var lhs: u8 = undefined;