Accounting for the fact that Wasm function indices are 32-bit, but in wasm64 we want uniform 64-bit pointers.
Includes reloc types for 64-bit table indices.
Differential Revision: https://reviews.llvm.org/D83729
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:
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;
case R_WASM_MEMORY_ADDR_REL_SLEB:
encodeSLEB128(static_cast<int32_t>(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<int64_t>(value), loc, 10);
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;
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<int64_t>(value), buf);
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;
}
} 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");
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];
}
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;
// 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<FunctionSymbol>(sym);
if (funcSym->hasTableIndex() && funcSym->getTableIndex() == 0)
continue;
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;
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:
"; 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
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)
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<MCSymbolWasm>(Layout.getBaseSymbol(*RelEntry.Symbol));
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;
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);
// 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());
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);
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:
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<GenericBinaryError>("Bad relocation function index",
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<GenericBinaryError>("Bad relocation offset",
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())
}
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:
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<WebAssemblySubtarget>().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);
} // 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)>;
--- /dev/null
+; 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