authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-04-08 16:47:23+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-04-09 00:14:09+02:00
loged9b0655f7e3ed8efa1fe1b1beb0fa13bf37b560
tree3ddbd5852385cbabc7c50889f4e5293c16996ccc
parent07e3e50fd28d98edaf893b9d58d97e7d2b895d38

stage2-wasm: introduce __u?(div|mod)ei5, revert prev approach

Co-authored-by: Jacob Young <jacobly0@users.noreply.github.com>

4 files changed, 105 insertions(+), 35 deletions(-)

lib/compiler_rt/divmodei4.zig+43-27
...@@ -10,45 +10,39 @@ const symbol = @import("../compiler_rt.zig").symbol;...@@ -10,45 +10,39 @@ const symbol = @import("../compiler_rt.zig").symbol;
10comptime {10comptime {
11 symbol(&__divei4, "__divei4");11 symbol(&__divei4, "__divei4");
12 symbol(&__modei4, "__modei4");12 symbol(&__modei4, "__modei4");
13 symbol(&__divei5, "__divei5");
14 symbol(&__modei5, "__modei5");
13}15}
1416
15inline fn limb(x: []u32, i: usize) *u32 {17inline fn limb(i: usize, len: usize) usize {
16 return if (endian == .little) &x[i] else &x[x.len - 1 - i];18 return if (endian == .little) i else len - 1 - i;
17}19}
1820
19inline fn neg(x: []u32) void {21inline fn neg(out: []u32, in: []const u32) void {
20 var ov: u1 = 1;22 var ov: u1 = 1;
21 for (0..x.len) |limb_index| {23 for (0..in.len) |limb_index| {
22 const l = limb(x, limb_index);24 const new, ov = @addWithOverflow(~in[limb(limb_index, in.len)], ov);
23 l.*, ov = @addWithOverflow(~l.*, ov);25 out[limb(limb_index, out.len)] = new;
24 }26 }
25}27}
2628
27const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable;29fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32, tu: []u32, tv: []u32) !void {
2830 const u_sign: i32 = @bitCast(u[limb(u.len - 1, u.len)]);
29fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void {31 const v_sign: i32 = @bitCast(v[limb(v.len - 1, v.len)]);
30 const u_sign: i32 = @bitCast(u[u.len - 1]);32 if (u_sign < 0) neg(tu, u);
31 const v_sign: i32 = @bitCast(v[v.len - 1]);33 if (v_sign < 0) neg(tv, v);
32 var ua: [max_limbs]u32 = undefined;34 try @call(.always_inline, udivmod, .{ q, r, if (u_sign < 0) tu else u, if (v_sign < 0) tv else v });
33 const us = ua[0..u.len];35 if (q) |x| if (u_sign ^ v_sign < 0) neg(x, x);
34 @memcpy(us, u);36 if (r) |x| if (u_sign < 0) neg(x, x);
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 });
41 if (q) |x| if (u_sign ^ v_sign < 0) neg(x);
42 if (r) |x| if (u_sign < 0) neg(x);
43}37}
4438
45pub fn __divei4(q_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, bits: usize) callconv(.c) void {39pub fn __divei4(q_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) void {
46 @setRuntimeSafety(compiler_rt.test_safety);40 @setRuntimeSafety(compiler_rt.test_safety);
47 const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits));41 const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits));
48 const q: []u32 = @ptrCast(@alignCast(q_p[0..byte_size]));42 const q: []u32 = @ptrCast(@alignCast(q_p[0..byte_size]));
49 const u: []const u32 = @ptrCast(@alignCast(u_p[0..byte_size]));43 const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
50 const v: []const u32 = @ptrCast(@alignCast(v_p[0..byte_size]));44 const v: []u32 = @ptrCast(@alignCast(v_p[0..byte_size]));
51 @call(.always_inline, divmod, .{ q, null, u, v }) catch unreachable;45 @call(.always_inline, divmod, .{ q, null, u, v, u, v }) catch unreachable;
52}46}
5347
54pub fn __modei4(r_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) void {48pub fn __modei4(r_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) void {
...@@ -57,5 +51,27 @@ pub fn __modei4(r_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) vo...@@ -57,5 +51,27 @@ pub fn __modei4(r_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) vo
57 const r: []u32 = @ptrCast(@alignCast(r_p[0..byte_size]));51 const r: []u32 = @ptrCast(@alignCast(r_p[0..byte_size]));
58 const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size]));52 const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
59 const v: []u32 = @ptrCast(@alignCast(v_p[0..byte_size]));53 const v: []u32 = @ptrCast(@alignCast(v_p[0..byte_size]));
60 @call(.always_inline, divmod, .{ null, r, u, v }) catch unreachable;54 @call(.always_inline, divmod, .{ null, r, u, v, u, v }) catch unreachable;
55}
56
57pub fn __divei5(q_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, t_p: [*]u8, bits: usize) callconv(.c) void {
58 @setRuntimeSafety(compiler_rt.test_safety);
59 const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits));
60 const q: []u32 = @ptrCast(@alignCast(q_p[0..byte_size]));
61 const u: []const u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
62 const v: []const u32 = @ptrCast(@alignCast(v_p[0..byte_size]));
63 const tu: []u32 = @ptrCast(@alignCast(t_p[0..byte_size]));
64 const tv: []u32 = @ptrCast(@alignCast(t_p[byte_size..][0..byte_size]));
65 @call(.always_inline, divmod, .{ q, null, u, v, tu, tv }) catch unreachable;
66}
67
68pub fn __modei5(r_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, t_p: [*]u8, bits: usize) callconv(.c) void {
69 @setRuntimeSafety(compiler_rt.test_safety);
70 const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits));
71 const r: []u32 = @ptrCast(@alignCast(r_p[0..byte_size]));
72 const u: []const u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
73 const v: []const u32 = @ptrCast(@alignCast(v_p[0..byte_size]));
74 const tu: []u32 = @ptrCast(@alignCast(t_p[0..byte_size]));
75 const tv: []u32 = @ptrCast(@alignCast(t_p[byte_size..][0..byte_size]));
76 @call(.always_inline, divmod, .{ null, r, u, v, tu, tv }) catch unreachable;
61}77}
lib/compiler_rt/udivmodei4.zig+28
...@@ -13,6 +13,8 @@ const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable; // max s...@@ -13,6 +13,8 @@ const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable; // max s
13comptime {13comptime {
14 symbol(&__udivei4, "__udivei4");14 symbol(&__udivei4, "__udivei4");
15 symbol(&__umodei4, "__umodei4");15 symbol(&__umodei4, "__umodei4");
16 symbol(&__udivei5, "__udivei5");
17 symbol(&__umodei5, "__umodei5");
16}18}
1719
18/// Get the value of a limb.20/// Get the value of a limb.
...@@ -132,6 +134,32 @@ pub fn __umodei4(r_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, bits: usize) ca...@@ -132,6 +134,32 @@ pub fn __umodei4(r_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, bits: usize) ca
132 @call(.always_inline, divmod, .{ null, r, u, v }) catch unreachable;134 @call(.always_inline, divmod, .{ null, r, u, v }) catch unreachable;
133}135}
134136
137pub fn __udivei5(q_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, t_p: [*]u8, bits: usize) callconv(.c) void {
138 @setRuntimeSafety(compiler_rt.test_safety);
139 const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits));
140 const q: []u32 = @ptrCast(@alignCast(q_p[0..byte_size]));
141 const u: []const u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
142 const v: []const u32 = @ptrCast(@alignCast(v_p[0..byte_size]));
143 const tu: []u32 = @ptrCast(@alignCast(t_p[0..byte_size]));
144 _ = tu;
145 const tv: []u32 = @ptrCast(@alignCast(t_p[byte_size..][0..byte_size]));
146 _ = tv;
147 @call(.always_inline, divmod, .{ q, null, u, v }) catch unreachable;
148}
149
150pub fn __umodei5(r_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, t_p: [*]u8, bits: usize) callconv(.c) void {
151 @setRuntimeSafety(compiler_rt.test_safety);
152 const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits));
153 const r: []u32 = @ptrCast(@alignCast(r_p[0..byte_size]));
154 const u: []const u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
155 const v: []const u32 = @ptrCast(@alignCast(v_p[0..byte_size]));
156 const tu: []u32 = @ptrCast(@alignCast(t_p[0..byte_size]));
157 _ = tu;
158 const tv: []u32 = @ptrCast(@alignCast(t_p[byte_size..][0..byte_size]));
159 _ = tv;
160 @call(.always_inline, divmod, .{ null, r, u, v }) catch unreachable;
161}
162
135test "__udivei4/__umodei4" {163test "__udivei4/__umodei4" {
136 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;164 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
137 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;165 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
src/codegen/wasm/CodeGen.zig+30-4
...@@ -2530,11 +2530,24 @@ fn intDiv(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue...@@ -2530,11 +2530,24 @@ fn intDiv(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
2530 },2530 },
2531 else => {2531 else => {
2532 const result = try cg.allocInt(ty);2532 const result = try cg.allocInt(ty);
2533 const bits = cg.intBackingBits(ty.bits);
2534 var tmp = try cg.allocInt(.{ .is_signed = false, .bits = bits * 2 });
2533 if (ty.is_signed) {2535 if (ty.is_signed) {
2534 _ = try cg.callIntrinsic(.__divei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });2536 _ = try cg.callIntrinsic(
2537 .__divei5,
2538 &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type },
2539 .void,
2540 &.{ result, lhs, rhs, tmp, .{ .imm32 = ty.bits } },
2541 );
2535 } else {2542 } else {
2536 _ = try cg.callIntrinsic(.__udivei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });2543 _ = try cg.callIntrinsic(
2544 .__udivei5,
2545 &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type },
2546 .void,
2547 &.{ result, lhs, rhs, tmp, .{ .imm32 = ty.bits } },
2548 );
2537 }2549 }
2550 tmp.free(cg);
2538 return result;2551 return result;
2539 },2552 },
2540 }2553 }
...@@ -2631,11 +2644,24 @@ fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue...@@ -2631,11 +2644,24 @@ fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
2631 },2644 },
2632 else => {2645 else => {
2633 const result = try cg.allocInt(ty);2646 const result = try cg.allocInt(ty);
2647 const bits = cg.intBackingBits(ty.bits);
2648 var tmp = try cg.allocInt(.{ .is_signed = false, .bits = bits * 2 });
2634 if (ty.is_signed) {2649 if (ty.is_signed) {
2635 _ = try cg.callIntrinsic(.__modei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });2650 _ = try cg.callIntrinsic(
2651 .__modei5,
2652 &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type },
2653 .void,
2654 &.{ result, lhs, rhs, tmp, .{ .imm32 = ty.bits } },
2655 );
2636 } else {2656 } else {
2637 _ = try cg.callIntrinsic(.__umodei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });2657 _ = try cg.callIntrinsic(
2658 .__umodei5,
2659 &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type },
2660 .void,
2661 &.{ result, lhs, rhs, tmp, .{ .imm32 = ty.bits } },
2662 );
2638 }2663 }
2664 tmp.free(cg);
2639 return result;2665 return result;
2640 },2666 },
2641 }2667 }
src/codegen/wasm/Mir.zig+4-4
...@@ -825,7 +825,7 @@ pub const Intrinsic = enum(u32) {...@@ -825,7 +825,7 @@ pub const Intrinsic = enum(u32) {
825 __ceilx,825 __ceilx,
826 __cosh,826 __cosh,
827 __cosx,827 __cosx,
828 __divei4,828 __divei5,
829 __divhf3,829 __divhf3,
830 __divtf3,830 __divtf3,
831 __divti3,831 __divti3,
...@@ -951,7 +951,7 @@ pub const Intrinsic = enum(u32) {...@@ -951,7 +951,7 @@ pub const Intrinsic = enum(u32) {
951 __lshrti3,951 __lshrti3,
952 __lttf2,952 __lttf2,
953 __ltxf2,953 __ltxf2,
954 __modei4,954 __modei5,
955 __modti3,955 __modti3,
956 __mulhf3,956 __mulhf3,
957 __mulodi4,957 __mulodi4,
...@@ -982,9 +982,9 @@ pub const Intrinsic = enum(u32) {...@@ -982,9 +982,9 @@ pub const Intrinsic = enum(u32) {
982 __truncxfdf2,982 __truncxfdf2,
983 __truncxfhf2,983 __truncxfhf2,
984 __truncxfsf2,984 __truncxfsf2,
985 __udivei4,985 __udivei5,
986 __udivti3,986 __udivti3,
987 __umodei4,987 __umodei5,
988 __umodti3,988 __umodti3,
989 ceilq,989 ceilq,
990 cos,990 cos,