[llvm-branch-commits] [lld] 53e3b81 - [lld][WebAssembly] Add support for handling table symbols

Andy Wingo via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Jan 14 02:25:16 PST 2021


Author: Andy Wingo
Date: 2021-01-14T11:13:13+01:00
New Revision: 53e3b81faaf32a495189182e0e4d635cbe19c5dd

URL: https://github.com/llvm/llvm-project/commit/53e3b81faaf32a495189182e0e4d635cbe19c5dd
DIFF: https://github.com/llvm/llvm-project/commit/53e3b81faaf32a495189182e0e4d635cbe19c5dd.diff

LOG: [lld][WebAssembly] Add support for handling table symbols

This commit adds table symbol support in a partial way, while still
including some special cases for the __indirect_function_table symbol.
No change in tests.

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

Added: 
    lld/wasm/InputTable.h

Modified: 
    lld/include/lld/Common/LLVM.h
    lld/wasm/InputChunks.cpp
    lld/wasm/InputFiles.cpp
    lld/wasm/InputFiles.h
    lld/wasm/MarkLive.cpp
    lld/wasm/SymbolTable.cpp
    lld/wasm/SymbolTable.h
    lld/wasm/Symbols.cpp
    lld/wasm/Symbols.h
    lld/wasm/SyntheticSections.cpp
    lld/wasm/SyntheticSections.h
    lld/wasm/Writer.cpp
    lld/wasm/WriterUtils.cpp
    lld/wasm/WriterUtils.h
    llvm/include/llvm/BinaryFormat/Wasm.h
    llvm/include/llvm/Object/Wasm.h
    llvm/lib/Object/WasmObjectFile.cpp
    llvm/tools/llvm-readobj/WasmDumper.cpp

Removed: 
    


################################################################################
diff  --git a/lld/include/lld/Common/LLVM.h b/lld/include/lld/Common/LLVM.h
index 663944771aa9..f6eca27b02ff 100644
--- a/lld/include/lld/Common/LLVM.h
+++ b/lld/include/lld/Common/LLVM.h
@@ -49,8 +49,11 @@ struct WasmEventType;
 struct WasmFunction;
 struct WasmGlobal;
 struct WasmGlobalType;
+struct WasmLimits;
 struct WasmRelocation;
 struct WasmSignature;
+struct WasmTable;
+struct WasmTableType;
 } // namespace wasm
 } // namespace llvm
 
@@ -88,8 +91,11 @@ using llvm::wasm::WasmEventType;
 using llvm::wasm::WasmFunction;
 using llvm::wasm::WasmGlobal;
 using llvm::wasm::WasmGlobalType;
+using llvm::wasm::WasmLimits;
 using llvm::wasm::WasmRelocation;
 using llvm::wasm::WasmSignature;
