[llvm] 3a293cb - [WebAssembly] Fix PIC/GOT codegen for wasm64

Wouter van Oortmerssen via llvm-commits llvm-commits at lists.llvm.org
Thu May 20 10:01:52 PDT 2021


Author: Wouter van Oortmerssen
Date: 2021-05-20T09:59:31-07:00
New Revision: 3a293cbf13a2b6ddb2c1f6d27077ee922f32c404

URL: https://github.com/llvm/llvm-project/commit/3a293cbf13a2b6ddb2c1f6d27077ee922f32c404
DIFF: https://github.com/llvm/llvm-project/commit/3a293cbf13a2b6ddb2c1f6d27077ee922f32c404.diff

LOG: [WebAssembly] Fix PIC/GOT codegen for wasm64

__table_base is know 64-bit, since in LLVM it represents a function pointer offset
__table_base32 is a copy in wasm32 for use in elem init expr, since no truncation may be used there.
New reloc R_WASM_TABLE_INDEX_REL_SLEB64 added

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

Added: 
    lld/test/wasm/shared64.s
    llvm/test/MC/WebAssembly/reloc-pic64.s

Modified: 
    lld/test/wasm/data-layout.s
    lld/wasm/Driver.cpp
    lld/wasm/InputChunks.cpp
    lld/wasm/InputElement.h
    lld/wasm/InputFiles.cpp
    lld/wasm/OutputSections.cpp
    lld/wasm/Relocations.cpp
    lld/wasm/Symbols.cpp
    lld/wasm/Symbols.h
    lld/wasm/SyntheticSections.cpp
    lld/wasm/Writer.cpp
    llvm/include/llvm/BinaryFormat/WasmRelocs.def
    llvm/lib/MC/WasmObjectWriter.cpp
    llvm/lib/Object/WasmObjectFile.cpp
    llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp

Removed: 
    


################################################################################
diff  --git a/lld/test/wasm/data-layout.s b/lld/test/wasm/data-layout.s
index 8399e9f43f1a..2a447aad6221 100644
--- a/lld/test/wasm/data-layout.s
+++ b/lld/test/wasm/data-layout.s
@@ -1,12 +1,12 @@
 # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/hello.s -o %t.hello32.o
 # RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t32.o
 # RUN: wasm-ld -m wasm32 -no-gc-sections --export=__data_end --export=__heap_base --allow-undefined --no-entry -o %t32.wasm %t32.o %t.hello32.o
-# RUN: obj2yaml %t32.wasm | FileCheck --check-prefixes CHECK,CHK32 %s
+# RUN: obj2yaml %t32.wasm | FileCheck -DPTR=I32 %s
 #
 # RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown %p/Inputs/hello.s -o %t.hello64.o
 # RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown %s -o %t64.o
 # RUN: wasm-ld -m wasm64 -no-gc-sections --export=__data_end --export=__heap_base --allow-undefined --no-entry -o %t64.wasm %t64.o %t.hello64.o
-# RUN: obj2yaml %t64.wasm | FileCheck --check-prefixes CHECK,CHK64 %s
+# RUN: obj2yaml %t64.wasm | FileCheck --check-prefixes CHECK,CHK64 -DPTR=I64 %s
 
         .section .data.foo,"",@
         .globl  foo
@@ -59,24 +59,22 @@ local_struct_internal_ptr:
 # CHECK-NEXT:   - Type:            GLOBAL
 # CHECK-NEXT:     Globals:
 # CHECK-NEXT:       - Index:           0
-# CHK32-NEXT:         Type:            I32
-# CHK64-NEXT:         Type:            I64
+# CHECK-NEXT:         Type:            [[PTR]]
 # CHECK-NEXT:         Mutable:         true
 # CHECK-NEXT:         InitExpr:
-# CHK32-NEXT:           Opcode:          I32_CONST
-# CHK64-NEXT:           Opcode:          I64_CONST
+# CHECK-NEXT:           Opcode:          [[PTR]]_CONST
 # CHECK-NEXT:           Value:           66624
 # CHECK-NEXT:       - Index:           1
-# CHECK-NEXT:         Type:            I32
+# CHECK-NEXT:         Type:            [[PTR]]
 # CHECK-NEXT:         Mutable:         false
 # CHECK-NEXT:         InitExpr:
-# CHECK-NEXT:           Opcode:          I32_CONST
+# CHECK-NEXT:           Opcode:          [[PTR]]_CONST
 # CHECK-NEXT:           Value:           1080
 # CHECK-NEXT:       - Index:           2
-# CHECK-NEXT:         Type:            I32
+# CHECK-NEXT:         Type:            [[PTR]]
 # CHECK-NEXT:         Mutable:         false
 # CHECK-NEXT:         InitExpr:
-# CHECK-NEXT:           Opcode:          I32_CONST
+# CHECK-NEXT:           Opcode:          [[PTR]]_CONST
 # CHECK-NEXT:           Value:           66624
 
 # CHECK:        - Type:            DATA
@@ -84,15 +82,13 @@ local_struct_internal_ptr:
 # CHECK-NEXT:       - SectionOffset:   7
 # CHECK-NEXT:         InitFlags:       0
 # CHECK-NEXT:         Offset:
-# CHK32-NEXT:           Opcode:          I32_CONST
-# CHK64-NEXT:           Opcode:          I64_CONST
+# CHECK-NEXT:           Opcode:          [[PTR]]_CONST
 # CHECK-NEXT:           Value:           1024
 # CHECK-NEXT:         Content:         68656C6C6F0A00
 # CHECK-NEXT:       - SectionOffset:   20
 # CHECK-NEXT:         InitFlags:       0
 # CHECK-NEXT:         Offset:
-# CHK32-NEXT:           Opcode:          I32_CONST
-# CHK64-NEXT:           Opcode:          I64_CONST
+# CHECK-NEXT:           Opcode:          [[PTR]]_CONST
 # CHECK-NEXT:           Value:           1040
 
 

diff  --git a/lld/test/wasm/shared64.s b/lld/test/wasm/shared64.s
new file mode 100644
index 000000000000..0cbe623272c9
--- /dev/null
+++ b/lld/test/wasm/shared64.s
@@ -0,0 +1,238 @@
+# RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown -o %t.o %s
+# RUN: wasm-ld -mwasm64 --experimental-pic -shared -o %t.wasm %t.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+.section .data.data,"",@
+data:
+  .p2align 2
+  .int32 2
+  .size data, 4
+
+.section .data.indirect_func_external,"",@
+indirect_func_external:
+  .int32 func_external
+.size indirect_func_external, 4
+
+.section .data.indirect_func,"",@
+indirect_func:
+  .int32 foo
+  .size indirect_func, 4
+
+# Test data relocations
+
+.section .data.data_addr,"",@
+data_addr:
+  .int32 data
+  .size data_addr, 4
+
+# .. against external symbols
+
+.section .data.data_addr_external,"",@
+data_addr_external:
+  .int32 data_external
+  .size data_addr_external, 4
+
+# .. including addends
+
+.section .data.extern_struct_internal_ptr,"",@
+extern_struct_internal_ptr:
+  .int32 extern_struct + 4
+  .size extern_struct_internal_ptr, 4
+
+# Test use of __stack_pointer
+
+.section .text,"",@
+foo:
+  # %ptr = alloca i32
+  # %0 = load i32, i32* @data, align 4
+  # %1 = load i32 ()*, i32 ()** @indirect_func, align 4
+  # call i32 %1()
+  # ret i32 %0
+  .functype foo () -> (i32)
+  .local    i64, i32
+  global.get  __stack_pointer
+  i64.const 16
+  i64.sub
+  local.tee 0
+  global.set  __stack_pointer
+  global.get  __memory_base
+  i64.const data at MBREL
+  i64.add
+  i32.load  0
+  local.set 1
+  global.get  indirect_func at GOT
+  i32.load  0
+  call_indirect  () -> (i32)
+  drop
+  local.get 0
+  i64.const 16
+  i64.add
+  global.set  __stack_pointer
+  local.get 1
+  end_function
+
+get_func_address:
+  .functype get_func_address () -> (i32)
+  global.get func_external at GOT
+  end_function
+
+get_data_address:
+  .functype get_data_address () -> (i32)
+  global.get  data_external at GOT
+  end_function
+
+get_local_func_address:
+  # Verify that a function which is otherwise not address taken *is* added to
+  # the wasm table with referenced via R_WASM_TABLE_INDEX_REL_SLEB64
+  .functype get_local_func_address () -> (i64)
+  global.get  __table_base
+  i64.const get_func_address at TBREL
+  i64.add
+  end_function
+
+.globl foo
+.globl data
+.globl indirect_func
+.globl indirect_func_external
+.globl data_addr
+.globl data_addr_external
+.globl extern_struct_internal_ptr
+.globl get_data_address
+.globl get_func_address
+.globl get_local_func_address
+
+.hidden foo
+.hidden data
+.hidden get_data_address
+.hidden get_func_address
+
+# Without this linking will fail because we import __stack_pointer (a mutable
+# global).
+# TODO(sbc): We probably want a nicer way to specify target_features section
+# in assembly.
+.section .custom_section.target_features,"",@
+.int8 1
+.int8 43
+.int8 15
+.ascii "mutable-globals"
+
+.functype func_external () -> ()
+
+# Linker-synthesized globals
+.globaltype __stack_pointer, i64
+.globaltype	__table_base, i64, immutable
+.globaltype	__memory_base, i64, immutable
+
+# check for dylink section at start
+
+# CHECK:      Sections:
+# CHECK-NEXT:   - Type:            CUSTOM
+# CHECK-NEXT:     Name:            dylink
+# CHECK-NEXT:     MemorySize:      24
+# CHECK-NEXT:     MemoryAlignment: 2
+# CHECK-NEXT:     TableSize:       2
+# CHECK-NEXT:     TableAlignment:  0
+# CHECK-NEXT:     Needed:          []
+# CHECK-NEXT:   - Type:            TYPE
+
+# check for import of __table_base and __memory_base globals
+
+# CHECK:        - Type:            IMPORT
+# CHECK-NEXT:     Imports:
+# CHECK-NEXT:       - Module:          env
+# CHECK-NEXT:         Field:           memory
+# CHECK-NEXT:         Kind:            MEMORY
+# CHECK-NEXT:         Memory:
+# CHECK-NEXT:           Flags:         [ IS_64 ]
+# CHECK-NEXT:           Minimum:       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:             Minimum:         0x2
+# CHECK-NEXT:       - Module:          env
+# CHECK-NEXT:         Field:           __stack_pointer
+# CHECK-NEXT:         Kind:            GLOBAL
+# CHECK-NEXT:         GlobalType:      I64
+# CHECK-NEXT:         GlobalMutable:   true
+# CHECK-NEXT:       - Module:          env
+# CHECK-NEXT:         Field:           __memory_base
+# CHECK-NEXT:         Kind:            GLOBAL
+# CHECK-NEXT:         GlobalType:      I64
+# CHECK-NEXT:         GlobalMutable:   false
+# CHECK-NEXT:       - Module:          env
+# CHECK-NEXT:         Field:           __table_base
+# CHECK-NEXT:         Kind:            GLOBAL
+# CHECK-NEXT:         GlobalType:      I64
+# CHECK-NEXT:         GlobalMutable:   false
+# CHECK-NEXT:       - Module:          env
+# CHECK-NEXT:         Field:           __table_base32
+# CHECK-NEXT:         Kind:            GLOBAL
+# CHECK-NEXT:         GlobalType:      I32
+# CHECK-NEXT:         GlobalMutable:   false
+# CHECK-NEXT:       - Module:          env
+# CHECK-NEXT:         Field:           func_external
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         SigIndex:        1
+# CHECK-NEXT:       - Module:          GOT.mem
+# CHECK-NEXT:         Field:           indirect_func
+# CHECK-NEXT:         Kind:            GLOBAL
+# CHECK-NEXT:         GlobalType:      I32
+# CHECK-NEXT:         GlobalMutable:   true
+# CHECK-NEXT:       - Module:          GOT.func
+# CHECK-NEXT:         Field:           func_external
+# CHECK-NEXT:         Kind:            GLOBAL
+# CHECK-NEXT:         GlobalType:      I32
+# CHECK-NEXT:         GlobalMutable:   true
+# CHECK-NEXT:       - Module:          GOT.mem
+# CHECK-NEXT:         Field:           data_external
+# CHECK-NEXT:         Kind:            GLOBAL
+# CHECK-NEXT:         GlobalType:      I32
+# CHECK-NEXT:         GlobalMutable:   true
+# CHECK-NEXT:       - Module:          GOT.mem
+# CHECK-NEXT:         Field:           extern_struct
+# CHECK-NEXT:         Kind:            GLOBAL
+# CHECK-NEXT:         GlobalType:      I32
+# CHECK-NEXT:         GlobalMutable:   true
+# CHECK-NEXT:   - Type:            FUNCTION
+
+# CHECK:        - Type:            EXPORT
+# CHECK-NEXT:     Exports:
+# CHECK-NEXT:       - Name:            __wasm_call_ctors
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         Index:           1
+
+# check for elem segment initialized with __table_base global as offset
+
+# CHECK:        - Type:            ELEM
+# CHECK-NEXT:     Segments:
+# CHECK-NEXT:       - Offset:
+# CHECK-NEXT:           Opcode:          GLOBAL_GET
+# CHECK-NEXT:           Index:           3
+# CHECK-NEXT:         Functions:       [ 4, 3 ]
+
+# check the generated code in __wasm_call_ctors and __wasm_apply_data_relocs functions
+# TODO(sbc): Disassemble and verify instructions.
+
+# CHECK:        - Type:            CODE
+# CHECK-NEXT:     Functions:
+# CHECK-NEXT:       - Index:           1
+# CHECK-NEXT:         Locals:          []
+# CHECK-NEXT:         Body:            10020B
+# CHECK-NEXT:       - Index:           2
+# CHECK-NEXT:         Locals:          []
+# CHECK-NEXT:         Body:            230142047C2305360200230142087C230241016A3602002301420C7C230141006A360200230142107C2306360200230142147C230741046A3602000B
+
+# check the data segment initialized with __memory_base global as offset
+
+# CHECK:        - Type:            DATA
+# CHECK-NEXT:     Segments:
+# CHECK-NEXT:       - SectionOffset:   6
+# CHECK-NEXT:         InitFlags:       0
+# CHECK-NEXT:         Offset:
+# CHECK-NEXT:           Opcode:          GLOBAL_GET
+# CHECK-NEXT:           Index:           1
+# CHECK-NEXT:         Content:         '020000000000000001000000000000000000000000000000'

diff  --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 9b01a84f7812..61eeb0bd2522 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -584,15 +584,9 @@ createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
 
 static InputGlobal *createGlobal(StringRef name, bool isMutable) {
   llvm::wasm::WasmGlobal wasmGlobal;
-  if (config->is64.getValueOr(false)) {
-    wasmGlobal.Type = {WASM_TYPE_I64, isMutable};
-    wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I64_CONST;
-    wasmGlobal.InitExpr.Value.Int64 = 0;
-  } else {
-    wasmGlobal.Type = {WASM_TYPE_I32, isMutable};
-    wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
-    wasmGlobal.InitExpr.Value.Int32 = 0;
-  }
+  bool is64 = config->is64.getValueOr(false);
+  wasmGlobal.Type = {uint8_t(is64 ? WASM_TYPE_I64 : WASM_TYPE_I32), isMutable};
+  wasmGlobal.InitExpr = intConst(0, is64);
   wasmGlobal.SymbolName = name;
   return make<InputGlobal>(wasmGlobal, nullptr);
 }
@@ -635,12 +629,19 @@ static void createSyntheticSymbols() {
     // which to load our static data and function table.
     // See:
     // https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
-    WasmSym::memoryBase = createUndefinedGlobal(
-        "__memory_base",
-        config->is64.getValueOr(false) ? &globalTypeI64 : &globalTypeI32);
-    WasmSym::tableBase = createUndefinedGlobal("__table_base", &globalTypeI32);
+    bool is64 = config->is64.getValueOr(false);
+    auto *globalType = is64 ? &globalTypeI64 : &globalTypeI32;
+    WasmSym::memoryBase = createUndefinedGlobal("__memory_base", globalType);
+    WasmSym::tableBase = createUndefinedGlobal("__table_base", globalType);
     WasmSym::memoryBase->markLive();
     WasmSym::tableBase->markLive();
+    if (is64) {
+      WasmSym::tableBase32 =
+          createUndefinedGlobal("__table_base32", &globalTypeI32);
+      WasmSym::tableBase32->markLive();
+    } else {
+      WasmSym::tableBase32 = nullptr;
+    }
   } else {
     // For non-PIC code
     WasmSym::stackPointer = createGlobalVariable("__stack_pointer", true);
@@ -673,6 +674,9 @@ static void createOptionalSymbols() {
     WasmSym::heapBase = symtab->addOptionalDataSymbol("__heap_base");
     WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
     WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
+    if (config->is64.getValueOr(false))
+      WasmSym::definedTableBase32 =
+          symtab->addOptionalDataSymbol("__table_base32");
   }
 
   // For non-shared memory programs we still need to define __tls_base since we

diff  --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp
index 279cd41d1ff2..99819713c281 100644
--- a/lld/wasm/InputChunks.cpp
+++ b/lld/wasm/InputChunks.cpp
@@ -134,6 +134,7 @@ void InputChunk::relocate(uint8_t *buf) const {
       encodeSLEB128(static_cast<int32_t>(value), loc, 5);
       break;
     case R_WASM_TABLE_INDEX_SLEB64:
+    case R_WASM_TABLE_INDEX_REL_SLEB64:
     case R_WASM_MEMORY_ADDR_SLEB64:
     case R_WASM_MEMORY_ADDR_REL_SLEB64:
       encodeSLEB128(static_cast<int64_t>(value), loc, 10);

diff  --git a/lld/wasm/InputElement.h b/lld/wasm/InputElement.h
index 97ef5ca7feab..f39a1727047f 100644
--- a/lld/wasm/InputElement.h
+++ b/lld/wasm/InputElement.h
@@ -42,6 +42,18 @@ class InputElement {
   llvm::Optional<uint32_t> assignedIndex;
 };
 
+inline WasmInitExpr intConst(uint64_t value, bool is64) {
+  WasmInitExpr ie;
+  if (is64) {
+    ie.Opcode = llvm::wasm::WASM_OPCODE_I64_CONST;
+    ie.Value.Int64 = static_cast<int64_t>(value);
+  } else {
+    ie.Opcode = llvm::wasm::WASM_OPCODE_I32_CONST;
+    ie.Value.Int32 = static_cast<int32_t>(value);
+  }
+  return ie;
+}
+
 class InputGlobal : public InputElement {
 public:
   InputGlobal(const WasmGlobal &g, ObjFile *f)
@@ -51,13 +63,7 @@ class InputGlobal : public InputElement {
   const WasmInitExpr &getInitExpr() const { return initExpr; }
 
   void setPointerValue(uint64_t value) {
-    if (config->is64.getValueOr(false)) {
-      assert(initExpr.Opcode == llvm::wasm::WASM_OPCODE_I64_CONST);
-      initExpr.Value.Int64 = value;
-    } else {
-      assert(initExpr.Opcode == llvm::wasm::WASM_OPCODE_I32_CONST);
-      initExpr.Value.Int32 = value;
-    }
+    initExpr = intConst(value, config->is64.getValueOr(false));
   }
 
 private:

diff  --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp
index 7fd9215f1171..faf7bbcfa926 100644
--- a/lld/wasm/InputFiles.cpp
+++ b/lld/wasm/InputFiles.cpp
@@ -156,14 +156,15 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
   case R_WASM_TABLE_INDEX_I64:
   case R_WASM_TABLE_INDEX_SLEB:
   case R_WASM_TABLE_INDEX_SLEB64:
-  case R_WASM_TABLE_INDEX_REL_SLEB: {
+  case R_WASM_TABLE_INDEX_REL_SLEB:
+  case R_WASM_TABLE_INDEX_REL_SLEB64: {
     if (!getFunctionSymbol(reloc.Index)->hasTableIndex())
       return 0;
     uint32_t index = getFunctionSymbol(reloc.Index)->getTableIndex();
-    if (reloc.Type == R_WASM_TABLE_INDEX_REL_SLEB)
+    if (reloc.Type == R_WASM_TABLE_INDEX_REL_SLEB ||
+        reloc.Type == R_WASM_TABLE_INDEX_REL_SLEB64)
       index -= config->tableBase;
     return index;
-
   }
   case R_WASM_MEMORY_ADDR_LEB:
   case R_WASM_MEMORY_ADDR_LEB64:

diff  --git a/lld/wasm/OutputSections.cpp b/lld/wasm/OutputSections.cpp
index 28f611712536..829fd97b2cf2 100644
--- a/lld/wasm/OutputSections.cpp
+++ b/lld/wasm/OutputSections.cpp
@@ -8,6 +8,7 @@
 
 #include "OutputSections.h"
 #include "InputChunks.h"
+#include "InputElement.h"
 #include "InputFiles.h"
 #include "OutputSegment.h"
 #include "WriterUtils.h"
@@ -162,12 +163,8 @@ void DataSection::finalizeContents() {
       if (config->isPic) {
         initExpr.Opcode = WASM_OPCODE_GLOBAL_GET;
         initExpr.Value.Global = WasmSym::memoryBase->getGlobalIndex();
-      } else if (config->is64.getValueOr(false)) {
-        initExpr.Opcode = WASM_OPCODE_I64_CONST;
-        initExpr.Value.Int64 = static_cast<int64_t>(segment->startVA);
       } else {
-        initExpr.Opcode = WASM_OPCODE_I32_CONST;
-        initExpr.Value.Int32 = static_cast<int32_t>(segment->startVA);      
+        initExpr = intConst(segment->startVA, config->is64.getValueOr(false));
       }
       writeInitExpr(os, initExpr);
     }

diff  --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp
index f909c377b9a5..ea0f6a063506 100644
--- a/lld/wasm/Relocations.cpp
+++ b/lld/wasm/Relocations.cpp
@@ -104,6 +104,7 @@ void scanRelocations(InputChunk *chunk) {
     case R_WASM_TABLE_INDEX_SLEB:
     case R_WASM_TABLE_INDEX_SLEB64:
     case R_WASM_TABLE_INDEX_REL_SLEB:
+    case R_WASM_TABLE_INDEX_REL_SLEB64:
       if (requiresGOTAccess(sym))
         break;
       out.elemSec->addEntry(cast<FunctionSymbol>(sym));

diff  --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index 6d960b4cf7e6..c18510ca5fce 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -87,6 +87,8 @@ GlobalSymbol *WasmSym::tlsSize;
 GlobalSymbol *WasmSym::tlsAlign;
 UndefinedGlobal *WasmSym::tableBase;
 DefinedData *WasmSym::definedTableBase;
+UndefinedGlobal *WasmSym::tableBase32;
+DefinedData *WasmSym::definedTableBase32;
 UndefinedGlobal *WasmSym::memoryBase;
 DefinedData *WasmSym::definedMemoryBase;
 TableSymbol *WasmSym::indirectFunctionTable;

diff  --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 0c557530236d..95c7dd12b894 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -564,6 +564,11 @@ struct WasmSym {
   // Used in PIC code for offset of indirect function table
   static UndefinedGlobal *tableBase;
   static DefinedData *definedTableBase;
+  // 32-bit copy in wasm64 to work around init expr limitations.
+  // These can potentially be removed again once we have
+  // https://github.com/WebAssembly/extended-const 
+  static UndefinedGlobal *tableBase32;
+  static DefinedData *definedTableBase32;
 
   // __memory_base
   // Used in PIC code for offset of global data

diff  --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index b10ecc04b4b2..b8cf1cbfddca 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -362,33 +362,30 @@ void GlobalSection::writeBody() {
     writeGlobalType(os, g->getType());
     writeInitExpr(os, g->getInitExpr());
   }
-  // TODO(wvo): when do these need I64_CONST?
+  bool is64 = config->is64.getValueOr(false);
+  uint8_t itype = is64 ? WASM_TYPE_I64 : WASM_TYPE_I32;
   for (const Symbol *sym : internalGotSymbols) {
     // In the case of dynamic linking, internal GOT entries
     // need to be mutable since they get updated to the correct
     // runtime value during `__wasm_apply_global_relocs`.
     bool mutable_ = config->isPic & !sym->isStub;
-    WasmGlobalType type{WASM_TYPE_I32, mutable_};
+    WasmGlobalType type{itype, mutable_};
     WasmInitExpr initExpr;
-    initExpr.Opcode = WASM_OPCODE_I32_CONST;
     if (auto *d = dyn_cast<DefinedData>(sym))
-      initExpr.Value.Int32 = d->getVA();
+      initExpr = intConst(d->getVA(), is64);
     else if (auto *f = dyn_cast<FunctionSymbol>(sym))
-      initExpr.Value.Int32 = f->isStub ? 0 : f->getTableIndex();
+      initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
     else {
       assert(isa<UndefinedData>(sym));
-      initExpr.Value.Int32 = 0;
+      initExpr = intConst(0, is64);
     }
     writeGlobalType(os, type);
     writeInitExpr(os, initExpr);
   }
   for (const DefinedData *sym : dataAddressGlobals) {
-    WasmGlobalType type{WASM_TYPE_I32, false};
-    WasmInitExpr initExpr;
-    initExpr.Opcode = WASM_OPCODE_I32_CONST;
-    initExpr.Value.Int32 = sym->getVA();
+    WasmGlobalType type{itype, false};
     writeGlobalType(os, type);
-    writeInitExpr(os, initExpr);
+    writeInitExpr(os, intConst(sym->getVA(), is64));
   }
 }
 
@@ -443,7 +440,10 @@ void ElemSection::writeBody() {
   WasmInitExpr initExpr;
   if (config->isPic) {
     initExpr.Opcode = WASM_OPCODE_GLOBAL_GET;
-    initExpr.Value.Global = WasmSym::tableBase->getGlobalIndex();
+    initExpr.Value.Global =
+        (config->is64.getValueOr(false) ? WasmSym::tableBase32
+                                        : WasmSym::tableBase)
+            ->getGlobalIndex();
   } else {
     initExpr.Opcode = WASM_OPCODE_I32_CONST;
     initExpr.Value.Int32 = config->tableBase;

diff  --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 1705c205eca4..90970ef326b9 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -1403,6 +1403,8 @@ void Writer::run() {
     config->tableBase = 1;
     if (WasmSym::definedTableBase)
       WasmSym::definedTableBase->setVA(config->tableBase);
+    if (WasmSym::definedTableBase32)
+      WasmSym::definedTableBase32->setVA(config->tableBase);
   }
 
   log("-- createOutputSegments");

diff  --git a/llvm/include/llvm/BinaryFormat/WasmRelocs.def b/llvm/include/llvm/BinaryFormat/WasmRelocs.def
index b83efc26e58d..80c9ebcb1fa2 100644
--- a/llvm/include/llvm/BinaryFormat/WasmRelocs.def
+++ b/llvm/include/llvm/BinaryFormat/WasmRelocs.def
@@ -26,3 +26,4 @@ WASM_RELOC(R_WASM_TABLE_NUMBER_LEB,       20)
 WASM_RELOC(R_WASM_MEMORY_ADDR_TLS_SLEB,   21)
 WASM_RELOC(R_WASM_FUNCTION_OFFSET_I64,    22)
 WASM_RELOC(R_WASM_MEMORY_ADDR_LOCREL_I32, 23)
+WASM_RELOC(R_WASM_TABLE_INDEX_REL_SLEB64, 24)

diff  --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index 541b464d3f30..23208183121e 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -528,6 +528,7 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm,
   }
 
   if (Type == wasm::R_WASM_TABLE_INDEX_REL_SLEB ||
+      Type == wasm::R_WASM_TABLE_INDEX_REL_SLEB64 ||
       Type == wasm::R_WASM_TABLE_INDEX_SLEB ||
       Type == wasm::R_WASM_TABLE_INDEX_SLEB64 ||
       Type == wasm::R_WASM_TABLE_INDEX_I32 ||
@@ -590,6 +591,7 @@ WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry,
 
   switch (RelEntry.Type) {
   case wasm::R_WASM_TABLE_INDEX_REL_SLEB:
+  case wasm::R_WASM_TABLE_INDEX_REL_SLEB64:
   case wasm::R_WASM_TABLE_INDEX_SLEB:
   case wasm::R_WASM_TABLE_INDEX_SLEB64:
   case wasm::R_WASM_TABLE_INDEX_I32:
@@ -598,7 +600,8 @@ WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry,
     const MCSymbolWasm *Base =
         cast<MCSymbolWasm>(Layout.getBaseSymbol(*RelEntry.Symbol));
     assert(Base->isFunction());
-    if (RelEntry.Type == wasm::R_WASM_TABLE_INDEX_REL_SLEB)
+    if (RelEntry.Type == wasm::R_WASM_TABLE_INDEX_REL_SLEB ||
+        RelEntry.Type == wasm::R_WASM_TABLE_INDEX_REL_SLEB64)
       return TableIndices[Base] - InitialTableOffset;
     else
       return TableIndices[Base];
@@ -742,6 +745,7 @@ void WasmObjectWriter::applyRelocations(
       writePatchableSLEB<5>(Stream, Value, Offset);
       break;
     case wasm::R_WASM_TABLE_INDEX_SLEB64:
+    case wasm::R_WASM_TABLE_INDEX_REL_SLEB64:
     case wasm::R_WASM_MEMORY_ADDR_SLEB64:
     case wasm::R_WASM_MEMORY_ADDR_REL_SLEB64:
       writePatchableSLEB<10>(Stream, Value, Offset);
@@ -1757,7 +1761,8 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler &Asm,
           Rel.Type != wasm::R_WASM_TABLE_INDEX_I64 &&
           Rel.Type != wasm::R_WASM_TABLE_INDEX_SLEB &&
           Rel.Type != wasm::R_WASM_TABLE_INDEX_SLEB64 &&
-          Rel.Type != wasm::R_WASM_TABLE_INDEX_REL_SLEB)
+          Rel.Type != wasm::R_WASM_TABLE_INDEX_REL_SLEB &&
+          Rel.Type != wasm::R_WASM_TABLE_INDEX_REL_SLEB64)
         return;
       assert(Rel.Symbol->isFunction());
       const MCSymbolWasm *Base =

diff  --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 8004c5eb25a9..38895e46d0e2 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -870,6 +870,7 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
     case wasm::R_WASM_TABLE_INDEX_I32:
     case wasm::R_WASM_TABLE_INDEX_I64:
     case wasm::R_WASM_TABLE_INDEX_REL_SLEB:
+    case wasm::R_WASM_TABLE_INDEX_REL_SLEB64:
       if (!isValidFunctionSymbol(Reloc.Index))
         return make_error<GenericBinaryError>(
             "invalid relocation function index", object_error::parse_failed);

diff  --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
index 1a79a2d597bb..9a2bd39a54a5 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
@@ -76,7 +76,8 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
       return wasm::R_WASM_GLOBAL_INDEX_LEB;
     case MCSymbolRefExpr::VK_WASM_TBREL:
       assert(SymA.isFunction());
-      return wasm::R_WASM_TABLE_INDEX_REL_SLEB;
+      return is64Bit() ? wasm::R_WASM_TABLE_INDEX_REL_SLEB64
+                       : wasm::R_WASM_TABLE_INDEX_REL_SLEB;
     case MCSymbolRefExpr::VK_WASM_TLSREL:
       return wasm::R_WASM_MEMORY_ADDR_TLS_SLEB;
     case MCSymbolRefExpr::VK_WASM_MBREL:

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
index 2383416d8a92..2468df9ab400 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
@@ -107,9 +107,8 @@ MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
         strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0;
     WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
     WasmSym->setGlobalType(wasm::WasmGlobalType{
-        uint8_t(Subtarget.hasAddr64() && strcmp(Name, "__table_base") != 0
-                    ? wasm::WASM_TYPE_I64
-                    : wasm::WASM_TYPE_I32),
+        uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
+                                      : wasm::WASM_TYPE_I32),
         Mutable});
     return WasmSym;
   }

diff  --git a/llvm/test/MC/WebAssembly/reloc-pic64.s b/llvm/test/MC/WebAssembly/reloc-pic64.s
new file mode 100644
index 000000000000..9a382a15a0a2
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/reloc-pic64.s
@@ -0,0 +1,209 @@
+# RUN: llvm-mc -triple=wasm64-unknown-unknown -filetype=obj < %s | obj2yaml | FileCheck %s
+# RUN: llvm-mc -triple=wasm64-unknown-unknown -mattr=+reference-types -filetype=obj < %s | obj2yaml | FileCheck --check-prefix=REF %s
+
+# Verify that @GOT relocation entries result in R_WASM_GLOBAL_INDEX_LEB against
+# against the corrsponding function or data symbol and that the corresponding
+# data symbols are imported as a wasm globals.
+
+load_default_data:
+    .functype   load_default_data () -> (i32)
+    global.get  default_data at GOT
+    i32.load    0
+    end_function
+
+load_default_func:
+    .functype   load_default_func () -> (i32)
+    global.get  default_func at GOT
+    i32.load    0
+    end_function
+
+load_hidden_data:
+    .functype   load_hidden_data () -> (i64)
+    global.get  __memory_base
+    i64.const   .L.hidden_data at MBREL
+    i64.add
+    end_function
+
+load_hidden_func:
+    .functype   load_hidden_func () -> (i64)
+    global.get  __table_base
+    i64.const   hidden_func at TBREL
+    i64.add
+    end_function
+
+hidden_func:
+    .functype   hidden_func () -> (i32)
+    i32.const 0
+    end_function
+
+.section .rodata.hidden_data,"",@
+.L.hidden_data:
+    .int8 100
+    .size .L.hidden_data, 1
+
+#.hidden hidden_func
+#.hidden hidden_data
+.size default_data, 4
+.functype default_func () -> (i32)
+
+# CHECK:      --- !WASM
+# CHECK-NEXT: FileHeader:
+# CHECK-NEXT:   Version:         0x1
+# CHECK-NEXT: Sections:
+# CHECK-NEXT:   - Type:            TYPE
+# CHECK-NEXT:     Signatures:
+# CHECK-NEXT:       - Index:           0
+# CHECK-NEXT:         ParamTypes:      []
+# CHECK-NEXT:         ReturnTypes:
+# CHECK-NEXT:           - I32
+# CHECK-NEXT:       - Index:           1
+# CHECK-NEXT:         ParamTypes:      []
+# CHECK-NEXT:         ReturnTypes:
+# CHECK-NEXT:           - I64
+# CHECK-NEXT:   - Type:            IMPORT
+# CHECK-NEXT:     Imports:
+# CHECK-NEXT:       - Module:          env
+# CHECK-NEXT:         Field:           __linear_memory
+# CHECK-NEXT:         Kind:            MEMORY
+# CHECK-NEXT:         Memory:
+# CHECK-NEXT:           Flags:           [ IS_64 ]
+# CHECK-NEXT:           Minimum:         0x1
+# CHECK-NEXT:       - Module:          env
+# CHECK-NEXT:         Field:           default_func
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         SigIndex:        0
+# 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:             Minimum:         0x1
+# CHECK-NEXT:       - Module:          GOT.mem
+# CHECK-NEXT:         Field:           default_data
+# CHECK-NEXT:         Kind:            GLOBAL
+# CHECK-NEXT:         GlobalType:      I32
+# CHECK-NEXT:         GlobalMutable:   true
+# CHECK-NEXT:       - Module:          GOT.func
+# CHECK-NEXT:         Field:           default_func
+# CHECK-NEXT:         Kind:            GLOBAL
+# CHECK-NEXT:         GlobalType:      I32
+# CHECK-NEXT:         GlobalMutable:   true
+# CHECK-NEXT:   - Type:            FUNCTION
+# CHECK-NEXT:     FunctionTypes:   [ 0, 0, 1, 1, 0 ]
+# CHECK-NEXT:   - Type:            ELEM
+# CHECK-NEXT:     Segments:
+# CHECK-NEXT:        Offset:
+# CHECK-NEXT:          Opcode:          I32_CONST
+# CHECK-NEXT:          Value:           1
+# CHECK-NEXT:        Functions:       [ 5 ]
+# CHECK-NEXT:   - Type:            DATACOUNT
+# CHECK-NEXT:     Count:           1
+# CHECK-NEXT:   - Type:            CODE
+# CHECK-NEXT:     Relocations:
+# CHECK-NEXT:       - Type:            R_WASM_GLOBAL_INDEX_LEB
+# CHECK-NEXT:         Index:           1
+# CHECK-NEXT:         Offset:          0x4
+# CHECK-NEXT:       - Type:            R_WASM_GLOBAL_INDEX_LEB
+# CHECK-NEXT:         Index:           3
+# CHECK-NEXT:         Offset:          0x10
+# CHECK-NEXT:       - Type:            R_WASM_MEMORY_ADDR_LEB
+# CHECK-NEXT:         Index:           5
+# CHECK-NEXT:         Offset:          0x1C
+# CHECK-NEXT:       - Type:            R_WASM_MEMORY_ADDR_REL_SLEB
+# CHECK-NEXT:         Index:           6
+# CHECK-NEXT:         Offset:          0x22
+# CHECK-NEXT:       - Type:            R_WASM_MEMORY_ADDR_LEB
+# CHECK-NEXT:         Index:           8
+# CHECK-NEXT:         Offset:          0x31
+# CHECK-NEXT:       - Type:            R_WASM_TABLE_INDEX_REL_SLEB
+# CHECK-NEXT:         Index:           9
+# CHECK-NEXT:         Offset:          0x37
+# CHECK-NEXT:     Functions:
+# CHECK-NEXT:       - Index:           1
+# CHECK-NEXT:         Locals:          []
+# CHECK-NEXT:         Body:            2380808080002802000B
+# CHECK-NEXT:       - Index:           2
+# CHECK-NEXT:         Locals:          []
+# CHECK-NEXT:         Body:            2381808080002802000B
+# CHECK-NEXT:       - Index:           3
+# CHECK-NEXT:         Locals:          []
+# CHECK-NEXT:         Body:            23808080800042808080808080808080007C0B
+# CHECK-NEXT:       - Index:           4
+# CHECK-NEXT:         Locals:          []
+# CHECK-NEXT:         Body:            23808080800042808080808080808080007C0B
+# CHECK-NEXT:       - Index:           5
+# CHECK-NEXT:         Locals:          []
+# CHECK-NEXT:         Body:            41000B
+# CHECK-NEXT:   - Type:            DATA
+# CHECK-NEXT:     Segments:
+# CHECK-NEXT:       - SectionOffset:   6
+# CHECK-NEXT:         InitFlags:       0
+# CHECK-NEXT:         Offset:
+# CHECK-NEXT:           Opcode:          I64_CONST
+# CHECK-NEXT:           Value:           0
+# CHECK-NEXT:         Content:         '64'
+# CHECK-NEXT:   - Type:            CUSTOM
+# CHECK-NEXT:     Name:            linking
+# CHECK-NEXT:     Version:         2
+# CHECK-NEXT:     SymbolTable:
+# CHECK-NEXT:       - Index:           0
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         Name:            load_default_data
+# CHECK-NEXT:         Flags:           [ BINDING_LOCAL ]
+# CHECK-NEXT:         Function:        1
+# CHECK-NEXT:       - Index:           1
+# CHECK-NEXT:         Kind:            DATA
+# CHECK-NEXT:         Name:            default_data
+# CHECK-NEXT:         Flags:           [ UNDEFINED ]
+# CHECK-NEXT:       - Index:           2
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         Name:            load_default_func
+# CHECK-NEXT:         Flags:           [ BINDING_LOCAL ]
+# CHECK-NEXT:         Function:        2
+# CHECK-NEXT:       - Index:           3
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         Name:            default_func
+# CHECK-NEXT:         Flags:           [ UNDEFINED ]
+# CHECK-NEXT:         Function:        0
+# CHECK-NEXT:       - Index:           4
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         Name:            load_hidden_data
+# CHECK-NEXT:         Flags:           [ BINDING_LOCAL ]
+# CHECK-NEXT:         Function:        3
+# CHECK-NEXT:       - Index:           5
+# CHECK-NEXT:         Kind:            DATA
+# CHECK-NEXT:         Name:            __memory_base
+# CHECK-NEXT:         Flags:           [ UNDEFINED ]
+# CHECK-NEXT:       - Index:           6
+# CHECK-NEXT:         Kind:            DATA
+# CHECK-NEXT:         Name:            .L.hidden_data
+# CHECK-NEXT:         Flags:           [ BINDING_LOCAL ]
+# CHECK-NEXT:         Segment:         0
+# CHECK-NEXT:         Size:            1
+# CHECK-NEXT:       - Index:           7
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         Name:            load_hidden_func
+# CHECK-NEXT:         Flags:           [ BINDING_LOCAL ]
+# CHECK-NEXT:         Function:        4
+# CHECK-NEXT:       - Index:           8
+# CHECK-NEXT:         Kind:            DATA
+# CHECK-NEXT:         Name:            __table_base
+# CHECK-NEXT:         Flags:           [ UNDEFINED ]
+# CHECK-NEXT:       - Index:           9
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         Name:            hidden_func
+# CHECK-NEXT:         Flags:           [ BINDING_LOCAL ]
+# CHECK-NEXT:         Function:        5
+# REF:              - Index:           10
+# REF-NEXT:           Kind:            TABLE
+# REF-NEXT:           Name:            __indirect_function_table
+# REF-NEXT:           Flags:           [ UNDEFINED, NO_STRIP ]
+# REF-NEXT:           Table:           0
+# CHECK-NEXT:     SegmentInfo:
+# CHECK-NEXT:       - Index:           0
+# CHECK-NEXT:         Name:            .rodata.hidden_data
+# CHECK-NEXT:         Alignment:       0
+# CHECK-NEXT:         Flags:           [  ]
+# CHECK-NEXT: ...


        


More information about the llvm-commits mailing list