Index: test/wasm/Inputs/call-indirect.ll =================================================================== --- test/wasm/Inputs/call-indirect.ll +++ test/wasm/Inputs/call-indirect.ll @@ -1,18 +1,20 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown-wasm" -@indirect_bar = hidden local_unnamed_addr global i32 ()* @bar, align 4 +@indirect_bar = internal local_unnamed_addr global i32 ()* @bar, align 4 +@indirect_foo = internal local_unnamed_addr global i32 ()* @foo, align 4 -; Function Attrs: norecurse nounwind readnone -define i32 @bar() #0 { +declare i32 @foo() local_unnamed_addr + +define i32 @bar() { entry: ret i32 1 } -; Function Attrs: nounwind define void @call_bar_indirect() local_unnamed_addr #1 { entry: %0 = load i32 ()*, i32 ()** @indirect_bar, align 4 + %1 = load i32 ()*, i32 ()** @indirect_foo, align 4 %call = tail call i32 %0() #2 ret void } Index: test/wasm/call-indirect.ll =================================================================== --- test/wasm/call-indirect.ll +++ test/wasm/call-indirect.ll @@ -66,15 +66,15 @@ ; CHECK-NEXT: - Name: _start ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 3 +; CHECK-NEXT: - Name: foo +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: Index: 2 ; CHECK-NEXT: - Name: bar ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 0 ; CHECK-NEXT: - Name: call_bar_indirect ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 1 -; CHECK-NEXT: - Name: foo -; CHECK-NEXT: Kind: FUNCTION -; CHECK-NEXT: Index: 2 ; CHECK: - Type: ELEM ; CHECK-NEXT: Segments: ; CHECK-NEXT: - Offset: @@ -93,10 +93,10 @@ ; CHECK-NEXT: Offset: ; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Value: 1024 -; CHECK-NEXT: Content: '0100000002000000' +; CHECK-NEXT: Content: '010000000200000002000000' ; CHECK-NEXT: - Type: CUSTOM ; CHECK-NEXT: Name: linking -; CHECK-NEXT: DataSize: 8 +; CHECK-NEXT: DataSize: 12 ; CHECK-NEXT: - Type: CUSTOM ; CHECK-NEXT: Name: name ; CHECK-NEXT: FunctionNames: Index: wasm/InputFiles.h =================================================================== --- wasm/InputFiles.h +++ wasm/InputFiles.h @@ -103,7 +103,6 @@ size_t NumGlobalImports() const { return GlobalImports; } int32_t FunctionIndexOffset = 0; - int32_t TableIndexOffset = 0; const WasmSection *CodeSection = nullptr; std::vector CodeRelocations; int32_t CodeOffset = 0; @@ -113,6 +112,7 @@ std::vector Segments; const std::vector &getSymbols() { return Symbols; } + const std::vector &getTableSymbols() { return TableSymbols; } private: Symbol *createDefined(const WasmSymbol &Sym, @@ -120,17 +120,21 @@ Symbol *createUndefined(const WasmSymbol &Sym); void initializeSymbols(); InputSegment *getSegment(const WasmSymbol &WasmSym); - const Symbol *getFunctionSymbol(uint32_t Index) const; - const Symbol *getGlobalSymbol(uint32_t Index) const; + Symbol *getFunctionSymbol(uint32_t FunctionIndex) const; + Symbol *getTableSymbol(uint32_t TableIndex) const; + Symbol *getGlobalSymbol(uint32_t GlobalIndex) const; // List of all symbols referenced or defined by this file. std::vector Symbols; // List of all function symbols indexed by the function index space - std::vector FunctionSymbols; + std::vector FunctionSymbols; // List of all global symbols indexed by the global index space - std::vector GlobalSymbols; + std::vector GlobalSymbols; + + // List of all indirect symbols indexed by table index space. + std::vector TableSymbols; uint32_t GlobalImports = 0; uint32_t FunctionImports = 0; Index: wasm/InputFiles.cpp =================================================================== --- wasm/InputFiles.cpp +++ wasm/InputFiles.cpp @@ -46,7 +46,6 @@ log("reloc info for: " + getName() + "\n" + " FunctionIndexOffset : " + Twine(FunctionIndexOffset) + "\n" + " NumFunctionImports : " + Twine(NumFunctionImports()) + "\n" + - " TableIndexOffset : " + Twine(TableIndexOffset) + "\n" + " NumGlobalImports : " + Twine(NumGlobalImports()) + "\n"); } @@ -54,11 +53,15 @@ return Index < NumFunctionImports(); } -const Symbol *ObjFile::getFunctionSymbol(uint32_t Index) const { +Symbol *ObjFile::getFunctionSymbol(uint32_t Index) const { return FunctionSymbols[Index]; } -const Symbol *ObjFile::getGlobalSymbol(uint32_t Index) const { +Symbol *ObjFile::getTableSymbol(uint32_t Index) const { + return TableSymbols[Index]; +} + +Symbol *ObjFile::getGlobalSymbol(uint32_t Index) const { return GlobalSymbols[Index]; } @@ -79,7 +82,11 @@ } uint32_t ObjFile::relocateTableIndex(uint32_t Original) const { - return Original + TableIndexOffset; + const Symbol *Sym = getTableSymbol(Original); + uint32_t Index = Sym->getTableIndex(); + DEBUG(dbgs() << "relocateTableIndex: " << toString(*Sym) << ": " << Original + << " -> " << Index << "\n"); + return Index; } uint32_t ObjFile::relocateGlobalIndex(uint32_t Original) const { @@ -183,8 +190,23 @@ } } - DEBUG(dbgs() << "Functions: " << FunctionSymbols.size() << "\n"); - DEBUG(dbgs() << "Globals : " << GlobalSymbols.size() << "\n"); + uint32_t SegmentCount = WasmObj->elements().size(); + if (SegmentCount) { + if (SegmentCount > 1) + fatal(getName() + ": contains more than element segment"); + const WasmElemSegment &Segment = WasmObj->elements()[0]; + if (Segment.TableIndex != 0) + fatal(getName() + ": unsupported table index"); + if (Segment.Offset.Value.Int32 != 0) + fatal(getName() + ": unsupported segment offset"); + TableSymbols.reserve(Segment.Functions.size()); + for (uint64_t FunctionIndex : Segment.Functions) + TableSymbols.push_back(getFunctionSymbol(FunctionIndex)); + } + + DEBUG(dbgs() << "TableSymbols: " << TableSymbols.size() << "\n"); + DEBUG(dbgs() << "Functions : " << FunctionSymbols.size() << "\n"); + DEBUG(dbgs() << "Globals : " << GlobalSymbols.size() << "\n"); } Symbol *ObjFile::createUndefined(const WasmSymbol &Sym) { Index: wasm/Symbols.h =================================================================== --- wasm/Symbols.h +++ wasm/Symbols.h @@ -72,6 +72,7 @@ bool hasFunctionType() const { return FunctionType != nullptr; } const WasmSignature &getFunctionType() const; uint32_t getOutputIndex() const; + uint32_t getTableIndex() const { return TableIndex.getValue(); } // Returns the virtual address of a defined global. // Only works for globals, not functions. @@ -84,6 +85,12 @@ // space of the output object. void setOutputIndex(uint32_t Index); + // Returns true if a table index has been set for this symbol + bool hasTableIndex() const { return TableIndex.hasValue(); } + + // Set the table index of the symbol + void setTableIndex(uint32_t Index); + void setVirtualAddress(uint32_t VA); void update(Kind K, InputFile *F = nullptr, const WasmSymbol *Sym = nullptr, @@ -108,6 +115,7 @@ const WasmSymbol *Sym = nullptr; const InputSegment *Segment = nullptr; llvm::Optional OutputIndex; + llvm::Optional TableIndex; llvm::Optional VirtualAddress; const WasmSignature *FunctionType; }; Index: wasm/Symbols.cpp =================================================================== --- wasm/Symbols.cpp +++ wasm/Symbols.cpp @@ -66,10 +66,16 @@ void Symbol::setOutputIndex(uint32_t Index) { DEBUG(dbgs() << "setOutputIndex " << Name << " -> " << Index << "\n"); - assert(!hasOutputIndex()); + assert(!OutputIndex.hasValue()); OutputIndex = Index; } +void Symbol::setTableIndex(uint32_t Index) { + DEBUG(dbgs() << "setTableIndex " << Name << " -> " << Index << "\n"); + assert(!TableIndex.hasValue()); + TableIndex = Index; +} + void Symbol::update(Kind K, InputFile *F, const WasmSymbol *WasmSym, const InputSegment *Seg, const WasmSignature *Sig) { SymbolKind = K; Index: wasm/Writer.cpp =================================================================== --- wasm/Writer.cpp +++ wasm/Writer.cpp @@ -105,8 +105,6 @@ uint32_t NumFunctions = 0; uint32_t NumGlobals = 0; uint32_t NumMemoryPages = 0; - uint32_t NumTableElems = 0; - uint32_t NumElements = 0; uint32_t InitialTableOffset = 0; std::vector Types; @@ -114,6 +112,7 @@ std::vector FunctionImports; std::vector GlobalImports; std::vector DefinedGlobals; + std::vector IndirectFunctions; // Elements that are used to construct the final output std::string Header; @@ -236,14 +235,24 @@ } void Writer::createTableSection() { + // Alwasy output a table section, even if there are no indirect calls. + // There are two reasons for this: + // 1. For executables it is usefull to have an empty table slot at 0 + // which can be filled with a null function call handler. + // 2. If we don't do this, any program that contains a call_indirect but + // no address-taken function will fail at validation time since it is + // a validation error to include a call_indirect instruction if there + // is not table. + uint32_t TableSize = InitialTableOffset + IndirectFunctions.size(); + SyntheticSection *Section = createSyntheticSection(WASM_SEC_TABLE); raw_ostream &OS = Section->getStream(); writeUleb128(OS, 1, "table count"); writeSleb128(OS, WASM_TYPE_ANYFUNC, "table type"); writeUleb128(OS, WASM_LIMITS_FLAG_HAS_MAX, "table flags"); - writeUleb128(OS, NumTableElems, "table initial size"); - writeUleb128(OS, NumTableElems, "table max size"); + writeUleb128(OS, TableSize, "table initial size"); + writeUleb128(OS, TableSize, "table max size"); } void Writer::createExportSection() { @@ -309,7 +318,7 @@ void Writer::createStartSection() {} void Writer::createElemSection() { - if (!NumElements) + if (IndirectFunctions.empty()) return; SyntheticSection *Section = createSyntheticSection(WASM_SEC_ELEM); @@ -321,13 +330,14 @@ InitExpr.Opcode = WASM_OPCODE_I32_CONST; InitExpr.Value.Int32 = InitialTableOffset; writeInitExpr(OS, InitExpr); - writeUleb128(OS, NumElements, "elem count"); + writeUleb128(OS, IndirectFunctions.size(), "elem count"); - for (ObjFile *File : Symtab->ObjectFiles) - for (const WasmElemSegment &Segment : File->getWasmObj()->elements()) - for (uint64_t FunctionIndex : Segment.Functions) - writeUleb128(OS, File->relocateFunctionIndex(FunctionIndex), - "function index"); + uint32_t TableIndex = InitialTableOffset; + for (const Symbol *Sym : IndirectFunctions) { + assert(Sym->getTableIndex() == TableIndex); + writeUleb128(OS, Sym->getOutputIndex(), "function index"); + TableIndex++; + } } void Writer::createCodeSection() { @@ -526,8 +536,6 @@ } void Writer::calculateOffsets() { - NumTableElems = InitialTableOffset; - for (ObjFile *File : Symtab->ObjectFiles) { const WasmObjectFile *WasmFile = File->getWasmObj(); @@ -537,34 +545,8 @@ NumFunctions += WasmFile->functions().size(); // Memory - if (WasmFile->memories().size()) { - if (WasmFile->memories().size() > 1) { - fatal(File->getName() + ": contains more than one memory"); - } - } - - // Table - uint32_t TableCount = WasmFile->tables().size(); - if (TableCount) { - if (TableCount > 1) - fatal(File->getName() + ": contains more than one table"); - File->TableIndexOffset = NumTableElems; - NumTableElems += WasmFile->tables()[0].Limits.Initial; - } - - // Elem - uint32_t SegmentCount = WasmFile->elements().size(); - if (SegmentCount) { - if (SegmentCount > 1) - fatal(File->getName() + ": contains more than element segment"); - - const WasmElemSegment &Segment = WasmFile->elements()[0]; - if (Segment.TableIndex != 0) - fatal(File->getName() + ": unsupported table index"); - if (Segment.Offset.Value.Int32 != 0) - fatal(File->getName() + ": unsupported segment offset"); - NumElements += Segment.Functions.size(); - } + if (WasmFile->memories().size() > 1) + fatal(File->getName() + ": contains more than one memory"); } } @@ -611,8 +593,11 @@ if (Config->EmitRelocs) DefinedGlobals.reserve(Symtab->getSymbols().size()); + uint32_t TableIndex = InitialTableOffset; + for (ObjFile *File : Symtab->ObjectFiles) { DEBUG(dbgs() << "assignSymbolIndexes: " << File->getName() << "\n"); + for (Symbol *Sym : File->getSymbols()) { if (Sym->hasOutputIndex() || !Sym->isDefined()) continue; @@ -628,6 +613,13 @@ Sym->setOutputIndex(GlobalIndex++); } } + + for (Symbol *Sym : File->getTableSymbols()) { + if (!Sym->hasTableIndex()) { + Sym->setTableIndex(TableIndex++); + IndirectFunctions.emplace_back(Sym); + } + } } }