1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 12:41:49 +01:00

[WebAssembly] Adding 64-bit version of R_WASM_MEMORY_ADDR_* relocs

This adds 4 new reloc types.

A lot of code that previously assumed any memory or offset values could be contained in a uint32_t (and often truncated results from functions returning 64-bit values) have been upgraded to uint64_t. This is not comprehensive: it is only the values that come in contact with the new relocation values and their dependents.

A new tablegen mapping was added to automatically upgrade loads/stores in the assembler, which otherwise has no way to select for these instructions (since they are indentical other than for the offset immediate). It follows a similar technique to https://reviews.llvm.org/D53307

Differential Revision: https://reviews.llvm.org/D81704
This commit is contained in:
Wouter van Oortmerssen 2020-06-05 09:03:12 -07:00
parent 0fa32417a7
commit 2eaa4e6cf6
20 changed files with 347 additions and 99 deletions

View File

@ -159,8 +159,8 @@ struct WasmElemSegment {
// the index of the segment, and the offset and size within the segment.
struct WasmDataReference {
uint32_t Segment;
uint32_t Offset;
uint32_t Size;
uint64_t Offset;
uint64_t Size;
};
struct WasmRelocation {

View File

@ -2,17 +2,21 @@
#error "WASM_RELOC must be defined"
#endif
WASM_RELOC(R_WASM_FUNCTION_INDEX_LEB, 0)
WASM_RELOC(R_WASM_TABLE_INDEX_SLEB, 1)
WASM_RELOC(R_WASM_TABLE_INDEX_I32, 2)
WASM_RELOC(R_WASM_MEMORY_ADDR_LEB, 3)
WASM_RELOC(R_WASM_MEMORY_ADDR_SLEB, 4)
WASM_RELOC(R_WASM_MEMORY_ADDR_I32, 5)
WASM_RELOC(R_WASM_TYPE_INDEX_LEB, 6)
WASM_RELOC(R_WASM_GLOBAL_INDEX_LEB, 7)
WASM_RELOC(R_WASM_FUNCTION_OFFSET_I32, 8)
WASM_RELOC(R_WASM_SECTION_OFFSET_I32, 9)
WASM_RELOC(R_WASM_EVENT_INDEX_LEB, 10)
WASM_RELOC(R_WASM_MEMORY_ADDR_REL_SLEB, 11)
WASM_RELOC(R_WASM_TABLE_INDEX_REL_SLEB, 12)
WASM_RELOC(R_WASM_GLOBAL_INDEX_I32, 13)
WASM_RELOC(R_WASM_FUNCTION_INDEX_LEB, 0)
WASM_RELOC(R_WASM_TABLE_INDEX_SLEB, 1)
WASM_RELOC(R_WASM_TABLE_INDEX_I32, 2)
WASM_RELOC(R_WASM_MEMORY_ADDR_LEB, 3)
WASM_RELOC(R_WASM_MEMORY_ADDR_SLEB, 4)
WASM_RELOC(R_WASM_MEMORY_ADDR_I32, 5)
WASM_RELOC(R_WASM_TYPE_INDEX_LEB, 6)
WASM_RELOC(R_WASM_GLOBAL_INDEX_LEB, 7)
WASM_RELOC(R_WASM_FUNCTION_OFFSET_I32, 8)
WASM_RELOC(R_WASM_SECTION_OFFSET_I32, 9)
WASM_RELOC(R_WASM_EVENT_INDEX_LEB, 10)
WASM_RELOC(R_WASM_MEMORY_ADDR_REL_SLEB, 11)
WASM_RELOC(R_WASM_TABLE_INDEX_REL_SLEB, 12)
WASM_RELOC(R_WASM_GLOBAL_INDEX_I32, 13)
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)

View File

@ -107,8 +107,10 @@ struct Function {
struct Relocation {
RelocType Type;
uint32_t Index;
// TODO(wvo): this would strictly be better as Hex64, but that will change
// all existing obj2yaml output.
yaml::Hex32 Offset;
int32_t Addend;
int64_t Addend;
};
struct DataSegment {

View File

@ -39,9 +39,13 @@ std::string llvm::wasm::relocTypetoString(uint32_t Type) {
bool llvm::wasm::relocTypeHasAddend(uint32_t Type) {
switch (Type) {
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_LEB64:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_SLEB64:
case R_WASM_MEMORY_ADDR_REL_SLEB:
case R_WASM_MEMORY_ADDR_REL_SLEB64:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_MEMORY_ADDR_I64:
case R_WASM_FUNCTION_OFFSET_I32:
case R_WASM_SECTION_OFFSET_I32:
return true;

View File

@ -185,21 +185,21 @@ raw_ostream &operator<<(raw_ostream &OS, const WasmRelocationEntry &Rel) {
// Write X as an (unsigned) LEB value at offset Offset in Stream, padded
// to allow patching.
static void writePatchableLEB(raw_pwrite_stream &Stream, uint32_t X,
uint64_t Offset) {
uint8_t Buffer[5];
unsigned SizeLen = encodeULEB128(X, Buffer, 5);
assert(SizeLen == 5);
template <int W>
void writePatchableLEB(raw_pwrite_stream &Stream, uint64_t X, uint64_t Offset) {
uint8_t Buffer[W];
unsigned SizeLen = encodeULEB128(X, Buffer, W);
assert(SizeLen == W);
Stream.pwrite((char *)Buffer, SizeLen, Offset);
}
// Write X as an signed LEB value at offset Offset in Stream, padded
// to allow patching.
static void writePatchableSLEB(raw_pwrite_stream &Stream, int32_t X,
uint64_t Offset) {
uint8_t Buffer[5];
unsigned SizeLen = encodeSLEB128(X, Buffer, 5);
assert(SizeLen == 5);
template <int W>
void writePatchableSLEB(raw_pwrite_stream &Stream, int64_t X, uint64_t Offset) {
uint8_t Buffer[W];
unsigned SizeLen = encodeSLEB128(X, Buffer, W);
assert(SizeLen == W);
Stream.pwrite((char *)Buffer, SizeLen, Offset);
}
@ -210,6 +210,12 @@ static void patchI32(raw_pwrite_stream &Stream, uint32_t X, uint64_t Offset) {
Stream.pwrite((char *)Buffer, sizeof(Buffer), Offset);
}
static void patchI64(raw_pwrite_stream &Stream, uint64_t X, uint64_t Offset) {
uint8_t Buffer[8];
support::endian::write64le(Buffer, X);
Stream.pwrite((char *)Buffer, sizeof(Buffer), Offset);
}
class WasmObjectWriter : public MCObjectWriter {
support::endian::Writer W;
@ -347,7 +353,7 @@ private:
updateCustomSectionRelocations(const SmallVector<WasmFunction, 4> &Functions,
const MCAsmLayout &Layout);
uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry);
uint64_t getProvisionalValue(const WasmRelocationEntry &RelEntry);
void applyRelocations(ArrayRef<WasmRelocationEntry> Relocations,
uint64_t ContentsOffset);
@ -410,8 +416,8 @@ void WasmObjectWriter::endSection(SectionBookkeeping &Section) {
// Write the final section size to the payload_len field, which follows
// the section id byte.
writePatchableLEB(static_cast<raw_pwrite_stream &>(W.OS), Size,
Section.SizeOffset);
writePatchableLEB<5>(static_cast<raw_pwrite_stream &>(W.OS), Size,
Section.SizeOffset);
}
// Emit the Wasm header.
@ -549,7 +555,7 @@ static const MCSymbolWasm *resolveSymbol(const MCSymbolWasm &Symbol) {
// by RelEntry. This value isn't used by the static linker; it just serves
// to make the object format more readable and more likely to be directly
// useable.
uint32_t
uint64_t
WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry) {
if ((RelEntry.Type == wasm::R_WASM_GLOBAL_INDEX_LEB ||
RelEntry.Type == wasm::R_WASM_GLOBAL_INDEX_I32) &&
@ -587,9 +593,13 @@ WasmObjectWriter::getProvisionalValue(const WasmRelocationEntry &RelEntry) {
return Section.getSectionOffset() + RelEntry.Addend;
}
case wasm::R_WASM_MEMORY_ADDR_LEB:
case wasm::R_WASM_MEMORY_ADDR_I32:
case wasm::R_WASM_MEMORY_ADDR_LEB64:
case wasm::R_WASM_MEMORY_ADDR_SLEB:
case wasm::R_WASM_MEMORY_ADDR_SLEB64:
case wasm::R_WASM_MEMORY_ADDR_REL_SLEB:
case wasm::R_WASM_MEMORY_ADDR_SLEB: {
case wasm::R_WASM_MEMORY_ADDR_REL_SLEB64:
case wasm::R_WASM_MEMORY_ADDR_I32:
case wasm::R_WASM_MEMORY_ADDR_I64: {
// Provisional value is address of the global
const MCSymbolWasm *Sym = resolveSymbol(*RelEntry.Symbol);
// For undefined symbols, use zero
@ -666,7 +676,7 @@ void WasmObjectWriter::applyRelocations(
RelEntry.Offset;
LLVM_DEBUG(dbgs() << "applyRelocation: " << RelEntry << "\n");
uint32_t Value = getProvisionalValue(RelEntry);
auto Value = getProvisionalValue(RelEntry);
switch (RelEntry.Type) {
case wasm::R_WASM_FUNCTION_INDEX_LEB:
@ -674,7 +684,10 @@ void WasmObjectWriter::applyRelocations(
case wasm::R_WASM_GLOBAL_INDEX_LEB:
case wasm::R_WASM_MEMORY_ADDR_LEB:
case wasm::R_WASM_EVENT_INDEX_LEB:
writePatchableLEB(Stream, Value, Offset);
writePatchableLEB<5>(Stream, Value, Offset);
break;
case wasm::R_WASM_MEMORY_ADDR_LEB64:
writePatchableLEB<10>(Stream, Value, Offset);
break;
case wasm::R_WASM_TABLE_INDEX_I32:
case wasm::R_WASM_MEMORY_ADDR_I32:
@ -683,11 +696,18 @@ void WasmObjectWriter::applyRelocations(
case wasm::R_WASM_GLOBAL_INDEX_I32:
patchI32(Stream, Value, Offset);
break;
case wasm::R_WASM_MEMORY_ADDR_I64:
patchI64(Stream, Value, Offset);
break;
case wasm::R_WASM_TABLE_INDEX_SLEB:
case wasm::R_WASM_TABLE_INDEX_REL_SLEB:
case wasm::R_WASM_MEMORY_ADDR_SLEB:
case wasm::R_WASM_MEMORY_ADDR_REL_SLEB:
writePatchableSLEB(Stream, Value, Offset);
writePatchableSLEB<5>(Stream, Value, Offset);
break;
case wasm::R_WASM_MEMORY_ADDR_SLEB64:
case wasm::R_WASM_MEMORY_ADDR_REL_SLEB64:
writePatchableSLEB<10>(Stream, Value, Offset);
break;
default:
llvm_unreachable("invalid relocation type");
@ -1420,9 +1440,8 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm,
// For each data symbol, export it in the symtab as a reference to the
// corresponding Wasm data segment.
wasm::WasmDataReference Ref = wasm::WasmDataReference{
DataSection.getSegmentIndex(),
static_cast<uint32_t>(Layout.getSymbolOffset(WS)),
static_cast<uint32_t>(Size)};
DataSection.getSegmentIndex(), Layout.getSymbolOffset(WS),
static_cast<uint64_t>(Size)};
DataLocations[&WS] = Ref;
LLVM_DEBUG(dbgs() << " -> segment index: " << Ref.Segment << "\n");

View File

@ -505,6 +505,17 @@ static bool supportsWasm32(uint64_t Type) {
}
}
static bool supportsWasm64(uint64_t Type) {
switch (Type) {
case wasm::R_WASM_MEMORY_ADDR_LEB64:
case wasm::R_WASM_MEMORY_ADDR_SLEB64:
case wasm::R_WASM_MEMORY_ADDR_I64:
return true;
default:
return supportsWasm32(Type);
}
}
static uint64_t resolveWasm32(RelocationRef R, uint64_t S, uint64_t A) {
switch (R.getType()) {
case wasm::R_WASM_FUNCTION_INDEX_LEB:
@ -526,6 +537,18 @@ static uint64_t resolveWasm32(RelocationRef R, uint64_t S, uint64_t A) {
}
}
static uint64_t resolveWasm64(RelocationRef R, uint64_t S, uint64_t A) {
switch (R.getType()) {
case wasm::R_WASM_MEMORY_ADDR_LEB64:
case wasm::R_WASM_MEMORY_ADDR_SLEB64:
case wasm::R_WASM_MEMORY_ADDR_I64:
// For wasm section, its offset at 0 -- ignoring Value
return A;
default:
return resolveWasm32(R, S, A);
}
}
std::pair<bool (*)(uint64_t), RelocationResolver>
getRelocationResolver(const ObjectFile &Obj) {
if (Obj.isCOFF()) {
@ -607,6 +630,8 @@ getRelocationResolver(const ObjectFile &Obj) {
} else if (Obj.isWasm()) {
if (Obj.getArch() == Triple::wasm32)
return {supportsWasm32, resolveWasm32};
if (Obj.getArch() == Triple::wasm64)
return {supportsWasm64, resolveWasm64};
return {nullptr, nullptr};
}

View File

@ -156,6 +156,10 @@ static int64_t readVarint64(WasmObjectFile::ReadContext &Ctx) {
return readLEB128(Ctx);
}
static uint64_t readVaruint64(WasmObjectFile::ReadContext &Ctx) {
return readULEB128(Ctx);
}
static uint8_t readOpcode(WasmObjectFile::ReadContext &Ctx) {
return readUint8(Ctx);
}
@ -558,12 +562,12 @@ Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
case wasm::WASM_SYMBOL_TYPE_DATA:
Info.Name = readString(Ctx);
if (IsDefined) {
uint32_t Index = readVaruint32(Ctx);
auto Index = readVaruint32(Ctx);
if (Index >= DataSegments.size())
return make_error<GenericBinaryError>("invalid data symbol index",
object_error::parse_failed);
uint32_t Offset = readVaruint32(Ctx);
uint32_t Size = readVaruint32(Ctx);
auto Offset = readVaruint64(Ctx);
auto Size = readVaruint64(Ctx);
if (Offset + Size > DataSegments[Index].Data.Content.size())
return make_error<GenericBinaryError>("invalid data symbol offset",
object_error::parse_failed);
@ -818,6 +822,15 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
object_error::parse_failed);
Reloc.Addend = readVarint32(Ctx);
break;
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_MEMORY_ADDR_REL_SLEB64:
if (!isValidDataSymbol(Reloc.Index))
return make_error<GenericBinaryError>("Bad relocation data index",
object_error::parse_failed);
Reloc.Addend = readVarint64(Ctx);
break;
case wasm::R_WASM_FUNCTION_OFFSET_I32:
if (!isValidFunctionSymbol(Reloc.Index))
return make_error<GenericBinaryError>("Bad relocation function index",
@ -840,12 +853,18 @@ Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) {
// also shouldn't overlap a function/element boundary, but we don't bother
// to check that.
uint64_t Size = 5;
if (Reloc.Type == wasm::R_WASM_MEMORY_ADDR_LEB64 ||
Reloc.Type == wasm::R_WASM_MEMORY_ADDR_SLEB64 ||
Reloc.Type == wasm::R_WASM_MEMORY_ADDR_REL_SLEB64)
Size = 10;
if (Reloc.Type == wasm::R_WASM_TABLE_INDEX_I32 ||
Reloc.Type == wasm::R_WASM_MEMORY_ADDR_I32 ||
Reloc.Type == wasm::R_WASM_SECTION_OFFSET_I32 ||
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)
Size = 8;
if (Reloc.Offset + Size > EndOffset)
return make_error<GenericBinaryError>("Bad relocation offset",
object_error::parse_failed);
@ -1331,8 +1350,13 @@ uint64_t WasmObjectFile::getWasmSymbolValue(const WasmSymbol &Sym) const {
// offset within the segment.
uint32_t SegmentIndex = Sym.Info.DataRef.Segment;
const wasm::WasmDataSegment &Segment = DataSegments[SegmentIndex].Data;
assert(Segment.Offset.Opcode == wasm::WASM_OPCODE_I32_CONST);
return Segment.Offset.Value.Int32 + Sym.Info.DataRef.Offset;
if (Segment.Offset.Opcode == wasm::WASM_OPCODE_I32_CONST) {
return Segment.Offset.Value.Int32 + Sym.Info.DataRef.Offset;
} else if (Segment.Offset.Opcode == wasm::WASM_OPCODE_I64_CONST) {
return Segment.Offset.Value.Int64 + Sym.Info.DataRef.Offset;
} else {
llvm_unreachable("unknown init expr opcode");
}
}
case wasm::WASM_SYMBOL_TYPE_SECTION:
return 0;

View File

@ -532,8 +532,11 @@ void WasmWriter::writeRelocSection(raw_ostream &OS, WasmYAML::Section &Sec,
encodeULEB128(Reloc.Index, OS);
switch (Reloc.Type) {
case wasm::R_WASM_MEMORY_ADDR_LEB:
case wasm::R_WASM_MEMORY_ADDR_LEB64:
case wasm::R_WASM_MEMORY_ADDR_SLEB:
case wasm::R_WASM_MEMORY_ADDR_SLEB64:
case wasm::R_WASM_MEMORY_ADDR_I32:
case wasm::R_WASM_MEMORY_ADDR_I64:
case wasm::R_WASM_FUNCTION_OFFSET_I32:
case wasm::R_WASM_SECTION_OFFSET_I32:
encodeULEB128(Reloc.Addend, OS);

View File

@ -846,6 +846,16 @@ public:
if (Op0.getImm() == -1)
Op0.setImm(Align);
}
if (getSTI().getTargetTriple().isArch64Bit()) {
// Upgrade 32-bit loads/stores to 64-bit. These mostly differ by having
// an offset64 arg instead of offset32, but to the assembler matcher
// they're both immediates so don't get selected for.
auto Opc64 = WebAssembly::getWasm64Opcode(
static_cast<uint16_t>(Inst.getOpcode()));
if (Opc64 >= 0) {
Inst.setOpcode(Opc64);
}
}
Out.emitInstruction(Inst, getSTI());
if (CurrentState == EndFunction) {
onEndOfFunction();

View File

@ -101,9 +101,6 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
case WebAssembly::OPERAND_I64IMM:
encodeSLEB128(int64_t(MO.getImm()), OS);
break;
case WebAssembly::OPERAND_OFFSET64:
encodeULEB128(uint64_t(MO.getImm()), OS);
break;
case WebAssembly::OPERAND_SIGNATURE:
OS << uint8_t(MO.getImm());
break;
@ -163,6 +160,7 @@ void WebAssemblyMCCodeEmitter::encodeInstruction(
break;
case WebAssembly::OPERAND_OFFSET64:
FixupKind = MCFixupKind(WebAssembly::fixup_uleb128_i64);
PaddedSize = 10;
break;
default:
llvm_unreachable("unexpected symbolic operand kind");

View File

@ -78,7 +78,8 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
return wasm::R_WASM_TABLE_INDEX_REL_SLEB;
case MCSymbolRefExpr::VK_WASM_MBREL:
assert(SymA.isData());
return wasm::R_WASM_MEMORY_ADDR_REL_SLEB;
return is64Bit() ? wasm::R_WASM_MEMORY_ADDR_REL_SLEB64
: wasm::R_WASM_MEMORY_ADDR_REL_SLEB;
case MCSymbolRefExpr::VK_WASM_TYPEINDEX:
return wasm::R_WASM_TYPE_INDEX_LEB;
default:
@ -87,12 +88,13 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
switch (unsigned(Fixup.getKind())) {
case WebAssembly::fixup_sleb128_i32:
case WebAssembly::fixup_sleb128_i64:
if (SymA.isFunction())
return wasm::R_WASM_TABLE_INDEX_SLEB;
return wasm::R_WASM_MEMORY_ADDR_SLEB;
case WebAssembly::fixup_sleb128_i64:
assert(SymA.isData());
return wasm::R_WASM_MEMORY_ADDR_SLEB64;
case WebAssembly::fixup_uleb128_i32:
case WebAssembly::fixup_uleb128_i64:
if (SymA.isGlobal())
return wasm::R_WASM_GLOBAL_INDEX_LEB;
if (SymA.isFunction())
@ -100,6 +102,9 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
if (SymA.isEvent())
return wasm::R_WASM_EVENT_INDEX_LEB;
return wasm::R_WASM_MEMORY_ADDR_LEB;
case WebAssembly::fixup_uleb128_i64:
assert(SymA.isData());
return wasm::R_WASM_MEMORY_ADDR_LEB64;
case FK_Data_4:
if (SymA.isFunction())
return wasm::R_WASM_TABLE_INDEX_I32;
@ -113,6 +118,9 @@ unsigned WebAssemblyWasmObjectWriter::getRelocType(const MCValue &Target,
return wasm::R_WASM_SECTION_OFFSET_I32;
}
return wasm::R_WASM_MEMORY_ADDR_I32;
case FK_Data_8:
assert(SymA.isData());
return wasm::R_WASM_MEMORY_ADDR_I64;
default:
llvm_unreachable("unimplemented fixup kind");
}

View File

@ -32,3 +32,9 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTargetInfo() {
RegisterTarget<Triple::wasm64> Y(getTheWebAssemblyTarget64(), "wasm64",
"WebAssembly 64-bit", "WebAssembly");
}
// Defines llvm::WebAssembly::getWasm64Opcode llvm::WebAssembly::getStackOpcode
// which have to be in a shared location between CodeGen and MC.
#define GET_INSTRMAP_INFO 1
#define GET_INSTRINFO_ENUM 1
#include "WebAssemblyGenInstrInfo.inc"

View File

@ -21,6 +21,13 @@ class Target;
Target &getTheWebAssemblyTarget32();
Target &getTheWebAssemblyTarget64();
namespace WebAssembly {
int getStackOpcode(unsigned short Opcode);
int getWasm64Opcode(unsigned short Opcode);
} // namespace WebAssembly
} // namespace llvm
#endif // LLVM_LIB_TARGET_WEBASSEMBLY_TARGETINFO_WEBASSEMBLYTARGETINFO_H

View File

@ -14,9 +14,10 @@
let UseNamedOperandTable = 1 in
multiclass ATOMIC_I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
list<dag> pattern_r, string asmstr_r,
string asmstr_s, bits<32> atomic_op> {
string asmstr_s, bits<32> atomic_op,
string is64 = "false"> {
defm "" : I<oops_r, iops_r, oops_s, iops_s, pattern_r, asmstr_r, asmstr_s,
!or(0xfe00, !and(0xff, atomic_op))>,
!or(0xfe00, !and(0xff, atomic_op)), is64>,
Requires<[HasAtomics]>;
}
@ -37,13 +38,13 @@ defm ATOMIC_NOTIFY_A32 :
(ins P2Align:$p2align, offset32_op:$off, I32:$addr, I32:$count),
(outs), (ins P2Align:$p2align, offset32_op:$off), [],
"atomic.notify \t$dst, ${off}(${addr})${p2align}, $count",
"atomic.notify \t${off}${p2align}", 0x00>;
"atomic.notify \t${off}${p2align}", 0x00, "false">;
defm ATOMIC_NOTIFY_A64 :
ATOMIC_I<(outs I32:$dst),
(ins P2Align:$p2align, offset64_op:$off, I64:$addr, I32:$count),
(outs), (ins P2Align:$p2align, offset64_op:$off), [],
"atomic.notify \t$dst, ${off}(${addr})${p2align}, $count",
"atomic.notify \t${off}${p2align}", 0x00>;
"atomic.notify \t${off}${p2align}", 0x00, "true">;
let mayLoad = 1 in {
defm ATOMIC_WAIT_I32_A32 :
ATOMIC_I<(outs I32:$dst),
@ -51,28 +52,28 @@ defm ATOMIC_WAIT_I32_A32 :
I64:$timeout),
(outs), (ins P2Align:$p2align, offset32_op:$off), [],
"i32.atomic.wait \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
"i32.atomic.wait \t${off}${p2align}", 0x01>;
"i32.atomic.wait \t${off}${p2align}", 0x01, "false">;
defm ATOMIC_WAIT_I32_A64 :
ATOMIC_I<(outs I32:$dst),
(ins P2Align:$p2align, offset64_op:$off, I64:$addr, I32:$exp,
I64:$timeout),
(outs), (ins P2Align:$p2align, offset64_op:$off), [],
"i32.atomic.wait \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
"i32.atomic.wait \t${off}${p2align}", 0x01>;
"i32.atomic.wait \t${off}${p2align}", 0x01, "true">;
defm ATOMIC_WAIT_I64_A32 :
ATOMIC_I<(outs I32:$dst),
(ins P2Align:$p2align, offset32_op:$off, I32:$addr, I64:$exp,
I64:$timeout),
(outs), (ins P2Align:$p2align, offset32_op:$off), [],
"i64.atomic.wait \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
"i64.atomic.wait \t${off}${p2align}", 0x02>;
"i64.atomic.wait \t${off}${p2align}", 0x02, "false">;
defm ATOMIC_WAIT_I64_A64 :
ATOMIC_I<(outs I32:$dst),
(ins P2Align:$p2align, offset64_op:$off, I64:$addr, I64:$exp,
I64:$timeout),
(outs), (ins P2Align:$p2align, offset64_op:$off), [],
"i64.atomic.wait \t$dst, ${off}(${addr})${p2align}, $exp, $timeout",
"i64.atomic.wait \t${off}${p2align}", 0x02>;
"i64.atomic.wait \t${off}${p2align}", 0x02, "true">;
} // mayLoad = 1
} // hasSideEffects = 1
@ -350,8 +351,8 @@ defm : LoadPatGlobalAddrOffOnly<i64, sext_aload_16_64, "ATOMIC_LOAD16_U_I64">;
//===----------------------------------------------------------------------===//
multiclass AtomicStore<WebAssemblyRegClass rc, string name, int atomic_op> {
defm "" : WebAssemblyStore<rc, name, !or(0xfe00, !and(0xff, atomic_op))>,
Requires<[HasAtomics]>;
defm "" : WebAssemblyStore<rc, name, !or(0xfe00, !and(0xff, atomic_op)),
[HasAtomics]>;
}
defm ATOMIC_STORE_I32 : AtomicStore<I32, "i32.atomic.store", 0x17>;
@ -486,13 +487,13 @@ multiclass WebAssemblyBinRMW<WebAssemblyRegClass rc, string name,
(ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$val),
(outs), (ins P2Align:$p2align, offset32_op:$off), [],
!strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $val"),
!strconcat(name, "\t${off}${p2align}"), atomic_op>;
!strconcat(name, "\t${off}${p2align}"), atomic_op, "false">;
defm "_A64" :
ATOMIC_I<(outs rc:$dst),
(ins P2Align:$p2align, offset64_op:$off, I64:$addr, rc:$val),
(outs), (ins P2Align:$p2align, offset64_op:$off), [],
!strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $val"),
!strconcat(name, "\t${off}${p2align}"), atomic_op>;
!strconcat(name, "\t${off}${p2align}"), atomic_op, "true">;
}
defm ATOMIC_RMW_ADD_I32 : WebAssemblyBinRMW<I32, "i32.atomic.rmw.add", 0x1e>;
@ -797,14 +798,14 @@ multiclass WebAssemblyTerRMW<WebAssemblyRegClass rc, string name,
rc:$new_),
(outs), (ins P2Align:$p2align, offset32_op:$off), [],
!strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $exp, $new_"),
!strconcat(name, "\t${off}${p2align}"), atomic_op>;
!strconcat(name, "\t${off}${p2align}"), atomic_op, "false">;
defm "_A64" :
ATOMIC_I<(outs rc:$dst),
(ins P2Align:$p2align, offset64_op:$off, I64:$addr, rc:$exp,
rc:$new_),
(outs), (ins P2Align:$p2align, offset64_op:$off), [],
!strconcat(name, "\t$dst, ${off}(${addr})${p2align}, $exp, $new_"),
!strconcat(name, "\t${off}${p2align}"), atomic_op>;
!strconcat(name, "\t${off}${p2align}"), atomic_op, "true">;
}
defm ATOMIC_RMW_CMPXCHG_I32 :

View File

@ -14,11 +14,13 @@
// WebAssembly Instruction Format.
// We instantiate 2 of these for every actual instruction (register based
// and stack based), see below.
class WebAssemblyInst<bits<32> inst, string asmstr, string stack> : StackRel,
Instruction {
class WebAssemblyInst<bits<32> inst, string asmstr, string stack, string is64>
: StackRel, Wasm64Rel, Instruction {
bits<32> Inst = inst; // Instruction encoding.
string StackBased = stack;
string BaseName = NAME;
string IsWasm64 = is64;
string Wasm32Name = !subst("_A64", "_A32", NAME);
let Namespace = "WebAssembly";
let Pattern = [];
let AsmString = asmstr;
@ -29,8 +31,8 @@ class WebAssemblyInst<bits<32> inst, string asmstr, string stack> : StackRel,
// Normal instructions. Default instantiation of a WebAssemblyInst.
class NI<dag oops, dag iops, list<dag> pattern, string stack,
string asmstr = "", bits<32> inst = -1>
: WebAssemblyInst<inst, asmstr, stack> {
string asmstr = "", bits<32> inst = -1, string is64 = "false">
: WebAssemblyInst<inst, asmstr, stack, is64> {
dag OutOperandList = oops;
dag InOperandList = iops;
let Pattern = pattern;
@ -52,11 +54,11 @@ class NI<dag oops, dag iops, list<dag> pattern, string stack,
// there is always an equivalent pair of instructions.
multiclass I<dag oops_r, dag iops_r, dag oops_s, dag iops_s,
list<dag> pattern_r, string asmstr_r = "", string asmstr_s = "",
bits<32> inst = -1> {
bits<32> inst = -1, string is64 = "false"> {
let isCodeGenOnly = 1 in
def "" : NI<oops_r, iops_r, pattern_r, "false", asmstr_r, inst>;
def "" : NI<oops_r, iops_r, pattern_r, "false", asmstr_r, inst, is64>;
let BaseName = NAME in
def _S : NI<oops_s, iops_s, [], "true", asmstr_s, inst>;
def _S : NI<oops_s, iops_s, [], "true", asmstr_s, inst, is64>;
}
// For instructions that have no register ops, so both sets are the same.

View File

@ -202,6 +202,19 @@ def getStackOpcode : InstrMapping {
let ValueCols = [["true"]];
}
//===----------------------------------------------------------------------===//
// WebAssembly 32 to 64-bit instruction mapping
//===----------------------------------------------------------------------===//
class Wasm64Rel;
def getWasm64Opcode : InstrMapping {
let FilterClass = "Wasm64Rel";
let RowFields = ["Wasm32Name"];
let ColFields = ["IsWasm64"];
let KeyCol = ["false"];
let ValueCols = [["true"]];
}
//===----------------------------------------------------------------------===//
// WebAssembly Instruction Format Definitions.
//===----------------------------------------------------------------------===//

View File

@ -41,19 +41,19 @@ def or_is_add : PatFrag<(ops node:$lhs, node:$rhs), (or node:$lhs, node:$rhs),[{
// Defines atomic and non-atomic loads, regular and extending.
multiclass WebAssemblyLoad<WebAssemblyRegClass rc, string Name, int Opcode,
list<Predicate> reqs> {
list<Predicate> reqs = []> {
let mayLoad = 1, UseNamedOperandTable = 1 in {
defm "_A32": I<(outs rc:$dst),
(ins P2Align:$p2align, offset32_op:$off, I32:$addr),
(outs), (ins P2Align:$p2align, offset32_op:$off),
[], !strconcat(Name, "\t$dst, ${off}(${addr})${p2align}"),
!strconcat(Name, "\t${off}${p2align}"), Opcode>,
!strconcat(Name, "\t${off}${p2align}"), Opcode, "false">,
Requires<reqs>;
defm "_A64": I<(outs rc:$dst),
(ins P2Align:$p2align, offset64_op:$off, I64:$addr),
(outs), (ins P2Align:$p2align, offset64_op:$off),
[], !strconcat(Name, "\t$dst, ${off}(${addr})${p2align}"),
!strconcat(Name, "\t${off}${p2align}"), Opcode>,
!strconcat(Name, "\t${off}${p2align}"), Opcode, "true">,
Requires<reqs>;
}
}
@ -236,21 +236,24 @@ defm : LoadPatGlobalAddrOffOnly<i64, extloadi16, "LOAD16_U_I64">;
defm : LoadPatGlobalAddrOffOnly<i64, extloadi32, "LOAD32_U_I64">;
// Defines atomic and non-atomic stores, regular and truncating
multiclass WebAssemblyStore<WebAssemblyRegClass rc, string Name, int Opcode> {
multiclass WebAssemblyStore<WebAssemblyRegClass rc, string Name, int Opcode,
list<Predicate> reqs = []> {
let mayStore = 1, UseNamedOperandTable = 1 in
defm "_A32" : I<(outs),
(ins P2Align:$p2align, offset32_op:$off, I32:$addr, rc:$val),
(outs),
(ins P2Align:$p2align, offset32_op:$off), [],
!strconcat(Name, "\t${off}(${addr})${p2align}, $val"),
!strconcat(Name, "\t${off}${p2align}"), Opcode>;
!strconcat(Name, "\t${off}${p2align}"), Opcode, "false">,
Requires<reqs>;
let mayStore = 1, UseNamedOperandTable = 1 in
defm "_A64" : I<(outs),
(ins P2Align:$p2align, offset64_op:$off, I64:$addr, rc:$val),
(outs),
(ins P2Align:$p2align, offset64_op:$off), [],
!strconcat(Name, "\t${off}(${addr})${p2align}, $val"),
!strconcat(Name, "\t${off}${p2align}"), Opcode>;
!strconcat(Name, "\t${off}${p2align}"), Opcode, "true">,
Requires<reqs>;
}
// Basic store.

View File

@ -13,10 +13,11 @@
//===----------------------------------------------------------------------===//
#include "WebAssemblyMCInstLower.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "TargetInfo/WebAssemblyTargetInfo.h"
#include "WebAssemblyAsmPrinter.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblyRuntimeLibcallSignatures.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/IR/Constants.h"
@ -29,11 +30,6 @@
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
// Defines llvm::WebAssembly::getStackOpcode to convert register instructions to
// stack instructions
#define GET_INSTRMAP_INFO 1
#include "WebAssemblyGenInstrInfo.inc"
// This disables the removal of registers when lowering into MC, as required
// by some current tests.
cl::opt<bool>

View File

@ -1,6 +1,5 @@
# RUN: llvm-mc -triple=wasm64-unknown-unknown -mattr=+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=wasm64-unknown-unknown -filetype=obj -mattr=+atomics,+unimplemented-simd128,+nontrapping-fptoint,+exception-handling -o %t.o < %s
# RUN: llvm-mc -triple=wasm64-unknown-unknown -filetype=obj -mattr=+atomics,+unimplemented-simd128,+nontrapping-fptoint,+exception-handling -o - < %s | obj2yaml | FileCheck %s -check-prefix=BIN
# Most of our other tests are for wasm32, this one adds some wasm64 specific tests.
@ -18,14 +17,18 @@ test:
f32.load 0
drop
# i64.const .L.str # get i64 relocatable.
# f32.load 0
# drop
i64.const .L.str # get i64 relocatable.
f32.load 0
drop
global.get myglob64 # get i64 from global
f32.load 0
drop
i64.const 0
f32.load .L.str # relocatable offset!
drop
### basic stores
f32.const 0.0
@ -36,21 +39,27 @@ test:
local.get 0 # get i64 from local.
f32.store 0
# f32.const 0.0
# i64.const .L.str # get i64 relocatable.
# f32.store 0
f32.const 0.0
i64.const .L.str # get i64 relocatable.
f32.store 0
f32.const 0.0
global.get myglob64 # get i64 from global
f32.store 0
f32.const 0.0
i64.const 0
f32.store .L.str # relocatable offset!
end_function
.section .rodata..L.str,"",@
.hidden .L.str
.type .L.str,@object
.L.str:
.asciz "Hello, World!"
.asciz "Hello, World!!!"
.int64 .L.str # relocatable inside data.
.size .L.str, 24
.globaltype myglob64, i64
@ -68,14 +77,18 @@ test:
# CHECK-NEXT: f32.load 0
# CHECK-NEXT: drop
# NCHECK: i64.const .L.str
# NCHECK-NEXT: f32.load 0
# NCHECK-NEXT: drop
# CHECK: i64.const .L.str
# CHECK-NEXT: f32.load 0
# CHECK-NEXT: drop
# CHECK: global.get myglob64
# CHECK-NEXT: f32.load 0
# CHECK-NEXT: drop
# CHECK: i64.const 0
# CHECK-NEXT: f32.load .L.str
# CHECK-NEXT: drop
# CHECK: f32.const 0x0p0
# CHECK-NEXT: i64.const 0
@ -85,14 +98,18 @@ test:
# CHECK-NEXT: local.get 0
# CHECK-NEXT: f32.store 0
# NCHECK: f32.const 0x0p0
# NCHECK-NEXT: i64.const .L.str
# NCHECK-NEXT: f32.store 0
# CHECK: f32.const 0x0p0
# CHECK-NEXT: i64.const .L.str
# CHECK-NEXT: f32.store 0
# CHECK: f32.const 0x0p0
# CHECK-NEXT: global.get myglob64
# CHECK-NEXT: f32.store 0
# CHECK: f32.const 0x0p0
# CHECK-NEXT: i64.const 0
# CHECK-NEXT: f32.store .L.str
# CHECK: end_function
# CHECK-NEXT: .Ltmp0:
@ -101,6 +118,108 @@ test:
# CHECK: .section .rodata..L.str,"",@
# CHECK-NEXT: .hidden .L.str
# CHECK-NEXT: .L.str:
# CHECK-NEXT: .asciz "Hello, World!"
# CHECK-NEXT: .asciz "Hello, World!!!"
# CHECK-NEXT: .int64 .L.str
# CHECK-NEXT: .size .L.str, 24
# CHECK: .globaltype myglob64, i64
# BIN: --- !WASM
# BIN-NEXT: FileHeader:
# BIN-NEXT: Version: 0x00000001
# BIN-NEXT: Sections:
# BIN-NEXT: - Type: TYPE
# BIN-NEXT: Signatures:
# BIN-NEXT: - Index: 0
# BIN-NEXT: ParamTypes:
# BIN-NEXT: - I64
# 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: 0x00000001
# BIN-NEXT: - Module: env
# BIN-NEXT: Field: __indirect_function_table
# BIN-NEXT: Kind: TABLE
# BIN-NEXT: Table:
# BIN-NEXT: ElemType: FUNCREF
# BIN-NEXT: Limits:
# BIN-NEXT: Initial: 0x00000000
# BIN-NEXT: - Module: env
# BIN-NEXT: Field: myglob64
# BIN-NEXT: Kind: GLOBAL
# BIN-NEXT: GlobalType: I64
# BIN-NEXT: GlobalMutable: true
# BIN-NEXT: - Type: FUNCTION
# BIN-NEXT: FunctionTypes: [ 0 ]
# BIN-NEXT: - Type: DATACOUNT
# BIN-NEXT: Count: 1
# BIN-NEXT: - Type: CODE
# BIN-NEXT: Relocations:
# BIN-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB64
# BIN-NEXT: Index: 1
# BIN-NEXT: Offset: 0x00000013
# BIN-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# BIN-NEXT: Index: 2
# BIN-NEXT: Offset: 0x00000022
# BIN-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB64
# BIN-NEXT: Index: 1
# BIN-NEXT: Offset: 0x0000002F
# BIN-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB64
# BIN-NEXT: Index: 1
# BIN-NEXT: Offset: 0x00000054
# BIN-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# BIN-NEXT: Index: 2
# BIN-NEXT: Offset: 0x00000067
# BIN-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB64
# BIN-NEXT: Index: 1
# BIN-NEXT: Offset: 0x00000078
# BIN-NEXT: Functions:
# BIN-NEXT: - Index: 0
# BIN-NEXT: Locals:
# BIN-NEXT: - Type: I64
# BIN-NEXT: Count: 1
# BIN-NEXT: Body: 42002A02001A20002A02001A42808080808080808080002A02001A2380808080002A02001A42002A02808080808080808080001A4300000000420038020043000000002000380200430000000042808080808080808080003802004300000000238080808000380200430000000042003802808080808080808080000B
# BIN-NEXT: - Type: DATA
# BIN-NEXT: Relocations:
# BIN-NEXT: - Type: R_WASM_MEMORY_ADDR_I64
# BIN-NEXT: Index: 1
# BIN-NEXT: Offset: 0x00000016
# BIN-NEXT: Segments:
# BIN-NEXT: - SectionOffset: 6
# BIN-NEXT: InitFlags: 0
# BIN-NEXT: Offset:
# BIN-NEXT: Opcode: I32_CONST
# BIN-NEXT: Value: 0
# BIN-NEXT: Content: 48656C6C6F2C20576F726C64212121000000000000000000
# 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: test
# BIN-NEXT: Flags: [ BINDING_LOCAL ]
# BIN-NEXT: Function: 0
# BIN-NEXT: - Index: 1
# BIN-NEXT: Kind: DATA
# BIN-NEXT: Name: .L.str
# BIN-NEXT: Flags: [ BINDING_LOCAL, VISIBILITY_HIDDEN ]
# BIN-NEXT: Segment: 0
# BIN-NEXT: Size: 24
# BIN-NEXT: - Index: 2
# BIN-NEXT: Kind: GLOBAL
# BIN-NEXT: Name: myglob64
# BIN-NEXT: Flags: [ UNDEFINED ]
# BIN-NEXT: Global: 0
# BIN-NEXT: SegmentInfo:
# BIN-NEXT: - Index: 0
# BIN-NEXT: Name: .rodata..L.str
# BIN-NEXT: Alignment: 0
# BIN-NEXT: Flags: [ ]
# BIN-NEXT: ...

View File

@ -182,6 +182,10 @@ void WasmDumper::printSectionHeaders() {
W.printNumber("Size", static_cast<uint64_t>(Seg.Content.size()));
if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I32_CONST)
W.printNumber("Offset", Seg.Offset.Value.Int32);
else if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I64_CONST)
W.printNumber("Offset", Seg.Offset.Value.Int64);
else
llvm_unreachable("unknown init expr opcode");
}
break;
}