[llvm-branch-commits] [lld] 38dfce7 - [WebAssembly] Add support for table linking to wasm-ld

Andy Wingo via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Jan 15 00:28:52 PST 2021


Author: Andy Wingo
Date: 2021-01-15T09:21:52+01:00
New Revision: 38dfce706f796dc109ea495dd69a8cb4c8fa819d

URL: https://github.com/llvm/llvm-project/commit/38dfce706f796dc109ea495dd69a8cb4c8fa819d
DIFF: https://github.com/llvm/llvm-project/commit/38dfce706f796dc109ea495dd69a8cb4c8fa819d.diff

LOG: [WebAssembly] Add support for table linking to wasm-ld

This patch adds support to wasm-ld for linking multiple table references
together, in a manner similar to wasm globals. The indirect function
table is synthesized as needed.

To manage the transitional period in which the compiler doesn't yet
produce TABLE_NUMBER relocations and doesn't residualize table symbols,
the linker will detect object files which have table imports or
definitions, but no table symbols. In that case it will synthesize
symbols for the defined and imported tables.

As a change, relocatable objects are now written with table symbols,
which can cause symbol renumbering in some of the tests. If no object
file requires an indirect function table, none will be written to the
file. Note that for legacy ObjFile inputs, this test is conservative: as
we don't have relocs for each use of the indirecy function table, we
just assume that any incoming indirect function table should be
propagated to the output.

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

Added: 
    

Modified: 
    lld/test/wasm/alias.s
    lld/test/wasm/init-fini.ll
    lld/test/wasm/local-symbols.ll
    lld/test/wasm/locals-duplicate.test
    lld/test/wasm/pie.ll
    lld/test/wasm/section-symbol-relocs.yaml
    lld/test/wasm/shared.ll
    lld/test/wasm/signature-mismatch.ll
    lld/test/wasm/stack-pointer.ll
    lld/test/wasm/weak-alias.ll
    lld/wasm/Driver.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

Removed: 
    


################################################################################
diff  --git a/lld/test/wasm/alias.s b/lld/test/wasm/alias.s
index 6c99f69da8b8..b2ab45e98d95 100644
--- a/lld/test/wasm/alias.s
+++ b/lld/test/wasm/alias.s
@@ -22,14 +22,6 @@ _start:
 # CHECK-NEXT:         ReturnTypes:     []
 # CHECK-NEXT:   - Type:            FUNCTION
 # CHECK-NEXT:     FunctionTypes:   [ 0 ]
-# CHECK-NEXT:   - Type:            TABLE
-# CHECK-NEXT:     Tables:
-# CHECK-NEXT:       - Index:           0
-# CHECK-NEXT:         ElemType:        FUNCREF
-# CHECK-NEXT:         Limits:
-# CHECK-NEXT:           Flags:           [ HAS_MAX ]
-# CHECK-NEXT:           Initial:         0x1
-# CHECK-NEXT:           Maximum:         0x1
 # CHECK-NEXT:   - Type:            MEMORY
 # CHECK-NEXT:     Memories:
 # CHECK-NEXT:       - Initial:         0x2

diff  --git a/lld/test/wasm/init-fini.ll b/lld/test/wasm/init-fini.ll
index 1e7644bc6d35..5631d58d68e5 100644
--- a/lld/test/wasm/init-fini.ll
+++ b/lld/test/wasm/init-fini.ll
@@ -139,15 +139,15 @@ entry:
 ; RELOC-NEXT:  InitFunctions [
 ; RELOC-NEXT:    0 (priority=101)
 ; RELOC-NEXT:    1 (priority=101)
-; RELOC-NEXT:    14 (priority=101)
-; RELOC-NEXT:    10 (priority=101)
-; RELOC-NEXT:    20 (priority=101)
-; RELOC-NEXT:    10 (priority=202)
-; RELOC-NEXT:    22 (priority=202)
+; RELOC-NEXT:    15 (priority=101)
+; RELOC-NEXT:    11 (priority=101)
+; RELOC-NEXT:    21 (priority=101)
+; RELOC-NEXT:    11 (priority=202)
+; RELOC-NEXT:    23 (priority=202)
 ; RELOC-NEXT:    0 (priority=1001)
-; RELOC-NEXT:    16 (priority=1001)
-; RELOC-NEXT:    10 (priority=2002)
-; RELOC-NEXT:    24 (priority=2002)
+; RELOC-NEXT:    17 (priority=1001)
+; RELOC-NEXT:    11 (priority=2002)
+; RELOC-NEXT:    25 (priority=2002)
 ; RELOC-NEXT:    9 (priority=4000)
-; RELOC-NEXT:    18 (priority=4000)
+; RELOC-NEXT:    19 (priority=4000)
 ; RELOC-NEXT:  ]

diff  --git a/lld/test/wasm/local-symbols.ll b/lld/test/wasm/local-symbols.ll
index 13c200d648e9..216aced9cf07 100644
--- a/lld/test/wasm/local-symbols.ll
+++ b/lld/test/wasm/local-symbols.ll
@@ -35,14 +35,6 @@ entry:
 ; CHECK-NEXT:         ReturnTypes:     []
 ; CHECK-NEXT:   - Type:            FUNCTION
 ; CHECK-NEXT:     FunctionTypes:   [ 0, 1 ]
-; CHECK-NEXT:   - Type:            TABLE
-; CHECK-NEXT:     Tables:
-; CHECK-NEXT:       - Index:           0
-; CHECK-NEXT:         ElemType:        FUNCREF
-; CHECK-NEXT:         Limits:
-; CHECK-NEXT:           Flags:           [ HAS_MAX ]
-; CHECK-NEXT:           Initial:         0x1
-; CHECK-NEXT:           Maximum:         0x1
 ; CHECK-NEXT:   - Type:            MEMORY
 ; CHECK-NEXT:     Memories:
 ; CHECK-NEXT:       - Initial:         0x2

diff  --git a/lld/test/wasm/locals-duplicate.test b/lld/test/wasm/locals-duplicate.test
index 07abb7485381..cf9a148d4ab7 100644
--- a/lld/test/wasm/locals-duplicate.test
+++ b/lld/test/wasm/locals-duplicate.test
@@ -254,40 +254,40 @@
 ; RELOC-NEXT:   - Type:            CODE
 ; RELOC-NEXT:     Relocations:
 ; RELOC-NEXT:       - Type:            R_WASM_MEMORY_ADDR_SLEB
-; RELOC-NEXT:         Index:           18
+; RELOC-NEXT:         Index:           19
 ; RELOC-NEXT:         Offset:          0x13
 ; RELOC-NEXT:       - Type:            R_WASM_MEMORY_ADDR_SLEB
 ; RELOC-NEXT:         Index:           3
 ; RELOC-NEXT:         Offset:          0x1C
 ; RELOC-NEXT:       - Type:            R_WASM_MEMORY_ADDR_SLEB
-; RELOC-NEXT:         Index:           19
+; RELOC-NEXT:         Index:           20
 ; RELOC-NEXT:         Offset:          0x25
 ; RELOC-NEXT:       - Type:            R_WASM_TABLE_INDEX_SLEB
-; RELOC-NEXT:         Index:           16
+; RELOC-NEXT:         Index:           17
 ; RELOC-NEXT:         Offset:          0x2E
 ; RELOC-NEXT:       - Type:            R_WASM_TABLE_INDEX_SLEB
 ; RELOC-NEXT:         Index:           0
 ; RELOC-NEXT:         Offset:          0x37
 ; RELOC-NEXT:       - Type:            R_WASM_TABLE_INDEX_SLEB
-; RELOC-NEXT:         Index:           17
+; RELOC-NEXT:         Index:           18
 ; RELOC-NEXT:         Offset:          0x40
 ; RELOC-NEXT:       - Type:            R_WASM_MEMORY_ADDR_SLEB
-; RELOC-NEXT:         Index:           10
+; RELOC-NEXT:         Index:           11
 ; RELOC-NEXT:         Offset:          0x58
 ; RELOC-NEXT:       - Type:            R_WASM_MEMORY_ADDR_SLEB
-; RELOC-NEXT:         Index:           22
+; RELOC-NEXT:         Index:           23
 ; RELOC-NEXT:         Offset:          0x61
 ; RELOC-NEXT:       - Type:            R_WASM_MEMORY_ADDR_SLEB
-; RELOC-NEXT:         Index:           23
+; RELOC-NEXT:         Index:           24
 ; RELOC-NEXT:         Offset:          0x6A
 ; RELOC-NEXT:       - Type:            R_WASM_TABLE_INDEX_SLEB
-; RELOC-NEXT:         Index:           8
+; RELOC-NEXT:         Index:           9
 ; RELOC-NEXT:         Offset:          0x73
 ; RELOC-NEXT:       - Type:            R_WASM_TABLE_INDEX_SLEB
-; RELOC-NEXT:         Index:           20
+; RELOC-NEXT:         Index:           21
 ; RELOC-NEXT:         Offset:          0x7C
 ; RELOC-NEXT:       - Type:            R_WASM_TABLE_INDEX_SLEB
-; RELOC-NEXT:         Index:           21
+; RELOC-NEXT:         Index:           22
 ; RELOC-NEXT:         Offset:          0x85
 ; RELOC-NEXT:     Functions:
 ; RELOC-NEXT:       - Index:           0
@@ -410,87 +410,92 @@
 ; RELOC-NEXT:         Flags:           [  ]
 ; RELOC-NEXT:         Function:        8
 ; RELOC-NEXT:       - Index:           8
+; RELOC-NEXT:         Kind:            TABLE
+; RELOC-NEXT:         Name:            __indirect_function_table
+; RELOC-NEXT:         Flags:           [ VISIBILITY_HIDDEN ]
+; RELOC-NEXT:         Table:           0
+; RELOC-NEXT:       - Index:           9
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            colliding_func1
 ; RELOC-NEXT:         Flags:           [  ]
 ; RELOC-NEXT:         Function:        9
-; RELOC-NEXT:       - Index:           9
+; RELOC-NEXT:       - Index:           10
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            get_global1B
 ; RELOC-NEXT:         Flags:           [  ]
 ; RELOC-NEXT:         Function:        12
-; RELOC-NEXT:       - Index:           10
+; RELOC-NEXT:       - Index:           11
 ; RELOC-NEXT:         Kind:            DATA
 ; RELOC-NEXT:         Name:            colliding_global1
 ; RELOC-NEXT:         Flags:           [  ]
 ; RELOC-NEXT:         Segment:         0
 ; RELOC-NEXT:         Offset:          4
 ; RELOC-NEXT:         Size:            4
-; RELOC-NEXT:       - Index:           11
+; RELOC-NEXT:       - Index:           12
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            get_global2B
 ; RELOC-NEXT:         Flags:           [  ]
 ; RELOC-NEXT:         Function:        13
-; RELOC-NEXT:       - Index:           12
+; RELOC-NEXT:       - Index:           13
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            get_global3B
 ; RELOC-NEXT:         Flags:           [  ]
 ; RELOC-NEXT:         Function:        14
-; RELOC-NEXT:       - Index:           13
+; RELOC-NEXT:       - Index:           14
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            get_func1B
 ; RELOC-NEXT:         Flags:           [  ]
 ; RELOC-NEXT:         Function:        15
-; RELOC-NEXT:       - Index:           14
+; RELOC-NEXT:       - Index:           15
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            get_func2B
 ; RELOC-NEXT:         Flags:           [  ]
 ; RELOC-NEXT:         Function:        16
-; RELOC-NEXT:       - Index:           15
+; RELOC-NEXT:       - Index:           16
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            get_func3B
 ; RELOC-NEXT:         Flags:           [  ]
 ; RELOC-NEXT:         Function:        17
-; RELOC-NEXT:       - Index:           16
+; RELOC-NEXT:       - Index:           17
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            colliding_func1
 ; RELOC-NEXT:         Flags:           [ BINDING_LOCAL ]
 ; RELOC-NEXT:         Function:        0
-; RELOC-NEXT:       - Index:           17
+; RELOC-NEXT:       - Index:           18
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            colliding_func3
 ; RELOC-NEXT:         Flags:           [ BINDING_LOCAL ]
 ; RELOC-NEXT:         Function:        2
-; RELOC-NEXT:       - Index:           18
+; RELOC-NEXT:       - Index:           19
 ; RELOC-NEXT:         Kind:            DATA
 ; RELOC-NEXT:         Name:            colliding_global1
 ; RELOC-NEXT:         Flags:           [ BINDING_LOCAL ]
 ; RELOC-NEXT:         Segment:         0
 ; RELOC-NEXT:         Size:            4
-; RELOC-NEXT:       - Index:           19
+; RELOC-NEXT:       - Index:           20
 ; RELOC-NEXT:         Kind:            DATA
 ; RELOC-NEXT:         Name:            colliding_global3
 ; RELOC-NEXT:         Flags:           [ BINDING_LOCAL ]
 ; RELOC-NEXT:         Segment:         2
 ; RELOC-NEXT:         Size:            4
-; RELOC-NEXT:       - Index:           20
+; RELOC-NEXT:       - Index:           21
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            colliding_func2
 ; RELOC-NEXT:         Flags:           [ BINDING_LOCAL ]
 ; RELOC-NEXT:         Function:        10
-; RELOC-NEXT:       - Index:           21
+; RELOC-NEXT:       - Index:           22
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            colliding_func3
 ; RELOC-NEXT:         Flags:           [ BINDING_LOCAL ]
 ; RELOC-NEXT:         Function:        11
-; RELOC-NEXT:       - Index:           22
+; RELOC-NEXT:       - Index:           23
 ; RELOC-NEXT:         Kind:            DATA
 ; RELOC-NEXT:         Name:            colliding_global2
 ; RELOC-NEXT:         Flags:           [ BINDING_LOCAL ]
 ; RELOC-NEXT:         Segment:         1
 ; RELOC-NEXT:         Offset:          4
 ; RELOC-NEXT:         Size:            4
-; RELOC-NEXT:       - Index:           23
+; RELOC-NEXT:       - Index:           24
 ; RELOC-NEXT:         Kind:            DATA
 ; RELOC-NEXT:         Name:            colliding_global3
 ; RELOC-NEXT:         Flags:           [ BINDING_LOCAL ]

diff  --git a/lld/test/wasm/pie.ll b/lld/test/wasm/pie.ll
index 84555a08f19d..1aca4df3a594 100644
--- a/lld/test/wasm/pie.ll
+++ b/lld/test/wasm/pie.ll
@@ -41,14 +41,6 @@ define void @_start() {
 ; CHECK:        - Type:            IMPORT
 ; CHECK-NEXT:     Imports:
 ; CHECK-NEXT:       - Module:          env
-; CHECK-NEXT:         Field:           __indirect_function_table
-; CHECK-NEXT:         Kind:            TABLE
-; CHECK-NEXT:         Table:
-; CHECK-NEXT:           Index:           0
-; CHECK-NEXT:           ElemType:        FUNCREF
-; CHECK-NEXT:           Limits:
-; CHECK-NEXT:             Initial:         0x1
-; CHECK-NEXT:       - Module:          env
 ; CHECK-NEXT:         Field:           __stack_pointer
 ; CHECK-NEXT:         Kind:            GLOBAL
 ; CHECK-NEXT:         GlobalType:      I32
@@ -63,6 +55,14 @@ define void @_start() {
 ; CHECK-NEXT:         Kind:            GLOBAL
 ; CHECK-NEXT:         GlobalType:      I32
 ; CHECK-NEXT:         GlobalMutable:   false
+; CHECK-NEXT:       - Module:          env
+; CHECK-NEXT:         Field:           __indirect_function_table
+; CHECK-NEXT:         Kind:            TABLE
+; CHECK-NEXT:         Table:
+; CHECK-NEXT:           Index:           0
+; CHECK-NEXT:           ElemType:        FUNCREF
+; CHECK-NEXT:           Limits:
+; CHECK-NEXT:             Initial:         0x1
 
 ; CHECK:        - Type:            START
 ; CHECK-NEXT:     StartFunction:   2

diff  --git a/lld/test/wasm/section-symbol-relocs.yaml b/lld/test/wasm/section-symbol-relocs.yaml
index 3248ed62521a..34126cbf1252 100644
--- a/lld/test/wasm/section-symbol-relocs.yaml
+++ b/lld/test/wasm/section-symbol-relocs.yaml
@@ -54,8 +54,8 @@ Sections:
 # RELOC-NEXT:     - Index:           0
 # RELOC-NEXT:       Kind:            SECTION
 # RELOC-NEXT:       Flags:           [ BINDING_LOCAL ]
-# RELOC-NEXT:       Section:         2
+# RELOC-NEXT:       Section:         1
 # RELOC-NEXT:     - Index:           1
 # RELOC-NEXT:       Kind:            SECTION
 # RELOC-NEXT:       Flags:           [ BINDING_LOCAL ]
-# RELOC-NEXT:       Section:         3
+# RELOC-NEXT:       Section:         2

diff  --git a/lld/test/wasm/shared.ll b/lld/test/wasm/shared.ll
index 98751adda489..61337fcc6a3a 100644
--- a/lld/test/wasm/shared.ll
+++ b/lld/test/wasm/shared.ll
@@ -69,14 +69,6 @@ declare void @func_external()
 ; CHECK-NEXT:         Memory:
 ; CHECK-NEXT:           Initial:       0x1
 ; CHECK-NEXT:       - Module:          env
-; CHECK-NEXT:         Field:           __indirect_function_table
-; CHECK-NEXT:         Kind:            TABLE
-; CHECK-NEXT:         Table:
-; CHECK-NEXT:           Index:           0
-; CHECK-NEXT:           ElemType:        FUNCREF
-; CHECK-NEXT:           Limits:
-; CHECK-NEXT:             Initial:         0x2
-; CHECK-NEXT:       - Module:          env
 ; CHECK-NEXT:         Field:           __stack_pointer
 ; CHECK-NEXT:         Kind:            GLOBAL
 ; CHECK-NEXT:         GlobalType:      I32
@@ -95,6 +87,14 @@ declare void @func_external()
 ; CHECK-NEXT:         Field:           func_external
 ; CHECK-NEXT:         Kind:            FUNCTION
 ; CHECK-NEXT:         SigIndex:        1
+; CHECK-NEXT:       - Module:          env
+; CHECK-NEXT:         Field:           __indirect_function_table
+; CHECK-NEXT:         Kind:            TABLE
+; CHECK-NEXT:         Table:
+; CHECK-NEXT:           Index:           0
+; CHECK-NEXT:           ElemType:        FUNCREF
+; CHECK-NEXT:           Limits:
+; CHECK-NEXT:             Initial:         0x2
 ; CHECK-NEXT:       - Module:          GOT.mem
 ; CHECK-NEXT:         Field:           indirect_func
 ; CHECK-NEXT:         Kind:            GLOBAL

diff  --git a/lld/test/wasm/signature-mismatch.ll b/lld/test/wasm/signature-mismatch.ll
index d5f95b0073c0..65ce73910ffa 100644
--- a/lld/test/wasm/signature-mismatch.ll
+++ b/lld/test/wasm/signature-mismatch.ll
@@ -80,17 +80,22 @@ declare i32 @ret32(i32, i64, i32) local_unnamed_addr
 ; RELOC-NEXT:         Segment:         0
 ; RELOC-NEXT:         Size:            4
 ; RELOC-NEXT:       - Index:           3
+; RELOC-NEXT:         Kind:            TABLE
+; RELOC-NEXT:         Name:            __indirect_function_table
+; RELOC-NEXT:         Flags:           [ VISIBILITY_HIDDEN ]
+; RELOC-NEXT:         Table:           0
+; RELOC-NEXT:       - Index:           4
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            call_ret32
 ; RELOC-NEXT:         Flags:           [ ]
 ; RELOC-NEXT:         Function:        3
-; RELOC-NEXT:       - Index:           4
+; RELOC-NEXT:       - Index:           5
 ; RELOC-NEXT:         Kind:            DATA
 ; RELOC-NEXT:         Name:            ret32_address
 ; RELOC-NEXT:         Flags:           [  ]
 ; RELOC-NEXT:         Segment:         1
 ; RELOC-NEXT:         Size:            4
-; RELOC-NEXT:       - Index:           5
+; RELOC-NEXT:       - Index:           6
 ; RELOC-NEXT:         Kind:            FUNCTION
 ; RELOC-NEXT:         Name:            'signature_mismatch:ret32'
 ; RELOC-NEXT:         Flags:           [ BINDING_LOCAL ]

diff  --git a/lld/test/wasm/stack-pointer.ll b/lld/test/wasm/stack-pointer.ll
index 8efb2aae1ee5..11ab62fab1ae 100644
--- a/lld/test/wasm/stack-pointer.ll
+++ b/lld/test/wasm/stack-pointer.ll
@@ -30,14 +30,6 @@ entry:
 ; CHECK-NEXT:         GlobalMutable:   true
 ; CHECK-NEXT:   - Type:            FUNCTION
 ; CHECK-NEXT:     FunctionTypes:   [ 0 ]
-; CHECK-NEXT:   - Type:            TABLE
-; CHECK-NEXT:     Tables:
-; CHECK-NEXT:       - Index:           0
-; CHECK-NEXT:         ElemType:        FUNCREF
-; CHECK-NEXT:         Limits:
-; CHECK-NEXT:           Flags:           [ HAS_MAX ]
-; CHECK-NEXT:           Initial:         0x1
-; CHECK-NEXT:           Maximum:         0x1
 ; CHECK-NEXT:   - Type:            MEMORY
 ; CHECK-NEXT:     Memories:
 ; CHECK-NEXT:       - Initial:         0x0

diff  --git a/lld/test/wasm/weak-alias.ll b/lld/test/wasm/weak-alias.ll
index 9d903f73dd32..387501963d4b 100644
--- a/lld/test/wasm/weak-alias.ll
+++ b/lld/test/wasm/weak-alias.ll
@@ -276,6 +276,11 @@ entry:
 ; RELOC-NEXT:         Name:            call_direct_ptr
 ; RELOC-NEXT:         Flags:           [  ]
 ; RELOC-NEXT:         Function:        5
+; RELOC-NEXT:       - Index:           8
+; RELOC-NEXT:         Kind:            TABLE
+; RELOC-NEXT:         Name:            __indirect_function_table
+; RELOC-NEXT:         Flags:           [ VISIBILITY_HIDDEN ]
+; RELOC-NEXT:         Table:           0
 ; RELOC-NEXT:   - Type:            CUSTOM
 ; RELOC-NEXT:     Name:            name
 ; RELOC-NEXT:     FunctionNames:

diff  --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 84fdb77aea2c..d349a6abf5e6 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -10,6 +10,7 @@
 #include "Config.h"
 #include "InputChunks.h"
 #include "InputGlobal.h"
+#include "InputTable.h"
 #include "MarkLive.h"
 #include "SymbolTable.h"
 #include "Writer.h"
@@ -787,6 +788,58 @@ static void wrapSymbols(ArrayRef<WrappedSymbol> wrapped) {
     symtab->wrap(w.sym, w.real, w.wrap);
 }
 
+static TableSymbol *createDefinedIndirectFunctionTable(StringRef name) {
+  const uint32_t invalidIndex = -1;
+  WasmLimits limits{0, 0, 0}; // Set by the writer.
+  WasmTableType type{uint8_t(ValType::FUNCREF), limits};
+  WasmTable desc{invalidIndex, type, name};
+  InputTable *table = make<InputTable>(desc, nullptr);
+  uint32_t flags = config->exportTable ? 0 : WASM_SYMBOL_VISIBILITY_HIDDEN;
+  TableSymbol *sym = symtab->addSyntheticTable(name, flags, table);
+  sym->markLive();
+  sym->forceExport = config->exportTable;
+  return sym;
+}
+
+static TableSymbol *createUndefinedIndirectFunctionTable(StringRef name) {
+  WasmLimits limits{0, 0, 0}; // Set by the writer.
+  WasmTableType *type = make<WasmTableType>();
+  type->ElemType = uint8_t(ValType::FUNCREF);
+  type->Limits = limits;
+  StringRef module(defaultModule);
+  uint32_t flags = config->exportTable ? 0 : WASM_SYMBOL_VISIBILITY_HIDDEN;
+  flags |= WASM_SYMBOL_UNDEFINED;
+  Symbol *sym =
+      symtab->addUndefinedTable(name, name, module, flags, nullptr, type);
+  sym->markLive();
+  sym->forceExport = config->exportTable;
+  return cast<TableSymbol>(sym);
+}
+
+static TableSymbol *resolveIndirectFunctionTable() {
+  // Even though we may not need a table, if the user explicitly specified
+  // --import-table or --export-table, ensure a table is residualized.
+  if (config->importTable)
+    return createUndefinedIndirectFunctionTable(functionTableName);
+  if (config->exportTable)
+    return createDefinedIndirectFunctionTable(functionTableName);
+
+  // Otherwise, check to the symtab to find the indirect function table.
+  if (Symbol *sym = symtab->find(functionTableName)) {
+    if (sym->isLive()) {
+      if (auto *t = dyn_cast<TableSymbol>(sym)) {
+        return t->isDefined()
+                   ? t
+                   : createDefinedIndirectFunctionTable(functionTableName);
+      }
+    }
+  }
+
+  // An indirect function table will only be present in the symbol table if
+  // needed by a reloc; if we get here, we don't need one.
+  return nullptr;
+}
+
 void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   WasmOptTable parser;
   opt::InputArgList args = parser.parse(argsArr.slice(1));
@@ -976,6 +1029,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // Do size optimizations: garbage collection
   markLive();
 
+  // Provide the indirect funciton table if needed.
+  WasmSym::indirectFunctionTable = resolveIndirectFunctionTable();
+
+  if (errorCount())
+    return;
+
   // Write the result to the file.
   writeResult();
 }

diff  --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp
index 1d0f016f325a..1101cfb1a210 100644
--- a/lld/wasm/InputFiles.cpp
+++ b/lld/wasm/InputFiles.cpp
@@ -310,6 +310,71 @@ static void setRelocs(const std::vector<T *> &chunks,
   }
 }
 
