From e1811f72eb1bae11934dfd423baa5b26efd99afd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jan 2021 13:04:51 -0700 Subject: [PATCH] stage2: link.C: use pwritev --- lib/std/fs/file.zig | 8 +++++++ src/link/C.zig | 58 ++++++++++++++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index d8fa529c17..2d5b311ece 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -459,6 +459,7 @@ pub const File = struct { return index; } + /// See https://github.com/ziglang/zig/issues/7699 pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize { if (is_windows) { // TODO improve this to use ReadFileScatter @@ -479,6 +480,7 @@ pub const File = struct { /// is not an error condition. /// The `iovecs` parameter is mutable because this function needs to mutate the fields in /// order to handle partial reads from the underlying OS layer. + /// See https://github.com/ziglang/zig/issues/7699 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize { if (iovecs.len == 0) return; @@ -500,6 +502,7 @@ pub const File = struct { } } + /// See https://github.com/ziglang/zig/issues/7699 pub fn preadv(self: File, iovecs: []const os.iovec, offset: u64) PReadError!usize { if (is_windows) { // TODO improve this to use ReadFileScatter @@ -520,6 +523,7 @@ pub const File = struct { /// is not an error condition. /// The `iovecs` parameter is mutable because this function needs to mutate the fields in /// order to handle partial reads from the underlying OS layer. + /// See https://github.com/ziglang/zig/issues/7699 pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void { if (iovecs.len == 0) return; @@ -582,6 +586,7 @@ pub const File = struct { } } + /// See https://github.com/ziglang/zig/issues/7699 pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize { if (is_windows) { // TODO improve this to use WriteFileScatter @@ -599,6 +604,7 @@ pub const File = struct { /// The `iovecs` parameter is mutable because this function needs to mutate the fields in /// order to handle partial writes from the underlying OS layer. + /// See https://github.com/ziglang/zig/issues/7699 pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void { if (iovecs.len == 0) return; @@ -615,6 +621,7 @@ pub const File = struct { } } + /// See https://github.com/ziglang/zig/issues/7699 pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize { if (is_windows) { // TODO improve this to use WriteFileScatter @@ -632,6 +639,7 @@ pub const File = struct { /// The `iovecs` parameter is mutable because this function needs to mutate the fields in /// order to handle partial writes from the underlying OS layer. + /// See https://github.com/ziglang/zig/issues/7699 pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void { if (iovecs.len == 0) return; diff --git a/src/link/C.zig b/src/link/C.zig index 10b98b854c..68eb56c5b9 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -41,13 +41,12 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio if (options.use_lld) return error.LLDHasNoCBackend; const file = try options.emit.?.directory.handle.createFile(sub_path, .{ - .truncate = true, + // Truncation is done on `flush`. + .truncate = false, .mode = link.determineMode(options), }); errdefer file.close(); - try file.writeAll(zig_h); - var c_file = try allocator.create(C); errdefer allocator.destroy(c_file); @@ -133,40 +132,61 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); - const file = self.base.file.?; + const module = self.base.options.module orelse + return error.LinkingWithoutZigSourceUnimplemented; - // The header is written upon opening; here we truncate and seek to after the header. - // TODO: use writev - try file.seekTo(zig_h.len); - try file.setEndPos(zig_h.len); + // We collect a list of buffers to write, and write them all at once with pwritev 😎 + var all_buffers = std.ArrayList(std.os.iovec_const).init(comp.gpa); + defer all_buffers.deinit(); - var buffered_writer = std.io.bufferedWriter(file.writer()); - const writer = buffered_writer.writer(); + // This is at least enough until we get to the function bodies without error handling. + try all_buffers.ensureCapacity(module.decl_table.count() + 1); - const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + var file_size: u64 = zig_h.len; + all_buffers.appendAssumeCapacity(.{ + .iov_base = zig_h, + .iov_len = zig_h.len, + }); + + var fn_count: usize = 0; // Forward decls and non-functions first. - // TODO: use writev for (module.decl_table.items()) |kv| { const decl = kv.value; const decl_tv = decl.typed_value.most_recent.typed_value; - if (decl_tv.val.castTag(.function)) |_| { - try writer.writeAll(decl.fn_link.c.fwd_decl.items); - } else { - try writer.writeAll(decl.link.c.code.items); - } + const buf = buf: { + if (decl_tv.val.castTag(.function)) |_| { + fn_count += 1; + break :buf decl.fn_link.c.fwd_decl.items; + } else { + break :buf decl.link.c.code.items; + } + }; + all_buffers.appendAssumeCapacity(.{ + .iov_base = buf.ptr, + .iov_len = buf.len, + }); + file_size += buf.len; } // Now the function bodies. + try all_buffers.ensureCapacity(all_buffers.items.len + fn_count); for (module.decl_table.items()) |kv| { const decl = kv.value; const decl_tv = decl.typed_value.most_recent.typed_value; if (decl_tv.val.castTag(.function)) |_| { - try writer.writeAll(decl.link.c.code.items); + const buf = decl.link.c.code.items; + all_buffers.appendAssumeCapacity(.{ + .iov_base = buf.ptr, + .iov_len = buf.len, + }); + file_size += buf.len; } } - try buffered_writer.flush(); + const file = self.base.file.?; + try file.setEndPos(file_size); + try file.pwritevAll(all_buffers.items, 0); } pub fn updateDeclExports(