From ef7fa76001f873824b0f64dfc2172ed2f304c348 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 24 Sep 2021 00:54:44 -0700 Subject: [PATCH] stage2: enable building freestanding libc with LLVM backend * LLVM backend: respect `sub_path` just like the other stage2 backends do. * Compilation has some new logic to only emit work queue jobs for building stuff when it believes itself to be capable. The linker backends no longer have duplicate logic; instead they respect the optional bit on the respective asset. --- src/Compilation.zig | 40 ++++++++++++++++++++++------------------ src/codegen/llvm.zig | 28 +++++++++++++--------------- src/link.zig | 3 +++ src/link/Coff.zig | 28 +++++++++++++--------------- src/link/Elf.zig | 18 +++++++----------- src/link/MachO.zig | 2 +- src/link/Wasm.zig | 2 +- 7 files changed, 60 insertions(+), 61 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 140ed40d99..00a737c2be 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1574,25 +1574,29 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for // stage1 but not stage2. - if (comp.bin_file.options.use_stage1) { - if (comp.bin_file.options.include_compiler_rt) { - if (is_exe_or_dyn_lib) { - try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} }); - } else if (options.output_mode != .Obj) { - // If build-obj with -fcompiler-rt is requested, that is handled specially - // elsewhere. In this case we are making a static library, so we ask - // for a compiler-rt object to put in it. - try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} }); - } + const capable_of_building_compiler_rt = comp.bin_file.options.use_stage1; + const capable_of_building_ssp = comp.bin_file.options.use_stage1; + const capable_of_building_zig_libc = comp.bin_file.options.use_stage1 or + comp.bin_file.options.use_llvm; + + if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) { + if (is_exe_or_dyn_lib) { + try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} }); + } else if (options.output_mode != .Obj) { + // If build-obj with -fcompiler-rt is requested, that is handled specially + // elsewhere. In this case we are making a static library, so we ask + // for a compiler-rt object to put in it. + try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} }); } - if (needs_c_symbols) { - // MinGW provides no libssp, use our own implementation. - if (comp.getTarget().isMinGW()) { - try comp.work_queue.writeItem(.{ .libssp = {} }); - } - if (!comp.bin_file.options.link_libc) { - try comp.work_queue.writeItem(.{ .zig_libc = {} }); - } + } + if (needs_c_symbols) { + // MinGW provides no libssp, use our own implementation. + if (comp.getTarget().isMinGW() and capable_of_building_ssp) { + try comp.work_queue.writeItem(.{ .libssp = {} }); + } + + if (!comp.bin_file.options.link_libc and capable_of_building_zig_libc) { + try comp.work_queue.writeItem(.{ .zig_libc = {} }); } } } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 92524a09b7..650628d8c2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -164,15 +164,17 @@ pub const Object = struct { /// * it works for functions not all globals. /// Therefore, this table keeps track of the mapping. decl_map: std.AutoHashMapUnmanaged(*const Module.Decl, *const llvm.Value), + /// Where to put the output object file, relative to bin_file.options.emit directory. + sub_path: []const u8, - pub fn create(gpa: *Allocator, options: link.Options) !*Object { + pub fn create(gpa: *Allocator, sub_path: []const u8, options: link.Options) !*Object { const obj = try gpa.create(Object); errdefer gpa.destroy(obj); - obj.* = try Object.init(gpa, options); + obj.* = try Object.init(gpa, sub_path, options); return obj; } - pub fn init(gpa: *Allocator, options: link.Options) !Object { + pub fn init(gpa: *Allocator, sub_path: []const u8, options: link.Options) !Object { const context = llvm.Context.create(); errdefer context.dispose(); @@ -251,6 +253,7 @@ pub const Object = struct { .context = context, .target_machine = target_machine, .decl_map = .{}, + .sub_path = sub_path, }; } @@ -301,23 +304,18 @@ pub const Object = struct { const mod = comp.bin_file.options.module.?; const cache_dir = mod.zig_cache_artifact_directory; - const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit != null) blk: { - const obj_basename = try std.zig.binNameAlloc(arena, .{ - .root_name = comp.bin_file.options.root_name, - .target = comp.bin_file.options.target, - .output_mode = .Obj, - }); - if (cache_dir.joinZ(arena, &[_][]const u8{obj_basename})) |p| { - break :blk p.ptr; - } else |err| { - return err; - } - } else null; + const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit| + try emit.directory.joinZ(arena, &[_][]const u8{self.sub_path}) + else + null; const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir); const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir); const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir); + const debug_emit_path = emit_bin_path orelse "(none)"; + log.debug("emit LLVM object to {s}", .{debug_emit_path}); + var error_message: [*:0]const u8 = undefined; if (self.target_machine.emitToFile( self.llvm_module, diff --git a/src/link.zig b/src/link.zig index 4f21a10d18..fe233e060f 100644 --- a/src/link.zig +++ b/src/link.zig @@ -245,6 +245,9 @@ pub const File = struct { }; if (use_lld) { + // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`, + // we also want to put the intermediary object file in the cache while the + // main emit directory is the cwd. file.intermediary_basename = sub_path; } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index a0e79eba20..fa234f608b 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio const self = try createEmpty(allocator, options); errdefer self.base.destroy(); - self.llvm_object = try LlvmObject.create(allocator, options); + self.llvm_object = try LlvmObject.create(allocator, sub_path, options); return self; } @@ -884,11 +884,8 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { - // Both stage1 and stage2 LLVM backend put the object file in the cache directory. - if (self.base.options.use_llvm) { - // Stage2 has to call flushModule since that outputs the LLVM object file. - if (!build_options.is_stage1 or !self.base.options.use_stage1) try self.flushModule(comp); - + const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; + if (use_stage1) { const obj_basename = try std.zig.binNameAlloc(arena, .{ .root_name = self.base.options.root_name, .target = self.base.options.target, @@ -1269,22 +1266,23 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { try argv.append(comp.libunwind_static_lib.?.full_object_path); } - // TODO: remove when stage2 can build compiler_rt.zig, c.zig and ssp.zig - // compiler-rt, libc and libssp - if (is_exe_or_dyn_lib and - !self.base.options.skip_linker_dependencies and - build_options.is_stage1 and self.base.options.use_stage1) - { + if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) { if (!self.base.options.link_libc) { - try argv.append(comp.libc_static_lib.?.full_object_path); + if (comp.libc_static_lib) |lib| { + try argv.append(lib.full_object_path); + } } // MinGW doesn't provide libssp symbols if (target.abi.isGnu()) { - try argv.append(comp.libssp_static_lib.?.full_object_path); + if (comp.libssp_static_lib) |lib| { + try argv.append(lib.full_object_path); + } } // MSVC compiler_rt is missing some stuff, so we build it unconditionally but // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. - try argv.append(comp.compiler_rt_static_lib.?.full_object_path); + if (comp.compiler_rt_static_lib) |lib| { + try argv.append(lib.full_object_path); + } } try argv.ensureUnusedCapacity(self.base.options.system_libs.count()); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index afa976d741..ec047ced35 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -235,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio const self = try createEmpty(allocator, options); errdefer self.base.destroy(); - self.llvm_object = try LlvmObject.create(allocator, options); + self.llvm_object = try LlvmObject.create(allocator, sub_path, options); return self; } @@ -1254,11 +1254,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { - // Both stage1 and stage2 LLVM backend put the object file in the cache directory. - if (self.base.options.use_llvm) { - // Stage2 has to call flushModule since that outputs the LLVM object file. - if (!build_options.is_stage1 or !self.base.options.use_stage1) try self.flushModule(comp); - + // stage1 puts the object file in the cache directory. + if (self.base.options.use_stage1) { const obj_basename = try std.zig.binNameAlloc(arena, .{ .root_name = self.base.options.root_name, .target = self.base.options.target, @@ -1621,14 +1618,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { } // libc - // TODO: enable when stage2 can build c.zig if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies and - !self.base.options.link_libc and - build_options.is_stage1 and - self.base.options.use_stage1) + !self.base.options.link_libc) { - try argv.append(comp.libc_static_lib.?.full_object_path); + if (comp.libc_static_lib) |lib| { + try argv.append(lib.full_object_path); + } } // compiler-rt diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 5551c31632..e69b85ca7f 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -290,7 +290,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio const self = try createEmpty(allocator, options); errdefer self.base.destroy(); - self.llvm_object = try LlvmObject.create(allocator, options); + self.llvm_object = try LlvmObject.create(allocator, sub_path, options); return self; } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 2f5620e47a..3de1fb49cc 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -121,7 +121,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio const self = try createEmpty(allocator, options); errdefer self.base.destroy(); - self.llvm_object = try LlvmObject.create(allocator, options); + self.llvm_object = try LlvmObject.create(allocator, sub_path, options); return self; }