+// Since LLVM 12, we expect that if an input file defines or uses a table, it
+// declares the tables using symbols and records each use with a relocation.
+// This way when the linker combines inputs, it can collate the tables used by
+// the inputs, assigning them distinct table numbers, and renumber all the uses
+// as appropriate.  At the same time, the linker has special logic to build the
+// indirect function table if it is needed.
+//
+// However, object files produced by LLVM 11 and earlier neither write table
+// symbols nor record relocations, and yet still use tables via call_indirect,
+// and via function pointer bitcasts.  We can detect these object files, as they
+// declare tables as imports or define them locally, but don't have table
+// symbols.  synthesizeTableSymbols serves as a shim when loading these older
+// input files, defining the missing symbols to allow the indirect function
+// table to be built.
+//
+// Table uses in these older files won't be relocated, as they have no
+// relocations.  In practice this isn't a problem, as these object files
+// typically just declare a single table named __indirect_function_table and
+// having table number 0, so relocation would be idempotent anyway.
+void ObjFile::synthesizeTableSymbols() {
+  uint32_t tableNumber = 0;
+  const WasmGlobalType *globalType = nullptr;
+  const WasmEventType *eventType = nullptr;
+  const WasmSignature *signature = nullptr;
+  if (wasmObj->getNumImportedTables()) {
+    for (const auto &import : wasmObj->imports()) {
+      if (import.Kind == WASM_EXTERNAL_TABLE) {
+        auto *info = make<WasmSymbolInfo>();
+        info->Name = import.Field;
+        info->Kind = WASM_SYMBOL_TYPE_TABLE;
+        info->ImportModule = import.Module;
+        info->ImportName = import.Field;
+        info->Flags = WASM_SYMBOL_UNDEFINED;
+        info->Flags |= WASM_SYMBOL_NO_STRIP;
+        info->ElementIndex = tableNumber++;
+        LLVM_DEBUG(dbgs() << "Synthesizing symbol for table import: "
+                          << info->Name << "\n");
+        auto *wasmSym = make<WasmSymbol>(*info, globalType, &import.Table,
+                                         eventType, signature);
+        symbols.push_back(createUndefined(*wasmSym, false));
+        // Because there are no TABLE_NUMBER relocs in this case, we can't
+        // compute accurate liveness info; instead, just mark the symbol as
+        // always live.
+        symbols.back()->markLive();
+      }
+    }
+  }
+  for (const auto &table : tables) {
+    auto *info = make<llvm::wasm::WasmSymbolInfo>();
+    // Empty name.
+    info->Kind = WASM_SYMBOL_TYPE_TABLE;
+    info->Flags = WASM_SYMBOL_BINDING_LOCAL;
+    info->Flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
+    info->Flags |= WASM_SYMBOL_NO_STRIP;
+    info->ElementIndex = tableNumber++;
+    LLVM_DEBUG(dbgs() << "Synthesizing symbol for table definition: "
+                      << info->Name << "\n");
+    auto *wasmSym = make<WasmSymbol>(*info, globalType, &table->getType(),
+                                     eventType, signature);
+    symbols.push_back(createDefined(*wasmSym));
+    // Mark live, for the same reasons as for imported tables.
+    symbols.back()->markLive();
+  }
+}
+
 void ObjFile::parse(bool ignoreComdats) {
   // Parse a memory buffer as a wasm file.
   LLVM_DEBUG(dbgs() << "Parsing object: " << toString(this) << "\n");
@@ -424,8 +489,11 @@ void ObjFile::parse(bool ignoreComdats) {
 
   // Populate `Symbols` based on the symbols in the object.
   symbols.reserve(wasmObj->getNumberOfSymbols());
+  bool haveTableSymbol = false;
   for (const SymbolRef &sym : wasmObj->symbols()) {
     const WasmSymbol &wasmSym = wasmObj->getWasmSymbol(sym.getRawDataRefImpl());
+    if (wasmSym.isTypeTable())
+      haveTableSymbol = true;
     if (wasmSym.isDefined()) {
       // createDefined may fail if the symbol is comdat excluded in which case
       // we fall back to creating an undefined symbol
@@ -437,6 +505,13 @@ void ObjFile::parse(bool ignoreComdats) {
     size_t idx = symbols.size();
     symbols.push_back(createUndefined(wasmSym, isCalledDirectly[idx]));
   }
+
+  // As a stopgap measure while implementing table support, if the object file
+  // has table definitions or imports but no table symbols, synthesize symbols
+  // for those tables.  Mark as NO_STRIP to ensure they reach the output file,
+  // even if there are no TABLE_NUMBER relocs against them.
+  if (!haveTableSymbol)
+    synthesizeTableSymbols();
 }
 
 bool ObjFile::isExcludedByComdat(InputChunk *chunk) const {

diff  --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h
index 4243a4447a92..8a471389e7d1 100644
--- a/lld/wasm/InputFiles.h
+++ b/lld/wasm/InputFiles.h
@@ -157,6 +157,7 @@ class ObjFile : public InputFile {
   Symbol *createUndefined(const WasmSymbol &sym, bool isCalledDirectly);
 
   bool isExcludedByComdat(InputChunk *chunk) const;
+  void synthesizeTableSymbols();
 
   std::unique_ptr<WasmObjectFile> wasmObj;
 };

diff  --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp
index c47e0953bfaa..f21774b0eec1 100644
--- a/lld/wasm/MarkLive.cpp
+++ b/lld/wasm/MarkLive.cpp
@@ -177,6 +177,9 @@ void markLive() {
     for (InputGlobal *g : symtab->syntheticGlobals)
       if (!g->live)
         message("removing unused section " + toString(g));
+    for (InputTable *t : symtab->syntheticTables)
+      if (!t->live)
+        message("removing unused section " + toString(t));
   }
 }
 

diff  --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index c5af5cafa8bd..d93b2a14dc5a 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -270,6 +270,18 @@ DefinedGlobal *SymbolTable::addOptionalGlobalSymbols(StringRef name,
   return replaceSymbol<DefinedGlobal>(s, name, flags, nullptr, global);
 }
 
+DefinedTable *SymbolTable::addSyntheticTable(StringRef name, uint32_t flags,
+                                             InputTable *table) {
+  LLVM_DEBUG(dbgs() << "addSyntheticTable: " << name << " -> " << table
+                    << "\n");
+  Symbol *s = find(name);
+  assert(!s || s->isUndefined());
+  if (!s)
+    s = insertName(name).first;
+  syntheticTables.emplace_back(table);
+  return replaceSymbol<DefinedTable>(s, name, flags, nullptr, table);
+}
+
 static bool shouldReplace(const Symbol *existing, InputFile *newFile,
                           uint32_t newFlags) {
   // If existing symbol is undefined, replace it.

diff  --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index 921a6edaa516..ce7219c3ba4f 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -93,6 +93,8 @@ class SymbolTable {
   DefinedData *addOptionalDataSymbol(StringRef name, uint64_t value = 0);
   DefinedGlobal *addOptionalGlobalSymbols(StringRef name, uint32_t flags,
                                           InputGlobal *global);
+  DefinedTable *addSyntheticTable(StringRef name, uint32_t flags,
+                                  InputTable *global);
 
   void handleSymbolVariants();
   void handleWeakUndefines();
@@ -103,6 +105,7 @@ class SymbolTable {
   std::vector<BitcodeFile *> bitcodeFiles;
   std::vector<InputFunction *> syntheticFunctions;
   std::vector<InputGlobal *> syntheticGlobals;
+  std::vector<InputTable *> syntheticTables;
 
 private:
   std::pair<Symbol *, bool> insert(StringRef name, const InputFile *file);

diff  --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index a403a47dcdd5..173ad3131509 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -91,6 +91,7 @@ UndefinedGlobal *WasmSym::tableBase;
 DefinedData *WasmSym::definedTableBase;
 UndefinedGlobal *WasmSym::memoryBase;
 DefinedData *WasmSym::definedMemoryBase;
+TableSymbol *WasmSym::indirectFunctionTable;
 
 WasmSymbolType Symbol::getWasmType() const {
   if (isa<FunctionSymbol>(this))

diff  --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 8434f82539b9..c8ee0e144881 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -568,6 +568,11 @@ struct WasmSym {
   // Used in PIC code for offset of global data
   static UndefinedGlobal *memoryBase;
   static DefinedData *definedMemoryBase;
+
+  // __indirect_function_table
+  // Used as an address space for function pointers, with each function that is
+  // used as a function pointer being allocated a slot.
+  static TableSymbol *indirectFunctionTable;
 };
 
 // A buffer class that is large enough to hold any Symbol-derived

diff  --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index 967c2e240924..6044601c4e6a 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -92,20 +92,11 @@ 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();
   if (config->importMemory)
     ++numImports;
-  if (config->importTable)
-    ++numImports;
   return numImports;
 }
 
@@ -154,17 +145,6 @@ void ImportSection::writeBody() {
     writeImport(os, import);
   }
 
-  if (config->importTable) {
-    uint32_t tableSize = config->tableBase + out.elemSec->numEntries();
-    WasmImport import;
-    import.Module = defaultModule;
-    import.Field = functionTableName;
-    import.Kind = WASM_EXTERNAL_TABLE;
-    import.Table.ElemType = WASM_TYPE_FUNCREF;
-    import.Table.Limits = {0, tableSize, 0};
-    writeImport(os, import);
-  }
-
   for (const Symbol *sym : importedSymbols) {
     WasmImport import;
     if (auto *f = dyn_cast<UndefinedFunction>(sym)) {
@@ -230,26 +210,9 @@ void FunctionSection::addFunction(InputFunction *func) {
 }
 
 void TableSection::writeBody() {
-  bool hasIndirectFunctionTable = !config->importTable;
-
-  uint32_t tableCount = inputTables.size();
-  if (hasIndirectFunctionTable)
-    tableCount++;
-
   raw_ostream &os = bodyOutputStream;
 
-  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});
-  }
-
+  writeUleb128(os, inputTables.size(), "table count");
   for (const InputTable *table : inputTables)
     writeTableType(os, table->getType());
 }

diff  --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h
index 7af931b4adc7..3795b38228d0 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();
+  ImportSection() : SyntheticSection(llvm::wasm::WASM_SEC_IMPORT) {}
   bool isNeeded() const override { return getNumImports() > 0; }
   void writeBody() override;
   void addImport(Symbol *sym);
@@ -150,16 +150,7 @@ class TableSection : public SyntheticSection {
 public:
   TableSection() : SyntheticSection(llvm::wasm::WASM_SEC_TABLE) {}
 
-  bool isNeeded() const override {
-    // 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;
-  }
-
+  bool isNeeded() const override { return inputTables.size() > 0; };
   void writeBody() override;
   void addTable(InputTable *table);
 

diff  --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 99cf48c08c52..763fce5a1f30 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -745,6 +745,19 @@ void Writer::createCommandExportWrappers() {
   }
 }
 
+static void finalizeIndirectFunctionTable() {
+  if (!WasmSym::indirectFunctionTable)
+    return;
+
+  uint32_t tableSize = config->tableBase + out.elemSec->numEntries();
+  WasmLimits limits = {0, tableSize, 0};
+  if (WasmSym::indirectFunctionTable->isDefined() && !config->growableTable) {
+    limits.Flags |= WASM_LIMITS_FLAG_HAS_MAX;
+    limits.Maximum = limits.Initial;
+  }
+  WasmSym::indirectFunctionTable->setLimits(limits);
+}
+
 static void scanRelocations() {
   for (ObjFile *file : symtab->objectFiles) {
     LLVM_DEBUG(dbgs() << "scanRelocations: " << file->getName() << "\n");
@@ -792,6 +805,9 @@ void Writer::assignIndexes() {
       out.tableSec->addTable(table);
   }
 
+  for (InputTable *table : symtab->syntheticTables)
+    out.tableSec->addTable(table);
+
   out.globalSec->assignIndexes();
 }
 
@@ -1341,6 +1357,8 @@ void Writer::run() {
 
   log("-- scanRelocations");
   scanRelocations();
+  log("-- finalizeIndirectFunctionTable");
+  finalizeIndirectFunctionTable();
   log("-- createSyntheticInitFunctions");
   createSyntheticInitFunctions();
   log("-- assignIndexes");


        


More information about the llvm-branch-commits mailing list