stage2: expose progress bar API to linker backends

This gives us insight as to what is happening when we are waiting for
things such as LLVM emit object and LLD linking.
This commit is contained in:
Andrew Kelley 2022-04-17 04:06:51 -07:00
parent 2aeaa1cfc4
commit a7c05c06be
11 changed files with 134 additions and 83 deletions

View File

@ -2084,7 +2084,13 @@ pub fn update(comp: *Compilation) !void {
}
}
try comp.performAllTheWork();
// If the terminal is dumb, we dont want to show the user all the output.
var progress: std.Progress = .{ .dont_print_on_dumb = true };
const main_progress_node = progress.start("", 0);
defer main_progress_node.end();
if (comp.color == .off) progress.terminal = null;
try comp.performAllTheWork(main_progress_node);
if (!use_stage1) {
if (comp.bin_file.options.module) |module| {
@ -2158,9 +2164,9 @@ pub fn update(comp: *Compilation) !void {
.path = dir_path,
};
try comp.flush();
try comp.flush(main_progress_node);
} else {
try comp.flush();
try comp.flush(main_progress_node);
}
// Failure here only means an unnecessary cache miss.
@ -2171,7 +2177,7 @@ pub fn update(comp: *Compilation) !void {
assert(comp.bin_file.lock == null);
comp.bin_file.lock = man.toOwnedLock();
} else {
try comp.flush();
try comp.flush(main_progress_node);
}
// Unload all source files to save memory.
@ -2188,8 +2194,8 @@ pub fn update(comp: *Compilation) !void {
}
}
fn flush(comp: *Compilation) !void {
try comp.bin_file.flush(comp); // This is needed before reading the error flags.
fn flush(comp: *Compilation, prog_node: *std.Progress.Node) !void {
try comp.bin_file.flush(comp, prog_node); // This is needed before reading the error flags.
comp.link_error_flags = comp.bin_file.errorFlags();
const use_stage1 = build_options.omit_stage2 or
@ -2590,14 +2596,10 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 {
return module.compile_log_text.items;
}
pub fn performAllTheWork(comp: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {
// If the terminal is dumb, we dont want to show the user all the
// output.
var progress: std.Progress = .{ .dont_print_on_dumb = true };
var main_progress_node = progress.start("", 0);
defer main_progress_node.end();
if (comp.color == .off) progress.terminal = null;
pub fn performAllTheWork(
comp: *Compilation,
main_progress_node: *std.Progress.Node,
) error{ TimerUnsupported, OutOfMemory }!void {
// Here we queue up all the AstGen tasks first, followed by C object compilation.
// We wait until the AstGen tasks are all completed before proceeding to the
// (at least for now) single-threaded main work queue. However, C object compilation

View File

@ -469,7 +469,12 @@ pub const Object = struct {
_ = builder.buildRet(is_lt);
}
pub fn flushModule(self: *Object, comp: *Compilation) !void {
pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void {
var sub_prog_node = prog_node.start("LLVM Emit Object", 0);
sub_prog_node.activate();
sub_prog_node.context.refresh();
defer sub_prog_node.end();
try self.genErrorNameTable(comp);
try self.genCmpLtErrorsLenFunction(comp);

View File

@ -573,7 +573,7 @@ pub const File = struct {
/// Commit pending changes and write headers. Takes into account final output mode
/// and `use_lld`, not only `effectiveOutputMode`.
pub fn flush(base: *File, comp: *Compilation) !void {
pub fn flush(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) !void {
if (comp.clang_preprocessor_mode == .yes) {
const emit = base.options.emit orelse return; // -fno-emit-bin
// TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
@ -591,32 +591,32 @@ pub const File = struct {
const use_lld = build_options.have_llvm and base.options.use_lld;
if (use_lld and base.options.output_mode == .Lib and base.options.link_mode == .Static) {
return base.linkAsArchive(comp);
return base.linkAsArchive(comp, prog_node);
}
switch (base.tag) {
.coff => return @fieldParentPtr(Coff, "base", base).flush(comp),
.elf => return @fieldParentPtr(Elf, "base", base).flush(comp),
.macho => return @fieldParentPtr(MachO, "base", base).flush(comp),
.c => return @fieldParentPtr(C, "base", base).flush(comp),
.wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp),
.spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp),
.plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp),
.nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp),
.coff => return @fieldParentPtr(Coff, "base", base).flush(comp, prog_node),
.elf => return @fieldParentPtr(Elf, "base", base).flush(comp, prog_node),
.macho => return @fieldParentPtr(MachO, "base", base).flush(comp, prog_node),
.c => return @fieldParentPtr(C, "base", base).flush(comp, prog_node),
.wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp, prog_node),
.spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp, prog_node),
.plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp, prog_node),
.nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp, prog_node),
}
}
/// Commit pending changes and write headers. Works based on `effectiveOutputMode`
/// rather than final output mode.
pub fn flushModule(base: *File, comp: *Compilation) !void {
pub fn flushModule(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) !void {
switch (base.tag) {
.coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp),
.elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp),
.macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp),
.c => return @fieldParentPtr(C, "base", base).flushModule(comp),
.wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp),
.spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp),
.plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp),
.nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp),
.coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp, prog_node),
.elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp, prog_node),
.macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp, prog_node),
.c => return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node),
.wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp, prog_node),
.spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp, prog_node),
.plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp, prog_node),
.nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp, prog_node),
}
}
@ -754,7 +754,7 @@ pub const File = struct {
}
}
pub fn linkAsArchive(base: *File, comp: *Compilation) !void {
pub fn linkAsArchive(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
@ -787,9 +787,9 @@ pub const File = struct {
}
}
if (base.options.object_format == .macho) {
try base.cast(MachO).?.flushObject(comp);
try base.cast(MachO).?.flushObject(comp, prog_node);
} else {
try base.flushModule(comp);
try base.flushModule(comp, prog_node);
}
break :blk try fs.path.join(arena, &.{
fs.path.dirname(full_out_path_z).?, base.intermediary_basename.?,

View File

@ -235,14 +235,18 @@ pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void
_ = decl;
}
pub fn flush(self: *C, comp: *Compilation) !void {
return self.flushModule(comp);
pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void {
return self.flushModule(comp, prog_node);
}
pub fn flushModule(self: *C, comp: *Compilation) !void {
pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
var sub_prog_node = prog_node.start("Flush Module", 0);
sub_prog_node.activate();
defer sub_prog_node.end();
const gpa = comp.gpa;
const module = self.base.options.module.?;

View File

@ -829,36 +829,40 @@ pub fn updateDeclExports(
}
}
pub fn flush(self: *Coff, comp: *Compilation) !void {
pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
if (self.base.options.emit == null) {
if (build_options.have_llvm) {
if (self.llvm_object) |llvm_object| {
return try llvm_object.flushModule(comp);
return try llvm_object.flushModule(comp, prog_node);
}
}
return;
}
if (build_options.have_llvm and self.base.options.use_lld) {
return self.linkWithLLD(comp);
return self.linkWithLLD(comp, prog_node);
} else {
switch (self.base.options.effectiveOutputMode()) {
.Exe, .Obj => {},
.Lib => return error.TODOImplementWritingLibFiles,
}
return self.flushModule(comp);
return self.flushModule(comp, prog_node);
}
}
pub fn flushModule(self: *Coff, comp: *Compilation) !void {
pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
if (build_options.have_llvm) {
if (self.llvm_object) |llvm_object| {
return try llvm_object.flushModule(comp);
return try llvm_object.flushModule(comp, prog_node);
}
}
var sub_prog_node = prog_node.start("COFF Flush", 0);
sub_prog_node.activate();
defer sub_prog_node.end();
if (self.text_section_size_dirty) {
// Write the new raw size in the .text header
var buf: [4]u8 = undefined;
@ -892,7 +896,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
}
}
fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
@ -924,7 +928,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
}
}
try self.flushModule(comp);
try self.flushModule(comp, prog_node);
if (fs.path.dirname(full_out_path)) |dirname| {
break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
@ -933,6 +937,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
}
} else null;
var sub_prog_node = prog_node.start("LLD Link", 0);
sub_prog_node.activate();
sub_prog_node.context.refresh();
defer sub_prog_node.end();
const is_lib = self.base.options.output_mode == .Lib;
const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;

