v8/test/mjsunit/wasm/gc-optimizations.js
Matthias Liedtke ad52bf8a4e [wasm-gc] Cleanup: Rename remaining '_static' op codes
Bug: v8:7748
Change-Id: Ie94e8f7f5afdaea7b4406bf8c57b294bf8f5733c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3865959
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82851}
2022-08-31 08:35:38 +00:00

531 lines
17 KiB
JavaScript

// 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
// This tests are meant to examine if Turbofan CsaLoadElimination works
// correctly for wasm. The TurboFan graphs can be examined with --trace-turbo.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// Fresh objects, known offsets
(function LoadEliminationtFreshKnownTest() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct = builder.addStruct([makeField(kWasmI32, true),
makeField(kWasmI32, true)]);
builder.addFunction("main", makeSig([kWasmI32], [kWasmI32]))
.addLocals(wasmRefNullType(struct), 1)
.addBody([
kExprI32Const, 10, // local1 = struct(10, 100);
kExprI32Const, 100,
kGCPrefix, kExprStructNew, struct,
kExprLocalSet, 1,
kExprLocalGet, 0, // Split control based on an unknown value
kExprIf, kWasmI32,
kExprLocalGet, 1, // local1.field1 = 42
kExprI32Const, 42,
kGCPrefix, kExprStructSet, struct, 1,
kExprLocalGet, 1, // local1.field1
kGCPrefix, kExprStructGet, struct, 1,
kExprElse,
kExprLocalGet, 1, // local1.field1 = 11
kExprI32Const, 11,
kGCPrefix, kExprStructSet, struct, 1,
kExprLocalGet, 1, // local1.field1 = 22
kExprI32Const, 22,
kGCPrefix, kExprStructSet, struct, 1,
kExprLocalGet, 1, // local1.field1 + local1.field1
kGCPrefix, kExprStructGet, struct, 1,
kExprLocalGet, 1,
kGCPrefix, kExprStructGet, struct, 1,
kExprI32Add,
kExprEnd,
kExprLocalGet, 1, // return if-result * (local1.field1 + local1.field0)
kGCPrefix, kExprStructGet, struct, 0,
kExprLocalGet, 1,
kGCPrefix, kExprStructGet, struct, 1,
kExprI32Add,
kExprI32Mul
])
.exportFunc();
let instance = builder.instantiate({});
assertEquals(instance.exports.main(1), 42 * (42 + 10));
assertEquals(instance.exports.main(0), (22 + 22) * (22 + 10));
})();
(function LoadEliminationtConstantKnownTest() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct = builder.addStruct([makeField(kWasmI32, true)]);
let replaced_value = 55
let param_1_value = 42
let init_value_1 = 5
let init_value_2 = 17
let tester = builder.addFunction("tester", makeSig(
[wasmRefType(struct), wasmRefType(struct)], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructGet, struct, 0,
kExprLocalGet, 0,
kExprI32Const, replaced_value,
kGCPrefix, kExprStructSet, struct, 0,
// We should eliminate this load and replace it with replaced_value
kExprLocalGet, 0,
kGCPrefix, kExprStructGet, struct, 0,
kExprLocalGet, 1,
kExprI32Const, param_1_value,
kGCPrefix, kExprStructSet, struct, 0,
// Although we could eliminate this load before, we cannot anymore,
// because the parameters may alias.
kExprLocalGet, 0,
kGCPrefix, kExprStructGet, struct, 0,
kExprI32Add, kExprI32Add
]);
function buildStruct(value) {
return [kExprI32Const, value,
kGCPrefix, kExprStructNew, struct];
}
builder.addFunction("main_non_aliasing", kSig_i_v)
.addBody([
...buildStruct(init_value_1), ...buildStruct(init_value_2),
kExprCallFunction, tester.index])
.exportFunc();
builder.addFunction("main_aliasing", kSig_i_v)
.addLocals(wasmRefNullType(struct), 1)
.addBody([
...buildStruct(init_value_1), kExprLocalSet, 0,
kExprLocalGet, 0, kExprRefAsNonNull,
kExprLocalGet, 0, kExprRefAsNonNull,
kExprCallFunction, tester.index])
.exportFunc();
let instance = builder.instantiate({});
assertEquals(init_value_1 + replaced_value + replaced_value,
instance.exports.main_non_aliasing());
assertEquals(init_value_1 + replaced_value + param_1_value,
instance.exports.main_aliasing());
})();
(function LoadEliminationtArbitraryKnownTest() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct = builder.addStruct([makeField(kWasmI32, true)]);
let initial_value = 19;
let replacing_value_1 = 55;
let replacing_value_2 = 37;
let id = builder.addFunction("id", makeSig([wasmRefNullType(struct)],
[wasmRefNullType(struct)]))
.addBody([kExprLocalGet, 0])
builder.addFunction("main", kSig_i_v)
.addLocals(wasmRefNullType(struct), 2)
.addBody([
// We store a fresh struct in local0
kExprI32Const, initial_value,
kGCPrefix, kExprStructNew, struct,
kExprLocalSet, 0,
// We pass it through a function and store it to local1. local1 may now
// alias with anything.
kExprLocalGet, 0, kExprCallFunction, id.index, kExprLocalSet, 1,
kExprLocalGet, 0,
kExprI32Const, replacing_value_1,
kGCPrefix, kExprStructSet, struct, 0,
// We should eliminate this load.
kExprLocalGet, 0, kGCPrefix, kExprStructGet, struct, 0,
kExprLocalGet, 1,
kExprI32Const, replacing_value_2,
kGCPrefix, kExprStructSet, struct, 0,
// We should not eliminate this load.
kExprLocalGet, 0, kGCPrefix, kExprStructGet, struct, 0,
kExprI32Add])
.exportFunc();
let instance = builder.instantiate({});
assertEquals(replacing_value_1 + replacing_value_2, instance.exports.main());
})();
(function LoadEliminationtFreshUnknownTest() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let array = builder.addArray(kWasmI64, true);
// parameter: unknown array index
builder.addFunction("main", makeSig([kWasmI32], [kWasmI32]))
.addLocals(wasmRefNullType(array), 1)
.addBody([
kExprI32Const, 5,
kGCPrefix, kExprArrayNewDefault, array,
kExprLocalSet, 1,
kExprLocalGet, 1, // a[i] = i for i = {0..4}
kExprI32Const, 0,
kExprI64Const, 0,
kGCPrefix, kExprArraySet, array,
kExprLocalGet, 1,
kExprI32Const, 1,
kExprI64Const, 1,
kGCPrefix, kExprArraySet, array,
kExprLocalGet, 1,
kExprI32Const, 2,
kExprI64Const, 2,
kGCPrefix, kExprArraySet, array,
kExprLocalGet, 1,
kExprI32Const, 3,
kExprI64Const, 3,
kGCPrefix, kExprArraySet, array,
kExprLocalGet, 1,
kExprI32Const, 4,
kExprI64Const, 4,
kGCPrefix, kExprArraySet, array,
// Get a constant index a[4] before setting unknown indices
kExprLocalGet, 1,
kExprI32Const, 4,
kGCPrefix, kExprArrayGet, array,
kExprLocalGet, 1, // Set a[local0] = 33
kExprLocalGet, 0,
kExprI64Const, 33,
kGCPrefix, kExprArraySet, array,
kExprLocalGet, 1, // Get a[local0]
kExprLocalGet, 0,
kGCPrefix, kExprArrayGet, array,
kExprLocalGet, 1, // Known index load cannot be eliminated anymore
kExprI32Const, 3,
kGCPrefix, kExprArrayGet, array,
// A load from different unknown index a[local0 + 1] cannot be eliminated
kExprLocalGet, 1,
kExprLocalGet, 0,
kExprI32Const, 1,
kExprI32Add,
kGCPrefix, kExprArrayGet, array,
kExprI64Add, // return a[4] * (a[local0] - (a[3] + a[local0 + 1]))
kExprI64Sub,
kExprI64Mul,
kExprI32ConvertI64 // To not have to worry about BigInts in JS world
])
.exportFunc();
let instance = builder.instantiate({});
assertEquals(4 * (33 - (3 + 1)), instance.exports.main(0));
assertEquals(4 * (33 - (3 + 2)), instance.exports.main(1));
assertEquals(4 * (33 - (3 + 3)), instance.exports.main(2));
assertEquals(4 * (33 - (33 + 4)), instance.exports.main(3));
})();
(function LoadEliminationtAllBetsAreOffTest() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct = builder.addStruct([makeField(kWasmI32, true)]);
let array = builder.addArray(kWasmI32, true);
let value_0 = 19;
let value_1 = 55;
let value_2 = 2;
let id = builder.addFunction("id", makeSig([wasmRefNullType(array)],
[wasmRefNullType(array)]))
.addBody([kExprLocalGet, 0])
// parameters: array, index
let tester = builder.addFunction("tester",
makeSig([wasmRefType(array), kWasmI32], [kWasmI32]))
.addLocals(wasmRefNullType(struct), 1)
.addLocals(wasmRefNullType(array), 1)
.addBody([
// We store a fresh struct in local1
kExprI32Const, 0,
kGCPrefix, kExprStructNew, struct,
kExprLocalSet, 2,
// We pass the array parameter through a function and store it to local2.
kExprLocalGet, 0, kExprCallFunction, id.index, kExprLocalSet, 3,
// Set the parameter array, the fresh struct, then the arbitrary array to
// an unknown offset.
kExprLocalGet, 0,
kExprI32Const, 5,
kExprI32Const, value_0,
kGCPrefix, kExprArraySet, array,
kExprLocalGet, 2,
kExprI32Const, value_1,
kGCPrefix, kExprStructSet, struct, 0,
kExprLocalGet, 3,
kExprLocalGet, 1,
kExprI32Const, value_2,
kGCPrefix, kExprArraySet, array,
// Neither load can be eliminated.
kExprLocalGet, 0,
kExprI32Const, 5,
kGCPrefix, kExprArrayGet, array,
kExprLocalGet, 2,
kGCPrefix, kExprStructGet, struct, 0,
kExprI32Add]);
builder.addFunction("main", kSig_i_i)
.addBody([
kExprI32Const, 10,
kGCPrefix, kExprArrayNewDefault, array,
kExprI32Const, 7,
kExprCallFunction, tester.index,
])
.exportFunc();
let instance = builder.instantiate({});
assertEquals(value_0 + value_1, instance.exports.main());
})();
(function EscapeAnalysisWithLoadElimination() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct1 = builder.addStruct([makeField(kWasmI32, true)]);
let struct2 = builder.addStruct([makeField(wasmRefNullType(struct1), true)]);
// TF should eliminate both allocations in this function.
builder.addFunction("main", kSig_i_i)
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct1,
kGCPrefix, kExprStructNew, struct2,
kGCPrefix, kExprStructGet, struct2, 0,
kGCPrefix, kExprStructGet, struct1, 0])
.exportFunc();
let instance = builder.instantiate({});
assertEquals(42, instance.exports.main(42));
})();
(function EscapeAnalysisWithInterveningEffect() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct1 = builder.addStruct([makeField(kWasmI32, true)]);
let struct2 = builder.addStruct([makeField(wasmRefNullType(struct1), true)]);
let nop = builder.addFunction("nop", kSig_v_v).addBody([]);
// TF should eliminate both allocations in this function, despite the
// intervening effectful call.
builder.addFunction("main", kSig_i_i)
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct1,
kExprCallFunction, nop.index,
kGCPrefix, kExprStructNew, struct2,
kExprLocalGet, 0,
kExprReturn])
.exportFunc();
let instance = builder.instantiate({});
assertEquals(42, instance.exports.main(42));
})();
(function AllocationFolding() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
let struct_index = builder.addStruct([makeField(kWasmI32, true)]);
let struct_2 = builder.addStruct([
makeField(wasmRefType(struct_index), false),
makeField(wasmRefType(struct_index), false)
]);
let global = builder.addGlobal(
wasmRefNullType(struct_2), true, [kExprRefNull, struct_2]);
// The three alocations should be folded.
builder.addFunction("main", kSig_i_i)
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct_index,
kExprI32Const, 43,
kGCPrefix, kExprStructNew, struct_index,
kGCPrefix, kExprStructNew, struct_2,
kExprGlobalSet, global.index,
kExprLocalGet, 0,
])
.exportFunc();
let instance = builder.instantiate();
assertEquals(10, instance.exports.main(10));
})();
(function PathBasedTypedOptimization() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
let super_struct = builder.addStruct([makeField(kWasmI32, true)]);
let mid_struct = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], super_struct);
let sub_struct = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI32, true),
makeField(kWasmI32, true)],
mid_struct);
let addToLocal = [kExprLocalGet, 1, kExprI32Add, kExprLocalSet, 1];
builder.addFunction(
"main", makeSig([wasmRefNullType(super_struct)], [kWasmI32]))
.addLocals(kWasmI32, 1)
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprRefTest, sub_struct,
// These casts have to be preserved.
kExprLocalGet, 0,
kGCPrefix, kExprRefCast, mid_struct,
kGCPrefix, kExprRefCast, sub_struct,
kGCPrefix, kExprStructGet, sub_struct, 1,
...addToLocal,
kExprIf, kWasmVoid,
// Both these casts should be optimized away.
kExprLocalGet, 0,
kGCPrefix, kExprRefCast, mid_struct,
kGCPrefix, kExprRefCast, sub_struct,
kGCPrefix, kExprStructGet, sub_struct, 1,
...addToLocal,
kExprBlock, kWasmRefNull, super_struct,
kExprLocalGet, 0,
// This should also get optimized away.
kGCPrefix, kExprBrOnCastFail, 0, mid_struct,
// So should this, despite being represented by a TypeGuard alias.
kGCPrefix, kExprRefCast, sub_struct,
kGCPrefix, kExprStructGet, sub_struct, 1,
...addToLocal,
kExprLocalGet, 0, // Due to the branch result type.
kExprEnd,
kExprDrop,
kExprElse,
// This (always trapping) cast should be preserved.
kExprLocalGet, 0,
kGCPrefix, kExprRefCast, sub_struct,
kGCPrefix, kExprStructGet, sub_struct, 1,
...addToLocal,
kExprEnd,
// This cast should be preserved.
kExprLocalGet, 0,
kGCPrefix, kExprRefCast, sub_struct,
kGCPrefix, kExprStructGet, sub_struct, 1,
kExprLocalGet, 1, kExprI32Add
])
.exportFunc();
builder.instantiate();
})();
(function IndependentCastNullRefType() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_super = builder.addStruct([makeField(kWasmI32, true)]);
let struct_b = builder.addStruct([makeField(kWasmI32, true)], struct_super);
let struct_a = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct_super);
let callee_sig = makeSig([wasmRefNullType(struct_a)], [kWasmI32]);
let callee = builder.addFunction("callee", callee_sig)
.addBody([
// Cast from struct_a to struct_b via common base type struct_super.
kExprLocalGet, 0,
kGCPrefix, kExprRefCast, struct_super,
kGCPrefix, kExprRefCast, struct_b, // annotated as 'ref null none'
kExprRefIsNull,
]);
builder.addFunction("main", kSig_i_i)
.addLocals(wasmRefNullType(struct_a), 1)
.addBody([
kExprLocalGet, 0,
kExprIf, kWasmVoid,
kExprI32Const, 10,
kExprI32Const, 100,
kGCPrefix, kExprStructNew, struct_a,
kExprLocalSet, 1,
kExprEnd,
kExprLocalGet, 1,
kExprCallFunction, callee.index
]).exportFunc();
let instance = builder.instantiate({});
// main calls 'callee(null)'
// -> (ref.is_null (ref.cast struct_b (ref.cast struct_super (local.get 0))))
// returns true.
assertEquals(1, instance.exports.main(0));
// main calls 'callee(struct.new struct_a)'
// -> (ref.cast struct_b) traps.
assertTraps(kTrapIllegalCast, () => instance.exports.main(1));
})();
(function StaticCastOfKnownNull() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_super = builder.addStruct([makeField(kWasmI32, true)]);
let struct_b = builder.addStruct([makeField(kWasmI32, true)], struct_super);
let struct_a = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct_super);
let callee_sig = makeSig([wasmRefNullType(struct_super)], [kWasmI32]);
let callee = builder.addFunction("callee", callee_sig)
.addBody([
kExprBlock, kWasmRefNull, struct_super,
kExprLocalGet, 0,
kExprBrOnNonNull, 0,
// local.get 0 is known to be null until end of block.
kExprLocalGet, 0,
// This cast is a no-op and shold be optimized away.
kGCPrefix, kExprRefCast, struct_b,
kExprEnd,
kExprRefIsNull,
]);
builder.addFunction("main", kSig_i_v)
.addBody([
kExprRefNull, struct_a,
kExprCallFunction, callee.index
]).exportFunc();
let instance = builder.instantiate({});
assertEquals(1, instance.exports.main());
})();