Home | History | Annotate | Download | only in compiler
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef V8_COMPILER_BYTECODE_GRAPH_BUILDER_H_
      6 #define V8_COMPILER_BYTECODE_GRAPH_BUILDER_H_
      7 
      8 #include "src/compiler/bytecode-analysis.h"
      9 #include "src/compiler/js-graph.h"
     10 #include "src/compiler/js-type-hint-lowering.h"
     11 #include "src/compiler/state-values-utils.h"
     12 #include "src/interpreter/bytecode-array-iterator.h"
     13 #include "src/interpreter/bytecode-flags.h"
     14 #include "src/interpreter/bytecodes.h"
     15 #include "src/source-position-table.h"
     16 
     17 namespace v8 {
     18 namespace internal {
     19 
     20 class VectorSlotPair;
     21 
     22 namespace compiler {
     23 
     24 class Reduction;
     25 class SourcePositionTable;
     26 
     27 // The BytecodeGraphBuilder produces a high-level IR graph based on
     28 // interpreter bytecodes.
     29 class BytecodeGraphBuilder {
     30  public:
     31   BytecodeGraphBuilder(
     32       Zone* local_zone, Handle<SharedFunctionInfo> shared,
     33       Handle<FeedbackVector> feedback_vector, BailoutId osr_offset,
     34       JSGraph* jsgraph, CallFrequency& invocation_frequency,
     35       SourcePositionTable* source_positions, Handle<Context> native_context,
     36       int inlining_id = SourcePosition::kNotInlined,
     37       JSTypeHintLowering::Flags flags = JSTypeHintLowering::kNoFlags,
     38       bool stack_check = true, bool analyze_environment_liveness = true);
     39 
     40   // Creates a graph by visiting bytecodes.
     41   void CreateGraph();
     42 
     43  private:
     44   class Environment;
     45   class OsrIteratorState;
     46   struct SubEnvironment;
     47 
     48   void RemoveMergeEnvironmentsBeforeOffset(int limit_offset);
     49   void AdvanceToOsrEntryAndPeelLoops(
     50       interpreter::BytecodeArrayIterator* iterator,
     51       SourcePositionTableIterator* source_position_iterator);
     52 
     53   void VisitSingleBytecode(
     54       SourcePositionTableIterator* source_position_iterator);
     55   void VisitBytecodes();
     56 
     57   // Get or create the node that represents the outer function closure.
     58   Node* GetFunctionClosure();
     59 
     60   // Builder for loading the a native context field.
     61   Node* BuildLoadNativeContextField(int index);
     62 
     63   // Helper function for creating a pair containing type feedback vector and
     64   // a feedback slot.
     65   VectorSlotPair CreateVectorSlotPair(int slot_id);
     66 
     67   void set_environment(Environment* env) { environment_ = env; }
     68   const Environment* environment() const { return environment_; }
     69   Environment* environment() { return environment_; }
     70 
     71   // Node creation helpers
     72   Node* NewNode(const Operator* op, bool incomplete = false) {
     73     return MakeNode(op, 0, static_cast<Node**>(nullptr), incomplete);
     74   }
     75 
     76   Node* NewNode(const Operator* op, Node* n1) {
     77     Node* buffer[] = {n1};
     78     return MakeNode(op, arraysize(buffer), buffer, false);
     79   }
     80 
     81   Node* NewNode(const Operator* op, Node* n1, Node* n2) {
     82     Node* buffer[] = {n1, n2};
     83     return MakeNode(op, arraysize(buffer), buffer, false);
     84   }
     85 
     86   Node* NewNode(const Operator* op, Node* n1, Node* n2, Node* n3) {
     87     Node* buffer[] = {n1, n2, n3};
     88     return MakeNode(op, arraysize(buffer), buffer, false);
     89   }
     90 
     91   Node* NewNode(const Operator* op, Node* n1, Node* n2, Node* n3, Node* n4) {
     92     Node* buffer[] = {n1, n2, n3, n4};
     93     return MakeNode(op, arraysize(buffer), buffer, false);
     94   }
     95 
     96   Node* NewNode(const Operator* op, Node* n1, Node* n2, Node* n3, Node* n4,
     97                 Node* n5, Node* n6) {
     98     Node* buffer[] = {n1, n2, n3, n4, n5, n6};
     99     return MakeNode(op, arraysize(buffer), buffer, false);
    100   }
    101 
    102   // Helpers to create new control nodes.
    103   Node* NewIfTrue() { return NewNode(common()->IfTrue()); }
    104   Node* NewIfFalse() { return NewNode(common()->IfFalse()); }
    105   Node* NewIfValue(int32_t value) { return NewNode(common()->IfValue(value)); }
    106   Node* NewIfDefault() { return NewNode(common()->IfDefault()); }
    107   Node* NewMerge() { return NewNode(common()->Merge(1), true); }
    108   Node* NewLoop() { return NewNode(common()->Loop(1), true); }
    109   Node* NewBranch(Node* condition, BranchHint hint = BranchHint::kNone,
    110                   IsSafetyCheck is_safety_check = IsSafetyCheck::kSafetyCheck) {
    111     return NewNode(common()->Branch(hint, is_safety_check), condition);
    112   }
    113   Node* NewSwitch(Node* condition, int control_output_count) {
    114     return NewNode(common()->Switch(control_output_count), condition);
    115   }
    116 
    117   // Creates a new Phi node having {count} input values.
    118   Node* NewPhi(int count, Node* input, Node* control);
    119   Node* NewEffectPhi(int count, Node* input, Node* control);
    120 
    121   // Helpers for merging control, effect or value dependencies.
    122   Node* MergeControl(Node* control, Node* other);
    123   Node* MergeEffect(Node* effect, Node* other_effect, Node* control);
    124   Node* MergeValue(Node* value, Node* other_value, Node* control);
    125 
    126   // The main node creation chokepoint. Adds context, frame state, effect,
    127   // and control dependencies depending on the operator.
    128   Node* MakeNode(const Operator* op, int value_input_count,
    129                  Node* const* value_inputs, bool incomplete);
    130 
    131   Node** EnsureInputBufferSize(int size);
    132 
    133   Node* const* GetCallArgumentsFromRegisters(Node* callee, Node* receiver,
    134                                              interpreter::Register first_arg,
    135                                              int arg_count);
    136   Node* const* ProcessCallVarArgs(ConvertReceiverMode receiver_mode,
    137                                   Node* callee, interpreter::Register first_reg,
    138                                   int arg_count);
    139   Node* ProcessCallArguments(const Operator* call_op, Node* const* args,
    140                              int arg_count);
    141   Node* ProcessCallArguments(const Operator* call_op, Node* callee,
    142                              interpreter::Register receiver, size_t reg_count);
    143   Node* const* GetConstructArgumentsFromRegister(
    144       Node* target, Node* new_target, interpreter::Register first_arg,
    145       int arg_count);
    146   Node* ProcessConstructArguments(const Operator* op, Node* const* args,
    147                                   int arg_count);
    148   Node* ProcessCallRuntimeArguments(const Operator* call_runtime_op,
    149                                     interpreter::Register receiver,
    150                                     size_t reg_count);
    151 
    152   // Prepare information for eager deoptimization. This information is carried
    153   // by dedicated {Checkpoint} nodes that are wired into the effect chain.
    154   // Conceptually this frame state is "before" a given operation.
    155   void PrepareEagerCheckpoint();
    156 
    157   // Prepare information for lazy deoptimization. This information is attached
    158   // to the given node and the output value produced by the node is combined.
    159   // Conceptually this frame state is "after" a given operation.
    160   void PrepareFrameState(Node* node, OutputFrameStateCombine combine);
    161 
    162   void BuildCreateArguments(CreateArgumentsType type);
    163   Node* BuildLoadGlobal(Handle<Name> name, uint32_t feedback_slot_index,
    164                         TypeofMode typeof_mode);
    165 
    166   enum class StoreMode {
    167     // Check the prototype chain before storing.
    168     kNormal,
    169     // Store value to the receiver without checking the prototype chain.
    170     kOwn,
    171   };
    172   void BuildNamedStore(StoreMode store_mode);
    173   void BuildLdaLookupSlot(TypeofMode typeof_mode);
    174   void BuildLdaLookupContextSlot(TypeofMode typeof_mode);
    175   void BuildLdaLookupGlobalSlot(TypeofMode typeof_mode);
    176   void BuildCallVarArgs(ConvertReceiverMode receiver_mode);
    177   void BuildCall(ConvertReceiverMode receiver_mode, Node* const* args,
    178                  size_t arg_count, int slot_id);
    179   void BuildCall(ConvertReceiverMode receiver_mode,
    180                  std::initializer_list<Node*> args, int slot_id) {
    181     BuildCall(receiver_mode, args.begin(), args.size(), slot_id);
    182   }
    183   void BuildUnaryOp(const Operator* op);
    184   void BuildBinaryOp(const Operator* op);
    185   void BuildBinaryOpWithImmediate(const Operator* op);
    186   void BuildCompareOp(const Operator* op);
    187   void BuildDelete(LanguageMode language_mode);
    188   void BuildCastOperator(const Operator* op);
    189   void BuildHoleCheckAndThrow(Node* condition, Runtime::FunctionId runtime_id,
    190                               Node* name = nullptr);
    191 
    192   // Optional early lowering to the simplified operator level.  Note that
    193   // the result has already been wired into the environment just like
    194   // any other invocation of {NewNode} would do.
    195   JSTypeHintLowering::LoweringResult TryBuildSimplifiedUnaryOp(
    196       const Operator* op, Node* operand, FeedbackSlot slot);
    197   JSTypeHintLowering::LoweringResult TryBuildSimplifiedBinaryOp(
    198       const Operator* op, Node* left, Node* right, FeedbackSlot slot);
    199   JSTypeHintLowering::LoweringResult TryBuildSimplifiedForInNext(
    200       Node* receiver, Node* cache_array, Node* cache_type, Node* index,
    201       FeedbackSlot slot);
    202   JSTypeHintLowering::LoweringResult TryBuildSimplifiedForInPrepare(
    203       Node* receiver, FeedbackSlot slot);
    204   JSTypeHintLowering::LoweringResult TryBuildSimplifiedToNumber(
    205       Node* input, FeedbackSlot slot);
    206   JSTypeHintLowering::LoweringResult TryBuildSimplifiedCall(const Operator* op,
    207                                                             Node* const* args,
    208                                                             int arg_count,
    209                                                             FeedbackSlot slot);
    210   JSTypeHintLowering::LoweringResult TryBuildSimplifiedConstruct(
    211       const Operator* op, Node* const* args, int arg_count, FeedbackSlot slot);
    212   JSTypeHintLowering::LoweringResult TryBuildSimplifiedLoadNamed(
    213       const Operator* op, Node* receiver, FeedbackSlot slot);
    214   JSTypeHintLowering::LoweringResult TryBuildSimplifiedLoadKeyed(
    215       const Operator* op, Node* receiver, Node* key, FeedbackSlot slot);
    216   JSTypeHintLowering::LoweringResult TryBuildSimplifiedStoreNamed(
    217       const Operator* op, Node* receiver, Node* value, FeedbackSlot slot);
    218   JSTypeHintLowering::LoweringResult TryBuildSimplifiedStoreKeyed(
    219       const Operator* op, Node* receiver, Node* key, Node* value,
    220       FeedbackSlot slot);
    221 
    222   // Applies the given early reduction onto the current environment.
    223   void ApplyEarlyReduction(JSTypeHintLowering::LoweringResult reduction);
    224 
    225   // Check the context chain for extensions, for lookup fast paths.
    226   Environment* CheckContextExtensions(uint32_t depth);
    227 
    228   // Helper function to create binary operation hint from the recorded
    229   // type feedback.
    230   BinaryOperationHint GetBinaryOperationHint(int operand_index);
    231 
    232   // Helper function to create compare operation hint from the recorded
    233   // type feedback.
    234   CompareOperationHint GetCompareOperationHint();
    235 
    236   // Helper function to create for-in mode from the recorded type feedback.
    237   ForInMode GetForInMode(int operand_index);
    238 
    239   // Helper function to compute call frequency from the recorded type
    240   // feedback.
    241   CallFrequency ComputeCallFrequency(int slot_id) const;
    242 
    243   // Helper function to extract the speculation mode from the recorded type
    244   // feedback.
    245   SpeculationMode GetSpeculationMode(int slot_id) const;
    246 
    247   // Control flow plumbing.
    248   void BuildJump();
    249   void BuildJumpIf(Node* condition);
    250   void BuildJumpIfNot(Node* condition);
    251   void BuildJumpIfEqual(Node* comperand);
    252   void BuildJumpIfNotEqual(Node* comperand);
    253   void BuildJumpIfTrue();
    254   void BuildJumpIfFalse();
    255   void BuildJumpIfToBooleanTrue();
    256   void BuildJumpIfToBooleanFalse();
    257   void BuildJumpIfNotHole();
    258   void BuildJumpIfJSReceiver();
    259 
    260   void BuildSwitchOnSmi(Node* condition);
    261   void BuildSwitchOnGeneratorState(
    262       const ZoneVector<ResumeJumpTarget>& resume_jump_targets,
    263       bool allow_fallthrough_on_executing);
    264 
    265   // Simulates control flow by forward-propagating environments.
    266   void MergeIntoSuccessorEnvironment(int target_offset);
    267   void BuildLoopHeaderEnvironment(int current_offset);
    268   void SwitchToMergeEnvironment(int current_offset);
    269 
    270   // Simulates control flow that exits the function body.
    271   void MergeControlToLeaveFunction(Node* exit);
    272 
    273   // Builds loop exit nodes for every exited loop between the current bytecode
    274   // offset and {target_offset}.
    275   void BuildLoopExitsForBranch(int target_offset);
    276   void BuildLoopExitsForFunctionExit(const BytecodeLivenessState* liveness);
    277   void BuildLoopExitsUntilLoop(int loop_offset,
    278                                const BytecodeLivenessState* liveness);
    279 
    280   // Helper for building a return (from an actual return or a suspend).
    281   void BuildReturn(const BytecodeLivenessState* liveness);
    282 
    283   // Simulates entry and exit of exception handlers.
    284   void ExitThenEnterExceptionHandlers(int current_offset);
    285 
    286   // Update the current position of the {SourcePositionTable} to that of the
    287   // bytecode at {offset}, if any.
    288   void UpdateSourcePosition(SourcePositionTableIterator* it, int offset);
    289 
    290   // Growth increment for the temporary buffer used to construct input lists to
    291   // new nodes.
    292   static const int kInputBufferSizeIncrement = 64;
    293 
    294   // An abstract representation for an exception handler that is being
    295   // entered and exited while the graph builder is iterating over the
    296   // underlying bytecode. The exception handlers within the bytecode are
    297   // well scoped, hence will form a stack during iteration.
    298   struct ExceptionHandler {
    299     int start_offset_;      // Start offset of the handled area in the bytecode.
    300     int end_offset_;        // End offset of the handled area in the bytecode.
    301     int handler_offset_;    // Handler entry offset within the bytecode.
    302     int context_register_;  // Index of register holding handler context.
    303   };
    304 
    305   // Field accessors
    306   Graph* graph() const { return jsgraph_->graph(); }
    307   CommonOperatorBuilder* common() const { return jsgraph_->common(); }
    308   Zone* graph_zone() const { return graph()->zone(); }
    309   JSGraph* jsgraph() const { return jsgraph_; }
    310   Isolate* isolate() const { return jsgraph_->isolate(); }
    311   JSOperatorBuilder* javascript() const { return jsgraph_->javascript(); }
    312   SimplifiedOperatorBuilder* simplified() const {
    313     return jsgraph_->simplified();
    314   }
    315   Zone* local_zone() const { return local_zone_; }
    316   const Handle<BytecodeArray>& bytecode_array() const {
    317     return bytecode_array_;
    318   }
    319   const Handle<FeedbackVector>& feedback_vector() const {
    320     return feedback_vector_;
    321   }
    322   const JSTypeHintLowering& type_hint_lowering() const {
    323     return type_hint_lowering_;
    324   }
    325   const FrameStateFunctionInfo* frame_state_function_info() const {
    326     return frame_state_function_info_;
    327   }
    328 
    329   const interpreter::BytecodeArrayIterator& bytecode_iterator() const {
    330     return *bytecode_iterator_;
    331   }
    332 
    333   void set_bytecode_iterator(
    334       interpreter::BytecodeArrayIterator* bytecode_iterator) {
    335     bytecode_iterator_ = bytecode_iterator;
    336   }
    337 
    338   const BytecodeAnalysis* bytecode_analysis() const {
    339     return bytecode_analysis_;
    340   }
    341 
    342   void set_bytecode_analysis(const BytecodeAnalysis* bytecode_analysis) {
    343     bytecode_analysis_ = bytecode_analysis;
    344   }
    345 
    346   int currently_peeled_loop_offset() const {
    347     return currently_peeled_loop_offset_;
    348   }
    349 
    350   void set_currently_peeled_loop_offset(int offset) {
    351     currently_peeled_loop_offset_ = offset;
    352   }
    353 
    354   bool stack_check() const { return stack_check_; }
    355 
    356   void set_stack_check(bool stack_check) { stack_check_ = stack_check; }
    357 
    358   bool analyze_environment_liveness() const {
    359     return analyze_environment_liveness_;
    360   }
    361 
    362   int current_exception_handler() { return current_exception_handler_; }
    363 
    364   void set_current_exception_handler(int index) {
    365     current_exception_handler_ = index;
    366   }
    367 
    368   bool needs_eager_checkpoint() const { return needs_eager_checkpoint_; }
    369   void mark_as_needing_eager_checkpoint(bool value) {
    370     needs_eager_checkpoint_ = value;
    371   }
    372 
    373   Handle<Context> native_context() const { return native_context_; }
    374 
    375 #define DECLARE_VISIT_BYTECODE(name, ...) void Visit##name();
    376   BYTECODE_LIST(DECLARE_VISIT_BYTECODE)
    377 #undef DECLARE_VISIT_BYTECODE
    378 
    379   Zone* local_zone_;
    380   JSGraph* jsgraph_;
    381   CallFrequency const invocation_frequency_;
    382   Handle<BytecodeArray> bytecode_array_;
    383   Handle<FeedbackVector> feedback_vector_;
    384   const JSTypeHintLowering type_hint_lowering_;
    385   const FrameStateFunctionInfo* frame_state_function_info_;
    386   const interpreter::BytecodeArrayIterator* bytecode_iterator_;
    387   const BytecodeAnalysis* bytecode_analysis_;
    388   Environment* environment_;
    389   BailoutId osr_offset_;
    390   int currently_peeled_loop_offset_;
    391   bool stack_check_;
    392   bool analyze_environment_liveness_;
    393 
    394   // Merge environments are snapshots of the environment at points where the
    395   // control flow merges. This models a forward data flow propagation of all
    396   // values from all predecessors of the merge in question. They are indexed by
    397   // the bytecode offset
    398   ZoneMap<int, Environment*> merge_environments_;
    399 
    400   // Generator merge environments are snapshots of the current resume
    401   // environment, tracing back through loop headers to the resume switch of a
    402   // generator. They allow us to model a single resume jump as several switch
    403   // statements across loop headers, keeping those loop headers reducible,
    404   // without having to merge the "executing" environments of the generator into
    405   // the "resuming" ones. They are indexed by the suspend id of the resume.
    406   ZoneMap<int, Environment*> generator_merge_environments_;
    407 
    408   // Exception handlers currently entered by the iteration.
    409   ZoneStack<ExceptionHandler> exception_handlers_;
    410   int current_exception_handler_;
    411 
    412   // Temporary storage for building node input lists.
    413   int input_buffer_size_;
    414   Node** input_buffer_;
    415 
    416   // Optimization to only create checkpoints when the current position in the
    417   // control-flow is not effect-dominated by another checkpoint already. All
    418   // operations that do not have observable side-effects can be re-evaluated.
    419   bool needs_eager_checkpoint_;
    420 
    421   // Nodes representing values in the activation record.
    422   SetOncePointer<Node> function_closure_;
    423 
    424   // Control nodes that exit the function body.
    425   ZoneVector<Node*> exit_controls_;
    426 
    427   StateValuesCache state_values_cache_;
    428 
    429   // The source position table, to be populated.
    430   SourcePositionTable* source_positions_;
    431 
    432   SourcePosition const start_position_;
    433 
    434   // The native context for which we optimize.
    435   Handle<Context> const native_context_;
    436 
    437   static int const kBinaryOperationHintIndex = 1;
    438   static int const kCountOperationHintIndex = 0;
    439   static int const kBinaryOperationSmiHintIndex = 1;
    440   static int const kUnaryOperationHintIndex = 0;
    441 
    442   DISALLOW_COPY_AND_ASSIGN(BytecodeGraphBuilder);
    443 };
    444 
    445 }  // namespace compiler
    446 }  // namespace internal
    447 }  // namespace v8
    448 
    449 #endif  // V8_COMPILER_BYTECODE_GRAPH_BUILDER_H_
    450