OLD | NEW |
1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/ast-graph-builder.h" | 5 #include "src/compiler/ast-graph-builder.h" |
6 | 6 |
7 #include "src/compiler.h" | 7 #include "src/compiler.h" |
8 #include "src/compiler/ast-loop-assignment-analyzer.h" | 8 #include "src/compiler/ast-loop-assignment-analyzer.h" |
9 #include "src/compiler/control-builders.h" | 9 #include "src/compiler/control-builders.h" |
10 #include "src/compiler/linkage.h" | 10 #include "src/compiler/linkage.h" |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
205 // paths going through the finally-block to dispatch after leaving the block. | 205 // paths going through the finally-block to dispatch after leaving the block. |
206 class AstGraphBuilder::ControlScope::DeferredCommands : public ZoneObject { | 206 class AstGraphBuilder::ControlScope::DeferredCommands : public ZoneObject { |
207 public: | 207 public: |
208 explicit DeferredCommands(AstGraphBuilder* owner) | 208 explicit DeferredCommands(AstGraphBuilder* owner) |
209 : owner_(owner), deferred_(owner->zone()) {} | 209 : owner_(owner), deferred_(owner->zone()) {} |
210 | 210 |
211 // One recorded control-flow command. | 211 // One recorded control-flow command. |
212 struct Entry { | 212 struct Entry { |
213 Command command; // The command type being applied on this path. | 213 Command command; // The command type being applied on this path. |
214 Statement* statement; // The target statement for the command or {NULL}. | 214 Statement* statement; // The target statement for the command or {NULL}. |
215 Node* value; // The passed value node for the command or {NULL}. | |
216 Node* token; // A token identifying this particular path. | 215 Node* token; // A token identifying this particular path. |
217 }; | 216 }; |
218 | 217 |
219 // Records a control-flow command while entering the finally-block. This also | 218 // Records a control-flow command while entering the finally-block. This also |
220 // generates a new dispatch token that identifies one particular path. | 219 // generates a new dispatch token that identifies one particular path. |
221 Node* RecordCommand(Command cmd, Statement* stmt, Node* value) { | 220 Node* RecordCommand(Command cmd, Statement* stmt, Node* value) { |
222 Node* token = NewPathTokenForDeferredCommand(); | 221 Node* token = NewPathTokenForDeferredCommand(); |
223 deferred_.push_back({cmd, stmt, value, token}); | 222 deferred_.push_back({cmd, stmt, token}); |
224 return token; | 223 return token; |
225 } | 224 } |
226 | 225 |
227 // Returns the dispatch token to be used to identify the implicit fall-through | 226 // Returns the dispatch token to be used to identify the implicit fall-through |
228 // path at the end of a try-block into the corresponding finally-block. | 227 // path at the end of a try-block into the corresponding finally-block. |
229 Node* GetFallThroughToken() { return NewPathTokenForImplicitFallThrough(); } | 228 Node* GetFallThroughToken() { return NewPathTokenForImplicitFallThrough(); } |
230 | 229 |
231 // Applies all recorded control-flow commands after the finally-block again. | 230 // Applies all recorded control-flow commands after the finally-block again. |
232 // This generates a dynamic dispatch on the token from the entry point. | 231 // This generates a dynamic dispatch on the token from the entry point. |
233 void ApplyDeferredCommands(Node* token) { | 232 void ApplyDeferredCommands(Node* token, Node* value) { |
234 SwitchBuilder dispatch(owner_, static_cast<int>(deferred_.size())); | 233 SwitchBuilder dispatch(owner_, static_cast<int>(deferred_.size())); |
235 dispatch.BeginSwitch(); | 234 dispatch.BeginSwitch(); |
236 for (size_t i = 0; i < deferred_.size(); ++i) { | 235 for (size_t i = 0; i < deferred_.size(); ++i) { |
237 Node* condition = NewPathDispatchCondition(token, deferred_[i].token); | 236 Node* condition = NewPathDispatchCondition(token, deferred_[i].token); |
238 dispatch.BeginLabel(static_cast<int>(i), condition); | 237 dispatch.BeginLabel(static_cast<int>(i), condition); |
239 dispatch.EndLabel(); | 238 dispatch.EndLabel(); |
240 } | 239 } |
241 for (size_t i = 0; i < deferred_.size(); ++i) { | 240 for (size_t i = 0; i < deferred_.size(); ++i) { |
242 dispatch.BeginCase(static_cast<int>(i)); | 241 dispatch.BeginCase(static_cast<int>(i)); |
243 owner_->execution_control()->PerformCommand( | 242 owner_->execution_control()->PerformCommand( |
244 deferred_[i].command, deferred_[i].statement, deferred_[i].value); | 243 deferred_[i].command, deferred_[i].statement, value); |
245 dispatch.EndCase(); | 244 dispatch.EndCase(); |
246 } | 245 } |
247 dispatch.EndSwitch(); | 246 dispatch.EndSwitch(); |
248 } | 247 } |
249 | 248 |
250 protected: | 249 protected: |
251 Node* NewPathTokenForDeferredCommand() { | 250 Node* NewPathTokenForDeferredCommand() { |
252 return owner_->jsgraph()->Constant(static_cast<int>(deferred_.size())); | 251 return owner_->jsgraph()->Constant(static_cast<int>(deferred_.size())); |
253 } | 252 } |
254 Node* NewPathTokenForImplicitFallThrough() { | 253 Node* NewPathTokenForImplicitFallThrough() { |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
362 : ControlScope(owner), commands_(commands), control_(control) { | 361 : ControlScope(owner), commands_(commands), control_(control) { |
363 builder()->try_nesting_level_++; // Increment nesting. | 362 builder()->try_nesting_level_++; // Increment nesting. |
364 } | 363 } |
365 ~ControlScopeForFinally() { | 364 ~ControlScopeForFinally() { |
366 builder()->try_nesting_level_--; // Decrement nesting. | 365 builder()->try_nesting_level_--; // Decrement nesting. |
367 } | 366 } |
368 | 367 |
369 protected: | 368 protected: |
370 virtual bool Execute(Command cmd, Statement* target, Node* value) OVERRIDE { | 369 virtual bool Execute(Command cmd, Statement* target, Node* value) OVERRIDE { |
371 Node* token = commands_->RecordCommand(cmd, target, value); | 370 Node* token = commands_->RecordCommand(cmd, target, value); |
372 control_->LeaveTry(token); | 371 control_->LeaveTry(token, value); |
373 return true; | 372 return true; |
374 } | 373 } |
375 | 374 |
376 private: | 375 private: |
377 DeferredCommands* commands_; | 376 DeferredCommands* commands_; |
378 TryFinallyBuilder* control_; | 377 TryFinallyBuilder* control_; |
379 }; | 378 }; |
380 | 379 |
381 | 380 |
382 AstGraphBuilder::AstGraphBuilder(Zone* local_zone, CompilationInfo* info, | 381 AstGraphBuilder::AstGraphBuilder(Zone* local_zone, CompilationInfo* info, |
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
712 environment()->Trim(current->stack_height()); | 711 environment()->Trim(current->stack_height()); |
713 if (current->Execute(command, target, value)) break; | 712 if (current->Execute(command, target, value)) break; |
714 current = current->outer_; | 713 current = current->outer_; |
715 } | 714 } |
716 builder()->set_environment(env); | 715 builder()->set_environment(env); |
717 DCHECK(current != NULL); // Always handled (unless stack is malformed). | 716 DCHECK(current != NULL); // Always handled (unless stack is malformed). |
718 } | 717 } |
719 | 718 |
720 | 719 |
721 void AstGraphBuilder::ControlScope::BreakTo(BreakableStatement* stmt) { | 720 void AstGraphBuilder::ControlScope::BreakTo(BreakableStatement* stmt) { |
722 PerformCommand(CMD_BREAK, stmt, nullptr); | 721 PerformCommand(CMD_BREAK, stmt, builder()->jsgraph()->TheHoleConstant()); |
723 } | 722 } |
724 | 723 |
725 | 724 |
726 void AstGraphBuilder::ControlScope::ContinueTo(BreakableStatement* stmt) { | 725 void AstGraphBuilder::ControlScope::ContinueTo(BreakableStatement* stmt) { |
727 PerformCommand(CMD_CONTINUE, stmt, nullptr); | 726 PerformCommand(CMD_CONTINUE, stmt, builder()->jsgraph()->TheHoleConstant()); |
728 } | 727 } |
729 | 728 |
730 | 729 |
731 void AstGraphBuilder::ControlScope::ReturnValue(Node* return_value) { | 730 void AstGraphBuilder::ControlScope::ReturnValue(Node* return_value) { |
732 PerformCommand(CMD_RETURN, nullptr, return_value); | 731 PerformCommand(CMD_RETURN, nullptr, return_value); |
733 } | 732 } |
734 | 733 |
735 | 734 |
736 void AstGraphBuilder::ControlScope::ThrowValue(Node* exception_value) { | 735 void AstGraphBuilder::ControlScope::ThrowValue(Node* exception_value) { |
737 PerformCommand(CMD_THROW, nullptr, exception_value); | 736 PerformCommand(CMD_THROW, nullptr, exception_value); |
(...skipping 517 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1255 | 1254 |
1256 // We keep a record of all paths that enter the finally-block to be able to | 1255 // We keep a record of all paths that enter the finally-block to be able to |
1257 // dispatch to the correct continuation point after the statements in the | 1256 // dispatch to the correct continuation point after the statements in the |
1258 // finally-block have been evaluated. | 1257 // finally-block have been evaluated. |
1259 // | 1258 // |
1260 // The try-finally construct can enter the finally-block in three ways: | 1259 // The try-finally construct can enter the finally-block in three ways: |
1261 // 1. By exiting the try-block normally, falling through at the end. | 1260 // 1. By exiting the try-block normally, falling through at the end. |
1262 // 2. By exiting the try-block with a function-local control flow transfer | 1261 // 2. By exiting the try-block with a function-local control flow transfer |
1263 // (i.e. through break/continue/return statements). | 1262 // (i.e. through break/continue/return statements). |
1264 // 3. By exiting the try-block with a thrown exception. | 1263 // 3. By exiting the try-block with a thrown exception. |
| 1264 Node* fallthrough_result = jsgraph()->TheHoleConstant(); |
1265 ControlScope::DeferredCommands* commands = | 1265 ControlScope::DeferredCommands* commands = |
1266 new (zone()) ControlScope::DeferredCommands(this); | 1266 new (zone()) ControlScope::DeferredCommands(this); |
1267 | 1267 |
1268 // Evaluate the try-block inside a control scope. This simulates a handler | 1268 // Evaluate the try-block inside a control scope. This simulates a handler |
1269 // that is intercepting all control commands. | 1269 // that is intercepting all control commands. |
1270 try_control.BeginTry(); | 1270 try_control.BeginTry(); |
1271 { | 1271 { |
1272 ControlScopeForFinally scope(this, commands, &try_control); | 1272 ControlScopeForFinally scope(this, commands, &try_control); |
1273 Visit(stmt->try_block()); | 1273 Visit(stmt->try_block()); |
1274 } | 1274 } |
1275 try_control.EndTry(commands->GetFallThroughToken()); | 1275 try_control.EndTry(commands->GetFallThroughToken(), fallthrough_result); |
| 1276 |
| 1277 // The result value semantics depend on how the block was entered: |
| 1278 // - ReturnStatement: It represents the return value being returned. |
| 1279 // - ThrowStatement: It represents the exception being thrown. |
| 1280 // - BreakStatement/ContinueStatement: Filled with the hole. |
| 1281 // - Falling through into finally-block: Filled with the hole. |
| 1282 Node* result = try_control.GetResultValueNode(); |
| 1283 |
| 1284 // TODO(mstarzinger): See FullCodeGenerator::EnterFinallyBlock. |
| 1285 environment()->Push(jsgraph()->SmiConstant(Code::kHeaderSize)); |
| 1286 environment()->Push(result); |
| 1287 environment()->Push(jsgraph()->TheHoleConstant()); // pending_message_obj |
| 1288 environment()->Push(jsgraph()->SmiConstant(0)); // has_pending_message |
| 1289 environment()->Push(jsgraph()->TheHoleConstant()); // pending_message_script |
1276 | 1290 |
1277 // Evaluate the finally-block. | 1291 // Evaluate the finally-block. |
1278 Visit(stmt->finally_block()); | 1292 Visit(stmt->finally_block()); |
1279 try_control.EndFinally(); | 1293 try_control.EndFinally(); |
1280 | 1294 |
| 1295 // TODO(mstarzinger): See FullCodeGenerator::ExitFinallyBlock. |
| 1296 environment()->Drop(5); |
| 1297 |
1281 // Dynamic dispatch after the finally-block. | 1298 // Dynamic dispatch after the finally-block. |
1282 Node* token = try_control.GetDispatchTokenNode(); | 1299 Node* token = try_control.GetDispatchTokenNode(); |
1283 commands->ApplyDeferredCommands(token); | 1300 commands->ApplyDeferredCommands(token, result); |
1284 | 1301 |
1285 // TODO(mstarzinger): Remove bailout once everything works. | 1302 // TODO(mstarzinger): Remove bailout once everything works. |
1286 if (!FLAG_turbo_exceptions) SetStackOverflow(); | 1303 if (!FLAG_turbo_exceptions) SetStackOverflow(); |
1287 } | 1304 } |
1288 | 1305 |
1289 | 1306 |
1290 void AstGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) { | 1307 void AstGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) { |
1291 // TODO(turbofan): Do we really need a separate reloc-info for this? | 1308 // TODO(turbofan): Do we really need a separate reloc-info for this? |
1292 Node* node = NewNode(javascript()->CallRuntime(Runtime::kDebugBreak, 0)); | 1309 Node* node = NewNode(javascript()->CallRuntime(Runtime::kDebugBreak, 0)); |
1293 PrepareFrameState(node, stmt->DebugBreakId()); | 1310 PrepareFrameState(node, stmt->DebugBreakId()); |
(...skipping 1721 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3015 } | 3032 } |
3016 if (!environment()->IsMarkedAsUnreachable()) { | 3033 if (!environment()->IsMarkedAsUnreachable()) { |
3017 // Update the current control dependency for control-producing nodes. | 3034 // Update the current control dependency for control-producing nodes. |
3018 if (NodeProperties::IsControl(result)) { | 3035 if (NodeProperties::IsControl(result)) { |
3019 environment_->UpdateControlDependency(result); | 3036 environment_->UpdateControlDependency(result); |
3020 } | 3037 } |
3021 // Add implicit exception continuation for throwing nodes. | 3038 // Add implicit exception continuation for throwing nodes. |
3022 if (!result->op()->HasProperty(Operator::kNoThrow) && inside_try_scope) { | 3039 if (!result->op()->HasProperty(Operator::kNoThrow) && inside_try_scope) { |
3023 Node* on_exception = graph()->NewNode(common()->IfException(), result); | 3040 Node* on_exception = graph()->NewNode(common()->IfException(), result); |
3024 environment_->UpdateControlDependency(on_exception); | 3041 environment_->UpdateControlDependency(on_exception); |
3025 execution_control()->ThrowValue(jsgraph()->UndefinedConstant()); | 3042 execution_control()->ThrowValue(result); |
3026 } | 3043 } |
3027 // Add implicit success continuation for throwing nodes. | 3044 // Add implicit success continuation for throwing nodes. |
3028 if (!result->op()->HasProperty(Operator::kNoThrow)) { | 3045 if (!result->op()->HasProperty(Operator::kNoThrow)) { |
3029 Node* on_success = graph()->NewNode(common()->IfSuccess(), result); | 3046 Node* on_success = graph()->NewNode(common()->IfSuccess(), result); |
3030 environment_->UpdateControlDependency(on_success); | 3047 environment_->UpdateControlDependency(on_success); |
3031 } | 3048 } |
3032 } | 3049 } |
3033 } | 3050 } |
3034 | 3051 |
3035 return result; | 3052 return result; |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3233 // Phi does not exist yet, introduce one. | 3250 // Phi does not exist yet, introduce one. |
3234 value = NewPhi(inputs, value, control); | 3251 value = NewPhi(inputs, value, control); |
3235 value->ReplaceInput(inputs - 1, other); | 3252 value->ReplaceInput(inputs - 1, other); |
3236 } | 3253 } |
3237 return value; | 3254 return value; |
3238 } | 3255 } |
3239 | 3256 |
3240 } // namespace compiler | 3257 } // namespace compiler |
3241 } // namespace internal | 3258 } // namespace internal |
3242 } // namespace v8 | 3259 } // namespace v8 |
OLD | NEW |