authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-29 20:49:16-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-02 13:16:17-07:00
loge3bed8d81dfd7198dd4c496f19a6791e27e41f26
tree51703dcbdd51c21207c282585345c2b081cb57b8
parentc710d5eefe3f83226f1651947239730e77af43cb

stage2: introduce CacheMode

The two CacheMode values are `whole` and `incremental`. `incremental` is what we had before; `whole` is new. Whole cache mode uses everything as inputs to the cache hash; and when a hit occurs it skips everything including linking. This is ideal for when source files change rarely and for backends that do not have good incremental compilation support, for example compiler-rt or libc compiled with LLVM with optimizations on. This is the main motivation for the additional mode, so that we can have LLVM-optimized compiler-rt/libc builds, without waiting for the LLVM backend every single time Zig is invoked. Incremental cache mode hashes only the input file path and a few target options, intentionally relying on collisions to locate already-existing build artifacts which can then be incrementally updated. The bespoke logic for caching stage1 backend build artifacts is removed since we now have a global caching mechanism for when we want to cache the entire compilation, *including* linking. Previously we had to get "creative" with libs.txt and a special byte in the hash id to communicate flags, so that when the cached artifacts were re-linked, we had this information from stage1 even though we didn't actually run it. Now that `CacheMode.whole` includes linking, this extra information does not need to be preserved for cache hits. So although this changeset introduces complexity, it also removes complexity. The main trickiness here comes from the inherent differences between the two modes: `incremental` wants a directory immediately to operate on, while `whole` doesn't know the output directory until the compilation is complete. This commit deals with this problem mostly inside `update()`, where, on a cache miss, it replaces `zig_cache_artifact_directory` with a temporary directory, and then renames it into place once the compilation is complete. Items remaining before this branch can be merged: * [ ] make sure these things make it into the cache manifest: - @import files - @embedFile files - we already add dep files from c but make sure the main .c files make it in there too, not just the included files * [ ] double check that the emit paths of other things besides the binary are working correctly. * [ ] test `-fno-emit-bin` + `-fstage1` * [ ] test `-femit-bin=foo` + `-fstage1` * [ ] implib emit directory copies bin_file_emit directory in create() and needs to be adjusted to be overridden as well. * [ ] make sure emit-h is handled correctly in the cache hash * [ ] Cache: detect duplicate files added to the manifest Some preliminary performance measurements of wall clock time and peak RSS used: stage1 behavior (1077 tests), llvm backend, release build: * cold global cache: 4.6s, 1.1 GiB * warm global cache: 3.4s, 980 MiB stage2 master branch behavior (575 tests), llvm backend, release build: * cold global cache: 0.62s, 191 MiB * warm global cache: 0.40s, 128 MiB stage2 this branch behavior (575 tests), llvm backend, release build: * cold global cache: 0.62s, 179 MiB * warm global cache: 0.27s, 90 MiB

14 files changed, 401 insertions(+), 218 deletions(-)

