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 {...@@ -849,10 +849,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
849 if (options.use_llvm) |explicit|849 if (options.use_llvm) |explicit|
850 break :blk explicit;850 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
856 // If we are outputting .c code we must use Zig backend.852 // If we are outputting .c code we must use Zig backend.
857 if (ofmt == .c)853 if (ofmt == .c)
858 break :blk false;854 break :blk false;
...@@ -861,6 +857,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -861,6 +857,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
861 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null)857 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null)
862 break :blk true;858 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
864 // The stage1 compiler depends on the stage1 C++ LLVM backend864 // The stage1 compiler depends on the stage1 C++ LLVM backend
865 // to compile zig code.865 // to compile zig code.
866 if (use_stage1)866 if (use_stage1)
...@@ -876,9 +876,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -876,9 +876,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
876 if (options.use_llvm == true) {876 if (options.use_llvm == true) {
877 return error.ZigCompilerNotBuiltWithLLVMExtensions;877 return error.ZigCompilerNotBuiltWithLLVMExtensions;
878 }878 }
879 if (options.machine_code_model != .default) {
880 return error.MachineCodeModelNotSupportedWithoutLlvm;
881 }
882 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {879 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {
883 return error.EmittingLlvmModuleRequiresUsingLlvmBackend;880 return error.EmittingLlvmModuleRequiresUsingLlvmBackend;
884 }881 }
...@@ -1793,6 +1790,10 @@ pub fn update(self: *Compilation) !void {...@@ -1793,6 +1790,10 @@ pub fn update(self: *Compilation) !void {
1793 }1790 }
1794 }1791 }
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
1796 // If there are any errors, we anticipate the source files being loaded1797 // If there are any errors, we anticipate the source files being loaded
1797 // to report error messages. Otherwise we unload all source files to save memory.1798 // to report error messages. Otherwise we unload all source files to save memory.
1798 // The ZIR needs to stay loaded in memory because (1) Decl objects contain references1799 // The ZIR needs to stay loaded in memory because (1) Decl objects contain references
...@@ -1808,6 +1809,37 @@ pub fn update(self: *Compilation) !void {...@@ -1808,6 +1809,37 @@ pub fn update(self: *Compilation) !void {
1808 }1809 }
1809}1810}
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
1811/// Having the file open for writing is problematic as far as executing the1843/// Having the file open for writing is problematic as far as executing the
1812/// binary is concerned. This will remove the write flag, or close the file,1844/// binary is concerned. This will remove the write flag, or close the file,
1813/// or whatever is needed so that it can be executed.1845/// 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...@@ -2764,6 +2796,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
2764 defer man.deinit();2796 defer man.deinit();
27652797
2766 man.hash.add(comp.clang_preprocessor_mode);2798 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
2768 try man.hashCSource(c_object.src);2803 try man.hashCSource(c_object.src);
27692804
...@@ -2787,16 +2822,29 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -2787,16 +2822,29 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
2787 comp.bin_file.options.root_name2822 comp.bin_file.options.root_name
2788 else2823 else
2789 c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len];2824 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);
2795 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {2827 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
2796 var argv = std.ArrayList([]const u8).init(comp.gpa);2828 var argv = std.ArrayList([]const u8).init(comp.gpa);
2797 defer argv.deinit();2829 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.
2800 const out_obj_path = try comp.tmpFilePath(arena, o_basename);2848 const out_obj_path = try comp.tmpFilePath(arena, o_basename);
2801 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});2849 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
2802 defer zig_cache_tmp_dir.close();2850 defer zig_cache_tmp_dir.close();
...@@ -2810,15 +2858,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -2810,15 +2858,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
2810 try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});2858 try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});
2811 try comp.addCCArgs(arena, &argv, ext, out_dep_path);2859 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);
2814 switch (comp.clang_preprocessor_mode) {2862 switch (comp.clang_preprocessor_mode) {
2815 .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),2863 .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
2816 .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),2864 .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
2817 .stdout => argv.appendAssumeCapacity("-E"),2865 .stdout => argv.appendAssumeCapacity("-E"),
2818 }2866 }
28192867 if (comp.clang_passthrough_mode) {
2820 try argv.append(c_object.src.src_path);2868 if (comp.emit_asm != null) {
2821 try argv.appendSlice(c_object.src.extra_flags);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
2823 if (comp.verbose_cc) {2879 if (comp.verbose_cc) {
2824 dump_argv(argv.items);2880 dump_argv(argv.items);
...@@ -2838,8 +2894,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -2838,8 +2894,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
2838 switch (term) {2894 switch (term) {
2839 .Exited => |code| {2895 .Exited => |code| {
2840 if (code != 0) {2896 if (code != 0) {
2841 // TODO https://github.com/ziglang/zig/issues/63422897 std.process.exit(code);
2842 std.process.exit(1);
2843 }2898 }
2844 if (comp.clang_preprocessor_mode == .stdout)2899 if (comp.clang_preprocessor_mode == .stdout)
2845 std.process.exit(0);2900 std.process.exit(0);
...@@ -2855,9 +2910,6 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -2855,9 +2910,6 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
28552910
2856 const stderr_reader = child.stderr.?.reader();2911 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));
2861 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);2913 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
28622914
2863 const term = child.wait() catch |err| {2915 const term = child.wait() catch |err| {
...@@ -2907,6 +2959,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -2907,6 +2959,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
2907 break :blk digest;2959 break :blk digest;
2908 };2960 };
29092961
2962 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, o_ext });
2963
2910 c_object.status = .{2964 c_object.status = .{
2911 .success = .{2965 .success = .{
2912 .object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{2966 .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"),...@@ -2434,7 +2434,14 @@ flagpd1("emit-codegen-only"),
2434flagpd1("emit-header-module"),2434flagpd1("emit-header-module"),
2435flagpd1("emit-html"),2435flagpd1("emit-html"),
2436flagpd1("emit-interface-stubs"),2436flagpd1("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},
2438flagpd1("emit-llvm-bc"),2445flagpd1("emit-llvm-bc"),
2439flagpd1("emit-llvm-only"),2446flagpd1("emit-llvm-only"),
2440flagpd1("emit-llvm-uselists"),2447flagpd1("emit-llvm-uselists"),
src/main.zig+29-6
...@@ -1151,6 +1151,7 @@ fn buildOutputType(...@@ -1151,6 +1151,7 @@ fn buildOutputType(
1151 var is_shared_lib = false;1151 var is_shared_lib = false;
1152 var linker_args = std.ArrayList([]const u8).init(arena);1152 var linker_args = std.ArrayList([]const u8).init(arena);
1153 var it = ClangArgIterator.init(arena, all_args);1153 var it = ClangArgIterator.init(arena, all_args);
1154 var emit_llvm = false;
1154 while (it.has_next) {1155 while (it.has_next) {
1155 it.next() catch |err| {1156 it.next() catch |err| {
1156 fatal("unable to parse command line parameters: {s}", .{@errorName(err)});1157 fatal("unable to parse command line parameters: {s}", .{@errorName(err)});
...@@ -1161,6 +1162,7 @@ fn buildOutputType(...@@ -1161,6 +1162,7 @@ fn buildOutputType(
1161 .c => c_out_mode = .object, // -c1162 .c => c_out_mode = .object, // -c
1162 .asm_only => c_out_mode = .assembly, // -S1163 .asm_only => c_out_mode = .assembly, // -S
1163 .preprocess_only => c_out_mode = .preprocessor, // -E1164 .preprocess_only => c_out_mode = .preprocessor, // -E
1165 .emit_llvm => emit_llvm = true,
1164 .other => {1166 .other => {
1165 try clang_argv.appendSlice(it.other_args);1167 try clang_argv.appendSlice(it.other_args);
1166 },1168 },
...@@ -1518,22 +1520,42 @@ fn buildOutputType(...@@ -1518,22 +1520,42 @@ fn buildOutputType(
1518 output_mode = if (is_shared_lib) .Lib else .Exe;1520 output_mode = if (is_shared_lib) .Lib else .Exe;
1519 emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;1521 emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
1520 enable_cache = true;1522 enable_cache = true;
1523 if (emit_llvm) {
1524 fatal("-emit-llvm cannot be used when linking", .{});
1525 }
1521 },1526 },
1522 .object => {1527 .object => {
1523 output_mode = .Obj;1528 output_mode = .Obj;
1524 if (out_path) |p| {1529 if (emit_llvm) {
1525 emit_bin = .{ .yes = p };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 }
1526 } else {1536 } 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 }
1528 }1542 }
1529 },1543 },
1530 .assembly => {1544 .assembly => {
1531 output_mode = .Obj;1545 output_mode = .Obj;
1532 emit_bin = .no;1546 emit_bin = .no;
1533 if (out_path) |p| {1547 if (emit_llvm) {
1534 emit_asm = .{ .yes = p };1548 if (out_path) |p| {
1549 emit_llvm_ir = .{ .yes = p };
1550 } else {
1551 emit_llvm_ir = .yes_default_path;
1552 }
1535 } else {1553 } 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 }
1537 }1559 }
1538 },1560 },
1539 .preprocessor => {1561 .preprocessor => {
...@@ -3663,6 +3685,7 @@ pub const ClangArgIterator = struct {...@@ -3663,6 +3685,7 @@ pub const ClangArgIterator = struct {
3663 no_red_zone,3685 no_red_zone,
3664 strip,3686 strip,
3665 exec_model,3687 exec_model,
3688 emit_llvm,
3666 };3689 };
36673690
3668 const Args = struct {3691 const Args = struct {
tools/update_clang_options.zig+4
...@@ -376,6 +376,10 @@ const known_options = [_]KnownOpt{...@@ -376,6 +376,10 @@ const known_options = [_]KnownOpt{
376 .name = "mexec-model",376 .name = "mexec-model",
377 .ident = "exec_model",377 .ident = "exec_model",
378 },378 },
379 .{
380 .name = "emit-llvm",
381 .ident = "emit_llvm",
382 },
379};383};
380384
381const blacklisted_options = [_][]const u8{};385const blacklisted_options = [_][]const u8{};