From 7fb3683c32fc24d12452f49e0c1639531e57c47d Mon Sep 17 00:00:00 2001 From: kcbanner Date: Tue, 13 Dec 2022 01:19:21 -0500 Subject: [PATCH] cbe: more msvc compatibility work - Add .StaticInitializer to ValueRenderLocation to indicate that the emitted values must be constant expressions (no function calls, struct casting). - Add new path for special float types (nan, inf) that works in constant expressions - Implement windows.teb() using a syscall for .stage2_c because x64 MSVC doesn't support any kind of inline asm --- lib/std/os/windows.zig | 43 +++++++++++++++++++++--- lib/zig.h | 74 +++++++++++++++++++++++++++++++++++++----- src/codegen/c.zig | 71 +++++++++++++++++++++++----------------- 3 files changed, 146 insertions(+), 42 deletions(-) diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index f261b9cae1..efd7c805cf 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1782,10 +1782,30 @@ pub fn teb() *TEB { \\ movl %%fs:0x18, %[ptr] : [ptr] "=r" (-> *TEB), ), - .x86_64 => asm volatile ( - \\ movq %%gs:0x30, %[ptr] - : [ptr] "=r" (-> *TEB), - ), + .x86_64 => blk: { + if (builtin.zig_backend == .stage2_c) { + // TODO: __asm is not available on x64 MSVC. This is a workaround + // until an intrinsic to read the gs register is available + var thread_information: THREAD_BASIC_INFORMATION = undefined; + const result = ntdll.NtQueryInformationThread( + kernel32.GetCurrentThread(), + .ThreadBasicInformation, + &thread_information, + @sizeOf(THREAD_BASIC_INFORMATION), + null); + + if (result == .SUCCESS) { + break :blk @ptrCast(*TEB, @alignCast(@alignOf(TEB), thread_information.TebBaseAddress)); + } else { + unexpectedStatus(result) catch unreachable; + } + } else { + break :blk asm volatile ( + \\ movq %%gs:0x30, %[ptr] + : [ptr] "=r" (-> *TEB), + ); + } + }, .aarch64 => asm volatile ( \\ mov %[ptr], x18 : [ptr] "=r" (-> *TEB), @@ -3455,6 +3475,21 @@ pub const ASSEMBLY_STORAGE_MAP = opaque {}; pub const FLS_CALLBACK_INFO = opaque {}; pub const RTL_BITMAP = opaque {}; pub const KAFFINITY = usize; +pub const KPRIORITY = i32; + +pub const CLIENT_ID = extern struct { + UniqueProcess: HANDLE, + UniqueThread: HANDLE, +}; + +pub const THREAD_BASIC_INFORMATION = extern struct { + ExitStatus: NTSTATUS, + TebBaseAddress: PVOID, + ClientId: CLIENT_ID, + AffinityMask: KAFFINITY, + Priority: KPRIORITY, + BasePriority: KPRIORITY, +}; pub const TEB = extern struct { Reserved1: [12]PVOID, diff --git a/lib/zig.h b/lib/zig.h index 2bced3c33d..b600e0bfc7 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -1151,8 +1151,8 @@ typedef signed __int128 zig_i128; #define zig_as_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) #define zig_as_i128(hi, lo) ((zig_i128)zig_as_u128(hi, lo)) -#define zig_as_init_u128(hi, lo) zig_as_u128(hi, lo) -#define zig_as_init_i128(hi, lo) zig_as_i128(hi, lo) +#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) +#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) #define zig_hi_u128(val) ((zig_u64)((val) >> 64)) #define zig_lo_u128(val) ((zig_u64)((val) >> 0)) #define zig_hi_i128(val) ((zig_i64)((val) >> 64)) @@ -1180,8 +1180,8 @@ 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) }) -#define zig_as_init_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } -#define zig_as_init_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } +#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) } #define zig_hi_u128(val) ((val).hi) #define zig_lo_u128(val) ((val).lo) #define zig_hi_i128(val) ((val).hi) @@ -1342,13 +1342,28 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { } // TODO: Implement -static zig_i128 zig_div_trunc_i128(zig_i128 lhs, zig_i128 rhs); +static zig_i128 zig_div_trunc_i128(zig_i128 lhs, zig_i128 rhs) { + +} // TODO: Implement -static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs); +zig_extern zig_u128 __udivmodti4(zig_u128 lhs, zig_u128 rhs, zig_u128* rem); +static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { + zig_u128 rem; + return __udivmodti4(lhs, rhs, &rem); +}; // TODO: Implement -static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs); +zig_extern zig_i128 __modti3(zig_i128 lhs, zig_i128 rhs); +static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { + return __modti3(lhs, rhs); +} + +// TODO: Implement +zig_extern zig_u128 __umodti3(zig_u128 lhs, zig_u128 rhs); +static zig_u128 zig_rem_u128(zig_u128 lhs, zig_u128 rhs) { + return __umodti3(lhs, rhs); +} static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { zig_i128 rem = zig_rem_i128(lhs, rhs); @@ -1413,8 +1428,16 @@ 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); } -// TODO: Implement -static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs); +#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); @@ -1636,14 +1659,22 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { /* ========================= Floating Point Support ========================= */ #if _MSC_VER +#define zig_msvc_flt_inf ((double)(1e+300 * 1e+300)) +#define zig_msvc_flt_inff ((float)(1e+300 * 1e+300)) +#define zig_msvc_flt_infl ((long double)(1e+300 * 1e+300)) +#define zig_msvc_flt_nan ((double)(zig_msvc_flt_inf * 0.f)) +#define zig_msvc_flt_nanf ((float)(zig_msvc_flt_inf * 0.f)) +#define zig_msvc_flt_nanl ((long double)(zig_msvc_flt_inf * 0.f)) #define __builtin_nan(str) nan(str) #define __builtin_nanf(str) nanf(str) #define __builtin_nanl(str) nanl(str) +#define __builtin_inf() zig_msvc_flt_inf #endif #define zig_has_f16 1 #define zig_bitSizeOf_f16 16 #define zig_libc_name_f16(name) __##name##h +#define zig_as_special_constant_f16(sign, name, arg, repr) zig_as_special_f16(sign, name, arg, repr) #define zig_as_special_f16(sign, name, arg, repr) sign zig_as_f16(__builtin_##name, )(arg) #if FLT_MANT_DIG == 11 typedef float zig_f16; @@ -1669,11 +1700,18 @@ typedef zig_i16 zig_f16; #define zig_as_f16(fp, repr) repr #undef zig_as_special_f16 #define zig_as_special_f16(sign, name, arg, repr) repr +#undef zig_as_special_constant_f16 +#define zig_as_special_constant_f16(sign, name, arg, repr) repr #endif #define zig_has_f32 1 #define zig_bitSizeOf_f32 32 #define zig_libc_name_f32(name) name##f +#if _MSC_VER +#define zig_as_special_constant_f32(sign, name, arg, repr) sign zig_as_f32(zig_msvc_flt_##name, ) +#else +#define zig_as_special_constant_f32(sign, name, arg, repr) zig_as_special_f32(sign, name, arg, repr) +#endif #define zig_as_special_f32(sign, name, arg, repr) sign zig_as_f32(__builtin_##name, )(arg) #if FLT_MANT_DIG == 24 typedef float zig_f32; @@ -1696,11 +1734,18 @@ typedef zig_i32 zig_f32; #define zig_as_f32(fp, repr) repr #undef zig_as_special_f32 #define zig_as_special_f32(sign, name, arg, repr) repr +#undef zig_as_special_constant_f32 +#define zig_as_special_constant_f32(sign, name, arg, repr) repr #endif #define zig_has_f64 1 #define zig_bitSizeOf_f64 64 #define zig_libc_name_f64(name) name +#if _MSC_VER +#define zig_as_special_constant_f64(sign, name, arg, repr) sign zig_as_f64(zig_msvc_flt_##name, ) +#else +#define zig_as_special_constant_f64(sign, name, arg, repr) zig_as_special_f64(sign, name, arg, repr) +#endif #define zig_as_special_f64(sign, name, arg, repr) sign zig_as_f64(__builtin_##name, )(arg) #if FLT_MANT_DIG == 53 typedef float zig_f64; @@ -1726,11 +1771,14 @@ typedef zig_i64 zig_f64; #define zig_as_f64(fp, repr) repr #undef zig_as_special_f64 #define zig_as_special_f64(sign, name, arg, repr) repr +#undef zig_as_special_constant_f64 +#define zig_as_special_constant_f64(sign, name, arg, repr) repr #endif #define zig_has_f80 1 #define zig_bitSizeOf_f80 80 #define zig_libc_name_f80(name) __##name##x +#define zig_as_special_constant_f80(sign, name, arg, repr) zig_as_special_f80(sign, name, arg, repr) #define zig_as_special_f80(sign, name, arg, repr) sign zig_as_f80(__builtin_##name, )(arg) #if FLT_MANT_DIG == 64 typedef float zig_f80; @@ -1759,11 +1807,14 @@ typedef zig_i128 zig_f80; #define zig_as_f80(fp, repr) repr #undef zig_as_special_f80 #define zig_as_special_f80(sign, name, arg, repr) repr +#undef zig_as_special_constant_f80 +#define zig_as_special_constant_f80(sign, name, arg, repr) repr #endif #define zig_has_f128 1 #define zig_bitSizeOf_f128 128 #define zig_libc_name_f128(name) name##q +#define zig_as_special_constant_f128(sign, name, arg, repr) zig_as_special_f128(sign, name, arg, repr) #define zig_as_special_f128(sign, name, arg, repr) sign zig_as_f128(__builtin_##name, )(arg) #if FLT_MANT_DIG == 113 typedef float zig_f128; @@ -1794,10 +1845,13 @@ typedef zig_i128 zig_f128; #define zig_as_f128(fp, repr) repr #undef zig_as_special_f128 #define zig_as_special_f128(sign, name, arg, repr) repr +#undef zig_as_special_constant_f128 +#define zig_as_special_constant_f128(sign, name, arg, repr) repr #endif #define zig_has_c_longdouble 1 #define zig_libc_name_c_longdouble(name) name##l +#define zig_as_special_constant_c_longdouble(sign, name, arg, repr) zig_as_special_c_longdouble(sign, name, arg, repr) #define zig_as_special_c_longdouble(sign, name, arg, repr) sign __builtin_##name##l(arg) #if !_MSC_VER // TODO: Is there a better way to detect this is just double? typedef long double zig_c_longdouble; @@ -1811,6 +1865,8 @@ typedef zig_i128 zig_c_longdouble; #define zig_as_c_longdouble(fp, repr) repr #undef zig_as_special_c_longdouble #define zig_as_special_c_longdouble(sign, name, arg, repr) repr +#undef zig_as_special_constant_c_longdouble +#define zig_as_special_constant_c_longdouble(sign, name, arg, repr) repr #endif #define zig_cast_f16 (zig_f16) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 444e76c212..7bfa59ea41 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -711,6 +711,10 @@ pub const DeclGen = struct { val = rt.data; } const target = dg.module.getTarget(); + const initializer_type: ValueRenderLocation = switch (location) { + .StaticInitializer => .StaticInitializer, + else => .Initializer, + }; const safety_on = switch (dg.module.optimizeMode()) { .Debug, .ReleaseSafe => true, @@ -785,9 +789,9 @@ pub const DeclGen = struct { } try writer.writeAll("{ .payload = "); - try dg.renderValue(writer, payload_ty, val, .Initializer); + try dg.renderValue(writer, payload_ty, val, initializer_type); try writer.writeAll(", .is_null = "); - try dg.renderValue(writer, Type.bool, val, .Initializer); + try dg.renderValue(writer, Type.bool, val, initializer_type); return writer.writeAll(" }"); }, .Struct => switch (ty.containerLayout()) { @@ -804,7 +808,7 @@ pub const DeclGen = struct { if (!field.ty.hasRuntimeBits()) continue; if (!empty) try writer.writeByte(','); - try dg.renderValue(writer, field.ty, val, .Initializer); + try dg.renderValue(writer, field.ty, val, initializer_type); empty = false; } @@ -825,14 +829,14 @@ pub const DeclGen = struct { const layout = ty.unionGetLayout(target); if (layout.tag_size != 0) { try writer.writeAll(" .tag = "); - try dg.renderValue(writer, tag_ty, val, .Initializer); + try dg.renderValue(writer, tag_ty, val, initializer_type); try writer.writeByte(','); } try writer.writeAll(" .payload = {"); } for (ty.unionFields().values()) |field| { if (!field.ty.hasRuntimeBits()) continue; - try dg.renderValue(writer, field.ty, val, .Initializer); + try dg.renderValue(writer, field.ty, val, initializer_type); break; } else try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); @@ -846,7 +850,7 @@ pub const DeclGen = struct { } try writer.writeAll("{ .payload = "); - try dg.renderValue(writer, ty.errorUnionPayload(), val, .Initializer); + try dg.renderValue(writer, ty.errorUnionPayload(), val, initializer_type); return writer.print(", .error = {x} }}", .{ try dg.fmtIntLiteral(ty.errorUnionSet(), val), }); @@ -873,7 +877,7 @@ pub const DeclGen = struct { var index: usize = 0; while (index < c_len) : (index += 1) { if (index > 0) try writer.writeAll(", "); - try dg.renderValue(writer, ty.childType(), val, .Initializer); + try dg.renderValue(writer, ty.childType(), val, initializer_type); } return writer.writeByte('}'); } @@ -957,7 +961,7 @@ pub const DeclGen = struct { } try writer.writeAll(", "); empty = false; - } else if (location != .StaticInitializer) { + } else { // isSignalNan is equivalent to isNan currently, and MSVC doens't have nans, so prefer nan const operation = if (std.math.isNan(f128_val)) "nan" @@ -968,7 +972,19 @@ pub const DeclGen = struct { else unreachable; + if (location == .StaticInitializer) { + if (!std.math.isNan(f128_val) and std.math.isSignalNan(f128_val)) + return dg.fail("TODO: C backend: implement nans rendering in static initializers", .{}); + + // MSVC doesn't have a way to define a custom or signaling NaN value in a constant expression + + // TODO: Re-enable this check, otherwise we're writing qnan bit patterns on msvc incorrectly + // if (std.math.isNan(f128_val) and f128_val != std.math.qnan_f128) + // return dg.fail("Only quiet nans are supported in global variable initializers", .{}); + } + try writer.writeAll("zig_as_special_"); + if (location == .StaticInitializer) try writer.writeAll("constant_"); try dg.renderTypeForBuiltinFnName(writer, ty); try writer.writeByte('('); if (std.math.signbit(f128_val)) try writer.writeByte('-'); @@ -987,7 +1003,6 @@ pub const DeclGen = struct { }; try writer.writeAll(", "); empty = false; - } try writer.print("{x}", .{try dg.fmtIntLiteralLoc(int_ty, int_val, location)}); if (!empty) try writer.writeByte(')'); @@ -1022,9 +1037,9 @@ pub const DeclGen = struct { var buf: Type.SlicePtrFieldTypeBuffer = undefined; try writer.writeByte('{'); - try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr, .Initializer); + try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr, initializer_type); try writer.writeAll(", "); - try dg.renderValue(writer, Type.usize, slice.len, .Initializer); + try dg.renderValue(writer, Type.usize, slice.len, initializer_type); try writer.writeByte('}'); }, .function => { @@ -1062,7 +1077,7 @@ pub const DeclGen = struct { try writer.writeByte('{'); const ai = ty.arrayInfo(); if (ai.sentinel) |s| { - try dg.renderValue(writer, ai.elem_type, s, .Initializer); + try dg.renderValue(writer, ai.elem_type, s, initializer_type); } else { try writer.writeByte('0'); } @@ -1085,6 +1100,7 @@ pub const DeclGen = struct { // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal const max_string_initializer_len = 65535; + const ai = ty.arrayInfo(); if (ai.elem_type.eql(Type.u8, dg.module)) { if (ai.len <= max_string_initializer_len) { @@ -1112,7 +1128,7 @@ pub const DeclGen = struct { } if (ai.sentinel) |s| { if (index != 0) try writer.writeByte(','); - try dg.renderValue(writer, ai.elem_type, s, .Initializer); + try dg.renderValue(writer, ai.elem_type, s, initializer_type); } try writer.writeByte('}'); } @@ -1122,11 +1138,11 @@ pub const DeclGen = struct { while (index < ai.len) : (index += 1) { if (index != 0) try writer.writeByte(','); const elem_val = try val.elemValue(dg.module, arena_allocator, index); - try dg.renderValue(writer, ai.elem_type, elem_val, .Initializer); + try dg.renderValue(writer, ai.elem_type, elem_val, initializer_type); } if (ai.sentinel) |s| { if (index != 0) try writer.writeByte(','); - try dg.renderValue(writer, ai.elem_type, s, .Initializer); + try dg.renderValue(writer, ai.elem_type, s, initializer_type); } try writer.writeByte('}'); } @@ -1162,9 +1178,9 @@ pub const DeclGen = struct { const payload_val = if (val.castTag(.opt_payload)) |pl| pl.data else Value.undef; try writer.writeAll("{ .payload = "); - try dg.renderValue(writer, payload_ty, payload_val, .Initializer); + try dg.renderValue(writer, payload_ty, payload_val, initializer_type); try writer.writeAll(", .is_null = "); - try dg.renderValue(writer, Type.bool, is_null_val, .Initializer); + try dg.renderValue(writer, Type.bool, is_null_val, initializer_type); try writer.writeAll(" }"); }, .ErrorSet => { @@ -1197,9 +1213,9 @@ pub const DeclGen = struct { const error_val = if (val.errorUnionIsPayload()) Value.zero else val; try writer.writeAll("{ .payload = "); - try dg.renderValue(writer, payload_ty, payload_val, .Initializer); + try dg.renderValue(writer, payload_ty, payload_val, initializer_type); try writer.writeAll(", .error = "); - try dg.renderValue(writer, error_ty, error_val, .Initializer); + try dg.renderValue(writer, error_ty, error_val, initializer_type); try writer.writeAll(" }"); }, .Enum => { @@ -1264,10 +1280,7 @@ pub const DeclGen = struct { if (!field_ty.hasRuntimeBits()) continue; if (!empty) try writer.writeByte(','); - try dg.renderValue(writer, field_ty, field_val, switch (location) { - .StaticInitializer => .StaticInitializer, - else => .Initializer, - }); + try dg.renderValue(writer, field_ty, field_val, initializer_type); empty = false; } @@ -1297,7 +1310,7 @@ pub const DeclGen = struct { if (eff_num_fields == 0) { try writer.writeByte('('); - try dg.renderValue(writer, ty, Value.undef, .Initializer); + try dg.renderValue(writer, ty, Value.undef, initializer_type); try writer.writeByte(')'); } else if (ty.bitSize(target) > 64) { // zig_or_u128(zig_or_u128(zig_shl_u128(a, a_off), zig_shl_u128(b, b_off)), zig_shl_u128(c, c_off)) @@ -1385,7 +1398,7 @@ pub const DeclGen = struct { try dg.renderTypecast(writer, ty); try writer.writeByte(')'); } - try dg.renderValue(writer, field_ty, union_obj.val, .Initializer); + try dg.renderValue(writer, field_ty, union_obj.val, initializer_type); } else { try writer.writeAll("0"); } @@ -1397,7 +1410,7 @@ pub const DeclGen = struct { const layout = ty.unionGetLayout(target); if (layout.tag_size != 0) { try writer.writeAll(".tag = "); - try dg.renderValue(writer, tag_ty, union_obj.tag, .Initializer); + try dg.renderValue(writer, tag_ty, union_obj.tag, initializer_type); try writer.writeAll(", "); } try writer.writeAll(".payload = {"); @@ -1406,11 +1419,11 @@ pub const DeclGen = struct { var it = ty.unionFields().iterator(); if (field_ty.hasRuntimeBits()) { try writer.print(".{ } = ", .{fmtIdent(field_name)}); - try dg.renderValue(writer, field_ty, union_obj.val, .Initializer); + try dg.renderValue(writer, field_ty, union_obj.val, initializer_type); } else while (it.next()) |field| { if (!field.value_ptr.ty.hasRuntimeBits()) continue; try writer.print(".{ } = ", .{fmtIdent(field.key_ptr.*)}); - try dg.renderValue(writer, field.value_ptr.ty, Value.undef, .Initializer); + try dg.renderValue(writer, field.value_ptr.ty, Value.undef, initializer_type); break; } else try writer.writeAll(".empty_union = 0"); if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); @@ -7239,7 +7252,7 @@ fn formatIntLiteral( else => { if (int_info.bits > 64 and data.location != null and data.location.? == .StaticInitializer) { // MSVC treats casting the struct initializer as not constant (C2099), so an alternate form is used in global initializers - try writer.print("zig_as_init_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); + try writer.print("zig_as_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); } else { try writer.print("zig_as_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); }