diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -46,9 +46,9 @@ 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 @@ 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(decodeSLEB128(loc, &bytesRead)); + case R_WASM_MEMORY_ADDR_REL_SLEB64: + existingValue = static_cast(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(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 @@ 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 @@ 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 @@ 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(value), loc, 5); break; + case R_WASM_MEMORY_ADDR_SLEB64: + case R_WASM_MEMORY_ADDR_REL_SLEB64: + encodeSLEB128(static_cast(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 @@ 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 @@ // 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(value), buf); + case R_WASM_MEMORY_ADDR_SLEB64: + return encodeSLEB128(static_cast(value), buf); default: llvm_unreachable("unexpected relocation type"); } @@ -207,13 +225,16 @@ 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); } diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h --- a/lld/wasm/InputFiles.h +++ b/lld/wasm/InputFiles.h @@ -103,9 +103,9 @@ 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]; }; diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -101,12 +101,16 @@ // 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 @@ // 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 @@ const WasmSymbol &sym = wasmObj->syms()[reloc.Index]; return tableEntriesRel[sym.Info.ElementIndex]; } + 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_LEB: - case R_WASM_MEMORY_ADDR_REL_SLEB: { + 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 @@ } // 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 @@ 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(sym) || sym->isUndefWeak()) return 0; return cast(sym)->getVirtualAddress() + reloc.Addend; @@ -211,8 +229,8 @@ return getEventSymbol(reloc.Index)->getEventIndex(); case R_WASM_FUNCTION_OFFSET_I32: { auto *f = cast(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 @@ 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 @@ } 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(name, flags, this, seg, offset, size); if (seg->discarded) diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp --- a/lld/wasm/Relocations.cpp +++ b/lld/wasm/Relocations.cpp @@ -88,6 +88,8 @@ 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 @@ 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. diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h --- a/lld/wasm/SymbolTable.h +++ b/lld/wasm/SymbolTable.h @@ -53,8 +53,8 @@ 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, diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -327,7 +327,7 @@ 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; diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -264,7 +264,7 @@ 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 @@ 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 { diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -252,7 +252,7 @@ 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 @@ 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; } diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp --- a/lld/wasm/SyntheticSections.cpp +++ b/lld/wasm/SyntheticSections.cpp @@ -287,6 +287,7 @@ 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}; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -224,6 +224,7 @@ log("mem: stack base = " + Twine(memoryPtr)); memoryPtr += config->zStackSize; auto *sp = cast(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 @@ if (WasmSym::tlsSize && seg->name == ".tdata") { auto *tlsSize = cast(WasmSym::tlsSize); + assert(tlsSize->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST); tlsSize->global->global.InitExpr.Value.Int32 = seg->size; auto *tlsAlign = cast(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; } } diff --git a/lld/wasm/WriterUtils.h b/lld/wasm/WriterUtils.h --- a/lld/wasm/WriterUtils.h +++ b/lld/wasm/WriterUtils.h @@ -18,9 +18,9 @@ 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 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); diff --git a/lld/wasm/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp --- a/lld/wasm/WriterUtils.cpp +++ b/lld/wasm/WriterUtils.cpp @@ -67,12 +67,12 @@ 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 @@ 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"); } diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -159,8 +159,8 @@ // 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 { diff --git a/llvm/include/llvm/BinaryFormat/WasmRelocs.def b/llvm/include/llvm/BinaryFormat/WasmRelocs.def --- a/llvm/include/llvm/BinaryFormat/WasmRelocs.def +++ b/llvm/include/llvm/BinaryFormat/WasmRelocs.def @@ -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) diff --git a/llvm/include/llvm/ObjectYAML/WasmYAML.h b/llvm/include/llvm/ObjectYAML/WasmYAML.h --- a/llvm/include/llvm/ObjectYAML/WasmYAML.h +++ b/llvm/include/llvm/ObjectYAML/WasmYAML.h @@ -107,8 +107,10 @@ 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 { diff --git a/llvm/lib/BinaryFormat/Wasm.cpp b/llvm/lib/BinaryFormat/Wasm.cpp --- a/llvm/lib/BinaryFormat/Wasm.cpp +++ b/llvm/lib/BinaryFormat/Wasm.cpp @@ -39,9 +39,13 @@ 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; diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -185,21 +185,21 @@ // 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 +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 +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 @@ 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 @@ updateCustomSectionRelocations(const SmallVector &Functions, const MCAsmLayout &Layout); - uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry); + uint64_t getProvisionalValue(const WasmRelocationEntry &RelEntry); void applyRelocations(ArrayRef Relocations, uint64_t ContentsOffset); @@ -410,8 +416,8 @@ // Write the final section size to the payload_len field, which follows // the section id byte. - writePatchableLEB(static_cast(W.OS), Size, - Section.SizeOffset); + writePatchableLEB<5>(static_cast(W.OS), Size, + Section.SizeOffset); } // Emit the Wasm header. @@ -549,7 +555,7 @@ // 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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(Layout.getSymbolOffset(WS)), - static_cast(Size)}; + DataSection.getSegmentIndex(), Layout.getSymbolOffset(WS), + static_cast(Size)}; DataLocations[&WS] = Ref; LLVM_DEBUG(dbgs() << " -> segment index: " << Ref.Segment << "\n"); diff --git a/llvm/lib/Object/RelocationResolver.cpp b/llvm/lib/Object/RelocationResolver.cpp --- a/llvm/lib/Object/RelocationResolver.cpp +++ b/llvm/lib/Object/RelocationResolver.cpp @@ -505,6 +505,17 @@ } } +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 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 getRelocationResolver(const ObjectFile &Obj) { if (Obj.isCOFF()) { @@ -607,6 +630,8 @@ } else if (Obj.isWasm()) { if (Obj.getArch() == Triple::wasm32) return {supportsWasm32, resolveWasm32}; + if (Obj.getArch() == Triple::wasm64) + return {supportsWasm64, resolveWasm64}; return {nullptr, nullptr}; } diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -156,6 +156,10 @@ 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 @@ 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("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("invalid data symbol offset", object_error::parse_failed); @@ -818,6 +822,15 @@ 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("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("Bad relocation function index", @@ -840,12 +853,18 @@ // 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("Bad relocation offset", object_error::parse_failed); @@ -1331,8 +1350,13 @@ // 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; diff --git a/llvm/lib/ObjectYAML/WasmEmitter.cpp b/llvm/lib/ObjectYAML/WasmEmitter.cpp --- a/llvm/lib/ObjectYAML/WasmEmitter.cpp +++ b/llvm/lib/ObjectYAML/WasmEmitter.cpp @@ -532,8 +532,11 @@ 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); diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -846,6 +846,16 @@ 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(Inst.getOpcode())); + if (Opc64 >= 0) { + Inst.setOpcode(Opc64); + } + } Out.emitInstruction(Inst, getSTI()); if (CurrentState == EndFunction) { onEndOfFunction(); diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -101,9 +101,6 @@ 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 @@ break; case WebAssembly::OPERAND_OFFSET64: FixupKind = MCFixupKind(WebAssembly::fixup_uleb128_i64); + PaddedSize = 10; break; default: llvm_unreachable("unexpected symbolic operand kind"); diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp @@ -78,7 +78,8 @@ 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 @@ 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 @@ 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 @@ 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"); } diff --git a/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.h b/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.h --- a/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.h +++ b/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.h @@ -21,6 +21,13 @@ 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 diff --git a/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.cpp b/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.cpp --- a/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.cpp +++ b/llvm/lib/Target/WebAssembly/TargetInfo/WebAssemblyTargetInfo.cpp @@ -32,3 +32,9 @@ RegisterTarget 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" diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td @@ -14,9 +14,10 @@ let UseNamedOperandTable = 1 in multiclass ATOMIC_I pattern_r, string asmstr_r, - string asmstr_s, bits<32> atomic_op> { + string asmstr_s, bits<32> atomic_op, + string is64 = "false"> { defm "" : I, + !or(0xfe00, !and(0xff, atomic_op)), is64>, Requires<[HasAtomics]>; } @@ -37,13 +38,13 @@ (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 @@ 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 @@ //===----------------------------------------------------------------------===// multiclass AtomicStore { - defm "" : WebAssemblyStore, - Requires<[HasAtomics]>; + defm "" : WebAssemblyStore; } defm ATOMIC_STORE_I32 : AtomicStore; @@ -486,13 +487,13 @@ (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; @@ -797,14 +798,14 @@ 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 : diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrFormats.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrFormats.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrFormats.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrFormats.td @@ -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 inst, string asmstr, string stack> : StackRel, - Instruction { +class WebAssemblyInst 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 @@ // Normal instructions. Default instantiation of a WebAssemblyInst. class NI pattern, string stack, - string asmstr = "", bits<32> inst = -1> - : WebAssemblyInst { + string asmstr = "", bits<32> inst = -1, string is64 = "false"> + : WebAssemblyInst { dag OutOperandList = oops; dag InOperandList = iops; let Pattern = pattern; @@ -52,11 +54,11 @@ // there is always an equivalent pair of instructions. multiclass I 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; + def "" : NI; let BaseName = NAME in - def _S : NI; + def _S : NI; } // For instructions that have no register ops, so both sets are the same. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -203,6 +203,19 @@ } //===----------------------------------------------------------------------===// +// 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. //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td @@ -41,19 +41,19 @@ // Defines atomic and non-atomic loads, regular and extending. multiclass WebAssemblyLoad reqs> { + list 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; 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; } } @@ -236,21 +236,24 @@ defm : LoadPatGlobalAddrOffOnly; // Defines atomic and non-atomic stores, regular and truncating -multiclass WebAssemblyStore { +multiclass WebAssemblyStore 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; 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; } // Basic store. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -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 diff --git a/llvm/test/MC/WebAssembly/wasm64.s b/llvm/test/MC/WebAssembly/wasm64.s --- a/llvm/test/MC/WebAssembly/wasm64.s +++ b/llvm/test/MC/WebAssembly/wasm64.s @@ -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 @@ 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 @@ 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 @@ # 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 @@ # 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 @@ # 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: ... diff --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp --- a/llvm/tools/llvm-readobj/WasmDumper.cpp +++ b/llvm/tools/llvm-readobj/WasmDumper.cpp @@ -182,6 +182,10 @@ W.printNumber("Size", static_cast(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; }