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;
1010comptime {
1111 symbol(&__divei4, "__divei4");
1212 symbol(&__modei4, "__modei4");
13 symbol(&__divei5, "__divei5");
14 symbol(&__modei5, "__modei5");
1315}
1416
15inline fn limb(x: []u32, i: usize) *u32 {
16 return if (endian == .little) &x[i] else &x[x.len - 1 - i];
17inline fn limb(i: usize, len: usize) usize {
18 return if (endian == .little) i else len - 1 - i;
1719}
1820
19inline fn neg(x: []u32) void {
21inline fn neg(out: []u32, in: []const u32) void {
2022 var ov: u1 = 1;
21 for (0..x.len) |limb_index| {
22 const l = limb(x, limb_index);
23 l.*, ov = @addWithOverflow(~l.*, ov);
23 for (0..in.len) |limb_index| {
24 const new, ov = @addWithOverflow(~in[limb(limb_index, in.len)], ov);
25 out[limb(limb_index, out.len)] = new;
2426 }
2527}
2628
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 {
30 const u_sign: i32 = @bitCast(u[u.len - 1]);
31 const v_sign: i32 = @bitCast(v[v.len - 1]);
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 });
41 if (q) |x| if (u_sign ^ v_sign < 0) neg(x);
42 if (r) |x| if (u_sign < 0) neg(x);
29fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32, tu: []u32, tv: []u32) !void {
30 const u_sign: i32 = @bitCast(u[limb(u.len - 1, u.len)]);
31 const v_sign: i32 = @bitCast(v[limb(v.len - 1, v.len)]);
32 if (u_sign < 0) neg(tu, u);
33 if (v_sign < 0) neg(tv, v);
34 try @call(.always_inline, udivmod, .{ q, r, if (u_sign < 0) tu else u, if (v_sign < 0) tv else v });
35 if (q) |x| if (u_sign ^ v_sign < 0) neg(x, x);
36 if (r) |x| if (u_sign < 0) neg(x, x);
4337}
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 {
4640 @setRuntimeSafety(compiler_rt.test_safety);
4741 const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits));
4842 const q: []u32 = @ptrCast(@alignCast(q_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]));
51 @call(.always_inline, divmod, .{ q, null, u, v }) catch unreachable;
43 const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
44 const v: []u32 = @ptrCast(@alignCast(v_p[0..byte_size]));
45 @call(.always_inline, divmod, .{ q, null, u, v, u, v }) catch unreachable;
5246}
5347
5448pub 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
5751 const r: []u32 = @ptrCast(@alignCast(r_p[0..byte_size]));
5852 const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
5953 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;
6177}
lib/compiler_rt/udivmodei4.zig+28
......@@ -13,6 +13,8 @@ const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable; // max s
1313comptime {
1414 symbol(&__udivei4, "__udivei4");
1515 symbol(&__umodei4, "__umodei4");
16 symbol(&__udivei5, "__udivei5");
17 symbol(&__umodei5, "__umodei5");
1618}
1719
1820/// 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
132134 @call(.always_inline, divmod, .{ null, r, u, v }) catch unreachable;
133135}
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
135163test "__udivei4/__umodei4" {
136164 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
137165 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
25302530 },
25312531 else => {
25322532 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 });
25332535 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 );
25352542 } 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 );
25372549 }
2550 tmp.free(cg);
25382551 return result;
25392552 },
25402553 }
......@@ -2631,11 +2644,24 @@ fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
26312644 },
26322645 else => {
26332646 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 });
26342649 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 );
26362656 } 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 );
26382663 }
2664 tmp.free(cg);
26392665 return result;
26402666 },
26412667 }
src/codegen/wasm/Mir.zig+4-4
......@@ -825,7 +825,7 @@ pub const Intrinsic = enum(u32) {
825825 __ceilx,
826826 __cosh,
827827 __cosx,
828 __divei4,
828 __divei5,
829829 __divhf3,
830830 __divtf3,
831831 __divti3,
......@@ -951,7 +951,7 @@ pub const Intrinsic = enum(u32) {
951951 __lshrti3,
952952 __lttf2,
953953 __ltxf2,
954 __modei4,
954 __modei5,
955955 __modti3,
956956 __mulhf3,
957957 __mulodi4,
......@@ -982,9 +982,9 @@ pub const Intrinsic = enum(u32) {
982982 __truncxfdf2,
983983 __truncxfhf2,
984984 __truncxfsf2,
985 __udivei4,
985 __udivei5,
986986 __udivti3,
987 __umodei4,
987 __umodei5,
988988 __umodti3,
989989 ceilq,
990990 cos,