diff --git a/lld/test/wasm/compress-relocs.ll b/lld/test/wasm/compress-relocs.ll
--- a/lld/test/wasm/compress-relocs.ll
+++ b/lld/test/wasm/compress-relocs.ll
@@ -1,5 +1,5 @@
 ; RUN: llc -filetype=obj %s -o %t.o
-; RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/call-indirect.s -o %t2.o
+; RUN: llvm-mc -mattr=+reference-types -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/call-indirect.s -o %t2.o
 ; RUN: wasm-ld --export-dynamic -o %t.wasm %t2.o %t.o
 ; RUN: obj2yaml %t.wasm | FileCheck %s
 ; RUN: wasm-ld --export-dynamic -O2 -o %t-opt.wasm %t2.o %t.o
@@ -22,5 +22,5 @@
 
 ; ERROR: wasm-ld: error: --compress-relocations is incompatible with output debug information. Please pass --strip-debug or --strip-all
 
-; CHECK:    Body:            410028028088808000118080808000001A410028028488808000118180808000001A0B
+; CHECK:    Body:            41002802808880800011808080800080808080001A41002802848880800011818080800080808080001A0B
 ; COMPRESS: Body:            4100280280081100001A4100280284081101001A0B
diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h b/llvm/include/llvm/MC/MCSymbolWasm.h
--- a/llvm/include/llvm/MC/MCSymbolWasm.h
+++ b/llvm/include/llvm/MC/MCSymbolWasm.h
@@ -18,6 +18,7 @@
   bool IsWeak = false;
   bool IsHidden = false;
   bool IsComdat = false;
+  bool OmitFromLinkingSection = false;
   mutable bool IsUsedInInitArray = false;
   mutable bool IsUsedInGOT = false;
   Optional<StringRef> ImportModule;
@@ -75,6 +76,12 @@
   bool isComdat() const { return IsComdat; }
   void setComdat(bool isComdat) { IsComdat = isComdat; }
 
+  // wasm-ld understands a finite set of symbol types.  This flag allows the
+  // compiler to avoid emitting symbol table entries that would confuse the
+  // linker, unless the user specifically requests the feature.
+  bool omitFromLinkingSection() const { return OmitFromLinkingSection; }
+  void setOmitFromLinkingSection() { OmitFromLinkingSection = true; }
+
   bool hasImportModule() const { return ImportModule.hasValue(); }
   StringRef getImportModule() const {
     if (ImportModule.hasValue())
diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -405,12 +405,17 @@
 
 void WasmObjectWriter::executePostLayoutBinding(MCAssembler &Asm,
                                                 const MCAsmLayout &Layout) {
-  // As a stopgap measure until call_indirect instructions start explicitly
-  // referencing the indirect function table via TABLE_NUMBER relocs, ensure
-  // that the indirect function table import makes it to the output if anything
-  // in the compilation unit has caused it to be present.
-  if (auto *Sym = Asm.getContext().lookupSymbol("__indirect_function_table"))
-    Asm.registerSymbol(*Sym);
+  // Some compilation units require the indirect function table to be present
+  // but don't explicitly reference it.  This is the case for call_indirect
+  // without the reference-types feature, and also function bitcasts in all
+  // cases.  In those cases the __indirect_function_table has the
+  // WASM_SYMBOL_NO_STRIP attribute.  Here we make sure this symbol makes it to
+  // the assembler, if needed.
+  if (auto *Sym = Asm.getContext().lookupSymbol("__indirect_function_table")) {
+    const auto *WasmSym = static_cast<const MCSymbolWasm *>(Sym);
+    if (WasmSym->isNoStrip())
+      Asm.registerSymbol(*Sym);
+  }
 
   // Build a map of sections to the function that defines them, for use
   // in recordRelocation.
@@ -509,21 +514,18 @@
       Type == wasm::R_WASM_TABLE_INDEX_I32 ||
       Type == wasm::R_WASM_TABLE_INDEX_I64) {
     // TABLE_INDEX relocs implicitly use the default indirect function table.
+    // We require the function table to have already been defined.
     auto TableName = "__indirect_function_table";
     MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(TableName));
-    if (Sym) {
-      if (!Sym->isFunctionTable())
-        Ctx.reportError(
-            Fixup.getLoc(),
-            "symbol '__indirect_function_table' is not a function table");
+    if (!Sym || !Sym->isFunctionTable()) {
+      Ctx.reportError(
+          Fixup.getLoc(),
+          "symbol '__indirect_function_table' is not a function table");
     } else {
-      Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(TableName));
-      Sym->setFunctionTable();
-      // The default function table is synthesized by the linker.
-      Sym->setUndefined();
+      // Ensure that __indirect_function_table reaches the output.
+      Sym->setNoStrip();
+      Asm.registerSymbol(*Sym);
     }
-    Sym->setUsedInReloc();
-    Asm.registerSymbol(*Sym);
   }
 
   // Relocation other than R_WASM_TYPE_INDEX_LEB are required to be
@@ -1211,6 +1213,9 @@
   if (Sym.isSection())
     return false;
 
+  if (Sym.omitFromLinkingSection())
+    return false;
+
   return true;
 }
 
@@ -1674,10 +1679,6 @@
       WS.setIndex(InvalidIndex);
       continue;
     }
-    if (WS.isTable() && WS.getName() == "__indirect_function_table") {
-      // For the moment, don't emit table symbols -- wasm-ld can't handle them.
-      continue;
-    }
     LLVM_DEBUG(dbgs() << "adding to symtab: " << WS << "\n");
 
     uint32_t Flags = 0;
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
@@ -171,9 +171,6 @@
 
 static MCSymbolWasm *GetOrCreateFunctionTableSymbol(MCContext &Ctx,
                                                     const StringRef &Name) {
-  // FIXME: Duplicates functionality from
-  // MC/WasmObjectWriter::recordRelocation, as well as WebAssemblyCodegen's
-  // WebAssembly:getOrCreateFunctionTableSymbol.
   MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name));
   if (Sym) {
     if (!Sym->isFunctionTable())
@@ -223,6 +220,7 @@
   };
   std::vector<NestingType> NestingStack;
 
+  MCSymbolWasm *DefaultFunctionTable = nullptr;
   MCSymbol *LastFunctionLabel = nullptr;
 
 public:
@@ -233,6 +231,15 @@
     setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
   }
 
+  void Initialize(MCAsmParser &Parser) override {
+    MCAsmParserExtension::Initialize(Parser);
+
+    DefaultFunctionTable = GetOrCreateFunctionTableSymbol(
+        getContext(), "__indirect_function_table");
+    if (!STI->checkFeatures("+reference-types"))
+      DefaultFunctionTable->setOmitFromLinkingSection();
+  }
+
 #define GET_ASSEMBLER_HEADER
 #include "WebAssemblyGenAsmMatcher.inc"
 
