[wasm] Count calls to exported functions

This CL adds a call counter in the WasmExportedFunctionData. The counter
is incremented every time a call to an exported WebAssembly function is
handled through the generic js-to-wasm wrapper.

Bug: v8:10982
Change-Id: Iad40b414b0c7d2f4ab340ff4ebb7b24c60b3a974
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2445873
Commit-Queue: Vicky Kontoura <vkont@google.com>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70336}
This commit is contained in:
Vicky Kontoura 2020-10-06 09:46:56 +00:00 committed by Commit Bot
parent 086eecbd96
commit cc5498572c
9 changed files with 106 additions and 1 deletions

View File

@ -3362,6 +3362,14 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
MemOperand(function_data,
WasmExportedFunctionData::kInstanceOffset - kHeapObjectTag));
// -------------------------------------------
// Increment the call count in function data.
// -------------------------------------------
__ SmiAddConstant(
MemOperand(function_data,
WasmExportedFunctionData::kCallCountOffset - kHeapObjectTag),
Smi::FromInt(1));
// -------------------------------------------
// Load values from the signature.
// -------------------------------------------

View File

@ -337,7 +337,8 @@ class SharedFunctionInfo : public HeapObject {
UncompiledDataWithPreparseData data);
inline bool HasUncompiledDataWithoutPreparseData() const;
inline bool HasWasmExportedFunctionData() const;
WasmExportedFunctionData wasm_exported_function_data() const;
V8_EXPORT_PRIVATE WasmExportedFunctionData
wasm_exported_function_data() const;
inline bool HasWasmJSFunctionData() const;
WasmJSFunctionData wasm_js_function_data() const;
inline bool HasWasmCapiFunctionData() const;

View File

@ -329,6 +329,7 @@ ACCESSORS(WasmExportedFunctionData, instance, WasmInstanceObject,
SMI_ACCESSORS(WasmExportedFunctionData, jump_table_offset,
kJumpTableOffsetOffset)
SMI_ACCESSORS(WasmExportedFunctionData, function_index, kFunctionIndexOffset)
SMI_ACCESSORS(WasmExportedFunctionData, call_count, kCallCountOffset)
ACCESSORS(WasmExportedFunctionData, c_wrapper_code, Object, kCWrapperCodeOffset)
ACCESSORS(WasmExportedFunctionData, wasm_call_target, Object,
kWasmCallTargetOffset)

View File

@ -1886,6 +1886,7 @@ Handle<WasmExportedFunction> WasmExportedFunction::New(
function_data->set_instance(*instance);
function_data->set_jump_table_offset(jump_table_offset);
function_data->set_function_index(func_index);
function_data->set_call_count(0);
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);

View File

@ -783,6 +783,7 @@ class WasmExportedFunctionData : public Struct {
DECL_ACCESSORS(instance, WasmInstanceObject)
DECL_INT_ACCESSORS(jump_table_offset)
DECL_INT_ACCESSORS(function_index)
DECL_INT_ACCESSORS(call_count)
DECL_ACCESSORS(c_wrapper_code, Object)
DECL_ACCESSORS(wasm_call_target, Object)
DECL_INT_ACCESSORS(packed_args_size)

View File

@ -15,6 +15,7 @@ extern class WasmExportedFunctionData extends Struct {
instance: WasmInstanceObject;
jump_table_offset: Smi;
function_index: Smi;
call_count: 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;

View File

@ -303,6 +303,7 @@ v8_source_set("cctest_sources") {
"wasm/test-run-wasm-simd-liftoff.cc",
"wasm/test-run-wasm-simd-scalar-lowering.cc",
"wasm/test-run-wasm-simd.cc",
"wasm/test-run-wasm-wrappers.cc",
"wasm/test-run-wasm.cc",
"wasm/test-streaming-compilation.cc",
"wasm/test-wasm-breakpoints.cc",

View File

@ -517,6 +517,7 @@
'test-run-wasm-simd-liftoff/*': [SKIP],
'test-run-wasm-simd-scalar-lowering/*': [SKIP],
'test-run-wasm-simd/*': [SKIP],
'test-run-wasm-wrappers/*': [SKIP],
'test-streaming-compilation/*': [SKIP],
'test-wasm-breakpoints/*': [SKIP],
'test-wasm-codegen/*': [SKIP],

View File

@ -0,0 +1,90 @@
// Copyright 2020 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.
#include "src/wasm/wasm-module-builder.h"
#include "src/wasm/wasm-objects-inl.h"
#include "test/cctest/cctest.h"
#include "test/common/wasm/flag-utils.h"
#include "test/common/wasm/test-signatures.h"
#include "test/common/wasm/wasm-macro-gen.h"
#include "test/common/wasm/wasm-module-runner.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace test_run_wasm_wrappers {
using testing::CompileAndInstantiateForTesting;
#ifdef V8_TARGET_ARCH_X64
namespace {
void Cleanup() {
// By sending a low memory notifications, we will try hard to collect all
// garbage and will therefore also invoke all weak callbacks of actually
// unreachable persistent handles.
Isolate* isolate = CcTest::InitIsolateOnce();
reinterpret_cast<v8::Isolate*>(isolate)->LowMemoryNotification();
}
} // namespace
TEST(CallCounter) {
{
// This test assumes use of the generic wrapper.
FlagScope<bool> use_wasm_generic_wrapper(&FLAG_wasm_generic_wrapper, true);
TestSignatures sigs;
AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
// Define the Wasm function.
WasmModuleBuilder* builder = zone.New<WasmModuleBuilder>(&zone);
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_ii());
f->builder()->AddExport(CStrVector("main"), f);
byte code[] = {WASM_I32_MUL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
WASM_END};
f->EmitCode(code, sizeof(code));
// Compile module.
ZoneBuffer buffer(&zone);
builder->WriteTo(&buffer);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
testing::SetupIsolateForWasmModule(isolate);
ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
MaybeHandle<WasmInstanceObject> instance = CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
MaybeHandle<WasmExportedFunction> maybe_export =
testing::GetExportedFunction(isolate, instance.ToHandleChecked(),
"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);
// Call the exported Wasm function and get the result.
Handle<Object> params[2] = {Handle<Object>(Smi::FromInt(6), isolate),
Handle<Object>(Smi::FromInt(7), isolate)};
static const int32_t kExpectedValue = 42;
Handle<Object> receiver = isolate->factory()->undefined_value();
MaybeHandle<Object> maybe_result =
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(result->IsSmi() && Smi::ToInt(*result) == kExpectedValue);
}
Cleanup();
}
#endif
} // namespace test_run_wasm_wrappers
} // namespace wasm
} // namespace internal
} // namespace v8