mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-19 08:36:44 +00: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:
parent
017969de76
commit
3b29376e3f
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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];
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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};
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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 :
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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: ...
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user