From 21f5f06f1f66c9f849ae39d868c2d4548078231f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 29 Jul 2025 23:49:19 +0300 Subject: [PATCH] Compilation: avoid passing Clang specific options to Aro --- src/Compilation.zig | 488 +++++++++++++++++++++++++------------------- src/main.zig | 1 - 2 files changed, 275 insertions(+), 214 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index cf911bd348..26eebab98d 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -6608,14 +6608,43 @@ pub fn addTranslateCCArgs( out_dep_path: ?[]const u8, owner_mod: *Package.Module, ) !void { + const target = &owner_mod.resolved_target.result; + try argv.appendSlice(&.{ "-x", "c" }); - try comp.addCCArgs(arena, argv, ext, out_dep_path, owner_mod); - // This gives us access to preprocessing entities, presumably at the cost of performance. - try argv.appendSlice(&.{ "-Xclang", "-detailed-preprocessing-record" }); + + const resource_path = try comp.dirs.zig_lib.join(arena, &.{"compiler/aro/include"}); + try argv.appendSlice(&.{ "-isystem", resource_path }); + + try comp.addCommonCCArgs(arena, argv, ext, out_dep_path, owner_mod); + + try argv.appendSlice(&[_][]const u8{ "-target", try target.zigTriple(arena) }); + + const mcpu = mcpu: { + var buf: std.ArrayListUnmanaged(u8) = .empty; + defer buf.deinit(comp.gpa); + + try buf.print(comp.gpa, "-mcpu={s}", .{target.cpu.model.name}); + + // TODO better serialization https://github.com/ziglang/zig/issues/4584 + 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); + + const plus_or_minus = "-+"[@intFromBool(is_enabled)]; + try buf.print(comp.gpa, "{c}{s}", .{ plus_or_minus, feature.name }); + } + break :mcpu try buf.toOwnedSlice(arena); + }; + try argv.append(mcpu); + + try argv.appendSlice(comp.global_cc_argv); + try argv.appendSlice(owner_mod.cc_argv); } /// Add common C compiler args between translate-c and C object compilation. -pub fn addCCArgs( +fn addCommonCCArgs( comp: *const Compilation, arena: Allocator, argv: *std.array_list.Managed([]const u8), @@ -6625,32 +6654,13 @@ pub fn addCCArgs( ) !void { const target = &mod.resolved_target.result; - // As of Clang 16.x, it will by default read extra flags from /etc/clang. - // I'm sure the person who implemented this means well, but they have a lot - // to learn about abstractions and where the appropriate boundaries between - // them are. The road to hell is paved with good intentions. Fortunately it - // can be disabled. - try argv.append("--no-default-config"); - - // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode - // we want Clang to infer it, and in normal mode we always want it off, which will be true since - // clang will detect stderr as a pipe rather than a terminal. - if (!comp.clang_passthrough_mode and ext.clangSupportsDiagnostics()) { - // Make stderr more easily parseable. - try argv.append("-fno-caret-diagnostics"); + if (target_util.supports_fpic(target)) { + // PIE needs to go before PIC because Clang interprets `-fno-PIE` to imply `-fno-PIC`, which + // we don't necessarily want. + try argv.append(if (comp.config.pie) "-fPIE" else "-fno-PIE"); + try argv.append(if (mod.pic) "-fPIC" else "-fno-PIC"); } - // We never want clang to invoke the system assembler for anything. So we would want - // this option always enabled. However, it only matters for some targets. To avoid - // "unused parameter" warnings, and to keep CLI spam to a minimum, we only put this - // flag on the command line if it is necessary. - if (target_util.clangMightShellOutForAssembly(target)) { - try argv.append("-integrated-as"); - } - - const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target); - try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple }); - switch (target.os.tag) { .ios, .macos, .tvos, .watchos => |os| { try argv.ensureUnusedCapacity(2); @@ -6676,78 +6686,6 @@ pub fn addCCArgs( else => {}, } - const xclang_flag = switch (ext) { - .assembly, .assembly_with_cpp => "-Xclangas", - else => "-Xclang", - }; - - if (target_util.clangSupportsTargetCpuArg(target)) { - if (target.cpu.model.llvm_name) |llvm_name| { - try argv.appendSlice(&[_][]const u8{ - xclang_flag, "-target-cpu", xclang_flag, 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); - - if (feature.llvm_name) |llvm_name| { - // We communicate float ABI to Clang through the dedicated options. - if (std.mem.startsWith(u8, llvm_name, "soft-float") or - std.mem.startsWith(u8, llvm_name, "hard-float")) - 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_flag, "-target-feature", xclang_flag }); - const plus_or_minus = "-+"[@intFromBool(is_enabled)]; - const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name }); - argv.appendAssumeCapacity(arg); - } - } - - if (target.cpu.arch.isThumb()) { - try argv.append(switch (ext) { - .assembly, .assembly_with_cpp => "-Wa,-mthumb", - else => "-mthumb", - }); - } - - if (target_util.llvmMachineAbi(target)) |mabi| { - // Clang's integrated Arm assembler doesn't support `-mabi` yet... - // Clang's FreeBSD driver doesn't support `-mabi` on PPC64 (ELFv2 is used anyway). - if (!(target.cpu.arch.isArm() and (ext == .assembly or ext == .assembly_with_cpp)) and - !(target.cpu.arch.isPowerPC64() and target.os.tag == .freebsd)) - { - try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{mabi})); - } - } - - // We might want to support -mfloat-abi=softfp for Arm and CSKY here in the future. - if (target_util.clangSupportsFloatAbiArg(target)) { - const fabi = @tagName(target.abi.float()); - - try argv.append(switch (target.cpu.arch) { - // For whatever reason, Clang doesn't support `-mfloat-abi` for s390x. - .s390x => try std.fmt.allocPrint(arena, "-m{s}-float", .{fabi}), - else => try std.fmt.allocPrint(arena, "-mfloat-abi={s}", .{fabi}), - }); - } - - if (target_util.supports_fpic(target)) { - // PIE needs to go before PIC because Clang interprets `-fno-PIE` to imply `-fno-PIC`, which - // we don't necessarily want. - try argv.append(if (comp.config.pie) "-fPIE" else "-fno-PIE"); - try argv.append(if (mod.pic) "-fPIC" else "-fno-PIC"); - } - if (comp.mingw_unicode_entry_point) { try argv.append("-municode"); } @@ -6784,45 +6722,6 @@ pub fn addCCArgs( if (ext != .assembly) { try argv.append(if (target.os.tag == .freestanding) "-ffreestanding" else "-fhosted"); - if (target_util.clangSupportsNoImplicitFloatArg(target) and target.abi.float() == .soft) { - try argv.append("-mno-implicit-float"); - } - - if (target_util.hasRedZone(target)) { - try argv.append(if (mod.red_zone) "-mred-zone" else "-mno-red-zone"); - } - - try argv.append(if (mod.omit_frame_pointer) "-fomit-frame-pointer" else "-fno-omit-frame-pointer"); - - const ssp_buf_size = mod.stack_protector; - if (ssp_buf_size != 0) { - try argv.appendSlice(&[_][]const u8{ - "-fstack-protector-strong", - "--param", - try std.fmt.allocPrint(arena, "ssp-buffer-size={d}", .{ssp_buf_size}), - }); - } else { - try argv.append("-fno-stack-protector"); - } - - try argv.append(if (mod.no_builtin) "-fno-builtin" else "-fbuiltin"); - - try argv.append(if (comp.function_sections) "-ffunction-sections" else "-fno-function-sections"); - try argv.append(if (comp.data_sections) "-fdata-sections" else "-fno-data-sections"); - - switch (mod.unwind_tables) { - .none => { - try argv.append("-fno-unwind-tables"); - try argv.append("-fno-asynchronous-unwind-tables"); - }, - .sync => { - // Need to override Clang's convoluted default logic. - try argv.append("-fno-asynchronous-unwind-tables"); - try argv.append("-funwind-tables"); - }, - .async => try argv.append("-fasynchronous-unwind-tables"), - } - try argv.append("-nostdinc"); if (ext == .cpp or ext == .hpp) { @@ -6961,80 +6860,6 @@ pub fn addCCArgs( else => {}, } - // Only assembly files support these flags. - switch (ext) { - .assembly, - .assembly_with_cpp, - => { - // The Clang assembler does not accept the list of CPU features like the - // compiler frontend does. Therefore we must hard-code the -m flags for - // all CPU features here. - switch (target.cpu.arch) { - .riscv32, .riscv32be, .riscv64, .riscv64be => { - const RvArchFeat = struct { char: u8, feat: std.Target.riscv.Feature }; - const letters = [_]RvArchFeat{ - .{ .char = 'm', .feat = .m }, - .{ .char = 'a', .feat = .a }, - .{ .char = 'f', .feat = .f }, - .{ .char = 'd', .feat = .d }, - .{ .char = 'c', .feat = .c }, - }; - const prefix: []const u8 = if (target.cpu.arch == .riscv64) "rv64" else "rv32"; - const prefix_len = 4; - assert(prefix.len == prefix_len); - var march_buf: [prefix_len + letters.len + 1]u8 = undefined; - var march_index: usize = prefix_len; - @memcpy(march_buf[0..prefix.len], prefix); - - if (target.cpu.has(.riscv, .e)) { - march_buf[march_index] = 'e'; - } else { - march_buf[march_index] = 'i'; - } - march_index += 1; - - for (letters) |letter| { - if (target.cpu.has(.riscv, letter.feat)) { - march_buf[march_index] = letter.char; - march_index += 1; - } - } - - const march_arg = try std.fmt.allocPrint(arena, "-march={s}", .{ - march_buf[0..march_index], - }); - try argv.append(march_arg); - - if (target.cpu.has(.riscv, .relax)) { - try argv.append("-mrelax"); - } else { - try argv.append("-mno-relax"); - } - if (target.cpu.has(.riscv, .save_restore)) { - try argv.append("-msave-restore"); - } else { - try argv.append("-mno-save-restore"); - } - }, - .mips, .mipsel, .mips64, .mips64el => { - if (target.cpu.model.llvm_name) |llvm_name| { - try argv.append(try std.fmt.allocPrint(arena, "-march={s}", .{llvm_name})); - } - }, - else => { - // TODO - }, - } - - if (target_util.clangAssemblerSupportsMcpuArg(target)) { - if (target.cpu.model.llvm_name) |llvm_name| { - try argv.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{llvm_name})); - } - } - }, - else => {}, - } - // Only compiled files support these flags. switch (ext) { .c, @@ -7133,6 +6958,243 @@ pub fn addCCArgs( }, else => {}, } +} + +/// Add common C compiler args and Clang specific args. +pub fn addCCArgs( + comp: *const Compilation, + arena: Allocator, + argv: *std.ArrayList([]const u8), + ext: FileExt, + out_dep_path: ?[]const u8, + mod: *Package.Module, +) !void { + const target = &mod.resolved_target.result; + + // As of Clang 16.x, it will by default read extra flags from /etc/clang. + // I'm sure the person who implemented this means well, but they have a lot + // to learn about abstractions and where the appropriate boundaries between + // them are. The road to hell is paved with good intentions. Fortunately it + // can be disabled. + try argv.append("--no-default-config"); + + // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode + // we want Clang to infer it, and in normal mode we always want it off, which will be true since + // clang will detect stderr as a pipe rather than a terminal. + if (!comp.clang_passthrough_mode and ext.clangSupportsDiagnostics()) { + // Make stderr more easily parseable. + try argv.append("-fno-caret-diagnostics"); + } + + // We never want clang to invoke the system assembler for anything. So we would want + // this option always enabled. However, it only matters for some targets. To avoid + // "unused parameter" warnings, and to keep CLI spam to a minimum, we only put this + // flag on the command line if it is necessary. + if (target_util.clangMightShellOutForAssembly(target)) { + try argv.append("-integrated-as"); + } + + const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target); + try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple }); + + if (target.cpu.arch.isThumb()) { + try argv.append(switch (ext) { + .assembly, .assembly_with_cpp => "-Wa,-mthumb", + else => "-mthumb", + }); + } + + if (target_util.llvmMachineAbi(target)) |mabi| { + // Clang's integrated Arm assembler doesn't support `-mabi` yet... + // Clang's FreeBSD driver doesn't support `-mabi` on PPC64 (ELFv2 is used anyway). + if (!(target.cpu.arch.isArm() and (ext == .assembly or ext == .assembly_with_cpp)) and + !(target.cpu.arch.isPowerPC64() and target.os.tag == .freebsd)) + { + try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{mabi})); + } + } + + // We might want to support -mfloat-abi=softfp for Arm and CSKY here in the future. + if (target_util.clangSupportsFloatAbiArg(target)) { + const fabi = @tagName(target.abi.float()); + + try argv.append(switch (target.cpu.arch) { + // For whatever reason, Clang doesn't support `-mfloat-abi` for s390x. + .s390x => try std.fmt.allocPrint(arena, "-m{s}-float", .{fabi}), + else => try std.fmt.allocPrint(arena, "-mfloat-abi={s}", .{fabi}), + }); + } + + try comp.addCommonCCArgs(arena, argv, ext, out_dep_path, mod); + + // Only assembly files support these flags. + switch (ext) { + .assembly, + .assembly_with_cpp, + => { + // The Clang assembler does not accept the list of CPU features like the + // compiler frontend does. Therefore we must hard-code the -m flags for + // all CPU features here. + switch (target.cpu.arch) { + .riscv32, .riscv32be, .riscv64, .riscv64be => { + const RvArchFeat = struct { char: u8, feat: std.Target.riscv.Feature }; + const letters = [_]RvArchFeat{ + .{ .char = 'm', .feat = .m }, + .{ .char = 'a', .feat = .a }, + .{ .char = 'f', .feat = .f }, + .{ .char = 'd', .feat = .d }, + .{ .char = 'c', .feat = .c }, + }; + const prefix: []const u8 = if (target.cpu.arch == .riscv64) "rv64" else "rv32"; + const prefix_len = 4; + assert(prefix.len == prefix_len); + var march_buf: [prefix_len + letters.len + 1]u8 = undefined; + var march_index: usize = prefix_len; + @memcpy(march_buf[0..prefix.len], prefix); + + if (target.cpu.has(.riscv, .e)) { + march_buf[march_index] = 'e'; + } else { + march_buf[march_index] = 'i'; + } + march_index += 1; + + for (letters) |letter| { + if (target.cpu.has(.riscv, letter.feat)) { + march_buf[march_index] = letter.char; + march_index += 1; + } + } + + const march_arg = try std.fmt.allocPrint(arena, "-march={s}", .{ + march_buf[0..march_index], + }); + try argv.append(march_arg); + + if (target.cpu.has(.riscv, .relax)) { + try argv.append("-mrelax"); + } else { + try argv.append("-mno-relax"); + } + if (target.cpu.has(.riscv, .save_restore)) { + try argv.append("-msave-restore"); + } else { + try argv.append("-mno-save-restore"); + } + }, + .mips, .mipsel, .mips64, .mips64el => { + if (target.cpu.model.llvm_name) |llvm_name| { + try argv.append(try std.fmt.allocPrint(arena, "-march={s}", .{llvm_name})); + } + }, + else => { + // TODO + }, + } + + if (target_util.clangAssemblerSupportsMcpuArg(target)) { + if (target.cpu.model.llvm_name) |llvm_name| { + try argv.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{llvm_name})); + } + } + }, + else => {}, + } + + // Non-preprocessed assembly files don't support these flags. + if (ext != .assembly) { + if (target_util.clangSupportsNoImplicitFloatArg(target) and target.abi.float() == .soft) { + try argv.append("-mno-implicit-float"); + } + + if (target_util.hasRedZone(target)) { + try argv.append(if (mod.red_zone) "-mred-zone" else "-mno-red-zone"); + } + + try argv.append(if (mod.omit_frame_pointer) "-fomit-frame-pointer" else "-fno-omit-frame-pointer"); + + const ssp_buf_size = mod.stack_protector; + if (ssp_buf_size != 0) { + try argv.appendSlice(&[_][]const u8{ + "-fstack-protector-strong", + "--param", + try std.fmt.allocPrint(arena, "ssp-buffer-size={d}", .{ssp_buf_size}), + }); + } else { + try argv.append("-fno-stack-protector"); + } + + try argv.append(if (mod.no_builtin) "-fno-builtin" else "-fbuiltin"); + + try argv.append(if (comp.function_sections) "-ffunction-sections" else "-fno-function-sections"); + try argv.append(if (comp.data_sections) "-fdata-sections" else "-fno-data-sections"); + + switch (mod.unwind_tables) { + .none => { + try argv.append("-fno-unwind-tables"); + try argv.append("-fno-asynchronous-unwind-tables"); + }, + .sync => { + // Need to override Clang's convoluted default logic. + try argv.append("-fno-asynchronous-unwind-tables"); + try argv.append("-funwind-tables"); + }, + .async => try argv.append("-fasynchronous-unwind-tables"), + } + } + + // Only compiled files support these flags. + switch (ext) { + .c, + .h, + .cpp, + .hpp, + .m, + .hm, + .mm, + .hmm, + .ll, + .bc, + => { + const xclang_flag = switch (ext) { + .assembly, .assembly_with_cpp => "-Xclangas", + else => "-Xclang", + }; + + if (target_util.clangSupportsTargetCpuArg(target)) { + if (target.cpu.model.llvm_name) |llvm_name| { + try argv.appendSlice(&[_][]const u8{ + xclang_flag, "-target-cpu", xclang_flag, 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); + + if (feature.llvm_name) |llvm_name| { + // We communicate float ABI to Clang through the dedicated options. + if (std.mem.startsWith(u8, llvm_name, "soft-float") or + std.mem.startsWith(u8, llvm_name, "hard-float")) + 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_flag, "-target-feature", xclang_flag }); + const plus_or_minus = "-+"[@intFromBool(is_enabled)]; + const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name }); + argv.appendAssumeCapacity(arg); + } + } + }, + else => {}, + } try argv.appendSlice(comp.global_cc_argv); try argv.appendSlice(mod.cc_argv); diff --git a/src/main.zig b/src/main.zig index f95ea7300e..de1c112c85 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4555,7 +4555,6 @@ fn cmdTranslateC( }; var argv = std.ArrayList([]const u8).init(arena); - try argv.append("arocc"); try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path, comp.root_mod); try argv.append(c_source_file.src_path); if (comp.verbose_cc) Compilation.dump_argv(argv.items);