diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-07-20 15:22:37 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-07-20 15:22:37 -0700 |
| commit | fe14e339458a578657f3890f00d654a15c84422c (patch) | |
| tree | 0b53177c9f8b84dbfbd67308ae06784f87387ee2 /src | |
| parent | a97e5e119afb80e0d6d047682b8301bab9423078 (diff) | |
| download | zig-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.zig | 158 | ||||
| -rw-r--r-- | src/Module.zig | 174 |
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. |
