diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-01-05 17:42:16 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-01-05 17:42:16 -0700 |
| commit | 38572ee89492d8499a5881d12c401d9bb0925167 (patch) | |
| tree | 2ff6f00b6545697c386740032f9a32d124cfb438 /src/Compilation.zig | |
| parent | 6c4924408b1957d493568271a0158b1190574dd0 (diff) | |
| parent | 3e39d0c44fbf0cfb56ef0c00fc8ec7c0d5e1c1ec (diff) | |
| download | zig-38572ee89492d8499a5881d12c401d9bb0925167.tar.gz zig-38572ee89492d8499a5881d12c401d9bb0925167.zip | |
Merge branch 'stage2-rework-cbe'
Reworks the C backend and -femit-h to properly participate in
incremental compilation.
closes #7602
Diffstat (limited to 'src/Compilation.zig')
| -rw-r--r-- | src/Compilation.zig | 150 |
1 files changed, 84 insertions, 66 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig index 9912520437..bf8fd73e93 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -27,7 +27,6 @@ const Cache = @import("Cache.zig"); const stage1 = @import("stage1.zig"); const translate_c = @import("translate_c.zig"); const c_codegen = @import("codegen/c.zig"); -const c_link = @import("link/C.zig"); const ThreadPool = @import("ThreadPool.zig"); const WaitGroup = @import("WaitGroup.zig"); const libtsan = @import("libtsan.zig"); @@ -138,8 +137,6 @@ emit_llvm_ir: ?EmitLoc, emit_analysis: ?EmitLoc, emit_docs: ?EmitLoc, -c_header: ?c_link.Header, - work_queue_wait_group: WaitGroup, pub const InnerError = Module.InnerError; @@ -164,6 +161,8 @@ pub const CSourceFile = struct { const Job = union(enum) { /// Write the machine code for a Decl to the output file. codegen_decl: *Module.Decl, + /// Render the .h file snippet for the Decl. + emit_h_decl: *Module.Decl, /// The Decl needs to be analyzed and possibly export itself. /// It may have already be analyzed, or it may have been determined /// to be outdated; in this case perform semantic analysis again. @@ -866,9 +865,13 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .root_pkg = root_pkg, .root_scope = root_scope, .zig_cache_artifact_directory = zig_cache_artifact_directory, + .emit_h = options.emit_h, }; break :blk module; - } else null; + } else blk: { + if (options.emit_h != null) return error.NoZigModuleForCHeader; + break :blk null; + }; errdefer if (module) |zm| zm.deinit(); const error_return_tracing = !strip and switch (options.optimize_mode) { @@ -996,7 +999,6 @@ 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, - .c_header = if (!use_llvm and options.emit_h != null) c_link.Header.init(gpa, options.emit_h) else null, .emit_asm = options.emit_asm, .emit_llvm_ir = options.emit_llvm_ir, .emit_analysis = options.emit_analysis, @@ -1218,10 +1220,6 @@ pub fn destroy(self: *Compilation) void { } self.failed_c_objects.deinit(gpa); - if (self.c_header) |*header| { - header.deinit(); - } - self.cache_parent.manifest_dir.close(); if (self.owned_link_dir) |*dir| dir.close(); @@ -1315,9 +1313,14 @@ pub fn update(self: *Compilation) !void { // This is needed before reading the error flags. try self.bin_file.flush(self); - self.link_error_flags = self.bin_file.errorFlags(); + if (!use_stage1) { + if (self.bin_file.options.module) |module| { + try link.File.C.flushEmitH(module); + } + } + // If there are any errors, we anticipate the source files being loaded // to report error messages. Otherwise we unload all source files to save memory. if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) { @@ -1325,20 +1328,6 @@ pub fn update(self: *Compilation) !void { module.root_scope.unload(self.gpa); } } - - // If we've chosen to emit a C header, flush the header to the disk. - if (self.c_header) |header| { - const header_path = header.emit_loc.?; - // If a directory has been provided, write the header there. Otherwise, just write it to the - // cache directory. - const header_dir = if (header_path.directory) |dir| - dir.handle - else - self.local_cache_directory.handle; - const header_file = try header_dir.createFile(header_path.basename, .{}); - defer header_file.close(); - try header.flush(header_file.writer()); - } } /// Having the file open for writing is problematic as far as executing the @@ -1357,7 +1346,8 @@ pub fn totalErrorCount(self: *Compilation) usize { var total: usize = self.failed_c_objects.items().len; if (self.bin_file.options.module) |module| { - total += module.failed_decls.items().len + + total += module.failed_decls.count() + + module.emit_h_failed_decls.count() + module.failed_exports.items().len + module.failed_files.items().len + @boolToInt(module.failed_root_src_file != null); @@ -1396,6 +1386,12 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { const source = try decl.scope.getSource(module); try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); } + for (module.emit_h_failed_decls.items()) |entry| { + const decl = entry.key; + const err_msg = entry.value; + const source = try decl.scope.getSource(module); + try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); + } for (module.failed_exports.items()) |entry| { const decl = entry.key.owner_decl; const err_msg = entry.value; @@ -1493,44 +1489,66 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); - self.bin_file.updateDecl(module, decl) catch |err| { - switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => { - decl.analysis = .dependency_failure; - }, - else => { - try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); - module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( - module.gpa, - decl.src(), - "unable to codegen: {s}", - .{@errorName(err)}, - )); - decl.analysis = .codegen_failure_retryable; - }, - } - return; + self.bin_file.updateDecl(module, decl) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => { + decl.analysis = .codegen_failure; + continue; + }, + else => { + try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); + module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + module.gpa, + decl.src(), + "unable to codegen: {s}", + .{@errorName(err)}, + )); + decl.analysis = .codegen_failure_retryable; + continue; + }, }; + }, + }, + .emit_h_decl => |decl| switch (decl.analysis) { + .unreferenced => unreachable, + .in_progress => unreachable, + .outdated => unreachable, - if (self.c_header) |*header| { - c_codegen.generateHeader(self, module, header, decl) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => { - decl.analysis = .dependency_failure; - }, - else => { - try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); - module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( - module.gpa, - decl.src(), - "unable to generate C header: {s}", - .{@errorName(err)}, - )); - decl.analysis = .codegen_failure_retryable; - }, - }; - } + .sema_failure, + .dependency_failure, + .sema_failure_retryable, + => continue, + + // emit-h only requires semantic analysis of the Decl to be complete, + // it does not depend on machine code generation to succeed. + .codegen_failure, .codegen_failure_retryable, .complete => { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); + const module = self.bin_file.options.module.?; + const emit_loc = module.emit_h.?; + const tv = decl.typed_value.most_recent.typed_value; + const emit_h = decl.getEmitH(module); + const fwd_decl = &emit_h.fwd_decl; + fwd_decl.shrinkRetainingCapacity(0); + + var dg: c_codegen.DeclGen = .{ + .module = module, + .error_msg = null, + .decl = decl, + .fwd_decl = fwd_decl.toManaged(module.gpa), + }; + defer dg.fwd_decl.deinit(); + + c_codegen.genHeader(&dg) catch |err| switch (err) { + error.AnalysisFail => { + try module.emit_h_failed_decls.put(module.gpa, decl, dg.error_msg.?); + continue; + }, + else => |e| return e, + }; + + fwd_decl.* = dg.fwd_decl.moveToUnmanaged(); + fwd_decl.shrink(module.gpa, fwd_decl.items.len); }, }, .analyze_decl => |decl| { @@ -2998,9 +3016,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node man.hash.add(comp.bin_file.options.function_sections); man.hash.add(comp.bin_file.options.is_test); man.hash.add(comp.bin_file.options.emit != null); - man.hash.add(comp.c_header != null); - if (comp.c_header) |header| { - man.hash.addEmitLoc(header.emit_loc.?); + man.hash.add(mod.emit_h != null); + if (mod.emit_h) |emit_h| { + man.hash.addEmitLoc(emit_h); } man.hash.addOptionalEmitLoc(comp.emit_asm); man.hash.addOptionalEmitLoc(comp.emit_llvm_ir); @@ -3105,10 +3123,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node }); break :blk try directory.join(arena, &[_][]const u8{bin_basename}); } else ""; - if (comp.c_header != null) { + if (mod.emit_h != null) { log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{}); } - const emit_h_path = try stage1LocPath(arena, if (comp.c_header) |header| header.emit_loc else null, directory); + const emit_h_path = try stage1LocPath(arena, mod.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); |
