[WebAssembly] 64-bit memory limits

This commit is contained in:
Wouter van Oortmerssen 2020-06-29 17:53:09 -07:00
parent fa1fecc73d
commit 4d135b0446
9 changed files with 63 additions and 36 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -224,8 +224,16 @@ 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;
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));
}

View File

@ -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 {

View File

@ -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<char, 4> Data;
@ -326,7 +326,7 @@ private:
void writeValueType(wasm::ValType Ty) { W.OS << static_cast<char>(Ty); }
void writeTypeSection(ArrayRef<WasmSignature> Signatures);
void writeImportSection(ArrayRef<wasm::WasmImport> Imports, uint32_t DataSize,
void writeImportSection(ArrayRef<wasm::WasmImport> Imports, uint64_t DataSize,
uint32_t NumElements);
void writeFunctionSection(ArrayRef<WasmFunction> Functions);
void writeExportSection(ArrayRef<wasm::WasmExport> Exports);
@ -730,12 +730,12 @@ void WasmObjectWriter::writeTypeSection(ArrayRef<WasmSignature> Signatures) {
}
void WasmObjectWriter::writeImportSection(ArrayRef<wasm::WasmImport> 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<wasm::WasmImport> 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<int32_t>().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<wasm::WasmSymbolInfo, 4> SymbolInfos;
SmallVector<std::pair<uint16_t, uint32_t>, 2> InitFuncs;
std::map<StringRef, std::vector<WasmComdatEntry>> 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

View File

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

View File

@ -522,6 +522,7 @@ void ScalarBitSetTraits<WasmYAML::LimitFlags>::bitset(
#define BCase(X) IO.bitSetCase(Value, #X, wasm::WASM_LIMITS_FLAG_##X)
BCase(HAS_MAX);
BCase(IS_SHARED);
BCase(IS_64);
#undef BCase
}

View File

@ -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