Commit 2d280825cd

LemonBoy <thatlemon@gmail.com>
2020-11-12 20:16:58
stage1: Disambiguate Wasm imports with same name
Closes #7088
1 parent 1061759
Changed files (1)
src
src/stage1/codegen.cpp
@@ -380,9 +380,36 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
     LLVMTypeRef fn_llvm_type = fn->raw_type_ref;
     LLVMValueRef llvm_fn = nullptr;
     if (fn->body_node == nullptr) {
+        assert(fn->proto_node->type == NodeTypeFnProto);
+        AstNodeFnProto *fn_proto = &fn->proto_node->data.fn_proto;
+
         const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref);
+
+        // The compiler tries to deduplicate extern definitions by looking up
+        // their name, this was introduced to allow the declaration of the same
+        // extern function with differing prototypes.
+        // When Wasm is targeted this check becomes a problem as the user may
+        // declare two (or more) extern functions sharing the same name but
+        // imported from different modules!
+        // To overcome this problem we generate a mangled identifier out of the
+        // import and the function name, this name is only visible within the
+        // compiler as we're telling LLVM (using 'wasm-import-name' and
+        // 'wasm-import-name') what the real function name is and where to find
+        // it.
+        const bool use_mangled_name = target_is_wasm(g->zig_target) &&
+                fn_proto->is_extern && fn_proto->lib_name != nullptr;
+        // Pick a weird name to avoid collisions...
+        // This whole function should be burned to the ground.
+        Buf *mangled_symbol_buf = use_mangled_name ?
+                buf_sprintf("%s|%s", unmangled_name, buf_ptr(fn_proto->lib_name)) :
+                nullptr;
+        symbol_name = use_mangled_name ?
+                buf_ptr(mangled_symbol_buf) : unmangled_name;
+
         LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, symbol_name);
+
         if (existing_llvm_fn) {
+            if (mangled_symbol_buf) buf_destroy(mangled_symbol_buf);
             return LLVMConstBitCast(existing_llvm_fn, LLVMPointerType(fn_llvm_type, fn_addrspace));
         } else {
             Buf *buf_symbol_name = buf_create_from_str(symbol_name);
@@ -392,12 +419,9 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
             if (entry == nullptr) {
                 llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type);
 
-                if (target_is_wasm(g->zig_target)) {
-                    assert(fn->proto_node->type == NodeTypeFnProto);
-                    AstNodeFnProto *fn_proto = &fn->proto_node->data.fn_proto;
-                    if (fn_proto-> is_extern && fn_proto->lib_name != nullptr ) {
-                        addLLVMFnAttrStr(llvm_fn, "wasm-import-module", buf_ptr(fn_proto->lib_name));
-                    }
+                if (use_mangled_name) {
+                    addLLVMFnAttrStr(llvm_fn, "wasm-import-name", unmangled_name);
+                    addLLVMFnAttrStr(llvm_fn, "wasm-import-module", buf_ptr(fn_proto->lib_name));
                 }
             } else {
                 assert(entry->value->id == TldIdFn);
@@ -407,8 +431,11 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
                 tld_fn->fn_entry->llvm_value = LLVMAddFunction(g->module, symbol_name,
                         tld_fn->fn_entry->raw_type_ref);
                 llvm_fn = LLVMConstBitCast(tld_fn->fn_entry->llvm_value, LLVMPointerType(fn_llvm_type, fn_addrspace));
+                if (mangled_symbol_buf) buf_destroy(mangled_symbol_buf);
                 return llvm_fn;
             }
+
+            if (mangled_symbol_buf) buf_destroy(mangled_symbol_buf);
         }
     } else {
         if (llvm_fn == nullptr) {