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 set_environment(Environment * env)67 void set_environment(Environment* env) { environment_ = env; } environment()68 const Environment* environment() const { return environment_; } 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 NewNode(const Operator * op,Node * n1)76 Node* NewNode(const Operator* op, Node* n1) { 77 Node* buffer[] = {n1}; 78 return MakeNode(op, arraysize(buffer), buffer, false); 79 } 80 NewNode(const Operator * op,Node * n1,Node * n2)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 NewNode(const Operator * op,Node * n1,Node * n2,Node * n3)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 NewNode(const Operator * op,Node * n1,Node * n2,Node * n3,Node * n4)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 NewNode(const Operator * op,Node * n1,Node * n2,Node * n3,Node * n4,Node * n5,Node * n6)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. NewIfTrue()103 Node* NewIfTrue() { return NewNode(common()->IfTrue()); } NewIfFalse()104 Node* NewIfFalse() { return NewNode(common()->IfFalse()); } NewIfValue(int32_t value)105 Node* NewIfValue(int32_t value) { return NewNode(common()->IfValue(value)); } NewIfDefault()106 Node* NewIfDefault() { return NewNode(common()->IfDefault()); } NewMerge()107 Node* NewMerge() { return NewNode(common()->Merge(1), true); } NewLoop()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 } NewSwitch(Node * condition,int control_output_count)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); BuildCall(ConvertReceiverMode receiver_mode,std::initializer_list<Node * > args,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 graph()306 Graph* graph() const { return jsgraph_->graph(); } common()307 CommonOperatorBuilder* common() const { return jsgraph_->common(); } graph_zone()308 Zone* graph_zone() const { return graph()->zone(); } jsgraph()309 JSGraph* jsgraph() const { return jsgraph_; } isolate()310 Isolate* isolate() const { return jsgraph_->isolate(); } javascript()311 JSOperatorBuilder* javascript() const { return jsgraph_->javascript(); } simplified()312 SimplifiedOperatorBuilder* simplified() const { 313 return jsgraph_->simplified(); 314 } local_zone()315 Zone* local_zone() const { return local_zone_; } bytecode_array()316 const Handle<BytecodeArray>& bytecode_array() const { 317 return bytecode_array_; 318 } feedback_vector()319 const Handle<FeedbackVector>& feedback_vector() const { 320 return feedback_vector_; 321 } type_hint_lowering()322 const JSTypeHintLowering& type_hint_lowering() const { 323 return type_hint_lowering_; 324 } frame_state_function_info()325 const FrameStateFunctionInfo* frame_state_function_info() const { 326 return frame_state_function_info_; 327 } 328 bytecode_iterator()329 const interpreter::BytecodeArrayIterator& bytecode_iterator() const { 330 return *bytecode_iterator_; 331 } 332 set_bytecode_iterator(interpreter::BytecodeArrayIterator * bytecode_iterator)333 void set_bytecode_iterator( 334 interpreter::BytecodeArrayIterator* bytecode_iterator) { 335 bytecode_iterator_ = bytecode_iterator; 336 } 337 bytecode_analysis()338 const BytecodeAnalysis* bytecode_analysis() const { 339 return bytecode_analysis_; 340 } 341 set_bytecode_analysis(const BytecodeAnalysis * bytecode_analysis)342 void set_bytecode_analysis(const BytecodeAnalysis* bytecode_analysis) { 343 bytecode_analysis_ = bytecode_analysis; 344 } 345 currently_peeled_loop_offset()346 int currently_peeled_loop_offset() const { 347 return currently_peeled_loop_offset_; 348 } 349 set_currently_peeled_loop_offset(int offset)350 void set_currently_peeled_loop_offset(int offset) { 351 currently_peeled_loop_offset_ = offset; 352 } 353 stack_check()354 bool stack_check() const { return stack_check_; } 355 set_stack_check(bool stack_check)356 void set_stack_check(bool stack_check) { stack_check_ = stack_check; } 357 analyze_environment_liveness()358 bool analyze_environment_liveness() const { 359 return analyze_environment_liveness_; 360 } 361 current_exception_handler()362 int current_exception_handler() { return current_exception_handler_; } 363 set_current_exception_handler(int index)364 void set_current_exception_handler(int index) { 365 current_exception_handler_ = index; 366 } 367 needs_eager_checkpoint()368 bool needs_eager_checkpoint() const { return needs_eager_checkpoint_; } mark_as_needing_eager_checkpoint(bool value)369 void mark_as_needing_eager_checkpoint(bool value) { 370 needs_eager_checkpoint_ = value; 371 } 372 native_context()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