@@ -480,6 +487,39 @@
         WebAssemblyOperand::IntOp{static_cast<int64_t>(BT)}));
   }
 
+  bool addFunctionTableOperand(OperandVector &Operands, MCSymbolWasm *Sym,
+                               SMLoc StartLoc, SMLoc EndLoc) {
+    const auto *Val = MCSymbolRefExpr::create(Sym, getContext());
+    Operands.push_back(std::make_unique<WebAssemblyOperand>(
+        WebAssemblyOperand::Symbol, StartLoc, EndLoc,
+        WebAssemblyOperand::SymOp{Val}));
+    return false;
+  }
+
+  bool addFunctionTableOperand(OperandVector &Operands, StringRef TableName,
+                               SMLoc StartLoc, SMLoc EndLoc) {
+    return addFunctionTableOperand(
+        Operands, GetOrCreateFunctionTableSymbol(getContext(), TableName),
+        StartLoc, EndLoc);
+  }
+
+  bool addDefaultFunctionTableOperand(OperandVector &Operands, SMLoc StartLoc,
+                                      SMLoc EndLoc) {
+    if (STI->checkFeatures("+reference-types")) {
+      return addFunctionTableOperand(Operands, DefaultFunctionTable, StartLoc,
+                                     EndLoc);
+    } else {
+      // For the MVP there is at most one table whose number is 0, but we can't
+      // write a table symbol or issue relocations.  Instead we just ensure the
+      // table is live and write a zero.
+      getStreamer().emitSymbolAttribute(DefaultFunctionTable, MCSA_NoDeadStrip);
+      Operands.push_back(std::make_unique<WebAssemblyOperand>(
+          WebAssemblyOperand::Integer, StartLoc, EndLoc,
+          WebAssemblyOperand::IntOp{0}));
+      return false;
+    }
+  }
+
   bool ParseInstruction(ParseInstructionInfo & /*Info*/, StringRef Name,
                         SMLoc NameLoc, OperandVector &Operands) override {
     // Note: Name does NOT point into the sourcecode, but to a local, so
@@ -516,6 +556,7 @@
     bool ExpectBlockType = false;
     bool ExpectFuncType = false;
     bool ExpectHeapType = false;
+    bool ExpectFunctionTable = false;
     if (Name == "block") {
       push(Block);
       ExpectBlockType = true;
@@ -562,15 +603,7 @@
         return true;
     } else if (Name == "call_indirect" || Name == "return_call_indirect") {
       ExpectFuncType = true;
-      // Ensure that the object file has a __indirect_function_table import, as
-      // we call_indirect against it.
-      auto &Ctx = getStreamer().getContext();
-      MCSymbolWasm *Sym =
-          GetOrCreateFunctionTableSymbol(Ctx, "__indirect_function_table");
-      // Until call_indirect emits TABLE_NUMBER relocs against this symbol, mark
-      // it as NO_STRIP so as to ensure that the indirect function table makes
-      // it to linked output.
-      Sym->setNoStrip();
+      ExpectFunctionTable = true;
     } else if (Name == "ref.null") {
       ExpectHeapType = true;
     }
@@ -586,7 +619,7 @@
         return true;
       // Got signature as block type, don't need more
       ExpectBlockType = false;
-      auto &Ctx = getStreamer().getContext();
+      auto &Ctx = getContext();
       // The "true" here will cause this to be a nameless symbol.
       MCSymbol *Sym = Ctx.createTempSymbol("typeindex", true);
       auto *WasmSym = cast<MCSymbolWasm>(Sym);
@@ -598,6 +631,16 @@
       Operands.push_back(std::make_unique<WebAssemblyOperand>(
           WebAssemblyOperand::Symbol, Loc.getLoc(), Loc.getEndLoc(),
           WebAssemblyOperand::SymOp{Expr}));
+
+      // Allow additional operands after the signature, notably for
+      // call_indirect against a named table.
+      if (Lexer.isNot(AsmToken::EndOfStatement)) {
+        if (expect(AsmToken::Comma, ","))
+          return true;
+        if (Lexer.is(AsmToken::EndOfStatement)) {
+          return error("Unexpected trailing comma");
+        }
+      }
     }
 
     while (Lexer.isNot(AsmToken::EndOfStatement)) {
@@ -623,6 +666,11 @@
               WebAssemblyOperand::Integer, Id.getLoc(), Id.getEndLoc(),
               WebAssemblyOperand::IntOp{static_cast<int64_t>(HeapType)}));
           Parser.Lex();
+        } else if (ExpectFunctionTable) {
+          if (addFunctionTableOperand(Operands, Id.getString(), Id.getLoc(),
+                                      Id.getEndLoc()))
+            return true;
+          Parser.Lex();
         } else {
           // Assume this identifier is a label.
           const MCExpr *Val;
@@ -689,6 +737,12 @@
       // Support blocks with no operands as default to void.
       addBlockTypeOperand(Operands, NameLoc, WebAssembly::BlockType::Void);
     }
+    if (ExpectFunctionTable && Operands.size() == 2) {
+      // If call_indirect doesn't specify a target table, supply one.
+      if (addDefaultFunctionTableOperand(Operands, NameLoc,
+                                         SMLoc::getFromPointer(Name.end())))
+        return true;
+    }
     Parser.Lex();
     return false;
   }
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
@@ -68,7 +68,7 @@
     for (auto I = Start, E = MI->getNumOperands(); I < E; ++I) {
       if (MI->getOpcode() == WebAssembly::CALL_INDIRECT &&
           I - Start == NumVariadicDefs) {
-        // Skip type and flags arguments when printing for tests
+        // Skip type and table arguments when printing for tests.
         ++I;
         continue;
       }
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -23,6 +23,7 @@
 #include "WebAssemblyMachineFunctionInfo.h"
 #include "WebAssemblyRegisterInfo.h"
 #include "WebAssemblyTargetMachine.h"
+#include "WebAssemblyUtilities.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/BinaryFormat/Wasm.h"
@@ -171,19 +172,25 @@
 
 void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
   for (auto &It : OutContext.getSymbols()) {
-    // Emit a .globaltype and .eventtype declaration.
+    // Emit .globaltype, .eventtype, or .tabletype declarations.
     auto Sym = cast<MCSymbolWasm>(It.getValue());
     if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_GLOBAL)
       getTargetStreamer()->emitGlobalType(Sym);
     else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_EVENT)
       getTargetStreamer()->emitEventType(Sym);
+    else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_TABLE)
+      getTargetStreamer()->emitTableType(Sym);
   }
 
   DenseSet<MCSymbol *> InvokeSymbols;
+  bool HasAddressTakenFunction = false;
   for (const auto &F : M) {
     if (F.isIntrinsic())
       continue;
 
+    if (F.hasAddressTaken())
+      HasAddressTakenFunction = true;
+
     // Emit function type info for all undefined functions
     if (F.isDeclarationForLinker()) {
       SmallVector<MVT, 4> Results;
@@ -242,6 +249,18 @@
     }
   }
 
