-; 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
@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
; 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
; 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
; 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
}
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);
}
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)
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
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));
};
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;
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));
}
struct WasmLimits {
uint8_t Flags;
- uint32_t Initial;
- uint32_t Maximum;
+ uint64_t Initial;
+ uint64_t Maximum;
};
struct WasmTable {
enum : unsigned {
WASM_LIMITS_FLAG_HAS_MAX = 0x1,
WASM_LIMITS_FLAG_IS_SHARED = 0x2,
+ WASM_LIMITS_FLAG_IS_64 = 0x4,
};
enum : unsigned {
MCSectionWasm *Section;
StringRef Name;
uint32_t InitFlags;
- uint32_t Offset;
+ uint64_t Offset;
uint32_t Alignment;
uint32_t LinkerFlags;
SmallVector<char, 4> Data;
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);
}
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);
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);
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);
}
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
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
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;
}
#define BCase(X) IO.bitSetCase(Value, #X, wasm::WASM_LIMITS_FLAG_##X)
BCase(HAS_MAX);
BCase(IS_SHARED);
+ BCase(IS_64);
#undef BCase
}
# 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