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 depth to the backwards branch for triggering OSR.
81 // For purposes of OSR, the loop depth is capped at `kMaxOsrUrgency - 1`.
82 // Once that urgency is reached, all loops become OSR candidates.
83 //
84 // The loop must have closed form, i.e. all loop elements are within the
85 // loop, the loop header precedes the body and next elements in the loop.
86 builder()->JumpLoop(&loop_header_,
87 std::min(loop_depth, BytecodeArray::kMaxOsrUrgency - 1),
88 source_position_);
89 }
90 }
91
BindContinueTarget()92 void LoopBuilder::BindContinueTarget() { continue_labels_.Bind(builder()); }
93
BindLoopEnd()94 void LoopBuilder::BindLoopEnd() { end_labels_.Bind(builder()); }
95
~SwitchBuilder()96 SwitchBuilder::~SwitchBuilder() {
97 #ifdef DEBUG
98 for (auto site : case_sites_) {
99 DCHECK(!site.has_referrer_jump() || site.is_bound());
100 }
101 #endif
102 }
103
BindCaseTargetForJumpTable(int case_value,CaseClause * clause)104 void SwitchBuilder::BindCaseTargetForJumpTable(int case_value,
105 CaseClause* clause) {
106 builder()->Bind(jump_table_, case_value);
107 BuildBlockCoverage(clause);
108 }
109
BindCaseTargetForCompareJump(int index,CaseClause * clause)110 void SwitchBuilder::BindCaseTargetForCompareJump(int index,
111 CaseClause* clause) {
112 builder()->Bind(&case_sites_.at(index));
113 BuildBlockCoverage(clause);
114 }
115
JumpToCaseIfTrue(BytecodeArrayBuilder::ToBooleanMode mode,int index)116 void SwitchBuilder::JumpToCaseIfTrue(BytecodeArrayBuilder::ToBooleanMode mode,
117 int index) {
118 builder()->JumpIfTrue(mode, &case_sites_.at(index));
119 }
120
121 // Precondition: tag is in the accumulator
EmitJumpTableIfExists(int min_case,int max_case,std::map<int,CaseClause * > & covered_cases)122 void SwitchBuilder::EmitJumpTableIfExists(
123 int min_case, int max_case, std::map<int, CaseClause*>& covered_cases) {
124 builder()->SwitchOnSmiNoFeedback(jump_table_);
125 fall_through_.Bind(builder());
126 for (int j = min_case; j <= max_case; ++j) {
127 if (covered_cases.find(j) == covered_cases.end()) {
128 this->BindCaseTargetForJumpTable(j, nullptr);
129 }
130 }
131 }
132
BindDefault(CaseClause * clause)133 void SwitchBuilder::BindDefault(CaseClause* clause) {
134 default_.Bind(builder());
135 BuildBlockCoverage(clause);
136 }
137
JumpToDefault()138 void SwitchBuilder::JumpToDefault() { this->EmitJump(&default_); }
139
JumpToFallThroughIfFalse()140 void SwitchBuilder::JumpToFallThroughIfFalse() {
141 this->EmitJumpIfFalse(BytecodeArrayBuilder::ToBooleanMode::kAlreadyBoolean,
142 &fall_through_);
143 }
144
~TryCatchBuilder()145 TryCatchBuilder::~TryCatchBuilder() {
146 if (block_coverage_builder_ != nullptr) {
147 block_coverage_builder_->IncrementBlockCounter(
148 statement_, SourceRangeKind::kContinuation);
149 }
150 }
151
BeginTry(Register context)152 void TryCatchBuilder::BeginTry(Register context) {
153 builder()->MarkTryBegin(handler_id_, context);
154 }
155
156
EndTry()157 void TryCatchBuilder::EndTry() {
158 builder()->MarkTryEnd(handler_id_);
159 builder()->Jump(&exit_);
160 builder()->MarkHandler(handler_id_, catch_prediction_);
161
162 if (block_coverage_builder_ != nullptr) {
163 block_coverage_builder_->IncrementBlockCounter(statement_,
164 SourceRangeKind::kCatch);
165 }
166 }
167
EndCatch()168 void TryCatchBuilder::EndCatch() { builder()->Bind(&exit_); }
169
~TryFinallyBuilder()170 TryFinallyBuilder::~TryFinallyBuilder() {
171 if (block_coverage_builder_ != nullptr) {
172 block_coverage_builder_->IncrementBlockCounter(
173 statement_, SourceRangeKind::kContinuation);
174 }
175 }
176
BeginTry(Register context)177 void TryFinallyBuilder::BeginTry(Register context) {
178 builder()->MarkTryBegin(handler_id_, context);
179 }
180
181
LeaveTry()182 void TryFinallyBuilder::LeaveTry() {
183 builder()->Jump(finalization_sites_.New());
184 }
185
186
EndTry()187 void TryFinallyBuilder::EndTry() {
188 builder()->MarkTryEnd(handler_id_);
189 }
190
191
BeginHandler()192 void TryFinallyBuilder::BeginHandler() {
193 builder()->Bind(&handler_);
194 builder()->MarkHandler(handler_id_, catch_prediction_);
195 }
196
BeginFinally()197 void TryFinallyBuilder::BeginFinally() {
198 finalization_sites_.Bind(builder());
199
200 if (block_coverage_builder_ != nullptr) {
201 block_coverage_builder_->IncrementBlockCounter(statement_,
202 SourceRangeKind::kFinally);
203 }
204 }
205
EndFinally()206 void TryFinallyBuilder::EndFinally() {
207 // Nothing to be done here.
208 }
209
~ConditionalControlFlowBuilder()210 ConditionalControlFlowBuilder::~ConditionalControlFlowBuilder() {
211 if (!else_labels_.is_bound()) else_labels_.Bind(builder());
212 end_labels_.Bind(builder());
213
214 DCHECK(end_labels_.empty() || end_labels_.is_bound());
215 DCHECK(then_labels_.empty() || then_labels_.is_bound());
216 DCHECK(else_labels_.empty() || else_labels_.is_bound());
217
218 // IfStatement requires a continuation counter, Conditional does not (as it
219 // can only contain expressions).
220 if (block_coverage_builder_ != nullptr && node_->IsIfStatement()) {
221 block_coverage_builder_->IncrementBlockCounter(
222 node_, SourceRangeKind::kContinuation);
223 }
224 }
225
JumpToEnd()226 void ConditionalControlFlowBuilder::JumpToEnd() {
227 DCHECK(end_labels_.empty()); // May only be called once.
228 builder()->Jump(end_labels_.New());
229 }
230
Then()231 void ConditionalControlFlowBuilder::Then() {
232 then_labels()->Bind(builder());
233 if (block_coverage_builder_ != nullptr) {
234 block_coverage_builder_->IncrementBlockCounter(block_coverage_then_slot_);
235 }
236 }
237
Else()238 void ConditionalControlFlowBuilder::Else() {
239 else_labels()->Bind(builder());
240 if (block_coverage_builder_ != nullptr) {
241 block_coverage_builder_->IncrementBlockCounter(block_coverage_else_slot_);
242 }
243 }
244
245 } // namespace interpreter
246 } // namespace internal
247 } // namespace v8
248