+  // When a function's address is taken, a TABLE_INDEX relocation is emitted
+  // against the function symbol at the use site.  However the relocation
+  // doesn't explicitly refer to the table.  In the future we may want to
+  // define a new kind of reloc against both the function and the table, so
+  // that the linker can see that the function symbol keeps the table alive,
+  // but for now manually mark the table as live.
+  if (HasAddressTakenFunction) {
+    MCSymbolWasm *FunctionTable =
+        WebAssembly::getOrCreateFunctionTableSymbol(OutContext, Subtarget);
+    OutStreamer->emitSymbolAttribute(FunctionTable, MCSA_NoDeadStrip);
+  }
+
   for (const auto &G : M.globals()) {
     if (!G.hasInitializer() && G.hasExternalLinkage()) {
       if (G.getValueType()->isSized()) {
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
@@ -869,19 +869,20 @@
   if (IsDirect) {
     MIB.addGlobalAddress(Func);
   } else {
-    // Add placeholders for the type index and immediate flags
+    // Placehoder for the type index.
     MIB.addImm(0);
-    MIB.addImm(0);
-
-    // Ensure that the object file has a __indirect_function_table import, as we
-    // call_indirect against it.
-    MCSymbolWasm *Sym = WebAssembly::getOrCreateFunctionTableSymbol(
-        MF->getMMI().getContext(), "__indirect_function_table");
-    // Until call_indirect emits TABLE_NUMBER relocs against this symbol, mark
-    // it as NO_STRIP so as to ensure that the indirect function table makes it
-    // to linked output.
-    Sym->setNoStrip();
-
+    // The table into which this call_indirect indexes.
+    MCSymbolWasm *Table = WebAssembly::getOrCreateFunctionTableSymbol(
+        MF->getMMI().getContext(), Subtarget);
+    if (Subtarget->hasReferenceTypes()) {
+      MIB.addSym(Table);
+    } else {
+      // Otherwise for the MVP there is at most one table whose number is 0, but
+      // we can't write a table symbol or issue relocations.  Instead we just
+      // ensure the table is live.
+      Table->setNoStrip();
+      MIB.addImm(0);
+    }
     // See if we must truncate the function pointer.
     // CALL_INDIRECT takes an i32, but in wasm64 we represent function pointers
     // as 64-bit for uniformity with other pointer types.
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -426,9 +426,10 @@
   return DoneMBB;
 }
 
-static MachineBasicBlock *LowerCallResults(MachineInstr &CallResults,
-                                           DebugLoc DL, MachineBasicBlock *BB,
-                                           const TargetInstrInfo &TII) {
+static MachineBasicBlock *
+LowerCallResults(MachineInstr &CallResults, DebugLoc DL, MachineBasicBlock *BB,
+                 const WebAssemblySubtarget *Subtarget,
+                 const TargetInstrInfo &TII) {
   MachineInstr &CallParams = *CallResults.getPrevNode();
   assert(CallParams.getOpcode() == WebAssembly::CALL_PARAMS);
   assert(CallResults.getOpcode() == WebAssembly::CALL_RESULTS ||
@@ -476,19 +477,21 @@
   for (auto Def : CallResults.defs())
     MIB.add(Def);
 
-  // Add placeholders for the type index and immediate flags
   if (IsIndirect) {
+    // Placeholder for the type index.
     MIB.addImm(0);
-    MIB.addImm(0);
-
-    // Ensure that the object file has a __indirect_function_table import, as we
-    // call_indirect against it.
-    MCSymbolWasm *Sym = WebAssembly::getOrCreateFunctionTableSymbol(
-        MF.getContext(), "__indirect_function_table");
-    // Until call_indirect emits TABLE_NUMBER relocs against this symbol, mark
-    // it as NO_STRIP so as to ensure that the indirect function table makes it
-    // to linked output.
-    Sym->setNoStrip();
+    // The table into which this call_indirect indexes.
+    MCSymbolWasm *Table =
+        WebAssembly::getOrCreateFunctionTableSymbol(MF.getContext(), Subtarget);
+    if (Subtarget->hasReferenceTypes()) {
+      MIB.addSym(Table);
+    } else {
+      // For the MVP there is at most one table whose number is 0, but we can't
+      // write a table symbol or issue relocations.  Instead we just ensure the
+      // table is live and write a zero.
+      Table->setNoStrip();
+      MIB.addImm(0);
+    }
   }
 
   for (auto Use : CallParams.uses())
@@ -535,7 +538,7 @@
                         WebAssembly::I64_TRUNC_U_F64);
   case WebAssembly::CALL_RESULTS:
   case WebAssembly::RET_CALL_RESULTS:
-    return LowerCallResults(MI, DL, BB, TII);
+    return LowerCallResults(MI, DL, BB, Subtarget, TII);
   }
 }
 
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td
@@ -48,6 +48,9 @@
   I<(outs), (ins variable_ops), (outs), (ins), [],
      "return_call_results", "return_call_results", -1>;
 
+// Note that instructions with variable_ops have custom printers in
+// WebAssemblyInstPrinter.cpp.
+
 let variadicOpsAreDefs = 1 in
 defm CALL :
   I<(outs), (ins function32_op:$callee, variable_ops),
@@ -56,9 +59,12 @@
 
 let variadicOpsAreDefs = 1 in
 defm CALL_INDIRECT :
- I<(outs), (ins TypeIndex:$type, i32imm:$flags, variable_ops),
-   (outs), (ins TypeIndex:$type, i32imm:$flags), [],
-   "call_indirect", "call_indirect\t$type", 0x11>;
+  I<(outs),
+    (ins TypeIndex:$type, table32_op:$table, variable_ops),
+    (outs),
+    (ins TypeIndex:$type, table32_op:$table),
+    [],
+    "call_indirect", "call_indirect\t$type, $table", 0x11>;
 
 let isReturn = 1, isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in
 defm RET_CALL :
@@ -69,9 +75,9 @@
 
 let isReturn = 1 in
 defm RET_CALL_INDIRECT :
-  I<(outs), (ins TypeIndex:$type, i32imm:$flags, variable_ops),
-    (outs), (ins TypeIndex:$type, i32imm:$flags), [],
-    "return_call_indirect\t", "return_call_indirect\t$type",
+  I<(outs), (ins TypeIndex:$type, table32_op:$table, variable_ops),
+    (outs), (ins TypeIndex:$type, table32_op:$table), [],
+    "return_call_indirect\t", "return_call_indirect\t$type, $table",
     0x13>,
   Requires<[HasTailCall]>;
 
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
--- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
@@ -161,6 +161,8 @@
       report_fatal_error("Global indexes with offsets not supported");
     if (WasmSym->isEvent())
       report_fatal_error("Event indexes with offsets not supported");
+    if (WasmSym->isTable())
+      report_fatal_error("Table indexes with offsets not supported");
 
     Expr = MCBinaryExpr::createAdd(
         Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h
@@ -24,6 +24,7 @@
 class MCSymbolWasm;
 class StringRef;
 class WebAssemblyFunctionInfo;
+class WebAssemblySubtarget;
 
 namespace WebAssembly {
 
@@ -41,10 +42,11 @@
 /// instruction.
 const MachineOperand &getCalleeOp(const MachineInstr &MI);
 
-/// Returns the operand number of a callee, assuming the argument is a call
-/// instruction.
-MCSymbolWasm *getOrCreateFunctionTableSymbol(MCContext &Ctx,
-                                             const StringRef &Name);
+/// Returns the __indirect_function_table, for use in call_indirect and in
+/// function bitcasts.
+MCSymbolWasm *
+getOrCreateFunctionTableSymbol(MCContext &Ctx,
+                               const WebAssemblySubtarget *Subtarget);
 
 /// Find a catch instruction from an EH pad. Returns null if no catch
 /// instruction found or the catch is in an invalid location.
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
--- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp
@@ -98,11 +98,9 @@
   }
 }
 
-MCSymbolWasm *
-WebAssembly::getOrCreateFunctionTableSymbol(MCContext &Ctx,
-                                            const StringRef &Name) {
-  // FIXME: Duplicates functionality from
-  // MC/WasmObjectWriter::recordRelocation.
+MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol(
+    MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
+  StringRef Name = "__indirect_function_table";
   MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name));
   if (Sym) {
     if (!Sym->isFunctionTable())
@@ -113,6 +111,9 @@
     // The default function table is synthesized by the linker.
     Sym->setUndefined();
   }
+  // MVP object files can't have symtab entries for tables.
+  if (!Subtarget->hasReferenceTypes())
+    Sym->setOmitFromLinkingSection();
   return Sym;
 }
 
diff --git a/llvm/test/CodeGen/WebAssembly/function-pointer64.ll b/llvm/test/CodeGen/WebAssembly/function-pointer64.ll
--- a/llvm/test/CodeGen/WebAssembly/function-pointer64.ll
+++ b/llvm/test/CodeGen/WebAssembly/function-pointer64.ll
@@ -1,4 +1,5 @@
 ; RUN: llc < %s -asm-verbose=false -O2 | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -mattr=+reference-types -O2 | FileCheck --check-prefix=REF %s
 ; RUN: llc < %s -asm-verbose=false -O2 --filetype=obj | obj2yaml | FileCheck --check-prefix=YAML %s
 
 ; This tests pointer features that may codegen differently in wasm64.
@@ -34,14 +35,16 @@
 ; CHECK-NEXT: i32.const 1
 ; CHECK-NEXT: local.get 0
 ; CHECK-NEXT: i32.wrap_i64
-; CHECK-NEXT: call_indirect (i32) -> ()
+; CHECK-NEXT: call_indirect (i32) -> (), 0
+; REF:        call_indirect (i32) -> (), __indirect_function_table
 
 ; CHECK:      .functype test () -> ()
 ; CHECK-NEXT: i64.const bar
 ; CHECK-NEXT: call foo
 
 
-; Check we're emitting a 64-bit reloc for `i64.const bar` and the global.
+; Check we're emitting a 64-bit relocs for the call_indirect, the
+; `i64.const bar` reference in code, and the global.
 
 ; YAML:      Memory:
 ; YAML-NEXT:   Flags:   [ IS_64 ]
@@ -51,6 +54,9 @@
 ; YAML:      - Type:   R_WASM_TABLE_INDEX_SLEB64
 ; YAML-NEXT:   Index:  0
 ; YAML-NEXT:   Offset: 0x16
+; YAML:      - Type:   R_WASM_TABLE_INDEX_SLEB64
+; YAML-NEXT:   Index:  0
+; YAML-NEXT:   Offset: 0x29
 
 ; YAML:      - Type:   DATA
 ; YAML:      - Type:   R_WASM_TABLE_INDEX_I64
diff --git a/llvm/test/CodeGen/WebAssembly/multivalue.ll b/llvm/test/CodeGen/WebAssembly/multivalue.ll
--- a/llvm/test/CodeGen/WebAssembly/multivalue.ll
+++ b/llvm/test/CodeGen/WebAssembly/multivalue.ll
@@ -1,4 +1,5 @@
 ; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mattr=+multivalue,+tail-call | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -mattr=+reference-types,+multivalue,+tail-call | FileCheck --check-prefix REF %s
 ; RUN: llc < %s -asm-verbose=false -verify-machineinstrs -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+multivalue,+tail-call | FileCheck %s --check-prefix REGS
 ; RUN: llc < %s --filetype=obj -mattr=+multivalue,+tail-call | obj2yaml | FileCheck %s --check-prefix OBJ
 
@@ -57,7 +58,8 @@
 ; CHECK-LABEL: pair_call_indirect:
 ; CHECK-NEXT: .functype pair_call_indirect (i32) -> (i32, i64)
 ; CHECK-NEXT: local.get 0{{$}}
-; CHECK-NEXT: call_indirect () -> (i32, i64){{$}}
+; CHECK-NEXT: call_indirect () -> (i32, i64), 0{{$}}
+; REF:        call_indirect () -> (i32, i64), __indirect_function_table{{$}}
 ; CHECK-NEXT: end_function{{$}}
 ; REGS: call_indirect $push{{[0-9]+}}=, $push{{[0-9]+}}=, $0{{$}}
 define %pair @pair_call_indirect(%pair()* %f) {
diff --git a/llvm/test/MC/WebAssembly/basic-assembly.s b/llvm/test/MC/WebAssembly/basic-assembly.s
--- a/llvm/test/MC/WebAssembly/basic-assembly.s
+++ b/llvm/test/MC/WebAssembly/basic-assembly.s
@@ -1,6 +1,6 @@
-# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+atomics,+unimplemented-simd128,+nontrapping-fptoint,+exception-handling < %s | FileCheck %s
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+reference-types,atomics,+unimplemented-simd128,+nontrapping-fptoint,+exception-handling < %s | FileCheck %s
 # Check that it converts to .o without errors, but don't check any output:
-# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+atomics,+unimplemented-simd128,+nontrapping-fptoint,+exception-handling -o %t.o < %s
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+reference-types,+atomics,+unimplemented-simd128,+nontrapping-fptoint,+exception-handling -o %t.o < %s
 
 
 empty_func:
@@ -156,7 +156,7 @@
 # CHECK-NEXT:      i64.const   1234
 # CHECK-NEXT:      call        something2
 # CHECK-NEXT:      i32.const   0
-# CHECK-NEXT:      call_indirect (i32, f64) -> ()
+# CHECK-NEXT:      call_indirect (i32, f64) -> (), __indirect_function_table
 # CHECK-NEXT:      i32.const   1
 # CHECK-NEXT:      i32.add
 # CHECK-NEXT:      local.tee   0
diff --git a/llvm/test/MC/WebAssembly/call-indirect-relocs.s b/llvm/test/MC/WebAssembly/call-indirect-relocs.s
new file mode 100644
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/call-indirect-relocs.s
@@ -0,0 +1,83 @@
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+reference-types < %s | FileCheck --check-prefix=CHECK %s
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+reference-types -filetype=obj < %s | obj2yaml | FileCheck -check-prefix=BIN %s
+
+test0:
+    .functype   test0 () -> ()
+    i32.const 42
+    f64.const 2.5
+    i32.const   0
+    call_indirect (i32, f64) -> (), empty_fref_table
+    end_function
+
+.tabletype empty_fref_table, funcref
+empty_fref_table:
+
+
+# CHECK:           .text
+# CHECK-LABEL: test0:
+# CHECK-NEXT:      .functype   test0 () -> ()
+# CHECK-NEXT:      i32.const   42
+# CHECK-NEXT:      f64.const   0x1.4p1
+# CHECK-NEXT:      i32.const   0
+# CHECK-NEXT:      call_indirect (i32, f64) -> (), empty_fref_table
+# CHECK-NEXT:      end_function
+
+# CHECK:           .tabletype empty_fref_table, funcref
+# CHECK: empty_fref_table:
+
+# BIN: --- !WASM
+# BIN-NEXT: FileHeader:
+# BIN-NEXT:   Version:         0x1
+# BIN-NEXT: Sections:
+# BIN-NEXT:   - Type:            TYPE
+# BIN-NEXT:     Signatures:
+# BIN-NEXT:       - Index:           0
+# BIN-NEXT:         ParamTypes:      []
+# BIN-NEXT:         ReturnTypes:     []
+# BIN-NEXT:       - Index:           1
+# BIN-NEXT:         ParamTypes:
+# BIN-NEXT:           - I32
+# BIN-NEXT:           - F64
+# BIN-NEXT:         ReturnTypes:     []
+# BIN-NEXT:   - Type:            IMPORT
+# BIN-NEXT:     Imports:
+# BIN-NEXT:       - Module:          env
+# BIN-NEXT:         Field:           __linear_memory
+# BIN-NEXT:         Kind:            MEMORY
+# BIN-NEXT:         Memory:
+# BIN-NEXT:           Initial:         0x0
+# BIN-NEXT:   - Type:            FUNCTION
+# BIN-NEXT:     FunctionTypes:   [ 0 ]
+# BIN-NEXT:   - Type:            TABLE
+# BIN-NEXT:     Tables:
+# BIN-NEXT:       - Index:           0
+# BIN-NEXT:         ElemType:        FUNCREF
+# BIN-NEXT:         Limits:
+# BIN-NEXT:           Initial:         0x0
+# BIN-NEXT:   - Type:            CODE
+# BIN-NEXT:     Relocations:
+# BIN-NEXT:       - Type:            R_WASM_TYPE_INDEX_LEB
+# BIN-NEXT:         Index:           1
+# BIN-NEXT:         Offset:          0x11
+# BIN-NEXT:       - Type:            R_WASM_TABLE_NUMBER_LEB
+# BIN-NEXT:         Index:           1
+# BIN-NEXT:         Offset:          0x16
+# BIN-NEXT:     Functions:
+# BIN-NEXT:       - Index:           0
+# BIN-NEXT:         Locals:          []
+# BIN-NEXT:         Body:            412A440000000000000440410011818080800080808080000B
+# BIN-NEXT:   - Type:            CUSTOM
+# BIN-NEXT:     Name:            linking
+# BIN-NEXT:     Version:         2
+# BIN-NEXT:     SymbolTable:
+# BIN-NEXT:       - Index:           0
+# BIN-NEXT:         Kind:            FUNCTION
+# BIN-NEXT:         Name:            test0
+# BIN-NEXT:         Flags:           [ BINDING_LOCAL ]
+# BIN-NEXT:         Function:        0
+# BIN-NEXT:       - Index:           1
+# BIN-NEXT:         Kind:            TABLE
+# BIN-NEXT:         Name:            empty_fref_table
+# BIN-NEXT:         Flags:           [ BINDING_LOCAL ]
+# BIN-NEXT:         Table:           0
+# BIN-NEXT: ...
diff --git a/llvm/test/MC/WebAssembly/function-alias.ll b/llvm/test/MC/WebAssembly/function-alias.ll
--- a/llvm/test/MC/WebAssembly/function-alias.ll
+++ b/llvm/test/MC/WebAssembly/function-alias.ll
@@ -1,4 +1,5 @@
 ; RUN: llc -filetype=obj %s -o - | llvm-readobj --symbols - | FileCheck %s
+; RUN: llc -filetype=obj %s -mattr=+reference-types -o - | llvm-readobj --symbols - | FileCheck --check-prefix=REF %s
 
 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
 target triple = "wasm32-unknown-unknown-wasm"
@@ -42,3 +43,44 @@
 ; CHECK-NEXT:     ElementIndex: 0x0
 ; CHECK-NEXT:   }
 ; CHECK-NEXT: ]
+
+; REF:      Symbols [
+; REF-NEXT:   Symbol {
+; REF-NEXT:     Name: func
+; REF-NEXT:     Type: FUNCTION (0x0)
+; REF-NEXT:     Flags [ (0x0)
+; REF-NEXT:     ]
+; REF-NEXT:     ElementIndex: 0x0
+; REF-NEXT:   }
+; REF-NEXT:   Symbol {
+; REF-NEXT:     Name: bar2
+; REF-NEXT:     Type: FUNCTION (0x0)
+; REF-NEXT:     Flags [ (0x0)
+; REF-NEXT:     ]
+; REF-NEXT:     ElementIndex: 0x0
+; REF-NEXT:   }
+; REF-NEXT:   Symbol {
+; REF-NEXT:     Name: foo
+; REF-NEXT:     Type: FUNCTION (0x0)
+; REF-NEXT:     Flags [ (0x0)
+; REF-NEXT:     ]
+; REF-NEXT:     ElementIndex: 0x0
+; REF-NEXT:   }
+; REF-NEXT:   Symbol {
+; REF-NEXT:     Name: bar
+; REF-NEXT:     Type: FUNCTION (0x0)
+; REF-NEXT:     Flags [ (0x0)
+; REF-NEXT:     ]
+; REF-NEXT:     ElementIndex: 0x0
+; REF-NEXT:   }
+; REF-NEXT:   Symbol {
+; REF-NEXT:     Name: __indirect_function_table
+; REF-NEXT:     Type: TABLE (0x5)
+; REF-NEXT:     Flags [ (0x90)
+; REF-NEXT:       NO_STRIP (0x80)
+; REF-NEXT:       UNDEFINED (0x10)
+; REF-NEXT:     ]
+; REF-NEXT:     ImportModule: env
+; REF-NEXT:     ElementIndex: 0x0
+; REF-NEXT:   }
+; REF-NEXT: ]
diff --git a/llvm/test/MC/WebAssembly/reloc-code.ll b/llvm/test/MC/WebAssembly/reloc-code.ll
--- a/llvm/test/MC/WebAssembly/reloc-code.ll
+++ b/llvm/test/MC/WebAssembly/reloc-code.ll
@@ -1,4 +1,5 @@
 ; RUN: llc -filetype=obj %s -o - | llvm-readobj -r --expand-relocs - | FileCheck %s
+; RUN: llc -filetype=obj -mattr=+reference-types %s -o - | llvm-readobj -r --expand-relocs - | FileCheck --check-prefix=REF %s
 
 target triple = "wasm32-unknown-unknown"
 
@@ -59,3 +60,51 @@
 ; CHECK-NEXT:     }
 ; CHECK-NEXT:   }
 ; CHECK-NEXT: ]