View File

@ -929,35 +929,39 @@ pub fn populateMissingMetadata(self: *Elf) !void {
}
}
pub fn flush(self: *Elf, comp: *Compilation) !void {
pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
if (self.base.options.emit == null) {
if (build_options.have_llvm) {
if (self.llvm_object) |llvm_object| {
return try llvm_object.flushModule(comp);
return try llvm_object.flushModule(comp, prog_node);
}
}
return;
}
const use_lld = build_options.have_llvm and self.base.options.use_lld;
if (use_lld) {
return self.linkWithLLD(comp);
return self.linkWithLLD(comp, prog_node);
}
switch (self.base.options.output_mode) {
.Exe, .Obj => return self.flushModule(comp),
.Exe, .Obj => return self.flushModule(comp, prog_node),
.Lib => return error.TODOImplementWritingLibFiles,
}
}
pub fn flushModule(self: *Elf, comp: *Compilation) !void {
pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
if (build_options.have_llvm) {
if (self.llvm_object) |llvm_object| {
return try llvm_object.flushModule(comp);
return try llvm_object.flushModule(comp, prog_node);
}
}
var sub_prog_node = prog_node.start("ELF Flush", 0);
sub_prog_node.activate();
defer sub_prog_node.end();
// TODO This linker code currently assumes there is only 1 compilation unit and it
// corresponds to the Zig source code.
const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
@ -1204,7 +1208,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
assert(!self.debug_strtab_dirty);
}
fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
@ -1236,7 +1240,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
}
}
try self.flushModule(comp);
try self.flushModule(comp, prog_node);
if (fs.path.dirname(full_out_path)) |dirname| {
break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
@ -1245,6 +1249,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
}
} else null;
var sub_prog_node = prog_node.start("LLD Link", 0);
sub_prog_node.activate();
sub_prog_node.context.refresh();
defer sub_prog_node.end();
const is_obj = self.base.options.output_mode == .Obj;
const is_lib = self.base.options.output_mode == .Lib;
const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;