+using llvm::wasm::WasmTable;
+using llvm::wasm::WasmTableType;
 } // end namespace lld.
 
 namespace std {

diff  --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp
index 52d19e6ddc10..d8c5f84c0487 100644
--- a/lld/wasm/InputChunks.cpp
+++ b/lld/wasm/InputChunks.cpp
@@ -69,6 +69,7 @@ void InputChunk::verifyRelocTargets() const {
     case R_WASM_GLOBAL_INDEX_LEB:
     case R_WASM_EVENT_INDEX_LEB:
     case R_WASM_MEMORY_ADDR_LEB:
+    case R_WASM_TABLE_NUMBER_LEB:
       existingValue = decodeULEB128(loc, &bytesRead);
       break;
     case R_WASM_MEMORY_ADDR_LEB64:
@@ -152,6 +153,7 @@ void InputChunk::writeTo(uint8_t *buf) const {
     case R_WASM_GLOBAL_INDEX_LEB:
     case R_WASM_EVENT_INDEX_LEB:
     case R_WASM_MEMORY_ADDR_LEB:
+    case R_WASM_TABLE_NUMBER_LEB:
       encodeULEB128(value, loc, 5);
       break;
     case R_WASM_MEMORY_ADDR_LEB64:
@@ -233,6 +235,7 @@ static unsigned writeCompressedReloc(uint8_t *buf, const WasmRelocation &rel,
   case R_WASM_EVENT_INDEX_LEB:
   case R_WASM_MEMORY_ADDR_LEB:
   case R_WASM_MEMORY_ADDR_LEB64:
+  case R_WASM_TABLE_NUMBER_LEB:
     return encodeULEB128(value, buf);
   case R_WASM_TABLE_INDEX_SLEB:
   case R_WASM_TABLE_INDEX_SLEB64:
@@ -251,6 +254,7 @@ static unsigned getRelocWidthPadded(const WasmRelocation &rel) {
   case R_WASM_GLOBAL_INDEX_LEB:
   case R_WASM_EVENT_INDEX_LEB:
   case R_WASM_MEMORY_ADDR_LEB:
+  case R_WASM_TABLE_NUMBER_LEB:
   case R_WASM_TABLE_INDEX_SLEB:
   case R_WASM_MEMORY_ADDR_SLEB:
     return 5;

diff  --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp
index eb37ae548b80..1d0f016f325a 100644
--- a/lld/wasm/InputFiles.cpp
+++ b/lld/wasm/InputFiles.cpp
@@ -11,6 +11,7 @@
 #include "InputChunks.h"
 #include "InputEvent.h"
 #include "InputGlobal.h"
+#include "InputTable.h"
 #include "OutputSegment.h"
 #include "SymbolTable.h"
 #include "lld/Common/ErrorHandler.h"
@@ -94,7 +95,8 @@ void ObjFile::dumpInfo() const {
       "\n              Symbols : " + Twine(symbols.size()) +
       "\n     Function Imports : " + Twine(wasmObj->getNumImportedFunctions()) +
       "\n       Global Imports : " + Twine(wasmObj->getNumImportedGlobals()) +
-      "\n        Event Imports : " + Twine(wasmObj->getNumImportedEvents()));
+      "\n        Event Imports : " + Twine(wasmObj->getNumImportedEvents()) +
+      "\n        Table Imports : " + Twine(wasmObj->getNumImportedTables()));
 }
 
 // Relocations contain either symbol or type indices.  This function takes a
@@ -188,7 +190,8 @@ uint64_t ObjFile::calcExpectedValue(const WasmRelocation &reloc) const {
   case R_WASM_FUNCTION_INDEX_LEB:
   case R_WASM_GLOBAL_INDEX_LEB:
   case R_WASM_GLOBAL_INDEX_I32:
-  case R_WASM_EVENT_INDEX_LEB: {
+  case R_WASM_EVENT_INDEX_LEB:
+  case R_WASM_TABLE_NUMBER_LEB: {
     const WasmSymbol &sym = wasmObj->syms()[reloc.Index];
     return sym.Info.ElementIndex;
   }
@@ -270,6 +273,8 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone)
   }
   case R_WASM_SECTION_OFFSET_I32:
     return getSectionSymbol(reloc.Index)->section->outputOffset + reloc.Addend;
+  case R_WASM_TABLE_NUMBER_LEB:
+    return getTableSymbol(reloc.Index)->getTableNumber();
   default:
     llvm_unreachable("unknown relocation type");
   }
@@ -405,6 +410,10 @@ void ObjFile::parse(bool ignoreComdats) {
   }
   setRelocs(functions, codeSection);
 
+  // Populate `Tables`.
+  for (const WasmTable &t : wasmObj->tables())
+    tables.emplace_back(make<InputTable>(t, this));
+
   // Populate `Globals`.
   for (const WasmGlobal &g : wasmObj->globals())
     globals.emplace_back(make<InputGlobal>(g, this));
@@ -449,6 +458,10 @@ EventSymbol *ObjFile::getEventSymbol(uint32_t index) const {
   return cast<EventSymbol>(symbols[index]);
 }
 
+TableSymbol *ObjFile::getTableSymbol(uint32_t index) const {
+  return cast<TableSymbol>(symbols[index]);
+}
+
 SectionSymbol *ObjFile::getSectionSymbol(uint32_t index) const {
   return cast<SectionSymbol>(symbols[index]);
 }
@@ -504,6 +517,13 @@ Symbol *ObjFile::createDefined(const WasmSymbol &sym) {
       return make<DefinedEvent>(name, flags, this, event);
     return symtab->addDefinedEvent(name, flags, this, event);
   }
+  case WASM_SYMBOL_TYPE_TABLE: {
+    InputTable *table =
+        tables[sym.Info.ElementIndex - wasmObj->getNumImportedTables()];
+    if (sym.isBindingLocal())
+      return make<DefinedTable>(name, flags, this, table);
+    return symtab->addDefinedTable(name, flags, this, table);
+  }
   }
   llvm_unreachable("unknown symbol kind");
 }
@@ -533,6 +553,14 @@ Symbol *ObjFile::createUndefined(const WasmSymbol &sym, bool isCalledDirectly) {
     return symtab->addUndefinedGlobal(name, sym.Info.ImportName,
                                       sym.Info.ImportModule, flags, this,
                                       sym.GlobalType);
+  case WASM_SYMBOL_TYPE_TABLE:
+    if (sym.isBindingLocal())
+      return make<UndefinedTable>(name, sym.Info.ImportName,
+                                  sym.Info.ImportModule, flags, this,
+                                  sym.TableType);
+    return symtab->addUndefinedTable(name, sym.Info.ImportName,
+                                     sym.Info.ImportModule, flags, this,
+                                     sym.TableType);
   case WASM_SYMBOL_TYPE_SECTION:
     llvm_unreachable("section symbols cannot be undefined");
   }

diff  --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h
index 18b4f8de9bb5..4243a4447a92 100644
--- a/lld/wasm/InputFiles.h
+++ b/lld/wasm/InputFiles.h
@@ -32,6 +32,7 @@ class InputFunction;
 class InputSegment;
 class InputGlobal;
 class InputEvent;
+class InputTable;
 class InputSection;
 
 // If --reproduce option is given, all input files are written