+
+; REF: Format: WASM
+; REF: Relocations [
+; REF-NEXT:   Section (5) CODE {
+; REF-NEXT:     Relocation {
+; REF-NEXT:       Type: R_WASM_MEMORY_ADDR_LEB (3)
+; REF-NEXT:       Offset: 0x9
+; REF-NEXT:       Symbol: b
+; REF-NEXT:       Addend: 0
+; REF-NEXT:     }
+; REF-NEXT:     Relocation {
+; REF-NEXT:       Type: R_WASM_MEMORY_ADDR_LEB (3)
+; REF-NEXT:       Offset: 0x14
+; REF-NEXT:       Symbol: a
+; REF-NEXT:       Addend: 0
+; REF-NEXT:     }
+; REF-NEXT:     Relocation {
+; REF-NEXT:       Type: R_WASM_TYPE_INDEX_LEB (6)
+; REF-NEXT:       Offset: 0x1A
+; REF-NEXT:       Index: 0x1
+; REF-NEXT:     }
+; REF-NEXT:     Relocation {
+; REF-NEXT:       Type: R_WASM_TABLE_NUMBER_LEB (20)
+; REF-NEXT:       Offset: 0x1F
+; REF-NEXT:       Symbol: __indirect_function_table
+; REF-NEXT:     }
+; REF-NEXT:     Relocation {
+; REF-NEXT:       Type: R_WASM_TYPE_INDEX_LEB (6)
+; REF-NEXT:       Offset: 0x28
+; REF-NEXT:       Index: 0x0
+; REF-NEXT:     }
+; REF-NEXT:     Relocation {
+; REF-NEXT:       Type: R_WASM_TABLE_NUMBER_LEB (20)
+; REF-NEXT:       Offset: 0x2D
+; REF-NEXT:       Symbol: __indirect_function_table
+; REF-NEXT:     }
+; REF-NEXT:     Relocation {
+; REF-NEXT:       Type: R_WASM_FUNCTION_INDEX_LEB (0)
+; REF-NEXT:       Offset: 0x35
+; REF-NEXT:       Symbol: c
+; REF-NEXT:     }
+; REF-NEXT:     Relocation {
+; REF-NEXT:       Type: R_WASM_FUNCTION_INDEX_LEB (0)
+; REF-NEXT:       Offset: 0x3C
+; REF-NEXT:       Symbol: d
+; REF-NEXT:     }
+; REF-NEXT:   }
+; REF-NEXT: ]
diff --git a/llvm/test/MC/WebAssembly/reloc-pic.s b/llvm/test/MC/WebAssembly/reloc-pic.s
--- a/llvm/test/MC/WebAssembly/reloc-pic.s
+++ b/llvm/test/MC/WebAssembly/reloc-pic.s
@@ -1,4 +1,5 @@
 # RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj < %s | obj2yaml | FileCheck %s
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+reference-types -filetype=obj < %s | obj2yaml | FileCheck --check-prefix=REF %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
@@ -190,6 +191,11 @@
 # 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
