v8/test/mjsunit/wasm/grow-memory-in-call.js

437 lines
19 KiB
JavaScript
Raw Normal View History

// Copyright 2017 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 --wasm-loop-unrolling
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
var initialMemoryPages = 1;
var maximumMemoryPages = 5;
// Grow memory in directly called functions.
print('=== grow_memory in direct calls ===');
// This test verifies that the current_memory instruction returns the correct
// value after returning from a function (direct call) that grew memory.
(function TestMemoryGrowInFunction() {
print('TestMemoryGrowInFunction ...');
let builder = new WasmModuleBuilder();
builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
let kGrowFunction =
builder.addFunction('grow', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
.exportFunc()
.index;
builder.addFunction('main', kSig_i_i)
.addBody([
kExprLocalGet, 0, // get number of new pages
kExprCallFunction, kGrowFunction, // call the grow function
kExprDrop, // drop the result of grow
kExprMemorySize, kMemoryZero // get the memory size
])
.exportFunc();
var instance = builder.instantiate();
// The caller should be aware that the memory was grown by the callee.
var deltaPages = 1;
assertEquals(
initialMemoryPages + deltaPages, instance.exports.main(deltaPages));
})();
// This test verifies that accessing a memory page that has been created inside
// a function (direct call) does not trap in the caller.
(function TestMemoryGrowAndAccessInFunction() {
print('TestMemoryGrowAndAccessInFunction ...');
let index = 2 * kPageSize - 4;
let builder = new WasmModuleBuilder();
builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
let kGrowFunction =
builder.addFunction('grow', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
.exportFunc()
.index;
builder.addFunction('load', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32LoadMem, 0, 0])
.exportFunc();
builder.addFunction('main', kSig_v_iii)
.addBody([
kExprLocalGet, 0, // get number of new pages
kExprCallFunction, kGrowFunction, // call the grow function
kExprDrop, // drop the result of grow
kExprLocalGet, 1, // get index
kExprLocalGet, 2, // get value
kExprI32StoreMem, 0, 0 // store
])
.exportFunc();
var instance = builder.instantiate();
assertTraps(kTrapMemOutOfBounds, () => instance.exports.load(index));
var deltaPages = 1;
instance.exports.main(deltaPages, index, 1234);
// The caller should be able to access memory that was grown by the callee.
assertEquals(1234, instance.exports.load(index));
})();
// This test verifies that when a function (direct call) grows and store
// something in the grown memory, the caller always reads from the grown
// memory. This checks that the memory start address gets updated in the caller.
(function TestMemoryGrowAndStoreInFunction() {
print('TestMemoryGrowAndStoreInFunction ...');
let index = 0;
let oldValue = 21;
let newValue = 42;
let deltaPages = 1;
let builder = new WasmModuleBuilder();
builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
let kGrowFunction =
builder.addFunction('grow', kSig_v_v)
.addBody([
kExprI32Const, deltaPages, // always grow memory by deltaPages
kExprMemoryGrow, kMemoryZero, // grow memory
kExprDrop, // drop the result of grow
kExprI32Const, index, // put index on stack
kExprI32Const, newValue, // put new value on stack
kExprI32StoreMem, 0, 0 // store
])
.exportFunc()
.index;
builder.addFunction('main', kSig_i_i)
.addBody([
kExprI32Const, index, // put index on stack
kExprI32Const, oldValue, // put old value on stack
kExprI32StoreMem, 0, 0, // store
kExprCallFunction, kGrowFunction, // call grow_and_store
kExprI32Const, index, // put index on stack
kExprI32LoadMem, 0, 0 // load from grown memory
])
.exportFunc();
var instance = builder.instantiate();
// The caller should always read from grown memory.
assertEquals(newValue, instance.exports.main());
})();
// This test verifies that the effects of growing memory in an directly
// called function inside a loop affect the result of current_memory when
// the loop is over.
(function TestMemoryGrowInFunctionInsideLoop() {
print('TestMemoryGrowInFunctionInsideLoop ...');
let builder = new WasmModuleBuilder();
builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
let kGrowFunction =
builder.addFunction('grow', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
.exportFunc()
.index;
builder.addFunction('main', kSig_i_ii)
.addBody([
// clang-format off
kExprLoop, kWasmVoid, // while
kExprLocalGet, 0, // -
kExprIf, kWasmVoid, // if <param0> != 0
// Grow memory.
kExprLocalGet, 1, // get number of new pages
kExprCallFunction, kGrowFunction, // call the grow function
kExprDrop, // drop the result of grow
// Decrease loop variable.
kExprLocalGet, 0, // -
kExprI32Const, 1, // -
kExprI32Sub, // -
kExprLocalSet, 0, // decrease <param0>
kExprBr, 1, // continue
kExprEnd, // end if
kExprEnd, // end loop
// Return the memory size.
kExprMemorySize, kMemoryZero // put memory size on stack
// clang-format on
])
.exportFunc();
// The caller should be aware that the memory was grown by the callee.
let instance = builder.instantiate();
let iterations = 4;
let deltaPages = 1;
assertEquals(
initialMemoryPages + iterations * deltaPages,
instance.exports.main(iterations, deltaPages));
})();
// This test verifies that the effects of writing to memory grown in an
// directly called function inside a loop are retained when the loop is over.
(function TestMemoryGrowAndStoreInFunctionInsideLoop() {
print('TestMemoryGrowAndStoreInFunctionInsideLoop ...');
let builder = new WasmModuleBuilder();
builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
builder.addFunction('store', kSig_i_ii)
.addBody([
kExprLocalGet, 0, kExprLocalGet, 1, kExprI32StoreMem, 0, 0,
kExprLocalGet, 1
])
.exportFunc();
let kGrowFunction =
builder.addFunction('grow', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
.exportFunc()
.index;
// parameters: iterations, deltaPages, index
builder.addFunction('main', kSig_i_iii)
.addBody([
// clang-format off
kExprLoop, kWasmVoid, // while
kExprLocalGet, 0, // -
kExprIf, kWasmVoid, // if <param0> != 0
// Grow memory.
kExprLocalGet, 1, // get number of new pages
kExprCallFunction, kGrowFunction, // call the grow function
kExprDrop, // drop the result of grow
// Increase counter in memory.
kExprLocalGet, 2, // put index (for store)
kExprLocalGet, 2, // put index (for load)
kExprI32LoadMem, 0, 0, // load from grown memory
kExprI32Const, 1, // -
kExprI32Add, // increase counter
kExprI32StoreMem, 0, 0, // store counter in memory
// Decrease loop variable.
kExprLocalGet, 0, // -
kExprI32Const, 1, // -
kExprI32Sub, // -
kExprLocalSet, 0, // decrease <param0>
kExprBr, 1, // continue
kExprEnd, // end if
kExprEnd, // end loop
// Return the value
kExprLocalGet, 2, // -
kExprI32LoadMem, 0, 0 // load from grown memory
// clang-format on
])
.exportFunc();
// The caller should always read the correct memory
let instance = builder.instantiate();
let iterations = 4;
let deltaPages = 1;
let index = 0;
let initialValue = 1;
let expectedValue = initialValue + iterations;
instance.exports.store(index, initialValue);
assertEquals(
expectedValue, instance.exports.main(iterations, deltaPages, index));
})();
// Grow memory in indirectly called functions.
print('\n=== grow_memory in indirect calls ===');
// This test verifies that the current_memory instruction returns the correct
// value after returning from a function (indirect call) that grew memory.
(function TestMemoryGrowInIndirectCall() {
print('TestMemoryGrowInIndirectCall ...');
let builder = new WasmModuleBuilder();
builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
let kGrowFunction =
builder.addFunction('grow', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
.exportFunc()
.index;
builder.addFunction('main', kSig_i_ii)
.addBody([
kExprLocalGet, 1, // get number of new pages
kExprLocalGet, 0, // get index of the function
kExprCallIndirect, 0, kTableZero, // call the function
kExprDrop, // drop the result of grow
kExprMemorySize, kMemoryZero // get the memory size
])
.exportFunc();
builder.appendToTable([kGrowFunction]);
var instance = builder.instantiate();
// The caller should be aware that the memory was grown by the callee.
var deltaPages = 1;
assertEquals(
initialMemoryPages + deltaPages,
instance.exports.main(kGrowFunction, deltaPages));
})();
// This test verifies that accessing a memory page that has been created inside
// a function (indirect call) does not trap in the caller.
(function TestMemoryGrowAndAccessInIndirectCall() {
print('TestMemoryGrowAndAccessInIndirectCall ...');
let index = 2 * kPageSize - 4;
let builder = new WasmModuleBuilder();
builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
let kGrowFunction =
builder.addFunction('grow', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
.exportFunc()
.index;
builder.addFunction('load', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32LoadMem, 0, 0])
.exportFunc();
let sig = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []);
builder.addFunction('main', sig)
.addBody([
kExprLocalGet, 1, // get number of new pages
kExprLocalGet, 0, // get index of the function
kExprCallIndirect, 0, kTableZero, // call the function
kExprDrop, // drop the result of grow
kExprLocalGet, 2, // get index
kExprLocalGet, 3, // get value
kExprI32StoreMem, 0, 0 // store
])
.exportFunc();
builder.appendToTable([kGrowFunction]);
var instance = builder.instantiate();
assertTraps(kTrapMemOutOfBounds, () => instance.exports.load(index));
let deltaPages = 1;
let value = 1234;
instance.exports.main(kGrowFunction, deltaPages, index, value);
// The caller should be able to access memory that was grown by the callee.
assertEquals(value, instance.exports.load(index));
})();
// This test verifies that when a function (indirect call) grows and store
// something in the grown memory, the caller always reads from the grown
// memory. This checks that the memory start address gets updated in the caller.
(function TestMemoryGrowAndStoreInIndirectCall() {
print('TestMemoryGrowAndStoreInIndirectCall ...');
let index = 0;
let oldValue = 21;
let newValue = 42;
let deltaPages = 1;
let builder = new WasmModuleBuilder();
builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
let kGrowFunction =
builder.addFunction('grow', kSig_v_v)
.addBody([
kExprI32Const, deltaPages, // always grow memory by deltaPages
kExprMemoryGrow, kMemoryZero, // grow memory
kExprDrop, // drop the result of grow
kExprI32Const, index, // put index on stack
kExprI32Const, newValue, // put new value on stack
kExprI32StoreMem, 0, 0 // store
])
.exportFunc()
.index;
builder.addFunction('main', kSig_i_i)
.addBody([
kExprI32Const, index, // put index on stack
kExprI32Const, oldValue, // put old value on stack
kExprI32StoreMem, 0, 0, // store
kExprLocalGet, 0, // get index of the function
kExprCallIndirect, 0, kTableZero, // call the function
kExprI32Const, index, // put index on stack
kExprI32LoadMem, 0, 0 // load from grown memory
])
.exportFunc();
builder.appendToTable([kGrowFunction]);
var instance = builder.instantiate();
// The caller should always read from grown memory.
assertEquals(42, instance.exports.main(kGrowFunction));
})();
// This test verifies that the effects of growing memory in an indirectly
// called function inside a loop affect the result of current_memory when
// the loop is over.
(function TestMemoryGrowInIndirectCallInsideLoop() {
print('TestMemoryGrowInIndirectCallInsideLoop ...');
let builder = new WasmModuleBuilder();
builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
let kGrowFunction =
builder.addFunction('grow', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
.exportFunc()
.index;
builder.addFunction('main', kSig_i_iii)
.addBody([
// clang-format off
kExprLoop, kWasmVoid, // while
kExprLocalGet, 1, // -
kExprIf, kWasmVoid, // if <param1> != 0
// Grow memory.
kExprLocalGet, 2, // get number of new pages
kExprLocalGet, 0, // get index of the function
kExprCallIndirect, 0, kTableZero, // call the function
kExprDrop, // drop the result of grow
// Decrease loop variable.
kExprLocalGet, 1, // -
kExprI32Const, 1, // -
kExprI32Sub, // -
kExprLocalSet, 1, // decrease <param1>
kExprBr, 1, // continue
kExprEnd, // end if
kExprEnd, // end loop
// Return the memory size.
kExprMemorySize, kMemoryZero // put memory size on stack
// clang-format on
])
.exportFunc();
builder.appendToTable([kGrowFunction]);
// The caller should be aware that the memory was grown by the callee.
let instance = builder.instantiate();
let deltaPages = 1;
let iterations = 4;
assertEquals(
initialMemoryPages + iterations * deltaPages,
instance.exports.main(kGrowFunction, iterations, deltaPages));
})();
// This test verifies that the effects of writing to memory grown in an
// indirectly called function inside a loop are retained when the loop is over.
(function TestMemoryGrowAndStoreInIndirectCallInsideLoop() {
print('TestMemoryGrowAndStoreInIndirectCallInsideLoop ...');
let builder = new WasmModuleBuilder();
let deltaPages = 1;
builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
let kGrowFunction =
builder.addFunction('grow', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
.exportFunc()
.index;
builder.addFunction('store', kSig_i_ii)
.addBody([
kExprLocalGet, 0, kExprLocalGet, 1, kExprI32StoreMem, 0, 0,
kExprLocalGet, 1
])
.exportFunc();
builder
.addFunction(
// parameters: function_index, iterations, deltaPages, index
'main', makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], [kWasmI32]))
.addBody([
// clang-format off
kExprLoop, kWasmVoid, // while
kExprLocalGet, 1, // -
kExprIf, kWasmVoid, // if <param1> != 0
// Grow memory.
kExprLocalGet, 2, // get number of new pages
kExprLocalGet, 0, // get index of the function
kExprCallIndirect, 0, kTableZero, // call the function
kExprDrop, // drop the result of grow
// Increase counter in memory.
kExprLocalGet, 3, // put index (for store)
kExprLocalGet, 3, // put index (for load)
kExprI32LoadMem, 0, 0, // load from grown memory
kExprI32Const, 1, // -
kExprI32Add, // increase counter
kExprI32StoreMem, 0, 0, // store counter in memory
// Decrease loop variable.
kExprLocalGet, 1, // -
kExprI32Const, 1, // -
kExprI32Sub, // -
kExprLocalSet, 1, // decrease <param1>
kExprBr, 1, // continue
kExprEnd, // end if
kExprEnd, // end loop
// Return the value
kExprLocalGet, 3, // -
kExprI32LoadMem, 0, 0 // load from grown memory
// clang-format on
])
.exportFunc();
builder.appendToTable([kGrowFunction]);
// The caller should be aware that the memory was grown by the callee.
let instance = builder.instantiate();
let iterations = 4;
let index = 0;
let initialValue = 1;
let expectedValue = initialValue + iterations;
instance.exports.store(index, initialValue);
assertEquals(
expectedValue,
instance.exports.main(kGrowFunction, iterations, deltaPages, index));
})();