aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-07-20 15:22:37 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-07-20 15:22:37 -0700
commitfe14e339458a578657f3890f00d654a15c84422c (patch)
tree0b53177c9f8b84dbfbd67308ae06784f87387ee2 /src
parenta97e5e119afb80e0d6d047682b8301bab9423078 (diff)
downloadzig-fe14e339458a578657f3890f00d654a15c84422c.tar.gz
zig-fe14e339458a578657f3890f00d654a15c84422c.zip
stage2: separate work queue item for functions than decls
Previously we had codegen_decl for both constant values as well as function bodies. A recent commit updated the linker backends to add updateFunc as a separate function than updateDecl, and now this commit does the same with work queue tasks. The frontend now distinguishes between function pointers and function bodies.
Diffstat (limited to 'src')
-rw-r--r--src/Compilation.zig158
-rw-r--r--src/Module.zig174
2 files changed, 203 insertions, 129 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 50d1f5760e..ea484c2d15 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -169,8 +169,10 @@ pub const CSourceFile = struct {
};
const Job = union(enum) {
- /// Write the machine code for a Decl to the output file.
+ /// Write the constant value for a Decl to the output file.
codegen_decl: *Module.Decl,
+ /// Write the machine code for a function to the output file.
+ codegen_func: *Module.Fn,
/// Render the .h file snippet for the Decl.
emit_h_decl: *Module.Decl,
/// The Decl needs to be analyzed and possibly export itself.
@@ -2006,54 +2008,56 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
const module = self.bin_file.options.module.?;
assert(decl.has_tv);
if (decl.val.castTag(.function)) |payload| {
- const func = payload.data;
+ if (decl.owns_tv) {
+ const func = payload.data;
+
+ var air = switch (func.state) {
+ .sema_failure, .dependency_failure => continue,
+ .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
+ error.AnalysisFail => {
+ assert(func.state != .in_progress);
+ continue;
+ },
+ error.OutOfMemory => return error.OutOfMemory,
+ },
+ .in_progress => unreachable,
+ .inline_only => unreachable, // don't queue work for this
+ .success => unreachable, // don't queue it twice
+ };
+ defer air.deinit(gpa);
+
+ log.debug("analyze liveness of {s}", .{decl.name});
+ var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);
+ defer liveness.deinit(gpa);
+
+ if (builtin.mode == .Debug and self.verbose_air) {
+ std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
+ @import("print_air.zig").dump(gpa, air, liveness);
+ std.debug.print("# End Function AIR: {s}:\n", .{decl.name});
+ }
- var air = switch (func.state) {
- .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
+ assert(decl.ty.hasCodeGenBits());
+
+ self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
error.AnalysisFail => {
- assert(func.state != .in_progress);
+ decl.analysis = .codegen_failure;
continue;
},
- error.OutOfMemory => return error.OutOfMemory,
- },
- .in_progress => unreachable,
- .inline_only => unreachable, // don't queue work for this
- .sema_failure, .dependency_failure => continue,
- .success => unreachable, // don't queue it twice
- };
- defer air.deinit(gpa);
-
- log.debug("analyze liveness of {s}", .{decl.name});
- var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);
- defer liveness.deinit(gpa);
-
- if (builtin.mode == .Debug and self.verbose_air) {
- std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
- @import("print_air.zig").dump(gpa, air, liveness);
- std.debug.print("# End Function AIR: {s}:\n", .{decl.name});
+ else => {
+ try module.failed_decls.ensureUnusedCapacity(gpa, 1);
+ module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
+ gpa,
+ decl.srcLoc(),
+ "unable to codegen: {s}",
+ .{@errorName(err)},
+ ));
+ decl.analysis = .codegen_failure_retryable;
+ continue;
+ },
+ };
+ continue;
}
-
- assert(decl.ty.hasCodeGenBits());
-
- self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {
- decl.analysis = .codegen_failure;
- continue;
- },
- else => {
- try module.failed_decls.ensureUnusedCapacity(gpa, 1);
- module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
- gpa,
- decl.srcLoc(),
- "unable to codegen: {s}",
- .{@errorName(err)},
- ));
- decl.analysis = .codegen_failure_retryable;
- continue;
- },
- };
- continue;
}
assert(decl.ty.hasCodeGenBits());
@@ -2078,6 +2082,72 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
};
},
},
+ .codegen_func => |func| switch (func.owner_decl.analysis) {
+ .unreferenced => unreachable,
+ .in_progress => unreachable,
+ .outdated => unreachable,
+
+ .file_failure,
+ .sema_failure,
+ .codegen_failure,
+ .dependency_failure,
+ .sema_failure_retryable,
+ => continue,
+
+ .complete, .codegen_failure_retryable => {
+ if (build_options.omit_stage2)
+ @panic("sadly stage2 is omitted from this build to save memory on the CI server");
+ switch (func.state) {
+ .sema_failure, .dependency_failure => continue,
+ .queued => {},
+ .in_progress => unreachable,
+ .inline_only => unreachable, // don't queue work for this
+ .success => unreachable, // don't queue it twice
+ }
+
+ const module = self.bin_file.options.module.?;
+ const decl = func.owner_decl;
+
+ var air = module.analyzeFnBody(decl, func) catch |err| switch (err) {
+ error.AnalysisFail => {
+ assert(func.state != .in_progress);
+ continue;
+ },
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+ defer air.deinit(gpa);
+
+ log.debug("analyze liveness of {s}", .{decl.name});
+ var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);
+ defer liveness.deinit(gpa);
+
+ if (builtin.mode == .Debug and self.verbose_air) {
+ std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
+ @import("print_air.zig").dump(gpa, air, liveness);
+ std.debug.print("# End Function AIR: {s}:\n", .{decl.name});
+ }
+
+ self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.AnalysisFail => {
+ decl.analysis = .codegen_failure;
+ continue;
+ },
+ else => {
+ try module.failed_decls.ensureUnusedCapacity(gpa, 1);
+ module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
+ gpa,
+ decl.srcLoc(),
+ "unable to codegen: {s}",
+ .{@errorName(err)},
+ ));
+ decl.analysis = .codegen_failure_retryable;
+ continue;
+ },
+ };
+ continue;
+ },
+ },
.emit_h_decl => |decl| switch (decl.analysis) {
.unreferenced => unreachable,
.in_progress => unreachable,
diff --git a/src/Module.zig b/src/Module.zig
index 9fadf67c6f..4930e7846c 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -2902,6 +2902,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
decl.generation = mod.generation;
return false;
}
+ log.debug("semaDecl {*} ({s})", .{ decl, decl.name });
var block_scope: Scope.Block = .{
.parent = null,
@@ -2938,106 +2939,109 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
if (decl_tv.val.castTag(.function)) |fn_payload| {
- var prev_type_has_bits = false;
- var prev_is_inline = false;
- var type_changed = true;
-
- if (decl.has_tv) {
- prev_type_has_bits = decl.ty.hasCodeGenBits();
- type_changed = !decl.ty.eql(decl_tv.ty);
- if (decl.getFunction()) |prev_func| {
- prev_is_inline = prev_func.state == .inline_only;
+ const func = fn_payload.data;
+ const owns_tv = func.owner_decl == decl;
+ if (owns_tv) {
+ var prev_type_has_bits = false;
+ var prev_is_inline = false;
+ var type_changed = true;
+
+ if (decl.has_tv) {
+ prev_type_has_bits = decl.ty.hasCodeGenBits();
+ type_changed = !decl.ty.eql(decl_tv.ty);
+ if (decl.getFunction()) |prev_func| {
+ prev_is_inline = prev_func.state == .inline_only;
+ }
+ decl.clearValues(gpa);
}
- decl.clearValues(gpa);
- }
-
- decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
- decl.val = try decl_tv.val.copy(&decl_arena.allocator);
- decl.align_val = try align_val.copy(&decl_arena.allocator);
- decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
- decl.has_tv = true;
- decl.owns_tv = fn_payload.data.owner_decl == decl;
- decl_arena_state.* = decl_arena.state;
- decl.value_arena = decl_arena_state;
- decl.analysis = .complete;
- decl.generation = mod.generation;
- const is_inline = decl_tv.ty.fnCallingConvention() == .Inline;
- if (!is_inline and decl_tv.ty.hasCodeGenBits()) {
- // We don't fully codegen the decl until later, but we do need to reserve a global
- // offset table index for it. This allows us to codegen decls out of dependency order,
- // increasing how many computations can be done in parallel.
- try mod.comp.bin_file.allocateDeclIndexes(decl);
- try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
- if (type_changed and mod.emit_h != null) {
- try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
+ decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
+ decl.val = try decl_tv.val.copy(&decl_arena.allocator);
+ decl.align_val = try align_val.copy(&decl_arena.allocator);
+ decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
+ decl.has_tv = true;
+ decl.owns_tv = owns_tv;
+ decl_arena_state.* = decl_arena.state;
+ decl.value_arena = decl_arena_state;
+ decl.analysis = .complete;
+ decl.generation = mod.generation;
+
+ const is_inline = decl_tv.ty.fnCallingConvention() == .Inline;
+ if (!is_inline and decl_tv.ty.hasCodeGenBits()) {
+ // We don't fully codegen the decl until later, but we do need to reserve a global
+ // offset table index for it. This allows us to codegen decls out of dependency order,
+ // increasing how many computations can be done in parallel.
+ try mod.comp.bin_file.allocateDeclIndexes(decl);
+ try mod.comp.work_queue.writeItem(.{ .codegen_func = func });
+ if (type_changed and mod.emit_h != null) {
+ try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
+ }
+ } else if (!prev_is_inline and prev_type_has_bits) {
+ mod.comp.bin_file.freeDecl(decl);
}
- } else if (!prev_is_inline and prev_type_has_bits) {
- mod.comp.bin_file.freeDecl(decl);
- }
- if (decl.is_exported) {
- const export_src = src; // TODO make this point at `export` token
- if (is_inline) {
- return mod.fail(&block_scope.base, export_src, "export of inline function", .{});
+ if (decl.is_exported) {
+ const export_src = src; // TODO make this point at `export` token
+ if (is_inline) {
+ return mod.fail(&block_scope.base, export_src, "export of inline function", .{});
+ }
+ // The scope needs to have the decl in it.
+ try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
}
- // The scope needs to have the decl in it.
- try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
- }
- return type_changed or is_inline != prev_is_inline;
- } else {
- var type_changed = true;
- if (decl.has_tv) {
- type_changed = !decl.ty.eql(decl_tv.ty);
- decl.clearValues(gpa);
+ return type_changed or is_inline != prev_is_inline;
}
+ }
+ var type_changed = true;
+ if (decl.has_tv) {
+ type_changed = !decl.ty.eql(decl_tv.ty);
+ decl.clearValues(gpa);
+ }
- decl.owns_tv = false;
- var queue_linker_work = false;
- if (decl_tv.val.castTag(.variable)) |payload| {
- const variable = payload.data;
- if (variable.owner_decl == decl) {
- decl.owns_tv = true;
- queue_linker_work = true;
+ decl.owns_tv = false;
+ var queue_linker_work = false;
+ if (decl_tv.val.castTag(.variable)) |payload| {
+ const variable = payload.data;
+ if (variable.owner_decl == decl) {
+ decl.owns_tv = true;
+ queue_linker_work = true;
- const copied_init = try variable.init.copy(&decl_arena.allocator);
- variable.init = copied_init;
- }
- } else if (decl_tv.val.castTag(.extern_fn)) |payload| {
- const owner_decl = payload.data;
- if (decl == owner_decl) {
- decl.owns_tv = true;
- queue_linker_work = true;
- }
+ const copied_init = try variable.init.copy(&decl_arena.allocator);
+ variable.init = copied_init;
}
+ } else if (decl_tv.val.castTag(.extern_fn)) |payload| {
+ const owner_decl = payload.data;
+ if (decl == owner_decl) {
+ decl.owns_tv = true;
+ queue_linker_work = true;
+ }
+ }
- decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
- decl.val = try decl_tv.val.copy(&decl_arena.allocator);
- decl.align_val = try align_val.copy(&decl_arena.allocator);
- decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
- decl.has_tv = true;
- decl_arena_state.* = decl_arena.state;
- decl.value_arena = decl_arena_state;
- decl.analysis = .complete;
- decl.generation = mod.generation;
-
- if (queue_linker_work and decl.ty.hasCodeGenBits()) {
- try mod.comp.bin_file.allocateDeclIndexes(decl);
- try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
+ decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
+ decl.val = try decl_tv.val.copy(&decl_arena.allocator);
+ decl.align_val = try align_val.copy(&decl_arena.allocator);
+ decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
+ decl.has_tv = true;
+ decl_arena_state.* = decl_arena.state;
+ decl.value_arena = decl_arena_state;
+ decl.analysis = .complete;
+ decl.generation = mod.generation;
- if (type_changed and mod.emit_h != null) {
- try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
- }
- }
+ if (queue_linker_work and decl.ty.hasCodeGenBits()) {
+ try mod.comp.bin_file.allocateDeclIndexes(decl);
+ try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
- if (decl.is_exported) {
- const export_src = src; // TODO point to the export token
- // The scope needs to have the decl in it.
- try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
+ if (type_changed and mod.emit_h != null) {
+ try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
}
+ }
- return type_changed;
+ if (decl.is_exported) {
+ const export_src = src; // TODO point to the export token
+ // The scope needs to have the decl in it.
+ try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
}
+
+ return type_changed;
}
/// Returns the depender's index of the dependee.