diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -72,6 +72,7 @@ existingValue = decodeULEB128(loc, &bytesRead); break; case R_WASM_TABLE_INDEX_SLEB: + case R_WASM_TABLE_INDEX_SLEB64: case R_WASM_TABLE_INDEX_REL_SLEB: case R_WASM_MEMORY_ADDR_SLEB: case R_WASM_MEMORY_ADDR_SLEB64: @@ -86,6 +87,7 @@ case R_WASM_GLOBAL_INDEX_I32: existingValue = read32le(loc); break; + case R_WASM_TABLE_INDEX_I64: case R_WASM_MEMORY_ADDR_I64: existingValue = read64le(loc); break; @@ -151,6 +153,7 @@ case R_WASM_MEMORY_ADDR_REL_SLEB: encodeSLEB128(static_cast(value), loc, 5); break; + case R_WASM_TABLE_INDEX_SLEB64: case R_WASM_MEMORY_ADDR_SLEB64: case R_WASM_MEMORY_ADDR_REL_SLEB64: encodeSLEB128(static_cast(value), loc, 10); @@ -162,6 +165,7 @@ case R_WASM_GLOBAL_INDEX_I32: write32le(loc, value); break; + case R_WASM_TABLE_INDEX_I64: case R_WASM_MEMORY_ADDR_I64: write64le(loc, value); break; @@ -219,6 +223,7 @@ case R_WASM_MEMORY_ADDR_LEB64: return encodeULEB128(value, buf); case R_WASM_TABLE_INDEX_SLEB: + case R_WASM_TABLE_INDEX_SLEB64: case R_WASM_MEMORY_ADDR_SLEB: case R_WASM_MEMORY_ADDR_SLEB64: return encodeSLEB128(static_cast(value), buf); @@ -237,6 +242,7 @@ case R_WASM_TABLE_INDEX_SLEB: case R_WASM_MEMORY_ADDR_SLEB: return 5; + case R_WASM_TABLE_INDEX_SLEB64: case R_WASM_MEMORY_ADDR_LEB64: case R_WASM_MEMORY_ADDR_SLEB64: return 10; @@ -382,7 +388,8 @@ } } else { const GlobalSymbol* baseSymbol = WasmSym::memoryBase; - if (rel.Type == R_WASM_TABLE_INDEX_I32) + if (rel.Type == R_WASM_TABLE_INDEX_I32 || + rel.Type == R_WASM_TABLE_INDEX_I64) baseSymbol = WasmSym::tableBase; writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); writeUleb128(os, baseSymbol->getGlobalIndex(), "base"); diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -126,7 +126,9 @@ uint64_t ObjFile::calcExpectedValue(const WasmRelocation &reloc) const { switch (reloc.Type) { case R_WASM_TABLE_INDEX_I32: - case R_WASM_TABLE_INDEX_SLEB: { + case R_WASM_TABLE_INDEX_I64: + case R_WASM_TABLE_INDEX_SLEB: + case R_WASM_TABLE_INDEX_SLEB64: { const WasmSymbol &sym = wasmObj->syms()[reloc.Index]; return tableEntries[sym.Info.ElementIndex]; } @@ -195,7 +197,9 @@ switch (reloc.Type) { case R_WASM_TABLE_INDEX_I32: + case R_WASM_TABLE_INDEX_I64: case R_WASM_TABLE_INDEX_SLEB: + case R_WASM_TABLE_INDEX_SLEB64: case R_WASM_TABLE_INDEX_REL_SLEB: { if (!getFunctionSymbol(reloc.Index)->hasTableIndex()) return 0; diff --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp --- a/lld/wasm/MarkLive.cpp +++ b/lld/wasm/MarkLive.cpp @@ -122,7 +122,9 @@ // functions used for weak-undefined symbols have this behaviour (compare // equal to null pointer, only reachable via direct call). if (reloc.Type == R_WASM_TABLE_INDEX_SLEB || - reloc.Type == R_WASM_TABLE_INDEX_I32) { + reloc.Type == R_WASM_TABLE_INDEX_SLEB64 || + reloc.Type == R_WASM_TABLE_INDEX_I32 || + reloc.Type == R_WASM_TABLE_INDEX_I64) { auto *funcSym = cast(sym); if (funcSym->hasTableIndex() && funcSym->getTableIndex() == 0) continue; diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp --- a/lld/wasm/Relocations.cpp +++ b/lld/wasm/Relocations.cpp @@ -70,7 +70,9 @@ switch (reloc.Type) { case R_WASM_TABLE_INDEX_I32: + case R_WASM_TABLE_INDEX_I64: case R_WASM_TABLE_INDEX_SLEB: + case R_WASM_TABLE_INDEX_SLEB64: case R_WASM_TABLE_INDEX_REL_SLEB: if (requiresGOTAccess(sym)) break; @@ -86,6 +88,7 @@ if (config->isPic) { switch (reloc.Type) { case R_WASM_TABLE_INDEX_SLEB: + case R_WASM_TABLE_INDEX_SLEB64: case R_WASM_MEMORY_ADDR_SLEB: case R_WASM_MEMORY_ADDR_LEB: case R_WASM_MEMORY_ADDR_SLEB64: @@ -97,6 +100,7 @@ "; recompile with -fPIC"); break; case R_WASM_TABLE_INDEX_I32: + case R_WASM_TABLE_INDEX_I64: case R_WASM_MEMORY_ADDR_I32: case R_WASM_MEMORY_ADDR_I64: // These relocation types are only present in the data section and diff --git a/llvm/include/llvm/BinaryFormat/WasmRelocs.def b/llvm/include/llvm/BinaryFormat/WasmRelocs.def --- a/llvm/include/llvm/BinaryFormat/WasmRelocs.def +++ b/llvm/include/llvm/BinaryFormat/WasmRelocs.def @@ -20,3 +20,5 @@ WASM_RELOC(R_WASM_MEMORY_ADDR_SLEB64, 15) WASM_RELOC(R_WASM_MEMORY_ADDR_I64, 16) WASM_RELOC(R_WASM_MEMORY_ADDR_REL_SLEB64, 17) +WASM_RELOC(R_WASM_TABLE_INDEX_SLEB64, 18) +WASM_RELOC(R_WASM_TABLE_INDEX_I64, 19) 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 @@ -556,7 +556,9 @@ switch (RelEntry.Type) { case wasm::R_WASM_TABLE_INDEX_REL_SLEB: case wasm::R_WASM_TABLE_INDEX_SLEB: - case wasm::R_WASM_TABLE_INDEX_I32: { + case wasm::R_WASM_TABLE_INDEX_SLEB64: + case wasm::R_WASM_TABLE_INDEX_I32: + case wasm::R_WASM_TABLE_INDEX_I64: { // Provisional value is table address of the resolved symbol itself const MCSymbolWasm *Base = cast(Layout.getBaseSymbol(*RelEntry.Symbol)); @@ -688,6 +690,7 @@ case wasm::R_WASM_GLOBAL_INDEX_I32: patchI32(Stream, Value, Offset); break; + case wasm::R_WASM_TABLE_INDEX_I64: case wasm::R_WASM_MEMORY_ADDR_I64: patchI64(Stream, Value, Offset); break; @@ -697,6 +700,7 @@ case wasm::R_WASM_MEMORY_ADDR_REL_SLEB: writePatchableSLEB<5>(Stream, Value, Offset); break; + case wasm::R_WASM_TABLE_INDEX_SLEB64: case wasm::R_WASM_MEMORY_ADDR_SLEB64: case wasm::R_WASM_MEMORY_ADDR_REL_SLEB64: writePatchableSLEB<10>(Stream, Value, Offset); @@ -1599,7 +1603,9 @@ // purely to make the object file's provisional values readable, and is // ignored by the linker, which re-calculates the relocations itself. if (Rel.Type != wasm::R_WASM_TABLE_INDEX_I32 && + Rel.Type != wasm::R_WASM_TABLE_INDEX_I64 && Rel.Type != wasm::R_WASM_TABLE_INDEX_SLEB && + Rel.Type != wasm::R_WASM_TABLE_INDEX_SLEB64 && Rel.Type != wasm::R_WASM_TABLE_INDEX_REL_SLEB) return; assert(Rel.Symbol->isFunction()); diff --git a/llvm/lib/Object/RelocationResolver.cpp b/llvm/lib/Object/RelocationResolver.cpp --- a/llvm/lib/Object/RelocationResolver.cpp +++ b/llvm/lib/Object/RelocationResolver.cpp @@ -531,6 +531,8 @@ case wasm::R_WASM_MEMORY_ADDR_LEB64: case wasm::R_WASM_MEMORY_ADDR_SLEB64: case wasm::R_WASM_MEMORY_ADDR_I64: + case wasm::R_WASM_TABLE_INDEX_SLEB64: + case wasm::R_WASM_TABLE_INDEX_I64: return true; default: return supportsWasm32(Type); @@ -563,6 +565,8 @@ case wasm::R_WASM_MEMORY_ADDR_LEB64: case wasm::R_WASM_MEMORY_ADDR_SLEB64: case wasm::R_WASM_MEMORY_ADDR_I64: + case wasm::R_WASM_TABLE_INDEX_SLEB64: + case wasm::R_WASM_TABLE_INDEX_I64: // For wasm section, its offset at 0 -- ignoring Value return A; default: diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -791,7 +791,9 @@ switch (Reloc.Type) { case wasm::R_WASM_FUNCTION_INDEX_LEB: case wasm::R_WASM_TABLE_INDEX_SLEB: + case wasm::R_WASM_TABLE_INDEX_SLEB64: case wasm::R_WASM_TABLE_INDEX_I32: + case wasm::R_WASM_TABLE_INDEX_I64: case wasm::R_WASM_TABLE_INDEX_REL_SLEB: if (!isValidFunctionSymbol(Reloc.Index)) return make_error("Bad relocation function index", @@ -871,7 +873,8 @@ Reloc.Type == wasm::R_WASM_FUNCTION_OFFSET_I32 || Reloc.Type == wasm::R_WASM_GLOBAL_INDEX_I32) Size = 4; - if (Reloc.Type == wasm::R_WASM_MEMORY_ADDR_I64) + if (Reloc.Type == wasm::R_WASM_TABLE_INDEX_I64 || + Reloc.Type == wasm::R_WASM_MEMORY_ADDR_I64) Size = 8; if (Reloc.Offset + Size > EndOffset) return make_error("Bad relocation offset", diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp @@ -92,7 +92,8 @@ return wasm::R_WASM_TABLE_INDEX_SLEB; return wasm::R_WASM_MEMORY_ADDR_SLEB; case WebAssembly::fixup_sleb128_i64: - assert(SymA.isData()); + if (SymA.isFunction()) + return wasm::R_WASM_TABLE_INDEX_SLEB64; return wasm::R_WASM_MEMORY_ADDR_SLEB64; case WebAssembly::fixup_uleb128_i32: if (SymA.isGlobal()) @@ -119,6 +120,8 @@ } return wasm::R_WASM_MEMORY_ADDR_I32; case FK_Data_8: + if (SymA.isFunction()) + return wasm::R_WASM_TABLE_INDEX_I64; assert(SymA.isData()); return wasm::R_WASM_MEMORY_ADDR_I64; default: 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 @@ -441,6 +441,19 @@ const MCInstrDesc &MCID = TII.get(CallOp); MachineInstrBuilder MIB(MF, MF.CreateMachineInstr(MCID, DL)); + // 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. + if (IsIndirect && MF.getSubtarget().hasAddr64()) { + Register Reg32 = + MF.getRegInfo().createVirtualRegister(&WebAssembly::I32RegClass); + auto &FnPtr = CallParams.getOperand(0); + BuildMI(*BB, CallResults.getIterator(), DL, + TII.get(WebAssembly::I32_WRAP_I64), Reg32) + .addReg(FnPtr.getReg()); + FnPtr.setReg(Reg32); + } + // Move the function pointer to the end of the arguments for indirect calls if (IsIndirect) { auto FnPtr = CallParams.getOperand(0); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -328,19 +328,25 @@ } // isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 def : Pat<(i32 (WebAssemblywrapper tglobaladdr:$addr)), - (CONST_I32 tglobaladdr:$addr)>, Requires<[IsNotPIC]>; + (CONST_I32 tglobaladdr:$addr)>, Requires<[IsNotPIC, HasAddr32]>; +def : Pat<(i64 (WebAssemblywrapper tglobaladdr:$addr)), + (CONST_I64 tglobaladdr:$addr)>, Requires<[IsNotPIC, HasAddr64]>; def : Pat<(i32 (WebAssemblywrapper tglobaladdr:$addr)), - (GLOBAL_GET_I32 tglobaladdr:$addr)>, Requires<[IsPIC]>; + (GLOBAL_GET_I32 tglobaladdr:$addr)>, Requires<[IsPIC, HasAddr32]>; def : Pat<(i32 (WebAssemblywrapperPIC tglobaladdr:$addr)), - (CONST_I32 tglobaladdr:$addr)>, Requires<[IsPIC]>; + (CONST_I32 tglobaladdr:$addr)>, Requires<[IsPIC, HasAddr32]>; +def : Pat<(i64 (WebAssemblywrapperPIC tglobaladdr:$addr)), + (CONST_I64 tglobaladdr:$addr)>, Requires<[IsPIC, HasAddr64]>; def : Pat<(i32 (WebAssemblywrapper texternalsym:$addr)), - (GLOBAL_GET_I32 texternalsym:$addr)>, Requires<[IsPIC]>; + (GLOBAL_GET_I32 texternalsym:$addr)>, Requires<[IsPIC, HasAddr32]>; def : Pat<(i32 (WebAssemblywrapper texternalsym:$addr)), - (CONST_I32 texternalsym:$addr)>, Requires<[IsNotPIC]>; + (CONST_I32 texternalsym:$addr)>, Requires<[IsNotPIC, HasAddr32]>; +def : Pat<(i64 (WebAssemblywrapper texternalsym:$addr)), + (CONST_I64 texternalsym:$addr)>, Requires<[IsNotPIC, HasAddr64]>; def : Pat<(i32 (WebAssemblywrapper mcsym:$sym)), (CONST_I32 mcsym:$sym)>; def : Pat<(i64 (WebAssemblywrapper mcsym:$sym)), (CONST_I64 mcsym:$sym)>; diff --git a/llvm/test/CodeGen/WebAssembly/function-pointer64.ll b/llvm/test/CodeGen/WebAssembly/function-pointer64.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/function-pointer64.ll @@ -0,0 +1,58 @@ +; RUN: llc < %s -asm-verbose=false -O2 | FileCheck %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. + +target datalayout = "e-m:e-p:64:64-i64:64-n32:64-S128" +target triple = "wasm64-unknown-unknown" + +define void @bar(i32 %n) { +entry: + ret void +} + +define void @foo(void (i32)* %fp) { +entry: + call void %fp(i32 1) + ret void +} + +define void @test() { +entry: + call void @foo(void (i32)* @bar) + store void (i32)* @bar, void (i32)** @fptr + ret void +} + +@fptr = global void (i32)* @bar + +; For simplicity (and compatibility with UB C/C++ code) we keep all types +; of pointers the same size, so function pointers (which are 32-bit indices +; in Wasm) are represented as 64-bit until called. + +; CHECK: .functype foo (i64) -> () +; CHECK-NEXT: i32.const 1 +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: i32.wrap_i64 +; CHECK-NEXT: call_indirect (i32) -> () + +; 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. + +; YAML: Memory: +; YAML-NEXT: Flags: [ IS_64 ] +; YAML-NEXT: Initial: 0x00000001 + +; YAML: - Type: CODE +; YAML: - Type: R_WASM_TABLE_INDEX_SLEB64 +; YAML-NEXT: Index: 0 +; YAML-NEXT: Offset: 0x00000016 + +; YAML: - Type: DATA +; YAML: - Type: R_WASM_TABLE_INDEX_I64 +; YAML-NEXT: Index: 0 +; YAML-NEXT: Offset: 0x00000006