src/Compilation.zig+380-209
......@@ -41,8 +41,8 @@ gpa: Allocator,
4141arena_state: std.heap.ArenaAllocator.State,
4242bin_file: *link.File,
4343c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
44stage1_lock: ?Cache.Lock = null,
45stage1_cache_manifest: *Cache.Manifest = undefined,
44/// This is a pointer to a local variable inside `update()`.
45whole_cache_manifest: ?*Cache.Manifest = null,
4646
4747link_error_flags: link.File.ErrorFlags = .{},
4848
......@@ -98,6 +98,9 @@ clang_argv: []const []const u8,
9898cache_parent: *Cache,
9999/// Path to own executable for invoking `zig clang`.
100100self_exe_path: ?[]const u8,
101/// null means -fno-emit-bin. Contains the basename of the
102/// outputted binary file in case we don't know the directory yet.
103whole_bin_basename: ?[]const u8,
101104zig_lib_directory: Directory,
102105local_cache_directory: Directory,
103106global_cache_directory: Directory,
......@@ -612,6 +615,15 @@ pub const Directory = struct {
612615 return std.fs.path.joinZ(allocator, paths);
613616 }
614617 }
618
619 /// Whether or not the handle should be closed, or the path should be freed
620 /// is determined by usage, however this function is provided for convenience
621 /// if it happens to be what the caller needs.
622 pub fn closeAndFree(self: *Directory, gpa: Allocator) void {
623 self.handle.close();
624 if (self.path) |p| gpa.free(p);
625 self.* = undefined;
626 }
615627};
616628
617629pub const EmitLoc = struct {
......@@ -631,6 +643,7 @@ pub const ClangPreprocessorMode = enum {
631643};
632644
633645pub const SystemLib = link.SystemLib;
646pub const CacheMode = link.CacheMode;
634647
635648pub const InitOptions = struct {
636649 zig_lib_directory: Directory,
......@@ -668,6 +681,7 @@ pub const InitOptions = struct {
668681 /// is externally modified - essentially anything other than zig-cache - then
669682 /// this flag would be set to disable this machinery to avoid false positives.
670683 disable_lld_caching: bool = false,
684 cache_mode: CacheMode = .incremental,
671685 object_format: ?std.Target.ObjectFormat = null,
672686 optimize_mode: std.builtin.Mode = .Debug,
673687 keep_source_files_loaded: bool = false,
......@@ -885,6 +899,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
885899 break :blk build_options.is_stage1;
886900 };
887901
902 const cache_mode = if (use_stage1) CacheMode.whole else options.cache_mode;
903
888904 // Make a decision on whether to use LLVM or our own backend.
889905 const use_llvm = build_options.have_llvm and blk: {
890906 if (options.use_llvm) |explicit|
......@@ -1219,18 +1235,26 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
12191235 // modified between incremental updates.
12201236 var hash = cache.hash;
12211237
1222 // Here we put the root source file path name, but *not* with addFile. We want the
1223 // hash to be the same regardless of the contents of the source file, because
1224 // incremental compilation will handle it, but we do want to namespace different
1225 // source file names because they are likely different compilations and therefore this
1226 // would be likely to cause cache hits.
1227 hash.addBytes(main_pkg.root_src_path);
1228 hash.addOptionalBytes(main_pkg.root_src_directory.path);
1229 {
1230 var local_arena = std.heap.ArenaAllocator.init(gpa);
1231 defer local_arena.deinit();
1232 var seen_table = std.AutoHashMap(*Package, void).init(local_arena.allocator());
1233 try addPackageTableToCacheHash(&hash, &local_arena, main_pkg.table, &seen_table, .path_bytes);
1238 switch (cache_mode) {
1239 .incremental => {
1240 // Here we put the root source file path name, but *not* with addFile.
1241 // We want the hash to be the same regardless of the contents of the
1242 // source file, because incremental compilation will handle it, but we
1243 // do want to namespace different source file names because they are
1244 // likely different compilations and therefore this would be likely to
1245 // cause cache hits.
1246 hash.addBytes(main_pkg.root_src_path);
1247 hash.addOptionalBytes(main_pkg.root_src_directory.path);
1248 {
1249 var seen_table = std.AutoHashMap(*Package, void).init(arena);
1250 try addPackageTableToCacheHash(&hash, &arena_allocator, main_pkg.table, &seen_table, .path_bytes);
1251 }
1252 },
1253 .whole => {
1254 // In this case, we postpone adding the input source file until
1255 // we create the cache manifest, in update(), because we want to
1256 // track it and packages as files.
1257 },
12341258 }
12351259 hash.add(valgrind);
12361260 hash.add(single_threaded);
......@@ -1238,9 +1262,35 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
12381262 hash.add(use_llvm);
12391263 hash.add(dll_export_fns);
12401264 hash.add(options.is_test);
1265 hash.add(options.test_evented_io);
1266 hash.addOptionalBytes(options.test_filter);
1267 hash.addOptionalBytes(options.test_name_prefix);
12411268 hash.add(options.skip_linker_dependencies);
12421269 hash.add(options.parent_compilation_link_libc);
12431270
1271 // In the case of incremental cache mode, this `zig_cache_artifact_directory`
1272 // is computed based on a hash of non-linker inputs, and it is where all
1273 // build artifacts are stored (even while in-progress).
1274 //
1275 // For whole cache mode, it is still used for builtin.zig so that the file
1276 // path to builtin.zig can remain consistent during a debugging session at
1277 // runtime. However, we don't know where to put outputs from the linker
1278 // or stage1 backend object files until the final cache hash, which is available
1279 // after the compilation is complete.
1280 //
1281 // Therefore, in whole cache mode, we additionally create a temporary cache
1282 // directory for these two kinds of build artifacts, and then rename it
1283 // into place after the final hash is known. However, we don't want
1284 // to create the temporary directory here, because in the case of a cache hit,
1285 // this would have been wasted syscalls to make the directory and then not
1286 // use it (or delete it).
1287 //
1288 // In summary, for whole cache mode, we simulate `-fno-emit-bin` in this
1289 // function, and `zig_cache_artifact_directory` is *wrong* except for builtin.zig,
1290 // and then at the beginning of `update()` when we find out whether we need
1291 // a temporary directory, we patch up all the places that the incorrect
1292 // `zig_cache_artifact_directory` was passed to various components of the compiler.
1293
12441294 const digest = hash.final();
12451295 const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
12461296 var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{});
......@@ -1374,6 +1424,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
13741424 };
13751425 }
13761426
1427 switch (cache_mode) {
1428 .whole => break :blk null,
1429 .incremental => {},
1430 }
1431
13771432 if (module) |zm| {
13781433 break :blk link.Emit{
13791434 .directory = zm.zig_cache_artifact_directory,
......@@ -1425,6 +1480,18 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
14251480 };
14261481 };
14271482
1483 // This is so that when doing `CacheMode.whole`, the mechanism in update()
1484 // can use it for communicating the result directory via `bin_file.emit`.
1485 // This is used to distinguish between -fno-emit-bin and -femit-bin
1486 // for `CacheMode.whole`.
1487 const whole_bin_basename: ?[]const u8 = if (options.emit_bin) |x|
1488 if (x.directory == null)
1489 x.basename
1490 else
1491 null
1492 else
1493 null;
1494
14281495 var system_libs: std.StringArrayHashMapUnmanaged(SystemLib) = .{};
14291496 errdefer system_libs.deinit(gpa);
14301497 try system_libs.ensureTotalCapacity(gpa, options.system_lib_names.len);
......@@ -1512,7 +1579,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
15121579 .skip_linker_dependencies = options.skip_linker_dependencies,
15131580 .parent_compilation_link_libc = options.parent_compilation_link_libc,
15141581 .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os,
1515 .disable_lld_caching = options.disable_lld_caching,
1582 .cache_mode = cache_mode,
1583 .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole,
15161584 .subsystem = options.subsystem,
15171585 .is_test = options.is_test,
15181586 .wasi_exec_model = wasi_exec_model,
......@@ -1529,6 +1597,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
15291597 .local_cache_directory = options.local_cache_directory,
15301598 .global_cache_directory = options.global_cache_directory,
15311599 .bin_file = bin_file,
1600 .whole_bin_basename = whole_bin_basename,
15321601 .emit_asm = options.emit_asm,
15331602 .emit_llvm_ir = options.emit_llvm_ir,
15341603 .emit_llvm_bc = options.emit_llvm_bc,
......@@ -1725,20 +1794,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17251794 return comp;
17261795}
17271796
1728fn releaseStage1Lock(comp: *Compilation) void {
1729 if (comp.stage1_lock) |*lock| {
1730 lock.release();
1731 comp.stage1_lock = null;
1732 }
1733}
1734
17351797pub fn destroy(self: *Compilation) void {
17361798 const optional_module = self.bin_file.options.module;
17371799 self.bin_file.destroy();
17381800 if (optional_module) |module| module.deinit();
17391801
1740 self.releaseStage1Lock();
1741
17421802 const gpa = self.gpa;
17431803 self.work_queue.deinit();
17441804 self.anon_work_queue.deinit();
......@@ -1815,22 +1875,135 @@ pub fn getTarget(self: Compilation) Target {
18151875 return self.bin_file.options.target;
18161876}
18171877
1878fn restorePrevZigCacheArtifactDirectory(comp: *Compilation, directory: *Directory) void {
1879 if (directory.path) |p| comp.gpa.free(p);
1880
1881 // Restore the Module's previous zig_cache_artifact_directory
1882 // This is only for cleanup purposes; Module.deinit calls close
1883 // on the handle of zig_cache_artifact_directory.
1884 if (comp.bin_file.options.module) |module| {
1885 const builtin_pkg = module.main_pkg.table.get("builtin").?;
1886 module.zig_cache_artifact_directory = builtin_pkg.root_src_directory;
1887 }
1888}
1889
1890fn cleanupTmpArtifactDirectory(
1891 comp: *Compilation,
1892 tmp_artifact_directory: *?Directory,
1893 tmp_dir_sub_path: []const u8,
1894) void {
1895 comp.gpa.free(tmp_dir_sub_path);
1896 if (tmp_artifact_directory.*) |*directory| {
1897 directory.handle.close();
1898 restorePrevZigCacheArtifactDirectory(comp, directory);
1899 }
1900}
1901
18181902/// Detect changes to source files, perform semantic analysis, and update the output files.
1819pub fn update(self: *Compilation) !void {
1903pub fn update(comp: *Compilation) !void {
18201904 const tracy_trace = trace(@src());
18211905 defer tracy_trace.end();
18221906
1823 self.clearMiscFailures();
1907 comp.clearMiscFailures();
1908
1909 var man: Cache.Manifest = undefined;
1910 defer if (comp.whole_cache_manifest != null) man.deinit();
1911
1912 var tmp_dir_sub_path: []const u8 = &.{};
1913 var tmp_artifact_directory: ?Directory = null;
1914 defer cleanupTmpArtifactDirectory(comp, &tmp_artifact_directory, tmp_dir_sub_path);
1915
1916 // If using the whole caching strategy, we check for *everything* up front, including
1917 // C source files.
1918 if (comp.bin_file.options.cache_mode == .whole) {
1919 // We are about to obtain this lock, so here we give other processes a chance first.
1920 comp.bin_file.releaseLock();
1921
1922 man = comp.cache_parent.obtain();
1923 try comp.addNonIncrementalStuffToCacheManifest(&man);
1924
1925 const is_hit = man.hit() catch |err| {
1926 // TODO properly bubble these up instead of emitting a warning
1927 const i = man.failed_file_index orelse return err;
1928 const file_path = man.files.items[i].path orelse return err;
1929 std.log.warn("{s}: {s}", .{ @errorName(err), file_path });
1930 return err;
1931 };
1932 if (is_hit) {
1933 const digest = man.final();
1934
1935 // Communicate the output binary location to parent Compilations.
1936 if (comp.whole_bin_basename) |basename| {
1937 const new_sub_path = try std.fs.path.join(comp.gpa, &.{
1938 "o", &digest, basename,
1939 });
1940 if (comp.bin_file.options.emit) |emit| {
1941 comp.gpa.free(emit.sub_path);
1942 }
1943 comp.bin_file.options.emit = .{
1944 .directory = comp.local_cache_directory,
1945 .sub_path = new_sub_path,
1946 };
1947 }
1948
1949 comp.emitOthers();
1950
1951 assert(comp.bin_file.lock == null);
1952 comp.bin_file.lock = man.toOwnedLock();
1953 return;
1954 }
1955 comp.whole_cache_manifest = &man;
1956
1957 // Initialize `bin_file.emit` with a temporary Directory so that compilation can
1958 // continue on the same path as incremental, using the temporary Directory.
1959 tmp_artifact_directory = d: {
1960 const s = std.fs.path.sep_str;
1961 const rand_int = std.crypto.random.int(u64);
1962
1963 tmp_dir_sub_path = try std.fmt.allocPrint(comp.gpa, "tmp" ++ s ++ "{x}", .{rand_int});
1964
1965 const path = try comp.local_cache_directory.join(comp.gpa, &.{tmp_dir_sub_path});
1966 errdefer comp.gpa.free(path);
1967
1968 const handle = try comp.local_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{});
1969 errdefer handle.close();
1970
1971 break :d .{
1972 .path = path,
1973 .handle = handle,
1974 };
1975 };
1976
1977 // This updates the output directory for stage1 backend and linker outputs.
1978 if (comp.bin_file.options.module) |module| {
1979 module.zig_cache_artifact_directory = tmp_artifact_directory.?;
1980 }
1981
1982 // This resets the link.File to operate as if we called openPath() in create()
1983 // instead of simulating -fno-emit-bin.
1984 var options = comp.bin_file.options;
1985 if (comp.whole_bin_basename) |basename| {
1986 if (options.emit) |emit| {
1987 comp.gpa.free(emit.sub_path);
1988 }
1989 options.emit = .{
1990 .directory = tmp_artifact_directory.?,
1991 .sub_path = basename,
1992 };
1993 }
1994 comp.bin_file.destroy();
1995 comp.bin_file = try link.File.openPath(comp.gpa, options);
1996 }
18241997
18251998 // For compiling C objects, we rely on the cache hash system to avoid duplicating work.
18261999 // Add a Job for each C object.
1827 try self.c_object_work_queue.ensureUnusedCapacity(self.c_object_table.count());
1828 for (self.c_object_table.keys()) |key| {
1829 self.c_object_work_queue.writeItemAssumeCapacity(key);
2000 try comp.c_object_work_queue.ensureUnusedCapacity(comp.c_object_table.count());
2001 for (comp.c_object_table.keys()) |key| {
2002 comp.c_object_work_queue.writeItemAssumeCapacity(key);
18302003 }
18312004
1832 const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_stage1;
1833 if (self.bin_file.options.module) |module| {
2005 const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1;
2006 if (comp.bin_file.options.module) |module| {
18342007 module.compile_log_text.shrinkAndFree(module.gpa, 0);
18352008 module.generation += 1;
18362009
......@@ -1845,7 +2018,7 @@ pub fn update(self: *Compilation) !void {
18452018 // import_table here.
18462019 // Likewise, in the case of `zig test`, the test runner is the root source file,
18472020 // and so there is nothing to import the main file.
1848 if (use_stage1 or self.bin_file.options.is_test) {
2021 if (use_stage1 or comp.bin_file.options.is_test) {
18492022 _ = try module.importPkg(module.main_pkg);
18502023 }
18512024
......@@ -1854,34 +2027,34 @@ pub fn update(self: *Compilation) !void {
18542027 // to update it.
18552028 // We still want AstGen work items for stage1 so that we expose compile errors
18562029 // that are implemented in stage2 but not stage1.
1857 try self.astgen_work_queue.ensureUnusedCapacity(module.import_table.count());
2030 try comp.astgen_work_queue.ensureUnusedCapacity(module.import_table.count());
18582031 for (module.import_table.values()) |value| {
1859 self.astgen_work_queue.writeItemAssumeCapacity(value);
2032 comp.astgen_work_queue.writeItemAssumeCapacity(value);
18602033 }
18612034
18622035 if (!use_stage1) {
18632036 // Put a work item in for checking if any files used with `@embedFile` changed.
18642037 {
1865 try self.embed_file_work_queue.ensureUnusedCapacity(module.embed_table.count());
2038 try comp.embed_file_work_queue.ensureUnusedCapacity(module.embed_table.count());
18662039 var it = module.embed_table.iterator();
18672040 while (it.next()) |entry| {
18682041 const embed_file = entry.value_ptr.*;
1869 self.embed_file_work_queue.writeItemAssumeCapacity(embed_file);
2042 comp.embed_file_work_queue.writeItemAssumeCapacity(embed_file);
18702043 }
18712044 }
18722045
1873 try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg });
1874 if (self.bin_file.options.is_test) {
1875 try self.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg });
2046 try comp.work_queue.writeItem(.{ .analyze_pkg = std_pkg });
2047 if (comp.bin_file.options.is_test) {
2048 try comp.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg });
18762049 }
18772050 }
18782051 }
18792052
1880 try self.performAllTheWork();
2053 try comp.performAllTheWork();
18812054
18822055 if (!use_stage1) {
1883 if (self.bin_file.options.module) |module| {
1884 if (self.bin_file.options.is_test and self.totalErrorCount() == 0) {
2056 if (comp.bin_file.options.module) |module| {
2057 if (comp.bin_file.options.is_test and comp.totalErrorCount() == 0) {
18852058 // The `test_functions` decl has been intentionally postponed until now,
18862059 // at which point we must populate it with the list of test functions that
18872060 // have been discovered and not filtered out.
......@@ -1910,39 +2083,184 @@ pub fn update(self: *Compilation) !void {
19102083 }
19112084 }
19122085
1913 if (self.totalErrorCount() != 0) {
2086 if (comp.totalErrorCount() != 0) {
19142087 // Skip flushing.
1915 self.link_error_flags = .{};
2088 comp.link_error_flags = .{};
19162089 return;
19172090 }
19182091
19192092 // This is needed before reading the error flags.
1920 try self.bin_file.flush(self);
1921 self.link_error_flags = self.bin_file.errorFlags();
2093 try comp.bin_file.flush(comp);
2094 comp.link_error_flags = comp.bin_file.errorFlags();
19222095
19232096 if (!use_stage1) {
1924 if (self.bin_file.options.module) |module| {
2097 if (comp.bin_file.options.module) |module| {
19252098 try link.File.C.flushEmitH(module);
19262099 }
19272100 }
19282101
19292102 // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and
19302103 // -femit-asm to handle, in the case of C objects.
1931 self.emitOthers();
2104 comp.emitOthers();
19322105
19332106 // If there are any errors, we anticipate the source files being loaded
19342107 // to report error messages. Otherwise we unload all source files to save memory.
19352108 // The ZIR needs to stay loaded in memory because (1) Decl objects contain references
19362109 // to it, and (2) generic instantiations, comptime calls, inline calls will need
19372110 // to reference the ZIR.
1938 if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
1939 if (self.bin_file.options.module) |module| {
2111 if (!comp.keep_source_files_loaded) {
2112 if (comp.bin_file.options.module) |module| {
19402113 for (module.import_table.values()) |file| {
1941 file.unloadTree(self.gpa);
1942 file.unloadSource(self.gpa);
2114 file.unloadTree(comp.gpa);
2115 file.unloadSource(comp.gpa);
2116 }
2117 }
2118 }
2119
2120 if (comp.whole_cache_manifest != null) {
2121 const digest = man.final();
2122
2123 // Rename the temporary directory into place.
2124 var directory = tmp_artifact_directory.?;
2125 tmp_artifact_directory = null;
2126
2127 directory.handle.close();
2128 defer restorePrevZigCacheArtifactDirectory(comp, &directory);
2129
2130 const o_sub_path = try std.fs.path.join(comp.gpa, &[_][]const u8{ "o", &digest });
2131 defer comp.gpa.free(o_sub_path);
2132
2133 try std.fs.rename(
2134 comp.local_cache_directory.handle,
2135 tmp_dir_sub_path,
2136 comp.local_cache_directory.handle,
2137 o_sub_path,
2138 );
2139
2140 // Failure here only means an unnecessary cache miss.
2141 man.writeManifest() catch |err| {
2142 log.warn("failed to write cache manifest: {s}", .{@errorName(err)});
2143 };
2144
2145 // Communicate the output binary location to parent Compilations.
2146 if (comp.whole_bin_basename) |basename| {
2147 comp.bin_file.options.emit = .{
2148 .directory = comp.local_cache_directory,
2149 .sub_path = try std.fs.path.join(comp.gpa, &.{ "o", &digest, basename }),
2150 };
2151 }
2152
2153 assert(comp.bin_file.lock == null);
2154 comp.bin_file.lock = man.toOwnedLock();
2155 return;
2156 }
2157}
2158
2159/// This is only observed at compile-time and used to emit a compile error
2160/// to remind the programmer to update multiple related pieces of code that
2161/// are in different locations. Bump this number when adding or deleting
2162/// anything from the link cache manifest.
2163pub const link_hash_implementation_version = 1;
2164
2165fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
2166 const gpa = comp.gpa;
2167 const target = comp.getTarget();
2168
2169 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
2170 errdefer arena_allocator.deinit();
2171 const arena = arena_allocator.allocator();
2172
2173 comptime assert(link_hash_implementation_version == 1);
2174
2175 if (comp.bin_file.options.module) |mod| {
2176 const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{
2177 mod.main_pkg.root_src_path,
2178 });
2179 _ = try man.addFile(main_zig_file, null);
2180 {
2181 var seen_table = std.AutoHashMap(*Package, void).init(arena);
2182
2183 // Skip builtin.zig; it is useless as an input, and we don't want to have to
2184 // write it before checking for a cache hit.
2185 const builtin_pkg = mod.main_pkg.table.get("builtin").?;
2186 try seen_table.put(builtin_pkg, {});
2187
2188 try addPackageTableToCacheHash(&man.hash, &arena_allocator, mod.main_pkg.table, &seen_table, .{ .files = man });
2189 }
2190 }
2191
2192 try man.addOptionalFile(comp.bin_file.options.linker_script);
2193 try man.addOptionalFile(comp.bin_file.options.version_script);
2194 try man.addListOfFiles(comp.bin_file.options.objects);
2195
2196 for (comp.c_object_table.keys()) |key| {
2197 _ = try man.addFile(key.src.src_path, null);
2198 man.hash.addListOfBytes(key.src.extra_flags);
2199 }
2200
2201 man.hash.addOptionalEmitLoc(comp.emit_asm);
2202 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
2203 man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);
2204 man.hash.addOptionalEmitLoc(comp.emit_analysis);
2205 man.hash.addOptionalEmitLoc(comp.emit_docs);
2206
2207 man.hash.addListOfBytes(comp.clang_argv);
2208
2209 man.hash.addOptional(comp.bin_file.options.stack_size_override);
2210 man.hash.addOptional(comp.bin_file.options.image_base_override);
2211 man.hash.addOptional(comp.bin_file.options.gc_sections);
2212 man.hash.add(comp.bin_file.options.eh_frame_hdr);
2213 man.hash.add(comp.bin_file.options.emit_relocs);
2214 man.hash.add(comp.bin_file.options.rdynamic);
2215 man.hash.addListOfBytes(comp.bin_file.options.lib_dirs);
2216 man.hash.addListOfBytes(comp.bin_file.options.rpath_list);
2217 man.hash.add(comp.bin_file.options.each_lib_rpath);
2218 man.hash.add(comp.bin_file.options.skip_linker_dependencies);
2219 man.hash.add(comp.bin_file.options.z_nodelete);
2220 man.hash.add(comp.bin_file.options.z_notext);
2221 man.hash.add(comp.bin_file.options.z_defs);
2222 man.hash.add(comp.bin_file.options.z_origin);
2223 man.hash.add(comp.bin_file.options.z_noexecstack);
2224 man.hash.add(comp.bin_file.options.z_now);
2225 man.hash.add(comp.bin_file.options.z_relro);
2226 man.hash.add(comp.bin_file.options.include_compiler_rt);
2227 if (comp.bin_file.options.link_libc) {
2228 man.hash.add(comp.bin_file.options.libc_installation != null);
2229 if (comp.bin_file.options.libc_installation) |libc_installation| {
2230 man.hash.addBytes(libc_installation.crt_dir.?);
2231 if (target.abi == .msvc) {
2232 man.hash.addBytes(libc_installation.msvc_lib_dir.?);
2233 man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
19432234 }
19442235 }
2236 man.hash.addOptionalBytes(comp.bin_file.options.dynamic_linker);
19452237 }
2238 man.hash.addOptionalBytes(comp.bin_file.options.soname);
2239 man.hash.addOptional(comp.bin_file.options.version);
2240 link.hashAddSystemLibs(&man.hash, comp.bin_file.options.system_libs);
2241 man.hash.addOptional(comp.bin_file.options.allow_shlib_undefined);
2242 man.hash.add(comp.bin_file.options.bind_global_refs_locally);
2243 man.hash.add(comp.bin_file.options.tsan);
2244 man.hash.addOptionalBytes(comp.bin_file.options.sysroot);
2245 man.hash.add(comp.bin_file.options.linker_optimization);
2246
2247 // WASM specific stuff
2248 man.hash.add(comp.bin_file.options.import_memory);
2249 man.hash.addOptional(comp.bin_file.options.initial_memory);
2250 man.hash.addOptional(comp.bin_file.options.max_memory);
2251 man.hash.addOptional(comp.bin_file.options.global_base);
2252
2253 // Mach-O specific stuff
2254 man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);
2255 man.hash.addListOfBytes(comp.bin_file.options.frameworks);
2256
2257 // COFF specific stuff
2258 man.hash.addOptional(comp.bin_file.options.subsystem);
2259 man.hash.add(comp.bin_file.options.tsaware);
2260 man.hash.add(comp.bin_file.options.nxcompat);
2261 man.hash.add(comp.bin_file.options.dynamicbase);
2262 man.hash.addOptional(comp.bin_file.options.major_subsystem_version);
2263 man.hash.addOptional(comp.bin_file.options.minor_subsystem_version);
19462264}
19472265
19482266fn emitOthers(comp: *Compilation) void {
......@@ -2988,7 +3306,9 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
29883306
29893307 const dep_basename = std.fs.path.basename(out_dep_path);
29903308 try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
2991 if (build_options.is_stage1 and comp.bin_file.options.use_stage1) try comp.stage1_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
3309 if (comp.whole_cache_manifest) |whole_cache_manifest| {
3310 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
3311 }
29923312
29933313 const digest = man.final();
29943314 const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
......@@ -3351,13 +3671,13 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
33513671 };
33523672}
33533673
3354pub fn tmpFilePath(comp: *Compilation, arena: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
3674pub fn tmpFilePath(comp: *Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
33553675 const s = std.fs.path.sep_str;
33563676 const rand_int = std.crypto.random.int(u64);
33573677 if (comp.local_cache_directory.path) |p| {
3358 return std.fmt.allocPrint(arena, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
3678 return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
33593679 } else {
3360 return std.fmt.allocPrint(arena, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix });
3680 return std.fmt.allocPrint(ally, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix });
33613681 }
33623682}
33633683
......@@ -4424,6 +4744,7 @@ fn buildOutputFromZig(
44244744 .global_cache_directory = comp.global_cache_directory,
44254745 .local_cache_directory = comp.global_cache_directory,
44264746 .zig_lib_directory = comp.zig_lib_directory,
4747 .cache_mode = .whole,
44274748 .target = target,
44284749 .root_name = root_name,
44294750 .main_pkg = &main_pkg,
......@@ -4501,10 +4822,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
45014822 mod.main_pkg.root_src_path,
45024823 });
45034824 const zig_lib_dir = comp.zig_lib_directory.path.?;
4504 const builtin_zig_path = try directory.join(arena, &[_][]const u8{"builtin.zig"});
45054825 const target = comp.getTarget();
4506 const id_symlink_basename = "stage1.id";
4507 const libs_txt_basename = "libs.txt";
45084826
45094827 // The include_compiler_rt stored in the bin file options here means that we need
45104828 // compiler-rt symbols *somehow*. However, in the context of using the stage1 backend
......@@ -4516,115 +4834,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
45164834 const include_compiler_rt = comp.bin_file.options.output_mode == .Obj and
45174835 comp.bin_file.options.include_compiler_rt;
45184836
4519 // We are about to obtain this lock, so here we give other processes a chance first.
4520 comp.releaseStage1Lock();
4521
4522 // Unlike with the self-hosted Zig module, stage1 does not support incremental compilation,
4523 // so we input all the zig source files into the cache hash system. We're going to keep
4524 // the artifact directory the same, however, so we take the same strategy as linking
4525 // does where we have a file which specifies the hash of the output directory so that we can
4526 // skip the expensive compilation step if the hash matches.
4527 var man = comp.cache_parent.obtain();
4528 defer man.deinit();
4529
4530 _ = try man.addFile(main_zig_file, null);
4531 {
4532 var seen_table = std.AutoHashMap(*Package, void).init(arena_allocator.allocator());
4533 try addPackageTableToCacheHash(&man.hash, &arena_allocator, mod.main_pkg.table, &seen_table, .{ .files = &man });
4534 }
4535 man.hash.add(comp.bin_file.options.valgrind);
4536 man.hash.add(comp.bin_file.options.single_threaded);
4537 man.hash.add(target.os.getVersionRange());
4538 man.hash.add(comp.bin_file.options.dll_export_fns);
4539 man.hash.add(comp.bin_file.options.function_sections);
4540 man.hash.add(include_compiler_rt);
4541 man.hash.add(comp.bin_file.options.is_test);
4542 man.hash.add(comp.bin_file.options.emit != null);
4543 man.hash.add(mod.emit_h != null);
4544 if (mod.emit_h) |emit_h| {
4545 man.hash.addEmitLoc(emit_h.loc);
4546 }
4547 man.hash.addOptionalEmitLoc(comp.emit_asm);
4548 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
4549 man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);
4550 man.hash.addOptionalEmitLoc(comp.emit_analysis);
4551 man.hash.addOptionalEmitLoc(comp.emit_docs);
4552 man.hash.add(comp.test_evented_io);
4553 man.hash.addOptionalBytes(comp.test_filter);
4554 man.hash.addOptionalBytes(comp.test_name_prefix);
4555 man.hash.addListOfBytes(comp.clang_argv);
4556
4557 // Capture the state in case we come back from this branch where the hash doesn't match.
4558 const prev_hash_state = man.hash.peekBin();
4559 const input_file_count = man.files.items.len;
4560
4561 const hit = man.hit() catch |err| {
4562 const i = man.failed_file_index orelse return err;
4563 const file_path = man.files.items[i].path orelse return err;
4564 fatal("unable to build stage1 zig object: {s}: {s}", .{ @errorName(err), file_path });
4565 };
4566 if (hit) {
4567 const digest = man.final();
4568
4569 // We use an extra hex-encoded byte here to store some flags.
4570 var prev_digest_buf: [digest.len + 2]u8 = undefined;
4571 const prev_digest: []u8 = Cache.readSmallFile(
4572 directory.handle,
4573 id_symlink_basename,
4574 &prev_digest_buf,
4575 ) catch |err| blk: {
4576 log.debug("stage1 {s} new_digest={s} error: {s}", .{
4577 mod.main_pkg.root_src_path,
4578 std.fmt.fmtSliceHexLower(&digest),
4579 @errorName(err),
4580 });
4581 // Handle this as a cache miss.
4582 break :blk prev_digest_buf[0..0];
4583 };
4584 if (prev_digest.len >= digest.len + 2) hit: {
4585 if (!mem.eql(u8, prev_digest[0..digest.len], &digest))
4586 break :hit;
4587
4588 log.debug("stage1 {s} digest={s} match - skipping invocation", .{
4589 mod.main_pkg.root_src_path,
4590 std.fmt.fmtSliceHexLower(&digest),
4591 });
4592 var flags_bytes: [1]u8 = undefined;
4593 _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch {
4594 log.warn("bad cache stage1 digest: '{s}'", .{std.fmt.fmtSliceHexLower(prev_digest)});
4595 break :hit;
4596 };
4597
4598 if (directory.handle.readFileAlloc(comp.gpa, libs_txt_basename, 10 * 1024 * 1024)) |libs_txt| {
4599 var it = mem.tokenize(u8, libs_txt, "\n");
4600 while (it.next()) |lib_name| {
4601 try comp.stage1AddLinkLib(lib_name);
4602 }
4603 } else |err| switch (err) {
4604 error.FileNotFound => {}, // That's OK, it just means 0 libs.
4605 else => {
4606 log.warn("unable to read cached list of link libs: {s}", .{@errorName(err)});
4607 break :hit;
4608 },
4609 }
4610 comp.stage1_lock = man.toOwnedLock();
4611 mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]);
4612 return;
4613 }
4614 log.debug("stage1 {s} prev_digest={s} new_digest={s}", .{
4615 mod.main_pkg.root_src_path,
4616 std.fmt.fmtSliceHexLower(prev_digest),
4617 std.fmt.fmtSliceHexLower(&digest),
4618 });
4619 man.unhit(prev_hash_state, input_file_count);
4620 }
4621
4622 // We are about to change the output file to be different, so we invalidate the build hash now.
4623 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
4624 error.FileNotFound => {},
4625 else => |e| return e,
4626 };
4627
46284837 const stage2_target = try arena.create(stage1.Stage2Target);
46294838 stage2_target.* = .{
46304839 .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
......@@ -4637,9 +4846,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
46374846 .llvm_target_abi = if (target_util.llvmMachineAbi(target)) |s| s.ptr else null,
46384847 };
46394848
4640 comp.stage1_cache_manifest = &man;
4641
46424849 const main_pkg_path = mod.main_pkg.root_src_directory.path orelse "";
4850 const builtin_pkg = mod.main_pkg.table.get("builtin").?;
4851 const builtin_zig_path = try builtin_pkg.root_src_directory.join(arena, &.{builtin_pkg.root_src_path});
46434852
46444853 const stage1_module = stage1.create(
46454854 @enumToInt(comp.bin_file.options.optimize_mode),
......@@ -4740,19 +4949,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
47404949 .have_dllmain_crt_startup = false,
47414950 };
47424951
4743 const inferred_lib_start_index = comp.bin_file.options.system_libs.count();
47444952 stage1_module.build_object();
47454953
4746 if (comp.bin_file.options.system_libs.count() > inferred_lib_start_index) {
4747 // We need to save the inferred link libs to the cache, otherwise if we get a cache hit
4748 // next time we will be missing these libs.
4749 var libs_txt = std.ArrayList(u8).init(arena);
4750 for (comp.bin_file.options.system_libs.keys()[inferred_lib_start_index..]) |key| {
4751 try libs_txt.writer().print("{s}\n", .{key});
4752 }
4753 try directory.handle.writeFile(libs_txt_basename, libs_txt.items);
4754 }
4755
47564954 mod.stage1_flags = .{
47574955 .have_c_main = stage1_module.have_c_main,
47584956 .have_winmain = stage1_module.have_winmain,
......@@ -4763,34 +4961,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
47634961 };
47644962
47654963 stage1_module.destroy();
4766
4767 const digest = man.final();
4768
4769 // Update the small file with the digest. If it fails we can continue; it only
4770 // means that the next invocation will have an unnecessary cache miss.
4771 const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);
4772 log.debug("stage1 {s} final digest={s} flags={x}", .{
4773 mod.main_pkg.root_src_path, std.fmt.fmtSliceHexLower(&digest), stage1_flags_byte,
4774 });
4775 var digest_plus_flags: [digest.len + 2]u8 = undefined;
4776 digest_plus_flags[0..digest.len].* = digest;
4777 assert(std.fmt.formatIntBuf(digest_plus_flags[digest.len..], stage1_flags_byte, 16, .lower, .{
4778 .width = 2,
4779 .fill = '0',
4780 }) == 2);
4781 log.debug("saved digest + flags: '{s}' (byte = {}) have_winmain_crt_startup={}", .{
4782 digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup,
4783 });
4784 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest_plus_flags) catch |err| {
4785 log.warn("failed to save stage1 hash digest file: {s}", .{@errorName(err)});
4786 };
4787 // Failure here only means an unnecessary cache miss.
4788 man.writeManifest() catch |err| {
4789 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
4790 };
4791 // We hang on to this lock so that the output file path can be used without
4792 // other processes clobbering it.
4793 comp.stage1_lock = man.toOwnedLock();
47944964}
47954965
47964966fn stage1LocPath(arena: Allocator, opt_loc: ?EmitLoc, cache_directory: Directory) ![]const u8 {
......@@ -4862,6 +5032,7 @@ pub fn build_crt_file(
48625032 .local_cache_directory = comp.global_cache_directory,
48635033 .global_cache_directory = comp.global_cache_directory,
48645034 .zig_lib_directory = comp.zig_lib_directory,
5035 .cache_mode = .whole,
48655036 .target = target,
48665037 .root_name = root_name,
48675038 .main_pkg = null,
src/Module.zig+1-1
......@@ -33,7 +33,7 @@ const build_options = @import("build_options");
3333gpa: Allocator,
3434comp: *Compilation,
3535
36/// Where our incremental compilation metadata serialization will go.
36/// Where build artifacts and incremental compilation metadata serialization go.
3737zig_cache_artifact_directory: Compilation.Directory,
3838/// Pointer to externally managed resource.
3939root_pkg: *Package,
src/glibc.zig+1
......@@ -1062,6 +1062,7 @@ fn buildSharedLib(
10621062 .local_cache_directory = zig_cache_directory,
10631063 .global_cache_directory = comp.global_cache_directory,
10641064 .zig_lib_directory = comp.zig_lib_directory,
1065 .cache_mode = .whole,
10651066 .target = comp.getTarget(),
10661067 .root_name = lib.name,
10671068 .main_pkg = null,
src/libcxx.zig+2
......@@ -177,6 +177,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
177177 .local_cache_directory = comp.global_cache_directory,
178178 .global_cache_directory = comp.global_cache_directory,
179179 .zig_lib_directory = comp.zig_lib_directory,
180 .cache_mode = .whole,
180181 .target = target,
181182 .root_name = root_name,
182183 .main_pkg = null,
......@@ -309,6 +310,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
309310 .local_cache_directory = comp.global_cache_directory,
310311 .global_cache_directory = comp.global_cache_directory,
311312 .zig_lib_directory = comp.zig_lib_directory,
313 .cache_mode = .whole,
312314 .target = target,
313315 .root_name = root_name,
314316 .main_pkg = null,
src/libtsan.zig+1
......@@ -199,6 +199,7 @@ pub fn buildTsan(comp: *Compilation) !void {
199199 .local_cache_directory = comp.global_cache_directory,
200200 .global_cache_directory = comp.global_cache_directory,
201201 .zig_lib_directory = comp.zig_lib_directory,
202 .cache_mode = .whole,
202203 .target = target,
203204 .root_name = root_name,
204205 .main_pkg = null,
src/libunwind.zig+1
......@@ -101,6 +101,7 @@ pub fn buildStaticLib(comp: *Compilation) !void {
101101 .local_cache_directory = comp.global_cache_directory,
102102 .global_cache_directory = comp.global_cache_directory,
103103 .zig_lib_directory = comp.zig_lib_directory,
104 .cache_mode = .whole,
104105 .target = target,
105106 .root_name = root_name,
106107 .main_pkg = null,
src/link.zig+5-3
......@@ -22,6 +22,8 @@ pub const SystemLib = struct {
2222 needed: bool = false,
2323};
2424
25pub const CacheMode = enum { incremental, whole };
26
2527pub fn hashAddSystemLibs(
2628 hh: *Cache.HashHelper,
2729 hm: std.StringArrayHashMapUnmanaged(SystemLib),
......@@ -44,10 +46,9 @@ pub const Emit = struct {
4446};
4547
4648pub const Options = struct {
47 /// This is `null` when -fno-emit-bin is used. When `openPath` or `flush` is called,
48 /// it will have already been null-checked.
49 /// This is `null` when `-fno-emit-bin` is used.
4950 emit: ?Emit,
50 /// This is `null` not building a Windows DLL, or when -fno-emit-implib is used.
51 /// This is `null` not building a Windows DLL, or when `-fno-emit-implib` is used.
5152 implib_emit: ?Emit,
5253 target: std.Target,
5354 output_mode: std.builtin.OutputMode,
......@@ -70,6 +71,7 @@ pub const Options = struct {
7071 entry_addr: ?u64 = null,
7172 stack_size_override: ?u64,
7273 image_base_override: ?u64,
74 cache_mode: CacheMode,
7375 include_compiler_rt: bool,
7476 /// Set to `true` to omit debug info.
7577 strip: bool,
src/link/Coff.zig+2
......@@ -920,6 +920,8 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
920920 man = comp.cache_parent.obtain();
921921 self.base.releaseLock();
922922
923 comptime assert(Compilation.link_hash_implementation_version == 1);
924
923925 try man.addListOfFiles(self.base.options.objects);
924926 for (comp.c_object_table.keys()) |key| {
925927 _ = try man.addFile(key.status.success.object_path, null);
src/link/Elf.zig+2
......@@ -1357,6 +1357,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13571357 // We are about to obtain this lock, so here we give other processes a chance first.
13581358 self.base.releaseLock();
13591359
1360 comptime assert(Compilation.link_hash_implementation_version == 1);
1361
13601362 try man.addOptionalFile(self.base.options.linker_script);
13611363 try man.addOptionalFile(self.base.options.version_script);
13621364 try man.addListOfFiles(self.base.options.objects);
src/link/MachO.zig+2
......@@ -466,6 +466,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
466466 // We are about to obtain this lock, so here we give other processes a chance first.
467467 self.base.releaseLock();
468468
469 comptime assert(Compilation.link_hash_implementation_version == 1);
470
469471 try man.addListOfFiles(self.base.options.objects);
470472 for (comp.c_object_table.keys()) |key| {
471473 _ = try man.addFile(key.status.success.object_path, null);
src/link/Wasm.zig+2
......@@ -1094,6 +1094,8 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
10941094 // We are about to obtain this lock, so here we give other processes a chance first.
10951095 self.base.releaseLock();
10961096
1097 comptime assert(Compilation.link_hash_implementation_version == 1);
1098
10971099 try man.addListOfFiles(self.base.options.objects);
10981100 for (comp.c_object_table.keys()) |key| {
10991101 _ = try man.addFile(key.status.success.object_path, null);
src/main.zig-4
......@@ -2578,10 +2578,6 @@ fn buildOutputType(
25782578 };
25792579 try comp.makeBinFileExecutable();
25802580
2581 if (build_options.is_stage1 and comp.stage1_lock != null and watch) {
2582 warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});
2583 }
2584
25852581 if (test_exec_args.items.len == 0 and object_format == .c) default_exec_args: {
25862582 // Default to using `zig run` to execute the produced .c code from `zig test`.
25872583 const c_code_loc = emit_bin_loc orelse break :default_exec_args;
src/musl.zig+1
......@@ -203,6 +203,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
203203 const sub_compilation = try Compilation.create(comp.gpa, .{
204204 .local_cache_directory = comp.global_cache_directory,
205205 .global_cache_directory = comp.global_cache_directory,
206 .cache_mode = .whole,
206207 .zig_lib_directory = comp.zig_lib_directory,
207208 .target = target,
208209 .root_name = "c",
src/stage1.zig+1-1
......@@ -458,7 +458,7 @@ export fn stage2_fetch_file(
458458 const comp = @intToPtr(*Compilation, stage1.userdata);
459459 const file_path = path_ptr[0..path_len];
460460 const max_file_size = std.math.maxInt(u32);
461 const contents = comp.stage1_cache_manifest.addFilePostFetch(file_path, max_file_size) catch return null;
461 const contents = comp.whole_cache_manifest.?.addFilePostFetch(file_path, max_file_size) catch return null;
462462 result_len.* = contents.len;
463463 // TODO https://github.com/ziglang/zig/issues/3328#issuecomment-716749475
464464 if (contents.len == 0) return @intToPtr(?[*]const u8, 0x1);