mirror of
https://github.com/ziglang/zig.git
synced 2024-12-12 22:25:21 +00:00
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.
This commit is contained in:
parent
a97e5e119a
commit
fe14e33945
@ -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) {
|
||||
.queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
|
||||
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});
|
||||
}
|
||||
|
||||
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,
|
||||
|
186
src/Module.zig
186
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;
|
||||
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;
|
||||
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;
|
||||
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_decl = decl });
|
||||
if (type_changed and mod.emit_h != null) {
|
||||
try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 });
|
||||
|
||||
if (type_changed and mod.emit_h != null) {
|
||||
try mod.comp.work_queue.writeItem(.{ .emit_h_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);
|
||||
}
|
||||
|
||||
return type_changed;
|
||||
}
|
||||
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;
|
||||
|
||||
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 });
|
||||
|
||||
if (type_changed and mod.emit_h != null) {
|
||||
try mod.comp.work_queue.writeItem(.{ .emit_h_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);
|
||||
}
|
||||
|
||||
return type_changed;
|
||||
}
|
||||
|
||||
/// Returns the depender's index of the dependee.
|
||||
|
Loading…
Reference in New Issue
Block a user