View File

@ -419,33 +419,34 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
return self;
}
pub fn flush(self: *MachO, comp: *Compilation) !void {
pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
if (self.base.options.emit == null) {
if (build_options.have_llvm) {
if (self.llvm_object) |llvm_object| {
try llvm_object.flushModule(comp);
try llvm_object.flushModule(comp, prog_node);
}
}
return;
}
if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) {
if (build_options.have_llvm) {
return self.base.linkAsArchive(comp);
return self.base.linkAsArchive(comp, prog_node);
} else {
log.err("TODO: non-LLVM archiver for MachO object files", .{});
return error.TODOImplementWritingStaticLibFiles;
}
}
try self.flushModule(comp);
try self.flushModule(comp, prog_node);
}
pub fn flushModule(self: *MachO, comp: *Compilation) !void {
pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
if (!use_stage1 and self.base.options.output_mode == .Obj)
return self.flushObject(comp);
return self.flushObject(comp, prog_node);
var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
defer arena_allocator.deinit();
@ -482,7 +483,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
const obj_basename = self.base.intermediary_basename orelse break :blk null;
try self.flushObject(comp);
try self.flushObject(comp, prog_node);
if (fs.path.dirname(full_out_path)) |dirname| {
break :blk try fs.path.join(arena, &.{ dirname, obj_basename });
@ -491,6 +492,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
}
} else null;
var sub_prog_node = prog_node.start("MachO Flush", 0);
sub_prog_node.activate();
defer sub_prog_node.end();
const is_lib = self.base.options.output_mode == .Lib;
const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
@ -1111,13 +1116,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
self.cold_start = false;
}
pub fn flushObject(self: *MachO, comp: *Compilation) !void {
pub fn flushObject(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
if (build_options.have_llvm)
if (self.llvm_object) |llvm_object|
return llvm_object.flushModule(comp);
return llvm_object.flushModule(comp, prog_node);
return error.TODOImplementWritingObjFiles;
}

View File

@ -97,11 +97,11 @@ pub fn freeDecl(self: *NvPtx, decl: *Module.Decl) void {
return self.llvm_object.freeDecl(decl);
}
pub fn flush(self: *NvPtx, comp: *Compilation) !void {
return self.flushModule(comp);
pub fn flush(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.Node) !void {
return self.flushModule(comp, prog_node);
}
pub fn flushModule(self: *NvPtx, comp: *Compilation) !void {
pub fn flushModule(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.Node) !void {
if (!build_options.have_llvm) return;
if (build_options.skip_non_native) {
@panic("Attempted to compile for architecture that was disabled by build configuration");
@ -117,5 +117,5 @@ pub fn flushModule(self: *NvPtx, comp: *Compilation) !void {
};
hack_comp.bin_file.options.emit = null;
}
return try self.llvm_object.flushModule(hack_comp);
return try self.llvm_object.flushModule(hack_comp, prog_node);
}

View File

@ -351,7 +351,7 @@ fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {
}
}
pub fn flush(self: *Plan9, comp: *Compilation) !void {
pub fn flush(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) !void {
assert(!self.base.options.use_lld);
switch (self.base.options.effectiveOutputMode()) {
@ -360,7 +360,7 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void {
.Obj => return error.TODOImplementPlan9Objs,
.Lib => return error.TODOImplementWritingLibFiles,
}
return self.flushModule(comp);
return self.flushModule(comp, prog_node);
}
pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void {
@ -387,7 +387,7 @@ fn declCount(self: *Plan9) usize {
return self.data_decl_table.count() + fn_decl_count;
}
pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) !void {
if (build_options.skip_non_native and builtin.object_format != .plan9) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
@ -396,6 +396,10 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
const tracy = trace(@src());
defer tracy.end();
var sub_prog_node = prog_node.start("Flush Module", 0);
sub_prog_node.activate();
defer sub_prog_node.end();
log.debug("flushModule", .{});
defer assert(self.hdr.entry != 0x0);

View File

@ -174,15 +174,15 @@ pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {
self.decl_table.swapRemoveAt(index);
}
pub fn flush(self: *SpirV, comp: *Compilation) !void {
pub fn flush(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.Node) !void {
if (build_options.have_llvm and self.base.options.use_lld) {
return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all.
} else {
return self.flushModule(comp);
return self.flushModule(comp, prog_node);
}
}
pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.Node) !void {
if (build_options.skip_non_native) {
@panic("Attempted to compile for architecture that was disabled by build configuration");
}
@ -190,6 +190,10 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
const tracy = trace(@src());
defer tracy.end();
var sub_prog_node = prog_node.start("Flush Module", 0);
sub_prog_node.activate();
defer sub_prog_node.end();
const module = self.base.options.module.?;
const target = comp.getTarget();

View File

@ -1477,32 +1477,36 @@ fn resetState(self: *Wasm) void {
self.code_section_index = null;
}
pub fn flush(self: *Wasm, comp: *Compilation) !void {
pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void {
if (self.base.options.emit == null) {
if (build_options.have_llvm) {
if (self.llvm_object) |llvm_object| {
return try llvm_object.flushModule(comp);
return try llvm_object.flushModule(comp, prog_node);
}
}
return;
}
if (build_options.have_llvm and self.base.options.use_lld) {
return self.linkWithLLD(comp);
return self.linkWithLLD(comp, prog_node);
} else {
return self.flushModule(comp);
return self.flushModule(comp, prog_node);
}
}
pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
if (build_options.have_llvm) {
if (self.llvm_object) |llvm_object| {
return try llvm_object.flushModule(comp);
return try llvm_object.flushModule(comp, prog_node);
}
}
var sub_prog_node = prog_node.start("WASM Flush", 0);
sub_prog_node.activate();
defer sub_prog_node.end();
// ensure the error names table is populated when an error name is referenced
try self.populateErrorNameTable();
@ -2028,7 +2032,7 @@ fn emitImport(self: *Wasm, writer: anytype, import: types.Import) !void {
}
}
fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
@ -2060,7 +2064,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
}
}
try self.flushModule(comp);
try self.flushModule(comp, prog_node);
if (fs.path.dirname(full_out_path)) |dirname| {
break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
@ -2069,6 +2073,11 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
}
} else null;
var sub_prog_node = prog_node.start("LLD Link", 0);
sub_prog_node.activate();
sub_prog_node.context.refresh();
defer sub_prog_node.end();
const is_obj = self.base.options.output_mode == .Obj;
const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt and !is_obj)