From 6af2990549709ec5e2bc1efeb5090a944f1a8bdf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 26 Sep 2020 01:42:54 -0700 Subject: [PATCH] implement -femit-asm, -femit-docs, -femit-llvm-ir, etc These CLI options are now forwarded to the stage1 backend. We're not going to support the -mllvm CLI option any longer. As a compromise, we unconditionally tell LLVM to output intel x86 syntax when using -femit-asm. Simplify stage1 logic; it no longer has the concept of an output directory. --output-dir is no longer a valid CLI option. cmake uses the `-femit-bin=[path]` option. Note the changes to test/cli.zig. This breaks the CLI API that Godbolt is using so we're going to want to open a PR to help them upgrade to the new CLI for the upcoming Zig 0.7.0 release. --- BRANCH_TODO | 13 +--- CMakeLists.txt | 6 +- src/Compilation.zig | 68 +++++++++++++---- src/llvm.zig | 3 + src/main.zig | 157 ++++++++++++++++++++++++++++++--------- src/stage1.zig | 19 +++-- src/stage1/all_types.hpp | 11 +-- src/stage1/codegen.cpp | 75 ++++++------------- src/stage1/stage1.cpp | 13 ++-- src/stage1/stage1.h | 24 ++++-- src/stage1/zig0.cpp | 13 ++-- test/cli.zig | 19 +++-- 12 files changed, 263 insertions(+), 158 deletions(-) diff --git a/BRANCH_TODO b/BRANCH_TODO index 6c04b4693..5d4293c5f 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -1,13 +1,7 @@ + * support -fno-emit-bin for godbolt * tests passing with -Dskip-non-native * `-ftime-report` * -fstack-report print stack size diagnostics\n" - * -fdump-analysis write analysis.json file with type information\n" - * -femit-docs create a docs/ dir with html documentation\n" - * -fno-emit-docs do not produce docs/ dir with html documentation\n" - * -femit-asm output .s (assembly code)\n" - * -fno-emit-asm (default) do not output .s (assembly code)\n" - * -femit-llvm-ir produce a .ll file with LLVM IR\n" - * -fno-emit-llvm-ir (default) do not produce a .ll file with LLVM IR\n" * mingw-w64 * MachO LLD linking * COFF LLD linking @@ -17,8 +11,8 @@ * On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process. * restore error messages for stage2_add_link_lib * windows CUSTOMBUILD : error : unable to build compiler_rt: FileNotFound [D:\a\1\s\build\zig_install_lib_files.vcxproj] - * try building some software with zig cc - * implement support for -femit-asm + * try building some software with zig cc to make sure it didn't regress + * restore the legacy -femit-h feature using the stage1 backend * implement proper parsing of clang stderr/stdout and exposing compile errors with the Compilation API * implement proper parsing of LLD stderr/stdout and exposing compile errors with the Compilation API @@ -56,3 +50,4 @@ * make std.Progress support multithreaded * update musl.zig static data to use native path separator in static data rather than replacing '/' at runtime * linking hello world with LLD, lld is silently calling exit(1) instead of reporting ok=false. when run standalone the error message is: ld.lld: error: section [index 3] has a sh_offset (0x57000) + sh_size (0x68) that is greater than the file size (0x57060) + * submit PR to godbolt and update the CLI options (see changes to test/cli.zig) diff --git a/CMakeLists.txt b/CMakeLists.txt index f812da9d7..c4d7b3976 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -449,7 +449,7 @@ endif() if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") set(ZIG1_RELEASE_ARG "") else() - set(ZIG1_RELEASE_ARG --release-fast --strip) + set(ZIG1_RELEASE_ARG -OReleaseFast --strip) endif() set(BUILD_ZIG1_ARGS @@ -458,8 +458,8 @@ set(BUILD_ZIG1_ARGS "-mcpu=${ZIG_TARGET_MCPU}" --name zig1 --override-lib-dir "${CMAKE_SOURCE_DIR}/lib" - --output-dir "${CMAKE_BINARY_DIR}" - ${ZIG1_RELEASE_ARG} + "-femit-bin=${ZIG1_OBJECT}" + "${ZIG1_RELEASE_ARG}" -lc --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end diff --git a/src/Compilation.zig b/src/Compilation.zig index b8856e8a4..d83140349 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -107,6 +107,12 @@ test_filter: ?[]const u8, test_name_prefix: ?[]const u8, test_evented_io: bool, +emit_h: ?EmitLoc, +emit_asm: ?EmitLoc, +emit_llvm_ir: ?EmitLoc, +emit_analysis: ?EmitLoc, +emit_docs: ?EmitLoc, + pub const InnerError = Module.InnerError; pub const CRTFile = struct { @@ -296,6 +302,12 @@ pub const InitOptions = struct { emit_h: ?EmitLoc = null, /// `null` means to not emit assembly. emit_asm: ?EmitLoc = null, + /// `null` means to not emit LLVM IR. + emit_llvm_ir: ?EmitLoc = null, + /// `null` means to not emit semantic analysis JSON. + emit_analysis: ?EmitLoc = null, + /// `null` means to not emit docs. + emit_docs: ?EmitLoc = null, link_mode: ?std.builtin.LinkMode = null, dll_export_fns: ?bool = false, /// Normally when using LLD to link, Zig uses a file named "lld.id" in the @@ -442,7 +454,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { break :blk false; }; - const link_libc = options.link_libc or (is_exe_or_dyn_lib and target_util.osRequiresLibC(options.target)); @@ -489,9 +500,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { break :pic explicit; } else must_pic; - if (options.emit_h != null) fatal("-femit-h not supported yet", .{}); // TODO - if (options.emit_asm != null) fatal("-femit-asm not supported yet", .{}); // TODO - const emit_bin = options.emit_bin orelse fatal("-fno-emit-bin not supported yet", .{}); // TODO // Make a decision on whether to use Clang for translate-c and compiling C files. @@ -579,6 +587,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { cache.hash.add(options.link_libcpp); cache.hash.add(options.output_mode); cache.hash.add(options.machine_code_model); + cache.hash.add(options.emit_bin != null); + cache.hash.add(options.emit_h != null); + cache.hash.add(options.emit_asm != null); + cache.hash.add(options.emit_llvm_ir != null); + cache.hash.add(options.emit_analysis != null); + cache.hash.add(options.emit_docs != null); // TODO audit this and make sure everything is in it const module: ?*Module = if (options.root_pkg) |root_pkg| blk: { @@ -752,6 +766,11 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .local_cache_directory = options.local_cache_directory, .global_cache_directory = options.global_cache_directory, .bin_file = bin_file, + .emit_h = options.emit_h, + .emit_asm = options.emit_asm, + .emit_llvm_ir = options.emit_llvm_ir, + .emit_analysis = options.emit_analysis, + .emit_docs = options.emit_docs, .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), .keep_source_files_loaded = options.keep_source_files_loaded, .use_clang = use_clang, @@ -1450,8 +1469,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void { try argv.ensureCapacity(argv.items.len + 3); switch (comp.clang_preprocessor_mode) { - .no => argv.appendSliceAssumeCapacity(&[_][]const u8{"-c", "-o", out_obj_path}), - .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{"-E", "-o", out_obj_path}), + .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }), + .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }), .stdout => argv.appendAssumeCapacity("-E"), } @@ -2498,15 +2517,35 @@ fn updateStage1Module(comp: *Compilation) !void { comp.is_test, ) orelse return error.OutOfMemory; + const bin_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = comp.bin_file.options.root_name, + .target = target, + .output_mode = .Obj, + }); + const emit_bin_path = try directory.join(arena, &[_][]const u8{bin_basename}); + const emit_h_path = try stage1LocPath(arena, comp.emit_h, directory); + const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory); + const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory); + const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory); + const emit_docs_path = try stage1LocPath(arena, comp.emit_docs, directory); const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null); - const output_dir = directory.path orelse "."; const test_filter = comp.test_filter orelse ""[0..0]; const test_name_prefix = comp.test_name_prefix orelse ""[0..0]; stage1_module.* = .{ .root_name_ptr = comp.bin_file.options.root_name.ptr, .root_name_len = comp.bin_file.options.root_name.len, - .output_dir_ptr = output_dir.ptr, - .output_dir_len = output_dir.len, + .emit_o_ptr = emit_bin_path.ptr, + .emit_o_len = emit_bin_path.len, + .emit_h_ptr = emit_h_path.ptr, + .emit_h_len = emit_h_path.len, + .emit_asm_ptr = emit_asm_path.ptr, + .emit_asm_len = emit_asm_path.len, + .emit_llvm_ir_ptr = emit_llvm_ir_path.ptr, + .emit_llvm_ir_len = emit_llvm_ir_path.len, + .emit_analysis_json_ptr = emit_analysis_path.ptr, + .emit_analysis_json_len = emit_analysis_path.len, + .emit_docs_ptr = emit_docs_path.ptr, + .emit_docs_len = emit_docs_path.len, .builtin_zig_path_ptr = builtin_zig_path.ptr, .builtin_zig_path_len = builtin_zig_path.len, .test_filter_ptr = test_filter.ptr, @@ -2530,11 +2569,6 @@ fn updateStage1Module(comp: *Compilation) !void { .enable_stack_probing = comp.bin_file.options.stack_check, .enable_time_report = comp.time_report, .enable_stack_report = false, - .dump_analysis = false, - .enable_doc_generation = false, - .emit_bin = true, - .emit_asm = false, - .emit_llvm_ir = false, .test_is_evented = comp.test_evented_io, .verbose_tokenize = comp.verbose_tokenize, .verbose_ast = comp.verbose_ast, @@ -2565,6 +2599,12 @@ fn updateStage1Module(comp: *Compilation) !void { comp.stage1_lock = man.toOwnedLock(); } +fn stage1LocPath(arena: *Allocator, opt_loc: ?EmitLoc, cache_directory: Directory) ![]const u8 { + const loc = opt_loc orelse return ""; + const directory = loc.directory orelse cache_directory; + return directory.join(arena, &[_][]const u8{loc.basename}); +} + fn createStage1Pkg( arena: *Allocator, name: []const u8, diff --git a/src/llvm.zig b/src/llvm.zig index 64a6d4e8b..ceefb62c5 100644 --- a/src/llvm.zig +++ b/src/llvm.zig @@ -72,3 +72,6 @@ pub const OSType = extern enum(c_int) { WASI = 34, Emscripten = 35, }; + +pub const ParseCommandLineOptions = ZigLLVMParseCommandLineOptions; +extern fn ZigLLVMParseCommandLineOptions(argc: usize, argv: [*]const [*:0]const u8) void; diff --git a/src/main.zig b/src/main.zig index 2204c1f3e..30338f11a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -195,8 +195,20 @@ const usage_build_generic = \\ -h, --help Print this help and exit \\ --watch Enable compiler REPL \\ --color [auto|off|on] Enable or disable colored error messages - \\ -femit-bin[=path] (default) output machine code + \\ -femit-bin[=path] (default) Output machine code \\ -fno-emit-bin Do not output machine code + \\ -femit-asm[=path] Output .s (assembly code) + \\ -fno-emit-asm (default) Do not output .s (assembly code) + \\ -femit-zir[=path] Produce a .zir file with Zig IR + \\ -fno-emit-zir (default) Do not produce a .zir file with Zig IR + \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions) + \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR + \\ -femit-h[=path] Generate a C header file (.h) + \\ -fno-emit-h (default) Do not generate a C header file (.h) + \\ -femit-docs[=path] Create a docs/ dir with html documentation + \\ -fno-emit-docs (default) Do not produce docs/ dir with html documentation + \\ -femit-analysis[=path] Write analysis JSON file with type information + \\ -fno-emit-analysis (default) Do not write analysis JSON file with type information \\ --show-builtin Output the source of @import("builtin") then exit \\ --cache-dir [path] Override the local cache directory \\ --global-cache-dir [path] Override the global cache directory @@ -210,10 +222,11 @@ const usage_build_generic = \\ small|kernel| \\ medium|large] \\ --name [name] Override root name (not a file path) - \\ -ODebug (default) optimizations off, safety on - \\ -OReleaseFast Optimizations on, safety off - \\ -OReleaseSafe Optimizations on, safety on - \\ -OReleaseSmall Optimize for small binary, safety off + \\ -O [mode] Choose what to optimize for + \\ Debug (default) Optimizations off, safety on + \\ ReleaseFast Optimizations on, safety off + \\ ReleaseSafe Optimizations on, safety on + \\ ReleaseSmall Optimize for small binary, safety off \\ --pkg-begin [name] [path] Make pkg available to import and push current pkg \\ --pkg-end Pop current pkg \\ --main-pkg-path Set the directory of the root package @@ -290,9 +303,57 @@ const Emit = union(enum) { no, yes_default_path, yes: []const u8, + + const Resolved = struct { + data: ?Compilation.EmitLoc, + dir: ?fs.Dir, + + fn deinit(self: *Resolved) void { + if (self.dir) |*dir| { + dir.close(); + } + } + }; + + fn resolve(emit: Emit, default_basename: []const u8) !Resolved { + var resolved: Resolved = .{ .data = null, .dir = null }; + errdefer resolved.deinit(); + + switch (emit) { + .no => {}, + .yes_default_path => { + resolved.data = Compilation.EmitLoc{ + .directory = .{ .path = null, .handle = fs.cwd() }, + .basename = default_basename, + }; + }, + .yes => |full_path| { + const basename = fs.path.basename(full_path); + if (fs.path.dirname(full_path)) |dirname| { + const handle = try fs.cwd().openDir(dirname, .{}); + resolved = .{ + .dir = handle, + .data = Compilation.EmitLoc{ + .basename = basename, + .directory = .{ + .path = dirname, + .handle = handle, + }, + }, + }; + } else { + resolved.data = Compilation.EmitLoc{ + .basename = basename, + .directory = .{ .path = null, .handle = fs.cwd() }, + }; + } + }, + } + return resolved; + } }; -pub fn buildOutputType( +fn buildOutputType( gpa: *Allocator, arena: *Allocator, all_args: []const []const u8, @@ -328,7 +389,10 @@ pub fn buildOutputType( var show_builtin = false; var emit_bin: Emit = .yes_default_path; var emit_asm: Emit = .no; + var emit_llvm_ir: Emit = .no; var emit_zir: Emit = .no; + var emit_docs: Emit = .no; + var emit_analysis: Emit = .no; var target_arch_os_abi: []const u8 = "native"; var target_mcpu: ?[]const u8 = null; var target_dynamic_linker: ?[]const u8 = null; @@ -667,6 +731,30 @@ pub fn buildOutputType( emit_h = .{ .yes = arg["-femit-h=".len..] }; } else if (mem.eql(u8, arg, "-fno-emit-h")) { emit_h = .no; + } else if (mem.eql(u8, arg, "-femit-asm")) { + emit_asm = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-asm=")) { + emit_asm = .{ .yes = arg["-femit-asm=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-asm")) { + emit_asm = .no; + } else if (mem.eql(u8, arg, "-femit-llvm-ir")) { + emit_llvm_ir = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-llvm-ir=")) { + emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) { + emit_llvm_ir = .no; + } else if (mem.eql(u8, arg, "-femit-docs")) { + emit_docs = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-docs=")) { + emit_docs = .{ .yes = arg["-femit-docs=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-docs")) { + emit_docs = .no; + } else if (mem.eql(u8, arg, "-femit-analysis")) { + emit_analysis = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-analysis=")) { + emit_analysis = .{ .yes = arg["-femit-analysis=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-analysis")) { + emit_analysis = .no; } else if (mem.eql(u8, arg, "-dynamic")) { link_mode = .Dynamic; } else if (mem.eql(u8, arg, "-static")) { @@ -1237,35 +1325,24 @@ pub fn buildOutputType( }, }; - var cleanup_emit_h_dir: ?fs.Dir = null; - defer if (cleanup_emit_h_dir) |*dir| dir.close(); + const default_h_basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name}); + var emit_h_resolved = try emit_h.resolve(default_h_basename); + defer emit_h_resolved.deinit(); - const emit_h_loc: ?Compilation.EmitLoc = switch (emit_h) { - .no => null, - .yes_default_path => Compilation.EmitLoc{ - .directory = .{ .path = null, .handle = fs.cwd() }, - .basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name}), - }, - .yes => |full_path| b: { - const basename = fs.path.basename(full_path); - if (fs.path.dirname(full_path)) |dirname| { - const handle = try fs.cwd().openDir(dirname, .{}); - cleanup_emit_h_dir = handle; - break :b Compilation.EmitLoc{ - .basename = basename, - .directory = .{ - .path = dirname, - .handle = handle, - }, - }; - } else { - break :b Compilation.EmitLoc{ - .basename = basename, - .directory = .{ .path = null, .handle = fs.cwd() }, - }; - } - }, - }; + const default_asm_basename = try std.fmt.allocPrint(arena, "{}.s", .{root_name}); + var emit_asm_resolved = try emit_asm.resolve(default_asm_basename); + defer emit_asm_resolved.deinit(); + + const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{}.ll", .{root_name}); + var emit_llvm_ir_resolved = try emit_llvm_ir.resolve(default_llvm_ir_basename); + defer emit_llvm_ir_resolved.deinit(); + + const default_analysis_basename = try std.fmt.allocPrint(arena, "{}-analysis.json", .{root_name}); + var emit_analysis_resolved = try emit_analysis.resolve(default_analysis_basename); + defer emit_analysis_resolved.deinit(); + + var emit_docs_resolved = try emit_docs.resolve("docs"); + defer emit_docs_resolved.deinit(); const zir_out_path: ?[]const u8 = switch (emit_zir) { .no => null, @@ -1365,6 +1442,12 @@ pub fn buildOutputType( }; }; + if (build_options.have_llvm and emit_asm != .no) { + // LLVM has no way to set this non-globally. + const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" }; + @import("llvm.zig").ParseCommandLineOptions(argv.len, &argv); + } + gimmeMoreOfThoseSweetSweetFileDescriptors(); const comp = Compilation.create(gpa, .{ @@ -1378,7 +1461,11 @@ pub fn buildOutputType( .output_mode = output_mode, .root_pkg = root_pkg, .emit_bin = emit_bin_loc, - .emit_h = emit_h_loc, + .emit_h = emit_h_resolved.data, + .emit_asm = emit_asm_resolved.data, + .emit_llvm_ir = emit_llvm_ir_resolved.data, + .emit_docs = emit_docs_resolved.data, + .emit_analysis = emit_analysis_resolved.data, .link_mode = link_mode, .dll_export_fns = dll_export_fns, .object_format = object_format, diff --git a/src/stage1.zig b/src/stage1.zig index 7a9609f31..d05c0ba7f 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -75,8 +75,18 @@ pub const Pkg = extern struct { pub const Module = extern struct { root_name_ptr: [*]const u8, root_name_len: usize, - output_dir_ptr: [*]const u8, - output_dir_len: usize, + emit_o_ptr: [*]const u8, + emit_o_len: usize, + emit_h_ptr: [*]const u8, + emit_h_len: usize, + emit_asm_ptr: [*]const u8, + emit_asm_len: usize, + emit_llvm_ir_ptr: [*]const u8, + emit_llvm_ir_len: usize, + emit_analysis_json_ptr: [*]const u8, + emit_analysis_json_len: usize, + emit_docs_ptr: [*]const u8, + emit_docs_len: usize, builtin_zig_path_ptr: [*]const u8, builtin_zig_path_len: usize, test_filter_ptr: [*]const u8, @@ -101,11 +111,6 @@ pub const Module = extern struct { enable_stack_probing: bool, enable_time_report: bool, enable_stack_report: bool, - dump_analysis: bool, - enable_doc_generation: bool, - emit_bin: bool, - emit_asm: bool, - emit_llvm_ir: bool, test_is_evented: bool, verbose_tokenize: bool, verbose_ast: bool, diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index 75a89d272..a1d0cd0aa 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -2116,12 +2116,12 @@ struct CodeGen { Buf llvm_triple_str; Buf global_asm; Buf o_file_output_path; + Buf h_file_output_path; Buf asm_file_output_path; Buf llvm_ir_file_output_path; + Buf analysis_json_output_path; + Buf docs_output_path; Buf *cache_dir; - // As an input parameter, mutually exclusive with enable_cache. But it gets - // populated in codegen_build_and_link. - Buf *output_dir; Buf *c_artifact_dir; const char **libc_include_dir_list; size_t libc_include_dir_len; @@ -2186,11 +2186,6 @@ struct CodeGen { bool dll_export_fns; bool have_stack_probing; bool function_sections; - bool enable_dump_analysis; - bool enable_doc_generation; - bool emit_bin; - bool emit_asm; - bool emit_llvm_ir; bool test_is_evented; bool valgrind_enabled; diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 0a8a1f7a9..fff1c325f 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -8243,9 +8243,9 @@ static void zig_llvm_emit_output(CodeGen *g) { const char *bin_filename = nullptr; const char *llvm_ir_filename = nullptr; - if (g->emit_bin) bin_filename = buf_ptr(&g->o_file_output_path); - if (g->emit_asm) asm_filename = buf_ptr(&g->asm_file_output_path); - if (g->emit_llvm_ir) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path); + if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path); + if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path); + if (buf_len(&g->llvm_ir_file_output_path) != 0) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path); // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire // pipeline multiple times if this is requested. @@ -8858,8 +8858,10 @@ static Error define_builtin_compile_vars(CodeGen *g) { Buf *contents; if (g->builtin_zig_path == nullptr) { // Then this is zig0 building stage2. We can make many assumptions about the compilation. + Buf *out_dir = buf_alloc(); + os_path_split(&g->o_file_output_path, out_dir, nullptr); g->builtin_zig_path = buf_alloc(); - os_path_join(g->output_dir, buf_create_from_str(builtin_zig_basename), g->builtin_zig_path); + os_path_join(out_dir, buf_create_from_str(builtin_zig_basename), g->builtin_zig_path); Buf *resolve_paths[] = { g->builtin_zig_path, }; *g->builtin_zig_path = os_path_resolve(resolve_paths, 1); @@ -8870,7 +8872,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { exit(1); } - g->compile_var_package = new_package(buf_ptr(g->output_dir), builtin_zig_basename, "builtin"); + g->compile_var_package = new_package(buf_ptr(out_dir), builtin_zig_basename, "builtin"); } else { Buf *resolve_paths[] = { g->builtin_zig_path, }; *g->builtin_zig_path = os_path_resolve(resolve_paths, 1); @@ -9232,33 +9234,20 @@ void codegen_add_time_event(CodeGen *g, const char *name) { g->timing_events.append({seconds, name}); } -static void resolve_out_paths(CodeGen *g) { - assert(g->output_dir != nullptr); - assert(g->root_out_name != nullptr); +void codegen_build_object(CodeGen *g) { + g->have_err_ret_tracing = detect_err_ret_tracing(g); - if (g->emit_bin) { - Buf *o_basename = buf_create_from_buf(g->root_out_name); - buf_append_str(o_basename, target_o_file_ext(g->zig_target)); - os_path_join(g->output_dir, o_basename, &g->o_file_output_path); - } - if (g->emit_asm) { - Buf *asm_basename = buf_create_from_buf(g->root_out_name); - const char *asm_ext = target_asm_file_ext(g->zig_target); - buf_append_str(asm_basename, asm_ext); - os_path_join(g->output_dir, asm_basename, &g->asm_file_output_path); - } - if (g->emit_llvm_ir) { - Buf *llvm_ir_basename = buf_create_from_buf(g->root_out_name); - const char *llvm_ir_ext = target_llvm_ir_file_ext(g->zig_target); - buf_append_str(llvm_ir_basename, llvm_ir_ext); - os_path_join(g->output_dir, llvm_ir_basename, &g->llvm_ir_file_output_path); - } -} + init(g); -static void output_type_information(CodeGen *g) { - if (g->enable_dump_analysis) { - const char *analysis_json_filename = buf_ptr(buf_sprintf("%s" OS_SEP "%s-analysis.json", - buf_ptr(g->output_dir), buf_ptr(g->root_out_name))); + codegen_add_time_event(g, "Semantic Analysis"); + const char *progress_name = "Semantic Analysis"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + + gen_root_source(g); + + if (buf_len(&g->analysis_json_output_path) != 0) { + const char *analysis_json_filename = buf_ptr(&g->analysis_json_output_path); FILE *f = fopen(analysis_json_filename, "wb"); if (f == nullptr) { fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno)); @@ -9270,9 +9259,9 @@ static void output_type_information(CodeGen *g) { exit(1); } } - if (g->enable_doc_generation) { + if (buf_len(&g->docs_output_path) != 0) { Error err; - Buf *doc_dir_path = buf_sprintf("%s" OS_SEP "docs", buf_ptr(g->output_dir)); + Buf *doc_dir_path = &g->docs_output_path; if ((err = os_make_path(doc_dir_path))) { fprintf(stderr, "Unable to create directory %s: %s\n", buf_ptr(doc_dir_path), err_str(err)); exit(1); @@ -9308,27 +9297,6 @@ static void output_type_information(CodeGen *g) { exit(1); } } -} - -void codegen_build_object(CodeGen *g) { - assert(g->output_dir != nullptr); - - g->have_err_ret_tracing = detect_err_ret_tracing(g); - - init(g); - - codegen_add_time_event(g, "Semantic Analysis"); - const char *progress_name = "Semantic Analysis"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - - gen_root_source(g); - - resolve_out_paths(g); - - if (g->enable_dump_analysis || g->enable_doc_generation) { - output_type_information(g); - } codegen_add_time_event(g, "Code Generation"); { @@ -9379,7 +9347,6 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget bool is_test_build) { CodeGen *g = heap::c_allocator.create(); - g->emit_bin = true; g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1"); g->subsystem = TargetSubsystemAuto; diff --git a/src/stage1/stage1.cpp b/src/stage1/stage1.cpp index b5b87c05d..1034f9ff8 100644 --- a/src/stage1/stage1.cpp +++ b/src/stage1/stage1.cpp @@ -69,7 +69,13 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) { CodeGen *g = reinterpret_cast(stage1); g->root_out_name = buf_create_from_mem(stage1->root_name_ptr, stage1->root_name_len); - g->output_dir = buf_create_from_mem(stage1->output_dir_ptr, stage1->output_dir_len); + buf_init_from_mem(&g->o_file_output_path, stage1->emit_o_ptr, stage1->emit_o_len); + buf_init_from_mem(&g->h_file_output_path, stage1->emit_h_ptr, stage1->emit_h_len); + buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len); + buf_init_from_mem(&g->llvm_ir_file_output_path, stage1->emit_llvm_ir_ptr, stage1->emit_llvm_ir_len); + buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len); + buf_init_from_mem(&g->docs_output_path, stage1->emit_docs_ptr, stage1->emit_docs_len); + if (stage1->builtin_zig_path_len != 0) { g->builtin_zig_path = buf_create_from_mem(stage1->builtin_zig_path_ptr, stage1->builtin_zig_path_len); } @@ -94,11 +100,6 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) { g->enable_time_report = stage1->enable_time_report; g->enable_stack_report = stage1->enable_stack_report; - g->enable_dump_analysis = stage1->dump_analysis; - g->enable_doc_generation = stage1->enable_doc_generation; - g->emit_bin = stage1->emit_bin; - g->emit_asm = stage1->emit_asm; - g->emit_llvm_ir = stage1->emit_llvm_ir; g->test_is_evented = stage1->test_is_evented; g->verbose_tokenize = stage1->verbose_tokenize; diff --git a/src/stage1/stage1.h b/src/stage1/stage1.h index c0bd0f70f..e5ffa62e1 100644 --- a/src/stage1/stage1.h +++ b/src/stage1/stage1.h @@ -141,8 +141,23 @@ struct ZigStage1 { const char *root_name_ptr; size_t root_name_len; - const char *output_dir_ptr; - size_t output_dir_len; + const char *emit_o_ptr; + size_t emit_o_len; + + const char *emit_h_ptr; + size_t emit_h_len; + + const char *emit_asm_ptr; + size_t emit_asm_len; + + const char *emit_llvm_ir_ptr; + size_t emit_llvm_ir_len; + + const char *emit_analysis_json_ptr; + size_t emit_analysis_json_len; + + const char *emit_docs_ptr; + size_t emit_docs_len; const char *builtin_zig_path_ptr; size_t builtin_zig_path_len; @@ -173,11 +188,6 @@ struct ZigStage1 { bool enable_stack_probing; bool enable_time_report; bool enable_stack_report; - bool dump_analysis; - bool enable_doc_generation; - bool emit_bin; - bool emit_asm; - bool emit_llvm_ir; bool test_is_evented; bool verbose_tokenize; bool verbose_ast; diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp index 5c384991f..7fd20c524 100644 --- a/src/stage1/zig0.cpp +++ b/src/stage1/zig0.cpp @@ -241,7 +241,7 @@ int main(int argc, char **argv) { Error err; const char *in_file = nullptr; - const char *output_dir = nullptr; + const char *emit_bin_path = nullptr; bool strip = false; const char *out_name = nullptr; bool verbose_tokenize = false; @@ -324,14 +324,14 @@ int main(int argc, char **argv) { cur_pkg = cur_pkg->parent; } else if (str_starts_with(arg, "-mcpu=")) { mcpu = arg + strlen("-mcpu="); + } else if (str_starts_with(arg, "-femit-bin=")) { + emit_bin_path = arg + strlen("-femit-bin="); } else if (i + 1 >= argc) { fprintf(stderr, "Expected another argument after %s\n", arg); return print_error_usage(arg0); } else { i += 1; - if (strcmp(arg, "--output-dir") == 0) { - output_dir = argv[i]; - } else if (strcmp(arg, "--color") == 0) { + if (strcmp(arg, "--color") == 0) { if (strcmp(argv[i], "auto") == 0) { color = ErrColorAuto; } else if (strcmp(argv[i], "on") == 0) { @@ -443,15 +443,14 @@ int main(int argc, char **argv) { stage1->verbose_llvm_ir = verbose_llvm_ir; stage1->verbose_cimport = verbose_cimport; stage1->verbose_llvm_cpu_features = verbose_llvm_cpu_features; - stage1->output_dir_ptr = output_dir; - stage1->output_dir_len = strlen(output_dir); + stage1->emit_o_ptr = emit_bin_path; + stage1->emit_o_len = strlen(emit_bin_path); stage1->root_pkg = cur_pkg; stage1->err_color = color; stage1->link_libc = link_libc; stage1->link_libcpp = link_libcpp; stage1->subsystem = subsystem; stage1->pic = true; - stage1->emit_bin = true; zig_stage1_build_object(stage1); diff --git a/test/cli.zig b/test/cli.zig index b9de23e25..c92636fb3 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -118,17 +118,20 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { \\} ); - const args = [_][]const u8{ + var args = std.ArrayList([]const u8).init(a); + try args.appendSlice(&[_][]const u8{ zig_exe, "build-obj", "--cache-dir", dir_path, "--name", "example", - "--output-dir", dir_path, - "--emit", "asm", - "-mllvm", "--x86-asm-syntax=intel", - "--strip", "--release-fast", - example_zig_path, "--disable-gen-h", - }; - _ = try exec(dir_path, &args); + "-fno-emit-bin", "-fno-emit-h", + "--strip", "-OReleaseFast", + example_zig_path, + }); + + const emit_asm_arg = try std.fmt.allocPrint(a, "-femit-asm={s}", .{example_s_path}); + try args.append(emit_asm_arg); + + _ = try exec(dir_path, args.items); const out_asm = try std.fs.cwd().readFileAlloc(a, example_s_path, std.math.maxInt(usize)); testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null);