[WebAssembly] 64-bit (function) pointer fixes.

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
This commit is contained in:
Wouter van Oortmerssen 2020-07-10 16:51:01 -07:00
parent b636e7d1fc
commit cc1b9b680f
12 changed files with 123 additions and 11 deletions

View file

@ -72,6 +72,7 @@ void InputChunk::verifyRelocTargets() const {
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 @@ void InputChunk::verifyRelocTargets() const {
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 @@ void InputChunk::writeTo(uint8_t *buf) const {
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);
@ -162,6 +165,7 @@ void InputChunk::writeTo(uint8_t *buf) const {
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 @@ static unsigned writeCompressedReloc(uint8_t *buf, const WasmRelocation &rel,
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);
@ -237,6 +242,7 @@ static unsigned getRelocWidthPadded(const WasmRelocation &rel) {
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 @@ void InputSegment::generateRelocationCode(raw_ostream &os) const {
}
} 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");

View file

@ -126,7 +126,9 @@ uint64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const {
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 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc) const {
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;

View file

@ -122,7 +122,9 @@ void MarkLive::mark() {
// 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;

View file

@ -70,7 +70,9 @@ void scanRelocations(InputChunk *chunk) {
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 @@ void scanRelocations(InputChunk *chunk) {
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 @@ void scanRelocations(InputChunk *chunk) {
"; 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

View file

@ -20,3 +20,5 @@ WASM_RELOC(R_WASM_MEMORY_ADDR_LEB64, 14)
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)

View file

@ -556,7 +556,9 @@ WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry,
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));
@ -688,6 +690,7 @@ void WasmObjectWriter::applyRelocations(
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 @@ void WasmObjectWriter::applyRelocations(
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 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
// 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());

View file

@ -531,6 +531,8 @@ static bool supportsWasm64(uint64_t 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:
return true;
default:
return supportsWasm32(Type);
@ -563,6 +565,8 @@ static uint64_t resolveWasm64(RelocationRef R, uint64_t S, uint64_t A) {
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:

View file

@ -791,7 +791,9 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
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",
@ -871,7 +873,8 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
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",

View file

@ -92,7 +92,8 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
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 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
}
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:

View file

@ -441,6 +441,19 @@ static MachineBasicBlock *LowerCallResults(MachineInstr &CallResults,
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);

View file

@ -328,19 +328,25 @@ 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)>, 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)>;

View file

@ -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