blob: 0e5618a2b15b96b44a0e0181c071e5c5ae653c00 [file] [log] [blame]
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --stress-compaction
load("test/mjsunit/wasm/wasm-module-builder.js");
function genMemoryGrowBuilder() {
var builder = new WasmModuleBuilder();
builder.addFunction("grow_memory", kSig_i_i)
.addBody([kExprGetLocal, 0, kExprMemoryGrow, kMemoryZero])
.exportFunc();
builder.addFunction("load", kSig_i_i)
.addBody([kExprGetLocal, 0, kExprI32LoadMem, 0, 0])
.exportFunc();
builder.addFunction("store", kSig_i_ii)
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem, 0, 0,
kExprGetLocal, 1])
.exportFunc();
builder.addFunction("load16", kSig_i_i)
.addBody([kExprGetLocal, 0, kExprI32LoadMem16U, 0, 0])
.exportFunc();
builder.addFunction("store16", kSig_i_ii)
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem16, 0, 0,
kExprGetLocal, 1])
.exportFunc();
builder.addFunction("load8", kSig_i_i)
.addBody([kExprGetLocal, 0, kExprI32LoadMem8U, 0, 0])
.exportFunc();
builder.addFunction("store8", kSig_i_ii)
.addBody([kExprGetLocal, 0, kExprGetLocal, 1, kExprI32StoreMem8, 0, 0,
kExprGetLocal, 1])
.exportFunc();
return builder;
}
// V8 internal memory size limit.
var kV8MaxPages = 32767;
// TODO(gdeepti): Generate tests programatically for all the sizes instead of
// current implementation.
function testMemoryGrowReadWrite32() {
var builder = genMemoryGrowBuilder();
builder.addMemory(1, undefined, false);
var module = builder.instantiate();
var offset;
function peek() { return module.exports.load(offset); }
function poke(value) { return module.exports.store(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
for(offset = 0; offset <= (kPageSize - 4); offset+=4) {
poke(20);
assertEquals(20, peek());
}
for (offset = kPageSize - 3; offset < kPageSize + 4; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
assertTraps(kTrapMemOutOfBounds, peek);
}
assertEquals(1, growMem(3));
for (offset = kPageSize; offset <= 4*kPageSize -4; offset+=4) {
poke(20);
assertEquals(20, peek());
}
for (offset = 4*kPageSize - 3; offset < 4*kPageSize + 4; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
assertTraps(kTrapMemOutOfBounds, peek);
}
assertEquals(4, growMem(15));
for (offset = 4*kPageSize - 3; offset <= 4*kPageSize + 4; offset+=4) {
poke(20);
assertEquals(20, peek());
}
for (offset = 19*kPageSize - 10; offset <= 19*kPageSize - 4; offset+=4) {
poke(20);
assertEquals(20, peek());
}
for (offset = 19*kPageSize - 3; offset < 19*kPageSize + 5; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
assertTraps(kTrapMemOutOfBounds, peek);
}
}
testMemoryGrowReadWrite32();
function testMemoryGrowReadWrite16() {
var builder = genMemoryGrowBuilder();
builder.addMemory(1, undefined, false);
var module = builder.instantiate();
var offset;
function peek() { return module.exports.load16(offset); }
function poke(value) { return module.exports.store16(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
for(offset = 0; offset <= (kPageSize - 2); offset+=2) {
poke(20);
assertEquals(20, peek());
}
for (offset = kPageSize - 1; offset < kPageSize + 4; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
assertTraps(kTrapMemOutOfBounds, peek);
}
assertEquals(1, growMem(3));
for (offset = kPageSize; offset <= 4*kPageSize -2; offset+=2) {
poke(20);
assertEquals(20, peek());
}
for (offset = 4*kPageSize - 1; offset < 4*kPageSize + 4; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
assertTraps(kTrapMemOutOfBounds, peek);
}
assertEquals(4, growMem(15));
for (offset = 4*kPageSize - 2; offset <= 4*kPageSize + 4; offset+=2) {
poke(20);
assertEquals(20, peek());
}
for (offset = 19*kPageSize - 10; offset <= 19*kPageSize - 2; offset+=2) {
poke(20);
assertEquals(20, peek());
}
for (offset = 19*kPageSize - 1; offset < 19*kPageSize + 5; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
assertTraps(kTrapMemOutOfBounds, peek);
}
}
testMemoryGrowReadWrite16();
function testMemoryGrowReadWrite8() {
var builder = genMemoryGrowBuilder();
builder.addMemory(1, undefined, false);
var module = builder.instantiate();
var offset;
function peek() { return module.exports.load8(offset); }
function poke(value) { return module.exports.store8(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
for(offset = 0; offset <= kPageSize - 1; offset++) {
poke(20);
assertEquals(20, peek());
}
for (offset = kPageSize; offset < kPageSize + 4; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
assertTraps(kTrapMemOutOfBounds, peek);
}
assertEquals(1, growMem(3));
for (offset = kPageSize; offset <= 4*kPageSize -1; offset++) {
poke(20);
assertEquals(20, peek());
}
for (offset = 4*kPageSize; offset < 4*kPageSize + 4; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
assertTraps(kTrapMemOutOfBounds, peek);
}
assertEquals(4, growMem(15));
for (offset = 4*kPageSize; offset <= 4*kPageSize + 4; offset++) {
poke(20);
assertEquals(20, peek());
}
for (offset = 19*kPageSize - 10; offset <= 19*kPageSize - 1; offset++) {
poke(20);
assertEquals(20, peek());
}
for (offset = 19*kPageSize; offset < 19*kPageSize + 5; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
assertTraps(kTrapMemOutOfBounds, peek);
}
}
testMemoryGrowReadWrite8();
function testMemoryGrowZeroInitialSize() {
var builder = genMemoryGrowBuilder();
builder.addMemory(0, undefined, false);
var module = builder.instantiate();
var offset;
function peek() { return module.exports.load(offset); }
function poke(value) { return module.exports.store(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
assertTraps(kTrapMemOutOfBounds, peek);
assertTraps(kTrapMemOutOfBounds, poke);
assertEquals(0, growMem(1));
for(offset = 0; offset <= kPageSize - 4; offset++) {
poke(20);
assertEquals(20, peek());
}
for(offset = kPageSize - 3; offset <= kPageSize + 5; offset++) {
assertTraps(kTrapMemOutOfBounds, peek);
}
offset = 3*kPageSize;
for (var i = 1; i < 4; i++) {
assertTraps(kTrapMemOutOfBounds, poke);
assertEquals(i, growMem(1));
}
poke(20);
assertEquals(20, peek());
}
testMemoryGrowZeroInitialSize();
function testMemoryGrowZeroInitialSize32() {
var builder = genMemoryGrowBuilder();
builder.addMemory(0, undefined, false);
var module = builder.instantiate();
var offset;
function peek() { return module.exports.load(offset); }
function poke(value) { return module.exports.store(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
assertTraps(kTrapMemOutOfBounds, peek);
assertTraps(kTrapMemOutOfBounds, poke);
assertEquals(0, growMem(1));
for(offset = 0; offset <= kPageSize - 4; offset++) {
poke(20);
assertEquals(20, peek());
}
for(offset = kPageSize - 3; offset <= kPageSize + 5; offset++) {
assertTraps(kTrapMemOutOfBounds, peek);
}
}
testMemoryGrowZeroInitialSize32();
function testMemoryGrowZeroInitialSize16() {
var builder = genMemoryGrowBuilder();
builder.addMemory(0, undefined, false);
var module = builder.instantiate();
var offset;
function peek() { return module.exports.load16(offset); }
function poke(value) { return module.exports.store16(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
assertTraps(kTrapMemOutOfBounds, peek);
assertTraps(kTrapMemOutOfBounds, poke);
assertEquals(0, growMem(1));
for(offset = 0; offset <= kPageSize - 2; offset++) {
poke(20);
assertEquals(20, peek());
}
for(offset = kPageSize - 1; offset <= kPageSize + 5; offset++) {
assertTraps(kTrapMemOutOfBounds, peek);
}
}
testMemoryGrowZeroInitialSize16();
function testMemoryGrowZeroInitialSize8() {
var builder = genMemoryGrowBuilder();
builder.addMemory(0, undefined, false);
var module = builder.instantiate();
var offset;
function peek() { return module.exports.load8(offset); }
function poke(value) { return module.exports.store8(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
assertTraps(kTrapMemOutOfBounds, peek);
assertTraps(kTrapMemOutOfBounds, poke);
assertEquals(0, growMem(1));
for(offset = 0; offset <= kPageSize - 1; offset++) {
poke(20);
assertEquals(20, peek());
}
for(offset = kPageSize; offset <= kPageSize + 5; offset++) {
assertTraps(kTrapMemOutOfBounds, peek);
}
}
testMemoryGrowZeroInitialSize8();
function testMemoryGrowTrapMaxPagesZeroInitialMemory() {
var builder = genMemoryGrowBuilder();
builder.addMemory(0, undefined, false);
var module = builder.instantiate();
function growMem(pages) { return module.exports.grow_memory(pages); }
assertEquals(-1, growMem(kV8MaxPages + 1));
}
testMemoryGrowTrapMaxPagesZeroInitialMemory();
function testMemoryGrowTrapMaxPages() {
var builder = genMemoryGrowBuilder();
builder.addMemory(1, 1, false);
var module = builder.instantiate();
function growMem(pages) { return module.exports.grow_memory(pages); }
assertEquals(-1, growMem(kV8MaxPages));
}
testMemoryGrowTrapMaxPages();
function testMemoryGrowTrapsWithNonSmiInput() {
var builder = genMemoryGrowBuilder();
builder.addMemory(0, undefined, false);
var module = builder.instantiate();
function growMem(pages) { return module.exports.grow_memory(pages); }
// The parameter of grow_memory is unsigned. Therefore -1 stands for
// UINT32_MIN, which cannot be represented as SMI.
assertEquals(-1, growMem(-1));
};
testMemoryGrowTrapsWithNonSmiInput();
function testMemoryGrowCurrentMemory() {
var builder = genMemoryGrowBuilder();
builder.addMemory(1, undefined, false);
builder.addFunction("memory_size", kSig_i_v)
.addBody([kExprMemorySize, kMemoryZero])
.exportFunc();
var module = builder.instantiate();
function growMem(pages) { return module.exports.grow_memory(pages); }
function MemSize() { return module.exports.memory_size(); }
assertEquals(1, MemSize());
assertEquals(1, growMem(1));
assertEquals(2, MemSize());
}
testMemoryGrowCurrentMemory();
function testMemoryGrowPreservesDataMemOp32() {
var builder = genMemoryGrowBuilder();
builder.addMemory(1, undefined, false);
var module = builder.instantiate();
var offset, val;
function peek() { return module.exports.load(offset); }
function poke(value) { return module.exports.store(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
for(offset = 0; offset <= (kPageSize - 4); offset+=4) {
poke(100000 - offset);
assertEquals(100000 - offset, peek());
}
assertEquals(1, growMem(3));
for(offset = 0; offset <= (kPageSize - 4); offset+=4) {
assertEquals(100000 - offset, peek());
}
}
testMemoryGrowPreservesDataMemOp32();
function testMemoryGrowPreservesDataMemOp16() {
var builder = genMemoryGrowBuilder();
builder.addMemory(1, undefined, false);
var module = builder.instantiate();
var offset, val;
function peek() { return module.exports.load16(offset); }
function poke(value) { return module.exports.store16(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
for(offset = 0; offset <= (kPageSize - 2); offset+=2) {
poke(65535 - offset);
assertEquals(65535 - offset, peek());
}
assertEquals(1, growMem(3));
for(offset = 0; offset <= (kPageSize - 2); offset+=2) {
assertEquals(65535 - offset, peek());
}
}
testMemoryGrowPreservesDataMemOp16();
function testMemoryGrowPreservesDataMemOp8() {
var builder = genMemoryGrowBuilder();
builder.addMemory(1, undefined, false);
var module = builder.instantiate();
var offset, val = 0;
function peek() { return module.exports.load8(offset); }
function poke(value) { return module.exports.store8(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
for(offset = 0; offset <= (kPageSize - 1); offset++, val++) {
poke(val);
assertEquals(val, peek());
if (val == 255) val = 0;
}
assertEquals(1, growMem(3));
val = 0;
for(offset = 0; offset <= (kPageSize - 1); offset++, val++) {
assertEquals(val, peek());
if (val == 255) val = 0;
}
}
testMemoryGrowPreservesDataMemOp8();
function testMemoryGrowOutOfBoundsOffset() {
var builder = genMemoryGrowBuilder();
builder.addMemory(1, undefined, false);
var module = builder.instantiate();
var offset, val;
function peek() { return module.exports.load(offset); }
function poke(value) { return module.exports.store(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
offset = 3*kPageSize + 4;
assertTraps(kTrapMemOutOfBounds, poke);
assertEquals(1, growMem(1));
assertTraps(kTrapMemOutOfBounds, poke);
assertEquals(2, growMem(1));
assertTraps(kTrapMemOutOfBounds, poke);
assertEquals(3, growMem(1));
for (offset = 3*kPageSize; offset <= 4*kPageSize - 4; offset++) {
poke(0xaced);
assertEquals(0xaced, peek());
}
for (offset = 4*kPageSize - 3; offset <= 4*kPageSize + 4; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
}
}
testMemoryGrowOutOfBoundsOffset();
function testMemoryGrowOutOfBoundsOffset2() {
var builder = new WasmModuleBuilder();
builder.addMemory(16, 128, false);
builder.addFunction("main", kSig_v_v)
.addBody([
kExprI32Const, 20,
kExprI32Const, 29,
kExprMemoryGrow, kMemoryZero,
kExprI32StoreMem, 0, 0xFF, 0xFF, 0xFF, 0x3a
])
.exportAs("main");
var module = builder.instantiate();
assertTraps(kTrapMemOutOfBounds, module.exports.main);
}
testMemoryGrowOutOfBoundsOffset2();
function testMemoryGrowDeclaredMaxTraps() {
var builder = genMemoryGrowBuilder();
builder.addMemory(1, 16, false);
var module = builder.instantiate();
function growMem(pages) { return module.exports.grow_memory(pages); }
assertEquals(1, growMem(5));
assertEquals(6, growMem(5));
assertEquals(-1, growMem(6));
}
testMemoryGrowDeclaredMaxTraps();
function testMemoryGrowDeclaredSpecMaxTraps() {
// The spec maximum is higher than the internal V8 maximum. This test only
// checks that grow_memory does not grow past the internally defined maximum
// to reflect the current implementation.
var builder = genMemoryGrowBuilder();
builder.addMemory(1, kSpecMaxPages, false);
var module = builder.instantiate();
function poke(value) { return module.exports.store(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
assertEquals(1, growMem(20));
assertEquals(-1, growMem(kV8MaxPages - 20));
}
testMemoryGrowDeclaredSpecMaxTraps();
function testMemoryGrow2Gb() {
print("testMemoryGrow2Gb");
var builder = genMemoryGrowBuilder();
builder.addMemory(1, undefined, false);
var module = builder.instantiate();
var offset, val;
function peek() { return module.exports.load(offset); }
function poke(value) { return module.exports.store(offset, value); }
function growMem(pages) { return module.exports.grow_memory(pages); }
for(offset = 0; offset <= (kPageSize - 4); offset+=4) {
poke(100000 - offset);
assertEquals(100000 - offset, peek());
}
let result = growMem(kV8MaxPages - 1);
if (result == 1 ){
for(offset = 0; offset <= (kPageSize - 4); offset+=4) {
assertEquals(100000 - offset, peek());
}
// Bounds check for large mem size
for(offset = (kV8MaxPages - 1) * kPageSize;
offset <= (kV8MaxPages * kPageSize - 4); offset+=4) {
poke(0xaced);
assertEquals(0xaced, peek());
}
for (offset = kV8MaxPages * kPageSize - 3;
offset <= kV8MaxPages * kPageSize + 4; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
}
// Check traps around 3GB/4GB boundaries
let offset_3gb = 49152 * kPageSize;
let offset_4gb = 2 * kV8MaxPages * kPageSize;
for (offset = offset_3gb - 5; offset < offset_3gb + 4; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
}
for (offset = offset_4gb - 5; offset < offset_4gb; offset++) {
assertTraps(kTrapMemOutOfBounds, poke);
}
} else {
// Allocating big chunks of memory can fail on gc_stress, especially on 32
// bit platforms. When grow_memory fails, expected result is -1.
assertEquals(-1, result);
}
}
testMemoryGrow2Gb();