@@ -139,6 +140,7 @@ class ObjFile : public InputFile {
   std::vector<InputFunction *> functions;
   std::vector<InputGlobal *> globals;
   std::vector<InputEvent *> events;
+  std::vector<InputTable *> tables;
   std::vector<InputSection *> customSections;
   llvm::DenseMap<uint32_t, InputSection *> customSectionsByIndex;
 
@@ -148,6 +150,7 @@ class ObjFile : public InputFile {
   GlobalSymbol *getGlobalSymbol(uint32_t index) const;
   SectionSymbol *getSectionSymbol(uint32_t index) const;
   EventSymbol *getEventSymbol(uint32_t index) const;
+  TableSymbol *getTableSymbol(uint32_t index) const;
 
 private:
   Symbol *createDefined(const WasmSymbol &sym);

diff  --git a/lld/wasm/InputTable.h b/lld/wasm/InputTable.h
new file mode 100644
index 000000000000..066975dacee2
--- /dev/null
+++ b/lld/wasm/InputTable.h
@@ -0,0 +1,60 @@
+//===- InputTable.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_INPUT_TABLE_H
+#define LLD_WASM_INPUT_TABLE_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 Table within an input file. These are combined to
+// form the final TABLES section.
+class InputTable {
+public:
+  InputTable(const WasmTable &t, ObjFile *f)
+      : file(f), table(t), live(!config->gcSections) {}
+
+  StringRef getName() const { return table.SymbolName; }
+  const WasmTableType &getType() const { return table.Type; }
+
+  // Somewhat confusingly, we generally use the term "table index" to refer to
+  // the offset of a function in the well-known indirect function table.  We
+  // refer to 
diff erent tables instead by "table numbers".
+  uint32_t getTableNumber() const { return tableNumber.getValue(); }
+  bool hasTableNumber() const { return tableNumber.hasValue(); }
+  void setTableNumber(uint32_t n) {
+    assert(!hasTableNumber());
+    tableNumber = n;
+  }
+
+  void setLimits(const WasmLimits &limits) { table.Type.Limits = limits; }
+
+  ObjFile *file;
+  WasmTable table;
+
+  bool live = false;
+
+protected:
+  llvm::Optional<uint32_t> tableNumber;
+};
+
+} // namespace wasm
+
+inline std::string toString(const wasm::InputTable *t) {
+  return (toString(t->file) + ":(" + t->getName() + ")").str();
+}
+
+} // namespace lld
+
+#endif // LLD_WASM_INPUT_TABLE_H

diff  --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp
index 63c12545d554..c47e0953bfaa 100644
--- a/lld/wasm/MarkLive.cpp
+++ b/lld/wasm/MarkLive.cpp
@@ -23,6 +23,7 @@
 #include "InputChunks.h"
 #include "InputEvent.h"
 #include "InputGlobal.h"
+#include "InputTable.h"
 #include "SymbolTable.h"
 #include "Symbols.h"
 
@@ -166,6 +167,9 @@ void markLive() {
       for (InputEvent *e : obj->events)
         if (!e->live)
           message("removing unused section " + toString(e));
+      for (InputTable *t : obj->tables)
+        if (!t->live)
+          message("removing unused section " + toString(t));
     }
     for (InputChunk *c : symtab->syntheticFunctions)
       if (!c->live)

diff  --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index d8a070c3c2f2..c5af5cafa8bd 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -11,6 +11,7 @@
 #include "InputChunks.h"
 #include "InputEvent.h"
 #include "InputGlobal.h"
+#include "InputTable.h"
 #include "WriterUtils.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
@@ -191,6 +192,22 @@ static void checkEventType(const Symbol *existing, const InputFile *file,
          toString(*newSig) + " in " + toString(file));
 }
 
