[WebAssembly] Add support for the event section

Summary:
This adds support for the 'event section' specified in the exception
handling proposal.

Wasm exception handling binary model spec:
https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md#changes-to-the-binary-model

Reviewers: sbc100, ruiu

Subscribers: dschuff, jgravelle-google, sunfish, llvm-commits

Differential Revision: https://reviews.llvm.org/D54875

llvm-svn: 348703
This commit is contained in:
Heejin Ahn 2018-12-08 06:17:43 +00:00
parent a2125b8d99
commit e915a71f18
19 changed files with 401 additions and 30 deletions

View File

@ -43,6 +43,8 @@ class WasmSymbol;
} // namespace object } // namespace object
namespace wasm { namespace wasm {
struct WasmEvent;
struct WasmEventType;
struct WasmFunction; struct WasmFunction;
struct WasmGlobal; struct WasmGlobal;
struct WasmGlobalType; struct WasmGlobalType;
@ -76,6 +78,8 @@ using llvm::object::WasmObjectFile;
using llvm::object::WasmSection; using llvm::object::WasmSection;
using llvm::object::WasmSegment; using llvm::object::WasmSegment;
using llvm::object::WasmSymbol; using llvm::object::WasmSymbol;
using llvm::wasm::WasmEvent;
using llvm::wasm::WasmEventType;
using llvm::wasm::WasmFunction; using llvm::wasm::WasmFunction;
using llvm::wasm::WasmGlobal; using llvm::wasm::WasmGlobal;
using llvm::wasm::WasmGlobalType; using llvm::wasm::WasmGlobalType;

View File

@ -0,0 +1,9 @@
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
declare void @llvm.wasm.throw(i32, i8*)
define void @foo(i8* %p) {
call void @llvm.wasm.throw(i32 0, i8* %p)
ret void
}

View File

@ -0,0 +1,9 @@
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
declare void @llvm.wasm.throw(i32, i8*)
define void @bar(i8* %p) {
call void @llvm.wasm.throw(i32 0, i8* %p)
ret void
}

View File

@ -0,0 +1,34 @@
; RUN: llc -filetype=obj -exception-model=wasm -mattr=+exception-handling %p/Inputs/event-section1.ll -o %t1.o
; RUN: llc -filetype=obj -exception-model=wasm -mattr=+exception-handling %p/Inputs/event-section2.ll -o %t2.o
; RUN: llc -filetype=obj -exception-model=wasm -mattr=+exception-handling %s -o %t.o
; RUN: wasm-ld -o %t.wasm %t.o %t1.o %t2.o
; RUN: obj2yaml %t.wasm | FileCheck %s
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
declare void @foo(i8*)
declare void @bar(i8*)
define void @_start() {
call void @foo(i8* null)
call void @bar(i8* null)
ret void
}
; CHECK: Sections:
; CHECK-NEXT: - Type: TYPE
; CHECK-NEXT: Signatures:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: ReturnType: NORESULT
; CHECK-NEXT: ParamTypes: []
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: ReturnType: NORESULT
; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - I32
; CHECK: - Type: EVENT
; CHECK-NEXT: Events:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Attribute: 0
; CHECK-NEXT: SigIndex: 1

View File

@ -306,8 +306,8 @@ static void handleWeakUndefines() {
// It is possible for undefined functions not to have a signature (eg. if // It is possible for undefined functions not to have a signature (eg. if
// added via "--undefined"), but weak undefined ones do have a signature. // added via "--undefined"), but weak undefined ones do have a signature.
assert(FuncSym->FunctionType); assert(FuncSym->Signature);
const WasmSignature &Sig = *FuncSym->FunctionType; const WasmSignature &Sig = *FuncSym->Signature;
// Add a synthetic dummy for weak undefined functions. These dummies will // Add a synthetic dummy for weak undefined functions. These dummies will
// be GC'd if not used as the target of any "call" instructions. // be GC'd if not used as the target of any "call" instructions.

View File

@ -55,6 +55,7 @@ void InputChunk::verifyRelocTargets() const {
case R_WEBASSEMBLY_TYPE_INDEX_LEB: case R_WEBASSEMBLY_TYPE_INDEX_LEB:
case R_WEBASSEMBLY_FUNCTION_INDEX_LEB: case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
case R_WEBASSEMBLY_EVENT_INDEX_LEB:
case R_WEBASSEMBLY_MEMORY_ADDR_LEB: case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
ExistingValue = decodeULEB128(Loc, &BytesRead); ExistingValue = decodeULEB128(Loc, &BytesRead);
break; break;
@ -111,6 +112,7 @@ void InputChunk::writeTo(uint8_t *Buf) const {
case R_WEBASSEMBLY_TYPE_INDEX_LEB: case R_WEBASSEMBLY_TYPE_INDEX_LEB:
case R_WEBASSEMBLY_FUNCTION_INDEX_LEB: case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
case R_WEBASSEMBLY_EVENT_INDEX_LEB:
case R_WEBASSEMBLY_MEMORY_ADDR_LEB: case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
encodeULEB128(Value, Loc, 5); encodeULEB128(Value, Loc, 5);
break; break;
@ -180,6 +182,7 @@ static unsigned writeCompressedReloc(uint8_t *Buf, const WasmRelocation &Rel,
case R_WEBASSEMBLY_TYPE_INDEX_LEB: case R_WEBASSEMBLY_TYPE_INDEX_LEB:
case R_WEBASSEMBLY_FUNCTION_INDEX_LEB: case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
case R_WEBASSEMBLY_EVENT_INDEX_LEB:
case R_WEBASSEMBLY_MEMORY_ADDR_LEB: case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
return encodeULEB128(Value, Buf); return encodeULEB128(Value, Buf);
case R_WEBASSEMBLY_TABLE_INDEX_SLEB: case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
@ -195,6 +198,7 @@ static unsigned getRelocWidthPadded(const WasmRelocation &Rel) {
case R_WEBASSEMBLY_TYPE_INDEX_LEB: case R_WEBASSEMBLY_TYPE_INDEX_LEB:
case R_WEBASSEMBLY_FUNCTION_INDEX_LEB: case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
case R_WEBASSEMBLY_EVENT_INDEX_LEB:
case R_WEBASSEMBLY_MEMORY_ADDR_LEB: case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
case R_WEBASSEMBLY_TABLE_INDEX_SLEB: case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
case R_WEBASSEMBLY_MEMORY_ADDR_SLEB: case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:

63
lld/wasm/InputEvent.h Normal file
View File

@ -0,0 +1,63 @@
//===- InputEvent.h ---------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Wasm events are features that suspend the current execution and transfer the
// control flow to a corresponding handler. Currently the only supported event
// kind is exceptions.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_WASM_INPUT_EVENT_H
#define LLD_WASM_INPUT_EVENT_H
#include "Config.h"
#include "InputFiles.h"
#include "WriterUtils.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/Object/Wasm.h"
namespace lld {
namespace wasm {
// Represents a single Wasm Event within an input file. These are combined to
// form the final EVENTS section.
class InputEvent {
public:
InputEvent(const WasmSignature &S, const WasmEvent &E, ObjFile *F)
: File(F), Event(E), Signature(S), Live(!Config->GcSections) {}
StringRef getName() const { return Event.SymbolName; }
const WasmEventType &getType() const { return Event.Type; }
uint32_t getEventIndex() const { return EventIndex.getValue(); }
bool hasEventIndex() const { return EventIndex.hasValue(); }
void setEventIndex(uint32_t Index) {
assert(!hasEventIndex());
EventIndex = Index;
}
ObjFile *File;
WasmEvent Event;
const WasmSignature &Signature;
bool Live = false;
protected:
llvm::Optional<uint32_t> EventIndex;
};
} // namespace wasm
inline std::string toString(const wasm::InputEvent *E) {
return (toString(E->File) + ":(" + E->getName() + ")").str();
}
} // namespace lld
#endif // LLD_WASM_INPUT_EVENT_H

View File

@ -10,6 +10,7 @@
#include "InputFiles.h" #include "InputFiles.h"
#include "Config.h" #include "Config.h"
#include "InputChunks.h" #include "InputChunks.h"
#include "InputEvent.h"
#include "InputGlobal.h" #include "InputGlobal.h"
#include "SymbolTable.h" #include "SymbolTable.h"
#include "lld/Common/ErrorHandler.h" #include "lld/Common/ErrorHandler.h"
@ -57,7 +58,8 @@ void ObjFile::dumpInfo() const {
log("info for: " + getName() + log("info for: " + getName() +
"\n Symbols : " + Twine(Symbols.size()) + "\n Symbols : " + Twine(Symbols.size()) +
"\n Function Imports : " + Twine(WasmObj->getNumImportedFunctions()) + "\n Function Imports : " + Twine(WasmObj->getNumImportedFunctions()) +
"\n Global Imports : " + Twine(WasmObj->getNumImportedGlobals())); "\n Global Imports : " + Twine(WasmObj->getNumImportedGlobals()) +
"\n Event Imports : " + Twine(WasmObj->getNumImportedEvents()));
} }
// Relocations contain either symbol or type indices. This function takes a // Relocations contain either symbol or type indices. This function takes a
@ -119,7 +121,8 @@ uint32_t ObjFile::calcExpectedValue(const WasmRelocation &Reloc) const {
case R_WEBASSEMBLY_TYPE_INDEX_LEB: case R_WEBASSEMBLY_TYPE_INDEX_LEB:
return Reloc.Index; return Reloc.Index;
case R_WEBASSEMBLY_FUNCTION_INDEX_LEB: case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: { case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
case R_WEBASSEMBLY_EVENT_INDEX_LEB: {
const WasmSymbol &Sym = WasmObj->syms()[Reloc.Index]; const WasmSymbol &Sym = WasmObj->syms()[Reloc.Index];
return Sym.Info.ElementIndex; return Sym.Info.ElementIndex;
} }
@ -147,6 +150,8 @@ uint32_t ObjFile::calcNewValue(const WasmRelocation &Reloc) const {
return getFunctionSymbol(Reloc.Index)->getFunctionIndex(); return getFunctionSymbol(Reloc.Index)->getFunctionIndex();
case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
return getGlobalSymbol(Reloc.Index)->getGlobalIndex(); return getGlobalSymbol(Reloc.Index)->getGlobalIndex();
case R_WEBASSEMBLY_EVENT_INDEX_LEB:
return getEventSymbol(Reloc.Index)->getEventIndex();
case R_WEBASSEMBLY_FUNCTION_OFFSET_I32: case R_WEBASSEMBLY_FUNCTION_OFFSET_I32:
if (auto *Sym = dyn_cast<DefinedFunction>(getFunctionSymbol(Reloc.Index))) { if (auto *Sym = dyn_cast<DefinedFunction>(getFunctionSymbol(Reloc.Index))) {
if (Sym->isLive()) if (Sym->isLive())
@ -267,6 +272,10 @@ void ObjFile::parse() {
for (const WasmGlobal &G : WasmObj->globals()) for (const WasmGlobal &G : WasmObj->globals())
Globals.emplace_back(make<InputGlobal>(G, this)); Globals.emplace_back(make<InputGlobal>(G, this));
// Populate `Events`.
for (const WasmEvent &E : WasmObj->events())
Events.emplace_back(make<InputEvent>(Types[E.Type.SigIndex], E, this));
// Populate `Symbols` based on the WasmSymbols in the object. // Populate `Symbols` based on the WasmSymbols in the object.
Symbols.reserve(WasmObj->getNumberOfSymbols()); Symbols.reserve(WasmObj->getNumberOfSymbols());
for (const SymbolRef &Sym : WasmObj->symbols()) { for (const SymbolRef &Sym : WasmObj->symbols()) {
@ -293,6 +302,10 @@ GlobalSymbol *ObjFile::getGlobalSymbol(uint32_t Index) const {
return cast<GlobalSymbol>(Symbols[Index]); return cast<GlobalSymbol>(Symbols[Index]);
} }
EventSymbol *ObjFile::getEventSymbol(uint32_t Index) const {
return cast<EventSymbol>(Symbols[Index]);
}
SectionSymbol *ObjFile::getSectionSymbol(uint32_t Index) const { SectionSymbol *ObjFile::getSectionSymbol(uint32_t Index) const {
return cast<SectionSymbol>(Symbols[Index]); return cast<SectionSymbol>(Symbols[Index]);
} }
@ -347,6 +360,13 @@ Symbol *ObjFile::createDefined(const WasmSymbol &Sym) {
assert(Sym.isBindingLocal()); assert(Sym.isBindingLocal());
return make<SectionSymbol>(Name, Flags, Section, this); return make<SectionSymbol>(Name, Flags, Section, this);
} }
case WASM_SYMBOL_TYPE_EVENT: {
InputEvent *Event =
Events[Sym.Info.ElementIndex - WasmObj->getNumImportedEvents()];
if (Sym.isBindingLocal())
return make<DefinedEvent>(Name, Flags, this, Event);
return Symtab->addDefinedEvent(Name, Flags, this, Event);
}
} }
llvm_unreachable("unknown symbol kind"); llvm_unreachable("unknown symbol kind");
} }
@ -357,7 +377,7 @@ Symbol *ObjFile::createUndefined(const WasmSymbol &Sym) {
switch (Sym.Info.Kind) { switch (Sym.Info.Kind) {
case WASM_SYMBOL_TYPE_FUNCTION: case WASM_SYMBOL_TYPE_FUNCTION:
return Symtab->addUndefinedFunction(Name, Flags, this, Sym.FunctionType); return Symtab->addUndefinedFunction(Name, Flags, this, Sym.Signature);
case WASM_SYMBOL_TYPE_DATA: case WASM_SYMBOL_TYPE_DATA:
return Symtab->addUndefinedData(Name, Flags, this); return Symtab->addUndefinedData(Name, Flags, this);
case WASM_SYMBOL_TYPE_GLOBAL: case WASM_SYMBOL_TYPE_GLOBAL:

View File

@ -27,6 +27,7 @@ class InputChunk;
class InputFunction; class InputFunction;
class InputSegment; class InputSegment;
class InputGlobal; class InputGlobal;
class InputEvent;
class InputSection; class InputSection;
class InputFile { class InputFile {
@ -108,6 +109,7 @@ public:
std::vector<InputSegment *> Segments; std::vector<InputSegment *> Segments;
std::vector<InputFunction *> Functions; std::vector<InputFunction *> Functions;
std::vector<InputGlobal *> Globals; std::vector<InputGlobal *> Globals;
std::vector<InputEvent *> Events;
std::vector<InputSection *> CustomSections; std::vector<InputSection *> CustomSections;
llvm::DenseMap<uint32_t, InputSection *> CustomSectionsByIndex; llvm::DenseMap<uint32_t, InputSection *> CustomSectionsByIndex;
@ -116,6 +118,7 @@ public:
DataSymbol *getDataSymbol(uint32_t Index) const; DataSymbol *getDataSymbol(uint32_t Index) const;
GlobalSymbol *getGlobalSymbol(uint32_t Index) const; GlobalSymbol *getGlobalSymbol(uint32_t Index) const;
SectionSymbol *getSectionSymbol(uint32_t Index) const; SectionSymbol *getSectionSymbol(uint32_t Index) const;
EventSymbol *getEventSymbol(uint32_t Index) const;
private: private:
Symbol *createDefined(const WasmSymbol &Sym); Symbol *createDefined(const WasmSymbol &Sym);

View File

@ -80,7 +80,7 @@ BitcodeCompiler::~BitcodeCompiler() = default;
static void undefine(Symbol *S) { static void undefine(Symbol *S) {
if (auto F = dyn_cast<DefinedFunction>(S)) if (auto F = dyn_cast<DefinedFunction>(S))
replaceSymbol<UndefinedFunction>(F, F->getName(), 0, F->getFile(), replaceSymbol<UndefinedFunction>(F, F->getName(), 0, F->getFile(),
F->FunctionType); F->Signature);
else if (isa<DefinedData>(S)) else if (isa<DefinedData>(S))
replaceSymbol<UndefinedData>(S, S->getName(), 0, S->getFile()); replaceSymbol<UndefinedData>(S, S->getName(), 0, S->getFile());
else else

View File

@ -22,6 +22,7 @@
#include "MarkLive.h" #include "MarkLive.h"
#include "Config.h" #include "Config.h"
#include "InputChunks.h" #include "InputChunks.h"
#include "InputEvent.h"
#include "InputGlobal.h" #include "InputGlobal.h"
#include "SymbolTable.h" #include "SymbolTable.h"
#include "Symbols.h" #include "Symbols.h"
@ -105,6 +106,9 @@ void lld::wasm::markLive() {
for (InputGlobal *G : Obj->Globals) for (InputGlobal *G : Obj->Globals)
if (!G->Live) if (!G->Live)
message("removing unused section " + toString(G)); message("removing unused section " + toString(G));
for (InputEvent *E : Obj->Events)
if (!E->Live)
message("removing unused section " + toString(E));
} }
for (InputChunk *C : Symtab->SyntheticFunctions) for (InputChunk *C : Symtab->SyntheticFunctions)
if (!C->Live) if (!C->Live)

View File

@ -40,6 +40,8 @@ static StringRef sectionTypeToString(uint32_t SectionType) {
return "MEMORY"; return "MEMORY";
case WASM_SEC_GLOBAL: case WASM_SEC_GLOBAL:
return "GLOBAL"; return "GLOBAL";
case WASM_SEC_EVENT:
return "EVENT";
case WASM_SEC_EXPORT: case WASM_SEC_EXPORT:
return "EXPORT"; return "EXPORT";
case WASM_SEC_START: case WASM_SEC_START:

View File

@ -10,6 +10,7 @@
#include "SymbolTable.h" #include "SymbolTable.h"
#include "Config.h" #include "Config.h"
#include "InputChunks.h" #include "InputChunks.h"
#include "InputEvent.h"
#include "InputGlobal.h" #include "InputGlobal.h"
#include "WriterUtils.h" #include "WriterUtils.h"
#include "lld/Common/ErrorHandler.h" #include "lld/Common/ErrorHandler.h"
@ -111,9 +112,9 @@ static void checkFunctionType(Symbol *Existing, const InputFile *File,
if (!NewSig) if (!NewSig)
return; return;
const WasmSignature *OldSig = ExistingFunction->FunctionType; const WasmSignature *OldSig = ExistingFunction->Signature;
if (!OldSig) { if (!OldSig) {
ExistingFunction->FunctionType = NewSig; ExistingFunction->Signature = NewSig;
return; return;
} }
@ -139,6 +140,28 @@ static void checkGlobalType(const Symbol *Existing, const InputFile *File,
} }
} }
static void checkEventType(const Symbol *Existing, const InputFile *File,
const WasmEventType *NewType,
const WasmSignature *NewSig) {
auto ExistingEvent = dyn_cast<EventSymbol>(Existing);
if (!isa<EventSymbol>(Existing)) {
reportTypeError(Existing, File, WASM_SYMBOL_TYPE_EVENT);
return;
}
const WasmEventType *OldType = cast<EventSymbol>(Existing)->getEventType();
const WasmSignature *OldSig = ExistingEvent->Signature;
if (NewType->Attribute != OldType->Attribute)
error("Event type mismatch: " + Existing->getName() + "\n>>> defined as " +
toString(*OldType) + " in " + toString(Existing->getFile()) +
"\n>>> defined as " + toString(*NewType) + " in " + toString(File));
if (*NewSig != *OldSig)
warn("Event signature mismatch: " + Existing->getName() +
"\n>>> defined as " + toString(*OldSig) + " in " +
toString(Existing->getFile()) + "\n>>> defined as " +
toString(*NewSig) + " in " + toString(File));
}
static void checkDataType(const Symbol *Existing, const InputFile *File) { static void checkDataType(const Symbol *Existing, const InputFile *File) {
if (!isa<DataSymbol>(Existing)) if (!isa<DataSymbol>(Existing))
reportTypeError(Existing, File, WASM_SYMBOL_TYPE_DATA); reportTypeError(Existing, File, WASM_SYMBOL_TYPE_DATA);
@ -222,10 +245,10 @@ Symbol *SymbolTable::addDefinedFunction(StringRef Name, uint32_t Flags,
// functions) but the old symbols does then preserve the old signature // functions) but the old symbols does then preserve the old signature
const WasmSignature *OldSig = nullptr; const WasmSignature *OldSig = nullptr;
if (auto* F = dyn_cast<FunctionSymbol>(S)) if (auto* F = dyn_cast<FunctionSymbol>(S))
OldSig = F->FunctionType; OldSig = F->Signature;
auto NewSym = replaceSymbol<DefinedFunction>(S, Name, Flags, File, Function); auto NewSym = replaceSymbol<DefinedFunction>(S, Name, Flags, File, Function);
if (!NewSym->FunctionType) if (!NewSym->Signature)
NewSym->FunctionType = OldSig; NewSym->Signature = OldSig;
} }
return S; return S;
} }
@ -271,6 +294,26 @@ Symbol *SymbolTable::addDefinedGlobal(StringRef Name, uint32_t Flags,
return S; return S;
} }
Symbol *SymbolTable::addDefinedEvent(StringRef Name, uint32_t Flags,
InputFile *File, InputEvent *Event) {
LLVM_DEBUG(dbgs() << "addDefinedEvent:" << Name << "\n");
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name, File);
if (WasInserted || S->isLazy()) {
replaceSymbol<DefinedEvent>(S, Name, Flags, File, Event);
return S;
}
checkEventType(S, File, &Event->getType(), &Event->Signature);
if (shouldReplace(S, File, Flags))
replaceSymbol<DefinedEvent>(S, Name, Flags, File, Event);
return S;
}
Symbol *SymbolTable::addUndefinedFunction(StringRef Name, uint32_t Flags, Symbol *SymbolTable::addUndefinedFunction(StringRef Name, uint32_t Flags,
InputFile *File, InputFile *File,
const WasmSignature *Sig) { const WasmSignature *Sig) {

View File

@ -56,6 +56,8 @@ public:
uint32_t Size); uint32_t Size);
Symbol *addDefinedGlobal(StringRef Name, uint32_t Flags, InputFile *File, Symbol *addDefinedGlobal(StringRef Name, uint32_t Flags, InputFile *File,
InputGlobal *G); InputGlobal *G);
Symbol *addDefinedEvent(StringRef Name, uint32_t Flags, InputFile *File,
InputEvent *E);
Symbol *addUndefinedFunction(StringRef Name, uint32_t Flags, InputFile *File, Symbol *addUndefinedFunction(StringRef Name, uint32_t Flags, InputFile *File,
const WasmSignature *Signature); const WasmSignature *Signature);

View File

@ -10,6 +10,7 @@
#include "Symbols.h" #include "Symbols.h"
#include "Config.h" #include "Config.h"
#include "InputChunks.h" #include "InputChunks.h"
#include "InputEvent.h"
#include "InputFiles.h" #include "InputFiles.h"
#include "InputGlobal.h" #include "InputGlobal.h"
#include "OutputSegment.h" #include "OutputSegment.h"
@ -38,6 +39,8 @@ WasmSymbolType Symbol::getWasmType() const {
return WASM_SYMBOL_TYPE_DATA; return WASM_SYMBOL_TYPE_DATA;
if (isa<GlobalSymbol>(this)) if (isa<GlobalSymbol>(this))
return WASM_SYMBOL_TYPE_GLOBAL; return WASM_SYMBOL_TYPE_GLOBAL;
if (isa<EventSymbol>(this))
return WASM_SYMBOL_TYPE_EVENT;
if (isa<SectionSymbol>(this)) if (isa<SectionSymbol>(this))
return WASM_SYMBOL_TYPE_SECTION; return WASM_SYMBOL_TYPE_SECTION;
llvm_unreachable("invalid symbol kind"); llvm_unreachable("invalid symbol kind");
@ -54,6 +57,8 @@ InputChunk *Symbol::getChunk() const {
bool Symbol::isLive() const { bool Symbol::isLive() const {
if (auto *G = dyn_cast<DefinedGlobal>(this)) if (auto *G = dyn_cast<DefinedGlobal>(this))
return G->Global->Live; return G->Global->Live;
if (auto *E = dyn_cast<DefinedEvent>(this))
return E->Event->Live;
if (InputChunk *C = getChunk()) if (InputChunk *C = getChunk())
return C->Live; return C->Live;
return Referenced; return Referenced;
@ -62,6 +67,8 @@ bool Symbol::isLive() const {
void Symbol::markLive() { void Symbol::markLive() {
if (auto *G = dyn_cast<DefinedGlobal>(this)) if (auto *G = dyn_cast<DefinedGlobal>(this))
G->Global->Live = true; G->Global->Live = true;
if (auto *E = dyn_cast<DefinedEvent>(this))
E->Event->Live = true;
if (InputChunk *C = getChunk()) if (InputChunk *C = getChunk())
C->Live = true; C->Live = true;
Referenced = true; Referenced = true;
@ -212,6 +219,32 @@ DefinedGlobal::DefinedGlobal(StringRef Name, uint32_t Flags, InputFile *File,
Global ? &Global->getType() : nullptr), Global ? &Global->getType() : nullptr),
Global(Global) {} Global(Global) {}
uint32_t EventSymbol::getEventIndex() const {
if (auto *F = dyn_cast<DefinedEvent>(this))
return F->Event->getEventIndex();
assert(EventIndex != INVALID_INDEX);
return EventIndex;
}
void EventSymbol::setEventIndex(uint32_t Index) {
LLVM_DEBUG(dbgs() << "setEventIndex " << Name << " -> " << Index << "\n");
assert(EventIndex == INVALID_INDEX);
EventIndex = Index;
}
bool EventSymbol::hasEventIndex() const {
if (auto *F = dyn_cast<DefinedEvent>(this))
return F->Event->hasEventIndex();
return EventIndex != INVALID_INDEX;
}
DefinedEvent::DefinedEvent(StringRef Name, uint32_t Flags, InputFile *File,
InputEvent *Event)
: EventSymbol(Name, DefinedEventKind, Flags, File,
Event ? &Event->getType() : nullptr,
Event ? &Event->Signature : nullptr),
Event(Event) {}
uint32_t SectionSymbol::getOutputSectionIndex() const { uint32_t SectionSymbol::getOutputSectionIndex() const {
LLVM_DEBUG(dbgs() << "getOutputSectionIndex: " << getName() << "\n"); LLVM_DEBUG(dbgs() << "getOutputSectionIndex: " << getName() << "\n");
assert(OutputSectionIndex != INVALID_INDEX); assert(OutputSectionIndex != INVALID_INDEX);
@ -246,6 +279,8 @@ std::string lld::toString(wasm::Symbol::Kind Kind) {
return "DefinedData"; return "DefinedData";
case wasm::Symbol::DefinedGlobalKind: case wasm::Symbol::DefinedGlobalKind:
return "DefinedGlobal"; return "DefinedGlobal";
case wasm::Symbol::DefinedEventKind:
return "DefinedEvent";
case wasm::Symbol::UndefinedFunctionKind: case wasm::Symbol::UndefinedFunctionKind:
return "UndefinedFunction"; return "UndefinedFunction";
case wasm::Symbol::UndefinedDataKind: case wasm::Symbol::UndefinedDataKind:

View File

@ -25,6 +25,7 @@ class InputChunk;
class InputSegment; class InputSegment;
class InputFunction; class InputFunction;
class InputGlobal; class InputGlobal;
class InputEvent;
class InputSection; class InputSection;
#define INVALID_INDEX UINT32_MAX #define INVALID_INDEX UINT32_MAX
@ -36,6 +37,7 @@ public:
DefinedFunctionKind, DefinedFunctionKind,
DefinedDataKind, DefinedDataKind,
DefinedGlobalKind, DefinedGlobalKind,
DefinedEventKind,
SectionKind, SectionKind,
UndefinedFunctionKind, UndefinedFunctionKind,
UndefinedDataKind, UndefinedDataKind,
@ -47,7 +49,8 @@ public:
bool isDefined() const { bool isDefined() const {
return SymbolKind == DefinedFunctionKind || SymbolKind == DefinedDataKind || return SymbolKind == DefinedFunctionKind || SymbolKind == DefinedDataKind ||
SymbolKind == DefinedGlobalKind || SymbolKind == SectionKind; SymbolKind == DefinedGlobalKind || SymbolKind == DefinedEventKind ||
SymbolKind == SectionKind;
} }
bool isUndefined() const { bool isUndefined() const {
@ -121,12 +124,12 @@ public:
void setFunctionIndex(uint32_t Index); void setFunctionIndex(uint32_t Index);
bool hasFunctionIndex() const; bool hasFunctionIndex() const;
const WasmSignature *FunctionType; const WasmSignature *Signature;
protected: protected:
FunctionSymbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F, FunctionSymbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F,
const WasmSignature *Type) const WasmSignature *Sig)
: Symbol(Name, K, Flags, F), FunctionType(Type) {} : Symbol(Name, K, Flags, F), Signature(Sig) {}
uint32_t TableIndex = INVALID_INDEX; uint32_t TableIndex = INVALID_INDEX;
uint32_t FunctionIndex = INVALID_INDEX; uint32_t FunctionIndex = INVALID_INDEX;
@ -267,6 +270,50 @@ public:
} }
}; };
// Wasm events are features that suspend the current execution and transfer the
// control flow to a corresponding handler. Currently the only supported event
// kind is exceptions.
//
// Event tags are values to distinguish different events. For exceptions, they
// can be used to distinguish different language's exceptions, i.e., all C++
// exceptions have the same tag. Wasm can generate code capable of doing
// different handling actions based on the tag of caught exceptions.
//
// A single EventSymbol object represents a single tag. C++ exception event
// symbol is a weak symbol generated in every object file in which exceptions
// are used, and has name '__cpp_exception' for linking.
class EventSymbol : public Symbol {
public:
static bool classof(const Symbol *S) { return S->kind() == DefinedEventKind; }
const WasmEventType *getEventType() const { return EventType; }
// Get/set the event index
uint32_t getEventIndex() const;
void setEventIndex(uint32_t Index);
bool hasEventIndex() const;
const WasmSignature *Signature;
protected:
EventSymbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F,
const WasmEventType *EventType, const WasmSignature *Sig)
: Symbol(Name, K, Flags, F), Signature(Sig), EventType(EventType) {}
const WasmEventType *EventType;
uint32_t EventIndex = INVALID_INDEX;
};
class DefinedEvent : public EventSymbol {
public:
DefinedEvent(StringRef Name, uint32_t Flags, InputFile *File,
InputEvent *Event);
static bool classof(const Symbol *S) { return S->kind() == DefinedEventKind; }
InputEvent *Event;
};
class LazySymbol : public Symbol { class LazySymbol : public Symbol {
public: public:
LazySymbol(StringRef Name, InputFile *File, LazySymbol(StringRef Name, InputFile *File,
@ -321,10 +368,11 @@ union SymbolUnion {
alignas(DefinedFunction) char A[sizeof(DefinedFunction)]; alignas(DefinedFunction) char A[sizeof(DefinedFunction)];
alignas(DefinedData) char B[sizeof(DefinedData)]; alignas(DefinedData) char B[sizeof(DefinedData)];
alignas(DefinedGlobal) char C[sizeof(DefinedGlobal)]; alignas(DefinedGlobal) char C[sizeof(DefinedGlobal)];
alignas(LazySymbol) char D[sizeof(LazySymbol)]; alignas(DefinedEvent) char D[sizeof(DefinedEvent)];
alignas(UndefinedFunction) char E[sizeof(UndefinedFunction)]; alignas(LazySymbol) char E[sizeof(LazySymbol)];
alignas(UndefinedData) char F[sizeof(UndefinedData)]; alignas(UndefinedFunction) char F[sizeof(UndefinedFunction)];
alignas(UndefinedGlobal) char G[sizeof(UndefinedGlobal)]; alignas(UndefinedData) char G[sizeof(UndefinedData)];
alignas(UndefinedGlobal) char H[sizeof(UndefinedGlobal)];
alignas(SectionSymbol) char I[sizeof(SectionSymbol)]; alignas(SectionSymbol) char I[sizeof(SectionSymbol)];
}; };

View File

@ -10,6 +10,7 @@
#include "Writer.h" #include "Writer.h"
#include "Config.h" #include "Config.h"
#include "InputChunks.h" #include "InputChunks.h"
#include "InputEvent.h"
#include "InputGlobal.h" #include "InputGlobal.h"
#include "OutputSections.h" #include "OutputSections.h"
#include "OutputSegment.h" #include "OutputSegment.h"
@ -80,6 +81,7 @@ private:
void createFunctionSection(); void createFunctionSection();
void createTableSection(); void createTableSection();
void createGlobalSection(); void createGlobalSection();
void createEventSection();
void createExportSection(); void createExportSection();
void createImportSection(); void createImportSection();
void createMemorySection(); void createMemorySection();
@ -111,10 +113,12 @@ private:
std::vector<const Symbol *> ImportedSymbols; std::vector<const Symbol *> ImportedSymbols;
unsigned NumImportedFunctions = 0; unsigned NumImportedFunctions = 0;
unsigned NumImportedGlobals = 0; unsigned NumImportedGlobals = 0;
unsigned NumImportedEvents = 0;
std::vector<WasmExport> Exports; std::vector<WasmExport> Exports;
std::vector<const DefinedData *> DefinedFakeGlobals; std::vector<const DefinedData *> DefinedFakeGlobals;
std::vector<InputGlobal *> InputGlobals; std::vector<InputGlobal *> InputGlobals;
std::vector<InputFunction *> InputFunctions; std::vector<InputFunction *> InputFunctions;
std::vector<InputEvent *> InputEvents;
std::vector<const FunctionSymbol *> IndirectFunctions; std::vector<const FunctionSymbol *> IndirectFunctions;
std::vector<const Symbol *> SymtabEntries; std::vector<const Symbol *> SymtabEntries;
std::vector<WasmInitEntry> InitFunctions; std::vector<WasmInitEntry> InitFunctions;
@ -182,11 +186,15 @@ void Writer::createImportSection() {
Import.Field = Sym->getName(); Import.Field = Sym->getName();
if (auto *FunctionSym = dyn_cast<FunctionSymbol>(Sym)) { if (auto *FunctionSym = dyn_cast<FunctionSymbol>(Sym)) {
Import.Kind = WASM_EXTERNAL_FUNCTION; Import.Kind = WASM_EXTERNAL_FUNCTION;
Import.SigIndex = lookupType(*FunctionSym->FunctionType); Import.SigIndex = lookupType(*FunctionSym->Signature);
} else { } else if (auto *GlobalSym = dyn_cast<GlobalSymbol>(Sym)) {
auto *GlobalSym = cast<GlobalSymbol>(Sym);
Import.Kind = WASM_EXTERNAL_GLOBAL; Import.Kind = WASM_EXTERNAL_GLOBAL;
Import.Global = *GlobalSym->getGlobalType(); Import.Global = *GlobalSym->getGlobalType();
} else {
auto *EventSym = cast<EventSymbol>(Sym);
Import.Kind = WASM_EXTERNAL_EVENT;
Import.Event.Attribute = EventSym->getEventType()->Attribute;
Import.Event.SigIndex = lookupType(*EventSym->Signature);
} }
writeImport(OS, Import); writeImport(OS, Import);
} }
@ -252,6 +260,31 @@ void Writer::createGlobalSection() {
} }
} }
// The event section contains a list of declared wasm events associated with the
// module. Currently the only supported event kind is exceptions. A single event
// entry represents a single event with an event tag. All C++ exceptions are
// represented by a single event. An event entry in this section contains
// information on what kind of event it is (e.g. exception) and the type of
// values contained in a single event object. (In wasm, an event can contain
// multiple values of primitive types. But for C++ exceptions, we just throw a
// pointer which is an i32 value (for wasm32 architecture), so the signature of
// C++ exception is (i32)->(void), because all event types are assumed to have
// void return type to share WasmSignature with functions.)
void Writer::createEventSection() {
unsigned NumEvents = InputEvents.size();
if (NumEvents == 0)
return;
SyntheticSection *Section = createSyntheticSection(WASM_SEC_EVENT);
raw_ostream &OS = Section->getStream();
writeUleb128(OS, NumEvents, "event count");
for (InputEvent *E : InputEvents) {
E->Event.Type.SigIndex = lookupType(E->Signature);
writeEvent(OS, E->Event);
}
}
void Writer::createTableSection() { void Writer::createTableSection() {
if (Config->ImportTable) if (Config->ImportTable)
return; return;
@ -478,6 +511,10 @@ void Writer::createLinkingSection() {
writeUleb128(Sub.OS, G->getGlobalIndex(), "index"); writeUleb128(Sub.OS, G->getGlobalIndex(), "index");
if (Sym->isDefined()) if (Sym->isDefined())
writeStr(Sub.OS, Sym->getName(), "sym name"); writeStr(Sub.OS, Sym->getName(), "sym name");
} else if (auto *E = dyn_cast<EventSymbol>(Sym)) {
writeUleb128(Sub.OS, E->getEventIndex(), "index");
if (Sym->isDefined())
writeStr(Sub.OS, Sym->getName(), "sym name");
} else if (isa<DataSymbol>(Sym)) { } else if (isa<DataSymbol>(Sym)) {
writeStr(Sub.OS, Sym->getName(), "sym name"); writeStr(Sub.OS, Sym->getName(), "sym name");
if (auto *DataSym = dyn_cast<DefinedData>(Sym)) { if (auto *DataSym = dyn_cast<DefinedData>(Sym)) {
@ -722,6 +759,7 @@ void Writer::createSections() {
createTableSection(); createTableSection();
createMemorySection(); createMemorySection();
createGlobalSection(); createGlobalSection();
createEventSection();
createExportSection(); createExportSection();
createElemSection(); createElemSection();
createCodeSection(); createCodeSection();
@ -760,8 +798,10 @@ void Writer::calculateImports() {
ImportedSymbols.emplace_back(Sym); ImportedSymbols.emplace_back(Sym);
if (auto *F = dyn_cast<FunctionSymbol>(Sym)) if (auto *F = dyn_cast<FunctionSymbol>(Sym))
F->setFunctionIndex(NumImportedFunctions++); F->setFunctionIndex(NumImportedFunctions++);
else if (auto *G = dyn_cast<GlobalSymbol>(Sym))
G->setGlobalIndex(NumImportedGlobals++);
else else
cast<GlobalSymbol>(Sym)->setGlobalIndex(NumImportedGlobals++); cast<EventSymbol>(Sym)->setEventIndex(NumImportedEvents++);
} }
} }
@ -797,6 +837,8 @@ void Writer::calculateExports() {
continue; continue;
} }
Export = {Name, WASM_EXTERNAL_GLOBAL, G->getGlobalIndex()}; Export = {Name, WASM_EXTERNAL_GLOBAL, G->getGlobalIndex()};
} else if (auto *E = dyn_cast<DefinedEvent>(Sym)) {
Export = {Name, WASM_EXTERNAL_EVENT, E->getEventIndex()};
} else { } else {
auto *D = cast<DefinedData>(Sym); auto *D = cast<DefinedData>(Sym);
DefinedFakeGlobals.emplace_back(D); DefinedFakeGlobals.emplace_back(D);
@ -874,6 +916,8 @@ void Writer::calculateTypes() {
// 1. Any signature used in the TYPE relocation // 1. Any signature used in the TYPE relocation
// 2. The signatures of all imported functions // 2. The signatures of all imported functions
// 3. The signatures of all defined functions // 3. The signatures of all defined functions
// 4. The signatures of all imported events
// 5. The signatures of all defined events
for (ObjFile *File : Symtab->ObjectFiles) { for (ObjFile *File : Symtab->ObjectFiles) {
ArrayRef<WasmSignature> Types = File->getWasmObj()->types(); ArrayRef<WasmSignature> Types = File->getWasmObj()->types();
@ -882,12 +926,18 @@ void Writer::calculateTypes() {
File->TypeMap[I] = registerType(Types[I]); File->TypeMap[I] = registerType(Types[I]);
} }
for (const Symbol *Sym : ImportedSymbols) for (const Symbol *Sym : ImportedSymbols) {
if (auto *F = dyn_cast<FunctionSymbol>(Sym)) if (auto *F = dyn_cast<FunctionSymbol>(Sym))
registerType(*F->FunctionType); registerType(*F->Signature);
else if (auto *E = dyn_cast<EventSymbol>(Sym))
registerType(*E->Signature);
}
for (const InputFunction *F : InputFunctions) for (const InputFunction *F : InputFunctions)
registerType(F->Signature); registerType(F->Signature);
for (const InputEvent *E : InputEvents)
registerType(E->Signature);
} }
void Writer::assignIndexes() { void Writer::assignIndexes() {
@ -959,6 +1009,22 @@ void Writer::assignIndexes() {
for (InputGlobal *Global : File->Globals) for (InputGlobal *Global : File->Globals)
AddDefinedGlobal(Global); AddDefinedGlobal(Global);
} }
assert(InputEvents.empty());
uint32_t EventIndex = NumImportedEvents;
auto AddDefinedEvent = [&](InputEvent *Event) {
if (Event->Live) {
LLVM_DEBUG(dbgs() << "AddDefinedEvent: " << EventIndex << "\n");
Event->setEventIndex(EventIndex++);
InputEvents.push_back(Event);
}
};
for (ObjFile *File : Symtab->ObjectFiles) {
LLVM_DEBUG(dbgs() << "Events: " << File->getName() << "\n");
for (InputEvent *Event : File->Events)
AddDefinedEvent(Event);
}
} }
static StringRef getOutputDataSegmentName(StringRef Name) { static StringRef getOutputDataSegmentName(StringRef Name) {
@ -1035,7 +1101,7 @@ void Writer::calculateInitFunctions() {
const WasmLinkingData &L = File->getWasmObj()->linkingData(); const WasmLinkingData &L = File->getWasmObj()->linkingData();
for (const WasmInitFunc &F : L.InitFunctions) { for (const WasmInitFunc &F : L.InitFunctions) {
FunctionSymbol *Sym = File->getFunctionSymbol(F.Symbol); FunctionSymbol *Sym = File->getFunctionSymbol(F.Symbol);
if (*Sym->FunctionType != WasmSignature{{}, {}}) if (*Sym->Signature != WasmSignature{{}, {}})
error("invalid signature for init func: " + toString(*Sym)); error("invalid signature for init func: " + toString(*Sym));
InitFunctions.emplace_back(WasmInitEntry{Sym, F.Priority}); InitFunctions.emplace_back(WasmInitEntry{Sym, F.Priority});
} }
@ -1080,8 +1146,10 @@ void Writer::run() {
if (errorHandler().Verbose) { if (errorHandler().Verbose) {
log("Defined Functions: " + Twine(InputFunctions.size())); log("Defined Functions: " + Twine(InputFunctions.size()));
log("Defined Globals : " + Twine(InputGlobals.size())); log("Defined Globals : " + Twine(InputGlobals.size()));
log("Defined Events : " + Twine(InputEvents.size()));
log("Function Imports : " + Twine(NumImportedFunctions)); log("Function Imports : " + Twine(NumImportedFunctions));
log("Global Imports : " + Twine(NumImportedGlobals)); log("Global Imports : " + Twine(NumImportedGlobals));
log("Event Imports : " + Twine(NumImportedEvents));
for (ObjFile *File : Symtab->ObjectFiles) for (ObjFile *File : Symtab->ObjectFiles)
File->dumpInfo(); File->dumpInfo();
} }

View File

@ -110,6 +110,15 @@ void wasm::writeGlobal(raw_ostream &OS, const WasmGlobal &Global) {
writeInitExpr(OS, Global.InitExpr); writeInitExpr(OS, Global.InitExpr);
} }
void wasm::writeEventType(raw_ostream &OS, const WasmEventType &Type) {
writeUleb128(OS, Type.Attribute, "event attribute");
writeUleb128(OS, Type.SigIndex, "sig index");
}
void wasm::writeEvent(raw_ostream &OS, const WasmEvent &Event) {
writeEventType(OS, Event.Type);
}
void wasm::writeTableType(raw_ostream &OS, const llvm::wasm::WasmTable &Type) { void wasm::writeTableType(raw_ostream &OS, const llvm::wasm::WasmTable &Type) {
writeU8(OS, WASM_TYPE_ANYFUNC, "table type"); writeU8(OS, WASM_TYPE_ANYFUNC, "table type");
writeLimits(OS, Type.Limits); writeLimits(OS, Type.Limits);
@ -126,6 +135,9 @@ void wasm::writeImport(raw_ostream &OS, const WasmImport &Import) {
case WASM_EXTERNAL_GLOBAL: case WASM_EXTERNAL_GLOBAL:
writeGlobalType(OS, Import.Global); writeGlobalType(OS, Import.Global);
break; break;
case WASM_EXTERNAL_EVENT:
writeEventType(OS, Import.Event);
break;
case WASM_EXTERNAL_MEMORY: case WASM_EXTERNAL_MEMORY:
writeLimits(OS, Import.Memory); writeLimits(OS, Import.Memory);
break; break;
@ -192,7 +204,13 @@ std::string lld::toString(const WasmSignature &Sig) {
return S.str(); return S.str();
} }
std::string lld::toString(const WasmGlobalType &Sig) { std::string lld::toString(const WasmGlobalType &Type) {
return (Sig.Mutable ? "var " : "const ") + return (Type.Mutable ? "var " : "const ") +
toString(static_cast<ValType>(Sig.Type)); toString(static_cast<ValType>(Type.Type));
}
std::string lld::toString(const WasmEventType &Type) {
if (Type.Attribute == WASM_EVENT_ATTRIBUTE_EXCEPTION)
return "exception";
return "unknown";
} }

View File

@ -45,6 +45,10 @@ void writeGlobalType(raw_ostream &OS, const llvm::wasm::WasmGlobalType &Type);
void writeGlobal(raw_ostream &OS, const llvm::wasm::WasmGlobal &Global); void writeGlobal(raw_ostream &OS, const llvm::wasm::WasmGlobal &Global);
void writeEventType(raw_ostream &OS, const llvm::wasm::WasmEventType &Type);
void writeEvent(raw_ostream &OS, const llvm::wasm::WasmEvent &Event);
void writeTableType(raw_ostream &OS, const llvm::wasm::WasmTable &Type); void writeTableType(raw_ostream &OS, const llvm::wasm::WasmTable &Type);
void writeImport(raw_ostream &OS, const llvm::wasm::WasmImport &Import); void writeImport(raw_ostream &OS, const llvm::wasm::WasmImport &Import);
@ -55,7 +59,8 @@ void writeExport(raw_ostream &OS, const llvm::wasm::WasmExport &Export);
std::string toString(llvm::wasm::ValType Type); std::string toString(llvm::wasm::ValType Type);
std::string toString(const llvm::wasm::WasmSignature &Sig); std::string toString(const llvm::wasm::WasmSignature &Sig);
std::string toString(const llvm::wasm::WasmGlobalType &Sig); std::string toString(const llvm::wasm::WasmGlobalType &Type);
std::string toString(const llvm::wasm::WasmEventType &Type);
} // namespace lld } // namespace lld