https://github.com/dschuff updated https://github.com/llvm/llvm-project/pull/201966
>From a046ff2e5194cb1c84a276d6bfe4c340d22d412e Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Fri, 5 Jun 2026 23:26:49 +0000 Subject: [PATCH 01/13] [WebAssembly] Add support for import and export name attributes on global vars Currently these are only supported on functions. This change adds support to addrspace(1) global variables, which lower to wasm globals. --- clang/include/clang/Basic/Attr.td | 6 +- .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/CodeGen/Targets/WebAssembly.cpp | 58 +++++++++++++++--- clang/lib/Sema/SemaWasm.cpp | 60 +++++++++++-------- .../CodeGen/WebAssembly/wasm-global-export.c | 8 +++ .../CodeGen/WebAssembly/wasm-global-import.c | 11 ++++ .../CodeGen/WebAssembly/wasm-import-err-fun.c | 3 + .../CodeGen/WebAssembly/wasm-import-err-var.c | 5 ++ llvm/lib/MC/WasmObjectWriter.cpp | 8 +++ .../WebAssembly/WebAssemblyAsmPrinter.cpp | 39 ++++++++++++ .../WebAssembly/export-metadata-global.ll | 18 ++++++ .../WebAssembly/import-metadata-global-err.ll | 9 +++ .../WebAssembly/import-metadata-global.ll | 26 ++++++++ 13 files changed, 216 insertions(+), 37 deletions(-) create mode 100644 clang/test/CodeGen/WebAssembly/wasm-global-export.c create mode 100644 clang/test/CodeGen/WebAssembly/wasm-global-import.c create mode 100644 clang/test/CodeGen/WebAssembly/wasm-import-err-fun.c create mode 100644 clang/test/CodeGen/WebAssembly/wasm-import-err-var.c create mode 100644 llvm/test/CodeGen/WebAssembly/export-metadata-global.ll create mode 100644 llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll create mode 100644 llvm/test/CodeGen/WebAssembly/import-metadata-global.ll diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index ec82a14e9393b..77f45dade1426 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2566,7 +2566,7 @@ def WebAssemblyExportName : InheritableAttr, let Spellings = [Clang<"export_name">]; let Args = [StringArgument<"ExportName">]; let Documentation = [WebAssemblyExportNameDocs]; - let Subjects = SubjectList<[Function], ErrorDiag>; + let Subjects = SubjectList<[Function, GlobalVar], ErrorDiag>; } def WebAssemblyImportModule : InheritableAttr, @@ -2574,7 +2574,7 @@ def WebAssemblyImportModule : InheritableAttr, let Spellings = [Clang<"import_module">]; let Args = [StringArgument<"ImportModule">]; let Documentation = [WebAssemblyImportModuleDocs]; - let Subjects = SubjectList<[Function], ErrorDiag>; + let Subjects = SubjectList<[Function, GlobalVar], ErrorDiag>; } def WebAssemblyImportName : InheritableAttr, @@ -2582,7 +2582,7 @@ def WebAssemblyImportName : InheritableAttr, let Spellings = [Clang<"import_name">]; let Args = [StringArgument<"ImportName">]; let Documentation = [WebAssemblyImportNameDocs]; - let Subjects = SubjectList<[Function], ErrorDiag>; + let Subjects = SubjectList<[Function, GlobalVar], ErrorDiag>; } def NoSplitStack : InheritableAttr { diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 423482fd08b27..493e5b91c17af 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13401,7 +13401,7 @@ def warn_mismatched_import : Warning< "previous declaration">, InGroup<IgnoredAttributes>; def warn_import_on_definition : Warning< - "import %select{module|name}0 cannot be applied to a function with a definition">, + "import %select{module|name}0 cannot be applied to a %select{function|variable}1 with a definition">, InGroup<IgnoredAttributes>; def err_preserve_field_info_not_field : Error< diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index 71454982c4f82..3c99f3c116052 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -8,6 +8,7 @@ #include "ABIInfoImpl.h" #include "TargetInfo.h" +#include "clang/Basic/DiagnosticFrontend.h" using namespace clang; using namespace clang::CodeGen; @@ -57,17 +58,58 @@ 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 *FD = dyn_cast_or_null<FunctionDecl>(D)) { - if (const auto *Attr = FD->getAttr<WebAssemblyImportModuleAttr>()) { - llvm::Function *Fn = cast<llvm::Function>(GV); - llvm::AttrBuilder B(GV->getContext()); - B.addAttribute("wasm-import-module", Attr->getImportModule()); - Fn->addFnAttrs(B); + if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) { + if (auto *Global = dyn_cast<llvm::GlobalVariable>(GV)) { + const auto *ModuleAttr = VD->getAttr<WebAssemblyImportModuleAttr>(); + const auto *NameAttr = VD->getAttr<WebAssemblyImportNameAttr>(); + if (ModuleAttr || NameAttr) { + if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) { + auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation(); + CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) + << "import attribute cannot be applied to a definition"; + return; + } + llvm::LLVMContext &Ctx = CGM.getLLVMContext(); + if (ModuleAttr) { + Global->setMetadata( + "wasm.import.module", + llvm::MDNode::get(Ctx, llvm::MDString::get(Ctx, ModuleAttr->getImportModule()))); + } + if (NameAttr) { + Global->setMetadata( + "wasm.import.name", + llvm::MDNode::get(Ctx, llvm::MDString::get(Ctx, NameAttr->getImportName()))); + } + } + if (const auto *Attr = VD->getAttr<WebAssemblyExportNameAttr>()) { + llvm::LLVMContext &Ctx = CGM.getLLVMContext(); + Global->setMetadata( + "wasm.export.name", + llvm::MDNode::get(Ctx, llvm::MDString::get(Ctx, Attr->getExportName()))); + } } - if (const auto *Attr = FD->getAttr<WebAssemblyImportNameAttr>()) { + return; + } + + if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) { + const auto *ModuleAttr = FD->getAttr<WebAssemblyImportModuleAttr>(); + const auto *NameAttr = FD->getAttr<WebAssemblyImportNameAttr>(); + if (ModuleAttr || NameAttr) { + if (FD->isThisDeclarationADefinition()) { + auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation(); + CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) + << "import attribute cannot be applied to a definition"; + auto *NonConstFD = const_cast<FunctionDecl *>(FD); + NonConstFD->dropAttr<WebAssemblyImportModuleAttr>(); + NonConstFD->dropAttr<WebAssemblyImportNameAttr>(); + return; + } llvm::Function *Fn = cast<llvm::Function>(GV); llvm::AttrBuilder B(GV->getContext()); - B.addAttribute("wasm-import-name", Attr->getImportName()); + if (ModuleAttr) + B.addAttribute("wasm-import-module", ModuleAttr->getImportModule()); + if (NameAttr) + B.addAttribute("wasm-import-name", NameAttr->getImportName()); Fn->addFnAttrs(B); } if (const auto *Attr = FD->getAttr<WebAssemblyExportNameAttr>()) { diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index 083460eb6e201..9b75fc62dce85 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -358,49 +358,59 @@ SemaWasm::mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL) { void SemaWasm::handleWebAssemblyImportModuleAttr(Decl *D, const ParsedAttr &AL) { - auto *FD = cast<FunctionDecl>(D); - StringRef Str; SourceLocation ArgLoc; if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) return; - if (FD->hasBody()) { - Diag(AL.getLoc(), diag::warn_import_on_definition) << 0; - return; - } - FD->addAttr(::new (getASTContext()) - WebAssemblyImportModuleAttr(getASTContext(), AL, Str)); + if (auto *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->hasBody()) { + Diag(AL.getLoc(), diag::warn_import_on_definition) << 0 << 0; + return; + } + FD->addAttr(::new (getASTContext()) + WebAssemblyImportModuleAttr(getASTContext(), AL, Str)); + } else if (auto *VD = dyn_cast<VarDecl>(D)) { + if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) { + Diag(AL.getLoc(), diag::warn_import_on_definition) << 0 << 1; + return; + } + VD->addAttr(::new (getASTContext()) + WebAssemblyImportModuleAttr(getASTContext(), AL, Str)); + } } void SemaWasm::handleWebAssemblyImportNameAttr(Decl *D, const ParsedAttr &AL) { - auto *FD = cast<FunctionDecl>(D); - StringRef Str; SourceLocation ArgLoc; if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) return; - if (FD->hasBody()) { - Diag(AL.getLoc(), diag::warn_import_on_definition) << 1; - return; - } - FD->addAttr(::new (getASTContext()) - WebAssemblyImportNameAttr(getASTContext(), AL, Str)); + if (auto *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->hasBody()) { + Diag(AL.getLoc(), diag::warn_import_on_definition) << 1 << 0; + return; + } + FD->addAttr(::new (getASTContext()) + WebAssemblyImportNameAttr(getASTContext(), AL, Str)); + } else if (auto *VD = dyn_cast<VarDecl>(D)) { + if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) { + Diag(AL.getLoc(), diag::warn_import_on_definition) << 1 << 1; + return; + } + VD->addAttr(::new (getASTContext()) + WebAssemblyImportNameAttr(getASTContext(), AL, Str)); + } } void SemaWasm::handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL) { ASTContext &Context = getASTContext(); - if (!isFuncOrMethodForAttrSubject(D)) { - Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) - << AL << AL.isRegularKeywordAttribute() << ExpectedFunction; - return; - } - auto *FD = cast<FunctionDecl>(D); - if (FD->isThisDeclarationADefinition()) { - Diag(D->getLocation(), diag::err_alias_is_definition) << FD << 0; - return; + if (auto *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->isThisDeclarationADefinition()) { + Diag(D->getLocation(), diag::err_alias_is_definition) << FD << 0; + return; + } } StringRef Str; diff --git a/clang/test/CodeGen/WebAssembly/wasm-global-export.c b/clang/test/CodeGen/WebAssembly/wasm-global-export.c new file mode 100644 index 0000000000000..3ea1da0f20ef6 --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/wasm-global-export.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm -o - %s | FileCheck %s + +// Test export_name +int __attribute__((address_space(1))) exported_g + __attribute__((export_name("global_g"))) = 42; + +// CHECK: @exported_g = addrspace(1) global i32 42, align 4, !wasm.export.name ![[MD_EXPORT:[0-9]+]] +// CHECK: ![[MD_EXPORT]] = !{!"global_g"} diff --git a/clang/test/CodeGen/WebAssembly/wasm-global-import.c b/clang/test/CodeGen/WebAssembly/wasm-global-import.c new file mode 100644 index 0000000000000..7ea5ef874b3bd --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/wasm-global-import.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm -o - %s | FileCheck %s + +// Test import_module and import_name +extern const int __attribute__((address_space(1))) imported_g + __attribute__((import_module("js"), import_name("global_g"))); + +int get_import(void) { return imported_g; } + +// CHECK: @imported_g = external addrspace(1) constant i32, align 4, !wasm.import.module ![[MD_MOD:[0-9]+]], !wasm.import.name ![[MD_NAME:[0-9]+]] +// CHECK: ![[MD_MOD]] = !{!"js"} +// CHECK: ![[MD_NAME]] = !{!"global_g"} diff --git a/clang/test/CodeGen/WebAssembly/wasm-import-err-fun.c b/clang/test/CodeGen/WebAssembly/wasm-import-err-fun.c new file mode 100644 index 0000000000000..c819f8a4e6b5e --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/wasm-import-err-fun.c @@ -0,0 +1,3 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm-only -verify %s + +void defined_fn(void) __attribute__((import_module("js"))) {} // expected-error {{import attribute cannot be applied to a definition}} diff --git a/clang/test/CodeGen/WebAssembly/wasm-import-err-var.c b/clang/test/CodeGen/WebAssembly/wasm-import-err-var.c new file mode 100644 index 0000000000000..31cfeddcdba7f --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/wasm-import-err-var.c @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm-only -Wno-extern-initializer -verify %s + +// Test definition inline +extern const int __attribute__((address_space(1))) defined_g_inline + __attribute__((import_module("js"))) = 42; // expected-error {{import attribute cannot be applied to a definition}} diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp index 44ca69f0ff302..be970ea3df7ee 100644 --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -1672,6 +1672,14 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler &Asm, assert(!WasmIndices.contains(&WS)); WasmIndices[&WS] = Global.Index; Globals.push_back(Global); + + if (WS.hasExportName()) { + wasm::WasmExport Export; + Export.Name = WS.getExportName(); + Export.Kind = wasm::WASM_EXTERNAL_GLOBAL; + Export.Index = Global.Index; + Exports.push_back(Export); + } } else { // An import; the index was assigned above LLVM_DEBUG(dbgs() << " -> global index: " diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index c25972343c96a..8bddc9068359b 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -179,6 +179,18 @@ MCSymbolWasm *WebAssemblyAsmPrinter::getMCSymbolForFunction( return WasmSym; } +static StringRef getWasmMetadata(const GlobalVariable &GV, StringRef Key) { + MDNode *MD = GV.getMetadata(Key); + if (!MD || MD->getNumOperands() == 0) + return {}; + + auto *Name = dyn_cast<MDString>(MD->getOperand(0)); + if (!Name) + return {}; + + return Name->getString(); +} + void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { if (GV->hasCommonLinkage()) { OutContext.reportError(SMLoc(), @@ -187,6 +199,16 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { return; } + if (!GV->isDeclaration()) { + if (!getWasmMetadata(*GV, "wasm.import.module").empty() || + !getWasmMetadata(*GV, "wasm.import.name").empty()) { + OutContext.reportError(SMLoc(), + "definition of global '" + GV->getName() + + "' cannot have import metadata"); + return; + } + } + if (!WebAssembly::isWasmVarAddressSpace(GV->getAddressSpace())) { AsmPrinter::emitGlobalVariable(GV); return; @@ -211,10 +233,27 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { emitVisibility(Sym, GV->getVisibility(), !GV->isDeclaration()); emitSymbolType(Sym); + if (GV->isDeclaration()) { + StringRef ImportModule = getWasmMetadata(*GV, "wasm.import.module"); + if (!ImportModule.empty()) { + Sym->setImportModule(OutContext.allocateString(ImportModule)); + getTargetStreamer()->emitImportModule(Sym, ImportModule); + } + StringRef ImportName = getWasmMetadata(*GV, "wasm.import.name"); + if (!ImportName.empty()) { + Sym->setImportName(OutContext.allocateString(ImportName)); + getTargetStreamer()->emitImportName(Sym, ImportName); + } + } if (GV->hasInitializer()) { assert(getSymbolPreferLocal(*GV) == Sym); emitLinkage(GV, Sym); OutStreamer->emitLabel(Sym); + StringRef ExportName = getWasmMetadata(*GV, "wasm.export.name"); + if (!ExportName.empty()) { + Sym->setExportName(OutContext.allocateString(ExportName)); + getTargetStreamer()->emitExportName(Sym, ExportName); + } // TODO: Actually emit the initializer value. Otherwise the global has the // default value for its type (0, ref.null, etc). OutStreamer->addBlankLine(); diff --git a/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll b/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll new file mode 100644 index 0000000000000..e7dbf0e4c29c5 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll @@ -0,0 +1,18 @@ +; 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" + +@exported_g = addrspace(1) global i32 42, !wasm.export.name !0 + +; ASM: .globaltype exported_g, i32 +; ASM: exported_g: +; ASM-NEXT: .export_name exported_g, "global_g" + +; OBJ: - Type: EXPORT +; OBJ: Exports: +; OBJ: - Name: global_g +; OBJ-NEXT: Kind: GLOBAL +; OBJ-NEXT: Index: 0 + +!0 = !{!"global_g"} diff --git a/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll b/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll new file mode 100644 index 0000000000000..b678884b6504b --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll @@ -0,0 +1,9 @@ +; RUN: not llc < %s 2>&1 | FileCheck %s + +target triple = "wasm32-unknown-unknown" + +@g = global i32 42, !wasm.import.module !0 + +; CHECK: error: definition of global 'g' cannot have import metadata + +!0 = !{!"js"} diff --git a/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll b/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll new file mode 100644 index 0000000000000..a3a052ec1534d --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll @@ -0,0 +1,26 @@ +; 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, !wasm.import.name !1 + +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" +; ASM-NEXT: .import_name imported_g, "global_g" + +; OBJ: - Type: IMPORT +; OBJ: Imports: +; OBJ: - Module: js +; OBJ-NEXT: Field: global_g +; OBJ-NEXT: Kind: GLOBAL +; OBJ-NEXT: GlobalType: I32 +; OBJ-NEXT: GlobalMutable: true + +!0 = !{!"js"} +!1 = !{!"global_g"} >From 661e2b82fe0fc1fcac84322b9eb71e0913ffaae2 Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Fri, 5 Jun 2026 23:57:03 +0000 Subject: [PATCH 02/13] update name merging logic and implement export name merging --- .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/include/clang/Sema/SemaWasm.h | 2 + clang/lib/Sema/SemaDecl.cpp | 6 +- clang/lib/Sema/SemaWasm.cpp | 101 +++++++++++++----- ...a-attribute-supported-attributes-list.test | 6 +- clang/test/Sema/attr-wasm.c | 23 +++- 6 files changed, 107 insertions(+), 33 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 493e5b91c17af..3c7a51245ab3d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13397,7 +13397,7 @@ def err_builtin_matrix_invalid_member : Error<"invalid matrix member '%0' expected %1">; def warn_mismatched_import : Warning< - "import %select{module|name}0 (%1) does not match the import %select{module|name}0 (%2) of the " + "%select{import module|import name|export name}0 (%1) does not match the %select{import module|import name|export name}0 (%2) of the " "previous declaration">, InGroup<IgnoredAttributes>; def warn_import_on_definition : Warning< diff --git a/clang/include/clang/Sema/SemaWasm.h b/clang/include/clang/Sema/SemaWasm.h index f82590755d183..c70cda259029e 100644 --- a/clang/include/clang/Sema/SemaWasm.h +++ b/clang/include/clang/Sema/SemaWasm.h @@ -44,6 +44,8 @@ class SemaWasm : public SemaBase { mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL); WebAssemblyImportModuleAttr * mergeImportModuleAttr(Decl *D, const WebAssemblyImportModuleAttr &AL); + WebAssemblyExportNameAttr * + mergeExportNameAttr(Decl *D, const WebAssemblyExportNameAttr &AL); void handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL); void handleWebAssemblyImportModuleAttr(Decl *D, const ParsedAttr &AL); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 694067bea9e48..8752f3417eef5 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2987,6 +2987,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.Wasm().mergeImportModuleAttr(D, *IMA); else if (const auto *INA = dyn_cast<WebAssemblyImportNameAttr>(Attr)) NewAttr = S.Wasm().mergeImportNameAttr(D, *INA); + else if (const auto *ENA = dyn_cast<WebAssemblyExportNameAttr>(Attr)) + NewAttr = S.Wasm().mergeExportNameAttr(D, *ENA); else if (const auto *TCBA = dyn_cast<EnforceTCBAttr>(Attr)) NewAttr = S.mergeEnforceTCBAttr(D, *TCBA); else if (const auto *TCBLA = dyn_cast<EnforceTCBLeafAttr>(Attr)) @@ -15385,14 +15387,14 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) { } if (UsedAttr *Attr = VD->getAttr<UsedAttr>()) { - if (!Attr->isInherited() && !VD->isThisDeclarationADefinition()) { + if (!Attr->isInherited() && !Attr->isImplicit() && !VD->isThisDeclarationADefinition()) { Diag(Attr->getLocation(), diag::warn_attribute_ignored_on_non_definition) << Attr; VD->dropAttr<UsedAttr>(); } } if (RetainAttr *Attr = VD->getAttr<RetainAttr>()) { - if (!Attr->isInherited() && !VD->isThisDeclarationADefinition()) { + if (!Attr->isInherited() && !Attr->isImplicit() && !VD->isThisDeclarationADefinition()) { Diag(Attr->getLocation(), diag::warn_attribute_ignored_on_non_definition) << Attr; VD->dropAttr<RetainAttr>(); diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index 9b75fc62dce85..4ea95617892c3 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -317,20 +317,33 @@ bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, WebAssemblyImportModuleAttr * SemaWasm::mergeImportModuleAttr(Decl *D, - const WebAssemblyImportModuleAttr &AL) { - auto *FD = cast<FunctionDecl>(D); - - if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportModuleAttr>()) { - if (ExistingAttr->getImportModule() == AL.getImportModule()) + const WebAssemblyImportModuleAttr &AL) { + if (auto *FD = dyn_cast<FunctionDecl>(D)) { + if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportModuleAttr>()) { + if (ExistingAttr->getImportModule() == AL.getImportModule()) + return nullptr; + Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) + << 0 << ExistingAttr->getImportModule() << AL.getImportModule(); + Diag(AL.getLoc(), diag::note_previous_attribute); return nullptr; - Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) - << 0 << ExistingAttr->getImportModule() << AL.getImportModule(); - Diag(AL.getLoc(), diag::note_previous_attribute); - return nullptr; - } - if (FD->hasBody()) { - Diag(AL.getLoc(), diag::warn_import_on_definition) << 0; - return nullptr; + } + if (FD->hasBody()) { + Diag(AL.getLoc(), diag::warn_import_on_definition) << 0 << 0; + return nullptr; + } + } else if (auto *VD = dyn_cast<VarDecl>(D)) { + if (const auto *ExistingAttr = VD->getAttr<WebAssemblyImportModuleAttr>()) { + if (ExistingAttr->getImportModule() == AL.getImportModule()) + return nullptr; + Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) + << 0 << ExistingAttr->getImportModule() << AL.getImportModule(); + Diag(AL.getLoc(), diag::note_previous_attribute); + return nullptr; + } + if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) { + Diag(AL.getLoc(), diag::warn_import_on_definition) << 0 << 1; + return nullptr; + } } return ::new (getASTContext()) WebAssemblyImportModuleAttr(getASTContext(), AL, AL.getImportModule()); @@ -338,24 +351,62 @@ SemaWasm::mergeImportModuleAttr(Decl *D, WebAssemblyImportNameAttr * SemaWasm::mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL) { - auto *FD = cast<FunctionDecl>(D); - - if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportNameAttr>()) { - if (ExistingAttr->getImportName() == AL.getImportName()) + if (auto *FD = dyn_cast<FunctionDecl>(D)) { + if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportNameAttr>()) { + if (ExistingAttr->getImportName() == AL.getImportName()) + return nullptr; + Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) + << 1 << ExistingAttr->getImportName() << AL.getImportName(); + Diag(AL.getLoc(), diag::note_previous_attribute); return nullptr; - Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) - << 1 << ExistingAttr->getImportName() << AL.getImportName(); - Diag(AL.getLoc(), diag::note_previous_attribute); - return nullptr; - } - if (FD->hasBody()) { - Diag(AL.getLoc(), diag::warn_import_on_definition) << 1; - return nullptr; + } + if (FD->hasBody()) { + Diag(AL.getLoc(), diag::warn_import_on_definition) << 1 << 0; + return nullptr; + } + } else if (auto *VD = dyn_cast<VarDecl>(D)) { + if (const auto *ExistingAttr = VD->getAttr<WebAssemblyImportNameAttr>()) { + if (ExistingAttr->getImportName() == AL.getImportName()) + return nullptr; + Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) + << 1 << ExistingAttr->getImportName() << AL.getImportName(); + Diag(AL.getLoc(), diag::note_previous_attribute); + return nullptr; + } + if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) { + Diag(AL.getLoc(), diag::warn_import_on_definition) << 1 << 1; + return nullptr; + } } return ::new (getASTContext()) WebAssemblyImportNameAttr(getASTContext(), AL, AL.getImportName()); } +WebAssemblyExportNameAttr * +SemaWasm::mergeExportNameAttr(Decl *D, const WebAssemblyExportNameAttr &AL) { + if (auto *FD = dyn_cast<FunctionDecl>(D)) { + if (const auto *ExistingAttr = FD->getAttr<WebAssemblyExportNameAttr>()) { + if (ExistingAttr->getExportName() == AL.getExportName()) + return nullptr; + Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) + << 2 << ExistingAttr->getExportName() << AL.getExportName(); + Diag(AL.getLoc(), diag::note_previous_attribute); + return nullptr; + } + } else if (auto *VD = dyn_cast<VarDecl>(D)) { + if (const auto *ExistingAttr = VD->getAttr<WebAssemblyExportNameAttr>()) { + if (ExistingAttr->getExportName() == AL.getExportName()) + return nullptr; + Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) + << 2 << ExistingAttr->getExportName() << AL.getExportName(); + Diag(AL.getLoc(), diag::note_previous_attribute); + return nullptr; + } + } + return ::new (getASTContext()) + WebAssemblyExportNameAttr(getASTContext(), AL, AL.getExportName()); +} + void SemaWasm::handleWebAssemblyImportModuleAttr(Decl *D, const ParsedAttr &AL) { StringRef Str; diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 03b9a77ec1814..d284370647f29 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -234,9 +234,9 @@ // CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType, SubjectMatchRule_type_alias) // CHECK-NEXT: Weak (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record) // CHECK-NEXT: WeakRef (SubjectMatchRule_variable, SubjectMatchRule_function) -// CHECK-NEXT: WebAssemblyExportName (SubjectMatchRule_function) -// CHECK-NEXT: WebAssemblyImportModule (SubjectMatchRule_function) -// CHECK-NEXT: WebAssemblyImportName (SubjectMatchRule_function) +// CHECK-NEXT: WebAssemblyExportName (SubjectMatchRule_function, SubjectMatchRule_variable_is_global) +// CHECK-NEXT: WebAssemblyImportModule (SubjectMatchRule_function, SubjectMatchRule_variable_is_global) +// CHECK-NEXT: WebAssemblyImportName (SubjectMatchRule_function, SubjectMatchRule_variable_is_global) // CHECK-NEXT: WorkGroupSizeHint (SubjectMatchRule_function) // CHECK-NEXT: XRayInstrument (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: XRayLogArgs (SubjectMatchRule_function, SubjectMatchRule_objc_method) diff --git a/clang/test/Sema/attr-wasm.c b/clang/test/Sema/attr-wasm.c index 9d380da460888..5c05a5fb33372 100644 --- a/clang/test/Sema/attr-wasm.c +++ b/clang/test/Sema/attr-wasm.c @@ -2,7 +2,8 @@ void name_a(void) __attribute__((import_name)); //expected-error {{'import_name' attribute takes one argument}} -int name_b __attribute__((import_name("foo"))); //expected-error {{'import_name' attribute only applies to functions}} +extern int name_b __attribute__((import_name("foo"))); +int name_b_def __attribute__((import_name("foo"))); //expected-warning {{import name cannot be applied to a variable with a definition}} void name_c(void) __attribute__((import_name("foo", "bar"))); //expected-error {{'import_name' attribute takes one argument}} @@ -14,7 +15,8 @@ void name_z(void) __attribute__((import_name("bar"))); //expected-warning {{impo void module_a(void) __attribute__((import_module)); //expected-error {{'import_module' attribute takes one argument}} -int module_b __attribute__((import_module("foo"))); //expected-error {{'import_module' attribute only applies to functions}} +extern int module_b __attribute__((import_module("foo"))); +int module_b_def __attribute__((import_module("foo"))); //expected-warning {{import module cannot be applied to a variable with a definition}} void module_c(void) __attribute__((import_module("foo", "bar"))); //expected-error {{'import_module' attribute takes one argument}} @@ -25,3 +27,20 @@ void module_z(void) __attribute__((import_module("foo"))); //expected-note {{pre void module_z(void) __attribute__((import_module("bar"))); //expected-warning {{import module (bar) does not match the import module (foo) of the previous declaration}} void both(void) __attribute__((import_name("foo"), import_module("bar"))); + +// export_name tests +void export_a(void) __attribute__((export_name)); //expected-error {{'export_name' attribute takes one argument}} +void export_b(void) __attribute__((export_name("foo", "bar"))); //expected-error {{'export_name' attribute takes one argument}} + +void export_c(void) __attribute__((export_name("foo"))); //expected-note {{previous attribute is here}} +void export_c(void) __attribute__((export_name("bar"))); //expected-warning {{export name (bar) does not match the export name (foo) of the previous declaration}} + +extern int export_d __attribute__((export_name("foo"))); //expected-note {{previous attribute is here}} +extern int export_d __attribute__((export_name("bar"))); //expected-warning {{export name (bar) does not match the export name (foo) of the previous declaration}} + +// Variable mismatch tests for import_module/name +extern int name_z_var __attribute__((import_name("foo"))); //expected-note {{previous attribute is here}} +extern int name_z_var __attribute__((import_name("bar"))); //expected-warning {{import name (bar) does not match the import name (foo) of the previous declaration}} + +extern int module_z_var __attribute__((import_module("foo"))); //expected-note {{previous attribute is here}} +extern int module_z_var __attribute__((import_module("bar"))); //expected-warning {{import module (bar) does not match the import module (foo) of the previous declaration}} >From bc699fbeca5cd0fedf5aa749a5be68d5afc061e4 Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Fri, 5 Jun 2026 23:57:31 +0000 Subject: [PATCH 03/13] clang-format --- clang/lib/CodeGen/Targets/WebAssembly.cpp | 16 +++++++++++----- clang/lib/Sema/SemaDecl.cpp | 6 ++++-- clang/lib/Sema/SemaWasm.cpp | 2 +- .../Target/WebAssembly/WebAssemblyAsmPrinter.cpp | 5 ++--- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index 3c99f3c116052..f6822ac8c70ab 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -64,7 +64,8 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { const auto *NameAttr = VD->getAttr<WebAssemblyImportNameAttr>(); if (ModuleAttr || NameAttr) { if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) { - auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation(); + auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() + : NameAttr->getLocation(); CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) << "import attribute cannot be applied to a definition"; return; @@ -73,19 +74,23 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { if (ModuleAttr) { Global->setMetadata( "wasm.import.module", - llvm::MDNode::get(Ctx, llvm::MDString::get(Ctx, ModuleAttr->getImportModule()))); + llvm::MDNode::get( + Ctx, + llvm::MDString::get(Ctx, ModuleAttr->getImportModule()))); } if (NameAttr) { Global->setMetadata( "wasm.import.name", - llvm::MDNode::get(Ctx, llvm::MDString::get(Ctx, NameAttr->getImportName()))); + llvm::MDNode::get( + Ctx, llvm::MDString::get(Ctx, NameAttr->getImportName()))); } } if (const auto *Attr = VD->getAttr<WebAssemblyExportNameAttr>()) { llvm::LLVMContext &Ctx = CGM.getLLVMContext(); Global->setMetadata( "wasm.export.name", - llvm::MDNode::get(Ctx, llvm::MDString::get(Ctx, Attr->getExportName()))); + llvm::MDNode::get( + Ctx, llvm::MDString::get(Ctx, Attr->getExportName()))); } } return; @@ -96,7 +101,8 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { const auto *NameAttr = FD->getAttr<WebAssemblyImportNameAttr>(); if (ModuleAttr || NameAttr) { if (FD->isThisDeclarationADefinition()) { - auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation(); + auto AttrLoc = + ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation(); CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) << "import attribute cannot be applied to a definition"; auto *NonConstFD = const_cast<FunctionDecl *>(FD); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8752f3417eef5..491adfc7d947f 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15387,14 +15387,16 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) { } if (UsedAttr *Attr = VD->getAttr<UsedAttr>()) { - if (!Attr->isInherited() && !Attr->isImplicit() && !VD->isThisDeclarationADefinition()) { + if (!Attr->isInherited() && !Attr->isImplicit() && + !VD->isThisDeclarationADefinition()) { Diag(Attr->getLocation(), diag::warn_attribute_ignored_on_non_definition) << Attr; VD->dropAttr<UsedAttr>(); } } if (RetainAttr *Attr = VD->getAttr<RetainAttr>()) { - if (!Attr->isInherited() && !Attr->isImplicit() && !VD->isThisDeclarationADefinition()) { + if (!Attr->isInherited() && !Attr->isImplicit() && + !VD->isThisDeclarationADefinition()) { Diag(Attr->getLocation(), diag::warn_attribute_ignored_on_non_definition) << Attr; VD->dropAttr<RetainAttr>(); diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index 4ea95617892c3..c3f531f15d1f3 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -317,7 +317,7 @@ bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, WebAssemblyImportModuleAttr * SemaWasm::mergeImportModuleAttr(Decl *D, - const WebAssemblyImportModuleAttr &AL) { + const WebAssemblyImportModuleAttr &AL) { if (auto *FD = dyn_cast<FunctionDecl>(D)) { if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportModuleAttr>()) { if (ExistingAttr->getImportModule() == AL.getImportModule()) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index 8bddc9068359b..f97ea54dcdf91 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -202,9 +202,8 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { if (!GV->isDeclaration()) { if (!getWasmMetadata(*GV, "wasm.import.module").empty() || !getWasmMetadata(*GV, "wasm.import.name").empty()) { - OutContext.reportError(SMLoc(), - "definition of global '" + GV->getName() + - "' cannot have import metadata"); + OutContext.reportError(SMLoc(), "definition of global '" + GV->getName() + + "' cannot have import metadata"); return; } } >From d0966bf5107eae734f11df6b2fa67e9831a9723c Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Fri, 21 Aug 2026 11:08:06 -0700 Subject: [PATCH 04/13] use attribute instead of metadata. Also allow the attribute on addrspace 0 globals, and allow omitting export names to use the default linkage name --- clang/include/clang/Basic/Attr.td | 2 +- clang/lib/CodeGen/Targets/WebAssembly.cpp | 31 +++++------- clang/lib/Sema/SemaWasm.cpp | 39 +++++---------- .../CodeGen/WebAssembly/wasm-global-export.c | 31 ++++++++++-- .../CodeGen/WebAssembly/wasm-global-import.c | 6 +-- .../CodeGen/WebAssembly/wasm-import-err-var.c | 3 ++ clang/test/Sema/attr-wasm.c | 9 +++- .../WebAssembly/WebAssemblyAsmPrinter.cpp | 47 ++++++++++--------- .../WebAssembly/export-metadata-global.ll | 23 ++++++++- .../WebAssembly/import-metadata-global-err.ll | 9 ++-- .../WebAssembly/import-metadata-global.ll | 6 +-- 11 files changed, 123 insertions(+), 83 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 823eb03823fd2..b68ce6893c172 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2588,7 +2588,7 @@ def BPFFastCall : InheritableAttr, def WebAssemblyExportName : InheritableAttr, TargetSpecificAttr<TargetWebAssembly> { let Spellings = [Clang<"export_name">]; - let Args = [StringArgument<"ExportName">]; + let Args = [StringArgument<"ExportName", 1>]; let Documentation = [WebAssemblyExportNameDocs]; let Subjects = SubjectList<[Function, GlobalVar], ErrorDiag>; } diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index f6822ac8c70ab..3e29e4a4469f2 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -70,27 +70,22 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { << "import attribute cannot be applied to a definition"; return; } - llvm::LLVMContext &Ctx = CGM.getLLVMContext(); - if (ModuleAttr) { - Global->setMetadata( - "wasm.import.module", - llvm::MDNode::get( - Ctx, - llvm::MDString::get(Ctx, ModuleAttr->getImportModule()))); - } - if (NameAttr) { - Global->setMetadata( - "wasm.import.name", - llvm::MDNode::get( - Ctx, llvm::MDString::get(Ctx, NameAttr->getImportName()))); + if (Global->getAddressSpace() == 0) { + auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() + : NameAttr->getLocation(); + CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) + << "import attribute cannot be applied to a non-wasm-variable global"; + return; } + if (ModuleAttr) + Global->addAttribute("wasm-import-module", + ModuleAttr->getImportModule()); + if (NameAttr) + Global->addAttribute("wasm-import-name", + NameAttr->getImportName()); } if (const auto *Attr = VD->getAttr<WebAssemblyExportNameAttr>()) { - llvm::LLVMContext &Ctx = CGM.getLLVMContext(); - Global->setMetadata( - "wasm.export.name", - llvm::MDNode::get( - Ctx, llvm::MDString::get(Ctx, Attr->getExportName()))); + Global->addAttribute("wasm-export-name", Attr->getExportName()); } } return; diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index c3f531f15d1f3..c7b3a153145fa 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -384,24 +384,13 @@ SemaWasm::mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL) { WebAssemblyExportNameAttr * SemaWasm::mergeExportNameAttr(Decl *D, const WebAssemblyExportNameAttr &AL) { - if (auto *FD = dyn_cast<FunctionDecl>(D)) { - if (const auto *ExistingAttr = FD->getAttr<WebAssemblyExportNameAttr>()) { - if (ExistingAttr->getExportName() == AL.getExportName()) - return nullptr; - Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) - << 2 << ExistingAttr->getExportName() << AL.getExportName(); - Diag(AL.getLoc(), diag::note_previous_attribute); - return nullptr; - } - } else if (auto *VD = dyn_cast<VarDecl>(D)) { - if (const auto *ExistingAttr = VD->getAttr<WebAssemblyExportNameAttr>()) { - if (ExistingAttr->getExportName() == AL.getExportName()) - return nullptr; - Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) - << 2 << ExistingAttr->getExportName() << AL.getExportName(); - Diag(AL.getLoc(), diag::note_previous_attribute); + if (const auto *ExistingAttr = D->getAttr<WebAssemblyExportNameAttr>()) { + if (ExistingAttr->getExportName() == AL.getExportName()) return nullptr; - } + Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import) + << 2 << ExistingAttr->getExportName() << AL.getExportName(); + Diag(AL.getLoc(), diag::note_previous_attribute); + return nullptr; } return ::new (getASTContext()) WebAssemblyExportNameAttr(getASTContext(), AL, AL.getExportName()); @@ -457,18 +446,16 @@ void SemaWasm::handleWebAssemblyImportNameAttr(Decl *D, const ParsedAttr &AL) { void SemaWasm::handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL) { ASTContext &Context = getASTContext(); - if (auto *FD = dyn_cast<FunctionDecl>(D)) { - if (FD->isThisDeclarationADefinition()) { - Diag(D->getLocation(), diag::err_alias_is_definition) << FD << 0; + StringRef Str; + if (AL.getNumArgs() == 0) { + if (auto *ND = dyn_cast<NamedDecl>(D)) + Str = ND->getName(); + } else { + SourceLocation ArgLoc; + if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) return; - } } - StringRef Str; - SourceLocation ArgLoc; - if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) - return; - D->addAttr(::new (Context) WebAssemblyExportNameAttr(Context, AL, Str)); D->addAttr(UsedAttr::CreateImplicit(Context)); } diff --git a/clang/test/CodeGen/WebAssembly/wasm-global-export.c b/clang/test/CodeGen/WebAssembly/wasm-global-export.c index 3ea1da0f20ef6..efe562dbf2f15 100644 --- a/clang/test/CodeGen/WebAssembly/wasm-global-export.c +++ b/clang/test/CodeGen/WebAssembly/wasm-global-export.c @@ -1,8 +1,33 @@ // RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm -o - %s | FileCheck %s -// Test export_name +// Test export_name with explicit name on addrspace(1) global int __attribute__((address_space(1))) exported_g __attribute__((export_name("global_g"))) = 42; -// CHECK: @exported_g = addrspace(1) global i32 42, align 4, !wasm.export.name ![[MD_EXPORT:[0-9]+]] -// CHECK: ![[MD_EXPORT]] = !{!"global_g"} +// Test export_name without argument on addrspace(1) global +int __attribute__((address_space(1))) exported_default_g + __attribute__((export_name)) = 43; + +// Test export_name with explicit name on addrspace(0) memory global +int exported_mem __attribute__((export_name("mem_g"))) = 100; + +// Test export_name without argument on addrspace(0) memory global +int exported_mem_default __attribute__((export_name)) = 101; + +// Test export_name on forward declaration propagating to definition +extern int var_propagate __attribute__((export_name("exported_propagate"))); +int var_propagate = 102; + +// CHECK: @exported_g = addrspace(1) global i32 42, align 4 #0 +// CHECK: @exported_default_g = addrspace(1) global i32 43, align 4 #1 +// CHECK: @exported_mem = global i32 100, align 4 #2 +// CHECK: @exported_mem_default = global i32 101, align 4 #3 +// CHECK: @var_propagate = global i32 102, align 4 #4 + +// CHECK: attributes #0 = { "wasm-export-name"="global_g" } +// CHECK: attributes #1 = { "wasm-export-name"="exported_default_g" } +// CHECK: attributes #2 = { "wasm-export-name"="mem_g" } +// CHECK: attributes #3 = { "wasm-export-name"="exported_mem_default" } +// CHECK: attributes #4 = { "wasm-export-name"="exported_propagate" } + + diff --git a/clang/test/CodeGen/WebAssembly/wasm-global-import.c b/clang/test/CodeGen/WebAssembly/wasm-global-import.c index 7ea5ef874b3bd..6e5b1bff0c54d 100644 --- a/clang/test/CodeGen/WebAssembly/wasm-global-import.c +++ b/clang/test/CodeGen/WebAssembly/wasm-global-import.c @@ -6,6 +6,6 @@ extern const int __attribute__((address_space(1))) imported_g int get_import(void) { return imported_g; } -// CHECK: @imported_g = external addrspace(1) constant i32, align 4, !wasm.import.module ![[MD_MOD:[0-9]+]], !wasm.import.name ![[MD_NAME:[0-9]+]] -// CHECK: ![[MD_MOD]] = !{!"js"} -// CHECK: ![[MD_NAME]] = !{!"global_g"} +// CHECK: @imported_g = external addrspace(1) constant i32, align 4 #0 +// CHECK: attributes #0 = { "wasm-import-module"="js" "wasm-import-name"="global_g" } + diff --git a/clang/test/CodeGen/WebAssembly/wasm-import-err-var.c b/clang/test/CodeGen/WebAssembly/wasm-import-err-var.c index 31cfeddcdba7f..3aef5a1626e1a 100644 --- a/clang/test/CodeGen/WebAssembly/wasm-import-err-var.c +++ b/clang/test/CodeGen/WebAssembly/wasm-import-err-var.c @@ -3,3 +3,6 @@ // Test definition inline extern const int __attribute__((address_space(1))) defined_g_inline __attribute__((import_module("js"))) = 42; // expected-error {{import attribute cannot be applied to a definition}} + + + diff --git a/clang/test/Sema/attr-wasm.c b/clang/test/Sema/attr-wasm.c index 5c05a5fb33372..93ee15b62dcd3 100644 --- a/clang/test/Sema/attr-wasm.c +++ b/clang/test/Sema/attr-wasm.c @@ -29,8 +29,9 @@ void module_z(void) __attribute__((import_module("bar"))); //expected-warning {{ void both(void) __attribute__((import_name("foo"), import_module("bar"))); // export_name tests -void export_a(void) __attribute__((export_name)); //expected-error {{'export_name' attribute takes one argument}} -void export_b(void) __attribute__((export_name("foo", "bar"))); //expected-error {{'export_name' attribute takes one argument}} +void export_a(void) __attribute__((export_name)); +extern int export_a_var __attribute__((export_name)); +void export_b(void) __attribute__((export_name("foo", "bar"))); //expected-error {{'export_name' attribute takes no more than 1 argument}} void export_c(void) __attribute__((export_name("foo"))); //expected-note {{previous attribute is here}} void export_c(void) __attribute__((export_name("bar"))); //expected-warning {{export name (bar) does not match the export name (foo) of the previous declaration}} @@ -44,3 +45,7 @@ extern int name_z_var __attribute__((import_name("bar"))); //expected-warning {{ extern int module_z_var __attribute__((import_module("foo"))); //expected-note {{previous attribute is here}} extern int module_z_var __attribute__((import_module("bar"))); //expected-warning {{import module (bar) does not match the import module (foo) of the previous declaration}} + +// Explicit 'used' on non-definition still warns, while 'export_name' (implicit 'used') does not +extern int explicit_used_var __attribute__((used)); //expected-warning {{'used' attribute ignored on a non-definition declaration}} + diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index 37fc61081c534..75e407cb44df3 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -185,18 +185,6 @@ MCSymbolWasm *WebAssemblyAsmPrinter::getMCSymbolForFunction( return WasmSym; } -static StringRef getWasmMetadata(const GlobalVariable &GV, StringRef Key) { - MDNode *MD = GV.getMetadata(Key); - if (!MD || MD->getNumOperands() == 0) - return {}; - - auto *Name = dyn_cast<MDString>(MD->getOperand(0)); - if (!Name) - return {}; - - return Name->getString(); -} - void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { if (GV->hasCommonLinkage()) { OutContext.reportError(SMLoc(), @@ -205,16 +193,28 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { return; } - if (!GV->isDeclaration()) { - if (!getWasmMetadata(*GV, "wasm.import.module").empty() || - !getWasmMetadata(*GV, "wasm.import.name").empty()) { + if (GV->hasAttribute("wasm-import-module") || + GV->hasAttribute("wasm-import-name")) { + if (!GV->isDeclaration()) { OutContext.reportError(SMLoc(), "definition of global '" + GV->getName() + - "' cannot have import metadata"); + "' cannot have import attribute"); + return; + } + if (!WebAssembly::isWasmVarAddressSpace(GV->getAddressSpace())) { + OutContext.reportError( + SMLoc(), "imported global '" + GV->getName() + + "' must be in a wasm variable address space"); return; } } if (!WebAssembly::isWasmVarAddressSpace(GV->getAddressSpace())) { + if (GV->hasAttribute("wasm-export-name")) { + auto *Sym = static_cast<MCSymbolWasm *>(getSymbol(GV)); + StringRef Name = GV->getAttribute("wasm-export-name").getValueAsString(); + Sym->setExportName(OutContext.allocateString(Name)); + getTargetStreamer()->emitExportName(Sym, Name); + } AsmPrinter::emitGlobalVariable(GV); return; } @@ -240,13 +240,15 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { emitVisibility(Sym, GV->getVisibility(), !GV->isDeclaration()); emitSymbolType(Sym); if (GV->isDeclaration()) { - StringRef ImportModule = getWasmMetadata(*GV, "wasm.import.module"); - if (!ImportModule.empty()) { + if (GV->hasAttribute("wasm-import-module")) { + StringRef ImportModule = + GV->getAttribute("wasm-import-module").getValueAsString(); Sym->setImportModule(OutContext.allocateString(ImportModule)); getTargetStreamer()->emitImportModule(Sym, ImportModule); } - StringRef ImportName = getWasmMetadata(*GV, "wasm.import.name"); - if (!ImportName.empty()) { + if (GV->hasAttribute("wasm-import-name")) { + StringRef ImportName = + GV->getAttribute("wasm-import-name").getValueAsString(); Sym->setImportName(OutContext.allocateString(ImportName)); getTargetStreamer()->emitImportName(Sym, ImportName); } @@ -255,8 +257,9 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { assert(getSymbolPreferLocal(*GV) == Sym); emitLinkage(GV, Sym); OutStreamer->emitLabel(Sym); - StringRef ExportName = getWasmMetadata(*GV, "wasm.export.name"); - if (!ExportName.empty()) { + if (GV->hasAttribute("wasm-export-name")) { + StringRef ExportName = + GV->getAttribute("wasm-export-name").getValueAsString(); Sym->setExportName(OutContext.allocateString(ExportName)); getTargetStreamer()->emitExportName(Sym, ExportName); } diff --git a/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll b/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll index e7dbf0e4c29c5..31d624f48ac6c 100644 --- a/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll +++ b/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll @@ -3,16 +3,35 @@ target triple = "wasm32-unknown-unknown" -@exported_g = addrspace(1) global i32 42, !wasm.export.name !0 +@exported_g = addrspace(1) global i32 42 #0 +@exported_mem = global i32 100 #1 ; ASM: .globaltype exported_g, i32 ; ASM: exported_g: ; ASM-NEXT: .export_name exported_g, "global_g" +; ASM: .export_name exported_mem, "mem_g" +; ASM: exported_mem: +; ASM-NEXT: .int32 100 + ; OBJ: - Type: EXPORT ; OBJ: Exports: ; OBJ: - Name: global_g ; OBJ-NEXT: Kind: GLOBAL ; OBJ-NEXT: Index: 0 -!0 = !{!"global_g"} +; OBJ: - Type: CUSTOM +; OBJ: Name: linking +; OBJ: SymbolTable: +; OBJ: - Index: 0 +; OBJ: Kind: GLOBAL +; OBJ: Name: exported_g +; OBJ: Flags: [ EXPORTED ] +; OBJ: - Index: 1 +; OBJ: Kind: DATA +; OBJ: Name: exported_mem +; OBJ: Flags: [ EXPORTED ] + +attributes #0 = { "wasm-export-name"="global_g" } +attributes #1 = { "wasm-export-name"="mem_g" } + diff --git a/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll b/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll index b678884b6504b..7c6a123b6d61a 100644 --- a/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll +++ b/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll @@ -2,8 +2,11 @@ target triple = "wasm32-unknown-unknown" -@g = global i32 42, !wasm.import.module !0 +@g = addrspace(1) global i32 42 #0 +; CHECK: error: definition of global 'g' cannot have import attribute -; CHECK: error: definition of global 'g' cannot have import metadata +@g2 = external global i32 #0 +; CHECK: error: imported global 'g2' must be in a wasm variable address space + +attributes #0 = { "wasm-import-module"="js" } -!0 = !{!"js"} diff --git a/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll b/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll index a3a052ec1534d..c0d3931eaf55c 100644 --- a/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll +++ b/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll @@ -3,7 +3,7 @@ target triple = "wasm32-unknown-unknown" -@imported_g = external addrspace(1) global i32, !wasm.import.module !0, !wasm.import.name !1 +@imported_g = external addrspace(1) global i32 #0 define i32 @get() { %v = load i32, ptr addrspace(1) @imported_g @@ -22,5 +22,5 @@ define i32 @get() { ; OBJ-NEXT: GlobalType: I32 ; OBJ-NEXT: GlobalMutable: true -!0 = !{!"js"} -!1 = !{!"global_g"} +attributes #0 = { "wasm-import-module"="js" "wasm-import-name"="global_g" } + >From 8c6a9cae9120a89b99a6b8aa9a689ede243428b6 Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Fri, 21 Aug 2026 12:34:52 -0700 Subject: [PATCH 05/13] only error on explicit import attributes on definitions --- clang/lib/CodeGen/Targets/WebAssembly.cpp | 30 ++++++++++++------- .../CodeGen/WebAssembly/wasm-global-import.c | 18 +++++++++++ 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index 3e29e4a4469f2..25461cda6dbc5 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -64,10 +64,14 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { const auto *NameAttr = VD->getAttr<WebAssemblyImportNameAttr>(); if (ModuleAttr || NameAttr) { if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) { - auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() - : NameAttr->getLocation(); - CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) - << "import attribute cannot be applied to a definition"; + bool IsExplicit = (ModuleAttr && !ModuleAttr->isInherited()) || + (NameAttr && !NameAttr->isInherited()); + if (IsExplicit) { + auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() + : NameAttr->getLocation(); + CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) + << "import attribute cannot be applied to a definition"; + } return; } if (Global->getAddressSpace() == 0) { @@ -96,13 +100,17 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { const auto *NameAttr = FD->getAttr<WebAssemblyImportNameAttr>(); if (ModuleAttr || NameAttr) { if (FD->isThisDeclarationADefinition()) { - auto AttrLoc = - ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation(); - CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) - << "import attribute cannot be applied to a definition"; - auto *NonConstFD = const_cast<FunctionDecl *>(FD); - NonConstFD->dropAttr<WebAssemblyImportModuleAttr>(); - NonConstFD->dropAttr<WebAssemblyImportNameAttr>(); + bool IsExplicit = (ModuleAttr && !ModuleAttr->isInherited()) || + (NameAttr && !NameAttr->isInherited()); + if (IsExplicit) { + auto AttrLoc = + ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation(); + CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) + << "import attribute cannot be applied to a definition"; + auto *NonConstFD = const_cast<FunctionDecl *>(FD); + NonConstFD->dropAttr<WebAssemblyImportModuleAttr>(); + NonConstFD->dropAttr<WebAssemblyImportNameAttr>(); + } return; } llvm::Function *Fn = cast<llvm::Function>(GV); diff --git a/clang/test/CodeGen/WebAssembly/wasm-global-import.c b/clang/test/CodeGen/WebAssembly/wasm-global-import.c index 6e5b1bff0c54d..3a4ce17a1fdfd 100644 --- a/clang/test/CodeGen/WebAssembly/wasm-global-import.c +++ b/clang/test/CodeGen/WebAssembly/wasm-global-import.c @@ -6,6 +6,24 @@ extern const int __attribute__((address_space(1))) imported_g int get_import(void) { return imported_g; } +// Test that defining a forward-declared imported global works and does not emit import attributes +extern const int __attribute__((address_space(1))) imported_def + __attribute__((import_module("js"), import_name("global_def"))); +const int __attribute__((address_space(1))) imported_def = 99; + +// Test that defining a forward-declared imported function works and does not emit import attributes +extern void imported_fn(void) + __attribute__((import_module("js"), import_name("fn"))); +void imported_fn(void) {} + // CHECK: @imported_g = external addrspace(1) constant i32, align 4 #0 +// CHECK: @imported_def = addrspace(1) constant i32 99, align 4{{$}} + +// CHECK: define void @imported_fn() + // CHECK: attributes #0 = { "wasm-import-module"="js" "wasm-import-name"="global_g" } +// CHECK-NOT: "wasm-import-module"="js" "wasm-import-name"="global_def" +// CHECK-NOT: "wasm-import-module"="js" "wasm-import-name"="fn" + + >From 21dc067d1c970982ad81914cbb3419de26749180 Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Fri, 21 Aug 2026 12:54:07 -0700 Subject: [PATCH 06/13] Use linkage name rather than AST name for export name --- clang/lib/CodeGen/Targets/WebAssembly.cpp | 10 ++++++++-- clang/lib/Sema/SemaWasm.cpp | 5 +---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index 25461cda6dbc5..ad9d71359a1de 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -89,7 +89,10 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { NameAttr->getImportName()); } if (const auto *Attr = VD->getAttr<WebAssemblyExportNameAttr>()) { - Global->addAttribute("wasm-export-name", Attr->getExportName()); + StringRef Name = Attr->getExportName(); + if (Name.empty()) + Name = Global->getName(); + Global->addAttribute("wasm-export-name", Name); } } return; @@ -124,7 +127,10 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { if (const auto *Attr = FD->getAttr<WebAssemblyExportNameAttr>()) { llvm::Function *Fn = cast<llvm::Function>(GV); llvm::AttrBuilder B(GV->getContext()); - B.addAttribute("wasm-export-name", Attr->getExportName()); + StringRef Name = Attr->getExportName(); + if (Name.empty()) + Name = Fn->getName(); + B.addAttribute("wasm-export-name", Name); Fn->addFnAttrs(B); } } diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index c7b3a153145fa..c30faae42ee12 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -447,10 +447,7 @@ void SemaWasm::handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL) { ASTContext &Context = getASTContext(); StringRef Str; - if (AL.getNumArgs() == 0) { - if (auto *ND = dyn_cast<NamedDecl>(D)) - Str = ND->getName(); - } else { + if (AL.getNumArgs() > 0) { SourceLocation ArgLoc; if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) return; >From 79d6e93b669a5588676bc393b9f22cec5da7c26d Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Wed, 26 Aug 2026 14:45:11 -0700 Subject: [PATCH 07/13] clang-format --- clang/lib/CodeGen/Targets/WebAssembly.cpp | 10 +++++----- llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index ad9d71359a1de..23c93d194aa48 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -78,15 +78,15 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation(); CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) - << "import attribute cannot be applied to a non-wasm-variable global"; + << "import attribute cannot be applied to a non-wasm-variable " + "global"; return; } if (ModuleAttr) Global->addAttribute("wasm-import-module", ModuleAttr->getImportModule()); if (NameAttr) - Global->addAttribute("wasm-import-name", - NameAttr->getImportName()); + Global->addAttribute("wasm-import-name", NameAttr->getImportName()); } if (const auto *Attr = VD->getAttr<WebAssemblyExportNameAttr>()) { StringRef Name = Attr->getExportName(); @@ -106,8 +106,8 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { bool IsExplicit = (ModuleAttr && !ModuleAttr->isInherited()) || (NameAttr && !NameAttr->isInherited()); if (IsExplicit) { - auto AttrLoc = - ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation(); + auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() + : NameAttr->getLocation(); CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) << "import attribute cannot be applied to a definition"; auto *NonConstFD = const_cast<FunctionDecl *>(FD); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index cfc41ac1137d1..4f54402d3f59f 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -194,9 +194,9 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { return; } if (!WebAssembly::isWasmVarAddressSpace(GV->getAddressSpace())) { - OutContext.reportError( - SMLoc(), "imported global '" + GV->getName() + - "' must be in a wasm variable address space"); + OutContext.reportError(SMLoc(), + "imported global '" + GV->getName() + + "' must be in a wasm variable address space"); return; } } >From 64ea940c4e5bd2c56c6e31bb8cb4962105274a32 Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Thu, 27 Aug 2026 17:16:19 -0700 Subject: [PATCH 08/13] use inline attributes --- llvm/test/CodeGen/WebAssembly/export-metadata-global.ll | 6 ++---- llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll | 6 ++---- llvm/test/CodeGen/WebAssembly/import-metadata-global.ll | 4 +--- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll b/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll index 31d624f48ac6c..207a2de019b06 100644 --- a/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll +++ b/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll @@ -3,8 +3,8 @@ target triple = "wasm32-unknown-unknown" -@exported_g = addrspace(1) global i32 42 #0 -@exported_mem = global i32 100 #1 +@exported_g = addrspace(1) global i32 42 "wasm-export-name"="global_g" +@exported_mem = global i32 100 "wasm-export-name"="mem_g" ; ASM: .globaltype exported_g, i32 ; ASM: exported_g: @@ -32,6 +32,4 @@ target triple = "wasm32-unknown-unknown" ; OBJ: Name: exported_mem ; OBJ: Flags: [ EXPORTED ] -attributes #0 = { "wasm-export-name"="global_g" } -attributes #1 = { "wasm-export-name"="mem_g" } diff --git a/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll b/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll index 7c6a123b6d61a..4c82377975c87 100644 --- a/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll +++ b/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll @@ -2,11 +2,9 @@ target triple = "wasm32-unknown-unknown" -@g = addrspace(1) global i32 42 #0 +@g = addrspace(1) global i32 42 "wasm-import-module"="js" ; CHECK: error: definition of global 'g' cannot have import attribute -@g2 = external global i32 #0 +@g2 = external global i32 "wasm-import-module"="js" ; CHECK: error: imported global 'g2' must be in a wasm variable address space -attributes #0 = { "wasm-import-module"="js" } - diff --git a/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll b/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll index c0d3931eaf55c..6698a79100ab1 100644 --- a/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll +++ b/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll @@ -3,7 +3,7 @@ target triple = "wasm32-unknown-unknown" -@imported_g = external addrspace(1) global i32 #0 +@imported_g = external addrspace(1) global i32 "wasm-import-module"="js" "wasm-import-name"="global_g" define i32 @get() { %v = load i32, ptr addrspace(1) @imported_g @@ -22,5 +22,3 @@ define i32 @get() { ; OBJ-NEXT: GlobalType: I32 ; OBJ-NEXT: GlobalMutable: true -attributes #0 = { "wasm-import-module"="js" "wasm-import-name"="global_g" } - >From 843a6a41828e782d3164d79a18096b5afcbe7285 Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Tue, 1 Sep 2026 15:55:27 -0700 Subject: [PATCH 09/13] Require argument for export_name and defer linkage-name export to follow-up --- clang/include/clang/Basic/Attr.td | 2 +- clang/lib/CodeGen/Targets/WebAssembly.cpp | 10 ++-------- clang/lib/Sema/SemaWasm.cpp | 8 +++----- .../CodeGen/WebAssembly/wasm-global-export.c | 20 +++++-------------- clang/test/Sema/attr-wasm.c | 6 +++--- 5 files changed, 14 insertions(+), 32 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index b68ce6893c172..823eb03823fd2 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2588,7 +2588,7 @@ def BPFFastCall : InheritableAttr, def WebAssemblyExportName : InheritableAttr, TargetSpecificAttr<TargetWebAssembly> { let Spellings = [Clang<"export_name">]; - let Args = [StringArgument<"ExportName", 1>]; + let Args = [StringArgument<"ExportName">]; let Documentation = [WebAssemblyExportNameDocs]; let Subjects = SubjectList<[Function, GlobalVar], ErrorDiag>; } diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index 23c93d194aa48..e899a7e69c632 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -89,10 +89,7 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { Global->addAttribute("wasm-import-name", NameAttr->getImportName()); } if (const auto *Attr = VD->getAttr<WebAssemblyExportNameAttr>()) { - StringRef Name = Attr->getExportName(); - if (Name.empty()) - Name = Global->getName(); - Global->addAttribute("wasm-export-name", Name); + Global->addAttribute("wasm-export-name", Attr->getExportName()); } } return; @@ -127,10 +124,7 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { if (const auto *Attr = FD->getAttr<WebAssemblyExportNameAttr>()) { llvm::Function *Fn = cast<llvm::Function>(GV); llvm::AttrBuilder B(GV->getContext()); - StringRef Name = Attr->getExportName(); - if (Name.empty()) - Name = Fn->getName(); - B.addAttribute("wasm-export-name", Name); + B.addAttribute("wasm-export-name", Attr->getExportName()); Fn->addFnAttrs(B); } } diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index c30faae42ee12..983cee7957f41 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -447,11 +447,9 @@ void SemaWasm::handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL) { ASTContext &Context = getASTContext(); StringRef Str; - if (AL.getNumArgs() > 0) { - SourceLocation ArgLoc; - if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) - return; - } + SourceLocation ArgLoc; + if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) + return; D->addAttr(::new (Context) WebAssemblyExportNameAttr(Context, AL, Str)); D->addAttr(UsedAttr::CreateImplicit(Context)); diff --git a/clang/test/CodeGen/WebAssembly/wasm-global-export.c b/clang/test/CodeGen/WebAssembly/wasm-global-export.c index efe562dbf2f15..63437074b3334 100644 --- a/clang/test/CodeGen/WebAssembly/wasm-global-export.c +++ b/clang/test/CodeGen/WebAssembly/wasm-global-export.c @@ -4,30 +4,20 @@ int __attribute__((address_space(1))) exported_g __attribute__((export_name("global_g"))) = 42; -// Test export_name without argument on addrspace(1) global -int __attribute__((address_space(1))) exported_default_g - __attribute__((export_name)) = 43; - // Test export_name with explicit name on addrspace(0) memory global int exported_mem __attribute__((export_name("mem_g"))) = 100; -// Test export_name without argument on addrspace(0) memory global -int exported_mem_default __attribute__((export_name)) = 101; - // Test export_name on forward declaration propagating to definition extern int var_propagate __attribute__((export_name("exported_propagate"))); int var_propagate = 102; // CHECK: @exported_g = addrspace(1) global i32 42, align 4 #0 -// CHECK: @exported_default_g = addrspace(1) global i32 43, align 4 #1 -// CHECK: @exported_mem = global i32 100, align 4 #2 -// CHECK: @exported_mem_default = global i32 101, align 4 #3 -// CHECK: @var_propagate = global i32 102, align 4 #4 +// CHECK: @exported_mem = global i32 100, align 4 #1 +// CHECK: @var_propagate = global i32 102, align 4 #2 // CHECK: attributes #0 = { "wasm-export-name"="global_g" } -// CHECK: attributes #1 = { "wasm-export-name"="exported_default_g" } -// CHECK: attributes #2 = { "wasm-export-name"="mem_g" } -// CHECK: attributes #3 = { "wasm-export-name"="exported_mem_default" } -// CHECK: attributes #4 = { "wasm-export-name"="exported_propagate" } +// CHECK: attributes #1 = { "wasm-export-name"="mem_g" } +// CHECK: attributes #2 = { "wasm-export-name"="exported_propagate" } + diff --git a/clang/test/Sema/attr-wasm.c b/clang/test/Sema/attr-wasm.c index 93ee15b62dcd3..23cb212d6acc9 100644 --- a/clang/test/Sema/attr-wasm.c +++ b/clang/test/Sema/attr-wasm.c @@ -29,9 +29,9 @@ void module_z(void) __attribute__((import_module("bar"))); //expected-warning {{ void both(void) __attribute__((import_name("foo"), import_module("bar"))); // export_name tests -void export_a(void) __attribute__((export_name)); -extern int export_a_var __attribute__((export_name)); -void export_b(void) __attribute__((export_name("foo", "bar"))); //expected-error {{'export_name' attribute takes no more than 1 argument}} +void export_a(void) __attribute__((export_name)); //expected-error {{'export_name' attribute takes one argument}} +extern int export_a_var __attribute__((export_name)); //expected-error {{'export_name' attribute takes one argument}} +void export_b(void) __attribute__((export_name("foo", "bar"))); //expected-error {{'export_name' attribute takes one argument}} void export_c(void) __attribute__((export_name("foo"))); //expected-note {{previous attribute is here}} void export_c(void) __attribute__((export_name("bar"))); //expected-warning {{export name (bar) does not match the export name (foo) of the previous declaration}} >From 57768fe9a1cbca492e24cefd70260fd284d39de8 Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Tue, 1 Sep 2026 15:57:25 -0700 Subject: [PATCH 10/13] Add test for import attribute on addrspace(0) global --- clang/test/CodeGen/WebAssembly/wasm-import-err-addrspace.c | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 clang/test/CodeGen/WebAssembly/wasm-import-err-addrspace.c diff --git a/clang/test/CodeGen/WebAssembly/wasm-import-err-addrspace.c b/clang/test/CodeGen/WebAssembly/wasm-import-err-addrspace.c new file mode 100644 index 0000000000000..bc075d6f4321e --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/wasm-import-err-addrspace.c @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm-only -verify %s + +// Test import on non-wasm-variable (addrspace 0) +extern const int defined_g_addrspace0 + __attribute__((import_module("js"))); // expected-error {{import attribute cannot be applied to a non-wasm-variable global}} + +int get_val(void) { return defined_g_addrspace0; } >From 9963d4f4f99648204ee72d4298668ec1d235ba0a Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Fri, 4 Sep 2026 13:10:36 -0700 Subject: [PATCH 11/13] Address review feedback: simplify Sema/CodeGen and reorganize tests --- clang/lib/CodeGen/Targets/WebAssembly.cpp | 10 ++--- clang/lib/Sema/SemaWasm.cpp | 6 ++- .../CodeGen/WebAssembly/wasm-global-export.c | 3 -- .../WebAssembly/wasm-import-err-addrspace.c | 7 --- .../CodeGen/WebAssembly/wasm-import-err-fun.c | 3 -- .../CodeGen/WebAssembly/wasm-import-err-var.c | 8 ---- .../CodeGen/WebAssembly/wasm-import-error.c | 21 +++++++++ .../WebAssembly/export-metadata-global.ll | 35 --------------- llvm/test/CodeGen/WebAssembly/export-name.ll | 45 ++++++++++++++++--- ...metadata-global-err.ll => import-error.ll} | 1 - .../WebAssembly/import-metadata-global.ll | 24 ---------- .../test/CodeGen/WebAssembly/import-module.ll | 27 +++++++++-- 12 files changed, 91 insertions(+), 99 deletions(-) delete mode 100644 clang/test/CodeGen/WebAssembly/wasm-import-err-addrspace.c delete mode 100644 clang/test/CodeGen/WebAssembly/wasm-import-err-fun.c delete mode 100644 clang/test/CodeGen/WebAssembly/wasm-import-err-var.c create mode 100644 clang/test/CodeGen/WebAssembly/wasm-import-error.c delete mode 100644 llvm/test/CodeGen/WebAssembly/export-metadata-global.ll rename llvm/test/CodeGen/WebAssembly/{import-metadata-global-err.ll => import-error.ll} (99%) delete mode 100644 llvm/test/CodeGen/WebAssembly/import-metadata-global.ll diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index e899a7e69c632..c37d1c6a910a9 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -114,18 +114,14 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { return; } llvm::Function *Fn = cast<llvm::Function>(GV); - llvm::AttrBuilder B(GV->getContext()); if (ModuleAttr) - B.addAttribute("wasm-import-module", ModuleAttr->getImportModule()); + Fn->addFnAttr("wasm-import-module", ModuleAttr->getImportModule()); if (NameAttr) - B.addAttribute("wasm-import-name", NameAttr->getImportName()); - Fn->addFnAttrs(B); + Fn->addFnAttr("wasm-import-name", NameAttr->getImportName()); } if (const auto *Attr = FD->getAttr<WebAssemblyExportNameAttr>()) { llvm::Function *Fn = cast<llvm::Function>(GV); - llvm::AttrBuilder B(GV->getContext()); - B.addAttribute("wasm-export-name", Attr->getExportName()); - Fn->addFnAttrs(B); + Fn->addFnAttr("wasm-export-name", Attr->getExportName()); } } diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index 983cee7957f41..cfc0a08a50e5a 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -410,7 +410,8 @@ void SemaWasm::handleWebAssemblyImportModuleAttr(Decl *D, } FD->addAttr(::new (getASTContext()) WebAssemblyImportModuleAttr(getASTContext(), AL, Str)); - } else if (auto *VD = dyn_cast<VarDecl>(D)) { + } else { + auto *VD = cast<VarDecl>(D); if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) { Diag(AL.getLoc(), diag::warn_import_on_definition) << 0 << 1; return; @@ -433,7 +434,8 @@ void SemaWasm::handleWebAssemblyImportNameAttr(Decl *D, const ParsedAttr &AL) { } FD->addAttr(::new (getASTContext()) WebAssemblyImportNameAttr(getASTContext(), AL, Str)); - } else if (auto *VD = dyn_cast<VarDecl>(D)) { + } else { + auto *VD = cast<VarDecl>(D); if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) { Diag(AL.getLoc(), diag::warn_import_on_definition) << 1 << 1; return; diff --git a/clang/test/CodeGen/WebAssembly/wasm-global-export.c b/clang/test/CodeGen/WebAssembly/wasm-global-export.c index 63437074b3334..e20009c1e35b3 100644 --- a/clang/test/CodeGen/WebAssembly/wasm-global-export.c +++ b/clang/test/CodeGen/WebAssembly/wasm-global-export.c @@ -18,6 +18,3 @@ int var_propagate = 102; // CHECK: attributes #0 = { "wasm-export-name"="global_g" } // CHECK: attributes #1 = { "wasm-export-name"="mem_g" } // CHECK: attributes #2 = { "wasm-export-name"="exported_propagate" } - - - diff --git a/clang/test/CodeGen/WebAssembly/wasm-import-err-addrspace.c b/clang/test/CodeGen/WebAssembly/wasm-import-err-addrspace.c deleted file mode 100644 index bc075d6f4321e..0000000000000 --- a/clang/test/CodeGen/WebAssembly/wasm-import-err-addrspace.c +++ /dev/null @@ -1,7 +0,0 @@ -// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm-only -verify %s - -// Test import on non-wasm-variable (addrspace 0) -extern const int defined_g_addrspace0 - __attribute__((import_module("js"))); // expected-error {{import attribute cannot be applied to a non-wasm-variable global}} - -int get_val(void) { return defined_g_addrspace0; } diff --git a/clang/test/CodeGen/WebAssembly/wasm-import-err-fun.c b/clang/test/CodeGen/WebAssembly/wasm-import-err-fun.c deleted file mode 100644 index c819f8a4e6b5e..0000000000000 --- a/clang/test/CodeGen/WebAssembly/wasm-import-err-fun.c +++ /dev/null @@ -1,3 +0,0 @@ -// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm-only -verify %s - -void defined_fn(void) __attribute__((import_module("js"))) {} // expected-error {{import attribute cannot be applied to a definition}} diff --git a/clang/test/CodeGen/WebAssembly/wasm-import-err-var.c b/clang/test/CodeGen/WebAssembly/wasm-import-err-var.c deleted file mode 100644 index 3aef5a1626e1a..0000000000000 --- a/clang/test/CodeGen/WebAssembly/wasm-import-err-var.c +++ /dev/null @@ -1,8 +0,0 @@ -// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm-only -Wno-extern-initializer -verify %s - -// Test definition inline -extern const int __attribute__((address_space(1))) defined_g_inline - __attribute__((import_module("js"))) = 42; // expected-error {{import attribute cannot be applied to a definition}} - - - diff --git a/clang/test/CodeGen/WebAssembly/wasm-import-error.c b/clang/test/CodeGen/WebAssembly/wasm-import-error.c new file mode 100644 index 0000000000000..45c022a0f8117 --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/wasm-import-error.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm-only -verify=fun -DTEST_FUN %s +// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm-only -Wno-extern-initializer -verify=var -DTEST_VAR %s +// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm-only -verify=addrspace -DTEST_ADDRSPACE %s + +#ifdef TEST_FUN +void defined_fn(void) __attribute__((import_module("js"))) {} // fun-error {{import attribute cannot be applied to a definition}} +#endif + +#ifdef TEST_VAR +// Test definition inline +extern const int __attribute__((address_space(1))) defined_g_inline + __attribute__((import_module("js"))) = 42; // var-error {{import attribute cannot be applied to a definition}} +#endif + +#ifdef TEST_ADDRSPACE +// Test import on non-wasm-variable (addrspace 0) +extern const int defined_g_addrspace0 + __attribute__((import_module("js"))); // addrspace-error {{import attribute cannot be applied to a non-wasm-variable global}} + +int get_val(void) { return defined_g_addrspace0; } +#endif diff --git a/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll b/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll deleted file mode 100644 index 207a2de019b06..0000000000000 --- a/llvm/test/CodeGen/WebAssembly/export-metadata-global.ll +++ /dev/null @@ -1,35 +0,0 @@ -; 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" - -@exported_g = addrspace(1) global i32 42 "wasm-export-name"="global_g" -@exported_mem = global i32 100 "wasm-export-name"="mem_g" - -; ASM: .globaltype exported_g, i32 -; ASM: exported_g: -; ASM-NEXT: .export_name exported_g, "global_g" - -; ASM: .export_name exported_mem, "mem_g" -; ASM: exported_mem: -; ASM-NEXT: .int32 100 - -; OBJ: - Type: EXPORT -; OBJ: Exports: -; OBJ: - Name: global_g -; OBJ-NEXT: Kind: GLOBAL -; OBJ-NEXT: Index: 0 - -; OBJ: - Type: CUSTOM -; OBJ: Name: linking -; OBJ: SymbolTable: -; OBJ: - Index: 0 -; OBJ: Kind: GLOBAL -; OBJ: Name: exported_g -; OBJ: Flags: [ EXPORTED ] -; OBJ: - Index: 1 -; OBJ: Kind: DATA -; OBJ: Name: exported_mem -; OBJ: Flags: [ EXPORTED ] - - diff --git a/llvm/test/CodeGen/WebAssembly/export-name.ll b/llvm/test/CodeGen/WebAssembly/export-name.ll index 815f6d3b4ed1e..a4564098f4550 100644 --- a/llvm/test/CodeGen/WebAssembly/export-name.ll +++ b/llvm/test/CodeGen/WebAssembly/export-name.ll @@ -1,16 +1,49 @@ ; RUN: llc < %s -asm-verbose=false -wasm-keep-registers | FileCheck %s +; RUN: llc < %s --filetype=obj | obj2yaml | FileCheck --check-prefix=OBJ %s target triple = "wasm32-unknown-unknown" -define void @test() #0 { +@exported_g = addrspace(1) global i32 42 "wasm-export-name"="global_g" +@exported_mem = global i32 100 "wasm-export-name"="mem_g" + +define void @test() "wasm-export-name"="foo" { ret void } -declare void @test2() #1 - - -attributes #0 = { "wasm-export-name"="foo" } -attributes #1 = { "wasm-export-name"="bar" } +declare void @test2() "wasm-export-name"="bar" ; CHECK: .export_name test, "foo" ; CHECK: .export_name test2, "bar" + +; CHECK: .globaltype exported_g, i32 +; CHECK: exported_g: +; CHECK-NEXT: .export_name exported_g, "global_g" + +; CHECK: .export_name exported_mem, "mem_g" +; CHECK: exported_mem: +; CHECK-NEXT: .int32 100 + +; OBJ: - Type: EXPORT +; OBJ: Exports: +; OBJ: - Name: foo +; OBJ-NEXT: Kind: FUNCTION +; OBJ-NEXT: Index: 0 +; OBJ: - Name: global_g +; OBJ-NEXT: Kind: GLOBAL +; OBJ-NEXT: Index: 0 + +; OBJ: - Type: CUSTOM +; OBJ: Name: linking +; OBJ: SymbolTable: +; OBJ: - Index: 0 +; OBJ: Kind: FUNCTION +; OBJ: Name: test +; OBJ: Flags: [ EXPORTED ] +; OBJ: - Index: 1 +; OBJ: Kind: GLOBAL +; OBJ: Name: exported_g +; OBJ: Flags: [ EXPORTED ] +; OBJ: - Index: 2 +; OBJ: Kind: DATA +; OBJ: Name: exported_mem +; OBJ: Flags: [ EXPORTED ] diff --git a/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll b/llvm/test/CodeGen/WebAssembly/import-error.ll similarity index 99% rename from llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll rename to llvm/test/CodeGen/WebAssembly/import-error.ll index 4c82377975c87..dd2fcac8ca218 100644 --- a/llvm/test/CodeGen/WebAssembly/import-metadata-global-err.ll +++ b/llvm/test/CodeGen/WebAssembly/import-error.ll @@ -7,4 +7,3 @@ target triple = "wasm32-unknown-unknown" @g2 = external global i32 "wasm-import-module"="js" ; CHECK: error: imported global 'g2' must be in a wasm variable address space - diff --git a/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll b/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll deleted file mode 100644 index 6698a79100ab1..0000000000000 --- a/llvm/test/CodeGen/WebAssembly/import-metadata-global.ll +++ /dev/null @@ -1,24 +0,0 @@ -; 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"="js" "wasm-import-name"="global_g" - -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" -; ASM-NEXT: .import_name imported_g, "global_g" - -; OBJ: - Type: IMPORT -; OBJ: Imports: -; OBJ: - Module: js -; OBJ-NEXT: Field: global_g -; OBJ-NEXT: Kind: GLOBAL -; OBJ-NEXT: GlobalType: I32 -; OBJ-NEXT: GlobalMutable: true - diff --git a/llvm/test/CodeGen/WebAssembly/import-module.ll b/llvm/test/CodeGen/WebAssembly/import-module.ll index 101459f47d482..72db8425af3f0 100644 --- a/llvm/test/CodeGen/WebAssembly/import-module.ll +++ b/llvm/test/CodeGen/WebAssembly/import-module.ll @@ -1,19 +1,40 @@ ; RUN: llc < %s -asm-verbose=false -wasm-keep-registers | FileCheck %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"="js" "wasm-import-name"="global_g" + define void @test() { call void @foo() call void @plain() ret void } -declare void @foo() #0 -declare void @plain() +define i32 @get() { + %v = load i32, ptr addrspace(1) @imported_g + ret i32 %v +} -attributes #0 = { "wasm-import-module"="bar" "wasm-import-name"="qux" } +declare void @foo() "wasm-import-module"="bar" "wasm-import-name"="qux" +declare void @plain() ; CHECK-NOT: .import_module plain ; CHECK: .import_module foo, "bar" ; CHECK: .import_name foo, "qux" ; CHECK-NOT: .import_module plain + +; CHECK: .globaltype imported_g, i32 +; CHECK-NEXT: .import_module imported_g, "js" +; CHECK-NEXT: .import_name imported_g, "global_g" + +; OBJ: - Type: IMPORT +; OBJ: Imports: +; OBJ: - Module: bar +; OBJ-NEXT: Field: qux +; OBJ-NEXT: Kind: FUNCTION +; OBJ: - Module: js +; OBJ-NEXT: Field: global_g +; OBJ-NEXT: Kind: GLOBAL +; OBJ-NEXT: GlobalType: I32 +; OBJ-NEXT: GlobalMutable: true >From e5f225782277a3abb2d39adc5d845aeb881041f3 Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Fri, 4 Sep 2026 14:45:23 -0700 Subject: [PATCH 12/13] Unconditionalize GlobalVariable Function casts, merge blocks --- clang/lib/CodeGen/Targets/WebAssembly.cpp | 60 ++++++++++------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index c37d1c6a910a9..5f57f9c334f6a 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -59,43 +59,40 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { 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)) { - const auto *ModuleAttr = VD->getAttr<WebAssemblyImportModuleAttr>(); - const auto *NameAttr = VD->getAttr<WebAssemblyImportNameAttr>(); - if (ModuleAttr || NameAttr) { - if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) { - bool IsExplicit = (ModuleAttr && !ModuleAttr->isInherited()) || - (NameAttr && !NameAttr->isInherited()); - if (IsExplicit) { - auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() - : NameAttr->getLocation(); - CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) - << "import attribute cannot be applied to a definition"; - } - return; - } - if (Global->getAddressSpace() == 0) { + auto *Global = cast<llvm::GlobalVariable>(GV); + const auto *ModuleAttr = VD->getAttr<WebAssemblyImportModuleAttr>(); + const auto *NameAttr = VD->getAttr<WebAssemblyImportNameAttr>(); + if (ModuleAttr || NameAttr) { + if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) { + bool IsExplicit = (ModuleAttr && !ModuleAttr->isInherited()) || + (NameAttr && !NameAttr->isInherited()); + if (IsExplicit) { auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation(); CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) - << "import attribute cannot be applied to a non-wasm-variable " - "global"; - return; + << "import attribute cannot be applied to a definition"; } - if (ModuleAttr) - Global->addAttribute("wasm-import-module", - ModuleAttr->getImportModule()); - if (NameAttr) - Global->addAttribute("wasm-import-name", NameAttr->getImportName()); + return; + } + if (Global->getAddressSpace() == 0) { + auto AttrLoc = + ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation(); + CGM.getDiags().Report(AttrLoc, diag::err_fe_backend_unsupported) + << "import attribute cannot be applied to a non-wasm-variable " + "global"; + return; } + if (ModuleAttr) + Global->addAttribute("wasm-import-module", + ModuleAttr->getImportModule()); + if (NameAttr) + Global->addAttribute("wasm-import-name", NameAttr->getImportName()); + } if (const auto *Attr = VD->getAttr<WebAssemblyExportNameAttr>()) { Global->addAttribute("wasm-export-name", Attr->getExportName()); } - } - return; - } - - if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) { + } else if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) { + auto *Fn = cast<llvm::Function>(GV); const auto *ModuleAttr = FD->getAttr<WebAssemblyImportModuleAttr>(); const auto *NameAttr = FD->getAttr<WebAssemblyImportNameAttr>(); if (ModuleAttr || NameAttr) { @@ -113,20 +110,15 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { } return; } - llvm::Function *Fn = cast<llvm::Function>(GV); if (ModuleAttr) Fn->addFnAttr("wasm-import-module", ModuleAttr->getImportModule()); if (NameAttr) Fn->addFnAttr("wasm-import-name", NameAttr->getImportName()); } if (const auto *Attr = FD->getAttr<WebAssemblyExportNameAttr>()) { - llvm::Function *Fn = cast<llvm::Function>(GV); Fn->addFnAttr("wasm-export-name", Attr->getExportName()); } - } - if (auto *FD = dyn_cast_or_null<FunctionDecl>(D)) { - llvm::Function *Fn = cast<llvm::Function>(GV); if (!FD->doesThisDeclarationHaveABody() && !FD->hasPrototype()) Fn->addFnAttr("no-prototype"); } >From 157d8a6357344fd4ded2f06c50234eee52dffceb Mon Sep 17 00:00:00 2001 From: Derek Schuff <[email protected]> Date: Fri, 4 Sep 2026 14:48:07 -0700 Subject: [PATCH 13/13] fix format --- clang/lib/CodeGen/Targets/WebAssembly.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index 5f57f9c334f6a..8f6f58bfb807e 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -88,9 +88,9 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { if (NameAttr) Global->addAttribute("wasm-import-name", NameAttr->getImportName()); } - if (const auto *Attr = VD->getAttr<WebAssemblyExportNameAttr>()) { - Global->addAttribute("wasm-export-name", Attr->getExportName()); - } + if (const auto *Attr = VD->getAttr<WebAssemblyExportNameAttr>()) { + Global->addAttribute("wasm-export-name", Attr->getExportName()); + } } else if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) { auto *Fn = cast<llvm::Function>(GV); const auto *ModuleAttr = FD->getAttr<WebAssemblyImportModuleAttr>(); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
