diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1646,6 +1646,13 @@ let Subjects = SubjectList<[Function], ErrorDiag>; } +def WebAssemblyExported : InheritableAttr, + TargetSpecificAttr { + let Spellings = [Clang<"exported">]; + let Documentation = [WebAssemblyExportedDocs]; + let Subjects = SubjectList<[Function], ErrorDiag>; +} + def WebAssemblyImportModule : InheritableAttr, TargetSpecificAttr { let Spellings = [Clang<"import_module">]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4165,6 +4165,16 @@ }]; } +def WebAssemblyExportedDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Clang supports the ``__attribute__((exported))`` +attribute for the WebAssembly target. This attribute may be attached to a +function declaration, where it causes the be exported from the linked +WebAssembly module. + }]; +} + def WebAssemblyImportModuleDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -777,24 +777,25 @@ CodeGen::CodeGenModule &CGM) const override { TargetCodeGenInfo::setTargetAttributes(D, GV, CGM); if (const auto *FD = dyn_cast_or_null(D)) { + llvm::Function *Fn = cast(GV); if (const auto *Attr = FD->getAttr()) { - llvm::Function *Fn = cast(GV); llvm::AttrBuilder B; B.addAttribute("wasm-import-module", Attr->getImportModule()); Fn->addAttributes(llvm::AttributeList::FunctionIndex, B); } if (const auto *Attr = FD->getAttr()) { - llvm::Function *Fn = cast(GV); llvm::AttrBuilder B; B.addAttribute("wasm-import-name", Attr->getImportName()); Fn->addAttributes(llvm::AttributeList::FunctionIndex, B); } if (const auto *Attr = FD->getAttr()) { - llvm::Function *Fn = cast(GV); llvm::AttrBuilder B; B.addAttribute("wasm-export-name", Attr->getExportName()); Fn->addAttributes(llvm::AttributeList::FunctionIndex, B); } + if (const auto *Attr = FD->getAttr()) { + Fn->addFnAttr("wasm-exported"); + } } if (auto *FD = dyn_cast_or_null(D)) { diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -5845,6 +5845,23 @@ D->addAttr(UsedAttr::CreateImplicit(S.Context)); } +static void handleWebAssemblyExportedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + if (!isFunctionOrMethod(D)) { + S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) + << "'exported'" << ExpectedFunction; + return; + } + + auto *FD = cast(D); + if (FD->isThisDeclarationADefinition()) { + S.Diag(D->getLocation(), diag::err_alias_is_definition) << FD << 0; + return; + } + + D->addAttr(::new (S.Context) WebAssemblyExportedAttr(S.Context, AL)); + D->addAttr(UsedAttr::CreateImplicit(S.Context)); +} + static void handleWebAssemblyImportModuleAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!isFunctionOrMethod(D)) { S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) @@ -6807,6 +6824,9 @@ case ParsedAttr::AT_WebAssemblyExportName: handleWebAssemblyExportNameAttr(S, D, AL); break; + case ParsedAttr::AT_WebAssemblyExported: + handleWebAssemblyExportedAttr(S, D, AL); + break; case ParsedAttr::AT_WebAssemblyImportModule: handleWebAssemblyImportModuleAttr(S, D, AL); break; diff --git a/clang/test/CodeGen/wasm-export.c b/clang/test/CodeGen/wasm-export.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/wasm-export.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm -o - %s | FileCheck %s + +int __attribute__((exported)) foo(void); + +int foo(void) { + return 43; +} + +// CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (i32 ()* @foo to i8*)] + +// CHECK: define i32 @foo() [[A:#[0-9]+]] + +// CHECK: attributes [[A]] = {{{.*}} "wasm-export" {{.*}}} diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -717,6 +717,15 @@ return expect(AsmToken::EndOfStatement, "EOL"); } + if (DirectiveID.getString() == ".export") { + auto SymName = expectIdent(); + if (SymName.empty()) + return true; + auto WasmSym = cast(Ctx.getOrCreateSymbol(SymName)); + WasmSym->setExported(); + TOut.emitExport(WasmSym); + } + if (DirectiveID.getString() == ".export_name") { auto SymName = expectIdent(); if (SymName.empty()) diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h @@ -51,6 +51,8 @@ /// .export_name virtual void emitExportName(const MCSymbolWasm *Sym, StringRef ExportName) = 0; + /// .export + virtual void emitExport(const MCSymbolWasm *Sym) = 0; protected: void emitValueType(wasm::ValType Type); @@ -72,6 +74,7 @@ void emitImportModule(const MCSymbolWasm *Sym, StringRef ImportModule) override; void emitImportName(const MCSymbolWasm *Sym, StringRef ImportName) override; void emitExportName(const MCSymbolWasm *Sym, StringRef ExportName) override; + void emitExport(const MCSymbolWasm *Sym) override; }; /// This part is for Wasm object output @@ -91,6 +94,7 @@ StringRef ImportName) override {} void emitExportName(const MCSymbolWasm *Sym, StringRef ExportName) override {} + void emitExport(const MCSymbolWasm *Sym) override {} }; /// This part is for null output @@ -108,6 +112,7 @@ void emitImportModule(const MCSymbolWasm *, StringRef) override {} void emitImportName(const MCSymbolWasm *, StringRef) override {} void emitExportName(const MCSymbolWasm *, StringRef) override {} + void emitExport(const MCSymbolWasm *) override {} }; } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp @@ -100,6 +100,10 @@ << ExportName << '\n'; } +void WebAssemblyTargetAsmStreamer::emitExport(const MCSymbolWasm *Sym) { + OS << "\t.export\t" << Sym->getName() << '\n'; +} + void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) { OS << "\t.indidx \t" << *Value << '\n'; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -134,6 +134,12 @@ } } + if (F.hasFnAttribute("wasm-exported")) { + auto *Sym = cast(getSymbol(&F)); + Sym->setExported(); + getTargetStreamer()->emitExport(Sym); + } + if (F.hasFnAttribute("wasm-export-name")) { auto *Sym = cast(getSymbol(&F)); StringRef Name = F.getFnAttribute("wasm-export-name").getValueAsString(); diff --git a/llvm/test/CodeGen/WebAssembly/export.ll b/llvm/test/CodeGen/WebAssembly/export.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/export.ll @@ -0,0 +1,17 @@ +; RUN: llc < %s -asm-verbose=false -wasm-keep-registers | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +define void @test() #0 { + ret void +} + +declare void @test2() #1 + + +attributes #0 = { "wasm-exported" } +attributes #1 = { "wasm-exported" } + +; CHECK: .export test +; CHECK: .export test2