Commit 287634a5 authored by Paolo Severini's avatar Paolo Severini Committed by Commit Bot

[test][compiler] Refactor 'inline JStoWasm calls' tests to use %ObserveNode

Modify the cctests for the inlined JS-to-Wasm calls to use the
%ObserveNode intrinsic, to verify that the JSCall node is actually
inlined . This requires a small refactoring of the %ObserveNode
implementation.

Bug: v8:11092
Change-Id: I01727143fec64c6c11c58b1b664f51daae5bfdb6
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2677811
Commit-Queue: Paolo Severini <paolosev@microsoft.com>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72838}
parent e98947a1
...@@ -39,6 +39,10 @@ OptimizedCompilationInfo::OptimizedCompilationInfo( ...@@ -39,6 +39,10 @@ OptimizedCompilationInfo::OptimizedCompilationInfo(
SetTracingFlags(shared->PassesFilter(FLAG_trace_turbo_filter)); SetTracingFlags(shared->PassesFilter(FLAG_trace_turbo_filter));
ConfigureFlags(); ConfigureFlags();
if (isolate->node_observer()) {
SetNodeObserver(isolate->node_observer());
}
} }
OptimizedCompilationInfo::OptimizedCompilationInfo( OptimizedCompilationInfo::OptimizedCompilationInfo(
......
...@@ -20,6 +20,7 @@ void ObserveNodeManager::StartObserving(Node* node, NodeObserver* observer) { ...@@ -20,6 +20,7 @@ void ObserveNodeManager::StartObserving(Node* node, NodeObserver* observer) {
DCHECK_NOT_NULL(observer); DCHECK_NOT_NULL(observer);
DCHECK(observations_.find(node->id()) == observations_.end()); DCHECK(observations_.find(node->id()) == observations_.end());
observer->set_has_observed_changes();
NodeObserver::Observation observation = observer->OnNodeCreated(node); NodeObserver::Observation observation = observer->OnNodeCreated(node);
if (observation == NodeObserver::Observation::kContinue) { if (observation == NodeObserver::Observation::kContinue) {
observations_[node->id()] = observations_[node->id()] =
......
...@@ -75,6 +75,12 @@ class NodeObserver : public ZoneObject { ...@@ -75,6 +75,12 @@ class NodeObserver : public ZoneObject {
const ObservableNodeState& old_state) { const ObservableNodeState& old_state) {
return Observation::kContinue; return Observation::kContinue;
} }
void set_has_observed_changes() { has_observed_changes_ = true; }
bool has_observed_changes() const { return has_observed_changes_; }
private:
bool has_observed_changes_ = false;
}; };
inline NodeObserver::~NodeObserver() = default; inline NodeObserver::~NodeObserver() = default;
......
...@@ -118,6 +118,7 @@ class Interpreter; ...@@ -118,6 +118,7 @@ class Interpreter;
} // namespace interpreter } // namespace interpreter
namespace compiler { namespace compiler {
class NodeObserver;
class PerIsolateCompilerCache; class PerIsolateCompilerCache;
} // namespace compiler } // namespace compiler
...@@ -463,7 +464,8 @@ using DebugObjectCache = std::vector<Handle<HeapObject>>; ...@@ -463,7 +464,8 @@ using DebugObjectCache = std::vector<Handle<HeapObject>>;
V(bool, only_terminate_in_safe_scope, false) \ V(bool, only_terminate_in_safe_scope, false) \
V(bool, detailed_source_positions_for_profiling, FLAG_detailed_line_info) \ V(bool, detailed_source_positions_for_profiling, FLAG_detailed_line_info) \
V(int, embedder_wrapper_type_index, -1) \ V(int, embedder_wrapper_type_index, -1) \
V(int, embedder_wrapper_object_index, -1) V(int, embedder_wrapper_object_index, -1) \
V(compiler::NodeObserver*, node_observer, nullptr)
#define THREAD_LOCAL_TOP_ACCESSOR(type, name) \ #define THREAD_LOCAL_TOP_ACCESSOR(type, name) \
inline void set_##name(type v) { thread_local_top()->name##_ = v; } \ inline void set_##name(type v) { thread_local_top()->name##_ = v; } \
......
...@@ -89,7 +89,6 @@ v8_source_set("cctest_sources") { ...@@ -89,7 +89,6 @@ v8_source_set("cctest_sources") {
"compiler/function-tester.cc", "compiler/function-tester.cc",
"compiler/function-tester.h", "compiler/function-tester.h",
"compiler/graph-builder-tester.h", "compiler/graph-builder-tester.h",
"compiler/node-observer-tester.cc",
"compiler/node-observer-tester.h", "compiler/node-observer-tester.h",
"compiler/serializer-tester.cc", "compiler/serializer-tester.cc",
"compiler/serializer-tester.h", "compiler/serializer-tester.h",
......
...@@ -196,6 +196,10 @@ ...@@ -196,6 +196,10 @@
'test-streaming-compilation/SingleThreadedTestDeserializationFails': [SKIP], 'test-streaming-compilation/SingleThreadedTestDeserializationFails': [SKIP],
'test-streaming-compilation/AsyncTestDeserializationFails': [SKIP], 'test-streaming-compilation/AsyncTestDeserializationFails': [SKIP],
'test-streaming-compilation/AsyncTestDeserializationBypassesCompilation': [SKIP], 'test-streaming-compilation/AsyncTestDeserializationBypassesCompilation': [SKIP],
# %ObserveNode tests relies on TurboFan.
'test-sloppy-equality/*' : [SKIP],
'test-js-to-wasm/*': [SKIP],
}], # variant == nooptimization }], # variant == nooptimization
############################################################################## ##############################################################################
......
// 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.
#include "test/cctest/compiler/node-observer-tester.h"
#include "src/api/api-inl.h"
#include "src/codegen/optimized-compilation-info.h"
#include "src/compiler/pipeline.h"
namespace v8 {
namespace internal {
namespace compiler {
void TestWithObserveNode::OptimizeFunctionWithObserver(
const char* function_name, NodeObserver* observer) {
DCHECK_NOT_NULL(function_name);
DCHECK_NOT_NULL(observer);
Local<Function> api_function = Local<Function>::Cast(
CcTest::global()
->Get(CcTest::isolate()->GetCurrentContext(), v8_str(function_name))
.ToLocalChecked());
Handle<JSFunction> function =
Handle<JSFunction>::cast(v8::Utils::OpenHandle(*api_function));
CHECK(function->shared().HasBytecodeArray());
Handle<SharedFunctionInfo> sfi(function->shared(), isolate_);
IsCompiledScope is_compiled_scope(sfi->is_compiled_scope(isolate_));
JSFunction::EnsureFeedbackVector(function, &is_compiled_scope);
OptimizedCompilationInfo compilation_info(main_zone(), isolate_, sfi,
function, CodeKind::TURBOFAN);
compilation_info.SetNodeObserver(observer);
compilation_info.ReopenHandlesInNewHandleScope(isolate_);
Handle<Code> code =
Pipeline::GenerateCodeForTesting(&compilation_info, isolate_)
.ToHandleChecked();
function->set_code(*code);
}
} // namespace compiler
} // namespace internal
} // namespace v8
...@@ -15,22 +15,28 @@ namespace v8 { ...@@ -15,22 +15,28 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
// Test TurboFan compilation using the %ObserveNode intrinsic. // Helpers to test TurboFan compilation using the %ObserveNode intrinsic.
class TestWithObserveNode : public HandleAndZoneScope { struct ObserveNodeScope {
public: public:
explicit TestWithObserveNode(Isolate* isolate, const char* script) ObserveNodeScope(Isolate* isolate, NodeObserver* node_observer)
: isolate_(isolate), script_(script) { : isolate_(isolate) {
DCHECK_NOT_NULL(isolate_); DCHECK_NOT_NULL(isolate_);
DCHECK_NOT_NULL(script_); DCHECK_NULL(isolate_->node_observer());
CompileRun(script_); isolate_->set_node_observer(node_observer);
} }
void OptimizeFunctionWithObserver(const char* function_name, ~ObserveNodeScope() {
NodeObserver* observer); DCHECK_NOT_NULL(isolate_->node_observer());
// Checks that the code wrapped by %ObserveNode() was actually compiled in
// the test.
CHECK(isolate_->node_observer()->has_observed_changes());
isolate_->set_node_observer(nullptr);
}
private: private:
Isolate* isolate_; Isolate* isolate_;
const char* script_;
}; };
class CreationObserver : public NodeObserver { class CreationObserver : public NodeObserver {
...@@ -53,7 +59,8 @@ class ModificationObserver : public NodeObserver { ...@@ -53,7 +59,8 @@ class ModificationObserver : public NodeObserver {
public: public:
explicit ModificationObserver( explicit ModificationObserver(
std::function<void(const Node*)> on_created_handler, std::function<void(const Node*)> on_created_handler,
std::function<void(const Node*, const ObservableNodeState& old_state)> std::function<NodeObserver::Observation(
const Node*, const ObservableNodeState& old_state)>
on_changed_handler) on_changed_handler)
: on_created_handler_(on_created_handler), : on_created_handler_(on_created_handler),
on_changed_handler_(on_changed_handler) { on_changed_handler_(on_changed_handler) {
...@@ -68,13 +75,13 @@ class ModificationObserver : public NodeObserver { ...@@ -68,13 +75,13 @@ class ModificationObserver : public NodeObserver {
Observation OnNodeChanged(const char* reducer_name, const Node* node, Observation OnNodeChanged(const char* reducer_name, const Node* node,
const ObservableNodeState& old_state) override { const ObservableNodeState& old_state) override {
on_changed_handler_(node, old_state); return on_changed_handler_(node, old_state);
return Observation::kContinue;
} }
private: private:
std::function<void(const Node*)> on_created_handler_; std::function<void(const Node*)> on_created_handler_;
std::function<void(const Node*, const ObservableNodeState& old_state)> std::function<NodeObserver::Observation(const Node*,
const ObservableNodeState& old_state)>
on_changed_handler_; on_changed_handler_;
}; };
......
...@@ -41,10 +41,13 @@ class TestSloppyEqualityFactory { ...@@ -41,10 +41,13 @@ class TestSloppyEqualityFactory {
[created_op](const Node* node) { [created_op](const Node* node) {
CHECK_EQ(created_op, node->opcode()); CHECK_EQ(created_op, node->opcode());
}, },
[modified_op](const Node* node, const ObservableNodeState& old_state) { [modified_op](const Node* node, const ObservableNodeState& old_state)
-> NodeObserver::Observation {
if (old_state.opcode() != node->opcode()) { if (old_state.opcode() != node->opcode()) {
CHECK_EQ(modified_op, node->opcode()); CHECK_EQ(modified_op, node->opcode());
return NodeObserver::Observation::kStop;
} }
return NodeObserver::Observation::kContinue;
}); });
} }
...@@ -122,11 +125,14 @@ TEST(TestSloppyEquality) { ...@@ -122,11 +125,14 @@ TEST(TestSloppyEquality) {
<< "%PrepareFunctionForOptimization(test);\n"; << "%PrepareFunctionForOptimization(test);\n";
for (const auto& args : c.warmup) { for (const auto& args : c.warmup) {
src << "test(" << args.first << ", " << args.second << ");\n" src << "test(" << args.first << ", " << args.second << ");\n"
<< "%OptimizeFunctionOnNextCall(test);"
<< "test(" << args.first << ", " << args.second << ");\n"; << "test(" << args.first << ", " << args.second << ");\n";
} }
TestWithObserveNode tester(isolate, src.str().c_str()); {
tester.OptimizeFunctionWithObserver("test", c.observer); compiler::ObserveNodeScope scope(isolate, c.observer);
CompileRun(src.str().c_str());
}
} }
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "src/api/api.h" #include "src/api/api.h"
#include "src/wasm/wasm-module-builder.h" #include "src/wasm/wasm-module-builder.h"
#include "test/cctest/cctest.h" #include "test/cctest/cctest.h"
#include "test/cctest/compiler/node-observer-tester.h"
#include "test/cctest/test-api.h" #include "test/cctest/test-api.h"
#include "test/common/wasm/flag-utils.h" #include "test/common/wasm/flag-utils.h"
#include "test/common/wasm/test-signatures.h" #include "test/common/wasm/test-signatures.h"
...@@ -48,6 +49,13 @@ struct ExportedFunction { ...@@ -48,6 +49,13 @@ struct ExportedFunction {
FunctionSig* signature; FunctionSig* signature;
std::vector<ValueType> locals; std::vector<ValueType> locals;
std::vector<uint8_t> code; std::vector<uint8_t> code;
bool DoesSignatureContainI64() const {
for (auto type : signature->all()) {
if (type == wasm::kWasmI64) return true;
}
return false;
}
}; };
#define WASM_CODE(...) __VA_ARGS__ #define WASM_CODE(...) __VA_ARGS__
...@@ -246,6 +254,8 @@ DECLARE_EXPORTED_FUNCTION_WITH_LOCALS( ...@@ -246,6 +254,8 @@ DECLARE_EXPORTED_FUNCTION_WITH_LOCALS(
WASM_I32V(kDeoptLoopCount))), WASM_I32V(kDeoptLoopCount))),
WASM_CALL_FUNCTION(0, WASM_F64_SCONVERT_I32(WASM_LOCAL_GET(0))))})) WASM_CALL_FUNCTION(0, WASM_F64_SCONVERT_I32(WASM_LOCAL_GET(0))))}))
enum TestMode { kJSToWasmInliningDisabled, kJSToWasmInliningEnabled };
class FastJSWasmCallTester { class FastJSWasmCallTester {
public: public:
FastJSWasmCallTester() FastJSWasmCallTester()
...@@ -269,6 +279,14 @@ class FastJSWasmCallTester { ...@@ -269,6 +279,14 @@ class FastJSWasmCallTester {
func->Emit(kExprEnd); func->Emit(kExprEnd);
builder_->AddExport(CStrVector(exported_func.name.c_str()), builder_->AddExport(CStrVector(exported_func.name.c_str()),
kExternalFunction, func->func_index()); kExternalFunction, func->func_index());
// JS-to-Wasm inlining is disabled when targeting 32 bits if the Wasm
// function signature contains an I64.
#if defined(V8_TARGET_ARCH_32_BIT)
if (exported_func.DoesSignatureContainI64()) {
test_mode_ = kJSToWasmInliningDisabled;
}
#endif
} }
// Executes a test function that returns a value of type T. // Executes a test function that returns a value of type T.
...@@ -351,9 +369,9 @@ class FastJSWasmCallTester { ...@@ -351,9 +369,9 @@ class FastJSWasmCallTester {
"let module = new WebAssembly.Module(buf);" "let module = new WebAssembly.Module(buf);"
"let instance = new WebAssembly.Instance(module, importObj);" "let instance = new WebAssembly.Instance(module, importObj);"
"function test(value) {" "function test(value) {"
" return instance.exports." + " return %ObserveNode(instance.exports." +
exported_function_name + exported_function_name +
"(value);" "(value));"
"}" "}"
"%PrepareFunctionForOptimization(test);" "%PrepareFunctionForOptimization(test);"
"test(" + "test(" +
...@@ -363,7 +381,8 @@ class FastJSWasmCallTester { ...@@ -363,7 +381,8 @@ class FastJSWasmCallTester {
"test(" + "test(" +
arg + ");"; arg + ");";
v8::Local<v8::Value> result_value = CompileRun(js_code.c_str()); v8::Local<v8::Value> result_value =
CompileRunWithJSWasmCallNodeObserver(js_code.c_str());
CHECK(CheckType<T>(result_value)); CHECK(CheckType<T>(result_value));
T result = ConvertJSValue<T>::Get(result_value, env.local()).ToChecked(); T result = ConvertJSValue<T>::Get(result_value, env.local()).ToChecked();
CHECK_EQ(result, expected_result); CHECK_EQ(result, expected_result);
...@@ -399,9 +418,9 @@ class FastJSWasmCallTester { ...@@ -399,9 +418,9 @@ class FastJSWasmCallTester {
exported_function_name + exported_function_name +
";" ";"
"function test() {" "function test() {"
" return " + " return %ObserveNode(" +
exported_function_name + exported_function_name +
"(arg);" "(arg));"
"}" "}"
"%PrepareFunctionForOptimization(test);" "%PrepareFunctionForOptimization(test);"
"test();"; "test();";
...@@ -410,7 +429,8 @@ class FastJSWasmCallTester { ...@@ -410,7 +429,8 @@ class FastJSWasmCallTester {
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
try_catch.Reset(); try_catch.Reset();
CompileRun("%OptimizeFunctionOnNextCall(test); test();"); CompileRunWithJSWasmCallNodeObserver(
"%OptimizeFunctionOnNextCall(test); test();");
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
} }
...@@ -557,7 +577,39 @@ class FastJSWasmCallTester { ...@@ -557,7 +577,39 @@ class FastJSWasmCallTester {
exported_function_name, args.size()) exported_function_name, args.size())
: GetJSTestCode(WasmModuleAsJSArray(), exported_function_name, : GetJSTestCode(WasmModuleAsJSArray(), exported_function_name,
args.size()); args.size());
return CompileRun(js_code.c_str()); return CompileRunWithJSWasmCallNodeObserver(js_code);
}
v8::Local<v8::Value> CompileRunWithJSWasmCallNodeObserver(
const std::string& js_code) {
compiler::ModificationObserver js_wasm_call_observer(
[](const compiler::Node* node) {
CHECK_EQ(compiler::IrOpcode::kJSCall, node->opcode());
},
[this](const compiler::Node* node,
const compiler::ObservableNodeState& old_state)
-> compiler::NodeObserver::Observation {
if (old_state.opcode() != node->opcode()) {
CHECK_EQ(compiler::IrOpcode::kJSCall, old_state.opcode());
// JS-to-Wasm inlining is disabled when targeting 32 bits if the
// Wasm function signature contains an I64.
if (test_mode_ == kJSToWasmInliningEnabled) {
CHECK_EQ(compiler::IrOpcode::kJSWasmCall, node->opcode());
} else {
CHECK_EQ(compiler::IrOpcode::kCall, node->opcode());
}
return compiler::NodeObserver::Observation::kStop;
}
return compiler::NodeObserver::Observation::kContinue;
});
{
compiler::ObserveNodeScope scope(CcTest::i_isolate(),
&js_wasm_call_observer);
return CompileRun(js_code.c_str());
}
} }
// Format the JS test code that loads and instantiates a Wasm module and // Format the JS test code that loads and instantiates a Wasm module and
...@@ -585,9 +637,9 @@ class FastJSWasmCallTester { ...@@ -585,9 +637,9 @@ class FastJSWasmCallTester {
wasm_exported_function_name + wasm_exported_function_name +
";" ";"
"function test() {" "function test() {"
" let result = " + " let result = %ObserveNode(" +
wasm_exported_function_name + "(" + js_args + wasm_exported_function_name + "(" + js_args +
");" "));"
" return result;" " return result;"
"}" "}"
"%PrepareFunctionForOptimization(test);" "%PrepareFunctionForOptimization(test);"
...@@ -652,10 +704,11 @@ class FastJSWasmCallTester { ...@@ -652,10 +704,11 @@ class FastJSWasmCallTester {
" var result = 0;" " var result = 0;"
" for (let i = 0; i < " + " for (let i = 0; i < " +
std::to_string(kDeoptLoopCount) + " + 5; i++) {"; std::to_string(kDeoptLoopCount) + " + 5; i++) {";
code += bigint_arg ? " result = " + wasm_exported_function_name + "(" + code += bigint_arg
js_args + "+ BigInt(b)) + BigInt(n);" ? " result = %ObserveNode(" + wasm_exported_function_name +
: " result = " + wasm_exported_function_name + "(" + "(" + js_args + " + BigInt(b))) + BigInt(n);"
js_args + "+ b) + n;"; : " result = %ObserveNode(" + wasm_exported_function_name +
"(" + js_args + " + b)) + n;";
code += code +=
" }" " }"
" return result;" " return result;"
...@@ -684,6 +737,7 @@ class FastJSWasmCallTester { ...@@ -684,6 +737,7 @@ class FastJSWasmCallTester {
AccountingAllocator allocator_; AccountingAllocator allocator_;
Zone zone_; Zone zone_;
WasmModuleBuilder* builder_; WasmModuleBuilder* builder_;
TestMode test_mode_ = kJSToWasmInliningEnabled;
}; };
TEST(TestFastJSWasmCall_Nop) { TEST(TestFastJSWasmCall_Nop) {
......
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