authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-16 20:26:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-17 23:50:38-07:00
log00f3d84f38c54e70716cf8e2908c899b49de1d88
treebf9d23c88e2474fbddcfb662a48c145cc97ca7b0
parente252f92b9947e82f2473a37a74d5f9dc278a1c1d

LLVM: support mixing extern and export with the same symbol name


2 files changed, 64 insertions(+), 13 deletions(-)

src/codegen/llvm.zig+59-12
...@@ -395,11 +395,11 @@ pub const Object = struct {...@@ -395,11 +395,11 @@ pub const Object = struct {
395 return slice.ptr;395 return slice.ptr;
396 }396 }
397397
398 fn genErrorNameTable(self: *Object, comp: *Compilation) !void {398 fn genErrorNameTable(self: *Object) !void {
399 // If self.error_name_table is null, there was no instruction that actually referenced the error table.399 // If self.error_name_table is null, there was no instruction that actually referenced the error table.
400 const error_name_table_ptr_global = self.error_name_table orelse return;400 const error_name_table_ptr_global = self.error_name_table orelse return;
401401
402 const mod = comp.bin_file.options.module.?;402 const mod = self.module;
403 const target = mod.getTarget();403 const target = mod.getTarget();
404404
405 const llvm_ptr_ty = self.context.intType(8).pointerType(0); // TODO: Address space405 const llvm_ptr_ty = self.context.intType(8).pointerType(0); // TODO: Address space
...@@ -413,8 +413,8 @@ pub const Object = struct {...@@ -413,8 +413,8 @@ pub const Object = struct {
413 const slice_alignment = slice_ty.abiAlignment(target);413 const slice_alignment = slice_ty.abiAlignment(target);
414414
415 const error_name_list = mod.error_name_list.items;415 const error_name_list = mod.error_name_list.items;
416 const llvm_errors = try comp.gpa.alloc(*const llvm.Value, error_name_list.len);416 const llvm_errors = try mod.gpa.alloc(*const llvm.Value, error_name_list.len);
417 defer comp.gpa.free(llvm_errors);417 defer mod.gpa.free(llvm_errors);
418418
419 llvm_errors[0] = llvm_slice_ty.getUndef();419 llvm_errors[0] = llvm_slice_ty.getUndef();
420 for (llvm_errors[1..]) |*llvm_error, i| {420 for (llvm_errors[1..]) |*llvm_error, i| {
...@@ -447,10 +447,10 @@ pub const Object = struct {...@@ -447,10 +447,10 @@ pub const Object = struct {
447 error_name_table_ptr_global.setInitializer(error_name_table_ptr);447 error_name_table_ptr_global.setInitializer(error_name_table_ptr);
448 }448 }
449449
450 fn genCmpLtErrorsLenFunction(object: *Object, comp: *Compilation) !void {450 fn genCmpLtErrorsLenFunction(object: *Object) !void {
451 // If there is no such function in the module, it means the source code does not need it.451 // If there is no such function in the module, it means the source code does not need it.
452 const llvm_fn = object.llvm_module.getNamedFunction(lt_errors_fn_name) orelse return;452 const llvm_fn = object.llvm_module.getNamedFunction(lt_errors_fn_name) orelse return;
453 const mod = comp.bin_file.options.module.?;453 const mod = object.module;
454 const errors_len = mod.global_error_set.count();454 const errors_len = mod.global_error_set.count();
455455
456 // Delete previous implementation. We replace it with every flush() because the456 // Delete previous implementation. We replace it with every flush() because the
...@@ -476,10 +476,10 @@ pub const Object = struct {...@@ -476,10 +476,10 @@ pub const Object = struct {
476 _ = builder.buildRet(is_lt);476 _ = builder.buildRet(is_lt);
477 }477 }
478478
479 fn genModuleLevelAssembly(object: *Object, comp: *Compilation) !void {479 fn genModuleLevelAssembly(object: *Object) !void {
480 const mod = comp.bin_file.options.module.?;480 const mod = object.module;
481 if (mod.global_assembly.count() == 0) return;481 if (mod.global_assembly.count() == 0) return;
482 var buffer = std.ArrayList(u8).init(comp.gpa);482 var buffer = std.ArrayList(u8).init(mod.gpa);
483 defer buffer.deinit();483 defer buffer.deinit();
484 var it = mod.global_assembly.iterator();484 var it = mod.global_assembly.iterator();
485 while (it.next()) |kv| {485 while (it.next()) |kv| {
...@@ -489,15 +489,53 @@ pub const Object = struct {...@@ -489,15 +489,53 @@ pub const Object = struct {
489 object.llvm_module.setModuleInlineAsm2(buffer.items.ptr, buffer.items.len - 1);489 object.llvm_module.setModuleInlineAsm2(buffer.items.ptr, buffer.items.len - 1);
490 }490 }
491491
492 fn resolveExportExternCollisions(object: *Object) !void {
493 const mod = object.module;
494
495 const export_keys = mod.decl_exports.keys();
496 for (mod.decl_exports.values()) |export_list, i| {
497 const decl_index = export_keys[i];
498 const llvm_global = object.decl_map.get(decl_index) orelse continue;
499 for (export_list) |exp| {
500 // Detect if the LLVM global has already been created as an extern. In such
501 // case, we need to replace all uses of it with this exported global.
502 // TODO update std.builtin.ExportOptions to have the name be a
503 // null-terminated slice.
504 const exp_name_z = try mod.gpa.dupeZ(u8, exp.options.name);
505 defer mod.gpa.free(exp_name_z);
506
507 const other_global = object.getLlvmGlobal(exp_name_z.ptr) orelse continue;
508 if (other_global == llvm_global) continue;
509
510 // replaceAllUsesWith requires the type to be unchanged. So we bitcast
511 // the new global to the old type and use that as the thing to replace
512 // old uses.
513 const new_global_ptr = llvm_global.constBitCast(other_global.typeOf());
514 other_global.replaceAllUsesWith(new_global_ptr);
515 llvm_global.takeName(other_global);
516 other_global.deleteGlobal();
517 // Problem: now we need to replace in the decl_map that
518 // the extern decl index points to this new global. However we don't
519 // know the decl index.
520 // Even if we did, a future incremental update to the extern would then
521 // treat the LLVM global as an extern rather than an export, so it would
522 // need a way to check that.
523 // This is a TODO that needs to be solved when making
524 // the LLVM backend support incremental compilation.
525 }
526 }
527 }
528
492 pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void {529 pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void {
493 var sub_prog_node = prog_node.start("LLVM Emit Object", 0);530 var sub_prog_node = prog_node.start("LLVM Emit Object", 0);
494 sub_prog_node.activate();531 sub_prog_node.activate();
495 sub_prog_node.context.refresh();532 sub_prog_node.context.refresh();
496 defer sub_prog_node.end();533 defer sub_prog_node.end();
497534
498 try self.genErrorNameTable(comp);535 try self.resolveExportExternCollisions();
499 try self.genCmpLtErrorsLenFunction(comp);536 try self.genErrorNameTable();
500 try self.genModuleLevelAssembly(comp);537 try self.genCmpLtErrorsLenFunction();
538 try self.genModuleLevelAssembly();
501539
502 if (self.di_builder) |dib| {540 if (self.di_builder) |dib| {
503 // When lowering debug info for pointers, we emitted the element types as541 // When lowering debug info for pointers, we emitted the element types as
...@@ -761,6 +799,14 @@ pub const Object = struct {...@@ -761,6 +799,14 @@ pub const Object = struct {
761 try self.updateDeclExports(module, decl_index, decl_exports);799 try self.updateDeclExports(module, decl_index, decl_exports);
762 }800 }
763801
802 /// TODO replace this with a call to `Module::getNamedValue`. This will require adding
803 /// a new wrapper in zig_llvm.h/zig_llvm.cpp.
804 fn getLlvmGlobal(o: Object, name: [*:0]const u8) ?*const llvm.Value {
805 if (o.llvm_module.getNamedFunction(name)) |x| return x;
806 if (o.llvm_module.getNamedGlobal(name)) |x| return x;
807 return null;
808 }
809
764 pub fn updateDeclExports(810 pub fn updateDeclExports(
765 self: *Object,811 self: *Object,
766 module: *Module,812 module: *Module,
...@@ -827,6 +873,7 @@ pub const Object = struct {...@@ -827,6 +873,7 @@ pub const Object = struct {
827 llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);873 llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
828 }874 }
829 }875 }
876
830 // If a Decl is exported more than one time (which is rare),877 // If a Decl is exported more than one time (which is rare),
831 // we add aliases for all but the first export.878 // we add aliases for all but the first export.
832 // TODO LLVM C API does not support deleting aliases. We need to879 // TODO LLVM C API does not support deleting aliases. We need to
test/behavior/basic.zig+5-1
...@@ -797,7 +797,11 @@ test "auto created variables have correct alignment" {...@@ -797,7 +797,11 @@ test "auto created variables have correct alignment" {
797}797}
798798
799test "extern variable with non-pointer opaque type" {799test "extern variable with non-pointer opaque type" {
800 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO800 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
801 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
802 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
803 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
804 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
801805
802 @export(var_to_export, .{ .name = "opaque_extern_var" });806 @export(var_to_export, .{ .name = "opaque_extern_var" });
803 try expect(@ptrCast(*align(1) u32, &opaque_extern_var).* == 42);807 try expect(@ptrCast(*align(1) u32, &opaque_extern_var).* == 42);