authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-13 22:15:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-13 22:15:03-07:00
log2627778b251deff3426b5dc21a5a591e0c823b3b
treea82d2822d58e01f277174a275b7063f113b5a63d
parent046dce9cefa221e46f59d3f2f3132556dec6432c

stage2: don't create empty object files when no zig source


7 files changed, 104 insertions(+), 151 deletions(-)

src-self-hosted/Compilation.zig+5-4
...@@ -1072,13 +1072,14 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void {...@@ -1072,13 +1072,14 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void {
1072 break :blk digest;1072 break :blk digest;
1073 };1073 };
10741074
1075 const full_object_path = if (comp.zig_cache_directory.path) |p|1075 const components = if (comp.zig_cache_directory.path) |p|
1076 try std.fs.path.join(comp.gpa, &[_][]const u8{ p, "o", &digest, o_basename })1076 &[_][]const u8{ p, "o", &digest, o_basename }
1077 else1077 else
1078 try std.fs.path.join(comp.gpa, &[_][]const u8{ "o", &digest, o_basename });1078 &[_][]const u8{ "o", &digest, o_basename };
1079
1079 c_object.status = .{1080 c_object.status = .{
1080 .success = .{1081 .success = .{
1081 .object_path = full_object_path,1082 .object_path = try std.fs.path.join(comp.gpa, components),
1082 .lock = ch.toOwnedLock(),1083 .lock = ch.toOwnedLock(),
1083 },1084 },
1084 };1085 };
src-self-hosted/link.zig+18-16
...@@ -126,17 +126,29 @@ pub const File = struct {...@@ -126,17 +126,29 @@ pub const File = struct {
126 pub fn openPath(allocator: *Allocator, options: Options) !*File {126 pub fn openPath(allocator: *Allocator, options: Options) !*File {
127 const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm127 const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm
128 const sub_path = if (use_lld) blk: {128 const sub_path = if (use_lld) blk: {
129 if (options.module == null) {
130 // No point in opening a file, we would not write anything to it. Initialize with empty.
131 return switch (options.object_format) {
132 .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
133 .elf => &(try Elf.createEmpty(allocator, options)).base,
134 .macho => &(try MachO.createEmpty(allocator, options)).base,
135 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
136 .c => unreachable, // Reported error earlier.
137 .hex => return error.HexObjectFormatUnimplemented,
138 .raw => return error.RawObjectFormatUnimplemented,
139 };
140 }
129 // Open a temporary object file, not the final output file because we want to link with LLD.141 // Open a temporary object file, not the final output file because we want to link with LLD.
130 break :blk try std.fmt.allocPrint(allocator, "{}{}", .{ options.sub_path, options.target.oFileExt() });142 break :blk try std.fmt.allocPrint(allocator, "{}{}", .{ options.sub_path, options.target.oFileExt() });
131 } else options.sub_path;143 } else options.sub_path;
132 errdefer if (use_lld) allocator.free(sub_path);144 errdefer if (use_lld) allocator.free(sub_path);
133145
134 const file: *File = switch (options.object_format) {146 const file: *File = switch (options.object_format) {
135 .coff, .pe => try Coff.openPath(allocator, sub_path, options),147 .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base,
136 .elf => try Elf.openPath(allocator, sub_path, options),148 .elf => &(try Elf.openPath(allocator, sub_path, options)).base,
137 .macho => try MachO.openPath(allocator, sub_path, options),149 .macho => &(try MachO.openPath(allocator, sub_path, options)).base,
138 .wasm => try Wasm.openPath(allocator, sub_path, options),150 .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,
139 .c => try C.openPath(allocator, sub_path, options),151 .c => &(try C.openPath(allocator, sub_path, options)).base,
140 .hex => return error.HexObjectFormatUnimplemented,152 .hex => return error.HexObjectFormatUnimplemented,
141 .raw => return error.RawObjectFormatUnimplemented,153 .raw => return error.RawObjectFormatUnimplemented,
142 };154 };
...@@ -216,19 +228,9 @@ pub const File = struct {...@@ -216,19 +228,9 @@ pub const File = struct {
216 }228 }
217 }229 }
218230
219 pub fn deinit(base: *File) void {231 pub fn destroy(base: *File) void {
220 switch (base.tag) {
221 .coff => @fieldParentPtr(Coff, "base", base).deinit(),
222 .elf => @fieldParentPtr(Elf, "base", base).deinit(),
223 .macho => @fieldParentPtr(MachO, "base", base).deinit(),
224 .c => @fieldParentPtr(C, "base", base).deinit(),
225 .wasm => @fieldParentPtr(Wasm, "base", base).deinit(),
226 }
227 if (base.file) |f| f.close();232 if (base.file) |f| f.close();
228 if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path);233 if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path);
229 }
230
231 pub fn destroy(base: *File) void {
232 switch (base.tag) {234 switch (base.tag) {
233 .coff => {235 .coff => {
234 const parent = @fieldParentPtr(Coff, "base", base);236 const parent = @fieldParentPtr(Coff, "base", base);
src-self-hosted/link/C.zig+2-2
...@@ -23,7 +23,7 @@ need_stddef: bool = false,...@@ -23,7 +23,7 @@ need_stddef: bool = false,
23need_stdint: bool = false,23need_stdint: bool = false,
24error_msg: *Compilation.ErrorMsg = undefined,24error_msg: *Compilation.ErrorMsg = undefined,
2525
26pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {26pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C {
27 assert(options.object_format == .c);27 assert(options.object_format == .c);
2828
29 if (options.use_llvm) return error.LLVMHasNoCBackend;29 if (options.use_llvm) return error.LLVMHasNoCBackend;
...@@ -48,7 +48,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -48,7 +48,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
48 .called = std.StringHashMap(void).init(allocator),48 .called = std.StringHashMap(void).init(allocator),
49 };49 };
5050
51 return &c_file.base;51 return c_file;
52}52}
5353
54pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {54pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
src-self-hosted/link/Coff.zig+26-54
...@@ -28,7 +28,7 @@ pub const base_tag: link.File.Tag = .coff;...@@ -28,7 +28,7 @@ pub const base_tag: link.File.Tag = .coff;
28const msdos_stub = @embedFile("msdos-stub.bin");28const msdos_stub = @embedFile("msdos-stub.bin");
2929
30base: link.File,30base: link.File,
31ptr_width: enum { p32, p64 },31ptr_width: PtrWidth,
32error_flags: link.File.ErrorFlags = .{},32error_flags: link.File.ErrorFlags = .{},
3333
34text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},34text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
...@@ -64,6 +64,8 @@ text_section_size_dirty: bool = false,...@@ -64,6 +64,8 @@ text_section_size_dirty: bool = false,
64/// and needs to be updated in the optional header.64/// and needs to be updated in the optional header.
65size_of_image_dirty: bool = false,65size_of_image_dirty: bool = false,
6666
67pub const PtrWidth = enum { p32, p64 };
68
67pub const TextBlock = struct {69pub const TextBlock = struct {
68 /// Offset of the code relative to the start of the text section70 /// Offset of the code relative to the start of the text section
69 text_offset: u32,71 text_offset: u32,
...@@ -111,7 +113,7 @@ pub const TextBlock = struct {...@@ -111,7 +113,7 @@ pub const TextBlock = struct {
111113
112pub const SrcFn = void;114pub const SrcFn = void;
113115
114pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*link.File {116pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Coff {
115 assert(options.object_format == .coff);117 assert(options.object_format == .coff);
116118
117 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO119 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...@@ -124,66 +126,17 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
124 });126 });
125 errdefer file.close();127 errdefer file.close();
126128
127 var coff_file = try allocator.create(Coff);129 const self = try createEmpty(allocator, options);
128 errdefer allocator.destroy(coff_file);130 errdefer self.base.destroy();
129
130 coff_file.* = openFile(allocator, file, options) catch |err| switch (err) {
131 error.IncrFailed => try createFile(allocator, file, options),
132 else => |e| return e,
133 };
134
135 return &coff_file.base;
136}
137
138/// Returns error.IncrFailed if incremental update could not be performed.
139fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
140 switch (options.output_mode) {
141 .Exe => {},
142 .Obj => return error.IncrFailed,
143 .Lib => return error.IncrFailed,
144 }
145 var self: Coff = .{
146 .base = .{
147 .file = file,
148 .tag = .coff,
149 .options = options,
150 .allocator = allocator,
151 },
152 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
153 32 => .p32,
154 64 => .p64,
155 else => return error.UnsupportedELFArchitecture,
156 },
157 };
158 errdefer self.deinit();
159131
160 // TODO implement reading the PE/COFF file132 self.base.file = file;
161 return error.IncrFailed;
162}
163133
164/// Truncates the existing file contents and overwrites the contents.
165/// Returns an error if `file` is not already open with +read +write +seek abilities.
166fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
167 // TODO Write object specific relocations, COFF symbol table, then enable object file output.134 // TODO Write object specific relocations, COFF symbol table, then enable object file output.
168 switch (options.output_mode) {135 switch (options.output_mode) {
169 .Exe => {},136 .Exe => {},
170 .Obj => return error.TODOImplementWritingObjFiles,137 .Obj => return error.TODOImplementWritingObjFiles,
171 .Lib => return error.TODOImplementWritingLibFiles,138 .Lib => return error.TODOImplementWritingLibFiles,
172 }139 }
173 var self: Coff = .{
174 .base = .{
175 .tag = .coff,
176 .options = options,
177 .allocator = allocator,
178 .file = file,
179 },
180 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
181 32 => .p32,
182 64 => .p64,
183 else => return error.UnsupportedCOFFArchitecture,
184 },
185 };
186 errdefer self.deinit();
187140
188 var coff_file_header_offset: u32 = 0;141 var coff_file_header_offset: u32 = 0;
189 if (options.output_mode == .Exe) {142 if (options.output_mode == .Exe) {
...@@ -426,6 +379,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff...@@ -426,6 +379,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff
426 return self;379 return self;
427}380}
428381
382pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff {
383 const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
384 0...32 => .p32,
385 33...64 => .p64,
386 else => return error.UnsupportedCOFFArchitecture,
387 };
388 const self = try gpa.create(Coff);
389 self.* = .{
390 .base = .{
391 .tag = .coff,
392 .options = options,
393 .allocator = gpa,
394 .file = null,
395 },
396 .ptr_width = ptr_width,
397 };
398 return self;
399}
400
429pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {401pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
430 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);402 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
431403
src-self-hosted/link/Elf.zig+27-26
...@@ -31,7 +31,7 @@ pub const base_tag: File.Tag = .elf;...@@ -31,7 +31,7 @@ pub const base_tag: File.Tag = .elf;
3131
32base: File,32base: File,
3333
34ptr_width: enum { p32, p64 },34ptr_width: PtrWidth,
3535
36/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.36/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
37/// Same order as in the file.37/// Same order as in the file.
...@@ -136,6 +136,8 @@ const alloc_den = 3;...@@ -136,6 +136,8 @@ const alloc_den = 3;
136const minimum_text_block_size = 64;136const minimum_text_block_size = 64;
137const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;137const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
138138
139pub const PtrWidth = enum { p32, p64 };
140
139pub const TextBlock = struct {141pub const TextBlock = struct {
140 /// Each decl always gets a local symbol with the fully qualified name.142 /// Each decl always gets a local symbol with the fully qualified name.
141 /// The vaddr and size are found here directly.143 /// The vaddr and size are found here directly.
...@@ -222,7 +224,7 @@ pub const SrcFn = struct {...@@ -222,7 +224,7 @@ pub const SrcFn = struct {
222 };224 };
223};225};
224226
225pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {227pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf {
226 assert(options.object_format == .elf);228 assert(options.object_format == .elf);
227229
228 if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO230 if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO
...@@ -234,31 +236,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -234,31 +236,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
234 });236 });
235 errdefer file.close();237 errdefer file.close();
236238
237 var elf_file = try allocator.create(Elf);239 const self = try createEmpty(allocator, options);
238 errdefer allocator.destroy(elf_file);240 errdefer self.base.destroy();
239
240 elf_file.* = try createFile(allocator, file, options);
241 return &elf_file.base;
242}
243241
244/// Truncates the existing file contents and overwrites the contents.242 self.base.file = file;
245/// Returns an error if `file` is not already open with +read +write +seek abilities.243 self.shdr_table_dirty = true;
246fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf {
247 var self: Elf = .{
248 .base = .{
249 .tag = .elf,
250 .options = options,
251 .allocator = allocator,
252 .file = file,
253 },
254 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
255 0 ... 32 => .p32,
256 33 ... 64 => .p64,
257 else => return error.UnsupportedELFArchitecture,
258 },
259 .shdr_table_dirty = true,
260 };
261 errdefer self.deinit();
262244
263 // Index 0 is always a null symbol.245 // Index 0 is always a null symbol.
264 try self.local_symbols.append(allocator, .{246 try self.local_symbols.append(allocator, .{
...@@ -289,6 +271,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf...@@ -289,6 +271,25 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf
289 return self;271 return self;
290}272}
291273
274pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
275 const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
276 0 ... 32 => .p32,
277 33 ... 64 => .p64,
278 else => return error.UnsupportedELFArchitecture,
279 };
280 const self = try gpa.create(Elf);
281 self.* = .{
282 .base = .{
283 .tag = .elf,
284 .options = options,
285 .allocator = gpa,
286 .file = null,
287 },
288 .ptr_width = ptr_width,
289 };
290 return self;
291}
292
292pub fn releaseLock(self: *Elf) void {293pub fn releaseLock(self: *Elf) void {
293 if (self.lock) |*lock| {294 if (self.lock) |*lock| {
294 lock.release();295 lock.release();
src-self-hosted/link/MachO.zig+13-42
...@@ -135,7 +135,7 @@ pub const SrcFn = struct {...@@ -135,7 +135,7 @@ pub const SrcFn = struct {
135 pub const empty = SrcFn{};135 pub const empty = SrcFn{};
136};136};
137137
138pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*File {138pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO {
139 assert(options.object_format == .macho);139 assert(options.object_format == .macho);
140140
141 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO141 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO
...@@ -148,61 +148,32 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -148,61 +148,32 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
148 });148 });
149 errdefer file.close();149 errdefer file.close();
150150
151 var macho_file = try allocator.create(MachO);151 const self = try createEmpty(allocator, options);
152 errdefer allocator.destroy(macho_file);152 errdefer self.base.destroy();
153153
154 macho_file.* = openFile(allocator, file, options) catch |err| switch (err) {154 self.base.file = file;
155 error.IncrFailed => try createFile(allocator, file, options),
156 else => |e| return e,
157 };
158
159 return &macho_file.base;
160}
161155
162/// Returns error.IncrFailed if incremental update could not be performed.
163fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {
164 switch (options.output_mode) {156 switch (options.output_mode) {
165 .Exe => {},157 .Exe => {},
166 .Obj => {},158 .Obj => {},
167 .Lib => return error.IncrFailed,159 .Lib => return error.TODOImplementWritingLibFiles,
168 }160 }
169 var self: MachO = .{
170 .base = .{
171 .file = file,
172 .tag = .macho,
173 .options = options,
174 .allocator = allocator,
175 },
176 };
177 errdefer self.deinit();
178161
179 // TODO implement reading the macho file162 try self.populateMissingMetadata();
180 return error.IncrFailed;
181 //try self.populateMissingMetadata();
182 //return self;
183}
184163
185/// Truncates the existing file contents and overwrites the contents.164 return self;
186/// Returns an error if `file` is not already open with +read +write +seek abilities.165}
187fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !MachO {
188 switch (options.output_mode) {
189 .Exe => {},
190 .Obj => {},
191 .Lib => return error.TODOImplementWritingLibFiles,
192 }
193166
194 var self: MachO = .{167pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
168 const self = try gpa.create(MachO);
169 self.* = .{
195 .base = .{170 .base = .{
196 .file = file,
197 .tag = .macho,171 .tag = .macho,
198 .options = options,172 .options = options,
199 .allocator = allocator,173 .allocator = gpa,
174 .file = null,
200 },175 },
201 };176 };
202 errdefer self.deinit();
203
204 try self.populateMissingMetadata();
205
206 return self;177 return self;
207}178}
208179
src-self-hosted/link/Wasm.zig+13-7
...@@ -50,7 +50,7 @@ base: link.File,...@@ -50,7 +50,7 @@ base: link.File,
50/// TODO: can/should we access some data structure in Module directly?50/// TODO: can/should we access some data structure in Module directly?
51funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},51funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
5252
53pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*link.File {53pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
54 assert(options.object_format == .wasm);54 assert(options.object_format == .wasm);
5555
56 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO56 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...@@ -60,21 +60,27 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
60 const file = try options.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });60 const file = try options.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
61 errdefer file.close();61 errdefer file.close();
6262
63 const wasm = try allocator.create(Wasm);63 const wasm = try createEmpty(allocator, options);
64 errdefer allocator.destroy(wasm);64 errdefer wasm.base.destroy();
65
66 wasm.base.file = file;
6567
66 try file.writeAll(&(spec.magic ++ spec.version));68 try file.writeAll(&(spec.magic ++ spec.version));
6769
70 return wasm;
71}
72
73pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm {
74 const wasm = try gpa.create(Wasm);
68 wasm.* = .{75 wasm.* = .{
69 .base = .{76 .base = .{
70 .tag = .wasm,77 .tag = .wasm,
71 .options = options,78 .options = options,
72 .file = file,79 .file = null,
73 .allocator = allocator,80 .allocator = gpa,
74 },81 },
75 };82 };
7683 return wasm;
77 return &wasm.base;
78}84}
7985
80pub fn deinit(self: *Wasm) void {86pub fn deinit(self: *Wasm) void {