Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions scripts/test/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@ def get_tests(test_dir, extensions=[], recursive=False):

# Unlinkable module accepted
'linking.wast',
'memory_max.wast',
'memory_max_i64.wast',

# Invalid module accepted
'unreached-invalid.wast',
Expand Down
3 changes: 3 additions & 0 deletions src/binaryen-c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,9 @@ BinaryenFeatures BinaryenFeatureCallIndirectOverlong(void) {
BinaryenFeatures BinaryenFeatureRelaxedAtomics(void) {
return static_cast<BinaryenFeatures>(FeatureSet::RelaxedAtomics);
}
BinaryenFeatures BinaryenFeatureCustomPageSizes(void) {
return static_cast<BinaryenFeatures>(FeatureSet::CustomPageSizes);
}
BinaryenFeatures BinaryenFeatureAll(void) {
return static_cast<BinaryenFeatures>(FeatureSet::All);
}
Expand Down
1 change: 1 addition & 0 deletions src/binaryen-c.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ BINARYEN_API BinaryenFeatures BinaryenFeatureFP16(void);
BINARYEN_API BinaryenFeatures BinaryenFeatureBulkMemoryOpt(void);
BINARYEN_API BinaryenFeatures BinaryenFeatureCallIndirectOverlong(void);
BINARYEN_API BinaryenFeatures BinaryenFeatureRelaxedAtomics(void);
BINARYEN_API BinaryenFeatures BinaryenFeatureCustomPageSizes(void);
BINARYEN_API BinaryenFeatures BinaryenFeatureAll(void);

// Modules
Expand Down
1 change: 1 addition & 0 deletions src/ir/module-utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ Memory* copyMemory(const Memory* memory, Module& out) {
ret->hasExplicitName = memory->hasExplicitName;
ret->initial = memory->initial;
ret->max = memory->max;
ret->pageSizeLog2 = memory->pageSizeLog2;
ret->shared = memory->shared;
ret->addressType = memory->addressType;
ret->module = memory->module;
Expand Down
1 change: 1 addition & 0 deletions src/js/binaryen.js-post.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ function initializeConstants() {
'BulkMemoryOpt',
'CallIndirectOverlong',
'RelaxedAtomics',
'CustomPageSizes',
'All'
].forEach(name => {
Module['Features'][name] = Module['_BinaryenFeature' + name]();
Expand Down
1 change: 1 addition & 0 deletions src/parser/context-decls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ Result<Memory*> ParseDeclsCtx::addMemoryDecl(Index pos,
m->initial = type.limits.initial;
m->max = type.limits.max ? *type.limits.max : Memory::kUnlimitedSize;
m->shared = type.shared;
m->pageSizeLog2 = type.pageSizeLog2;
if (name) {
// TODO: if the existing memory is not explicitly named, fix its name
// and continue.
Expand Down
28 changes: 21 additions & 7 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ struct Limits {
struct MemType {
Type addressType;
Limits limits;
uint8_t pageSizeLog2;
bool shared;
};

Expand Down Expand Up @@ -353,7 +354,9 @@ template<typename Ctx> struct TypeParserCtx {
Result<LimitsT> makeLimits(uint64_t, std::optional<uint64_t>) { return Ok{}; }
LimitsT getLimitsFromData(DataStringT) { return Ok{}; }

MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; }
MemTypeT makeMemType(Type, LimitsT, bool, std::optional<uint8_t>) {
return Ok{};
}

HeapType getBlockTypeFromResult(const std::vector<Type> results) {
assert(results.size() == 1);
Expand Down Expand Up @@ -1072,13 +1075,20 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
data.insert(data.end(), str.begin(), str.end());
}

Limits getLimitsFromData(const std::vector<char>& data) {
uint64_t size = (data.size() + Memory::kPageSize - 1) / Memory::kPageSize;
Limits getLimitsFromData(const std::vector<char>& data,
std::optional<uint8_t> pageSizeLog2) {
uint8_t _pageSizeLog2 = pageSizeLog2.value_or(16);
uint64_t size =
(data.size() + (1 << _pageSizeLog2) - 1) / (1 << _pageSizeLog2);
return {size, size};
}

MemType makeMemType(Type addressType, Limits limits, bool shared) {
return {addressType, limits, shared};
MemType makeMemType(Type addressType,
Limits limits,
bool shared,
std::optional<uint8_t> pageSize) {
uint8_t pageSizeLog2 = pageSize.value_or(16);
return {addressType, limits, pageSizeLog2, shared};
}

Result<TypeUseT>
Expand Down Expand Up @@ -1447,8 +1457,12 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,

Type makeTableType(Type addressType, LimitsT, Type type) { return type; }

LimitsT getLimitsFromData(DataStringT) { return Ok{}; }
MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; }
LimitsT getLimitsFromData(DataStringT, std::optional<uint8_t>) {
return Ok{};
}
MemTypeT makeMemType(Type, LimitsT, bool, std::optional<uint8_t>) {
return Ok{};
}

Result<> addFunc(Name name,
const std::vector<Name>&,
Expand Down
45 changes: 41 additions & 4 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,34 @@ template<typename Ctx> Result<typename Ctx::LimitsT> limits64(Ctx& ctx) {
return ctx.makeLimits(uint64_t(*n), m);
}

// memtype ::= (limits32 | 'i32' limits32 | 'i64' limit64) shared?
// mempagesize? ::= ('(' 'pagesize' u64 ')') ?
template<typename Ctx> Result<std::optional<uint8_t>> mempagesize(Ctx& ctx) {
if (!ctx.in.takeSExprStart("pagesize"sv)) {
return std::nullopt; // No pagesize specified
}
auto pageSize = ctx.in.takeU64();
if (!pageSize) {
return ctx.in.err("expected page size");
}

if (!Bits::isPowerOf2(*pageSize)) {
return ctx.in.err("page size must be a power of two");
}

if (!ctx.in.takeRParen()) {
return ctx.in.err("expected end of mempagesize");
}

uint8_t pageSizeLog2 = (uint8_t)Bits::ceilLog2(*pageSize);

if (pageSizeLog2 != 0 && pageSizeLog2 != Memory::kDefaultPageSizeLog2) {
return ctx.in.err("memory page size can only be 1 or 64 KiB");
}

return std::make_optional<uint8_t>(pageSizeLog2);
}

// memtype ::= (limits32 | 'i32' limits32 | 'i64' limit64) shared? mempagesize?
// note: the index type 'i32' or 'i64' is already parsed to simplify parsing of
// memory abbreviations.
template<typename Ctx> Result<typename Ctx::MemTypeT> memtype(Ctx& ctx) {
Expand All @@ -847,7 +874,9 @@ Result<typename Ctx::MemTypeT> memtypeContinued(Ctx& ctx, Type addressType) {
if (ctx.in.takeKeyword("shared"sv)) {
shared = true;
}
return ctx.makeMemType(addressType, *limits, shared);
auto pageSize = mempagesize(ctx);
CHECK_ERR(pageSize);
return ctx.makeMemType(addressType, *limits, shared, *pageSize);
}

// memorder ::= 'seqcst' | 'acqrel'
Expand Down Expand Up @@ -3558,6 +3587,8 @@ template<typename Ctx> MaybeResult<> memory(Ctx& ctx) {

std::optional<typename Ctx::MemTypeT> mtype;
std::optional<typename Ctx::DataStringT> data;
auto mempageSize = mempagesize(ctx);
CHECK_ERR(mempageSize);
if (ctx.in.takeSExprStart("data"sv)) {
if (import) {
return ctx.in.err("imported memories cannot have inline data");
Expand All @@ -3567,9 +3598,15 @@ template<typename Ctx> MaybeResult<> memory(Ctx& ctx) {
if (!ctx.in.takeRParen()) {
return ctx.in.err("expected end of inline data");
}
mtype =
ctx.makeMemType(addressType, ctx.getLimitsFromData(*datastr), false);
mtype = ctx.makeMemType(addressType,
ctx.getLimitsFromData(*datastr, *mempageSize),
false,
*mempageSize);
data = *datastr;
} else if ((*mempageSize).has_value()) {
// If we have a memory page size not within a memtype expression, we expect
// a memory abbreviation.
return ctx.in.err("expected data segment in memory abbreviation");
} else {
auto type = memtypeContinued(ctx, addressType);
CHECK_ERR(type);
Expand Down
45 changes: 26 additions & 19 deletions src/passes/LLVMMemoryCopyFillLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,17 @@ struct LLVMMemoryCopyFillLowering
void createMemoryCopyFunc(Module* module) {
Builder b(*module);
Index dst = 0, src = 1, size = 2, start = 3, end = 4, step = 5, i = 6;
Name memory = module->memories.front()->name;
Name memoryName = module->memories.front()->name;
Address::address32_t pageSizeLog2 = module->memories.front()->pageSizeLog2;
Block* body = b.makeBlock();
// end = memory size in bytes
body->list.push_back(
b.makeLocalSet(end,
b.makeBinary(BinaryOp::MulInt32,
b.makeMemorySize(memory),
b.makeConst(Memory::kPageSize))));
body->list.push_back(b.makeLocalSet(
end,
pageSizeLog2 == 0
? static_cast<Expression*>(b.makeMemorySize(memoryName))
: static_cast<Expression*>(b.makeBinary(BinaryOp::ShlInt32,
b.makeMemorySize(memoryName),
b.makeConst(pageSizeLog2)))));
// if dst + size > memsize or src + size > memsize, then trap.
body->list.push_back(b.makeIf(
b.makeBinary(BinaryOp::OrInt32,
Expand Down Expand Up @@ -187,9 +190,9 @@ struct LLVMMemoryCopyFillLowering
b.makeLocalGet(src, Type::i32),
b.makeLocalGet(i, Type::i32)),
Type::i32,
memory),
memoryName),
Type::i32,
memory),
memoryName),
// i += step
b.makeLocalSet(i,
b.makeBinary(BinaryOp::AddInt32,
Expand All @@ -203,19 +206,23 @@ struct LLVMMemoryCopyFillLowering
void createMemoryFillFunc(Module* module) {
Builder b(*module);
Index dst = 0, val = 1, size = 2;
Name memory = module->memories.front()->name;
Name memoryName = module->memories.front()->name;
Address::address32_t pageSizeLog2 = module->memories.front()->pageSizeLog2;
Block* body = b.makeBlock();

// if dst + size > memsize in bytes, then trap.
body->list.push_back(
b.makeIf(b.makeBinary(BinaryOp::GtUInt32,
b.makeBinary(BinaryOp::AddInt32,
b.makeLocalGet(dst, Type::i32),
b.makeLocalGet(size, Type::i32)),
b.makeBinary(BinaryOp::MulInt32,
b.makeMemorySize(memory),
b.makeConst(Memory::kPageSize))),
b.makeUnreachable()));
body->list.push_back(b.makeIf(
b.makeBinary(
BinaryOp::GtUInt32,
b.makeBinary(BinaryOp::AddInt32,
b.makeLocalGet(dst, Type::i32),
b.makeLocalGet(size, Type::i32)),
pageSizeLog2 == 0
? static_cast<Expression*>(b.makeMemorySize(memoryName))
: static_cast<Expression*>(b.makeBinary(BinaryOp::ShlInt32,
b.makeMemorySize(memoryName),
b.makeConst(pageSizeLog2)))),
b.makeUnreachable()));

body->list.push_back(b.makeBlock(
"out",
Expand All @@ -241,7 +248,7 @@ struct LLVMMemoryCopyFillLowering
b.makeLocalGet(size, Type::i32)),
b.makeLocalGet(val, Type::i32),
Type::i32,
memory),
memoryName),
b.makeBreak("copy", nullptr)}))));
module->getFunction(memFillFuncName)->body = body;
}
Expand Down
4 changes: 2 additions & 2 deletions src/passes/Memory64Lowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ struct Memory64Lowering : public WalkerPass<PostWalker<Memory64Lowering>> {
for (auto& memory : module->memories) {
if (memory->is64()) {
memory->addressType = Type::i32;
if (memory->hasMax() && memory->max > Memory::kMaxSize32) {
memory->max = Memory::kMaxSize32;
if (memory->hasMax() && memory->max > memory->maxSize32()) {
memory->max = memory->maxSize32();
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/passes/MemoryPacking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ void MemoryPacking::calculateRanges(Module* module,
// Check if we can rule out a trap by it being in bounds.
if (auto* c = segment->offset->dynCast<Const>()) {
auto* memory = module->getMemory(segment->memory);
auto memorySize = memory->initial * Memory::kPageSize;
auto memorySize = memory->initial << memory->pageSizeLog2;
Index start = c->value.getUnsigned();
Index size = segment->data.size();
Index end;
Expand Down
Loading
Loading