authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-24 00:54:44-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-24 01:00:15-07:00
logef7fa76001f873824b0f64dfc2172ed2f304c348
treeea739ef9170f446568c6a328ff9428f78c0ad165
parentf215d98043ef948a996ac036609f4b71fa9c3c13

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.

7 files changed, 60 insertions(+), 61 deletions(-)

src/Compilation.zig+22-18
......@@ -1574,25 +1574,29 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
15741574 // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler
15751575 // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for
15761576 // stage1 but not stage2.
1577 if (comp.bin_file.options.use_stage1) {
1578 if (comp.bin_file.options.include_compiler_rt) {
1579 if (is_exe_or_dyn_lib) {
1580 try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });
1581 } else if (options.output_mode != .Obj) {
1582 // If build-obj with -fcompiler-rt is requested, that is handled specially
1583 // elsewhere. In this case we are making a static library, so we ask
1584 // for a compiler-rt object to put in it.
1585 try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} });
1586 }
1577 const capable_of_building_compiler_rt = comp.bin_file.options.use_stage1;
1578 const capable_of_building_ssp = comp.bin_file.options.use_stage1;
1579 const capable_of_building_zig_libc = comp.bin_file.options.use_stage1 or
1580 comp.bin_file.options.use_llvm;
1581
1582 if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) {
1583 if (is_exe_or_dyn_lib) {
1584 try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });
1585 } else if (options.output_mode != .Obj) {
1586 // If build-obj with -fcompiler-rt is requested, that is handled specially
1587 // elsewhere. In this case we are making a static library, so we ask
1588 // for a compiler-rt object to put in it.
1589 try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} });
15871590 }
1588 if (needs_c_symbols) {
1589 // MinGW provides no libssp, use our own implementation.
1590 if (comp.getTarget().isMinGW()) {
1591 try comp.work_queue.writeItem(.{ .libssp = {} });
1592 }
1593 if (!comp.bin_file.options.link_libc) {
1594 try comp.work_queue.writeItem(.{ .zig_libc = {} });
1595 }
1591 }
1592 if (needs_c_symbols) {
1593 // MinGW provides no libssp, use our own implementation.
1594 if (comp.getTarget().isMinGW() and capable_of_building_ssp) {
1595 try comp.work_queue.writeItem(.{ .libssp = {} });
1596 }
1597
1598 if (!comp.bin_file.options.link_libc and capable_of_building_zig_libc) {
1599 try comp.work_queue.writeItem(.{ .zig_libc = {} });
15961600 }
15971601 }
15981602 }
src/codegen/llvm.zig+13-15
......@@ -164,15 +164,17 @@ pub const Object = struct {
164164 /// * it works for functions not all globals.
165165 /// Therefore, this table keeps track of the mapping.
166166 decl_map: std.AutoHashMapUnmanaged(*const Module.Decl, *const llvm.Value),
167 /// Where to put the output object file, relative to bin_file.options.emit directory.
168 sub_path: []const u8,
167169
168 pub fn create(gpa: *Allocator, options: link.Options) !*Object {
170 pub fn create(gpa: *Allocator, sub_path: []const u8, options: link.Options) !*Object {
169171 const obj = try gpa.create(Object);
170172 errdefer gpa.destroy(obj);
171 obj.* = try Object.init(gpa, options);
173 obj.* = try Object.init(gpa, sub_path, options);
172174 return obj;
173175 }
174176
175 pub fn init(gpa: *Allocator, options: link.Options) !Object {
177 pub fn init(gpa: *Allocator, sub_path: []const u8, options: link.Options) !Object {
176178 const context = llvm.Context.create();
177179 errdefer context.dispose();
178180
......@@ -251,6 +253,7 @@ pub const Object = struct {
251253 .context = context,
252254 .target_machine = target_machine,
253255 .decl_map = .{},
256 .sub_path = sub_path,
254257 };
255258 }
256259
......@@ -301,23 +304,18 @@ pub const Object = struct {
301304 const mod = comp.bin_file.options.module.?;
302305 const cache_dir = mod.zig_cache_artifact_directory;
303306
304 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit != null) blk: {
305 const obj_basename = try std.zig.binNameAlloc(arena, .{
306 .root_name = comp.bin_file.options.root_name,
307 .target = comp.bin_file.options.target,
308 .output_mode = .Obj,
309 });
310 if (cache_dir.joinZ(arena, &[_][]const u8{obj_basename})) |p| {
311 break :blk p.ptr;
312 } else |err| {
313 return err;
314 }
315 } else null;
307 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit|
308 try emit.directory.joinZ(arena, &[_][]const u8{self.sub_path})
309 else
310 null;
316311
317312 const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);
318313 const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);
319314 const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir);
320315
316 const debug_emit_path = emit_bin_path orelse "(none)";
317 log.debug("emit LLVM object to {s}", .{debug_emit_path});
318
321319 var error_message: [*:0]const u8 = undefined;
322320 if (self.target_machine.emitToFile(
323321 self.llvm_module,
src/link.zig+3
......@@ -245,6 +245,9 @@ pub const File = struct {
245245 };
246246
247247 if (use_lld) {
248 // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`,
249 // we also want to put the intermediary object file in the cache while the
250 // main emit directory is the cwd.
248251 file.intermediary_basename = sub_path;
249252 }
250253
src/link/Coff.zig+13-15
......@@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
132132 const self = try createEmpty(allocator, options);
133133 errdefer self.base.destroy();
134134
135 self.llvm_object = try LlvmObject.create(allocator, options);
135 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
136136 return self;
137137 }
138138
......@@ -884,11 +884,8 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
884884 // If there is no Zig code to compile, then we should skip flushing the output file because it
885885 // will not be part of the linker line anyway.
886886 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
887 // Both stage1 and stage2 LLVM backend put the object file in the cache directory.
888 if (self.base.options.use_llvm) {
889 // Stage2 has to call flushModule since that outputs the LLVM object file.
890 if (!build_options.is_stage1 or !self.base.options.use_stage1) try self.flushModule(comp);
891
887 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
888 if (use_stage1) {
892889 const obj_basename = try std.zig.binNameAlloc(arena, .{
893890 .root_name = self.base.options.root_name,
894891 .target = self.base.options.target,
......@@ -1269,22 +1266,23 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
12691266 try argv.append(comp.libunwind_static_lib.?.full_object_path);
12701267 }
12711268
1272 // TODO: remove when stage2 can build compiler_rt.zig, c.zig and ssp.zig
1273 // compiler-rt, libc and libssp
1274 if (is_exe_or_dyn_lib and
1275 !self.base.options.skip_linker_dependencies and
1276 build_options.is_stage1 and self.base.options.use_stage1)
1277 {
1269 if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
12781270 if (!self.base.options.link_libc) {
1279 try argv.append(comp.libc_static_lib.?.full_object_path);
1271 if (comp.libc_static_lib) |lib| {
1272 try argv.append(lib.full_object_path);
1273 }
12801274 }
12811275 // MinGW doesn't provide libssp symbols
12821276 if (target.abi.isGnu()) {
1283 try argv.append(comp.libssp_static_lib.?.full_object_path);
1277 if (comp.libssp_static_lib) |lib| {
1278 try argv.append(lib.full_object_path);
1279 }
12841280 }
12851281 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
12861282 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
1287 try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
1283 if (comp.compiler_rt_static_lib) |lib| {
1284 try argv.append(lib.full_object_path);
1285 }
12881286 }
12891287
12901288 try argv.ensureUnusedCapacity(self.base.options.system_libs.count());
src/link/Elf.zig+7-11
......@@ -235,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
235235 const self = try createEmpty(allocator, options);
236236 errdefer self.base.destroy();
237237
238 self.llvm_object = try LlvmObject.create(allocator, options);
238 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
239239 return self;
240240 }
241241
......@@ -1254,11 +1254,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12541254 // If there is no Zig code to compile, then we should skip flushing the output file because it
12551255 // will not be part of the linker line anyway.
12561256 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
1257 // Both stage1 and stage2 LLVM backend put the object file in the cache directory.
1258 if (self.base.options.use_llvm) {
1259 // Stage2 has to call flushModule since that outputs the LLVM object file.
1260 if (!build_options.is_stage1 or !self.base.options.use_stage1) try self.flushModule(comp);
1261
1257 // stage1 puts the object file in the cache directory.
1258 if (self.base.options.use_stage1) {
12621259 const obj_basename = try std.zig.binNameAlloc(arena, .{
12631260 .root_name = self.base.options.root_name,
12641261 .target = self.base.options.target,
......@@ -1621,14 +1618,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
16211618 }
16221619
16231620 // libc
1624 // TODO: enable when stage2 can build c.zig
16251621 if (is_exe_or_dyn_lib and
16261622 !self.base.options.skip_linker_dependencies and
1627 !self.base.options.link_libc and
1628 build_options.is_stage1 and
1629 self.base.options.use_stage1)
1623 !self.base.options.link_libc)
16301624 {
1631 try argv.append(comp.libc_static_lib.?.full_object_path);
1625 if (comp.libc_static_lib) |lib| {
1626 try argv.append(lib.full_object_path);
1627 }
16321628 }
16331629
16341630 // compiler-rt
src/link/MachO.zig+1-1
......@@ -290,7 +290,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
290290 const self = try createEmpty(allocator, options);
291291 errdefer self.base.destroy();
292292
293 self.llvm_object = try LlvmObject.create(allocator, options);
293 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
294294 return self;
295295 }
296296
src/link/Wasm.zig+1-1
......@@ -121,7 +121,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
121121 const self = try createEmpty(allocator, options);
122122 errdefer self.base.destroy();
123123
124 self.llvm_object = try LlvmObject.create(allocator, options);
124 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
125125 return self;
126126 }
127127