// Copyright 2021 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: --experimental-wasm-gc --no-liftoff

d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");

// This is benchmark to investigate at which point it is more efficient to call
// a memcpy-based builtin for array.copy, rather than copying
// element-by-element.
// How to run:
// - Set {iterations} to a high number to get better measurements
// - Change the value of {length} to find point at which the builtin becomes
//   faster.
// - Change {array_type} if you want to test different types.
// Right now, the limit is found to be around 10.
(function ArrayCopyBenchmark() {

  let array_length = 10;
  let iterations = 1;

  var builder = new WasmModuleBuilder();
  let struct_index = builder.addStruct([makeField(kWasmI32, true),
                                        makeField(kWasmI8, false)]);
  let array_type = kWasmI32;  // Also try kWasmI64, wasmOptRefType(struct_index)
  var array_index = builder.addArray(array_type, true);
  var from = builder.addGlobal(wasmOptRefType(array_index), true);
  var to = builder.addGlobal(wasmOptRefType(array_index), true);

  builder.addFunction("init", kSig_v_v)
    .addBody([
      ...wasmI32Const(array_length),
      kGCPrefix, kExprRttCanon, array_index,
      kGCPrefix, kExprArrayNewDefaultWithRtt, array_index,
      kExprGlobalSet, from.index,
      ...wasmI32Const(array_length),
      kGCPrefix, kExprRttCanon, array_index,
      kGCPrefix, kExprArrayNewDefaultWithRtt, array_index,
      kExprGlobalSet, to.index
    ])
    .exportFunc();

  builder.addFunction("array_copy", kSig_v_v)
    .addLocals(kWasmI32, 1)
    .addBody([
      kExprLoop, kWasmVoid,
        kExprGlobalGet, to.index,
        ...wasmI32Const(0),
        kExprGlobalGet, from.index,
        ...wasmI32Const(0),
        ...wasmI32Const(array_length),
        kGCPrefix, kExprArrayCopy, array_index, array_index,
        // Outer loop: run everything {iterations} times.
        kExprLocalGet, 0, kExprI32Const, 1, kExprI32Add, kExprLocalSet, 0,
        kExprLocalGet, 0, ...wasmI32Const(iterations), kExprI32LtS,
        kExprBrIf, 0,
      kExprEnd])
    .exportFunc();

  builder.addFunction("loop_copy", kSig_v_v)
    .addLocals(kWasmI32, 2)
    .addBody([
      kExprLoop, kWasmVoid,
        ...wasmI32Const(0),
        kExprLocalSet, 0,
        kExprGlobalGet, from.index, kExprRefAsNonNull,
        kExprGlobalGet, to.index, kExprRefAsNonNull,
        kExprLet, kWasmVoid, 1, 2, kWasmRef, array_index,
        kExprLoop, kWasmVoid,
          kExprLocalGet, 1,  // array
          kExprLocalGet, 2,  // index
          // value
          kExprLocalGet, 0, kExprLocalGet, 2,
          kGCPrefix, kExprArrayGet, array_index,
          // array.set
          kGCPrefix, kExprArraySet, array_index,
          // index++
          kExprLocalGet, 2, kExprI32Const, 1, kExprI32Add, kExprLocalSet, 2,
          // if (index < array_length) goto loop;
          kExprLocalGet, 2, ...wasmI32Const(array_length), kExprI32LtU,
          kExprBrIf, 0,
        kExprEnd,
        kExprEnd,
      // Outer loop: run everything {iterations} times.
      kExprLocalGet, 1, kExprI32Const, 1, kExprI32Add, kExprLocalSet, 1,
      kExprLocalGet, 1, ...wasmI32Const(iterations), kExprI32LtS,
      kExprBrIf, 0,
      kExprEnd])
    .exportFunc();

  var instance = builder.instantiate({});

  instance.exports.init();
  print("Array length: " + array_length + ", #iterations: " + iterations);
  {
    let before = Date.now();
    instance.exports.array_copy();
    let after = Date.now();
    print("array.copy: " + (after - before) + "ms");
  }
  {
    let before = Date.now();
    instance.exports.loop_copy();
    let after = Date.now();
    print("loop copy: " + (after - before) + "ms");
  }
})();