v8/test/cctest/compiler/serializer-tester.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

386 lines
12 KiB
C++
Raw Normal View History

// 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* source)
: canonical_(main_isolate()) {
// The tests only make sense in the context of concurrent compilation.
FLAG_concurrent_inlining = true;
// --local-heaps is enabled by default, but some bots disable it.
// Ensure that it is enabled here because we have reverse implication
// from --no-local-heaps to --no-concurrent-inlining.
if (!FLAG_local_heaps) FLAG_local_heaps = 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();
std::string function_string = "(function() { ";
function_string += 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 |
i::OptimizedCompilationInfo::kFunctionContextSpecializing |
i::OptimizedCompilationInfo::kLoopPeeling |
i::OptimizedCompilationInfo::kBailoutOnUninitialized |
i::OptimizedCompilationInfo::kAllocationFolding |
i::OptimizedCompilationInfo::kSplitting |
i::OptimizedCompilationInfo::kAnalyzeEnvironmentLiveness;
Optimize(function, main_zone(), main_isolate(), flags, &broker_);
// Update handle to the corresponding serialized Handle in the broker.
function =
broker_->FindCanonicalPersistentHandleForTesting<JSFunction>(*function);
function_ = JSFunctionRef(broker(), function);
}
TEST(SerializeEmptyFunction) {
SerializerTester tester(
"function f() {}; %EnsureFeedbackVectorForFunction(f); return f;");
JSFunctionRef function = tester.function();
CHECK(tester.broker()->IsSerializedForCompilation(
function.shared(), function.feedback_vector()));
}
// 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* source, int argc = 0,
Handle<Object> argv[] = {}) {
SerializerTester tester(source);
JSFunctionRef f = tester.function();
CHECK(tester.broker()->IsSerializedForCompilation(f.shared(),
f.feedback_vector()));
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(tester.broker(), sfi);
Handle<FeedbackVector> fv(
tester.broker()->FindCanonicalPersistentHandleForTesting<FeedbackVector>(
g_func->feedback_vector()));
FeedbackVectorRef g_fv(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() {};"
[TurboFan] Serializing context information In TurboFan, context specialization is an optimization that tries to either replace the load of a value from the context with a constant, or if that can't be achieved, at least reduce the hops up the context chain by starting a walk to the required depth from the first constant context that it can reach. Currently, this optimization is performed by looking into the heap during a reducer pass. With fully concurrent TurboFan, we need to instead gather information about contexts we may want to perform this optimization on during serialization. This CL adds functionality to the serializer to recognize and model operations that affect the context register. We add to the hinting structure already used by the serializer. There is a new type of hint: a VirtualContext. This is a tuple consisting of a handle to a Context, and a distance field that indicates how far away in a to-be-realized chain this VirtualContext sits from the context in the handle. For example: bytecode stream: ... CreateBlockContext ... After a block context is created, the accumulator now contains a VirtualContext Hint with a distance of 1 from any context hints that we are keeping track of in the current context register. More details in the design doc here: https://docs.google.com/document/d/1Y0LKKCEenLWyAZTetoAIpKTZRCxaNdkYV8X1GaCax2A/edit?usp=sharing Change-Id: I63732ebd106cc138fb1e9789d0676ece63e15d27 Bug: v8:7790 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1605941 Commit-Queue: Michael Stanton <mvstanton@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Maya Lekova <mslekova@chromium.org> Cr-Commit-Position: refs/heads/master@{#62370}
2019-06-26 07:41:57 +00:00
" 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) {
CheckForSerializedInlinee(
"function apply(foo, arg) { return foo(arg); };"
"%EnsureFeedbackVectorForFunction(apply);"
"function test() {"
" const lambda = (a) => a;"
" %EnsureFeedbackVectorForFunction(lambda);"
" let bound = apply.bind(null, lambda).bind(null, 42);"
" %TurbofanStaticAssert(bound() == 42); return apply;"
"};"
"%EnsureFeedbackVectorForFunction(test);"
"test(); return test;");
}
TEST(BoundFunctionArguments) {
CheckForSerializedInlinee(
"function apply(foo, arg) { return foo(arg); };"
"%EnsureFeedbackVectorForFunction(apply);"
"function test() {"
" const lambda = (a) => a;"
" %EnsureFeedbackVectorForFunction(lambda);"
" let bound = apply.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