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,...@@ -41,8 +41,8 @@ gpa: Allocator,
41arena_state: std.heap.ArenaAllocator.State,41arena_state: std.heap.ArenaAllocator.State,
42bin_file: *link.File,42bin_file: *link.File,
43c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},43c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
44stage1_lock: ?Cache.Lock = null,44/// This is a pointer to a local variable inside `update()`.
45stage1_cache_manifest: *Cache.Manifest = undefined,45whole_cache_manifest: ?*Cache.Manifest = null,
4646
47link_error_flags: link.File.ErrorFlags = .{},47link_error_flags: link.File.ErrorFlags = .{},
4848
...@@ -98,6 +98,9 @@ clang_argv: []const []const u8,...@@ -98,6 +98,9 @@ clang_argv: []const []const u8,
98cache_parent: *Cache,98cache_parent: *Cache,
99/// Path to own executable for invoking `zig clang`.99/// Path to own executable for invoking `zig clang`.
100self_exe_path: ?[]const u8,100self_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,
101zig_lib_directory: Directory,104zig_lib_directory: Directory,
102local_cache_directory: Directory,105local_cache_directory: Directory,
103global_cache_directory: Directory,106global_cache_directory: Directory,
...@@ -612,6 +615,15 @@ pub const Directory = struct {...@@ -612,6 +615,15 @@ pub const Directory = struct {
612 return std.fs.path.joinZ(allocator, paths);615 return std.fs.path.joinZ(allocator, paths);
613 }616 }
614 }617 }
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 }
615};627};
616628
617pub const EmitLoc = struct {629pub const EmitLoc = struct {
...@@ -631,6 +643,7 @@ pub const ClangPreprocessorMode = enum {...@@ -631,6 +643,7 @@ pub const ClangPreprocessorMode = enum {
631};643};
632644
633pub const SystemLib = link.SystemLib;645pub const SystemLib = link.SystemLib;
646pub const CacheMode = link.CacheMode;
634647
635pub const InitOptions = struct {648pub const InitOptions = struct {
636 zig_lib_directory: Directory,649 zig_lib_directory: Directory,
...@@ -668,6 +681,7 @@ pub const InitOptions = struct {...@@ -668,6 +681,7 @@ pub const InitOptions = struct {
668 /// is externally modified - essentially anything other than zig-cache - then681 /// is externally modified - essentially anything other than zig-cache - then
669 /// this flag would be set to disable this machinery to avoid false positives.682 /// this flag would be set to disable this machinery to avoid false positives.
670 disable_lld_caching: bool = false,683 disable_lld_caching: bool = false,
684 cache_mode: CacheMode = .incremental,
671 object_format: ?std.Target.ObjectFormat = null,685 object_format: ?std.Target.ObjectFormat = null,
672 optimize_mode: std.builtin.Mode = .Debug,686 optimize_mode: std.builtin.Mode = .Debug,
673 keep_source_files_loaded: bool = false,687 keep_source_files_loaded: bool = false,
...@@ -885,6 +899,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -885,6 +899,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
885 break :blk build_options.is_stage1;899 break :blk build_options.is_stage1;
886 };900 };
887901
902 const cache_mode = if (use_stage1) CacheMode.whole else options.cache_mode;
903
888 // Make a decision on whether to use LLVM or our own backend.904 // Make a decision on whether to use LLVM or our own backend.
889 const use_llvm = build_options.have_llvm and blk: {905 const use_llvm = build_options.have_llvm and blk: {
890 if (options.use_llvm) |explicit|906 if (options.use_llvm) |explicit|
...@@ -1219,18 +1235,26 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1219,18 +1235,26 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1219 // modified between incremental updates.1235 // modified between incremental updates.
1220 var hash = cache.hash;1236 var hash = cache.hash;
12211237
1222 // Here we put the root source file path name, but *not* with addFile. We want the1238 switch (cache_mode) {
1223 // hash to be the same regardless of the contents of the source file, because1239 .incremental => {
1224 // incremental compilation will handle it, but we do want to namespace different1240 // Here we put the root source file path name, but *not* with addFile.
1225 // source file names because they are likely different compilations and therefore this1241 // We want the hash to be the same regardless of the contents of the
1226 // would be likely to cause cache hits.1242 // source file, because incremental compilation will handle it, but we
1227 hash.addBytes(main_pkg.root_src_path);1243 // do want to namespace different source file names because they are
1228 hash.addOptionalBytes(main_pkg.root_src_directory.path);1244 // likely different compilations and therefore this would be likely to
1229 {1245 // cause cache hits.
1230 var local_arena = std.heap.ArenaAllocator.init(gpa);1246 hash.addBytes(main_pkg.root_src_path);
1231 defer local_arena.deinit();1247 hash.addOptionalBytes(main_pkg.root_src_directory.path);
1232 var seen_table = std.AutoHashMap(*Package, void).init(local_arena.allocator());1248 {
1233 try addPackageTableToCacheHash(&hash, &local_arena, main_pkg.table, &seen_table, .path_bytes);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 },
1234 }1258 }
1235 hash.add(valgrind);1259 hash.add(valgrind);
1236 hash.add(single_threaded);1260 hash.add(single_threaded);
...@@ -1238,9 +1262,35 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1238,9 +1262,35 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1238 hash.add(use_llvm);1262 hash.add(use_llvm);
1239 hash.add(dll_export_fns);1263 hash.add(dll_export_fns);
1240 hash.add(options.is_test);1264 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);
1241 hash.add(options.skip_linker_dependencies);1268 hash.add(options.skip_linker_dependencies);
1242 hash.add(options.parent_compilation_link_libc);1269 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
1244 const digest = hash.final();1294 const digest = hash.final();
1245 const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });1295 const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
1246 var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{});1296 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 {...@@ -1374,6 +1424,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1374 };1424 };
1375 }1425 }
13761426
1427 switch (cache_mode) {
1428 .whole => break :blk null,
1429 .incremental => {},
1430 }
1431
1377 if (module) |zm| {1432 if (module) |zm| {
1378 break :blk link.Emit{1433 break :blk link.Emit{
1379 .directory = zm.zig_cache_artifact_directory,1434 .directory = zm.zig_cache_artifact_directory,
...@@ -1425,6 +1480,18 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1425,6 +1480,18 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1425 };1480 };
1426 };1481 };
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
1428 var system_libs: std.StringArrayHashMapUnmanaged(SystemLib) = .{};1495 var system_libs: std.StringArrayHashMapUnmanaged(SystemLib) = .{};
1429 errdefer system_libs.deinit(gpa);1496 errdefer system_libs.deinit(gpa);
1430 try system_libs.ensureTotalCapacity(gpa, options.system_lib_names.len);1497 try system_libs.ensureTotalCapacity(gpa, options.system_lib_names.len);
...@@ -1512,7 +1579,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1512,7 +1579,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1512 .skip_linker_dependencies = options.skip_linker_dependencies,1579 .skip_linker_dependencies = options.skip_linker_dependencies,
1513 .parent_compilation_link_libc = options.parent_compilation_link_libc,1580 .parent_compilation_link_libc = options.parent_compilation_link_libc,
1514 .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os,1581 .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,
1516 .subsystem = options.subsystem,1584 .subsystem = options.subsystem,
1517 .is_test = options.is_test,1585 .is_test = options.is_test,
1518 .wasi_exec_model = wasi_exec_model,1586 .wasi_exec_model = wasi_exec_model,
...@@ -1529,6 +1597,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1529,6 +1597,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1529 .local_cache_directory = options.local_cache_directory,1597 .local_cache_directory = options.local_cache_directory,
1530 .global_cache_directory = options.global_cache_directory,1598 .global_cache_directory = options.global_cache_directory,
1531 .bin_file = bin_file,1599 .bin_file = bin_file,
1600 .whole_bin_basename = whole_bin_basename,
1532 .emit_asm = options.emit_asm,1601 .emit_asm = options.emit_asm,
1533 .emit_llvm_ir = options.emit_llvm_ir,1602 .emit_llvm_ir = options.emit_llvm_ir,
1534 .emit_llvm_bc = options.emit_llvm_bc,1603 .emit_llvm_bc = options.emit_llvm_bc,
...@@ -1725,20 +1794,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1725,20 +1794,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1725 return comp;1794 return comp;
1726}1795}
17271796
1728fn releaseStage1Lock(comp: *Compilation) void {
1729 if (comp.stage1_lock) |*lock| {
1730 lock.release();
1731 comp.stage1_lock = null;
1732 }
1733}
1734
1735pub fn destroy(self: *Compilation) void {1797pub fn destroy(self: *Compilation) void {
1736 const optional_module = self.bin_file.options.module;1798 const optional_module = self.bin_file.options.module;
1737 self.bin_file.destroy();1799 self.bin_file.destroy();
1738 if (optional_module) |module| module.deinit();1800 if (optional_module) |module| module.deinit();
17391801
1740 self.releaseStage1Lock();
1741
1742 const gpa = self.gpa;1802 const gpa = self.gpa;
1743 self.work_queue.deinit();1803 self.work_queue.deinit();
1744 self.anon_work_queue.deinit();1804 self.anon_work_queue.deinit();
...@@ -1815,22 +1875,135 @@ pub fn getTarget(self: Compilation) Target {...@@ -1815,22 +1875,135 @@ pub fn getTarget(self: Compilation) Target {
1815 return self.bin_file.options.target;1875 return self.bin_file.options.target;
1816}1876}
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
1818/// Detect changes to source files, perform semantic analysis, and update the output files.1902/// 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 {
1820 const tracy_trace = trace(@src());1904 const tracy_trace = trace(@src());
1821 defer tracy_trace.end();1905 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
1825 // For compiling C objects, we rely on the cache hash system to avoid duplicating work.1998 // For compiling C objects, we rely on the cache hash system to avoid duplicating work.
1826 // Add a Job for each C object.1999 // Add a Job for each C object.
1827 try self.c_object_work_queue.ensureUnusedCapacity(self.c_object_table.count());2000 try comp.c_object_work_queue.ensureUnusedCapacity(comp.c_object_table.count());
1828 for (self.c_object_table.keys()) |key| {2001 for (comp.c_object_table.keys()) |key| {
1829 self.c_object_work_queue.writeItemAssumeCapacity(key);2002 comp.c_object_work_queue.writeItemAssumeCapacity(key);
1830 }2003 }
18312004
1832 const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_stage1;2005 const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1;
1833 if (self.bin_file.options.module) |module| {2006 if (comp.bin_file.options.module) |module| {
1834 module.compile_log_text.shrinkAndFree(module.gpa, 0);2007 module.compile_log_text.shrinkAndFree(module.gpa, 0);
1835 module.generation += 1;2008 module.generation += 1;
18362009
...@@ -1845,7 +2018,7 @@ pub fn update(self: *Compilation) !void {...@@ -1845,7 +2018,7 @@ pub fn update(self: *Compilation) !void {
1845 // import_table here.2018 // import_table here.
1846 // Likewise, in the case of `zig test`, the test runner is the root source file,2019 // Likewise, in the case of `zig test`, the test runner is the root source file,
1847 // and so there is nothing to import the main file.2020 // 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) {
1849 _ = try module.importPkg(module.main_pkg);2022 _ = try module.importPkg(module.main_pkg);
1850 }2023 }
18512024
...@@ -1854,34 +2027,34 @@ pub fn update(self: *Compilation) !void {...@@ -1854,34 +2027,34 @@ pub fn update(self: *Compilation) !void {
1854 // to update it.2027 // to update it.
1855 // We still want AstGen work items for stage1 so that we expose compile errors2028 // We still want AstGen work items for stage1 so that we expose compile errors
1856 // that are implemented in stage2 but not stage1.2029 // 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());
1858 for (module.import_table.values()) |value| {2031 for (module.import_table.values()) |value| {
1859 self.astgen_work_queue.writeItemAssumeCapacity(value);2032 comp.astgen_work_queue.writeItemAssumeCapacity(value);
1860 }2033 }
18612034
1862 if (!use_stage1) {2035 if (!use_stage1) {
1863 // Put a work item in for checking if any files used with `@embedFile` changed.2036 // Put a work item in for checking if any files used with `@embedFile` changed.
1864 {2037 {
1865 try self.embed_file_work_queue.ensureUnusedCapacity(module.embed_table.count());2038 try comp.embed_file_work_queue.ensureUnusedCapacity(module.embed_table.count());
1866 var it = module.embed_table.iterator();2039 var it = module.embed_table.iterator();
1867 while (it.next()) |entry| {2040 while (it.next()) |entry| {
1868 const embed_file = entry.value_ptr.*;2041 const embed_file = entry.value_ptr.*;
1869 self.embed_file_work_queue.writeItemAssumeCapacity(embed_file);2042 comp.embed_file_work_queue.writeItemAssumeCapacity(embed_file);
1870 }2043 }
1871 }2044 }
18722045
1873 try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg });2046 try comp.work_queue.writeItem(.{ .analyze_pkg = std_pkg });
1874 if (self.bin_file.options.is_test) {2047 if (comp.bin_file.options.is_test) {
1875 try self.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg });2048 try comp.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg });
1876 }2049 }
1877 }2050 }
1878 }2051 }
18792052
1880 try self.performAllTheWork();2053 try comp.performAllTheWork();
18812054
1882 if (!use_stage1) {2055 if (!use_stage1) {
1883 if (self.bin_file.options.module) |module| {2056 if (comp.bin_file.options.module) |module| {
1884 if (self.bin_file.options.is_test and self.totalErrorCount() == 0) {2057 if (comp.bin_file.options.is_test and comp.totalErrorCount() == 0) {
1885 // The `test_functions` decl has been intentionally postponed until now,2058 // The `test_functions` decl has been intentionally postponed until now,
1886 // at which point we must populate it with the list of test functions that2059 // at which point we must populate it with the list of test functions that
1887 // have been discovered and not filtered out.2060 // have been discovered and not filtered out.
...@@ -1910,39 +2083,184 @@ pub fn update(self: *Compilation) !void {...@@ -1910,39 +2083,184 @@ pub fn update(self: *Compilation) !void {
1910 }2083 }
1911 }2084 }
19122085
1913 if (self.totalErrorCount() != 0) {2086 if (comp.totalErrorCount() != 0) {
1914 // Skip flushing.2087 // Skip flushing.
1915 self.link_error_flags = .{};2088 comp.link_error_flags = .{};
1916 return;2089 return;
1917 }2090 }
19182091
1919 // This is needed before reading the error flags.2092 // This is needed before reading the error flags.
1920 try self.bin_file.flush(self);2093 try comp.bin_file.flush(comp);
1921 self.link_error_flags = self.bin_file.errorFlags();2094 comp.link_error_flags = comp.bin_file.errorFlags();
19222095
1923 if (!use_stage1) {2096 if (!use_stage1) {
1924 if (self.bin_file.options.module) |module| {2097 if (comp.bin_file.options.module) |module| {
1925 try link.File.C.flushEmitH(module);2098 try link.File.C.flushEmitH(module);
1926 }2099 }
1927 }2100 }
19282101
1929 // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and2102 // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and
1930 // -femit-asm to handle, in the case of C objects.2103 // -femit-asm to handle, in the case of C objects.
1931 self.emitOthers();2104 comp.emitOthers();
19322105
1933 // If there are any errors, we anticipate the source files being loaded2106 // If there are any errors, we anticipate the source files being loaded
1934 // to report error messages. Otherwise we unload all source files to save memory.2107 // to report error messages. Otherwise we unload all source files to save memory.
1935 // The ZIR needs to stay loaded in memory because (1) Decl objects contain references2108 // The ZIR needs to stay loaded in memory because (1) Decl objects contain references
1936 // to it, and (2) generic instantiations, comptime calls, inline calls will need2109 // to it, and (2) generic instantiations, comptime calls, inline calls will need
1937 // to reference the ZIR.2110 // to reference the ZIR.
1938 if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {2111 if (!comp.keep_source_files_loaded) {
1939 if (self.bin_file.options.module) |module| {2112 if (comp.bin_file.options.module) |module| {
1940 for (module.import_table.values()) |file| {2113 for (module.import_table.values()) |file| {
1941 file.unloadTree(self.gpa);2114 file.unloadTree(comp.gpa);
1942 file.unloadSource(self.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.?);
1943 }2234 }
1944 }2235 }
2236 man.hash.addOptionalBytes(comp.bin_file.options.dynamic_linker);
1945 }2237 }
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);
1946}2264}
19472265
1948fn emitOthers(comp: *Compilation) void {2266fn emitOthers(comp: *Compilation) void {
...@@ -2988,7 +3306,9 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {...@@ -2988,7 +3306,9 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
29883306
2989 const dep_basename = std.fs.path.basename(out_dep_path);3307 const dep_basename = std.fs.path.basename(out_dep_path);
2990 try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);3308 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
2993 const digest = man.final();3313 const digest = man.final();
2994 const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });3314 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...@@ -3351,13 +3671,13 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
3351 };3671 };
3352}3672}
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 {
3355 const s = std.fs.path.sep_str;3675 const s = std.fs.path.sep_str;
3356 const rand_int = std.crypto.random.int(u64);3676 const rand_int = std.crypto.random.int(u64);
3357 if (comp.local_cache_directory.path) |p| {3677 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 });
3359 } else {3679 } 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 });
3361 }3681 }
3362}3682}
33633683
...@@ -4424,6 +4744,7 @@ fn buildOutputFromZig(...@@ -4424,6 +4744,7 @@ fn buildOutputFromZig(
4424 .global_cache_directory = comp.global_cache_directory,4744 .global_cache_directory = comp.global_cache_directory,
4425 .local_cache_directory = comp.global_cache_directory,4745 .local_cache_directory = comp.global_cache_directory,
4426 .zig_lib_directory = comp.zig_lib_directory,4746 .zig_lib_directory = comp.zig_lib_directory,
4747 .cache_mode = .whole,
4427 .target = target,4748 .target = target,
4428 .root_name = root_name,4749 .root_name = root_name,
4429 .main_pkg = &main_pkg,4750 .main_pkg = &main_pkg,
...@@ -4501,10 +4822,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -4501,10 +4822,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
4501 mod.main_pkg.root_src_path,4822 mod.main_pkg.root_src_path,
4502 });4823 });
4503 const zig_lib_dir = comp.zig_lib_directory.path.?;4824 const zig_lib_dir = comp.zig_lib_directory.path.?;
4504 const builtin_zig_path = try directory.join(arena, &[_][]const u8{"builtin.zig"});
4505 const target = comp.getTarget();4825 const target = comp.getTarget();
4506 const id_symlink_basename = "stage1.id";
4507 const libs_txt_basename = "libs.txt";
45084826
4509 // The include_compiler_rt stored in the bin file options here means that we need4827 // The include_compiler_rt stored in the bin file options here means that we need
4510 // compiler-rt symbols *somehow*. However, in the context of using the stage1 backend4828 // 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...@@ -4516,115 +4834,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
4516 const include_compiler_rt = comp.bin_file.options.output_mode == .Obj and4834 const include_compiler_rt = comp.bin_file.options.output_mode == .Obj and
4517 comp.bin_file.options.include_compiler_rt;4835 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
4628 const stage2_target = try arena.create(stage1.Stage2Target);4837 const stage2_target = try arena.create(stage1.Stage2Target);
4629 stage2_target.* = .{4838 stage2_target.* = .{
4630 .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch4839 .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
...@@ -4637,9 +4846,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -4637,9 +4846,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
4637 .llvm_target_abi = if (target_util.llvmMachineAbi(target)) |s| s.ptr else null,4846 .llvm_target_abi = if (target_util.llvmMachineAbi(target)) |s| s.ptr else null,
4638 };4847 };
46394848
4640 comp.stage1_cache_manifest = &man;
4641
4642 const main_pkg_path = mod.main_pkg.root_src_directory.path orelse "";4849 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
4644 const stage1_module = stage1.create(4853 const stage1_module = stage1.create(
4645 @enumToInt(comp.bin_file.options.optimize_mode),4854 @enumToInt(comp.bin_file.options.optimize_mode),
...@@ -4740,19 +4949,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -4740,19 +4949,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
4740 .have_dllmain_crt_startup = false,4949 .have_dllmain_crt_startup = false,
4741 };4950 };
47424951
4743 const inferred_lib_start_index = comp.bin_file.options.system_libs.count();
4744 stage1_module.build_object();4952 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
4756 mod.stage1_flags = .{4954 mod.stage1_flags = .{
4757 .have_c_main = stage1_module.have_c_main,4955 .have_c_main = stage1_module.have_c_main,
4758 .have_winmain = stage1_module.have_winmain,4956 .have_winmain = stage1_module.have_winmain,
...@@ -4763,34 +4961,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -4763,34 +4961,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
4763 };4961 };
47644962
4765 stage1_module.destroy();4963 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();
4794}4964}
47954965
4796fn stage1LocPath(arena: Allocator, opt_loc: ?EmitLoc, cache_directory: Directory) ![]const u8 {4966fn stage1LocPath(arena: Allocator, opt_loc: ?EmitLoc, cache_directory: Directory) ![]const u8 {
...@@ -4862,6 +5032,7 @@ pub fn build_crt_file(...@@ -4862,6 +5032,7 @@ pub fn build_crt_file(
4862 .local_cache_directory = comp.global_cache_directory,5032 .local_cache_directory = comp.global_cache_directory,
4863 .global_cache_directory = comp.global_cache_directory,5033 .global_cache_directory = comp.global_cache_directory,
4864 .zig_lib_directory = comp.zig_lib_directory,5034 .zig_lib_directory = comp.zig_lib_directory,
5035 .cache_mode = .whole,
4865 .target = target,5036 .target = target,
4866 .root_name = root_name,5037 .root_name = root_name,
4867 .main_pkg = null,5038 .main_pkg = null,
src/Module.zig+1-1
...@@ -33,7 +33,7 @@ const build_options = @import("build_options");...@@ -33,7 +33,7 @@ const build_options = @import("build_options");
33gpa: Allocator,33gpa: Allocator,
34comp: *Compilation,34comp: *Compilation,
3535
36/// Where our incremental compilation metadata serialization will go.36/// Where build artifacts and incremental compilation metadata serialization go.
37zig_cache_artifact_directory: Compilation.Directory,37zig_cache_artifact_directory: Compilation.Directory,
38/// Pointer to externally managed resource.38/// Pointer to externally managed resource.
39root_pkg: *Package,39root_pkg: *Package,
src/glibc.zig+1
...@@ -1062,6 +1062,7 @@ fn buildSharedLib(...@@ -1062,6 +1062,7 @@ fn buildSharedLib(
1062 .local_cache_directory = zig_cache_directory,1062 .local_cache_directory = zig_cache_directory,
1063 .global_cache_directory = comp.global_cache_directory,1063 .global_cache_directory = comp.global_cache_directory,
1064 .zig_lib_directory = comp.zig_lib_directory,1064 .zig_lib_directory = comp.zig_lib_directory,
1065 .cache_mode = .whole,
1065 .target = comp.getTarget(),1066 .target = comp.getTarget(),
1066 .root_name = lib.name,1067 .root_name = lib.name,
1067 .main_pkg = null,1068 .main_pkg = null,
src/libcxx.zig+2
...@@ -177,6 +177,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {...@@ -177,6 +177,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
177 .local_cache_directory = comp.global_cache_directory,177 .local_cache_directory = comp.global_cache_directory,
178 .global_cache_directory = comp.global_cache_directory,178 .global_cache_directory = comp.global_cache_directory,
179 .zig_lib_directory = comp.zig_lib_directory,179 .zig_lib_directory = comp.zig_lib_directory,
180 .cache_mode = .whole,
180 .target = target,181 .target = target,
181 .root_name = root_name,182 .root_name = root_name,
182 .main_pkg = null,183 .main_pkg = null,
...@@ -309,6 +310,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {...@@ -309,6 +310,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
309 .local_cache_directory = comp.global_cache_directory,310 .local_cache_directory = comp.global_cache_directory,
310 .global_cache_directory = comp.global_cache_directory,311 .global_cache_directory = comp.global_cache_directory,
311 .zig_lib_directory = comp.zig_lib_directory,312 .zig_lib_directory = comp.zig_lib_directory,
313 .cache_mode = .whole,
312 .target = target,314 .target = target,
313 .root_name = root_name,315 .root_name = root_name,
314 .main_pkg = null,316 .main_pkg = null,
src/libtsan.zig+1
...@@ -199,6 +199,7 @@ pub fn buildTsan(comp: *Compilation) !void {...@@ -199,6 +199,7 @@ pub fn buildTsan(comp: *Compilation) !void {
199 .local_cache_directory = comp.global_cache_directory,199 .local_cache_directory = comp.global_cache_directory,
200 .global_cache_directory = comp.global_cache_directory,200 .global_cache_directory = comp.global_cache_directory,
201 .zig_lib_directory = comp.zig_lib_directory,201 .zig_lib_directory = comp.zig_lib_directory,
202 .cache_mode = .whole,
202 .target = target,203 .target = target,
203 .root_name = root_name,204 .root_name = root_name,
204 .main_pkg = null,205 .main_pkg = null,
src/libunwind.zig+1
...@@ -101,6 +101,7 @@ pub fn buildStaticLib(comp: *Compilation) !void {...@@ -101,6 +101,7 @@ pub fn buildStaticLib(comp: *Compilation) !void {
101 .local_cache_directory = comp.global_cache_directory,101 .local_cache_directory = comp.global_cache_directory,
102 .global_cache_directory = comp.global_cache_directory,102 .global_cache_directory = comp.global_cache_directory,
103 .zig_lib_directory = comp.zig_lib_directory,103 .zig_lib_directory = comp.zig_lib_directory,
104 .cache_mode = .whole,
104 .target = target,105 .target = target,
105 .root_name = root_name,106 .root_name = root_name,
106 .main_pkg = null,107 .main_pkg = null,
src/link.zig+5-3
...@@ -22,6 +22,8 @@ pub const SystemLib = struct {...@@ -22,6 +22,8 @@ pub const SystemLib = struct {
22 needed: bool = false,22 needed: bool = false,
23};23};
2424
25pub const CacheMode = enum { incremental, whole };
26
25pub fn hashAddSystemLibs(27pub fn hashAddSystemLibs(
26 hh: *Cache.HashHelper,28 hh: *Cache.HashHelper,
27 hm: std.StringArrayHashMapUnmanaged(SystemLib),29 hm: std.StringArrayHashMapUnmanaged(SystemLib),
...@@ -44,10 +46,9 @@ pub const Emit = struct {...@@ -44,10 +46,9 @@ pub const Emit = struct {
44};46};
4547
46pub const Options = struct {48pub const Options = struct {
47 /// This is `null` when -fno-emit-bin is used. When `openPath` or `flush` is called,49 /// This is `null` when `-fno-emit-bin` is used.
48 /// it will have already been null-checked.
49 emit: ?Emit,50 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.
51 implib_emit: ?Emit,52 implib_emit: ?Emit,
52 target: std.Target,53 target: std.Target,
53 output_mode: std.builtin.OutputMode,54 output_mode: std.builtin.OutputMode,
...@@ -70,6 +71,7 @@ pub const Options = struct {...@@ -70,6 +71,7 @@ pub const Options = struct {
70 entry_addr: ?u64 = null,71 entry_addr: ?u64 = null,
71 stack_size_override: ?u64,72 stack_size_override: ?u64,
72 image_base_override: ?u64,73 image_base_override: ?u64,
74 cache_mode: CacheMode,
73 include_compiler_rt: bool,75 include_compiler_rt: bool,
74 /// Set to `true` to omit debug info.76 /// Set to `true` to omit debug info.
75 strip: bool,77 strip: bool,
src/link/Coff.zig+2
...@@ -920,6 +920,8 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -920,6 +920,8 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
920 man = comp.cache_parent.obtain();920 man = comp.cache_parent.obtain();
921 self.base.releaseLock();921 self.base.releaseLock();
922922
923 comptime assert(Compilation.link_hash_implementation_version == 1);
924
923 try man.addListOfFiles(self.base.options.objects);925 try man.addListOfFiles(self.base.options.objects);
924 for (comp.c_object_table.keys()) |key| {926 for (comp.c_object_table.keys()) |key| {
925 _ = try man.addFile(key.status.success.object_path, null);927 _ = 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 {...@@ -1357,6 +1357,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1357 // We are about to obtain this lock, so here we give other processes a chance first.1357 // We are about to obtain this lock, so here we give other processes a chance first.
1358 self.base.releaseLock();1358 self.base.releaseLock();
13591359
1360 comptime assert(Compilation.link_hash_implementation_version == 1);
1361
1360 try man.addOptionalFile(self.base.options.linker_script);1362 try man.addOptionalFile(self.base.options.linker_script);
1361 try man.addOptionalFile(self.base.options.version_script);1363 try man.addOptionalFile(self.base.options.version_script);
1362 try man.addListOfFiles(self.base.options.objects);1364 try man.addListOfFiles(self.base.options.objects);
src/link/MachO.zig+2
...@@ -466,6 +466,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -466,6 +466,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
466 // We are about to obtain this lock, so here we give other processes a chance first.466 // We are about to obtain this lock, so here we give other processes a chance first.
467 self.base.releaseLock();467 self.base.releaseLock();
468468
469 comptime assert(Compilation.link_hash_implementation_version == 1);
470
469 try man.addListOfFiles(self.base.options.objects);471 try man.addListOfFiles(self.base.options.objects);
470 for (comp.c_object_table.keys()) |key| {472 for (comp.c_object_table.keys()) |key| {
471 _ = try man.addFile(key.status.success.object_path, null);473 _ = 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 {...@@ -1094,6 +1094,8 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
1094 // We are about to obtain this lock, so here we give other processes a chance first.1094 // We are about to obtain this lock, so here we give other processes a chance first.
1095 self.base.releaseLock();1095 self.base.releaseLock();
10961096
1097 comptime assert(Compilation.link_hash_implementation_version == 1);
1098
1097 try man.addListOfFiles(self.base.options.objects);1099 try man.addListOfFiles(self.base.options.objects);
1098 for (comp.c_object_table.keys()) |key| {1100 for (comp.c_object_table.keys()) |key| {
1099 _ = try man.addFile(key.status.success.object_path, null);1101 _ = try man.addFile(key.status.success.object_path, null);
src/main.zig-4
...@@ -2578,10 +2578,6 @@ fn buildOutputType(...@@ -2578,10 +2578,6 @@ fn buildOutputType(
2578 };2578 };
2579 try comp.makeBinFileExecutable();2579 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
2585 if (test_exec_args.items.len == 0 and object_format == .c) default_exec_args: {2581 if (test_exec_args.items.len == 0 and object_format == .c) default_exec_args: {
2586 // Default to using `zig run` to execute the produced .c code from `zig test`.2582 // Default to using `zig run` to execute the produced .c code from `zig test`.
2587 const c_code_loc = emit_bin_loc orelse break :default_exec_args;2583 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 {...@@ -203,6 +203,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
203 const sub_compilation = try Compilation.create(comp.gpa, .{203 const sub_compilation = try Compilation.create(comp.gpa, .{
204 .local_cache_directory = comp.global_cache_directory,204 .local_cache_directory = comp.global_cache_directory,
205 .global_cache_directory = comp.global_cache_directory,205 .global_cache_directory = comp.global_cache_directory,
206 .cache_mode = .whole,
206 .zig_lib_directory = comp.zig_lib_directory,207 .zig_lib_directory = comp.zig_lib_directory,
207 .target = target,208 .target = target,
208 .root_name = "c",209 .root_name = "c",
src/stage1.zig+1-1
...@@ -458,7 +458,7 @@ export fn stage2_fetch_file(...@@ -458,7 +458,7 @@ export fn stage2_fetch_file(
458 const comp = @intToPtr(*Compilation, stage1.userdata);458 const comp = @intToPtr(*Compilation, stage1.userdata);
459 const file_path = path_ptr[0..path_len];459 const file_path = path_ptr[0..path_len];
460 const max_file_size = std.math.maxInt(u32);460 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;
462 result_len.* = contents.len;462 result_len.* = contents.len;
463 // TODO https://github.com/ziglang/zig/issues/3328#issuecomment-716749475463 // TODO https://github.com/ziglang/zig/issues/3328#issuecomment-716749475
464 if (contents.len == 0) return @intToPtr(?[*]const u8, 0x1);464 if (contents.len == 0) return @intToPtr(?[*]const u8, 0x1);