| author | |
| committer | |
| log | aa7874657b3439134eb4cd8b65271fb9cc38fdad |
| tree | babc9d61c1cbed627cdba474787e7959c67cd4dc |
| parent | fff887874e917e2955002c323fb0286cedfbfd84 |
5 files changed, 283 insertions(+), 60 deletions(-)
lib/compiler_rt/divmodei4.zig+15-8| ... | @@ -24,23 +24,30 @@ inline fn neg(x: []u32) void { | ... | @@ -24,23 +24,30 @@ inline fn neg(x: []u32) void { |
| 24 | } | 24 | } |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | /// Mutates the arguments! | 27 | const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable; |
| 28 | fn divmod(q: ?[]u32, r: ?[]u32, u: []u32, v: []u32) !void { | 28 | |
| 29 | fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void { | ||
| 29 | const u_sign: i32 = @bitCast(u[u.len - 1]); | 30 | const u_sign: i32 = @bitCast(u[u.len - 1]); |
| 30 | const v_sign: i32 = @bitCast(v[v.len - 1]); | 31 | const v_sign: i32 = @bitCast(v[v.len - 1]); |
| 31 | if (u_sign < 0) neg(u); | 32 | var ua: [max_limbs]u32 = undefined; |
| 32 | if (v_sign < 0) neg(v); | 33 | const us = ua[0..u.len]; |
| 33 | try @call(.always_inline, udivmod, .{ q, r, u, v }); | 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 }); | ||
| 34 | if (q) |x| if (u_sign ^ v_sign < 0) neg(x); | 41 | if (q) |x| if (u_sign ^ v_sign < 0) neg(x); |
| 35 | if (r) |x| if (u_sign < 0) neg(x); | 42 | if (r) |x| if (u_sign < 0) neg(x); |
| 36 | } | 43 | } |
| 37 | 44 | ||
| 38 | pub fn __divei4(q_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) void { | 45 | pub fn __divei4(q_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, bits: usize) callconv(.c) void { |
| 39 | @setRuntimeSafety(compiler_rt.test_safety); | 46 | @setRuntimeSafety(compiler_rt.test_safety); |
| 40 | const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits)); | 47 | const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits)); |
| 41 | const q: []u32 = @ptrCast(@alignCast(q_p[0..byte_size])); | 48 | const q: []u32 = @ptrCast(@alignCast(q_p[0..byte_size])); |
| 42 | const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size])); | 49 | const u: []const u32 = @ptrCast(@alignCast(u_p[0..byte_size])); |
| 43 | const v: []u32 = @ptrCast(@alignCast(v_p[0..byte_size])); | 50 | const v: []const u32 = @ptrCast(@alignCast(v_p[0..byte_size])); |
| 44 | @call(.always_inline, divmod, .{ q, null, u, v }) catch unreachable; | 51 | @call(.always_inline, divmod, .{ q, null, u, v }) catch unreachable; |
| 45 | } | 52 | } |
| 46 | 53 |
lib/compiler_rt/limb64.zig+39-13| ... | @@ -25,10 +25,26 @@ inline fn limbSet(limbs: []u64, i: usize, value: u64) void { | ... | @@ -25,10 +25,26 @@ inline fn limbSet(limbs: []u64, i: usize, value: u64) void { |
| 25 | } | 25 | } |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | fn limbCount(bits: u16) u16 { | 28 | fn usedLimbCount(bits: u16) u16 { |
| 29 | return divCeil(u16, bits, 64) catch unreachable; | 29 | return divCeil(u16, bits, 64) catch unreachable; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | fn limbCount(bits: u16) u16 { | ||
| 33 | return @divExact(std.zig.target.intByteSize(&builtin.target, bits), 8); | ||
| 34 | } | ||
| 35 | |||
| 36 | fn 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 | |||
| 32 | fn Limbs(T: type) type { | 48 | fn Limbs(T: type) type { |
| 33 | const int_info = @typeInfo(T).int; | 49 | const int_info = @typeInfo(T).int; |
| 34 | const limb_cnt = comptime limbCount(int_info.bits); | 50 | const limb_cnt = comptime limbCount(int_info.bits); |
| ... | @@ -60,7 +76,7 @@ comptime { | ... | @@ -60,7 +76,7 @@ comptime { |
| 60 | } | 76 | } |
| 61 | 77 | ||
| 62 | fn __addo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool { | 78 | fn __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); |
| 64 | const out = out_ptr[0..limb_cnt]; | 80 | const out = out_ptr[0..limb_cnt]; |
| 65 | const a = a_ptr[0..limb_cnt]; | 81 | const a = a_ptr[0..limb_cnt]; |
| 66 | const b = b_ptr[0..limb_cnt]; | 82 | 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 | ... | @@ -92,11 +108,13 @@ fn __addo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_s |
| 92 | 108 | ||
| 93 | if (bits % 64 == 0) { | 109 | if (bits % 64 == 0) { |
| 94 | limbSet(out, i, limb); | 110 | limbSet(out, i, limb); |
| 111 | fixLastLimb(out_ptr, is_signed, bits); | ||
| 95 | return carry != 0; | 112 | return carry != 0; |
| 96 | } else { | 113 | } else { |
| 97 | assert(carry == 0); | 114 | assert(carry == 0); |
| 98 | const wrapped_limb = limbWrap(limb, is_signed, bits); | 115 | const wrapped_limb = limbWrap(limb, is_signed, bits); |
| 99 | limbSet(out, i, wrapped_limb); | 116 | limbSet(out, i, wrapped_limb); |
| 117 | fixLastLimb(out_ptr, is_signed, bits); | ||
| 100 | return wrapped_limb != limb; | 118 | return wrapped_limb != limb; |
| 101 | } | 119 | } |
| 102 | } | 120 | } |
| ... | @@ -132,7 +150,7 @@ comptime { | ... | @@ -132,7 +150,7 @@ comptime { |
| 132 | } | 150 | } |
| 133 | 151 | ||
| 134 | fn __subo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool { | 152 | fn __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); |
| 136 | const out = out_ptr[0..limb_cnt]; | 154 | const out = out_ptr[0..limb_cnt]; |
| 137 | const a = a_ptr[0..limb_cnt]; | 155 | const a = a_ptr[0..limb_cnt]; |
| 138 | const b = b_ptr[0..limb_cnt]; | 156 | 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 | ... | @@ -164,10 +182,12 @@ fn __subo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_s |
| 164 | 182 | ||
| 165 | if (bits % 64 == 0) { | 183 | if (bits % 64 == 0) { |
| 166 | limbSet(out, i, limb); | 184 | limbSet(out, i, limb); |
| 185 | fixLastLimb(out_ptr, is_signed, bits); | ||
| 167 | return borrow != 0; | 186 | return borrow != 0; |
| 168 | } else { | 187 | } else { |
| 169 | const wrapped_limb = limbWrap(limb, is_signed, bits); | 188 | const wrapped_limb = limbWrap(limb, is_signed, bits); |
| 170 | limbSet(out, i, wrapped_limb); | 189 | limbSet(out, i, wrapped_limb); |
| 190 | fixLastLimb(out_ptr, is_signed, bits); | ||
| 171 | return borrow != 0 or wrapped_limb != limb; | 191 | return borrow != 0 or wrapped_limb != limb; |
| 172 | } | 192 | } |
| 173 | } | 193 | } |
| ... | @@ -206,7 +226,7 @@ comptime { | ... | @@ -206,7 +226,7 @@ comptime { |
| 206 | // a == b -> 0 | 226 | // a == b -> 0 |
| 207 | // a > b -> 1 | 227 | // a > b -> 1 |
| 208 | fn __cmp_limb64(a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) i8 { | 228 | fn __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); |
| 210 | const a = a_ptr[0..limb_cnt]; | 230 | const a = a_ptr[0..limb_cnt]; |
| 211 | const b = b_ptr[0..limb_cnt]; | 231 | const b = b_ptr[0..limb_cnt]; |
| 212 | 232 | ||
| ... | @@ -391,7 +411,7 @@ comptime { | ... | @@ -391,7 +411,7 @@ comptime { |
| 391 | } | 411 | } |
| 392 | 412 | ||
| 393 | fn __not_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void { | 413 | fn __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); |
| 395 | const out = out_ptr[0..limb_cnt]; | 415 | const out = out_ptr[0..limb_cnt]; |
| 396 | const a = a_ptr[0..limb_cnt]; | 416 | const a = a_ptr[0..limb_cnt]; |
| 397 | 417 | ||
| ... | @@ -405,6 +425,7 @@ fn __not_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16 | ... | @@ -405,6 +425,7 @@ fn __not_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16 |
| 405 | limb = limbWrap(limb, is_signed, bits); | 425 | limb = limbWrap(limb, is_signed, bits); |
| 406 | } | 426 | } |
| 407 | limbSet(out, i, limb); | 427 | limbSet(out, i, limb); |
| 428 | fixLastLimb(out_ptr, is_signed, bits); | ||
| 408 | } | 429 | } |
| 409 | 430 | ||
| 410 | fn test__not_limb64(comptime T: type, a: T, expected: T) !void { | 431 | fn test__not_limb64(comptime T: type, a: T, expected: T) !void { |
| ... | @@ -436,7 +457,7 @@ comptime { | ... | @@ -436,7 +457,7 @@ comptime { |
| 436 | } | 457 | } |
| 437 | 458 | ||
| 438 | fn __shlo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) bool { | 459 | fn __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); |
| 440 | const out = out_ptr[0..limb_cnt]; | 461 | const out = out_ptr[0..limb_cnt]; |
| 441 | const a = a_ptr[0..limb_cnt]; | 462 | const a = a_ptr[0..limb_cnt]; |
| 442 | 463 | ||
| ... | @@ -477,6 +498,7 @@ fn __shlo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bo | ... | @@ -477,6 +498,7 @@ fn __shlo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bo |
| 477 | overflow = overflow or limbGet(a, j) != sign_extend; | 498 | overflow = overflow or limbGet(a, j) != sign_extend; |
| 478 | } | 499 | } |
| 479 | 500 | ||
| 501 | fixLastLimb(out_ptr, is_signed, bits); | ||
| 480 | return overflow; | 502 | return overflow; |
| 481 | } | 503 | } |
| 482 | 504 | ||
| ... | @@ -526,7 +548,7 @@ comptime { | ... | @@ -526,7 +548,7 @@ comptime { |
| 526 | } | 548 | } |
| 527 | 549 | ||
| 528 | fn __shr_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) void { | 550 | fn __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); |
| 530 | const out = out_ptr[0..limb_cnt]; | 552 | const out = out_ptr[0..limb_cnt]; |
| 531 | const a = a_ptr[0..limb_cnt]; | 553 | const a = a_ptr[0..limb_cnt]; |
| 532 | 554 | ||
| ... | @@ -594,7 +616,7 @@ comptime { | ... | @@ -594,7 +616,7 @@ comptime { |
| 594 | } | 616 | } |
| 595 | 617 | ||
| 596 | fn __clz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { | 618 | fn __clz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { |
| 597 | const limb_cnt = limbCount(bits); | 619 | const limb_cnt = usedLimbCount(bits); |
| 598 | const a = a_ptr[0..limb_cnt]; | 620 | const a = a_ptr[0..limb_cnt]; |
| 599 | 621 | ||
| 600 | var res: u16 = 0; | 622 | var res: u16 = 0; |
| ... | @@ -652,7 +674,7 @@ comptime { | ... | @@ -652,7 +674,7 @@ comptime { |
| 652 | } | 674 | } |
| 653 | 675 | ||
| 654 | fn __ctz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { | 676 | fn __ctz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { |
| 655 | const limb_cnt = limbCount(bits); | 677 | const limb_cnt = usedLimbCount(bits); |
| 656 | const a = a_ptr[0..limb_cnt]; | 678 | const a = a_ptr[0..limb_cnt]; |
| 657 | 679 | ||
| 658 | var res: u16 = 0; | 680 | var res: u16 = 0; |
| ... | @@ -705,7 +727,7 @@ comptime { | ... | @@ -705,7 +727,7 @@ comptime { |
| 705 | } | 727 | } |
| 706 | 728 | ||
| 707 | fn __popcount_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { | 729 | fn __popcount_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { |
| 708 | const limb_cnt = limbCount(bits); | 730 | const limb_cnt = usedLimbCount(bits); |
| 709 | const a = a_ptr[0..limb_cnt]; | 731 | const a = a_ptr[0..limb_cnt]; |
| 710 | 732 | ||
| 711 | var res: u16 = 0; | 733 | var res: u16 = 0; |
| ... | @@ -751,7 +773,7 @@ comptime { | ... | @@ -751,7 +773,7 @@ comptime { |
| 751 | } | 773 | } |
| 752 | 774 | ||
| 753 | fn __bitreverse_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void { | 775 | fn __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); |
| 755 | const out = out_ptr[0..limb_cnt]; | 777 | const out = out_ptr[0..limb_cnt]; |
| 756 | const a = a_ptr[0..limb_cnt]; | 778 | const a = a_ptr[0..limb_cnt]; |
| 757 | 779 | ||
| ... | @@ -764,6 +786,7 @@ fn __bitreverse_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bi | ... | @@ -764,6 +786,7 @@ fn __bitreverse_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bi |
| 764 | if (bits % 64 != 0) { | 786 | if (bits % 64 != 0) { |
| 765 | __shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits); | 787 | __shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits); |
| 766 | } | 788 | } |
| 789 | fixLastLimb(out_ptr, is_signed, bits); | ||
| 767 | } | 790 | } |
| 768 | 791 | ||
| 769 | fn test__bitreverse_limb64(comptime T: type, a: T, expected: T) !void { | 792 | fn test__bitreverse_limb64(comptime T: type, a: T, expected: T) !void { |
| ... | @@ -797,7 +820,7 @@ comptime { | ... | @@ -797,7 +820,7 @@ comptime { |
| 797 | } | 820 | } |
| 798 | 821 | ||
| 799 | fn __byteswap_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void { | 822 | fn __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); |
| 801 | const out = out_ptr[0..limb_cnt]; | 824 | const out = out_ptr[0..limb_cnt]; |
| 802 | const a = a_ptr[0..limb_cnt]; | 825 | const a = a_ptr[0..limb_cnt]; |
| 803 | 826 | ||
| ... | @@ -812,6 +835,7 @@ fn __byteswap_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits | ... | @@ -812,6 +835,7 @@ fn __byteswap_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits |
| 812 | if (bits % 64 != 0) { | 835 | if (bits % 64 != 0) { |
| 813 | __shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits); | 836 | __shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits); |
| 814 | } | 837 | } |
| 838 | fixLastLimb(out_ptr, is_signed, bits); | ||
| 815 | } | 839 | } |
| 816 | 840 | ||
| 817 | fn test__byteswap_limb64(comptime T: type, a: T, expected: T) !void { | 841 | fn test__byteswap_limb64(comptime T: type, a: T, expected: T) !void { |
| ... | @@ -861,7 +885,7 @@ fn mulwide(a: u64, b: u64) [2]u64 { | ... | @@ -861,7 +885,7 @@ fn mulwide(a: u64, b: u64) [2]u64 { |
| 861 | } | 885 | } |
| 862 | 886 | ||
| 863 | fn __mulo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool { | 887 | fn __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); |
| 865 | 889 | ||
| 866 | const out = out_ptr[0..limb_cnt]; | 890 | const out = out_ptr[0..limb_cnt]; |
| 867 | const a = a_ptr[0..limb_cnt]; | 891 | 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 | ... | @@ -921,6 +945,8 @@ fn __mulo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_s |
| 921 | limbSet(out, limb_cnt - 1, last); | 945 | limbSet(out, limb_cnt - 1, last); |
| 922 | } | 946 | } |
| 923 | 947 | ||
| 948 | fixLastLimb(out_ptr, is_signed, bits); | ||
| 949 | |||
| 924 | if (!is_signed) { | 950 | if (!is_signed) { |
| 925 | return !hi_zero or raw_last != last; | 951 | return !hi_zero or raw_last != last; |
| 926 | } | 952 | } |
src/codegen/wasm/CodeGen.zig+119-39| ... | @@ -2357,6 +2357,15 @@ const IntType = struct { | ... | @@ -2357,6 +2357,15 @@ const IntType = struct { |
| 2357 | } | 2357 | } |
| 2358 | }; | 2358 | }; |
| 2359 | 2359 | ||
| 2360 | fn 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 | |||
| 2360 | fn intAdd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue { | 2369 | fn intAdd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue { |
| 2361 | switch (ty.bits) { | 2370 | switch (ty.bits) { |
| 2362 | 0 => unreachable, | 2371 | 0 => unreachable, |
| ... | @@ -2518,7 +2527,15 @@ fn intDiv(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue | ... | @@ -2518,7 +2527,15 @@ fn intDiv(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue |
| 2518 | return cg.callIntrinsic(.__udivti3, &.{ .i128_type, .i128_type }, Type.i128, &.{ lhs, rhs }); | 2527 | return cg.callIntrinsic(.__udivti3, &.{ .i128_type, .i128_type }, Type.i128, &.{ lhs, rhs }); |
| 2519 | } | 2528 | } |
| 2520 | }, | 2529 | }, |
| 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 | }, | ||
| 2522 | } | 2539 | } |
| 2523 | } | 2540 | } |
| 2524 | 2541 | ||
| ... | @@ -2570,7 +2587,22 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W | ... | @@ -2570,7 +2587,22 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W |
| 2570 | try cg.addTag(.i64_sub); | 2587 | try cg.addTag(.i64_sub); |
| 2571 | return .stack; | 2588 | return .stack; |
| 2572 | }, | 2589 | }, |
| 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 | }, | ||
| 2574 | } | 2606 | } |
| 2575 | } | 2607 | } |
| 2576 | 2608 | ||
| ... | @@ -2596,7 +2628,15 @@ fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue | ... | @@ -2596,7 +2628,15 @@ fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue |
| 2596 | return cg.callIntrinsic(.__umodti3, &.{ .i128_type, .i128_type }, Type.i128, &.{ lhs, rhs }); | 2628 | return cg.callIntrinsic(.__umodti3, &.{ .i128_type, .i128_type }, Type.i128, &.{ lhs, rhs }); |
| 2597 | } | 2629 | } |
| 2598 | }, | 2630 | }, |
| 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 | }, | ||
| 2600 | } | 2640 | } |
| 2601 | } | 2641 | } |
| 2602 | 2642 | ||
| ... | @@ -3315,26 +3355,43 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { | ... | @@ -3315,26 +3355,43 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3315 | }, | 3355 | }, |
| 3316 | 128 => return operand, | 3356 | 128 => return operand, |
| 3317 | else => { | 3357 | else => { |
| 3318 | const bits = mem.alignForward(u16, ty.bits, 64); | 3358 | const bits = cg.intBackingBits(ty.bits); |
| 3319 | if (ty.bits == bits) return operand; | 3359 | if (ty.bits == bits) return operand; |
| 3320 | 3360 | ||
| 3321 | const result = try cg.allocInt(ty); | 3361 | const result = try cg.allocInt(ty); |
| 3322 | 3362 | ||
| 3323 | const len = bits / 8; | 3363 | const copy_len = (ty.bits / 64) * 8; |
| 3324 | try cg.memcpy(result, operand, .{ .imm32 = len - 8 }); | 3364 | try cg.memcpy(result, operand, .{ .imm32 = copy_len }); |
| 3325 | 3365 | ||
| 3326 | try cg.emitWValue(result); | 3366 | if (ty.bits % 64 != 0) { |
| 3327 | _ = try cg.load(operand, Type.u64, len - 8); | 3367 | const pad = 64 - ty.bits % 64; |
| 3328 | if (ty.is_signed) { | 3368 | |
| 3329 | try cg.addImm64(bits - ty.bits); | 3369 | try cg.emitWValue(result); |
| 3330 | try cg.addTag(.i64_shl); | 3370 | _ = try cg.load(operand, Type.u64, copy_len); |
| 3331 | try cg.addImm64(bits - ty.bits); | 3371 | if (ty.is_signed) { |
| 3332 | try cg.addTag(.i64_shr_s); | 3372 | try cg.addImm64(pad); |
| 3333 | } else { | 3373 | try cg.addTag(.i64_shl); |
| 3334 | try cg.addImm64(~@as(u64, 0) >> @intCast(bits - ty.bits)); | 3374 | try cg.addImm64(pad); |
| 3335 | try cg.addTag(.i64_and); | 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); | ||
| 3336 | } | 3394 | } |
| 3337 | try cg.store(.stack, .stack, Type.u64, result.offset() + len - 8); | ||
| 3338 | 3395 | ||
| 3339 | return result; | 3396 | return result; |
| 3340 | }, | 3397 | }, |
| ... | @@ -3354,8 +3411,8 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { | ... | @@ -3354,8 +3411,8 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3354 | } else { | 3411 | } else { |
| 3355 | return .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_ty.bits) }; | 3412 | return .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_ty.bits) }; |
| 3356 | } | 3413 | } |
| 3357 | } else { | 3414 | } else if (int_ty.bits <= 128) { |
| 3358 | const result = try cg.allocStack(Type.u128); | 3415 | const result = try cg.allocInt(int_ty); |
| 3359 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, Type.u64, 0); | 3416 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, Type.u64, 0); |
| 3360 | 3417 | ||
| 3361 | if (int_ty.is_signed) { | 3418 | if (int_ty.is_signed) { |
| ... | @@ -3363,6 +3420,24 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { | ... | @@ -3363,6 +3420,24 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3363 | } else { | 3420 | } else { |
| 3364 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast(128 - int_ty.bits) }, Type.u64, 8); | 3421 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast(128 - int_ty.bits) }, Type.u64, 8); |
| 3365 | } | 3422 | } |
| 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 | |||
| 3366 | return result; | 3441 | return result; |
| 3367 | } | 3442 | } |
| 3368 | } | 3443 | } |
| ... | @@ -3375,10 +3450,23 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { | ... | @@ -3375,10 +3450,23 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3375 | return .{ .imm32 = ~@as(u32, 0) << @intCast(int_ty.bits - 1) }; | 3450 | return .{ .imm32 = ~@as(u32, 0) << @intCast(int_ty.bits - 1) }; |
| 3376 | } else if (int_ty.bits <= 64) { | 3451 | } else if (int_ty.bits <= 64) { |
| 3377 | return .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 1) }; | 3452 | return .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 1) }; |
| 3378 | } else { | 3453 | } else if (int_ty.bits <= 128) { |
| 3379 | const result = try cg.allocStack(Type.u128); | 3454 | const result = try cg.allocInt(int_ty); |
| 3380 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0); | 3455 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0); |
| 3381 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 65) }, Type.u64, 8); | 3456 | 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 | |||
| 3382 | return result; | 3470 | return result; |
| 3383 | } | 3471 | } |
| 3384 | } | 3472 | } |
| ... | @@ -3572,12 +3660,17 @@ fn intZeroValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { | ... | @@ -3572,12 +3660,17 @@ fn intZeroValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3572 | 1...32 => return .{ .imm32 = 0 }, | 3660 | 1...32 => return .{ .imm32 = 0 }, |
| 3573 | 33...64 => return .{ .imm64 = 0 }, | 3661 | 33...64 => return .{ .imm64 = 0 }, |
| 3574 | 65...128 => { | 3662 | 65...128 => { |
| 3575 | const result = try cg.allocStack(Type.u128); | 3663 | const result = try cg.allocInt(int_ty); |
| 3576 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0); | 3664 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0); |
| 3577 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8); | 3665 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8); |
| 3578 | return result; | 3666 | return result; |
| 3579 | }, | 3667 | }, |
| 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 | }, | ||
| 3581 | } | 3674 | } |
| 3582 | } | 3675 | } |
| 3583 | 3676 | ||
| ... | @@ -3757,17 +3850,8 @@ fn intShlOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro | ... | @@ -3757,17 +3850,8 @@ fn intShlOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro |
| 3757 | } | 3850 | } |
| 3758 | 3851 | ||
| 3759 | fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue { | 3852 | fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue { |
| 3760 | const src_bits: u16 = switch (src_ty.bits) { | 3853 | const src_bits: u16 = cg.intBackingBits(src_ty.bits); |
| 3761 | 0 => unreachable, | 3854 | const dest_bits: u16 = cg.intBackingBits(dest_ty.bits); |
| 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 | }; | ||
| 3771 | 3855 | ||
| 3772 | if (src_bits == dest_bits) { | 3856 | if (src_bits == dest_bits) { |
| 3773 | return operand; | 3857 | return operand; |
| ... | @@ -3859,11 +3943,7 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn | ... | @@ -3859,11 +3943,7 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn |
| 3859 | fn intTrunc(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue { | 3943 | fn intTrunc(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue { |
| 3860 | var result = try cg.intCast(dest_ty, src_ty, operand); | 3944 | var result = try cg.intCast(dest_ty, src_ty, operand); |
| 3861 | 3945 | ||
| 3862 | const dest_wasm_bits: u16 = switch (dest_ty.bits) { | 3946 | const dest_wasm_bits = cg.intBackingBits(dest_ty.bits); |
| 3863 | 0 => unreachable, | ||
| 3864 | 1...32 => 32, | ||
| 3865 | else => mem.alignForward(u16, dest_ty.bits, 64), | ||
| 3866 | }; | ||
| 3867 | 3947 | ||
| 3868 | if (dest_wasm_bits != dest_ty.bits) { | 3948 | if (dest_wasm_bits != dest_ty.bits) { |
| 3869 | result = try cg.intWrap(dest_ty, result); | 3949 | result = try cg.intWrap(dest_ty, result); |
src/codegen/wasm/Mir.zig+4| ... | @@ -825,6 +825,7 @@ pub const Intrinsic = enum(u32) { | ... | @@ -825,6 +825,7 @@ pub const Intrinsic = enum(u32) { |
| 825 | __ceilx, | 825 | __ceilx, |
| 826 | __cosh, | 826 | __cosh, |
| 827 | __cosx, | 827 | __cosx, |
| 828 | __divei4, | ||
| 828 | __divhf3, | 829 | __divhf3, |
| 829 | __divtf3, | 830 | __divtf3, |
| 830 | __divti3, | 831 | __divti3, |
| ... | @@ -950,6 +951,7 @@ pub const Intrinsic = enum(u32) { | ... | @@ -950,6 +951,7 @@ pub const Intrinsic = enum(u32) { |
| 950 | __lshrti3, | 951 | __lshrti3, |
| 951 | __lttf2, | 952 | __lttf2, |
| 952 | __ltxf2, | 953 | __ltxf2, |
| 954 | __modei4, | ||
| 953 | __modti3, | 955 | __modti3, |
| 954 | __mulhf3, | 956 | __mulhf3, |
| 955 | __mulodi4, | 957 | __mulodi4, |
| ... | @@ -980,7 +982,9 @@ pub const Intrinsic = enum(u32) { | ... | @@ -980,7 +982,9 @@ pub const Intrinsic = enum(u32) { |
| 980 | __truncxfdf2, | 982 | __truncxfdf2, |
| 981 | __truncxfhf2, | 983 | __truncxfhf2, |
| 982 | __truncxfsf2, | 984 | __truncxfsf2, |
| 985 | __udivei4, | ||
| 983 | __udivti3, | 986 | __udivti3, |
| 987 | __umodei4, | ||
| 984 | __umodti3, | 988 | __umodti3, |
| 985 | ceilq, | 989 | ceilq, |
| 986 | cos, | 990 | cos, |
test/behavior/math.zig+106| ... | @@ -1736,6 +1736,112 @@ test "@abs > 128 bits" { | ... | @@ -1736,6 +1736,112 @@ test "@abs > 128 bits" { |
| 1736 | try testAbs(i200, minInt(i200), 1 << 199); | 1736 | try testAbs(i200, minInt(i200), 1 << 199); |
| 1737 | } | 1737 | } |
| 1738 | 1738 | ||
| 1739 | fn testRem(comptime T: type, numerator: T, denominator: T, expected: T) !void { | ||
| 1740 | try expect(@rem(numerator, denominator) == expected); | ||
| 1741 | } | ||
| 1742 | |||
| 1743 | test "@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 | |||
| 1765 | fn testMod(comptime T: type, numerator: T, denominator: T, expected: T) !void { | ||
| 1766 | try expect(@mod(numerator, denominator) == expected); | ||
| 1767 | } | ||
| 1768 | |||
| 1769 | test "@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 | |||
| 1791 | fn testDivFloor(comptime T: type, numerator: T, denominator: T, expected: T) !void { | ||
| 1792 | try expect(@divFloor(numerator, denominator) == expected); | ||
| 1793 | } | ||
| 1794 | |||
| 1795 | test "@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 | |||
| 1818 | fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void { | ||
| 1819 | try expect(@divTrunc(numerator, denominator) == expected); | ||
| 1820 | } | ||
| 1821 | |||
| 1822 | test "@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 | |||
| 1739 | test "overflow arithmetic with u0 values" { | 1845 | test "overflow arithmetic with u0 values" { |
| 1740 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | 1846 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 1741 | 1847 |