authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-23 23:39:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-23 23:46:45-07:00
logf215d98043ef948a996ac036609f4b71fa9c3c13
treefcb9818017f619be3940831834c3cbbf553c9173
parent418105589a2723ca372596e5893e0e1e030efe87

stage2: LLVM backend: improved naming and exporting

Introduce an explicit decl_map for *Decl to LLVMValueRef. Doc comment reproduced here: Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function, but that has some downsides: * we have to compute the fully qualified name every time we want to do the lookup * for externally linked functions, the name is not fully qualified, but when a Decl goes from exported to not exported and vice-versa, we would use the wrong version of the name and incorrectly get function not found in the llvm module. * it works for functions not all globals. Therefore, this table keeps track of the mapping. Non-exported functions now use fully-qualified symbol names. `Module.Decl.getFullyQualifiedName` now returns a sentinel-terminated slice which is useful to pass to LLVMAddFunction. Instead of using aliases for all external symbols, now the LLVM backend takes advantage of LLVMSetValueName to rename functions that become exported. Aliases are still used for the second and remaining exports. freeDecl is now handled properly in the LLVM backend, deleting the LLVMValueRef corresponding to the Decl being deleted. The linker backends for ELF, COFF, Mach-O, and Wasm had to be updated to forward the freeDecl call to the LLVM backend.

8 files changed, 96 insertions(+), 41 deletions(-)

src/Module.zig+2-2
......@@ -619,11 +619,11 @@ pub const Decl = struct {
619619 return decl.namespace.renderFullyQualifiedName(unqualified_name, writer);
620620 }
621621
622 pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![]u8 {
622 pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![:0]u8 {
623623 var buffer = std.ArrayList(u8).init(gpa);
624624 defer buffer.deinit();
625625 try decl.renderFullyQualifiedName(buffer.writer());
626 return buffer.toOwnedSlice();
626 return buffer.toOwnedSliceSentinel(0);
627627 }
628628
629629 pub fn typedValue(decl: Decl) error{AnalysisFail}!TypedValue {
src/codegen/llvm.zig+71-34
......@@ -155,6 +155,15 @@ pub const Object = struct {
155155 llvm_module: *const llvm.Module,
156156 context: *const llvm.Context,
157157 target_machine: *const llvm.TargetMachine,
158 /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function,
159 /// but that has some downsides:
160 /// * we have to compute the fully qualified name every time we want to do the lookup
161 /// * for externally linked functions, the name is not fully qualified, but when
162 /// a Decl goes from exported to not exported and vice-versa, we would use the wrong
163 /// version of the name and incorrectly get function not found in the llvm module.
164 /// * it works for functions not all globals.
165 /// Therefore, this table keeps track of the mapping.
166 decl_map: std.AutoHashMapUnmanaged(*const Module.Decl, *const llvm.Value),
158167
159168 pub fn create(gpa: *Allocator, options: link.Options) !*Object {
160169 const obj = try gpa.create(Object);
......@@ -241,18 +250,20 @@ pub const Object = struct {
241250 .llvm_module = llvm_module,
242251 .context = context,
243252 .target_machine = target_machine,
253 .decl_map = .{},
244254 };
245255 }
246256
247 pub fn deinit(self: *Object) void {
257 pub fn deinit(self: *Object, gpa: *Allocator) void {
248258 self.target_machine.dispose();
249259 self.llvm_module.dispose();
250260 self.context.dispose();
261 self.decl_map.deinit(gpa);
251262 self.* = undefined;
252263 }
253264
254265 pub fn destroy(self: *Object, gpa: *Allocator) void {
255 self.deinit();
266 self.deinit(gpa);
256267 gpa.destroy(self);
257268 }
258269
......@@ -450,41 +461,62 @@ pub const Object = struct {
450461 ) !void {
451462 // If the module does not already have the function, we ignore this function call
452463 // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`.
453 const llvm_fn = self.llvm_module.getNamedFunction(decl.name) orelse return;
464 const llvm_fn = self.decl_map.get(decl) orelse return;
454465 const is_extern = decl.val.tag() == .extern_fn;
455 if (is_extern or exports.len != 0) {
466 if (is_extern) {
467 llvm_fn.setValueName(decl.name);
468 llvm_fn.setUnnamedAddr(.False);
456469 llvm_fn.setLinkage(.External);
470 } else if (exports.len != 0) {
471 const exp_name = exports[0].options.name;
472 llvm_fn.setValueName2(exp_name.ptr, exp_name.len);
457473 llvm_fn.setUnnamedAddr(.False);
474 switch (exports[0].options.linkage) {
475 .Internal => unreachable,
476 .Strong => llvm_fn.setLinkage(.External),
477 .Weak => llvm_fn.setLinkage(.WeakODR),
478 .LinkOnce => llvm_fn.setLinkage(.LinkOnceODR),
479 }
480 // If a Decl is exported more than one time (which is rare),
481 // we add aliases for all but the first export.
482 // TODO LLVM C API does not support deleting aliases. We need to
483 // patch it to support this or figure out how to wrap the C++ API ourselves.
484 // Until then we iterate over existing aliases and make them point
485 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.
486 for (exports[1..]) |exp| {
487 const exp_name_z = try module.gpa.dupeZ(u8, exp.options.name);
488 defer module.gpa.free(exp_name_z);
489
490 if (self.llvm_module.getNamedGlobalAlias(exp_name_z.ptr, exp_name_z.len)) |alias| {
491 alias.setAliasee(llvm_fn);
492 } else {
493 const alias = self.llvm_module.addAlias(llvm_fn.typeOf(), llvm_fn, exp_name_z);
494 switch (exp.options.linkage) {
495 .Internal => alias.setLinkage(.Internal),
496 .Strong => alias.setLinkage(.External),
497 .Weak => {
498 if (is_extern) {
499 alias.setLinkage(.ExternalWeak);
500 } else {
501 alias.setLinkage(.WeakODR);
502 }
503 },
504 .LinkOnce => alias.setLinkage(.LinkOnceODR),
505 }
506 }
507 }
458508 } else {
509 const fqn = try decl.getFullyQualifiedName(module.gpa);
510 defer module.gpa.free(fqn);
511 llvm_fn.setValueName2(fqn.ptr, fqn.len);
459512 llvm_fn.setLinkage(.Internal);
460513 llvm_fn.setUnnamedAddr(.True);
461514 }
462 // TODO LLVM C API does not support deleting aliases. We need to
463 // patch it to support this or figure out how to wrap the C++ API ourselves.
464 // Until then we iterate over existing aliases and make them point
465 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.
466 for (exports) |exp| {
467 const exp_name_z = try module.gpa.dupeZ(u8, exp.options.name);
468 defer module.gpa.free(exp_name_z);
469
470 if (self.llvm_module.getNamedGlobalAlias(exp_name_z.ptr, exp_name_z.len)) |alias| {
471 alias.setAliasee(llvm_fn);
472 } else {
473 const alias = self.llvm_module.addAlias(llvm_fn.typeOf(), llvm_fn, exp_name_z);
474 switch (exp.options.linkage) {
475 .Internal => alias.setLinkage(.Internal),
476 .Strong => alias.setLinkage(.External),
477 .Weak => {
478 if (is_extern) {
479 alias.setLinkage(.ExternalWeak);
480 } else {
481 alias.setLinkage(.WeakODR);
482 }
483 },
484 .LinkOnce => alias.setLinkage(.LinkOnceODR),
485 }
486 }
487 }
515 }
516
517 pub fn freeDecl(self: *Object, decl: *Module.Decl) void {
518 const llvm_value = self.decl_map.get(decl) orelse return;
519 llvm_value.deleteGlobal();
488520 }
489521};
490522
......@@ -493,9 +525,8 @@ pub const DeclGen = struct {
493525 object: *Object,
494526 module: *Module,
495527 decl: *Module.Decl,
496 err_msg: ?*Module.ErrorMsg,
497
498528 gpa: *Allocator,
529 err_msg: ?*Module.ErrorMsg,
499530
500531 fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
501532 @setCold(true);
......@@ -540,7 +571,8 @@ pub const DeclGen = struct {
540571 /// Note that this can be called before the function's semantic analysis has
541572 /// completed, so if any attributes rely on that, they must be done in updateFunc, not here.
542573 fn resolveLlvmFunction(self: *DeclGen, decl: *Module.Decl) !*const llvm.Value {
543 if (self.llvmModule().getNamedFunction(decl.name)) |llvm_fn| return llvm_fn;
574 const gop = try self.object.decl_map.getOrPut(self.gpa, decl);
575 if (gop.found_existing) return gop.value_ptr.*;
544576
545577 assert(decl.has_tv);
546578 const zig_fn_type = decl.ty;
......@@ -570,7 +602,12 @@ pub const DeclGen = struct {
570602 .False,
571603 );
572604 const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace");
573 const llvm_fn = self.llvmModule().addFunctionInAddressSpace(decl.name, fn_type, llvm_addrspace);
605
606 const fqn = try decl.getFullyQualifiedName(self.gpa);
607 defer self.gpa.free(fqn);
608
609 const llvm_fn = self.llvmModule().addFunctionInAddressSpace(fqn, fn_type, llvm_addrspace);
610 gop.value_ptr.* = llvm_fn;
574611
575612 const is_extern = decl.val.tag() == .extern_fn;
576613 if (!is_extern) {
src/codegen/llvm/bindings.zig+9
......@@ -151,6 +151,15 @@ pub const Value = opaque {
151151
152152 pub const setFunctionCallConv = LLVMSetFunctionCallConv;
153153 extern fn LLVMSetFunctionCallConv(Fn: *const Value, CC: CallConv) void;
154
155 pub const setValueName = LLVMSetValueName;
156 extern fn LLVMSetValueName(Val: *const Value, Name: [*:0]const u8) void;
157
158 pub const setValueName2 = LLVMSetValueName2;
159 extern fn LLVMSetValueName2(Val: *const Value, Name: [*]const u8, NameLen: usize) void;
160
161 pub const deleteFunction = LLVMDeleteFunction;
162 extern fn LLVMDeleteFunction(Fn: *const Value) void;
154163};
155164
156165pub const Type = opaque {
src/link/Coff.zig+3-1
......@@ -770,7 +770,9 @@ fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []co
770770}
771771
772772pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
773 if (self.llvm_object) |_| return;
773 if (build_options.have_llvm) {
774 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);
775 }
774776
775777 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
776778 self.freeTextBlock(&decl.link.coff);
src/link/Elf.zig+3-1
......@@ -2147,7 +2147,9 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
21472147}
21482148
21492149pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
2150 if (self.llvm_object) |_| return;
2150 if (build_options.have_llvm) {
2151 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);
2152 }
21512153
21522154 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
21532155 self.freeTextBlock(&decl.link.elf);
src/link/MachO.zig+3
......@@ -3501,6 +3501,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
35013501}
35023502
35033503pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
3504 if (build_options.have_llvm) {
3505 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);
3506 }
35043507 log.debug("freeDecl {*}", .{decl});
35053508 _ = self.decls.swapRemove(decl);
35063509 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
src/link/Wasm.zig+4
......@@ -339,6 +339,10 @@ pub fn updateDeclExports(
339339}
340340
341341pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
342 if (build_options.have_llvm) {
343 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);
344 }
345
342346 if (self.getFuncidx(decl)) |func_idx| {
343347 switch (decl.val.tag()) {
344348 .function => _ = self.funcs.swapRemove(func_idx),
src/stage1/codegen.cpp+1-3
......@@ -487,9 +487,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
487487 if (mangled_symbol_buf) buf_destroy(mangled_symbol_buf);
488488 }
489489 } else {
490 if (llvm_fn == nullptr) {
491 llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type);
492 }
490 llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type);
493491
494492 for (size_t i = 1; i < fn->export_list.length; i += 1) {
495493 GlobalExport *fn_export = &fn->export_list.items[i];