From b2d0675f90cc4e26fdf7a81c1edda8f1b8a00f1f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 18 Oct 2026 17:49:31 -0400 Subject: [PATCH] Type: make `c_longdouble` an invalid type without `x87` Also invert `xclang_flags` loop nesting which will make it easier to translate features to something without `-Xclang` in the future. --- lib/std/Target.zig | 18 ++++++++-------- lib/std/math/float.zig | 9 ++++---- lib/std/zig/AstGen.zig | 16 ++++++++++++++ lib/std/zig/Zir.zig | 6 ++++++ src/Compilation.zig | 49 +++++++++++++++++++++--------------------- src/Sema.zig | 13 +++++++++++ src/Type.zig | 5 ++++- src/print_zir.zig | 10 +++++++-- 8 files changed, 86 insertions(+), 40 deletions(-) diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 629f07a5c8b56ffb7370b41a1a6f983edb6d82c3..bba3026b739c3dfd1da086be5ac6114513f83a01 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -3212,7 +3212,7 @@ pub const CType = enum { longdouble, }; -/// Returns `null` if no C ABI is defined for this target. +/// Returns `null` if no C ABI for `c_type` is defined for this target. pub fn cTypeByteSize(t: *const Target, c_type: CType) ?u16 { return switch (c_type) { .char, @@ -3238,7 +3238,7 @@ pub fn cTypeByteSize(t: *const Target, c_type: CType) ?u16 { }; } -/// Returns `null` if no C ABI is defined for this target. +/// Returns `null` if no C ABI for `c_type` is defined for this target. pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 { return switch (target.os.tag) { .freestanding, @@ -3285,7 +3285,7 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 { .x86, .x86_64, - => 80, + => if (target.cpu.has(.x86, .x87)) 80 else null, .alpha, .riscv32, @@ -3345,7 +3345,7 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 { .x86_64, => switch (target.abi) { .android, .ohos => 64, - else => 80, + else => if (target.cpu.has(.x86, .x87)) 80 else null, }, .powerpc, @@ -3409,7 +3409,7 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 { .long, .ulong => 32, .longlong, .ulonglong, .double => 64, .longdouble => switch (target.abi) { - .gnu => 80, + .gnu => if (target.cpu.has(.x86, .x87)) 80 else null, else => 64, }, }, @@ -3420,7 +3420,7 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 { .long, .ulong => 32, .longlong, .ulonglong, .double => 64, .longdouble => switch (target.abi) { - .gnu => 80, + .gnu => if (target.cpu.has(.x86, .x87)) 80 else null, else => 64, }, }, @@ -3454,7 +3454,7 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 { }, .longlong, .ulonglong, .double => 64, .longdouble => switch (target.cpu.arch) { - .x86_64 => 80, + .x86_64 => if (target.cpu.has(.x86, .x87)) 80 else null, else => 64, }, }, @@ -3492,14 +3492,14 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 { .short, .ushort => 16, .int, .uint, .float => 32, .long, .ulong, .longlong, .ulonglong, .double => 64, - .longdouble => 80, + .longdouble => if (target.cpu.has(.x86, .x87)) 80 else null, }, .opengl => null, }; } -/// Returns `null` if no C ABI is defined for this target. +/// Returns `null` if no C ABI for `c_type` is defined for this target. pub fn cTypeAlignment(target: *const Target, c_type: CType) ?u16 { // Overrides for unusual alignments switch (target.cpu.arch) { diff --git a/lib/std/math/float.zig b/lib/std/math/float.zig index e84bcdc4f295e1edad123673991df5b85f1cea4c..0b16875cc3559756a91ac10f96afee9d9a9fb5f1 100644 --- a/lib/std/math/float.zig +++ b/lib/std/math/float.zig @@ -12,10 +12,11 @@ pub const long_double = struct { inline fn bitWidth(x: anytype) u16 { const T = @TypeOf(x); - return switch (T) { - f80, f128, c_longdouble => @typeInfo(T).float.bits, - else => @compileError("Unsupported type: " ++ @typeName(T) ++ "\nPass a `f80`, `f128`, or `c_longdouble`."), - }; + switch (@typeInfo(T)) { + .float => |float| if (float.bits >= 80) return float.bits, + else => {}, + } + @compileError("Unsupported type: " ++ @typeName(T) ++ "\nPass a `f80`, `f128`, or `c_longdouble`."); } /// Returns the sign + exponent bits of a `long double`. diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 4260ea1ce9d46694f484d912eae78f25a7d7f1f1..d29faf0747dcef2aa1b0ae828c94b947bc00ca64 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -8140,6 +8140,22 @@ fn identifier( // if not @"" syntax, just use raw token slice if (ident_name_raw[0] != '@') { if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| { + const maybe_c_type: ?std.Target.CType = switch (zir_const_ref) { + else => null, + .c_char_type => .char, + .c_short_type => .short, + .c_ushort_type => .ushort, + .c_int_type => .int, + .c_uint_type => .uint, + .c_long_type => .long, + .c_ulong_type => .ulong, + .c_longlong_type => .longlong, + .c_ulonglong_type => .ulonglong, + .c_longdouble_type => .longdouble, + }; + if (maybe_c_type) |c_type| { + _ = try gz.addExtendedNodeSmall(.validate_c_type, ident, @backingInt(c_type)); + } return rvalue(gz, ri, zir_const_ref, ident); } diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 17874289d8ed6b38f65309b86bbd0cddea8a8aee..ee43492c8fe9d26ec3f3484f7f1a7804e1c9869c 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -2186,6 +2186,11 @@ pub const Inst = struct { /// For example: Provided with `error{Foo}!?f64`, returns `f64`. /// `operand` is payload index to `UnNode`. float_op_result_ty, + /// Validate that the given `CType` is valid for the current target. + /// This can be folded into the `@CType` tag once that is implemented. + /// `operand` is `src_node: Ast.Node.Offset`. + /// `small` is a `std.Target.CType`. + validate_c_type, pub const InstData = struct { opcode: Extended, @@ -4441,6 +4446,7 @@ fn findTrackableInner( .astgen_error, .float_op_result_ty, .round_op_ty, + .validate_c_type, => return, // `@TypeOf` has a body. diff --git a/src/Compilation.zig b/src/Compilation.zig index 5a98fa435ce9f77181cb491f02a656b446cca4e2..38a905723bce8df08bcb2bf1850ecfd9bfe25870 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -6761,39 +6761,40 @@ pub fn addCCArgs( else => &.{"-Xclang"}, }; - for (xclang_flags) |xclang| { - if (target_util.clangSupportsTargetCpuArg(target)) { - if (target.cpu.model.llvm_name) |llvm_name| { + if (target_util.clangSupportsTargetCpuArg(target)) { + if (target.cpu.model.llvm_name) |llvm_name| { + for (xclang_flags) |xclang| { try argv.appendSlice(&[_][]const u8{ xclang, "-target-cpu", xclang, llvm_name, }); } } + } - // It would be really nice if there was a more compact way to communicate this info to Clang. - const all_features_list = target.cpu.arch.allFeaturesList(); - try argv.ensureUnusedCapacity(all_features_list.len * 4); - for (all_features_list, 0..) |feature, index_usize| { - const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize)); - const is_enabled = target.cpu.features.isEnabled(index); + // It would be really nice if there was a more compact way to communicate this info to Clang. + const all_features_list = target.cpu.arch.allFeaturesList(); + try argv.ensureUnusedCapacity(all_features_list.len * xclang_flags.len * 4); + for (all_features_list, 0..) |feature, index_usize| { + const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize)); + const is_enabled = target.cpu.features.isEnabled(index); - if (feature.llvm_name) |llvm_name| { - // We communicate these to Clang through the dedicated options. - if (std.mem.startsWith(u8, llvm_name, "soft-float") or - std.mem.startsWith(u8, llvm_name, "hard-float") or - (target.cpu.arch.isPowerPC() and std.mem.startsWith(u8, llvm_name, "64bit")) or - (target.cpu.arch.isX86() and std.mem.startsWith(u8, llvm_name, "x32")) or - (target.cpu.arch == .s390x and std.mem.eql(u8, llvm_name, "backchain"))) - continue; + if (feature.llvm_name) |llvm_name| { + // We communicate these to Clang through the dedicated options. + if (std.mem.startsWith(u8, llvm_name, "soft-float") or + std.mem.startsWith(u8, llvm_name, "hard-float") or + (target.cpu.arch.isPowerPC() and std.mem.startsWith(u8, llvm_name, "64bit")) or + (target.cpu.arch.isX86() and std.mem.startsWith(u8, llvm_name, "x32")) or + (target.cpu.arch == .s390x and std.mem.eql(u8, llvm_name, "backchain"))) + continue; - // Ignore these until we figure out how to handle the concept of omitting features. - // See https://github.com/ziglang/zig/issues/23539 - if (target_util.isDynamicAMDGCNFeature(target, feature)) continue; + // Ignore these until we figure out how to handle the concept of omitting features. + // See https://github.com/ziglang/zig/issues/23539 + if (target_util.isDynamicAMDGCNFeature(target, feature)) continue; - argv.appendSliceAssumeCapacity(&[_][]const u8{ xclang, "-target-feature", xclang }); - const plus_or_minus = "-+"[@intFromBool(is_enabled)]; - const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name }); - argv.appendAssumeCapacity(arg); + const plus_or_minus = "-+"[@intFromBool(is_enabled)]; + const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name }); + for (xclang_flags) |xclang| { + argv.appendSliceAssumeCapacity(&[_][]const u8{ xclang, "-target-feature", xclang, arg }); } } } diff --git a/src/Sema.zig b/src/Sema.zig index 208bde58a95cd3c8b0dbf5b5ce6d68d67804e092..7f497ea7c2e43a0e596751c2ffe4f6cb8123fd74 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1469,6 +1469,11 @@ fn analyzeBodyInner( }, .astgen_error => return sema.failTransitive(.astgen_error), .float_op_result_ty => try sema.zirFloatOpResultType(block, extended), + .validate_c_type => { + try sema.zirValidateCType(block, extended); + i += 1; + continue; + }, }; }, @@ -4315,6 +4320,14 @@ fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: boo } } +fn zirValidateCType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { + const c_type: std.Target.CType = @fromBackingInt(@intCast(extended.small)); + if (sema.pt.zcu.getTarget().cTypeBitSize(c_type)) |_| return; + const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@bitCast(extended.operand)); + const src = block.nodeOffset(src_node); + return sema.fail(block, src, "'c_{t}' is not valid on this target", .{c_type}); +} + fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; diff --git a/src/Type.zig b/src/Type.zig index 7a4256263882de8fe68ec8331e256b3dabd53db8..f2707c942e2f24a1e94f5f9058b23db4f3eccc4b 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -3178,7 +3178,10 @@ pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool }, .float => switch (ty.floatBits(zcu.getTarget())) { else => true, - 80 => |bits| std.zig.target.compilerRtFloatAbi(zcu.getTarget(), bits) == .hard, + 80 => |bits| switch (std.zig.target.compilerRtFloatAbi(zcu.getTarget(), bits)) { + .soft => false, + .hard => true, + }, }, .@"fn" => { if (position != .other) return false; diff --git a/src/print_zir.zig b/src/print_zir.zig index 427acc16ebbbbb30109b57ca7f2170330183ba97..191fa6e7aa783bbb56b03bd6a4e12dea6a6acb78 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -712,8 +712,14 @@ const Writer = struct { .std_lang_value => try self.writeStdLangValue(stream, extended), .inplace_arith_result_ty => try self.writeInplaceArithResultTy(stream, extended), - .dbg_empty_stmt => try stream.writeAll("))"), - .astgen_error => try stream.writeAll("))"), + .dbg_empty_stmt, .astgen_error => try stream.writeAll("))"), + + .validate_c_type => { + const c_type: std.Target.CType = @fromBackingInt(@intCast(extended.small)); + const src_node: Ast.Node.Offset = @fromBackingInt(@bitCast(extended.operand)); + try stream.print("{t})) ", .{c_type}); + try self.writeSrcNode(stream, src_node); + }, } } -- 2.54.0