diff --git a/llvm/test/MC/WebAssembly/tail-call-encodings.s b/llvm/test/MC/WebAssembly/tail-call-encodings.s
--- a/llvm/test/MC/WebAssembly/tail-call-encodings.s
+++ b/llvm/test/MC/WebAssembly/tail-call-encodings.s
@@ -1,4 +1,5 @@
 # RUN: llvm-mc -show-encoding -triple=wasm32-unknown-unknown -mattr=+tail-call < %s | FileCheck %s
+# RUN: llvm-mc -show-encoding -triple=wasm32-unknown-unknown -mattr=+reference-types,+tail-call < %s | FileCheck --check-prefix=REF %s
 
 bar1:
     .functype bar1 () -> ()
@@ -16,7 +17,8 @@
 foo2:
     .functype foo2 () -> ()
 
-    # CHECK: return_call_indirect (i32) -> (i32) # encoding: [0x13,
+    # REF: return_call_indirect (i32) -> (i32), __indirect_function_table # encoding: [0x13,
+    # CHECK: return_call_indirect (i32) -> (i32), 0 # encoding: [0x13,
     # CHECK-NEXT: fixup A - offset: 1, value: .Ltypeindex0@TYPEINDEX, kind: fixup_uleb128_i32
     return_call_indirect (i32) -> (i32)
 
