authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-04 04:54:30-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-04 04:54:30-05:00
log5c228765f1094d30e64d13c0077c67b2867ecd6a
tree3987430030804d36895f680f5a5573db6b7e66cb
parent3c87d4e14ec6b0c2442372cbcc60174d654edddc
parentb6d6152e6514dcb4e67750cbb22b90b38ebedf49
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10504 from ziglang/linker-plumbing

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

7 files changed, 138 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+21-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,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {...@@ -412,6 +409,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
412 },409 },
413 .ptr_width = ptr_width,410 .ptr_width = ptr_width,
414 };411 };
412
413 const use_llvm = build_options.have_llvm and options.use_llvm;
414 const use_stage1 = build_options.is_stage1 and options.use_stage1;
415 if (use_llvm and !use_stage1) {
416 self.llvm_object = try LlvmObject.create(gpa, options);
417 }
415 return self;418 return self;
416}419}
417420
...@@ -817,6 +820,14 @@ pub fn updateDeclExports(...@@ -817,6 +820,14 @@ pub fn updateDeclExports(
817}820}
818821
819pub fn flush(self: *Coff, comp: *Compilation) !void {822pub fn flush(self: *Coff, comp: *Compilation) !void {
823 if (self.base.options.emit == null) {
824 if (build_options.have_llvm) {
825 if (self.llvm_object) |llvm_object| {
826 return try llvm_object.flushModule(comp);
827 }
828 }
829 return;
830 }
820 if (build_options.have_llvm and self.base.options.use_lld) {831 if (build_options.have_llvm and self.base.options.use_lld) {
821 return self.linkWithLLD(comp);832 return self.linkWithLLD(comp);
822 } else {833 } else {
...@@ -905,9 +916,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -905,9 +916,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
905916
906 try self.flushModule(comp);917 try self.flushModule(comp);
907918
908 break :blk try fs.path.join(arena, &.{919 if (fs.path.dirname(full_out_path)) |dirname| {
909 fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?,920 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
910 });921 } else {
922 break :blk self.base.intermediary_basename.?;
923 }
911 } else null;924 } else null;
912925
913 const is_lib = self.base.options.output_mode == .Lib;926 const is_lib = self.base.options.output_mode == .Lib;
src/link/Elf.zig+38-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,11 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -307,9 +304,11 @@ 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 const use_llvm = build_options.have_llvm and options.use_llvm;
311 // and create the llvm_object here. Also openPath needs to308 const use_stage1 = build_options.is_stage1 and options.use_stage1;
312 // not override this field or there will be a memory leak.309 if (use_llvm and !use_stage1) {
310 self.llvm_object = try LlvmObject.create(gpa, options);
311 }
313 return self;312 return self;
314}313}
315314
...@@ -788,14 +787,21 @@ pub const abbrev_pad1 = 5;...@@ -788,14 +787,21 @@ pub const abbrev_pad1 = 5;
788pub const abbrev_parameter = 6;787pub const abbrev_parameter = 6;
789788
790pub fn flush(self: *Elf, comp: *Compilation) !void {789pub fn flush(self: *Elf, comp: *Compilation) !void {
791 if (build_options.have_llvm and self.base.options.use_lld) {790 if (self.base.options.emit == null) {
792 return self.linkWithLLD(comp);791 if (build_options.have_llvm) {
793 } else {792 if (self.llvm_object) |llvm_object| {
794 switch (self.base.options.effectiveOutputMode()) {793 return try llvm_object.flushModule(comp);
795 .Exe, .Obj => {},794 }
796 .Lib => return error.TODOImplementWritingLibFiles,
797 }795 }
798 return self.flushModule(comp);796 return;
797 }
798 const use_lld = build_options.have_llvm and self.base.options.use_lld;
799 if (use_lld) {
800 return self.linkWithLLD(comp);
801 }
802 switch (self.base.options.output_mode) {
803 .Exe, .Obj => return self.flushModule(comp),
804 .Lib => return error.TODOImplementWritingLibFiles,
799 }805 }
800}806}
801807
...@@ -803,8 +809,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -803,8 +809,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
803 const tracy = trace(@src());809 const tracy = trace(@src());
804 defer tracy.end();810 defer tracy.end();
805811
806 if (build_options.have_llvm)812 if (build_options.have_llvm) {
807 if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);813 if (self.llvm_object) |llvm_object| {
814 return try llvm_object.flushModule(comp);
815 }
816 }
808817
809 // TODO This linker code currently assumes there is only 1 compilation unit and it818 // TODO This linker code currently assumes there is only 1 compilation unit and it
810 // corresponds to the Zig source code.819 // corresponds to the Zig source code.
...@@ -1327,9 +1336,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1327,9 +1336,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13271336
1328 try self.flushModule(comp);1337 try self.flushModule(comp);
13291338
1330 break :blk try fs.path.join(arena, &.{1339 if (fs.path.dirname(full_out_path)) |dirname| {
1331 fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?,1340 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
1332 });1341 } else {
1342 break :blk self.base.intermediary_basename.?;
1343 }
1333 } else null;1344 } else null;
13341345
1335 const is_obj = self.base.options.output_mode == .Obj;1346 const is_obj = self.base.options.output_mode == .Obj;
...@@ -1446,10 +1457,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1446,10 +1457,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1446 };1457 };
1447 }1458 }
14481459
1449 // Due to a deficiency in LLD, we need to special-case BPF to a simple file copy when generating1460 // 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 resolve1461 // 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.1462 // 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())) {1463 // before even generating the relocatable.
1464 if (self.base.options.output_mode == .Obj and
1465 (self.base.options.lto or target.isBpfFreestanding()))
1466 {
1453 // In this case we must do a simple file copy1467 // In this case we must do a simple file copy
1454 // here. TODO: think carefully about how we can avoid this redundant operation when doing1468 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1455 // build-obj. See also the corresponding TODO in linkAsArchive.1469 // build-obj. See also the corresponding TODO in linkAsArchive.
...@@ -1473,7 +1487,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1473,7 +1487,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1473 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});1487 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
1474 }1488 }
1475 } else {1489 } else {
1476
1477 // Create an LLD command line and invoke it.1490 // Create an LLD command line and invoke it.
1478 var argv = std.ArrayList([]const u8).init(self.base.allocator);1491 var argv = std.ArrayList([]const u8).init(self.base.allocator);
1479 defer argv.deinit();1492 defer argv.deinit();
src/link/MachO.zig+24-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,24 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {...@@ -395,10 +396,24 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
395 .needs_prealloc = needs_prealloc,396 .needs_prealloc = needs_prealloc,
396 };397 };
397398
399 const use_llvm = build_options.have_llvm and options.use_llvm;
400 const use_stage1 = build_options.is_stage1 and options.use_stage1;
401 if (use_llvm and !use_stage1) {
402 self.llvm_object = try LlvmObject.create(gpa, options);
403 }
404
398 return self;405 return self;
399}406}
400407
401pub fn flush(self: *MachO, comp: *Compilation) !void {408pub fn flush(self: *MachO, comp: *Compilation) !void {
409 if (self.base.options.emit == null) {
410 if (build_options.have_llvm) {
411 if (self.llvm_object) |llvm_object| {
412 return try llvm_object.flushModule(comp);
413 }
414 }
415 return;
416 }
402 if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) {417 if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) {
403 if (build_options.have_llvm) {418 if (build_options.have_llvm) {
404 return self.base.linkAsArchive(comp);419 return self.base.linkAsArchive(comp);
...@@ -449,9 +464,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -449,9 +464,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
449464
450 try self.flushObject(comp);465 try self.flushObject(comp);
451466
452 break :blk try fs.path.join(arena, &.{467 if (fs.path.dirname(full_out_path)) |dirname| {
453 fs.path.dirname(full_out_path).?, obj_basename,468 break :blk try fs.path.join(arena, &.{ dirname, obj_basename });
454 });469 } else {
470 break :blk obj_basename;
471 }
455 } else null;472 } else null;
456473
457 const is_lib = self.base.options.output_mode == .Lib;474 const is_lib = self.base.options.output_mode == .Lib;
src/link/Wasm.zig+23-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,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {...@@ -148,7 +145,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
148 .allocator = gpa,145 .allocator = gpa,
149 },146 },
150 };147 };
151 return wasm_bin;148 const use_llvm = build_options.have_llvm and options.use_llvm;
149 const use_stage1 = build_options.is_stage1 and options.use_stage1;
150 if (use_llvm and !use_stage1) {
151 self.llvm_object = try LlvmObject.create(gpa, options);
152 }
153 return self;
152}154}
153155
154pub fn deinit(self: *Wasm) void {156pub fn deinit(self: *Wasm) void {
...@@ -576,6 +578,14 @@ fn resetState(self: *Wasm) void {...@@ -576,6 +578,14 @@ fn resetState(self: *Wasm) void {
576}578}
577579
578pub fn flush(self: *Wasm, comp: *Compilation) !void {580pub fn flush(self: *Wasm, comp: *Compilation) !void {
581 if (self.base.options.emit == null) {
582 if (build_options.have_llvm) {
583 if (self.llvm_object) |llvm_object| {
584 return try llvm_object.flushModule(comp);
585 }
586 }
587 return;
588 }
579 if (build_options.have_llvm and self.base.options.use_lld) {589 if (build_options.have_llvm and self.base.options.use_lld) {
580 return self.linkWithLLD(comp);590 return self.linkWithLLD(comp);
581 } else {591 } else {
...@@ -1075,9 +1085,11 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -1075,9 +1085,11 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
10751085
1076 try self.flushModule(comp);1086 try self.flushModule(comp);
10771087
1078 break :blk try fs.path.join(arena, &.{1088 if (fs.path.dirname(full_out_path)) |dirname| {
1079 fs.path.dirname(full_out_path).?, self.base.intermediary_basename.?,1089 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
1080 });1090 } else {
1091 break :blk self.base.intermediary_basename.?;
1092 }
1081 } else null;1093 } else null;
10821094
1083 const is_obj = self.base.options.output_mode == .Obj;1095 const is_obj = self.base.options.output_mode == .Obj;