authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-03 20:03:22-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-03 20:03:22-07:00
logff66a18555dc6c00bd928f48462c3fc44f86ab6c
treee30f1af8d95b1e634d4076cbc521ee2b35d5c90c
parent81fa31c05456facea1d1963a1e7f665351fb248d

linker: fix build-obj and -fno-emit-bin

This commit fixes two problems: * `zig build-obj` regressed from the cache-mode branch. It would crash because it assumed that dirname on the emit bin path would not be null. This assumption was invalid when outputting to the current working directory - a pretty common use case for `zig build-obj`. * When using the LLVM backend, `-fno-emit-bin` combined with any other kind of emitting, such as `-femit-asm`, emitted nothing. Both issues are now fixed.

7 files changed, 129 insertions(+), 73 deletions(-)

src/codegen/llvm.zig+14-18
...@@ -181,8 +181,6 @@ pub const Object = struct {...@@ -181,8 +181,6 @@ pub const Object = struct {
181 /// The backing memory for `type_map`. Periodically garbage collected after flush().181 /// The backing memory for `type_map`. Periodically garbage collected after flush().
182 /// The code for doing the periodical GC is not yet implemented.182 /// The code for doing the periodical GC is not yet implemented.
183 type_map_arena: std.heap.ArenaAllocator,183 type_map_arena: std.heap.ArenaAllocator,
184 /// Where to put the output object file, relative to bin_file.options.emit directory.
185 sub_path: []const u8,
186184
187 pub const TypeMap = std.HashMapUnmanaged(185 pub const TypeMap = std.HashMapUnmanaged(
188 Type,186 Type,
...@@ -191,14 +189,14 @@ pub const Object = struct {...@@ -191,14 +189,14 @@ pub const Object = struct {
191 std.hash_map.default_max_load_percentage,189 std.hash_map.default_max_load_percentage,
192 );190 );
193191
194 pub fn create(gpa: Allocator, sub_path: []const u8, options: link.Options) !*Object {192 pub fn create(gpa: Allocator, options: link.Options) !*Object {
195 const obj = try gpa.create(Object);193 const obj = try gpa.create(Object);
196 errdefer gpa.destroy(obj);194 errdefer gpa.destroy(obj);
197 obj.* = try Object.init(gpa, sub_path, options);195 obj.* = try Object.init(gpa, options);
198 return obj;196 return obj;
199 }197 }
200198
201 pub fn init(gpa: Allocator, sub_path: []const u8, options: link.Options) !Object {199 pub fn init(gpa: Allocator, options: link.Options) !Object {
202 const context = llvm.Context.create();200 const context = llvm.Context.create();
203 errdefer context.dispose();201 errdefer context.dispose();
204202
...@@ -271,7 +269,6 @@ pub const Object = struct {...@@ -271,7 +269,6 @@ pub const Object = struct {
271 .decl_map = .{},269 .decl_map = .{},
272 .type_map = .{},270 .type_map = .{},
273 .type_map_arena = std.heap.ArenaAllocator.init(gpa),271 .type_map_arena = std.heap.ArenaAllocator.init(gpa),
274 .sub_path = sub_path,
275 };272 };
276 }273 }
277274
...@@ -324,19 +321,22 @@ pub const Object = struct {...@@ -324,19 +321,22 @@ pub const Object = struct {
324 const mod = comp.bin_file.options.module.?;321 const mod = comp.bin_file.options.module.?;
325 const cache_dir = mod.zig_cache_artifact_directory;322 const cache_dir = mod.zig_cache_artifact_directory;
326323
327 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit| blk: {324 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit|
328 const full_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path});325 try emit.basenamePath(arena, try arena.dupeZ(u8, comp.bin_file.intermediary_basename.?))
329 break :blk try std.fs.path.joinZ(arena, &.{326 else
330 std.fs.path.dirname(full_out_path).?, self.sub_path,327 null;
331 });
332 } else null;
333328
334 const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);329 const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);
335 const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);330 const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);
336 const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir);331 const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir);
337332
338 const debug_emit_path = emit_bin_path orelse "(none)";333 const emit_asm_msg = emit_asm_path orelse "(none)";
339 log.debug("emit LLVM object to {s}", .{debug_emit_path});334 const emit_bin_msg = emit_bin_path orelse "(none)";
335 const emit_llvm_ir_msg = emit_llvm_ir_path orelse "(none)";
336 const emit_llvm_bc_msg = emit_llvm_bc_path orelse "(none)";
337 log.debug("emit LLVM object asm={s} bin={s} ir={s} bc={s}", .{
338 emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg,
339 });
340340
341 var error_message: [*:0]const u8 = undefined;341 var error_message: [*:0]const u8 = undefined;
342 if (self.target_machine.emitToFile(342 if (self.target_machine.emitToFile(
...@@ -354,10 +354,6 @@ pub const Object = struct {...@@ -354,10 +354,6 @@ pub const Object = struct {
354 )) {354 )) {
355 defer llvm.disposeMessage(error_message);355 defer llvm.disposeMessage(error_message);
356356
357 const emit_asm_msg = emit_asm_path orelse "(none)";
358 const emit_bin_msg = emit_bin_path orelse "(none)";
359 const emit_llvm_ir_msg = emit_llvm_ir_path orelse "(none)";
360 const emit_llvm_bc_msg = emit_llvm_bc_path orelse "(none)";
361 log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{357 log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{
362 emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg,358 emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg,
363 error_message,359 error_message,
src/glibc.zig+2-2
...@@ -225,7 +225,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -225,7 +225,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
225 });225 });
226 },226 },
227 .scrt1_o => {227 .scrt1_o => {
228 const start_os: Compilation.CSourceFile = blk: {228 const start_o: Compilation.CSourceFile = blk: {
229 var args = std.ArrayList([]const u8).init(arena);229 var args = std.ArrayList([]const u8).init(arena);
230 try add_include_dirs(comp, arena, &args);230 try add_include_dirs(comp, arena, &args);
231 try args.appendSlice(&[_][]const u8{231 try args.appendSlice(&[_][]const u8{
...@@ -266,7 +266,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -266,7 +266,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
266 .extra_flags = args.items,266 .extra_flags = args.items,
267 };267 };
268 };268 };
269 return comp.build_crt_file("Scrt1", .Obj, &[_]Compilation.CSourceFile{ start_os, abi_note_o });269 return comp.build_crt_file("Scrt1", .Obj, &[_]Compilation.CSourceFile{ start_o, abi_note_o });
270 },270 },
271 .libc_nonshared_a => {271 .libc_nonshared_a => {
272 const target = comp.getTarget();272 const target = comp.getTarget();
src/link.zig+16-2
...@@ -43,6 +43,21 @@ pub const Emit = struct {...@@ -43,6 +43,21 @@ pub const Emit = struct {
43 directory: Compilation.Directory,43 directory: Compilation.Directory,
44 /// Path to the output file, relative to `directory`.44 /// Path to the output file, relative to `directory`.
45 sub_path: []const u8,45 sub_path: []const u8,
46
47 /// Returns the full path to `basename` if it were in the same directory as the
48 /// `Emit` sub_path.
49 pub fn basenamePath(emit: Emit, arena: Allocator, basename: [:0]const u8) ![:0]const u8 {
50 const full_path = if (emit.directory.path) |p|
51 try fs.path.join(arena, &[_][]const u8{ p, emit.sub_path })
52 else
53 emit.sub_path;
54
55 if (fs.path.dirname(full_path)) |dirname| {
56 return try fs.path.joinZ(arena, &.{ dirname, basename });
57 } else {
58 return basename;
59 }
60 }
46};61};
4762
48pub const Options = struct {63pub const Options = struct {
...@@ -533,9 +548,8 @@ pub const File = struct {...@@ -533,9 +548,8 @@ pub const File = struct {
533 /// Commit pending changes and write headers. Takes into account final output mode548 /// Commit pending changes and write headers. Takes into account final output mode
534 /// and `use_lld`, not only `effectiveOutputMode`.549 /// and `use_lld`, not only `effectiveOutputMode`.
535 pub fn flush(base: *File, comp: *Compilation) !void {550 pub fn flush(base: *File, comp: *Compilation) !void {
536 const emit = base.options.emit orelse return; // -fno-emit-bin
537
538 if (comp.clang_preprocessor_mode == .yes) {551 if (comp.clang_preprocessor_mode == .yes) {
552 const emit = base.options.emit orelse return; // -fno-emit-bin
539 // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)553 // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
540 // Until then, we do `lld -r -o output.o input.o` even though the output is the same554 // Until then, we do `lld -r -o output.o input.o` even though the output is the same
541 // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file555 // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file
src/link/Coff.zig+18-8
...@@ -129,11 +129,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -129,11 +129,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
129 assert(options.object_format == .coff);129 assert(options.object_format == .coff);
130130
131 if (build_options.have_llvm and options.use_llvm) {131 if (build_options.have_llvm and options.use_llvm) {
132 const self = try createEmpty(allocator, options);132 return createEmpty(allocator, options);
133 errdefer self.base.destroy();
134
135 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
136 return self;
137 }133 }
138134
139 const file = try options.emit.?.directory.handle.createFile(sub_path, .{135 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
...@@ -403,6 +399,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {...@@ -403,6 +399,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
403 else => return error.UnsupportedCOFFArchitecture,399 else => return error.UnsupportedCOFFArchitecture,
404 };400 };
405 const self = try gpa.create(Coff);401 const self = try gpa.create(Coff);
402 errdefer gpa.destroy(self);
406 self.* = .{403 self.* = .{
407 .base = .{404 .base = .{
408 .tag = .coff,405 .tag = .coff,
...@@ -412,6 +409,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {...@@ -412,6 +409,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
412 },409 },
413 .ptr_width = ptr_width,410 .ptr_width = ptr_width,
414 };411 };
412 if (build_options.have_llvm and options.use_llvm) {
413 self.llvm_object = try LlvmObject.create(gpa, options);
414 }
415 return self;415 return self;
416}416}
417417
...@@ -817,6 +817,14 @@ pub fn updateDeclExports(...@@ -817,6 +817,14 @@ pub fn updateDeclExports(
817}817}
818818
819pub fn flush(self: *Coff, comp: *Compilation) !void {819pub fn flush(self: *Coff, comp: *Compilation) !void {
820 if (self.base.options.emit == null) {
821 if (build_options.have_llvm) {
822 if (self.llvm_object) |llvm_object| {
823 return try llvm_object.flushModule(comp);
824 }
825 }
826 return;
827 }
820 if (build_options.have_llvm and self.base.options.use_lld) {828 if (build_options.have_llvm and self.base.options.use_lld) {
821 return self.linkWithLLD(comp);829 return self.linkWithLLD(comp);
822 } else {830 } else {
...@@ -905,9 +913,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -905,9 +913,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
905913
906 try self.flushModule(comp);914 try self.flushModule(comp);
907915
908 break :blk try fs.path.join(arena, &.{916 if (fs.path.dirname(full_out_path)) |dirname| {
909 fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?,917 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
910 });918 } else {
919 break :blk self.base.intermediary_basename.?;
920 }
911 } else null;921 } else null;
912922
913 const is_lib = self.base.options.output_mode == .Lib;923 const is_lib = self.base.options.output_mode == .Lib;
src/link/Elf.zig+36-25
...@@ -241,11 +241,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -241,11 +241,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
241 assert(options.object_format == .elf);241 assert(options.object_format == .elf);
242242
243 if (build_options.have_llvm and options.use_llvm) {243 if (build_options.have_llvm and options.use_llvm) {
244 const self = try createEmpty(allocator, options);244 return createEmpty(allocator, options);
245 errdefer self.base.destroy();
246
247 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
248 return self;
249 }245 }
250246
251 const file = try options.emit.?.directory.handle.createFile(sub_path, .{247 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
...@@ -298,6 +294,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -298,6 +294,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
298 };294 };
299 const self = try gpa.create(Elf);295 const self = try gpa.create(Elf);
300 errdefer gpa.destroy(self);296 errdefer gpa.destroy(self);
297
301 self.* = .{298 self.* = .{
302 .base = .{299 .base = .{
303 .tag = .elf,300 .tag = .elf,
...@@ -307,9 +304,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -307,9 +304,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
307 },304 },
308 .ptr_width = ptr_width,305 .ptr_width = ptr_width,
309 };306 };
310 // TODO get rid of the sub_path parameter to LlvmObject.create307 if (build_options.have_llvm and options.use_llvm) {
311 // and create the llvm_object here. Also openPath needs to308 self.llvm_object = try LlvmObject.create(gpa, options);
312 // not override this field or there will be a memory leak.309 }
313 return self;310 return self;
314}311}
315312
...@@ -788,14 +785,21 @@ pub const abbrev_pad1 = 5;...@@ -788,14 +785,21 @@ pub const abbrev_pad1 = 5;
788pub const abbrev_parameter = 6;785pub const abbrev_parameter = 6;
789786
790pub fn flush(self: *Elf, comp: *Compilation) !void {787pub fn flush(self: *Elf, comp: *Compilation) !void {
791 if (build_options.have_llvm and self.base.options.use_lld) {788 if (self.base.options.emit == null) {
792 return self.linkWithLLD(comp);789 if (build_options.have_llvm) {
793 } else {790 if (self.llvm_object) |llvm_object| {
794 switch (self.base.options.effectiveOutputMode()) {791 return try llvm_object.flushModule(comp);
795 .Exe, .Obj => {},792 }
796 .Lib => return error.TODOImplementWritingLibFiles,
797 }793 }
798 return self.flushModule(comp);794 return;
795 }
796 const use_lld = build_options.have_llvm and self.base.options.use_lld;
797 if (use_lld) {
798 return self.linkWithLLD(comp);
799 }
800 switch (self.base.options.output_mode) {
801 .Exe, .Obj => return self.flushModule(comp),
802 .Lib => return error.TODOImplementWritingLibFiles,
799 }803 }
800}804}
801805
...@@ -803,8 +807,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -803,8 +807,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
803 const tracy = trace(@src());807 const tracy = trace(@src());
804 defer tracy.end();808 defer tracy.end();
805809
806 if (build_options.have_llvm)810 if (build_options.have_llvm) {
807 if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);811 if (self.llvm_object) |llvm_object| {
812 return try llvm_object.flushModule(comp);
813 }
814 }
808815
809 // TODO This linker code currently assumes there is only 1 compilation unit and it816 // TODO This linker code currently assumes there is only 1 compilation unit and it
810 // corresponds to the Zig source code.817 // corresponds to the Zig source code.
...@@ -1327,9 +1334,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1327,9 +1334,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13271334
1328 try self.flushModule(comp);1335 try self.flushModule(comp);
13291336
1330 break :blk try fs.path.join(arena, &.{1337 if (fs.path.dirname(full_out_path)) |dirname| {
1331 fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?,1338 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
1332 });1339 } else {
1340 break :blk self.base.intermediary_basename.?;
1341 }
1333 } else null;1342 } else null;
13341343
1335 const is_obj = self.base.options.output_mode == .Obj;1344 const is_obj = self.base.options.output_mode == .Obj;
...@@ -1446,10 +1455,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1446,10 +1455,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1446 };1455 };
1447 }1456 }
14481457
1449 // Due to a deficiency in LLD, we need to special-case BPF to a simple file copy when generating1458 // Due to a deficiency in LLD, we need to special-case BPF to a simple file
1450 // relocatables. Normally, we would expect `lld -r` to work. However, because LLD wants to resolve1459 // copy when generating relocatables. Normally, we would expect `lld -r` to work.
1451 // BPF relocations which it shouldn't, it fails before even generating the relocatable.1460 // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails
1452 if (self.base.options.output_mode == .Obj and (self.base.options.lto or target.isBpfFreestanding())) {1461 // before even generating the relocatable.
1462 if (self.base.options.output_mode == .Obj and
1463 (self.base.options.lto or target.isBpfFreestanding()))
1464 {
1453 // In this case we must do a simple file copy1465 // In this case we must do a simple file copy
1454 // here. TODO: think carefully about how we can avoid this redundant operation when doing1466 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1455 // build-obj. See also the corresponding TODO in linkAsArchive.1467 // build-obj. See also the corresponding TODO in linkAsArchive.
...@@ -1473,7 +1485,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1473,7 +1485,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1473 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});1485 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
1474 }1486 }
1475 } else {1487 } else {
1476
1477 // Create an LLD command line and invoke it.1488 // Create an LLD command line and invoke it.
1478 var argv = std.ArrayList([]const u8).init(self.base.allocator);1489 var argv = std.ArrayList([]const u8).init(self.base.allocator);
1479 defer argv.deinit();1490 defer argv.deinit();
src/link/MachO.zig+22-7
...@@ -313,11 +313,10 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -313,11 +313,10 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
313 // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`,313 // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`,
314 // we also want to put the intermediary object file in the cache while the314 // we also want to put the intermediary object file in the cache while the
315 // main emit directory is the cwd.315 // main emit directory is the cwd.
316 const sub_path = try std.fmt.allocPrint(allocator, "{s}{s}", .{316 self.llvm_object = try LlvmObject.create(allocator, options);
317 self.base.intermediary_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{
317 emit.sub_path, options.object_format.fileExt(options.target.cpu.arch),318 emit.sub_path, options.object_format.fileExt(options.target.cpu.arch),
318 });319 });
319 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
320 self.base.intermediary_basename = sub_path;
321 }320 }
322321
323 if (options.output_mode == .Lib and322 if (options.output_mode == .Lib and
...@@ -373,7 +372,6 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -373,7 +372,6 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
373}372}
374373
375pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {374pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
376 const self = try gpa.create(MachO);
377 const cpu_arch = options.target.cpu.arch;375 const cpu_arch = options.target.cpu.arch;
378 const os_tag = options.target.os.tag;376 const os_tag = options.target.os.tag;
379 const abi = options.target.abi;377 const abi = options.target.abi;
...@@ -383,6 +381,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {...@@ -383,6 +381,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
383 const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator);381 const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator);
384 const needs_prealloc = !(build_options.is_stage1 and options.use_stage1);382 const needs_prealloc = !(build_options.is_stage1 and options.use_stage1);
385383
384 const self = try gpa.create(MachO);
385 errdefer gpa.destroy(self);
386
386 self.* = .{387 self.* = .{
387 .base = .{388 .base = .{
388 .tag = .macho,389 .tag = .macho,
...@@ -395,10 +396,22 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {...@@ -395,10 +396,22 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
395 .needs_prealloc = needs_prealloc,396 .needs_prealloc = needs_prealloc,
396 };397 };
397398
399 if (build_options.have_llvm and options.use_llvm) {
400 self.llvm_object = try LlvmObject.create(gpa, options);
401 }
402
398 return self;403 return self;
399}404}
400405
401pub fn flush(self: *MachO, comp: *Compilation) !void {406pub fn flush(self: *MachO, comp: *Compilation) !void {
407 if (self.base.options.emit == null) {
408 if (build_options.have_llvm) {
409 if (self.llvm_object) |llvm_object| {
410 return try llvm_object.flushModule(comp);
411 }
412 }
413 return;
414 }
402 if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) {415 if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) {
403 if (build_options.have_llvm) {416 if (build_options.have_llvm) {
404 return self.base.linkAsArchive(comp);417 return self.base.linkAsArchive(comp);
...@@ -449,9 +462,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -449,9 +462,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
449462
450 try self.flushObject(comp);463 try self.flushObject(comp);
451464
452 break :blk try fs.path.join(arena, &.{465 if (fs.path.dirname(full_out_path)) |dirname| {
453 fs.path.dirname(full_out_path).?, obj_basename,466 break :blk try fs.path.join(arena, &.{ dirname, obj_basename });
454 });467 } else {
468 break :blk obj_basename;
469 }
455 } else null;470 } else null;
456471
457 const is_lib = self.base.options.output_mode == .Lib;472 const is_lib = self.base.options.output_mode == .Lib;
src/link/Wasm.zig+21-11
...@@ -101,11 +101,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -101,11 +101,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
101 assert(options.object_format == .wasm);101 assert(options.object_format == .wasm);
102102
103 if (build_options.have_llvm and options.use_llvm) {103 if (build_options.have_llvm and options.use_llvm) {
104 const self = try createEmpty(allocator, options);104 return createEmpty(allocator, options);
105 errdefer self.base.destroy();
106
107 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
108 return self;
109 }105 }
110106
111 // TODO: read the file and keep valid parts instead of truncating107 // TODO: read the file and keep valid parts instead of truncating
...@@ -139,8 +135,9 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -139,8 +135,9 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
139}135}
140136
141pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {137pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
142 const wasm_bin = try gpa.create(Wasm);138 const self = try gpa.create(Wasm);
143 wasm_bin.* = .{139 errdefer gpa.destroy(self);
140 self.* = .{
144 .base = .{141 .base = .{
145 .tag = .wasm,142 .tag = .wasm,
146 .options = options,143 .options = options,
...@@ -148,7 +145,10 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {...@@ -148,7 +145,10 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
148 .allocator = gpa,145 .allocator = gpa,
149 },146 },
150 };147 };
151 return wasm_bin;148 if (build_options.have_llvm and options.use_llvm) {
149 self.llvm_object = try LlvmObject.create(gpa, options);
150 }
151 return self;
152}152}
153153
154pub fn deinit(self: *Wasm) void {154pub fn deinit(self: *Wasm) void {
...@@ -576,6 +576,14 @@ fn resetState(self: *Wasm) void {...@@ -576,6 +576,14 @@ fn resetState(self: *Wasm) void {
576}576}
577577
578pub fn flush(self: *Wasm, comp: *Compilation) !void {578pub fn flush(self: *Wasm, comp: *Compilation) !void {
579 if (self.base.options.emit == null) {
580 if (build_options.have_llvm) {
581 if (self.llvm_object) |llvm_object| {
582 return try llvm_object.flushModule(comp);
583 }
584 }
585 return;
586 }
579 if (build_options.have_llvm and self.base.options.use_lld) {587 if (build_options.have_llvm and self.base.options.use_lld) {
580 return self.linkWithLLD(comp);588 return self.linkWithLLD(comp);
581 } else {589 } else {
...@@ -1075,9 +1083,11 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -1075,9 +1083,11 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
10751083
1076 try self.flushModule(comp);1084 try self.flushModule(comp);
10771085
1078 break :blk try fs.path.join(arena, &.{1086 if (fs.path.dirname(full_out_path)) |dirname| {
1079 fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?,1087 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
1080 });1088 } else {
1089 break :blk self.base.intermediary_basename.?;
1090 }
1081 } else null;1091 } else null;
10821092
1083 const is_obj = self.base.options.output_mode == .Obj;1093 const is_obj = self.base.options.output_mode == .Obj;