diff --git a/llvm/test/MC/WebAssembly/type-index.s b/llvm/test/MC/WebAssembly/type-index.s
--- a/llvm/test/MC/WebAssembly/type-index.s
+++ b/llvm/test/MC/WebAssembly/type-index.s
@@ -1,8 +1,8 @@
-# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+unimplemented-simd128,+nontrapping-fptoint,+exception-handling < %s | FileCheck %s
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+reference-types,+unimplemented-simd128,+nontrapping-fptoint,+exception-handling < %s | FileCheck %s
 # Check that it converts to .o without errors:
-# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+unimplemented-simd128,+nontrapping-fptoint,+exception-handling < %s | obj2yaml | FileCheck -check-prefix=BIN %s
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+reference-types,+unimplemented-simd128,+nontrapping-fptoint,+exception-handling < %s | obj2yaml | FileCheck -check-prefix=BIN %s
 
-# Minimal test for type indices in call_indirect.
+# Minimal test for type indices and table references in call_indirect.
 
 test0:
     .functype   test0 (i32) -> (i32)
@@ -53,10 +53,13 @@
 # BIN-NEXT:       - Type:            R_WASM_TYPE_INDEX_LEB
 # BIN-NEXT:         Index:           1
 # BIN-NEXT:         Offset:          0x4
+# BIN-NEXT:       - Type:            R_WASM_TABLE_NUMBER_LEB
+# BIN-NEXT:         Index:           1
+# BIN-NEXT:         Offset:          0x9
 # BIN-NEXT:     Functions:
 # BIN-NEXT:       - Index:           0
 # BIN-NEXT:         Locals:          []
