authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-06 18:21:24-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-07 00:47:10-04:00
loge9fc58eab77d60dfb02155ff17178b496d75d035
tree97cfc4358856f63f1676065df5a6055ded0c6f8b
parentd9b0c984aaf4f5e738ca4d06f160a9110f9167ec

LLVM: handle extern function name collisions

Zig allows multiple extern functions with the same name, and the backends have to handle this possibility. For LLVM, we keep a sparse map of collisions, and then resolve them in flushModule(). This introduces some technical debt that will have to be resolved when adding incremental compilation support to the LLVM backend.

2 files changed, 54 insertions(+), 12 deletions(-)

src/codegen/llvm.zig+47-11
......@@ -214,6 +214,10 @@ pub const Object = struct {
214214 /// Note that the values are not added until flushModule, when all errors in
215215 /// the compilation are known.
216216 error_name_table: ?*const llvm.Value,
217 /// This map is usually very close to empty. It tracks only the cases when a
218 /// second extern Decl could not be emitted with the correct name due to a
219 /// name collision.
220 extern_collisions: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void),
217221
218222 pub const TypeMap = std.HashMapUnmanaged(
219223 Type,
......@@ -376,6 +380,7 @@ pub const Object = struct {
376380 .type_map_arena = std.heap.ArenaAllocator.init(gpa),
377381 .di_type_map = .{},
378382 .error_name_table = null,
383 .extern_collisions = .{},
379384 };
380385 }
381386
......@@ -392,6 +397,7 @@ pub const Object = struct {
392397 self.decl_map.deinit(gpa);
393398 self.type_map.deinit(gpa);
394399 self.type_map_arena.deinit();
400 self.extern_collisions.deinit(gpa);
395401 self.* = undefined;
396402 }
397403
......@@ -508,6 +514,22 @@ pub const Object = struct {
508514 fn resolveExportExternCollisions(object: *Object) !void {
509515 const mod = object.module;
510516
517 // This map has externs with incorrect symbol names.
518 for (object.extern_collisions.keys()) |decl_index| {
519 const entry = object.decl_map.getEntry(decl_index) orelse continue;
520 const llvm_global = entry.value_ptr.*;
521 // Same logic as below but for externs instead of exports.
522 const decl = mod.declPtr(decl_index);
523 const other_global = object.getLlvmGlobal(decl.name) orelse continue;
524 if (other_global == llvm_global) continue;
525
526 const new_global_ptr = other_global.constBitCast(llvm_global.typeOf());
527 llvm_global.replaceAllUsesWith(new_global_ptr);
528 object.deleteLlvmGlobal(llvm_global);
529 entry.value_ptr.* = new_global_ptr;
530 }
531 object.extern_collisions.clearRetainingCapacity();
532
511533 const export_keys = mod.decl_exports.keys();
512534 for (mod.decl_exports.values()) |export_list, i| {
513535 const decl_index = export_keys[i];
......@@ -997,6 +1019,15 @@ pub const Object = struct {
9971019 return null;
9981020 }
9991021
1022 /// TODO can this be done with simpler logic / different API binding?
1023 fn deleteLlvmGlobal(o: Object, llvm_global: *const llvm.Value) void {
1024 if (o.llvm_module.getNamedFunction(llvm_global.getValueName()) != null) {
1025 llvm_global.deleteFunction();
1026 return;
1027 }
1028 return llvm_global.deleteGlobal();
1029 }
1030
10001031 pub fn updateDeclExports(
10011032 self: *Object,
10021033 module: *Module,
......@@ -1009,6 +1040,12 @@ pub const Object = struct {
10091040 const decl = module.declPtr(decl_index);
10101041 if (decl.isExtern()) {
10111042 llvm_global.setValueName(decl.name);
1043 if (self.getLlvmGlobal(decl.name)) |other_global| {
1044 if (other_global != llvm_global) {
1045 log.debug("updateDeclExports isExtern()=true setValueName({s}) conflict", .{decl.name});
1046 try self.extern_collisions.put(module.gpa, decl_index, {});
1047 }
1048 }
10121049 llvm_global.setUnnamedAddr(.False);
10131050 llvm_global.setLinkage(.External);
10141051 if (self.di_map.get(decl)) |di_node| {
......@@ -2143,11 +2180,8 @@ pub const DeclGen = struct {
21432180 log.debug("gen: {s} type: {}, value: {}", .{
21442181 decl.name, decl.ty.fmtDebug(), decl.val.fmtDebug(),
21452182 });
2146
2147 if (decl.val.castTag(.function)) |func_payload| {
2148 _ = func_payload;
2149 @panic("TODO llvm backend genDecl function pointer");
2150 } else if (decl.val.castTag(.extern_fn)) |extern_fn| {
2183 assert(decl.val.tag() != .function);
2184 if (decl.val.castTag(.extern_fn)) |extern_fn| {
21512185 _ = try dg.resolveLlvmFunction(extern_fn.data.owner_decl);
21522186 } else {
21532187 const target = dg.module.getTarget();
......@@ -2246,12 +2280,14 @@ pub const DeclGen = struct {
22462280 if (!is_extern) {
22472281 llvm_fn.setLinkage(.Internal);
22482282 llvm_fn.setUnnamedAddr(.True);
2249 } else if (dg.module.getTarget().isWasm()) {
2250 dg.addFnAttrString(llvm_fn, "wasm-import-name", std.mem.sliceTo(decl.name, 0));
2251 if (decl.getExternFn().?.lib_name) |lib_name| {
2252 const module_name = std.mem.sliceTo(lib_name, 0);
2253 if (!std.mem.eql(u8, module_name, "c")) {
2254 dg.addFnAttrString(llvm_fn, "wasm-import-module", module_name);
2283 } else {
2284 if (dg.module.getTarget().isWasm()) {
2285 dg.addFnAttrString(llvm_fn, "wasm-import-name", std.mem.sliceTo(decl.name, 0));
2286 if (decl.getExternFn().?.lib_name) |lib_name| {
2287 const module_name = std.mem.sliceTo(lib_name, 0);
2288 if (!std.mem.eql(u8, module_name, "c")) {
2289 dg.addFnAttrString(llvm_fn, "wasm-import-module", module_name);
2290 }
22552291 }
22562292 }
22572293 }
test/behavior/bugs/529.zig+7-1
......@@ -8,8 +8,14 @@ comptime {
88 _ = @import("529_other_file_2.zig");
99}
1010
11const builtin = @import("builtin");
12
1113test "issue 529 fixed" {
12 if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO
14 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
15 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
16 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
17 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
18 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1319
1420 @import("529_other_file.zig").issue529(null);
1521 issue529(null);