forked from OSchip/llvm-project
[WebAssembly] Add new `export_name` clang attribute for controlling wasm export names
This is equivalent to the existing `import_name` and `import_module` attributes which control the import names in the final wasm binary produced by lld. This maps the existing This attribute currently requires a string rather than using the symbol name for a couple of reasons: 1. Avoid confusion with static and dynamic linking which is based on symbol name. Exporting a function from a wasm module using this directive is orthogonal to both static and dynamic linking. 2. Avoids name mangling. Differential Revision: https://reviews.llvm.org/D70520
This commit is contained in:
parent
8db5143b1a
commit
881d877846
|
@ -1600,6 +1600,14 @@ def BPFPreserveAccessIndex : InheritableAttr,
|
|||
let LangOpts = [COnly];
|
||||
}
|
||||
|
||||
def WebAssemblyExportName : InheritableAttr,
|
||||
TargetSpecificAttr<TargetWebAssembly> {
|
||||
let Spellings = [Clang<"export_name">];
|
||||
let Args = [StringArgument<"ExportName">];
|
||||
let Documentation = [WebAssemblyExportNameDocs];
|
||||
let Subjects = SubjectList<[Function], ErrorDiag>;
|
||||
}
|
||||
|
||||
def WebAssemblyImportModule : InheritableAttr,
|
||||
TargetSpecificAttr<TargetWebAssembly> {
|
||||
let Spellings = [Clang<"import_module">];
|
||||
|
|
|
@ -4101,6 +4101,21 @@ For more information see
|
|||
or `msvc documentation <https://docs.microsoft.com/pl-pl/cpp/cpp/selectany>`_.
|
||||
}]; }
|
||||
|
||||
def WebAssemblyExportNameDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
Clang supports the ``__attribute__((export_name(<name>)))``
|
||||
attribute for the WebAssembly target. This attribute may be attached to a
|
||||
function declaration, where it modifies how the symbol is to be exported
|
||||
from the linked WebAssembly.
|
||||
|
||||
WebAssembly functions are exported via string name. By default when a symbol
|
||||
is exported, the export name for C/C++ symbols are the same as their C/C++
|
||||
symbol names. This attribute can be used to override the default behavior, and
|
||||
request a specific string name be used instead.
|
||||
}];
|
||||
}
|
||||
|
||||
def WebAssemblyImportModuleDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
|
|
|
@ -779,6 +779,12 @@ public:
|
|||
B.addAttribute("wasm-import-name", Attr->getImportName());
|
||||
Fn->addAttributes(llvm::AttributeList::FunctionIndex, B);
|
||||
}
|
||||
if (const auto *Attr = FD->getAttr<WebAssemblyExportNameAttr>()) {
|
||||
llvm::Function *Fn = cast<llvm::Function>(GV);
|
||||
llvm::AttrBuilder B;
|
||||
B.addAttribute("wasm-export-name", Attr->getExportName());
|
||||
Fn->addAttributes(llvm::AttributeList::FunctionIndex, B);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto *FD = dyn_cast_or_null<FunctionDecl>(D)) {
|
||||
|
|
|
@ -5754,6 +5754,28 @@ static void handleBPFPreserveAccessIndexAttr(Sema &S, Decl *D,
|
|||
Rec->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
|
||||
}
|
||||
|
||||
static void handleWebAssemblyExportNameAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
if (!isFunctionOrMethod(D)) {
|
||||
S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
|
||||
<< "'export_name'" << ExpectedFunction;
|
||||
return;
|
||||
}
|
||||
|
||||
auto *FD = cast<FunctionDecl>(D);
|
||||
if (FD->isThisDeclarationADefinition()) {
|
||||
S.Diag(D->getLocation(), diag::err_alias_is_definition) << FD << 0;
|
||||
return;
|
||||
}
|
||||
|
||||
StringRef Str;
|
||||
SourceLocation ArgLoc;
|
||||
if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
|
||||
return;
|
||||
|
||||
FD->addAttr(::new (S.Context)
|
||||
WebAssemblyExportNameAttr(S.Context, AL, Str));
|
||||
}
|
||||
|
||||
static void handleWebAssemblyImportModuleAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
if (!isFunctionOrMethod(D)) {
|
||||
S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
|
||||
|
@ -6672,6 +6694,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
|
|||
case ParsedAttr::AT_BPFPreserveAccessIndex:
|
||||
handleBPFPreserveAccessIndexAttr(S, D, AL);
|
||||
break;
|
||||
case ParsedAttr::AT_WebAssemblyExportName:
|
||||
handleWebAssemblyExportNameAttr(S, D, AL);
|
||||
break;
|
||||
case ParsedAttr::AT_WebAssemblyImportModule:
|
||||
handleWebAssemblyImportModuleAttr(S, D, AL);
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// RUN: %clang_cc1 -triple wasm32-unknown-unknown-wasm -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
int __attribute__((export_name("bar"))) foo(void);
|
||||
|
||||
int foo(void) {
|
||||
return 43;
|
||||
}
|
||||
|
||||
// CHECK: define i32 @foo() [[A:#[0-9]+]]
|
||||
|
||||
// CHECK: attributes [[A]] = {{{.*}} "wasm-export-name"="bar" {{.*}}}
|
|
@ -151,6 +151,7 @@
|
|||
// CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType)
|
||||
// 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: WorkGroupSizeHint (SubjectMatchRule_function)
|
||||
|
|
|
@ -42,8 +42,8 @@ WebAssembly-specific options:
|
|||
.. option:: --export-dynamic
|
||||
|
||||
When building an executable, export any non-hidden symbols. By default only
|
||||
the entry point and any symbols marked with --export/--export-all are
|
||||
exported.
|
||||
the entry point and any symbols marked as exports (either via the command line
|
||||
or via the `export-name` source attribute) are exported.
|
||||
|
||||
.. option:: --global-base=<value>
|
||||
|
||||
|
@ -116,16 +116,18 @@ Imports and Exports
|
|||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When building a shared library any symbols marked as ``visibility=default`` will
|
||||
be exported. When building an executable, only the entry point and symbols
|
||||
flagged as ``WASM_SYMBOL_EXPORTED`` are exported by default. In LLVM the
|
||||
``WASM_SYMBOL_EXPORTED`` flag is applied to any symbol in the ``llvm.used`` list
|
||||
which corresponds to ``__attribute__((used))`` in C/C++ sources.
|
||||
be exported.
|
||||
|
||||
When building an executable, only the entry point (``_start``) and symbols with
|
||||
the ``WASM_SYMBOL_EXPORTED`` flag are exported by default. In LLVM the
|
||||
``WASM_SYMBOL_EXPORTED`` flag is set by the ``wasm-export-name`` attribute which
|
||||
in turn can be set using ``__attribute__((export_name))`` clang attribute.
|
||||
|
||||
In addition, symbols can be exported via the linker command line using
|
||||
``--export``.
|
||||
|
||||
Finally, just like with native ELF linker the ``--export-dynamic`` flag can be
|
||||
used to export symbol in the executable which are marked as
|
||||
used to export symbols in the executable which are marked as
|
||||
``visibility=default``.
|
||||
|
||||
Garbage Collection
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
; RUN: llc -filetype=obj %s -o %t.o
|
||||
; RUN: wasm-ld -o %t.wasm %t.o
|
||||
; RUN: obj2yaml %t.wasm | FileCheck %s
|
||||
|
||||
target triple = "wasm32-unknown-unknown"
|
||||
|
||||
define void @foo() #0 {
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @_start() {
|
||||
call void @foo()
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "wasm-export-name"="bar" }
|
||||
|
||||
; CHECK: - Type: EXPORT
|
||||
; CHECK-NEXT: Exports:
|
||||
; CHECK-NEXT: - Name: memory
|
||||
; CHECK-NEXT: Kind: MEMORY
|
||||
; CHECK-NEXT: Index: 0
|
||||
; CHECK-NEXT: - Name: bar
|
||||
; CHECK-NEXT: Kind: FUNCTION
|
||||
; CHECK-NEXT: Index: 0
|
||||
; CHECK-NEXT: - Name: _start
|
||||
; CHECK-NEXT: Kind: FUNCTION
|
||||
; CHECK-NEXT: Index: 1
|
|
@ -130,6 +130,9 @@ public:
|
|||
void writeTo(uint8_t *sectionStart) const override;
|
||||
StringRef getName() const override { return function->SymbolName; }
|
||||
StringRef getDebugName() const override { return function->DebugName; }
|
||||
StringRef getExportName() const {
|
||||
return function ? function->ExportName : "";
|
||||
}
|
||||
uint32_t getComdat() const override { return function->Comdat; }
|
||||
uint32_t getFunctionInputOffset() const { return getInputSectionOffset(); }
|
||||
uint32_t getFunctionCodeOffset() const { return function->CodeOffset; }
|
||||
|
|
|
@ -519,6 +519,10 @@ void Writer::calculateExports() {
|
|||
StringRef name = sym->getName();
|
||||
WasmExport export_;
|
||||
if (auto *f = dyn_cast<DefinedFunction>(sym)) {
|
||||
StringRef exportName = f->function->getExportName();
|
||||
if (!exportName.empty()) {
|
||||
name = exportName;
|
||||
}
|
||||
export_ = {name, WASM_EXTERNAL_FUNCTION, f->getFunctionIndex()};
|
||||
} else if (auto *g = dyn_cast<DefinedGlobal>(sym)) {
|
||||
// TODO(sbc): Remove this check once to mutable global proposal is
|
||||
|
|
|
@ -131,6 +131,7 @@ struct WasmFunction {
|
|||
uint32_t CodeSectionOffset;
|
||||
uint32_t Size;
|
||||
uint32_t CodeOffset; // start of Locals and Body
|
||||
StringRef ExportName; // from the "export" section
|
||||
StringRef SymbolName; // from the "linking" section
|
||||
StringRef DebugName; // from the "name" section
|
||||
uint32_t Comdat; // from the "comdat info" section
|
||||
|
@ -179,6 +180,7 @@ struct WasmSymbolInfo {
|
|||
uint32_t Flags;
|
||||
StringRef ImportModule; // For undefined symbols the module of the import
|
||||
StringRef ImportName; // For undefined symbols the name of the import
|
||||
StringRef ExportName; // For symbols to be exported from the final module
|
||||
union {
|
||||
// For function or global symbols, the index in function or global index
|
||||
// space.
|
||||
|
|
|
@ -21,6 +21,7 @@ class MCSymbolWasm : public MCSymbol {
|
|||
mutable bool IsUsedInGOT = false;
|
||||
Optional<std::string> ImportModule;
|
||||
Optional<std::string> ImportName;
|
||||
Optional<std::string> ExportName;
|
||||
wasm::WasmSignature *Signature = nullptr;
|
||||
Optional<wasm::WasmGlobalType> GlobalType;
|
||||
Optional<wasm::WasmEventType> EventType;
|
||||
|
@ -87,6 +88,10 @@ public:
|
|||
}
|
||||
void setImportName(StringRef Name) { ImportName = Name; }
|
||||
|
||||
bool hasExportName() const { return ExportName.hasValue(); }
|
||||
const StringRef getExportName() const { return ExportName.getValue(); }
|
||||
void setExportName(StringRef Name) { ExportName = Name; }
|
||||
|
||||
void setUsedInGOT() const { IsUsedInGOT = true; }
|
||||
bool isUsedInGOT() const { return IsUsedInGOT; }
|
||||
|
||||
|
|
|
@ -280,6 +280,7 @@ private:
|
|||
uint32_t StartFunction = -1;
|
||||
bool HasLinkingSection = false;
|
||||
bool HasDylinkSection = false;
|
||||
bool SeenCodeSection = false;
|
||||
wasm::WasmLinkingData LinkingData;
|
||||
uint32_t NumImportedGlobals = 0;
|
||||
uint32_t NumImportedFunctions = 0;
|
||||
|
|
|
@ -1324,6 +1324,14 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
|
|||
Comdats[C->getName()].emplace_back(
|
||||
WasmComdatEntry{wasm::WASM_COMDAT_FUNCTION, Index});
|
||||
}
|
||||
|
||||
if (WS.hasExportName()) {
|
||||
wasm::WasmExport Export;
|
||||
Export.Name = WS.getExportName();
|
||||
Export.Kind = wasm::WASM_EXTERNAL_FUNCTION;
|
||||
Export.Index = Index;
|
||||
Exports.push_back(Export);
|
||||
}
|
||||
} else {
|
||||
// An import; the index was assigned above.
|
||||
Index = WasmIndices.find(&WS)->second;
|
||||
|
@ -1454,6 +1462,8 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
|
|||
}
|
||||
if (WS.hasImportName())
|
||||
Flags |= wasm::WASM_SYMBOL_EXPLICIT_NAME;
|
||||
if (WS.hasExportName())
|
||||
Flags |= wasm::WASM_SYMBOL_EXPORTED;
|
||||
|
||||
wasm::WasmSymbolInfo Info;
|
||||
Info.Name = WS.getName();
|
||||
|
|
|
@ -343,7 +343,7 @@ Error WasmObjectFile::parseDylinkSection(ReadContext &Ctx) {
|
|||
|
||||
Error WasmObjectFile::parseNameSection(ReadContext &Ctx) {
|
||||
llvm::DenseSet<uint64_t> Seen;
|
||||
if (Functions.size() != FunctionTypes.size()) {
|
||||
if (FunctionTypes.size() && !SeenCodeSection) {
|
||||
return make_error<GenericBinaryError>("Names must come after code section",
|
||||
object_error::parse_failed);
|
||||
}
|
||||
|
@ -389,7 +389,7 @@ Error WasmObjectFile::parseNameSection(ReadContext &Ctx) {
|
|||
|
||||
Error WasmObjectFile::parseLinkingSection(ReadContext &Ctx) {
|
||||
HasLinkingSection = true;
|
||||
if (Functions.size() != FunctionTypes.size()) {
|
||||
if (FunctionTypes.size() && !SeenCodeSection) {
|
||||
return make_error<GenericBinaryError>(
|
||||
"Linking data must come after code section",
|
||||
object_error::parse_failed);
|
||||
|
@ -940,6 +940,7 @@ Error WasmObjectFile::parseImportSection(ReadContext &Ctx) {
|
|||
Error WasmObjectFile::parseFunctionSection(ReadContext &Ctx) {
|
||||
uint32_t Count = readVaruint32(Ctx);
|
||||
FunctionTypes.reserve(Count);
|
||||
Functions.resize(Count);
|
||||
uint32_t NumTypes = Signatures.size();
|
||||
while (Count--) {
|
||||
uint32_t Type = readVaruint32(Ctx);
|
||||
|
@ -1029,9 +1030,11 @@ Error WasmObjectFile::parseExportSection(ReadContext &Ctx) {
|
|||
Ex.Index = readVaruint32(Ctx);
|
||||
switch (Ex.Kind) {
|
||||
case wasm::WASM_EXTERNAL_FUNCTION:
|
||||
if (!isValidFunctionIndex(Ex.Index))
|
||||
|
||||
if (!isDefinedFunctionIndex(Ex.Index))
|
||||
return make_error<GenericBinaryError>("Invalid function export",
|
||||
object_error::parse_failed);
|
||||
getDefinedFunction(Ex.Index).ExportName = Ex.Name;
|
||||
break;
|
||||
case wasm::WASM_EXTERNAL_GLOBAL:
|
||||
if (!isValidGlobalIndex(Ex.Index))
|
||||
|
@ -1132,6 +1135,7 @@ Error WasmObjectFile::parseStartSection(ReadContext &Ctx) {
|
|||
}
|
||||
|
||||
Error WasmObjectFile::parseCodeSection(ReadContext &Ctx) {
|
||||
SeenCodeSection = true;
|
||||
CodeSection = Sections.size();
|
||||
uint32_t FunctionCount = readVaruint32(Ctx);
|
||||
if (FunctionCount != FunctionTypes.size()) {
|
||||
|
@ -1139,14 +1143,14 @@ Error WasmObjectFile::parseCodeSection(ReadContext &Ctx) {
|
|||
object_error::parse_failed);
|
||||
}
|
||||
|
||||
while (FunctionCount--) {
|
||||
wasm::WasmFunction Function;
|
||||
for (uint32_t i = 0; i < FunctionCount; i++) {
|
||||
wasm::WasmFunction& Function = Functions[i];
|
||||
const uint8_t *FunctionStart = Ctx.Ptr;
|
||||
uint32_t Size = readVaruint32(Ctx);
|
||||
const uint8_t *FunctionEnd = Ctx.Ptr + Size;
|
||||
|
||||
Function.CodeOffset = Ctx.Ptr - FunctionStart;
|
||||
Function.Index = NumImportedFunctions + Functions.size();
|
||||
Function.Index = NumImportedFunctions + i;
|
||||
Function.CodeSectionOffset = FunctionStart - Ctx.Start;
|
||||
Function.Size = FunctionEnd - FunctionStart;
|
||||
|
||||
|
@ -1165,7 +1169,6 @@ Error WasmObjectFile::parseCodeSection(ReadContext &Ctx) {
|
|||
Function.Comdat = UINT32_MAX;
|
||||
Ctx.Ptr += BodySize;
|
||||
assert(Ctx.Ptr == FunctionEnd);
|
||||
Functions.push_back(Function);
|
||||
}
|
||||
if (Ctx.Ptr != Ctx.End)
|
||||
return make_error<GenericBinaryError>("Code section ended prematurely",
|
||||
|
|
|
@ -712,6 +712,18 @@ public:
|
|||
return expect(AsmToken::EndOfStatement, "EOL");
|
||||
}
|
||||
|
||||
if (DirectiveID.getString() == ".export_name") {
|
||||
auto SymName = expectIdent();
|
||||
if (SymName.empty())
|
||||
return true;
|
||||
if (expect(AsmToken::Comma, ","))
|
||||
return true;
|
||||
auto ExportName = expectIdent();
|
||||
auto WasmSym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(SymName));
|
||||
WasmSym->setExportName(ExportName);
|
||||
TOut.emitExportName(WasmSym, ExportName);
|
||||
}
|
||||
|
||||
if (DirectiveID.getString() == ".import_module") {
|
||||
auto SymName = expectIdent();
|
||||
if (SymName.empty())
|
||||
|
|
|
@ -94,6 +94,12 @@ void WebAssemblyTargetAsmStreamer::emitImportName(const MCSymbolWasm *Sym,
|
|||
<< ImportName << '\n';
|
||||
}
|
||||
|
||||
void WebAssemblyTargetAsmStreamer::emitExportName(const MCSymbolWasm *Sym,
|
||||
StringRef ExportName) {
|
||||
OS << "\t.export_name\t" << Sym->getName() << ", "
|
||||
<< ExportName << '\n';
|
||||
}
|
||||
|
||||
void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) {
|
||||
OS << "\t.indidx \t" << *Value << '\n';
|
||||
}
|
||||
|
|
|
@ -48,6 +48,9 @@ public:
|
|||
/// .import_name
|
||||
virtual void emitImportName(const MCSymbolWasm *Sym,
|
||||
StringRef ImportName) = 0;
|
||||
/// .export_name
|
||||
virtual void emitExportName(const MCSymbolWasm *Sym,
|
||||
StringRef ExportName) = 0;
|
||||
|
||||
protected:
|
||||
void emitValueType(wasm::ValType Type);
|
||||
|
@ -68,6 +71,7 @@ public:
|
|||
void emitEventType(const MCSymbolWasm *Sym) override;
|
||||
void emitImportModule(const MCSymbolWasm *Sym, StringRef ImportModule) override;
|
||||
void emitImportName(const MCSymbolWasm *Sym, StringRef ImportName) override;
|
||||
void emitExportName(const MCSymbolWasm *Sym, StringRef ExportName) override;
|
||||
};
|
||||
|
||||
/// This part is for Wasm object output
|
||||
|
@ -85,6 +89,8 @@ public:
|
|||
StringRef ImportModule) override {}
|
||||
void emitImportName(const MCSymbolWasm *Sym,
|
||||
StringRef ImportName) override {}
|
||||
void emitExportName(const MCSymbolWasm *Sym,
|
||||
StringRef ExportName) override {}
|
||||
};
|
||||
|
||||
/// This part is for null output
|
||||
|
@ -101,6 +107,7 @@ public:
|
|||
void emitEventType(const MCSymbolWasm *) override {}
|
||||
void emitImportModule(const MCSymbolWasm *, StringRef) override {}
|
||||
void emitImportName(const MCSymbolWasm *, StringRef) override {}
|
||||
void emitExportName(const MCSymbolWasm *, StringRef) override {}
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
|
|
@ -96,8 +96,11 @@ void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
|
|||
}
|
||||
|
||||
for (const auto &F : M) {
|
||||
if (F.isIntrinsic())
|
||||
continue;
|
||||
|
||||
// Emit function type info for all undefined functions
|
||||
if (F.isDeclarationForLinker() && !F.isIntrinsic()) {
|
||||
if (F.isDeclarationForLinker()) {
|
||||
SmallVector<MVT, 4> Results;
|
||||
SmallVector<MVT, 4> Params;
|
||||
computeSignatureVTs(F.getFunctionType(), F, TM, Params, Results);
|
||||
|
@ -130,6 +133,13 @@ void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
|
|||
getTargetStreamer()->emitImportName(Sym, Name);
|
||||
}
|
||||
}
|
||||
|
||||
if (F.hasFnAttribute("wasm-export-name")) {
|
||||
auto *Sym = cast<MCSymbolWasm>(getSymbol(&F));
|
||||
StringRef Name = F.getFnAttribute("wasm-export-name").getValueAsString();
|
||||
Sym->setExportName(Name);
|
||||
getTargetStreamer()->emitExportName(Sym, Name);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &G : M.globals()) {
|
||||
|
|
|
@ -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-export-name"="foo" }
|
||||
attributes #1 = { "wasm-export-name"="bar" }
|
||||
|
||||
; CHECK: .export_name test, foo
|
||||
; CHECK: .export_name test2, bar
|
|
@ -0,0 +1,26 @@
|
|||
# RUN: llvm-mc -triple=wasm32-unknown-unknown < %s | FileCheck %s
|
||||
# Check that it also comiled to object for format.
|
||||
# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -o - < %s | obj2yaml | FileCheck -check-prefix=CHECK-OBJ %s
|
||||
|
||||
foo:
|
||||
.globl foo
|
||||
.functype foo () -> ()
|
||||
.export_name foo, bar
|
||||
end_function
|
||||
|
||||
# CHECK: .export_name foo, bar
|
||||
|
||||
# CHECK-OBJ: - Type: EXPORT
|
||||
# CHECK-OBJ-NEXT: Exports:
|
||||
# CHECK-OBJ-NEXT: - Name: bar
|
||||
# CHECK-OBJ-NEXT: Kind: FUNCTION
|
||||
# CHECK-OBJ-NEXT: Index: 0
|
||||
|
||||
# CHECK-OBJ: Name: linking
|
||||
# CHECK-OBJ-NEXT: Version: 2
|
||||
# CHECK-OBJ-NEXT: SymbolTable:
|
||||
# CHECK-OBJ-NEXT: - Index: 0
|
||||
# CHECK-OBJ-NEXT: Kind: FUNCTION
|
||||
# CHECK-OBJ-NEXT: Name: foo
|
||||
# CHECK-OBJ-NEXT: Flags: [ EXPORTED ]
|
||||
# CHECK-OBJ-NEXT: Function: 0
|
Loading…
Reference in New Issue