Commit 940efafd authored by jochen's avatar jochen Committed by Commit bot

Teach Scopes whether they will end up being lazily compiled or not

For now keep the logic in compiler.cc and add a DCHECK that the scopes
and compiler.cc agree.

Use this knowledge to only created ScopeInfos for literals we'll
actually compile.

BUG=v8:5394,v8:5422
R=marja@chromium.org,verwaest@chromium.org

Review-Url: https://codereview.chromium.org/2399833002
Cr-Commit-Position: refs/heads/master@{#40074}
parent 78f16b39
......@@ -288,6 +288,13 @@ Token::Value Assignment::binary_op() const {
return Token::ILLEGAL;
}
bool FunctionLiteral::ShouldEagerCompile() const {
return scope()->ShouldEagerCompile();
}
void FunctionLiteral::SetShouldEagerCompile() {
scope()->SetShouldEagerCompile();
}
bool FunctionLiteral::AllowsLazyCompilation() {
return scope()->AllowsLazyCompilation();
......
......@@ -2648,12 +2648,8 @@ class FunctionLiteral final : public Expression {
// function will be called immediately:
// - (function() { ... })();
// - var x = function() { ... }();
bool should_eager_compile() const {
return ShouldEagerCompile::decode(bit_field_);
}
void set_should_eager_compile() {
bit_field_ = ShouldEagerCompile::update(bit_field_, true);
}
bool ShouldEagerCompile() const;
void SetShouldEagerCompile();
// A hint that we expect this function to be called (exactly) once,
// i.e. we suspect it's an initialization function.
......@@ -2734,11 +2730,11 @@ class FunctionLiteral final : public Expression {
HasDuplicateParameters::encode(has_duplicate_parameters ==
kHasDuplicateParameters) |
IsFunction::encode(is_function) |
ShouldEagerCompile::encode(eager_compile_hint == kShouldEagerCompile) |
RequiresClassFieldInit::encode(false) |
ShouldNotBeUsedOnceHintField::encode(false) |
DontOptimizeReasonField::encode(kNoReason) |
IsClassFieldInitializer::encode(false);
if (eager_compile_hint == kShouldEagerCompile) SetShouldEagerCompile();
}
class FunctionTypeBits
......@@ -2746,9 +2742,8 @@ class FunctionLiteral final : public Expression {
class Pretenure : public BitField<bool, FunctionTypeBits::kNext, 1> {};
class HasDuplicateParameters : public BitField<bool, Pretenure::kNext, 1> {};
class IsFunction : public BitField<bool, HasDuplicateParameters::kNext, 1> {};
class ShouldEagerCompile : public BitField<bool, IsFunction::kNext, 1> {};
class ShouldNotBeUsedOnceHintField
: public BitField<bool, ShouldEagerCompile::kNext, 1> {};
: public BitField<bool, IsFunction::kNext, 1> {};
class RequiresClassFieldInit
: public BitField<bool, ShouldNotBeUsedOnceHintField::kNext, 1> {};
class IsClassFieldInitializer
......
......@@ -303,6 +303,7 @@ void Scope::SetDefaults() {
is_declaration_scope_ = false;
is_lazily_parsed_ = false;
should_eager_compile_ = false;
}
bool Scope::HasSimpleParameters() {
......@@ -310,6 +311,22 @@ bool Scope::HasSimpleParameters() {
return !scope->is_function_scope() || scope->has_simple_parameters();
}
bool Scope::ShouldEagerCompile() const {
if (is_declaration_scope() &&
!AsDeclarationScope()->AllowsLazyCompilation()) {
return true;
}
return !is_lazily_parsed_ && should_eager_compile_;
}
void Scope::SetShouldEagerCompile() {
should_eager_compile_ = true;
for (Scope* inner = inner_scope_; inner != nullptr; inner = inner->sibling_) {
if (inner->is_function_scope()) continue;
inner->SetShouldEagerCompile();
}
}
void DeclarationScope::set_asm_module() {
asm_module_ = true;
// Mark any existing inner function scopes as asm function scopes.
......@@ -552,6 +569,9 @@ void DeclarationScope::Analyze(ParseInfo* info, AnalyzeMode mode) {
scope->outer_scope()->scope_type() == SCRIPT_SCOPE ||
scope->outer_scope()->already_resolved_);
// The outer scope is never lazy.
scope->SetShouldEagerCompile();
scope->AllocateVariables(info, mode);
// Ensuring that the outer script scope has a scope info avoids having
......@@ -1415,6 +1435,7 @@ void Scope::Print(int n) {
}
if (inner_scope_calls_eval_) Indent(n1, "// inner scope calls 'eval'\n");
if (is_lazily_parsed_) Indent(n1, "// lazily parsed\n");
if (should_eager_compile_) Indent(n1, "// will be compiled\n");
if (num_stack_slots_ > 0) {
Indent(n1, "// ");
PrintF("%d stack slots\n", num_stack_slots_);
......@@ -1894,7 +1915,21 @@ void Scope::AllocateScopeInfosRecursively(Isolate* isolate, AnalyzeMode mode,
// Allocate ScopeInfos for inner scopes.
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
scope->AllocateScopeInfosRecursively(isolate, mode, next_outer_scope);
AnalyzeMode next_mode = mode;
bool next_eager = should_eager_compile_;
if (scope->is_function_scope()) {
// Make sure all inner scopes have are consistently marked: we can't
// eager compile inner functions of lazy functions, but if a function
// should be eagerly compiled, all its inner scopes are compiled as well.
next_eager = should_eager_compile_ ? scope->ShouldEagerCompile() : false;
// The ScopeIterator which uses the AnalyzeMode::kDebugger only expects
// to find ScopeInfos for the current function and all its inner
// non-function scopes (see ScopeIterator::GetNestedScopeChain).
next_mode = AnalyzeMode::kRegular;
}
scope->should_eager_compile_ = next_eager;
scope->AllocateScopeInfosRecursively(isolate, next_mode, next_outer_scope);
}
}
......
......@@ -419,6 +419,12 @@ class Scope: public ZoneObject {
bool is_lazily_parsed() const { return is_lazily_parsed_; }
bool ShouldEagerCompile() const;
// Marks this scope and all inner scopes (except for inner function scopes)
// such that they get eagerly compiled.
void SetShouldEagerCompile();
protected:
explicit Scope(Zone* zone);
......@@ -447,7 +453,7 @@ class Scope: public ZoneObject {
DCHECK(!already_resolved_);
// A lazily parsed scope doesn't contain enough information to create a
// ScopeInfo from it.
if (is_lazily_parsed_) return false;
if (!ShouldEagerCompile()) return false;
// The debugger expects all functions to have scope infos.
// TODO(jochen|yangguo): Remove this requirement.
if (is_function_scope()) return true;
......@@ -523,6 +529,7 @@ class Scope: public ZoneObject {
bool is_declaration_scope_ : 1;
bool is_lazily_parsed_ : 1;
bool should_eager_compile_ : 1;
// Create a non-local variable with a given name.
// These variables are looked up dynamically at runtime.
......
......@@ -32,6 +32,20 @@ PARSE_INFO_GETTER(Handle<SharedFunctionInfo>, shared_info)
#undef PARSE_INFO_GETTER
#undef PARSE_INFO_GETTER_WITH_DEFAULT
bool CompilationInfo::is_debug() const {
return parse_info() ? parse_info()->is_debug() : false;
}
void CompilationInfo::set_is_debug() {
CHECK(parse_info());
parse_info()->set_is_debug();
}
void CompilationInfo::PrepareForSerializing() {
if (parse_info()) parse_info()->set_will_serialize();
SetFlag(kSerializing);
}
bool CompilationInfo::has_shared_info() const {
return parse_info_ && !parse_info_->shared_info().is_null();
}
......
......@@ -39,7 +39,7 @@ class CompilationInfo final {
kRequiresFrame = 1 << 3,
kMustNotHaveEagerFrame = 1 << 4,
kDeoptimizationSupport = 1 << 5,
kDebug = 1 << 6,
kAccessorInliningEnabled = 1 << 6,
kSerializing = 1 << 7,
kFunctionContextSpecializing = 1 << 8,
kFrameSpecializing = 1 << 9,
......@@ -52,7 +52,6 @@ class CompilationInfo final {
kBailoutOnUninitialized = 1 << 16,
kOptimizeFromBytecode = 1 << 17,
kTypeFeedbackEnabled = 1 << 18,
kAccessorInliningEnabled = 1 << 19,
};
CompilationInfo(ParseInfo* parse_info, Handle<JSFunction> closure);
......@@ -122,13 +121,13 @@ class CompilationInfo final {
// Inner functions that cannot be compiled w/o context are compiled eagerly.
// Always include deoptimization support to avoid having to recompile again.
void MarkAsDebug() {
SetFlag(kDebug);
set_is_debug();
SetFlag(kDeoptimizationSupport);
}
bool is_debug() const { return GetFlag(kDebug); }
bool is_debug() const;
void PrepareForSerializing() { SetFlag(kSerializing); }
void PrepareForSerializing();
bool will_serialize() const { return GetFlag(kSerializing); }
......@@ -346,6 +345,8 @@ class CompilationInfo final {
bool GetFlag(Flag flag) const { return (flags_ & flag) != 0; }
void set_is_debug();
unsigned flags_;
Code::Flags code_flags_;
......
......@@ -1394,6 +1394,10 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) {
}
}
// When we call PrepareForSerializing below, we will change the shared
// ParseInfo. Make sure to reset it.
bool old_will_serialize_value = info->parse_info()->will_serialize();
// If the current code has reloc info for serialization, also include
// reloc info for serialization for the new code, so that deopt support
// can be added without losing IC state.
......@@ -1404,6 +1408,8 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) {
EnsureFeedbackMetadata(&unoptimized);
if (!FullCodeGenerator::MakeCode(&unoptimized)) return false;
info->parse_info()->set_will_serialize(old_will_serialize_value);
// TODO(4280): For now we play it safe and remove the bytecode array when we
// switch to baseline code. We might consider keeping around the bytecode so
// that it can be used as the "source of truth" eventually. Note that this
......@@ -1764,31 +1770,12 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
if (outer_info->will_serialize()) info.PrepareForSerializing();
if (outer_info->is_debug()) info.MarkAsDebug();
// Determine if the function can be lazily compiled. This is necessary to
// allow some of our builtin JS files to be lazily compiled. These
// builtins cannot be handled lazily by the parser, since we have to know
// if a function uses the special natives syntax, which is something the
// parser records.
// If the debugger requests compilation for break points, we cannot be
// aggressive about lazy compilation, because it might trigger compilation
// of functions without an outer context when setting a breakpoint through
// Debug::FindSharedFunctionInfoInScript.
bool allow_lazy = literal->AllowsLazyCompilation() && !info.is_debug();
bool lazy = FLAG_lazy && allow_lazy && !literal->should_eager_compile();
// Consider compiling eagerly when targeting the code cache.
lazy &= !(FLAG_serialize_eager && info.will_serialize());
// Consider compiling eagerly when compiling bytecode for Ignition.
lazy &=
!(FLAG_ignition && FLAG_ignition_eager && !isolate->serializer_enabled());
// Generate code
TimerEventScope<TimerEventCompileCode> timer(isolate);
RuntimeCallTimerScope runtimeTimer(isolate, &RuntimeCallStats::CompileCode);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileCode");
if (lazy) {
if (!literal->ShouldEagerCompile()) {
info.SetCode(isolate->builtins()->CompileLazy());
Scope* outer_scope = literal->scope()->GetOuterScopeWithContext();
if (outer_scope) {
......@@ -1798,8 +1785,7 @@ Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfo(
// Code generation will ensure that the feedback vector is present and
// appropriately sized.
DCHECK(!info.code().is_null());
if (literal->should_eager_compile() &&
literal->should_be_used_once_hint()) {
if (literal->should_be_used_once_hint()) {
info.code()->MarkToBeExecutedOnce(isolate);
}
// Update the shared function info with the scope info.
......
......@@ -56,6 +56,8 @@ class ParseInfo {
FLAG_ACCESSOR(kIsNamedExpression, is_named_expression,
set_is_named_expression)
FLAG_ACCESSOR(kCallsEval, calls_eval, set_calls_eval)
FLAG_ACCESSOR(kDebug, is_debug, set_is_debug)
FLAG_ACCESSOR(kSerializing, will_serialize, set_will_serialize)
#undef FLAG_ACCESSOR
......@@ -204,8 +206,10 @@ class ParseInfo {
kAllowLazyParsing = 1 << 7,
kIsNamedExpression = 1 << 8,
kCallsEval = 1 << 9,
kDebug = 1 << 10,
kSerializing = 1 << 11,
// ---------- Output flags --------------------------
kAstValueFactoryOwned = 1 << 10
kAstValueFactoryOwned = 1 << 12
};
//------------- Inputs to parsing and scope analysis -----------------------
......
......@@ -190,6 +190,7 @@ class ParserBase {
classifier_(nullptr),
scanner_(scanner),
stack_overflow_(false),
default_eager_compile_hint_(FunctionLiteral::kShouldLazyCompile),
allow_lazy_(false),
allow_natives_(false),
allow_tailcalls_(false),
......@@ -224,26 +225,28 @@ class ParserBase {
void set_stack_limit(uintptr_t stack_limit) { stack_limit_ = stack_limit; }
void set_default_eager_compile_hint(
FunctionLiteral::EagerCompileHint eager_compile_hint) {
default_eager_compile_hint_ = eager_compile_hint;
}
FunctionLiteral::EagerCompileHint default_eager_compile_hint() const {
return default_eager_compile_hint_;
}
Zone* zone() const { return zone_; }
protected:
friend class v8::internal::ExpressionClassifier<ParserTypes<Impl>>;
// clang-format off
enum AllowRestrictedIdentifiers {
kAllowRestrictedIdentifiers,
kDontAllowRestrictedIdentifiers
};
enum Mode {
PARSE_LAZILY,
PARSE_EAGERLY
};
enum Mode { PARSE_LAZILY, PARSE_EAGERLY };
enum LazyParsingResult {
kLazyParsingComplete,
kLazyParsingAborted
};
enum LazyParsingResult { kLazyParsingComplete, kLazyParsingAborted };
enum VariableDeclarationContext {
kStatementListItem,
......@@ -251,11 +254,7 @@ class ParserBase {
kForStatement
};
enum class FunctionBodyType {
kNormal,
kSingleExpression
};
// clang-format on
enum class FunctionBodyType { kNormal, kSingleExpression };
class Checkpoint;
class ClassLiteralChecker;
......@@ -1444,6 +1443,8 @@ class ParserBase {
Scanner* scanner_;
bool stack_overflow_;
FunctionLiteral::EagerCompileHint default_eager_compile_hint_;
bool allow_lazy_;
bool allow_natives_;
bool allow_tailcalls_;
......@@ -2288,8 +2289,8 @@ ParserBase<Impl>::ParseClassFieldForInitializer(bool has_initializer,
initializer_state.materialized_literal_count(),
initializer_state.expected_property_count(), 0,
FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::kAnonymousExpression,
FunctionLiteral::kShouldLazyCompile, initializer_scope->start_position());
FunctionLiteral::kAnonymousExpression, default_eager_compile_hint_,
initializer_scope->start_position());
function_literal->set_is_class_field_initializer(true);
return function_literal;
}
......@@ -3083,7 +3084,7 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) {
// be called immediately. If we happen to have parsed a preceding
// function literal eagerly, we can also compile it eagerly.
if (result->IsFunctionLiteral() && mode() == PARSE_EAGERLY) {
result->AsFunctionLiteral()->set_should_eager_compile();
result->AsFunctionLiteral()->SetShouldEagerCompile();
}
}
Scanner::Location spread_pos;
......@@ -3415,7 +3416,7 @@ ParserBase<Impl>::ParseMemberExpressionContinuation(ExpressionT expression,
if (expression->IsFunctionLiteral() && mode() == PARSE_EAGERLY) {
// If the tag function looks like an IIFE, set_parenthesized() to
// force eager compilation.
expression->AsFunctionLiteral()->set_should_eager_compile();
expression->AsFunctionLiteral()->SetShouldEagerCompile();
}
}
expression = ParseTemplateLiteral(expression, pos, CHECK_OK);
......@@ -3920,7 +3921,7 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
FunctionKind kind = formal_parameters.scope->function_kind();
FunctionLiteral::EagerCompileHint eager_compile_hint =
FunctionLiteral::kShouldLazyCompile;
default_eager_compile_hint_;
bool should_be_used_once_hint = false;
{
FunctionState function_state(&function_state_, &scope_state_,
......@@ -4692,7 +4693,7 @@ ParserBase<Impl>::ParseExpressionOrLabelledStatement(
// Identifier ':' Statement
//
// ExpressionStatement[Yield] :
// [lookahead {{, function, class, let [}] Expression[In, ?Yield] ;
// [lookahead notin {{, function, class, let [}] Expression[In, ?Yield] ;
int pos = peek_position();
......
......@@ -284,8 +284,7 @@ FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name,
name, function_scope, body, materialized_literal_count,
expected_property_count, parameter_count,
FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::kAnonymousExpression,
FunctionLiteral::kShouldLazyCompile, pos);
FunctionLiteral::kAnonymousExpression, default_eager_compile_hint(), pos);
function_literal->set_requires_class_field_init(requires_class_field_init);
......@@ -646,6 +645,27 @@ Parser::Parser(ParseInfo* info)
// ParseInfo during background parsing.
DCHECK(!info->script().is_null() || info->source_stream() != nullptr ||
info->character_stream() != nullptr);
// Determine if functions can be lazily compiled. This is necessary to
// allow some of our builtin JS files to be lazily compiled. These
// builtins cannot be handled lazily by the parser, since we have to know
// if a function uses the special natives syntax, which is something the
// parser records.
// If the debugger requests compilation for break points, we cannot be
// aggressive about lazy compilation, because it might trigger compilation
// of functions without an outer context when setting a breakpoint through
// Debug::FindSharedFunctionInfoInScript
bool can_compile_lazily = FLAG_lazy && !info->is_debug();
// Consider compiling eagerly when targeting the code cache.
can_compile_lazily &= !(FLAG_serialize_eager && info->will_serialize());
// Consider compiling eagerly when compiling bytecode for Ignition.
can_compile_lazily &= !(FLAG_ignition && FLAG_ignition_eager &&
info->isolate()->serializer_enabled());
set_default_eager_compile_hint(can_compile_lazily
? FunctionLiteral::kShouldLazyCompile
: FunctionLiteral::kShouldEagerCompile);
set_allow_lazy(FLAG_lazy && info->allow_lazy_parsing() &&
!info->is_native() && info->extension() == nullptr);
set_allow_natives(FLAG_allow_natives_syntax || info->is_native());
......@@ -2566,7 +2586,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
FunctionLiteral::EagerCompileHint eager_compile_hint =
function_state_->next_function_is_parenthesized()
? FunctionLiteral::kShouldEagerCompile
: FunctionLiteral::kShouldLazyCompile;
: default_eager_compile_hint();
// Determine if the function can be parsed lazily. Lazy parsing is
// different from lazy compilation; we need to parse more eagerly than we
......
......@@ -313,7 +313,7 @@ class PreParserExpression {
// More dummy implementations of things PreParser doesn't need to track:
void set_index(int index) {} // For YieldExpressions
void set_should_eager_compile() {}
void SetShouldEagerCompile() {}
void set_should_be_used_once_hint() {}
int position() const { return kNoSourcePosition; }
......
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