+static void checkTableType(const Symbol *existing, const InputFile *file,
+                           const WasmTableType *newType) {
+  if (!isa<TableSymbol>(existing)) {
+    reportTypeError(existing, file, WASM_SYMBOL_TYPE_TABLE);
+    return;
+  }
+
+  const WasmTableType *oldType = cast<TableSymbol>(existing)->getTableType();
+  if (newType->ElemType != oldType->ElemType) {
+    error("Table type mismatch: " + existing->getName() + "\n>>> defined as " +
+          toString(*oldType) + " in " + toString(existing->getFile()) +
+          "\n>>> defined as " + toString(*newType) + " in " + toString(file));
+  }
+  // FIXME: No assertions currently on the limits.
+}
+
 static void checkDataType(const Symbol *existing, const InputFile *file) {
   if (!isa<DataSymbol>(existing))
     reportTypeError(existing, file, WASM_SYMBOL_TYPE_DATA);
@@ -410,6 +427,30 @@ Symbol *SymbolTable::addDefinedEvent(StringRef name, uint32_t flags,
   return s;
 }
 
+Symbol *SymbolTable::addDefinedTable(StringRef name, uint32_t flags,
+                                     InputFile *file, InputTable *table) {
+  LLVM_DEBUG(dbgs() << "addDefinedTable:" << name << "\n");
+
+  Symbol *s;
+  bool wasInserted;
+  std::tie(s, wasInserted) = insert(name, file);
+
+  auto replaceSym = [&]() {
+    replaceSymbol<DefinedTable>(s, name, flags, file, table);
+  };
+
+  if (wasInserted || s->isLazy()) {
+    replaceSym();
+    return s;
+  }
+
+  checkTableType(s, file, &table->getType());
+
+  if (shouldReplace(s, file, flags))
+    replaceSym();
+  return s;
+}
+
 // This function get called when an undefined symbol is added, and there is
 // already an existing one in the symbols table.  In this case we check that
 // custom 'import-module' and 'import-field' symbol attributes agree.
@@ -553,6 +594,30 @@ Symbol *SymbolTable::addUndefinedGlobal(StringRef name,
   return s;
 }
 
+Symbol *SymbolTable::addUndefinedTable(StringRef name,
+                                       Optional<StringRef> importName,
+                                       Optional<StringRef> importModule,
+                                       uint32_t flags, InputFile *file,
+                                       const WasmTableType *type) {
+  LLVM_DEBUG(dbgs() << "addUndefinedTable: " << name << "\n");
+  assert(flags & WASM_SYMBOL_UNDEFINED);
+
+  Symbol *s;
+  bool wasInserted;
+  std::tie(s, wasInserted) = insert(name, file);
+  if (s->traced)
+    printTraceSymbolUndefined(name, file);
+
+  if (wasInserted)
+    replaceSymbol<UndefinedTable>(s, name, importName, importModule, flags,
+                                  file, type);
+  else if (auto *lazy = dyn_cast<LazySymbol>(s))
+    lazy->fetch();
+  else if (s->isDefined())
+    checkTableType(s, file, type);
+  return s;
+}
+
 void SymbolTable::addLazy(ArchiveFile *file, const Archive::Symbol *sym) {
   LLVM_DEBUG(dbgs() << "addLazy: " << sym->getName() << "\n");
   StringRef name = sym->getName();

diff  --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index d3a937a7a473..921a6edaa516 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -60,6 +60,8 @@ class SymbolTable {
                            InputGlobal *g);
   Symbol *addDefinedEvent(StringRef name, uint32_t flags, InputFile *file,
                           InputEvent *e);
+  Symbol *addDefinedTable(StringRef name, uint32_t flags, InputFile *file,
+                          InputTable *t);
 
   Symbol *addUndefinedFunction(StringRef name,
                                llvm::Optional<StringRef> importName,
@@ -73,6 +75,11 @@ class SymbolTable {
                              llvm::Optional<StringRef> importModule,
                              uint32_t flags, InputFile *file,
                              const WasmGlobalType *type);
+  Symbol *addUndefinedTable(StringRef name,
+                            llvm::Optional<StringRef> importName,
+                            llvm::Optional<StringRef> importModule,
+                            uint32_t flags, InputFile *file,
+                            const WasmTableType *type);
 
   void addLazy(ArchiveFile *f, const llvm::object::Archive::Symbol *sym);
 

diff  --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index aa3b6be8a9b8..a403a47dcdd5 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -12,9 +12,11 @@
 #include "InputEvent.h"
 #include "InputFiles.h"
 #include "InputGlobal.h"
+#include "InputTable.h"
 #include "OutputSections.h"
 #include "OutputSegment.h"
 #include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
 #include "lld/Common/Strings.h"
 
 #define DEBUG_TYPE "lld"
@@ -46,6 +48,8 @@ std::string toString(wasm::Symbol::Kind kind) {
     return "DefinedData";
   case wasm::Symbol::DefinedGlobalKind:
     return "DefinedGlobal";
+  case wasm::Symbol::DefinedTableKind:
+    return "DefinedTable";
   case wasm::Symbol::DefinedEventKind:
     return "DefinedEvent";
   case wasm::Symbol::UndefinedFunctionKind:
@@ -54,6 +58,8 @@ std::string toString(wasm::Symbol::Kind kind) {
     return "UndefinedData";
   case wasm::Symbol::UndefinedGlobalKind:
     return "UndefinedGlobal";
+  case wasm::Symbol::UndefinedTableKind:
+    return "UndefinedTable";
   case wasm::Symbol::LazyKind:
     return "LazyKind";
   case wasm::Symbol::SectionKind:
@@ -95,6 +101,8 @@ WasmSymbolType Symbol::getWasmType() const {
     return WASM_SYMBOL_TYPE_GLOBAL;
   if (isa<EventSymbol>(this))
     return WASM_SYMBOL_TYPE_EVENT;
+  if (isa<TableSymbol>(this))
+    return WASM_SYMBOL_TYPE_TABLE;
   if (isa<SectionSymbol>(this) || isa<OutputSectionSymbol>(this))
     return WASM_SYMBOL_TYPE_SECTION;
   llvm_unreachable("invalid symbol kind");
@@ -130,6 +138,8 @@ bool Symbol::isLive() const {
     return g->global->live;
   if (auto *e = dyn_cast<DefinedEvent>(this))
     return e->event->live;
+  if (auto *t = dyn_cast<DefinedTable>(this))
+    return t->table->live;
   if (InputChunk *c = getChunk())
     return c->live;
   return referenced;
@@ -143,6 +153,8 @@ void Symbol::markLive() {
     g->global->live = true;
   if (auto *e = dyn_cast<DefinedEvent>(this))
     e->event->live = true;
+  if (auto *t = dyn_cast<DefinedTable>(this))
+    t->table->live = true;
   if (InputChunk *c = getChunk())
     c->live = true;
   referenced = true;
@@ -339,6 +351,39 @@ DefinedEvent::DefinedEvent(StringRef name, uint32_t flags, InputFile *file,
                   event ? &event->signature : nullptr),
       event(event) {}
 
+void TableSymbol::setLimits(const WasmLimits &limits) {
+  if (auto *t = dyn_cast<DefinedTable>(this))
+    t->table->setLimits(limits);
+  auto *newType = make<WasmTableType>(*tableType);
+  newType->Limits = limits;
+  tableType = newType;
+}
+
+uint32_t TableSymbol::getTableNumber() const {
+  if (const auto *t = dyn_cast<DefinedTable>(this))
+    return t->table->getTableNumber();
+  assert(tableNumber != INVALID_INDEX);
+  return tableNumber;
+}
+
+void TableSymbol::setTableNumber(uint32_t number) {
+  LLVM_DEBUG(dbgs() << "setTableNumber " << name << " -> " << number << "\n");
+  assert(tableNumber == INVALID_INDEX);
+  tableNumber = number;
+}
+
+bool TableSymbol::hasTableNumber() const {
+  if (const auto *t = dyn_cast<DefinedTable>(this))
+    return t->table->hasTableNumber();
+  return tableNumber != INVALID_INDEX;
+}
+
+DefinedTable::DefinedTable(StringRef name, uint32_t flags, InputFile *file,
+                           InputTable *table)
+    : TableSymbol(name, DefinedTableKind, flags, file,
+                  table ? &table->getType() : nullptr),
+      table(table) {}
+
 const OutputSectionSymbol *SectionSymbol::getOutputSectionSymbol() const {
   assert(section->outputSec && section->outputSec->sectionSym);
   return section->outputSec->sectionSym;

diff  --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 4fa1a834db37..8434f82539b9 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -35,6 +35,7 @@ class InputFunction;
 class InputGlobal;
 class InputEvent;
 class InputSection;
+class InputTable;
 class OutputSection;
 
 #define INVALID_INDEX UINT32_MAX
@@ -47,11 +48,13 @@ class Symbol {
     DefinedDataKind,
     DefinedGlobalKind,
     DefinedEventKind,
+    DefinedTableKind,
     SectionKind,
     OutputSectionKind,
     UndefinedFunctionKind,
     UndefinedDataKind,
     UndefinedGlobalKind,
+    UndefinedTableKind,
     LazyKind,
   };
 
@@ -61,7 +64,9 @@ class Symbol {
 
   bool isUndefined() const {
     return symbolKind == UndefinedFunctionKind ||
-           symbolKind == UndefinedDataKind || symbolKind == UndefinedGlobalKind;
+           symbolKind == UndefinedDataKind ||
+           symbolKind == UndefinedGlobalKind ||
+           symbolKind == UndefinedTableKind;
   }
 
   bool isLazy() const { return symbolKind == LazyKind; }
@@ -359,6 +364,55 @@ class UndefinedGlobal : public GlobalSymbol {
   llvm::Optional<StringRef> importModule;
 };
 
+class TableSymbol : public Symbol {
+public:
+  static bool classof(const Symbol *s) {
+    return s->kind() == DefinedTableKind || s->kind() == UndefinedTableKind;
+  }
+
+  const WasmTableType *getTableType() const { return tableType; }
+  void setLimits(const WasmLimits &limits);
+
+  // Get/set the table number
+  uint32_t getTableNumber() const;
+  void setTableNumber(uint32_t number);
+  bool hasTableNumber() const;
+
+protected:
+  TableSymbol(StringRef name, Kind k, uint32_t flags, InputFile *f,
+              const WasmTableType *type)
+      : Symbol(name, k, flags, f), tableType(type) {}
+
+  const WasmTableType *tableType;
+  uint32_t tableNumber = INVALID_INDEX;
+};
+
+class DefinedTable : public TableSymbol {
+public:
+  DefinedTable(StringRef name, uint32_t flags, InputFile *file,
+               InputTable *table);
+
+  static bool classof(const Symbol *s) { return s->kind() == DefinedTableKind; }
+
+  InputTable *table;
+};
+
+class UndefinedTable : public TableSymbol {
+public:
+  UndefinedTable(StringRef name, llvm::Optional<StringRef> importName,
+                 llvm::Optional<StringRef> importModule, uint32_t flags,
+                 InputFile *file, const WasmTableType *type)
+      : TableSymbol(name, UndefinedTableKind, flags, file, type),
+        importName(importName), importModule(importModule) {}
+
+  static bool classof(const Symbol *s) {
+    return s->kind() == UndefinedTableKind;
+  }
+
+  llvm::Optional<StringRef> importName;
+  llvm::Optional<StringRef> importModule;
+};
+
 // 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.
@@ -524,11 +578,13 @@ union SymbolUnion {
   alignas(DefinedData) char b[sizeof(DefinedData)];
   alignas(DefinedGlobal) char c[sizeof(DefinedGlobal)];
   alignas(DefinedEvent) char d[sizeof(DefinedEvent)];
-  alignas(LazySymbol) char e[sizeof(LazySymbol)];
-  alignas(UndefinedFunction) char f[sizeof(UndefinedFunction)];
-  alignas(UndefinedData) char g[sizeof(UndefinedData)];
-  alignas(UndefinedGlobal) char h[sizeof(UndefinedGlobal)];
-  alignas(SectionSymbol) char i[sizeof(SectionSymbol)];
+  alignas(DefinedTable) char e[sizeof(DefinedTable)];
+  alignas(LazySymbol) char f[sizeof(LazySymbol)];
+  alignas(UndefinedFunction) char g[sizeof(UndefinedFunction)];
+  alignas(UndefinedData) char h[sizeof(UndefinedData)];
+  alignas(UndefinedGlobal) char i[sizeof(UndefinedGlobal)];
+  alignas(UndefinedTable) char j[sizeof(UndefinedTable)];
+  alignas(SectionSymbol) char k[sizeof(SectionSymbol)];
 };
 
 // It is important to keep the size of SymbolUnion small for performance and

diff  --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 768a265b3d09..967c2e240924 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -15,6 +15,7 @@
 #include "InputChunks.h"
 #include "InputEvent.h"
 #include "InputGlobal.h"
+#include "InputTable.h"
 #include "OutputSegment.h"
 #include "SymbolTable.h"
 #include "llvm/Support/Path.h"
@@ -91,6 +92,13 @@ void TypeSection::writeBody() {
     writeSig(bodyOutputStream, *sig);
 }
 
+ImportSection::ImportSection() : SyntheticSection(llvm::wasm::WASM_SEC_IMPORT) {
+  // FIXME: Remove when we treat __indirect_function_table as any other symbol.
+  if (config->importTable) {
+    numImportedTables++;
+  }
+}
+
 uint32_t ImportSection::getNumImports() const {
   assert(isSealed);
   uint32_t numImports = importedSymbols.size() + gotSymbols.size();
@@ -117,8 +125,10 @@ void ImportSection::addImport(Symbol *sym) {
     f->setFunctionIndex(numImportedFunctions++);
   else if (auto *g = dyn_cast<GlobalSymbol>(sym))
     g->setGlobalIndex(numImportedGlobals++);
+  else if (auto *e = dyn_cast<EventSymbol>(sym))
+    e->setEventIndex(numImportedEvents++);
   else
-    cast<EventSymbol>(sym)->setEventIndex(numImportedEvents++);
+    cast<TableSymbol>(sym)->setTableNumber(numImportedTables++);
 }
 
 void ImportSection::writeBody() {
@@ -163,6 +173,9 @@ void ImportSection::writeBody() {
     } else if (auto *g = dyn_cast<UndefinedGlobal>(sym)) {
       import.Field = g->importName ? *g->importName : sym->getName();
       import.Module = g->importModule ? *g->importModule : defaultModule;
+    } else if (auto *t = dyn_cast<UndefinedTable>(sym)) {
+      import.Field = t->importName ? *t->importName : sym->getName();
+      import.Module = t->importModule ? *t->importModule : defaultModule;
     } else {
       import.Field = sym->getName();
       import.Module = defaultModule;
@@ -174,11 +187,14 @@ void ImportSection::writeBody() {
     } else if (auto *globalSym = dyn_cast<GlobalSymbol>(sym)) {
       import.Kind = WASM_EXTERNAL_GLOBAL;
       import.Global = *globalSym->getGlobalType();
-    } else {
-      auto *eventSym = cast<EventSymbol>(sym);
+    } else if (auto *eventSym = dyn_cast<EventSymbol>(sym)) {
       import.Kind = WASM_EXTERNAL_EVENT;
       import.Event.Attribute = eventSym->getEventType()->Attribute;
       import.Event.SigIndex = out.typeSec->lookupType(*eventSym->signature);
+    } else {
+      auto *tableSym = cast<TableSymbol>(sym);
+      import.Kind = WASM_EXTERNAL_TABLE;
+      import.Table = *tableSym->getTableType();
     }
     writeImport(os, import);
   }
@@ -214,16 +230,37 @@ void FunctionSection::addFunction(InputFunction *func) {
 }
 
 void TableSection::writeBody() {
-  uint32_t tableSize = config->tableBase + out.elemSec->numEntries();
+  bool hasIndirectFunctionTable = !config->importTable;
+
+  uint32_t tableCount = inputTables.size();
+  if (hasIndirectFunctionTable)
+    tableCount++;
 
   raw_ostream &os = bodyOutputStream;
-  writeUleb128(os, 1, "table count");
-  WasmLimits limits;
-  if (config->growableTable)
-    limits = {0, tableSize, 0};
-  else
-    limits = {WASM_LIMITS_FLAG_HAS_MAX, tableSize, tableSize};
-  writeTableType(os, WasmTableType{WASM_TYPE_FUNCREF, limits});
+
+  writeUleb128(os, tableCount, "table count");
+
+  if (hasIndirectFunctionTable) {
+    uint32_t tableSize = config->tableBase + out.elemSec->numEntries();
+    WasmLimits limits;
+    if (config->growableTable)
+      limits = {0, tableSize, 0};
+    else
+      limits = {WASM_LIMITS_FLAG_HAS_MAX, tableSize, tableSize};
+    writeTableType(os, WasmTableType{WASM_TYPE_FUNCREF, limits});
+  }
+
+  for (const InputTable *table : inputTables)
+    writeTableType(os, table->getType());
+}
+
+void TableSection::addTable(InputTable *table) {
+  if (!table->live)
+    return;
+  uint32_t tableNumber =
+      out.importSec->getNumImportedTables() + inputTables.size();
+  inputTables.push_back(table);
+  table->setTableNumber(tableNumber);
 }
 
 void MemorySection::writeBody() {
@@ -458,6 +495,10 @@ void LinkingSection::writeBody() {
         writeUleb128(sub.os, e->getEventIndex(), "index");
         if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0)
           writeStr(sub.os, sym->getName(), "sym name");
+      } else if (auto *t = dyn_cast<TableSymbol>(sym)) {
+        writeUleb128(sub.os, t->getTableNumber(), "table number");
+        if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0)
+          writeStr(sub.os, sym->getName(), "sym name");
       } else if (isa<DataSymbol>(sym)) {
         writeStr(sub.os, sym->getName(), "sym name");
         if (auto *dataSym = dyn_cast<DefinedData>(sym)) {

diff  --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h
index b4d833d212a6..7af931b4adc7 100644
--- a/lld/wasm/SyntheticSections.h
+++ b/lld/wasm/SyntheticSections.h
@@ -98,7 +98,7 @@ class TypeSection : public SyntheticSection {
 
 class ImportSection : public SyntheticSection {
 public:
-  ImportSection() : SyntheticSection(llvm::wasm::WASM_SEC_IMPORT) {}
+  ImportSection();
   bool isNeeded() const override { return getNumImports() > 0; }
   void writeBody() override;
   void addImport(Symbol *sym);
@@ -117,6 +117,10 @@ class ImportSection : public SyntheticSection {
     assert(isSealed);
     return numImportedEvents;
   }
+  uint32_t getNumImportedTables() const {
+    assert(isSealed);
+    return numImportedTables;
+  }
 
   std::vector<const Symbol *> importedSymbols;
   std::vector<const Symbol *> gotSymbols;
@@ -126,6 +130,7 @@ class ImportSection : public SyntheticSection {
   unsigned numImportedGlobals = 0;
   unsigned numImportedFunctions = 0;
   unsigned numImportedEvents = 0;
+  unsigned numImportedTables = 0;
 };
 
 class FunctionSection : public SyntheticSection {
@@ -146,18 +151,19 @@ class TableSection : public SyntheticSection {
   TableSection() : SyntheticSection(llvm::wasm::WASM_SEC_TABLE) {}
 
   bool isNeeded() const override {
-    // Always output a table section (or table import), even if there are no
-    // indirect calls.  There are two reasons for this:
-    //  1. For executables it is useful 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.
-    return !config->importTable;
+    // The linker currently always writes an indirect function table to the
+    // output, so unless the indirect function table is imported, we need a
+    // table section.  FIXME: Treat __indirect_function_table as a normal
+    // symbol, and only residualize a table section as needed.
+    if (!config->importTable)
+      return true;
+    return inputTables.size() > 0;
   }
 
   void writeBody() override;
+  void addTable(InputTable *table);
+
+  std::vector<InputTable *> inputTables;
 };
 
 class MemorySection : public SyntheticSection {

diff  --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 710404943df2..99cf48c08c52 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -11,6 +11,7 @@
 #include "InputChunks.h"
 #include "InputEvent.h"
 #include "InputGlobal.h"
+#include "InputTable.h"
 #include "MapFile.h"
 #include "OutputSections.h"
 #include "OutputSegment.h"
@@ -574,6 +575,8 @@ static bool shouldImport(Symbol *sym) {
     return g->importName.hasValue();
   if (auto *f = dyn_cast<UndefinedFunction>(sym))
     return f->importName.hasValue();
+  if (auto *t = dyn_cast<UndefinedTable>(sym))
+    return t->importName.hasValue();
 
   return false;
 }
@@ -636,10 +639,12 @@ void Writer::calculateExports() {
       export_ = {name, WASM_EXTERNAL_GLOBAL, g->getGlobalIndex()};
     } else if (auto *e = dyn_cast<DefinedEvent>(sym)) {
       export_ = {name, WASM_EXTERNAL_EVENT, e->getEventIndex()};
-    } else {
-      auto *d = cast<DefinedData>(sym);
+    } else if (auto *d = dyn_cast<DefinedData>(sym)) {
       out.globalSec->dataAddressGlobals.push_back(d);
       export_ = {name, WASM_EXTERNAL_GLOBAL, globalIndex++};
+    } else {
+      auto *t = cast<DefinedTable>(sym);
+      export_ = {name, WASM_EXTERNAL_TABLE, t->getTableNumber()};
     }
 
     LLVM_DEBUG(dbgs() << "Export: " << name << "\n");
@@ -781,6 +786,12 @@ void Writer::assignIndexes() {
       out.eventSec->addEvent(event);
   }
 
+  for (ObjFile *file : symtab->objectFiles) {
+    LLVM_DEBUG(dbgs() << "Tables: " << file->getName() << "\n");
+    for (InputTable *table : file->tables)
+      out.tableSec->addTable(table);
+  }
+
   out.globalSec->assignIndexes();
 }
 
@@ -1385,10 +1396,12 @@ void Writer::run() {
     log("Defined Functions: " + Twine(out.functionSec->inputFunctions.size()));
     log("Defined Globals  : " + Twine(out.globalSec->numGlobals()));
     log("Defined Events   : " + Twine(out.eventSec->inputEvents.size()));
+    log("Defined Tables   : " + Twine(out.tableSec->inputTables.size()));
     log("Function Imports : " +
         Twine(out.importSec->getNumImportedFunctions()));
     log("Global Imports   : " + Twine(out.importSec->getNumImportedGlobals()));
     log("Event Imports    : " + Twine(out.importSec->getNumImportedEvents()));
+    log("Table Imports    : " + Twine(out.importSec->getNumImportedTables()));
     for (ObjFile *file : symtab->objectFiles)
       file->dumpInfo();
   }

diff  --git a/lld/wasm/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp
index b53637f27f27..80845882b936 100644
--- a/lld/wasm/WriterUtils.cpp
+++ b/lld/wasm/WriterUtils.cpp
@@ -64,6 +64,21 @@ std::string toString(const WasmEventType &type) {
   return "unknown";
 }
 
+static std::string toString(const llvm::wasm::WasmLimits &limits) {
+  std::string ret;
+  ret += "flags=0x" + std::to_string(limits.Flags);
+  ret += "; initial=" + std::to_string(limits.Initial);
+  if (limits.Flags & WASM_LIMITS_FLAG_HAS_MAX)
+    ret += "; max=" + std::to_string(limits.Maximum);
+  return ret;
+}
+
+std::string toString(const WasmTableType &type) {
+  SmallString<128> ret("");
+  return "type=" + toString(static_cast<ValType>(type.ElemType)) +
+         "; limits=[" + toString(type.Limits) + "]";
+}
+
 namespace wasm {
 void debugWrite(uint64_t offset, const Twine &msg) {
   LLVM_DEBUG(dbgs() << format("  | %08lld: ", offset) << msg << "\n");
@@ -202,7 +217,7 @@ void writeEvent(raw_ostream &os, const WasmEvent &event) {
 }
 
 void writeTableType(raw_ostream &os, const WasmTableType &type) {
-  writeU8(os, WASM_TYPE_FUNCREF, "table type");
+  writeValueType(os, ValType(type.ElemType), "table type");
   writeLimits(os, type.Limits);
 }
 

diff  --git a/lld/wasm/WriterUtils.h b/lld/wasm/WriterUtils.h
index 5854c8c3eb19..7b0b78c976d2 100644
--- a/lld/wasm/WriterUtils.h
+++ b/lld/wasm/WriterUtils.h
@@ -69,6 +69,7 @@ std::string toString(llvm::wasm::ValType type);
 std::string toString(const llvm::wasm::WasmSignature &sig);
 std::string toString(const llvm::wasm::WasmGlobalType &type);
 std::string toString(const llvm::wasm::WasmEventType &type);
+std::string toString(const llvm::wasm::WasmTableType &type);
 
 } // namespace lld
 

diff  --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index 71022792841c..bdef959fafdc 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -192,8 +192,8 @@ struct WasmSymbolInfo {
   // For symbols to be exported from the final module
   Optional<StringRef> ExportName;
   union {
-    // For function or global symbols, the index in function or global index
-    // space.
+    // For function, table, or global symbols, the index in function, table, or
+    // global index space.
     uint32_t ElementIndex;
     // For a data symbols, the address of the data relative to segment.
     WasmDataReference DataRef;

diff  --git a/llvm/include/llvm/Object/Wasm.h b/llvm/include/llvm/Object/Wasm.h
index 089046d7d42f..f7cd2e622ae3 100644
--- a/llvm/include/llvm/Object/Wasm.h
+++ b/llvm/include/llvm/Object/Wasm.h
@@ -35,7 +35,8 @@ namespace object {
 class WasmSymbol {
 public:
   WasmSymbol(const wasm::WasmSymbolInfo &Info,
-             const wasm::WasmGlobalType *GlobalType, const uint8_t TableType,
+             const wasm::WasmGlobalType *GlobalType,
+             const wasm::WasmTableType *TableType,
              const wasm::WasmEventType *EventType,
              const wasm::WasmSignature *Signature)
       : Info(Info), GlobalType(GlobalType), TableType(TableType),
@@ -43,7 +44,7 @@ class WasmSymbol {
 
   const wasm::WasmSymbolInfo &Info;
   const wasm::WasmGlobalType *GlobalType;
-  const uint8_t TableType;
+  const wasm::WasmTableType *TableType;
   const wasm::WasmEventType *EventType;
   const wasm::WasmSignature *Signature;
 

diff  --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index c9b13e4afb4e..dac16d225bf5 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -527,7 +527,7 @@ Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
     wasm::WasmSymbolInfo Info;
     const wasm::WasmSignature *Signature = nullptr;
     const wasm::WasmGlobalType *GlobalType = nullptr;
-    uint8_t TableType = 0;
+    const wasm::WasmTableType *TableType = nullptr;
     const wasm::WasmEventType *EventType = nullptr;
 
     Info.Kind = readUint8(Ctx);
@@ -609,7 +609,7 @@ Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
         Info.Name = readString(Ctx);
         unsigned TableIndex = Info.ElementIndex - NumImportedTables;
         wasm::WasmTable &Table = Tables[TableIndex];
-        TableType = Table.Type.ElemType;
+        TableType = &Table.Type;
         if (Table.SymbolName.empty())
           Table.SymbolName = Info.Name;
       } else {
@@ -620,8 +620,7 @@ Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
         } else {
           Info.Name = Import.Field;
         }
-        TableType = Import.Table.ElemType;
-        // FIXME: Parse limits here too.
+        TableType = &Import.Table;
         if (!Import.Module.empty()) {
           Info.ImportModule = Import.Module;
         }

diff  --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp
index 488c6faa26b8..fb7134d20a85 100644
--- a/llvm/tools/llvm-readobj/WasmDumper.cpp
+++ b/llvm/tools/llvm-readobj/WasmDumper.cpp
@@ -24,7 +24,7 @@ static const EnumEntry<unsigned> WasmSymbolTypes[] = {
 #define ENUM_ENTRY(X)                                                          \
   { #X, wasm::WASM_SYMBOL_TYPE_##X }
     ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA),  ENUM_ENTRY(GLOBAL),
-    ENUM_ENTRY(SECTION),  ENUM_ENTRY(EVENT),
+    ENUM_ENTRY(SECTION),  ENUM_ENTRY(EVENT), ENUM_ENTRY(TABLE),
 #undef ENUM_ENTRY
 };
 


        


More information about the llvm-branch-commits mailing list