| author | |
| committer | |
| log | ed9b0655f7e3ed8efa1fe1b1beb0fa13bf37b560 |
| tree | 3ddbd5852385cbabc7c50889f4e5293c16996ccc |
| parent | 07e3e50fd28d98edaf893b9d58d97e7d2b895d38 |
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; |
| 10 | comptime { | 10 | comptime { |
| 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 | } |
| 14 | 16 | ||
| 15 | inline fn limb(x: []u32, i: usize) *u32 { | 17 | inline 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 | } |
| 18 | 20 | ||
| 19 | inline fn neg(x: []u32) void { | 21 | inline 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 | } |
| 26 | 28 | ||
| 27 | const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable; | 29 | fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32, tu: []u32, tv: []u32) !void { |
| 28 | 30 | const u_sign: i32 = @bitCast(u[limb(u.len - 1, u.len)]); | |
| 29 | fn 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 | } |
| 44 | 38 | ||
| 45 | pub fn __divei4(q_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, bits: usize) callconv(.c) void { | 39 | pub 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 | } |
| 53 | 47 | ||
| 54 | pub fn __modei4(r_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) void { | 48 | pub 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 | |||
| 57 | pub 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 | |||
| 68 | pub 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 |
| 13 | comptime { | 13 | comptime { |
| 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 | } |
| 17 | 19 | ||
| 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 | } |
| 134 | 136 | ||
| 137 | pub 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 | |||
| 150 | pub 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 | |||
| 135 | test "__udivei4/__umodei4" { | 163 | test "__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, |