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,6 +405,13 @@ 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); + // Build a map of sections to the function that defines them, for use // in recordRelocation. for (const MCSymbol &S : Asm.symbols()) { 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 @@ -160,6 +160,24 @@ } }; +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(Ctx.lookupSymbol(Name)); + if (Sym) { + if (!Sym->isFunctionTable()) + Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table"); + } else { + Sym = cast(Ctx.getOrCreateSymbol(Name)); + Sym->setFunctionTable(); + // The default function table is synthesized by the linker. + Sym->setUndefined(); + } + return Sym; +} + class WebAssemblyAsmParser final : public MCTargetAsmParser { MCAsmParser &Parser; MCAsmLexer &Lexer; @@ -531,6 +549,15 @@ 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(); } else if (Name == "ref.null") { ExpectHeapType = true; } 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 @@ -20,12 +20,14 @@ #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" #include "WebAssemblyTargetMachine.h" +#include "WebAssemblyUtilities.h" #include "llvm/Analysis/BranchProbabilityInfo.h" #include "llvm/CodeGen/FastISel.h" #include "llvm/CodeGen/FunctionLoweringInfo.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" @@ -878,6 +880,15 @@ // Add placeholders for the type index and immediate flags 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(); } for (unsigned ArgReg : Args) 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 @@ -16,6 +16,7 @@ #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" #include "WebAssemblyTargetMachine.h" +#include "WebAssemblyUtilities.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/MachineInstrBuilder.h" @@ -477,6 +478,15 @@ if (IsIndirect) { 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(); } for (auto Use : CallParams.uses()) 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 @@ -19,6 +19,9 @@ class MachineInstr; class MachineOperand; +class MCContext; +class MCSymbolWasm; +class StringRef; class WebAssemblyFunctionInfo; namespace WebAssembly { @@ -37,6 +40,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); + } // end namespace WebAssembly } // end namespace llvm 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 @@ -15,6 +15,7 @@ #include "WebAssemblyMachineFunctionInfo.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/MC/MCContext.h" using namespace llvm; const char *const WebAssembly::ClangCallTerminateFn = "__clang_call_terminate"; @@ -96,3 +97,21 @@ llvm_unreachable("Not a call instruction"); } } + +MCSymbolWasm * +WebAssembly::getOrCreateFunctionTableSymbol(MCContext &Ctx, + const StringRef &Name) { + // FIXME: Duplicates functionality from + // MC/WasmObjectWriter::recordRelocation. + MCSymbolWasm *Sym = cast_or_null(Ctx.lookupSymbol(Name)); + if (Sym) { + if (!Sym->isFunctionTable()) + Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table"); + } else { + Sym = cast(Ctx.getOrCreateSymbol(Name)); + Sym->setFunctionTable(); + // The default function table is synthesized by the linker. + Sym->setUndefined(); + } + return Sym; +} diff --git a/llvm/test/CodeGen/WebAssembly/call-indirect.ll b/llvm/test/CodeGen/WebAssembly/call-indirect.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/call-indirect.ll @@ -0,0 +1,28 @@ +; RUN: llc < %s -asm-verbose=false -O2 | FileCheck --check-prefix=CHECK %s +; RUN: llc < %s -asm-verbose=false -O2 --filetype=obj | obj2yaml | FileCheck --check-prefix=OBJ %s + +; Test that compilation units with call_indirect but without any +; function pointer declarations still get a table. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +; CHECK-LABEL: call_indirect_void: +; CHECK-NEXT: .functype call_indirect_void (i32) -> () +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: call_indirect () -> () +; CHECK-NEXT: end_function +define void @call_indirect_void(void ()* %callee) { + call void %callee() + ret void +} + +; OBJ: Imports: +; OBJ-NEXT: - Module: env +; OBJ-NEXT: Field: __linear_memory +; OBJ-NEXT: Kind: MEMORY +; OBJ-NEXT: Memory: +; OBJ-NEXT: Initial: 0x0 +; OBJ-NEXT: - Module: env +; OBJ-NEXT: Field: __indirect_function_table +; OBJ-NEXT: Kind: TABLE 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 @@ -38,6 +38,14 @@ # BIN-NEXT: Kind: MEMORY # BIN-NEXT: Memory: # BIN-NEXT: Initial: 0x0 +# BIN-NEXT: - Module: env +# BIN-NEXT: Field: __indirect_function_table +# BIN-NEXT: Kind: TABLE +# BIN-NEXT: Table: +# BIN-NEXT: Index: 0 +# BIN-NEXT: ElemType: FUNCREF +# BIN-NEXT: Limits: +# BIN-NEXT: Initial: 0x0 # BIN-NEXT: - Type: FUNCTION # BIN-NEXT: FunctionTypes: [ 0 ] # BIN-NEXT: - Type: CODE