Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: src/interpreter/bytecode-generator.cc

Issue 1613443002: [interpreter] Implement handling of try-finally constructs. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Add interpreter tests. Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 the V8 project authors. All rights reserved. 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 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/interpreter/bytecode-generator.h" 5 #include "src/interpreter/bytecode-generator.h"
6 6
7 #include "src/ast/scopes.h" 7 #include "src/ast/scopes.h"
8 #include "src/compiler.h" 8 #include "src/compiler.h"
9 #include "src/interpreter/bytecode-register-allocator.h" 9 #include "src/interpreter/bytecode-register-allocator.h"
10 #include "src/interpreter/control-flow-builders.h" 10 #include "src/interpreter/control-flow-builders.h"
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
81 class BytecodeGenerator::ControlScope BASE_EMBEDDED { 81 class BytecodeGenerator::ControlScope BASE_EMBEDDED {
82 public: 82 public:
83 explicit ControlScope(BytecodeGenerator* generator) 83 explicit ControlScope(BytecodeGenerator* generator)
84 : generator_(generator), outer_(generator->execution_control()) { 84 : generator_(generator), outer_(generator->execution_control()) {
85 generator_->set_execution_control(this); 85 generator_->set_execution_control(this);
86 } 86 }
87 virtual ~ControlScope() { generator_->set_execution_control(outer()); } 87 virtual ~ControlScope() { generator_->set_execution_control(outer()); }
88 88
89 void Break(Statement* stmt) { PerformCommand(CMD_BREAK, stmt); } 89 void Break(Statement* stmt) { PerformCommand(CMD_BREAK, stmt); }
90 void Continue(Statement* stmt) { PerformCommand(CMD_CONTINUE, stmt); } 90 void Continue(Statement* stmt) { PerformCommand(CMD_CONTINUE, stmt); }
91 void ReturnAccumulator() { PerformCommand(CMD_RETURN, nullptr); }
92 void ReThrowAccumulator() { PerformCommand(CMD_RETHROW, nullptr); }
93
94 class DeferredCommands;
91 95
92 protected: 96 protected:
93 enum Command { CMD_BREAK, CMD_CONTINUE }; 97 enum Command { CMD_BREAK, CMD_CONTINUE, CMD_RETURN, CMD_RETHROW };
94 void PerformCommand(Command command, Statement* statement); 98 void PerformCommand(Command command, Statement* statement);
95 virtual bool Execute(Command command, Statement* statement) = 0; 99 virtual bool Execute(Command command, Statement* statement) = 0;
96 100
97 BytecodeGenerator* generator() const { return generator_; } 101 BytecodeGenerator* generator() const { return generator_; }
98 ControlScope* outer() const { return outer_; } 102 ControlScope* outer() const { return outer_; }
99 103
100 private: 104 private:
101 BytecodeGenerator* generator_; 105 BytecodeGenerator* generator_;
102 ControlScope* outer_; 106 ControlScope* outer_;
103 107
104 DISALLOW_COPY_AND_ASSIGN(ControlScope); 108 DISALLOW_COPY_AND_ASSIGN(ControlScope);
105 }; 109 };
106 110
107 111
112 // Helper class for a try-finally control scope. It can record intercepted
113 // control-flow commands that cause entry into a finally-block, and re-apply
114 // them after again leaving that block. Special tokens are used to identify
115 // paths going through the finally-block to dispatch after leaving the block.
116 class BytecodeGenerator::ControlScope::DeferredCommands final {
117 public:
118 DeferredCommands(BytecodeGenerator* generator, Register token_register,
119 Register result_register)
120 : generator_(generator),
121 deferred_(generator->zone()),
122 token_register_(token_register),
123 result_register_(result_register) {}
124
125 // One recorded control-flow command.
126 struct Entry {
127 Command command; // The command type being applied on this path.
128 Statement* statement; // The target statement for the command or {nullptr}.
129 int token; // A token identifying this particular path.
130 };
131
132 // Records a control-flow command while entering the finally-block. This also
133 // generates a new dispatch token that identifies one particular path.
rmcilroy 2016/01/21 14:30:49 Add a comment that this expects the result to be i
Michael Starzinger 2016/01/22 10:18:11 Done.
134 void RecordCommand(Command command, Statement* statement) {
135 int token = static_cast<int>(deferred_.size());
136 deferred_.push_back({command, statement, token});
137 builder()->StoreAccumulatorInRegister(result_register_);
rmcilroy 2016/01/21 14:30:49 nit - newline above here
Michael Starzinger 2016/01/22 10:18:11 Done.
138 builder()->LoadLiteral(Smi::FromInt(token));
139 builder()->StoreAccumulatorInRegister(token_register_);
140 }
141
142 // Records the dispatch token to be used to identify the re-throw path when
143 // the finally-block has been entered through the exception handler.
rmcilroy 2016/01/21 14:30:49 Ditto (comment that exception should be in accumul
Michael Starzinger 2016/01/22 10:18:11 Done.
144 void RecordHandlerReThrowPath() {
145 // The accumulator contains the exception object.
146 RecordCommand(CMD_RETHROW, nullptr);
147 }
148
149 // Records the dispatch token to be used to identify the implicit fall-through
150 // path at the end of a try-block into the corresponding finally-block.
151 void RecordFallThroughPath() {
152 builder()->LoadLiteral(Smi::FromInt(-1));
153 builder()->StoreAccumulatorInRegister(token_register_);
154 }
155
156 // Applies all recorded control-flow commands after the finally-block again.
157 // This generates a dynamic dispatch on the token from the entry point.
158 void ApplyDeferredCommands() {
159 SwitchBuilder dispatch(builder(), static_cast<int>(deferred_.size() + 1));
oth 2016/01/21 14:06:44 Comment on the +1.
Michael Starzinger 2016/01/21 14:24:53 Done.
160 for (size_t i = 0; i < deferred_.size(); ++i) {
161 Entry& entry = deferred_[i];
162 builder()->LoadLiteral(Smi::FromInt(entry.token));
163 builder()->CompareOperation(Token::EQ_STRICT, token_register_,
164 Strength::WEAK);
165 dispatch.Case(static_cast<int>(i));
166 }
167 dispatch.DefaultAt(static_cast<int>(deferred_.size()));
rmcilroy 2016/01/21 14:30:49 I'm guessing the default case here only for the fa
Michael Starzinger 2016/01/22 10:18:11 Acknowledged. Comment already added due to Orions
168 for (size_t i = 0; i < deferred_.size(); ++i) {
169 Entry& entry = deferred_[i];
170 dispatch.SetCaseTarget(static_cast<int>(i));
171 builder()->LoadAccumulatorWithRegister(result_register_);
172 execution_control()->PerformCommand(entry.command, entry.statement);
173 }
174 dispatch.SetCaseTarget(static_cast<int>(deferred_.size()));
175 }
176
177 BytecodeArrayBuilder* builder() { return generator_->builder(); }
178 ControlScope* execution_control() { return generator_->execution_control(); }
179
180 private:
181 BytecodeGenerator* generator_;
182 ZoneVector<Entry> deferred_;
183 Register token_register_;
184 Register result_register_;
185 };
186
187
188 // Scoped class for dealing with control flow reaching the function level.
189 class BytecodeGenerator::ControlScopeForTopLevel final
190 : public BytecodeGenerator::ControlScope {
191 public:
192 explicit ControlScopeForTopLevel(BytecodeGenerator* generator)
193 : ControlScope(generator) {}
194
195 protected:
196 virtual bool Execute(Command command, Statement* statement) {
197 switch (command) {
198 case CMD_BREAK:
199 case CMD_CONTINUE:
200 break;
201 case CMD_RETURN:
202 generator()->builder()->Return();
203 return true;
204 case CMD_RETHROW:
205 // TODO(mstarzinger): Should be a ReThrow instead.
206 generator()->builder()->Throw();
207 return true;
208 }
209 return false;
210 }
211 };
212
213
108 // Scoped class for enabling break inside blocks and switch blocks. 214 // Scoped class for enabling break inside blocks and switch blocks.
109 class BytecodeGenerator::ControlScopeForBreakable final 215 class BytecodeGenerator::ControlScopeForBreakable final
110 : public BytecodeGenerator::ControlScope { 216 : public BytecodeGenerator::ControlScope {
111 public: 217 public:
112 ControlScopeForBreakable(BytecodeGenerator* generator, 218 ControlScopeForBreakable(BytecodeGenerator* generator,
113 BreakableStatement* statement, 219 BreakableStatement* statement,
114 BreakableControlFlowBuilder* control_builder) 220 BreakableControlFlowBuilder* control_builder)
115 : ControlScope(generator), 221 : ControlScope(generator),
116 statement_(statement), 222 statement_(statement),
117 control_builder_(control_builder) {} 223 control_builder_(control_builder) {}
118 224
119 protected: 225 protected:
120 virtual bool Execute(Command command, Statement* statement) { 226 virtual bool Execute(Command command, Statement* statement) {
121 if (statement != statement_) return false; 227 if (statement != statement_) return false;
122 switch (command) { 228 switch (command) {
123 case CMD_BREAK: 229 case CMD_BREAK:
124 control_builder_->Break(); 230 control_builder_->Break();
125 return true; 231 return true;
126 case CMD_CONTINUE: 232 case CMD_CONTINUE:
233 case CMD_RETURN:
234 case CMD_RETHROW:
127 break; 235 break;
128 } 236 }
129 return false; 237 return false;
130 } 238 }
131 239
132 private: 240 private:
133 Statement* statement_; 241 Statement* statement_;
134 BreakableControlFlowBuilder* control_builder_; 242 BreakableControlFlowBuilder* control_builder_;
135 }; 243 };
136 244
(...skipping 13 matching lines...) Expand all
150 protected: 258 protected:
151 virtual bool Execute(Command command, Statement* statement) { 259 virtual bool Execute(Command command, Statement* statement) {
152 if (statement != statement_) return false; 260 if (statement != statement_) return false;
153 switch (command) { 261 switch (command) {
154 case CMD_BREAK: 262 case CMD_BREAK:
155 loop_builder_->Break(); 263 loop_builder_->Break();
156 return true; 264 return true;
157 case CMD_CONTINUE: 265 case CMD_CONTINUE:
158 loop_builder_->Continue(); 266 loop_builder_->Continue();
159 return true; 267 return true;
268 case CMD_RETURN:
269 case CMD_RETHROW:
270 break;
160 } 271 }
161 return false; 272 return false;
162 } 273 }
163 274
164 private: 275 private:
165 Statement* statement_; 276 Statement* statement_;
166 LoopBuilder* loop_builder_; 277 LoopBuilder* loop_builder_;
167 }; 278 };
168 279
169 280
281 // Scoped class for enabling 'throw' in try-catch constructs.
282 class BytecodeGenerator::ControlScopeForTryCatch final
283 : public BytecodeGenerator::ControlScope {
284 public:
285 ControlScopeForTryCatch(BytecodeGenerator* generator,
286 TryCatchBuilder* try_catch_builder)
287 : ControlScope(generator), try_catch_builder_(try_catch_builder) {}
288
289 protected:
290 virtual bool Execute(Command command, Statement* statement) {
291 switch (command) {
292 case CMD_BREAK:
293 case CMD_CONTINUE:
294 case CMD_RETURN:
295 break;
296 case CMD_RETHROW:
297 USE(try_catch_builder_);
rmcilroy 2016/01/21 14:30:49 nit - add a TODO here for now?
Michael Starzinger 2016/01/22 10:18:11 Done.
298 UNIMPLEMENTED();
299 return true;
300 }
301 return false;
302 }
303
304 private:
305 TryCatchBuilder* try_catch_builder_;
306 };
307
308
309 // Scoped class for enabling control flow through try-finally constructs.
310 class BytecodeGenerator::ControlScopeForTryFinally final
311 : public BytecodeGenerator::ControlScope {
312 public:
313 ControlScopeForTryFinally(BytecodeGenerator* generator,
314 TryFinallyBuilder* try_finally_builder,
315 DeferredCommands* commands)
316 : ControlScope(generator),
317 try_finally_builder_(try_finally_builder),
318 commands_(commands) {}
319
320 protected:
321 virtual bool Execute(Command command, Statement* statement) {
322 switch (command) {
323 case CMD_BREAK:
324 case CMD_CONTINUE:
325 case CMD_RETURN:
326 case CMD_RETHROW:
327 commands_->RecordCommand(command, statement);
328 try_finally_builder_->LeaveTry();
329 return true;
330 }
331 return false;
332 }
333
334 private:
335 TryFinallyBuilder* try_finally_builder_;
336 DeferredCommands* commands_;
337 };
338
339
170 void BytecodeGenerator::ControlScope::PerformCommand(Command command, 340 void BytecodeGenerator::ControlScope::PerformCommand(Command command,
171 Statement* statement) { 341 Statement* statement) {
172 ControlScope* current = this; 342 ControlScope* current = this;
173 do { 343 do {
174 if (current->Execute(command, statement)) return; 344 if (current->Execute(command, statement)) return;
175 current = current->outer(); 345 current = current->outer();
176 } while (current != nullptr); 346 } while (current != nullptr);
177 UNREACHABLE(); 347 UNREACHABLE();
178 } 348 }
179 349
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
358 } 528 }
359 529
360 530
361 Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) { 531 Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
362 set_info(info); 532 set_info(info);
363 set_scope(info->scope()); 533 set_scope(info->scope());
364 534
365 // Initialize the incoming context. 535 // Initialize the incoming context.
366 ContextScope incoming_context(this, scope(), false); 536 ContextScope incoming_context(this, scope(), false);
367 537
538 // Initialize control scope.
539 ControlScopeForTopLevel control(this);
540
368 builder()->set_parameter_count(info->num_parameters_including_this()); 541 builder()->set_parameter_count(info->num_parameters_including_this());
369 builder()->set_locals_count(scope()->num_stack_slots()); 542 builder()->set_locals_count(scope()->num_stack_slots());
370 builder()->set_context_count(scope()->MaxNestedContextChainLength()); 543 builder()->set_context_count(scope()->MaxNestedContextChainLength());
371 544
372 // Build function context only if there are context allocated variables. 545 // Build function context only if there are context allocated variables.
373 if (scope()->NeedsContext()) { 546 if (scope()->NeedsContext()) {
374 // Push a new inner context scope for the function. 547 // Push a new inner context scope for the function.
375 VisitNewLocalFunctionContext(); 548 VisitNewLocalFunctionContext();
376 ContextScope local_function_context(this, scope(), false); 549 ContextScope local_function_context(this, scope(), false);
377 VisitBuildLocalActivationContext(); 550 VisitBuildLocalActivationContext();
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after
654 } 827 }
655 828
656 829
657 void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) { 830 void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
658 execution_control()->Break(stmt->target()); 831 execution_control()->Break(stmt->target());
659 } 832 }
660 833
661 834
662 void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { 835 void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
663 VisitForAccumulatorValue(stmt->expression()); 836 VisitForAccumulatorValue(stmt->expression());
664 builder()->Return(); 837 execution_control()->ReturnAccumulator();
665 } 838 }
666 839
667 840
668 void BytecodeGenerator::VisitWithStatement(WithStatement* stmt) { 841 void BytecodeGenerator::VisitWithStatement(WithStatement* stmt) {
669 UNIMPLEMENTED(); 842 UNIMPLEMENTED();
670 } 843 }
671 844
672 845
673 void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { 846 void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
674 // We need this scope because we visit for register values. We have to 847 // We need this scope because we visit for register values. We have to
(...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after
902 TryCatchBuilder try_control_builder(builder()); 1075 TryCatchBuilder try_control_builder(builder());
903 1076
904 // Preserve the context in a dedicated register, so that it can be restored 1077 // Preserve the context in a dedicated register, so that it can be restored
905 // when the handler is entered by the stack-unwinding machinery. 1078 // when the handler is entered by the stack-unwinding machinery.
906 // TODO(mstarzinger): Be smarter about register allocation. 1079 // TODO(mstarzinger): Be smarter about register allocation.
907 Register context = register_allocator()->NewRegister(); 1080 Register context = register_allocator()->NewRegister();
908 1081
909 // Evaluate the try-block inside a control scope. This simulates a handler 1082 // Evaluate the try-block inside a control scope. This simulates a handler
910 // that is intercepting 'throw' control commands. 1083 // that is intercepting 'throw' control commands.
911 try_control_builder.BeginTry(context); 1084 try_control_builder.BeginTry(context);
912 // TODO(mstarzinger): Control scope is missing! 1085 {
913 Visit(stmt->try_block()); 1086 ControlScopeForTryCatch scope(this, &try_control_builder);
1087 Visit(stmt->try_block());
1088 }
914 try_control_builder.EndTry(); 1089 try_control_builder.EndTry();
915 1090
916 // Clear message object as we enter the catch block. 1091 // Clear message object as we enter the catch block.
917 // TODO(mstarzinger): Implement this! 1092 // TODO(mstarzinger): Implement this!
918 1093
919 // Create a catch scope that binds the exception. 1094 // Create a catch scope that binds the exception.
920 VisitNewLocalCatchContext(stmt->variable()); 1095 VisitNewLocalCatchContext(stmt->variable());
921 1096
922 // Evaluate the catch-block. 1097 // Evaluate the catch-block.
923 VisitInScope(stmt->catch_block(), stmt->scope()); 1098 VisitInScope(stmt->catch_block(), stmt->scope());
924 try_control_builder.EndCatch(); 1099 try_control_builder.EndCatch();
925 } 1100 }
926 1101
927 1102
928 void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { 1103 void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
929 TryFinallyBuilder try_control_builder(builder()); 1104 TryFinallyBuilder try_control_builder(builder());
930 1105
1106 // We keep a record of all paths that enter the finally-block to be able to
1107 // dispatch to the correct continuation point after the statements in the
1108 // finally-block have been evaluated.
1109 //
1110 // The try-finally construct can enter the finally-block in three ways:
1111 // 1. By exiting the try-block normally, falling through at the end.
1112 // 2. By exiting the try-block with a function-local control flow transfer
1113 // (i.e. through break/continue/return statements).
1114 // 3. By exiting the try-block with a thrown exception.
1115 //
1116 // The result register semantics depend on how the block was entered:
1117 // - ReturnStatement: It represents the return value being returned.
1118 // - ThrowStatement: It represents the exception being thrown.
1119 // - BreakStatement/ContinueStatement: Undefined and not used.
1120 // - Falling through into finally-block: Undefined and not used.
1121 Register token = register_allocator()->NewRegister();
1122 Register result = register_allocator()->NewRegister();
1123 ControlScope::DeferredCommands commands(this, token, result);
1124
931 // Preserve the context in a dedicated register, so that it can be restored 1125 // Preserve the context in a dedicated register, so that it can be restored
932 // when the handler is entered by the stack-unwinding machinery. 1126 // when the handler is entered by the stack-unwinding machinery.
933 // TODO(mstarzinger): Be smarter about register allocation. 1127 // TODO(mstarzinger): Be smarter about register allocation.
934 Register context = register_allocator()->NewRegister(); 1128 Register context = register_allocator()->NewRegister();
935 1129
936 // Evaluate the try-block inside a control scope. This simulates a handler 1130 // Evaluate the try-block inside a control scope. This simulates a handler
937 // that is intercepting all control commands. 1131 // that is intercepting all control commands.
938 try_control_builder.BeginTry(context); 1132 try_control_builder.BeginTry(context);
939 // TODO(mstarzinger): Control scope is missing! 1133 {
940 Visit(stmt->try_block()); 1134 ControlScopeForTryFinally scope(this, &try_control_builder, &commands);
1135 Visit(stmt->try_block());
1136 }
941 try_control_builder.EndTry(); 1137 try_control_builder.EndTry();
942 1138
1139 // Record fall-through and exception cases.
1140 commands.RecordFallThroughPath();
1141 try_control_builder.LeaveTry();
1142 try_control_builder.BeginHandler();
1143 commands.RecordHandlerReThrowPath();
1144 try_control_builder.BeginFinally();
rmcilroy 2016/01/21 14:30:49 nit - newline before BeginFinally (I think it woul
Michael Starzinger 2016/01/22 10:18:11 Done. Also note that there is still functionality
1145
943 // Clear message object as we enter the finally block. 1146 // Clear message object as we enter the finally block.
944 // TODO(mstarzinger): Implement this! 1147 // TODO(mstarzinger): Implement this!
945 1148
946 // Evaluate the finally-block. 1149 // Evaluate the finally-block.
947 Visit(stmt->finally_block()); 1150 Visit(stmt->finally_block());
948 try_control_builder.EndFinally(); 1151 try_control_builder.EndFinally();
1152
1153 // Dynamic dispatch after the finally-block.
1154 commands.ApplyDeferredCommands();
949 } 1155 }
950 1156
951 1157
952 void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { 1158 void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
953 UNIMPLEMENTED(); 1159 UNIMPLEMENTED();
954 } 1160 }
955 1161
956 1162
957 void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { 1163 void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
958 // Find or build a shared function info. 1164 // Find or build a shared function info.
(...skipping 1319 matching lines...) Expand 10 before | Expand all | Expand 10 after
2278 } 2484 }
2279 2485
2280 2486
2281 int BytecodeGenerator::feedback_index(FeedbackVectorSlot slot) const { 2487 int BytecodeGenerator::feedback_index(FeedbackVectorSlot slot) const {
2282 return info()->feedback_vector()->GetIndex(slot); 2488 return info()->feedback_vector()->GetIndex(slot);
2283 } 2489 }
2284 2490
2285 } // namespace interpreter 2491 } // namespace interpreter
2286 } // namespace internal 2492 } // namespace internal
2287 } // namespace v8 2493 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698