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.
master
Andrew Kelley 2020-09-26 01:42:54 -07:00
parent a337046832
commit 6af2990549
12 changed files with 263 additions and 158 deletions

View File

@ -1,13 +1,7 @@
* support -fno-emit-bin for godbolt
* tests passing with -Dskip-non-native * tests passing with -Dskip-non-native
* `-ftime-report` * `-ftime-report`
* -fstack-report print stack size diagnostics\n" * -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 * mingw-w64
* MachO LLD linking * MachO LLD linking
* COFF 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. * 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 * 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] * 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 * try building some software with zig cc to make sure it didn't regress
* implement support for -femit-asm * 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 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 * implement proper parsing of LLD stderr/stdout and exposing compile errors with the Compilation API
@ -56,3 +50,4 @@
* make std.Progress support multithreaded * make std.Progress support multithreaded
* update musl.zig static data to use native path separator in static data rather than replacing '/' at runtime * 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) * 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)

View File

@ -449,7 +449,7 @@ endif()
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
set(ZIG1_RELEASE_ARG "") set(ZIG1_RELEASE_ARG "")
else() else()
set(ZIG1_RELEASE_ARG --release-fast --strip) set(ZIG1_RELEASE_ARG -OReleaseFast --strip)
endif() endif()
set(BUILD_ZIG1_ARGS set(BUILD_ZIG1_ARGS
@ -458,8 +458,8 @@ set(BUILD_ZIG1_ARGS
"-mcpu=${ZIG_TARGET_MCPU}" "-mcpu=${ZIG_TARGET_MCPU}"
--name zig1 --name zig1
--override-lib-dir "${CMAKE_SOURCE_DIR}/lib" --override-lib-dir "${CMAKE_SOURCE_DIR}/lib"
--output-dir "${CMAKE_BINARY_DIR}" "-femit-bin=${ZIG1_OBJECT}"
${ZIG1_RELEASE_ARG} "${ZIG1_RELEASE_ARG}"
-lc -lc
--pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}"
--pkg-end --pkg-end

View File

@ -107,6 +107,12 @@ test_filter: ?[]const u8,
test_name_prefix: ?[]const u8, test_name_prefix: ?[]const u8,
test_evented_io: bool, 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 InnerError = Module.InnerError;
pub const CRTFile = struct { pub const CRTFile = struct {
@ -296,6 +302,12 @@ pub const InitOptions = struct {
emit_h: ?EmitLoc = null, emit_h: ?EmitLoc = null,
/// `null` means to not emit assembly. /// `null` means to not emit assembly.
emit_asm: ?EmitLoc = null, 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, link_mode: ?std.builtin.LinkMode = null,
dll_export_fns: ?bool = false, dll_export_fns: ?bool = false,
/// Normally when using LLD to link, Zig uses a file named "lld.id" in the /// 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; break :blk false;
}; };
const link_libc = options.link_libc or const link_libc = options.link_libc or
(is_exe_or_dyn_lib and target_util.osRequiresLibC(options.target)); (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; break :pic explicit;
} else must_pic; } 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 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. // 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.link_libcpp);
cache.hash.add(options.output_mode); cache.hash.add(options.output_mode);
cache.hash.add(options.machine_code_model); 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 // TODO audit this and make sure everything is in it
const module: ?*Module = if (options.root_pkg) |root_pkg| blk: { 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, .local_cache_directory = options.local_cache_directory,
.global_cache_directory = options.global_cache_directory, .global_cache_directory = options.global_cache_directory,
.bin_file = bin_file, .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), .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
.keep_source_files_loaded = options.keep_source_files_loaded, .keep_source_files_loaded = options.keep_source_files_loaded,
.use_clang = use_clang, .use_clang = use_clang,
@ -1450,8 +1469,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void {
try argv.ensureCapacity(argv.items.len + 3); try argv.ensureCapacity(argv.items.len + 3);
switch (comp.clang_preprocessor_mode) { switch (comp.clang_preprocessor_mode) {
.no => argv.appendSliceAssumeCapacity(&[_][]const u8{"-c", "-o", out_obj_path}), .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
.yes => argv.appendSliceAssumeCapacity(&[_][]const u8{"-E", "-o", out_obj_path}), .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
.stdout => argv.appendAssumeCapacity("-E"), .stdout => argv.appendAssumeCapacity("-E"),
} }
@ -2498,15 +2517,35 @@ fn updateStage1Module(comp: *Compilation) !void {
comp.is_test, comp.is_test,
) orelse return error.OutOfMemory; ) 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 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_filter = comp.test_filter orelse ""[0..0];
const test_name_prefix = comp.test_name_prefix orelse ""[0..0]; const test_name_prefix = comp.test_name_prefix orelse ""[0..0];
stage1_module.* = .{ stage1_module.* = .{
.root_name_ptr = comp.bin_file.options.root_name.ptr, .root_name_ptr = comp.bin_file.options.root_name.ptr,
.root_name_len = comp.bin_file.options.root_name.len, .root_name_len = comp.bin_file.options.root_name.len,
.output_dir_ptr = output_dir.ptr, .emit_o_ptr = emit_bin_path.ptr,
.output_dir_len = output_dir.len, .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_ptr = builtin_zig_path.ptr,
.builtin_zig_path_len = builtin_zig_path.len, .builtin_zig_path_len = builtin_zig_path.len,
.test_filter_ptr = test_filter.ptr, .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_stack_probing = comp.bin_file.options.stack_check,
.enable_time_report = comp.time_report, .enable_time_report = comp.time_report,
.enable_stack_report = false, .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, .test_is_evented = comp.test_evented_io,
.verbose_tokenize = comp.verbose_tokenize, .verbose_tokenize = comp.verbose_tokenize,
.verbose_ast = comp.verbose_ast, .verbose_ast = comp.verbose_ast,
@ -2565,6 +2599,12 @@ fn updateStage1Module(comp: *Compilation) !void {
comp.stage1_lock = man.toOwnedLock(); 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( fn createStage1Pkg(
arena: *Allocator, arena: *Allocator,
name: []const u8, name: []const u8,

View File

@ -72,3 +72,6 @@ pub const OSType = extern enum(c_int) {
WASI = 34, WASI = 34,
Emscripten = 35, Emscripten = 35,
}; };
pub const ParseCommandLineOptions = ZigLLVMParseCommandLineOptions;
extern fn ZigLLVMParseCommandLineOptions(argc: usize, argv: [*]const [*:0]const u8) void;

View File

@ -195,8 +195,20 @@ const usage_build_generic =
\\ -h, --help Print this help and exit \\ -h, --help Print this help and exit
\\ --watch Enable compiler REPL \\ --watch Enable compiler REPL
\\ --color [auto|off|on] Enable or disable colored error messages \\ --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 \\ -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 \\ --show-builtin Output the source of @import("builtin") then exit
\\ --cache-dir [path] Override the local cache directory \\ --cache-dir [path] Override the local cache directory
\\ --global-cache-dir [path] Override the global cache directory \\ --global-cache-dir [path] Override the global cache directory
@ -210,10 +222,11 @@ const usage_build_generic =
\\ small|kernel| \\ small|kernel|
\\ medium|large] \\ medium|large]
\\ --name [name] Override root name (not a file path) \\ --name [name] Override root name (not a file path)
\\ -ODebug (default) optimizations off, safety on \\ -O [mode] Choose what to optimize for
\\ -OReleaseFast Optimizations on, safety off \\ Debug (default) Optimizations off, safety on
\\ -OReleaseSafe Optimizations on, safety on \\ ReleaseFast Optimizations on, safety off
\\ -OReleaseSmall Optimize for small binary, 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-begin [name] [path] Make pkg available to import and push current pkg
\\ --pkg-end Pop current pkg \\ --pkg-end Pop current pkg
\\ --main-pkg-path Set the directory of the root package \\ --main-pkg-path Set the directory of the root package
@ -290,9 +303,57 @@ const Emit = union(enum) {
no, no,
yes_default_path, yes_default_path,
yes: []const u8, 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, gpa: *Allocator,
arena: *Allocator, arena: *Allocator,
all_args: []const []const u8, all_args: []const []const u8,
@ -328,7 +389,10 @@ pub fn buildOutputType(
var show_builtin = false; var show_builtin = false;
var emit_bin: Emit = .yes_default_path; var emit_bin: Emit = .yes_default_path;
var emit_asm: Emit = .no; var emit_asm: Emit = .no;
var emit_llvm_ir: Emit = .no;
var emit_zir: 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_arch_os_abi: []const u8 = "native";
var target_mcpu: ?[]const u8 = null; var target_mcpu: ?[]const u8 = null;
var target_dynamic_linker: ?[]const u8 = null; var target_dynamic_linker: ?[]const u8 = null;
@ -667,6 +731,30 @@ pub fn buildOutputType(
emit_h = .{ .yes = arg["-femit-h=".len..] }; emit_h = .{ .yes = arg["-femit-h=".len..] };
} else if (mem.eql(u8, arg, "-fno-emit-h")) { } else if (mem.eql(u8, arg, "-fno-emit-h")) {
emit_h = .no; 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")) { } else if (mem.eql(u8, arg, "-dynamic")) {
link_mode = .Dynamic; link_mode = .Dynamic;
} else if (mem.eql(u8, arg, "-static")) { } else if (mem.eql(u8, arg, "-static")) {
@ -1237,35 +1325,24 @@ pub fn buildOutputType(
}, },
}; };
var cleanup_emit_h_dir: ?fs.Dir = null; const default_h_basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name});
defer if (cleanup_emit_h_dir) |*dir| dir.close(); 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) { const default_asm_basename = try std.fmt.allocPrint(arena, "{}.s", .{root_name});
.no => null, var emit_asm_resolved = try emit_asm.resolve(default_asm_basename);
.yes_default_path => Compilation.EmitLoc{ defer emit_asm_resolved.deinit();
.directory = .{ .path = null, .handle = fs.cwd() },
.basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name}), 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);
.yes => |full_path| b: { defer emit_llvm_ir_resolved.deinit();
const basename = fs.path.basename(full_path);
if (fs.path.dirname(full_path)) |dirname| { const default_analysis_basename = try std.fmt.allocPrint(arena, "{}-analysis.json", .{root_name});
const handle = try fs.cwd().openDir(dirname, .{}); var emit_analysis_resolved = try emit_analysis.resolve(default_analysis_basename);
cleanup_emit_h_dir = handle; defer emit_analysis_resolved.deinit();
break :b Compilation.EmitLoc{
.basename = basename, var emit_docs_resolved = try emit_docs.resolve("docs");
.directory = .{ defer emit_docs_resolved.deinit();
.path = dirname,
.handle = handle,
},
};
} else {
break :b Compilation.EmitLoc{
.basename = basename,
.directory = .{ .path = null, .handle = fs.cwd() },
};
}
},
};
const zir_out_path: ?[]const u8 = switch (emit_zir) { const zir_out_path: ?[]const u8 = switch (emit_zir) {
.no => null, .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(); gimmeMoreOfThoseSweetSweetFileDescriptors();
const comp = Compilation.create(gpa, .{ const comp = Compilation.create(gpa, .{
@ -1378,7 +1461,11 @@ pub fn buildOutputType(
.output_mode = output_mode, .output_mode = output_mode,
.root_pkg = root_pkg, .root_pkg = root_pkg,
.emit_bin = emit_bin_loc, .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, .link_mode = link_mode,
.dll_export_fns = dll_export_fns, .dll_export_fns = dll_export_fns,
.object_format = object_format, .object_format = object_format,

View File

@ -75,8 +75,18 @@ pub const Pkg = extern struct {
pub const Module = extern struct { pub const Module = extern struct {
root_name_ptr: [*]const u8, root_name_ptr: [*]const u8,
root_name_len: usize, root_name_len: usize,
output_dir_ptr: [*]const u8, emit_o_ptr: [*]const u8,
output_dir_len: usize, 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_ptr: [*]const u8,
builtin_zig_path_len: usize, builtin_zig_path_len: usize,
test_filter_ptr: [*]const u8, test_filter_ptr: [*]const u8,
@ -101,11 +111,6 @@ pub const Module = extern struct {
enable_stack_probing: bool, enable_stack_probing: bool,
enable_time_report: bool, enable_time_report: bool,
enable_stack_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, test_is_evented: bool,
verbose_tokenize: bool, verbose_tokenize: bool,
verbose_ast: bool, verbose_ast: bool,

View File

@ -2116,12 +2116,12 @@ struct CodeGen {
Buf llvm_triple_str; Buf llvm_triple_str;
Buf global_asm; Buf global_asm;
Buf o_file_output_path; Buf o_file_output_path;
Buf h_file_output_path;
Buf asm_file_output_path; Buf asm_file_output_path;
Buf llvm_ir_file_output_path; Buf llvm_ir_file_output_path;
Buf analysis_json_output_path;
Buf docs_output_path;
Buf *cache_dir; 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; Buf *c_artifact_dir;
const char **libc_include_dir_list; const char **libc_include_dir_list;
size_t libc_include_dir_len; size_t libc_include_dir_len;
@ -2186,11 +2186,6 @@ struct CodeGen {
bool dll_export_fns; bool dll_export_fns;
bool have_stack_probing; bool have_stack_probing;
bool function_sections; 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 test_is_evented;
bool valgrind_enabled; bool valgrind_enabled;

View File

@ -8243,9 +8243,9 @@ static void zig_llvm_emit_output(CodeGen *g) {
const char *bin_filename = nullptr; const char *bin_filename = nullptr;
const char *llvm_ir_filename = nullptr; const char *llvm_ir_filename = nullptr;
if (g->emit_bin) bin_filename = buf_ptr(&g->o_file_output_path); if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path);
if (g->emit_asm) asm_filename = buf_ptr(&g->asm_file_output_path); if (buf_len(&g->asm_file_output_path) != 0) 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->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 // 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. // pipeline multiple times if this is requested.
@ -8858,8 +8858,10 @@ static Error define_builtin_compile_vars(CodeGen *g) {
Buf *contents; Buf *contents;
if (g->builtin_zig_path == nullptr) { if (g->builtin_zig_path == nullptr) {
// Then this is zig0 building stage2. We can make many assumptions about the compilation. // 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(); 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, }; Buf *resolve_paths[] = { g->builtin_zig_path, };
*g->builtin_zig_path = os_path_resolve(resolve_paths, 1); *g->builtin_zig_path = os_path_resolve(resolve_paths, 1);
@ -8870,7 +8872,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
exit(1); 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 { } else {
Buf *resolve_paths[] = { g->builtin_zig_path, }; Buf *resolve_paths[] = { g->builtin_zig_path, };
*g->builtin_zig_path = os_path_resolve(resolve_paths, 1); *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}); g->timing_events.append({seconds, name});
} }
static void resolve_out_paths(CodeGen *g) { void codegen_build_object(CodeGen *g) {
assert(g->output_dir != nullptr); g->have_err_ret_tracing = detect_err_ret_tracing(g);
assert(g->root_out_name != nullptr);
if (g->emit_bin) { init(g);
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);
}
}
static void output_type_information(CodeGen *g) { codegen_add_time_event(g, "Semantic Analysis");
if (g->enable_dump_analysis) { const char *progress_name = "Semantic Analysis";
const char *analysis_json_filename = buf_ptr(buf_sprintf("%s" OS_SEP "%s-analysis.json", codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node,
buf_ptr(g->output_dir), buf_ptr(g->root_out_name))); 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"); FILE *f = fopen(analysis_json_filename, "wb");
if (f == nullptr) { if (f == nullptr) {
fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno)); 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); exit(1);
} }
} }
if (g->enable_doc_generation) { if (buf_len(&g->docs_output_path) != 0) {
Error err; 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))) { 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)); fprintf(stderr, "Unable to create directory %s: %s\n", buf_ptr(doc_dir_path), err_str(err));
exit(1); exit(1);
@ -9308,27 +9297,6 @@ static void output_type_information(CodeGen *g) {
exit(1); 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"); 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) bool is_test_build)
{ {
CodeGen *g = heap::c_allocator.create<CodeGen>(); CodeGen *g = heap::c_allocator.create<CodeGen>();
g->emit_bin = true;
g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1"); g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1");
g->subsystem = TargetSubsystemAuto; g->subsystem = TargetSubsystemAuto;

View File

@ -69,7 +69,13 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
CodeGen *g = reinterpret_cast<CodeGen *>(stage1); CodeGen *g = reinterpret_cast<CodeGen *>(stage1);
g->root_out_name = buf_create_from_mem(stage1->root_name_ptr, stage1->root_name_len); 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) { 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); 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_time_report = stage1->enable_time_report;
g->enable_stack_report = stage1->enable_stack_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->test_is_evented = stage1->test_is_evented;
g->verbose_tokenize = stage1->verbose_tokenize; g->verbose_tokenize = stage1->verbose_tokenize;

View File

@ -141,8 +141,23 @@ struct ZigStage1 {
const char *root_name_ptr; const char *root_name_ptr;
size_t root_name_len; size_t root_name_len;
const char *output_dir_ptr; const char *emit_o_ptr;
size_t output_dir_len; 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; const char *builtin_zig_path_ptr;
size_t builtin_zig_path_len; size_t builtin_zig_path_len;
@ -173,11 +188,6 @@ struct ZigStage1 {
bool enable_stack_probing; bool enable_stack_probing;
bool enable_time_report; bool enable_time_report;
bool enable_stack_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 test_is_evented;
bool verbose_tokenize; bool verbose_tokenize;
bool verbose_ast; bool verbose_ast;

View File

@ -241,7 +241,7 @@ int main(int argc, char **argv) {
Error err; Error err;
const char *in_file = nullptr; const char *in_file = nullptr;
const char *output_dir = nullptr; const char *emit_bin_path = nullptr;
bool strip = false; bool strip = false;
const char *out_name = nullptr; const char *out_name = nullptr;
bool verbose_tokenize = false; bool verbose_tokenize = false;
@ -324,14 +324,14 @@ int main(int argc, char **argv) {
cur_pkg = cur_pkg->parent; cur_pkg = cur_pkg->parent;
} else if (str_starts_with(arg, "-mcpu=")) { } else if (str_starts_with(arg, "-mcpu=")) {
mcpu = arg + strlen("-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) { } else if (i + 1 >= argc) {
fprintf(stderr, "Expected another argument after %s\n", arg); fprintf(stderr, "Expected another argument after %s\n", arg);
return print_error_usage(arg0); return print_error_usage(arg0);
} else { } else {
i += 1; i += 1;
if (strcmp(arg, "--output-dir") == 0) { if (strcmp(arg, "--color") == 0) {
output_dir = argv[i];
} else if (strcmp(arg, "--color") == 0) {
if (strcmp(argv[i], "auto") == 0) { if (strcmp(argv[i], "auto") == 0) {
color = ErrColorAuto; color = ErrColorAuto;
} else if (strcmp(argv[i], "on") == 0) { } 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_llvm_ir = verbose_llvm_ir;
stage1->verbose_cimport = verbose_cimport; stage1->verbose_cimport = verbose_cimport;
stage1->verbose_llvm_cpu_features = verbose_llvm_cpu_features; stage1->verbose_llvm_cpu_features = verbose_llvm_cpu_features;
stage1->output_dir_ptr = output_dir; stage1->emit_o_ptr = emit_bin_path;
stage1->output_dir_len = strlen(output_dir); stage1->emit_o_len = strlen(emit_bin_path);
stage1->root_pkg = cur_pkg; stage1->root_pkg = cur_pkg;
stage1->err_color = color; stage1->err_color = color;
stage1->link_libc = link_libc; stage1->link_libc = link_libc;
stage1->link_libcpp = link_libcpp; stage1->link_libcpp = link_libcpp;
stage1->subsystem = subsystem; stage1->subsystem = subsystem;
stage1->pic = true; stage1->pic = true;
stage1->emit_bin = true;
zig_stage1_build_object(stage1); zig_stage1_build_object(stage1);

View File

@ -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", zig_exe, "build-obj",
"--cache-dir", dir_path, "--cache-dir", dir_path,
"--name", "example", "--name", "example",
"--output-dir", dir_path, "-fno-emit-bin", "-fno-emit-h",
"--emit", "asm", "--strip", "-OReleaseFast",
"-mllvm", "--x86-asm-syntax=intel", example_zig_path,
"--strip", "--release-fast", });
example_zig_path, "--disable-gen-h",
}; const emit_asm_arg = try std.fmt.allocPrint(a, "-femit-asm={s}", .{example_s_path});
_ = try exec(dir_path, &args); 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)); 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); testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null);