grow-memory-in-call.js 18.9 KB
Newer Older
1 2 3 4
// 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.

5
// Flags: --expose-wasm --stress-compaction
6

7
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
8 9 10 11 12 13 14 15 16

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.
17 18
(function TestMemoryGrowInFunction() {
  print('TestMemoryGrowInFunction ...');
19 20 21 22
  let builder = new WasmModuleBuilder();
  builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
  let kGrowFunction =
      builder.addFunction('grow', kSig_i_i)
23
          .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
24 25 26 27
          .exportFunc()
          .index;
  builder.addFunction('main', kSig_i_i)
      .addBody([
28
        kExprLocalGet, 0,                  // get number of new pages
29 30 31 32 33 34 35 36 37 38 39 40 41 42
        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.
43 44
(function TestMemoryGrowAndAccessInFunction() {
  print('TestMemoryGrowAndAccessInFunction ...');
45 46 47 48 49
  let index = 2 * kPageSize - 4;
  let builder = new WasmModuleBuilder();
  builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
  let kGrowFunction =
      builder.addFunction('grow', kSig_i_i)
50
          .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
51 52 53
          .exportFunc()
          .index;
  builder.addFunction('load', kSig_i_i)
54
      .addBody([kExprLocalGet, 0, kExprI32LoadMem, 0, 0])
55 56 57
      .exportFunc();
  builder.addFunction('main', kSig_v_iii)
      .addBody([
58
        kExprLocalGet, 0,                  // get number of new pages
59 60
        kExprCallFunction, kGrowFunction,  // call the grow function
        kExprDrop,                         // drop the result of grow
61 62
        kExprLocalGet, 1,                  // get index
        kExprLocalGet, 2,                  // get value
63 64 65 66 67 68 69 70 71 72 73 74 75 76
        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.
77 78
(function TestMemoryGrowAndStoreInFunction() {
  print('TestMemoryGrowAndStoreInFunction ...');
79 80 81 82 83 84 85 86 87 88
  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
89
            kExprMemoryGrow, kMemoryZero,  // grow memory
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
            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());
})();

112 113 114
// 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.
115 116
(function TestMemoryGrowInFunctionInsideLoop() {
  print('TestMemoryGrowInFunctionInsideLoop ...');
117 118 119 120
  let builder = new WasmModuleBuilder();
  builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
  let kGrowFunction =
      builder.addFunction('grow', kSig_i_i)
121
          .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
122 123 124 125 126
          .exportFunc()
          .index;
  builder.addFunction('main', kSig_i_ii)
      .addBody([
        // clang-format off
127
        kExprLoop, kWasmVoid,                   // while
128
          kExprLocalGet, 0,                     // -
129
          kExprIf, kWasmVoid,                   // if <param0> != 0
130
            // Grow memory.
131
            kExprLocalGet, 1,                   // get number of new pages
132 133 134
            kExprCallFunction, kGrowFunction,   // call the grow function
            kExprDrop,                          // drop the result of grow
            // Decrease loop variable.
135
            kExprLocalGet, 0,                   // -
136 137
            kExprI32Const, 1,                   // -
            kExprI32Sub,                        // -
138
            kExprLocalSet, 0,                   // decrease <param0>
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
            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.
158 159
(function TestMemoryGrowAndStoreInFunctionInsideLoop() {
  print('TestMemoryGrowAndStoreInFunctionInsideLoop ...');
160 161 162 163
  let builder = new WasmModuleBuilder();
  builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
  builder.addFunction('store', kSig_i_ii)
      .addBody([
164 165
        kExprLocalGet, 0, kExprLocalGet, 1, kExprI32StoreMem, 0, 0,
        kExprLocalGet, 1
166 167 168 169
      ])
      .exportFunc();
  let kGrowFunction =
      builder.addFunction('grow', kSig_i_i)
170
          .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
171 172 173 174 175 176
          .exportFunc()
          .index;
  // parameters:  iterations, deltaPages, index
  builder.addFunction('main', kSig_i_iii)
      .addBody([
        // clang-format off
177
        kExprLoop, kWasmVoid,                   // while
178
          kExprLocalGet, 0,                     // -
179
          kExprIf, kWasmVoid,                   // if <param0> != 0
180
            // Grow memory.
181
            kExprLocalGet, 1,                   // get number of new pages
182 183 184
            kExprCallFunction, kGrowFunction,   // call the grow function
            kExprDrop,                          // drop the result of grow
            // Increase counter in memory.
185 186
            kExprLocalGet, 2,                   // put index (for store)
                kExprLocalGet, 2,               // put index (for load)
187 188 189 190 191
                kExprI32LoadMem, 0, 0,          // load from grown memory
              kExprI32Const, 1,                 // -
              kExprI32Add,                      // increase counter
            kExprI32StoreMem, 0, 0,             // store counter in memory
            // Decrease loop variable.
192
            kExprLocalGet, 0,                   // -
193 194
            kExprI32Const, 1,                   // -
            kExprI32Sub,                        // -
195
            kExprLocalSet, 0,                   // decrease <param0>
196 197 198 199
            kExprBr, 1,                         // continue
          kExprEnd,                             // end if
        kExprEnd,                               // end loop
        // Return the value
200
        kExprLocalGet, 2,                       // -
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
        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));
})();

217 218 219 220 221
// 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.
222 223
(function TestMemoryGrowInIndirectCall() {
  print('TestMemoryGrowInIndirectCall ...');
224 225 226 227
  let builder = new WasmModuleBuilder();
  builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
  let kGrowFunction =
      builder.addFunction('grow', kSig_i_i)
228
          .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
229 230 231 232
          .exportFunc()
          .index;
  builder.addFunction('main', kSig_i_ii)
      .addBody([
233 234
        kExprLocalGet, 1,                  // get number of new pages
        kExprLocalGet, 0,                  // get index of the function
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
        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.
251 252
(function TestMemoryGrowAndAccessInIndirectCall() {
  print('TestMemoryGrowAndAccessInIndirectCall ...');
253 254 255 256 257
  let index = 2 * kPageSize - 4;
  let builder = new WasmModuleBuilder();
  builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
  let kGrowFunction =
      builder.addFunction('grow', kSig_i_i)
258
          .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
259 260 261
          .exportFunc()
          .index;
  builder.addFunction('load', kSig_i_i)
262
      .addBody([kExprLocalGet, 0, kExprI32LoadMem, 0, 0])
263 264 265 266
      .exportFunc();
  let sig = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []);
  builder.addFunction('main', sig)
      .addBody([
267 268
        kExprLocalGet, 1,                  // get number of new pages
        kExprLocalGet, 0,                  // get index of the function
269 270
        kExprCallIndirect, 0, kTableZero,  // call the function
        kExprDrop,                         // drop the result of grow
271 272
        kExprLocalGet, 2,                  // get index
        kExprLocalGet, 3,                  // get value
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
        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.
289 290
(function TestMemoryGrowAndStoreInIndirectCall() {
  print('TestMemoryGrowAndStoreInIndirectCall ...');
291 292 293 294 295 296 297 298 299 300
  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
301
            kExprMemoryGrow, kMemoryZero,  // grow memory
302 303 304 305 306 307 308 309 310 311 312 313
            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
314
        kExprLocalGet, 0,                  // get index of the function
315 316 317 318 319 320 321 322 323 324
        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));
})();
325 326 327 328

// 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.
329 330
(function TestMemoryGrowInIndirectCallInsideLoop() {
  print('TestMemoryGrowInIndirectCallInsideLoop ...');
331 332 333 334
  let builder = new WasmModuleBuilder();
  builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
  let kGrowFunction =
      builder.addFunction('grow', kSig_i_i)
335
          .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
336 337 338 339 340
          .exportFunc()
          .index;
  builder.addFunction('main', kSig_i_iii)
      .addBody([
        // clang-format off
341
        kExprLoop, kWasmVoid,                   // while
342
          kExprLocalGet, 1,                     // -
343
          kExprIf, kWasmVoid,                   // if <param1> != 0
344
            // Grow memory.
345 346
            kExprLocalGet, 2,                   // get number of new pages
            kExprLocalGet, 0,                   // get index of the function
347 348 349
            kExprCallIndirect, 0, kTableZero,   // call the function
            kExprDrop,                          // drop the result of grow
            // Decrease loop variable.
350
            kExprLocalGet, 1,                   // -
351 352
            kExprI32Const, 1,                   // -
            kExprI32Sub,                        // -
353
            kExprLocalSet, 1,                   // decrease <param1>
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
            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.
374 375
(function TestMemoryGrowAndStoreInIndirectCallInsideLoop() {
  print('TestMemoryGrowAndStoreInIndirectCallInsideLoop ...');
376 377 378 379 380
  let builder = new WasmModuleBuilder();
  let deltaPages = 1;
  builder.addMemory(initialMemoryPages, maximumMemoryPages, true);
  let kGrowFunction =
      builder.addFunction('grow', kSig_i_i)
381
          .addBody([kExprLocalGet, 0, kExprMemoryGrow, kMemoryZero])
382 383 384 385
          .exportFunc()
          .index;
  builder.addFunction('store', kSig_i_ii)
      .addBody([
386 387
        kExprLocalGet, 0, kExprLocalGet, 1, kExprI32StoreMem, 0, 0,
        kExprLocalGet, 1
388 389 390 391 392 393 394 395
      ])
      .exportFunc();
  builder
      .addFunction(
          // parameters:  function_index, iterations, deltaPages, index
          'main', makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], [kWasmI32]))
      .addBody([
        // clang-format off
396
        kExprLoop, kWasmVoid,                   // while
397
          kExprLocalGet, 1,                     // -
398
          kExprIf, kWasmVoid,                   // if <param1> != 0
399
            // Grow memory.
400 401
            kExprLocalGet, 2,                   // get number of new pages
            kExprLocalGet, 0,                   // get index of the function
402 403 404
            kExprCallIndirect, 0, kTableZero,   // call the function
            kExprDrop,                          // drop the result of grow
            // Increase counter in memory.
405 406
            kExprLocalGet, 3,                   // put index (for store)
                kExprLocalGet, 3,               // put index (for load)
407 408 409 410 411
                kExprI32LoadMem, 0, 0,          // load from grown memory
              kExprI32Const, 1,                 // -
              kExprI32Add,                      // increase counter
            kExprI32StoreMem, 0, 0,             // store counter in memory
            // Decrease loop variable.
412
            kExprLocalGet, 1,                   // -
413 414
            kExprI32Const, 1,                   // -
            kExprI32Sub,                        // -
415
            kExprLocalSet, 1,                   // decrease <param1>
416 417 418 419
            kExprBr, 1,                         // continue
          kExprEnd,                             // end if
        kExprEnd,                               // end loop
        // Return the value
420
        kExprLocalGet, 3,                       // -
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
        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));
})();