Commit eb0ef4d7 authored by Vicky Kontoura's avatar Vicky Kontoura Committed by Commit Bot

[wasm] Reverse count logic for the tiering of js-to-wasm wrappers

This CL reverses the count logic for the tiering strategy of the
js-to-wasm wrappers. The initial approach was that calls to each
function were counted up, until a threshold was reached and the function
would tier up. With this CL, each function is assigned a budget of calls
that can be handled through the generic wrapper. Calls are counted down
until the budget is exhausted, which will trigger the tier-up
for the function.

This approach comes with two advantages. Firstly, determining whether
a function's budget is exhausted is as simple as checking the flags set
from the decrement of the budget. Secondly, the code generated by the
generic wrapper does not depend on the specific value of the initial
budget.

Bug: v8:10982
Change-Id: I5e186c6cf836a9c197b41d0f7ad075b07c87a4da
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2532300Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Vicky Kontoura <vkont@google.com>
Cr-Commit-Position: refs/heads/master@{#71153}
parent 83a2f390
......@@ -3032,22 +3032,20 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
WasmExportedFunctionData::kInstanceOffset - kHeapObjectTag));
// -------------------------------------------
// Increment the call count in function data.
// Decrement the budget of the generic wrapper in function data.
// -------------------------------------------
__ SmiAddConstant(
MemOperand(function_data,
WasmExportedFunctionData::kCallCountOffset - kHeapObjectTag),
Smi::FromInt(1));
MemOperand(function_data, WasmExportedFunctionData::kWrapperBudgetOffset -
kHeapObjectTag),
Smi::FromInt(-1));
// -------------------------------------------
// Check if the call count reached the threshold.
// Check if the budget of the generic wrapper reached 0 (zero).
// -------------------------------------------
// Instead of a specific comparison, we can directly use the flags set
// from the previous addition.
Label compile_wrapper, compile_wrapper_done;
__ SmiCompare(
MemOperand(function_data,
WasmExportedFunctionData::kCallCountOffset - kHeapObjectTag),
Smi::FromInt(wasm::kGenericWrapperThreshold));
__ j(greater_equal, &compile_wrapper);
__ j(less_equal, &compile_wrapper);
__ bind(&compile_wrapper_done);
// -------------------------------------------
......
......@@ -126,7 +126,7 @@ constexpr int kAnonymousFuncIndex = -1;
// The number of calls to an exported wasm function that will be handled
// by the generic wrapper. Once this threshold is reached, a specific wrapper
// is to be compiled for the function's signature.
constexpr uint32_t kGenericWrapperThreshold = 6;
constexpr uint32_t kGenericWrapperBudget = 6;
} // namespace wasm
} // namespace internal
......
......@@ -333,7 +333,7 @@ SMI_ACCESSORS(WasmExportedFunctionData, jump_table_offset,
kJumpTableOffsetOffset)
SMI_ACCESSORS(WasmExportedFunctionData, function_index, kFunctionIndexOffset)
ACCESSORS(WasmExportedFunctionData, signature, Foreign, kSignatureOffset)
SMI_ACCESSORS(WasmExportedFunctionData, call_count, kCallCountOffset)
SMI_ACCESSORS(WasmExportedFunctionData, wrapper_budget, kWrapperBudgetOffset)
ACCESSORS(WasmExportedFunctionData, c_wrapper_code, Object, kCWrapperCodeOffset)
ACCESSORS(WasmExportedFunctionData, wasm_call_target, Object,
kWasmCallTargetOffset)
......
......@@ -1891,7 +1891,7 @@ Handle<WasmExportedFunction> WasmExportedFunction::New(
function_data->set_jump_table_offset(jump_table_offset);
function_data->set_function_index(func_index);
function_data->set_signature(*sig_foreign);
function_data->set_call_count(0);
function_data->set_wrapper_budget(wasm::kGenericWrapperBudget);
function_data->set_c_wrapper_code(Smi::zero(), SKIP_WRITE_BARRIER);
function_data->set_wasm_call_target(Smi::zero(), SKIP_WRITE_BARRIER);
function_data->set_packed_args_size(0);
......
......@@ -763,7 +763,7 @@ class WasmExportedFunctionData : public Struct {
DECL_INT_ACCESSORS(jump_table_offset)
DECL_INT_ACCESSORS(function_index)
DECL_ACCESSORS(signature, Foreign)
DECL_INT_ACCESSORS(call_count)
DECL_INT_ACCESSORS(wrapper_budget)
DECL_ACCESSORS(c_wrapper_code, Object)
DECL_ACCESSORS(wasm_call_target, Object)
DECL_INT_ACCESSORS(packed_args_size)
......
......@@ -18,7 +18,7 @@ extern class WasmExportedFunctionData extends Struct {
jump_table_offset: Smi;
function_index: Smi;
signature: Foreign;
call_count: Smi;
wrapper_budget: Smi;
// The remaining fields are for fast calling from C++. The contract is
// that they are lazily populated, and either all will be present or none.
c_wrapper_code: Object;
......
......@@ -29,7 +29,7 @@ void Cleanup() {
} // namespace
TEST(CallCounter) {
TEST(WrapperBudget) {
{
// This test assumes use of the generic wrapper.
FlagScope<bool> use_wasm_generic_wrapper(&FLAG_wasm_generic_wrapper, true);
......@@ -61,9 +61,12 @@ TEST(CallCounter) {
"main");
Handle<WasmExportedFunction> main_export = maybe_export.ToHandleChecked();
// Check that the counter has initially a value of 0.
CHECK_EQ(main_export->shared().wasm_exported_function_data().call_count(),
0);
// Check that the generic-wrapper budget has initially a value of
// kGenericWrapperBudget.
CHECK_EQ(
main_export->shared().wasm_exported_function_data().wrapper_budget(),
kGenericWrapperBudget);
CHECK_GT(kGenericWrapperBudget, 0);
// Call the exported Wasm function and get the result.
Handle<Object> params[2] = {Handle<Object>(Smi::FromInt(6), isolate),
......@@ -74,9 +77,10 @@ TEST(CallCounter) {
Execution::Call(isolate, main_export, receiver, 2, params);
Handle<Object> result = maybe_result.ToHandleChecked();
// Check that the counter has now a value of 1.
CHECK_EQ(main_export->shared().wasm_exported_function_data().call_count(),
1);
// Check that the budget has now a value of (kGenericWrapperBudget - 1).
CHECK_EQ(
main_export->shared().wasm_exported_function_data().wrapper_budget(),
kGenericWrapperBudget - 1);
CHECK(result->IsSmi() && Smi::ToInt(*result) == kExpectedValue);
}
......@@ -115,15 +119,17 @@ TEST(WrapperReplacement) {
"main");
Handle<WasmExportedFunction> main_export = maybe_export.ToHandleChecked();
// Check that the counter has initially a value of 0.
CHECK_EQ(main_export->shared().wasm_exported_function_data().call_count(),
0);
CHECK_GT(kGenericWrapperThreshold, 0);
// Call the exported Wasm function as many times as required to reach the
// threshold for compiling the specific wrapper.
const int threshold = static_cast<int>(kGenericWrapperThreshold);
for (int i = 1; i < threshold; ++i) {
// Check that the generic-wrapper budget has initially a value of
// kGenericWrapperBudget.
CHECK_EQ(
main_export->shared().wasm_exported_function_data().wrapper_budget(),
kGenericWrapperBudget);
CHECK_GT(kGenericWrapperBudget, 0);
// Call the exported Wasm function as many times as required to almost
// exhaust the budget for using the generic wrapper.
const int budget = static_cast<int>(kGenericWrapperBudget);
for (int i = budget; i > 1; --i) {
// Verify that the wrapper to be used is still the generic one.
Code wrapper =
main_export->shared().wasm_exported_function_data().wrapper_code();
......@@ -137,10 +143,11 @@ TEST(WrapperReplacement) {
MaybeHandle<Object> maybe_result =
Execution::Call(isolate, main_export, receiver, 1, params);
Handle<Object> result = maybe_result.ToHandleChecked();
// Verify that the counter has now a value of i and the return value is
// correct.
CHECK_EQ(main_export->shared().wasm_exported_function_data().call_count(),
i);
// Verify that the budget has now a value of (i - 1) and the return value
// is correct.
CHECK_EQ(
main_export->shared().wasm_exported_function_data().wrapper_budget(),
i - 1);
CHECK(result->IsSmi() && Smi::ToInt(*result) == expected_value);
}
......@@ -162,9 +169,10 @@ TEST(WrapperReplacement) {
MaybeHandle<Object> maybe_result =
Execution::Call(isolate, main_export, receiver, 1, params);
Handle<Object> result = maybe_result.ToHandleChecked();
// Check that the counter has the threshold value and the result is correct.
CHECK_EQ(main_export->shared().wasm_exported_function_data().call_count(),
kGenericWrapperThreshold);
// Check that the budget has been exhausted and the result is correct.
CHECK_EQ(
main_export->shared().wasm_exported_function_data().wrapper_budget(),
0);
CHECK(result->IsSmi() && Smi::ToInt(*result) == expected_value);
// Verify that the wrapper-code object has changed.
......@@ -235,14 +243,14 @@ TEST(EagerWrapperReplacement) {
WasmExportedFunctionData id_function_data =
id_export->shared().wasm_exported_function_data();
// Set the call count for add to (threshold - 1),
// Set the remaining generic-wrapper budget for add to 1,
// so that the next call to it will cause the function to tier up.
add_function_data.set_call_count(kGenericWrapperThreshold - 1);
add_function_data.set_wrapper_budget(1);
// Verify that the call counts for all functions are correct.
CHECK_EQ(add_function_data.call_count(), kGenericWrapperThreshold - 1);
CHECK_EQ(mult_function_data.call_count(), 0);
CHECK_EQ(id_function_data.call_count(), 0);
// Verify that the generic-wrapper budgets for all functions are correct.
CHECK_EQ(add_function_data.wrapper_budget(), 1);
CHECK_EQ(mult_function_data.wrapper_budget(), kGenericWrapperBudget);
CHECK_EQ(id_function_data.wrapper_budget(), kGenericWrapperBudget);
// Verify that all functions are set to use the generic wrapper.
CHECK(add_function_data.wrapper_code().is_builtin() &&
......@@ -267,10 +275,10 @@ TEST(EagerWrapperReplacement) {
CHECK(result->IsSmi() && Smi::ToInt(*result) == expected_value);
}
// Verify that the call counts for all functions are correct.
CHECK_EQ(add_function_data.call_count(), kGenericWrapperThreshold);
CHECK_EQ(mult_function_data.call_count(), 0);
CHECK_EQ(id_function_data.call_count(), 0);
// Verify that the generic-wrapper budgets for all functions are correct.
CHECK_EQ(add_function_data.wrapper_budget(), 0);
CHECK_EQ(mult_function_data.wrapper_budget(), kGenericWrapperBudget);
CHECK_EQ(id_function_data.wrapper_budget(), kGenericWrapperBudget);
// Verify that the tier up of the add function replaced the wrapper
// for both the add and the mult functions, but not the id function.
......@@ -293,9 +301,9 @@ TEST(EagerWrapperReplacement) {
Handle<Object> result = maybe_result.ToHandleChecked();
CHECK(result->IsSmi() && Smi::ToInt(*result) == expected_value);
}
// Verify that mult's call count is still 0, which means that the call
// Verify that mult's budget is still intact, which means that the call
// didn't go through the generic wrapper.
CHECK_EQ(mult_function_data.call_count(), 0);
CHECK_EQ(mult_function_data.wrapper_budget(), kGenericWrapperBudget);
// Call the id function to verify that the generic wrapper is used.
{
......@@ -308,9 +316,9 @@ TEST(EagerWrapperReplacement) {
Handle<Object> result = maybe_result.ToHandleChecked();
CHECK(result->IsSmi() && Smi::ToInt(*result) == expected_value);
}
// Verify that id's call count increased to 1, which means that the call
// Verify that id's budget decreased by 1, which means that the call
// used the generic wrapper.
CHECK_EQ(id_function_data.call_count(), 1);
CHECK_EQ(id_function_data.wrapper_budget(), kGenericWrapperBudget - 1);
}
Cleanup();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment