authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-16 16:37:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-16 16:39:04-07:00
logd11f42c2b24e80388e1ea648ee97fae612fd76a4
tree0a2ea0de9f4b6c38ab688c0759f3fce1ba6a84a8
parent6d37ae95edc06f15e4e77f64e8e637dd5d269183

zig cc: support -S and -emit-llvm CLI parameters

closes #6425

4 files changed, 116 insertions(+), 28 deletions(-)

src/Compilation.zig+75-21
......@@ -849,10 +849,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
849849 if (options.use_llvm) |explicit|
850850 break :blk explicit;
851851
852 // If we have no zig code to compile, no need for LLVM.
853 if (options.main_pkg == null)
854 break :blk false;
855
856852 // If we are outputting .c code we must use Zig backend.
857853 if (ofmt == .c)
858854 break :blk false;
......@@ -861,6 +857,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
861857 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null)
862858 break :blk true;
863859
860 // If we have no zig code to compile, no need for LLVM.
861 if (options.main_pkg == null)
862 break :blk false;
863
864864 // The stage1 compiler depends on the stage1 C++ LLVM backend
865865 // to compile zig code.
866866 if (use_stage1)
......@@ -876,9 +876,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
876876 if (options.use_llvm == true) {
877877 return error.ZigCompilerNotBuiltWithLLVMExtensions;
878878 }
879 if (options.machine_code_model != .default) {
880 return error.MachineCodeModelNotSupportedWithoutLlvm;
881 }
882879 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {
883880 return error.EmittingLlvmModuleRequiresUsingLlvmBackend;
884881 }
......@@ -1793,6 +1790,10 @@ pub fn update(self: *Compilation) !void {
17931790 }
17941791 }
17951792
1793 // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and
1794 // -femit-asm to handle, in the case of C objects.
1795 try self.emitOthers();
1796
17961797 // If there are any errors, we anticipate the source files being loaded
17971798 // to report error messages. Otherwise we unload all source files to save memory.
17981799 // The ZIR needs to stay loaded in memory because (1) Decl objects contain references
......@@ -1808,6 +1809,37 @@ pub fn update(self: *Compilation) !void {
18081809 }
18091810}
18101811
1812fn emitOthers(comp: *Compilation) !void {
1813 if (comp.bin_file.options.output_mode != .Obj or comp.bin_file.options.module != null or
1814 comp.c_object_table.count() == 0)
1815 {
1816 return;
1817 }
1818 const obj_path = comp.c_object_table.keys()[0].status.success.object_path;
1819 const cwd = std.fs.cwd();
1820 const ext = std.fs.path.extension(obj_path);
1821 const basename = obj_path[0 .. obj_path.len - ext.len];
1822 // This obj path always ends with the object file extension, but if we change the
1823 // extension to .ll, .bc, or .s, then it will be the path to those things.
1824 const outs = [_]struct {
1825 emit: ?EmitLoc,
1826 ext: []const u8,
1827 }{
1828 .{ .emit = comp.emit_asm, .ext = ".s" },
1829 .{ .emit = comp.emit_llvm_ir, .ext = ".ll" },
1830 .{ .emit = comp.emit_llvm_bc, .ext = ".bc" },
1831 };
1832 for (outs) |out| {
1833 if (out.emit) |loc| {
1834 if (loc.directory) |directory| {
1835 const src_path = try std.fmt.allocPrint(comp.gpa, "{s}{s}", .{ basename, out.ext });
1836 defer comp.gpa.free(src_path);
1837 try cwd.copyFile(src_path, directory.handle, loc.basename, .{});
1838 }
1839 }
1840 }
1841}
1842
18111843/// Having the file open for writing is problematic as far as executing the
18121844/// binary is concerned. This will remove the write flag, or close the file,
18131845/// or whatever is needed so that it can be executed.
......@@ -2764,6 +2796,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
27642796 defer man.deinit();
27652797
27662798 man.hash.add(comp.clang_preprocessor_mode);
2799 man.hash.addOptionalEmitLoc(comp.emit_asm);
2800 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
2801 man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);
27672802
27682803 try man.hashCSource(c_object.src);
27692804
......@@ -2787,16 +2822,29 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
27872822 comp.bin_file.options.root_name
27882823 else
27892824 c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len];
2790 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{
2791 o_basename_noext,
2792 comp.bin_file.options.object_format.fileExt(comp.bin_file.options.target.cpu.arch),
2793 });
27942825
2826 const o_ext = comp.bin_file.options.object_format.fileExt(comp.bin_file.options.target.cpu.arch);
27952827 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
27962828 var argv = std.ArrayList([]const u8).init(comp.gpa);
27972829 defer argv.deinit();
27982830
2799 // We can't know the digest until we do the C compiler invocation, so we need a temporary filename.
2831 // In case we are doing passthrough mode, we need to detect -S and -emit-llvm.
2832 const out_ext = e: {
2833 if (!comp.clang_passthrough_mode)
2834 break :e o_ext;
2835 if (comp.emit_asm != null)
2836 break :e ".s";
2837 if (comp.emit_llvm_ir != null)
2838 break :e ".ll";
2839 if (comp.emit_llvm_bc != null)
2840 break :e ".bc";
2841
2842 break :e o_ext;
2843 };
2844 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext });
2845
2846 // We can't know the digest until we do the C compiler invocation,
2847 // so we need a temporary filename.
28002848 const out_obj_path = try comp.tmpFilePath(arena, o_basename);
28012849 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
28022850 defer zig_cache_tmp_dir.close();
......@@ -2810,15 +2858,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
28102858 try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});
28112859 try comp.addCCArgs(arena, &argv, ext, out_dep_path);
28122860
2813 try argv.ensureCapacity(argv.items.len + 3);
2861 try argv.ensureUnusedCapacity(6 + c_object.src.extra_flags.len);
28142862 switch (comp.clang_preprocessor_mode) {
28152863 .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
28162864 .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
28172865 .stdout => argv.appendAssumeCapacity("-E"),
28182866 }
2819
2820 try argv.append(c_object.src.src_path);
2821 try argv.appendSlice(c_object.src.extra_flags);
2867 if (comp.clang_passthrough_mode) {
2868 if (comp.emit_asm != null) {
2869 argv.appendAssumeCapacity("-S");
2870 } else if (comp.emit_llvm_ir != null) {
2871 argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" });
2872 } else if (comp.emit_llvm_bc != null) {
2873 argv.appendAssumeCapacity("-emit-llvm");
2874 }
2875 }
2876 argv.appendAssumeCapacity(c_object.src.src_path);
2877 argv.appendSliceAssumeCapacity(c_object.src.extra_flags);
28222878
28232879 if (comp.verbose_cc) {
28242880 dump_argv(argv.items);
......@@ -2838,8 +2894,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
28382894 switch (term) {
28392895 .Exited => |code| {
28402896 if (code != 0) {
2841 // TODO https://github.com/ziglang/zig/issues/6342
2842 std.process.exit(1);
2897 std.process.exit(code);
28432898 }
28442899 if (comp.clang_preprocessor_mode == .stdout)
28452900 std.process.exit(0);
......@@ -2855,9 +2910,6 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
28552910
28562911 const stderr_reader = child.stderr.?.reader();
28572912
2858 // TODO https://github.com/ziglang/zig/issues/6343
2859 // Please uncomment and use stdout once this issue is fixed
2860 // const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32));
28612913 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
28622914
28632915 const term = child.wait() catch |err| {
......@@ -2907,6 +2959,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
29072959 break :blk digest;
29082960 };
29092961
2962 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, o_ext });
2963
29102964 c_object.status = .{
29112965 .success = .{
29122966 .object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{
src/clang_options_data.zig+8-1
......@@ -2434,7 +2434,14 @@ flagpd1("emit-codegen-only"),
24342434flagpd1("emit-header-module"),
24352435flagpd1("emit-html"),
24362436flagpd1("emit-interface-stubs"),
2437flagpd1("emit-llvm"),
2437.{
2438 .name = "emit-llvm",
2439 .syntax = .flag,
2440 .zig_equivalent = .emit_llvm,
2441 .pd1 = true,
2442 .pd2 = false,
2443 .psl = false,
2444},
24382445flagpd1("emit-llvm-bc"),
24392446flagpd1("emit-llvm-only"),
24402447flagpd1("emit-llvm-uselists"),
src/main.zig+29-6
......@@ -1151,6 +1151,7 @@ fn buildOutputType(
11511151 var is_shared_lib = false;
11521152 var linker_args = std.ArrayList([]const u8).init(arena);
11531153 var it = ClangArgIterator.init(arena, all_args);
1154 var emit_llvm = false;
11541155 while (it.has_next) {
11551156 it.next() catch |err| {
11561157 fatal("unable to parse command line parameters: {s}", .{@errorName(err)});
......@@ -1161,6 +1162,7 @@ fn buildOutputType(
11611162 .c => c_out_mode = .object, // -c
11621163 .asm_only => c_out_mode = .assembly, // -S
11631164 .preprocess_only => c_out_mode = .preprocessor, // -E
1165 .emit_llvm => emit_llvm = true,
11641166 .other => {
11651167 try clang_argv.appendSlice(it.other_args);
11661168 },
......@@ -1518,22 +1520,42 @@ fn buildOutputType(
15181520 output_mode = if (is_shared_lib) .Lib else .Exe;
15191521 emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
15201522 enable_cache = true;
1523 if (emit_llvm) {
1524 fatal("-emit-llvm cannot be used when linking", .{});
1525 }
15211526 },
15221527 .object => {
15231528 output_mode = .Obj;
1524 if (out_path) |p| {
1525 emit_bin = .{ .yes = p };
1529 if (emit_llvm) {
1530 emit_bin = .no;
1531 if (out_path) |p| {
1532 emit_llvm_bc = .{ .yes = p };
1533 } else {
1534 emit_llvm_bc = .yes_default_path;
1535 }
15261536 } else {
1527 emit_bin = .yes_default_path;
1537 if (out_path) |p| {
1538 emit_bin = .{ .yes = p };
1539 } else {
1540 emit_bin = .yes_default_path;
1541 }
15281542 }
15291543 },
15301544 .assembly => {
15311545 output_mode = .Obj;
15321546 emit_bin = .no;
1533 if (out_path) |p| {
1534 emit_asm = .{ .yes = p };
1547 if (emit_llvm) {
1548 if (out_path) |p| {
1549 emit_llvm_ir = .{ .yes = p };
1550 } else {
1551 emit_llvm_ir = .yes_default_path;
1552 }
15351553 } else {
1536 emit_asm = .yes_default_path;
1554 if (out_path) |p| {
1555 emit_asm = .{ .yes = p };
1556 } else {
1557 emit_asm = .yes_default_path;
1558 }
15371559 }
15381560 },
15391561 .preprocessor => {
......@@ -3663,6 +3685,7 @@ pub const ClangArgIterator = struct {
36633685 no_red_zone,
36643686 strip,
36653687 exec_model,
3688 emit_llvm,
36663689 };
36673690
36683691 const Args = struct {
tools/update_clang_options.zig+4
......@@ -376,6 +376,10 @@ const known_options = [_]KnownOpt{
376376 .name = "mexec-model",
377377 .ident = "exec_model",
378378 },
379 .{
380 .name = "emit-llvm",
381 .ident = "emit_llvm",
382 },
379383};
380384
381385const blacklisted_options = [_][]const u8{};