v8/src/interpreter/bytecode-generator.h
Santiago Aboy Solanes 18b4b6b93c [interpreter] Merge nested loops that share the same header offset
This CL merges nested loops that share the same header offset with its
parent loop, by not emitting JumpLoop bytecode for these inner loops.
Instead, we generate a Jump to its parent's JumpToHeader (which in
turn can be a JumpLoop or another Jump to its parent's JumpToHeader).

Originally, every loop had a unique first Bytecode to jump to. Since
IterationBody StackChecks are going to become implicit this will no
longer be the case.

As a note, this CL just sets the foundation that the follow-up CLs
will build on top of. Since we have explicit StackChecks, and they
are at the beginning of loops we do not have nested loops as of now.

Bug: v8:10149, v8:9960
Change-Id: I6daee4d2c6d6216f022228c87c4aa74e163997b2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2062390
Commit-Queue: Santiago Aboy Solanes <solanes@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66626}
2020-03-09 12:41:27 +00:00

546 lines
22 KiB
C++

// Copyright 2015 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.
#ifndef V8_INTERPRETER_BYTECODE_GENERATOR_H_
#define V8_INTERPRETER_BYTECODE_GENERATOR_H_
#include "src/ast/ast.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-register.h"
#include "src/interpreter/bytecodes.h"
#include "src/objects/feedback-vector.h"
#include "src/objects/function-kind.h"
namespace v8 {
namespace internal {
class AstNodeSourceRanges;
class AstStringConstants;
class BytecodeArray;
class UnoptimizedCompilationInfo;
enum class SourceRangeKind;
namespace interpreter {
class TopLevelDeclarationsBuilder;
class LoopBuilder;
class BlockCoverageBuilder;
class BytecodeJumpTable;
class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
public:
explicit BytecodeGenerator(
UnoptimizedCompilationInfo* info,
const AstStringConstants* ast_string_constants,
std::vector<FunctionLiteral*>* eager_inner_literals);
void GenerateBytecode(uintptr_t stack_limit);
template <typename LocalIsolate>
Handle<BytecodeArray> FinalizeBytecode(LocalIsolate* isolate,
Handle<Script> script);
template <typename LocalIsolate>
Handle<ByteArray> FinalizeSourcePositionTable(LocalIsolate* isolate);
#ifdef DEBUG
int CheckBytecodeMatches(BytecodeArray bytecode);
#endif
#define DECLARE_VISIT(type) void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
// Visiting function for declarations list and statements are overridden.
void VisitModuleDeclarations(Declaration::List* declarations);
void VisitGlobalDeclarations(Declaration::List* declarations);
void VisitDeclarations(Declaration::List* declarations);
void VisitStatements(const ZonePtrList<Statement>* statments);
private:
class AccumulatorPreservingScope;
class ContextScope;
class ControlScope;
class ControlScopeForBreakable;
class ControlScopeForIteration;
class ControlScopeForTopLevel;
class ControlScopeForTryCatch;
class ControlScopeForTryFinally;
class CurrentScope;
class EffectResultScope;
class ExpressionResultScope;
class FeedbackSlotCache;
class IteratorRecord;
class LoopScope;
class NaryCodeCoverageSlots;
class OptionalChainNullLabelScope;
class RegisterAllocationScope;
class TestResultScope;
class TopLevelDeclarationsBuilder;
class ValueResultScope;
using ToBooleanMode = BytecodeArrayBuilder::ToBooleanMode;
enum class TestFallthrough { kThen, kElse, kNone };
enum class TypeHint { kAny, kBoolean, kString };
enum class AccumulatorPreservingMode { kNone, kPreserve };
// An assignment has to evaluate its LHS before its RHS, but has to assign to
// the LHS after both evaluations are done. This class stores the data
// computed in the LHS evaulation that has to live across the RHS evaluation,
// and is used in the actual LHS assignment.
class AssignmentLhsData {
public:
static AssignmentLhsData NonProperty(Expression* expr);
static AssignmentLhsData NamedProperty(Expression* object_expr,
Register object,
const AstRawString* name);
static AssignmentLhsData KeyedProperty(Register object, Register key);
static AssignmentLhsData PrivateMethodOrAccessor(AssignType type,
Property* property);
static AssignmentLhsData NamedSuperProperty(
RegisterList super_property_args);
static AssignmentLhsData KeyedSuperProperty(
RegisterList super_property_args);
AssignType assign_type() const { return assign_type_; }
Expression* expr() const {
DCHECK(assign_type_ == NON_PROPERTY || assign_type_ == PRIVATE_METHOD ||
assign_type_ == PRIVATE_GETTER_ONLY ||
assign_type_ == PRIVATE_SETTER_ONLY ||
assign_type_ == PRIVATE_GETTER_AND_SETTER);
return expr_;
}
Expression* object_expr() const {
DCHECK_EQ(assign_type_, NAMED_PROPERTY);
return object_expr_;
}
Register object() const {
DCHECK(assign_type_ == NAMED_PROPERTY || assign_type_ == KEYED_PROPERTY);
return object_;
}
Register key() const {
DCHECK(assign_type_ == KEYED_PROPERTY);
return key_;
}
const AstRawString* name() const {
DCHECK(assign_type_ == NAMED_PROPERTY);
return name_;
}
RegisterList super_property_args() const {
DCHECK(assign_type_ == NAMED_SUPER_PROPERTY ||
assign_type_ == KEYED_SUPER_PROPERTY);
return super_property_args_;
}
private:
AssignmentLhsData(AssignType assign_type, Expression* expr,
RegisterList super_property_args, Register object,
Register key, Expression* object_expr,
const AstRawString* name)
: assign_type_(assign_type),
expr_(expr),
super_property_args_(super_property_args),
object_(object),
key_(key),
object_expr_(object_expr),
name_(name) {}
AssignType assign_type_;
// Different assignment types use different fields:
//
// NON_PROPERTY: expr
// NAMED_PROPERTY: object_expr, object, name
// KEYED_PROPERTY, PRIVATE_METHOD: object, key
// NAMED_SUPER_PROPERTY: super_property_args
// KEYED_SUPER_PROPERT: super_property_args
Expression* expr_;
RegisterList super_property_args_;
Register object_;
Register key_;
Expression* object_expr_;
const AstRawString* name_;
};
void GenerateBytecodeBody();
template <typename LocalIsolate>
void AllocateDeferredConstants(LocalIsolate* isolate, Handle<Script> script);
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
// Dispatched from VisitBinaryOperation.
void VisitArithmeticExpression(BinaryOperation* binop);
void VisitCommaExpression(BinaryOperation* binop);
void VisitLogicalOrExpression(BinaryOperation* binop);
void VisitLogicalAndExpression(BinaryOperation* binop);
void VisitNullishExpression(BinaryOperation* binop);
// Dispatched from VisitNaryOperation.
void VisitNaryArithmeticExpression(NaryOperation* expr);
void VisitNaryCommaExpression(NaryOperation* expr);
void VisitNaryLogicalOrExpression(NaryOperation* expr);
void VisitNaryLogicalAndExpression(NaryOperation* expr);
void VisitNaryNullishExpression(NaryOperation* expr);
// Dispatched from VisitUnaryOperation.
void VisitVoid(UnaryOperation* expr);
void VisitTypeOf(UnaryOperation* expr);
void VisitNot(UnaryOperation* expr);
void VisitDelete(UnaryOperation* expr);
// Visits a typeof expression for the value on which to perform the typeof.
void VisitForTypeOfValue(Expression* expr);
// Used by flow control routines to evaluate loop condition.
void VisitCondition(Expression* expr);
// Visit the arguments expressions in |args| and store them in |args_regs|,
// growing |args_regs| for each argument visited.
void VisitArguments(const ZonePtrList<Expression>* args,
RegisterList* arg_regs);
// Visit a keyed super property load. The optional
// |opt_receiver_out| register will have the receiver stored to it
// if it's a valid register. The loaded value is placed in the
// accumulator.
void VisitKeyedSuperPropertyLoad(Property* property,
Register opt_receiver_out);
// Visit a named super property load. The optional
// |opt_receiver_out| register will have the receiver stored to it
// if it's a valid register. The loaded value is placed in the
// accumulator.
void VisitNamedSuperPropertyLoad(Property* property,
Register opt_receiver_out);
void VisitPropertyLoad(Register obj, Property* expr);
void VisitPropertyLoadForRegister(Register obj, Property* expr,
Register destination);
AssignmentLhsData PrepareAssignmentLhs(
Expression* lhs, AccumulatorPreservingMode accumulator_preserving_mode =
AccumulatorPreservingMode::kNone);
void BuildAssignment(const AssignmentLhsData& data, Token::Value op,
LookupHoistingMode lookup_hoisting_mode);
void BuildThisVariableLoad();
void BuildDeclareCall(Runtime::FunctionId id);
Expression* GetDestructuringDefaultValue(Expression** target);
void BuildDestructuringArrayAssignment(
ArrayLiteral* pattern, Token::Value op,
LookupHoistingMode lookup_hoisting_mode);
void BuildDestructuringObjectAssignment(
ObjectLiteral* pattern, Token::Value op,
LookupHoistingMode lookup_hoisting_mode);
void BuildLoadNamedProperty(const Expression* object_expr, Register object,
const AstRawString* name);
void BuildStoreNamedProperty(const Expression* object_expr, Register object,
const AstRawString* name);
void BuildVariableLoad(Variable* variable, HoleCheckMode hole_check_mode,
TypeofMode typeof_mode = NOT_INSIDE_TYPEOF);
void BuildVariableLoadForAccumulatorValue(
Variable* variable, HoleCheckMode hole_check_mode,
TypeofMode typeof_mode = NOT_INSIDE_TYPEOF);
void BuildVariableAssignment(
Variable* variable, Token::Value op, HoleCheckMode hole_check_mode,
LookupHoistingMode lookup_hoisting_mode = LookupHoistingMode::kNormal);
void BuildLiteralCompareNil(Token::Value compare_op,
BytecodeArrayBuilder::NilValue nil);
void BuildReturn(int source_position = kNoSourcePosition);
void BuildAsyncReturn(int source_position = kNoSourcePosition);
void BuildAsyncGeneratorReturn();
void BuildReThrow();
void BuildHoleCheckForVariableAssignment(Variable* variable, Token::Value op);
void BuildThrowIfHole(Variable* variable);
void BuildNewLocalActivationContext();
void BuildLocalActivationContextInitialization();
void BuildNewLocalBlockContext(Scope* scope);
void BuildNewLocalCatchContext(Scope* scope);
void BuildNewLocalWithContext(Scope* scope);
void BuildGeneratorPrologue();
void BuildSuspendPoint(int position);
void BuildAwait(int position = kNoSourcePosition);
void BuildAwait(Expression* await_expr);
void BuildFinalizeIteration(IteratorRecord iterator, Register done,
Register iteration_continuation_token);
void BuildGetIterator(IteratorType hint);
// Create an IteratorRecord with pre-allocated registers holding the next
// method and iterator object.
IteratorRecord BuildGetIteratorRecord(Register iterator_next,
Register iterator_object,
IteratorType hint);
// Create an IteratorRecord allocating new registers to hold the next method
// and iterator object.
IteratorRecord BuildGetIteratorRecord(IteratorType hint);
void BuildIteratorNext(const IteratorRecord& iterator, Register next_result);
void BuildIteratorClose(const IteratorRecord& iterator,
Expression* expr = nullptr);
void BuildCallIteratorMethod(Register iterator, const AstRawString* method,
RegisterList receiver_and_args,
BytecodeLabel* if_called,
BytecodeLabels* if_notcalled);
void BuildFillArrayWithIterator(IteratorRecord iterator, Register array,
Register index, Register value,
FeedbackSlot next_value_slot,
FeedbackSlot next_done_slot,
FeedbackSlot index_slot,
FeedbackSlot element_slot);
// Create Array literals. |expr| can be nullptr, but if provided,
// a boilerplate will be used to create an initial array for elements
// before the first spread.
void BuildCreateArrayLiteral(const ZonePtrList<Expression>* elements,
ArrayLiteral* expr);
void BuildCreateObjectLiteral(Register literal, uint8_t flags, size_t entry);
void AllocateTopLevelRegisters();
void VisitArgumentsObject(Variable* variable);
void VisitRestArgumentsArray(Variable* rest);
void VisitCallSuper(Call* call);
void BuildInvalidPropertyAccess(MessageTemplate tmpl, Property* property);
void BuildPrivateBrandCheck(Property* property, Register object,
MessageTemplate tmpl);
void BuildPrivateGetterAccess(Register obj, Register access_pair);
void BuildPrivateSetterAccess(Register obj, Register access_pair,
Register value);
void BuildPrivateMethods(ClassLiteral* expr, bool is_static,
Register home_object);
void BuildClassLiteral(ClassLiteral* expr, Register name);
void VisitClassLiteral(ClassLiteral* expr, Register name);
void VisitNewTargetVariable(Variable* variable);
void VisitThisFunctionVariable(Variable* variable);
void BuildPrivateBrandInitialization(Register receiver);
void BuildInstanceMemberInitialization(Register constructor,
Register instance);
void BuildGeneratorObjectVariableInitialization();
void VisitBlockDeclarationsAndStatements(Block* stmt);
void VisitSetHomeObject(Register value, Register home_object,
LiteralProperty* property);
void VisitLiteralAccessor(Register home_object, LiteralProperty* property,
Register value_out);
void VisitForInAssignment(Expression* expr);
void VisitModuleNamespaceImports();
// Visit a logical OR/AND within a test context, rewiring the jumps based
// on the expression values.
void VisitLogicalTest(Token::Value token, Expression* left, Expression* right,
int right_coverage_slot);
void VisitNaryLogicalTest(Token::Value token, NaryOperation* expr,
const NaryCodeCoverageSlots* coverage_slots);
// Visit a (non-RHS) test for a logical op, which falls through if the test
// fails or jumps to the appropriate labels if it succeeds.
void VisitLogicalTestSubExpression(Token::Value token, Expression* expr,
BytecodeLabels* then_labels,
BytecodeLabels* else_labels,
int coverage_slot);
// Helpers for binary and nary logical op value expressions.
bool VisitLogicalOrSubExpression(Expression* expr, BytecodeLabels* end_labels,
int coverage_slot);
bool VisitLogicalAndSubExpression(Expression* expr,
BytecodeLabels* end_labels,
int coverage_slot);
// Helper for binary and nary nullish op value expressions.
bool VisitNullishSubExpression(Expression* expr, BytecodeLabels* end_labels,
int coverage_slot);
// Visit the body of a loop iteration.
void VisitIterationBody(IterationStatement* stmt, LoopBuilder* loop_builder);
// Visit a statement and switch scopes, the context is in the accumulator.
void VisitInScope(Statement* stmt, Scope* scope);
void BuildPushUndefinedIntoRegisterList(RegisterList* reg_list);
void BuildLoadPropertyKey(LiteralProperty* property, Register out_reg);
int AllocateBlockCoverageSlotIfEnabled(AstNode* node, SourceRangeKind kind);
int AllocateNaryBlockCoverageSlotIfEnabled(NaryOperation* node, size_t index);
void BuildIncrementBlockCoverageCounterIfEnabled(AstNode* node,
SourceRangeKind kind);
void BuildIncrementBlockCoverageCounterIfEnabled(int coverage_array_slot);
void BuildTest(ToBooleanMode mode, BytecodeLabels* then_labels,
BytecodeLabels* else_labels, TestFallthrough fallthrough);
template <typename TryBodyFunc, typename CatchBodyFunc>
void BuildTryCatch(TryBodyFunc try_body_func, CatchBodyFunc catch_body_func,
HandlerTable::CatchPrediction catch_prediction,
TryCatchStatement* stmt_for_coverage = nullptr);
template <typename TryBodyFunc, typename FinallyBodyFunc>
void BuildTryFinally(TryBodyFunc try_body_func,
FinallyBodyFunc finally_body_func,
HandlerTable::CatchPrediction catch_prediction,
TryFinallyStatement* stmt_for_coverage = nullptr);
template <typename ExpressionFunc>
void BuildOptionalChain(ExpressionFunc expression_func);
// Visitors for obtaining expression result in the accumulator, in a
// register, or just getting the effect. Some visitors return a TypeHint which
// specifies the type of the result of the visited expression.
TypeHint VisitForAccumulatorValue(Expression* expr);
void VisitForAccumulatorValueOrTheHole(Expression* expr);
V8_WARN_UNUSED_RESULT Register VisitForRegisterValue(Expression* expr);
V8_INLINE void VisitForRegisterValue(Expression* expr, Register destination);
void VisitAndPushIntoRegisterList(Expression* expr, RegisterList* reg_list);
void VisitForEffect(Expression* expr);
void VisitForTest(Expression* expr, BytecodeLabels* then_labels,
BytecodeLabels* else_labels, TestFallthrough fallthrough);
void VisitForNullishTest(Expression* expr, BytecodeLabels* then_labels,
BytecodeLabels* test_next_labels,
BytecodeLabels* else_labels);
void VisitInSameTestExecutionScope(Expression* expr);
Register GetRegisterForLocalVariable(Variable* variable);
// Returns the runtime function id for a store to super for the function's
// language mode.
inline Runtime::FunctionId StoreToSuperRuntimeId();
inline Runtime::FunctionId StoreKeyedToSuperRuntimeId();
// Returns a cached slot, or create and cache a new slot if one doesn't
// already exists.
FeedbackSlot GetCachedLoadGlobalICSlot(TypeofMode typeof_mode,
Variable* variable);
FeedbackSlot GetCachedStoreGlobalICSlot(LanguageMode language_mode,
Variable* variable);
FeedbackSlot GetCachedLoadICSlot(const Expression* expr,
const AstRawString* name);
FeedbackSlot GetCachedStoreICSlot(const Expression* expr,
const AstRawString* name);
FeedbackSlot GetDummyCompareICSlot();
int GetCachedCreateClosureSlot(FunctionLiteral* literal);
void AddToEagerLiteralsIfEager(FunctionLiteral* literal);
// Checks if the visited expression is one shot, i.e executed only once. Any
// expression either in a top level code or an IIFE that is not within a loop
// is eligible for one shot optimizations.
inline bool ShouldOptimizeAsOneShot() const;
static constexpr ToBooleanMode ToBooleanModeFromTypeHint(TypeHint type_hint) {
return type_hint == TypeHint::kBoolean ? ToBooleanMode::kAlreadyBoolean
: ToBooleanMode::kConvertToBoolean;
}
inline Register generator_object() const;
inline BytecodeArrayBuilder* builder() { return &builder_; }
inline Zone* zone() const { return zone_; }
inline DeclarationScope* closure_scope() const { return closure_scope_; }
inline UnoptimizedCompilationInfo* info() const { return info_; }
inline const AstStringConstants* ast_string_constants() const {
return ast_string_constants_;
}
inline Scope* current_scope() const { return current_scope_; }
inline void set_current_scope(Scope* scope) { current_scope_ = scope; }
inline ControlScope* execution_control() const { return execution_control_; }
inline void set_execution_control(ControlScope* scope) {
execution_control_ = scope;
}
inline ContextScope* execution_context() const { return execution_context_; }
inline void set_execution_context(ContextScope* context) {
execution_context_ = context;
}
inline void set_execution_result(ExpressionResultScope* execution_result) {
execution_result_ = execution_result;
}
ExpressionResultScope* execution_result() const { return execution_result_; }
BytecodeRegisterAllocator* register_allocator() {
return builder()->register_allocator();
}
TopLevelDeclarationsBuilder* top_level_builder() {
DCHECK_NOT_NULL(top_level_builder_);
return top_level_builder_;
}
inline LanguageMode language_mode() const;
inline FunctionKind function_kind() const;
inline FeedbackVectorSpec* feedback_spec();
inline int feedback_index(FeedbackSlot slot) const;
inline FeedbackSlotCache* feedback_slot_cache() {
return feedback_slot_cache_;
}
inline HandlerTable::CatchPrediction catch_prediction() const {
return catch_prediction_;
}
inline void set_catch_prediction(HandlerTable::CatchPrediction value) {
catch_prediction_ = value;
}
LoopScope* current_loop_scope() const { return current_loop_scope_; }
void set_current_loop_scope(LoopScope* loop_scope) {
current_loop_scope_ = loop_scope;
}
Zone* zone_;
BytecodeArrayBuilder builder_;
UnoptimizedCompilationInfo* info_;
const AstStringConstants* ast_string_constants_;
DeclarationScope* closure_scope_;
Scope* current_scope_;
// External vector of literals to be eagerly compiled.
std::vector<FunctionLiteral*>* eager_inner_literals_;
FeedbackSlotCache* feedback_slot_cache_;
TopLevelDeclarationsBuilder* top_level_builder_;
BlockCoverageBuilder* block_coverage_builder_;
ZoneVector<std::pair<FunctionLiteral*, size_t>> function_literals_;
ZoneVector<std::pair<NativeFunctionLiteral*, size_t>>
native_function_literals_;
ZoneVector<std::pair<ObjectLiteral*, size_t>> object_literals_;
ZoneVector<std::pair<ArrayLiteral*, size_t>> array_literals_;
ZoneVector<std::pair<ClassLiteral*, size_t>> class_literals_;
ZoneVector<std::pair<GetTemplateObject*, size_t>> template_objects_;
ControlScope* execution_control_;
ContextScope* execution_context_;
ExpressionResultScope* execution_result_;
Register incoming_new_target_or_generator_;
BytecodeLabels* optional_chaining_null_labels_;
// Dummy feedback slot for compare operations, where we don't care about
// feedback
SharedFeedbackSlot dummy_feedback_slot_;
BytecodeJumpTable* generator_jump_table_;
int suspend_count_;
// TODO(solanes): assess if we can move loop_depth_ into LoopScope.
int loop_depth_;
LoopScope* current_loop_scope_;
HandlerTable::CatchPrediction catch_prediction_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_GENERATOR_H_