aboutsummaryrefslogtreecommitdiff
path: root/src/Compilation.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-01-05 17:42:16 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-01-05 17:42:16 -0700
commit38572ee89492d8499a5881d12c401d9bb0925167 (patch)
tree2ff6f00b6545697c386740032f9a32d124cfb438 /src/Compilation.zig
parent6c4924408b1957d493568271a0158b1190574dd0 (diff)
parent3e39d0c44fbf0cfb56ef0c00fc8ec7c0d5e1c1ec (diff)
downloadzig-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.zig150
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);