authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-04-08 03:33:54+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-04-09 00:14:09+02:00
logaa7874657b3439134eb4cd8b65271fb9cc38fdad
treebabc9d61c1cbed627cdba474787e7959c67cd4dc
parentfff887874e917e2955002c323fb0286cedfbfd84

stage2-wasm: bigint div mod rem


5 files changed, 283 insertions(+), 60 deletions(-)

lib/compiler_rt/divmodei4.zig+15-8
......@@ -24,23 +24,30 @@ inline fn neg(x: []u32) void {
2424 }
2525}
2626
27/// Mutates the arguments!
28fn divmod(q: ?[]u32, r: ?[]u32, u: []u32, v: []u32) !void {
27const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable;
28
29fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void {
2930 const u_sign: i32 = @bitCast(u[u.len - 1]);
3031 const v_sign: i32 = @bitCast(v[v.len - 1]);
31 if (u_sign < 0) neg(u);
32 if (v_sign < 0) neg(v);
33 try @call(.always_inline, udivmod, .{ q, r, u, v });
32 var ua: [max_limbs]u32 = undefined;
33 const us = ua[0..u.len];
34 @memcpy(us, u);
35 var va: [max_limbs]u32 = undefined;
36 const vs = va[0..v.len];
37 @memcpy(vs, v);
38 if (u_sign < 0) neg(us);
39 if (v_sign < 0) neg(vs);
40 try @call(.always_inline, udivmod, .{ q, r, us, vs });
3441 if (q) |x| if (u_sign ^ v_sign < 0) neg(x);
3542 if (r) |x| if (u_sign < 0) neg(x);
3643}
3744
38pub fn __divei4(q_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) void {
45pub fn __divei4(q_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, bits: usize) callconv(.c) void {
3946 @setRuntimeSafety(compiler_rt.test_safety);
4047 const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits));
4148 const q: []u32 = @ptrCast(@alignCast(q_p[0..byte_size]));
42 const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
43 const v: []u32 = @ptrCast(@alignCast(v_p[0..byte_size]));
49 const u: []const u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
50 const v: []const u32 = @ptrCast(@alignCast(v_p[0..byte_size]));
4451 @call(.always_inline, divmod, .{ q, null, u, v }) catch unreachable;
4552}
4653
lib/compiler_rt/limb64.zig+39-13
......@@ -25,10 +25,26 @@ inline fn limbSet(limbs: []u64, i: usize, value: u64) void {
2525 }
2626}
2727
28fn limbCount(bits: u16) u16 {
28fn usedLimbCount(bits: u16) u16 {
2929 return divCeil(u16, bits, 64) catch unreachable;
3030}
3131
32fn limbCount(bits: u16) u16 {
33 return @divExact(std.zig.target.intByteSize(&builtin.target, bits), 8);
34}
35
36fn fixLastLimb(out_ptr: [*]u64, is_signed: bool, bits: u16) void {
37 const limb_cnt = usedLimbCount(bits);
38 const true_limb_cnt = limbCount(bits);
39 if (limb_cnt == true_limb_cnt) return;
40 const true_out = out_ptr[0..true_limb_cnt];
41
42 const sign: u64 = if (!is_signed or @as(i64, @bitCast(true_out[limb_cnt - 1])) >= 0) 0 else ~@as(u64, 0);
43 for (limb_cnt..true_limb_cnt) |i| {
44 true_out[i] = sign;
45 }
46}
47
3248fn Limbs(T: type) type {
3349 const int_info = @typeInfo(T).int;
3450 const limb_cnt = comptime limbCount(int_info.bits);
......@@ -60,7 +76,7 @@ comptime {
6076}
6177
6278fn __addo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool {
63 const limb_cnt = limbCount(bits);
79 const limb_cnt = usedLimbCount(bits);
6480 const out = out_ptr[0..limb_cnt];
6581 const a = a_ptr[0..limb_cnt];
6682 const b = b_ptr[0..limb_cnt];
......@@ -92,11 +108,13 @@ fn __addo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_s
92108
93109 if (bits % 64 == 0) {
94110 limbSet(out, i, limb);
111 fixLastLimb(out_ptr, is_signed, bits);
95112 return carry != 0;
96113 } else {
97114 assert(carry == 0);
98115 const wrapped_limb = limbWrap(limb, is_signed, bits);
99116 limbSet(out, i, wrapped_limb);
117 fixLastLimb(out_ptr, is_signed, bits);
100118 return wrapped_limb != limb;
101119 }
102120}
......@@ -132,7 +150,7 @@ comptime {
132150}
133151
134152fn __subo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool {
135 const limb_cnt = limbCount(bits);
153 const limb_cnt = usedLimbCount(bits);
136154 const out = out_ptr[0..limb_cnt];
137155 const a = a_ptr[0..limb_cnt];
138156 const b = b_ptr[0..limb_cnt];
......@@ -164,10 +182,12 @@ fn __subo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_s
164182
165183 if (bits % 64 == 0) {
166184 limbSet(out, i, limb);
185 fixLastLimb(out_ptr, is_signed, bits);
167186 return borrow != 0;
168187 } else {
169188 const wrapped_limb = limbWrap(limb, is_signed, bits);
170189 limbSet(out, i, wrapped_limb);
190 fixLastLimb(out_ptr, is_signed, bits);
171191 return borrow != 0 or wrapped_limb != limb;
172192 }
173193}
......@@ -206,7 +226,7 @@ comptime {
206226// a == b -> 0
207227// a > b -> 1
208228fn __cmp_limb64(a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) i8 {
209 const limb_cnt = limbCount(bits);
229 const limb_cnt = usedLimbCount(bits);
210230 const a = a_ptr[0..limb_cnt];
211231 const b = b_ptr[0..limb_cnt];
212232
......@@ -391,7 +411,7 @@ comptime {
391411}
392412
393413fn __not_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void {
394 const limb_cnt = limbCount(bits);
414 const limb_cnt = usedLimbCount(bits);
395415 const out = out_ptr[0..limb_cnt];
396416 const a = a_ptr[0..limb_cnt];
397417
......@@ -405,6 +425,7 @@ fn __not_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16
405425 limb = limbWrap(limb, is_signed, bits);
406426 }
407427 limbSet(out, i, limb);
428 fixLastLimb(out_ptr, is_signed, bits);
408429}
409430
410431fn test__not_limb64(comptime T: type, a: T, expected: T) !void {
......@@ -436,7 +457,7 @@ comptime {
436457}
437458
438459fn __shlo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) bool {
439 const limb_cnt = limbCount(bits);
460 const limb_cnt = usedLimbCount(bits);
440461 const out = out_ptr[0..limb_cnt];
441462 const a = a_ptr[0..limb_cnt];
442463
......@@ -477,6 +498,7 @@ fn __shlo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bo
477498 overflow = overflow or limbGet(a, j) != sign_extend;
478499 }
479500
501 fixLastLimb(out_ptr, is_signed, bits);
480502 return overflow;
481503}
482504
......@@ -526,7 +548,7 @@ comptime {
526548}
527549
528550fn __shr_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) void {
529 const limb_cnt = limbCount(bits);
551 const limb_cnt = usedLimbCount(bits);
530552 const out = out_ptr[0..limb_cnt];
531553 const a = a_ptr[0..limb_cnt];
532554
......@@ -594,7 +616,7 @@ comptime {
594616}
595617
596618fn __clz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 {
597 const limb_cnt = limbCount(bits);
619 const limb_cnt = usedLimbCount(bits);
598620 const a = a_ptr[0..limb_cnt];
599621
600622 var res: u16 = 0;
......@@ -652,7 +674,7 @@ comptime {
652674}
653675
654676fn __ctz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 {
655 const limb_cnt = limbCount(bits);
677 const limb_cnt = usedLimbCount(bits);
656678 const a = a_ptr[0..limb_cnt];
657679
658680 var res: u16 = 0;
......@@ -705,7 +727,7 @@ comptime {
705727}
706728
707729fn __popcount_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 {
708 const limb_cnt = limbCount(bits);
730 const limb_cnt = usedLimbCount(bits);
709731 const a = a_ptr[0..limb_cnt];
710732
711733 var res: u16 = 0;
......@@ -751,7 +773,7 @@ comptime {
751773}
752774
753775fn __bitreverse_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void {
754 const limb_cnt = limbCount(bits);
776 const limb_cnt = usedLimbCount(bits);
755777 const out = out_ptr[0..limb_cnt];
756778 const a = a_ptr[0..limb_cnt];
757779
......@@ -764,6 +786,7 @@ fn __bitreverse_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bi
764786 if (bits % 64 != 0) {
765787 __shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits);
766788 }
789 fixLastLimb(out_ptr, is_signed, bits);
767790}
768791
769792fn test__bitreverse_limb64(comptime T: type, a: T, expected: T) !void {
......@@ -797,7 +820,7 @@ comptime {
797820}
798821
799822fn __byteswap_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void {
800 const limb_cnt = limbCount(bits);
823 const limb_cnt = usedLimbCount(bits);
801824 const out = out_ptr[0..limb_cnt];
802825 const a = a_ptr[0..limb_cnt];
803826
......@@ -812,6 +835,7 @@ fn __byteswap_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits
812835 if (bits % 64 != 0) {
813836 __shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits);
814837 }
838 fixLastLimb(out_ptr, is_signed, bits);
815839}
816840
817841fn test__byteswap_limb64(comptime T: type, a: T, expected: T) !void {
......@@ -861,7 +885,7 @@ fn mulwide(a: u64, b: u64) [2]u64 {
861885}
862886
863887fn __mulo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool {
864 const limb_cnt = limbCount(bits);
888 const limb_cnt = usedLimbCount(bits);
865889
866890 const out = out_ptr[0..limb_cnt];
867891 const a = a_ptr[0..limb_cnt];
......@@ -921,6 +945,8 @@ fn __mulo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_s
921945 limbSet(out, limb_cnt - 1, last);
922946 }
923947
948 fixLastLimb(out_ptr, is_signed, bits);
949
924950 if (!is_signed) {
925951 return !hi_zero or raw_last != last;
926952 }
src/codegen/wasm/CodeGen.zig+119-39
......@@ -2357,6 +2357,15 @@ const IntType = struct {
23572357 }
23582358};
23592359
2360fn intBackingBits(cg: *CodeGen, bits: u16) u16 {
2361 return switch (bits) {
2362 0 => unreachable,
2363 1...32 => 32,
2364 33...64 => 64,
2365 else => std.zig.target.intByteSize(cg.target, bits) * 8,
2366 };
2367}
2368
23602369fn intAdd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
23612370 switch (ty.bits) {
23622371 0 => unreachable,
......@@ -2518,7 +2527,15 @@ fn intDiv(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
25182527 return cg.callIntrinsic(.__udivti3, &.{ .i128_type, .i128_type }, Type.i128, &.{ lhs, rhs });
25192528 }
25202529 },
2521 else => return cg.fail("TODO: Support intDiv for integer bitsize: {d}", .{ty.bits}),
2530 else => {
2531 const result = try cg.allocInt(ty);
2532 if (ty.is_signed) {
2533 _ = try cg.callIntrinsic(.__divei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });
2534 } else {
2535 _ = try cg.callIntrinsic(.__udivei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });
2536 }
2537 return result;
2538 },
25222539 }
25232540}
25242541
......@@ -2570,7 +2587,22 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W
25702587 try cg.addTag(.i64_sub);
25712588 return .stack;
25722589 },
2573 else => return cg.fail("TODO: Support intDivFloor for signed integer bitsize: {d}", .{ty.bits}),
2590 else => {
2591 const q = try cg.intDiv(ty, lhs, rhs);
2592
2593 const zero = try cg.intZeroValue(ty);
2594
2595 const r = try cg.intRem(ty, lhs, rhs);
2596 _ = try cg.intCmp(ty, .neq, r, zero);
2597
2598 const sign_xor = try cg.intXor(ty, lhs, rhs);
2599 _ = try cg.intCmp(ty, .lt, sign_xor, zero);
2600 var adjust = try (try cg.intAnd(.u32, .stack, .stack)).toLocal(cg, Type.u32);
2601
2602 const adjust_bigint = try cg.intCast(ty, .u32, adjust);
2603 adjust.free(cg);
2604 return try cg.intSub(ty, q, adjust_bigint);
2605 },
25742606 }
25752607}
25762608
......@@ -2596,7 +2628,15 @@ fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
25962628 return cg.callIntrinsic(.__umodti3, &.{ .i128_type, .i128_type }, Type.i128, &.{ lhs, rhs });
25972629 }
25982630 },
2599 else => return cg.fail("TODO: Support intRem for integer bitsize: {d}", .{ty.bits}),
2631 else => {
2632 const result = try cg.allocInt(ty);
2633 if (ty.is_signed) {
2634 _ = try cg.callIntrinsic(.__modei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });
2635 } else {
2636 _ = try cg.callIntrinsic(.__umodei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });
2637 }
2638 return result;
2639 },
26002640 }
26012641}
26022642
......@@ -3315,26 +3355,43 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
33153355 },
33163356 128 => return operand,
33173357 else => {
3318 const bits = mem.alignForward(u16, ty.bits, 64);
3358 const bits = cg.intBackingBits(ty.bits);
33193359 if (ty.bits == bits) return operand;
33203360
33213361 const result = try cg.allocInt(ty);
33223362
3323 const len = bits / 8;
3324 try cg.memcpy(result, operand, .{ .imm32 = len - 8 });
3363 const copy_len = (ty.bits / 64) * 8;
3364 try cg.memcpy(result, operand, .{ .imm32 = copy_len });
33253365
3326 try cg.emitWValue(result);
3327 _ = try cg.load(operand, Type.u64, len - 8);
3328 if (ty.is_signed) {
3329 try cg.addImm64(bits - ty.bits);
3330 try cg.addTag(.i64_shl);
3331 try cg.addImm64(bits - ty.bits);
3332 try cg.addTag(.i64_shr_s);
3333 } else {
3334 try cg.addImm64(~@as(u64, 0) >> @intCast(bits - ty.bits));
3335 try cg.addTag(.i64_and);
3366 if (ty.bits % 64 != 0) {
3367 const pad = 64 - ty.bits % 64;
3368
3369 try cg.emitWValue(result);
3370 _ = try cg.load(operand, Type.u64, copy_len);
3371 if (ty.is_signed) {
3372 try cg.addImm64(pad);
3373 try cg.addTag(.i64_shl);
3374 try cg.addImm64(pad);
3375 try cg.addTag(.i64_shr_s);
3376 } else {
3377 try cg.addImm64(~@as(u64, 0) >> @intCast(pad));
3378 try cg.addTag(.i64_and);
3379 }
3380 try cg.store(.stack, .stack, Type.u64, result.offset() + copy_len);
3381 }
3382
3383 const full_len = @divExact(bits, 8);
3384 if (copy_len + 16 == full_len) { // last limb needs sign extended
3385 try cg.emitWValue(result);
3386 if (ty.is_signed) {
3387 _ = try cg.load(result, Type.u64, copy_len);
3388 try cg.addImm64(63);
3389 try cg.addTag(.i64_shr_s);
3390 } else {
3391 try cg.addImm64(0);
3392 }
3393 try cg.store(.stack, .stack, Type.u64, result.offset() + copy_len + 8);
33363394 }
3337 try cg.store(.stack, .stack, Type.u64, result.offset() + len - 8);
33383395
33393396 return result;
33403397 },
......@@ -3354,8 +3411,8 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
33543411 } else {
33553412 return .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_ty.bits) };
33563413 }
3357 } else {
3358 const result = try cg.allocStack(Type.u128);
3414 } else if (int_ty.bits <= 128) {
3415 const result = try cg.allocInt(int_ty);
33593416 try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, Type.u64, 0);
33603417
33613418 if (int_ty.is_signed) {
......@@ -3363,6 +3420,24 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
33633420 } else {
33643421 try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast(128 - int_ty.bits) }, Type.u64, 8);
33653422 }
3423 return result;
3424 } else {
3425 const result = try cg.allocInt(int_ty);
3426 const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);
3427 const normal_len = (int_ty.bits / 64) * 8;
3428
3429 try cg.memset(Type.u8, result, .{ .imm32 = normal_len }, .{ .imm32 = 0xFF });
3430
3431 if (int_ty.is_signed) {
3432 try cg.store(result, .{ .imm64 = (~@as(u64, 0) >> @intCast((normal_len + 8) * 8 - int_ty.bits)) >> 1 }, Type.u64, normal_len);
3433 } else {
3434 try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast((normal_len + 8) * 8 - int_ty.bits) }, Type.u64, normal_len);
3435 }
3436
3437 if (normal_len + 16 == full_len) {
3438 try cg.store(result, .{ .imm64 = 0 }, Type.u64, full_len - 8);
3439 }
3440
33663441 return result;
33673442 }
33683443}
......@@ -3375,10 +3450,23 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
33753450 return .{ .imm32 = ~@as(u32, 0) << @intCast(int_ty.bits - 1) };
33763451 } else if (int_ty.bits <= 64) {
33773452 return .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 1) };
3378 } else {
3379 const result = try cg.allocStack(Type.u128);
3453 } else if (int_ty.bits <= 128) {
3454 const result = try cg.allocInt(int_ty);
33803455 try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0);
33813456 try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 65) }, Type.u64, 8);
3457 return result;
3458 } else {
3459 const result = try cg.allocInt(int_ty);
3460 const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);
3461 const normal_len = (int_ty.bits / 64) * 8;
3462
3463 try cg.memset(Type.u8, result, .{ .imm32 = normal_len }, .{ .imm32 = 0 });
3464 try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - normal_len * 8 - 1) }, Type.u64, normal_len);
3465
3466 if (normal_len + 16 == full_len) {
3467 try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, Type.u64, full_len - 8);
3468 }
3469
33823470 return result;
33833471 }
33843472}
......@@ -3572,12 +3660,17 @@ fn intZeroValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
35723660 1...32 => return .{ .imm32 = 0 },
35733661 33...64 => return .{ .imm64 = 0 },
35743662 65...128 => {
3575 const result = try cg.allocStack(Type.u128);
3663 const result = try cg.allocInt(int_ty);
35763664 try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0);
35773665 try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8);
35783666 return result;
35793667 },
3580 else => return cg.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_ty.bits}),
3668 else => {
3669 const result = try cg.allocInt(int_ty);
3670 const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);
3671 try cg.memset(Type.u8, result, .{ .imm32 = full_len }, .{ .imm32 = 0 });
3672 return result;
3673 },
35813674 }
35823675}
35833676
......@@ -3757,17 +3850,8 @@ fn intShlOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro
37573850}
37583851
37593852fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue {
3760 const src_bits: u16 = switch (src_ty.bits) {
3761 0 => unreachable,
3762 1...32 => 32,
3763 else => mem.alignForward(u16, src_ty.bits, 64),
3764 };
3765
3766 const dest_bits: u16 = switch (dest_ty.bits) {
3767 0 => unreachable,
3768 1...32 => 32,
3769 else => mem.alignForward(u16, dest_ty.bits, 64),
3770 };
3853 const src_bits: u16 = cg.intBackingBits(src_ty.bits);
3854 const dest_bits: u16 = cg.intBackingBits(dest_ty.bits);
37713855
37723856 if (src_bits == dest_bits) {
37733857 return operand;
......@@ -3859,11 +3943,7 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn
38593943fn intTrunc(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue {
38603944 var result = try cg.intCast(dest_ty, src_ty, operand);
38613945
3862 const dest_wasm_bits: u16 = switch (dest_ty.bits) {
3863 0 => unreachable,
3864 1...32 => 32,
3865 else => mem.alignForward(u16, dest_ty.bits, 64),
3866 };
3946 const dest_wasm_bits = cg.intBackingBits(dest_ty.bits);
38673947
38683948 if (dest_wasm_bits != dest_ty.bits) {
38693949 result = try cg.intWrap(dest_ty, result);
src/codegen/wasm/Mir.zig+4
......@@ -825,6 +825,7 @@ pub const Intrinsic = enum(u32) {
825825 __ceilx,
826826 __cosh,
827827 __cosx,
828 __divei4,
828829 __divhf3,
829830 __divtf3,
830831 __divti3,
......@@ -950,6 +951,7 @@ pub const Intrinsic = enum(u32) {
950951 __lshrti3,
951952 __lttf2,
952953 __ltxf2,
954 __modei4,
953955 __modti3,
954956 __mulhf3,
955957 __mulodi4,
......@@ -980,7 +982,9 @@ pub const Intrinsic = enum(u32) {
980982 __truncxfdf2,
981983 __truncxfhf2,
982984 __truncxfsf2,
985 __udivei4,
983986 __udivti3,
987 __umodei4,
984988 __umodti3,
985989 ceilq,
986990 cos,
test/behavior/math.zig+106
......@@ -1736,6 +1736,112 @@ test "@abs > 128 bits" {
17361736 try testAbs(i200, minInt(i200), 1 << 199);
17371737}
17381738
1739fn testRem(comptime T: type, numerator: T, denominator: T, expected: T) !void {
1740 try expect(@rem(numerator, denominator) == expected);
1741}
1742
1743test "@rem > 128 bits" {
1744 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1745
1746 try testRem(u140, 0, maxInt(u140), 0);
1747 try testRem(u140, maxInt(u140), maxInt(u140), 0);
1748 try testRem(u140, maxInt(u140), 2, 1);
1749 try testRem(u140, (1 << 139) + 5, 1 << 70, 5);
1750 try testRem(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, 7);
1751 try testRem(u200, 123, 1 << 100, 123);
1752 try testRem(u200, 1 << 120, 1 << 60, 0);
1753 try testRem(u200, maxInt(u200), 1 << 100, (1 << 100) - 1);
1754
1755 try testRem(i140, 0, maxInt(i140), 0);
1756 try testRem(i140, maxInt(i140), maxInt(i140), 0);
1757 try testRem(i140, -((1 << 100) + 1), 1 << 50, -1);
1758 try testRem(i140, (1 << 100) + 1, -(1 << 50), 1);
1759 try testRem(i140, -((1 << 100) + 1), -(1 << 50), -1);
1760 try testRem(i200, minInt(i200), 1, 0);
1761 try testRem(i200, minInt(i200), -2, 0);
1762 try testRem(i200, maxInt(i200), 2, 1);
1763}
1764
1765fn testMod(comptime T: type, numerator: T, denominator: T, expected: T) !void {
1766 try expect(@mod(numerator, denominator) == expected);
1767}
1768
1769test "@mod > 128 bits" {
1770 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1771
1772 try testMod(u140, 0, maxInt(u140), 0);
1773 try testMod(u140, maxInt(u140), maxInt(u140), 0);
1774 try testMod(u140, maxInt(u140), 2, 1);
1775 try testMod(u140, (1 << 139) + 5, 1 << 70, 5);
1776 try testMod(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, 7);
1777 try testMod(u200, 123, 1 << 100, 123);
1778 try testMod(u200, 1 << 120, 1 << 60, 0);
1779 try testMod(u200, maxInt(u200), 1 << 100, (1 << 100) - 1);
1780
1781 try testMod(i140, 0, maxInt(i140), 0);
1782 try testMod(i140, maxInt(i140), maxInt(i140), 0);
1783 try testMod(i140, -((1 << 100) + 1), 1 << 50, (1 << 50) - 1);
1784 try testMod(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50) + 1);
1785 try testMod(i140, -((1 << 100) + 1), -(1 << 50), -1);
1786 try testMod(i200, minInt(i200), 1, 0);
1787 try testMod(i200, minInt(i200), -2, 0);
1788 try testMod(i200, maxInt(i200), 2, 1);
1789}
1790
1791fn testDivFloor(comptime T: type, numerator: T, denominator: T, expected: T) !void {
1792 try expect(@divFloor(numerator, denominator) == expected);
1793}
1794
1795test "@divFloor > 128 bits" {
1796 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1797
1798 try testDivFloor(u140, 0, maxInt(u140), 0);
1799 try testDivFloor(u140, maxInt(u140), maxInt(u140), 1);
1800 try testDivFloor(u140, maxInt(u140), 2, maxInt(u140) >> 1);
1801 try testDivFloor(u140, (1 << 139) + 5, 1 << 70, 1 << 69);
1802 try testDivFloor(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, (1 << 50) + 1);
1803 try testDivFloor(u200, 123, 1 << 100, 0);
1804 try testDivFloor(u200, 1 << 120, 1 << 60, 1 << 60);
1805 try testDivFloor(u200, maxInt(u200), 1 << 100, (1 << 100) - 1);
1806
1807 try testDivFloor(i140, 0, maxInt(i140), 0);
1808 try testDivFloor(i140, maxInt(i140), maxInt(i140), 1);
1809 try testDivFloor(i140, -((1 << 100) + 1), 1 << 50, -(1 << 50) - 1);
1810 try testDivFloor(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50) - 1);
1811 try testDivFloor(i140, -((1 << 100) + 1), -(1 << 50), 1 << 50);
1812 try testDivFloor(i200, -3, 2, -2);
1813 try testDivFloor(i200, minInt(i200), 1, minInt(i200));
1814 try testDivFloor(i200, minInt(i200), -2, 1 << 198);
1815 try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1);
1816}
1817
1818fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void {
1819 try expect(@divTrunc(numerator, denominator) == expected);
1820}
1821
1822test "@divTrunc > 128 bits" {
1823 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1824
1825 try testDivTrunc(u140, 0, maxInt(u140), 0);
1826 try testDivTrunc(u140, maxInt(u140), maxInt(u140), 1);
1827 try testDivTrunc(u140, maxInt(u140), 2, maxInt(u140) >> 1);
1828 try testDivTrunc(u140, (1 << 139) + 5, 1 << 70, 1 << 69);
1829 try testDivTrunc(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, (1 << 50) + 1);
1830 try testDivTrunc(u200, 123, 1 << 100, 0);
1831 try testDivTrunc(u200, 1 << 120, 1 << 60, 1 << 60);
1832 try testDivTrunc(u200, maxInt(u200), 1 << 100, (1 << 100) - 1);
1833
1834 try testDivTrunc(i140, 0, maxInt(i140), 0);
1835 try testDivTrunc(i140, maxInt(i140), maxInt(i140), 1);
1836 try testDivTrunc(i140, -((1 << 100) + 1), 1 << 50, -(1 << 50));
1837 try testDivTrunc(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50));
1838 try testDivTrunc(i140, -((1 << 100) + 1), -(1 << 50), 1 << 50);
1839 try testDivTrunc(i200, -3, 2, -1);
1840 try testDivTrunc(i200, minInt(i200), 1, minInt(i200));
1841 try testDivTrunc(i200, minInt(i200), -2, 1 << 198);
1842 try testDivTrunc(i200, maxInt(i200), 2, (1 << 198) - 1);
1843}
1844
17391845test "overflow arithmetic with u0 values" {
17401846 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
17411847