| author | |
| committer | |
| log | e00b5daa1fda95a58cce42016d158e283e23d130 |
| tree | c2f38d273769a935cf07a2a662ee78bdc9dede0c |
| parent | c32f7a451356817091f874a97c78dd9abb9468fe |
| parent | 773def30c24f739647112e8221fdf398b5ef12e7 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31784
Reviewed-by: Andrew Kelley <andrew@ziglang.org>19 files changed, 2304 insertions(+), 339 deletions(-)
lib/compiler_rt/divmodei4.zig+40-17| ... | ... | @@ -10,29 +10,30 @@ const symbol = @import("../compiler_rt.zig").symbol; |
| 10 | 10 | comptime { |
| 11 | 11 | symbol(&__divei4, "__divei4"); |
| 12 | 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 { | |
| 16 | return if (endian == .little) &x[i] else &x[x.len - 1 - i]; | |
| 17 | inline fn limb(i: usize, len: usize) usize { | |
| 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 | 22 | 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; | |
| 24 | 26 | } |
| 25 | 27 | } |
| 26 | 28 | |
| 27 | /// Mutates the arguments! | |
| 28 | fn divmod(q: ?[]u32, r: ?[]u32, u: []u32, v: []u32) !void { | |
| 29 | const u_sign: i32 = @bitCast(u[u.len - 1]); | |
| 30 | 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 }); | |
| 34 | if (q) |x| if (u_sign ^ v_sign < 0) neg(x); | |
| 35 | if (r) |x| if (u_sign < 0) neg(x); | |
| 29 | fn 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); | |
| 36 | 37 | } |
| 37 | 38 | |
| 38 | 39 | pub fn __divei4(q_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) void { |
| ... | ... | @@ -41,7 +42,7 @@ pub fn __divei4(q_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) vo |
| 41 | 42 | const q: []u32 = @ptrCast(@alignCast(q_p[0..byte_size])); |
| 42 | 43 | const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size])); |
| 43 | 44 | const v: []u32 = @ptrCast(@alignCast(v_p[0..byte_size])); |
| 44 | @call(.always_inline, divmod, .{ q, null, u, v }) catch unreachable; | |
| 45 | @call(.always_inline, divmod, .{ q, null, u, v, u, v }) catch unreachable; | |
| 45 | 46 | } |
| 46 | 47 | |
| 47 | 48 | pub fn __modei4(r_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) void { |
| ... | ... | @@ -50,5 +51,27 @@ pub fn __modei4(r_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) vo |
| 50 | 51 | const r: []u32 = @ptrCast(@alignCast(r_p[0..byte_size])); |
| 51 | 52 | const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size])); |
| 52 | 53 | const v: []u32 = @ptrCast(@alignCast(v_p[0..byte_size])); |
| 53 | @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; | |
| 54 | 77 | } |
lib/compiler_rt/limb64.zig+875-15| ... | ... | @@ -7,6 +7,7 @@ const divCeil = std.math.divCeil; |
| 7 | 7 | |
| 8 | 8 | const builtin = @import("builtin"); |
| 9 | 9 | const compiler_rt = @import("../compiler_rt.zig"); |
| 10 | const symbol = @import("../compiler_rt.zig").symbol; | |
| 10 | 11 | |
| 11 | 12 | const endian = builtin.cpu.arch.endian(); |
| 12 | 13 | |
| ... | ... | @@ -24,10 +25,44 @@ inline fn limbSet(limbs: []u64, i: usize, value: u64) void { |
| 24 | 25 | } |
| 25 | 26 | } |
| 26 | 27 | |
| 27 | fn limbCount(bits: u16) u16 { | |
| 28 | fn usedLimbCount(bits: u16) u16 { | |
| 28 | 29 | return divCeil(u16, bits, 64) catch unreachable; |
| 29 | 30 | } |
| 30 | 31 | |
| 32 | fn limbCount(bits: u16) u16 { | |
| 33 | return @divExact(std.zig.target.intByteSize(&builtin.target, bits), 8); | |
| 34 | } | |
| 35 | ||
| 36 | fn varLimbs(ptr: [*]u64, bits: u16) []u64 { | |
| 37 | const limb_cnt = usedLimbCount(bits); | |
| 38 | const true_limb_cnt = limbCount(bits); | |
| 39 | return switch (endian) { | |
| 40 | .little => ptr[0..limb_cnt], | |
| 41 | .big => ptr[true_limb_cnt - limb_cnt .. true_limb_cnt], | |
| 42 | }; | |
| 43 | } | |
| 44 | ||
| 45 | fn constLimbs(ptr: [*]const u64, bits: u16) []const u64 { | |
| 46 | const limb_cnt = usedLimbCount(bits); | |
| 47 | const true_limb_cnt = limbCount(bits); | |
| 48 | return switch (endian) { | |
| 49 | .little => ptr[0..limb_cnt], | |
| 50 | .big => ptr[true_limb_cnt - limb_cnt .. true_limb_cnt], | |
| 51 | }; | |
| 52 | } | |
| 53 | ||
| 54 | fn fixLastLimb(out_ptr: [*]u64, is_signed: bool, bits: u16) void { | |
| 55 | const limb_cnt = usedLimbCount(bits); | |
| 56 | const true_limb_cnt = limbCount(bits); | |
| 57 | if (limb_cnt == true_limb_cnt) return; | |
| 58 | const true_out = out_ptr[0..true_limb_cnt]; | |
| 59 | ||
| 60 | const sign: u64 = if (!is_signed or @as(i64, @bitCast(true_out[limb_cnt - 1])) >= 0) 0 else ~@as(u64, 0); | |
| 61 | for (limb_cnt..true_limb_cnt) |i| { | |
| 62 | true_out[i] = sign; | |
| 63 | } | |
| 64 | } | |
| 65 | ||
| 31 | 66 | fn Limbs(T: type) type { |
| 32 | 67 | const int_info = @typeInfo(T).int; |
| 33 | 68 | const limb_cnt = comptime limbCount(int_info.bits); |
| ... | ... | @@ -55,14 +90,14 @@ fn limbWrap(limb: u64, is_signed: bool, bits: u16) u64 { |
| 55 | 90 | } |
| 56 | 91 | |
| 57 | 92 | comptime { |
| 58 | @export(&__addo_limb64, .{ .name = "__addo_limb64", .linkage = compiler_rt.linkage, .visibility = compiler_rt.visibility }); | |
| 93 | symbol(&__addo_limb64, "__addo_limb64"); | |
| 59 | 94 | } |
| 60 | 95 | |
| 61 | 96 | fn __addo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool { |
| 62 | const limb_cnt = limbCount(bits); | |
| 63 | const out = out_ptr[0..limb_cnt]; | |
| 64 | const a = a_ptr[0..limb_cnt]; | |
| 65 | const b = b_ptr[0..limb_cnt]; | |
| 97 | const limb_cnt = usedLimbCount(bits); | |
| 98 | const out = varLimbs(out_ptr, bits); | |
| 99 | const a = constLimbs(a_ptr, bits); | |
| 100 | const b = constLimbs(b_ptr, bits); | |
| 66 | 101 | |
| 67 | 102 | var carry: u1 = 0; |
| 68 | 103 | var i: usize = 0; |
| ... | ... | @@ -91,11 +126,13 @@ fn __addo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_s |
| 91 | 126 | |
| 92 | 127 | if (bits % 64 == 0) { |
| 93 | 128 | limbSet(out, i, limb); |
| 129 | fixLastLimb(out_ptr, is_signed, bits); | |
| 94 | 130 | return carry != 0; |
| 95 | 131 | } else { |
| 96 | 132 | assert(carry == 0); |
| 97 | 133 | const wrapped_limb = limbWrap(limb, is_signed, bits); |
| 98 | 134 | limbSet(out, i, wrapped_limb); |
| 135 | fixLastLimb(out_ptr, is_signed, bits); | |
| 99 | 136 | return wrapped_limb != limb; |
| 100 | 137 | } |
| 101 | 138 | } |
| ... | ... | @@ -124,17 +161,20 @@ test __addo_limb64 { |
| 124 | 161 | try test__addo_limb64(i64, maxInt(i64), 1, .{ minInt(i64), true }); |
| 125 | 162 | try test__addo_limb64(i65, maxInt(i65), 1, .{ minInt(i65), true }); |
| 126 | 163 | try test__addo_limb64(i255, -3, 2, .{ -1, false }); |
| 164 | ||
| 165 | try test__addo_limb64(u150, maxInt(u150), 2, .{ 1, true }); | |
| 166 | try test__addo_limb64(i150, -3, 2, .{ -1, false }); | |
| 127 | 167 | } |
| 128 | 168 | |
| 129 | 169 | comptime { |
| 130 | @export(&__subo_limb64, .{ .name = "__subo_limb64", .linkage = compiler_rt.linkage, .visibility = compiler_rt.visibility }); | |
| 170 | symbol(&__subo_limb64, "__subo_limb64"); | |
| 131 | 171 | } |
| 132 | 172 | |
| 133 | 173 | fn __subo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool { |
| 134 | const limb_cnt = limbCount(bits); | |
| 135 | const out = out_ptr[0..limb_cnt]; | |
| 136 | const a = a_ptr[0..limb_cnt]; | |
| 137 | const b = b_ptr[0..limb_cnt]; | |
| 174 | const limb_cnt = usedLimbCount(bits); | |
| 175 | const out = varLimbs(out_ptr, bits); | |
| 176 | const a = constLimbs(a_ptr, bits); | |
| 177 | const b = constLimbs(b_ptr, bits); | |
| 138 | 178 | |
| 139 | 179 | var borrow: u1 = 0; |
| 140 | 180 | var i: usize = 0; |
| ... | ... | @@ -163,10 +203,12 @@ fn __subo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_s |
| 163 | 203 | |
| 164 | 204 | if (bits % 64 == 0) { |
| 165 | 205 | limbSet(out, i, limb); |
| 206 | fixLastLimb(out_ptr, is_signed, bits); | |
| 166 | 207 | return borrow != 0; |
| 167 | 208 | } else { |
| 168 | 209 | const wrapped_limb = limbWrap(limb, is_signed, bits); |
| 169 | 210 | limbSet(out, i, wrapped_limb); |
| 211 | fixLastLimb(out_ptr, is_signed, bits); | |
| 170 | 212 | return borrow != 0 or wrapped_limb != limb; |
| 171 | 213 | } |
| 172 | 214 | } |
| ... | ... | @@ -195,19 +237,22 @@ test __subo_limb64 { |
| 195 | 237 | try test__subo_limb64(i64, minInt(i64), 1, .{ maxInt(i64), true }); |
| 196 | 238 | try test__subo_limb64(i65, minInt(i65), 1, .{ maxInt(i65), true }); |
| 197 | 239 | try test__subo_limb64(i255, -1, 2, .{ -3, false }); |
| 240 | ||
| 241 | try test__subo_limb64(u150, 2, maxInt(u150), .{ 3, true }); | |
| 242 | try test__subo_limb64(i150, -3, 2, .{ -5, false }); | |
| 198 | 243 | } |
| 199 | 244 | |
| 200 | 245 | comptime { |
| 201 | @export(&__cmp_limb64, .{ .name = "__cmp_limb64", .linkage = compiler_rt.linkage, .visibility = compiler_rt.visibility }); | |
| 246 | symbol(&__cmp_limb64, "__cmp_limb64"); | |
| 202 | 247 | } |
| 203 | 248 | |
| 204 | 249 | // a < b -> -1 |
| 205 | 250 | // a == b -> 0 |
| 206 | 251 | // a > b -> 1 |
| 207 | 252 | fn __cmp_limb64(a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) i8 { |
| 208 | const limb_cnt = limbCount(bits); | |
| 209 | const a = a_ptr[0..limb_cnt]; | |
| 210 | const b = b_ptr[0..limb_cnt]; | |
| 253 | const limb_cnt = usedLimbCount(bits); | |
| 254 | const a = constLimbs(a_ptr, bits); | |
| 255 | const b = constLimbs(b_ptr, bits); | |
| 211 | 256 | |
| 212 | 257 | var i: usize = 0; |
| 213 | 258 | if (is_signed) { |
| ... | ... | @@ -263,4 +308,819 @@ test __cmp_limb64 { |
| 263 | 308 | try test__cmp_limb64(i255, -3, 2, -1); |
| 264 | 309 | try test__cmp_limb64(i255, -5, -5, 0); |
| 265 | 310 | try test__cmp_limb64(i255, 2, -3, 1); |
| 311 | ||
| 312 | try test__cmp_limb64(u150, maxInt(u150) - 5, maxInt(u150) - 5, 0); | |
| 313 | try test__cmp_limb64(i150, minInt(i150), -5, -1); | |
| 314 | } | |
| 315 | ||
| 316 | comptime { | |
| 317 | symbol(&__and_limb64, "__and_limb64"); | |
| 318 | } | |
| 319 | ||
| 320 | fn __and_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, bits: u16) callconv(.c) void { | |
| 321 | const limb_cnt = limbCount(bits); | |
| 322 | const out = out_ptr[0..limb_cnt]; | |
| 323 | const a = a_ptr[0..limb_cnt]; | |
| 324 | const b = b_ptr[0..limb_cnt]; | |
| 325 | ||
| 326 | var i: usize = 0; | |
| 327 | while (i < limb_cnt) : (i += 1) { | |
| 328 | limbSet(out, i, limbGet(a, i) & limbGet(b, i)); | |
| 329 | } | |
| 330 | } | |
| 331 | ||
| 332 | fn test__and_limb64(comptime T: type, a: T, b: T, expected: T) !void { | |
| 333 | const int_info = @typeInfo(T).int; | |
| 334 | ||
| 335 | var a_limbs = asLimbs(a); | |
| 336 | var b_limbs = asLimbs(b); | |
| 337 | var out: Limbs(T) = undefined; | |
| 338 | __and_limb64(&out, &a_limbs, &b_limbs, int_info.bits); | |
| 339 | ||
| 340 | const expected_limbs = asLimbs(expected); | |
| 341 | try testing.expectEqual(expected_limbs, out); | |
| 342 | } | |
| 343 | ||
| 344 | test __and_limb64 { | |
| 345 | try test__and_limb64(u64, 1, 2, 0); | |
| 346 | try test__and_limb64(u64, maxInt(u64), 2, 2); | |
| 347 | try test__and_limb64(u65, maxInt(u65), 2, 2); | |
| 348 | try test__and_limb64(u255, maxInt(u255), 7, 7); | |
| 349 | ||
| 350 | try test__and_limb64(i64, 1, 2, 0); | |
| 351 | try test__and_limb64(i64, -1, 2, 2); | |
| 352 | try test__and_limb64(i65, minInt(i65), -1, minInt(i65)); | |
| 353 | try test__and_limb64(i255, -1, 2, 2); | |
| 354 | ||
| 355 | try test__and_limb64(u150, maxInt(u150), 7, 7); | |
| 356 | try test__and_limb64(i150, -2, 3, 2); | |
| 357 | } | |
| 358 | ||
| 359 | comptime { | |
| 360 | symbol(&__or_limb64, "__or_limb64"); | |
| 361 | } | |
| 362 | ||
| 363 | fn __or_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, bits: u16) callconv(.c) void { | |
| 364 | const limb_cnt = limbCount(bits); | |
| 365 | const out = out_ptr[0..limb_cnt]; | |
| 366 | const a = a_ptr[0..limb_cnt]; | |
| 367 | const b = b_ptr[0..limb_cnt]; | |
| 368 | ||
| 369 | var i: usize = 0; | |
| 370 | while (i < limb_cnt) : (i += 1) { | |
| 371 | limbSet(out, i, limbGet(a, i) | limbGet(b, i)); | |
| 372 | } | |
| 373 | } | |
| 374 | ||
| 375 | fn test__or_limb64(comptime T: type, a: T, b: T, expected: T) !void { | |
| 376 | const int_info = @typeInfo(T).int; | |
| 377 | ||
| 378 | var a_limbs = asLimbs(a); | |
| 379 | var b_limbs = asLimbs(b); | |
| 380 | var out: Limbs(T) = undefined; | |
| 381 | __or_limb64(&out, &a_limbs, &b_limbs, int_info.bits); | |
| 382 | ||
| 383 | const expected_limbs = asLimbs(expected); | |
| 384 | try testing.expectEqual(expected_limbs, out); | |
| 385 | } | |
| 386 | ||
| 387 | test __or_limb64 { | |
| 388 | try test__or_limb64(u64, 1, 2, 3); | |
| 389 | try test__or_limb64(u64, maxInt(u64), 2, maxInt(u64)); | |
| 390 | try test__or_limb64(u65, maxInt(u65), 2, maxInt(u65)); | |
| 391 | try test__or_limb64(u255, 1, 2, 3); | |
| 392 | ||
| 393 | try test__or_limb64(i64, 1, 2, 3); | |
| 394 | try test__or_limb64(i64, -1, 2, -1); | |
| 395 | try test__or_limb64(i65, minInt(i65), 1, minInt(i65) + 1); | |
| 396 | try test__or_limb64(i255, -3, 2, -1); | |
| 397 | ||
| 398 | try test__or_limb64(u150, maxInt(u150) - 1, 3, maxInt(u150)); | |
| 399 | try test__or_limb64(i150, -2, 3, -1); | |
| 400 | } | |
| 401 | ||
| 402 | comptime { | |
| 403 | symbol(&__xor_limb64, "__xor_limb64"); | |
| 404 | } | |
| 405 | ||
| 406 | fn __xor_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, bits: u16) callconv(.c) void { | |
| 407 | const limb_cnt = limbCount(bits); | |
| 408 | const out = out_ptr[0..limb_cnt]; | |
| 409 | const a = a_ptr[0..limb_cnt]; | |
| 410 | const b = b_ptr[0..limb_cnt]; | |
| 411 | ||
| 412 | var i: usize = 0; | |
| 413 | while (i < limb_cnt) : (i += 1) { | |
| 414 | limbSet(out, i, limbGet(a, i) ^ limbGet(b, i)); | |
| 415 | } | |
| 416 | } | |
| 417 | ||
| 418 | fn test__xor_limb64(comptime T: type, a: T, b: T, expected: T) !void { | |
| 419 | const int_info = @typeInfo(T).int; | |
| 420 | ||
| 421 | var a_limbs = asLimbs(a); | |
| 422 | var b_limbs = asLimbs(b); | |
| 423 | var out: Limbs(T) = undefined; | |
| 424 | __xor_limb64(&out, &a_limbs, &b_limbs, int_info.bits); | |
| 425 | ||
| 426 | const expected_limbs = asLimbs(expected); | |
| 427 | try testing.expectEqual(expected_limbs, out); | |
| 428 | } | |
| 429 | ||
| 430 | test __xor_limb64 { | |
| 431 | try test__xor_limb64(u64, 1, 2, 3); | |
| 432 | try test__xor_limb64(u64, 3, 2, 1); | |
| 433 | try test__xor_limb64(u65, maxInt(u65), 2, maxInt(u65) - 2); | |
| 434 | try test__xor_limb64(u255, 7, 3, 4); | |
| 435 | ||
| 436 | try test__xor_limb64(i64, 3, 2, 1); | |
| 437 | try test__xor_limb64(i64, -1, 2, -3); | |
| 438 | try test__xor_limb64(i65, minInt(i65), -1, maxInt(i65)); | |
| 439 | try test__xor_limb64(i255, -3, 2, -1); | |
| 440 | ||
| 441 | try test__xor_limb64(u150, maxInt(u150) - 1, 3, maxInt(u150) - 2); | |
| 442 | try test__xor_limb64(i150, -2, 3, -3); | |
| 443 | } | |
| 444 | ||
| 445 | comptime { | |
| 446 | symbol(&__not_limb64, "__not_limb64"); | |
| 447 | } | |
| 448 | ||
| 449 | fn __not_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void { | |
| 450 | const limb_cnt = usedLimbCount(bits); | |
| 451 | const out = varLimbs(out_ptr, bits); | |
| 452 | const a = constLimbs(a_ptr, bits); | |
| 453 | ||
| 454 | var i: usize = 0; | |
| 455 | while (i < limb_cnt - 1) : (i += 1) { | |
| 456 | limbSet(out, i, ~limbGet(a, i)); | |
| 457 | } | |
| 458 | ||
| 459 | var limb: u64 = ~limbGet(a, i); | |
| 460 | if (!is_signed and bits % 64 != 0) { | |
| 461 | limb = limbWrap(limb, is_signed, bits); | |
| 462 | } | |
| 463 | limbSet(out, i, limb); | |
| 464 | fixLastLimb(out_ptr, is_signed, bits); | |
| 465 | } | |
| 466 | ||
| 467 | fn test__not_limb64(comptime T: type, a: T, expected: T) !void { | |
| 468 | const int_info = @typeInfo(T).int; | |
| 469 | const is_signed = int_info.signedness == .signed; | |
| 470 | ||
| 471 | var a_limbs = asLimbs(a); | |
| 472 | var out: Limbs(T) = undefined; | |
| 473 | __not_limb64(&out, &a_limbs, is_signed, int_info.bits); | |
| 474 | ||
| 475 | const expected_limbs = asLimbs(expected); | |
| 476 | try testing.expectEqual(expected_limbs, out); | |
| 477 | } | |
| 478 | ||
| 479 | test __not_limb64 { | |
| 480 | try test__not_limb64(u64, 1, maxInt(u64) - 1); | |
| 481 | try test__not_limb64(u64, 3, maxInt(u64) - 3); | |
| 482 | try test__not_limb64(u65, maxInt(u65), 0); | |
| 483 | try test__not_limb64(u255, 7, maxInt(u255) - 7); | |
| 484 | ||
| 485 | try test__not_limb64(i64, 3, -4); | |
| 486 | try test__not_limb64(i64, -1, 0); | |
| 487 | try test__not_limb64(i65, minInt(i65), maxInt(i65)); | |
| 488 | try test__not_limb64(i255, -3, 2); | |
| 489 | ||
| 490 | try test__not_limb64(u150, maxInt(u150), 0); | |
| 491 | try test__not_limb64(i150, maxInt(i150), minInt(i150)); | |
| 492 | } | |
| 493 | ||
| 494 | comptime { | |
| 495 | symbol(&__shlo_limb64, "__shlo_limb64"); | |
| 496 | } | |
| 497 | ||
| 498 | fn __shlo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) bool { | |
| 499 | const limb_cnt = usedLimbCount(bits); | |
| 500 | const out = varLimbs(out_ptr, bits); | |
| 501 | const a = constLimbs(a_ptr, bits); | |
| 502 | ||
| 503 | assert(shift < bits); | |
| 504 | ||
| 505 | const limb_shift = shift / 64; | |
| 506 | const bit_shift = shift % 64; | |
| 507 | ||
| 508 | var carry: u64 = 0; | |
| 509 | var i: usize = 0; | |
| 510 | while (i < limb_cnt - 1) : (i += 1) { | |
| 511 | if (i < limb_shift) { | |
| 512 | limbSet(out, i, 0); | |
| 513 | } else { | |
| 514 | const limb = limbGet(a, i - limb_shift); | |
| 515 | limbSet(out, i, (limb << @intCast(bit_shift)) | carry); | |
| 516 | carry = if (bit_shift != 0) (limb >> @intCast(64 - bit_shift)) else 0; | |
| 517 | } | |
| 518 | } | |
| 519 | ||
| 520 | const limb = limbGet(a, i - limb_shift); | |
| 521 | const raw_last = (limb << @intCast(bit_shift)) | carry; | |
| 522 | carry = if (bit_shift != 0) (limb >> @intCast(64 - bit_shift)) else 0; | |
| 523 | ||
| 524 | const last = if (bits % 64 == 0) raw_last else limbWrap(raw_last, is_signed, bits); | |
| 525 | limbSet(out, i, last); | |
| 526 | ||
| 527 | const sign_extend: u64 = if (is_signed and (last >> 63) == 1) ~@as(u64, 0) else 0; | |
| 528 | const expected_carry: u64 = if (bit_shift == 0) 0 else sign_extend >> @intCast(64 - bit_shift); | |
| 529 | ||
| 530 | var overflow = carry != expected_carry; | |
| 531 | if (bits % 64 != 0) { | |
| 532 | overflow = overflow or raw_last != last; | |
| 533 | } | |
| 534 | ||
| 535 | var j = limb_cnt - limb_shift; | |
| 536 | while (j < limb_cnt) : (j += 1) { | |
| 537 | overflow = overflow or limbGet(a, j) != sign_extend; | |
| 538 | } | |
| 539 | ||
| 540 | fixLastLimb(out_ptr, is_signed, bits); | |
| 541 | return overflow; | |
| 542 | } | |
| 543 | ||
| 544 | fn test__shlo_limb64(comptime T: type, a: T, shift: u16, expected: struct { T, bool }) !void { | |
| 545 | const int_info = @typeInfo(T).int; | |
| 546 | const is_signed = int_info.signedness == .signed; | |
| 547 | ||
| 548 | var a_limbs = asLimbs(a); | |
| 549 | var out: Limbs(T) = undefined; | |
| 550 | const overflow = __shlo_limb64(&out, &a_limbs, shift, is_signed, int_info.bits); | |
| 551 | ||
| 552 | const expected_limbs = asLimbs(expected[0]); | |
| 553 | try testing.expectEqual(expected_limbs, out); | |
| 554 | try testing.expectEqual(expected[1], overflow); | |
| 555 | } | |
| 556 | ||
| 557 | test __shlo_limb64 { | |
| 558 | try test__shlo_limb64(u64, 0x1234_5678_9ABC_DEF0, 4, .{ 0x2345_6789_ABCD_EF00, true }); | |
| 559 | try test__shlo_limb64(u64, 0x8000_0000_0000_0001, 63, .{ 0x8000_0000_0000_0000, true }); | |
| 560 | try test__shlo_limb64(u65, 1, 64, .{ 0x1_0000_0000_0000_0000, false }); | |
| 561 | try test__shlo_limb64(u65, 0x1_0000_0000_0000_0000, 1, .{ 0, true }); | |
| 562 | try test__shlo_limb64(u128, 0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0, 4, .{ 0x2345_6789_ABCD_EF01_2345_6789_ABCD_EF00, true }); | |
| 563 | try test__shlo_limb64(u255, maxInt(u255), 1, .{ maxInt(u255) - 1, true }); | |
| 564 | try test__shlo_limb64(u633, 1 << 299, 333, .{ 1 << 632, false }); | |
| 565 | try test__shlo_limb64(u633, 1 << 300, 333, .{ 0, true }); | |
| 566 | try test__shlo_limb64(u633, 1 << 298, 333, .{ 1 << 631, false }); | |
| 567 | ||
| 568 | try test__shlo_limb64(i64, -2, 1, .{ -4, false }); | |
| 569 | try test__shlo_limb64(i64, minInt(i64), 1, .{ 0, true }); | |
| 570 | try test__shlo_limb64(i64, minInt(i64), 63, .{ 0, true }); | |
| 571 | try test__shlo_limb64(i65, minInt(i63), 1, .{ minInt(i64), false }); | |
| 572 | try test__shlo_limb64(i65, -1, 17, .{ -1 << 17, false }); | |
| 573 | try test__shlo_limb64(i65, -3, 64, .{ -1 << 64, true }); | |
| 574 | try test__shlo_limb64(i128, -0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0, 4, .{ -0x2345_6789_ABCD_EF01_2345_6789_ABCD_EF00, true }); | |
| 575 | try test__shlo_limb64(i255, -3, 1, .{ -6, false }); | |
| 576 | try test__shlo_limb64(i633, 1 << 298, 333, .{ 1 << 631, false }); | |
| 577 | try test__shlo_limb64(i633, 1 << 299, 333, .{ minInt(i633), true }); | |
| 578 | try test__shlo_limb64(i633, 1 << 300, 333, .{ 0, true }); | |
| 579 | try test__shlo_limb64(i633, 1 << 297, 333, .{ 1 << 630, false }); | |
| 580 | try test__shlo_limb64(i633, -1 << 299, 333, .{ -1 << 632, false }); | |
| 581 | try test__shlo_limb64(i633, -1 << 300, 333, .{ 0, true }); | |
| 582 | try test__shlo_limb64(i633, -1 << 298, 333, .{ -1 << 631, false }); | |
| 583 | ||
| 584 | try test__shlo_limb64(u150, maxInt(u150), 1, .{ maxInt(u150) - 1, true }); | |
| 585 | try test__shlo_limb64(i150, -3, 1, .{ -6, false }); | |
| 586 | } | |
| 587 | ||
| 588 | comptime { | |
| 589 | symbol(&__shr_limb64, "__shr_limb64"); | |
| 590 | } | |
| 591 | ||
| 592 | fn __shr_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) void { | |
| 593 | const limb_cnt = usedLimbCount(bits); | |
| 594 | const out = varLimbs(out_ptr, bits); | |
| 595 | const a = constLimbs(a_ptr, bits); | |
| 596 | ||
| 597 | assert(shift < bits); | |
| 598 | ||
| 599 | const limb_shift = shift / 64; | |
| 600 | const bit_shift = shift % 64; | |
| 601 | ||
| 602 | const ms = limbGet(a, limb_cnt - 1); | |
| 603 | const sign_extend: u64 = if (is_signed and (ms >> 63) == 1) ~@as(u64, 0) else 0; | |
| 604 | ||
| 605 | var carry: u64 = if (bit_shift != 0) (sign_extend << @intCast(64 - bit_shift)) else 0; | |
| 606 | var i: usize = 0; | |
| 607 | while (i < limb_cnt) : (i += 1) { | |
| 608 | const j = limb_cnt - 1 - i; | |
| 609 | if (i < limb_shift) { | |
| 610 | limbSet(out, j, sign_extend); | |
| 611 | } else { | |
| 612 | const limb = limbGet(a, j + limb_shift); | |
| 613 | limbSet(out, j, (limb >> @intCast(bit_shift)) | carry); | |
| 614 | carry = if (bit_shift != 0) (limb << @intCast(64 - bit_shift)) else 0; | |
| 615 | } | |
| 616 | } | |
| 617 | ||
| 618 | fixLastLimb(out_ptr, is_signed, bits); | |
| 619 | } | |
| 620 | ||
| 621 | fn test__shr_limb64(comptime T: type, a: T, shift: u16, expected: T) !void { | |
| 622 | const int_info = @typeInfo(T).int; | |
| 623 | const is_signed = int_info.signedness == .signed; | |
| 624 | ||
| 625 | var a_limbs = asLimbs(a); | |
| 626 | var out: Limbs(T) = undefined; | |
| 627 | __shr_limb64(&out, &a_limbs, shift, is_signed, int_info.bits); | |
| 628 | ||
| 629 | const expected_limbs = asLimbs(expected); | |
| 630 | try testing.expectEqual(expected_limbs, out); | |
| 631 | } | |
| 632 | ||
| 633 | test __shr_limb64 { | |
| 634 | try test__shr_limb64(u64, 0x1234_5678_9ABC_DEF0, 4, 0x0123_4567_89AB_CDEF); | |
| 635 | try test__shr_limb64(u64, 0x8000_0000_0000_0001, 63, 1); | |
| 636 | try test__shr_limb64(u65, 0x1_0000_0000_0000_0000, 64, 1); | |
| 637 | try test__shr_limb64(u65, 0x1_0000_0000_0000_0001, 1, 0x0_8000_0000_0000_0000); | |
| 638 | try test__shr_limb64(u128, 0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0, 4, 0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF); | |
| 639 | try test__shr_limb64(u255, maxInt(u255), 1, maxInt(u254)); | |
| 640 | try test__shr_limb64(u633, 1 << 333, 333, 1); | |
| 641 | try test__shr_limb64(u633, 1 << 334, 333, 2); | |
| 642 | try test__shr_limb64(u633, 1 << 332, 333, 0); | |
| 643 | ||
| 644 | try test__shr_limb64(i64, -2, 1, -1); | |
| 645 | try test__shr_limb64(i64, minInt(i64), 63, -1); | |
| 646 | try test__shr_limb64(i65, minInt(i65), 1, minInt(i65) | (1 << 63)); | |
| 647 | try test__shr_limb64(i65, -1, 17, -1); | |
| 648 | try test__shr_limb64(i128, -0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0, 4, -0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF); | |
| 649 | try test__shr_limb64(i255, -3, 1, -2); | |
| 650 | try test__shr_limb64(i633, 1 << 333, 333, 1); | |
| 651 | try test__shr_limb64(i633, 1 << 334, 333, 2); | |
| 652 | try test__shr_limb64(i633, 1 << 332, 333, 0); | |
| 653 | try test__shr_limb64(i633, -1 << 333, 333, -1); | |
| 654 | try test__shr_limb64(i633, -1 << 334, 333, -2); | |
| 655 | try test__shr_limb64(i633, -1 << 332, 333, -1); | |
| 656 | ||
| 657 | try test__shr_limb64(u150, maxInt(u150), 1, maxInt(u149)); | |
| 658 | try test__shr_limb64(i150, -3, 1, -2); | |
| 659 | } | |
| 660 | ||
| 661 | comptime { | |
| 662 | symbol(&__clz_limb64, "__clz_limb64"); | |
| 663 | } | |
| 664 | ||
| 665 | fn __clz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { | |
| 666 | const limb_cnt = usedLimbCount(bits); | |
| 667 | const a = constLimbs(a_ptr, bits); | |
| 668 | ||
| 669 | var res: u16 = 0; | |
| 670 | var i: usize = 0; | |
| 671 | ||
| 672 | if (bits % 64 != 0) { | |
| 673 | const limb = limbGet(a, limb_cnt - 1); | |
| 674 | if (limb == 0) { | |
| 675 | res += bits % 64; | |
| 676 | } else { | |
| 677 | return @clz(limb << @intCast(64 - bits % 64)); | |
| 678 | } | |
| 679 | i += 1; | |
| 680 | } | |
| 681 | ||
| 682 | while (i < limb_cnt) : (i += 1) { | |
| 683 | const j = limb_cnt - 1 - i; | |
| 684 | const limb = limbGet(a, j); | |
| 685 | if (limb == 0) { | |
| 686 | res += 64; | |
| 687 | } else { | |
| 688 | res += @clz(limb); | |
| 689 | break; | |
| 690 | } | |
| 691 | } | |
| 692 | ||
| 693 | return res; | |
| 694 | } | |
| 695 | ||
| 696 | fn test__clz_limb64(comptime T: type, a: T, expected: u16) !void { | |
| 697 | const int_info = @typeInfo(T).int; | |
| 698 | ||
| 699 | var a_limbs = asLimbs(a); | |
| 700 | const out = __clz_limb64(&a_limbs, int_info.bits); | |
| 701 | ||
| 702 | try testing.expectEqual(expected, out); | |
| 703 | } | |
| 704 | ||
| 705 | test __clz_limb64 { | |
| 706 | try test__clz_limb64(u64, 0, 64); | |
| 707 | try test__clz_limb64(u65, 1 << 64, 0); | |
| 708 | try test__clz_limb64(u65, 1 << 9, 55); | |
| 709 | try test__clz_limb64(u128, 1 << 31, 96); | |
| 710 | try test__clz_limb64(u255, 1 << 62, 192); | |
| 711 | ||
| 712 | try test__clz_limb64(i64, -1, 0); | |
| 713 | try test__clz_limb64(i65, minInt(i65), 0); | |
| 714 | try test__clz_limb64(i65, 1 << 32, 32); | |
| 715 | try test__clz_limb64(i128, 0, 128); | |
| 716 | try test__clz_limb64(i255, 1 << 130, 124); | |
| 717 | ||
| 718 | try test__clz_limb64(u150, 1 << 31, 118); | |
| 719 | try test__clz_limb64(i150, maxInt(u65) - 1, 85); | |
| 720 | } | |
| 721 | ||
| 722 | comptime { | |
| 723 | symbol(&__ctz_limb64, "__ctz_limb64"); | |
| 724 | } | |
| 725 | ||
| 726 | fn __ctz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { | |
| 727 | const limb_cnt = usedLimbCount(bits); | |
| 728 | const a = constLimbs(a_ptr, bits); | |
| 729 | ||
| 730 | var res: u16 = 0; | |
| 731 | var i: usize = 0; | |
| 732 | while (i < limb_cnt - 1) : (i += 1) { | |
| 733 | const limb = limbGet(a, i); | |
| 734 | if (limb == 0) { | |
| 735 | res += 64; | |
| 736 | } else { | |
| 737 | res += @ctz(limb); | |
| 738 | return res; | |
| 739 | } | |
| 740 | } | |
| 741 | ||
| 742 | const limb = limbGet(a, i); | |
| 743 | if (bits % 64 != 0 and limb == 0) { | |
| 744 | res += bits % 64; | |
| 745 | } else { | |
| 746 | res += @ctz(limb); | |
| 747 | } | |
| 748 | ||
| 749 | return res; | |
| 750 | } | |
| 751 | ||
| 752 | fn test__ctz_limb64(comptime T: type, a: T, expected: u16) !void { | |
| 753 | const int_info = @typeInfo(T).int; | |
| 754 | ||
| 755 | var a_limbs = asLimbs(a); | |
| 756 | const out = __ctz_limb64(&a_limbs, int_info.bits); | |
| 757 | ||
| 758 | try testing.expectEqual(expected, out); | |
| 759 | } | |
| 760 | ||
| 761 | test __ctz_limb64 { | |
| 762 | try test__ctz_limb64(u64, 1 << 17, 17); | |
| 763 | try test__ctz_limb64(u65, 1 << 64, 64); | |
| 764 | try test__ctz_limb64(u65, 0, 65); | |
| 765 | try test__ctz_limb64(u128, 1 << 100, 100); | |
| 766 | try test__ctz_limb64(u255, 1 << 200, 200); | |
| 767 | ||
| 768 | try test__ctz_limb64(i64, -1 << 9, 9); | |
| 769 | try test__ctz_limb64(i65, minInt(i65), 64); | |
| 770 | try test__ctz_limb64(i65, 0, 65); | |
| 771 | try test__ctz_limb64(i128, -1 << 73, 73); | |
| 772 | try test__ctz_limb64(i255, 1 << 130, 130); | |
| 773 | ||
| 774 | try test__ctz_limb64(u150, 1 << 101, 101); | |
| 775 | try test__ctz_limb64(i150, -1 << 74, 74); | |
| 776 | } | |
| 777 | ||
| 778 | comptime { | |
| 779 | symbol(&__popcount_limb64, "__popcount_limb64"); | |
| 780 | } | |
| 781 | ||
| 782 | fn __popcount_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 { | |
| 783 | const limb_cnt = usedLimbCount(bits); | |
| 784 | const a = constLimbs(a_ptr, bits); | |
| 785 | ||
| 786 | var res: u16 = 0; | |
| 787 | var i: usize = 0; | |
| 788 | while (i < limb_cnt - 1) : (i += 1) { | |
| 789 | res += @popCount(limbGet(a, i)); | |
| 790 | } | |
| 791 | ||
| 792 | var limb = limbGet(a, i); | |
| 793 | if (bits % 64 != 0) { | |
| 794 | limb <<= @intCast(64 - bits % 64); | |
| 795 | } | |
| 796 | res += @popCount(limb); | |
| 797 | ||
| 798 | return res; | |
| 799 | } | |
| 800 | ||
| 801 | fn test__popcount_limb64(comptime T: type, a: T, expected: u16) !void { | |
| 802 | const int_info = @typeInfo(T).int; | |
| 803 | ||
| 804 | var a_limbs = asLimbs(a); | |
| 805 | const out = __popcount_limb64(&a_limbs, int_info.bits); | |
| 806 | ||
| 807 | try testing.expectEqual(expected, out); | |
| 808 | } | |
| 809 | ||
| 810 | test __popcount_limb64 { | |
| 811 | try test__popcount_limb64(u64, 0xF0F0_0000_0000_0001, 9); | |
| 812 | try test__popcount_limb64(u65, 1 << 64, 1); | |
| 813 | try test__popcount_limb64(u65, maxInt(u65), 65); | |
| 814 | try test__popcount_limb64(u128, (1 << 100) | (1 << 5) | 1, 3); | |
| 815 | try test__popcount_limb64(u255, maxInt(u255), 255); | |
| 816 | ||
| 817 | try test__popcount_limb64(i64, -1, 64); | |
| 818 | try test__popcount_limb64(i65, minInt(i65), 1); | |
| 819 | try test__popcount_limb64(i65, -1, 65); | |
| 820 | try test__popcount_limb64(i128, -1 << 7, 121); | |
| 821 | try test__popcount_limb64(i255, -1 << 200, 55); | |
| 822 | ||
| 823 | try test__popcount_limb64(u150, (1 << 149) | (1 << 65) | 1, 3); | |
| 824 | try test__popcount_limb64(i150, -1 << 7, 143); | |
| 825 | } | |
| 826 | ||
| 827 | comptime { | |
| 828 | symbol(&__bitreverse_limb64, "__bitreverse_limb64"); | |
| 829 | } | |
| 830 | ||
| 831 | fn __bitreverse_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void { | |
| 832 | const limb_cnt = usedLimbCount(bits); | |
| 833 | const out = varLimbs(out_ptr, bits); | |
| 834 | const a = constLimbs(a_ptr, bits); | |
| 835 | ||
| 836 | var i: usize = 0; | |
| 837 | while (i < limb_cnt) : (i += 1) { | |
| 838 | const j = limb_cnt - 1 - i; | |
| 839 | limbSet(out, j, @bitReverse(limbGet(a, i))); | |
| 840 | } | |
| 841 | ||
| 842 | if (bits % 64 != 0) { | |
| 843 | __shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits); | |
| 844 | } | |
| 845 | fixLastLimb(out_ptr, is_signed, bits); | |
| 846 | } | |
| 847 | ||
| 848 | fn test__bitreverse_limb64(comptime T: type, a: T, expected: T) !void { | |
| 849 | const int_info = @typeInfo(T).int; | |
| 850 | const is_signed = int_info.signedness == .signed; | |
| 851 | ||
| 852 | var a_limbs = asLimbs(a); | |
| 853 | var out: Limbs(T) = undefined; | |
| 854 | __bitreverse_limb64(&out, &a_limbs, is_signed, int_info.bits); | |
| 855 | ||
| 856 | const expected_limbs = asLimbs(expected); | |
| 857 | try testing.expectEqual(expected_limbs, out); | |
| 858 | } | |
| 859 | ||
| 860 | test __bitreverse_limb64 { | |
| 861 | try test__bitreverse_limb64(u64, 1 << 7, 1 << 56); | |
| 862 | try test__bitreverse_limb64(u65, 1 << 64, 1); | |
| 863 | try test__bitreverse_limb64(u65, 1 << 9, 1 << 55); | |
| 864 | try test__bitreverse_limb64(u128, 1 << 100, 1 << 27); | |
| 865 | try test__bitreverse_limb64(u255, 1 << 200, 1 << 54); | |
| 866 | ||
| 867 | try test__bitreverse_limb64(i64, -1, -1); | |
| 868 | try test__bitreverse_limb64(i65, 1 << 32, 1 << 32); | |
| 869 | try test__bitreverse_limb64(i65, minInt(i65), 1); | |
| 870 | try test__bitreverse_limb64(i128, 1 << 63, 1 << 64); | |
| 871 | try test__bitreverse_limb64(i255, 1 << 130, 1 << 124); | |
| 872 | ||
| 873 | try test__bitreverse_limb64(u150, 1 << 9, 1 << 140); | |
| 874 | try test__bitreverse_limb64(i150, minInt(i150), 1); | |
| 875 | } | |
| 876 | ||
| 877 | comptime { | |
| 878 | symbol(&__byteswap_limb64, "__byteswap_limb64"); | |
| 879 | } | |
| 880 | ||
| 881 | fn __byteswap_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void { | |
| 882 | const limb_cnt = usedLimbCount(bits); | |
| 883 | const out = varLimbs(out_ptr, bits); | |
| 884 | const a = constLimbs(a_ptr, bits); | |
| 885 | ||
| 886 | assert(bits % 8 == 0); | |
| 887 | ||
| 888 | var i: usize = 0; | |
| 889 | while (i < limb_cnt) : (i += 1) { | |
| 890 | const j = limb_cnt - 1 - i; | |
| 891 | limbSet(out, j, @byteSwap(limbGet(a, i))); | |
| 892 | } | |
| 893 | ||
| 894 | if (bits % 64 != 0) { | |
| 895 | __shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits); | |
| 896 | } | |
| 897 | fixLastLimb(out_ptr, is_signed, bits); | |
| 898 | } | |
| 899 | ||
| 900 | fn test__byteswap_limb64(comptime T: type, a: T, expected: T) !void { | |
| 901 | const int_info = @typeInfo(T).int; | |
| 902 | const is_signed = int_info.signedness == .signed; | |
| 903 | ||
| 904 | var a_limbs = asLimbs(a); | |
| 905 | var out: Limbs(T) = undefined; | |
| 906 | __byteswap_limb64(&out, &a_limbs, is_signed, int_info.bits); | |
| 907 | ||
| 908 | const expected_limbs = asLimbs(expected); | |
| 909 | try testing.expectEqual(expected_limbs, out); | |
| 910 | } | |
| 911 | ||
| 912 | test __byteswap_limb64 { | |
| 913 | try test__byteswap_limb64(u64, 0x0123_4567_89AB_CDEF, 0xEFCD_AB89_6745_2301); | |
| 914 | try test__byteswap_limb64(u72, 0x01_23_45_67_89_AB_CD_EF_11, 0x11_EF_CD_AB_89_67_45_23_01); | |
| 915 | try test__byteswap_limb64(u128, 1 << 72, 1 << 48); | |
| 916 | try test__byteswap_limb64(u248, 1, 1 << 240); | |
| 917 | try test__byteswap_limb64(u256, 1 << 120, 1 << 128); | |
| 918 | ||
| 919 | try test__byteswap_limb64(i64, minInt(i64), 128); | |
| 920 | try test__byteswap_limb64(i72, 1, 1 << 64); | |
| 921 | try test__byteswap_limb64(i72, -1, -1); | |
| 922 | try test__byteswap_limb64(i128, 1 << 56, 1 << 64); | |
| 923 | try test__byteswap_limb64(i248, minInt(i248), 128); | |
| 924 | ||
| 925 | try test__byteswap_limb64(u152, 1, 1 << 144); | |
| 926 | try test__byteswap_limb64(i152, 1 << 56, 1 << 88); | |
| 927 | } | |
| 928 | ||
| 929 | comptime { | |
| 930 | symbol(&__mulo_limb64, "__mulo_limb64"); | |
| 931 | } | |
| 932 | ||
| 933 | inline fn add3(x: *[3]u64, start: usize, v0: u64) void { | |
| 934 | var i = start; | |
| 935 | var v = v0; | |
| 936 | while (i < 3) : (i += 1) { | |
| 937 | const s = @addWithOverflow(x[i], v); | |
| 938 | x[i] = s[0]; | |
| 939 | if (s[1] == 0) break; | |
| 940 | v = 1; | |
| 941 | } | |
| 942 | } | |
| 943 | ||
| 944 | fn mulwide(a: u64, b: u64) [2]u64 { | |
| 945 | const muldXi = @import("mulXi3.zig").muldXi; | |
| 946 | const limbs: [2]u64 = @bitCast(muldXi(u64, a, b)); | |
| 947 | return switch (endian) { | |
| 948 | .little => limbs, | |
| 949 | .big => .{ limbs[1], limbs[0] }, | |
| 950 | }; | |
| 951 | } | |
| 952 | ||
| 953 | fn __mulo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool { | |
| 954 | const limb_cnt = usedLimbCount(bits); | |
| 955 | ||
| 956 | const out = varLimbs(out_ptr, bits); | |
| 957 | const a = constLimbs(a_ptr, bits); | |
| 958 | const b = constLimbs(b_ptr, bits); | |
| 959 | ||
| 960 | @memset(out, 0); | |
| 961 | ||
| 962 | const all_ones = ~@as(u64, 0); | |
| 963 | const a_neg = is_signed and ((limbGet(a, limb_cnt - 1) >> 63) != 0); | |
| 964 | const b_neg = is_signed and ((limbGet(b, limb_cnt - 1) >> 63) != 0); | |
| 965 | ||
| 966 | var carry: [3]u64 = @splat(0); | |
| 967 | var hi_zero = true; | |
| 968 | var hi_ones = true; | |
| 969 | var hi_borrow: u1 = 0; | |
| 970 | var raw_last: u64 = 0; | |
| 971 | ||
| 972 | var k: usize = 0; | |
| 973 | while (k < 2 * limb_cnt) : (k += 1) { | |
| 974 | var acc = carry; | |
| 975 | ||
| 976 | var i: usize = if (k < limb_cnt) 0 else k - (limb_cnt - 1); | |
| 977 | while (i < limb_cnt and i <= k) : (i += 1) { | |
| 978 | const j = k - i; | |
| 979 | if (j >= limb_cnt) continue; | |
| 980 | ||
| 981 | const p = mulwide(limbGet(a, i), limbGet(b, j)); | |
| 982 | add3(&acc, 0, p[0]); | |
| 983 | add3(&acc, 1, p[1]); | |
| 984 | } | |
| 985 | ||
| 986 | var limb = acc[0]; | |
| 987 | if (k < limb_cnt) { | |
| 988 | limbSet(out, k, limb); | |
| 989 | if (k == limb_cnt - 1) raw_last = limb; | |
| 990 | } else { | |
| 991 | if (is_signed) { | |
| 992 | const h = k - limb_cnt; | |
| 993 | ||
| 994 | const s0 = @subWithOverflow(limb, if (a_neg) limbGet(b, h) else 0); | |
| 995 | const s1 = @subWithOverflow(s0[0], if (b_neg) limbGet(a, h) else 0); | |
| 996 | const s2 = @subWithOverflow(s1[0], hi_borrow); | |
| 997 | ||
| 998 | limb = s2[0]; | |
| 999 | hi_borrow = @intFromBool(s0[1] != 0 or s1[1] != 0 or s2[1] != 0); | |
| 1000 | } | |
| 1001 | ||
| 1002 | hi_zero = hi_zero and limb == 0; | |
| 1003 | hi_ones = hi_ones and limb == all_ones; | |
| 1004 | } | |
| 1005 | ||
| 1006 | carry = .{ acc[1], acc[2], 0 }; | |
| 1007 | } | |
| 1008 | ||
| 1009 | const last = if (bits % 64 == 0) raw_last else limbWrap(raw_last, is_signed, bits); | |
| 1010 | if (bits % 64 != 0) { | |
| 1011 | limbSet(out, limb_cnt - 1, last); | |
| 1012 | } | |
| 1013 | ||
| 1014 | fixLastLimb(out_ptr, is_signed, bits); | |
| 1015 | ||
| 1016 | if (!is_signed) { | |
| 1017 | return !hi_zero or raw_last != last; | |
| 1018 | } | |
| 1019 | ||
| 1020 | const sign_extend: u64 = if ((last >> 63) == 1) all_ones else 0; | |
| 1021 | return (raw_last != last) or if (sign_extend == 0) !hi_zero else !hi_ones; | |
| 1022 | } | |
| 1023 | ||
| 1024 | fn test__mulo_limb64(comptime T: type, a: T, b: T, expected: struct { T, bool }) !void { | |
| 1025 | const int_info = @typeInfo(T).int; | |
| 1026 | const is_signed = int_info.signedness == .signed; | |
| 1027 | ||
| 1028 | var a_limbs = asLimbs(a); | |
| 1029 | var b_limbs = asLimbs(b); | |
| 1030 | var out: Limbs(T) = undefined; | |
| 1031 | const overflow = __mulo_limb64(&out, &a_limbs, &b_limbs, is_signed, int_info.bits); | |
| 1032 | ||
| 1033 | const expected_limbs = asLimbs(expected[0]); | |
| 1034 | try testing.expectEqual(expected_limbs, out); | |
| 1035 | try testing.expectEqual(expected[1], overflow); | |
| 1036 | } | |
| 1037 | ||
| 1038 | test __mulo_limb64 { | |
| 1039 | try test__mulo_limb64(u64, 3, 5, .{ 15, false }); | |
| 1040 | try test__mulo_limb64(u64, maxInt(u64), 2, .{ maxInt(u64) - 1, true }); | |
| 1041 | try test__mulo_limb64(u65, 1 << 32, 1 << 32, .{ 1 << 64, false }); | |
| 1042 | try test__mulo_limb64(u65, 1 << 64, 2, .{ 0, true }); | |
| 1043 | try test__mulo_limb64(u128, 1 << 80, 1 << 40, .{ 1 << 120, false }); | |
| 1044 | try test__mulo_limb64(u128, 1 << 100, 1 << 40, .{ 0, true }); | |
| 1045 | try test__mulo_limb64(u255, 7, 9, .{ 63, false }); | |
| 1046 | try test__mulo_limb64(u255, maxInt(u255), 2, .{ maxInt(u255) - 1, true }); | |
| 1047 | ||
| 1048 | try test__mulo_limb64(i64, -3, 2, .{ -6, false }); | |
| 1049 | try test__mulo_limb64(i64, maxInt(i64), 2, .{ -2, true }); | |
| 1050 | try test__mulo_limb64(i65, 1 << 63, 2, .{ minInt(i65), true }); | |
| 1051 | try test__mulo_limb64(i65, -1 << 32, 1 << 16, .{ -1 << 48, false }); | |
| 1052 | try test__mulo_limb64(i128, 1 << 100, 1 << 27, .{ minInt(i128), true }); | |
| 1053 | try test__mulo_limb64(i128, -1 << 80, 1 << 40, .{ -1 << 120, false }); | |
| 1054 | try test__mulo_limb64(i255, -3, 2, .{ -6, false }); | |
| 1055 | try test__mulo_limb64(i255, maxInt(i255), 2, .{ -2, true }); | |
| 1056 | ||
| 1057 | try test__mulo_limb64(u200, 0, maxInt(u200), .{ 0, false }); | |
| 1058 | try test__mulo_limb64(u200, 1, maxInt(u200), .{ maxInt(u200), false }); | |
| 1059 | try test__mulo_limb64(u200, 1 << 100, 1 << 99, .{ 1 << 199, false }); | |
| 1060 | try test__mulo_limb64(u200, 1 << 100, 1 << 100, .{ 0, true }); | |
| 1061 | try test__mulo_limb64(u200, maxInt(u200), maxInt(u200), .{ 1, true }); | |
| 1062 | ||
| 1063 | try test__mulo_limb64(i200, 0, -1, .{ 0, false }); | |
| 1064 | try test__mulo_limb64(i200, -1, -1, .{ 1, false }); | |
| 1065 | try test__mulo_limb64(i200, -1, minInt(i200), .{ minInt(i200), true }); | |
| 1066 | try test__mulo_limb64(i200, maxInt(i200), 2, .{ -2, true }); | |
| 1067 | try test__mulo_limb64(i200, 1 << 100, 1 << 98, .{ 1 << 198, false }); | |
| 1068 | try test__mulo_limb64(i200, 1 << 100, 1 << 99, .{ minInt(i200), true }); | |
| 1069 | try test__mulo_limb64(i200, maxInt(i200), maxInt(i200), .{ 1, true }); | |
| 1070 | try test__mulo_limb64(i200, minInt(i200), minInt(i200), .{ 0, true }); | |
| 1071 | ||
| 1072 | try test__mulo_limb64(u150, maxInt(u150), 2, .{ maxInt(u150) - 1, true }); | |
| 1073 | try test__mulo_limb64(i150, maxInt(i150), 2, .{ -2, true }); | |
| 1074 | } | |
| 1075 | ||
| 1076 | comptime { | |
| 1077 | symbol(&__abs_limb64, "__abs_limb64"); | |
| 1078 | } | |
| 1079 | ||
| 1080 | fn __abs_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, bits: u16) callconv(.c) void { | |
| 1081 | const limb_cnt = limbCount(bits); | |
| 1082 | const out = out_ptr[0..limb_cnt]; | |
| 1083 | const a = a_ptr[0..limb_cnt]; | |
| 1084 | ||
| 1085 | const ms = limbGet(a, limb_cnt - 1); | |
| 1086 | if ((ms >> 63) == 0) { | |
| 1087 | @memcpy(out, a); | |
| 1088 | return; | |
| 1089 | } | |
| 1090 | ||
| 1091 | var carry: u1 = 1; | |
| 1092 | var i: usize = 0; | |
| 1093 | while (i < limb_cnt) : (i += 1) { | |
| 1094 | const s = @addWithOverflow(~limbGet(a, i), carry); | |
| 1095 | limbSet(out, i, s[0]); | |
| 1096 | carry = s[1]; | |
| 1097 | } | |
| 1098 | } | |
| 1099 | ||
| 1100 | fn test__abs_limb64(comptime T: type, a: T, expected: @Int(.unsigned, @typeInfo(T).int.bits)) !void { | |
| 1101 | const int_info = @typeInfo(T).int; | |
| 1102 | comptime assert(int_info.signedness == .signed); | |
| 1103 | ||
| 1104 | var a_limbs = asLimbs(a); | |
| 1105 | var out: Limbs(@TypeOf(expected)) = undefined; | |
| 1106 | __abs_limb64(&out, &a_limbs, int_info.bits); | |
| 1107 | ||
| 1108 | const expected_limbs = asLimbs(expected); | |
| 1109 | try testing.expectEqual(expected_limbs, out); | |
| 1110 | } | |
| 1111 | ||
| 1112 | test __abs_limb64 { | |
| 1113 | try test__abs_limb64(i64, 0, 0); | |
| 1114 | try test__abs_limb64(i64, -1, 1); | |
| 1115 | try test__abs_limb64(i64, minInt(i64), 1 << 63); | |
| 1116 | try test__abs_limb64(i65, -1, 1); | |
| 1117 | try test__abs_limb64(i65, minInt(i65), 1 << 64); | |
| 1118 | try test__abs_limb64(i65, maxInt(i65), maxInt(i65)); | |
| 1119 | try test__abs_limb64(i128, -1 << 80, 1 << 80); | |
| 1120 | try test__abs_limb64(i128, 1 << 64, 1 << 64); | |
| 1121 | try test__abs_limb64(i200, -1 << 198, 1 << 198); | |
| 1122 | try test__abs_limb64(i255, -5, 5); | |
| 1123 | try test__abs_limb64(i255, minInt(i255), 1 << 254); | |
| 1124 | ||
| 1125 | try test__abs_limb64(i150, -40, 40); | |
| 266 | 1126 | } |
lib/compiler_rt/mulXi3.zig+1-1| ... | ... | @@ -63,7 +63,7 @@ fn DoubleInt(comptime T: type) type { |
| 63 | 63 | }; |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | fn muldXi(comptime T: type, a: T, b: T) DoubleInt(T) { | |
| 66 | pub fn muldXi(comptime T: type, a: T, b: T) DoubleInt(T) { | |
| 67 | 67 | const DT = DoubleInt(T); |
| 68 | 68 | const word_t = compiler_rt.HalveInt(DT, false); |
| 69 | 69 | const bits_in_word_2 = @sizeOf(T) * 8 / 2; |
lib/compiler_rt/udivmodei4.zig+28| ... | ... | @@ -13,6 +13,8 @@ const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable; // max s |
| 13 | 13 | comptime { |
| 14 | 14 | symbol(&__udivei4, "__udivei4"); |
| 15 | 15 | symbol(&__umodei4, "__umodei4"); |
| 16 | symbol(&__udivei5, "__udivei5"); | |
| 17 | symbol(&__umodei5, "__umodei5"); | |
| 16 | 18 | } |
| 17 | 19 | |
| 18 | 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 | 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 | 163 | test "__udivei4/__umodei4" { |
| 136 | 164 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 137 | 165 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
src/codegen/wasm/CodeGen.zig+472-221| ... | ... | @@ -3,6 +3,7 @@ const builtin = @import("builtin"); |
| 3 | 3 | const Allocator = std.mem.Allocator; |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const testing = std.testing; |
| 6 | const math = std.math; | |
| 6 | 7 | const mem = std.mem; |
| 7 | 8 | const log = std.log.scoped(.codegen); |
| 8 | 9 | |
| ... | ... | @@ -1324,6 +1325,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1324 | 1325 | .mod, |
| 1325 | 1326 | .max, |
| 1326 | 1327 | .min, |
| 1328 | .div_exact, | |
| 1327 | 1329 | .div_trunc, |
| 1328 | 1330 | .div_floor, |
| 1329 | 1331 | => |tag| { |
| ... | ... | @@ -1349,6 +1351,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1349 | 1351 | .mod => try cg.floatMod(float_ty, lhs, rhs), |
| 1350 | 1352 | .max => try cg.floatMax(float_ty, lhs, rhs), |
| 1351 | 1353 | .min => try cg.floatMin(float_ty, lhs, rhs), |
| 1354 | .div_exact => try cg.floatDiv(float_ty, lhs, rhs), | |
| 1352 | 1355 | .div_trunc => try cg.floatDivTrunc(float_ty, lhs, rhs), |
| 1353 | 1356 | .div_floor => try cg.floatDivFloor(float_ty, lhs, rhs), |
| 1354 | 1357 | else => unreachable, |
| ... | ... | @@ -1366,6 +1369,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1366 | 1369 | .mod => try cg.intMod(int_ty, lhs, rhs), |
| 1367 | 1370 | .max => try cg.intMax(int_ty, lhs, rhs), |
| 1368 | 1371 | .min => try cg.intMin(int_ty, lhs, rhs), |
| 1372 | .div_exact => try cg.intDiv(int_ty, lhs, rhs), | |
| 1369 | 1373 | .div_trunc => try cg.intDiv(int_ty, lhs, rhs), |
| 1370 | 1374 | .div_floor => try cg.intDivFloor(int_ty, lhs, rhs), |
| 1371 | 1375 | else => unreachable, |
| ... | ... | @@ -1389,19 +1393,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1389 | 1393 | const result = try cg.floatDiv(.fromType(cg, ty), lhs, rhs); |
| 1390 | 1394 | try cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 1391 | 1395 | }, |
| 1392 | .div_exact => { | |
| 1393 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 1394 | const lhs = try cg.resolveInst(bin_op.lhs); | |
| 1395 | const rhs = try cg.resolveInst(bin_op.rhs); | |
| 1396 | const ty = cg.typeOfIndex(inst); | |
| 1397 | ||
| 1398 | if (ty.zigTypeTag(zcu) == .vector) { | |
| 1399 | return cg.fail("TODO: implement AIR op: div_exact for vectors", .{}); | |
| 1400 | } | |
| 1401 | ||
| 1402 | const result = try cg.intDiv(.fromType(cg, ty), lhs, rhs); | |
| 1403 | try cg.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); | |
| 1404 | }, | |
| 1405 | 1396 | .abs => { |
| 1406 | 1397 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1407 | 1398 | const operand = try cg.resolveInst(ty_op.operand); |
| ... | ... | @@ -2363,6 +2354,15 @@ const IntType = struct { |
| 2363 | 2354 | } |
| 2364 | 2355 | }; |
| 2365 | 2356 | |
| 2357 | fn intBackingBits(cg: *CodeGen, bits: u16) u16 { | |
| 2358 | return switch (bits) { | |
| 2359 | 0 => unreachable, | |
| 2360 | 1...32 => 32, | |
| 2361 | 33...64 => 64, | |
| 2362 | else => std.zig.target.intByteSize(cg.target, bits) * 8, | |
| 2363 | }; | |
| 2364 | } | |
| 2365 | ||
| 2366 | 2366 | fn intAdd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue { |
| 2367 | 2367 | switch (ty.bits) { |
| 2368 | 2368 | 0 => unreachable, |
| ... | ... | @@ -2486,7 +2486,19 @@ fn intMul(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue |
| 2486 | 2486 | return .stack; |
| 2487 | 2487 | }, |
| 2488 | 2488 | 65...128 => return cg.callIntrinsic(.__multi3, &.{ .i128_type, .i128_type }, Type.i128, &.{ lhs, rhs }), |
| 2489 | else => return cg.fail("TODO: Support intMul for integer bitsize: {d}", .{ty.bits}), | |
| 2489 | else => { | |
| 2490 | const result = try cg.allocInt(ty); | |
| 2491 | ||
| 2492 | try cg.lowerToStack(result); | |
| 2493 | try cg.lowerToStack(lhs); | |
| 2494 | try cg.lowerToStack(rhs); | |
| 2495 | try cg.addImm32(@intFromBool(ty.is_signed)); | |
| 2496 | try cg.addImm32(ty.bits); | |
| 2497 | try cg.addCallIntrinsic(.__mulo_limb64); | |
| 2498 | try cg.addTag(.drop); | |
| 2499 | ||
| 2500 | return result; | |
| 2501 | }, | |
| 2490 | 2502 | } |
| 2491 | 2503 | } |
| 2492 | 2504 | |
| ... | ... | @@ -2512,7 +2524,28 @@ fn intDiv(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue |
| 2512 | 2524 | return cg.callIntrinsic(.__udivti3, &.{ .i128_type, .i128_type }, Type.i128, &.{ lhs, rhs }); |
| 2513 | 2525 | } |
| 2514 | 2526 | }, |
| 2515 | else => return cg.fail("TODO: Support intDiv for integer bitsize: {d}", .{ty.bits}), | |
| 2527 | else => { | |
| 2528 | const result = try cg.allocInt(ty); | |
| 2529 | const bits = cg.intBackingBits(ty.bits); | |
| 2530 | var tmp = try cg.allocInt(.{ .is_signed = false, .bits = bits * 2 }); | |
| 2531 | if (ty.is_signed) { | |
| 2532 | _ = try cg.callIntrinsic( | |
| 2533 | .__divei5, | |
| 2534 | &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, | |
| 2535 | .void, | |
| 2536 | &.{ result, lhs, rhs, tmp, .{ .imm32 = ty.bits } }, | |
| 2537 | ); | |
| 2538 | } else { | |
| 2539 | _ = try cg.callIntrinsic( | |
| 2540 | .__udivei5, | |
| 2541 | &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, | |
| 2542 | .void, | |
| 2543 | &.{ result, lhs, rhs, tmp, .{ .imm32 = ty.bits } }, | |
| 2544 | ); | |
| 2545 | } | |
| 2546 | tmp.free(cg); | |
| 2547 | return result; | |
| 2548 | }, | |
| 2516 | 2549 | } |
| 2517 | 2550 | } |
| 2518 | 2551 | |
| ... | ... | @@ -2564,7 +2597,22 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W |
| 2564 | 2597 | try cg.addTag(.i64_sub); |
| 2565 | 2598 | return .stack; |
| 2566 | 2599 | }, |
| 2567 | else => return cg.fail("TODO: Support intDivFloor for signed integer bitsize: {d}", .{ty.bits}), | |
| 2600 | else => { | |
| 2601 | const q = try cg.intDiv(ty, lhs, rhs); | |
| 2602 | ||
| 2603 | const zero = try cg.intZeroValue(ty); | |
| 2604 | ||
| 2605 | const r = try cg.intRem(ty, lhs, rhs); | |
| 2606 | _ = try cg.intCmp(ty, .neq, r, zero); | |
| 2607 | ||
| 2608 | const sign_xor = try cg.intXor(ty, lhs, rhs); | |
| 2609 | _ = try cg.intCmp(ty, .lt, sign_xor, zero); | |
| 2610 | var adjust = try (try cg.intAnd(.u32, .stack, .stack)).toLocal(cg, Type.u32); | |
| 2611 | ||
| 2612 | const adjust_bigint = try cg.intCast(ty, .u32, adjust); | |
| 2613 | adjust.free(cg); | |
| 2614 | return try cg.intSub(ty, q, adjust_bigint); | |
| 2615 | }, | |
| 2568 | 2616 | } |
| 2569 | 2617 | } |
| 2570 | 2618 | |
| ... | ... | @@ -2590,7 +2638,28 @@ fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue |
| 2590 | 2638 | return cg.callIntrinsic(.__umodti3, &.{ .i128_type, .i128_type }, Type.i128, &.{ lhs, rhs }); |
| 2591 | 2639 | } |
| 2592 | 2640 | }, |
| 2593 | else => return cg.fail("TODO: Support intRem for integer bitsize: {d}", .{ty.bits}), | |
| 2641 | else => { | |
| 2642 | const result = try cg.allocInt(ty); | |
| 2643 | const bits = cg.intBackingBits(ty.bits); | |
| 2644 | var tmp = try cg.allocInt(.{ .is_signed = false, .bits = bits * 2 }); | |
| 2645 | if (ty.is_signed) { | |
| 2646 | _ = try cg.callIntrinsic( | |
| 2647 | .__modei5, | |
| 2648 | &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, | |
| 2649 | .void, | |
| 2650 | &.{ result, lhs, rhs, tmp, .{ .imm32 = ty.bits } }, | |
| 2651 | ); | |
| 2652 | } else { | |
| 2653 | _ = try cg.callIntrinsic( | |
| 2654 | .__umodei5, | |
| 2655 | &.{ .usize_type, .usize_type, .usize_type, .usize_type, .usize_type }, | |
| 2656 | .void, | |
| 2657 | &.{ result, lhs, rhs, tmp, .{ .imm32 = ty.bits } }, | |
| 2658 | ); | |
| 2659 | } | |
| 2660 | tmp.free(cg); | |
| 2661 | return result; | |
| 2662 | }, | |
| 2594 | 2663 | } |
| 2595 | 2664 | } |
| 2596 | 2665 | |
| ... | ... | @@ -2635,7 +2704,17 @@ fn intAnd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue |
| 2635 | 2704 | |
| 2636 | 2705 | return result; |
| 2637 | 2706 | }, |
| 2638 | else => return cg.fail("TODO: Support intAnd for integer bitsize: {d}", .{ty.bits}), | |
| 2707 | else => { | |
| 2708 | const result = try cg.allocInt(ty); | |
| 2709 | ||
| 2710 | try cg.lowerToStack(result); | |
| 2711 | try cg.lowerToStack(lhs); | |
| 2712 | try cg.lowerToStack(rhs); | |
| 2713 | try cg.addImm32(ty.bits); | |
| 2714 | try cg.addCallIntrinsic(.__and_limb64); | |
| 2715 | ||
| 2716 | return result; | |
| 2717 | }, | |
| 2639 | 2718 | } |
| 2640 | 2719 | } |
| 2641 | 2720 | |
| ... | ... | @@ -2669,7 +2748,17 @@ fn intOr(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue |
| 2669 | 2748 | |
| 2670 | 2749 | return result; |
| 2671 | 2750 | }, |
| 2672 | else => return cg.fail("TODO: Support intOr for integer bitsize: {d}", .{ty.bits}), | |
| 2751 | else => { | |
| 2752 | const result = try cg.allocInt(ty); | |
| 2753 | ||
| 2754 | try cg.lowerToStack(result); | |
| 2755 | try cg.lowerToStack(lhs); | |
| 2756 | try cg.lowerToStack(rhs); | |
| 2757 | try cg.addImm32(ty.bits); | |
| 2758 | try cg.addCallIntrinsic(.__or_limb64); | |
| 2759 | ||
| 2760 | return result; | |
| 2761 | }, | |
| 2673 | 2762 | } |
| 2674 | 2763 | } |
| 2675 | 2764 | |
| ... | ... | @@ -2703,7 +2792,17 @@ fn intXor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue |
| 2703 | 2792 | |
| 2704 | 2793 | return result; |
| 2705 | 2794 | }, |
| 2706 | else => return cg.fail("TODO: Support intXor for integer bitsize: {d}", .{ty.bits}), | |
| 2795 | else => { | |
| 2796 | const result = try cg.allocInt(ty); | |
| 2797 | ||
| 2798 | try cg.lowerToStack(result); | |
| 2799 | try cg.lowerToStack(lhs); | |
| 2800 | try cg.lowerToStack(rhs); | |
| 2801 | try cg.addImm32(ty.bits); | |
| 2802 | try cg.addCallIntrinsic(.__xor_limb64); | |
| 2803 | ||
| 2804 | return result; | |
| 2805 | }, | |
| 2707 | 2806 | } |
| 2708 | 2807 | } |
| 2709 | 2808 | |
| ... | ... | @@ -2761,11 +2860,22 @@ fn intNot(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 2761 | 2860 | |
| 2762 | 2861 | return result; |
| 2763 | 2862 | }, |
| 2764 | else => return cg.fail("TODO: Support intNot for integer bitsize: {d}", .{ty.bits}), | |
| 2863 | else => { | |
| 2864 | const result = try cg.allocInt(ty); | |
| 2865 | ||
| 2866 | try cg.lowerToStack(result); | |
| 2867 | try cg.lowerToStack(operand); | |
| 2868 | try cg.addImm32(@intFromBool(ty.is_signed)); | |
| 2869 | try cg.addImm32(ty.bits); | |
| 2870 | try cg.addCallIntrinsic(.__not_limb64); | |
| 2871 | ||
| 2872 | return result; | |
| 2873 | }, | |
| 2765 | 2874 | } |
| 2766 | 2875 | } |
| 2767 | 2876 | |
| 2768 | 2877 | // rhs is a shift count, pointing to i32 value |
| 2878 | // does not perform wrapping, padding bits does not satisfy invariant | |
| 2769 | 2879 | fn intShl(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue { |
| 2770 | 2880 | switch (ty.bits) { |
| 2771 | 2881 | 0 => unreachable, |
| ... | ... | @@ -2783,7 +2893,19 @@ fn intShl(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue |
| 2783 | 2893 | return .stack; |
| 2784 | 2894 | }, |
| 2785 | 2895 | 65...128 => return cg.callIntrinsic(.__ashlti3, &.{ .i128_type, .i32_type }, Type.i128, &.{ lhs, rhs }), |
| 2786 | else => return cg.fail("TODO: Support intShl for integer bitsize: {d}", .{ty.bits}), | |
| 2896 | else => { | |
| 2897 | const result = try cg.allocInt(ty); | |
| 2898 | ||
| 2899 | try cg.lowerToStack(result); | |
| 2900 | try cg.lowerToStack(lhs); | |
| 2901 | try cg.lowerToStack(rhs); | |
| 2902 | try cg.addImm32(@intFromBool(ty.is_signed)); | |
| 2903 | try cg.addImm32(ty.bits); | |
| 2904 | try cg.addCallIntrinsic(.__shlo_limb64); | |
| 2905 | try cg.addTag(.drop); | |
| 2906 | ||
| 2907 | return result; | |
| 2908 | }, | |
| 2787 | 2909 | } |
| 2788 | 2910 | } |
| 2789 | 2911 | |
| ... | ... | @@ -2811,7 +2933,18 @@ fn intShr(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue |
| 2811 | 2933 | return cg.callIntrinsic(.__lshrti3, &.{ .i128_type, .i32_type }, Type.i128, &.{ lhs, rhs }); |
| 2812 | 2934 | } |
| 2813 | 2935 | }, |
| 2814 | else => return cg.fail("TODO: Support intShr for integer bitsize: {d}", .{ty.bits}), | |
| 2936 | else => { | |
| 2937 | const result = try cg.allocInt(ty); | |
| 2938 | ||
| 2939 | try cg.lowerToStack(result); | |
| 2940 | try cg.lowerToStack(lhs); | |
| 2941 | try cg.lowerToStack(rhs); | |
| 2942 | try cg.addImm32(@intFromBool(ty.is_signed)); | |
| 2943 | try cg.addImm32(ty.bits); | |
| 2944 | try cg.addCallIntrinsic(.__shr_limb64); | |
| 2945 | ||
| 2946 | return result; | |
| 2947 | }, | |
| 2815 | 2948 | } |
| 2816 | 2949 | } |
| 2817 | 2950 | |
| ... | ... | @@ -2871,7 +3004,16 @@ fn intAbs(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 2871 | 3004 | const b = try cg.intSub(u128_ty, a, mask); |
| 2872 | 3005 | return b; |
| 2873 | 3006 | }, |
| 2874 | else => return cg.fail("TODO: Support intAbs for integer bitsize: {d}", .{ty.bits}), | |
| 3007 | else => { | |
| 3008 | const result = try cg.allocInt(ty); | |
| 3009 | ||
| 3010 | try cg.lowerToStack(result); | |
| 3011 | try cg.lowerToStack(operand); | |
| 3012 | try cg.addImm32(ty.bits); | |
| 3013 | try cg.addCallIntrinsic(.__abs_limb64); | |
| 3014 | ||
| 3015 | return result; | |
| 3016 | }, | |
| 2875 | 3017 | } |
| 2876 | 3018 | } |
| 2877 | 3019 | |
| ... | ... | @@ -2938,7 +3080,13 @@ fn intClz(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 2938 | 3080 | try cg.addTag(.i32_wrap_i64); |
| 2939 | 3081 | return .stack; |
| 2940 | 3082 | }, |
| 2941 | else => return cg.fail("TODO: Support intClz for integer bitsize: {d}", .{ty.bits}), | |
| 3083 | else => { | |
| 3084 | try cg.lowerToStack(operand); | |
| 3085 | try cg.addImm32(ty.bits); | |
| 3086 | try cg.addCallIntrinsic(.__clz_limb64); | |
| 3087 | ||
| 3088 | return .stack; | |
| 3089 | }, | |
| 2942 | 3090 | } |
| 2943 | 3091 | } |
| 2944 | 3092 | |
| ... | ... | @@ -2984,7 +3132,13 @@ fn intCtz(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 2984 | 3132 | try cg.addTag(.i32_wrap_i64); |
| 2985 | 3133 | return .stack; |
| 2986 | 3134 | }, |
| 2987 | else => return cg.fail("TODO: Support intCtz for integer bitsize: {d}", .{ty.bits}), | |
| 3135 | else => { | |
| 3136 | try cg.lowerToStack(operand); | |
| 3137 | try cg.addImm32(ty.bits); | |
| 3138 | try cg.addCallIntrinsic(.__ctz_limb64); | |
| 3139 | ||
| 3140 | return .stack; | |
| 3141 | }, | |
| 2988 | 3142 | } |
| 2989 | 3143 | } |
| 2990 | 3144 | |
| ... | ... | @@ -3024,7 +3178,13 @@ fn intPopCount(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3024 | 3178 | try cg.addTag(.i32_wrap_i64); |
| 3025 | 3179 | return .stack; |
| 3026 | 3180 | }, |
| 3027 | else => return cg.fail("TODO: Support intPopCount for integer bitsize: {d}", .{ty.bits}), | |
| 3181 | else => { | |
| 3182 | try cg.lowerToStack(operand); | |
| 3183 | try cg.addImm32(ty.bits); | |
| 3184 | try cg.addCallIntrinsic(.__popcount_limb64); | |
| 3185 | ||
| 3186 | return .stack; | |
| 3187 | }, | |
| 3028 | 3188 | } |
| 3029 | 3189 | } |
| 3030 | 3190 | |
| ... | ... | @@ -3083,7 +3243,17 @@ fn intBitReverse(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3083 | 3243 | return tmp; |
| 3084 | 3244 | } |
| 3085 | 3245 | }, |
| 3086 | else => return cg.fail("TODO: Support intBitReverse for integer bitsize: {d}", .{ty.bits}), | |
| 3246 | else => { | |
| 3247 | const result = try cg.allocInt(ty); | |
| 3248 | ||
| 3249 | try cg.lowerToStack(result); | |
| 3250 | try cg.lowerToStack(operand); | |
| 3251 | try cg.addImm32(@intFromBool(ty.is_signed)); | |
| 3252 | try cg.addImm32(ty.bits); | |
| 3253 | try cg.addCallIntrinsic(.__bitreverse_limb64); | |
| 3254 | ||
| 3255 | return result; | |
| 3256 | }, | |
| 3087 | 3257 | } |
| 3088 | 3258 | } |
| 3089 | 3259 | |
| ... | ... | @@ -3111,35 +3281,48 @@ fn intByteSwap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3111 | 3281 | return cg.intShr(ty, intrin_ret, .{ .imm32 = 64 - ty.bits }); |
| 3112 | 3282 | }, |
| 3113 | 3283 | 65...128 => { |
| 3114 | const tmp = try cg.allocStack(Type.u128); | |
| 3284 | const result = try cg.allocStack(Type.u128); | |
| 3115 | 3285 | |
| 3116 | const low = try cg.load(operand, Type.u64, 0); | |
| 3117 | const high = try cg.load(operand, Type.u64, 8); | |
| 3286 | try cg.emitWValue(result); | |
| 3118 | 3287 | |
| 3288 | const low = try cg.load(operand, Type.u64, 0); | |
| 3119 | 3289 | const swap_low = try cg.callIntrinsic( |
| 3120 | 3290 | .__bswapdi2, |
| 3121 | 3291 | &.{.u64_type}, |
| 3122 | 3292 | Type.u64, |
| 3123 | 3293 | &.{low}, |
| 3124 | 3294 | ); |
| 3295 | try cg.store(.stack, swap_low, Type.u64, result.offset() + 8); | |
| 3296 | ||
| 3297 | try cg.emitWValue(result); | |
| 3298 | ||
| 3299 | const high = try cg.load(operand, Type.u64, 8); | |
| 3125 | 3300 | const swap_high = try cg.callIntrinsic( |
| 3126 | 3301 | .__bswapdi2, |
| 3127 | 3302 | &.{.u64_type}, |
| 3128 | 3303 | Type.u64, |
| 3129 | 3304 | &.{high}, |
| 3130 | 3305 | ); |
| 3131 | ||
| 3132 | try cg.store(tmp, swap_low, Type.u64, tmp.offset() + 8); | |
| 3133 | try cg.store(tmp, swap_high, Type.u64, tmp.offset()); | |
| 3306 | try cg.store(.stack, swap_high, Type.u64, result.offset()); | |
| 3134 | 3307 | |
| 3135 | 3308 | if (ty.bits < 128) { |
| 3136 | 3309 | const shift_ty: IntType = .{ .is_signed = ty.is_signed, .bits = 128 }; |
| 3137 | return cg.intShr(shift_ty, tmp, .{ .imm32 = 128 - ty.bits }); | |
| 3310 | return cg.intShr(shift_ty, result, .{ .imm32 = 128 - ty.bits }); | |
| 3138 | 3311 | } else { |
| 3139 | return tmp; | |
| 3312 | return result; | |
| 3140 | 3313 | } |
| 3141 | 3314 | }, |
| 3142 | else => return cg.fail("TODO: Support intByteSwap for integer bitsize: {d}", .{ty.bits}), | |
| 3315 | else => { | |
| 3316 | const result = try cg.allocInt(ty); | |
| 3317 | ||
| 3318 | try cg.lowerToStack(result); | |
| 3319 | try cg.lowerToStack(operand); | |
| 3320 | try cg.addImm32(@intFromBool(ty.is_signed)); | |
| 3321 | try cg.addImm32(ty.bits); | |
| 3322 | try cg.addCallIntrinsic(.__byteswap_limb64); | |
| 3323 | ||
| 3324 | return result; | |
| 3325 | }, | |
| 3143 | 3326 | } |
| 3144 | 3327 | } |
| 3145 | 3328 | |
| ... | ... | @@ -3197,7 +3380,49 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue { |
| 3197 | 3380 | return result; |
| 3198 | 3381 | }, |
| 3199 | 3382 | 128 => return operand, |
| 3200 | else => return cg.fail("TODO: Support intWrap for integer bitsize: {d}", .{ty.bits}), | |
| 3383 | else => { | |
| 3384 | const bits = cg.intBackingBits(ty.bits); | |
| 3385 | if (ty.bits == bits) return operand; | |
| 3386 | ||
| 3387 | const result = try cg.allocInt(ty); | |
| 3388 | ||
| 3389 | const used_len = (math.divCeil(u16, ty.bits, 64) catch unreachable) * 8; | |
| 3390 | ||
| 3391 | if (ty.bits % 64 != 0) { | |
| 3392 | try cg.memcpy(result, operand, .{ .imm32 = used_len - 8 }); | |
| 3393 | const pad = 64 - ty.bits % 64; | |
| 3394 | ||
| 3395 | try cg.emitWValue(result); | |
| 3396 | _ = try cg.load(operand, Type.u64, used_len - 8); | |
| 3397 | if (ty.is_signed) { | |
| 3398 | try cg.addImm64(pad); | |
| 3399 | try cg.addTag(.i64_shl); | |
| 3400 | try cg.addImm64(pad); | |
| 3401 | try cg.addTag(.i64_shr_s); | |
| 3402 | } else { | |
| 3403 | try cg.addImm64(~@as(u64, 0) >> @intCast(pad)); | |
| 3404 | try cg.addTag(.i64_and); | |
| 3405 | } | |
| 3406 | try cg.store(.stack, .stack, Type.u64, result.offset() + used_len - 8); | |
| 3407 | } else { | |
| 3408 | try cg.memcpy(result, operand, .{ .imm32 = used_len }); | |
| 3409 | } | |
| 3410 | ||
| 3411 | const full_len = @divExact(bits, 8); | |
| 3412 | if (used_len + 8 == full_len) { // last limb needs sign extended | |
| 3413 | try cg.emitWValue(result); | |
| 3414 | if (ty.is_signed) { | |
| 3415 | _ = try cg.load(result, Type.u64, used_len - 8); | |
| 3416 | try cg.addImm64(63); | |
| 3417 | try cg.addTag(.i64_shr_s); | |
| 3418 | } else { | |
| 3419 | try cg.addImm64(0); | |
| 3420 | } | |
| 3421 | try cg.store(.stack, .stack, Type.u64, result.offset() + used_len); | |
| 3422 | } | |
| 3423 | ||
| 3424 | return result; | |
| 3425 | }, | |
| 3201 | 3426 | } |
| 3202 | 3427 | } |
| 3203 | 3428 | |
| ... | ... | @@ -3214,8 +3439,8 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3214 | 3439 | } else { |
| 3215 | 3440 | return .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_ty.bits) }; |
| 3216 | 3441 | } |
| 3217 | } else { | |
| 3218 | const result = try cg.allocStack(Type.u128); | |
| 3442 | } else if (int_ty.bits <= 128) { | |
| 3443 | const result = try cg.allocInt(int_ty); | |
| 3219 | 3444 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, Type.u64, 0); |
| 3220 | 3445 | |
| 3221 | 3446 | if (int_ty.is_signed) { |
| ... | ... | @@ -3223,6 +3448,24 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3223 | 3448 | } else { |
| 3224 | 3449 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast(128 - int_ty.bits) }, Type.u64, 8); |
| 3225 | 3450 | } |
| 3451 | return result; | |
| 3452 | } else { | |
| 3453 | const result = try cg.allocInt(int_ty); | |
| 3454 | const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8); | |
| 3455 | const used_len = (math.divCeil(u16, int_ty.bits, 64) catch unreachable) * 8; | |
| 3456 | ||
| 3457 | try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0xFF }); | |
| 3458 | ||
| 3459 | if (int_ty.is_signed) { | |
| 3460 | try cg.store(result, .{ .imm64 = (~@as(u64, 0) >> @intCast(used_len * 8 - int_ty.bits)) >> 1 }, Type.u64, used_len - 8); | |
| 3461 | } else { | |
| 3462 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast(used_len * 8 - int_ty.bits) }, Type.u64, used_len - 8); | |
| 3463 | } | |
| 3464 | ||
| 3465 | if (used_len + 8 == full_len) { | |
| 3466 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, full_len - 8); | |
| 3467 | } | |
| 3468 | ||
| 3226 | 3469 | return result; |
| 3227 | 3470 | } |
| 3228 | 3471 | } |
| ... | ... | @@ -3235,10 +3478,23 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3235 | 3478 | return .{ .imm32 = ~@as(u32, 0) << @intCast(int_ty.bits - 1) }; |
| 3236 | 3479 | } else if (int_ty.bits <= 64) { |
| 3237 | 3480 | return .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 1) }; |
| 3238 | } else { | |
| 3239 | const result = try cg.allocStack(Type.u128); | |
| 3481 | } else if (int_ty.bits <= 128) { | |
| 3482 | const result = try cg.allocInt(int_ty); | |
| 3240 | 3483 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0); |
| 3241 | 3484 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 65) }, Type.u64, 8); |
| 3485 | return result; | |
| 3486 | } else { | |
| 3487 | const result = try cg.allocInt(int_ty); | |
| 3488 | const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8); | |
| 3489 | const used_len = (math.divCeil(u16, int_ty.bits, 64) catch unreachable) * 8; | |
| 3490 | ||
| 3491 | try cg.memset(Type.u8, result, .{ .imm32 = used_len - 8 }, .{ .imm32 = 0 }); | |
| 3492 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - (used_len - 8) * 8 - 1) }, Type.u64, used_len - 8); | |
| 3493 | ||
| 3494 | if (used_len + 8 == full_len) { | |
| 3495 | try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, Type.u64, full_len - 8); | |
| 3496 | } | |
| 3497 | ||
| 3242 | 3498 | return result; |
| 3243 | 3499 | } |
| 3244 | 3500 | } |
| ... | ... | @@ -3256,20 +3512,20 @@ fn intAddSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError |
| 3256 | 3512 | defer rhs_is_neg.free(cg); |
| 3257 | 3513 | const min_val = try cg.intMinValue(int_ty); |
| 3258 | 3514 | |
| 3259 | try cg.emitWValue(min_val); | |
| 3260 | try cg.emitWValue(max_val); | |
| 3515 | try cg.lowerToStack(min_val); | |
| 3516 | try cg.lowerToStack(max_val); | |
| 3261 | 3517 | try cg.emitWValue(rhs_is_neg); |
| 3262 | 3518 | try cg.addTag(.select); |
| 3263 | 3519 | |
| 3264 | try cg.emitWValue(op_val); | |
| 3520 | try cg.lowerToStack(op_val); | |
| 3265 | 3521 | const overflow_cmp = try cg.intCmp(int_ty, .lt, op_val, lhs); |
| 3266 | 3522 | const is_overflow = try cg.intCmp(.u32, .neq, rhs_is_neg, overflow_cmp); |
| 3267 | 3523 | try cg.emitWValue(is_overflow); |
| 3268 | 3524 | try cg.addTag(.select); |
| 3269 | 3525 | return .stack; |
| 3270 | 3526 | } else { |
| 3271 | try cg.emitWValue(max_val); | |
| 3272 | try cg.emitWValue(op_val); | |
| 3527 | try cg.lowerToStack(max_val); | |
| 3528 | try cg.lowerToStack(op_val); | |
| 3273 | 3529 | |
| 3274 | 3530 | const is_overflow = try cg.intCmp(int_ty, .lt, op_val, lhs); |
| 3275 | 3531 | try cg.emitWValue(is_overflow); |
| ... | ... | @@ -3290,12 +3546,12 @@ fn intSubSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError |
| 3290 | 3546 | const max_val = try cg.intMaxValue(int_ty); |
| 3291 | 3547 | const min_val = try cg.intMinValue(int_ty); |
| 3292 | 3548 | |
| 3293 | try cg.emitWValue(max_val); | |
| 3294 | try cg.emitWValue(min_val); | |
| 3549 | try cg.lowerToStack(max_val); | |
| 3550 | try cg.lowerToStack(min_val); | |
| 3295 | 3551 | try cg.emitWValue(rhs_is_neg); |
| 3296 | 3552 | try cg.addTag(.select); |
| 3297 | 3553 | |
| 3298 | try cg.emitWValue(op_val); | |
| 3554 | try cg.lowerToStack(op_val); | |
| 3299 | 3555 | const overflow_cmp = try cg.intCmp(int_ty, .gt, op_val, lhs); |
| 3300 | 3556 | const is_overflow = try cg.intCmp(.u32, .neq, rhs_is_neg, overflow_cmp); |
| 3301 | 3557 | try cg.emitWValue(is_overflow); |
| ... | ... | @@ -3304,8 +3560,8 @@ fn intSubSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError |
| 3304 | 3560 | } else { |
| 3305 | 3561 | const zero = try cg.intZeroValue(int_ty); |
| 3306 | 3562 | |
| 3307 | try cg.emitWValue(zero); | |
| 3308 | try cg.emitWValue(op_val); | |
| 3563 | try cg.lowerToStack(zero); | |
| 3564 | try cg.lowerToStack(op_val); | |
| 3309 | 3565 | const is_overflow = try cg.intCmp(int_ty, .lt, lhs, rhs); |
| 3310 | 3566 | try cg.emitWValue(is_overflow); |
| 3311 | 3567 | try cg.addTag(.select); |
| ... | ... | @@ -3314,43 +3570,6 @@ fn intSubSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError |
| 3314 | 3570 | } |
| 3315 | 3571 | |
| 3316 | 3572 | fn intMulSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue { |
| 3317 | // Remove when > 128 int ops will be implemented in backend | |
| 3318 | if (int_ty.bits == 128) { | |
| 3319 | if (!int_ty.is_signed) { | |
| 3320 | return cg.fail("TODO: mul_sat for unsigned 128-bit integers", .{}); | |
| 3321 | } | |
| 3322 | ||
| 3323 | const overflow_ret = try cg.allocStack(Type.i32); | |
| 3324 | const ret = try cg.callIntrinsic( | |
| 3325 | .__muloti4, | |
| 3326 | &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type }, | |
| 3327 | Type.i128, | |
| 3328 | &.{ lhs, rhs, overflow_ret }, | |
| 3329 | ); | |
| 3330 | try cg.lowerToStack(ret); | |
| 3331 | ||
| 3332 | const xor = try cg.intXor(int_ty, lhs, rhs); | |
| 3333 | const sign_v = try cg.intShr(int_ty, xor, .{ .imm32 = 127 }); | |
| 3334 | ||
| 3335 | // xor ~@as(u127, 0) | |
| 3336 | try cg.emitWValue(sign_v); | |
| 3337 | const lsb = try cg.load(sign_v, Type.u64, 0); | |
| 3338 | _ = try cg.intXor(.u64, lsb, .{ .imm64 = ~@as(u64, 0) }); | |
| 3339 | try cg.store(.stack, .stack, Type.u64, sign_v.offset()); | |
| 3340 | ||
| 3341 | try cg.emitWValue(sign_v); | |
| 3342 | const msb = try cg.load(sign_v, Type.u64, 8); | |
| 3343 | _ = try cg.intXor(.u64, msb, .{ .imm64 = ~@as(u64, 0) >> 1 }); | |
| 3344 | try cg.store(.stack, .stack, Type.u64, sign_v.offset() + 8); | |
| 3345 | ||
| 3346 | try cg.lowerToStack(sign_v); | |
| 3347 | _ = try cg.load(overflow_ret, Type.i32, 0); | |
| 3348 | try cg.addTag(.i32_eqz); | |
| 3349 | try cg.addTag(.select); | |
| 3350 | ||
| 3351 | return .stack; | |
| 3352 | } | |
| 3353 | ||
| 3354 | 3573 | const ext_ty: IntType = .{ .is_signed = int_ty.is_signed, .bits = int_ty.bits * 2 }; |
| 3355 | 3574 | |
| 3356 | 3575 | const lhs_ext = try cg.intCast(ext_ty, int_ty, lhs); |
| ... | ... | @@ -3366,10 +3585,10 @@ fn intMulSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError |
| 3366 | 3585 | if (int_ty.is_signed) { |
| 3367 | 3586 | const min_val = try cg.intMinValue(int_ty); |
| 3368 | 3587 | |
| 3369 | try cg.emitWValue(min_val); | |
| 3588 | try cg.lowerToStack(min_val); | |
| 3370 | 3589 | |
| 3371 | try cg.emitWValue(max_val); | |
| 3372 | try cg.emitWValue(op_val); | |
| 3590 | try cg.lowerToStack(max_val); | |
| 3591 | try cg.lowerToStack(op_val); | |
| 3373 | 3592 | const max_ext = try cg.intCast(ext_ty, int_ty, max_val); |
| 3374 | 3593 | const ov_pos = try cg.intCmp(ext_ty, .lt, max_ext, mul_ext); |
| 3375 | 3594 | try cg.emitWValue(ov_pos); |
| ... | ... | @@ -3377,12 +3596,12 @@ fn intMulSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError |
| 3377 | 3596 | |
| 3378 | 3597 | const min_ext = try cg.intCast(ext_ty, int_ty, min_val); |
| 3379 | 3598 | const ov_neg = try cg.intCmp(ext_ty, .gt, min_ext, mul_ext); |
| 3380 | try cg.emitWValue(ov_neg); | |
| 3599 | try cg.lowerToStack(ov_neg); | |
| 3381 | 3600 | try cg.addTag(.select); |
| 3382 | 3601 | return .stack; |
| 3383 | 3602 | } else { |
| 3384 | try cg.emitWValue(max_val); | |
| 3385 | try cg.emitWValue(op_val); | |
| 3603 | try cg.lowerToStack(max_val); | |
| 3604 | try cg.lowerToStack(op_val); | |
| 3386 | 3605 | const max_ext = try cg.intCast(ext_ty, int_ty, max_val); |
| 3387 | 3606 | const is_overflow = try cg.intCmp(ext_ty, .lt, max_ext, mul_ext); |
| 3388 | 3607 | try cg.emitWValue(is_overflow); |
| ... | ... | @@ -3405,20 +3624,20 @@ fn intShlSat(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError |
| 3405 | 3624 | const zero = try cg.intZeroValue(int_ty); |
| 3406 | 3625 | const min_val = try cg.intMinValue(int_ty); |
| 3407 | 3626 | |
| 3408 | try cg.emitWValue(min_val); | |
| 3409 | try cg.emitWValue(max_val); | |
| 3627 | try cg.lowerToStack(min_val); | |
| 3628 | try cg.lowerToStack(max_val); | |
| 3410 | 3629 | const lhs_is_neg = try cg.intCmp(int_ty, .lt, lhs, zero); |
| 3411 | 3630 | try cg.emitWValue(lhs_is_neg); |
| 3412 | 3631 | try cg.addTag(.select); |
| 3413 | 3632 | |
| 3414 | try cg.emitWValue(op_val); | |
| 3633 | try cg.lowerToStack(op_val); | |
| 3415 | 3634 | const is_overflow = try cg.intCmp(int_ty, .neq, check_val, lhs); |
| 3416 | 3635 | try cg.emitWValue(is_overflow); |
| 3417 | 3636 | try cg.addTag(.select); |
| 3418 | 3637 | return .stack; |
| 3419 | 3638 | } else { |
| 3420 | try cg.emitWValue(max_val); | |
| 3421 | try cg.emitWValue(op_val); | |
| 3639 | try cg.lowerToStack(max_val); | |
| 3640 | try cg.lowerToStack(op_val); | |
| 3422 | 3641 | const is_overflow = try cg.intCmp(int_ty, .neq, check_val, lhs); |
| 3423 | 3642 | try cg.emitWValue(is_overflow); |
| 3424 | 3643 | try cg.addTag(.select); |
| ... | ... | @@ -3432,12 +3651,17 @@ fn intZeroValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue { |
| 3432 | 3651 | 1...32 => return .{ .imm32 = 0 }, |
| 3433 | 3652 | 33...64 => return .{ .imm64 = 0 }, |
| 3434 | 3653 | 65...128 => { |
| 3435 | const result = try cg.allocStack(Type.u128); | |
| 3654 | const result = try cg.allocInt(int_ty); | |
| 3436 | 3655 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0); |
| 3437 | 3656 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8); |
| 3438 | 3657 | return result; |
| 3439 | 3658 | }, |
| 3440 | else => return cg.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_ty.bits}), | |
| 3659 | else => { | |
| 3660 | const result = try cg.allocInt(int_ty); | |
| 3661 | const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8); | |
| 3662 | try cg.memset(Type.u8, result, .{ .imm32 = full_len }, .{ .imm32 = 0 }); | |
| 3663 | return result; | |
| 3664 | }, | |
| 3441 | 3665 | } |
| 3442 | 3666 | } |
| 3443 | 3667 | |
| ... | ... | @@ -3561,68 +3785,6 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner |
| 3561 | 3785 | _ = try cg.intCmp(new_ty, .neq, res_upcast, bin_op); |
| 3562 | 3786 | try cg.addLocal(.local_set, overflow_bit.local.value); |
| 3563 | 3787 | break :blk res_tmp; |
| 3564 | } else if (int_ty.bits == 128 and !int_ty.is_signed) blk: { | |
| 3565 | var lhs_lsb = try (try cg.load(lhs, Type.u64, 0)).toLocal(cg, Type.u64); | |
| 3566 | defer lhs_lsb.free(cg); | |
| 3567 | var lhs_msb = try (try cg.load(lhs, Type.u64, 8)).toLocal(cg, Type.u64); | |
| 3568 | defer lhs_msb.free(cg); | |
| 3569 | var rhs_lsb = try (try cg.load(rhs, Type.u64, 0)).toLocal(cg, Type.u64); | |
| 3570 | defer rhs_lsb.free(cg); | |
| 3571 | var rhs_msb = try (try cg.load(rhs, Type.u64, 8)).toLocal(cg, Type.u64); | |
| 3572 | defer rhs_msb.free(cg); | |
| 3573 | ||
| 3574 | const zero: WValue = .{ .imm64 = 0 }; | |
| 3575 | ||
| 3576 | const cross_1 = try cg.callIntrinsic( | |
| 3577 | .__multi3, | |
| 3578 | &[_]InternPool.Index{.i64_type} ** 4, | |
| 3579 | Type.i128, | |
| 3580 | &.{ lhs_msb, zero, rhs_lsb, zero }, | |
| 3581 | ); | |
| 3582 | const cross_2 = try cg.callIntrinsic( | |
| 3583 | .__multi3, | |
| 3584 | &[_]InternPool.Index{.i64_type} ** 4, | |
| 3585 | Type.i128, | |
| 3586 | &.{ rhs_msb, zero, lhs_lsb, zero }, | |
| 3587 | ); | |
| 3588 | const mul_lsb = try cg.callIntrinsic( | |
| 3589 | .__multi3, | |
| 3590 | &[_]InternPool.Index{.i64_type} ** 4, | |
| 3591 | Type.i128, | |
| 3592 | &.{ rhs_lsb, zero, lhs_lsb, zero }, | |
| 3593 | ); | |
| 3594 | ||
| 3595 | const rhs_msb_not_zero = try cg.intCmp(.u64, .neq, rhs_msb, zero); | |
| 3596 | const lhs_msb_not_zero = try cg.intCmp(.u64, .neq, lhs_msb, zero); | |
| 3597 | const both_msb_not_zero = try cg.intAnd(.u32, rhs_msb_not_zero, lhs_msb_not_zero); | |
| 3598 | ||
| 3599 | const cross_1_msb = try cg.load(cross_1, .u64, 8); | |
| 3600 | const cross_1_msb_not_zero = try cg.intCmp(.u64, .neq, cross_1_msb, zero); | |
| 3601 | const cond_1 = try cg.intOr(.u32, both_msb_not_zero, cross_1_msb_not_zero); | |
| 3602 | ||
| 3603 | const cross_2_msb = try cg.load(cross_2, Type.u64, 8); | |
| 3604 | const cross_2_msb_not_zero = try cg.intCmp(.u64, .neq, cross_2_msb, zero); | |
| 3605 | const cond_2 = try cg.intOr(.u32, cond_1, cross_2_msb_not_zero); | |
| 3606 | ||
| 3607 | const cross_1_lsb = try cg.load(cross_1, Type.u64, 0); | |
| 3608 | const cross_2_lsb = try cg.load(cross_2, Type.u64, 0); | |
| 3609 | const cross_add = try cg.intAdd(.u64, cross_1_lsb, cross_2_lsb); | |
| 3610 | ||
| 3611 | var mul_lsb_msb = try (try cg.load(mul_lsb, Type.u64, 8)).toLocal(cg, Type.u64); | |
| 3612 | defer mul_lsb_msb.free(cg); | |
| 3613 | var all_add = try (try cg.intAdd(.u64, cross_add, mul_lsb_msb)).toLocal(cg, Type.u64); | |
| 3614 | defer all_add.free(cg); | |
| 3615 | const add_overflow = try cg.intCmp(.u64, .lt, all_add, mul_lsb_msb); | |
| 3616 | ||
| 3617 | _ = try cg.intOr(.u32, cond_2, add_overflow); | |
| 3618 | try cg.addLocal(.local_set, overflow_bit.local.value); | |
| 3619 | ||
| 3620 | const tmp_result = try cg.allocStack(Type.u128); | |
| 3621 | try cg.emitWValue(tmp_result); | |
| 3622 | const mul_lsb_lsb = try cg.load(mul_lsb, Type.u64, 0); | |
| 3623 | try cg.store(.stack, mul_lsb_lsb, Type.u64, tmp_result.offset()); | |
| 3624 | try cg.store(tmp_result, all_add, Type.u64, 8); | |
| 3625 | break :blk tmp_result; | |
| 3626 | 3788 | } else if (int_ty.bits == 128 and int_ty.is_signed) blk: { |
| 3627 | 3789 | const overflow_ret = try cg.allocStack(Type.i32); |
| 3628 | 3790 | const res = try cg.callIntrinsic( |
| ... | ... | @@ -3634,44 +3796,53 @@ fn intMulOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) Inner |
| 3634 | 3796 | _ = try cg.load(overflow_ret, Type.i32, 0); |
| 3635 | 3797 | try cg.addLocal(.local_set, overflow_bit.local.value); |
| 3636 | 3798 | break :blk res; |
| 3637 | } else return cg.fail("TODO: intMulOverflow for bitsize {d}", .{int_ty.bits}); | |
| 3799 | } else { | |
| 3800 | const result = try cg.allocInt(int_ty); | |
| 3801 | ||
| 3802 | try cg.lowerToStack(result); | |
| 3803 | try cg.lowerToStack(lhs); | |
| 3804 | try cg.lowerToStack(rhs); | |
| 3805 | try cg.addImm32(@intFromBool(int_ty.is_signed)); | |
| 3806 | try cg.addImm32(int_ty.bits); | |
| 3807 | try cg.addCallIntrinsic(.__mulo_limb64); | |
| 3808 | ||
| 3809 | return .{ .result = result, .ov = .stack }; | |
| 3810 | }; | |
| 3638 | 3811 | |
| 3639 | 3812 | return .{ .result = result_val, .ov = .{ .local = overflow_bit.local } }; |
| 3640 | 3813 | } |
| 3641 | 3814 | |
| 3642 | fn intShlOverflow(cg: *CodeGen, int_ty: IntType, lhs: WValue, rhs: WValue) InnerError!OverflowResult { | |
| 3643 | switch (int_ty.bits) { | |
| 3815 | fn intShlOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!OverflowResult { | |
| 3816 | switch (ty.bits) { | |
| 3644 | 3817 | 0 => unreachable, |
| 3645 | 3818 | 1...128 => { |
| 3646 | const raw_shl = try cg.intShl(int_ty, lhs, rhs); | |
| 3647 | const wrapped_shl = try cg.intWrap(int_ty, raw_shl); | |
| 3648 | const shl_tmp = try cg.toLocalInt(wrapped_shl, int_ty); | |
| 3819 | const raw_shl = try cg.intShl(ty, lhs, rhs); | |
| 3820 | const wrapped_shl = try cg.intWrap(ty, raw_shl); | |
| 3821 | const shl_tmp = try cg.toLocalInt(wrapped_shl, ty); | |
| 3649 | 3822 | |
| 3650 | const shr = try cg.intShr(int_ty, shl_tmp, rhs); | |
| 3651 | const overflow_bit = try cg.intCmp(int_ty, .neq, shr, lhs); | |
| 3823 | const shr = try cg.intShr(ty, shl_tmp, rhs); | |
| 3824 | const overflow_bit = try cg.intCmp(ty, .neq, shr, lhs); | |
| 3652 | 3825 | |
| 3653 | 3826 | return .{ .result = shl_tmp, .ov = overflow_bit }; |
| 3654 | 3827 | }, |
| 3655 | else => return cg.fail("TODO: Support intShlOverflow for integer bitsize: {d}", .{int_ty.bits}), | |
| 3828 | else => { | |
| 3829 | const result = try cg.allocInt(ty); | |
| 3830 | ||
| 3831 | try cg.lowerToStack(result); | |
| 3832 | try cg.lowerToStack(lhs); | |
| 3833 | try cg.lowerToStack(rhs); | |
| 3834 | try cg.addImm32(@intFromBool(ty.is_signed)); | |
| 3835 | try cg.addImm32(ty.bits); | |
| 3836 | try cg.addCallIntrinsic(.__shlo_limb64); | |
| 3837 | ||
| 3838 | return .{ .result = result, .ov = .stack }; | |
| 3839 | }, | |
| 3656 | 3840 | } |
| 3657 | 3841 | } |
| 3658 | 3842 | |
| 3659 | 3843 | fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue { |
| 3660 | const src_bits: u16 = switch (src_ty.bits) { | |
| 3661 | 0 => unreachable, | |
| 3662 | 1...32 => 32, | |
| 3663 | 33...64 => 64, | |
| 3664 | 65...128 => 128, | |
| 3665 | else => unreachable, | |
| 3666 | }; | |
| 3667 | ||
| 3668 | const dest_bits: u16 = switch (dest_ty.bits) { | |
| 3669 | 0 => unreachable, | |
| 3670 | 1...32 => 32, | |
| 3671 | 33...64 => 64, | |
| 3672 | 65...128 => 128, | |
| 3673 | else => unreachable, | |
| 3674 | }; | |
| 3844 | const src_bits: u16 = cg.intBackingBits(src_ty.bits); | |
| 3845 | const dest_bits: u16 = cg.intBackingBits(dest_ty.bits); | |
| 3675 | 3846 | |
| 3676 | 3847 | if (src_bits == dest_bits) { |
| 3677 | 3848 | return operand; |
| ... | ... | @@ -3683,34 +3854,78 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn |
| 3683 | 3854 | return .stack; |
| 3684 | 3855 | } else if (src_bits == 32 and dest_bits == 64) { |
| 3685 | 3856 | try cg.emitWValue(operand); |
| 3686 | try cg.addTag(if (dest_ty.is_signed) .i64_extend_i32_s else .i64_extend_i32_u); | |
| 3857 | try cg.addTag(if (src_ty.is_signed) .i64_extend_i32_s else .i64_extend_i32_u); | |
| 3687 | 3858 | return .stack; |
| 3688 | } else if (dest_bits == 128) { | |
| 3689 | const stack_ptr = try cg.allocStack(Type.u128); | |
| 3690 | try cg.emitWValue(stack_ptr); | |
| 3859 | } else if (dest_bits >= 128) { | |
| 3860 | const result = try cg.allocInt(dest_ty); | |
| 3691 | 3861 | |
| 3692 | const lhs = if (src_bits == 32) blk: { | |
| 3693 | const sign_ty: IntType = .{ .is_signed = dest_ty.is_signed, .bits = 64 }; | |
| 3694 | break :blk try (try cg.intCast(sign_ty, src_ty, operand)).toLocal(cg, Type.u64); | |
| 3695 | } else operand; | |
| 3862 | const dest_len = dest_bits / 8; | |
| 3696 | 3863 | |
| 3697 | try cg.store(.stack, lhs, Type.u64, stack_ptr.offset()); | |
| 3698 | ||
| 3699 | if (dest_ty.is_signed) { | |
| 3700 | try cg.emitWValue(stack_ptr); | |
| 3701 | const shr = try cg.intShr(IntType.i64, lhs, .{ .imm32 = 63 }); | |
| 3702 | try cg.store(.stack, shr, Type.u64, 8 + stack_ptr.offset()); | |
| 3864 | if (dest_bits <= src_bits) { | |
| 3865 | assert(src_bits >= 128); | |
| 3866 | try cg.memcpy(result, operand, .{ .imm32 = dest_len }); | |
| 3703 | 3867 | } else { |
| 3704 | try cg.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); | |
| 3705 | } | |
| 3868 | var src_len: u32 = undefined; | |
| 3869 | if (src_bits == 32) { | |
| 3870 | try cg.emitWValue(result); | |
| 3871 | try cg.emitWValue(operand); | |
| 3872 | try cg.addTag(if (src_ty.is_signed) .i64_extend_i32_s else .i64_extend_i32_u); | |
| 3873 | try cg.store(.stack, .stack, Type.u64, result.offset()); | |
| 3874 | src_len = 8; | |
| 3875 | } else if (src_bits == 64) { | |
| 3876 | try cg.emitWValue(result); | |
| 3877 | try cg.emitWValue(operand); | |
| 3878 | try cg.store(.stack, .stack, Type.u64, result.offset()); | |
| 3879 | src_len = 8; | |
| 3880 | } else { | |
| 3881 | src_len = src_bits / 8; | |
| 3882 | try cg.memcpy(result, operand, .{ .imm32 = src_len }); | |
| 3883 | } | |
| 3706 | 3884 | |
| 3707 | if (src_bits == 32) { | |
| 3708 | var tmp_lhs = lhs; | |
| 3709 | tmp_lhs.free(cg); | |
| 3885 | if (dest_bits == 128) { | |
| 3886 | if (src_ty.is_signed) { | |
| 3887 | try cg.emitWValue(result); | |
| 3888 | if (src_bits == 32) { | |
| 3889 | try cg.emitWValue(operand); | |
| 3890 | try cg.addTag(if (dest_ty.is_signed) .i64_extend_i32_s else .i64_extend_i32_u); | |
| 3891 | } else if (src_bits == 64) { | |
| 3892 | try cg.emitWValue(operand); | |
| 3893 | } else unreachable; | |
| 3894 | const shr = try cg.intShr(IntType.i64, .stack, .{ .imm32 = 63 }); | |
| 3895 | try cg.store(.stack, shr, Type.u64, 8 + result.offset()); | |
| 3896 | } else { | |
| 3897 | try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8); | |
| 3898 | } | |
| 3899 | } else { | |
| 3900 | var pad = result; | |
| 3901 | pad.stack_offset.value += src_len; | |
| 3902 | const memset_len = dest_len - src_len; | |
| 3903 | if (src_ty.is_signed) { | |
| 3904 | if (src_bits == 32) { | |
| 3905 | try cg.emitWValue(operand); | |
| 3906 | _ = try cg.intShr(IntType.i32, .stack, .{ .imm32 = 31 }); | |
| 3907 | } else if (src_bits == 64) { | |
| 3908 | try cg.emitWValue(operand); | |
| 3909 | _ = try cg.intShr(IntType.i64, .stack, .{ .imm32 = 63 }); | |
| 3910 | try cg.addTag(.i32_wrap_i64); | |
| 3911 | } else { | |
| 3912 | _ = try cg.load(operand, Type.u64, src_len - 8); | |
| 3913 | _ = try cg.intShr(IntType.i64, .stack, .{ .imm32 = 63 }); | |
| 3914 | try cg.addTag(.i32_wrap_i64); | |
| 3915 | } | |
| 3916 | var sign_byte = try @as(WValue, .stack).toLocal(cg, Type.u32); | |
| 3917 | try cg.memset(Type.u8, pad, .{ .imm32 = memset_len }, sign_byte); | |
| 3918 | sign_byte.free(cg); | |
| 3919 | } else { | |
| 3920 | try cg.memset(Type.u8, pad, .{ .imm32 = memset_len }, .{ .imm32 = 0 }); | |
| 3921 | } | |
| 3922 | } | |
| 3710 | 3923 | } |
| 3711 | 3924 | |
| 3712 | return stack_ptr; | |
| 3925 | return result; | |
| 3713 | 3926 | } else { |
| 3927 | assert(dest_bits <= 64); | |
| 3928 | assert(src_bits >= 128); | |
| 3714 | 3929 | const load_ty = if (dest_bits == 32) Type.u32 else Type.u64; |
| 3715 | 3930 | return cg.load(operand, load_ty, 0); |
| 3716 | 3931 | } |
| ... | ... | @@ -3719,13 +3934,7 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn |
| 3719 | 3934 | fn intTrunc(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue { |
| 3720 | 3935 | var result = try cg.intCast(dest_ty, src_ty, operand); |
| 3721 | 3936 | |
| 3722 | const dest_wasm_bits: u16 = switch (dest_ty.bits) { | |
| 3723 | 0 => unreachable, | |
| 3724 | 1...32 => 32, | |
| 3725 | 33...64 => 64, | |
| 3726 | 65...128 => 128, | |
| 3727 | else => return cg.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{dest_ty.bits}), | |
| 3728 | }; | |
| 3937 | const dest_wasm_bits = cg.intBackingBits(dest_ty.bits); | |
| 3729 | 3938 | |
| 3730 | 3939 | if (dest_wasm_bits != dest_ty.bits) { |
| 3731 | 3940 | result = try cg.intWrap(dest_ty, result); |
| ... | ... | @@ -4275,7 +4484,34 @@ fn intFromFloat(cg: *CodeGen, dest_ty: IntType, src_ty: FloatType, operand: WVal |
| 4275 | 4484 | return cg.callIntrinsic(intrinsic, &.{.f128_type}, Type.u128, &.{operand}); |
| 4276 | 4485 | }, |
| 4277 | 4486 | }, |
| 4278 | else => return cg.fail("TODO: Support intFromFloat for integer bitsize: {d}", .{dest_ty.bits}), | |
| 4487 | else => { | |
| 4488 | const result = try cg.allocInt(dest_ty); | |
| 4489 | ||
| 4490 | switch (src_ty) { | |
| 4491 | .f16 => { | |
| 4492 | const intrinsic: Mir.Intrinsic = if (dest_ty.is_signed) .__fixhfei else .__fixunshfei; | |
| 4493 | _ = try cg.callIntrinsic(intrinsic, &.{ .usize_type, .usize_type, .f16_type }, .void, &.{ result, .{ .imm32 = dest_ty.bits }, operand }); | |
| 4494 | }, | |
| 4495 | .f32 => { | |
| 4496 | const intrinsic: Mir.Intrinsic = if (dest_ty.is_signed) .__fixsfei else .__fixunssfei; | |
| 4497 | _ = try cg.callIntrinsic(intrinsic, &.{ .usize_type, .usize_type, .f32_type }, .void, &.{ result, .{ .imm32 = dest_ty.bits }, operand }); | |
| 4498 | }, | |
| 4499 | .f64 => { | |
| 4500 | const intrinsic: Mir.Intrinsic = if (dest_ty.is_signed) .__fixdfei else .__fixunsdfei; | |
| 4501 | _ = try cg.callIntrinsic(intrinsic, &.{ .usize_type, .usize_type, .f64_type }, .void, &.{ result, .{ .imm32 = dest_ty.bits }, operand }); | |
| 4502 | }, | |
| 4503 | .f80 => { | |
| 4504 | const intrinsic: Mir.Intrinsic = if (dest_ty.is_signed) .__fixxfei else .__fixunsxfei; | |
| 4505 | _ = try cg.callIntrinsic(intrinsic, &.{ .usize_type, .usize_type, .f80_type }, .void, &.{ result, .{ .imm32 = dest_ty.bits }, operand }); | |
| 4506 | }, | |
| 4507 | .f128 => { | |
| 4508 | const intrinsic: Mir.Intrinsic = if (dest_ty.is_signed) .__fixtfei else .__fixunstfei; | |
| 4509 | _ = try cg.callIntrinsic(intrinsic, &.{ .usize_type, .usize_type, .f128_type }, .void, &.{ result, .{ .imm32 = dest_ty.bits }, operand }); | |
| 4510 | }, | |
| 4511 | } | |
| 4512 | ||
| 4513 | return result; | |
| 4514 | }, | |
| 4279 | 4515 | } |
| 4280 | 4516 | } |
| 4281 | 4517 | |
| ... | ... | @@ -4295,7 +4531,10 @@ fn floatFromInt(cg: *CodeGen, dest_ty: FloatType, src_ty: IntType, operand: WVal |
| 4295 | 4531 | const intrinsic: Mir.Intrinsic = if (src_ty.is_signed) .__floattihf else .__floatuntihf; |
| 4296 | 4532 | return cg.callIntrinsic(intrinsic, &.{.i128_type}, Type.f16, &.{operand}); |
| 4297 | 4533 | }, |
| 4298 | else => return cg.fail("TODO: Support floatFromInt for {d}-bit int to 16-bit float", .{src_ty.bits}), | |
| 4534 | else => { | |
| 4535 | const intrinsic: Mir.Intrinsic = if (src_ty.is_signed) .__floateihf else .__floatuneihf; | |
| 4536 | return cg.callIntrinsic(intrinsic, &.{ .usize_type, .usize_type }, Type.f16, &.{ operand, .{ .imm32 = src_ty.bits } }); | |
| 4537 | }, | |
| 4299 | 4538 | }, |
| 4300 | 4539 | .f32 => switch (src_ty.bits) { |
| 4301 | 4540 | 0 => unreachable, |
| ... | ... | @@ -4313,7 +4552,10 @@ fn floatFromInt(cg: *CodeGen, dest_ty: FloatType, src_ty: IntType, operand: WVal |
| 4313 | 4552 | const intrinsic: Mir.Intrinsic = if (src_ty.is_signed) .__floattisf else .__floatuntisf; |
| 4314 | 4553 | return cg.callIntrinsic(intrinsic, &.{.i128_type}, Type.f32, &.{operand}); |
| 4315 | 4554 | }, |
| 4316 | else => return cg.fail("TODO: Support floatFromInt for {d}-bit int to 32-bit float", .{src_ty.bits}), | |
| 4555 | else => { | |
| 4556 | const intrinsic: Mir.Intrinsic = if (src_ty.is_signed) .__floateisf else .__floatuneisf; | |
| 4557 | return cg.callIntrinsic(intrinsic, &.{ .usize_type, .usize_type }, Type.f32, &.{ operand, .{ .imm32 = src_ty.bits } }); | |
| 4558 | }, | |
| 4317 | 4559 | }, |
| 4318 | 4560 | .f64 => switch (src_ty.bits) { |
| 4319 | 4561 | 0 => unreachable, |
| ... | ... | @@ -4331,7 +4573,10 @@ fn floatFromInt(cg: *CodeGen, dest_ty: FloatType, src_ty: IntType, operand: WVal |
| 4331 | 4573 | const intrinsic: Mir.Intrinsic = if (src_ty.is_signed) .__floattidf else .__floatuntidf; |
| 4332 | 4574 | return cg.callIntrinsic(intrinsic, &.{.i128_type}, Type.f64, &.{operand}); |
| 4333 | 4575 | }, |
| 4334 | else => return cg.fail("TODO: Support floatFromInt for {d}-bit int to 64-bit float", .{src_ty.bits}), | |
| 4576 | else => { | |
| 4577 | const intrinsic: Mir.Intrinsic = if (src_ty.is_signed) .__floateidf else .__floatuneidf; | |
| 4578 | return cg.callIntrinsic(intrinsic, &.{ .usize_type, .usize_type }, Type.f64, &.{ operand, .{ .imm32 = src_ty.bits } }); | |
| 4579 | }, | |
| 4335 | 4580 | }, |
| 4336 | 4581 | .f80 => switch (src_ty.bits) { |
| 4337 | 4582 | 0 => unreachable, |
| ... | ... | @@ -4347,7 +4592,10 @@ fn floatFromInt(cg: *CodeGen, dest_ty: FloatType, src_ty: IntType, operand: WVal |
| 4347 | 4592 | const intrinsic: Mir.Intrinsic = if (src_ty.is_signed) .__floattixf else .__floatuntixf; |
| 4348 | 4593 | return cg.callIntrinsic(intrinsic, &.{.i128_type}, Type.f80, &.{operand}); |
| 4349 | 4594 | }, |
| 4350 | else => return cg.fail("TODO: Support floatFromInt for {d}-bit int to 80-bit float", .{src_ty.bits}), | |
| 4595 | else => { | |
| 4596 | const intrinsic: Mir.Intrinsic = if (src_ty.is_signed) .__floateixf else .__floatuneixf; | |
| 4597 | return cg.callIntrinsic(intrinsic, &.{ .usize_type, .usize_type }, Type.f80, &.{ operand, .{ .imm32 = src_ty.bits } }); | |
| 4598 | }, | |
| 4351 | 4599 | }, |
| 4352 | 4600 | .f128 => switch (src_ty.bits) { |
| 4353 | 4601 | 0 => unreachable, |
| ... | ... | @@ -4363,7 +4611,10 @@ fn floatFromInt(cg: *CodeGen, dest_ty: FloatType, src_ty: IntType, operand: WVal |
| 4363 | 4611 | const intrinsic: Mir.Intrinsic = if (src_ty.is_signed) .__floattitf else .__floatuntitf; |
| 4364 | 4612 | return cg.callIntrinsic(intrinsic, &.{.i128_type}, Type.f128, &.{operand}); |
| 4365 | 4613 | }, |
| 4366 | else => return cg.fail("TODO: Support floatFromInt for {d}-bit int to 128-bit float", .{src_ty.bits}), | |
| 4614 | else => { | |
| 4615 | const intrinsic: Mir.Intrinsic = if (src_ty.is_signed) .__floateitf else .__floatuneitf; | |
| 4616 | return cg.callIntrinsic(intrinsic, &.{ .usize_type, .usize_type }, Type.f128, &.{ operand, .{ .imm32 = src_ty.bits } }); | |
| 4617 | }, | |
| 4367 | 4618 | }, |
| 4368 | 4619 | } |
| 4369 | 4620 | } |
src/codegen/wasm/Mir.zig+37| ... | ... | @@ -825,6 +825,7 @@ pub const Intrinsic = enum(u32) { |
| 825 | 825 | __ceilx, |
| 826 | 826 | __cosh, |
| 827 | 827 | __cosx, |
| 828 | __divei5, | |
| 828 | 829 | __divhf3, |
| 829 | 830 | __divtf3, |
| 830 | 831 | __divti3, |
| ... | ... | @@ -846,33 +847,43 @@ pub const Intrinsic = enum(u32) { |
| 846 | 847 | __fabsh, |
| 847 | 848 | __fabsx, |
| 848 | 849 | __fixdfdi, |
| 850 | __fixdfei, | |
| 849 | 851 | __fixdfsi, |
| 850 | 852 | __fixdfti, |
| 851 | 853 | __fixhfdi, |
| 854 | __fixhfei, | |
| 852 | 855 | __fixhfsi, |
| 853 | 856 | __fixhfti, |
| 854 | 857 | __fixsfdi, |
| 858 | __fixsfei, | |
| 855 | 859 | __fixsfsi, |
| 856 | 860 | __fixsfti, |
| 857 | 861 | __fixtfdi, |
| 862 | __fixtfei, | |
| 858 | 863 | __fixtfsi, |
| 859 | 864 | __fixtfti, |
| 860 | 865 | __fixunsdfdi, |
| 866 | __fixunsdfei, | |
| 861 | 867 | __fixunsdfsi, |
| 862 | 868 | __fixunsdfti, |
| 863 | 869 | __fixunshfdi, |
| 870 | __fixunshfei, | |
| 864 | 871 | __fixunshfsi, |
| 865 | 872 | __fixunshfti, |
| 866 | 873 | __fixunssfdi, |
| 874 | __fixunssfei, | |
| 867 | 875 | __fixunssfsi, |
| 868 | 876 | __fixunssfti, |
| 869 | 877 | __fixunstfdi, |
| 878 | __fixunstfei, | |
| 870 | 879 | __fixunstfsi, |
| 871 | 880 | __fixunstfti, |
| 872 | 881 | __fixunsxfdi, |
| 882 | __fixunsxfei, | |
| 873 | 883 | __fixunsxfsi, |
| 874 | 884 | __fixunsxfti, |
| 875 | 885 | __fixxfdi, |
| 886 | __fixxfei, | |
| 876 | 887 | __fixxfsi, |
| 877 | 888 | __fixxfti, |
| 878 | 889 | __floatdidf, |
| ... | ... | @@ -880,6 +891,11 @@ pub const Intrinsic = enum(u32) { |
| 880 | 891 | __floatdisf, |
| 881 | 892 | __floatditf, |
| 882 | 893 | __floatdixf, |
| 894 | __floateidf, | |
| 895 | __floateihf, | |
| 896 | __floateisf, | |
| 897 | __floateitf, | |
| 898 | __floateixf, | |
| 883 | 899 | __floatsidf, |
| 884 | 900 | __floatsihf, |
| 885 | 901 | __floatsisf, |
| ... | ... | @@ -895,6 +911,11 @@ pub const Intrinsic = enum(u32) { |
| 895 | 911 | __floatundisf, |
| 896 | 912 | __floatunditf, |
| 897 | 913 | __floatundixf, |
| 914 | __floatuneidf, | |
| 915 | __floatuneihf, | |
| 916 | __floatuneisf, | |
| 917 | __floatuneitf, | |
| 918 | __floatuneixf, | |
| 898 | 919 | __floatunsidf, |
| 899 | 920 | __floatunsihf, |
| 900 | 921 | __floatunsisf, |
| ... | ... | @@ -930,6 +951,7 @@ pub const Intrinsic = enum(u32) { |
| 930 | 951 | __lshrti3, |
| 931 | 952 | __lttf2, |
| 932 | 953 | __ltxf2, |
| 954 | __modei5, | |
| 933 | 955 | __modti3, |
| 934 | 956 | __mulhf3, |
| 935 | 957 | __mulodi4, |
| ... | ... | @@ -960,7 +982,9 @@ pub const Intrinsic = enum(u32) { |
| 960 | 982 | __truncxfdf2, |
| 961 | 983 | __truncxfhf2, |
| 962 | 984 | __truncxfsf2, |
| 985 | __udivei5, | |
| 963 | 986 | __udivti3, |
| 987 | __umodei5, | |
| 964 | 988 | __umodti3, |
| 965 | 989 | ceilq, |
| 966 | 990 | cos, |
| ... | ... | @@ -1007,4 +1031,17 @@ pub const Intrinsic = enum(u32) { |
| 1007 | 1031 | __addo_limb64, |
| 1008 | 1032 | __subo_limb64, |
| 1009 | 1033 | __cmp_limb64, |
| 1034 | __and_limb64, | |
| 1035 | __or_limb64, | |
| 1036 | __xor_limb64, | |
| 1037 | __not_limb64, | |
| 1038 | __shlo_limb64, | |
| 1039 | __shr_limb64, | |
| 1040 | __clz_limb64, | |
| 1041 | __ctz_limb64, | |
| 1042 | __popcount_limb64, | |
| 1043 | __bitreverse_limb64, | |
| 1044 | __byteswap_limb64, | |
| 1045 | __mulo_limb64, | |
| 1046 | __abs_limb64, | |
| 1010 | 1047 | }; |
test/behavior/bit_shifting.zig-1| ... | ... | @@ -151,7 +151,6 @@ test "Saturating Shift Left" { |
| 151 | 151 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; |
| 152 | 152 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 153 | 153 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 154 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 155 | 154 | |
| 156 | 155 | const S = struct { |
| 157 | 156 | fn shlSat(x: anytype, y: std.math.Log2Int(@TypeOf(x))) @TypeOf(x) { |
test/behavior/bitcast.zig-4| ... | ... | @@ -35,7 +35,6 @@ test "@bitCast iX -> uX (8, 16, 128)" { |
| 35 | 35 | |
| 36 | 36 | test "@bitCast iX -> uX exotic integers" { |
| 37 | 37 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 38 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 39 | 38 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 40 | 39 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 41 | 40 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| ... | ... | @@ -80,7 +79,6 @@ fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signe |
| 80 | 79 | |
| 81 | 80 | test "bitcast uX to bytes" { |
| 82 | 81 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 83 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 84 | 82 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 85 | 83 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 86 | 84 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| ... | ... | @@ -296,7 +294,6 @@ test "triple level result location with bitcast sandwich passed as tuple element |
| 296 | 294 | |
| 297 | 295 | test "@bitCast packed struct of floats" { |
| 298 | 296 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 299 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 300 | 297 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 301 | 298 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 302 | 299 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | ... | @@ -334,7 +331,6 @@ test "@bitCast packed struct of floats" { |
| 334 | 331 | |
| 335 | 332 | test "comptime @bitCast packed struct to int and back" { |
| 336 | 333 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 337 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 338 | 334 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 339 | 335 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 340 | 336 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/byteswap.zig-1| ... | ... | @@ -42,7 +42,6 @@ test "@byteSwap exotic integers" { |
| 42 | 42 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 43 | 43 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; |
| 44 | 44 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 45 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 46 | 45 | |
| 47 | 46 | const ByteSwapIntTest = struct { |
| 48 | 47 | fn run() !void { |
test/behavior/cast.zig+50| ... | ... | @@ -126,6 +126,56 @@ test "@floatFromInt" { |
| 126 | 126 | try comptime S.doTheTest(); |
| 127 | 127 | } |
| 128 | 128 | |
| 129 | fn testIntFromFloat(comptime F: type, f: F, comptime I: type, i: I) !void { | |
| 130 | try expect(@as(I, @intFromFloat(f)) == i); | |
| 131 | } | |
| 132 | ||
| 133 | test "@intFromFloat > 128 bits" { | |
| 134 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 135 | if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; | |
| 136 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 137 | ||
| 138 | try testIntFromFloat(f16, 1024, u140, 1024); | |
| 139 | try testIntFromFloat(f16, -1024, i140, -1024); | |
| 140 | ||
| 141 | try testIntFromFloat(f32, 1 << 24, u140, 1 << 24); | |
| 142 | try testIntFromFloat(f32, -1 << 24, i140, -1 << 24); | |
| 143 | ||
| 144 | try testIntFromFloat(f64, 1 << 53, u200, 1 << 53); | |
| 145 | try testIntFromFloat(f64, -1 << 53, i200, -1 << 53); | |
| 146 | ||
| 147 | try testIntFromFloat(f80, 1 << 63, u200, 1 << 63); | |
| 148 | try testIntFromFloat(f80, -1 << 63, i200, -1 << 63); | |
| 149 | ||
| 150 | try testIntFromFloat(f128, 1 << 100, u200, 1 << 100); | |
| 151 | try testIntFromFloat(f128, -1 << 100, i200, -1 << 100); | |
| 152 | } | |
| 153 | ||
| 154 | fn testFloatFromInt(comptime I: type, i: I, comptime F: type, expected: F) !void { | |
| 155 | try expect(@as(F, @floatFromInt(i)) == expected); | |
| 156 | } | |
| 157 | ||
| 158 | test "@floatFromInt > 128 bits" { | |
| 159 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 160 | if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; | |
| 161 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 162 | ||
| 163 | try testFloatFromInt(u140, 1024, f16, 1024); | |
| 164 | try testFloatFromInt(i140, -1024, f16, -1024); | |
| 165 | ||
| 166 | try testFloatFromInt(u140, 1 << 24, f32, 1 << 24); | |
| 167 | try testFloatFromInt(i140, -1 << 24, f32, -1 << 24); | |
| 168 | ||
| 169 | try testFloatFromInt(u200, 1 << 53, f64, 1 << 53); | |
| 170 | try testFloatFromInt(i200, -1 << 53, f64, -1 << 53); | |
| 171 | ||
| 172 | try testFloatFromInt(u200, 1 << 63, f80, 1 << 63); | |
| 173 | try testFloatFromInt(i200, -1 << 63, f80, -1 << 63); | |
| 174 | ||
| 175 | try testFloatFromInt(u200, 1 << 100, f128, 1 << 100); | |
| 176 | try testFloatFromInt(i200, -1 << 100, f128, -1 << 100); | |
| 177 | } | |
| 178 | ||
| 129 | 179 | test "@floatFromInt(f80)" { |
| 130 | 180 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 131 | 181 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/behavior/cast_int.zig+78| ... | ... | @@ -3,6 +3,7 @@ const std = @import("std"); |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | 5 | const maxInt = std.math.maxInt; |
| 6 | const minInt = std.math.minInt; | |
| 6 | 7 | |
| 7 | 8 | test "@intCast i32 to u7" { |
| 8 | 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -136,6 +137,83 @@ test "coerce non byte-sized integers accross 32bits boundary" { |
| 136 | 137 | } |
| 137 | 138 | } |
| 138 | 139 | |
| 140 | fn testIntCast(comptime S: type, a: S, comptime D: type, expected: D) !void { | |
| 141 | const actual: D = @intCast(a); | |
| 142 | try expect(actual == expected); | |
| 143 | } | |
| 144 | ||
| 145 | test "@intCast <= 64 bits" { | |
| 146 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 147 | ||
| 148 | try testIntCast(i32, minInt(i32), i64, minInt(i32)); | |
| 149 | try testIntCast(i32, maxInt(i32), i64, maxInt(i32)); | |
| 150 | try testIntCast(u32, maxInt(u32), u64, maxInt(u32)); | |
| 151 | try testIntCast(u32, maxInt(i32), i64, maxInt(i32)); | |
| 152 | try testIntCast(u32, maxInt(u32), i64, maxInt(u32)); | |
| 153 | ||
| 154 | try testIntCast(i32, 0, u32, 0); | |
| 155 | try testIntCast(i32, maxInt(i32), u32, maxInt(i32)); | |
| 156 | try testIntCast(i64, 0, u64, 0); | |
| 157 | try testIntCast(i64, maxInt(i64), u64, maxInt(i64)); | |
| 158 | ||
| 159 | try testIntCast(u32, 0, i32, 0); | |
| 160 | try testIntCast(u32, maxInt(i32), i32, maxInt(i32)); | |
| 161 | try testIntCast(u64, 0, i64, 0); | |
| 162 | try testIntCast(u64, maxInt(i64), i64, maxInt(i64)); | |
| 163 | ||
| 164 | try testIntCast(i64, minInt(i32), i32, minInt(i32)); | |
| 165 | try testIntCast(i64, maxInt(i32), i32, maxInt(i32)); | |
| 166 | try testIntCast(u64, maxInt(u32), u32, maxInt(u32)); | |
| 167 | try testIntCast(i64, maxInt(u32), u32, maxInt(u32)); | |
| 168 | } | |
| 169 | ||
| 170 | test "@intCast > 128 bits" { | |
| 171 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 172 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 173 | ||
| 174 | try testIntCast(u8, 123, u140, 123); | |
| 175 | try testIntCast(u64, 1 << 63, u140, 1 << 63); | |
| 176 | try testIntCast(u127, maxInt(u127), u140, maxInt(u127)); | |
| 177 | try testIntCast(i8, -42, i140, -42); | |
| 178 | try testIntCast(i64, minInt(i64), i140, minInt(i64)); | |
| 179 | try testIntCast(i127, maxInt(i127), i140, maxInt(i127)); | |
| 180 | try testIntCast(u127, 1 << 100, i140, 1 << 100); | |
| 181 | try testIntCast(i127, 1 << 100, u140, 1 << 100); | |
| 182 | ||
| 183 | try testIntCast(u140, 0, u128, 0); | |
| 184 | try testIntCast(u140, 1 << 100, u128, 1 << 100); | |
| 185 | try testIntCast(u140, maxInt(u128), u128, maxInt(u128)); | |
| 186 | try testIntCast(i140, -1, i128, -1); | |
| 187 | try testIntCast(i140, minInt(i128), i128, minInt(i128)); | |
| 188 | try testIntCast(i140, maxInt(i128), i128, maxInt(i128)); | |
| 189 | try testIntCast(u140, 1 << 100, i128, 1 << 100); | |
| 190 | try testIntCast(i140, 1 << 100, u128, 1 << 100); | |
| 191 | ||
| 192 | try testIntCast(u16, 255, u256, 255); | |
| 193 | try testIntCast(u128, 1 << 127, u256, 1 << 127); | |
| 194 | try testIntCast(i16, -7, i256, -7); | |
| 195 | try testIntCast(i128, minInt(i128), i256, minInt(i128)); | |
| 196 | try testIntCast(u128, maxInt(i128), i256, maxInt(i128)); | |
| 197 | try testIntCast(i128, 1 << 100, u256, 1 << 100); | |
| 198 | ||
| 199 | try testIntCast(u256, 1 << 139, u140, 1 << 139); | |
| 200 | try testIntCast(u256, maxInt(u140), u140, maxInt(u140)); | |
| 201 | try testIntCast(i256, -1, i140, -1); | |
| 202 | try testIntCast(i256, minInt(i140), i140, minInt(i140)); | |
| 203 | try testIntCast(i256, maxInt(i140), i140, maxInt(i140)); | |
| 204 | try testIntCast(u256, 1 << 120, i140, 1 << 120); | |
| 205 | try testIntCast(i256, 1 << 120, u140, 1 << 120); | |
| 206 | ||
| 207 | try testIntCast(i257, maxInt(i256), i256, maxInt(i256)); | |
| 208 | try testIntCast(i257, minInt(i256), i256, minInt(i256)); | |
| 209 | try testIntCast(u257, maxInt(u256), u256, maxInt(u256)); | |
| 210 | try testIntCast(u257, 1 << 255, u256, 1 << 255); | |
| 211 | ||
| 212 | try testIntCast(u32, maxInt(u32), i255, maxInt(u32)); | |
| 213 | try testIntCast(u64, maxInt(u64), i255, maxInt(u64)); | |
| 214 | try testIntCast(u128, maxInt(u128), i255, maxInt(u128)); | |
| 215 | } | |
| 216 | ||
| 139 | 217 | const Piece = packed struct { |
| 140 | 218 | color: Color, |
| 141 | 219 | type: Type, |
test/behavior/eval.zig-1| ... | ... | @@ -511,7 +511,6 @@ var foo_contents = Foo{ .name = "a" }; |
| 511 | 511 | const foo_ref = &foo_contents; |
| 512 | 512 | |
| 513 | 513 | test "runtime 128 bit integer division" { |
| 514 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 515 | 514 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 516 | 515 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 517 | 516 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
test/behavior/math.zig+573-9| ... | ... | @@ -61,17 +61,17 @@ fn assertFalse(b: bool) !void { |
| 61 | 61 | try expect(!b); |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | test "@clz" { | |
| 64 | test "@clz small" { | |
| 65 | 65 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 66 | 66 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 67 | 67 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 68 | 68 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO |
| 69 | 69 | |
| 70 | try testClz(); | |
| 71 | try comptime testClz(); | |
| 70 | try testClzSmall(); | |
| 71 | try comptime testClzSmall(); | |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | fn testClz() !void { | |
| 74 | fn testClzSmall() !void { | |
| 75 | 75 | try expect(testOneClz(u8, 0b10001010) == 0); |
| 76 | 76 | try expect(testOneClz(u8, 0b00001010) == 4); |
| 77 | 77 | try expect(testOneClz(u8, 0b00011010) == 3); |
| ... | ... | @@ -142,17 +142,17 @@ fn expectVectorsEqual(a: anytype, b: anytype) !void { |
| 142 | 142 | try expect(@reduce(.And, a == b)); |
| 143 | 143 | } |
| 144 | 144 | |
| 145 | test "@ctz" { | |
| 145 | test "@ctz small" { | |
| 146 | 146 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 147 | 147 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 148 | 148 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 149 | 149 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 150 | 150 | |
| 151 | try testCtz(); | |
| 152 | try comptime testCtz(); | |
| 151 | try testCtzSmall(); | |
| 152 | try comptime testCtzSmall(); | |
| 153 | 153 | } |
| 154 | 154 | |
| 155 | fn testCtz() !void { | |
| 155 | fn testCtzSmall() !void { | |
| 156 | 156 | try expect(testOneCtz(u8, 0b10100000) == 5); |
| 157 | 157 | try expect(testOneCtz(u8, 0b10001010) == 1); |
| 158 | 158 | try expect(testOneCtz(u8, 0b00000000) == 8); |
| ... | ... | @@ -1119,10 +1119,38 @@ test "@mulWithOverflow bitsize 128 bits" { |
| 1119 | 1119 | try testMulWithOverflow(i128, -1 << 63, -1 << 64, -1 << 127, 1); |
| 1120 | 1120 | } |
| 1121 | 1121 | |
| 1122 | test "@mulWithOverflow > 128 bits" { | |
| 1123 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1124 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1125 | ||
| 1126 | try testMulWithOverflow(u140, 0, maxInt(u140), 0, 0); | |
| 1127 | try testMulWithOverflow(u140, 1, maxInt(u140), maxInt(u140), 0); | |
| 1128 | try testMulWithOverflow(u140, 1 << 70, 1 << 69, 1 << 139, 0); | |
| 1129 | try testMulWithOverflow(u140, 1 << 70, 1 << 70, 0, 1); | |
| 1130 | ||
| 1131 | try testMulWithOverflow(u200, 1 << 100, 1 << 99, 1 << 199, 0); | |
| 1132 | try testMulWithOverflow(u200, 1 << 100, 1 << 100, 0, 1); | |
| 1133 | try testMulWithOverflow(u200, maxInt(u200), maxInt(u200), 1, 1); | |
| 1134 | try testMulWithOverflow(u200, maxInt(u200) - 1, 2, maxInt(u200) - 3, 1); | |
| 1135 | ||
| 1136 | try testMulWithOverflow(i140, 0, -1, 0, 0); | |
| 1137 | try testMulWithOverflow(i140, -1, -1, 1, 0); | |
| 1138 | try testMulWithOverflow(i140, 1 << 69, 1 << 69, 1 << 138, 0); | |
| 1139 | try testMulWithOverflow(i140, 1 << 69, 1 << 70, minInt(i140), 1); | |
| 1140 | try testMulWithOverflow(i140, -1 << 70, 1 << 20, -1 << 90, 0); | |
| 1141 | try testMulWithOverflow(i140, minInt(i140), -1, minInt(i140), 1); | |
| 1142 | ||
| 1143 | try testMulWithOverflow(i200, 1 << 100, 1 << 98, 1 << 198, 0); | |
| 1144 | try testMulWithOverflow(i200, 1 << 100, 1 << 99, minInt(i200), 1); | |
| 1145 | try testMulWithOverflow(i200, -1 << 120, 1 << 30, -1 << 150, 0); | |
| 1146 | try testMulWithOverflow(i200, minInt(i200), minInt(i200), 0, 1); | |
| 1147 | try testMulWithOverflow(i200, maxInt(i200), 2, -2, 1); | |
| 1148 | try testMulWithOverflow(i200, maxInt(i200), maxInt(i200), 1, 1); | |
| 1149 | } | |
| 1150 | ||
| 1122 | 1151 | test "@mulWithOverflow bitsize 256 bits" { |
| 1123 | 1152 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1124 | 1153 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 1125 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 1126 | 1154 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 1127 | 1155 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 1128 | 1156 | |
| ... | ... | @@ -1317,6 +1345,542 @@ test "@shlWithOverflow > 64 bits" { |
| 1317 | 1345 | try testShlWithOverflow(i128, 0x0100_0000_0000_0000_0000000000000000, 9, 0, 1); |
| 1318 | 1346 | } |
| 1319 | 1347 | |
| 1348 | test "@shlWithOverflow > 128 bits" { | |
| 1349 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1350 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1351 | ||
| 1352 | try testShlWithOverflow(u140, 1 << 100, 20, 1 << 120, 0); | |
| 1353 | try testShlWithOverflow(u140, 1 << 100, 40, 0, 1); | |
| 1354 | try testShlWithOverflow(u140, 3, 138, (1 << 139) | (1 << 138), 0); | |
| 1355 | try testShlWithOverflow(u140, 7, 138, (1 << 139) | (1 << 138), 1); | |
| 1356 | ||
| 1357 | try testShlWithOverflow(u256, 1 << 200, 40, 1 << 240, 0); | |
| 1358 | try testShlWithOverflow(u256, 1 << 200, 55, 1 << 255, 0); | |
| 1359 | try testShlWithOverflow(u256, 1 << 200, 56, 0, 1); | |
| 1360 | try testShlWithOverflow(u256, maxInt(u256), 1, maxInt(u256) - 1, 1); | |
| 1361 | ||
| 1362 | try testShlWithOverflow(i140, 1 << 100, 20, 1 << 120, 0); | |
| 1363 | try testShlWithOverflow(i140, 1 << 100, 39, minInt(i140), 1); | |
| 1364 | try testShlWithOverflow(i140, -1 << 20, 10, -1 << 30, 0); | |
| 1365 | try testShlWithOverflow(i140, minInt(i140), 1, 0, 1); | |
| 1366 | ||
| 1367 | try testShlWithOverflow(i256, 1 << 200, 30, 1 << 230, 0); | |
| 1368 | try testShlWithOverflow(i256, 1 << 200, 55, minInt(i256), 1); | |
| 1369 | try testShlWithOverflow(i256, -1 << 120, 40, -1 << 160, 0); | |
| 1370 | try testShlWithOverflow(i256, minInt(i256), 1, 0, 1); | |
| 1371 | } | |
| 1372 | ||
| 1373 | fn testAnd(comptime T: type, a: T, b: T, expected: T) !void { | |
| 1374 | try expect((a & b) == expected); | |
| 1375 | } | |
| 1376 | ||
| 1377 | test "and > 128 bits" { | |
| 1378 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1379 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1380 | ||
| 1381 | try testAnd(u140, (1 << 139) | (1 << 70) | 0xaa, (1 << 139) | (1 << 69) | 0xcc, (1 << 139) | 0x88); | |
| 1382 | try testAnd(u140, maxInt(u140), 1 << 100, 1 << 100); | |
| 1383 | try testAnd(u140, 0, maxInt(u140), 0); | |
| 1384 | try testAnd(u140, (1 << 80) | (1 << 17) | 1, (1 << 17) | (1 << 9) | 1, (1 << 17) | 1); | |
| 1385 | ||
| 1386 | try testAnd(u256, maxInt(u256), (1 << 255) | (1 << 200) | 7, (1 << 255) | (1 << 200) | 7); | |
| 1387 | try testAnd(u256, (1 << 255) | (1 << 5), (1 << 254) | (1 << 5), 1 << 5); | |
| 1388 | try testAnd(u256, (1 << 130) | (1 << 64) | (1 << 2), (1 << 130) | (1 << 63) | (1 << 2), (1 << 130) | (1 << 2)); | |
| 1389 | try testAnd(u256, 0, 1 << 200, 0); | |
| 1390 | ||
| 1391 | try testAnd(i140, -1, 1 << 17, 1 << 17); | |
| 1392 | try testAnd(i140, minInt(i140), -1, minInt(i140)); | |
| 1393 | try testAnd(i140, -1 << 40, (1 << 100) | (1 << 80) | (1 << 40), (1 << 100) | (1 << 80) | (1 << 40)); | |
| 1394 | try testAnd(i140, 0, maxInt(i140), 0); | |
| 1395 | ||
| 1396 | try testAnd(i256, -1, 1 << 200, 1 << 200); | |
| 1397 | try testAnd(i256, minInt(i256), maxInt(i256), 0); | |
| 1398 | try testAnd(i256, -1 << 130, -1 << 129, -1 << 130); | |
| 1399 | try testAnd(i256, minInt(i256), -1 << 10, minInt(i256)); | |
| 1400 | } | |
| 1401 | ||
| 1402 | fn testOr(comptime T: type, a: T, b: T, expected: T) !void { | |
| 1403 | try expect((a | b) == expected); | |
| 1404 | } | |
| 1405 | ||
| 1406 | test "or > 128 bits" { | |
| 1407 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1408 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1409 | ||
| 1410 | try testOr(u140, 0, 1 << 139, 1 << 139); | |
| 1411 | try testOr(u140, (1 << 70) | 0xa, (1 << 69) | 0x5, (1 << 70) | (1 << 69) | 0xf); | |
| 1412 | try testOr(u140, maxInt(u140), 0, maxInt(u140)); | |
| 1413 | try testOr(u140, (1 << 17) | (1 << 3), (1 << 17) | (1 << 1), (1 << 17) | 0xa); | |
| 1414 | ||
| 1415 | try testOr(u256, 0, 1 << 255, 1 << 255); | |
| 1416 | try testOr(u256, (1 << 200) | 0x30, (1 << 199) | 0x0f, (1 << 200) | (1 << 199) | 0x3f); | |
| 1417 | try testOr(u256, maxInt(u256), 1 << 17, maxInt(u256)); | |
| 1418 | try testOr(u256, 1 << 130, 1 << 64, (1 << 130) | (1 << 64)); | |
| 1419 | ||
| 1420 | try testOr(i140, -1, 0, -1); | |
| 1421 | try testOr(i140, minInt(i140), 1, minInt(i140) + 1); | |
| 1422 | try testOr(i140, -1 << 40, (1 << 5) | 1, (-1 << 40) | 0x21); | |
| 1423 | try testOr(i140, 0, maxInt(i140), maxInt(i140)); | |
| 1424 | ||
| 1425 | try testOr(i256, -1, 1 << 200, -1); | |
| 1426 | try testOr(i256, minInt(i256), 1 << 17, minInt(i256) | (1 << 17)); | |
| 1427 | try testOr(i256, -1 << 130, 0xff, (-1 << 130) | 0xff); | |
| 1428 | try testOr(i256, 0, maxInt(i256), maxInt(i256)); | |
| 1429 | } | |
| 1430 | ||
| 1431 | fn testXor(comptime T: type, a: T, b: T, expected: T) !void { | |
| 1432 | try expect((a ^ b) == expected); | |
| 1433 | } | |
| 1434 | ||
| 1435 | test "xor > 128 bits" { | |
| 1436 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1437 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1438 | ||
| 1439 | try testXor(u140, 0, maxInt(u140), maxInt(u140)); | |
| 1440 | try testXor(u140, 1 << 139, 1 << 139, 0); | |
| 1441 | try testXor(u140, (1 << 70) | 0xa, (1 << 69) | 0x5, (1 << 70) | (1 << 69) | 0xf); | |
| 1442 | try testXor(u140, maxInt(u140), 1 << 100, maxInt(u140) ^ (1 << 100)); | |
| 1443 | ||
| 1444 | try testXor(u256, 0, maxInt(u256), maxInt(u256)); | |
| 1445 | try testXor(u256, 1 << 255, 1 << 255, 0); | |
| 1446 | try testXor(u256, (1 << 200) | (1 << 5), (1 << 199) | (1 << 5), (1 << 200) | (1 << 199)); | |
| 1447 | try testXor(u256, maxInt(u256), 1 << 17, maxInt(u256) ^ (1 << 17)); | |
| 1448 | ||
| 1449 | try testXor(i140, -1, -1, 0); | |
| 1450 | try testXor(i140, -1, 0, -1); | |
| 1451 | try testXor(i140, minInt(i140), -1, maxInt(i140)); | |
| 1452 | try testXor(i140, -1 << 40, -1 << 39, 1 << 39); | |
| 1453 | ||
| 1454 | try testXor(i256, -1, -1, 0); | |
| 1455 | try testXor(i256, -1, 0, -1); | |
| 1456 | try testXor(i256, minInt(i256), -1, maxInt(i256)); | |
| 1457 | try testXor(i256, -1 << 130, -1 << 129, 1 << 129); | |
| 1458 | } | |
| 1459 | ||
| 1460 | fn testNot(comptime T: type, a: T, expected: T) !void { | |
| 1461 | try expect((~a) == expected); | |
| 1462 | } | |
| 1463 | ||
| 1464 | test "not > 128 bits" { | |
| 1465 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1466 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1467 | ||
| 1468 | try testNot(u140, 0, maxInt(u140)); | |
| 1469 | try testNot(u140, maxInt(u140), 0); | |
| 1470 | try testNot(u140, 1 << 139, maxInt(u140) ^ (1 << 139)); | |
| 1471 | try testNot(u140, (1 << 17) | 1, maxInt(u140) ^ ((1 << 17) | 1)); | |
| 1472 | ||
| 1473 | try testNot(u256, 0, maxInt(u256)); | |
| 1474 | try testNot(u256, maxInt(u256), 0); | |
| 1475 | try testNot(u256, 1 << 255, maxInt(u256) ^ (1 << 255)); | |
| 1476 | try testNot(u256, (1 << 200) | (1 << 5), maxInt(u256) ^ ((1 << 200) | (1 << 5))); | |
| 1477 | ||
| 1478 | try testNot(i140, -1, 0); | |
| 1479 | try testNot(i140, 0, -1); | |
| 1480 | try testNot(i140, minInt(i140), maxInt(i140)); | |
| 1481 | try testNot(i140, -1 << 10, (1 << 10) - 1); | |
| 1482 | ||
| 1483 | try testNot(i256, -1, 0); | |
| 1484 | try testNot(i256, 0, -1); | |
| 1485 | try testNot(i256, minInt(i256), maxInt(i256)); | |
| 1486 | try testNot(i256, -1 << 200, (1 << 200) - 1); | |
| 1487 | } | |
| 1488 | ||
| 1489 | fn testShl(comptime T: type, a: T, b: std.math.Log2Int(T), expected: T) !void { | |
| 1490 | try expect((a << b) == expected); | |
| 1491 | } | |
| 1492 | ||
| 1493 | test "shl > 128 bits" { | |
| 1494 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1495 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1496 | ||
| 1497 | try testShl(u140, 1 << 5, 10, 1 << 15); | |
| 1498 | try testShl(u140, 3, 138, (1 << 139) | (1 << 138)); | |
| 1499 | try testShl(u140, 1 << 139, 1, 0); | |
| 1500 | try testShl(u140, (1 << 70) | 1, 3, (1 << 73) | 8); | |
| 1501 | ||
| 1502 | try testShl(u256, 1 << 200, 20, 1 << 220); | |
| 1503 | try testShl(u256, 1 << 255, 1, 0); | |
| 1504 | try testShl(u256, (1 << 128) | 5, 7, (1 << 135) | 0x280); | |
| 1505 | try testShl(u256, maxInt(u256), 1, maxInt(u256) - 1); | |
| 1506 | ||
| 1507 | try testShl(i140, 1 << 20, 5, 1 << 25); | |
| 1508 | try testShl(i140, -1, 7, -128); | |
| 1509 | try testShl(i140, minInt(i140), 1, 0); | |
| 1510 | try testShl(i140, -1 << 10, 5, -1 << 15); | |
| 1511 | ||
| 1512 | try testShl(i256, 1 << 200, 30, 1 << 230); | |
| 1513 | try testShl(i256, -1, 200, -1 << 200); | |
| 1514 | try testShl(i256, minInt(i256), 1, 0); | |
| 1515 | try testShl(i256, -1 << 100, 50, -1 << 150); | |
| 1516 | } | |
| 1517 | ||
| 1518 | fn testShr(comptime T: type, a: T, b: std.math.Log2Int(T), expected: T) !void { | |
| 1519 | try expect((a >> b) == expected); | |
| 1520 | } | |
| 1521 | ||
| 1522 | test "shr > 128 bits" { | |
| 1523 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1524 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1525 | ||
| 1526 | try testShr(u140, 1 << 139, 39, 1 << 100); | |
| 1527 | try testShr(u140, (1 << 70) | 8, 3, (1 << 67) | 1); | |
| 1528 | try testShr(u140, 1, 1, 0); | |
| 1529 | try testShr(u140, maxInt(u140), 139, 1); | |
| 1530 | ||
| 1531 | try testShr(u256, 1 << 255, 55, 1 << 200); | |
| 1532 | try testShr(u256, (1 << 200) | (1 << 7), 7, (1 << 193) | 1); | |
| 1533 | try testShr(u256, 1, 1, 0); | |
| 1534 | try testShr(u256, maxInt(u256), 255, 1); | |
| 1535 | ||
| 1536 | try testShr(i140, -1, 17, -1); | |
| 1537 | try testShr(i140, minInt(i140), 1, minInt(i140) >> 1); | |
| 1538 | try testShr(i140, -1 << 80, 40, -1 << 40); | |
| 1539 | try testShr(i140, -5, 1, -3); | |
| 1540 | ||
| 1541 | try testShr(i256, -1, 200, -1); | |
| 1542 | try testShr(i256, minInt(i256), 1, minInt(i256) >> 1); | |
| 1543 | try testShr(i256, -1 << 180, 80, -1 << 100); | |
| 1544 | try testShr(i256, -5, 1, -3); | |
| 1545 | } | |
| 1546 | ||
| 1547 | fn testClz(comptime T: type, a: T, expected: u16) !void { | |
| 1548 | try expect(@clz(a) == expected); | |
| 1549 | } | |
| 1550 | ||
| 1551 | test "@clz > 128 bits" { | |
| 1552 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1553 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1554 | ||
| 1555 | try testClz(u140, 0, 140); | |
| 1556 | try testClz(u140, 1 << 139, 0); | |
| 1557 | try testClz(u140, 1 << 70, 69); | |
| 1558 | try testClz(u140, maxInt(u140), 0); | |
| 1559 | ||
| 1560 | try testClz(u256, 0, 256); | |
| 1561 | try testClz(u256, 1 << 255, 0); | |
| 1562 | try testClz(u256, 1 << 200, 55); | |
| 1563 | try testClz(u256, 1, 255); | |
| 1564 | ||
| 1565 | try testClz(i140, -1, 0); | |
| 1566 | try testClz(i140, minInt(i140), 0); | |
| 1567 | try testClz(i140, 1 << 70, 69); | |
| 1568 | try testClz(i140, 0, 140); | |
| 1569 | ||
| 1570 | try testClz(i256, -1, 0); | |
| 1571 | try testClz(i256, minInt(i256), 0); | |
| 1572 | try testClz(i256, 1 << 200, 55); | |
| 1573 | try testClz(i256, 0, 256); | |
| 1574 | } | |
| 1575 | ||
| 1576 | fn testCtz(comptime T: type, a: T, expected: u16) !void { | |
| 1577 | try expect(@ctz(a) == expected); | |
| 1578 | } | |
| 1579 | ||
| 1580 | test "@ctz > 128 bits" { | |
| 1581 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1582 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1583 | ||
| 1584 | try testCtz(u140, 0, 140); | |
| 1585 | try testCtz(u140, 1 << 139, 139); | |
| 1586 | try testCtz(u140, 1 << 70, 70); | |
| 1587 | try testCtz(u140, maxInt(u140), 0); | |
| 1588 | ||
| 1589 | try testCtz(u256, 0, 256); | |
| 1590 | try testCtz(u256, 1 << 255, 255); | |
| 1591 | try testCtz(u256, 1 << 200, 200); | |
| 1592 | try testCtz(u256, 3 << 5, 5); | |
| 1593 | ||
| 1594 | try testCtz(i140, -1, 0); | |
| 1595 | try testCtz(i140, minInt(i140), 139); | |
| 1596 | try testCtz(i140, 0, 140); | |
| 1597 | try testCtz(i140, -1 << 70, 70); | |
| 1598 | ||
| 1599 | try testCtz(i256, -1, 0); | |
| 1600 | try testCtz(i256, minInt(i256), 255); | |
| 1601 | try testCtz(i256, 0, 256); | |
| 1602 | try testCtz(i256, -1 << 200, 200); | |
| 1603 | } | |
| 1604 | ||
| 1605 | fn testPopCount(comptime T: type, a: T, expected: u16) !void { | |
| 1606 | try expect(@popCount(a) == expected); | |
| 1607 | } | |
| 1608 | ||
| 1609 | test "@popCount > 128 bits" { | |
| 1610 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1611 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1612 | ||
| 1613 | try testPopCount(u140, 0, 0); | |
| 1614 | try testPopCount(u140, maxInt(u140), 140); | |
| 1615 | try testPopCount(u140, (1 << 139) | (1 << 70) | 1, 3); | |
| 1616 | try testPopCount(u140, (1 << 5) - 1, 5); | |
| 1617 | ||
| 1618 | try testPopCount(u256, 0, 0); | |
| 1619 | try testPopCount(u256, maxInt(u256), 256); | |
| 1620 | try testPopCount(u256, (1 << 255) | (1 << 200) | (1 << 17) | (1 << 3), 4); | |
| 1621 | try testPopCount(u256, (1 << 64) - 1, 64); | |
| 1622 | ||
| 1623 | try testPopCount(i140, -1, 140); | |
| 1624 | try testPopCount(i140, minInt(i140), 1); | |
| 1625 | try testPopCount(i140, 0, 0); | |
| 1626 | try testPopCount(i140, -1 << 70, 70); | |
| 1627 | ||
| 1628 | try testPopCount(i256, -1, 256); | |
| 1629 | try testPopCount(i256, minInt(i256), 1); | |
| 1630 | try testPopCount(i256, 0, 0); | |
| 1631 | try testPopCount(i256, -1 << 200, 56); | |
| 1632 | } | |
| 1633 | ||
| 1634 | fn testBitReverse(comptime T: type, a: T, expected: T) !void { | |
| 1635 | try expect(@bitReverse(a) == expected); | |
| 1636 | } | |
| 1637 | ||
| 1638 | test "@bitReverse > 128 bits" { | |
| 1639 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1640 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1641 | ||
| 1642 | try testBitReverse(u140, 1 << 139, 1); | |
| 1643 | try testBitReverse(u140, 1 << 70, 1 << 69); | |
| 1644 | try testBitReverse(u140, 0, 0); | |
| 1645 | try testBitReverse(u140, maxInt(u140), maxInt(u140)); | |
| 1646 | ||
| 1647 | try testBitReverse(u256, 1 << 255, 1); | |
| 1648 | try testBitReverse(u256, 1 << 200, 1 << 55); | |
| 1649 | try testBitReverse(u256, 0, 0); | |
| 1650 | try testBitReverse(u256, maxInt(u256), maxInt(u256)); | |
| 1651 | ||
| 1652 | try testBitReverse(i140, -1, -1); | |
| 1653 | try testBitReverse(i140, minInt(i140), 1); | |
| 1654 | try testBitReverse(i140, 1 << 70, 1 << 69); | |
| 1655 | try testBitReverse(i140, 0, 0); | |
| 1656 | ||
| 1657 | try testBitReverse(i256, -1, -1); | |
| 1658 | try testBitReverse(i256, minInt(i256), 1); | |
| 1659 | try testBitReverse(i256, 1 << 200, 1 << 55); | |
| 1660 | try testBitReverse(i256, 0, 0); | |
| 1661 | } | |
| 1662 | ||
| 1663 | fn testByteSwap(comptime T: type, a: T, expected: T) !void { | |
| 1664 | try expect(@byteSwap(a) == expected); | |
| 1665 | } | |
| 1666 | ||
| 1667 | test "@byteSwap > 128 bits" { | |
| 1668 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1669 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1670 | ||
| 1671 | try testByteSwap(u144, 1 << 136, 1); | |
| 1672 | try testByteSwap(u144, 1, 1 << 136); | |
| 1673 | try testByteSwap(u144, 0, 0); | |
| 1674 | try testByteSwap(u144, maxInt(u144), maxInt(u144)); | |
| 1675 | ||
| 1676 | try testByteSwap(u256, 1 << 248, 1); | |
| 1677 | try testByteSwap(u256, 1 << 120, 1 << 128); | |
| 1678 | try testByteSwap(u256, 1, 1 << 248); | |
| 1679 | try testByteSwap(u256, maxInt(u256), maxInt(u256)); | |
| 1680 | ||
| 1681 | try testByteSwap(i144, -1, -1); | |
| 1682 | try testByteSwap(i144, minInt(i144), 128); | |
| 1683 | try testByteSwap(i144, 1, 1 << 136); | |
| 1684 | try testByteSwap(i144, 1 << 64, 1 << 72); | |
| 1685 | ||
| 1686 | try testByteSwap(i256, -1, -1); | |
| 1687 | try testByteSwap(i256, minInt(i256), 128); | |
| 1688 | try testByteSwap(i256, 1, 1 << 248); | |
| 1689 | try testByteSwap(i256, 1 << 120, 1 << 128); | |
| 1690 | } | |
| 1691 | ||
| 1692 | fn testMax(comptime T: type, a: T, b: T, expected: T) !void { | |
| 1693 | try expect(@max(a, b) == expected); | |
| 1694 | } | |
| 1695 | ||
| 1696 | test "@max > 128 bits" { | |
| 1697 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1698 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1699 | ||
| 1700 | try testMax(u140, 0, maxInt(u140), maxInt(u140)); | |
| 1701 | try testMax(u140, 1 << 139, 1 << 138, 1 << 139); | |
| 1702 | try testMax(u140, (1 << 100) + 7, (1 << 100) + 3, (1 << 100) + 7); | |
| 1703 | try testMax(u140, maxInt(u140) - 1, maxInt(u140), maxInt(u140)); | |
| 1704 | ||
| 1705 | try testMax(u200, 1 << 199, 1 << 198, 1 << 199); | |
| 1706 | try testMax(u200, (1 << 150) + (1 << 17), (1 << 150) + (1 << 18), (1 << 150) + (1 << 18)); | |
| 1707 | try testMax(u200, 0, 1 << 123, 1 << 123); | |
| 1708 | try testMax(u200, maxInt(u200), maxInt(u200) - 1, maxInt(u200)); | |
| 1709 | ||
| 1710 | try testMax(i140, -1, 0, 0); | |
| 1711 | try testMax(i140, minInt(i140), maxInt(i140), maxInt(i140)); | |
| 1712 | try testMax(i140, -1 << 70, -1 << 69, -1 << 69); | |
| 1713 | try testMax(i140, (1 << 100) - 1, 1 << 100, 1 << 100); | |
| 1714 | ||
| 1715 | try testMax(i200, -1, minInt(i200), -1); | |
| 1716 | try testMax(i200, -1 << 150, -1 << 149, -1 << 149); | |
| 1717 | try testMax(i200, 1 << 198, (1 << 198) - 1, 1 << 198); | |
| 1718 | try testMax(i200, maxInt(i200), 0, maxInt(i200)); | |
| 1719 | } | |
| 1720 | ||
| 1721 | fn testMin(comptime T: type, a: T, b: T, expected: T) !void { | |
| 1722 | try expect(@min(a, b) == expected); | |
| 1723 | } | |
| 1724 | ||
| 1725 | test "@min > 128 bits" { | |
| 1726 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1727 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1728 | ||
| 1729 | try testMin(u140, 0, maxInt(u140), 0); | |
| 1730 | try testMin(u140, 1 << 139, 1 << 138, 1 << 138); | |
| 1731 | try testMin(u140, (1 << 100) + 7, (1 << 100) + 3, (1 << 100) + 3); | |
| 1732 | try testMin(u140, maxInt(u140) - 1, maxInt(u140), maxInt(u140) - 1); | |
| 1733 | ||
| 1734 | try testMin(u200, 1 << 199, 1 << 198, 1 << 198); | |
| 1735 | try testMin(u200, (1 << 150) + (1 << 17), (1 << 150) + (1 << 18), (1 << 150) + (1 << 17)); | |
| 1736 | try testMin(u200, 0, 1 << 123, 0); | |
| 1737 | try testMin(u200, maxInt(u200), maxInt(u200) - 1, maxInt(u200) - 1); | |
| 1738 | ||
| 1739 | try testMin(i140, -1, 0, -1); | |
| 1740 | try testMin(i140, minInt(i140), maxInt(i140), minInt(i140)); | |
| 1741 | try testMin(i140, -1 << 70, -1 << 69, -1 << 70); | |
| 1742 | try testMin(i140, (1 << 100) - 1, 1 << 100, (1 << 100) - 1); | |
| 1743 | ||
| 1744 | try testMin(i200, -1, minInt(i200), minInt(i200)); | |
| 1745 | try testMin(i200, -1 << 150, -1 << 149, -1 << 150); | |
| 1746 | try testMin(i200, 1 << 198, (1 << 198) - 1, (1 << 198) - 1); | |
| 1747 | try testMin(i200, maxInt(i200), 0, 0); | |
| 1748 | } | |
| 1749 | ||
| 1750 | fn testAbs(comptime T: type, a: T, expected: anytype) !void { | |
| 1751 | try expect(@abs(a) == expected); | |
| 1752 | } | |
| 1753 | ||
| 1754 | test "@abs > 128 bits" { | |
| 1755 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1756 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1757 | ||
| 1758 | try testAbs(u140, 0, 0); | |
| 1759 | try testAbs(u140, 1 << 139, 1 << 139); | |
| 1760 | try testAbs(u200, 123456789, 123456789); | |
| 1761 | try testAbs(u200, maxInt(u200), maxInt(u200)); | |
| 1762 | ||
| 1763 | try testAbs(i140, 0, 0); | |
| 1764 | try testAbs(i140, 1, 1); | |
| 1765 | try testAbs(i140, -1, 1); | |
| 1766 | try testAbs(i140, minInt(i140), 1 << 139); | |
| 1767 | ||
| 1768 | try testAbs(i200, 1 << 198, 1 << 198); | |
| 1769 | try testAbs(i200, -1 << 198, 1 << 198); | |
| 1770 | try testAbs(i200, maxInt(i200), maxInt(i200)); | |
| 1771 | try testAbs(i200, minInt(i200), 1 << 199); | |
| 1772 | } | |
| 1773 | ||
| 1774 | fn testRem(comptime T: type, numerator: T, denominator: T, expected: T) !void { | |
| 1775 | try expect(@rem(numerator, denominator) == expected); | |
| 1776 | } | |
| 1777 | ||
| 1778 | test "@rem > 128 bits" { | |
| 1779 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1780 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1781 | ||
| 1782 | try testRem(u140, 0, maxInt(u140), 0); | |
| 1783 | try testRem(u140, maxInt(u140), maxInt(u140), 0); | |
| 1784 | try testRem(u140, maxInt(u140), 2, 1); | |
| 1785 | try testRem(u140, (1 << 139) + 5, 1 << 70, 5); | |
| 1786 | try testRem(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, 7); | |
| 1787 | try testRem(u200, 123, 1 << 100, 123); | |
| 1788 | try testRem(u200, 1 << 120, 1 << 60, 0); | |
| 1789 | try testRem(u200, maxInt(u200), 1 << 100, (1 << 100) - 1); | |
| 1790 | ||
| 1791 | try testRem(i140, 0, maxInt(i140), 0); | |
| 1792 | try testRem(i140, maxInt(i140), maxInt(i140), 0); | |
| 1793 | try testRem(i140, -((1 << 100) + 1), 1 << 50, -1); | |
| 1794 | try testRem(i140, (1 << 100) + 1, -(1 << 50), 1); | |
| 1795 | try testRem(i140, -((1 << 100) + 1), -(1 << 50), -1); | |
| 1796 | try testRem(i200, minInt(i200), 1, 0); | |
| 1797 | try testRem(i200, minInt(i200), -2, 0); | |
| 1798 | try testRem(i200, maxInt(i200), 2, 1); | |
| 1799 | } | |
| 1800 | ||
| 1801 | fn testMod(comptime T: type, numerator: T, denominator: T, expected: T) !void { | |
| 1802 | try expect(@mod(numerator, denominator) == expected); | |
| 1803 | } | |
| 1804 | ||
| 1805 | test "@mod > 128 bits" { | |
| 1806 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1807 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1808 | ||
| 1809 | try testMod(u140, 0, maxInt(u140), 0); | |
| 1810 | try testMod(u140, maxInt(u140), maxInt(u140), 0); | |
| 1811 | try testMod(u140, maxInt(u140), 2, 1); | |
| 1812 | try testMod(u140, (1 << 139) + 5, 1 << 70, 5); | |
| 1813 | try testMod(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, 7); | |
| 1814 | try testMod(u200, 123, 1 << 100, 123); | |
| 1815 | try testMod(u200, 1 << 120, 1 << 60, 0); | |
| 1816 | try testMod(u200, maxInt(u200), 1 << 100, (1 << 100) - 1); | |
| 1817 | ||
| 1818 | try testMod(i140, 0, maxInt(i140), 0); | |
| 1819 | try testMod(i140, maxInt(i140), maxInt(i140), 0); | |
| 1820 | try testMod(i140, -((1 << 100) + 1), 1 << 50, (1 << 50) - 1); | |
| 1821 | try testMod(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50) + 1); | |
| 1822 | try testMod(i140, -((1 << 100) + 1), -(1 << 50), -1); | |
| 1823 | try testMod(i200, minInt(i200), 1, 0); | |
| 1824 | try testMod(i200, minInt(i200), -2, 0); | |
| 1825 | try testMod(i200, maxInt(i200), 2, 1); | |
| 1826 | } | |
| 1827 | ||
| 1828 | fn testDivFloor(comptime T: type, numerator: T, denominator: T, expected: T) !void { | |
| 1829 | try expect(@divFloor(numerator, denominator) == expected); | |
| 1830 | } | |
| 1831 | ||
| 1832 | test "@divFloor > 128 bits" { | |
| 1833 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1834 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1835 | ||
| 1836 | try testDivFloor(u140, 0, maxInt(u140), 0); | |
| 1837 | try testDivFloor(u140, maxInt(u140), maxInt(u140), 1); | |
| 1838 | try testDivFloor(u140, maxInt(u140), 2, maxInt(u140) >> 1); | |
| 1839 | try testDivFloor(u140, (1 << 139) + 5, 1 << 70, 1 << 69); | |
| 1840 | try testDivFloor(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, (1 << 50) + 1); | |
| 1841 | try testDivFloor(u200, 123, 1 << 100, 0); | |
| 1842 | try testDivFloor(u200, 1 << 120, 1 << 60, 1 << 60); | |
| 1843 | try testDivFloor(u200, maxInt(u200), 1 << 100, (1 << 100) - 1); | |
| 1844 | ||
| 1845 | try testDivFloor(i140, 0, maxInt(i140), 0); | |
| 1846 | try testDivFloor(i140, maxInt(i140), maxInt(i140), 1); | |
| 1847 | try testDivFloor(i140, -((1 << 100) + 1), 1 << 50, -(1 << 50) - 1); | |
| 1848 | try testDivFloor(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50) - 1); | |
| 1849 | try testDivFloor(i140, -((1 << 100) + 1), -(1 << 50), 1 << 50); | |
| 1850 | try testDivFloor(i200, -3, 2, -2); | |
| 1851 | try testDivFloor(i200, minInt(i200), 1, minInt(i200)); | |
| 1852 | try testDivFloor(i200, minInt(i200), -2, 1 << 198); | |
| 1853 | try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1); | |
| 1854 | } | |
| 1855 | ||
| 1856 | fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void { | |
| 1857 | try expect(@divTrunc(numerator, denominator) == expected); | |
| 1858 | } | |
| 1859 | ||
| 1860 | test "@divTrunc > 128 bits" { | |
| 1861 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1862 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 1863 | ||
| 1864 | try testDivTrunc(u140, 0, maxInt(u140), 0); | |
| 1865 | try testDivTrunc(u140, maxInt(u140), maxInt(u140), 1); | |
| 1866 | try testDivTrunc(u140, maxInt(u140), 2, maxInt(u140) >> 1); | |
| 1867 | try testDivTrunc(u140, (1 << 139) + 5, 1 << 70, 1 << 69); | |
| 1868 | try testDivTrunc(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, (1 << 50) + 1); | |
| 1869 | try testDivTrunc(u200, 123, 1 << 100, 0); | |
| 1870 | try testDivTrunc(u200, 1 << 120, 1 << 60, 1 << 60); | |
| 1871 | try testDivTrunc(u200, maxInt(u200), 1 << 100, (1 << 100) - 1); | |
| 1872 | ||
| 1873 | try testDivTrunc(i140, 0, maxInt(i140), 0); | |
| 1874 | try testDivTrunc(i140, maxInt(i140), maxInt(i140), 1); | |
| 1875 | try testDivTrunc(i140, -((1 << 100) + 1), 1 << 50, -(1 << 50)); | |
| 1876 | try testDivTrunc(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50)); | |
| 1877 | try testDivTrunc(i140, -((1 << 100) + 1), -(1 << 50), 1 << 50); | |
| 1878 | try testDivTrunc(i200, -3, 2, -1); | |
| 1879 | try testDivTrunc(i200, minInt(i200), 1, minInt(i200)); | |
| 1880 | try testDivTrunc(i200, minInt(i200), -2, 1 << 198); | |
| 1881 | try testDivTrunc(i200, maxInt(i200), 2, (1 << 198) - 1); | |
| 1882 | } | |
| 1883 | ||
| 1320 | 1884 | test "overflow arithmetic with u0 values" { |
| 1321 | 1885 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 1322 | 1886 |
test/behavior/packed-struct.zig-2| ... | ... | @@ -582,7 +582,6 @@ test "packed struct fields modification" { |
| 582 | 582 | |
| 583 | 583 | test "nested packed struct field access test" { |
| 584 | 584 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 585 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO packed structs larger than 64 bits | |
| 586 | 585 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 587 | 586 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 588 | 587 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| ... | ... | @@ -736,7 +735,6 @@ test "nested packed struct at non-zero offset 2" { |
| 736 | 735 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 737 | 736 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 738 | 737 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 739 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO packed structs larger than 64 bits | |
| 740 | 738 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 741 | 739 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 742 | 740 |
test/behavior/saturating_arithmetic.zig+104-62| ... | ... | @@ -4,6 +4,14 @@ const minInt = std.math.minInt; |
| 4 | 4 | const maxInt = std.math.maxInt; |
| 5 | 5 | const expect = std.testing.expect; |
| 6 | 6 | |
| 7 | fn testSatAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void { | |
| 8 | try expect((lhs +| rhs) == expected); | |
| 9 | ||
| 10 | var x = lhs; | |
| 11 | x +|= rhs; | |
| 12 | try expect(x == expected); | |
| 13 | } | |
| 14 | ||
| 7 | 15 | test "saturating add" { |
| 8 | 16 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 9 | 17 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -28,32 +36,23 @@ test "saturating add" { |
| 28 | 36 | try testSatAdd(u2, 3, 2, 3); |
| 29 | 37 | try testSatAdd(u3, 7, 1, 7); |
| 30 | 38 | } |
| 31 | ||
| 32 | fn testSatAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void { | |
| 33 | try expect((lhs +| rhs) == expected); | |
| 34 | ||
| 35 | var x = lhs; | |
| 36 | x +|= rhs; | |
| 37 | try expect(x == expected); | |
| 38 | } | |
| 39 | 39 | }; |
| 40 | 40 | |
| 41 | 41 | try S.doTheTest(); |
| 42 | 42 | try comptime S.doTheTest(); |
| 43 | 43 | |
| 44 | try comptime S.testSatAdd(comptime_int, 0, 0, 0); | |
| 45 | try comptime S.testSatAdd(comptime_int, -1, 1, 0); | |
| 46 | try comptime S.testSatAdd(comptime_int, 3, 2, 5); | |
| 47 | try comptime S.testSatAdd(comptime_int, -3, -2, -5); | |
| 48 | try comptime S.testSatAdd(comptime_int, 3, -2, 1); | |
| 49 | try comptime S.testSatAdd(comptime_int, -3, 2, -1); | |
| 50 | try comptime S.testSatAdd(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 1119305249183743626545271163355074748512); | |
| 51 | try comptime S.testSatAdd(comptime_int, 7, -593423721213448152027139550640105366508, -593423721213448152027139550640105366501); | |
| 44 | try comptime testSatAdd(comptime_int, 0, 0, 0); | |
| 45 | try comptime testSatAdd(comptime_int, -1, 1, 0); | |
| 46 | try comptime testSatAdd(comptime_int, 3, 2, 5); | |
| 47 | try comptime testSatAdd(comptime_int, -3, -2, -5); | |
| 48 | try comptime testSatAdd(comptime_int, 3, -2, 1); | |
| 49 | try comptime testSatAdd(comptime_int, -3, 2, -1); | |
| 50 | try comptime testSatAdd(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 1119305249183743626545271163355074748512); | |
| 51 | try comptime testSatAdd(comptime_int, 7, -593423721213448152027139550640105366508, -593423721213448152027139550640105366501); | |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 54 | test "saturating add 128bit" { |
| 55 | 55 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 56 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 57 | 56 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 58 | 57 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 59 | 58 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| ... | ... | @@ -65,19 +64,20 @@ test "saturating add 128bit" { |
| 65 | 64 | try testSatAdd(i128, minInt(i128), maxInt(i128), -1); |
| 66 | 65 | try testSatAdd(u128, maxInt(u128), 1, maxInt(u128)); |
| 67 | 66 | } |
| 68 | fn testSatAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void { | |
| 69 | try expect((lhs +| rhs) == expected); | |
| 70 | ||
| 71 | var x = lhs; | |
| 72 | x +|= rhs; | |
| 73 | try expect(x == expected); | |
| 74 | } | |
| 75 | 67 | }; |
| 76 | 68 | |
| 77 | 69 | try S.doTheTest(); |
| 78 | 70 | try comptime S.doTheTest(); |
| 79 | 71 | } |
| 80 | 72 | |
| 73 | fn testSatSub(comptime T: type, lhs: T, rhs: T, expected: T) !void { | |
| 74 | try expect((lhs -| rhs) == expected); | |
| 75 | ||
| 76 | var x = lhs; | |
| 77 | x -|= rhs; | |
| 78 | try expect(x == expected); | |
| 79 | } | |
| 80 | ||
| 81 | 81 | test "saturating subtraction" { |
| 82 | 82 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 83 | 83 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -101,32 +101,23 @@ test "saturating subtraction" { |
| 101 | 101 | try testSatSub(u8, 10, 3, 7); |
| 102 | 102 | try testSatSub(u8, 0, 255, 0); |
| 103 | 103 | } |
| 104 | ||
| 105 | fn testSatSub(comptime T: type, lhs: T, rhs: T, expected: T) !void { | |
| 106 | try expect((lhs -| rhs) == expected); | |
| 107 | ||
| 108 | var x = lhs; | |
| 109 | x -|= rhs; | |
| 110 | try expect(x == expected); | |
| 111 | } | |
| 112 | 104 | }; |
| 113 | 105 | |
| 114 | 106 | try S.doTheTest(); |
| 115 | 107 | try comptime S.doTheTest(); |
| 116 | 108 | |
| 117 | try comptime S.testSatSub(comptime_int, 0, 0, 0); | |
| 118 | try comptime S.testSatSub(comptime_int, 1, 1, 0); | |
| 119 | try comptime S.testSatSub(comptime_int, 3, 2, 1); | |
| 120 | try comptime S.testSatSub(comptime_int, -3, -2, -1); | |
| 121 | try comptime S.testSatSub(comptime_int, 3, -2, 5); | |
| 122 | try comptime S.testSatSub(comptime_int, -3, 2, -5); | |
| 123 | try comptime S.testSatSub(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 182846383813587550256162760261375991602); | |
| 124 | try comptime S.testSatSub(comptime_int, 7, -593423721213448152027139550640105366508, 593423721213448152027139550640105366515); | |
| 109 | try comptime testSatSub(comptime_int, 0, 0, 0); | |
| 110 | try comptime testSatSub(comptime_int, 1, 1, 0); | |
| 111 | try comptime testSatSub(comptime_int, 3, 2, 1); | |
| 112 | try comptime testSatSub(comptime_int, -3, -2, -1); | |
| 113 | try comptime testSatSub(comptime_int, 3, -2, 5); | |
| 114 | try comptime testSatSub(comptime_int, -3, 2, -5); | |
| 115 | try comptime testSatSub(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 182846383813587550256162760261375991602); | |
| 116 | try comptime testSatSub(comptime_int, 7, -593423721213448152027139550640105366508, 593423721213448152027139550640105366515); | |
| 125 | 117 | } |
| 126 | 118 | |
| 127 | 119 | test "saturating subtraction 128bit" { |
| 128 | 120 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 129 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 130 | 121 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 131 | 122 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 132 | 123 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| ... | ... | @@ -138,14 +129,6 @@ test "saturating subtraction 128bit" { |
| 138 | 129 | try testSatSub(i128, minInt(i128), -maxInt(i128), -1); |
| 139 | 130 | try testSatSub(u128, 0, maxInt(u128), 0); |
| 140 | 131 | } |
| 141 | ||
| 142 | fn testSatSub(comptime T: type, lhs: T, rhs: T, expected: T) !void { | |
| 143 | try expect((lhs -| rhs) == expected); | |
| 144 | ||
| 145 | var x = lhs; | |
| 146 | x -|= rhs; | |
| 147 | try expect(x == expected); | |
| 148 | } | |
| 149 | 132 | }; |
| 150 | 133 | |
| 151 | 134 | try S.doTheTest(); |
| ... | ... | @@ -257,7 +240,6 @@ test "saturating mul i64, i128" { |
| 257 | 240 | |
| 258 | 241 | test "saturating multiplication" { |
| 259 | 242 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 260 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 261 | 243 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 262 | 244 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 263 | 245 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| ... | ... | @@ -294,6 +276,14 @@ test "saturating multiplication" { |
| 294 | 276 | try comptime testSatMul(comptime_int, 7, -593423721213448152027139550640105366508, -4153966048494137064189976854480737565556); |
| 295 | 277 | } |
| 296 | 278 | |
| 279 | fn testSatShl(comptime Lhs: type, lhs: Lhs, comptime Rhs: type, rhs: Rhs, expected: Lhs) !void { | |
| 280 | try expect((lhs <<| rhs) == expected); | |
| 281 | ||
| 282 | var x = lhs; | |
| 283 | x <<|= rhs; | |
| 284 | try expect(x == expected); | |
| 285 | } | |
| 286 | ||
| 297 | 287 | test "saturating shift-left" { |
| 298 | 288 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 299 | 289 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -320,23 +310,15 @@ test "saturating shift-left" { |
| 320 | 310 | try testSatShl(u8, 0, u4, 8, 0); |
| 321 | 311 | try testSatShl(u8, 3, u4, 8, maxInt(u8)); |
| 322 | 312 | } |
| 323 | ||
| 324 | fn testSatShl(comptime Lhs: type, lhs: Lhs, comptime Rhs: type, rhs: Rhs, expected: Lhs) !void { | |
| 325 | try expect((lhs <<| rhs) == expected); | |
| 326 | ||
| 327 | var x = lhs; | |
| 328 | x <<|= rhs; | |
| 329 | try expect(x == expected); | |
| 330 | } | |
| 331 | 313 | }; |
| 332 | 314 | |
| 333 | 315 | try S.doTheTest(); |
| 334 | 316 | try comptime S.doTheTest(); |
| 335 | 317 | |
| 336 | try comptime S.testSatShl(comptime_int, 0, comptime_int, 0, 0); | |
| 337 | try comptime S.testSatShl(comptime_int, 1, comptime_int, 2, 4); | |
| 338 | try comptime S.testSatShl(comptime_int, 13, comptime_int, 150, 18554220005177478453757717602843436772975706112); | |
| 339 | try comptime S.testSatShl(comptime_int, -582769, comptime_int, 180, -893090893854873184096635538665358532628308979495815656505344); | |
| 318 | try comptime testSatShl(comptime_int, 0, comptime_int, 0, 0); | |
| 319 | try comptime testSatShl(comptime_int, 1, comptime_int, 2, 4); | |
| 320 | try comptime testSatShl(comptime_int, 13, comptime_int, 150, 18554220005177478453757717602843436772975706112); | |
| 321 | try comptime testSatShl(comptime_int, -582769, comptime_int, 180, -893090893854873184096635538665358532628308979495815656505344); | |
| 340 | 322 | } |
| 341 | 323 | |
| 342 | 324 | test "saturating shift-left large rhs" { |
| ... | ... | @@ -386,3 +368,63 @@ test "saturating shl uses the LHS type" { |
| 386 | 368 | |
| 387 | 369 | try expect((1 <<| @as(u8, 200)) == 1606938044258990275541962092341162602522202993782792835301376); |
| 388 | 370 | } |
| 371 | ||
| 372 | test "sat add > 128 bits" { | |
| 373 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 374 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 375 | ||
| 376 | try testSatAdd(u140, 0, 0, 0); | |
| 377 | try testSatAdd(u140, maxInt(u140), 1, maxInt(u140)); | |
| 378 | try testSatAdd(u200, 1 << 150, 1 << 20, (1 << 150) + (1 << 20)); | |
| 379 | try testSatAdd(u200, maxInt(u200), maxInt(u200), maxInt(u200)); | |
| 380 | ||
| 381 | try testSatAdd(i140, minInt(i140), -1, minInt(i140)); | |
| 382 | try testSatAdd(i140, maxInt(i140), 1, maxInt(i140)); | |
| 383 | try testSatAdd(i200, -1 << 150, 1 << 149, -1 << 149); | |
| 384 | try testSatAdd(i200, maxInt(i200), maxInt(i200), maxInt(i200)); | |
| 385 | } | |
| 386 | ||
| 387 | test "sat sub > 128 bits" { | |
| 388 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 389 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 390 | ||
| 391 | try testSatSub(u140, 0, 1, 0); | |
| 392 | try testSatSub(u140, maxInt(u140), maxInt(u140), 0); | |
| 393 | try testSatSub(u200, 1 << 150, 1 << 20, (1 << 150) - (1 << 20)); | |
| 394 | try testSatSub(u200, maxInt(u200), 0, maxInt(u200)); | |
| 395 | ||
| 396 | try testSatSub(i140, minInt(i140), 1, minInt(i140)); | |
| 397 | try testSatSub(i140, maxInt(i140), -1, maxInt(i140)); | |
| 398 | try testSatSub(i200, -1 << 150, 1 << 149, -3 << 149); | |
| 399 | try testSatSub(i200, 0, minInt(i200), maxInt(i200)); | |
| 400 | } | |
| 401 | ||
| 402 | test "sat mul > 128 bits" { | |
| 403 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 404 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 405 | ||
| 406 | try testSatMul(u140, 0, maxInt(u140), 0); | |
| 407 | try testSatMul(u140, 1 << 70, 1 << 69, 1 << 139); | |
| 408 | try testSatMul(u200, maxInt(u200), 2, maxInt(u200)); | |
| 409 | try testSatMul(u200, maxInt(u200) - 1, 1, maxInt(u200) - 1); | |
| 410 | ||
| 411 | try testSatMul(i140, -1, maxInt(i140), -maxInt(i140)); | |
| 412 | try testSatMul(i140, minInt(i140), -1, maxInt(i140)); | |
| 413 | try testSatMul(i200, 1 << 100, 1 << 99, maxInt(i200)); | |
| 414 | try testSatMul(i200, -1 << 150, 1 << 30, -1 << 180); | |
| 415 | } | |
| 416 | ||
| 417 | test "sat shl > 128 bits" { | |
| 418 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 419 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 420 | ||
| 421 | try testSatShl(u140, 0, u8, 17, 0); | |
| 422 | try testSatShl(u140, 1 << 100, u8, 20, 1 << 120); | |
| 423 | try testSatShl(u200, maxInt(u200), u8, 1, maxInt(u200)); | |
| 424 | try testSatShl(u200, 1 << 199, u8, 1, maxInt(u200)); | |
| 425 | ||
| 426 | try testSatShl(i140, 0, u8, 17, 0); | |
| 427 | try testSatShl(i140, 1 << 100, u8, 38, 1 << 138); | |
| 428 | try testSatShl(i140, 1 << 100, u8, 39, maxInt(i140)); | |
| 429 | try testSatShl(i200, minInt(i200) + 1, u8, 1, minInt(i200)); | |
| 430 | } |
test/behavior/struct.zig-1| ... | ... | @@ -540,7 +540,6 @@ test "zero-bit field in packed struct" { |
| 540 | 540 | test "packed struct with non-ABI-aligned field" { |
| 541 | 541 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 542 | 542 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 543 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 544 | 543 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 545 | 544 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 546 | 545 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
test/behavior/switch.zig-2| ... | ... | @@ -1457,8 +1457,6 @@ test "switch on nested packed containers" { |
| 1457 | 1457 | } |
| 1458 | 1458 | |
| 1459 | 1459 | test "switch on large types" { |
| 1460 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 1461 | ||
| 1462 | 1460 | const S = struct { |
| 1463 | 1461 | fn doTheTest(a: u128, b: i500) !void { |
| 1464 | 1462 | switch (a) { |
test/behavior/switch_loop.zig-2| ... | ... | @@ -566,8 +566,6 @@ test "switch loop with packed unions with OPV" { |
| 566 | 566 | } |
| 567 | 567 | |
| 568 | 568 | test "switch loop on large types" { |
| 569 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 570 | ||
| 571 | 569 | const S = struct { |
| 572 | 570 | fn doTheTest(a: u128, b: i500) !void { |
| 573 | 571 | label: switch (a) { |
test/behavior/truncate.zig+46| ... | ... | @@ -1,4 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const math = std.math; | |
| 3 | const maxInt = math.maxInt; | |
| 4 | const minInt = math.minInt; | |
| 2 | 5 | const builtin = @import("builtin"); |
| 3 | 6 | const assert = std.debug.assert; |
| 4 | 7 | const expect = std.testing.expect; |
| ... | ... | @@ -64,6 +67,49 @@ test "truncate on comptime integer" { |
| 64 | 67 | try expect(w == 0); |
| 65 | 68 | } |
| 66 | 69 | |
| 70 | fn testTruncate(comptime S: type, a: S, comptime D: type, expected: D) !void { | |
| 71 | const actual: D = @truncate(a); | |
| 72 | try expect(actual == expected); | |
| 73 | } | |
| 74 | ||
| 75 | test "@truncate > 128 bits" { | |
| 76 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 77 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 78 | ||
| 79 | try testTruncate(u140, 0, u128, 0); | |
| 80 | try testTruncate(u140, maxInt(u140), u128, maxInt(u128)); | |
| 81 | try testTruncate(u140, 1 << 139, u128, 0); | |
| 82 | try testTruncate(u140, (1 << 139) | (1 << 64) | 0x55, u128, (1 << 64) | 0x55); | |
| 83 | try testTruncate(u140, (1 << 100) | (1 << 63), u128, (1 << 100) | (1 << 63)); | |
| 84 | try testTruncate(u140, (1 << 130) | 0xabcd, u16, 0xabcd); | |
| 85 | ||
| 86 | try testTruncate(u256, 1 << 200, u128, 0); | |
| 87 | try testTruncate(u256, (1 << 200) | (1 << 127) | 1, u128, (1 << 127) | 1); | |
| 88 | try testTruncate(u256, maxInt(u256), u128, maxInt(u128)); | |
| 89 | try testTruncate(u256, (1 << 255) | (1 << 128) | 0x1234_5678_9abc_def0, u64, 0x1234_5678_9abc_def0); | |
| 90 | try testTruncate(u256, (1 << 250) | (1 << 32), u32, 0); | |
| 91 | try testTruncate(u256, (1 << 129) | (1 << 63), u64, 1 << 63); | |
| 92 | ||
| 93 | try testTruncate(i140, 0, i128, 0); | |
| 94 | try testTruncate(i140, -1, i128, -1); | |
| 95 | try testTruncate(i140, -2, i8, -2); | |
| 96 | try testTruncate(i140, -1 << 80, i64, 0); | |
| 97 | try testTruncate(i140, (-1 << 80) | 0x1234, i16, 0x1234); | |
| 98 | try testTruncate(i140, minInt(i140), i128, 0); | |
| 99 | try testTruncate(i140, maxInt(i140), i128, -1); | |
| 100 | try testTruncate(i140, (1 << 127) - 1, i128, maxInt(i128)); | |
| 101 | ||
| 102 | try testTruncate(i256, -1, i128, -1); | |
| 103 | try testTruncate(i256, minInt(i256), i128, 0); | |
| 104 | try testTruncate(i256, (-1 << 128) | maxInt(i128), i128, maxInt(i128)); | |
| 105 | try testTruncate(i256, (-1 << 200) | (1 << 127), i128, minInt(i128)); | |
| 106 | try testTruncate(i256, -255, i8, 1); | |
| 107 | try testTruncate(i256, (-1 << 64) | 0x1234_5678, i32, 0x1234_5678); | |
| 108 | ||
| 109 | try testTruncate(i257, maxInt(i257), i256, -1); | |
| 110 | try testTruncate(u257, maxInt(u257), u256, maxInt(u256)); | |
| 111 | } | |
| 112 | ||
| 67 | 113 | test "truncate on vectors" { |
| 68 | 114 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 69 | 115 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |