[WebAssembly] Initial implementation of PIC code generation
authorSam Clegg <sbc@chromium.org>
Tue, 26 Mar 2019 19:46:15 +0000 (19:46 +0000)
committerSam Clegg <sbc@chromium.org>
Tue, 26 Mar 2019 19:46:15 +0000 (19:46 +0000)
This change implements lowering of references global symbols in PIC
mode.

This change implements lowering of global references in PIC mode using a
new @GOT reference type. @GOT references can be used with function or
data symbol names combined with the get_global instruction. In this case
the linker will insert the wasm global that stores the address of the
symbol (either in memory for data symbols or in the wasm table for
function symbols).

For now I'm continuing to use the R_WASM_GLOBAL_INDEX_LEB relocation
type for this type of reference which means that this relocation type
can refer to either a global or a function or data symbol. We could
choose to introduce specific relocation types for GOT entries in the
future.  See the current dynamic linking proposal:

https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md

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

llvm-svn: 357022

23 files changed:
lld/test/wasm/shared.ll
lld/wasm/Driver.cpp
lld/wasm/InputChunks.cpp
lld/wasm/InputFiles.cpp
lld/wasm/Symbols.cpp
lld/wasm/Symbols.h
lld/wasm/Writer.cpp
llvm/include/llvm/MC/MCSymbolWasm.h
llvm/lib/MC/WasmObjectWriter.cpp
llvm/lib/Object/WasmObjectFile.cpp
llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h
llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp
llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
llvm/lib/Target/WebAssembly/WebAssemblyISD.def
llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td
llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.h
llvm/test/CodeGen/WebAssembly/address-offsets.ll
llvm/test/CodeGen/WebAssembly/call-pic.ll [new file with mode: 0644]
llvm/test/CodeGen/WebAssembly/load-store-pic.ll [new file with mode: 0644]
llvm/test/MC/WebAssembly/reloc-pic.s [new file with mode: 0644]

index a40cfbe..39e10ac 100644 (file)
@@ -1,10 +1,11 @@
-; RUN: llc -O0 -filetype=obj %s -o %t.o
+; RUN: llc -relocation-model=pic -filetype=obj %s -o %t.o
 ; RUN: wasm-ld -shared -o %t.wasm %t.o
 ; RUN: obj2yaml %t.wasm | FileCheck %s
 
 target triple = "wasm32-unknown-unknown"
 
 @data = hidden global i32 2, align 4
+@data_external = external global i32
 @indirect_func = local_unnamed_addr global i32 ()* @foo, align 4
 @indirect_func_external = local_unnamed_addr global void ()* @func_external, align 4
 
@@ -13,18 +14,22 @@ entry:
   ; To ensure we use __stack_pointer
   %ptr = alloca i32
   %0 = load i32, i32* @data, align 4
-  ; TODO(sbc): Re-enable once the codegen supports generating the correct
-  ; relocation type when referencing external data in shared libraries.
-  ; %1 = load i32, i32* @data_external, align 4
   %1 = load i32 ()*, i32 ()** @indirect_func, align 4
   call i32 %1()
   ret i32 %0
 }
 
-declare void @func_external()
+define default i32* @get_data_address() {
+entry:
+  ret i32* @data_external
+}
 
-@data_external = external global i32
+define default i8* @get_func_address() {
+entry:
+  ret i8* bitcast (void ()* @func_external to i8*)
+}
 
+declare void @func_external()
 
 ; check for dylink section at start
 
@@ -43,6 +48,11 @@ declare void @func_external()
 ; CHECK:        - Type:            IMPORT
 ; CHECK-NEXT:     Imports:
 ; CHECK-NEXT:       - Module:          env
+; CHECK-NEXT:         Field:           memory
+; CHECK-NEXT:         Kind:            MEMORY
+; CHECK-NEXT:         Memory:
+; CHECK-NEXT:           Initial:       0x0000000
+; CHECK-NEXT:       - Module:          env
 ; CHECK-NEXT:         Field:           __indirect_function_table
 ; CHECK-NEXT:         Kind:            TABLE
 ; CHECK-NEXT:         Table:
@@ -64,15 +74,21 @@ declare void @func_external()
 ; CHECK-NEXT:         Kind:            GLOBAL
 ; CHECK-NEXT:         GlobalType:      I32
 ; CHECK-NEXT:         GlobalMutable:   false
-; XCHECK-NEXT:       - Module:          env
-; XCHECK-NEXT:         Field:           data_external
-; XCHECK-NEXT:         Kind:            GLOBAL
-; XCHECK-NEXT:         GlobalType:      I32
-; XCHECK-NEXT:         GlobalMutable:   true
 ; 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:           data_external
+; 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:   - Type:            FUNCTION
 
 ; check for elem segment initialized with __table_base global as offset
 
index f9dfd5c..8f8d00d 100644 (file)
@@ -519,6 +519,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
   }
 
   if (Config->Shared) {
+    Config->ImportMemory = true;
     Config->ExportDynamic = true;
     Config->AllowUndefined = true;
   }
index 238c111..5d6dfa0 100644 (file)
@@ -74,11 +74,14 @@ void InputChunk::verifyRelocTargets() const {
 
     if (BytesRead && BytesRead != 5)
       warn("expected LEB at relocation site be 5-byte padded");
-    uint32_t ExpectedValue = File->calcExpectedValue(Rel);
-    if (ExpectedValue != ExistingValue)
-      warn("unexpected existing value for " + reloctTypeToString(Rel.Type) +
-           ": existing=" + Twine(ExistingValue) +
-           " expected=" + Twine(ExpectedValue));
+
+    if (Rel.Type != R_WASM_GLOBAL_INDEX_LEB) {
+      uint32_t ExpectedValue = File->calcExpectedValue(Rel);
+      if (ExpectedValue != ExistingValue)
+        warn("unexpected existing value for " + reloctTypeToString(Rel.Type) +
+             ": existing=" + Twine(ExistingValue) +
+             " expected=" + Twine(ExpectedValue));
+    }
   }
 }
 
index b43cab7..2e49696 100644 (file)
@@ -152,9 +152,12 @@ uint32_t ObjFile::calcNewValue(const WasmRelocation &Reloc) const {
     return TypeMap[Reloc.Index];
   case R_WASM_FUNCTION_INDEX_LEB:
     return getFunctionSymbol(Reloc.Index)->getFunctionIndex();
-  case R_WASM_GLOBAL_INDEX_LEB:
-    return getGlobalSymbol(Reloc.Index)->getGlobalIndex();
-  case R_WASM_EVENT_INDEX_LEB:
+  case R_WASM_GLOBAL_INDEX_LEB: {
+    const Symbol* Sym = Symbols[Reloc.Index];
+    if (auto GS = dyn_cast<GlobalSymbol>(Sym))
+      return GS->getGlobalIndex();
+    return Sym->getGOTIndex();
+  } case R_WASM_EVENT_INDEX_LEB:
     return getEventSymbol(Reloc.Index)->getEventIndex();
   case R_WASM_FUNCTION_OFFSET_I32:
     if (auto *Sym = dyn_cast<DefinedFunction>(getFunctionSymbol(Reloc.Index))) {
index f8ea471..248581c 100644 (file)
@@ -93,6 +93,12 @@ void Symbol::setOutputSymbolIndex(uint32_t Index) {
   OutputSymbolIndex = Index;
 }
 
+void Symbol::setGOTIndex(uint32_t Index) {
+  LLVM_DEBUG(dbgs() << "setGOTIndex " << Name << " -> " << Index << "\n");
+  assert(GOTIndex == INVALID_INDEX);
+  GOTIndex = Index;
+}
+
 bool Symbol::isWeak() const {
   return (Flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK;
 }
index b59e8bb..a2ecef1 100644 (file)
@@ -113,6 +113,16 @@ public:
 
   const WasmSignature* getSignature() const;
 
+  bool isInGOT() const { return GOTIndex != INVALID_INDEX; }
+
+  uint32_t getGOTIndex() const {
+    assert(GOTIndex != INVALID_INDEX);
+    return GOTIndex;
+  }
+
+  void setGOTIndex(uint32_t Index);
+  bool hasGOTIndex() const { return GOTIndex != INVALID_INDEX; }
+
 protected:
   Symbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F)
       : IsUsedInRegularObj(false), ForceExport(false), Traced(false),
@@ -124,6 +134,7 @@ protected:
   uint32_t Flags;
   InputFile *File;
   uint32_t OutputSymbolIndex = INVALID_INDEX;
+  uint32_t GOTIndex = INVALID_INDEX;
   bool Referenced;
 };
 
@@ -249,21 +260,6 @@ public:
   static bool classof(const Symbol *S) {
     return S->kind() == UndefinedDataKind;
   }
-
-  // Undefined data symbols are imported as wasm globals so also have a global
-  // index.
-  uint32_t getGlobalIndex() const {
-    assert(GlobalIndex != INVALID_INDEX);
-    return GlobalIndex;
-  }
-  void setGlobalIndex(uint32_t Index) {
-    assert(GlobalIndex == INVALID_INDEX);
-    GlobalIndex = Index;
-  }
-  bool hasGlobalIndex() const { return GlobalIndex != INVALID_INDEX; }
-
-protected:
-  uint32_t GlobalIndex = INVALID_INDEX;
 };
 
 class GlobalSymbol : public Symbol {
index 5f12ce7..480257f 100644 (file)
@@ -118,6 +118,7 @@ private:
   std::vector<const WasmSignature *> Types;
   DenseMap<WasmSignature, int32_t> TypeIndices;
   std::vector<const Symbol *> ImportedSymbols;
+  std::vector<const Symbol *> GOTSymbols;
   unsigned NumImportedFunctions = 0;
   unsigned NumImportedGlobals = 0;
   unsigned NumImportedEvents = 0;
@@ -147,7 +148,7 @@ private:
 } // anonymous namespace
 
 void Writer::createImportSection() {
-  uint32_t NumImports = ImportedSymbols.size();
+  uint32_t NumImports = ImportedSymbols.size() + GOTSymbols.size();
   if (Config->ImportMemory)
     ++NumImports;
   if (Config->ImportTable)
@@ -204,9 +205,6 @@ void Writer::createImportSection() {
     if (auto *FunctionSym = dyn_cast<FunctionSymbol>(Sym)) {
       Import.Kind = WASM_EXTERNAL_FUNCTION;
       Import.SigIndex = lookupType(*FunctionSym->Signature);
-    } else if (auto *DataSym = dyn_cast<UndefinedData>(Sym)) {
-      Import.Kind = WASM_EXTERNAL_GLOBAL;
-      Import.Global = {WASM_TYPE_I32, true};
     } else if (auto *GlobalSym = dyn_cast<GlobalSymbol>(Sym)) {
       Import.Kind = WASM_EXTERNAL_GLOBAL;
       Import.Global = *GlobalSym->getGlobalType();
@@ -218,6 +216,18 @@ void Writer::createImportSection() {
     }
     writeImport(OS, Import);
   }
+
+  for (const Symbol *Sym : GOTSymbols) {
+    WasmImport Import;
+    Import.Kind = WASM_EXTERNAL_GLOBAL;
+    Import.Global = {WASM_TYPE_I32, true};
+    if (isa<DataSymbol>(Sym))
+      Import.Module = "GOT.mem";
+    else
+      Import.Module = "GOT.func";
+    Import.Field = Sym->getName();
+    writeImport(OS, Import);
+  }
 }
 
 void Writer::createTypeSection() {
@@ -957,9 +967,9 @@ void Writer::calculateImports() {
       continue;
     if (!Sym->IsUsedInRegularObj)
       continue;
-    // In relocatable output we don't generate imports for data symbols.
-    // These live only in the symbol table.
-    if (Config->Relocatable && isa<DataSymbol>(Sym))
+    // We don't generate imports for data symbols. They however can be imported
+    // as GOT entries.
+    if (isa<DataSymbol>(Sym))
       continue;
 
     LLVM_DEBUG(dbgs() << "import: " << Sym->getName() << "\n");
@@ -968,8 +978,6 @@ void Writer::calculateImports() {
       F->setFunctionIndex(NumImportedFunctions++);
     else if (auto *G = dyn_cast<GlobalSymbol>(Sym))
       G->setGlobalIndex(NumImportedGlobals++);
-    else if (auto *D = dyn_cast<UndefinedData>(Sym))
-      D->setGlobalIndex(NumImportedGlobals++);
     else
       cast<EventSymbol>(Sym)->setEventIndex(NumImportedEvents++);
   }
@@ -1145,6 +1153,13 @@ void Writer::processRelocations(InputChunk *Chunk) {
               DataSym->getName());
       break;
     }
+    case R_WASM_GLOBAL_INDEX_LEB: {
+      auto* Sym = File->getSymbols()[Reloc.Index];
+      if (!isa<GlobalSymbol>(Sym) && !Sym->isInGOT()) {
+        Sym->setGOTIndex(NumImportedGlobals++);
+        GOTSymbols.push_back(Sym);
+      }
+    }
     }
   }
 }
index 88759fe..c50cd0e 100644 (file)
@@ -18,6 +18,7 @@ class MCSymbolWasm : public MCSymbol {
   bool IsWeak = false;
   bool IsHidden = false;
   bool IsComdat = false;
+  mutable bool IsUsedInGOT = false;
   Optional<std::string> ImportModule;
   Optional<std::string> ImportName;
   wasm::WasmSignature *Signature = nullptr;
@@ -78,6 +79,9 @@ public:
   }
   void setImportName(StringRef Name) { ImportName = Name; }
 
+  void setUsedInGOT() const { IsUsedInGOT = true; }
+  bool isUsedInGOT() const { return IsUsedInGOT; }
+
   const wasm::WasmSignature *getSignature() const { return Signature; }
   void setSignature(wasm::WasmSignature *Sig) { Signature = Sig; }
 
index 0014b69..adf0aef 100644 (file)
@@ -243,6 +243,7 @@ class WasmObjectWriter : public MCObjectWriter {
   // Maps function/global symbols to the function/global/event/section index
   // space.
   DenseMap<const MCSymbolWasm *, uint32_t> WasmIndices;
+  DenseMap<const MCSymbolWasm *, uint32_t> GOTIndices;
   // Maps data symbols to the Wasm segment and offset/size with the segment.
   DenseMap<const MCSymbolWasm *, wasm::WasmDataReference> DataLocations;
 
@@ -260,7 +261,6 @@ class WasmObjectWriter : public MCObjectWriter {
 
   DenseMap<WasmSignature, uint32_t, WasmSignatureDenseMapInfo> SignatureIndices;
   SmallVector<WasmSignature, 4> Signatures;
-  SmallVector<WasmGlobal, 4> Globals;
   SmallVector<WasmDataSegment, 4> DataSegments;
   unsigned NumFunctionImports = 0;
   unsigned NumGlobalImports = 0;
@@ -288,6 +288,7 @@ private:
     DataRelocations.clear();
     TypeIndices.clear();
     WasmIndices.clear();
+    GOTIndices.clear();
     TableIndices.clear();
     DataLocations.clear();
     CustomSections.clear();
@@ -296,7 +297,6 @@ private:
     CustomSectionsRelocations.clear();
     SignatureIndices.clear();
     Signatures.clear();
-    Globals.clear();
     DataSegments.clear();
     SectionFunctions.clear();
     NumFunctionImports = 0;
@@ -326,7 +326,6 @@ private:
   void writeImportSection(ArrayRef<wasm::WasmImport> Imports, uint32_t DataSize,
                           uint32_t NumElements);
   void writeFunctionSection(ArrayRef<WasmFunction> Functions);
-  void writeGlobalSection();
   void writeExportSection(ArrayRef<wasm::WasmExport> Exports);
   void writeElemSection(ArrayRef<uint32_t> TableElems);
   void writeCodeSection(const MCAssembler &Asm, const MCAsmLayout &Layout,
@@ -545,6 +544,9 @@ void WasmObjectWriter::recordRelocation(MCAssembler &Asm,
     SymA->setUsedInReloc();
   }
 
+  if (RefA->getKind() == MCSymbolRefExpr::VK_GOT)
+    SymA->setUsedInGOT();
+
   WasmRelocationEntry Rec(FixupOffset, SymA, C, Type, &FixupSection);
   LLVM_DEBUG(dbgs() << "WasmReloc: " << Rec << "\n");
 
@@ -575,6 +577,11 @@ static const MCSymbolWasm *resolveSymbol(const MCSymbolWasm &Symbol) {
 // useable.
 uint32_t
 WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry) {
+  if (RelEntry.Type == wasm::R_WASM_GLOBAL_INDEX_LEB && !RelEntry.Symbol->isGlobal()) {
+    assert(GOTIndices.count(RelEntry.Symbol) > 0 && "symbol not found in GOT index space");
+    return GOTIndices[RelEntry.Symbol];
+  }
+
   switch (RelEntry.Type) {
   case wasm::R_WASM_TABLE_INDEX_SLEB:
   case wasm::R_WASM_TABLE_INDEX_I32: {
@@ -590,9 +597,7 @@ WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry) {
   case wasm::R_WASM_GLOBAL_INDEX_LEB:
   case wasm::R_WASM_EVENT_INDEX_LEB:
     // Provisional value is function/global/event Wasm index
-    if (!WasmIndices.count(RelEntry.Symbol))
-      report_fatal_error("symbol not found in wasm index space: " +
-                         RelEntry.Symbol->getName());
+    assert(WasmIndices.count(RelEntry.Symbol) > 0 && "symbol not found in wasm index space");
     return WasmIndices[RelEntry.Symbol];
   case wasm::R_WASM_FUNCTION_OFFSET_I32:
   case wasm::R_WASM_SECTION_OFFSET_I32: {
@@ -787,26 +792,6 @@ void WasmObjectWriter::writeFunctionSection(ArrayRef<WasmFunction> Functions) {
   endSection(Section);
 }
 
-void WasmObjectWriter::writeGlobalSection() {
-  if (Globals.empty())
-    return;
-
-  SectionBookkeeping Section;
-  startSection(Section, wasm::WASM_SEC_GLOBAL);
-
-  encodeULEB128(Globals.size(), W.OS);
-  for (const WasmGlobal &Global : Globals) {
-    writeValueType(static_cast<wasm::ValType>(Global.Type.Type));
-    W.OS << char(Global.Type.Mutable);
-
-    W.OS << char(wasm::WASM_OPCODE_I32_CONST);
-    encodeSLEB128(Global.InitialValue, W.OS);
-    W.OS << char(wasm::WASM_OPCODE_END);
-  }
-
-  endSection(Section);
-}
-
 void WasmObjectWriter::writeEventSection(ArrayRef<wasm::WasmEventType> Events) {
   if (Events.empty())
     return;
@@ -1208,17 +1193,19 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
         Import.Kind = wasm::WASM_EXTERNAL_FUNCTION;
         Import.SigIndex = getFunctionType(WS);
         Imports.push_back(Import);
+        assert(WasmIndices.count(&WS) == 0);
         WasmIndices[&WS] = NumFunctionImports++;
       } else if (WS.isGlobal()) {
         if (WS.isWeak())
           report_fatal_error("undefined global symbol cannot be weak");
 
         wasm::WasmImport Import;
-        Import.Module = WS.getImportModule();
         Import.Field = WS.getImportName();
         Import.Kind = wasm::WASM_EXTERNAL_GLOBAL;
+        Import.Module = WS.getImportModule();
         Import.Global = WS.getGlobalType();
         Imports.push_back(Import);
+        assert(WasmIndices.count(&WS) == 0);
         WasmIndices[&WS] = NumGlobalImports++;
       } else if (WS.isEvent()) {
         if (WS.isWeak())
@@ -1231,11 +1218,30 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
         Import.Event.Attribute = wasm::WASM_EVENT_ATTRIBUTE_EXCEPTION;
         Import.Event.SigIndex = getEventType(WS);
         Imports.push_back(Import);
+        assert(WasmIndices.count(&WS) == 0);
         WasmIndices[&WS] = NumEventImports++;
       }
     }
   }
 
+  // Add imports for GOT globals
+  for (const MCSymbol &S : Asm.symbols()) {
+    const auto &WS = static_cast<const MCSymbolWasm &>(S);
+    if (WS.isUsedInGOT()) {
+      wasm::WasmImport Import;
+      if (WS.isFunction())
+        Import.Module = "GOT.func";
+      else
+        Import.Module = "GOT.mem";
+      Import.Field = WS.getName();
+      Import.Kind = wasm::WASM_EXTERNAL_GLOBAL;
+      Import.Global = {wasm::WASM_TYPE_I32, true};
+      Imports.push_back(Import);
+      assert(GOTIndices.count(&WS) == 0);
+      GOTIndices[&WS] = NumGlobalImports++;
+    }
+  }
+
   // Populate DataSegments and CustomSections, which must be done before
   // populating DataLocations.
   for (MCSection &Sec : Asm) {
@@ -1401,10 +1407,12 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
         wasm::WasmEventType Event;
         Event.SigIndex = getEventType(WS);
         Event.Attribute = wasm::WASM_EVENT_ATTRIBUTE_EXCEPTION;
+        assert(WasmIndices.count(&WS) == 0);
         WasmIndices[&WS] = Index;
         Events.push_back(Event);
       } else {
         // An import; the index was assigned above.
+        assert(WasmIndices.count(&WS) > 0);
         Index = WasmIndices.find(&WS)->second;
       }
       LLVM_DEBUG(dbgs() << "  -> event index: " << WasmIndices.find(&WS)->second
@@ -1434,6 +1442,7 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
     if (ResolvedSym->isFunction()) {
       assert(WasmIndices.count(ResolvedSym) > 0);
       uint32_t WasmIndex = WasmIndices.find(ResolvedSym)->second;
+      assert(WasmIndices.count(&WS) == 0);
       WasmIndices[&WS] = WasmIndex;
       LLVM_DEBUG(dbgs() << "  -> index:" << WasmIndex << "\n");
     } else if (ResolvedSym->isData()) {
@@ -1586,7 +1595,6 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
   writeFunctionSection(Functions);
   // Skip the "table" section; we import the table instead.
   // Skip the "memory" section; we import the memory instead.
-  writeGlobalSection();
   writeEventSection(Events);
   writeExportSection(Exports);
   writeElemSection(TableElems);
index 14b17bc..7a27691 100644 (file)
@@ -777,7 +777,11 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
                                               object_error::parse_failed);
       break;
     case wasm::R_WASM_GLOBAL_INDEX_LEB:
-      if (!isValidGlobalSymbol(Reloc.Index))
+      // R_WASM_GLOBAL_INDEX_LEB are can be used against function and data
+      // symbols to refer to thier GOT enties.
+      if (!isValidGlobalSymbol(Reloc.Index) &&
+          !isValidDataSymbol(Reloc.Index) &&
+          !isValidFunctionSymbol(Reloc.Index))
         return make_error<GenericBinaryError>("Bad relocation global index",
                                               object_error::parse_failed);
       break;
index 5db7291..303fd3e 100644 (file)
@@ -94,7 +94,12 @@ enum TOF {
   MO_SYMBOL_GLOBAL = 0x2,
   MO_SYMBOL_EVENT = 0x4,
   MO_SYMBOL_MASK = 0x7,
+
+  // Address of data symbol via a wasm global.  This adds a level of indirection
+  // similar to the GOT on native platforms.
+  MO_GOT = 0x8,
 };
+
 } // end namespace WebAssemblyII
 
 } // end namespace llvm
index 3b80085..1a9c714 100644 (file)
@@ -46,6 +46,10 @@ static bool isFunctionSignatureRef(const MCSymbolRefExpr *Ref) {
   return Ref->getKind() == MCSymbolRefExpr::VK_WebAssembly_TYPEINDEX;
 }
 
+static bool isGOTRef(const MCSymbolRefExpr *Ref) {
+  return Ref->getKind() == MCSymbolRefExpr::VK_GOT;
+}
+
 static const MCSection *getFixupSection(const MCExpr *Expr) {
   if (auto SyExp = dyn_cast<MCSymbolRefExpr>(Expr)) {
     if (SyExp->getSymbol().isInSection())
@@ -79,14 +83,14 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
   case WebAssembly::fixup_code_sleb128_i64:
     llvm_unreachable("fixup_sleb128_i64 not implemented yet");
   case WebAssembly::fixup_code_uleb128_i32:
+    if (SymA.isGlobal() || isGOTRef(RefA))
+      return wasm::R_WASM_GLOBAL_INDEX_LEB;
     if (SymA.isFunction()) {
       if (isFunctionSignatureRef(RefA))
         return wasm::R_WASM_TYPE_INDEX_LEB;
       else
         return wasm::R_WASM_FUNCTION_INDEX_LEB;
     }
-    if (SymA.isGlobal())
-      return wasm::R_WASM_GLOBAL_INDEX_LEB;
     if (SymA.isEvent())
       return wasm::R_WASM_EVENT_INDEX_LEB;
     return wasm::R_WASM_MEMORY_ADDR_LEB;
index 15df3d4..1705748 100644 (file)
@@ -151,7 +151,7 @@ private:
     return MVT::INVALID_SIMPLE_VALUE_TYPE;
   }
   bool computeAddress(const Value *Obj, Address &Addr);
-  void materializeLoadStoreOperands(Address &Addr);
+  bool materializeLoadStoreOperands(Address &Addr);
   void addLoadStoreOperands(const Address &Addr, const MachineInstrBuilder &MIB,
                             MachineMemOperand *MMO);
   unsigned maskI1Value(unsigned Reg, const Value *V);
@@ -374,10 +374,13 @@ bool WebAssemblyFastISel::computeAddress(const Value *Obj, Address &Addr) {
   return Addr.getReg() != 0;
 }
 
-void WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) {
+bool WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) {
   if (Addr.isRegBase()) {
     unsigned Reg = Addr.getReg();
     if (Reg == 0) {
+      const GlobalValue *GV = Addr.getGlobalValue();
+      if (GV && TLI.isPositionIndependent())
+        return false;
       Reg = createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
                                                    : &WebAssembly::I32RegClass);
       unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64
@@ -387,6 +390,7 @@ void WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) {
       Addr.setReg(Reg);
     }
   }
+  return true;
 }
 
 void WebAssemblyFastISel::addLoadStoreOperands(const Address &Addr,
@@ -604,7 +608,9 @@ unsigned WebAssemblyFastISel::fastMaterializeAlloca(const AllocaInst *AI) {
 }
 
 unsigned WebAssemblyFastISel::fastMaterializeConstant(const Constant *C) {
-  if (const auto *GV = dyn_cast<GlobalValue>(C)) {
+  if (const GlobalValue *GV = dyn_cast<GlobalValue>(C)) {
+    if (TLI.isPositionIndependent())
+      return 0;
     unsigned ResultReg =
         createResultReg(Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
                                                : &WebAssembly::I32RegClass);
@@ -1181,7 +1187,8 @@ bool WebAssemblyFastISel::selectLoad(const Instruction *I) {
     return false;
   }
 
-  materializeLoadStoreOperands(Addr);
+  if (!materializeLoadStoreOperands(Addr))
+    return false;
 
   unsigned ResultReg = createResultReg(RC);
   auto MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc),
@@ -1233,7 +1240,8 @@ bool WebAssemblyFastISel::selectStore(const Instruction *I) {
     return false;
   }
 
-  materializeLoadStoreOperands(Addr);
+  if (!materializeLoadStoreOperands(Addr))
+    return false;
 
   unsigned ValueReg = getRegForValue(Store->getValueOperand());
   if (ValueReg == 0)
index 36b1464..af5fcd8 100644 (file)
@@ -17,7 +17,11 @@ HANDLE_NODETYPE(CALL1)
 HANDLE_NODETYPE(CALL0)
 HANDLE_NODETYPE(RETURN)
 HANDLE_NODETYPE(ARGUMENT)
+// A wrapper node for TargetExternalSymbol, TargetGlobalAddress, and MCSymbol
 HANDLE_NODETYPE(Wrapper)
+// A special wapper used in PIC code for __memory_base/__table_base relcative
+// access.
+HANDLE_NODETYPE(WrapperPIC)
 HANDLE_NODETYPE(BR_IF)
 HANDLE_NODETYPE(BR_TABLE)
 HANDLE_NODETYPE(SHUFFLE)
index aa7bba8..dc9afb7 100644 (file)
@@ -729,6 +729,18 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
     FINode = DAG.getIntPtrConstant(0, DL);
   }
 
+  if (Callee->getOpcode() == ISD::GlobalAddress) {
+    // If the callee is a GlobalAddress node (quite common, every direct call
+    // is) turn it into a TargetGlobalAddress node so that LowerGlobalAddress
+    // doesn't at MO_GOT which is not needed for direct calls.
+    GlobalAddressSDNode* GA = cast<GlobalAddressSDNode>(Callee);
+    Callee = DAG.getTargetGlobalAddress(GA->getGlobal(), DL,
+                                        getPointerTy(DAG.getDataLayout()),
+                                        GA->getOffset());
+    Callee = DAG.getNode(WebAssemblyISD::Wrapper, DL,
+                         getPointerTy(DAG.getDataLayout()), Callee);
+  }
+
   // Compute the operands for the CALLn node.
   SmallVector<SDValue, 16> Ops;
   Ops.push_back(Chain);
@@ -983,9 +995,35 @@ SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op,
          "Unexpected target flags on generic GlobalAddressSDNode");
   if (GA->getAddressSpace() != 0)
     fail(DL, DAG, "WebAssembly only expects the 0 address space");
-  return DAG.getNode(
-      WebAssemblyISD::Wrapper, DL, VT,
-      DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset()));
+
+  unsigned Flags = 0;
+  if (isPositionIndependent()) {
+    const GlobalValue *GV = GA->getGlobal();
+    if (getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV)) {
+      MachineFunction &MF = DAG.getMachineFunction();
+      MVT PtrVT = getPointerTy(MF.getDataLayout());
+      const char *BaseName;
+      if (GV->getValueType()->isFunctionTy())
+        BaseName = MF.createExternalSymbolName("__table_base");
+      else
+        BaseName = MF.createExternalSymbolName("__memory_base");
+      SDValue BaseAddr =
+          DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT,
+                      DAG.getTargetExternalSymbol(BaseName, PtrVT));
+
+      SDValue SymAddr = DAG.getNode(
+          WebAssemblyISD::WrapperPIC, DL, VT,
+          DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset()));
+
+      return DAG.getNode(ISD::ADD, DL, VT, BaseAddr, SymAddr);
+    } else {
+      Flags |= WebAssemblyII::MO_GOT;
+    }
+  }
+
+  return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
+                     DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT,
+                                                GA->getOffset(), Flags));
 }
 
 SDValue
