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 #include "src/interpreter/control-flow-builders.h"
6 #include "src/objects/objects-inl.h"
7 
8 namespace v8 {
9 namespace internal {
10 namespace interpreter {
11 
12 
~BreakableControlFlowBuilder()13 BreakableControlFlowBuilder::~BreakableControlFlowBuilder() {
14   BindBreakTarget();
15   DCHECK(break_labels_.empty() || break_labels_.is_bound());
16   if (block_coverage_builder_ != nullptr) {
17     block_coverage_builder_->IncrementBlockCounter(
18         node_, SourceRangeKind::kContinuation);
19   }
20 }
21 
BindBreakTarget()22 void BreakableControlFlowBuilder::BindBreakTarget() {
23   break_labels_.Bind(builder());
24 }
25 
EmitJump(BytecodeLabels * sites)26 void BreakableControlFlowBuilder::EmitJump(BytecodeLabels* sites) {
27   builder()->Jump(sites->New());
28 }
29 
EmitJumpIfTrue(BytecodeArrayBuilder::ToBooleanMode mode,BytecodeLabels * sites)30 void BreakableControlFlowBuilder::EmitJumpIfTrue(
31     BytecodeArrayBuilder::ToBooleanMode mode, BytecodeLabels* sites) {
32   builder()->JumpIfTrue(mode, sites->New());
33 }
34 
EmitJumpIfFalse(BytecodeArrayBuilder::ToBooleanMode mode,BytecodeLabels * sites)35 void BreakableControlFlowBuilder::EmitJumpIfFalse(
36     BytecodeArrayBuilder::ToBooleanMode mode, BytecodeLabels* sites) {
37   builder()->JumpIfFalse(mode, sites->New());
38 }
39 
EmitJumpIfUndefined(BytecodeLabels * sites)40 void BreakableControlFlowBuilder::EmitJumpIfUndefined(BytecodeLabels* sites) {
41   builder()->JumpIfUndefined(sites->New());
42 }
43 
EmitJumpIfNull(BytecodeLabels * sites)44 void BreakableControlFlowBuilder::EmitJumpIfNull(BytecodeLabels* sites) {
45   builder()->JumpIfNull(sites->New());
46 }
47 
~LoopBuilder()48 LoopBuilder::~LoopBuilder() {
49   DCHECK(continue_labels_.empty() || continue_labels_.is_bound());
50   DCHECK(end_labels_.empty() || end_labels_.is_bound());
51 }
52 
LoopHeader()53 void LoopBuilder::LoopHeader() {
54   // Jumps from before the loop header into the loop violate ordering
55   // requirements of bytecode basic blocks. The only entry into a loop
56   // must be the loop header. Surely breaks is okay? Not if nested
57   // and misplaced between the headers.
58   DCHECK(break_labels_.empty() && continue_labels_.empty() &&
59          end_labels_.empty());
60   builder()->Bind(&loop_header_);
61 }
62 
LoopBody()63 void LoopBuilder::LoopBody() {
64   if (block_coverage_builder_ != nullptr) {
65     block_coverage_builder_->IncrementBlockCounter(block_coverage_body_slot_);
66   }
67 }
68 
JumpToHeader(int loop_depth,LoopBuilder * const parent_loop)69 void LoopBuilder::JumpToHeader(int loop_depth, LoopBuilder* const parent_loop) {
70   BindLoopEnd();
71   if (parent_loop &&
72       loop_header_.offset() == parent_loop->loop_header_.offset()) {
73     // TurboFan can't cope with multiple loops that have the same loop header
74     // bytecode offset. If we have an inner loop with the same header offset
75     // than its parent loop, we do not create a JumpLoop bytecode. Instead, we
76     // Jump to our parent's JumpToHeader which in turn can be a JumpLoop or, iff
77     // they are a nested inner loop too, a Jump to its parent's JumpToHeader.
78     parent_loop->JumpToLoopEnd();
79   } else {
80     // Pass the proper loop nesting level to the backwards branch, to trigger
81     // on-stack replacement when armed for the given loop nesting depth.
82     int level = std::min(loop_depth, AbstractCode::kMaxLoopNestingMarker - 1);
83     // Loop must have closed form, i.e. all loop elements are within the loop,
84     // the loop header precedes the body and next elements in the loop.
85     builder()->JumpLoop(&loop_header_, level, source_position_);
86   }
87 }
88 
BindContinueTarget()89 void LoopBuilder::BindContinueTarget() { continue_labels_.Bind(builder()); }
90 
BindLoopEnd()91 void LoopBuilder::BindLoopEnd() { end_labels_.Bind(builder()); }
92 
~SwitchBuilder()93 SwitchBuilder::~SwitchBuilder() {
94 #ifdef DEBUG
95   for (auto site : case_sites_) {
96     DCHECK(!site.has_referrer_jump() || site.is_bound());
97   }
98 #endif
99 }
100 
BindCaseTargetForJumpTable(int case_value,CaseClause * clause)101 void SwitchBuilder::BindCaseTargetForJumpTable(int case_value,
102                                                CaseClause* clause) {
103   builder()->Bind(jump_table_, case_value);
104   BuildBlockCoverage(clause);
105 }
106 
BindCaseTargetForCompareJump(int index,CaseClause * clause)107 void SwitchBuilder::BindCaseTargetForCompareJump(int index,
108                                                  CaseClause* clause) {
109   builder()->Bind(&case_sites_.at(index));
110   BuildBlockCoverage(clause);
111 }
112 
JumpToCaseIfTrue(BytecodeArrayBuilder::ToBooleanMode mode,int index)113 void SwitchBuilder::JumpToCaseIfTrue(BytecodeArrayBuilder::ToBooleanMode mode,
114                                      int index) {
115   builder()->JumpIfTrue(mode, &case_sites_.at(index));
116 }
117 
118 // Precondition: tag is in the accumulator
EmitJumpTableIfExists(int min_case,int max_case,std::map<int,CaseClause * > & covered_cases)119 void SwitchBuilder::EmitJumpTableIfExists(
120     int min_case, int max_case, std::map<int, CaseClause*>& covered_cases) {
121   builder()->SwitchOnSmiNoFeedback(jump_table_);
122   fall_through_.Bind(builder());
123   for (int j = min_case; j <= max_case; ++j) {
124     if (covered_cases.find(j) == covered_cases.end()) {
125       this->BindCaseTargetForJumpTable(j, nullptr);
126     }
127   }
128 }
129 
BindDefault(CaseClause * clause)130 void SwitchBuilder::BindDefault(CaseClause* clause) {
131   default_.Bind(builder());
132   BuildBlockCoverage(clause);
133 }
134 
JumpToDefault()135 void SwitchBuilder::JumpToDefault() { this->EmitJump(&default_); }
136 
JumpToFallThroughIfFalse()137 void SwitchBuilder::JumpToFallThroughIfFalse() {
138   this->EmitJumpIfFalse(BytecodeArrayBuilder::ToBooleanMode::kAlreadyBoolean,
139                         &fall_through_);
140 }
141 
~TryCatchBuilder()142 TryCatchBuilder::~TryCatchBuilder() {
143   if (block_coverage_builder_ != nullptr) {
144     block_coverage_builder_->IncrementBlockCounter(
145         statement_, SourceRangeKind::kContinuation);
146   }
147 }
148 
BeginTry(Register context)149 void TryCatchBuilder::BeginTry(Register context) {
150   builder()->MarkTryBegin(handler_id_, context);
151 }
152 
153 
EndTry()154 void TryCatchBuilder::EndTry() {
155   builder()->MarkTryEnd(handler_id_);
156   builder()->Jump(&exit_);
157   builder()->MarkHandler(handler_id_, catch_prediction_);
158 
159   if (block_coverage_builder_ != nullptr) {
160     block_coverage_builder_->IncrementBlockCounter(statement_,
161                                                    SourceRangeKind::kCatch);
162   }
163 }
164 
EndCatch()165 void TryCatchBuilder::EndCatch() { builder()->Bind(&exit_); }
166 
~TryFinallyBuilder()167 TryFinallyBuilder::~TryFinallyBuilder() {
168   if (block_coverage_builder_ != nullptr) {
169     block_coverage_builder_->IncrementBlockCounter(
170         statement_, SourceRangeKind::kContinuation);
171   }
172 }
173 
BeginTry(Register context)174 void TryFinallyBuilder::BeginTry(Register context) {
175   builder()->MarkTryBegin(handler_id_, context);
176 }
177 
178 
LeaveTry()179 void TryFinallyBuilder::LeaveTry() {
180   builder()->Jump(finalization_sites_.New());
181 }
182 
183 
EndTry()184 void TryFinallyBuilder::EndTry() {
185   builder()->MarkTryEnd(handler_id_);
186 }
187 
188 
BeginHandler()189 void TryFinallyBuilder::BeginHandler() {
190   builder()->Bind(&handler_);
191   builder()->MarkHandler(handler_id_, catch_prediction_);
192 }
193 
BeginFinally()194 void TryFinallyBuilder::BeginFinally() {
195   finalization_sites_.Bind(builder());
196 
197   if (block_coverage_builder_ != nullptr) {
198     block_coverage_builder_->IncrementBlockCounter(statement_,
199                                                    SourceRangeKind::kFinally);
200   }
201 }
202 
EndFinally()203 void TryFinallyBuilder::EndFinally() {
204   // Nothing to be done here.
205 }
206 
~ConditionalControlFlowBuilder()207 ConditionalControlFlowBuilder::~ConditionalControlFlowBuilder() {
208   if (!else_labels_.is_bound()) else_labels_.Bind(builder());
209   end_labels_.Bind(builder());
210 
211   DCHECK(end_labels_.empty() || end_labels_.is_bound());
212   DCHECK(then_labels_.empty() || then_labels_.is_bound());
213   DCHECK(else_labels_.empty() || else_labels_.is_bound());
214 
215   // IfStatement requires a continuation counter, Conditional does not (as it
216   // can only contain expressions).
217   if (block_coverage_builder_ != nullptr && node_->IsIfStatement()) {
218     block_coverage_builder_->IncrementBlockCounter(
219         node_, SourceRangeKind::kContinuation);
220   }
221 }
222 
JumpToEnd()223 void ConditionalControlFlowBuilder::JumpToEnd() {
224   DCHECK(end_labels_.empty());  // May only be called once.
225   builder()->Jump(end_labels_.New());
226 }
227 
Then()228 void ConditionalControlFlowBuilder::Then() {
229   then_labels()->Bind(builder());
230   if (block_coverage_builder_ != nullptr) {
231     block_coverage_builder_->IncrementBlockCounter(block_coverage_then_slot_);
232   }
233 }
234 
Else()235 void ConditionalControlFlowBuilder::Else() {
236   else_labels()->Bind(builder());
237   if (block_coverage_builder_ != nullptr) {
238     block_coverage_builder_->IncrementBlockCounter(block_coverage_else_slot_);
239   }
240 }
241 
242 }  // namespace interpreter
243 }  // namespace internal
244 }  // namespace v8
245