Commit 1aa2c32055

kcbanner <kcbanner@gmail.com>
2023-01-20 05:36:42
cbe: fixes for x86
- Emit calling convention - Fix .Naked handling for msvc - Add teb helper for x86 - Fix 128-bit shl implementation when rhs is >= 64 - Add 128-bit shl tests
1 parent ce6de2d
Changed files (8)
lib/compiler_rt/aulldiv.zig
@@ -7,7 +7,7 @@ const common = @import("common.zig");
 pub const panic = common.panic;
 
 comptime {
-    if (arch == .x86 and abi == .msvc) {
+    if (arch == .x86 and abi == .msvc and builtin.zig_backend != .stage2_c) {
         // Don't let LLVM apply the stdcall name mangling on those MSVC builtins
         @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = common.linkage, .visibility = common.visibility });
         @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = common.linkage, .visibility = common.visibility });
lib/compiler_rt/aullrem.zig
@@ -7,7 +7,7 @@ const common = @import("common.zig");
 pub const panic = common.panic;
 
 comptime {
-    if (arch == .x86 and abi == .msvc) {
+    if (arch == .x86 and abi == .msvc and builtin.zig_backend != .stage2_c) {
         // Don't let LLVM apply the stdcall name mangling on those MSVC builtins
         @export(_allrem, .{ .name = "\x01__allrem", .linkage = common.linkage, .visibility = common.visibility });
         @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = common.linkage, .visibility = common.visibility });
lib/std/os/windows.zig
@@ -1804,14 +1804,21 @@ pub fn UnlockFile(
 
 /// This is a workaround for the C backend until zig has the ability to put
 /// C code in inline assembly.
+extern fn zig_x86_windows_teb() callconv(.C) *anyopaque;
 extern fn zig_x86_64_windows_teb() callconv(.C) *anyopaque;
 
 pub fn teb() *TEB {
     return switch (native_arch) {
-        .x86 => asm volatile (
-            \\ movl %%fs:0x18, %[ptr]
-            : [ptr] "=r" (-> *TEB),
-        ),
+        .x86 => blk: {
+            if (builtin.zig_backend == .stage2_c) {
+                break :blk @ptrCast(*TEB, @alignCast(@alignOf(TEB), zig_x86_windows_teb()));
+            } else {
+                break :blk asm volatile (
+                    \\ movl %%fs:0x18, %[ptr]
+                    : [ptr] "=r" (-> *TEB),
+                );
+            }
+        },
         .x86_64 => blk: {
             if (builtin.zig_backend == .stage2_c) {
                 break :blk @ptrCast(*TEB, @alignCast(@alignOf(TEB), zig_x86_64_windows_teb()));
lib/std/start_windows_tls.zig
@@ -7,14 +7,12 @@ export var _tls_end: u8 linksection(".tls$ZZZ") = 0;
 export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;
 export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;
 
+const tls_array: u32 = 0x2c;
 comptime {
     if (builtin.target.cpu.arch == .x86) {
         // The __tls_array is the offset of the ThreadLocalStoragePointer field
         // in the TEB block whose base address held in the %fs segment.
-        asm (
-            \\ .global __tls_array
-            \\ __tls_array = 0x2C
-        );
+        @export(tls_array, .{ .name = "_tls_array" });
     }
 }
 
lib/zig.h
@@ -52,15 +52,20 @@ typedef char bool;
 
 #if _MSC_VER
 #define zig_const_arr
+#define zig_callconv(c) __##c
 #else
 #define zig_const_arr static const
+#define zig_callconv(c) __attribute__((c))
 #endif
 
 #if zig_has_attribute(naked) || defined(zig_gnuc)
+#define zig_naked_decl __attribute__((naked))
 #define zig_naked __attribute__((naked))
 #elif defined(_MSC_VER)
+#define zig_naked_decl
 #define zig_naked __declspec(naked)
 #else
+#define zig_naked_decl zig_naked_unavailable
 #define zig_naked zig_naked_unavailable
 #endif
 
@@ -1214,8 +1219,14 @@ typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128;
 
 #define zig_as_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) })
 #define zig_as_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) })
+
+#if _MSC_VER
 #define zig_as_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) }
 #define zig_as_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) }
+#else
+#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo)
+#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo)
+#endif
 #define zig_hi_u128(val) ((val).hi)
 #define zig_lo_u128(val) ((val).lo)
 #define zig_hi_i128(val) ((val).hi)
@@ -1344,13 +1355,13 @@ static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) {
 
 static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) {
     if (rhs == zig_as_u8(0)) return lhs;
-    if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = lhs.lo << rhs, .lo = zig_minInt_u64 };
+    if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 };
     return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs };
 }
 
 static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) {
     if (rhs == zig_as_u8(0)) return lhs;
-    if (rhs >= zig_as_u8(64)) return (zig_i128){ .hi = lhs.lo << rhs, .lo = zig_minInt_u64 };
+    if (rhs >= zig_as_u8(64)) return (zig_i128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 };
     return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs };
 }
 