index 4a2bf2a..40a8f60 100644 (file)
@@ -15,6 +15,9 @@
 // WebAssembly Instruction Predicate Definitions.
 //===----------------------------------------------------------------------===//
 
+def IsPIC     : Predicate<"TM.isPositionIndependent()">;
+def IsNotPIC  : Predicate<"!TM.isPositionIndependent()">;
+
 def HasAddr32 : Predicate<"!Subtarget->hasAddr64()">;
 
 def HasAddr64 : Predicate<"Subtarget->hasAddr64()">;
@@ -67,14 +70,16 @@ def SDT_WebAssemblyCallSeqStart : SDCallSeqStart<[SDTCisVT<0, iPTR>,
                                                   SDTCisVT<1, iPTR>]>;
 def SDT_WebAssemblyCallSeqEnd :
     SDCallSeqEnd<[SDTCisVT<0, iPTR>, SDTCisVT<1, iPTR>]>;
-def SDT_WebAssemblyCall0    : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>;
-def SDT_WebAssemblyCall1    : SDTypeProfile<1, -1, [SDTCisPtrTy<1>]>;
-def SDT_WebAssemblyBrTable  : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>;
-def SDT_WebAssemblyArgument : SDTypeProfile<1, 1, [SDTCisVT<1, i32>]>;
-def SDT_WebAssemblyReturn   : SDTypeProfile<0, -1, []>;
-def SDT_WebAssemblyWrapper  : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
-                                                   SDTCisPtrTy<0>]>;
-def SDT_WebAssemblyThrow    : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>;
+def SDT_WebAssemblyCall0      : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>;
+def SDT_WebAssemblyCall1      : SDTypeProfile<1, -1, [SDTCisPtrTy<1>]>;
+def SDT_WebAssemblyBrTable    : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>;
+def SDT_WebAssemblyArgument   : SDTypeProfile<1, 1, [SDTCisVT<1, i32>]>;
+def SDT_WebAssemblyReturn     : SDTypeProfile<0, -1, []>;
+def SDT_WebAssemblyWrapper    : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
+                                                     SDTCisPtrTy<0>]>;
+def SDT_WebAssemblyWrapperPIC : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
+                                                     SDTCisPtrTy<0>]>;
+def SDT_WebAssemblyThrow      : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>;
 
 //===----------------------------------------------------------------------===//
 // WebAssembly-specific DAG Nodes.
@@ -101,6 +106,8 @@ def WebAssemblyreturn   : SDNode<"WebAssemblyISD::RETURN",
                                  SDT_WebAssemblyReturn, [SDNPHasChain]>;
 def WebAssemblywrapper  : SDNode<"WebAssemblyISD::Wrapper",
                                  SDT_WebAssemblyWrapper>;
+def WebAssemblywrapperPIC  : SDNode<"WebAssemblyISD::WrapperPIC",
+                                     SDT_WebAssemblyWrapperPIC>;
 def WebAssemblythrow : SDNode<"WebAssemblyISD::THROW", SDT_WebAssemblyThrow,
                               [SDNPHasChain, SDNPVariadic]>;
 
@@ -295,9 +302,20 @@ defm CONST_F64 : I<(outs F64:$res), (ins f64imm_op:$imm),
 } // isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1
 
 def : Pat<(i32 (WebAssemblywrapper tglobaladdr:$addr)),
-          (CONST_I32 tglobaladdr:$addr)>;
+          (CONST_I32 tglobaladdr:$addr)>, Requires<[IsNotPIC]>;
+
+def : Pat<(i32 (WebAssemblywrapper tglobaladdr:$addr)),
+          (GLOBAL_GET_I32 tglobaladdr:$addr)>, Requires<[IsPIC]>;
+
+def : Pat<(i32 (WebAssemblywrapperPIC tglobaladdr:$addr)),
+          (CONST_I32 tglobaladdr:$addr)>, Requires<[IsPIC]>;
+
+def : Pat<(i32 (WebAssemblywrapper texternalsym:$addr)),
+          (GLOBAL_GET_I32 texternalsym:$addr)>, Requires<[IsPIC]>;
+
 def : Pat<(i32 (WebAssemblywrapper texternalsym:$addr)),
-          (CONST_I32 texternalsym:$addr)>;
+          (CONST_I32 texternalsym:$addr)>, Requires<[IsNotPIC]>;
+
 def : Pat<(i32 (WebAssemblywrapper mcsym:$sym)), (CONST_I32 mcsym:$sym)>;
 def : Pat<(i64 (WebAssemblywrapper mcsym:$sym)), (CONST_I64 mcsym:$sym)>;
 
index 46a87f9..6916b16 100644 (file)
@@ -95,7 +95,7 @@ def : LoadPatImmOff<f64, load, or_is_add, LOAD_F64>;
 
 class LoadPatGlobalAddr<ValueType ty, PatFrag kind, NI inst> :
   Pat<(ty (kind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)))),
-      (inst 0, tglobaladdr:$off, I32:$addr)>;
+      (inst 0, tglobaladdr:$off, I32:$addr)>, Requires<[IsNotPIC]>;
 
 def : LoadPatGlobalAddr<i32, load, LOAD_I32>;
 def : LoadPatGlobalAddr<i64, load, LOAD_I64>;
@@ -113,7 +113,7 @@ def : LoadPatOffsetOnly<f64, load, LOAD_F64>;
 
 class LoadPatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> :
   Pat<(ty (kind (WebAssemblywrapper tglobaladdr:$off))),
-      (inst 0, tglobaladdr:$off, (CONST_I32 0))>;
+      (inst 0, tglobaladdr:$off, (CONST_I32 0))>, Requires<[IsNotPIC]>;
 
 def : LoadPatGlobalAddrOffOnly<i32, load, LOAD_I32>;
 def : LoadPatGlobalAddrOffOnly<i64, load, LOAD_I64>;
@@ -285,7 +285,7 @@ def : StorePatImmOff<f64, store, or_is_add, STORE_F64>;
 class StorePatGlobalAddr<ValueType ty, PatFrag kind, NI inst> :
   Pat<(kind ty:$val,
             (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off))),
-      (inst 0, tglobaladdr:$off, I32:$addr, ty:$val)>;
+      (inst 0, tglobaladdr:$off, I32:$addr, ty:$val)>, Requires<[IsNotPIC]>;
 def : StorePatGlobalAddr<i32, store, STORE_I32>;
 def : StorePatGlobalAddr<i64, store, STORE_I64>;
 def : StorePatGlobalAddr<f32, store, STORE_F32>;
@@ -301,7 +301,7 @@ def : StorePatOffsetOnly<f64, store, STORE_F64>;
 
 class StorePatGlobalAddrOffOnly<ValueType ty, PatFrag kind, NI inst> :
   Pat<(kind ty:$val, (WebAssemblywrapper tglobaladdr:$off)),
-      (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>;
+      (inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>, Requires<[IsNotPIC]>;
 def : StorePatGlobalAddrOffOnly<i32, store, STORE_I32>;
 def : StorePatGlobalAddrOffOnly<i64, store, STORE_I64>;
 def : StorePatGlobalAddrOffOnly<f32, store, STORE_F32>;
index e357842..9ca9779 100644 (file)
@@ -73,16 +73,18 @@ MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
   auto *WasmSym = cast<MCSymbolWasm>(Printer.GetExternalSymbolSymbol(Name));
   const WebAssemblySubtarget &Subtarget = Printer.getSubtarget();
 
-  // Except for the two exceptions (__stack_pointer and __cpp_exception), all
-  // other external symbols used by CodeGen are functions. It's OK to hardcode
-  // knowledge of specific symbols here; this method is precisely there for
-  // fetching the signatures of known Clang-provided symbols.
-  if (strcmp(Name, "__stack_pointer") == 0) {
+  // Except for certain known symbols, all symbols used by CodeGen are
+  // functions. It's OK to hardcode knowledge of specific symbols here; this
+  // method is precisely there for fetching the signatures of known
+  // Clang-provided symbols.
+  if (strcmp(Name, "__stack_pointer") == 0 ||
+      strcmp(Name, "__memory_base") == 0 || strcmp(Name, "__table_base") == 0) {
+    bool Mutable = strcmp(Name, "__stack_pointer") == 0;
     WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
     WasmSym->setGlobalType(wasm::WasmGlobalType{
         uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
                                       : wasm::WASM_TYPE_I32),
-        true});
+        Mutable});
     return WasmSym;
   }
 
@@ -118,19 +120,23 @@ MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
   return WasmSym;
 }
 
-MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(MCSymbol *Sym,
-                                                     int64_t Offset,
-                                                     bool IsFunc, bool IsGlob,
-                                                     bool IsEvent) const {
-  const MCExpr *Expr =
-      MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx);
+MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(
+    MCSymbol *Sym, int64_t Offset, bool IsFunc, unsigned TargetFlags) const {
+  MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None;
+  if (TargetFlags & WebAssemblyII::MO_GOT)
+    Kind = MCSymbolRefExpr::VK_GOT;
+  const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Kind, Ctx);
 
   if (Offset != 0) {
-    if (IsFunc)
+    if (TargetFlags & WebAssemblyII::MO_GOT)
+      report_fatal_error("GOT symbol references do not support offsets");
+    unsigned Type = TargetFlags & WebAssemblyII::MO_SYMBOL_MASK;
+    assert((Type == WebAssemblyII::MO_SYMBOL_FUNCTION) == IsFunc);
+    if (Type == WebAssemblyII::MO_SYMBOL_FUNCTION || IsFunc)
       report_fatal_error("Function addresses with offsets not supported");
-    if (IsGlob)
+    if (Type == WebAssemblyII::MO_SYMBOL_GLOBAL)
       report_fatal_error("Global indexes with offsets not supported");
-    if (IsEvent)
+    if (Type == WebAssemblyII::MO_SYMBOL_EVENT)
       report_fatal_error("Event indexes with offsets not supported");
     Expr =
         MCBinaryExpr::createAdd(Expr, MCConstantExpr::create(Offset, Ctx), Ctx);
@@ -230,11 +236,9 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
       break;
     }
     case MachineOperand::MO_GlobalAddress:
-      assert(MO.getTargetFlags() == WebAssemblyII::MO_NO_FLAG &&
-             "WebAssembly does not use target flags on GlobalAddresses");
       MCOp = lowerSymbolOperand(GetGlobalAddressSymbol(MO), MO.getOffset(),
                                 MO.getGlobal()->getValueType()->isFunctionTy(),
-                                false, false);
+                                MO.getTargetFlags());
       break;
     case MachineOperand::MO_ExternalSymbol:
       // The target flag indicates whether this is a symbol for a
@@ -242,18 +246,14 @@ void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
       assert((MO.getTargetFlags() & ~WebAssemblyII::MO_SYMBOL_MASK) == 0 &&
              "WebAssembly uses only symbol flags on ExternalSymbols");
       MCOp = lowerSymbolOperand(
-          GetExternalSymbolSymbol(MO), /*Offset=*/0,
-          (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_FUNCTION) != 0,
-          (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_GLOBAL) != 0,
-          (MO.getTargetFlags() & WebAssemblyII::MO_SYMBOL_EVENT) != 0);
+          GetExternalSymbolSymbol(MO), /*Offset=*/0, false, MO.getTargetFlags());
       break;
     case MachineOperand::MO_MCSymbol:
       // This is currently used only for LSDA symbols (GCC_except_table),
       // because global addresses or other external symbols are handled above.
       assert(MO.getTargetFlags() == 0 &&
              "WebAssembly does not use target flags on MCSymbol");
-      MCOp = lowerSymbolOperand(MO.getMCSymbol(), /*Offset=*/0, false, false,
-                                false);
+      MCOp = lowerSymbolOperand(MO.getMCSymbol(), /*Offset=*/0, false, MO.getTargetFlags());
       break;
     }
 
index 5753fb7..125ba1d 100644 (file)
@@ -33,7 +33,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyMCInstLower {
   MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const;
   MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const;
   MCOperand lowerSymbolOperand(MCSymbol *Sym, int64_t Offset, bool IsFunc,
-                               bool IsGlob, bool IsEvent) const;
+                               unsigned flags) const;
 
 public:
   WebAssemblyMCInstLower(MCContext &ctx, WebAssemblyAsmPrinter &printer)
index 86d6f15..2d50769 100644 (file)
@@ -1,4 +1,6 @@
-; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s -check-prefixes=CHECK,NON-PIC
+; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -relocation-model=pic | FileCheck %s -check-prefixes=CHECK,PIC
+
 
 ; Test folding constant offsets and symbols into load and store addresses under
 ; a variety of circumstances.
@@ -10,8 +12,10 @@ target triple = "wasm32-unknown-unknown"
 
 ; CHECK-LABEL: load_test0:
 ; CHECK-NEXT: .functype load_test0 () -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 0{{$}}
-; CHECK-NEXT: i32.load  $push1=, g+40($pop0){{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT:  i32.load  $push1=, g+40($pop0){{$}}
+; PIC-NEXT:   global.get $push0=, g@GOT{{$}}
+; PIC-NEXT:   i32.load  $push1=, 40($pop0){{$}}
 ; CHECK-NEXT: return    $pop1{{$}}
 define i32 @load_test0() {
   %t = load i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4
@@ -20,8 +24,10 @@ define i32 @load_test0() {
 
 ; CHECK-LABEL: load_test0_noinbounds:
 ; CHECK-NEXT: .functype load_test0_noinbounds () -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 0{{$}}
-; CHECK-NEXT: i32.load  $push1=, g+40($pop0){{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT:  i32.load  $push1=, g+40($pop0){{$}}
+; PIC-NEXT:   global.get $push0=, g@GOT{{$}}
+; PIC-NEXT:   i32.load  $push1=, 40($pop0){{$}}
 ; CHECK-NEXT: return    $pop1{{$}}
 define i32 @load_test0_noinbounds() {
   %t = load i32, i32* getelementptr ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4
@@ -34,7 +40,7 @@ define i32 @load_test0_noinbounds() {
 
 ; CHECK-LABEL: load_test1:
 ; CHECK-NEXT: .functype load_test1 (i32) -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
+; CHECK-NEX T: i32.const $push0=, 2{{$}}
 ; CHECK-NEX T: i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.load  $push2=, g+40($pop1){{$}}
 ; CHECK-NEX T: return    $pop2{{$}}
@@ -47,7 +53,7 @@ define i32 @load_test1(i32 %n) {
 
 ; CHECK-LABEL: load_test2:
 ; CHECK-NEXT: .functype load_test2 (i32) -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
+; CHECK-NEX T:  i32.const $push0=, 2{{$}}
 ; CHECK-NEX T: i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.load  $push2=, g+40($pop1){{$}}
 ; CHECK-NEX T: return    $pop2{{$}}
@@ -60,7 +66,7 @@ define i32 @load_test2(i32 %n) {
 
 ; CHECK-LABEL: load_test3:
 ; CHECK-NEXT: .functype load_test3 (i32) -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
+; CHECK-NEX T: i32.const $push0=, 2{{$}}
 ; CHECK-NEX T: i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.load  $push2=, g+40($pop1){{$}}
 ; CHECK-NEX T: return    $pop2{{$}}
@@ -73,7 +79,7 @@ define i32 @load_test3(i32 %n) {
 
 ; CHECK-LABEL: load_test4:
 ; CHECK-NEXT: .functype load_test4 (i32) -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
+; CHECK-NEX T: i32.const $push0=, 2{{$}}
 ; CHECK-NEX T: i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.load  $push2=, g+40($pop1){{$}}
 ; CHECK-NEX T: return    $pop2{{$}}
@@ -85,7 +91,7 @@ define i32 @load_test4(i32 %n) {
 
 ; CHECK-LABEL: load_test5:
 ; CHECK-NEXT: .functype load_test5 (i32) -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
+; CHECK-NEX T: i32.const $push0=, 2{{$}}
 ; CHECK-NEX T: i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.load  $push2=, g+40($pop1){{$}}
 ; CHECK-NEX T: return    $pop2{{$}}
@@ -97,7 +103,7 @@ define i32 @load_test5(i32 %n) {
 
 ; CHECK-LABEL: load_test6:
 ; CHECK-NEXT: .functype load_test6 (i32) -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
+; CHECK-NEX T:  i32.const $push0=, 2{{$}}
 ; CHECK-NEX T: i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.load  $push2=, g+40($pop1){{$}}
 ; CHECK-NEX T: return    $pop2{{$}}
@@ -110,7 +116,7 @@ define i32 @load_test6(i32 %n) {
 
 ; CHECK-LABEL: load_test7:
 ; CHECK-NEXT: .functype load_test7 (i32) -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
+; CHECK-NEX T: i32.const $push0=, 2{{$}}
 ; CHECK-NEX T: i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.load  $push2=, g+40($pop1){{$}}
 ; CHECK-NEX T: return    $pop2{{$}}
@@ -123,7 +129,7 @@ define i32 @load_test7(i32 %n) {
 
 ; CHECK-LABEL: load_test8:
 ; CHECK-NEXT: .functype load_test8 (i32) -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
+; CHECK-NEX T: i32.const $push0=, 2{{$}}
 ; CHECK-NEX T: i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.load  $push2=, g+40($pop1){{$}}
 ; CHECK-NEX T: return    $pop2{{$}}
@@ -135,10 +141,16 @@ define i32 @load_test8(i32 %n) {
 }
 
 ; CHECK-LABEL: load_test9:
-; CHECK-NEXT: .functype load_test9 () -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 0{{$}}
-; CHECK-NEXT: i32.load  $push1=, g-40($pop0){{$}}
-; CHECK-NEXT: return    $pop1{{$}}
+; CHECK-NEXT:  .functype load_test9 () -> (i32){{$}}
+; NON-PIC-NEXT: i32.const  $push0=, 0{{$}}
+; NON-PIC-NEXT: i32.load   $push1=, g-40($pop0){{$}}
+; NON-PIC_NEXT: return     $pop1{{$}}
+
+; PIC-NEXT: global.get $push1=, g@GOT{{$}}
+; PIC-NEXT: i32.const  $push0=, -40{{$}}
+; PIC-NEXT: i32.add    $push2=, $pop1, $pop0{{$}}
+; PIC-NEXT: i32.load   $push3=, 0($pop2)
+; PIC-NEXT: return     $pop3{{$}}
 define i32 @load_test9() {
   %t = load i32, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 1073741814), align 4
   ret i32 %t
@@ -146,12 +158,21 @@ define i32 @load_test9() {
 
 ; CHECK-LABEL: load_test10:
 ; CHECK-NEXT: .functype load_test10 (i32) -> (i32){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $0, $pop0{{$}}
-; CHECK-NEXT: i32.const $push2=, g-40{{$}}
-; CHECK-NEXT: i32.add   $push3=, $pop1, $pop2{{$}}
-; CHECK-NEXT: i32.load  $push4=, 0($pop3){{$}}
-; CHECK-NEXT: return    $pop4{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $0, $pop0{{$}}
+; NON-PIC-NEXT:  i32.const $push2=, g-40{{$}}
+; NON-PIC-NEXT:  i32.add   $push3=, $pop1, $pop2{{$}}
+; NON-PIC-NEXT:  i32.load  $push4=, 0($pop3){{$}}
+; NON-PIC-NEXT:  return    $pop4{{$}}
+
+; PIC-NEXT:   global.get $push2=, g@GOT{{$}}
+; PIC-NEXT:   i32.const $push0=, 2{{$}}
+; PIC-NEXT:   i32.shl   $push1=, $0, $pop0{{$}}
+; PIC-NEXT:   i32.add   $push3=, $pop2, $pop1{{$}}
+; PIC-NEXT:   i32.const $push4=, -40{{$}}
+; PIC-NEXT:   i32.add   $push5=, $pop3, $pop4{{$}}
+; PIC-NEXT:   i32.load  $push6=, 0($pop5){{$}}
+; PIC-NEXT:   return    $pop6{{$}}
 define i32 @load_test10(i32 %n) {
   %add = add nsw i32 %n, -10
   %arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
@@ -335,9 +356,11 @@ define i32 @load_test21(i32* %p, i32 %n) {
 
 ; CHECK-LABEL: store_test0:
 ; CHECK-NEXT: .functype store_test0 (i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 0{{$}}
-; CHECK-NEXT: i32.store g+40($pop0), $0{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT: i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT: i32.store g+40($pop0), $0{{$}}
+; PIC-NEXT:     global.get $push0=, g@GOT{{$}}
+; PIC-NEXT:     i32.store 40($pop0), $0
+; CHECK-NEXT:   return{{$}}
 define void @store_test0(i32 %i) {
   store i32 %i, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4
   ret void
@@ -345,9 +368,11 @@ define void @store_test0(i32 %i) {
 
 ; CHECK-LABEL: store_test0_noinbounds:
 ; CHECK-NEXT: .functype store_test0_noinbounds (i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 0{{$}}
-; CHECK-NEXT: i32.store g+40($pop0), $0{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT: i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT: i32.store g+40($pop0), $0{{$}}
+; PIC-NEXT:     global.get $push0=, g@GOT{{$}}
+; PIC-NEXT:     i32.store 40($pop0), $0{{$}}
+; CHECK-NEXT:  return{{$}}
 define void @store_test0_noinbounds(i32 %i) {
   store i32 %i, i32* getelementptr ([0 x i32], [0 x i32]* @g, i32 0, i32 10), align 4
   ret void
@@ -355,8 +380,8 @@ define void @store_test0_noinbounds(i32 %i) {
 
 ; CHECK-LABEL: store_test1:
 ; CHECK-NEXT: .functype store_test1 (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $0, $pop0{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}}
 ; CHECK-NEX T: return{{$}}
 define void @store_test1(i32 %n, i32 %i) {
@@ -368,8 +393,8 @@ define void @store_test1(i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test2:
 ; CHECK-NEXT: .functype store_test2 (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $0, $pop0{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}}
 ; CHECK-NEX T: return{{$}}
 define void @store_test2(i32 %n, i32 %i) {
@@ -381,8 +406,8 @@ define void @store_test2(i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test3:
 ; CHECK-NEXT: .functype store_test3 (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $0, $pop0{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}}
 ; CHECK-NEX T: return{{$}}
 define void @store_test3(i32 %n, i32 %i) {
@@ -394,8 +419,8 @@ define void @store_test3(i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test4:
 ; CHECK-NEXT: .functype store_test4 (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $0, $pop0{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}}
 ; CHECK-NEX T: return{{$}}
 define void @store_test4(i32 %n, i32 %i) {
@@ -406,8 +431,8 @@ define void @store_test4(i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test5:
 ; CHECK-NEXT: .functype store_test5 (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $0, $pop0{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}}
 ; CHECK-NEX T: return{{$}}
 define void @store_test5(i32 %n, i32 %i) {
@@ -418,8 +443,8 @@ define void @store_test5(i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test6:
 ; CHECK-NEXT: .functype store_test6 (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $0, $pop0{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}}
 ; CHECK-NEX T: return{{$}}
 define void @store_test6(i32 %n, i32 %i) {
@@ -431,8 +456,8 @@ define void @store_test6(i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test7:
 ; CHECK-NEXT: .functype store_test7 (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $0, $pop0{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}}
 ; CHECK-NEX T: return{{$}}
 define void @store_test7(i32 %n, i32 %i) {
@@ -444,8 +469,8 @@ define void @store_test7(i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test8:
 ; CHECK-NEXT: .functype store_test8 (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $0, $pop0{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $0, $pop0{{$}}
 ; CHECK-NEX T: i32.store g+40($pop1), $1{{$}}
 ; CHECK-NEX T: return{{$}}
 define void @store_test8(i32 %n, i32 %i) {
@@ -457,9 +482,13 @@ define void @store_test8(i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test9:
 ; CHECK-NEXT: .functype store_test9 (i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 0{{$}}
-; CHECK-NEXT: i32.store g-40($pop0), $0{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const  $push0=, 0{{$}}
+; NON-PIC-NEXT:  i32.store  g-40($pop0), $0{{$}}
+; PIC-NEXT:      global.get $push1=, g@GOT{{$}}
+; PIC-NEXT:      i32.const  $push0=, -40{{$}}
+; PIC-NEXT:      i32.add    $push2=, $pop1, $pop0{{$}}
+; PIC-NEXT:      i32.store  0($pop2), $0
+; CHECK-NEXT:  return{{$}}
 define void @store_test9(i32 %i) {
   store i32 %i, i32* getelementptr inbounds ([0 x i32], [0 x i32]* @g, i32 0, i32 1073741814), align 4
   ret void
@@ -467,12 +496,19 @@ define void @store_test9(i32 %i) {
 
 ; CHECK-LABEL: store_test10:
 ; CHECK-NEXT: .functype store_test10 (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $0, $pop0{{$}}
-; CHECK-NEXT: i32.const $push2=, g-40{{$}}
-; CHECK-NEXT: i32.add   $push3=, $pop1, $pop2{{$}}
-; CHECK-NEXT: i32.store 0($pop3), $1{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $0, $pop0{{$}}
+; NON-PIC-NEXT:  i32.const $push2=, g-40{{$}}
+; NON-PIC-NEXT:  i32.add   $push3=, $pop1, $pop2{{$}}
+; NON-PIC-NEXT:  i32.store 0($pop3), $1{{$}}
+; PIC-NEXT: global.get $push2=, g@GOT{{$}}
+; PIC-NEXT: i32.const  $push0=, 2{{$}}
+; PIC-NEXT: i32.shl    $push1=, $0, $pop0{{$}}
+; PIC-NEXT: i32.add    $push3=, $pop2, $pop1{{$}}
+; PIC-NEXT: i32.const  $push4=, -40{{$}}
+; PIC-NEXT: i32.add    $push5=, $pop3, $pop4{{$}}
+; PIC-NEXT: i32.store  0($pop5), $1{{$}}
+; CHECK-NEXT:  return{{$}}
 define void @store_test10(i32 %n, i32 %i) {
   %add = add nsw i32 %n, -10
   %arrayidx = getelementptr inbounds [0 x i32], [0 x i32]* @g, i32 0, i32 %add
@@ -482,8 +518,8 @@ define void @store_test10(i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test11:
 ; CHECK-NEXT: .functype store_test11 (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.store 40($0), $1{{$}}
-; CHECK-NEXT: return{{$}}
+; CHECK-NEXT:  i32.store 40($0), $1{{$}}
+; CHECK-NEXT:  return{{$}}
 define void @store_test11(i32* %p, i32 %i) {
   %arrayidx = getelementptr inbounds i32, i32* %p, i32 10
   store i32 %i, i32* %arrayidx, align 4
@@ -492,10 +528,10 @@ define void @store_test11(i32* %p, i32 %i) {
 
 ; CHECK-LABEL: store_test11_noinbounds:
 ; CHECK-NEXT: .functype store_test11_noinbounds (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 40{{$}}
-; CHECK-NEXT: i32.add   $push1=, $0, $pop0{{$}}
-; CHECK-NEXT: i32.store 0($pop1), $1{{$}}
-; CHECK-NEXT: return{{$}}
+; CHECK-NEXT:  i32.const $push0=, 40{{$}}
+; CHECK-NEXT:  i32.add   $push1=, $0, $pop0{{$}}
+; CHECK-NEXT:  i32.store 0($pop1), $1{{$}}
+; CHECK-NEXT:  return{{$}}
 define void @store_test11_noinbounds(i32* %p, i32 %i) {
   %arrayidx = getelementptr i32, i32* %p, i32 10
   store i32 %i, i32* %arrayidx, align 4
@@ -504,13 +540,13 @@ define void @store_test11_noinbounds(i32* %p, i32 %i) {
 
 ; CHECK-LABEL: store_test12:
 ; CHECK-NEXT: .functype store_test12 (i32, i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $1, $pop0{{$}}
-; CHECK-NEXT: i32.add   $push2=, $0, $pop1{{$}}
-; CHECK-NEXT: i32.const $push3=, 40{{$}}
-; CHECK-NEXT: i32.add   $push4=, $pop2, $pop3{{$}}
-; CHECK-NEXT: i32.store 0($pop4), $2{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $1, $pop0{{$}}
+; NON-PIC-NEXT:  i32.add   $push2=, $0, $pop1{{$}}
+; NON-PIC-NEXT:  i32.const $push3=, 40{{$}}
+; NON-PIC-NEXT:  i32.add   $push4=, $pop2, $pop3{{$}}
+; NON-PIC-NEXT:  i32.store 0($pop4), $2{{$}}
+; NON-PIC-NEXT:  return{{$}}
 define void @store_test12(i32* %p, i32 %n, i32 %i) {
   %add = add nsw i32 %n, 10
   %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add
@@ -520,13 +556,13 @@ define void @store_test12(i32* %p, i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test13:
 ; CHECK-NEXT: .functype store_test13 (i32, i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $1, $pop0{{$}}
-; CHECK-NEXT: i32.add   $push2=, $0, $pop1{{$}}
-; CHECK-NEXT: i32.const $push3=, 40{{$}}
-; CHECK-NEXT: i32.add   $push4=, $pop2, $pop3{{$}}
-; CHECK-NEXT: i32.store 0($pop4), $2{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $1, $pop0{{$}}
+; NON-PIC-NEXT:  i32.add   $push2=, $0, $pop1{{$}}
+; NON-PIC-NEXT:  i32.const $push3=, 40{{$}}
+; NON-PIC-NEXT:  i32.add   $push4=, $pop2, $pop3{{$}}
+; NON-PIC-NEXT:  i32.store 0($pop4), $2{{$}}
+; NON-PIC-NEXT:  return{{$}}
 define void @store_test13(i32* %p, i32 %n, i32 %i) {
   %add = add nsw i32 10, %n
   %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add
@@ -536,11 +572,11 @@ define void @store_test13(i32* %p, i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test14:
 ; CHECK-NEXT: .functype store_test14 (i32, i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $1, $pop0{{$}}
-; CHECK-NEXT: i32.add   $push2=, $0, $pop1{{$}}
-; CHECK-NEXT: i32.store 40($pop2), $2{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $1, $pop0{{$}}
+; NON-PIC-NEXT:  i32.add   $push2=, $0, $pop1{{$}}
+; NON-PIC-NEXT:  i32.store 40($pop2), $2{{$}}
+; NON-PIC-NEXT:  return{{$}}
 define void @store_test14(i32* %p, i32 %n, i32 %i) {
   %add.ptr = getelementptr inbounds i32, i32* %p, i32 %n
   %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10
@@ -550,13 +586,13 @@ define void @store_test14(i32* %p, i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test15:
 ; CHECK-NEXT: .functype store_test15 (i32, i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $1, $pop0{{$}}
-; CHECK-NEXT: i32.add   $push2=, $0, $pop1{{$}}
-; CHECK-NEXT: i32.const $push3=, 40{{$}}
-; CHECK-NEXT: i32.add   $push4=, $pop2, $pop3{{$}}
-; CHECK-NEXT: i32.store 0($pop4), $2{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $1, $pop0{{$}}
+; NON-PIC-NEXT:  i32.add   $push2=, $0, $pop1{{$}}
+; NON-PIC-NEXT:  i32.const $push3=, 40{{$}}
+; NON-PIC-NEXT:  i32.add   $push4=, $pop2, $pop3{{$}}
+; NON-PIC-NEXT:  i32.store 0($pop4), $2{{$}}
+; NON-PIC-NEXT:  return{{$}}
 define void @store_test15(i32* %p, i32 %n, i32 %i) {
   %add.ptr = getelementptr inbounds i32, i32* %p, i32 10
   %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n
@@ -566,13 +602,13 @@ define void @store_test15(i32* %p, i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test16:
 ; CHECK-NEXT: .functype store_test16 (i32, i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $1, $pop0{{$}}
-; CHECK-NEXT: i32.add   $push2=, $0, $pop1{{$}}
-; CHECK-NEXT: i32.const $push3=, 40{{$}}
-; CHECK-NEXT: i32.add   $push4=, $pop2, $pop3{{$}}
-; CHECK-NEXT: i32.store 0($pop4), $2{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $1, $pop0{{$}}
+; NON-PIC-NEXT:  i32.add   $push2=, $0, $pop1{{$}}
+; NON-PIC-NEXT:  i32.const $push3=, 40{{$}}
+; NON-PIC-NEXT:  i32.add   $push4=, $pop2, $pop3{{$}}
+; NON-PIC-NEXT:  i32.store 0($pop4), $2{{$}}
+; NON-PIC-NEXT:  return{{$}}
 define void @store_test16(i32* %p, i32 %n, i32 %i) {
   %add.ptr = getelementptr inbounds i32, i32* %p, i32 10
   %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 %n
@@ -582,13 +618,13 @@ define void @store_test16(i32* %p, i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test17:
 ; CHECK-NEXT: .functype store_test17 (i32, i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $1, $pop0{{$}}
-; CHECK-NEXT: i32.add   $push2=, $0, $pop1{{$}}
-; CHECK-NEXT: i32.const $push3=, 40{{$}}
-; CHECK-NEXT: i32.add   $push4=, $pop2, $pop3{{$}}
-; CHECK-NEXT: i32.store 0($pop4), $2{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $1, $pop0{{$}}
+; NON-PIC-NEXT:  i32.add   $push2=, $0, $pop1{{$}}
+; NON-PIC-NEXT:  i32.const $push3=, 40{{$}}
+; NON-PIC-NEXT:  i32.add   $push4=, $pop2, $pop3{{$}}
+; NON-PIC-NEXT:  i32.store 0($pop4), $2{{$}}
+; NON-PIC-NEXT:  return{{$}}
 define void @store_test17(i32* %p, i32 %n, i32 %i) {
   %add = add nsw i32 %n, 10
   %add.ptr = getelementptr inbounds i32, i32* %p, i32 %add
@@ -598,11 +634,11 @@ define void @store_test17(i32* %p, i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test18:
 ; CHECK-NEXT: .functype store_test18 (i32, i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $1, $pop0{{$}}
-; CHECK-NEXT: i32.add   $push2=, $0, $pop1{{$}}
-; CHECK-NEXT: i32.store 40($pop2), $2{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $1, $pop0{{$}}
+; NON-PIC-NEXT:  i32.add   $push2=, $0, $pop1{{$}}
+; NON-PIC-NEXT:  i32.store 40($pop2), $2{{$}}
+; NON-PIC-NEXT:  return{{$}}
 define void @store_test18(i32* %p, i32 %n, i32 %i) {
   %add.ptr = getelementptr inbounds i32, i32* %p, i32 %n
   %add.ptr1 = getelementptr inbounds i32, i32* %add.ptr, i32 10
@@ -612,13 +648,13 @@ define void @store_test18(i32* %p, i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test19:
 ; CHECK-NEXT: .functype store_test19 (i32, i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $1, $pop0{{$}}
-; CHECK-NEXT: i32.add   $push2=, $0, $pop1{{$}}
-; CHECK-NEXT: i32.const $push3=, 40{{$}}
-; CHECK-NEXT: i32.add   $push4=, $pop2, $pop3{{$}}
-; CHECK-NEXT: i32.store 0($pop4), $2{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $1, $pop0{{$}}
+; NON-PIC-NEXT:  i32.add   $push2=, $0, $pop1{{$}}
+; NON-PIC-NEXT:  i32.const $push3=, 40{{$}}
+; NON-PIC-NEXT:  i32.add   $push4=, $pop2, $pop3{{$}}
+; NON-PIC-NEXT:  i32.store 0($pop4), $2{{$}}
+; NON-PIC-NEXT:  return{{$}}
 define void @store_test19(i32* %p, i32 %n, i32 %i) {
   %add = add nsw i32 10, %n
   %add.ptr = getelementptr inbounds i32, i32* %p, i32 %add
@@ -628,10 +664,10 @@ define void @store_test19(i32* %p, i32 %n, i32 %i) {
 
 ; CHECK-LABEL: store_test20:
 ; CHECK-NEXT: .functype store_test20 (i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, -40{{$}}
-; CHECK-NEXT: i32.add   $push1=, $0, $pop0{{$}}
-; CHECK-NEXT: i32.store 0($pop1), $1{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, -40{{$}}
+; NON-PIC-NEXT:  i32.add   $push1=, $0, $pop0{{$}}
+; NON-PIC-NEXT:  i32.store 0($pop1), $1{{$}}
+; NON-PIC-NEXT:  return{{$}}
 define void @store_test20(i32* %p, i32 %i) {
   %arrayidx = getelementptr inbounds i32, i32* %p, i32 -10
   store i32 %i, i32* %arrayidx, align 4
@@ -640,13 +676,13 @@ define void @store_test20(i32* %p, i32 %i) {
 
 ; CHECK-LABEL: store_test21:
 ; CHECK-NEXT: .functype store_test21 (i32, i32, i32) -> (){{$}}
-; CHECK-NEXT: i32.const $push0=, 2{{$}}
-; CHECK-NEXT: i32.shl   $push1=, $1, $pop0{{$}}
-; CHECK-NEXT: i32.add   $push2=, $0, $pop1{{$}}
-; CHECK-NEXT: i32.const $push3=, -40{{$}}
-; CHECK-NEXT: i32.add   $push4=, $pop2, $pop3{{$}}
-; CHECK-NEXT: i32.store 0($pop4), $2{{$}}
-; CHECK-NEXT: return{{$}}
+; NON-PIC-NEXT:  i32.const $push0=, 2{{$}}
+; NON-PIC-NEXT:  i32.shl   $push1=, $1, $pop0{{$}}
+; NON-PIC-NEXT:  i32.add   $push2=, $0, $pop1{{$}}
+; NON-PIC-NEXT:  i32.const $push3=, -40{{$}}
+; NON-PIC-NEXT:  i32.add   $push4=, $pop2, $pop3{{$}}
+; NON-PIC-NEXT:  i32.store 0($pop4), $2{{$}}
+; NON-PIC-NEXT:  return{{$}}
 define void @store_test21(i32* %p, i32 %n, i32 %i) {
   %add = add nsw i32 %n, -10
   %arrayidx = getelementptr inbounds i32, i32* %p, i32 %add
diff --git a/llvm/test/CodeGen/WebAssembly/call-pic.ll b/llvm/test/CodeGen/WebAssembly/call-pic.ll
new file mode 100644 (file)
index 0000000..7fd93c6
--- /dev/null
@@ -0,0 +1,52 @@
+; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -relocation-model=pic -fast-isel=1 | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -relocation-model=pic -fast-isel=0 | FileCheck %s
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+declare i32 @foo()
+declare i32 @bar()
+declare hidden i32 @hidden_function();
+
+@indirect_func = global i32 ()* @foo
+
+define void @call_indirect_func() {
+; CHECK-LABEL: call_indirect_func:
+; CHECK:      global.get $push[[L0:[0-9]+]]=, __memory_base{{$}}
+; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, indirect_func{{$}}
+; CHECK-NEXT: i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}}
+; CHECK-NEXT: i32.load $push[[L3:[0-9]+]]=, 0($pop[[L2]]){{$}}
+; CHECK-NEXT: i32.call_indirect $push[[L4:[0-9]+]]=, $pop[[L3]]{{$}}
+  %1 = load i32 ()*, i32 ()** @indirect_func, align 4
+  %call = call i32 %1()
+  ret void
+}
+
+define void @call_direct() {
+; CHECK-LABEL: call_direct:
+; CHECK: .functype call_direct () -> ()
+; CHECK-NEXT: i32.call $push0=, foo{{$}}
+; CHECK-NEXT: drop $pop0{{$}}
+; CHECK-NEXT: return{{$}}
+  %call = call i32 @foo()
+  ret void
+}
+
+define i8* @get_function_address() {
+; CHECK-LABEL: get_function_address:
+; CHECK:       global.get $push[[L0:[0-9]+]]=, bar@GOT{{$}}
+; CHECK-NEXT:  return $pop[[L0]]{{$}}
+; CHECK-NEXT:  end_function{{$}}
+
+  ret i8* bitcast (i32 ()* @bar to i8*)
+}
+
+define i8* @get_function_address_hidden() {
+; CHECK-LABEL: get_function_address_hidden:
+; CHECK:       global.get $push[[L0:[0-9]+]]=, __table_base{{$}}
+; CHECK-NEXT:  i32.const $push[[L1:[0-9]+]]=, hidden_function{{$}}
+; CHECK-NEXT:  i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}}
+; CHECK-NEXT:  return $pop[[L2]]{{$}}
+; CHECK-NEXT:  end_function{{$}}
+
+  ret i8* bitcast (i32 ()* @hidden_function to i8*)
+}
diff --git a/llvm/test/CodeGen/WebAssembly/load-store-pic.ll b/llvm/test/CodeGen/WebAssembly/load-store-pic.ll
new file mode 100644 (file)
index 0000000..bb449ca
--- /dev/null
@@ -0,0 +1,153 @@
+; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s -check-prefixes=NON-PIC,CHECK
+; RUN: llc < %s -asm-verbose=false -relocation-model=pic -fast-isel -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s -check-prefixes=PIC,CHECK
+; RUN: llc < %s -asm-verbose=false -relocation-model=pic -fast-isel=false -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s -check-prefixes=PIC,CHECK
+
+; Test that globals assemble as expected with -fPIC.
+; We test here both with and without fast-isel.
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+@hidden_global         = external hidden global i32
+@hidden_global_array   = external hidden global [10 x i32]
+@external_global       = external        global i32
+@external_global_array = external        global [10 x i32]
+
+declare i32 @foo();
+
+; For hidden symbols PIC code needs to offset all loads and stores
+; by the value of the __memory_base global
+
+define i32 @load_hidden_global() {
+; CHECK-LABEL: load_hidden_global:
+; PIC:         global.get $push[[L0:[0-9]+]]=, __memory_base{{$}}
+; PIC-NEXT:    i32.const $push[[L1:[0-9]+]]=, hidden_global{{$}}
+; PIC-NEXT:    i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}}
+; PIC-NEXT:    i32.load $push[[L3:[0-9]+]]=, 0($pop[[L2]]){{$}}
+
+; NON-PIC:         i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT:    i32.load $push1=, hidden_global($pop0){{$}}
+; CHECK-NEXT:    end_function
+
+  %1 = load i32, i32* @hidden_global
+  ret i32 %1
+}
+
+define i32 @load_hidden_global_offset() {
+; CHECK-LABEL: load_hidden_global_offset:
+; PIC:         global.get $push[[L0:[0-9]+]]=, __memory_base{{$}}
+; PIC-NEXT:    i32.const $push[[L1:[0-9]+]]=, hidden_global_array{{$}}
+; PIC-NEXT:    i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1:[0-9]+]]{{$}}
+; PIC-NEXT:    i32.const $push[[L3:[0-9]+]]=, 20{{$}}
+; PIC-NEXT:    i32.add $push[[L4:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}}
+; PIC-NEXT:    i32.load $push{{[0-9]+}}=, 0($pop[[L4]]){{$}}
+
+; NON-PIC:     i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT:i32.load  $push1=, hidden_global_array+20($pop0){{$}}
+; CHECK-NEXT:  end_function
+
+  %1 = getelementptr [10 x i32], [10 x i32]* @hidden_global_array, i32 0, i32 5
+  %2 = load i32, i32* %1
+  ret i32 %2
+}
+
+; Store to a hidden global
+
+define void @store_hidden_global(i32 %n) {
+; CHECK-LABEL: store_hidden_global:
+; PIC:         global.get $push[[L0:[0-9]+]]=, __memory_base{{$}}
+; PIC-NEXT:    i32.const $push[[L1:[0-9]+]]=, hidden_global{{$}}
+; PIC-NEXT:    i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}}
+; PIC-NEXT:    i32.store 0($pop[[L2]]), $0{{$}}
+
+; NON-PIC:       i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT:  i32.store hidden_global($pop0), $0{{$}}
+; CHECK-NEXT:    end_function
+
+  store i32 %n, i32* @hidden_global
+  ret void
+}
+
+define void @store_hidden_global_offset(i32 %n) {
+; CHECK-LABEL: store_hidden_global_offset:
+; PIC:         global.get $push[[L0:[0-9]+]]=, __memory_base{{$}}
+; PIC-NEXT:    i32.const $push[[L1:[0-9]+]]=, hidden_global_array{{$}}
+; PIC-NEXT:    i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}}
+; PIC-NEXT:    i32.const $push[[L3:[0-9]+]]=, 20{{$}}
+; PIC-NEXT:    i32.add $push[[L4:[0-9]+]]=, $pop[[L2]], $pop[[L3]]{{$}}
+; PIC-NEXT:    i32.store 0($pop[[L4]]), $0{{$}}
+
+; NON-PIC:      i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT: i32.store hidden_global_array+20($pop0), $0{{$}}
+; CHECK-NEXT:   end_function
+
+  %1 = getelementptr [10 x i32], [10 x i32]* @hidden_global_array, i32 0, i32 5
+  store i32 %n, i32* %1
+  ret void
+}
+
+; For non-hidden globals PIC code has to load the address from a wasm global
+; using the @GOT relocation type.
+
+
+define i32 @load_external_global() {
+; CHECK-LABEL:  load_external_global:
+; PIC:          global.get $push[[L0:[0-9]+]]=, external_global@GOT{{$}}
+; PIC-NEXT:     i32.load $push{{[0-9]+}}=, 0($pop[[L0]]){{$}}
+
+; NON-PIC:      i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT: i32.load $push1=, external_global($pop0){{$}}
+; CHECK-NEXT:   end_function
+
+  %1 = load i32, i32* @external_global
+  ret i32 %1
+}
+
+define i32 @load_external_global_offset() {
+; CHECK-LABEL:  load_external_global_offset:
+; PIC:          global.get $push[[L0:[0-9]+]]=, external_global_array@GOT{{$}}
+; PIC-NEXT:     i32.const $push[[L1:[0-9]+]]=, 20{{$}}
+; PIC-NEXT:     i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}}
+; PIC-NEXT:     i32.load $push{{[0-9]+}}=, 0($pop[[L2]]){{$}}
+
+; NON-PIC:      i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT: i32.load $push1=, external_global_array+20($pop0){{$}}
+; CHECK-NEXT:   end_function
+
+  %1 = getelementptr [10 x i32], [10 x i32]* @external_global_array, i32 0, i32 5
+  %2 = load i32, i32* %1
+  ret i32 %2
+}
+
+; Store to a non-hidden global via the wasm global.
+
+define void @store_external_global(i32 %n) {
+; CHECK-LABEL:  store_external_global:
+; PIC:          global.get $push[[L0:[0-9]+]]=, external_global@GOT{{$}}
+; PIC-NEXT:     i32.store 0($pop[[L0]]), $0{{$}}
+
+; NON-PIC:      i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT: i32.store external_global($pop0), $0{{$}}
+; CHECK-NEXT:   end_function
+
+  store i32 %n, i32* @external_global
+  ret void
+}
+
+define void @store_external_global_offset(i32 %n) {
+; CHECK-LABEL:  store_external_global_offset:
+; PIC:          global.get $push[[L0:[0-9]+]]=, external_global_array@GOT{{$}}
+; PIC-NEXT:     i32.const $push[[L1:[0-9]+]]=, 20{{$}}
+; PIC-NEXT:     i32.add $push[[L2:[0-9]+]]=, $pop[[L0]], $pop[[L1]]{{$}}
+; PIC-NEXT:     i32.store 0($pop[[L2]]), $0{{$}}
+
+; NON-PIC:      i32.const $push0=, 0{{$}}
+; NON-PIC-NEXT: i32.store external_global_array+20($pop0), $0{{$}}
+; CHECK-NEXT:   end_function
+
+  %1 = getelementptr [10 x i32], [10 x i32]* @external_global_array, i32 0, i32 5
+  store i32 %n, i32* %1
+  ret void
+}
+
+; PIC: .globaltype __memory_base, i32
diff --git a/llvm/test/MC/WebAssembly/reloc-pic.s b/llvm/test/MC/WebAssembly/reloc-pic.s
new file mode 100644 (file)
index 0000000..49aaa85
--- /dev/null
@@ -0,0 +1,99 @@
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj < %s | obj2yaml | FileCheck %s
+
+# Verify that @GOT relocation entryes 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@GOT
+    i32.load    0
+    end_function
+
+load_default_func:
+    .functype   load_default_func () -> (i32)
+    global.get  default_func@GOT
+    i32.load    0
+    end_function
+
+.size default_data, 4
+.functype default_func () -> (i32)
+
+# CHECK:      --- !WASM
+# CHECK-NEXT: FileHeader:
+# CHECK-NEXT:   Version:         0x00000001
+# CHECK-NEXT: Sections:
+# CHECK-NEXT:   - Type:            TYPE
+# CHECK-NEXT:     Signatures:
+# CHECK-NEXT:       - Index:           0
+# CHECK-NEXT:         ReturnType:      I32
+# CHECK-NEXT:         ParamTypes:      []
+# 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:           Initial:         0x00000000
+# CHECK-NEXT:       - Module:          env
+# CHECK-NEXT:         Field:           __indirect_function_table
+# CHECK-NEXT:         Kind:            TABLE
+# CHECK-NEXT:         Table:
+# CHECK-NEXT:           ElemType:        FUNCREF
+# CHECK-NEXT:           Limits:
+# CHECK-NEXT:             Initial:         0x00000000
+# CHECK-NEXT:       - Module:          env
+# CHECK-NEXT:         Field:           default_func
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         SigIndex:        0
+# 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 ]
+# CHECK-NEXT:   - Type:            CODE
+# CHECK-NEXT:     Relocations:
+# CHECK-NEXT:       - Type:            R_WASM_GLOBAL_INDEX_LEB
+# CHECK-NEXT:         Index:           1
+# CHECK-NEXT:         Offset:          0x00000004
+# CHECK-NEXT:       - Type:            R_WASM_GLOBAL_INDEX_LEB
+# CHECK-NEXT:         Index:           3
+# CHECK-NEXT:         Offset:          0x00000010
+# CHECK-NEXT:     Functions:
+# CHECK-NEXT:       - Index:           1
+# CHECK-NEXT:         Locals:          []
+# CHECK-NEXT:         Body:            2380808080002800000B
+# CHECK-NEXT:       - Index:           2
+# CHECK-NEXT:         Locals:          []
+# CHECK-NEXT:         Body:            2381808080002800000B
+# 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: ...