diff options
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 74 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 75 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 2 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 128 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 7 |
5 files changed, 198 insertions, 88 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index e958dca6e..18a66fb2c 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -99,12 +99,11 @@ int32_t WasmBinaryWriter::writeU32LEBPlaceholder() { return ret; } -void WasmBinaryWriter::writeResizableLimits(Address initial, - Address maximum, - bool hasMaximum, - bool shared) { +void WasmBinaryWriter::writeResizableLimits( + Address initial, Address maximum, bool hasMaximum, bool shared, bool is64) { uint32_t flags = (hasMaximum ? (uint32_t)BinaryConsts::HasMaximum : 0U) | - (shared ? (uint32_t)BinaryConsts::IsShared : 0U); + (shared ? (uint32_t)BinaryConsts::IsShared : 0U) | + (is64 ? (uint32_t)BinaryConsts::Is64 : 0U); o << U32LEB(flags); o << U32LEB(initial); if (hasMaximum) { @@ -203,7 +202,8 @@ void WasmBinaryWriter::writeMemory() { writeResizableLimits(wasm->memory.initial, wasm->memory.max, wasm->memory.hasMax(), - wasm->memory.shared); + wasm->memory.shared, + wasm->memory.is64()); finishSection(start); } @@ -267,7 +267,8 @@ void WasmBinaryWriter::writeImports() { writeResizableLimits(wasm->memory.initial, wasm->memory.max, wasm->memory.hasMax(), - wasm->memory.shared); + wasm->memory.shared, + wasm->memory.is64()); } if (wasm->table.imported()) { BYN_TRACE("write one table\n"); @@ -277,7 +278,8 @@ void WasmBinaryWriter::writeImports() { writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.hasMax(), - /*shared=*/false); + /*shared=*/false, + /*is64*/ false); } finishSection(start); } @@ -505,7 +507,8 @@ void WasmBinaryWriter::writeFunctionTableDeclaration() { writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.hasMax(), - /*shared=*/false); + /*shared=*/false, + /*is64*/ false); finishSection(start); } @@ -752,6 +755,8 @@ void WasmBinaryWriter::writeFeaturesSection() { return BinaryConsts::UserSections::MultivalueFeature; case FeatureSet::GC: return BinaryConsts::UserSections::GCFeature; + case FeatureSet::Memory64: + return BinaryConsts::UserSections::Memory64Feature; default: WASM_UNREACHABLE("unexpected feature flag"); } @@ -1317,6 +1322,7 @@ void WasmBinaryBuilder::readMemory() { getResizableLimits(wasm.memory.initial, wasm.memory.max, wasm.memory.shared, + wasm.memory.indexType, Memory::kUnlimitedSize); } @@ -1370,15 +1376,18 @@ Name WasmBinaryBuilder::getEventName(Index index) { void WasmBinaryBuilder::getResizableLimits(Address& initial, Address& max, bool& shared, + Type& indexType, Address defaultIfNoMax) { auto flags = getU32LEB(); initial = getU32LEB(); bool hasMax = (flags & BinaryConsts::HasMaximum) != 0; bool isShared = (flags & BinaryConsts::IsShared) != 0; + bool is64 = (flags & BinaryConsts::Is64) != 0; if (isShared && !hasMax) { throwError("shared memory must have max size"); } shared = isShared; + indexType = is64 ? Type::i64 : Type::i32; if (hasMax) { max = getU32LEB(); } else { @@ -1425,11 +1434,18 @@ void WasmBinaryBuilder::readImports() { } wasm.table.exists = true; bool is_shared; - getResizableLimits( - wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); + Type indexType; + getResizableLimits(wasm.table.initial, + wasm.table.max, + is_shared, + indexType, + Table::kUnlimitedSize); if (is_shared) { throwError("Tables may not be shared"); } + if (indexType == Type::i64) { + throwError("Tables may not be 64-bit"); + } break; } case ExternalKind::Memory: { @@ -1440,6 +1456,7 @@ void WasmBinaryBuilder::readImports() { getResizableLimits(wasm.memory.initial, wasm.memory.max, wasm.memory.shared, + wasm.memory.indexType, Memory::kUnlimitedSize); break; } @@ -2096,11 +2113,18 @@ void WasmBinaryBuilder::readFunctionTableDeclaration() { throwError("ElementType must be funcref in MVP"); } bool is_shared; - getResizableLimits( - wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); + Type indexType; + getResizableLimits(wasm.table.initial, + wasm.table.max, + is_shared, + indexType, + Table::kUnlimitedSize); if (is_shared) { throwError("Tables may not be shared"); } + if (indexType == Type::i64) { + throwError("Tables may not be 64-bit"); + } } void WasmBinaryBuilder::readTableElements() { @@ -2309,6 +2333,8 @@ void WasmBinaryBuilder::readFeatures(size_t payloadLen) { wasm.features.setMultivalue(); } else if (name == BinaryConsts::UserSections::GCFeature) { wasm.features.setGC(); + } else if (name == BinaryConsts::UserSections::Memory64Feature) { + wasm.features.setMemory64(); } } } @@ -2452,14 +2478,24 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { case BinaryConsts::BrOnExn: visitBrOnExn((curr = allocator.alloc<BrOnExn>())->cast<BrOnExn>()); break; - case BinaryConsts::MemorySize: - visitMemorySize( - (curr = allocator.alloc<MemorySize>())->cast<MemorySize>()); + case BinaryConsts::MemorySize: { + auto size = allocator.alloc<MemorySize>(); + if (wasm.memory.is64()) { + size->make64(); + } + curr = size; + visitMemorySize(size); break; - case BinaryConsts::MemoryGrow: - visitMemoryGrow( - (curr = allocator.alloc<MemoryGrow>())->cast<MemoryGrow>()); + } + case BinaryConsts::MemoryGrow: { + auto grow = allocator.alloc<MemoryGrow>(); + if (wasm.memory.is64()) { + grow->make64(); + } + curr = grow; + visitMemoryGrow(grow); break; + } case BinaryConsts::AtomicPrefix: { code = static_cast<uint8_t>(getU32LEB()); if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) { diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 2883519f2..8bbcc9ac0 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -50,12 +50,13 @@ int unhex(char c) { namespace wasm { -static Address getCheckedAddress(const Element* s, const char* errorText) { - uint64_t num = atoll(s->c_str()); - if (num > std::numeric_limits<Address::address_t>::max()) { - throw ParseException(errorText, s->line, s->col); +static Address getAddress(const Element* s) { return atoll(s->c_str()); } + +static void +checkAddress(Address a, const char* errorText, const Element* errorElem) { + if (a > std::numeric_limits<Address::address32_t>::max()) { + throw ParseException(errorText, errorElem->line, errorElem->col); } - return num; } static bool elementStartsWith(Element& s, IString str) { @@ -1023,12 +1024,18 @@ Expression* SExpressionWasmBuilder::makeDrop(Element& s) { Expression* SExpressionWasmBuilder::makeMemorySize(Element& s) { auto ret = allocator.alloc<MemorySize>(); + if (wasm.memory.is64()) { + ret->make64(); + } ret->finalize(); return ret; } Expression* SExpressionWasmBuilder::makeMemoryGrow(Element& s) { auto ret = allocator.alloc<MemoryGrow>(); + if (wasm.memory.is64()) { + ret->make64(); + } ret->delta = parseExpression(s[1]); ret->finalize(); return ret; @@ -2007,18 +2014,40 @@ void SExpressionWasmBuilder::stringToBinary(const char* input, data.resize(actual); } +Index SExpressionWasmBuilder::parseMemoryIndex(Element& s, Index i) { + if (i < s.size() && s[i]->isStr()) { + if (s[i]->str() == "i64") { + i++; + wasm.memory.indexType = Type::i64; + } else if (s[i]->str() == "i32") { + i++; + wasm.memory.indexType = Type::i32; + } + } + return i; +} + Index SExpressionWasmBuilder::parseMemoryLimits(Element& s, Index i) { - wasm.memory.initial = getCheckedAddress(s[i++], "excessive memory init"); + i = parseMemoryIndex(s, i); if (i == s.size()) { - wasm.memory.max = Memory::kUnlimitedSize; - return i; + throw ParseException("missing memory limits", s.line, s.col); } - uint64_t max = atoll(s[i]->c_str()); - if (max > Memory::kMaxSize) { - throw ParseException("total memory must be <= 4GB", s[i]->line, s[i]->col); + auto initElem = s[i++]; + wasm.memory.initial = getAddress(initElem); + if (!wasm.memory.is64()) { + checkAddress(wasm.memory.initial, "excessive memory init", initElem); } - wasm.memory.max = max; - return ++i; + if (i == s.size()) { + wasm.memory.max = Memory::kUnlimitedSize; + } else { + auto maxElem = s[i++]; + wasm.memory.max = getAddress(maxElem); + if (!wasm.memory.is64() && wasm.memory.max > Memory::kMaxSize32) { + throw ParseException( + "total memory must be <= 4GB", maxElem->line, maxElem->col); + } + } + return i; } void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { @@ -2031,6 +2060,7 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { if (s[i]->dollared()) { wasm.memory.name = s[i++]->str(); } + i = parseMemoryIndex(s, i); Name importModule, importBase; if (s[i]->isList()) { auto& inner = *s[i]; @@ -2057,8 +2087,9 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { throw ParseException("bad import ending", inner.line, inner.col); } // (memory (data ..)) format + auto j = parseMemoryIndex(inner, 1); auto offset = allocator.alloc<Const>()->set(Literal(int32_t(0))); - parseInnerData(*s[i], 1, offset, false); + parseInnerData(inner, j, offset, false); wasm.memory.initial = wasm.memory.segments[0].data.size(); return; } @@ -2075,7 +2106,11 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { if (elementStartsWith(curr, DATA)) { offsetValue = 0; } else { - offsetValue = getCheckedAddress(curr[j++], "excessive memory offset"); + auto offsetElem = curr[j++]; + offsetValue = getAddress(offsetElem); + if (!wasm.memory.is64()) { + checkAddress(offsetValue, "excessive memory offset", offsetElem); + } } const char* input = curr[j]->c_str(); auto* offset = allocator.alloc<Const>(); @@ -2263,12 +2298,14 @@ void SExpressionWasmBuilder::parseImport(Element& s) { wasm.table.module = module; wasm.table.base = base; if (j < inner.size() - 1) { - wasm.table.initial = - getCheckedAddress(inner[j++], "excessive table init size"); + auto initElem = inner[j++]; + wasm.table.initial = getAddress(initElem); + checkAddress(wasm.table.initial, "excessive table init size", initElem); } if (j < inner.size() - 1) { - wasm.table.max = - getCheckedAddress(inner[j++], "excessive table max size"); + auto maxElem = inner[j++]; + wasm.table.max = getAddress(maxElem); + checkAddress(wasm.table.max, "excessive table max size", maxElem); } else { wasm.table.max = Table::kUnlimitedSize; } diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index edfdf31d1..30816546a 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1907,7 +1907,7 @@ void StackIRGenerator::emitScopeEnd(Expression* curr) { StackInst* StackIRGenerator::makeStackInst(StackInst::Op op, Expression* origin) { - auto* ret = allocator.alloc<StackInst>(); + auto* ret = module.allocator.alloc<StackInst>(); ret->op = op; ret->origin = origin; auto stackType = origin->type; diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index c141fdd6f..505753c88 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -388,6 +388,8 @@ private: void validateAlignment( size_t align, Type type, Index bytes, bool isAtomic, Expression* curr); void validateMemBytes(uint8_t bytes, Type type, Expression* curr); + + Type indexType() { return getModule()->memory.indexType; } }; void FunctionValidator::noteLabelName(Name name) { @@ -931,7 +933,10 @@ void FunctionValidator::visitLoad(Load* curr) { validateMemBytes(curr->bytes, curr->type, curr); validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr); shouldBeEqualOrFirstIsUnreachable( - curr->ptr->type, Type(Type::i32), curr, "load pointer type must be i32"); + curr->ptr->type, + indexType(), + curr, + "load pointer type must match memory index type"); if (curr->isAtomic) { shouldBeFalse(curr->signed_, curr, "atomic loads must be unsigned"); shouldBeIntOrUnreachable( @@ -963,7 +968,10 @@ void FunctionValidator::visitStore(Store* curr) { validateAlignment( curr->align, curr->valueType, curr->bytes, curr->isAtomic, curr); shouldBeEqualOrFirstIsUnreachable( - curr->ptr->type, Type(Type::i32), curr, "store pointer type must be i32"); + curr->ptr->type, + indexType(), + curr, + "store pointer must match memory index type"); shouldBeUnequal(curr->value->type, Type(Type::none), curr, @@ -986,10 +994,11 @@ void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) { curr, "Atomic operation with non-shared memory"); validateMemBytes(curr->bytes, curr->type, curr); - shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, - Type(Type::i32), - curr, - "AtomicRMW pointer type must be i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->ptr->type, + indexType(), + curr, + "AtomicRMW pointer type must match memory index type"); shouldBeEqualOrFirstIsUnreachable(curr->type, curr->value->type, curr, @@ -1009,7 +1018,10 @@ void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) { "Atomic operation with non-shared memory"); validateMemBytes(curr->bytes, curr->type, curr); shouldBeEqualOrFirstIsUnreachable( - curr->ptr->type, Type(Type::i32), curr, "cmpxchg pointer type must be i32"); + curr->ptr->type, + indexType(), + curr, + "cmpxchg pointer must match memory index type"); if (curr->expected->type != Type::unreachable && curr->replacement->type != Type::unreachable) { shouldBeEqual(curr->expected->type, @@ -1042,10 +1054,11 @@ void FunctionValidator::visitAtomicWait(AtomicWait* curr) { "Atomic operation with non-shared memory"); shouldBeEqualOrFirstIsUnreachable( curr->type, Type(Type::i32), curr, "AtomicWait must have type i32"); - shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, - Type(Type::i32), - curr, - "AtomicWait pointer type must be i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->ptr->type, + indexType(), + curr, + "AtomicWait pointer must match memory index type"); shouldBeIntOrUnreachable( curr->expected->type, curr, "AtomicWait expected type must be int"); shouldBeEqualOrFirstIsUnreachable( @@ -1070,10 +1083,11 @@ void FunctionValidator::visitAtomicNotify(AtomicNotify* curr) { "Atomic operation with non-shared memory"); shouldBeEqualOrFirstIsUnreachable( curr->type, Type(Type::i32), curr, "AtomicNotify must have type i32"); - shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, - Type(Type::i32), - curr, - "AtomicNotify pointer type must be i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->ptr->type, + indexType(), + curr, + "AtomicNotify pointer must match memory index type"); shouldBeEqualOrFirstIsUnreachable( curr->notifyCount->type, Type(Type::i32), @@ -1230,10 +1244,11 @@ void FunctionValidator::visitSIMDLoad(SIMDLoad* curr) { getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); shouldBeEqualOrFirstIsUnreachable( curr->type, Type(Type::v128), curr, "load_splat must have type v128"); - shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, - Type(Type::i32), - curr, - "load_splat address must have type i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->ptr->type, + indexType(), + curr, + "load_splat address must match memory index type"); Type memAlignType = Type::none; switch (curr->op) { case LoadSplatVec8x16: @@ -1264,7 +1279,10 @@ void FunctionValidator::visitMemoryInit(MemoryInit* curr) { shouldBeEqualOrFirstIsUnreachable( curr->type, Type(Type::none), curr, "memory.init must have type none"); shouldBeEqualOrFirstIsUnreachable( - curr->dest->type, Type(Type::i32), curr, "memory.init dest must be an i32"); + curr->dest->type, + indexType(), + curr, + "memory.init dest must match memory index type"); shouldBeEqualOrFirstIsUnreachable(curr->offset->type, Type(Type::i32), curr, @@ -1304,13 +1322,20 @@ void FunctionValidator::visitMemoryCopy(MemoryCopy* curr) { shouldBeEqualOrFirstIsUnreachable( curr->type, Type(Type::none), curr, "memory.copy must have type none"); shouldBeEqualOrFirstIsUnreachable( - curr->dest->type, Type(Type::i32), curr, "memory.copy dest must be an i32"); - shouldBeEqualOrFirstIsUnreachable(curr->source->type, - Type(Type::i32), - curr, - "memory.copy source must be an i32"); + curr->dest->type, + indexType(), + curr, + "memory.copy dest must match memory index type"); + shouldBeEqualOrFirstIsUnreachable( + curr->source->type, + indexType(), + curr, + "memory.copy source must match memory index type"); shouldBeEqualOrFirstIsUnreachable( - curr->size->type, Type(Type::i32), curr, "memory.copy size must be an i32"); + curr->size->type, + indexType(), + curr, + "memory.copy size must match memory index type"); shouldBeTrue( getModule()->memory.exists, curr, "Memory operations require a memory"); } @@ -1322,13 +1347,19 @@ void FunctionValidator::visitMemoryFill(MemoryFill* curr) { shouldBeEqualOrFirstIsUnreachable( curr->type, Type(Type::none), curr, "memory.fill must have type none"); shouldBeEqualOrFirstIsUnreachable( - curr->dest->type, Type(Type::i32), curr, "memory.fill dest must be an i32"); + curr->dest->type, + indexType(), + curr, + "memory.fill dest must match memory index type"); shouldBeEqualOrFirstIsUnreachable(curr->value->type, Type(Type::i32), curr, "memory.fill value must be an i32"); shouldBeEqualOrFirstIsUnreachable( - curr->size->type, Type(Type::i32), curr, "memory.fill size must be an i32"); + curr->size->type, + indexType(), + curr, + "memory.fill size must match memory index type"); shouldBeTrue( getModule()->memory.exists, curr, "Memory operations require a memory"); } @@ -1907,9 +1938,9 @@ void FunctionValidator::visitMemoryGrow(MemoryGrow* curr) { shouldBeTrue( getModule()->memory.exists, curr, "Memory operations require a memory"); shouldBeEqualOrFirstIsUnreachable(curr->delta->type, - Type(Type::i32), + indexType(), curr, - "memory.grow must have i32 operand"); + "memory.grow must match memory index type"); } void FunctionValidator::visitRefIsNull(RefIsNull* curr) { @@ -2113,7 +2144,7 @@ void FunctionValidator::visitFunction(Function* curr) { } } -static bool checkOffset(Expression* curr, Address add, Address max) { +static bool checkSegmentOffset(Expression* curr, Address add, Address max) { if (curr->is<GlobalGet>()) { return true; } @@ -2122,10 +2153,10 @@ static bool checkOffset(Expression* curr, Address add, Address max) { return false; } uint64_t raw = c->value.getInteger(); - if (raw > std::numeric_limits<Address::address_t>::max()) { + if (raw > std::numeric_limits<Address::address32_t>::max()) { return false; } - if (raw + uint64_t(add) > std::numeric_limits<Address::address_t>::max()) { + if (raw + uint64_t(add) > std::numeric_limits<Address::address32_t>::max()) { return false; } Address offset = raw; @@ -2354,12 +2385,14 @@ static void validateMemory(Module& module, ValidationInfo& info) { auto& curr = module.memory; info.shouldBeFalse( curr.initial > curr.max, "memory", "memory max >= initial"); - info.shouldBeTrue(curr.initial <= Memory::kMaxSize, - "memory", - "initial memory must be <= 4GB"); - info.shouldBeTrue(!curr.hasMax() || curr.max <= Memory::kMaxSize, - "memory", - "max memory must be <= 4GB, or unlimited"); + if (!curr.is64()) { + info.shouldBeTrue(curr.initial <= Memory::kMaxSize32, + "memory", + "initial memory must be <= 4GB"); + info.shouldBeTrue(!curr.hasMax() || curr.max <= Memory::kMaxSize32, + "memory", + "max memory must be <= 4GB, or unlimited"); + } info.shouldBeTrue(!curr.shared || curr.hasMax(), "memory", "shared memory must have max size"); @@ -2385,9 +2418,9 @@ static void validateMemory(Module& module, ValidationInfo& info) { "segment offset should be i32")) { continue; } - info.shouldBeTrue(checkOffset(segment.offset, - segment.data.size(), - curr.initial * Memory::kPageSize), + info.shouldBeTrue(checkSegmentOffset(segment.offset, + segment.data.size(), + curr.initial * Memory::kPageSize), segment.offset, "segment offset should be reasonable"); if (segment.offset->is<Const>()) { @@ -2416,11 +2449,12 @@ static void validateTable(Module& module, ValidationInfo& info) { Type(Type::i32), segment.offset, "segment offset should be i32"); - info.shouldBeTrue(checkOffset(segment.offset, - segment.data.size(), - module.table.initial * Table::kPageSize), - segment.offset, - "segment offset should be reasonable"); + info.shouldBeTrue( + checkSegmentOffset(segment.offset, + segment.data.size(), + module.table.initial * Table::kPageSize), + segment.offset, + "segment offset should be reasonable"); for (auto name : segment.data) { info.shouldBeTrue( module.getFunctionOrNull(name), name, "segment name should be valid"); diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 87488cfaf..6cc61a8a1 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -46,6 +46,7 @@ const char* TailCallFeature = "tail-call"; const char* ReferenceTypesFeature = "reference-types"; const char* MultivalueFeature = "multivalue"; const char* GCFeature = "gc"; +const char* Memory64Feature = "memory64"; } // namespace UserSections } // namespace BinaryConsts @@ -881,13 +882,15 @@ void Drop::finalize() { } } -void MemorySize::finalize() { type = Type::i32; } +void MemorySize::make64() { type = ptrType = Type::i64; } +void MemorySize::finalize() { type = ptrType; } +void MemoryGrow::make64() { type = ptrType = Type::i64; } void MemoryGrow::finalize() { if (delta->type == Type::unreachable) { type = Type::unreachable; } else { - type = Type::i32; + type = ptrType; } } |