@@ -1379,6 +1390,10 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) {
 }
 
 zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs);
+static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) {
+    return zig_bitcast_u128(__multi3(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs)));
+}
+
 static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) {
     return __multi3(lhs, rhs);
 }
@@ -1474,17 +1489,6 @@ static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) {
     return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits);
 }
 
-#if _MSC_VER
-static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) {
-    zig_u64 lo_carry;
-    zig_u64 lo = _umul128(lhs.lo, rhs.lo, &lo_carry);
-    zig_u64 hi = lhs.hi * rhs.lo + lhs.lo * rhs.hi + lo_carry;
-    return zig_as_u128(hi, lo);
-}
-#else
-static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs); // TODO
-#endif
-
 static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) {
     return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits);
 }
@@ -2218,8 +2222,11 @@ zig_msvc_atomics(u16, 16)
 zig_msvc_atomics(i16, 16)
 zig_msvc_atomics(u32, )
 zig_msvc_atomics(i32, )
+
+#if _M_X64
 zig_msvc_atomics(u64, 64)
 zig_msvc_atomics(i64, 64)
+#endif
 
 #define zig_msvc_flt_atomics(Type, ReprType, suffix) \
     static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \
@@ -2259,7 +2266,9 @@ zig_msvc_atomics(i64, 64)
     }
 
 zig_msvc_flt_atomics(f32, u32, )
+#if _M_X64
 zig_msvc_flt_atomics(f64, u64, 64)
+#endif
 
 #if _M_IX86
 static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, zig_u32* arg) {
@@ -2283,7 +2292,7 @@ static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desir
     }
     return exchanged;
 }
-#else
+#else /* _M_IX86 */
 static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, zig_u64* arg) {
     return _InterlockedExchangePointer(obj, arg);
 }
@@ -2305,14 +2314,12 @@ static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desir
     }
     return exchanged;
 }
-#endif
 
 static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expected, zig_u128 desired) {
     return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_i64*)expected);
 }
 
-static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) {
-    return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_u64*)expected);
+static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) {    return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_u64*)expected);
 }
 
 #define zig_msvc_atomics_128xchg(Type) \
@@ -2350,6 +2357,7 @@ zig_msvc_atomics_128op(u128, and)
 zig_msvc_atomics_128op(u128, nand)
 zig_msvc_atomics_128op(u128, min)
 zig_msvc_atomics_128op(u128, max)
+#endif /* _M_IX86 */
 
 #endif /* _MSC_VER && (_M_IX86 || _M_X64) */
 
