[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 017969de76
commit 3b29376e3f
32 changed files with 453 additions and 155 deletions

View File

@ -46,9 +46,9 @@ StringRef InputChunk::getComdatName() const {
void InputChunk::verifyRelocTargets() const {
for (const WasmRelocation &rel : relocations) {
uint32_t existingValue;
uint64_t existingValue;
unsigned bytesRead = 0;
uint32_t offset = rel.Offset - getInputSectionOffset();
auto offset = rel.Offset - getInputSectionOffset();
const uint8_t *loc = data().data() + offset;
switch (rel.Type) {
case R_WASM_TYPE_INDEX_LEB:
@ -56,20 +56,26 @@ void InputChunk::verifyRelocTargets() const {
case R_WASM_GLOBAL_INDEX_LEB:
case R_WASM_EVENT_INDEX_LEB:
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_LEB64:
existingValue = decodeULEB128(loc, &bytesRead);
break;
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_REL_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_SLEB64:
case R_WASM_MEMORY_ADDR_REL_SLEB:
existingValue = static_cast<uint32_t>(decodeSLEB128(loc, &bytesRead));
case R_WASM_MEMORY_ADDR_REL_SLEB64:
existingValue = static_cast<uint64_t>(decodeSLEB128(loc, &bytesRead));
break;
case R_WASM_TABLE_INDEX_I32:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_FUNCTION_OFFSET_I32:
case R_WASM_SECTION_OFFSET_I32:
case R_WASM_GLOBAL_INDEX_I32:
existingValue = static_cast<uint32_t>(read32le(loc));
existingValue = read32le(loc);
break;
case R_WASM_MEMORY_ADDR_I64:
existingValue = read64le(loc);
break;
default:
llvm_unreachable("unknown relocation type");
@ -80,7 +86,7 @@ void InputChunk::verifyRelocTargets() const {
if (rel.Type != R_WASM_GLOBAL_INDEX_LEB &&
rel.Type != R_WASM_GLOBAL_INDEX_I32) {
uint32_t expectedValue = file->calcExpectedValue(rel);
auto expectedValue = file->calcExpectedValue(rel);
if (expectedValue != existingValue)
warn("unexpected existing value for " + relocTypeToString(rel.Type) +
": existing=" + Twine(existingValue) +
@ -108,7 +114,7 @@ void InputChunk::writeTo(uint8_t *buf) const {
for (const WasmRelocation &rel : relocations) {
uint8_t *loc = buf + rel.Offset + off;
uint32_t value = file->calcNewValue(rel);
auto value = file->calcNewValue(rel);
LLVM_DEBUG(dbgs() << "apply reloc: type=" << relocTypeToString(rel.Type));
if (rel.Type != R_WASM_TYPE_INDEX_LEB)
LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName());
@ -124,12 +130,19 @@ void InputChunk::writeTo(uint8_t *buf) const {
case R_WASM_MEMORY_ADDR_LEB:
encodeULEB128(value, loc, 5);
break;
case R_WASM_MEMORY_ADDR_LEB64:
encodeULEB128(value, loc, 10);
break;
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_REL_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_REL_SLEB:
encodeSLEB128(static_cast<int32_t>(value), loc, 5);
break;
case R_WASM_MEMORY_ADDR_SLEB64:
case R_WASM_MEMORY_ADDR_REL_SLEB64:
encodeSLEB128(static_cast<int64_t>(value), loc, 10);
break;
case R_WASM_TABLE_INDEX_I32:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_FUNCTION_OFFSET_I32:
@ -137,6 +150,9 @@ void InputChunk::writeTo(uint8_t *buf) const {
case R_WASM_GLOBAL_INDEX_I32:
write32le(loc, value);
break;
case R_WASM_MEMORY_ADDR_I64:
write64le(loc, value);
break;
default:
llvm_unreachable("unknown relocation type");
}
@ -181,17 +197,19 @@ void InputFunction::setTableIndex(uint32_t index) {
// Write a relocation value without padding and return the number of bytes
// witten.
static unsigned writeCompressedReloc(uint8_t *buf, const WasmRelocation &rel,
uint32_t value) {
uint64_t value) {
switch (rel.Type) {
case R_WASM_TYPE_INDEX_LEB:
case R_WASM_FUNCTION_INDEX_LEB:
case R_WASM_GLOBAL_INDEX_LEB:
case R_WASM_EVENT_INDEX_LEB:
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_LEB64:
return encodeULEB128(value, buf);
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
return encodeSLEB128(static_cast<int32_t>(value), buf);
case R_WASM_MEMORY_ADDR_SLEB64:
return encodeSLEB128(static_cast<int64_t>(value), buf);
default:
llvm_unreachable("unexpected relocation type");
}
@ -207,13 +225,16 @@ static unsigned getRelocWidthPadded(const WasmRelocation &rel) {
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
return 5;
case R_WASM_MEMORY_ADDR_LEB64:
case R_WASM_MEMORY_ADDR_SLEB64:
return 10;
default:
llvm_unreachable("unexpected relocation type");
}
}
static unsigned getRelocWidth(const WasmRelocation &rel, uint32_t value) {
uint8_t buf[5];
static unsigned getRelocWidth(const WasmRelocation &rel, uint64_t value) {
uint8_t buf[10];
return writeCompressedReloc(buf, rel, value);
}

View File

@ -101,12 +101,16 @@ uint32_t ObjFile::calcNewIndex(const WasmRelocation &reloc) const {
// Relocations can contain addend for combined sections. This function takes a
// relocation and returns updated addend by offset in the output section.
uint32_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const {
uint64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const {
switch (reloc.Type) {
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_LEB64:
case R_WASM_MEMORY_ADDR_SLEB64:
case R_WASM_MEMORY_ADDR_SLEB:
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:
return reloc.Addend;
case R_WASM_SECTION_OFFSET_I32:
@ -119,7 +123,7 @@ uint32_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const {
// Calculate the value we expect to find at the relocation location.
// This is used as a sanity check before applying a relocation to a given
// location. It is useful for catching bugs in the compiler and linker.
uint32_t ObjFile::calcExpectedValue(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: {
@ -130,17 +134,27 @@ uint32_t ObjFile::calcExpectedValue(const WasmRelocation &reloc) const {
const WasmSymbol &sym = wasmObj->syms()[reloc.Index];
return tableEntriesRel[sym.Info.ElementIndex];
}
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_REL_SLEB: {
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: {
const WasmSymbol &sym = wasmObj->syms()[reloc.Index];
if (sym.isUndefined())
return 0;
const WasmSegment &segment =
wasmObj->dataSegments()[sym.Info.DataRef.Segment];
return segment.Data.Offset.Value.Int32 + sym.Info.DataRef.Offset +
reloc.Addend;
if (segment.Data.Offset.Opcode == WASM_OPCODE_I32_CONST)
return segment.Data.Offset.Value.Int32 + sym.Info.DataRef.Offset +
reloc.Addend;
else if (segment.Data.Offset.Opcode == WASM_OPCODE_I64_CONST)
return segment.Data.Offset.Value.Int64 + sym.Info.DataRef.Offset +
reloc.Addend;
else
llvm_unreachable("unknown init expr opcode");
}
case R_WASM_FUNCTION_OFFSET_I32: {
const WasmSymbol &sym = wasmObj->syms()[reloc.Index];
@ -166,7 +180,7 @@ uint32_t ObjFile::calcExpectedValue(const WasmRelocation &reloc) const {
}
// Translate from the relocation's index into the final linked output value.
uint32_t ObjFile::calcNewValue(const WasmRelocation &reloc) const {
uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc) const {
const Symbol* sym = nullptr;
if (reloc.Type != R_WASM_TYPE_INDEX_LEB) {
sym = symbols[reloc.Index];
@ -191,10 +205,14 @@ uint32_t ObjFile::calcNewValue(const WasmRelocation &reloc) const {
return index;
}
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_I32:
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:
if (isa<UndefinedData>(sym) || sym->isUndefWeak())
return 0;
return cast<DefinedData>(sym)->getVirtualAddress() + reloc.Addend;
@ -211,8 +229,8 @@ uint32_t ObjFile::calcNewValue(const WasmRelocation &reloc) const {
return getEventSymbol(reloc.Index)->getEventIndex();
case R_WASM_FUNCTION_OFFSET_I32: {
auto *f = cast<DefinedFunction>(sym);
return f->function->outputOffset + f->function->getFunctionCodeOffset() +
reloc.Addend;
return f->function->outputOffset +
(f->function->getFunctionCodeOffset() + reloc.Addend);
}
case R_WASM_SECTION_OFFSET_I32:
return getSectionSymbol(reloc.Index)->section->outputOffset + reloc.Addend;
@ -272,12 +290,15 @@ void ObjFile::parse(bool ignoreComdats) {
tableEntriesRel.resize(totalFunctions);
tableEntries.resize(totalFunctions);
for (const WasmElemSegment &seg : wasmObj->elements()) {
if (seg.Offset.Opcode != WASM_OPCODE_I32_CONST)
int64_t offset;
if (seg.Offset.Opcode == WASM_OPCODE_I32_CONST)
offset = seg.Offset.Value.Int32;
else if (seg.Offset.Opcode == WASM_OPCODE_I64_CONST)
offset = seg.Offset.Value.Int64;
else
fatal(toString(this) + ": invalid table elements");
uint32_t offset = seg.Offset.Value.Int32;
for (uint32_t index = 0; index < seg.Functions.size(); index++) {
uint32_t functionIndex = seg.Functions[index];
for (size_t index = 0; index < seg.Functions.size(); index++) {
auto functionIndex = seg.Functions[index];
tableEntriesRel[functionIndex] = index;
tableEntries[functionIndex] = offset + index;
}
@ -411,8 +432,8 @@ Symbol *ObjFile::createDefined(const WasmSymbol &sym) {
}
case WASM_SYMBOL_TYPE_DATA: {
InputSegment *seg = segments[sym.Info.DataRef.Segment];
uint32_t offset = sym.Info.DataRef.Offset;
uint32_t size = sym.Info.DataRef.Size;
auto offset = sym.Info.DataRef.Offset;
auto size = sym.Info.DataRef.Size;
if (sym.isBindingLocal())
return make<DefinedData>(name, flags, this, seg, offset, size);
if (seg->discarded)

View File

@ -103,9 +103,9 @@ public:
void dumpInfo() const;
uint32_t calcNewIndex(const WasmRelocation &reloc) const;
uint32_t calcNewValue(const WasmRelocation &reloc) const;
uint32_t calcNewAddend(const WasmRelocation &reloc) const;
uint32_t calcExpectedValue(const WasmRelocation &reloc) const;
uint64_t calcNewValue(const WasmRelocation &reloc) const;
uint64_t calcNewAddend(const WasmRelocation &reloc) const;
uint64_t calcExpectedValue(const WasmRelocation &reloc) const;
Symbol *getSymbol(const WasmRelocation &reloc) const {
return symbols[reloc.Index];
};

View File

@ -88,6 +88,8 @@ void scanRelocations(InputChunk *chunk) {
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_LEB:
case R_WASM_MEMORY_ADDR_SLEB64:
case R_WASM_MEMORY_ADDR_LEB64:
// Certain relocation types can't be used when building PIC output,
// since they would require absolute symbol addresses at link time.
error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +
@ -96,6 +98,7 @@ void scanRelocations(InputChunk *chunk) {
break;
case R_WASM_TABLE_INDEX_I32:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_MEMORY_ADDR_I64:
// These relocation types are only present in the data section and
// will be converted into code by `generateRelocationCode`. This code
// requires the symbols to have GOT entires.

View File

@ -327,7 +327,7 @@ Symbol *SymbolTable::addDefinedFunction(StringRef name, uint32_t flags,
Symbol *SymbolTable::addDefinedData(StringRef name, uint32_t flags,
InputFile *file, InputSegment *segment,
uint32_t address, uint32_t size) {
uint64_t address, uint64_t size) {
LLVM_DEBUG(dbgs() << "addDefinedData:" << name << " addr:" << address
<< "\n");
Symbol *s;

View File

@ -53,8 +53,8 @@ public:
Symbol *addDefinedFunction(StringRef name, uint32_t flags, InputFile *file,
InputFunction *function);
Symbol *addDefinedData(StringRef name, uint32_t flags, InputFile *file,
InputSegment *segment, uint32_t address,
uint32_t size);
InputSegment *segment, uint64_t address,
uint64_t size);
Symbol *addDefinedGlobal(StringRef name, uint32_t flags, InputFile *file,
InputGlobal *g);
Symbol *addDefinedEvent(StringRef name, uint32_t flags, InputFile *file,

View File

@ -252,7 +252,7 @@ DefinedFunction::DefinedFunction(StringRef name, uint32_t flags, InputFile *f,
function ? &function->signature : nullptr),
function(function) {}
uint32_t DefinedData::getVirtualAddress() const {
uint64_t DefinedData::getVirtualAddress() const {
LLVM_DEBUG(dbgs() << "getVirtualAddress: " << getName() << "\n");
if (segment) {
// For thread local data, the symbol location is relative to the start of
@ -265,18 +265,18 @@ uint32_t DefinedData::getVirtualAddress() const {
return offset;
}
void DefinedData::setVirtualAddress(uint32_t value) {
void DefinedData::setVirtualAddress(uint64_t value) {
LLVM_DEBUG(dbgs() << "setVirtualAddress " << name << " -> " << value << "\n");
assert(!segment);
offset = value;
}
uint32_t DefinedData::getOutputSegmentOffset() const {
uint64_t DefinedData::getOutputSegmentOffset() const {
LLVM_DEBUG(dbgs() << "getOutputSegmentOffset: " << getName() << "\n");
return segment->outputSegmentOffset + offset;
}
uint32_t DefinedData::getOutputSegmentIndex() const {
uint64_t DefinedData::getOutputSegmentIndex() const {
LLVM_DEBUG(dbgs() << "getOutputSegmentIndex: " << getName() << "\n");
return segment->outputSeg->index;
}

View File

@ -264,7 +264,7 @@ class DefinedData : public DataSymbol {
public:
// Constructor for regular data symbols originating from input files.
DefinedData(StringRef name, uint32_t flags, InputFile *f,
InputSegment *segment, uint32_t offset, uint32_t size)
InputSegment *segment, uint64_t offset, uint64_t size)
: DataSymbol(name, DefinedDataKind, flags, f), segment(segment),
offset(offset), size(size) {}
@ -275,19 +275,19 @@ public:
static bool classof(const Symbol *s) { return s->kind() == DefinedDataKind; }
// Returns the output virtual address of a defined data symbol.
uint32_t getVirtualAddress() const;
void setVirtualAddress(uint32_t va);
uint64_t getVirtualAddress() const;
void setVirtualAddress(uint64_t va);
// Returns the offset of a defined data symbol within its OutputSegment.
uint32_t getOutputSegmentOffset() const;
uint32_t getOutputSegmentIndex() const;
uint32_t getSize() const { return size; }
uint64_t getOutputSegmentOffset() const;
uint64_t getOutputSegmentIndex() const;
uint64_t getSize() const { return size; }
InputSegment *segment = nullptr;
protected:
uint32_t offset = 0;
uint32_t size = 0;
uint64_t offset = 0;
uint64_t size = 0;
};
class UndefinedData : public DataSymbol {

View File

@ -287,6 +287,7 @@ void GlobalSection::writeBody() {
writeUleb128(os, numGlobals(), "global count");
for (InputGlobal *g : inputGlobals)
writeGlobal(os, g->global);
// TODO(wvo): when do these need I64_CONST?
for (const Symbol *sym : staticGotSymbols) {
WasmGlobal global;
global.Type = {WASM_TYPE_I32, false};

View File

@ -224,6 +224,7 @@ void Writer::layoutMemory() {
log("mem: stack base = " + Twine(memoryPtr));
memoryPtr += config->zStackSize;
auto *sp = cast<DefinedGlobal>(WasmSym::stackPointer);
assert(sp->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
sp->global->global.InitExpr.Value.Int32 = memoryPtr;
log("mem: stack top = " + Twine(memoryPtr));
};
@ -256,10 +257,13 @@ void Writer::layoutMemory() {
if (WasmSym::tlsSize && seg->name == ".tdata") {
auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
assert(tlsSize->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
tlsSize->global->global.InitExpr.Value.Int32 = seg->size;
auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
tlsAlign->global->global.InitExpr.Value.Int32 = 1U << seg->alignment;
assert(tlsAlign->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
tlsAlign->global->global.InitExpr.Value.Int32 = int64_t{1}
<< seg->alignment;
}
}

View File

@ -67,12 +67,12 @@ void debugWrite(uint64_t offset, const Twine &msg) {
LLVM_DEBUG(dbgs() << format(" | %08lld: ", offset) << msg << "\n");
}
void writeUleb128(raw_ostream &os, uint32_t number, const Twine &msg) {
void writeUleb128(raw_ostream &os, uint64_t number, const Twine &msg) {
debugWrite(os.tell(), msg + "[" + utohexstr(number) + "]");
encodeULEB128(number, os);
}
void writeSleb128(raw_ostream &os, int32_t number, const Twine &msg) {
void writeSleb128(raw_ostream &os, int64_t number, const Twine &msg) {
debugWrite(os.tell(), msg + "[" + utohexstr(number) + "]");
encodeSLEB128(number, os);
}
@ -127,12 +127,12 @@ void writeI32Const(raw_ostream &os, int32_t number, const Twine &msg) {
writeSleb128(os, number, msg);
}
void writeI64Const(raw_ostream &os, int32_t number, const Twine &msg) {
void writeI64Const(raw_ostream &os, int64_t number, const Twine &msg) {
writeU8(os, WASM_OPCODE_I64_CONST, "i64.const");
writeSleb128(os, number, msg);
}
void writeMemArg(raw_ostream &os, uint32_t alignment, uint32_t offset) {
void writeMemArg(raw_ostream &os, uint32_t alignment, uint64_t offset) {
writeUleb128(os, alignment, "alignment");
writeUleb128(os, offset, "offset");
}

View File

@ -18,9 +18,9 @@ namespace wasm {
void debugWrite(uint64_t offset, const Twine &msg);
void writeUleb128(raw_ostream &os, uint32_t number, const Twine &msg);
void writeUleb128(raw_ostream &os, uint64_t number, const Twine &msg);
void writeSleb128(raw_ostream &os, int32_t number, const Twine &msg);
void writeSleb128(raw_ostream &os, int64_t number, const Twine &msg);
void writeBytes(raw_ostream &os, const char *bytes, size_t count,
const Twine &msg);
@ -38,9 +38,9 @@ void writeSig(raw_ostream &os, const llvm::wasm::WasmSignature &sig);
void writeI32Const(raw_ostream &os, int32_t number, const Twine &msg);
void writeI64Const(raw_ostream &os, int32_t number, const Twine &msg);
void writeI64Const(raw_ostream &os, int64_t number, const Twine &msg);
void writeMemArg(raw_ostream &os, uint32_t alignment, uint32_t offset);
void writeMemArg(raw_ostream &os, uint32_t alignment, uint64_t offset);
void writeInitExpr(raw_ostream &os, const llvm::wasm::WasmInitExpr &initExpr);

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;
}