Commit 037f7f62 authored by ishell's avatar ishell Committed by Commit bot

[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}
parent 40bdbef9
...@@ -276,6 +276,14 @@ class V8_EXPORT Debug { ...@@ -276,6 +276,14 @@ class V8_EXPORT Debug {
*/ */
static MaybeLocal<Array> GetInternalProperties(Isolate* isolate, static MaybeLocal<Array> GetInternalProperties(Isolate* isolate,
Local<Value> value); 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);
}; };
......
...@@ -8089,6 +8089,15 @@ void Debug::SetLiveEditEnabled(Isolate* isolate, bool enable) { ...@@ -8089,6 +8089,15 @@ void Debug::SetLiveEditEnabled(Isolate* isolate, bool enable) {
internal_isolate->debug()->set_live_edit_enabled(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, MaybeLocal<Array> Debug::GetInternalProperties(Isolate* v8_isolate,
Local<Value> value) { Local<Value> value) {
......
...@@ -927,7 +927,13 @@ void BytecodeGraphBuilder::BuildCall(TailCallMode tail_call_mode) { ...@@ -927,7 +927,13 @@ void BytecodeGraphBuilder::BuildCall(TailCallMode tail_call_mode) {
void BytecodeGraphBuilder::VisitCall() { BuildCall(TailCallMode::kDisallow); } 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() { void BytecodeGraphBuilder::VisitCallJSRuntime() {
FrameStateBeforeAndAfter states(this); FrameStateBeforeAndAfter states(this);
......
...@@ -2885,6 +2885,14 @@ std::string Isolate::GetTurboCfgFileName() { ...@@ -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 // Heap::detached_contexts tracks detached contexts as pairs
// (number of GC since the context was detached, the context). // (number of GC since the context was detached, the context).
......
...@@ -1096,9 +1096,7 @@ class Isolate { ...@@ -1096,9 +1096,7 @@ class Isolate {
bool is_tail_call_elimination_enabled() const { bool is_tail_call_elimination_enabled() const {
return is_tail_call_elimination_enabled_; return is_tail_call_elimination_enabled_;
} }
void set_tail_call_elimination_enabled(bool enabled) { void SetTailCallEliminationEnabled(bool enabled);
is_tail_call_elimination_enabled_ = enabled;
}
void AddDetachedContext(Handle<Context> context); void AddDetachedContext(Handle<Context> context);
void CheckDetachedContextsAfterGC(); void CheckDetachedContextsAfterGC();
......
...@@ -784,7 +784,8 @@ Parser::Parser(ParseInfo* info) ...@@ -784,7 +784,8 @@ Parser::Parser(ParseInfo* info)
DCHECK(!info->script().is_null() || info->source_stream() != NULL); DCHECK(!info->script().is_null() || info->source_stream() != NULL);
set_allow_lazy(info->allow_lazy_parsing()); set_allow_lazy(info->allow_lazy_parsing());
set_allow_natives(FLAG_allow_natives_syntax || info->is_native()); 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(FLAG_harmony_sloppy);
set_allow_harmony_sloppy_function(FLAG_harmony_sloppy_function); set_allow_harmony_sloppy_function(FLAG_harmony_sloppy_function);
set_allow_harmony_sloppy_let(FLAG_harmony_sloppy_let); set_allow_harmony_sloppy_let(FLAG_harmony_sloppy_let);
......
...@@ -8070,3 +8070,60 @@ TEST(BreakLocationIterator) { ...@@ -8070,3 +8070,60 @@ TEST(BreakLocationIterator) {
DisableDebugger(isolate); 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);
}
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