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