authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 15:22:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 15:22:37-07:00
logfe14e339458a578657f3890f00d654a15c84422c
tree0b53177c9f8b84dbfbd67308ae06784f87387ee2
parenta97e5e119afb80e0d6d047682b8301bab9423078

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.

2 files changed, 203 insertions(+), 129 deletions(-)

src/Compilation.zig+114-44
......@@ -169,8 +169,10 @@ pub const CSourceFile = struct {
169169};
170170
171171const Job = union(enum) {
172 /// Write the machine code for a Decl to the output file.
172 /// Write the constant value for a Decl to the output file.
173173 codegen_decl: *Module.Decl,
174 /// Write the machine code for a function to the output file.
175 codegen_func: *Module.Fn,
174176 /// Render the .h file snippet for the Decl.
175177 emit_h_decl: *Module.Decl,
176178 /// The Decl needs to be analyzed and possibly export itself.
......@@ -2006,54 +2008,56 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
20062008 const module = self.bin_file.options.module.?;
20072009 assert(decl.has_tv);
20082010 if (decl.val.castTag(.function)) |payload| {
2009 const func = payload.data;
2011 if (decl.owns_tv) {
2012 const func = payload.data;
2013
2014 var air = switch (func.state) {
2015 .sema_failure, .dependency_failure => continue,
2016 .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
2017 error.AnalysisFail => {
2018 assert(func.state != .in_progress);
2019 continue;
2020 },
2021 error.OutOfMemory => return error.OutOfMemory,
2022 },
2023 .in_progress => unreachable,
2024 .inline_only => unreachable, // don't queue work for this
2025 .success => unreachable, // don't queue it twice
2026 };
2027 defer air.deinit(gpa);
2028
2029 log.debug("analyze liveness of {s}", .{decl.name});
2030 var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);
2031 defer liveness.deinit(gpa);
2032
2033 if (builtin.mode == .Debug and self.verbose_air) {
2034 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
2035 @import("print_air.zig").dump(gpa, air, liveness);
2036 std.debug.print("# End Function AIR: {s}:\n", .{decl.name});
2037 }
20102038
2011 var air = switch (func.state) {
2012 .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
2039 assert(decl.ty.hasCodeGenBits());
2040
2041 self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {
2042 error.OutOfMemory => return error.OutOfMemory,
20132043 error.AnalysisFail => {
2014 assert(func.state != .in_progress);
2044 decl.analysis = .codegen_failure;
20152045 continue;
20162046 },
2017 error.OutOfMemory => return error.OutOfMemory,
2018 },
2019 .in_progress => unreachable,
2020 .inline_only => unreachable, // don't queue work for this
2021 .sema_failure, .dependency_failure => continue,
2022 .success => unreachable, // don't queue it twice
2023 };
2024 defer air.deinit(gpa);
2025
2026 log.debug("analyze liveness of {s}", .{decl.name});
2027 var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);
2028 defer liveness.deinit(gpa);
2029
2030 if (builtin.mode == .Debug and self.verbose_air) {
2031 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
2032 @import("print_air.zig").dump(gpa, air, liveness);
2033 std.debug.print("# End Function AIR: {s}:\n", .{decl.name});
2047 else => {
2048 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
2049 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2050 gpa,
2051 decl.srcLoc(),
2052 "unable to codegen: {s}",
2053 .{@errorName(err)},
2054 ));
2055 decl.analysis = .codegen_failure_retryable;
2056 continue;
2057 },
2058 };
2059 continue;
20342060 }
2035
2036 assert(decl.ty.hasCodeGenBits());
2037
2038 self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {
2039 error.OutOfMemory => return error.OutOfMemory,
2040 error.AnalysisFail => {
2041 decl.analysis = .codegen_failure;
2042 continue;
2043 },
2044 else => {
2045 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
2046 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2047 gpa,
2048 decl.srcLoc(),
2049 "unable to codegen: {s}",
2050 .{@errorName(err)},
2051 ));
2052 decl.analysis = .codegen_failure_retryable;
2053 continue;
2054 },
2055 };
2056 continue;
20572061 }
20582062
20592063 assert(decl.ty.hasCodeGenBits());
......@@ -2078,6 +2082,72 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
20782082 };
20792083 },
20802084 },
2085 .codegen_func => |func| switch (func.owner_decl.analysis) {
2086 .unreferenced => unreachable,
2087 .in_progress => unreachable,
2088 .outdated => unreachable,
2089
2090 .file_failure,
2091 .sema_failure,
2092 .codegen_failure,
2093 .dependency_failure,
2094 .sema_failure_retryable,
2095 => continue,
2096
2097 .complete, .codegen_failure_retryable => {
2098 if (build_options.omit_stage2)
2099 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2100 switch (func.state) {
2101 .sema_failure, .dependency_failure => continue,
2102 .queued => {},
2103 .in_progress => unreachable,
2104 .inline_only => unreachable, // don't queue work for this
2105 .success => unreachable, // don't queue it twice
2106 }
2107
2108 const module = self.bin_file.options.module.?;
2109 const decl = func.owner_decl;
2110
2111 var air = module.analyzeFnBody(decl, func) catch |err| switch (err) {
2112 error.AnalysisFail => {
2113 assert(func.state != .in_progress);
2114 continue;
2115 },
2116 error.OutOfMemory => return error.OutOfMemory,
2117 };
2118 defer air.deinit(gpa);
2119
2120 log.debug("analyze liveness of {s}", .{decl.name});
2121 var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);
2122 defer liveness.deinit(gpa);
2123
2124 if (builtin.mode == .Debug and self.verbose_air) {
2125 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
2126 @import("print_air.zig").dump(gpa, air, liveness);
2127 std.debug.print("# End Function AIR: {s}:\n", .{decl.name});
2128 }
2129
2130 self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {
2131 error.OutOfMemory => return error.OutOfMemory,
2132 error.AnalysisFail => {
2133 decl.analysis = .codegen_failure;
2134 continue;
2135 },
2136 else => {
2137 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
2138 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2139 gpa,
2140 decl.srcLoc(),
2141 "unable to codegen: {s}",
2142 .{@errorName(err)},
2143 ));
2144 decl.analysis = .codegen_failure_retryable;
2145 continue;
2146 },
2147 };
2148 continue;
2149 },
2150 },
20812151 .emit_h_decl => |decl| switch (decl.analysis) {
20822152 .unreferenced => unreachable,
20832153 .in_progress => unreachable,
src/Module.zig+89-85
......@@ -2902,6 +2902,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
29022902 decl.generation = mod.generation;
29032903 return false;
29042904 }
2905 log.debug("semaDecl {*} ({s})", .{ decl, decl.name });
29052906
29062907 var block_scope: Scope.Block = .{
29072908 .parent = null,
......@@ -2938,106 +2939,109 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
29382939 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
29392940
29402941 if (decl_tv.val.castTag(.function)) |fn_payload| {
2941 var prev_type_has_bits = false;
2942 var prev_is_inline = false;
2943 var type_changed = true;
2944
2945 if (decl.has_tv) {
2946 prev_type_has_bits = decl.ty.hasCodeGenBits();
2947 type_changed = !decl.ty.eql(decl_tv.ty);
2948 if (decl.getFunction()) |prev_func| {
2949 prev_is_inline = prev_func.state == .inline_only;
2942 const func = fn_payload.data;
2943 const owns_tv = func.owner_decl == decl;
2944 if (owns_tv) {
2945 var prev_type_has_bits = false;
2946 var prev_is_inline = false;
2947 var type_changed = true;
2948
2949 if (decl.has_tv) {
2950 prev_type_has_bits = decl.ty.hasCodeGenBits();
2951 type_changed = !decl.ty.eql(decl_tv.ty);
2952 if (decl.getFunction()) |prev_func| {
2953 prev_is_inline = prev_func.state == .inline_only;
2954 }
2955 decl.clearValues(gpa);
29502956 }
2951 decl.clearValues(gpa);
2952 }
2953
2954 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
2955 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
2956 decl.align_val = try align_val.copy(&decl_arena.allocator);
2957 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
2958 decl.has_tv = true;
2959 decl.owns_tv = fn_payload.data.owner_decl == decl;
2960 decl_arena_state.* = decl_arena.state;
2961 decl.value_arena = decl_arena_state;
2962 decl.analysis = .complete;
2963 decl.generation = mod.generation;
29642957
2965 const is_inline = decl_tv.ty.fnCallingConvention() == .Inline;
2966 if (!is_inline and decl_tv.ty.hasCodeGenBits()) {
2967 // We don't fully codegen the decl until later, but we do need to reserve a global
2968 // offset table index for it. This allows us to codegen decls out of dependency order,
2969 // increasing how many computations can be done in parallel.
2970 try mod.comp.bin_file.allocateDeclIndexes(decl);
2971 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
2972 if (type_changed and mod.emit_h != null) {
2973 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
2958 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
2959 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
2960 decl.align_val = try align_val.copy(&decl_arena.allocator);
2961 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
2962 decl.has_tv = true;
2963 decl.owns_tv = owns_tv;
2964 decl_arena_state.* = decl_arena.state;
2965 decl.value_arena = decl_arena_state;
2966 decl.analysis = .complete;
2967 decl.generation = mod.generation;
2968
2969 const is_inline = decl_tv.ty.fnCallingConvention() == .Inline;
2970 if (!is_inline and decl_tv.ty.hasCodeGenBits()) {
2971 // We don't fully codegen the decl until later, but we do need to reserve a global
2972 // offset table index for it. This allows us to codegen decls out of dependency order,
2973 // increasing how many computations can be done in parallel.
2974 try mod.comp.bin_file.allocateDeclIndexes(decl);
2975 try mod.comp.work_queue.writeItem(.{ .codegen_func = func });
2976 if (type_changed and mod.emit_h != null) {
2977 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
2978 }
2979 } else if (!prev_is_inline and prev_type_has_bits) {
2980 mod.comp.bin_file.freeDecl(decl);
29742981 }
2975 } else if (!prev_is_inline and prev_type_has_bits) {
2976 mod.comp.bin_file.freeDecl(decl);
2977 }
29782982
2979 if (decl.is_exported) {
2980 const export_src = src; // TODO make this point at `export` token
2981 if (is_inline) {
2982 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});
2983 if (decl.is_exported) {
2984 const export_src = src; // TODO make this point at `export` token
2985 if (is_inline) {
2986 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});
2987 }
2988 // The scope needs to have the decl in it.
2989 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
29832990 }
2984 // The scope needs to have the decl in it.
2985 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
2986 }
2987 return type_changed or is_inline != prev_is_inline;
2988 } else {
2989 var type_changed = true;
2990 if (decl.has_tv) {
2991 type_changed = !decl.ty.eql(decl_tv.ty);
2992 decl.clearValues(gpa);
2991 return type_changed or is_inline != prev_is_inline;
29932992 }
2993 }
2994 var type_changed = true;
2995 if (decl.has_tv) {
2996 type_changed = !decl.ty.eql(decl_tv.ty);
2997 decl.clearValues(gpa);
2998 }
29942999
2995 decl.owns_tv = false;
2996 var queue_linker_work = false;
2997 if (decl_tv.val.castTag(.variable)) |payload| {
2998 const variable = payload.data;
2999 if (variable.owner_decl == decl) {
3000 decl.owns_tv = true;
3001 queue_linker_work = true;
3000 decl.owns_tv = false;
3001 var queue_linker_work = false;
3002 if (decl_tv.val.castTag(.variable)) |payload| {
3003 const variable = payload.data;
3004 if (variable.owner_decl == decl) {
3005 decl.owns_tv = true;
3006 queue_linker_work = true;
30023007
3003 const copied_init = try variable.init.copy(&decl_arena.allocator);
3004 variable.init = copied_init;
3005 }
3006 } else if (decl_tv.val.castTag(.extern_fn)) |payload| {
3007 const owner_decl = payload.data;
3008 if (decl == owner_decl) {
3009 decl.owns_tv = true;
3010 queue_linker_work = true;
3011 }
3008 const copied_init = try variable.init.copy(&decl_arena.allocator);
3009 variable.init = copied_init;
30123010 }
3011 } else if (decl_tv.val.castTag(.extern_fn)) |payload| {
3012 const owner_decl = payload.data;
3013 if (decl == owner_decl) {
3014 decl.owns_tv = true;
3015 queue_linker_work = true;
3016 }
3017 }
30133018
3014 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
3015 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
3016 decl.align_val = try align_val.copy(&decl_arena.allocator);
3017 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3018 decl.has_tv = true;
3019 decl_arena_state.* = decl_arena.state;
3020 decl.value_arena = decl_arena_state;
3021 decl.analysis = .complete;
3022 decl.generation = mod.generation;
3023
3024 if (queue_linker_work and decl.ty.hasCodeGenBits()) {
3025 try mod.comp.bin_file.allocateDeclIndexes(decl);
3026 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
3019 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
3020 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
3021 decl.align_val = try align_val.copy(&decl_arena.allocator);
3022 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3023 decl.has_tv = true;
3024 decl_arena_state.* = decl_arena.state;
3025 decl.value_arena = decl_arena_state;
3026 decl.analysis = .complete;
3027 decl.generation = mod.generation;
30273028
3028 if (type_changed and mod.emit_h != null) {
3029 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
3030 }
3031 }
3029 if (queue_linker_work and decl.ty.hasCodeGenBits()) {
3030 try mod.comp.bin_file.allocateDeclIndexes(decl);
3031 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
30323032
3033 if (decl.is_exported) {
3034 const export_src = src; // TODO point to the export token
3035 // The scope needs to have the decl in it.
3036 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
3033 if (type_changed and mod.emit_h != null) {
3034 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
30373035 }
3036 }
30383037
3039 return type_changed;
3038 if (decl.is_exported) {
3039 const export_src = src; // TODO point to the export token
3040 // The scope needs to have the decl in it.
3041 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
30403042 }
3043
3044 return type_changed;
30413045}
30423046
30433047/// Returns the depender's index of the dependee.