[WebAssembly] 64-bit memory limits
authorWouter van Oortmerssen <aardappel@gmail.com>
Tue, 30 Jun 2020 00:53:09 +0000 (17:53 -0700)
committerWouter van Oortmerssen <aardappel@gmail.com>
Mon, 6 Jul 2020 19:40:45 +0000 (12:40 -0700)
lld/test/wasm/data-layout.ll
lld/wasm/SyntheticSections.cpp
lld/wasm/SyntheticSections.h
lld/wasm/Writer.cpp
llvm/include/llvm/BinaryFormat/Wasm.h
llvm/lib/MC/WasmObjectWriter.cpp
llvm/lib/Object/WasmObjectFile.cpp
llvm/lib/ObjectYAML/WasmYAML.cpp
llvm/test/MC/WebAssembly/wasm64.s

index 759c544..863447c 100644 (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
index 0ed3ea2..70d6a10 100644 (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)
index 6cf593c..3e125ca 100644 (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
index 0434a4c..1401dc5 100644 (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));
   }
index b8d3b3f..d8d72ca 100644 (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 {
index c6029b6..d1290b0 100644 (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
index 25990b0..bb2e81d 100644 (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;
 }
 
index b12fd44..d1aa118 100644 (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
 }
 
index 2ec331f..b897188 100644 (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