about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_codegen_llvm/attributes.rs11
-rw-r--r--src/librustc_codegen_utils/symbol_names.rs28
-rw-r--r--src/test/run-make/wasm-symbols-different-module/Makefile28
-rw-r--r--src/test/run-make/wasm-symbols-different-module/bar.rs33
-rw-r--r--src/test/run-make/wasm-symbols-different-module/baz.rs22
-rw-r--r--src/test/run-make/wasm-symbols-different-module/foo.rs23
-rw-r--r--src/test/run-make/wasm-symbols-different-module/log.rs16
-rw-r--r--src/test/run-make/wasm-symbols-different-module/verify-imports.js32
8 files changed, 189 insertions, 4 deletions
diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs
index 5479a1f3144..3f2a51b45bd 100644
--- a/src/librustc_codegen_llvm/attributes.rs
+++ b/src/librustc_codegen_llvm/attributes.rs
@@ -344,6 +344,17 @@ pub fn from_fn_attrs(
                 const_cstr!("wasm-import-module"),
                 &module,
             );
+
+            let name = codegen_fn_attrs.link_name.unwrap_or_else(|| {
+                cx.tcx.item_name(instance.def_id())
+            });
+            let name = CString::new(&name.as_str()[..]).unwrap();
+            llvm::AddFunctionAttrStringValue(
+                llfn,
+                llvm::AttributePlace::Function,
+                const_cstr!("wasm-import-name"),
+                &name,
+            );
         }
     }
 }
diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs
index c52c6cfa83c..922964ee45f 100644
--- a/src/librustc_codegen_utils/symbol_names.rs
+++ b/src/librustc_codegen_utils/symbol_names.rs
@@ -142,12 +142,32 @@ fn symbol_name(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Symbol {
     };
 
     let attrs = tcx.codegen_fn_attrs(def_id);
+
+    // Foreign items by default use no mangling for their symbol name. There's a
+    // few exceptions to this rule though:
+    //
+    // * This can be overridden with the `#[link_name]` attribute
+    //
+    // * On the wasm32 targets there is a bug (or feature) in LLD [1] where the
+    //   same-named symbol when imported from different wasm modules will get
+    //   hooked up incorectly. As a result foreign symbols, on the wasm target,
+    //   with a wasm import module, get mangled. Additionally our codegen will
+    //   deduplicate symbols based purely on the symbol name, but for wasm this
+    //   isn't quite right because the same-named symbol on wasm can come from
+    //   different modules. For these reasons if `#[link(wasm_import_module)]`
+    //   is present we mangle everything on wasm because the demangled form will
+    //   show up in the `wasm-import-name` custom attribute in LLVM IR.
+    //
+    // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316
     if is_foreign {
-        if let Some(name) = attrs.link_name {
-            return name;
+        if tcx.sess.target.target.arch != "wasm32" ||
+            !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id)
+        {
+            if let Some(name) = attrs.link_name {
+                return name;
+            }
+            return tcx.item_name(def_id);
         }
-        // Don't mangle foreign items.
-        return tcx.item_name(def_id);
     }
 
     if let Some(name) = attrs.export_name {
diff --git a/src/test/run-make/wasm-symbols-different-module/Makefile b/src/test/run-make/wasm-symbols-different-module/Makefile
new file mode 100644
index 00000000000..bb6a5d3c9d2
--- /dev/null
+++ b/src/test/run-make/wasm-symbols-different-module/Makefile
@@ -0,0 +1,28 @@
+-include ../../run-make-fulldeps/tools.mk
+
+# only-wasm32-bare
+
+all:
+	$(RUSTC) foo.rs --target wasm32-unknown-unknown
+	$(NODE) verify-imports.js $(TMPDIR)/foo.wasm a/foo b/foo
+	$(RUSTC) foo.rs --target wasm32-unknown-unknown -C lto
+	$(NODE) verify-imports.js $(TMPDIR)/foo.wasm a/foo b/foo
+	$(RUSTC) foo.rs --target wasm32-unknown-unknown -O
+	$(NODE) verify-imports.js $(TMPDIR)/foo.wasm a/foo b/foo
+	$(RUSTC) foo.rs --target wasm32-unknown-unknown -O -C lto
+	$(NODE) verify-imports.js $(TMPDIR)/foo.wasm a/foo b/foo
+
+	$(RUSTC) bar.rs --target wasm32-unknown-unknown
+	$(NODE) verify-imports.js $(TMPDIR)/bar.wasm m1/f m1/g m2/f
+	$(RUSTC) bar.rs --target wasm32-unknown-unknown -C lto
+	$(NODE) verify-imports.js $(TMPDIR)/bar.wasm m1/f m1/g m2/f
+	$(RUSTC) bar.rs --target wasm32-unknown-unknown -O
+	$(NODE) verify-imports.js $(TMPDIR)/bar.wasm m1/f m1/g m2/f
+	$(RUSTC) bar.rs --target wasm32-unknown-unknown -O -C lto
+	$(NODE) verify-imports.js $(TMPDIR)/bar.wasm m1/f m1/g m2/f
+
+	$(RUSTC) baz.rs --target wasm32-unknown-unknown
+	$(NODE) verify-imports.js $(TMPDIR)/baz.wasm sqlite/allocate sqlite/deallocate
+
+	$(RUSTC) log.rs --target wasm32-unknown-unknown
+	$(NODE) verify-imports.js $(TMPDIR)/log.wasm test/log
diff --git a/src/test/run-make/wasm-symbols-different-module/bar.rs b/src/test/run-make/wasm-symbols-different-module/bar.rs
new file mode 100644
index 00000000000..7567060d781
--- /dev/null
+++ b/src/test/run-make/wasm-symbols-different-module/bar.rs
@@ -0,0 +1,33 @@
+//! Issue #50021
+
+#![crate_type = "cdylib"]
+
+mod m1 {
+    #[link(wasm_import_module = "m1")]
+    extern "C" {
+        pub fn f();
+    }
+    #[link(wasm_import_module = "m1")]
+    extern "C" {
+        pub fn g();
+    }
+}
+
+mod m2 {
+    #[link(wasm_import_module = "m2")]
+    extern "C" {
+        pub fn f(_: i32);
+    }
+}
+
+#[no_mangle]
+pub unsafe fn run() {
+    m1::f();
+    m1::g();
+
+    // In generated code, expected:
+    // (import "m2" "f" (func $f (param i32)))
+    // but got:
+    // (import "m1" "f" (func $f (param i32)))
+    m2::f(0);
+}
diff --git a/src/test/run-make/wasm-symbols-different-module/baz.rs b/src/test/run-make/wasm-symbols-different-module/baz.rs
new file mode 100644
index 00000000000..fbb78619bb8
--- /dev/null
+++ b/src/test/run-make/wasm-symbols-different-module/baz.rs
@@ -0,0 +1,22 @@
+//! Issue #63562
+
+#![crate_type = "cdylib"]
+
+mod foo {
+    #[link(wasm_import_module = "sqlite")]
+    extern "C" {
+        pub fn allocate(size: usize) -> i32;
+        pub fn deallocate(ptr: i32, size: usize);
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn allocate() {
+    unsafe {
+        foo::allocate(1);
+        foo::deallocate(1, 2);
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn deallocate() {}
diff --git a/src/test/run-make/wasm-symbols-different-module/foo.rs b/src/test/run-make/wasm-symbols-different-module/foo.rs
new file mode 100644
index 00000000000..a4ba7e714cc
--- /dev/null
+++ b/src/test/run-make/wasm-symbols-different-module/foo.rs
@@ -0,0 +1,23 @@
+#![crate_type = "cdylib"]
+
+mod a {
+    #[link(wasm_import_module = "a")]
+    extern "C" {
+        pub fn foo();
+    }
+}
+
+mod b {
+    #[link(wasm_import_module = "b")]
+    extern "C" {
+        pub fn foo();
+    }
+}
+
+#[no_mangle]
+pub fn start() {
+    unsafe {
+        a::foo();
+        b::foo();
+    }
+}
diff --git a/src/test/run-make/wasm-symbols-different-module/log.rs b/src/test/run-make/wasm-symbols-different-module/log.rs
new file mode 100644
index 00000000000..ea3e0b4b2be
--- /dev/null
+++ b/src/test/run-make/wasm-symbols-different-module/log.rs
@@ -0,0 +1,16 @@
+//! Issue #56309
+
+#![crate_type = "cdylib"]
+
+#[link(wasm_import_module = "test")]
+extern "C" {
+    fn log(message_data: u32, message_size: u32);
+}
+
+#[no_mangle]
+pub fn main() {
+    let message = "Hello, world!";
+    unsafe {
+        log(message.as_ptr() as u32, message.len() as u32);
+    }
+}
diff --git a/src/test/run-make/wasm-symbols-different-module/verify-imports.js b/src/test/run-make/wasm-symbols-different-module/verify-imports.js
new file mode 100644
index 00000000000..7e9f90cf8bd
--- /dev/null
+++ b/src/test/run-make/wasm-symbols-different-module/verify-imports.js
@@ -0,0 +1,32 @@
+const fs = require('fs');
+const process = require('process');
+const assert = require('assert');
+const buffer = fs.readFileSync(process.argv[2]);
+
+let m = new WebAssembly.Module(buffer);
+let list = WebAssembly.Module.imports(m);
+console.log('imports', list);
+if (list.length !== process.argv.length - 3)
+  throw new Error("wrong number of imports")
+
+const imports = new Map();
+for (let i = 3; i < process.argv.length; i++) {
+  const [module, name] = process.argv[i].split('/');
+  if (!imports.has(module))
+    imports.set(module, new Map());
+  imports.get(module).set(name, true);
+}
+
+for (let i of list) {
+  if (imports.get(i.module) === undefined || imports.get(i.module).get(i.name) === undefined)
+    throw new Error(`didn't find import of ${i.module}::${i.name}`);
+  imports.get(i.module).delete(i.name);
+
+  if (imports.get(i.module).size === 0)
+    imports.delete(i.module);
+}
+
+console.log(imports);
+if (imports.size !== 0) {
+  throw new Error('extra imports');
+}