authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-28 15:42:09-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-28 15:42:09-07:00
log412a2f966e18aa792089ac1f41482222d7f2434f
tree36f4d7f00ce93bc3f10844e3bbf56f75c8ddba8d
parent91a73a177bc20fa0219dbb6c3cf3dda1c2a465db

store stage1 flags in a trailing byte in the hash id symlink

When we get a cache hit for a stage1 compilation, we need to know about some of the flags such as have_winmain or have_dllmain to know which subsystem to infer during linking. To do this, we append a hex-encoded byte into the intentionally-dangling symlink which contains the cache hash digest rather than a filename. The hex-encoded byte contains the flags we need to infer the subsystem during linking.

4 files changed, 56 insertions(+), 27 deletions(-)

BRANCH_TODO+3
...@@ -1,3 +1,6 @@...@@ -1,3 +1,6 @@
1 * the have_foo flags that we get from stage1 have to be stored in the cache otherwise we get
2 a different result for subsystem when we have a cached stage1 execution result.
3 same deal with extern "foo" libraries used
1 * add jobs to build import libs for windows DLLs for explicitly linked libs4 * add jobs to build import libs for windows DLLs for explicitly linked libs
2 * add jobs to build import libs for windows DLLs for extern "foo" functions used5 * add jobs to build import libs for windows DLLs for extern "foo" functions used
3 * MachO LLD linking6 * MachO LLD linking
src/Compilation.zig+36-14
...@@ -2512,16 +2512,25 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2512,16 +2512,25 @@ fn updateStage1Module(comp: *Compilation) !void {
2512 if (try man.hit()) {2512 if (try man.hit()) {
2513 const digest = man.final();2513 const digest = man.final();
25142514
2515 var prev_digest_buf: [digest.len]u8 = undefined;2515 // We use an extra hex-encoded byte here to store some flags.
2516 var prev_digest_buf: [digest.len + 2]u8 = undefined;
2516 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {2517 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
2517 log.debug("stage1 {} new_digest={} readlink error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });2518 log.debug("stage1 {} new_digest={} readlink error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });
2518 // Handle this as a cache miss.2519 // Handle this as a cache miss.
2519 break :blk prev_digest_buf[0..0];2520 break :blk prev_digest_buf[0..0];
2520 };2521 };
2521 if (mem.eql(u8, prev_digest, &digest)) {2522 if (prev_digest.len >= digest.len + 2) {
2522 log.debug("stage1 {} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest });2523 if (mem.eql(u8, prev_digest[0..digest.len], &digest)) {
2523 comp.stage1_lock = man.toOwnedLock();2524 log.debug("stage1 {} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest });
2524 return;2525 var flags_bytes: [1]u8 = undefined;
2526 if (std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..])) |_| {
2527 comp.stage1_lock = man.toOwnedLock();
2528 mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]);
2529 return;
2530 } else |err| {
2531 log.warn("bad cache stage1 digest: '{s}'", .{prev_digest});
2532 }
2533 }
2525 }2534 }
2526 log.debug("stage1 {} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest });2535 log.debug("stage1 {} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest });
2527 man.unhit(prev_hash_state, input_file_count);2536 man.unhit(prev_hash_state, input_file_count);
...@@ -2642,22 +2651,35 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2642,22 +2651,35 @@ fn updateStage1Module(comp: *Compilation) !void {
2642 };2651 };
2643 stage1_module.build_object();2652 stage1_module.build_object();
26442653
2645 mod.have_c_main = stage1_module.have_c_main;2654 mod.stage1_flags = .{
2646 mod.have_winmain = stage1_module.have_winmain;2655 .have_c_main = stage1_module.have_c_main,
2647 mod.have_wwinmain = stage1_module.have_wwinmain;2656 .have_winmain = stage1_module.have_winmain,
2648 mod.have_winmain_crt_startup = stage1_module.have_winmain_crt_startup;2657 .have_wwinmain = stage1_module.have_wwinmain,
2649 mod.have_wwinmain_crt_startup = stage1_module.have_wwinmain_crt_startup;2658 .have_winmain_crt_startup = stage1_module.have_winmain_crt_startup,
2650 mod.have_dllmain_crt_startup = stage1_module.have_dllmain_crt_startup;2659 .have_wwinmain_crt_startup = stage1_module.have_wwinmain_crt_startup,
2660 .have_dllmain_crt_startup = stage1_module.have_dllmain_crt_startup,
2661 };
26512662
2652 stage1_module.destroy();2663 stage1_module.destroy();
26532664
2654 const digest = man.final();2665 const digest = man.final();
26552666
2656 log.debug("stage1 {} final digest={}", .{ mod.root_pkg.root_src_path, digest });
2657
2658 // Update the dangling symlink with the digest. If it fails we can continue; it only2667 // Update the dangling symlink with the digest. If it fails we can continue; it only
2659 // means that the next invocation will have an unnecessary cache miss.2668 // means that the next invocation will have an unnecessary cache miss.
2660 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {2669 const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);
2670 log.debug("stage1 {} final digest={} flags={x}", .{
2671 mod.root_pkg.root_src_path, digest, stage1_flags_byte,
2672 });
2673 var digest_plus_flags: [digest.len + 2]u8 = undefined;
2674 digest_plus_flags[0..digest.len].* = digest;
2675 assert(std.fmt.formatIntBuf(digest_plus_flags[digest.len..], stage1_flags_byte, 16, false, .{
2676 .width = 2,
2677 .fill = '0',
2678 }) == 2);
2679 log.debug("saved digest + flags: '{s}' (byte = {}) have_winmain_crt_startup={}", .{
2680 digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup,
2681 });
2682 directory.handle.symLink(&digest_plus_flags, id_symlink_basename, .{}) catch |err| {
2661 log.warn("failed to save stage1 hash digest symlink: {}", .{@errorName(err)});2683 log.warn("failed to save stage1 hash digest symlink: {}", .{@errorName(err)});
2662 };2684 };
2663 // Again failure here only means an unnecessary cache miss.2685 // Again failure here only means an unnecessary cache miss.
src/Module.zig+9-6
...@@ -75,12 +75,15 @@ global_error_set: std.StringHashMapUnmanaged(u16) = .{},...@@ -75,12 +75,15 @@ global_error_set: std.StringHashMapUnmanaged(u16) = .{},
75/// previous analysis.75/// previous analysis.
76generation: u32 = 0,76generation: u32 = 0,
7777
78have_winmain: bool = false,78stage1_flags: packed struct {
79have_wwinmain: bool = false,79 have_winmain: bool = false,
80have_winmain_crt_startup: bool = false,80 have_wwinmain: bool = false,
81have_wwinmain_crt_startup: bool = false,81 have_winmain_crt_startup: bool = false,
82have_dllmain_crt_startup: bool = false,82 have_wwinmain_crt_startup: bool = false,
83have_c_main: bool = false,83 have_dllmain_crt_startup: bool = false,
84 have_c_main: bool = false,
85 reserved: u2 = 0,
86} = .{},
8487
85pub const Export = struct {88pub const Export = struct {
86 options: std.builtin.ExportOptions,89 options: std.builtin.ExportOptions,
src/link/Coff.zig+8-7
...@@ -943,14 +943,15 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -943,14 +943,15 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
943 switch (target.os.tag) {943 switch (target.os.tag) {
944 .windows => {944 .windows => {
945 if (self.base.options.module) |module| {945 if (self.base.options.module) |module| {
946 if (module.have_dllmain_crt_startup or is_dyn_lib)946 if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib)
947 break :blk null;947 break :blk null;
948 if (module.have_c_main or self.base.options.is_test or948 if (module.stage1_flags.have_c_main or self.base.options.is_test or
949 module.have_winmain_crt_startup or module.have_wwinmain_crt_startup)949 module.stage1_flags.have_winmain_crt_startup or
950 module.stage1_flags.have_wwinmain_crt_startup)
950 {951 {
951 break :blk .Console;952 break :blk .Console;
952 }953 }
953 if (module.have_winmain or module.have_wwinmain)954 if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain)
954 break :blk .Windows;955 break :blk .Windows;
955 }956 }
956 },957 },
...@@ -1068,11 +1069,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -1068,11 +1069,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
1068 try argv.append("-NODEFAULTLIB");1069 try argv.append("-NODEFAULTLIB");
1069 if (!is_lib) {1070 if (!is_lib) {
1070 if (self.base.options.module) |module| {1071 if (self.base.options.module) |module| {
1071 if (module.have_winmain) {1072 if (module.stage1_flags.have_winmain) {
1072 try argv.append("-ENTRY:WinMain");1073 try argv.append("-ENTRY:WinMain");
1073 } else if (module.have_wwinmain) {1074 } else if (module.stage1_flags.have_wwinmain) {
1074 try argv.append("-ENTRY:wWinMain");1075 try argv.append("-ENTRY:wWinMain");
1075 } else if (module.have_wwinmain_crt_startup) {1076 } else if (module.stage1_flags.have_wwinmain_crt_startup) {
1076 try argv.append("-ENTRY:wWinMainCRTStartup");1077 try argv.append("-ENTRY:wWinMainCRTStartup");
1077 } else {1078 } else {
1078 try argv.append("-ENTRY:WinMainCRTStartup");1079 try argv.append("-ENTRY:WinMainCRTStartup");