| // 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. |
| |
| #include "src/interpreter/control-flow-builders.h" |
| #include "src/objects/objects-inl.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace interpreter { |
| |
| |
| BreakableControlFlowBuilder::~BreakableControlFlowBuilder() { |
| BindBreakTarget(); |
| DCHECK(break_labels_.empty() || break_labels_.is_bound()); |
| if (block_coverage_builder_ != nullptr) { |
| block_coverage_builder_->IncrementBlockCounter( |
| node_, SourceRangeKind::kContinuation); |
| } |
| } |
| |
| void BreakableControlFlowBuilder::BindBreakTarget() { |
| break_labels_.Bind(builder()); |
| } |
| |
| void BreakableControlFlowBuilder::EmitJump(BytecodeLabels* sites) { |
| builder()->Jump(sites->New()); |
| } |
| |
| void BreakableControlFlowBuilder::EmitJumpIfTrue( |
| BytecodeArrayBuilder::ToBooleanMode mode, BytecodeLabels* sites) { |
| builder()->JumpIfTrue(mode, sites->New()); |
| } |
| |
| void BreakableControlFlowBuilder::EmitJumpIfFalse( |
| BytecodeArrayBuilder::ToBooleanMode mode, BytecodeLabels* sites) { |
| builder()->JumpIfFalse(mode, sites->New()); |
| } |
| |
| void BreakableControlFlowBuilder::EmitJumpIfUndefined(BytecodeLabels* sites) { |
| builder()->JumpIfUndefined(sites->New()); |
| } |
| |
| void BreakableControlFlowBuilder::EmitJumpIfNull(BytecodeLabels* sites) { |
| builder()->JumpIfNull(sites->New()); |
| } |
| |
| LoopBuilder::~LoopBuilder() { |
| DCHECK(continue_labels_.empty() || continue_labels_.is_bound()); |
| DCHECK(end_labels_.empty() || end_labels_.is_bound()); |
| } |
| |
| void LoopBuilder::LoopHeader() { |
| // Jumps from before the loop header into the loop violate ordering |
| // requirements of bytecode basic blocks. The only entry into a loop |
| // must be the loop header. Surely breaks is okay? Not if nested |
| // and misplaced between the headers. |
| DCHECK(break_labels_.empty() && continue_labels_.empty() && |
| end_labels_.empty()); |
| builder()->Bind(&loop_header_); |
| } |
| |
| void LoopBuilder::LoopBody() { |
| if (block_coverage_builder_ != nullptr) { |
| block_coverage_builder_->IncrementBlockCounter(block_coverage_body_slot_); |
| } |
| } |
| |
| void LoopBuilder::JumpToHeader(int loop_depth, LoopBuilder* const parent_loop) { |
| BindLoopEnd(); |
| if (parent_loop && |
| loop_header_.offset() == parent_loop->loop_header_.offset()) { |
| // TurboFan can't cope with multiple loops that have the same loop header |
| // bytecode offset. If we have an inner loop with the same header offset |
| // than its parent loop, we do not create a JumpLoop bytecode. Instead, we |
| // Jump to our parent's JumpToHeader which in turn can be a JumpLoop or, iff |
| // they are a nested inner loop too, a Jump to its parent's JumpToHeader. |
| parent_loop->JumpToLoopEnd(); |
| } else { |
| // Pass the proper loop nesting level to the backwards branch, to trigger |
| // on-stack replacement when armed for the given loop nesting depth. |
| int level = Min(loop_depth, AbstractCode::kMaxLoopNestingMarker - 1); |
| // Loop must have closed form, i.e. all loop elements are within the loop, |
| // the loop header precedes the body and next elements in the loop. |
| builder()->JumpLoop(&loop_header_, level, source_position_); |
| } |
| } |
| |
| void LoopBuilder::BindContinueTarget() { continue_labels_.Bind(builder()); } |
| |
| void LoopBuilder::BindLoopEnd() { end_labels_.Bind(builder()); } |
| |
| SwitchBuilder::~SwitchBuilder() { |
| #ifdef DEBUG |
| for (auto site : case_sites_) { |
| DCHECK(!site.has_referrer_jump() || site.is_bound()); |
| } |
| #endif |
| } |
| |
| void SwitchBuilder::SetCaseTarget(int index, CaseClause* clause) { |
| BytecodeLabel& site = case_sites_.at(index); |
| builder()->Bind(&site); |
| if (block_coverage_builder_) { |
| block_coverage_builder_->IncrementBlockCounter(clause, |
| SourceRangeKind::kBody); |
| } |
| } |
| |
| TryCatchBuilder::~TryCatchBuilder() { |
| if (block_coverage_builder_ != nullptr) { |
| block_coverage_builder_->IncrementBlockCounter( |
| statement_, SourceRangeKind::kContinuation); |
| } |
| } |
| |
| void TryCatchBuilder::BeginTry(Register context) { |
| builder()->MarkTryBegin(handler_id_, context); |
| } |
| |
| |
| void TryCatchBuilder::EndTry() { |
| builder()->MarkTryEnd(handler_id_); |
| builder()->Jump(&exit_); |
| builder()->MarkHandler(handler_id_, catch_prediction_); |
| |
| if (block_coverage_builder_ != nullptr) { |
| block_coverage_builder_->IncrementBlockCounter(statement_, |
| SourceRangeKind::kCatch); |
| } |
| } |
| |
| void TryCatchBuilder::EndCatch() { builder()->Bind(&exit_); } |
| |
| TryFinallyBuilder::~TryFinallyBuilder() { |
| if (block_coverage_builder_ != nullptr) { |
| block_coverage_builder_->IncrementBlockCounter( |
| statement_, SourceRangeKind::kContinuation); |
| } |
| } |
| |
| void TryFinallyBuilder::BeginTry(Register context) { |
| builder()->MarkTryBegin(handler_id_, context); |
| } |
| |
| |
| void TryFinallyBuilder::LeaveTry() { |
| builder()->Jump(finalization_sites_.New()); |
| } |
| |
| |
| void TryFinallyBuilder::EndTry() { |
| builder()->MarkTryEnd(handler_id_); |
| } |
| |
| |
| void TryFinallyBuilder::BeginHandler() { |
| builder()->Bind(&handler_); |
| builder()->MarkHandler(handler_id_, catch_prediction_); |
| } |
| |
| void TryFinallyBuilder::BeginFinally() { |
| finalization_sites_.Bind(builder()); |
| |
| if (block_coverage_builder_ != nullptr) { |
| block_coverage_builder_->IncrementBlockCounter(statement_, |
| SourceRangeKind::kFinally); |
| } |
| } |
| |
| void TryFinallyBuilder::EndFinally() { |
| // Nothing to be done here. |
| } |
| |
| ConditionalControlFlowBuilder::~ConditionalControlFlowBuilder() { |
| if (!else_labels_.is_bound()) else_labels_.Bind(builder()); |
| end_labels_.Bind(builder()); |
| |
| DCHECK(end_labels_.empty() || end_labels_.is_bound()); |
| DCHECK(then_labels_.empty() || then_labels_.is_bound()); |
| DCHECK(else_labels_.empty() || else_labels_.is_bound()); |
| |
| // IfStatement requires a continuation counter, Conditional does not (as it |
| // can only contain expressions). |
| if (block_coverage_builder_ != nullptr && node_->IsIfStatement()) { |
| block_coverage_builder_->IncrementBlockCounter( |
| node_, SourceRangeKind::kContinuation); |
| } |
| } |
| |
| void ConditionalControlFlowBuilder::JumpToEnd() { |
| DCHECK(end_labels_.empty()); // May only be called once. |
| builder()->Jump(end_labels_.New()); |
| } |
| |
| void ConditionalControlFlowBuilder::Then() { |
| then_labels()->Bind(builder()); |
| if (block_coverage_builder_ != nullptr) { |
| block_coverage_builder_->IncrementBlockCounter(block_coverage_then_slot_); |
| } |
| } |
| |
| void ConditionalControlFlowBuilder::Else() { |
| else_labels()->Bind(builder()); |
| if (block_coverage_builder_ != nullptr) { |
| block_coverage_builder_->IncrementBlockCounter(block_coverage_else_slot_); |
| } |
| } |
| |
| } // namespace interpreter |
| } // namespace internal |
| } // namespace v8 |