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 {...@@ -169,8 +169,10 @@ pub const CSourceFile = struct {
169};169};
170170
171const Job = union(enum) {171const 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.
173 codegen_decl: *Module.Decl,173 codegen_decl: *Module.Decl,
174 /// Write the machine code for a function to the output file.
175 codegen_func: *Module.Fn,
174 /// Render the .h file snippet for the Decl.176 /// Render the .h file snippet for the Decl.
175 emit_h_decl: *Module.Decl,177 emit_h_decl: *Module.Decl,
176 /// The Decl needs to be analyzed and possibly export itself.178 /// The Decl needs to be analyzed and possibly export itself.
...@@ -2006,54 +2008,56 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2006,54 +2008,56 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2006 const module = self.bin_file.options.module.?;2008 const module = self.bin_file.options.module.?;
2007 assert(decl.has_tv);2009 assert(decl.has_tv);
2008 if (decl.val.castTag(.function)) |payload| {2010 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) {2039 assert(decl.ty.hasCodeGenBits());
2012 .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {2040
2041 self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {
2042 error.OutOfMemory => return error.OutOfMemory,
2013 error.AnalysisFail => {2043 error.AnalysisFail => {
2014 assert(func.state != .in_progress);2044 decl.analysis = .codegen_failure;
2015 continue;2045 continue;
2016 },2046 },
2017 error.OutOfMemory => return error.OutOfMemory,2047 else => {
2018 },2048 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
2019 .in_progress => unreachable,2049 module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
2020 .inline_only => unreachable, // don't queue work for this2050 gpa,
2021 .sema_failure, .dependency_failure => continue,2051 decl.srcLoc(),
2022 .success => unreachable, // don't queue it twice2052 "unable to codegen: {s}",
2023 };2053 .{@errorName(err)},
2024 defer air.deinit(gpa);2054 ));
20252055 decl.analysis = .codegen_failure_retryable;
2026 log.debug("analyze liveness of {s}", .{decl.name});2056 continue;
2027 var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);2057 },
2028 defer liveness.deinit(gpa);2058 };
20292059 continue;
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});
2034 }2060 }
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;
2057 }2061 }
20582062
2059 assert(decl.ty.hasCodeGenBits());2063 assert(decl.ty.hasCodeGenBits());
...@@ -2078,6 +2082,72 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2078,6 +2082,72 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2078 };2082 };
2079 },2083 },
2080 },2084 },
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 },
2081 .emit_h_decl => |decl| switch (decl.analysis) {2151 .emit_h_decl => |decl| switch (decl.analysis) {
2082 .unreferenced => unreachable,2152 .unreferenced => unreachable,
2083 .in_progress => unreachable,2153 .in_progress => unreachable,
src/Module.zig+89-85
...@@ -2902,6 +2902,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -2902,6 +2902,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
2902 decl.generation = mod.generation;2902 decl.generation = mod.generation;
2903 return false;2903 return false;
2904 }2904 }
2905 log.debug("semaDecl {*} ({s})", .{ decl, decl.name });
29052906
2906 var block_scope: Scope.Block = .{2907 var block_scope: Scope.Block = .{
2907 .parent = null,2908 .parent = null,
...@@ -2938,106 +2939,109 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -2938,106 +2939,109 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
2938 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);2939 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
29392940
2940 if (decl_tv.val.castTag(.function)) |fn_payload| {2941 if (decl_tv.val.castTag(.function)) |fn_payload| {
2941 var prev_type_has_bits = false;2942 const func = fn_payload.data;
2942 var prev_is_inline = false;2943 const owns_tv = func.owner_decl == decl;
2943 var type_changed = true;2944 if (owns_tv) {
29442945 var prev_type_has_bits = false;
2945 if (decl.has_tv) {2946 var prev_is_inline = false;
2946 prev_type_has_bits = decl.ty.hasCodeGenBits();2947 var type_changed = true;
2947 type_changed = !decl.ty.eql(decl_tv.ty);2948
2948 if (decl.getFunction()) |prev_func| {2949 if (decl.has_tv) {
2949 prev_is_inline = prev_func.state == .inline_only;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);
2950 }2956 }
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;2958 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
2966 if (!is_inline and decl_tv.ty.hasCodeGenBits()) {2959 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
2967 // We don't fully codegen the decl until later, but we do need to reserve a global2960 decl.align_val = try align_val.copy(&decl_arena.allocator);
2968 // offset table index for it. This allows us to codegen decls out of dependency order,2961 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
2969 // increasing how many computations can be done in parallel.2962 decl.has_tv = true;
2970 try mod.comp.bin_file.allocateDeclIndexes(decl);2963 decl.owns_tv = owns_tv;
2971 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });2964 decl_arena_state.* = decl_arena.state;
2972 if (type_changed and mod.emit_h != null) {2965 decl.value_arena = decl_arena_state;
2973 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });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);
2974 }2981 }
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) {2983 if (decl.is_exported) {
2980 const export_src = src; // TODO make this point at `export` token2984 const export_src = src; // TODO make this point at `export` token
2981 if (is_inline) {2985 if (is_inline) {
2982 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});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);
2983 }2990 }
2984 // The scope needs to have the decl in it.2991 return type_changed or is_inline != prev_is_inline;
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);
2993 }2992 }
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;3000 decl.owns_tv = false;
2996 var queue_linker_work = false;3001 var queue_linker_work = false;
2997 if (decl_tv.val.castTag(.variable)) |payload| {3002 if (decl_tv.val.castTag(.variable)) |payload| {
2998 const variable = payload.data;3003 const variable = payload.data;
2999 if (variable.owner_decl == decl) {3004 if (variable.owner_decl == decl) {
3000 decl.owns_tv = true;3005 decl.owns_tv = true;
3001 queue_linker_work = true;3006 queue_linker_work = true;
30023007
3003 const copied_init = try variable.init.copy(&decl_arena.allocator);3008 const copied_init = try variable.init.copy(&decl_arena.allocator);
3004 variable.init = copied_init;3009 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 }
3012 }3010 }
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);3019 decl.ty = try decl_tv.ty.copy(&decl_arena.allocator);
3015 decl.val = try decl_tv.val.copy(&decl_arena.allocator);3020 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
3016 decl.align_val = try align_val.copy(&decl_arena.allocator);3021 decl.align_val = try align_val.copy(&decl_arena.allocator);
3017 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);3022 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3018 decl.has_tv = true;3023 decl.has_tv = true;
3019 decl_arena_state.* = decl_arena.state;3024 decl_arena_state.* = decl_arena.state;
3020 decl.value_arena = decl_arena_state;3025 decl.value_arena = decl_arena_state;
3021 decl.analysis = .complete;3026 decl.analysis = .complete;
3022 decl.generation = mod.generation;3027 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 });
30273028
3028 if (type_changed and mod.emit_h != null) {3029 if (queue_linker_work and decl.ty.hasCodeGenBits()) {
3029 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });3030 try mod.comp.bin_file.allocateDeclIndexes(decl);
3030 }3031 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
3031 }
30323032
3033 if (decl.is_exported) {3033 if (type_changed and mod.emit_h != null) {
3034 const export_src = src; // TODO point to the export token3034 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
3035 // The scope needs to have the decl in it.
3036 try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl);
3037 }3035 }
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);
3040 }3042 }
3043
3044 return type_changed;
3041}3045}
30423046
3043/// Returns the depender's index of the dependee.3047/// Returns the depender's index of the dependee.