@@ -2359,7 +2367,7 @@ zig_msvc_atomics_128op(u128, max)
 
 static inline void* zig_x86_64_windows_teb(void) {
 #if _MSC_VER
-    return __readgsqword(0x30);
+    return (void*)__readgsqword(0x30);
 #else
     void* teb;
     __asm volatile(" movq %%gs:0x30, %[ptr]": [ptr]"=r"(teb)::);
@@ -2367,6 +2375,18 @@ static inline void* zig_x86_64_windows_teb(void) {
 #endif
 }
 
+#elif (_MSC_VER && _M_IX86) || defined(__i386__) || defined(__X86__)
+
+static inline void* zig_x86_windows_teb(void) {
+#if _MSC_VER
+    return (void*)__readfsdword(0x18);
+#else
+    void* teb;
+    __asm volatile(" movl %%fs:0x18, %[ptr]": [ptr]"=r"(teb)::);
+    return teb;
+#endif
+}
+
 #endif
 
 #if (_MSC_VER && (_M_IX86 || _M_X64)) || defined(__i386__) || defined(__x86_64__)
src/codegen/c.zig
@@ -1459,7 +1459,12 @@ pub const DeclGen = struct {
 
     fn renderFunctionSignature(dg: *DeclGen, w: anytype, kind: TypedefKind, export_index: u32) !void {
         const fn_info = dg.decl.ty.fnInfo();
-        if (fn_info.cc == .Naked) try w.writeAll("zig_naked ");
+        if (fn_info.cc == .Naked) {
+            switch (kind) {
+                .Forward => try w.writeAll("zig_naked_decl "),
+                .Complete => try w.writeAll("zig_naked "),
+            }
+        }
         if (dg.decl.val.castTag(.function)) |func_payload|
             if (func_payload.data.is_cold) try w.writeAll("zig_cold ");
 
@@ -1469,6 +1474,13 @@ pub const DeclGen = struct {
 
         try dg.renderType(w, ret_ty, kind);
         try w.writeByte(' ');
+
+        if (toCallingConvention(fn_info.cc)) |call_conv| {
+            try w.print("zig_callconv({s}) ", .{call_conv});
+        }
+
+        if (fn_info.alignment > 0 and kind == .Complete) try w.print(" zig_align_fn({})", .{ fn_info.alignment });
+
         try dg.renderDeclName(w, dg.decl_index, export_index);
         try w.writeByte('(');
 
@@ -1488,7 +1500,7 @@ pub const DeclGen = struct {
             try dg.renderType(w, Type.void, kind);
         }
         try w.writeByte(')');
-        if (fn_info.alignment > 0) try w.print(" zig_align_fn({})", .{fn_info.alignment});
+        if (fn_info.alignment > 0 and kind == .Forward) try w.print(" zig_align_fn({})", .{fn_info.alignment});
     }
 
     fn renderPtrToFnTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
@@ -7035,6 +7047,15 @@ fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void {
     return w.writeAll(toMemoryOrder(order));
 }
 
+fn toCallingConvention(call_conv: std.builtin.CallingConvention) ?[]const u8 {
+    return switch (call_conv) {
+        .Stdcall => "stdcall",
+        .Fastcall => "fastcall",
+        .Vectorcall => "vectorcall",
+        else => null,
+    };
+}
+
 fn toAtomicRmwSuffix(order: std.builtin.AtomicRmwOp) []const u8 {
     return switch (order) {
         .Xchg => "xchg",
test/behavior/align.zig
@@ -65,6 +65,9 @@ test "alignment and size of structs with 128-bit fields" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 
+    // https://github.com/ziglang/zig/issues/14371
+    if (builtin.zig_backend == .stage2_c and builtin.target.cpu.arch == .x86) return error.SkipZigTest;
+
     const A = struct {
         x: u128,
     };
test/behavior/int128.zig
@@ -84,3 +84,31 @@ test "truncate int128" {
         try expect(@truncate(i128, buff) == maxInt(i128));
     }
 }
+
+test "shift int128" {
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
+
+    const types = .{ u128, i128 };
+    inline for (types) |t| {
+        try testShlTrunc(t, 0x8, 123);
+        comptime try testShlTrunc(t, 0x8, 123);
+
+        try testShlTrunc(t, 0x40000000_00000000, 64);
+        comptime try testShlTrunc(t, 0x40000000_00000000, 64);
+
+        try testShlTrunc(t, 0x01000000_00000000_00000000, 38);
+        comptime try testShlTrunc(t, 0x01000000_00000000_00000000, 38);
+
+        try testShlTrunc(t, 0x00000008_00000000_00000000_00000000, 27);
+        comptime try testShlTrunc(t, 0x00000008_00000000_00000000_00000000, 27);
+    }
+}
+
+fn testShlTrunc(comptime Type: type, x: Type, rhs: u7) !void {
+    const shifted = x << rhs;
+    try expect(shifted == @as(Type, 0x40000000_00000000_00000000_00000000));
+}