| 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 | 24 | } |
| 25 | 25 | } |
| 26 | 26 | |
| 27 | /// Mutates the arguments! | |
| 28 | fn divmod(q: ?[]u32, r: ?[]u32, u: []u32, v: []u32) !void { | |
| 27 | const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable; | |
| 28 | ||
| 29 | fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void { | |
| 29 | 30 | const u_sign: i32 = @bitCast(u[u.len - 1]); |
| 30 | 31 | 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 }); | |
| 34 | 41 | if (q) |x| if (u_sign ^ v_sign < 0) neg(x); |
| 35 | 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 | 46 | @setRuntimeSafety(compiler_rt.test_safety); |
| 40 | 47 | const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits)); |
| 41 | 48 | 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])); | |
| 44 | 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 | 25 | } |
| 26 | 26 | } |
| 27 | 27 | |
| 28 | fn limbCount(bits: u16) u16 { | |
| 28 | fn usedLimbCount(bits: u16) u16 { | |
| 29 | 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 | 48 | fn Limbs(T: type) type { |
| 33 | 49 | const int_info = @typeInfo(T).int; |
| 34 | 50 | const limb_cnt = comptime limbCount(int_info.bits); |
| ... | ... | @@ -60,7 +76,7 @@ comptime { |
| 60 | 76 | } |
| 61 | 77 | |
| 62 | 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 | 80 | const out = out_ptr[0..limb_cnt]; |
| 65 | 81 | const a = a_ptr[0..limb_cnt]; |
| 66 | 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 | 108 | |
| 93 | 109 | if (bits % 64 == 0) { |
| 94 | 110 | limbSet(out, i, limb); |
| 111 | fixLastLimb(out_ptr, is_signed, bits); | |
| 95 | 112 | return carry != 0; |
| 96 | 113 | } else { |
| 97 | 114 | assert(carry == 0); |
| 98 | 115 | const wrapped_limb = limbWrap(limb, is_signed, bits); |
| 99 | 116 | limbSet(out, i, wrapped_limb); |
| 117 | fixLastLimb(out_ptr, is_signed, bits); | |
| 100 | 118 | return wrapped_limb != limb; |
| 101 | 119 | } |
| 102 | 120 | } |
| ... | ... | @@ -132,7 +150,7 @@ comptime { |
| 132 | 150 | } |
| 133 | 151 | |
| 134 | 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 | 154 | const out = out_ptr[0..limb_cnt]; |
| 137 | 155 | const a = a_ptr[0..limb_cnt]; |
| 138 | 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 | 182 | |
| 165 | 183 | if (bits % 64 == 0) { |
| 166 | 184 | limbSet(out, i, limb); |
| 185 | fixLastLimb(out_ptr, is_signed, bits); | |
| 167 | 186 | return borrow != 0; |
| 168 | 187 | } else { |
| 169 | 188 | const wrapped_limb = limbWrap(limb, is_signed, bits); |
| 170 | 189 | limbSet(out, i, wrapped_limb); |
| 190 | fixLastLimb(out_ptr, is_signed, bits); | |
| 171 | 191 | return borrow != 0 or wrapped_limb != limb; |
| 172 | 192 | } |
| 173 | 193 | } |
| ... | ... | @@ -206,7 +226,7 @@ comptime { |
| 206 | 226 | // a == b -> 0 |
| 207 | 227 | // a > b -> 1 |
| 208 | 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 | 230 | const a = a_ptr[0..limb_cnt]; |
| 211 | 231 | const b = b_ptr[0..limb_cnt]; |
| 212 | 232 | |
| ... | ... | @@ -391,7 +411,7 @@ comptime { |
| 391 | 411 | } |
| 392 | 412 | |
| 393 | 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 | 415 | const out = out_ptr[0..limb_cnt]; |
| 396 | 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 | 425 | limb = limbWrap(limb, is_signed, bits); |
| 406 | 426 | } |
| 407 | 427 | limbSet(out, i, limb); |
| 428 | fixLastLimb(out_ptr, is_signed, bits); | |
| 408 | 429 | } |
| 409 | 430 | |
| 410 | 431 | fn test__not_limb64(comptime T: type, a: T, expected: T) !void { |
| ... | ... | @@ -436,7 +457,7 @@ comptime { |
| 436 | 457 | } |
| 437 | 458 | |
| 438 | 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 | 461 | const out = out_ptr[0..limb_cnt]; |
| 441 | 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 | 498 | overflow = overflow or limbGet(a, j) != sign_extend; |
| 478 | 499 | } |
| 479 | 500 | |
| 501 | fixLastLimb(out_ptr, is_signed, bits); | |
| 480 | 502 | return overflow; |
| 481 | 503 | } |
| 482 | 504 | |
| ... | ... | @@ -526,7 +548,7 @@ comptime { |
| 526 | 548 | } |
| 527 | 549 | |
| 528 | 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 | 552 | const out = out_ptr[0..limb_cnt]; |
| 531 | 553 | const a = a_ptr[0..limb_cnt]; |
| 532 | 554 | |
| ... | ... | @@ -594,7 +616,7 @@ comptime { |
| 594 | 616 | } |
| 595 | 617 | |
| 596 | 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 | 620 | const a = a_ptr[0..limb_cnt]; |
| 599 | 621 | |
| 600 | 622 | var res: u16 = 0; |
| ... | ... | @@ -652,7 +674,7 @@ comptime { |
| 652 | 674 | } |
| 653 | 675 | |
| 654 | 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 | 678 | const a = a_ptr[0..limb_cnt]; |
| 657 | 679 | |
| 658 | 680 | var res: u16 = 0; |
| ... | ... | @@ -705,7 +727,7 @@ comptime { |
| 705 | 727 | } |
| 706 | 728 | |
| 707 | 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 | 731 | const a = a_ptr[0..limb_cnt]; |
| 710 | 732 | |
| 711 | 733 | var res: u16 = 0; |
| ... | ... | @@ -751,7 +773,7 @@ comptime { |
| 751 | 773 | } |
| 752 | 774 | |
| 753 | 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 | 777 | const out = out_ptr[0..limb_cnt]; |
| 756 | 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 | 786 | if (bits % 64 != 0) { |
| 765 | 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 | 792 | fn test__bitreverse_limb64(comptime T: type, a: T, expected: T) !void { |
| ... | ... | @@ -797,7 +820,7 @@ comptime { |
| 797 | 820 | } |
| 798 | 821 | |
| 799 | 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 | 824 | const out = out_ptr[0..limb_cnt]; |
| 802 | 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 | 835 | if (bits % 64 != 0) { |
| 813 | 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 | 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 | 885 | } |
| 862 | 886 | |
| 863 | 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 | 890 | const out = out_ptr[0..limb_cnt]; |
| 867 | 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 | 945 | limbSet(out, limb_cnt - 1, last); |
| 922 | 946 | } |
| 923 | 947 | |
| 948 | fixLastLimb(out_ptr, is_signed, bits); | |
| 949 | ||
| 924 | 950 | if (!is_signed) { |
| 925 | 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 | 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 | 2369 | fn intAdd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue { |
| 2361 | 2370 | switch (ty.bits) { |
| 2362 | 2371 | 0 => unreachable, |
| ... | ... | @@ -2518,7 +2527,15 @@ fn intDiv(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue |
| 2518 | 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 | 2587 | try cg.addTag(.i64_sub); |
| 2571 | 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 | 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 | 3355 | }, |
| 3316 | 3356 | 128 => return operand, |
| 3317 | 3357 | else => { |
| 3318 | const bits = mem.alignForward(u16, ty.bits, 64); | |
| 3358 | const bits = cg.intBackingBits(ty.bits); | |
| 3319 | 3359 | if (ty.bits == bits) return operand; |
| 3320 | 3360 | |
| 3321 | 3361 | const result = try cg.allocInt(ty); |
| 3322 | 3362 | |
| 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 }); | |
| 3325 | 3365 | |
| 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); | |
| 3336 | 3394 | } |
| 3337 | try cg.store(.stack, .stack, Type.u64, result.offset() + len - 8); | |
| 3338 | 3395 | |
| 3339 | 3396 | return result; |
| 3340 | 3397 | }, |
| ... | ... | @@ -3354,8 +3411,8 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3354 | 3411 | } else { |
| 3355 | 3412 | return .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_ty.bits) }; |
| 3356 | 3413 | } |
| 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); | |
| 3359 | 3416 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, Type.u64, 0); |
| 3360 | 3417 | |
| 3361 | 3418 | if (int_ty.is_signed) { |
| ... | ... | @@ -3363,6 +3420,24 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3363 | 3420 | } else { |
| 3364 | 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 | 3441 | return result; |
| 3367 | 3442 | } |
| 3368 | 3443 | } |
| ... | ... | @@ -3375,10 +3450,23 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3375 | 3450 | return .{ .imm32 = ~@as(u32, 0) << @intCast(int_ty.bits - 1) }; |
| 3376 | 3451 | } else if (int_ty.bits <= 64) { |
| 3377 | 3452 | 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); | |
| 3380 | 3455 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0); |
| 3381 | 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 | 3470 | return result; |
| 3383 | 3471 | } |
| 3384 | 3472 | } |
| ... | ... | @@ -3572,12 +3660,17 @@ fn intZeroValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3572 | 3660 | 1...32 => return .{ .imm32 = 0 }, |
| 3573 | 3661 | 33...64 => return .{ .imm64 = 0 }, |
| 3574 | 3662 | 65...128 => { |
| 3575 | const result = try cg.allocStack(Type.u128); | |
| 3663 | const result = try cg.allocInt(int_ty); | |
| 3576 | 3664 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0); |
| 3577 | 3665 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8); |
| 3578 | 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 | 3850 | } |
| 3758 | 3851 | |
| 3759 | 3852 | fn 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); | |
| 3771 | 3855 | |
| 3772 | 3856 | if (src_bits == dest_bits) { |
| 3773 | 3857 | return operand; |
| ... | ... | @@ -3859,11 +3943,7 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn |
| 3859 | 3943 | fn intTrunc(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue { |
| 3860 | 3944 | var result = try cg.intCast(dest_ty, src_ty, operand); |
| 3861 | 3945 | |
| 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); | |
| 3867 | 3947 | |
| 3868 | 3948 | if (dest_wasm_bits != dest_ty.bits) { |
| 3869 | 3949 | result = try cg.intWrap(dest_ty, result); |
src/codegen/wasm/Mir.zig+4| ... | ... | @@ -825,6 +825,7 @@ pub const Intrinsic = enum(u32) { |
| 825 | 825 | __ceilx, |
| 826 | 826 | __cosh, |
| 827 | 827 | __cosx, |
| 828 | __divei4, | |
| 828 | 829 | __divhf3, |
| 829 | 830 | __divtf3, |
| 830 | 831 | __divti3, |
| ... | ... | @@ -950,6 +951,7 @@ pub const Intrinsic = enum(u32) { |
| 950 | 951 | __lshrti3, |
| 951 | 952 | __lttf2, |
| 952 | 953 | __ltxf2, |
| 954 | __modei4, | |
| 953 | 955 | __modti3, |
| 954 | 956 | __mulhf3, |
| 955 | 957 | __mulodi4, |
| ... | ... | @@ -980,7 +982,9 @@ pub const Intrinsic = enum(u32) { |
| 980 | 982 | __truncxfdf2, |
| 981 | 983 | __truncxfhf2, |
| 982 | 984 | __truncxfsf2, |
| 985 | __udivei4, | |
| 983 | 986 | __udivti3, |
| 987 | __umodei4, | |
| 984 | 988 | __umodti3, |
| 985 | 989 | ceilq, |
| 986 | 990 | cos, |
test/behavior/math.zig+106| ... | ... | @@ -1736,6 +1736,112 @@ test "@abs > 128 bits" { |
| 1736 | 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 | 1845 | test "overflow arithmetic with u0 values" { |
| 1740 | 1846 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 1741 | 1847 |