From 2627778b251deff3426b5dc21a5a591e0c823b3b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 13 Sep 2020 22:15:03 -0700 Subject: [PATCH] stage2: don't create empty object files when no zig source --- src-self-hosted/Compilation.zig | 9 ++-- src-self-hosted/link.zig | 34 +++++++------- src-self-hosted/link/C.zig | 4 +- src-self-hosted/link/Coff.zig | 80 +++++++++++---------------------- src-self-hosted/link/Elf.zig | 53 +++++++++++----------- src-self-hosted/link/MachO.zig | 63 +++++++------------------- src-self-hosted/link/Wasm.zig | 20 ++++++--- 7 files changed, 108 insertions(+), 155 deletions(-) diff --git a/src-self-hosted/Compilation.zig b/src-self-hosted/Compilation.zig index d8c0bec199..c4eeaf7354 100644 --- a/src-self-hosted/Compilation.zig +++ b/src-self-hosted/Compilation.zig @@ -1072,13 +1072,14 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void { break :blk digest; }; - const full_object_path = if (comp.zig_cache_directory.path) |p| - try std.fs.path.join(comp.gpa, &[_][]const u8{ p, "o", &digest, o_basename }) + const components = if (comp.zig_cache_directory.path) |p| + &[_][]const u8{ p, "o", &digest, o_basename } else - try std.fs.path.join(comp.gpa, &[_][]const u8{ "o", &digest, o_basename }); + &[_][]const u8{ "o", &digest, o_basename }; + c_object.status = .{ .success = .{ - .object_path = full_object_path, + .object_path = try std.fs.path.join(comp.gpa, components), .lock = ch.toOwnedLock(), }, }; diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 4078b263e9..8158e7ee55 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -126,17 +126,29 @@ pub const File = struct { pub fn openPath(allocator: *Allocator, options: Options) !*File { const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm const sub_path = if (use_lld) blk: { + if (options.module == null) { + // No point in opening a file, we would not write anything to it. Initialize with empty. + return switch (options.object_format) { + .coff, .pe => &(try Coff.createEmpty(allocator, options)).base, + .elf => &(try Elf.createEmpty(allocator, options)).base, + .macho => &(try MachO.createEmpty(allocator, options)).base, + .wasm => &(try Wasm.createEmpty(allocator, options)).base, + .c => unreachable, // Reported error earlier. + .hex => return error.HexObjectFormatUnimplemented, + .raw => return error.RawObjectFormatUnimplemented, + }; + } // Open a temporary object file, not the final output file because we want to link with LLD. break :blk try std.fmt.allocPrint(allocator, "{}{}", .{ options.sub_path, options.target.oFileExt() }); } else options.sub_path; errdefer if (use_lld) allocator.free(sub_path); const file: *File = switch (options.object_format) { - .coff, .pe => try Coff.openPath(allocator, sub_path, options), - .elf => try Elf.openPath(allocator, sub_path, options), - .macho => try MachO.openPath(allocator, sub_path, options), - .wasm => try Wasm.openPath(allocator, sub_path, options), - .c => try C.openPath(allocator, sub_path, options), + .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base, + .elf => &(try Elf.openPath(allocator, sub_path, options)).base, + .macho => &(try MachO.openPath(allocator, sub_path, options)).base, + .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, + .c => &(try C.openPath(allocator, sub_path, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -216,19 +228,9 @@ pub const File = struct { } } - pub fn deinit(base: *File) void { - switch (base.tag) { - .coff => @fieldParentPtr(Coff, "base", base).deinit(), - .elf => @fieldParentPtr(Elf, "base", base).deinit(), - .macho => @fieldParentPtr(MachO, "base", base).deinit(), - .c => @fieldParentPtr(C, "base", base).deinit(), - .wasm => @fieldParentPtr(Wasm, "base", base).deinit(), - } + pub fn destroy(base: *File) void { if (base.file) |f| f.close(); if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path); - } - - pub fn destroy(base: *File) void { switch (base.tag) { .coff => { const parent = @fieldParentPtr(Coff, "base", base); diff --git a/src-self-hosted/link/C.zig b/src-self-hosted/link/C.zig index 54c244d3e5..f2c3d30a61 100644 --- a/src-self-hosted/link/C.zig +++ b/src-self-hosted/link/C.zig @@ -23,7 +23,7 @@ need_stddef: bool = false, need_stdint: bool = false, error_msg: *Compilation.ErrorMsg = undefined, -pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File { +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C { assert(options.object_format == .c); if (options.use_llvm) return error.LLVMHasNoCBackend; @@ -48,7 +48,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio .called = std.StringHashMap(void).init(allocator), }; - return &c_file.base; + return c_file; } pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { diff --git a/src-self-hosted/link/Coff.zig b/src-self-hosted/link/Coff.zig index 308c9c2332..ffec465155 100644 --- a/src-self-hosted/link/Coff.zig +++ b/src-self-hosted/link/Coff.zig @@ -28,7 +28,7 @@ pub const base_tag: link.File.Tag = .coff; const msdos_stub = @embedFile("msdos-stub.bin"); base: link.File, -ptr_width: enum { p32, p64 }, +ptr_width: PtrWidth, error_flags: link.File.ErrorFlags = .{}, text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, @@ -64,6 +64,8 @@ text_section_size_dirty: bool = false, /// and needs to be updated in the optional header. size_of_image_dirty: bool = false, +pub const PtrWidth = enum { p32, p64 }; + pub const TextBlock = struct { /// Offset of the code relative to the start of the text section text_offset: u32, @@ -111,7 +113,7 @@ pub const TextBlock = struct { pub const SrcFn = void; -pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*link.File { +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Coff { assert(options.object_format == .coff); if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO @@ -124,66 +126,17 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio }); errdefer file.close(); - var coff_file = try allocator.create(Coff); - errdefer allocator.destroy(coff_file); + const self = try createEmpty(allocator, options); + errdefer self.base.destroy(); - coff_file.* = openFile(allocator, file, options) catch |err| switch (err) { - error.IncrFailed => try createFile(allocator, file, options), - else => |e| return e, - }; + self.base.file = file; - return &coff_file.base; -} - -/// Returns error.IncrFailed if incremental update could not be performed. -fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff { - switch (options.output_mode) { - .Exe => {}, - .Obj => return error.IncrFailed, - .Lib => return error.IncrFailed, - } - var self: Coff = .{ - .base = .{ - .file = file, - .tag = .coff, - .options = options, - .allocator = allocator, - }, - .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { - 32 => .p32, - 64 => .p64, - else => return error.UnsupportedELFArchitecture, - }, - }; - errdefer self.deinit(); - - // TODO implement reading the PE/COFF file - return error.IncrFailed; -} - -/// Truncates the existing file contents and overwrites the contents. -/// Returns an error if `file` is not already open with +read +write +seek abilities. -fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff { // TODO Write object specific relocations, COFF symbol table, then enable object file output. switch (options.output_mode) { .Exe => {}, .Obj => return error.TODOImplementWritingObjFiles, .Lib => return error.TODOImplementWritingLibFiles, } - var self: Coff = .{ - .base = .{ - .tag = .coff, - .options = options, - .allocator = allocator, - .file = file, - }, - .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { - 32 => .p32, - 64 => .p64, - else => return error.UnsupportedCOFFArchitecture, - }, - }; - errdefer self.deinit(); var coff_file_header_offset: u32 = 0; if (options.output_mode == .Exe) { @@ -426,6 +379,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff return self; } +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff { + const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { + 0...32 => .p32, + 33...64 => .p64, + else => return error.UnsupportedCOFFArchitecture, + }; + const self = try gpa.create(Coff); + self.* = .{ + .base = .{ + .tag = .coff, + .options = options, + .allocator = gpa, + .file = null, + }, + .ptr_width = ptr_width, + }; + return self; +} + pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void { try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); diff --git a/src-self-hosted/link/Elf.zig b/src-self-hosted/link/Elf.zig index 37ae1b6d69..d419cbc297 100644 --- a/src-self-hosted/link/Elf.zig +++ b/src-self-hosted/link/Elf.zig @@ -31,7 +31,7 @@ pub const base_tag: File.Tag = .elf; base: File, -ptr_width: enum { p32, p64 }, +ptr_width: PtrWidth, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. @@ -136,6 +136,8 @@ const alloc_den = 3; const minimum_text_block_size = 64; const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den; +pub const PtrWidth = enum { p32, p64 }; + pub const TextBlock = struct { /// Each decl always gets a local symbol with the fully qualified name. /// The vaddr and size are found here directly. @@ -222,7 +224,7 @@ pub const SrcFn = struct { }; }; -pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File { +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf { assert(options.object_format == .elf); if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO @@ -234,31 +236,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio }); errdefer file.close(); - var elf_file = try allocator.create(Elf); - errdefer allocator.destroy(elf_file); + const self = try createEmpty(allocator, options); + errdefer self.base.destroy(); - elf_file.* = try createFile(allocator, file, options); - return &elf_file.base; -} - -/// Truncates the existing file contents and overwrites the contents. -/// Returns an error if `file` is not already open with +read +write +seek abilities. -fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { - var self: Elf = .{ - .base = .{ - .tag = .elf, - .options = options, - .allocator = allocator, - .file = file, - }, - .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { - 0 ... 32 => .p32, - 33 ... 64 => .p64, - else => return error.UnsupportedELFArchitecture, - }, - .shdr_table_dirty = true, - }; - errdefer self.deinit(); + self.base.file = file; + self.shdr_table_dirty = true; // Index 0 is always a null symbol. try self.local_symbols.append(allocator, .{ @@ -289,6 +271,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf return self; } +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { + const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { + 0 ... 32 => .p32, + 33 ... 64 => .p64, + else => return error.UnsupportedELFArchitecture, + }; + const self = try gpa.create(Elf); + self.* = .{ + .base = .{ + .tag = .elf, + .options = options, + .allocator = gpa, + .file = null, + }, + .ptr_width = ptr_width, + }; + return self; +} + pub fn releaseLock(self: *Elf) void { if (self.lock) |*lock| { lock.release(); diff --git a/src-self-hosted/link/MachO.zig b/src-self-hosted/link/MachO.zig index 0c7e3ce2ed..08a948ed0e 100644 --- a/src-self-hosted/link/MachO.zig +++ b/src-self-hosted/link/MachO.zig @@ -135,7 +135,7 @@ pub const SrcFn = struct { pub const empty = SrcFn{}; }; -pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File { +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO { assert(options.object_format == .macho); if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO @@ -148,64 +148,35 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio }); errdefer file.close(); - var macho_file = try allocator.create(MachO); - errdefer allocator.destroy(macho_file); + const self = try createEmpty(allocator, options); + errdefer self.base.destroy(); - macho_file.* = openFile(allocator, file, options) catch |err| switch (err) { - error.IncrFailed => try createFile(allocator, file, options), - else => |e| return e, - }; + self.base.file = file; - return &macho_file.base; -} - -/// Returns error.IncrFailed if incremental update could not be performed. -fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO { - switch (options.output_mode) { - .Exe => {}, - .Obj => {}, - .Lib => return error.IncrFailed, - } - var self: MachO = .{ - .base = .{ - .file = file, - .tag = .macho, - .options = options, - .allocator = allocator, - }, - }; - errdefer self.deinit(); - - // TODO implement reading the macho file - return error.IncrFailed; - //try self.populateMissingMetadata(); - //return self; -} - -/// Truncates the existing file contents and overwrites the contents. -/// Returns an error if `file` is not already open with +read +write +seek abilities. -fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO { switch (options.output_mode) { .Exe => {}, .Obj => {}, .Lib => return error.TODOImplementWritingLibFiles, } - var self: MachO = .{ - .base = .{ - .file = file, - .tag = .macho, - .options = options, - .allocator = allocator, - }, - }; - errdefer self.deinit(); - try self.populateMissingMetadata(); return self; } +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { + const self = try gpa.create(MachO); + self.* = .{ + .base = .{ + .tag = .macho, + .options = options, + .allocator = gpa, + .file = null, + }, + }; + return self; +} + pub fn flush(self: *MachO, comp: *Compilation) !void { switch (self.base.options.output_mode) { .Exe => { diff --git a/src-self-hosted/link/Wasm.zig b/src-self-hosted/link/Wasm.zig index 25e623bebb..2351c6a3ea 100644 --- a/src-self-hosted/link/Wasm.zig +++ b/src-self-hosted/link/Wasm.zig @@ -50,7 +50,7 @@ base: link.File, /// TODO: can/should we access some data structure in Module directly? funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, -pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*link.File { +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { assert(options.object_format == .wasm); if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO @@ -60,21 +60,27 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio const file = try options.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); errdefer file.close(); - const wasm = try allocator.create(Wasm); - errdefer allocator.destroy(wasm); + const wasm = try createEmpty(allocator, options); + errdefer wasm.base.destroy(); + + wasm.base.file = file; try file.writeAll(&(spec.magic ++ spec.version)); + return wasm; +} + +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm { + const wasm = try gpa.create(Wasm); wasm.* = .{ .base = .{ .tag = .wasm, .options = options, - .file = file, - .allocator = allocator, + .file = null, + .allocator = gpa, }, }; - - return &wasm.base; + return wasm; } pub fn deinit(self: *Wasm) void {