From 4d135b0446dc34885811bf103ba2c8b69fe6793b Mon Sep 17 00:00:00 2001 From: Wouter van Oortmerssen Date: Mon, 29 Jun 2020 17:53:09 -0700 Subject: [PATCH] [WebAssembly] 64-bit memory limits --- lld/test/wasm/data-layout.ll | 37 ++++++++++++++++++++--------------- lld/wasm/SyntheticSections.cpp | 4 ++++ lld/wasm/SyntheticSections.h | 4 ++-- lld/wasm/Writer.cpp | 24 +++++++++++++++++------ llvm/include/llvm/BinaryFormat/Wasm.h | 5 +++-- llvm/lib/MC/WasmObjectWriter.cpp | 19 ++++++++++-------- llvm/lib/Object/WasmObjectFile.cpp | 4 ++-- llvm/lib/ObjectYAML/WasmYAML.cpp | 1 + llvm/test/MC/WebAssembly/wasm64.s | 1 + 9 files changed, 63 insertions(+), 36 deletions(-) diff --git a/lld/test/wasm/data-layout.ll b/lld/test/wasm/data-layout.ll index 759c5440fe9..863447cfd26 100644 --- a/lld/test/wasm/data-layout.ll +++ b/lld/test/wasm/data-layout.ll @@ -1,7 +1,12 @@ -; RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/hello.s -o %t.hello.o -; RUN: llc -filetype=obj %s -o %t.o +; RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/hello.s -o %t.hello32.o +; RUN: llc -mtriple=wasm32-unknown-unknown -filetype=obj %s -o %t32.o +; RUN: wasm-ld -m wasm32 -no-gc-sections --export=__data_end --export=__heap_base --allow-undefined --no-entry -o %t32.wasm %t32.o %t.hello32.o +; RUN: obj2yaml %t32.wasm | FileCheck --check-prefixes CHECK,CHK32 %s -target triple = "wasm32-unknown-unknown" +; RUN: llvm-mc -filetype=obj -triple=wasm64-unknown-unknown %p/Inputs/hello.s -o %t.hello64.o +; RUN: llc -mtriple=wasm64-unknown-unknown -filetype=obj %s -o %t64.o +; RUN: wasm-ld -m wasm64 -no-gc-sections --export=__data_end --export=__heap_base --allow-undefined --no-entry -o %t64.wasm %t64.o %t.hello64.o +; RUN: obj2yaml %t64.wasm | FileCheck --check-prefixes CHECK,CHK64 %s @foo = hidden global i32 1, align 4 @aligned_bar = hidden global i32 3, align 16 @@ -13,26 +18,28 @@ target triple = "wasm32-unknown-unknown" @local_struct = hidden global %struct.s zeroinitializer, align 4 @local_struct_internal_ptr = hidden local_unnamed_addr global i32* getelementptr inbounds (%struct.s, %struct.s* @local_struct, i32 0, i32 1), align 4 -; RUN: wasm-ld -no-gc-sections --export=__data_end --export=__heap_base --allow-undefined --no-entry -o %t.wasm %t.o %t.hello.o -; RUN: obj2yaml %t.wasm | FileCheck %s - ; CHECK: - Type: MEMORY ; CHECK-NEXT: Memories: -; CHECK-NEXT: - Initial: 0x00000002 +; CHK32-NEXT: - Initial: 0x00000002 +; CHK64-NEXT: - Flags: [ IS_64 ] +; CHK64-NEXT: Initial: 0x00000002 ; CHECK-NEXT: - Type: GLOBAL ; CHECK-NEXT: Globals: ; CHECK-NEXT: - Index: 0 -; CHECK-NEXT: Type: I32 +; CHK32-NEXT: Type: I32 +; CHK64-NEXT: Type: I64 ; CHECK-NEXT: Mutable: true ; CHECK-NEXT: InitExpr: -; CHECK-NEXT: Opcode: I32_CONST +; CHK32-NEXT: Opcode: I32_CONST +; CHK64-NEXT: Opcode: I64_CONST ; CHECK-NEXT: Value: 66624 ; CHECK-NEXT: - Index: 1 ; CHECK-NEXT: Type: I32 ; CHECK-NEXT: Mutable: false ; CHECK-NEXT: InitExpr: ; CHECK-NEXT: Opcode: I32_CONST -; CHECK-NEXT: Value: 1080 +; CHK32-NEXT: Value: 1080 +; CHK64-NEXT: Value: 1088 ; CHECK-NEXT: - Index: 2 ; CHECK-NEXT: Type: I32 ; CHECK-NEXT: Mutable: false @@ -53,13 +60,11 @@ target triple = "wasm32-unknown-unknown" ; CHECK-NEXT: Offset: ; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Value: 1040 -; CHECK-NEXT: Content: '0100000000000000000000000000000003000000000000000004000034040000' -; CHECK-NEXT: - Type: CUSTOM ; RUN: wasm-ld -no-gc-sections --allow-undefined --no-entry \ -; RUN: --initial-memory=131072 --max-memory=131072 -o %t_max.wasm %t.o \ -; RUN: %t.hello.o +; RUN: --initial-memory=131072 --max-memory=131072 -o %t_max.wasm %t32.o \ +; RUN: %t.hello32.o ; RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-MAX ; CHECK-MAX: - Type: MEMORY @@ -70,7 +75,7 @@ target triple = "wasm32-unknown-unknown" ; RUN: wasm-ld -no-gc-sections --allow-undefined --no-entry --shared-memory \ ; RUN: --features=atomics,bulk-memory --initial-memory=131072 \ -; RUN: --max-memory=131072 -o %t_max.wasm %t.o %t.hello.o +; RUN: --max-memory=131072 -o %t_max.wasm %t32.o %t.hello32.o ; RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-SHARED ; CHECK-SHARED: - Type: MEMORY @@ -79,7 +84,7 @@ target triple = "wasm32-unknown-unknown" ; CHECK-SHARED-NEXT: Initial: 0x00000002 ; CHECK-SHARED-NEXT: Maximum: 0x00000002 -; RUN: wasm-ld --relocatable -o %t_reloc.wasm %t.o %t.hello.o +; RUN: wasm-ld --relocatable -o %t_reloc.wasm %t32.o %t.hello32.o ; RUN: obj2yaml %t_reloc.wasm | FileCheck %s -check-prefix=RELOC ; RELOC: - Type: DATA diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp index 0ed3ea273d7..70d6a10200c 100644 --- a/lld/wasm/SyntheticSections.cpp +++ b/lld/wasm/SyntheticSections.cpp @@ -139,6 +139,8 @@ void ImportSection::writeBody() { } if (config->sharedMemory) import.Memory.Flags |= WASM_LIMITS_FLAG_IS_SHARED; + if (config->is64) + import.Memory.Flags |= WASM_LIMITS_FLAG_IS_64; writeImport(os, import); } @@ -234,6 +236,8 @@ void MemorySection::writeBody() { flags |= WASM_LIMITS_FLAG_HAS_MAX; if (config->sharedMemory) flags |= WASM_LIMITS_FLAG_IS_SHARED; + if (config->is64) + flags |= WASM_LIMITS_FLAG_IS_64; writeUleb128(os, flags, "memory limits flags"); writeUleb128(os, numMemoryPages, "initial pages"); if (hasMax) diff --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h index 6cf593cf016..3e125ca84e4 100644 --- a/lld/wasm/SyntheticSections.h +++ b/lld/wasm/SyntheticSections.h @@ -167,8 +167,8 @@ public: bool isNeeded() const override { return !config->importMemory; } void writeBody() override; - uint32_t numMemoryPages = 0; - uint32_t maxMemoryPages = 0; + uint64_t numMemoryPages = 0; + uint64_t maxMemoryPages = 0; }; // The event section contains a list of declared wasm events associated with the diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index 0434a4cf029..1401dc50931 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -224,8 +224,16 @@ void Writer::layoutMemory() { 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; + switch (sp->global->global.InitExpr.Opcode) { + case WASM_OPCODE_I32_CONST: + sp->global->global.InitExpr.Value.Int32 = memoryPtr; + break; + case WASM_OPCODE_I64_CONST: + sp->global->global.InitExpr.Value.Int64 = memoryPtr; + break; + default: + llvm_unreachable("init expr must be i32/i64.const"); + } log("mem: stack top = " + Twine(memoryPtr)); }; @@ -296,13 +304,16 @@ void Writer::layoutMemory() { if (WasmSym::heapBase) WasmSym::heapBase->setVirtualAddress(memoryPtr); + uint64_t maxMemorySetting = 1ULL << (config->is64 ? 48 : 32); + if (config->initialMemory != 0) { if (config->initialMemory != alignTo(config->initialMemory, WasmPageSize)) error("initial memory must be " + Twine(WasmPageSize) + "-byte aligned"); if (memoryPtr > config->initialMemory) error("initial memory too small, " + Twine(memoryPtr) + " bytes needed"); - if (config->initialMemory > (1ULL << 32)) - error("initial memory too large, cannot be greater than 4294967296"); + if (config->initialMemory > maxMemorySetting) + error("initial memory too large, cannot be greater than " + + Twine(maxMemorySetting)); memoryPtr = config->initialMemory; } out.dylinkSec->memSize = memoryPtr; @@ -316,8 +327,9 @@ void Writer::layoutMemory() { error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned"); if (memoryPtr > config->maxMemory) error("maximum memory too small, " + Twine(memoryPtr) + " bytes needed"); - if (config->maxMemory > (1ULL << 32)) - error("maximum memory too large, cannot be greater than 4294967296"); + if (config->maxMemory > maxMemorySetting) + error("maximum memory too large, cannot be greater than " + + Twine(maxMemorySetting)); out.memorySec->maxMemoryPages = config->maxMemory / WasmPageSize; log("mem: max pages = " + Twine(out.memorySec->maxMemoryPages)); } diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h index b8d3b3f4d66..d8d72cacf22 100644 --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -63,8 +63,8 @@ struct WasmExport { struct WasmLimits { uint8_t Flags; - uint32_t Initial; - uint32_t Maximum; + uint64_t Initial; + uint64_t Maximum; }; struct WasmTable { @@ -282,6 +282,7 @@ enum : unsigned { enum : unsigned { WASM_LIMITS_FLAG_HAS_MAX = 0x1, WASM_LIMITS_FLAG_IS_SHARED = 0x2, + WASM_LIMITS_FLAG_IS_64 = 0x4, }; enum : unsigned { diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp index c6029b66a38..d1290b050ef 100644 --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -108,7 +108,7 @@ struct WasmDataSegment { MCSectionWasm *Section; StringRef Name; uint32_t InitFlags; - uint32_t Offset; + uint64_t Offset; uint32_t Alignment; uint32_t LinkerFlags; SmallVector Data; @@ -326,7 +326,7 @@ private: void writeValueType(wasm::ValType Ty) { W.OS << static_cast(Ty); } void writeTypeSection(ArrayRef Signatures); - void writeImportSection(ArrayRef Imports, uint32_t DataSize, + void writeImportSection(ArrayRef Imports, uint64_t DataSize, uint32_t NumElements); void writeFunctionSection(ArrayRef Functions); void writeExportSection(ArrayRef Exports); @@ -730,12 +730,12 @@ void WasmObjectWriter::writeTypeSection(ArrayRef Signatures) { } void WasmObjectWriter::writeImportSection(ArrayRef Imports, - uint32_t DataSize, + uint64_t DataSize, uint32_t NumElements) { if (Imports.empty()) return; - uint32_t NumPages = (DataSize + wasm::WasmPageSize - 1) / wasm::WasmPageSize; + uint64_t NumPages = (DataSize + wasm::WasmPageSize - 1) / wasm::WasmPageSize; SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_IMPORT); @@ -755,8 +755,8 @@ void WasmObjectWriter::writeImportSection(ArrayRef Imports, W.OS << char(Import.Global.Mutable ? 1 : 0); break; case wasm::WASM_EXTERNAL_MEMORY: - encodeULEB128(0, W.OS); // flags - encodeULEB128(NumPages, W.OS); // initial + encodeULEB128(Import.Memory.Flags, W.OS); + encodeULEB128(NumPages, W.OS); // initial break; case wasm::WASM_EXTERNAL_TABLE: W.OS << char(Import.Table.ElemType); @@ -935,7 +935,9 @@ uint32_t WasmObjectWriter::writeDataSection(const MCAsmLayout &Layout) { if (Segment.InitFlags & wasm::WASM_SEGMENT_HAS_MEMINDEX) encodeULEB128(0, W.OS); // memory index if ((Segment.InitFlags & wasm::WASM_SEGMENT_IS_PASSIVE) == 0) { - W.OS << char(wasm::WASM_OPCODE_I32_CONST); + W.OS << char(Segment.Offset > std::numeric_limits().max() + ? wasm::WASM_OPCODE_I64_CONST + : wasm::WASM_OPCODE_I32_CONST); encodeSLEB128(Segment.Offset, W.OS); // offset W.OS << char(wasm::WASM_OPCODE_END); } @@ -1187,7 +1189,7 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm, SmallVector SymbolInfos; SmallVector, 2> InitFuncs; std::map> Comdats; - uint32_t DataSize = 0; + uint64_t DataSize = 0; // For now, always emit the memory import, since loads and stores are not // valid without it. In the future, we could perhaps be more clever and omit @@ -1196,6 +1198,7 @@ uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm, MemImport.Module = "env"; MemImport.Field = "__linear_memory"; MemImport.Kind = wasm::WASM_EXTERNAL_MEMORY; + MemImport.Memory.Flags = is64Bit() ? wasm::WASM_LIMITS_FLAG_IS_64 : 0; Imports.push_back(MemImport); // For now, always emit the table section, since indirect calls are not diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp index 25990b01211..bb2e81d6404 100644 --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -208,9 +208,9 @@ static Error readInitExpr(wasm::WasmInitExpr &Expr, static wasm::WasmLimits readLimits(WasmObjectFile::ReadContext &Ctx) { wasm::WasmLimits Result; Result.Flags = readVaruint32(Ctx); - Result.Initial = readVaruint32(Ctx); + Result.Initial = readVaruint64(Ctx); if (Result.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) - Result.Maximum = readVaruint32(Ctx); + Result.Maximum = readVaruint64(Ctx); return Result; } diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp index b12fd448de5..d1aa1181a34 100644 --- a/llvm/lib/ObjectYAML/WasmYAML.cpp +++ b/llvm/lib/ObjectYAML/WasmYAML.cpp @@ -522,6 +522,7 @@ void ScalarBitSetTraits::bitset( #define BCase(X) IO.bitSetCase(Value, #X, wasm::WASM_LIMITS_FLAG_##X) BCase(HAS_MAX); BCase(IS_SHARED); + BCase(IS_64); #undef BCase } diff --git a/llvm/test/MC/WebAssembly/wasm64.s b/llvm/test/MC/WebAssembly/wasm64.s index 2ec331f751d..b89718816a9 100644 --- a/llvm/test/MC/WebAssembly/wasm64.s +++ b/llvm/test/MC/WebAssembly/wasm64.s @@ -147,6 +147,7 @@ test: # BIN-NEXT: Field: __linear_memory # BIN-NEXT: Kind: MEMORY # BIN-NEXT: Memory: +# BIN-NEXT: Flags: [ IS_64 ] # BIN-NEXT: Initial: 0x00000001 # BIN-NEXT: - Module: env # BIN-NEXT: Field: __indirect_function_table -- 2.11.0