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 {...@@ -1574,25 +1574,29 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1574 // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler1574 // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler
1575 // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for1575 // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for
1576 // stage1 but not stage2.1576 // stage1 but not stage2.
1577 if (comp.bin_file.options.use_stage1) {1577 const capable_of_building_compiler_rt = comp.bin_file.options.use_stage1;
1578 if (comp.bin_file.options.include_compiler_rt) {1578 const capable_of_building_ssp = comp.bin_file.options.use_stage1;
1579 if (is_exe_or_dyn_lib) {1579 const capable_of_building_zig_libc = comp.bin_file.options.use_stage1 or
1580 try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });1580 comp.bin_file.options.use_llvm;
1581 } else if (options.output_mode != .Obj) {1581
1582 // If build-obj with -fcompiler-rt is requested, that is handled specially1582 if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) {
1583 // elsewhere. In this case we are making a static library, so we ask1583 if (is_exe_or_dyn_lib) {
1584 // for a compiler-rt object to put in it.1584 try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });
1585 try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} });1585 } else if (options.output_mode != .Obj) {
1586 }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 = {} });
1587 }1590 }
1588 if (needs_c_symbols) {1591 }
1589 // MinGW provides no libssp, use our own implementation.1592 if (needs_c_symbols) {
1590 if (comp.getTarget().isMinGW()) {1593 // MinGW provides no libssp, use our own implementation.
1591 try comp.work_queue.writeItem(.{ .libssp = {} });1594 if (comp.getTarget().isMinGW() and capable_of_building_ssp) {
1592 }1595 try comp.work_queue.writeItem(.{ .libssp = {} });
1593 if (!comp.bin_file.options.link_libc) {1596 }
1594 try comp.work_queue.writeItem(.{ .zig_libc = {} });1597
1595 }1598 if (!comp.bin_file.options.link_libc and capable_of_building_zig_libc) {
1599 try comp.work_queue.writeItem(.{ .zig_libc = {} });
1596 }1600 }
1597 }1601 }
1598 }1602 }
src/codegen/llvm.zig+13-15
...@@ -164,15 +164,17 @@ pub const Object = struct {...@@ -164,15 +164,17 @@ pub const Object = struct {
164 /// * it works for functions not all globals.164 /// * it works for functions not all globals.
165 /// Therefore, this table keeps track of the mapping.165 /// Therefore, this table keeps track of the mapping.
166 decl_map: std.AutoHashMapUnmanaged(*const Module.Decl, *const llvm.Value),166 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 {
169 const obj = try gpa.create(Object);171 const obj = try gpa.create(Object);
170 errdefer gpa.destroy(obj);172 errdefer gpa.destroy(obj);
171 obj.* = try Object.init(gpa, options);173 obj.* = try Object.init(gpa, sub_path, options);
172 return obj;174 return obj;
173 }175 }
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 {
176 const context = llvm.Context.create();178 const context = llvm.Context.create();
177 errdefer context.dispose();179 errdefer context.dispose();
178180
...@@ -251,6 +253,7 @@ pub const Object = struct {...@@ -251,6 +253,7 @@ pub const Object = struct {
251 .context = context,253 .context = context,
252 .target_machine = target_machine,254 .target_machine = target_machine,
253 .decl_map = .{},255 .decl_map = .{},
256 .sub_path = sub_path,
254 };257 };
255 }258 }
256259
...@@ -301,23 +304,18 @@ pub const Object = struct {...@@ -301,23 +304,18 @@ pub const Object = struct {
301 const mod = comp.bin_file.options.module.?;304 const mod = comp.bin_file.options.module.?;
302 const cache_dir = mod.zig_cache_artifact_directory;305 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: {307 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit|
305 const obj_basename = try std.zig.binNameAlloc(arena, .{308 try emit.directory.joinZ(arena, &[_][]const u8{self.sub_path})
306 .root_name = comp.bin_file.options.root_name,309 else
307 .target = comp.bin_file.options.target,310 null;
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;
316311
317 const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);312 const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);
318 const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);313 const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);
319 const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir);314 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
321 var error_message: [*:0]const u8 = undefined;319 var error_message: [*:0]const u8 = undefined;
322 if (self.target_machine.emitToFile(320 if (self.target_machine.emitToFile(
323 self.llvm_module,321 self.llvm_module,
src/link.zig+3
...@@ -245,6 +245,9 @@ pub const File = struct {...@@ -245,6 +245,9 @@ pub const File = struct {
245 };245 };
246246
247 if (use_lld) {247 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.
248 file.intermediary_basename = sub_path;251 file.intermediary_basename = sub_path;
249 }252 }
250253
src/link/Coff.zig+13-15
...@@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
132 const self = try createEmpty(allocator, options);132 const self = try createEmpty(allocator, options);
133 errdefer self.base.destroy();133 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);
136 return self;136 return self;
137 }137 }
138138
...@@ -884,11 +884,8 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -884,11 +884,8 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
884 // If there is no Zig code to compile, then we should skip flushing the output file because it884 // If there is no Zig code to compile, then we should skip flushing the output file because it
885 // will not be part of the linker line anyway.885 // will not be part of the linker line anyway.
886 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {886 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.887 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
888 if (self.base.options.use_llvm) {888 if (use_stage1) {
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
892 const obj_basename = try std.zig.binNameAlloc(arena, .{889 const obj_basename = try std.zig.binNameAlloc(arena, .{
893 .root_name = self.base.options.root_name,890 .root_name = self.base.options.root_name,
894 .target = self.base.options.target,891 .target = self.base.options.target,
...@@ -1269,22 +1266,23 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -1269,22 +1266,23 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
1269 try argv.append(comp.libunwind_static_lib.?.full_object_path);1266 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1270 }1267 }
12711268
1272 // TODO: remove when stage2 can build compiler_rt.zig, c.zig and ssp.zig1269 if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
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 {
1278 if (!self.base.options.link_libc) {1270 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 }
1280 }1274 }
1281 // MinGW doesn't provide libssp symbols1275 // MinGW doesn't provide libssp symbols
1282 if (target.abi.isGnu()) {1276 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 }
1284 }1280 }
1285 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but1281 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
1286 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.1282 // 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 }
1288 }1286 }
12891287
1290 try argv.ensureUnusedCapacity(self.base.options.system_libs.count());1288 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...@@ -235,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
235 const self = try createEmpty(allocator, options);235 const self = try createEmpty(allocator, options);
236 errdefer self.base.destroy();236 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);
239 return self;239 return self;
240 }240 }
241241
...@@ -1254,11 +1254,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1254,11 +1254,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1254 // If there is no Zig code to compile, then we should skip flushing the output file because it1254 // If there is no Zig code to compile, then we should skip flushing the output file because it
1255 // will not be part of the linker line anyway.1255 // will not be part of the linker line anyway.
1256 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {1256 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.1257 // stage1 puts the object file in the cache directory.
1258 if (self.base.options.use_llvm) {1258 if (self.base.options.use_stage1) {
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
1262 const obj_basename = try std.zig.binNameAlloc(arena, .{1259 const obj_basename = try std.zig.binNameAlloc(arena, .{
1263 .root_name = self.base.options.root_name,1260 .root_name = self.base.options.root_name,
1264 .target = self.base.options.target,1261 .target = self.base.options.target,
...@@ -1621,14 +1618,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1621,14 +1618,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1621 }1618 }
16221619
1623 // libc1620 // libc
1624 // TODO: enable when stage2 can build c.zig
1625 if (is_exe_or_dyn_lib and1621 if (is_exe_or_dyn_lib and
1626 !self.base.options.skip_linker_dependencies and1622 !self.base.options.skip_linker_dependencies and
1627 !self.base.options.link_libc and1623 !self.base.options.link_libc)
1628 build_options.is_stage1 and
1629 self.base.options.use_stage1)
1630 {1624 {
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 }
1632 }1628 }
16331629
1634 // compiler-rt1630 // compiler-rt
src/link/MachO.zig+1-1
...@@ -290,7 +290,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -290,7 +290,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
290 const self = try createEmpty(allocator, options);290 const self = try createEmpty(allocator, options);
291 errdefer self.base.destroy();291 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);
294 return self;294 return self;
295 }295 }
296296
src/link/Wasm.zig+1-1
...@@ -121,7 +121,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -121,7 +121,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
121 const self = try createEmpty(allocator, options);121 const self = try createEmpty(allocator, options);
122 errdefer self.base.destroy();122 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);
125 return self;125 return self;
126 }126 }
127127