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 {...@@ -619,11 +619,11 @@ pub const Decl = struct {
619 return decl.namespace.renderFullyQualifiedName(unqualified_name, writer);619 return decl.namespace.renderFullyQualifiedName(unqualified_name, writer);
620 }620 }
621621
622 pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![]u8 {622 pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![:0]u8 {
623 var buffer = std.ArrayList(u8).init(gpa);623 var buffer = std.ArrayList(u8).init(gpa);
624 defer buffer.deinit();624 defer buffer.deinit();
625 try decl.renderFullyQualifiedName(buffer.writer());625 try decl.renderFullyQualifiedName(buffer.writer());
626 return buffer.toOwnedSlice();626 return buffer.toOwnedSliceSentinel(0);
627 }627 }
628628
629 pub fn typedValue(decl: Decl) error{AnalysisFail}!TypedValue {629 pub fn typedValue(decl: Decl) error{AnalysisFail}!TypedValue {
src/codegen/llvm.zig+71-34
...@@ -155,6 +155,15 @@ pub const Object = struct {...@@ -155,6 +155,15 @@ pub const Object = struct {
155 llvm_module: *const llvm.Module,155 llvm_module: *const llvm.Module,
156 context: *const llvm.Context,156 context: *const llvm.Context,
157 target_machine: *const llvm.TargetMachine,157 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
159 pub fn create(gpa: *Allocator, options: link.Options) !*Object {168 pub fn create(gpa: *Allocator, options: link.Options) !*Object {
160 const obj = try gpa.create(Object);169 const obj = try gpa.create(Object);
...@@ -241,18 +250,20 @@ pub const Object = struct {...@@ -241,18 +250,20 @@ pub const Object = struct {
241 .llvm_module = llvm_module,250 .llvm_module = llvm_module,
242 .context = context,251 .context = context,
243 .target_machine = target_machine,252 .target_machine = target_machine,
253 .decl_map = .{},
244 };254 };
245 }255 }
246256
247 pub fn deinit(self: *Object) void {257 pub fn deinit(self: *Object, gpa: *Allocator) void {
248 self.target_machine.dispose();258 self.target_machine.dispose();
249 self.llvm_module.dispose();259 self.llvm_module.dispose();
250 self.context.dispose();260 self.context.dispose();
261 self.decl_map.deinit(gpa);
251 self.* = undefined;262 self.* = undefined;
252 }263 }
253264
254 pub fn destroy(self: *Object, gpa: *Allocator) void {265 pub fn destroy(self: *Object, gpa: *Allocator) void {
255 self.deinit();266 self.deinit(gpa);
256 gpa.destroy(self);267 gpa.destroy(self);
257 }268 }
258269
...@@ -450,41 +461,62 @@ pub const Object = struct {...@@ -450,41 +461,62 @@ pub const Object = struct {
450 ) !void {461 ) !void {
451 // If the module does not already have the function, we ignore this function call462 // If the module does not already have the function, we ignore this function call
452 // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`.463 // 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;
454 const is_extern = decl.val.tag() == .extern_fn;465 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);
456 llvm_fn.setLinkage(.External);469 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);
457 llvm_fn.setUnnamedAddr(.False);473 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 }
458 } else {508 } else {
509 const fqn = try decl.getFullyQualifiedName(module.gpa);
510 defer module.gpa.free(fqn);
511 llvm_fn.setValueName2(fqn.ptr, fqn.len);
459 llvm_fn.setLinkage(.Internal);512 llvm_fn.setLinkage(.Internal);
460 llvm_fn.setUnnamedAddr(.True);513 llvm_fn.setUnnamedAddr(.True);
461 }514 }
462 // TODO LLVM C API does not support deleting aliases. We need to515 }
463 // patch it to support this or figure out how to wrap the C++ API ourselves.516
464 // Until then we iterate over existing aliases and make them point517 pub fn freeDecl(self: *Object, decl: *Module.Decl) void {
465 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.518 const llvm_value = self.decl_map.get(decl) orelse return;
466 for (exports) |exp| {519 llvm_value.deleteGlobal();
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 }
488 }520 }
489};521};
490522
...@@ -493,9 +525,8 @@ pub const DeclGen = struct {...@@ -493,9 +525,8 @@ pub const DeclGen = struct {
493 object: *Object,525 object: *Object,
494 module: *Module,526 module: *Module,
495 decl: *Module.Decl,527 decl: *Module.Decl,
496 err_msg: ?*Module.ErrorMsg,
497
498 gpa: *Allocator,528 gpa: *Allocator,
529 err_msg: ?*Module.ErrorMsg,
499530
500 fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {531 fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
501 @setCold(true);532 @setCold(true);
...@@ -540,7 +571,8 @@ pub const DeclGen = struct {...@@ -540,7 +571,8 @@ pub const DeclGen = struct {
540 /// Note that this can be called before the function's semantic analysis has571 /// Note that this can be called before the function's semantic analysis has
541 /// completed, so if any attributes rely on that, they must be done in updateFunc, not here.572 /// completed, so if any attributes rely on that, they must be done in updateFunc, not here.
542 fn resolveLlvmFunction(self: *DeclGen, decl: *Module.Decl) !*const llvm.Value {573 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
545 assert(decl.has_tv);577 assert(decl.has_tv);
546 const zig_fn_type = decl.ty;578 const zig_fn_type = decl.ty;
...@@ -570,7 +602,12 @@ pub const DeclGen = struct {...@@ -570,7 +602,12 @@ pub const DeclGen = struct {
570 .False,602 .False,
571 );603 );
572 const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace");604 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
575 const is_extern = decl.val.tag() == .extern_fn;612 const is_extern = decl.val.tag() == .extern_fn;
576 if (!is_extern) {613 if (!is_extern) {
src/codegen/llvm/bindings.zig+9
...@@ -151,6 +151,15 @@ pub const Value = opaque {...@@ -151,6 +151,15 @@ pub const Value = opaque {
151151
152 pub const setFunctionCallConv = LLVMSetFunctionCallConv;152 pub const setFunctionCallConv = LLVMSetFunctionCallConv;
153 extern fn LLVMSetFunctionCallConv(Fn: *const Value, CC: CallConv) void;153 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;
154};163};
155164
156pub const Type = opaque {165pub const Type = opaque {
src/link/Coff.zig+3-1
...@@ -770,7 +770,9 @@ fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []co...@@ -770,7 +770,9 @@ fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []co
770}770}
771771
772pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {772pub 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
775 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.777 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
776 self.freeTextBlock(&decl.link.coff);778 self.freeTextBlock(&decl.link.coff);
src/link/Elf.zig+3-1
...@@ -2147,7 +2147,9 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {...@@ -2147,7 +2147,9 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
2147}2147}
21482148
2149pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {2149pub 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
2152 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.2154 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2153 self.freeTextBlock(&decl.link.elf);2155 self.freeTextBlock(&decl.link.elf);
src/link/MachO.zig+3
...@@ -3501,6 +3501,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void {...@@ -3501,6 +3501,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
3501}3501}
35023502
3503pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {3503pub 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 }
3504 log.debug("freeDecl {*}", .{decl});3507 log.debug("freeDecl {*}", .{decl});
3505 _ = self.decls.swapRemove(decl);3508 _ = self.decls.swapRemove(decl);
3506 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.3509 // 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(...@@ -339,6 +339,10 @@ pub fn updateDeclExports(
339}339}
340340
341pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {341pub 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
342 if (self.getFuncidx(decl)) |func_idx| {346 if (self.getFuncidx(decl)) |func_idx| {
343 switch (decl.val.tag()) {347 switch (decl.val.tag()) {
344 .function => _ = self.funcs.swapRemove(func_idx),348 .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) {...@@ -487,9 +487,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
487 if (mangled_symbol_buf) buf_destroy(mangled_symbol_buf);487 if (mangled_symbol_buf) buf_destroy(mangled_symbol_buf);
488 }488 }
489 } else {489 } else {
490 if (llvm_fn == nullptr) {490 llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type);
491 llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type);
492 }
493491
494 for (size_t i = 1; i < fn->export_list.length; i += 1) {492 for (size_t i = 1; i < fn->export_list.length; i += 1) {
495 GlobalExport *fn_export = &fn->export_list.items[i];493 GlobalExport *fn_export = &fn->export_list.items[i];