| author | |
| committer | |
| log | 676e4f3824054cf39c87d008909b0e57bb9bdcc8 |
| tree | 5d00fe5e028d1bee54aac16303a2824053c57abf |
| parent | f07d33f54b3448019f5e7c74c1f9063a5079b961 |
- Add cpuid / getXCR0 functions for the cbe to use instead of asm blocks
- Don't cast between 128 bit types during truncation
- Fixup truncation to use functions for shifts / adds
- Fixup float casts for undefined values
- Add test for 128 bit integer truncation4 files changed, 107 insertions(+), 32 deletions(-)
lib/std/zig/system/x86.zig+23-8| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const Target = std.Target; | 3 | const Target = std.Target; |
| 3 | const CrossTarget = std.zig.CrossTarget; | 4 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 5 | ||
| ... | @@ -527,25 +528,39 @@ const CpuidLeaf = packed struct { | ... | @@ -527,25 +528,39 @@ const CpuidLeaf = packed struct { |
| 527 | edx: u32, | 528 | edx: u32, |
| 528 | }; | 529 | }; |
| 529 | 530 | ||
| 531 | extern fn zig_cpuid(leaf_id: u32, subid: u32, eax: *u32, ebx: *u32, ecx: *u32, edx: *u32) void; | ||
| 532 | |||
| 530 | fn cpuid(leaf_id: u32, subid: u32) CpuidLeaf { | 533 | fn cpuid(leaf_id: u32, subid: u32) CpuidLeaf { |
| 531 | // valid for both x86 and x86_64 | 534 | // valid for both x86 and x86_64 |
| 532 | var eax: u32 = undefined; | 535 | var eax: u32 = undefined; |
| 533 | var ebx: u32 = undefined; | 536 | var ebx: u32 = undefined; |
| 534 | var ecx: u32 = undefined; | 537 | var ecx: u32 = undefined; |
| 535 | var edx: u32 = undefined; | 538 | var edx: u32 = undefined; |
| 536 | asm volatile ("cpuid" | 539 | |
| 537 | : [_] "={eax}" (eax), | 540 | if (builtin.zig_backend == .stage2_c) { |
| 538 | [_] "={ebx}" (ebx), | 541 | zig_cpuid(leaf_id, subid, &eax, &ebx, &ecx, &edx); |
| 539 | [_] "={ecx}" (ecx), | 542 | } else { |
| 540 | [_] "={edx}" (edx), | 543 | asm volatile ("cpuid" |
| 541 | : [_] "{eax}" (leaf_id), | 544 | : [_] "={eax}" (eax), |
| 542 | [_] "{ecx}" (subid), | 545 | [_] "={ebx}" (ebx), |
| 543 | ); | 546 | [_] "={ecx}" (ecx), |
| 547 | [_] "={edx}" (edx), | ||
| 548 | : [_] "{eax}" (leaf_id), | ||
| 549 | [_] "{ecx}" (subid), | ||
| 550 | ); | ||
| 551 | } | ||
| 552 | |||
| 544 | return .{ .eax = eax, .ebx = ebx, .ecx = ecx, .edx = edx }; | 553 | return .{ .eax = eax, .ebx = ebx, .ecx = ecx, .edx = edx }; |
| 545 | } | 554 | } |
| 546 | 555 | ||
| 556 | extern fn zig_get_xcr0() u32; | ||
| 557 | |||
| 547 | // Read control register 0 (XCR0). Used to detect features such as AVX. | 558 | // Read control register 0 (XCR0). Used to detect features such as AVX. |
| 548 | fn getXCR0() u32 { | 559 | fn getXCR0() u32 { |
| 560 | if (builtin.zig_backend == .stage2_c) { | ||
| 561 | return zig_get_xcr0(); | ||
| 562 | } | ||
| 563 | |||
| 549 | return asm volatile ( | 564 | return asm volatile ( |
| 550 | \\ xor %%ecx, %%ecx | 565 | \\ xor %%ecx, %%ecx |
| 551 | \\ xgetbv | 566 | \\ xgetbv |
lib/zig.h+41-7| ... | @@ -6,6 +6,12 @@ | ... | @@ -6,6 +6,12 @@ |
| 6 | #include <stddef.h> | 6 | #include <stddef.h> |
| 7 | #include <stdint.h> | 7 | #include <stdint.h> |
| 8 | 8 | ||
| 9 | #if _MSC_VER | ||
| 10 | #include <intrin.h> | ||
| 11 | #else | ||
| 12 | #include <cpuid.h> | ||
| 13 | #endif | ||
| 14 | |||
| 9 | #if !defined(__cplusplus) && __STDC_VERSION__ <= 201710L | 15 | #if !defined(__cplusplus) && __STDC_VERSION__ <= 201710L |
| 10 | #if __STDC_VERSION__ >= 199901L | 16 | #if __STDC_VERSION__ >= 199901L |
| 11 | #include <stdbool.h> | 17 | #include <stdbool.h> |
| ... | @@ -188,7 +194,6 @@ typedef char bool; | ... | @@ -188,7 +194,6 @@ typedef char bool; |
| 188 | #define zig_atomic_load(obj, order, type) __atomic_load_n (obj, order) | 194 | #define zig_atomic_load(obj, order, type) __atomic_load_n (obj, order) |
| 189 | #define zig_fence(order) __atomic_thread_fence(order) | 195 | #define zig_fence(order) __atomic_thread_fence(order) |
| 190 | #elif _MSC_VER && (_M_IX86 || _M_X64) | 196 | #elif _MSC_VER && (_M_IX86 || _M_X64) |
| 191 | #include <intrin.h> | ||
| 192 | #define memory_order_relaxed 0 | 197 | #define memory_order_relaxed 0 |
| 193 | #define memory_order_consume 1 | 198 | #define memory_order_consume 1 |
| 194 | #define memory_order_acquire 2 | 199 | #define memory_order_acquire 2 |
| ... | @@ -1367,6 +1372,11 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { | ... | @@ -1367,6 +1372,11 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1367 | return res; | 1372 | return res; |
| 1368 | } | 1373 | } |
| 1369 | 1374 | ||
| 1375 | zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs); | ||
| 1376 | static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1377 | return __multi3(lhs, rhs); | ||
| 1378 | } | ||
| 1379 | |||
| 1370 | zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs); | 1380 | zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs); |
| 1371 | static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { | 1381 | static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { |
| 1372 | return __udivti3(lhs, rhs); | 1382 | return __udivti3(lhs, rhs); |
| ... | @@ -1392,6 +1402,10 @@ static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { | ... | @@ -1392,6 +1402,10 @@ static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1392 | return zig_add_i128(rem, (((lhs.hi ^ rhs.hi) & rem.hi) < zig_as_i64(0) ? rhs : zig_as_i128(0, 0))); | 1402 | return zig_add_i128(rem, (((lhs.hi ^ rhs.hi) & rem.hi) < zig_as_i64(0) ? rhs : zig_as_i128(0, 0))); |
| 1393 | } | 1403 | } |
| 1394 | 1404 | ||
| 1405 | static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1406 | return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_as_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_as_i128(0, 0)) < zig_as_i32(0))); | ||
| 1407 | } | ||
| 1408 | |||
| 1395 | #endif /* zig_has_int128 */ | 1409 | #endif /* zig_has_int128 */ |
| 1396 | 1410 | ||
| 1397 | #define zig_div_floor_u128 zig_div_trunc_u128 | 1411 | #define zig_div_floor_u128 zig_div_trunc_u128 |
| ... | @@ -1465,11 +1479,6 @@ static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { | ... | @@ -1465,11 +1479,6 @@ static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { |
| 1465 | static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs); // TODO | 1479 | static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs); // TODO |
| 1466 | #endif | 1480 | #endif |
| 1467 | 1481 | ||
| 1468 | zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs); | ||
| 1469 | static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1470 | return __multi3(lhs, rhs); | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1482 | static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { |
| 1474 | return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits); | 1483 | return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits); |
| 1475 | } | 1484 | } |
| ... | @@ -2118,7 +2127,6 @@ zig_float_builtins(f128) | ... | @@ -2118,7 +2127,6 @@ zig_float_builtins(f128) |
| 2118 | zig_float_builtins(c_longdouble) | 2127 | zig_float_builtins(c_longdouble) |
| 2119 | 2128 | ||
| 2120 | #if _MSC_VER && (_M_IX86 || _M_X64) | 2129 | #if _MSC_VER && (_M_IX86 || _M_X64) |
| 2121 | #include <intrin.h> | ||
| 2122 | 2130 | ||
| 2123 | // TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 | 2131 | // TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 |
| 2124 | 2132 | ||
| ... | @@ -2338,3 +2346,29 @@ zig_msvc_atomics_128op(u128, min) | ... | @@ -2338,3 +2346,29 @@ zig_msvc_atomics_128op(u128, min) |
| 2338 | zig_msvc_atomics_128op(u128, max) | 2346 | zig_msvc_atomics_128op(u128, max) |
| 2339 | 2347 | ||
| 2340 | #endif | 2348 | #endif |
| 2349 | |||
| 2350 | /* ========================= Special Case Intrinsics ========================= */ | ||
| 2351 | |||
| 2352 | static inline void zig_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, zig_u32* ebx, zig_u32* ecx, zig_u32* edx) { | ||
| 2353 | #if _MSC_VER | ||
| 2354 | zig_u32 cpu_info[4]; | ||
| 2355 | __cpuidex(cpu_info, leaf_id, subid); | ||
| 2356 | *eax = cpu_info[0]; | ||
| 2357 | *ebx = cpu_info[1]; | ||
| 2358 | *ecx = cpu_info[2]; | ||
| 2359 | *edx = cpu_info[3]; | ||
| 2360 | #else | ||
| 2361 | __cpuid_count(leaf_id, subid, eax, ebx, ecx, edx); | ||
| 2362 | #endif | ||
| 2363 | } | ||
| 2364 | |||
| 2365 | static inline zig_u32 zig_get_xcr0() { | ||
| 2366 | #if _MSC_VER | ||
| 2367 | return (zig_u32)_xgetbv(0); | ||
| 2368 | #else | ||
| 2369 | zig_u32 eax; | ||
| 2370 | zig_u32 edx; | ||
| 2371 | __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); | ||
| 2372 | return eax; | ||
| 2373 | #endif | ||
| 2374 | } |
src/codegen/c.zig+29-17| ... | @@ -746,9 +746,9 @@ pub const DeclGen = struct { | ... | @@ -746,9 +746,9 @@ pub const DeclGen = struct { |
| 746 | var int_pl = Type.Payload.Bits{ .base = .{ .tag = .int_signed }, .data = bits }; | 746 | var int_pl = Type.Payload.Bits{ .base = .{ .tag = .int_signed }, .data = bits }; |
| 747 | const int_ty = Type.initPayload(&int_pl.base); | 747 | const int_ty = Type.initPayload(&int_pl.base); |
| 748 | 748 | ||
| 749 | try writer.writeByte('('); | 749 | try writer.writeAll("zig_cast_"); |
| 750 | try dg.renderTypecast(writer, ty); | 750 | try dg.renderTypeForBuiltinFnName(writer, ty); |
| 751 | try writer.writeAll(")zig_as_"); | 751 | try writer.writeAll(" zig_as_"); |
| 752 | try dg.renderTypeForBuiltinFnName(writer, ty); | 752 | try dg.renderTypeForBuiltinFnName(writer, ty); |
| 753 | try writer.writeByte('('); | 753 | try writer.writeByte('('); |
| 754 | switch (bits) { | 754 | switch (bits) { |
| ... | @@ -3616,16 +3616,14 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3616,16 +3616,14 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3616 | try writer.writeAll(" = "); | 3616 | try writer.writeAll(" = "); |
| 3617 | 3617 | ||
| 3618 | const needs_lo = operand_int_info.bits > 64 and dest_bits <= 64; | 3618 | const needs_lo = operand_int_info.bits > 64 and dest_bits <= 64; |
| 3619 | if (!needs_lo or dest_c_bits != 64 or dest_int_info.signedness != operand_int_info.signedness) { | ||
| 3620 | try writer.writeByte('('); | ||
| 3621 | try f.renderTypecast(writer, inst_ty); | ||
| 3622 | try writer.writeByte(')'); | ||
| 3623 | } | ||
| 3624 | |||
| 3625 | if (needs_lo) { | 3619 | if (needs_lo) { |
| 3626 | try writer.writeAll("zig_lo_"); | 3620 | try writer.writeAll("zig_lo_"); |
| 3627 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); | 3621 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); |
| 3628 | try writer.writeByte('('); | 3622 | try writer.writeByte('('); |
| 3623 | } else if (dest_c_bits <= 64) { | ||
| 3624 | try writer.writeByte('('); | ||
| 3625 | try f.renderTypecast(writer, inst_ty); | ||
| 3626 | try writer.writeByte(')'); | ||
| 3629 | } | 3627 | } |
| 3630 | 3628 | ||
| 3631 | if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { | 3629 | if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { |
| ... | @@ -3640,11 +3638,11 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3640,11 +3638,11 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3640 | std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); | 3638 | std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); |
| 3641 | 3639 | ||
| 3642 | const mask_val = try inst_ty.maxInt(stack.get(), target); | 3640 | const mask_val = try inst_ty.maxInt(stack.get(), target); |
| 3643 | 3641 | try writer.writeAll("zig_and_"); | |
| 3644 | // TODO: This needs to use _and_ to do this to support > 64 bits and !zig_has_int128 | 3642 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); |
| 3645 | try writer.writeByte('('); | 3643 | try writer.writeByte('('); |
| 3646 | try f.writeCValue(writer, operand, .Other); | 3644 | try f.writeCValue(writer, operand, .FunctionArgument); |
| 3647 | try writer.print(" & {x})", .{try f.fmtIntLiteral(inst_ty, mask_val)}); | 3645 | try writer.print(", {x})", .{try f.fmtIntLiteral(operand_ty, mask_val)}); |
| 3648 | }, | 3646 | }, |
| 3649 | .signed => { | 3647 | .signed => { |
| 3650 | const c_bits = toCIntBits(operand_int_info.bits) orelse | 3648 | const c_bits = toCIntBits(operand_int_info.bits) orelse |
| ... | @@ -3655,10 +3653,24 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3655,10 +3653,24 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3655 | }; | 3653 | }; |
| 3656 | const shift_val = Value.initPayload(&shift_pl.base); | 3654 | const shift_val = Value.initPayload(&shift_pl.base); |
| 3657 | 3655 | ||
| 3658 | // TODO: This needs to use shl and shr to do this to support > 64 bits and !zig_has_int128 | 3656 | try writer.writeAll("zig_shr_"); |
| 3659 | try writer.print("((int{d}_t)((uint{0d}_t)", .{c_bits}); | 3657 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); |
| 3660 | try f.writeCValue(writer, operand, .Other); | 3658 | if (c_bits == 128) { |
| 3661 | try writer.print(" << {}) >> {0})", .{try f.fmtIntLiteral(Type.u8, shift_val)}); | 3659 | try writer.print("(zig_bitcast_i{d}(", .{c_bits}); |
| 3660 | } else { | ||
| 3661 | try writer.print("((int{d}_t)", .{c_bits}); | ||
| 3662 | } | ||
| 3663 | try writer.print("zig_shl_u{d}(", .{c_bits}); | ||
| 3664 | if (c_bits == 128) { | ||
| 3665 | try writer.print("zig_bitcast_u{d}(", .{c_bits}); | ||
| 3666 | } else { | ||
| 3667 | try writer.print("(uint{d}_t)", .{c_bits}); | ||
| 3668 | } | ||
| 3669 | try f.writeCValue(writer, operand, .FunctionArgument); | ||
| 3670 | if (c_bits == 128) try writer.writeByte(')'); | ||
| 3671 | try writer.print(", {})", .{ try f.fmtIntLiteral(Type.u8, shift_val) }); | ||
| 3672 | if (c_bits == 128) try writer.writeByte(')'); | ||
| 3673 | try writer.print(", {})", .{ try f.fmtIntLiteral(Type.u8, shift_val) }); | ||
| 3662 | }, | 3674 | }, |
| 3663 | } | 3675 | } |
| 3664 | 3676 |
test/behavior/basic.zig+14| ... | @@ -37,6 +37,20 @@ test "truncate to non-power-of-two integers" { | ... | @@ -37,6 +37,20 @@ test "truncate to non-power-of-two integers" { |
| 37 | try testTrunc(i32, i5, std.math.maxInt(i32), -1); | 37 | try testTrunc(i32, i5, std.math.maxInt(i32), -1); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | test "truncate to non-power-of-two integers from 128-bit" { | ||
| 41 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 42 | |||
| 43 | try testTrunc(u128, u1, 0xffffffff_ffffffff_ffffffff_01010101, 0x01); | ||
| 44 | try testTrunc(u128, u1, 0xffffffff_ffffffff_ffffffff_01010110, 0x00); | ||
| 45 | try testTrunc(u128, u2, 0xffffffff_ffffffff_ffffffff_01010101, 0x01); | ||
| 46 | try testTrunc(u128, u2, 0xffffffff_ffffffff_ffffffff_01010102, 0x02); | ||
| 47 | try testTrunc(i128, i5, -4, -4); | ||
| 48 | try testTrunc(i128, i5, 4, 4); | ||
| 49 | try testTrunc(i128, i5, -28, 4); | ||
| 50 | try testTrunc(i128, i5, 28, -4); | ||
| 51 | try testTrunc(i128, i5, std.math.maxInt(i128), -1); | ||
| 52 | } | ||
| 53 | |||
| 40 | fn testTrunc(comptime Big: type, comptime Little: type, big: Big, little: Little) !void { | 54 | fn testTrunc(comptime Big: type, comptime Little: type, big: Big, little: Little) !void { |
| 41 | try expect(@truncate(Little, big) == little); | 55 | try expect(@truncate(Little, big) == little); |
| 42 | } | 56 | } |