-# BIN-NEXT:         Body:            118180808000000B
+# BIN-NEXT:         Body:            11818080800080808080000B
 # BIN-NEXT:   - Type:            CUSTOM
 # BIN-NEXT:     Name:            linking
 # BIN-NEXT:     Version:         2
@@ -66,4 +69,9 @@
 # BIN-NEXT:         Name:            test0
 # BIN-NEXT:         Flags:           [ BINDING_LOCAL ]
 # BIN-NEXT:         Function:        0
+# BIN-NEXT:       - Index:           1
+# BIN-NEXT:         Kind:            TABLE
+# BIN-NEXT:         Name:            __indirect_function_table
+# BIN-NEXT:         Flags:           [ UNDEFINED ]
+# BIN-NEXT:         Table:           0
 # BIN-NEXT: ...
diff --git a/llvm/test/MC/WebAssembly/weak-alias.s b/llvm/test/MC/WebAssembly/weak-alias.s
--- a/llvm/test/MC/WebAssembly/weak-alias.s
+++ b/llvm/test/MC/WebAssembly/weak-alias.s
@@ -1,5 +1,5 @@
-# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -o %t.o < %s
-# RUN: obj2yaml %t.o | FileCheck %s
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj < %s | obj2yaml | FileCheck --check-prefix=CHECK %s
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+reference-types -filetype=obj < %s | obj2yaml | FileCheck --check-prefix=REF %s
 
 # 'foo_alias()' is weak alias of function 'foo()'
 # 'bar_alias' is weak alias of global variable 'bar'
@@ -78,7 +78,7 @@
 # CHECK:        - Type:            TYPE
 # CHECK-NEXT:     Signatures:
 # CHECK-NEXT:       - Index:           0
-# CHECK-NEXT:         ParamTypes:
+# CHECK-NEXT:         ParamTypes:      []
 # CHECK-NEXT:         ReturnTypes:
 # CHECK-NEXT:           - I32
 # CHECK-NEXT:   - Type:            IMPORT
@@ -128,19 +128,19 @@
 # CHECK-NEXT:         Offset:          0x37
 # CHECK-NEXT:     Functions:
 # CHECK-NEXT:       - Index:           0
-# CHECK-NEXT:         Locals:
+# CHECK-NEXT:         Locals:          []
 # CHECK-NEXT:         Body:            41000B
 # CHECK-NEXT:       - Index:           1
-# CHECK-NEXT:         Locals:
+# CHECK-NEXT:         Locals:          []
 # CHECK-NEXT:         Body:            1080808080000B
 # CHECK-NEXT:       - Index:           2
-# CHECK-NEXT:         Locals:
+# CHECK-NEXT:         Locals:          []
 # CHECK-NEXT:         Body:            1080808080000B
 # CHECK-NEXT:       - Index:           3
-# CHECK-NEXT:         Locals:
+# CHECK-NEXT:         Locals:          []
 # CHECK-NEXT:         Body:            410028028880808000118080808000000B
 # CHECK-NEXT:       - Index:           4
-# CHECK-NEXT:         Locals:
+# CHECK-NEXT:         Locals:          []
 # CHECK-NEXT:         Body:            410028029080808000118080808000000B
 # CHECK-NEXT:   - Type:            DATA
 # CHECK-NEXT:     Relocations:
@@ -231,17 +231,195 @@
 # CHECK-NEXT:       - Index:           0
 # CHECK-NEXT:         Name:            .data.bar
 # CHECK-NEXT:         Alignment:       3
-# CHECK-NEXT:         Flags:           [ ]
+# CHECK-NEXT:         Flags:           [  ]
 # CHECK-NEXT:       - Index:           1
 # CHECK-NEXT:         Name:            .data.direct_address
 # CHECK-NEXT:         Alignment:       3
-# CHECK-NEXT:         Flags:           [ ]
+# CHECK-NEXT:         Flags:           [  ]
 # CHECK-NEXT:       - Index:           2
 # CHECK-NEXT:         Name:            .data.alias_address
 # CHECK-NEXT:         Alignment:       3
-# CHECK-NEXT:         Flags:           [ ]
+# CHECK-NEXT:         Flags:           [  ]
 # CHECK-NEXT: ...
 
+# REF:        - Type:            TYPE
+# REF-NEXT:     Signatures:
+# REF-NEXT:       - Index:           0
+# REF-NEXT:         ParamTypes:      []
+# REF-NEXT:         ReturnTypes:
+# REF-NEXT:           - I32
+# REF-NEXT:   - Type:            IMPORT
+# REF-NEXT:     Imports:
+# REF-NEXT:       - Module:          env
+# REF-NEXT:         Field:           __linear_memory
+# REF-NEXT:         Kind:            MEMORY
+# REF-NEXT:         Memory:
+# REF-NEXT:           Initial:         0x1
+# REF-NEXT:       - Module:          env
+# REF-NEXT:         Field:           __indirect_function_table
+# REF-NEXT:         Kind:            TABLE
+# REF-NEXT:         Table:
+# REF-NEXT:           Index:           0
+# REF-NEXT:           ElemType:        FUNCREF
+# REF-NEXT:           Limits:
+# REF-NEXT:             Initial:         0x1
+# REF-NEXT:   - Type:            FUNCTION
+# REF-NEXT:     FunctionTypes:   [ 0, 0, 0, 0, 0 ]
+# REF-NEXT:   - Type:            ELEM
+# REF-NEXT:     Segments:
+# REF-NEXT:       - Offset:
+# REF-NEXT:           Opcode:          I32_CONST
+# REF-NEXT:           Value:           1
+# REF-NEXT:         Functions:       [ 0 ]
+# REF-NEXT:   - Type:            DATACOUNT
+# REF-NEXT:     Count:           3
+# REF-NEXT:   - Type:            CODE
+# REF-NEXT:     Relocations:
+# REF-NEXT:       - Type:            R_WASM_FUNCTION_INDEX_LEB
+# REF-NEXT:         Index:           0
+# REF-NEXT:         Offset:          0x9
+# REF-NEXT:       - Type:            R_WASM_FUNCTION_INDEX_LEB
+# REF-NEXT:         Index:           3
+# REF-NEXT:         Offset:          0x12
+# REF-NEXT:       - Type:            R_WASM_MEMORY_ADDR_LEB
+# REF-NEXT:         Index:           5
+# REF-NEXT:         Offset:          0x1E
+# REF-NEXT:       - Type:            R_WASM_TYPE_INDEX_LEB
+# REF-NEXT:         Index:           0
+# REF-NEXT:         Offset:          0x24
+# REF-NEXT:       - Type:            R_WASM_TABLE_NUMBER_LEB
+# REF-NEXT:         Index:           6
+# REF-NEXT:         Offset:          0x29
+# REF-NEXT:       - Type:            R_WASM_MEMORY_ADDR_LEB
+# REF-NEXT:         Index:           8
+# REF-NEXT:         Offset:          0x35
+# REF-NEXT:       - Type:            R_WASM_TYPE_INDEX_LEB
+# REF-NEXT:         Index:           0
+# REF-NEXT:         Offset:          0x3B
+# REF-NEXT:       - Type:            R_WASM_TABLE_NUMBER_LEB
+# REF-NEXT:         Index:           6
+# REF-NEXT:         Offset:          0x40
+# REF-NEXT:     Functions:
+# REF-NEXT:       - Index:           0
+# REF-NEXT:         Locals:          []
+# REF-NEXT:         Body:            41000B
+# REF-NEXT:       - Index:           1
+# REF-NEXT:         Locals:          []
+# REF-NEXT:         Body:            1080808080000B
+# REF-NEXT:       - Index:           2
+# REF-NEXT:         Locals:          []
+# REF-NEXT:         Body:            1080808080000B
+# REF-NEXT:       - Index:           3
+# REF-NEXT:         Locals:          []
+# REF-NEXT:         Body:            41002802888080800011808080800080808080000B
+# REF-NEXT:       - Index:           4
+# REF-NEXT:         Locals:          []
+# REF-NEXT:         Body:            41002802908080800011808080800080808080000B
+# REF-NEXT:   - Type:            DATA
+# REF-NEXT:     Relocations:
+# REF-NEXT:       - Type:            R_WASM_TABLE_INDEX_I32
+# REF-NEXT:         Index:           0
+# REF-NEXT:         Offset:          0x13
+# REF-NEXT:       - Type:            R_WASM_TABLE_INDEX_I32
+# REF-NEXT:         Index:           3
+# REF-NEXT:         Offset:          0x20
+# REF-NEXT:     Segments:
+# REF-NEXT:       - SectionOffset:   6
+# REF-NEXT:         InitFlags:       0
+# REF-NEXT:         Offset:
+# REF-NEXT:           Opcode:          I32_CONST
+# REF-NEXT:           Value:           0
+# REF-NEXT:         Content:         '0700000000000000'
+# REF-NEXT:       - SectionOffset:   19
+# REF-NEXT:         InitFlags:       0
+# REF-NEXT:         Offset:
+# REF-NEXT:           Opcode:          I32_CONST
+# REF-NEXT:           Value:           8
+# REF-NEXT:         Content:         '0100000000000000'
+# REF-NEXT:       - SectionOffset:   32
+# REF-NEXT:         InitFlags:       0
+# REF-NEXT:         Offset:
+# REF-NEXT:           Opcode:          I32_CONST
+# REF-NEXT:           Value:           16
+# REF-NEXT:         Content:         '0100000000000000'
+# REF-NEXT:   - Type:            CUSTOM
+# REF-NEXT:     Name:            linking
+# REF-NEXT:     Version:         2
+# REF-NEXT:     SymbolTable:
+# REF-NEXT:       - Index:           0
+# REF-NEXT:         Kind:            FUNCTION
+# REF-NEXT:         Name:            foo
+# REF-NEXT:         Flags:           [ VISIBILITY_HIDDEN ]
+# REF-NEXT:         Function:        0
+# REF-NEXT:       - Index:           1
+# REF-NEXT:         Kind:            FUNCTION
+# REF-NEXT:         Name:            call_direct
+# REF-NEXT:         Flags:           [ VISIBILITY_HIDDEN ]
+# REF-NEXT:         Function:        1
+# REF-NEXT:       - Index:           2
+# REF-NEXT:         Kind:            FUNCTION
+# REF-NEXT:         Name:            call_alias
+# REF-NEXT:         Flags:           [ VISIBILITY_HIDDEN ]
+# REF-NEXT:         Function:        2
+# REF-NEXT:       - Index:           3
+# REF-NEXT:         Kind:            FUNCTION
+# REF-NEXT:         Name:            foo_alias
+# REF-NEXT:         Flags:           [ BINDING_WEAK, VISIBILITY_HIDDEN, NO_STRIP ]
+# REF-NEXT:         Function:        0
+# REF-NEXT:       - Index:           4
+# REF-NEXT:         Kind:            FUNCTION
+# REF-NEXT:         Name:            call_direct_ptr
+# REF-NEXT:         Flags:           [ VISIBILITY_HIDDEN ]
+# REF-NEXT:         Function:        3
+# REF-NEXT:       - Index:           5
+# REF-NEXT:         Kind:            DATA
+# REF-NEXT:         Name:            direct_address
+# REF-NEXT:         Flags:           [  ]
+# REF-NEXT:         Segment:         1
+# REF-NEXT:         Size:            4
+# REF-NEXT:       - Index:           6
+# REF-NEXT:         Kind:            TABLE
+# REF-NEXT:         Name:            __indirect_function_table
+# REF-NEXT:         Flags:           [ UNDEFINED, NO_STRIP ]
+# REF-NEXT:         Table:           0
+# REF-NEXT:       - Index:           7
+# REF-NEXT:         Kind:            FUNCTION
+# REF-NEXT:         Name:            call_alias_ptr
+# REF-NEXT:         Flags:           [ VISIBILITY_HIDDEN ]
+# REF-NEXT:         Function:        4
+# REF-NEXT:       - Index:           8
+# REF-NEXT:         Kind:            DATA
+# REF-NEXT:         Name:            alias_address
+# REF-NEXT:         Flags:           [  ]
+# REF-NEXT:         Segment:         2
+# REF-NEXT:         Size:            4
+# REF-NEXT:       - Index:           9
+# REF-NEXT:         Kind:            DATA
+# REF-NEXT:         Name:            bar
+# REF-NEXT:         Flags:           [  ]
+# REF-NEXT:         Segment:         0
+# REF-NEXT:         Size:            4
+# REF-NEXT:       - Index:           10
+# REF-NEXT:         Kind:            DATA
+# REF-NEXT:         Name:            bar_alias
+# REF-NEXT:         Flags:           [ BINDING_WEAK, VISIBILITY_HIDDEN, NO_STRIP ]
+# REF-NEXT:         Segment:         0
+# REF-NEXT:         Size:            4
+# REF-NEXT:     SegmentInfo:
+# REF-NEXT:       - Index:           0
+# REF-NEXT:         Name:            .data.bar
+# REF-NEXT:         Alignment:       3
+# REF-NEXT:         Flags:           [  ]
+# REF-NEXT:       - Index:           1
+# REF-NEXT:         Name:            .data.direct_address
+# REF-NEXT:         Alignment:       3
+# REF-NEXT:         Flags:           [  ]
+# REF-NEXT:       - Index:           2
+# REF-NEXT:         Name:            .data.alias_address
+# REF-NEXT:         Alignment:       3
+# REF-NEXT:         Flags:           [  ]
+# REF-NEXT: ...
+
 # CHECK-SYMS: SYMBOL TABLE:
 # CHECK-SYMS-NEXT: 00000001 g     F CODE	.hidden foo
 # CHECK-SYMS-NEXT: 00000006 g     F CODE	.hidden call_direct