[api] Add a switch that controls if ES2015 tail call elimination feature is enabled or not.

BUG=v8:4698
LOG=N
TBR=rossberg@chromium.org

Review URL: https://codereview.chromium.org/1842763002

Cr-Commit-Position: refs/heads/master@{#35132}
This commit is contained in:
ishell 2016-03-30 04:04:17 -07:00 committed by Commit bot
parent 40bdbef975
commit 037f7f6215
7 changed files with 92 additions and 5 deletions

View File

@ -276,6 +276,14 @@ class V8_EXPORT Debug {
*/
static MaybeLocal<Array> GetInternalProperties(Isolate* isolate,
Local<Value> value);
/**
* Defines if the ES2015 tail call elimination feature is enabled or not.
* The change of this flag triggers deoptimization of all functions that
* contain calls at tail position.
*/
static bool IsTailCallEliminationEnabled(Isolate* isolate);
static void SetTailCallEliminationEnabled(Isolate* isolate, bool enabled);
};

View File

@ -8089,6 +8089,15 @@ void Debug::SetLiveEditEnabled(Isolate* isolate, bool enable) {
internal_isolate->debug()->set_live_edit_enabled(enable);
}
bool Debug::IsTailCallEliminationEnabled(Isolate* isolate) {
i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
return internal_isolate->is_tail_call_elimination_enabled();
}
void Debug::SetTailCallEliminationEnabled(Isolate* isolate, bool enabled) {
i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
internal_isolate->SetTailCallEliminationEnabled(enabled);
}
MaybeLocal<Array> Debug::GetInternalProperties(Isolate* v8_isolate,
Local<Value> value) {

View File

@ -927,7 +927,13 @@ void BytecodeGraphBuilder::BuildCall(TailCallMode tail_call_mode) {
void BytecodeGraphBuilder::VisitCall() { BuildCall(TailCallMode::kDisallow); }
void BytecodeGraphBuilder::VisitTailCall() { BuildCall(TailCallMode::kAllow); }
void BytecodeGraphBuilder::VisitTailCall() {
TailCallMode tail_call_mode =
bytecode_array_->GetIsolate()->is_tail_call_elimination_enabled()
? TailCallMode::kAllow
: TailCallMode::kDisallow;
BuildCall(tail_call_mode);
}
void BytecodeGraphBuilder::VisitCallJSRuntime() {
FrameStateBeforeAndAfter states(this);

View File

@ -2885,6 +2885,14 @@ std::string Isolate::GetTurboCfgFileName() {
}
}
void Isolate::SetTailCallEliminationEnabled(bool enabled) {
if (is_tail_call_elimination_enabled_ == enabled) return;
is_tail_call_elimination_enabled_ = enabled;
// TODO(ishell): Introduce DependencyGroup::kTailCallChangedGroup to
// deoptimize only those functions that are affected by the change of this
// flag.
internal::Deoptimizer::DeoptimizeAll(this);
}
// Heap::detached_contexts tracks detached contexts as pairs
// (number of GC since the context was detached, the context).

View File

@ -1096,9 +1096,7 @@ class Isolate {
bool is_tail_call_elimination_enabled() const {
return is_tail_call_elimination_enabled_;
}
void set_tail_call_elimination_enabled(bool enabled) {
is_tail_call_elimination_enabled_ = enabled;
}
void SetTailCallEliminationEnabled(bool enabled);
void AddDetachedContext(Handle<Context> context);
void CheckDetachedContextsAfterGC();

View File

@ -784,7 +784,8 @@ Parser::Parser(ParseInfo* info)
DCHECK(!info->script().is_null() || info->source_stream() != NULL);
set_allow_lazy(info->allow_lazy_parsing());
set_allow_natives(FLAG_allow_natives_syntax || info->is_native());
set_allow_tailcalls(FLAG_harmony_tailcalls && !info->is_native());
set_allow_tailcalls(FLAG_harmony_tailcalls && !info->is_native() &&
info->isolate()->is_tail_call_elimination_enabled());
set_allow_harmony_sloppy(FLAG_harmony_sloppy);
set_allow_harmony_sloppy_function(FLAG_harmony_sloppy_function);
set_allow_harmony_sloppy_let(FLAG_harmony_sloppy_let);

View File

@ -8070,3 +8070,60 @@ TEST(BreakLocationIterator) {
DisableDebugger(isolate);
}
TEST(DisableTailCallElimination) {
i::FLAG_allow_natives_syntax = true;
i::FLAG_harmony_tailcalls = true;
// TODO(ishell, 4698): Investigate why TurboFan in --always-opt mode makes
// stack[2].getFunctionName() return null.
i::FLAG_turbo_inlining = false;
DebugLocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
CHECK(v8::Debug::IsTailCallEliminationEnabled(isolate));
CompileRun(
"'use strict'; \n"
"Error.prepareStackTrace = (error,stack) => { \n"
" error.strace = stack; \n"
" return error.message + \"\\n at \" + stack.join(\"\\n at \"); \n"
"} \n"
" \n"
"function getCaller() { \n"
" var e = new Error(); \n"
" e.stack; // prepare stack trace \n"
" var stack = e.strace; \n"
" %GlobalPrint('caller: '); \n"
" %GlobalPrint(stack[2].getFunctionName()); \n"
" %GlobalPrint('\\n'); \n"
" return stack[2].getFunctionName(); \n"
"} \n"
"function f() { \n"
" var caller = getCaller(); \n"
" if (caller === 'g') return 1; \n"
" if (caller === 'h') return 2; \n"
" return 0; \n"
"} \n"
"function g() { \n"
" return f(); \n"
"} \n"
"function h() { \n"
" var result = g(); \n"
" return result; \n"
"} \n"
"%NeverOptimizeFunction(getCaller); \n"
"%NeverOptimizeFunction(f); \n"
"%NeverOptimizeFunction(h); \n"
"");
ExpectInt32("h();", 2);
ExpectInt32("h(); %OptimizeFunctionOnNextCall(g); h();", 2);
v8::Debug::SetTailCallEliminationEnabled(isolate, false);
CHECK(!v8::Debug::IsTailCallEliminationEnabled(isolate));
ExpectInt32("h();", 1);
ExpectInt32("h(); %OptimizeFunctionOnNextCall(g); h();", 1);
v8::Debug::SetTailCallEliminationEnabled(isolate, true);
CHECK(v8::Debug::IsTailCallEliminationEnabled(isolate));
ExpectInt32("h();", 2);
ExpectInt32("h(); %OptimizeFunctionOnNextCall(g); h();", 2);
}