v8/test/mjsunit/wasm/speculative-inlining.js
Jakob Kummerow 6168782925 [wasm-gc] call_ref: consume a type immediate
Per https://github.com/WebAssembly/function-references/pull/76,
call_ref and return_call_ref should consume type immediates specifying
the signature of the funcref. This is a breaking change.

To ease the migration, this patch introduces a temporary alternative
binary encoding for call_ref:
- 0x14 continues to *not* take a type immediate for now.
- 0x17 (formerly "let") is the new call_ref *with* type immediate. Module
  producers are encouraged to emit this encoding ASAP.
- After a few weeks of transitionary period, we'll update 0x14 to
  take a type immediate as well. At this point, module producers will be
  encouraged to switch back to 0x14.
- After a few more weeks of transitionary period, we'll drop 0x17 again.

We're not doing the same dance for return_call_ref because it currently
has no uses that we know of.

Bug: v8:7748,v8:9495
Change-Id: Id8d468be3949f84571efff713c937ffd1addff70
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3863280
Reviewed-by: Matthias Liedtke <mliedtke@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Auto-Submit: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82839}
2022-08-30 17:51:49 +00:00

246 lines
8.5 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: --wasm-speculative-inlining --experimental-wasm-return-call
// Flags: --experimental-wasm-typed-funcref --experimental-wasm-type-reflection
// Flags: --no-wasm-tier-up --wasm-dynamic-tiering --wasm-tiering-budget=100
// Flags: --allow-natives-syntax
// These tests check if functions are speculatively inlined as expected. We do
// not check automatically which functions are inlined. To get more insight, run
// with --trace-wasm-speculative-inlining, --trace-turbo, --trace-wasm and (for
// the last test only) --trace.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function CallRefSpecSucceededTest() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
// f(x) = x - 1
let callee = builder.addFunction("callee", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub]);
let global = builder.addGlobal(wasmRefType(callee.type_index), false,
[kExprRefFunc, callee.index]);
// g(x) = f(5) + x
builder.addFunction("main", kSig_i_i)
.addBody([kExprI32Const, 5, kExprGlobalGet, global.index,
kExprCallRef, callee.type_index,
kExprLocalGet, 0, kExprI32Add])
.exportAs("main");
let instance = builder.instantiate();
// Run 'main' until it is tiered-up.
while (%IsLiftoffFunction(instance.exports.main)) {
assertEquals(14, instance.exports.main(10));
}
// The tiered-up function should have {callee} speculatively inlined.
assertEquals(14, instance.exports.main(10));
})();
(function CallRefSpecFailedTest() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_index = builder.addType(kSig_i_i);
// h(x) = x - 1
let callee0 = builder.addFunction("callee0", sig_index)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub]);
// f(x) = x - 2
let callee1 = builder.addFunction("callee1", sig_index)
.addBody([kExprLocalGet, 0, kExprI32Const, 2, kExprI32Sub]);
let global0 = builder.addGlobal(wasmRefType(sig_index), false,
[kExprRefFunc, callee0.index]);
let global1 = builder.addGlobal(wasmRefType(sig_index), false,
[kExprRefFunc, callee1.index]);
// g(x, y) = if (y) { h(5) + x } else { f(7) + x }
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprLocalGet, 1,
kExprIf, kWasmI32,
kExprI32Const, 5, kExprGlobalGet, global0.index,
kExprCallRef, sig_index,
kExprLocalGet, 0, kExprI32Add,
kExprElse,
kExprI32Const, 7, kExprGlobalGet, global1.index,
kExprCallRef, sig_index,
kExprLocalGet, 0, kExprI32Add,
kExprEnd])
.exportAs("main");
let instance = builder.instantiate();
// Run 'main' until it is tiered-up.
while (%IsLiftoffFunction(instance.exports.main)) {
assertEquals(14, instance.exports.main(10, 1));
}
// Tier-up is done, and {callee0} should be inlined in the trace.
assertEquals(14, instance.exports.main(10, 1))
// Now, run main with {callee1} instead. The correct reference should still be
// called after inlining.
assertEquals(15, instance.exports.main(10, 0));
})();
(function CallReturnRefSpecSucceededTest() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
// f(x) = x - 1
let callee = builder.addFunction("callee", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub]);
let global = builder.addGlobal(wasmRefType(callee.type_index), false,
[kExprRefFunc, callee.index]);
// g(x) = f(5 + x)
builder.addFunction("main", kSig_i_i)
.addBody([kExprI32Const, 5, kExprLocalGet, 0, kExprI32Add,
kExprGlobalGet, global.index,
kExprReturnCallRef, callee.type_index])
.exportAs("main");
let instance = builder.instantiate();
// Run 'main' until it is tiered-up.
while (%IsLiftoffFunction(instance.exports.main)) {
assertEquals(14, instance.exports.main(10));
}
// After tier-up, the tail call should be speculatively inlined.
assertEquals(14, instance.exports.main(10));
})();
(function CallReturnRefSpecFailedTest() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_index = builder.addType(kSig_i_i);
// h(x) = x - 1
let callee0 = builder.addFunction("callee0", sig_index)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub]);
// f(x) = x - 2
let callee1 = builder.addFunction("callee1", sig_index)
.addBody([kExprLocalGet, 0, kExprI32Const, 2, kExprI32Sub]);
let global0 = builder.addGlobal(wasmRefType(sig_index), false,
[kExprRefFunc, callee0.index]);
let global1 = builder.addGlobal(wasmRefType(sig_index), false,
[kExprRefFunc, callee1.index]);
// g(x, y) = if (y) { h(x) } else { f(x) }
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprLocalGet, 1,
kExprIf, kWasmI32,
kExprLocalGet, 0, kExprGlobalGet, global0.index,
kExprReturnCallRef, sig_index,
kExprElse,
kExprLocalGet, 0, kExprGlobalGet, global1.index,
kExprReturnCallRef, sig_index,
kExprEnd])
.exportAs("main");
let instance = builder.instantiate();
// Run 'main' until it is tiered-up.
while (%IsLiftoffFunction(instance.exports.main)) {
assertEquals(9, instance.exports.main(10, 1));
}
// After tier-up, {callee0} should be inlined in the trace.
assertEquals(9, instance.exports.main(10, 1))
// Now, run main with {callee1} instead. The correct reference should still be
// called.
assertEquals(8, instance.exports.main(10, 0));
})();
(function CallRefImportedFunction() {
print(arguments.callee.name);
let instance1 = function() {
let builder = new WasmModuleBuilder();
let f1 = builder.addImport("m", "i_f1", kSig_i_i);
let f2 = builder.addImport("m", "i_f2", kSig_i_i);
builder.addExport("f1", f1);
builder.addExport("f2", f2);
return builder.instantiate({m : { i_f1 : x => x + 1, i_f2 : x => x + 2}});
}();
let instance2 = function() {
let builder = new WasmModuleBuilder();
let sig1 = builder.addType(kSig_i_i);
let sig2 = builder.addType(kSig_i_ii);
builder.addFunction("callee", sig2)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("main", makeSig([kWasmI32,
wasmRefType(sig1)], [kWasmI32]))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprCallRef, sig1])
.exportFunc();
return builder.instantiate({});
}();
// Run 'main' until it is tiered-up.
while (%IsLiftoffFunction(instance2.exports.main)) {
assertEquals(1, instance2.exports.main(0, instance1.exports.f1));
}
// The function f1 defined in another module should not be inlined.
assertEquals(1, instance2.exports.main(0, instance1.exports.f1));
})();
// Check that we handle WasmJSFunctions properly and do not inline them, both
// in the monomorphic and polymorphic case.
(function CallRefWasmJsFunction() {
print(arguments.callee.name);
let f1 = new WebAssembly.Function({parameters: ["i32"], results: ["i32"]},
x => x + 1);
let f2 = new WebAssembly.Function({parameters: ["i32"], results: ["i32"]},
x => x * 2);
let instance2 = function() {
let builder = new WasmModuleBuilder();
let sig = builder.addType(kSig_i_i);
builder.addFunction("main", makeSig(
[kWasmI32, wasmRefType(sig), wasmRefType(sig)], [kWasmI32]))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprCallRef, sig,
kExprLocalGet, 0, kExprLocalGet, 2, kExprCallRef, sig,
kExprI32Add])
.exportFunc();
return builder.instantiate({});
}();
var i = 0;
// Run 'main' until it is tiered-up. The first argument should try to be
// spec-inlined monomorphically. We pass f2 to the second argument 80% of the
// time, so it should try to be spec-inlined polymorphically.
while (%IsLiftoffFunction(instance2.exports.main)) {
if (i % 5 == 0) {
assertEquals(12, instance2.exports.main(5, f1, f1));
} else {
assertEquals(16, instance2.exports.main(5, f1, f2));
}
i++;
}
// WebAssembly.Function objects should not be inlined.
assertEquals(16, instance2.exports.main(5, f1, f2));
assertEquals(12, instance2.exports.main(5, f1, f1));
})();