| ... | ... | @@ -3538,44 +3538,61 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3538 | 3538 | const cache_directory = if (want_local_cache) mod.local_zir_cache else mod.global_zir_cache; |
| 3539 | 3539 | const zir_dir = cache_directory.handle; |
| 3540 | 3540 | |
| 3541 | | var cache_file: ?std.fs.File = null; |
| 3542 | | defer if (cache_file) |f| f.close(); |
| 3543 | | |
| 3544 | 3541 | // Determine whether we need to reload the file from disk and redo parsing and AstGen. |
| 3545 | | switch (file.status) { |
| 3546 | | .never_loaded, .retryable_failure => cached: { |
| 3542 | var lock: std.fs.File.Lock = switch (file.status) { |
| 3543 | .never_loaded, .retryable_failure => lock: { |
| 3547 | 3544 | // First, load the cached ZIR code, if any. |
| 3548 | 3545 | log.debug("AstGen checking cache: {s} (local={}, digest={s})", .{ |
| 3549 | 3546 | file.sub_file_path, want_local_cache, &digest, |
| 3550 | 3547 | }); |
| 3551 | 3548 | |
| 3552 | | // We ask for a lock in order to coordinate with other zig processes. |
| 3553 | | // If another process is already working on this file, we will get the cached |
| 3554 | | // version. Likewise if we're working on AstGen and another process asks for |
| 3555 | | // the cached file, they'll get it. |
| 3556 | | cache_file = zir_dir.openFile(&digest, .{ .lock = .Shared }) catch |err| switch (err) { |
| 3557 | | error.PathAlreadyExists => unreachable, // opening for reading |
| 3558 | | error.NoSpaceLeft => unreachable, // opening for reading |
| 3559 | | error.NotDir => unreachable, // no dir components |
| 3560 | | error.InvalidUtf8 => unreachable, // it's a hex encoded name |
| 3561 | | error.BadPathName => unreachable, // it's a hex encoded name |
| 3562 | | error.NameTooLong => unreachable, // it's a fixed size name |
| 3563 | | error.PipeBusy => unreachable, // it's not a pipe |
| 3564 | | error.WouldBlock => unreachable, // not asking for non-blocking I/O |
| 3565 | | |
| 3566 | | error.SymLinkLoop, |
| 3567 | | error.FileNotFound, |
| 3568 | | error.Unexpected, |
| 3569 | | => break :cached, |
| 3570 | | |
| 3571 | | else => |e| return e, // Retryable errors are handled at callsite. |
| 3572 | | }; |
| 3549 | break :lock .Shared; |
| 3550 | }, |
| 3551 | .parse_failure, .astgen_failure, .success_zir => lock: { |
| 3552 | const unchanged_metadata = |
| 3553 | stat.size == file.stat.size and |
| 3554 | stat.mtime == file.stat.mtime and |
| 3555 | stat.inode == file.stat.inode; |
| 3556 | |
| 3557 | if (unchanged_metadata) { |
| 3558 | log.debug("unmodified metadata of file: {s}", .{file.sub_file_path}); |
| 3559 | return; |
| 3560 | } |
| 3561 | |
| 3562 | log.debug("metadata changed: {s}", .{file.sub_file_path}); |
| 3563 | |
| 3564 | break :lock .Exclusive; |
| 3565 | }, |
| 3566 | }; |
| 3567 | |
| 3568 | // We ask for a lock in order to coordinate with other zig processes. |
| 3569 | // If another process is already working on this file, we will get the cached |
| 3570 | // version. Likewise if we're working on AstGen and another process asks for |
| 3571 | // the cached file, they'll get it. |
| 3572 | const cache_file = zir_dir.createFile(&digest, .{ |
| 3573 | .read = true, |
| 3574 | .truncate = false, |
| 3575 | .lock = lock, |
| 3576 | }) catch |err| switch (err) { |
| 3577 | error.NotDir => unreachable, // no dir components |
| 3578 | error.InvalidUtf8 => unreachable, // it's a hex encoded name |
| 3579 | error.BadPathName => unreachable, // it's a hex encoded name |
| 3580 | error.NameTooLong => unreachable, // it's a fixed size name |
| 3581 | error.PipeBusy => unreachable, // it's not a pipe |
| 3582 | error.WouldBlock => unreachable, // not asking for non-blocking I/O |
| 3583 | error.FileNotFound => unreachable, // no dir components |
| 3584 | |
| 3585 | else => |e| return e, // Retryable errors are handled at callsite. |
| 3586 | }; |
| 3587 | defer cache_file.close(); |
| 3573 | 3588 | |
| 3589 | while (true) { |
| 3590 | update: { |
| 3574 | 3591 | // First we read the header to determine the lengths of arrays. |
| 3575 | | const header = cache_file.?.reader().readStruct(Zir.Header) catch |err| switch (err) { |
| 3592 | const header = cache_file.reader().readStruct(Zir.Header) catch |err| switch (err) { |
| 3576 | 3593 | // This can happen if Zig bails out of this function between creating |
| 3577 | 3594 | // the cached file and writing it. |
| 3578 | | error.EndOfStream => break :cached, |
| 3595 | error.EndOfStream => break :update, |
| 3579 | 3596 | else => |e| return e, |
| 3580 | 3597 | }; |
| 3581 | 3598 | const unchanged_metadata = |
| ... | ... | @@ -3585,7 +3602,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3585 | 3602 | |
| 3586 | 3603 | if (!unchanged_metadata) { |
| 3587 | 3604 | log.debug("AstGen cache stale: {s}", .{file.sub_file_path}); |
| 3588 | | break :cached; |
| 3605 | break :update; |
| 3589 | 3606 | } |
| 3590 | 3607 | log.debug("AstGen cache hit: {s} instructions_len={d}", .{ |
| 3591 | 3608 | file.sub_file_path, header.instructions_len, |
| ... | ... | @@ -3637,13 +3654,13 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3637 | 3654 | .iov_len = header.extra_len * 4, |
| 3638 | 3655 | }, |
| 3639 | 3656 | }; |
| 3640 | | const amt_read = try cache_file.?.readvAll(&iovecs); |
| 3657 | const amt_read = try cache_file.readvAll(&iovecs); |
| 3641 | 3658 | const amt_expected = zir.instructions.len * 9 + |
| 3642 | 3659 | zir.string_bytes.len + |
| 3643 | 3660 | zir.extra.len * 4; |
| 3644 | 3661 | if (amt_read != amt_expected) { |
| 3645 | 3662 | log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path}); |
| 3646 | | break :cached; |
| 3663 | break :update; |
| 3647 | 3664 | } |
| 3648 | 3665 | if (data_has_safety_tag) { |
| 3649 | 3666 | const tags = zir.instructions.items(.tag); |
| ... | ... | @@ -3679,42 +3696,22 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3679 | 3696 | return error.AnalysisFail; |
| 3680 | 3697 | } |
| 3681 | 3698 | return; |
| 3682 | | }, |
| 3683 | | .parse_failure, .astgen_failure, .success_zir => { |
| 3684 | | const unchanged_metadata = |
| 3685 | | stat.size == file.stat.size and |
| 3686 | | stat.mtime == file.stat.mtime and |
| 3687 | | stat.inode == file.stat.inode; |
| 3688 | | |
| 3689 | | if (unchanged_metadata) { |
| 3690 | | log.debug("unmodified metadata of file: {s}", .{file.sub_file_path}); |
| 3691 | | return; |
| 3692 | | } |
| 3699 | } |
| 3693 | 3700 | |
| 3694 | | log.debug("metadata changed: {s}", .{file.sub_file_path}); |
| 3695 | | }, |
| 3696 | | } |
| 3697 | | if (cache_file) |f| { |
| 3698 | | f.close(); |
| 3699 | | cache_file = null; |
| 3701 | // If we already have the exclusive lock then it is our job to update. |
| 3702 | if (builtin.os.tag == .wasi or lock == .Exclusive) break; |
| 3703 | // Otherwise, unlock to give someone a chance to get the exclusive lock |
| 3704 | // and then upgrade to an exclusive lock. |
| 3705 | cache_file.unlock(); |
| 3706 | lock = .Exclusive; |
| 3707 | try cache_file.lock(lock); |
| 3700 | 3708 | } |
| 3701 | | cache_file = zir_dir.createFile(&digest, .{ .lock = .Exclusive }) catch |err| switch (err) { |
| 3702 | | error.NotDir => unreachable, // no dir components |
| 3703 | | error.InvalidUtf8 => unreachable, // it's a hex encoded name |
| 3704 | | error.BadPathName => unreachable, // it's a hex encoded name |
| 3705 | | error.NameTooLong => unreachable, // it's a fixed size name |
| 3706 | | error.PipeBusy => unreachable, // it's not a pipe |
| 3707 | | error.WouldBlock => unreachable, // not asking for non-blocking I/O |
| 3708 | | error.FileNotFound => unreachable, // no dir components |
| 3709 | 3709 | |
| 3710 | | else => |e| { |
| 3711 | | const pkg_path = file.pkg.root_src_directory.path orelse "."; |
| 3712 | | const cache_path = cache_directory.path orelse "."; |
| 3713 | | log.warn("unable to save cached ZIR code for {s}/{s} to {s}/{s}: {s}", .{ |
| 3714 | | pkg_path, file.sub_file_path, cache_path, &digest, @errorName(e), |
| 3715 | | }); |
| 3716 | | return; |
| 3717 | | }, |
| 3710 | // The cache is definitely stale so delete the contents to avoid an underwrite later. |
| 3711 | cache_file.setEndPos(0) catch |err| switch (err) { |
| 3712 | error.FileTooBig => unreachable, // 0 is not too big |
| 3713 | |
| 3714 | else => |e| return e, |
| 3718 | 3715 | }; |
| 3719 | 3716 | |
| 3720 | 3717 | mod.lockAndClearFileCompileError(file); |
| ... | ... | @@ -3871,7 +3868,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3871 | 3868 | .iov_len = file.zir.extra.len * 4, |
| 3872 | 3869 | }, |
| 3873 | 3870 | }; |
| 3874 | | cache_file.?.writevAll(&iovecs) catch |err| { |
| 3871 | cache_file.writevAll(&iovecs) catch |err| { |
| 3875 | 3872 | const pkg_path = file.pkg.root_src_directory.path orelse "."; |
| 3876 | 3873 | const cache_path = cache_directory.path orelse "."; |
| 3877 | 3874 | log.warn("unable to write cached ZIR code for {s}/{s} to {s}/{s}: {s}", .{ |