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