https://github.com/Changqing-JING created 
https://github.com/llvm/llvm-project/pull/200722

This adds a source-level way to override the import module for imported
WebAssembly globals without introducing new global-specific attributes.

Clang lowers annotate("wasm-import-module", "...") on globals to
!wasm.import.module metadata, and the WebAssembly backend uses that metadata
when emitting the import. Invalid uses are ignored with a warning.

>From 0c49fe895b6c2b04bd8053c14d81c9dd422bac6e Mon Sep 17 00:00:00 2001
From: Changqing Jing <[email protected]>
Date: Mon, 1 Jun 2026 13:39:26 +0800
Subject: [PATCH] [clang][WebAssembly] support custom module name of import
 globals

---
 clang/lib/CodeGen/Targets/WebAssembly.cpp     | 45 +++++++++++++++++++
 .../WebAssembly/wasm-global-import-module.cpp | 19 ++++++++
 .../WebAssembly/WebAssemblyAsmPrinter.cpp     | 28 ++++++++++--
 .../import-module-global-metadata.ll          | 24 ++++++++++
 4 files changed, 112 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGen/WebAssembly/wasm-global-import-module.cpp
 create mode 100644 
llvm/test/CodeGen/WebAssembly/import-module-global-metadata.ll

diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp 
b/clang/lib/CodeGen/Targets/WebAssembly.cpp
index ebe996a4edd8d..6ba23eff096c1 100644
--- a/clang/lib/CodeGen/Targets/WebAssembly.cpp
+++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp
@@ -8,10 +8,49 @@
 
 #include "ABIInfoImpl.h"
 #include "TargetInfo.h"
+#include "clang/AST/Expr.h"
+#include "clang/Basic/DiagnosticFrontend.h"
 
 using namespace clang;
 using namespace clang::CodeGen;
 
+namespace {
+
+void warnInvalidWebAssemblyImportModuleAnnotation(const AnnotateAttr *AA,
+                                                  CodeGenModule &CGM) {
+  CGM.getDiags().Report(AA->getLocation(), diag::warn_fe_backend_unsupported)
+      << "ignoring 'annotate(\"wasm-import-module\", ...)' because it "
+         "requires exactly one string literal argument";
+}
+
+void maybeSetWebAssemblyImportModuleMetadata(const VarDecl *VD,
+                                             llvm::GlobalVariable *GV,
+                                             CodeGen::CodeGenModule &CGM) {
+  for (const auto *AA : VD->specific_attrs<AnnotateAttr>()) {
+    if (AA->getAnnotation() != "wasm-import-module")
+      continue;
+
+    if (AA->args_size() != 1) {
+      warnInvalidWebAssemblyImportModuleAnnotation(AA, CGM);
+      continue;
+    }
+
+    const auto *Module = dyn_cast<StringLiteral>(
+        (*AA->args_begin())->IgnoreUnlessSpelledInSource());
+    if (!Module) {
+      warnInvalidWebAssemblyImportModuleAnnotation(AA, CGM);
+      continue;
+    }
+
+    llvm::LLVMContext &Ctx = CGM.getLLVMContext();
+    GV->setMetadata(
+        "wasm.import.module",
+        llvm::MDNode::get(Ctx, llvm::MDString::get(Ctx, Module->getString())));
+  }
+}
+
+} // namespace
+
 
//===----------------------------------------------------------------------===//
 // WebAssembly ABI Implementation
 //
@@ -57,6 +96,12 @@ class WebAssemblyTargetCodeGenInfo final : public 
TargetCodeGenInfo {
   void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
                            CodeGen::CodeGenModule &CGM) const override {
     TargetCodeGenInfo::setTargetAttributes(D, GV, CGM);
+    if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
+      if (auto *Global = dyn_cast<llvm::GlobalVariable>(GV))
+        maybeSetWebAssemblyImportModuleMetadata(VD, Global, CGM);
+      return;
+    }
+
     if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) {
       if (const auto *Attr = FD->getAttr<WebAssemblyImportModuleAttr>()) {
         llvm::Function *Fn = cast<llvm::Function>(GV);
diff --git a/clang/test/CodeGen/WebAssembly/wasm-global-import-module.cpp 
b/clang/test/CodeGen/WebAssembly/wasm-global-import-module.cpp
new file mode 100644
index 0000000000000..62fe13af9e27a
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/wasm-global-import-module.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm -verify -o - 
%s | FileCheck %s
+
+extern "C" {
+extern const int [[clang::address_space(1)]] imported_g
+    __attribute__((annotate("wasm-import-module", "js")));
+extern const int [[clang::address_space(1)]] bad_arity
+    __attribute__((annotate("wasm-import-module"))); // expected-warning 
{{ignoring 'annotate("wasm-import-module", ...)' because it requires exactly 
one string literal argument}}
+extern const int [[clang::address_space(1)]] bad_type
+    __attribute__((annotate("wasm-import-module", 42))); // expected-warning 
{{ignoring 'annotate("wasm-import-module", ...)' because it requires exactly 
one string literal argument}}
+extern const int [[clang::address_space(1)]] default_g;
+
+int get() { return imported_g + bad_arity + bad_type + default_g; }
+}
+
+// CHECK: @imported_g = external addrspace(1) constant i32, align 4, 
!wasm.import.module ![[MD:[0-9]+]]
+// CHECK: @bad_arity = external addrspace(1) constant i32, align 4
+// CHECK: @bad_type = external addrspace(1) constant i32, align 4
+// CHECK: @default_g = external addrspace(1) constant i32, align 4
+// CHECK: ![[MD]] = !{!"js"}
\ No newline at end of file
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index edea99e629407..f7942fb15bc19 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -55,6 +55,18 @@ using namespace llvm;
 
 #define DEBUG_TYPE "asm-printer"
 
+static StringRef getWasmImportModuleMetadata(const GlobalVariable &GV) {
+  MDNode *MD = GV.getMetadata("wasm.import.module");
+  if (!MD || MD->getNumOperands() == 0)
+    return {};
+
+  auto *Name = dyn_cast<MDString>(MD->getOperand(0));
+  if (!Name)
+    return {};
+
+  return Name->getString();
+}
+
 extern cl::opt<bool> WasmKeepRegisters;
 
 
//===----------------------------------------------------------------------===//
@@ -211,6 +223,14 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const 
GlobalVariable *GV) {
 
   emitVisibility(Sym, GV->getVisibility(), !GV->isDeclaration());
   emitSymbolType(Sym);
+  if (GV->isDeclaration()) {
+    StringRef ImportModule = getWasmImportModuleMetadata(*GV);
+    if (!ImportModule.empty()) {
+      Sym->setImportModule(OutContext.allocateString(ImportModule));
+      getTargetStreamer()->emitImportModule(Sym, ImportModule);
+    }
+  }
+
   if (GV->hasInitializer()) {
     assert(getSymbolPreferLocal(*GV) == Sym);
     emitLinkage(GV, Sym);
@@ -236,8 +256,7 @@ MCSymbol 
*WebAssemblyAsmPrinter::getOrCreateWasmSymbol(StringRef Name) {
   if (Name == "__stack_pointer" || Name == "__tls_base" ||
       Name == "__memory_base" || Name == "__table_base" ||
       Name == "__tls_size" || Name == "__tls_align") {
-    bool Mutable =
-        Name == "__stack_pointer" || Name == "__tls_base";
+    bool Mutable = Name == "__stack_pointer" || Name == "__tls_base";
     WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
     WasmSym->setGlobalType(wasm::WasmGlobalType{
         uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
@@ -477,7 +496,7 @@ void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) {
     OutStreamer->switchSection(Producers);
     OutStreamer->emitULEB128IntValue(FieldCount);
     for (auto &Producers : {std::make_pair("language", &Languages),
-            std::make_pair("processed-by", &Tools)}) {
+                            std::make_pair("processed-by", &Tools)}) {
       if (Producers.second->empty())
         continue;
       OutStreamer->emitULEB128IntValue(strlen(Producers.first));
@@ -586,7 +605,8 @@ void WebAssemblyAsmPrinter::EmitFunctionAttributes(Module 
&M) {
   // Emit a custom section for each unique attribute.
   for (const auto &[Name, Symbols] : CustomSections) {
     MCSectionWasm *CustomSection = OutContext.getWasmSection(
-        ".custom_section.llvm.func_attr.annotate." + Name, 
SectionKind::getMetadata());
+        ".custom_section.llvm.func_attr.annotate." + Name,
+        SectionKind::getMetadata());
     OutStreamer->pushSection();
     OutStreamer->switchSection(CustomSection);
 
diff --git a/llvm/test/CodeGen/WebAssembly/import-module-global-metadata.ll 
b/llvm/test/CodeGen/WebAssembly/import-module-global-metadata.ll
new file mode 100644
index 0000000000000..35691585a7960
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/import-module-global-metadata.ll
@@ -0,0 +1,24 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck --check-prefix=ASM %s
+; RUN: llc < %s --filetype=obj | obj2yaml | FileCheck --check-prefix=OBJ %s
+
+target triple = "wasm32-unknown-unknown"
+
+@imported_g = external addrspace(1) global i32, !wasm.import.module !0
+
+define i32 @get() {
+  %v = load i32, ptr addrspace(1) @imported_g
+  ret i32 %v
+}
+
+; ASM: .globaltype imported_g, i32
+; ASM-NEXT: .import_module imported_g, "js"
+
+; OBJ:      - Type:            IMPORT
+; OBJ:        Imports:
+; OBJ:          - Module:          js
+; OBJ-NEXT:       Field:           imported_g
+; OBJ-NEXT:       Kind:            GLOBAL
+; OBJ-NEXT:       GlobalType:      I32
+; OBJ-NEXT:       GlobalMutable:   true
+
+!0 = !{!"js"}
\ No newline at end of file

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to