aboutsummaryrefslogtreecommitdiff
path: root/src/Compilation.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Compilation.zig')
-rw-r--r--src/Compilation.zig642
1 files changed, 323 insertions, 319 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index bf7246b8d8..c31a9ae012 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -5655,115 +5655,60 @@ pub const CImportResult = struct {
};
/// Caller owns returned memory.
-pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module) !CImportResult {
+pub fn cImport(
+ comp: *Compilation,
+ c_src: []const u8,
+ owner_mod: *Package.Module,
+ prog_node: std.Progress.Node,
+) !CImportResult {
dev.check(.translate_c_command);
- const tracy_trace = trace(@src());
- defer tracy_trace.end();
-
- const cimport_zig_basename = "cimport.zig";
+ const cimport_basename = "cimport.h";
+ const translated_basename = "cimport.zig";
var man = comp.obtainCObjectCacheManifest(owner_mod);
defer man.deinit();
- man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
+ man.hash.add(@as(u16, 0x7dd9)); // Random number to distinguish translate-c from compiling C objects
man.hash.addBytes(c_src);
- man.hash.add(comp.config.c_frontend);
-
- // If the previous invocation resulted in clang errors, we will see a hit
- // here with 0 files in the manifest, in which case it is actually a miss.
- // We need to "unhit" in this case, to keep the digests matching.
- const prev_hash_state = man.hash.peekBin();
- const actual_hit = hit: {
- _ = try man.hit();
- if (man.files.entries.len == 0) {
- man.unhit(prev_hash_state, 0);
- break :hit false;
- }
- break :hit true;
- };
- const digest = if (!actual_hit) digest: {
+
+ const digest, const is_hit = if (try man.hit()) .{ man.finalBin(), true } else digest: {
var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
defer arena_allocator.deinit();
const arena = arena_allocator.allocator();
- const tmp_digest = man.hash.peek();
- const tmp_dir_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &tmp_digest });
- var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{});
- defer zig_cache_tmp_dir.close();
- const cimport_basename = "cimport.h";
- const out_h_path = try comp.dirs.local_cache.join(arena, &[_][]const u8{
- tmp_dir_sub_path, cimport_basename,
- });
+ const tmp_basename = std.fmt.hex(std.crypto.random.int(u64));
+ const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename;
+ const cache_dir = comp.dirs.local_cache.handle;
+ const out_h_sub_path = tmp_sub_path ++ fs.path.sep_str ++ cimport_basename;
+
+ try cache_dir.makePath(tmp_sub_path);
+
+ const out_h_path = try comp.dirs.local_cache.join(arena, &.{out_h_sub_path});
+ const translated_path = try comp.dirs.local_cache.join(arena, &.{ tmp_sub_path, translated_basename });
const out_dep_path = try std.fmt.allocPrint(arena, "{s}.d", .{out_h_path});
- try zig_cache_tmp_dir.writeFile(.{ .sub_path = cimport_basename, .data = c_src });
- if (comp.verbose_cimport) {
- log.info("C import source: {s}", .{out_h_path});
- }
+ if (comp.verbose_cimport) log.info("writing C import source to {s}", .{out_h_path});
+ try cache_dir.writeFile(.{ .sub_path = out_h_sub_path, .data = c_src });
var argv = std.array_list.Managed([]const u8).init(comp.gpa);
defer argv.deinit();
-
- try argv.append(@tagName(comp.config.c_frontend)); // argv[0] is program name, actual args start at [1]
try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path, owner_mod);
+ try argv.appendSlice(&.{ out_h_path, "-o", translated_path });
- try argv.append(out_h_path);
-
- if (comp.verbose_cc) {
- dump_argv(argv.items);
- }
- var tree = switch (comp.config.c_frontend) {
- .aro => tree: {
- if (true) @panic("TODO");
- break :tree undefined;
- },
- .clang => tree: {
- if (!build_options.have_llvm) unreachable;
- const translate_c = @import("translate_c.zig");
-
- // Convert to null terminated args.
- const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1);
- new_argv_with_sentinel[argv.items.len] = null;
- const new_argv = new_argv_with_sentinel[0..argv.items.len :null];
- for (argv.items, 0..) |arg, i| {
- new_argv[i] = try arena.dupeZ(u8, arg);
- }
-
- const c_headers_dir_path_z = try comp.dirs.zig_lib.joinZ(arena, &.{"include"});
- var errors = std.zig.ErrorBundle.empty;
- errdefer errors.deinit(comp.gpa);
- break :tree translate_c.translate(
- comp.gpa,
- new_argv.ptr,
- new_argv.ptr + new_argv.len,
- &errors,
- c_headers_dir_path_z,
- ) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.SemanticAnalyzeFail => {
- return CImportResult{
- .digest = undefined,
- .cache_hit = actual_hit,
- .errors = errors,
- };
- },
- };
- },
- };
- defer tree.deinit(comp.gpa);
-
- if (comp.verbose_cimport) {
- log.info("C import .d file: {s}", .{out_dep_path});
- }
+ if (comp.verbose_cc) dump_argv(argv.items);
+ var stdout: []u8 = undefined;
+ try @import("main.zig").translateC(comp.gpa, arena, argv.items, prog_node, &stdout);
+ if (comp.verbose_cimport and stdout.len != 0) log.info("unexpected stdout: {s}", .{stdout});
- const dep_basename = fs.path.basename(out_dep_path);
- try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
+ const dep_sub_path = out_h_sub_path ++ ".d";
+ if (comp.verbose_cimport) log.info("processing dep file at {s}", .{dep_sub_path});
+ try man.addDepFilePost(cache_dir, dep_sub_path);
switch (comp.cache_use) {
.whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| {
whole.cache_manifest_mutex.lock();
defer whole.cache_manifest_mutex.unlock();
- try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
+ try whole_cache_manifest.addDepFilePost(cache_dir, dep_sub_path);
},
.incremental, .none => {},
}
@@ -5771,19 +5716,12 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module
const bin_digest = man.finalBin();
const hex_digest = Cache.binToHex(bin_digest);
const o_sub_path = "o" ++ fs.path.sep_str ++ hex_digest;
- var o_dir = try comp.dirs.local_cache.handle.makeOpenPath(o_sub_path, .{});
- defer o_dir.close();
-
- var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{});
- defer out_zig_file.close();
-
- const formatted = try tree.renderAlloc(comp.gpa);
- defer comp.gpa.free(formatted);
- try out_zig_file.writeAll(formatted);
+ if (comp.verbose_cimport) log.info("renaming {s} to {s}", .{ tmp_sub_path, o_sub_path });
+ try renameTmpIntoCache(comp.dirs.local_cache, tmp_sub_path, o_sub_path);
- break :digest bin_digest;
- } else man.finalBin();
+ break :digest .{ bin_digest, false };
+ };
if (man.have_exclusive_lock) {
// Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
@@ -5795,9 +5733,9 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module
};
}
- return CImportResult{
+ return .{
.digest = digest,
- .cache_hit = actual_hit,
+ .cache_hit = is_hit,
.errors = std.zig.ErrorBundle.empty,
};
}
@@ -6747,51 +6685,63 @@ 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, .aro);
+
+ 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),
ext: FileExt,
out_dep_path: ?[]const u8,
mod: *Package.Module,
+ c_frontend: Config.CFrontend,
) !void {
const target = &mod.resolved_target.result;
+ const is_clang = c_frontend == .clang;
- // 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");
+ 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");
}
- 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| {
+ .ios, .macos, .tvos, .watchos => |os| if (is_clang) {
try argv.ensureUnusedCapacity(2);
// Pass the proper -m<os>-version-min argument for darwin.
const ver = target.os.version_range.semver.min;
@@ -6815,78 +6765,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");
}
@@ -6923,45 +6801,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) {
@@ -7085,12 +6924,14 @@ pub fn addCCArgs(
.mm,
.hmm,
=> {
- try argv.append("-fno-spell-checking");
+ if (is_clang) {
+ try argv.append("-fno-spell-checking");
- if (target.os.tag == .windows and target.abi.isGnu()) {
- // windows.h has files such as pshpack1.h which do #pragma packing,
- // triggering a clang warning. So for this target, we disable this warning.
- try argv.append("-Wno-pragma-pack");
+ if (target.os.tag == .windows and target.abi.isGnu()) {
+ // windows.h has files such as pshpack1.h which do #pragma packing,
+ // triggering a clang warning. So for this target, we disable this warning.
+ try argv.append("-Wno-pragma-pack");
+ }
}
if (mod.optimize_mode != .Debug) {
@@ -7100,80 +6941,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,
@@ -7191,7 +6958,7 @@ pub fn addCCArgs(
try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mod.code_model)}));
}
- {
+ if (is_clang) {
var san_arg: std.ArrayListUnmanaged(u8) = .empty;
const prefix = "-fsanitize=";
if (mod.sanitize_c != .off) {
@@ -7272,6 +7039,243 @@ pub fn addCCArgs(
},
else => {},
}
+}
+
+/// Add common C compiler args and Clang specific args.
+pub fn addCCArgs(
+ comp: *const Compilation,
+ arena: Allocator,
+ argv: *std.array_list.Managed([]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, comp.config.c_frontend);
+
+ // 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);