v8/test/cctest/compiler/serializer-tester.cc
Jakob Gruber 0dba97f8dc [compiler] Make JSFunction bg-serialized
This wraps up the transition away from kSerialized ref kinds.

Since JSFunctionRef is a complex type, we don't attempt full
consistency on the background thread. Instead, we serialize functions
on the background in a partially-racy manner, in which consistency
between different JSFunction fields is *not* guaranteed. Consistency
is later verified through a new compilation dependency kind during
finalization.

Bug: v8:7790, v8:12004
Change-Id: Ic2b78af9c9fe183c8769d323132bb304b151dc75
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2968404
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75789}
2021-07-19 13:47:16 +00:00

387 lines
15 KiB
C++

// Copyright 2019 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.
// Serializer tests don't make sense in lite mode, as it doesn't gather
// IC feedback.
#ifndef V8_LITE_MODE
#include "test/cctest/compiler/serializer-tester.h"
#include "src/api/api-inl.h"
#include "src/codegen/optimized-compilation-info.h"
#include "src/compiler/serializer-for-background-compilation.h"
#include "src/compiler/zone-stats.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace compiler {
SerializerTester::SerializerTester(const char* global_source,
const char* local_source)
: canonical_(main_isolate()) {
// The tests only make sense in the context of concurrent compilation.
FLAG_concurrent_inlining = true;
// The tests don't make sense when optimizations are turned off.
FLAG_opt = true;
// We need the IC to feed it to the serializer.
FLAG_use_ic = true;
// We need manual control over when a given function is optimized.
FLAG_always_opt = false;
// We need allocation of executable memory for the compilation.
FLAG_jitless = false;
FLAG_allow_natives_syntax = true;
FlagList::EnforceFlagImplications();
CompileRun(global_source);
std::string function_string = "(function() { ";
function_string += local_source;
function_string += " })();";
Handle<JSFunction> function = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Local<v8::Function>::Cast(CompileRun(function_string.c_str()))));
uint32_t flags = i::OptimizedCompilationInfo::kInlining;
Optimize(function, main_zone(), main_isolate(), flags, &broker_);
// Update handle to the corresponding serialized Handle in the broker.
function =
broker_->FindCanonicalPersistentHandleForTesting<JSFunction>(*function);
function_ = MakeRef(broker(), function);
DCHECK_NOT_NULL(broker_->dependencies());
}
TEST(SerializeEmptyFunction) {
SerializerTester tester(
"", "function f() {}; %EnsureFeedbackVectorForFunction(f); return f;");
JSFunctionRef function = tester.function();
CHECK(tester.broker()->IsSerializedForCompilation(
function.shared(tester.broker()->dependencies()),
function.feedback_vector(tester.broker()->dependencies())));
}
// This helper function allows for testing whether an inlinee candidate
// was properly serialized. It expects that the top-level function (that is
// run through the SerializerTester) will return its inlinee candidate.
void CheckForSerializedInlinee(const char* global_source,
const char* local_source, int argc = 0,
Handle<Object> argv[] = {}) {
SerializerTester tester(global_source, local_source);
JSFunctionRef f = tester.function();
CHECK(tester.broker()->IsSerializedForCompilation(
f.shared(tester.broker()->dependencies()),
f.feedback_vector(tester.broker()->dependencies())));
MaybeHandle<Object> g_obj = Execution::Call(
tester.isolate(), tester.function().object(),
tester.isolate()->factory()->undefined_value(), argc, argv);
Handle<Object> g;
CHECK(g_obj.ToHandle(&g));
CHECK_WITH_MSG(
g->IsJSFunction(),
"The return value of the outer function must be a function too");
Handle<JSFunction> g_func = Handle<JSFunction>::cast(g);
// Look up corresponding serialized Handles in the broker.
Handle<SharedFunctionInfo> sfi(
tester.broker()
->FindCanonicalPersistentHandleForTesting<SharedFunctionInfo>(
g_func->shared()));
SharedFunctionInfoRef g_sfi = MakeRef(tester.broker(), sfi);
Handle<FeedbackVector> fv(
tester.broker()->FindCanonicalPersistentHandleForTesting<FeedbackVector>(
g_func->feedback_vector()));
FeedbackVectorRef g_fv = MakeRef(tester.broker(), fv);
CHECK(tester.broker()->IsSerializedForCompilation(g_sfi, g_fv));
}
TEST(SerializeInlinedClosure) {
CheckForSerializedInlinee("",
"function f() {"
" function g(){ return g; }"
" %EnsureFeedbackVectorForFunction(g);"
" return g();"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"f(); return f;");
}
TEST(SerializeInlinedFunction) {
CheckForSerializedInlinee("",
"function g() {};"
"%EnsureFeedbackVectorForFunction(g);"
"function f() {"
" g(); return g;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"f(); return f;");
}
TEST(SerializeCallUndefinedReceiver) {
CheckForSerializedInlinee("",
"function g(a,b,c) {};"
"%EnsureFeedbackVectorForFunction(g);"
"function f() {"
" g(1,2,3); return g;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"f(); return f;");
}
TEST(SerializeCallUndefinedReceiver2) {
CheckForSerializedInlinee("",
"function g(a,b) {};"
"%EnsureFeedbackVectorForFunction(g);"
"function f() {"
" g(1,2); return g;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"f(); return f;");
}
TEST(SerializeCallProperty) {
CheckForSerializedInlinee("",
"let obj = {"
" g: function g(a,b,c) {}"
"};"
"%EnsureFeedbackVectorForFunction(obj.g);"
"function f() {"
" obj.g(1,2,3); return obj.g;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"f(); return f;");
}
TEST(SerializeCallProperty2) {
CheckForSerializedInlinee("",
"let obj = {"
" g: function g(a,b) {}"
"};"
"%EnsureFeedbackVectorForFunction(obj.g);"
"function f() {"
" obj.g(1,2); return obj.g;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"f(); return f;");
}
TEST(SerializeCallAnyReceiver) {
CheckForSerializedInlinee("",
"let obj = {"
" g: function g() {}"
"};"
"%EnsureFeedbackVectorForFunction(obj.g);"
"function f() {"
" with(obj) {"
" g(); return g;"
" };"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"f(); return f;");
}
TEST(SerializeCallWithSpread) {
CheckForSerializedInlinee("",
"function g(args) {};"
"%EnsureFeedbackVectorForFunction(g);"
"const arr = [1,2,3];"
"function f() {"
" g(...arr); return g;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"f(); return f;");
}
// The following test causes the CallIC of `g` to turn megamorphic,
// thus allowing us to test if we forward arguments hints (`callee` in this
// example) and correctly serialize the inlining candidate `j`.
TEST(SerializeCallArguments) {
CheckForSerializedInlinee("",
"function g(callee) { callee(); };"
"function h() {};"
"function i() {};"
"%EnsureFeedbackVectorForFunction(g);"
"g(h); g(i);"
"function f() {"
" function j() {};"
" g(j);"
" return j;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"var j = f();"
"%EnsureFeedbackVectorForFunction(j);"
"f(); return f;");
}
TEST(SerializeConstruct) {
CheckForSerializedInlinee("",
"function g() {};"
"%EnsureFeedbackVectorForFunction(g);"
"function f() {"
" new g(); return g;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"f(); return f;");
}
TEST(SerializeConstructWithSpread) {
CheckForSerializedInlinee("",
"function g(a, b, c) {};"
"%EnsureFeedbackVectorForFunction(g);"
"const arr = [1, 2];"
"function f() {"
" new g(0, ...arr); return g;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"f(); return f;");
}
TEST(SerializeConstructSuper) {
CheckForSerializedInlinee("",
"class A {};"
"class B extends A { constructor() { super(); } };"
"%EnsureFeedbackVectorForFunction(A);"
"%EnsureFeedbackVectorForFunction(B);"
"function f() {"
" new B(); return A;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"f(); return f;");
}
TEST(SerializeConditionalJump) {
CheckForSerializedInlinee("",
"function g(callee) { callee(); };"
"function h() {};"
"function i() {};"
"%EnsureFeedbackVectorForFunction(g);"
"let a = true;"
"g(h); g(i);"
"function f() {"
" function q() {};"
" if (a) g(q);"
" return q;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"var q = f();"
"%EnsureFeedbackVectorForFunction(q);"
"f(); return f;");
}
TEST(SerializeUnconditionalJump) {
CheckForSerializedInlinee("",
"function g(callee) { callee(); };"
"function h() {};"
"function i() {};"
"%EnsureFeedbackVectorForFunction(g);"
"%EnsureFeedbackVectorForFunction(h);"
"%EnsureFeedbackVectorForFunction(i);"
"let a = false;"
"g(h); g(i);"
"function f() {"
" function p() {};"
" function q() {};"
" if (a) q();"
" else g(p);"
" return p;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"var p = f();"
"%EnsureFeedbackVectorForFunction(p);"
"f(); return f;");
}
TEST(MergeJumpTargetEnvironment) {
CheckForSerializedInlinee(
"",
"function f() {"
" let g;"
" while (true) {"
" if (g === undefined) {g = ()=>1; break;} else {g = ()=>2; break};"
" };"
" g(); return g;"
"};"
"%EnsureFeedbackVectorForFunction(f);"
"%EnsureFeedbackVectorForFunction(f());"
"f(); return f;"); // Two calls to f to make g() megamorhpic.
}
TEST(BoundFunctionTarget) {
const char* global = "function apply1(foo, arg) { return foo(arg); };";
CheckForSerializedInlinee(
global,
"%EnsureFeedbackVectorForFunction(apply1);"
"function test() {"
" const lambda = (a) => a;"
" %EnsureFeedbackVectorForFunction(lambda);"
" let bound = apply1.bind(null, lambda).bind(null, 42);"
" %TurbofanStaticAssert(bound() == 42); return apply1;"
"};"
"%EnsureFeedbackVectorForFunction(test);"
"test(); return test;");
}
TEST(BoundFunctionArguments) {
const char* global = "function apply2(foo, arg) { return foo(arg); };";
CheckForSerializedInlinee(
global,
"%EnsureFeedbackVectorForFunction(apply2);"
"function test() {"
" const lambda = (a) => a;"
" %EnsureFeedbackVectorForFunction(lambda);"
" let bound = apply2.bind(null, lambda).bind(null, 42);"
" %TurbofanStaticAssert(bound() == 42); return lambda;"
"};"
"%EnsureFeedbackVectorForFunction(test);"
"test(); return test;");
}
TEST(ArrowFunctionInlined) {
// The loop is to ensure there is a feedback vector for the arrow function
// {b}.
CheckForSerializedInlinee("",
"function foo() {"
" let b = x => x * x;"
" let a = [1, 2, 3].map(b);"
" return b;"
"}"
"%EnsureFeedbackVectorForFunction(foo);"
"for (let i = 0; i < 100; ++i) foo();"
"return foo;");
}
TEST(BoundFunctionResult) {
CheckForSerializedInlinee(
"",
"function id(x) { return x }"
"function foo() { id.bind(undefined, 42)(); return id; }"
"%PrepareFunctionForOptimization(foo);"
"%PrepareFunctionForOptimization(id);"
"foo();"
"foo();"
"%OptimizeFunctionOnNextCall(foo);"
"foo(); return foo;");
}
TEST(MultipleFunctionCalls) {
CheckForSerializedInlinee(
"",
"function inc(x) { return ++x; }"
"function dec(x) { return --x; }"
"function apply(f, x) { return f(x); }"
"function foo() { apply(inc, 42); apply(dec, 42); return dec; }"
"%PrepareFunctionForOptimization(inc);"
"%PrepareFunctionForOptimization(dec);"
"%PrepareFunctionForOptimization(apply);"
"%PrepareFunctionForOptimization(foo);"
"foo();"
"foo();"
"%OptimizeFunctionOnNextCall(foo);"
"foo(); return foo;");
}
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_LITE_MODE