Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 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/full-codegen/full-codegen.h" | 5 #include "src/full-codegen/full-codegen.h" |
| 6 | 6 |
| 7 #include "src/ast/ast.h" | 7 #include "src/ast/ast.h" |
| 8 #include "src/ast/ast-numbering.h" | 8 #include "src/ast/ast-numbering.h" |
| 9 #include "src/ast/prettyprinter.h" | 9 #include "src/ast/prettyprinter.h" |
| 10 #include "src/ast/scopeinfo.h" | 10 #include "src/ast/scopeinfo.h" |
| (...skipping 835 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 846 PrepareForBailoutForId(stmt->ThenId(), NO_REGISTERS); | 846 PrepareForBailoutForId(stmt->ThenId(), NO_REGISTERS); |
| 847 __ bind(&then_part); | 847 __ bind(&then_part); |
| 848 Visit(stmt->then_statement()); | 848 Visit(stmt->then_statement()); |
| 849 | 849 |
| 850 PrepareForBailoutForId(stmt->ElseId(), NO_REGISTERS); | 850 PrepareForBailoutForId(stmt->ElseId(), NO_REGISTERS); |
| 851 } | 851 } |
| 852 __ bind(&done); | 852 __ bind(&done); |
| 853 PrepareForBailoutForId(stmt->IfId(), NO_REGISTERS); | 853 PrepareForBailoutForId(stmt->IfId(), NO_REGISTERS); |
| 854 } | 854 } |
| 855 | 855 |
| 856 | 856 void FullCodeGenerator::EmitContinue(Statement* target) { |
| 857 void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) { | |
| 858 Comment cmnt(masm_, "[ ContinueStatement"); | |
| 859 SetStatementPosition(stmt); | |
| 860 NestedStatement* current = nesting_stack_; | 857 NestedStatement* current = nesting_stack_; |
| 861 int stack_depth = 0; | 858 int stack_depth = 0; |
| 862 int context_length = 0; | 859 int context_length = 0; |
| 863 // When continuing, we clobber the unpredictable value in the accumulator | 860 // When continuing, we clobber the unpredictable value in the accumulator |
| 864 // with one that's safe for GC. If we hit an exit from the try block of | 861 // with one that's safe for GC. If we hit an exit from the try block of |
| 865 // try...finally on our way out, we will unconditionally preserve the | 862 // try...finally on our way out, we will unconditionally preserve the |
| 866 // accumulator on the stack. | 863 // accumulator on the stack. |
| 867 ClearAccumulator(); | 864 ClearAccumulator(); |
| 868 while (!current->IsContinueTarget(stmt->target())) { | 865 while (!current->IsContinueTarget(target)) { |
| 866 if (current->IsTryFinally()) { | |
|
Michael Starzinger
2016/02/05 13:44:58
suggestion: Does it make sense to record a code co
Jarin
2016/02/05 13:55:08
Done.
| |
| 867 current->Exit(&stack_depth, &context_length); | |
| 868 DCHECK_EQ(0, stack_depth); | |
| 869 DCHECK_EQ(0, context_length); | |
| 870 current->AsTryFinally()->deferred_commands()->RecordContinue(target); | |
| 871 return; | |
| 872 } | |
| 869 current = current->Exit(&stack_depth, &context_length); | 873 current = current->Exit(&stack_depth, &context_length); |
| 870 } | 874 } |
| 871 __ Drop(stack_depth); | 875 __ Drop(stack_depth); |
| 872 if (context_length > 0) { | 876 if (context_length > 0) { |
| 873 while (context_length > 0) { | 877 while (context_length > 0) { |
| 874 LoadContextField(context_register(), Context::PREVIOUS_INDEX); | 878 LoadContextField(context_register(), Context::PREVIOUS_INDEX); |
| 875 --context_length; | 879 --context_length; |
| 876 } | 880 } |
| 877 StoreToFrameField(StandardFrameConstants::kContextOffset, | 881 StoreToFrameField(StandardFrameConstants::kContextOffset, |
| 878 context_register()); | 882 context_register()); |
| 879 } | 883 } |
| 880 | 884 |
| 881 __ jmp(current->AsIteration()->continue_label()); | 885 __ jmp(current->AsIteration()->continue_label()); |
| 882 } | 886 } |
| 883 | 887 |
| 888 void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) { | |
| 889 Comment cmnt(masm_, "[ ContinueStatement"); | |
| 890 SetStatementPosition(stmt); | |
| 891 EmitContinue(stmt->target()); | |
| 892 } | |
| 884 | 893 |
| 885 void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) { | 894 void FullCodeGenerator::EmitBreak(Statement* target) { |
| 886 Comment cmnt(masm_, "[ BreakStatement"); | |
| 887 SetStatementPosition(stmt); | |
| 888 NestedStatement* current = nesting_stack_; | 895 NestedStatement* current = nesting_stack_; |
| 889 int stack_depth = 0; | 896 int stack_depth = 0; |
| 890 int context_length = 0; | 897 int context_length = 0; |
| 891 // When breaking, we clobber the unpredictable value in the accumulator | 898 // When breaking, we clobber the unpredictable value in the accumulator |
| 892 // with one that's safe for GC. If we hit an exit from the try block of | 899 // with one that's safe for GC. If we hit an exit from the try block of |
| 893 // try...finally on our way out, we will unconditionally preserve the | 900 // try...finally on our way out, we will unconditionally preserve the |
| 894 // accumulator on the stack. | 901 // accumulator on the stack. |
| 895 ClearAccumulator(); | 902 ClearAccumulator(); |
| 896 while (!current->IsBreakTarget(stmt->target())) { | 903 while (!current->IsBreakTarget(target)) { |
| 904 if (current->IsTryFinally()) { | |
|
Michael Starzinger
2016/02/05 13:44:58
suggestion: Does it make sense to record a code co
Jarin
2016/02/05 13:55:08
Done.
| |
| 905 current->Exit(&stack_depth, &context_length); | |
| 906 DCHECK_EQ(0, stack_depth); | |
| 907 DCHECK_EQ(0, context_length); | |
| 908 current->AsTryFinally()->deferred_commands()->RecordBreak(target); | |
| 909 return; | |
| 910 } | |
| 897 current = current->Exit(&stack_depth, &context_length); | 911 current = current->Exit(&stack_depth, &context_length); |
| 898 } | 912 } |
| 899 __ Drop(stack_depth); | 913 __ Drop(stack_depth); |
| 900 if (context_length > 0) { | 914 if (context_length > 0) { |
| 901 while (context_length > 0) { | 915 while (context_length > 0) { |
| 902 LoadContextField(context_register(), Context::PREVIOUS_INDEX); | 916 LoadContextField(context_register(), Context::PREVIOUS_INDEX); |
| 903 --context_length; | 917 --context_length; |
| 904 } | 918 } |
| 905 StoreToFrameField(StandardFrameConstants::kContextOffset, | 919 StoreToFrameField(StandardFrameConstants::kContextOffset, |
| 906 context_register()); | 920 context_register()); |
| 907 } | 921 } |
| 908 | 922 |
| 909 __ jmp(current->AsBreakable()->break_label()); | 923 __ jmp(current->AsBreakable()->break_label()); |
| 910 } | 924 } |
| 911 | 925 |
| 926 void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) { | |
| 927 Comment cmnt(masm_, "[ BreakStatement"); | |
| 928 SetStatementPosition(stmt); | |
| 929 EmitBreak(stmt->target()); | |
| 930 } | |
| 912 | 931 |
| 913 void FullCodeGenerator::EmitUnwindBeforeReturn() { | 932 void FullCodeGenerator::EmitUnwindAndReturn() { |
| 914 NestedStatement* current = nesting_stack_; | 933 NestedStatement* current = nesting_stack_; |
| 915 int stack_depth = 0; | 934 int stack_depth = 0; |
| 916 int context_length = 0; | 935 int context_length = 0; |
| 917 while (current != NULL) { | 936 while (current != NULL) { |
| 937 if (current->IsTryFinally()) { | |
| 938 Comment cmnt(masm(), "[ Deferred return through finally"); | |
| 939 current->Exit(&stack_depth, &context_length); | |
| 940 DCHECK_EQ(0, stack_depth); | |
| 941 DCHECK_EQ(0, context_length); | |
| 942 current->AsTryFinally()->deferred_commands()->RecordReturn(); | |
| 943 return; | |
| 944 } | |
| 918 current = current->Exit(&stack_depth, &context_length); | 945 current = current->Exit(&stack_depth, &context_length); |
| 919 } | 946 } |
| 920 __ Drop(stack_depth); | 947 __ Drop(stack_depth); |
| 948 EmitReturnSequence(); | |
| 921 } | 949 } |
| 922 | 950 |
| 923 | 951 |
| 924 void FullCodeGenerator::EmitPropertyKey(ObjectLiteralProperty* property, | 952 void FullCodeGenerator::EmitPropertyKey(ObjectLiteralProperty* property, |
| 925 BailoutId bailout_id) { | 953 BailoutId bailout_id) { |
| 926 VisitForStackValue(property->key()); | 954 VisitForStackValue(property->key()); |
| 927 __ CallRuntime(Runtime::kToName); | 955 __ CallRuntime(Runtime::kToName); |
| 928 PrepareForBailoutForId(bailout_id, NO_REGISTERS); | 956 PrepareForBailoutForId(bailout_id, NO_REGISTERS); |
| 929 __ Push(result_register()); | 957 __ Push(result_register()); |
| 930 } | 958 } |
| 931 | 959 |
| 932 | 960 |
| 933 void FullCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { | 961 void FullCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { |
| 934 Comment cmnt(masm_, "[ ReturnStatement"); | 962 Comment cmnt(masm_, "[ ReturnStatement"); |
| 935 SetStatementPosition(stmt); | 963 SetStatementPosition(stmt); |
| 936 Expression* expr = stmt->expression(); | 964 Expression* expr = stmt->expression(); |
| 937 VisitForAccumulatorValue(expr); | 965 VisitForAccumulatorValue(expr); |
| 938 EmitUnwindBeforeReturn(); | 966 EmitUnwindAndReturn(); |
| 939 EmitReturnSequence(); | |
| 940 } | 967 } |
| 941 | 968 |
| 942 | 969 |
| 943 void FullCodeGenerator::VisitWithStatement(WithStatement* stmt) { | 970 void FullCodeGenerator::VisitWithStatement(WithStatement* stmt) { |
| 944 Comment cmnt(masm_, "[ WithStatement"); | 971 Comment cmnt(masm_, "[ WithStatement"); |
| 945 SetStatementPosition(stmt); | 972 SetStatementPosition(stmt); |
| 946 | 973 |
| 947 VisitForAccumulatorValue(stmt->expression()); | 974 VisitForAccumulatorValue(stmt->expression()); |
| 948 Callable callable = CodeFactory::ToObject(isolate()); | 975 Callable callable = CodeFactory::ToObject(isolate()); |
| 949 __ Move(callable.descriptor().GetRegisterParameter(0), result_register()); | 976 __ Move(callable.descriptor().GetRegisterParameter(0), result_register()); |
| (...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1181 | 1208 |
| 1182 | 1209 |
| 1183 void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { | 1210 void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { |
| 1184 Comment cmnt(masm_, "[ TryFinallyStatement"); | 1211 Comment cmnt(masm_, "[ TryFinallyStatement"); |
| 1185 SetStatementPosition(stmt, SKIP_BREAK); | 1212 SetStatementPosition(stmt, SKIP_BREAK); |
| 1186 | 1213 |
| 1187 // Try finally is compiled by setting up a try-handler on the stack while | 1214 // Try finally is compiled by setting up a try-handler on the stack while |
| 1188 // executing the try body, and removing it again afterwards. | 1215 // executing the try body, and removing it again afterwards. |
| 1189 // | 1216 // |
| 1190 // The try-finally construct can enter the finally block in three ways: | 1217 // The try-finally construct can enter the finally block in three ways: |
| 1191 // 1. By exiting the try-block normally. This removes the try-handler and | 1218 // 1. By exiting the try-block normally. This exits the try block, |
| 1192 // calls the finally block code before continuing. | 1219 // pushes the continuation token and falls through to the finally |
| 1220 // block. | |
| 1193 // 2. By exiting the try-block with a function-local control flow transfer | 1221 // 2. By exiting the try-block with a function-local control flow transfer |
| 1194 // (break/continue/return). The site of the, e.g., break removes the | 1222 // (break/continue/return). The site of the, e.g., break exits the |
| 1195 // try handler and calls the finally block code before continuing | 1223 // try block, pushes the continuation token and jumps to the |
| 1196 // its outward control transfer. | 1224 // finally block. After the finally block executes, the execution |
| 1197 // 3. By exiting the try-block with a thrown exception. | 1225 // continues based on the continuation token to a block that |
| 1198 // This can happen in nested function calls. It traverses the try-handler | 1226 // continues with the control flow transfer. |
| 1199 // chain and consumes the try-handler entry before jumping to the | 1227 // 3. By exiting the try-block with a thrown exception. In the handler, |
| 1200 // handler code. The handler code then calls the finally-block before | 1228 // we push the exception and continuation token and jump to the |
| 1201 // rethrowing the exception. | 1229 // finally block (which will again dispatch based on the token once |
| 1202 // | 1230 // it is finished). |
| 1203 // The finally block must assume a return address on top of the stack | 1231 |
| 1204 // (or in the link register on ARM chips) and a value (return value or | |
| 1205 // exception) in the result register (rax/eax/r0), both of which must | |
| 1206 // be preserved. The return address isn't GC-safe, so it should be | |
| 1207 // cooked before GC. | |
| 1208 Label try_entry, handler_entry, finally_entry; | 1232 Label try_entry, handler_entry, finally_entry; |
| 1233 DeferredCommands deferred(this, &finally_entry); | |
| 1209 | 1234 |
| 1210 // Jump to try-handler setup and try-block code. | 1235 // Jump to try-handler setup and try-block code. |
| 1211 __ jmp(&try_entry); | 1236 __ jmp(&try_entry); |
| 1212 __ bind(&handler_entry); | 1237 __ bind(&handler_entry); |
| 1213 PrepareForBailoutForId(stmt->HandlerId(), NO_REGISTERS); | 1238 PrepareForBailoutForId(stmt->HandlerId(), NO_REGISTERS); |
| 1214 | 1239 |
| 1215 // Exception handler code. This code is only executed when an exception | 1240 // Exception handler code. This code is only executed when an exception |
| 1216 // is thrown. The exception is in the result register, and must be | 1241 // is thrown. Record the continuation and jump to the finally block. |
| 1217 // preserved by the finally block. Call the finally block and then | 1242 { |
| 1218 // rethrow the exception if it returns. | 1243 Comment cmt_handler(masm(), "[ Finally handler"); |
| 1219 __ Call(&finally_entry); | 1244 deferred.RecordThrow(); |
| 1220 __ Push(result_register()); | |
| 1221 __ CallRuntime(Runtime::kReThrow); | |
| 1222 | |
| 1223 // Finally block implementation. | |
| 1224 __ bind(&finally_entry); | |
| 1225 EnterFinallyBlock(); | |
| 1226 { Finally finally_body(this); | |
| 1227 Visit(stmt->finally_block()); | |
| 1228 } | 1245 } |
| 1229 ExitFinallyBlock(); // Return to the calling code. | |
| 1230 | 1246 |
| 1231 // Set up try handler. | 1247 // Set up try handler. |
| 1232 __ bind(&try_entry); | 1248 __ bind(&try_entry); |
| 1233 int handler_index = NewHandlerTableEntry(); | 1249 int handler_index = NewHandlerTableEntry(); |
| 1234 EnterTryBlock(handler_index, &handler_entry); | 1250 EnterTryBlock(handler_index, &handler_entry); |
| 1235 { TryFinally try_body(this, &finally_entry); | 1251 { |
| 1252 TryFinally try_body(this, &deferred); | |
| 1236 Visit(stmt->try_block()); | 1253 Visit(stmt->try_block()); |
| 1237 } | 1254 } |
| 1238 ExitTryBlock(handler_index); | 1255 ExitTryBlock(handler_index); |
| 1239 // Execute the finally block on the way out. Clobber the unpredictable | 1256 // Execute the finally block on the way out. Clobber the unpredictable |
| 1240 // value in the result register with one that's safe for GC because the | 1257 // value in the result register with one that's safe for GC because the |
| 1241 // finally block will unconditionally preserve the result register on the | 1258 // finally block will unconditionally preserve the result register on the |
| 1242 // stack. | 1259 // stack. |
| 1243 ClearAccumulator(); | 1260 ClearAccumulator(); |
| 1244 __ Call(&finally_entry); | 1261 deferred.EmitFallThrough(); |
| 1262 // Fall through to the finally block. | |
| 1263 | |
| 1264 // Finally block implementation. | |
| 1265 __ bind(&finally_entry); | |
| 1266 Comment cmnt_finally(masm(), "[ Finally block"); | |
| 1267 EnterFinallyBlock(); | |
| 1268 { | |
| 1269 Finally finally_body(this); | |
| 1270 Visit(stmt->finally_block()); | |
| 1271 } | |
| 1272 ExitFinallyBlock(); // Return to the calling code. | |
| 1273 | |
| 1274 { | |
| 1275 Comment cmnt_deferred(masm(), "[ Post-finally dispatch"); | |
| 1276 deferred.EmitCommands(); | |
| 1277 } | |
| 1245 } | 1278 } |
| 1246 | 1279 |
| 1247 | 1280 |
| 1248 void FullCodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { | 1281 void FullCodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { |
| 1249 Comment cmnt(masm_, "[ DebuggerStatement"); | 1282 Comment cmnt(masm_, "[ DebuggerStatement"); |
| 1250 SetStatementPosition(stmt); | 1283 SetStatementPosition(stmt); |
| 1251 | 1284 |
| 1252 __ DebugBreak(); | 1285 __ DebugBreak(); |
| 1253 // Ignore the return value. | 1286 // Ignore the return value. |
| 1254 | 1287 |
| (...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1478 __ Drop(*stack_depth); // Down to the handler block. | 1511 __ Drop(*stack_depth); // Down to the handler block. |
| 1479 // Restore the context to its dedicated register and the stack. | 1512 // Restore the context to its dedicated register and the stack. |
| 1480 STATIC_ASSERT(TryFinally::kElementCount == 1); | 1513 STATIC_ASSERT(TryFinally::kElementCount == 1); |
| 1481 __ Pop(codegen_->context_register()); | 1514 __ Pop(codegen_->context_register()); |
| 1482 codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset, | 1515 codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset, |
| 1483 codegen_->context_register()); | 1516 codegen_->context_register()); |
| 1484 } else { | 1517 } else { |
| 1485 // Down to the handler block and also drop context. | 1518 // Down to the handler block and also drop context. |
| 1486 __ Drop(*stack_depth + kElementCount); | 1519 __ Drop(*stack_depth + kElementCount); |
| 1487 } | 1520 } |
| 1488 __ Call(finally_entry_); | |
| 1489 | |
| 1490 *stack_depth = 0; | 1521 *stack_depth = 0; |
| 1491 *context_length = 0; | 1522 *context_length = 0; |
| 1492 return previous_; | 1523 return previous_; |
| 1493 } | 1524 } |
| 1494 | 1525 |
| 1526 void FullCodeGenerator::DeferredCommands::RecordBreak(Statement* target) { | |
| 1527 TokenId token = dispenser_.GetBreakContinueToken(); | |
| 1528 commands_.push_back({kBreak, token, target}); | |
| 1529 EmitJumpToFinally(token); | |
| 1530 } | |
| 1531 | |
| 1532 void FullCodeGenerator::DeferredCommands::RecordContinue(Statement* target) { | |
| 1533 TokenId token = dispenser_.GetBreakContinueToken(); | |
| 1534 commands_.push_back({kContinue, token, target}); | |
| 1535 EmitJumpToFinally(token); | |
| 1536 } | |
| 1537 | |
| 1538 void FullCodeGenerator::DeferredCommands::RecordReturn() { | |
| 1539 if (return_token_ == TokenDispenserForFinally::kInvalidToken) { | |
| 1540 return_token_ = TokenDispenserForFinally::kReturnToken; | |
| 1541 commands_.push_back({kReturn, return_token_, nullptr}); | |
| 1542 } | |
| 1543 EmitJumpToFinally(return_token_); | |
| 1544 } | |
| 1545 | |
| 1546 void FullCodeGenerator::DeferredCommands::RecordThrow() { | |
| 1547 if (throw_token_ == TokenDispenserForFinally::kInvalidToken) { | |
| 1548 throw_token_ = TokenDispenserForFinally::kThrowToken; | |
| 1549 commands_.push_back({kThrow, throw_token_, nullptr}); | |
| 1550 } | |
| 1551 EmitJumpToFinally(throw_token_); | |
| 1552 } | |
| 1553 | |
| 1554 void FullCodeGenerator::DeferredCommands::EmitFallThrough() { | |
| 1555 __ Push(Smi::FromInt(TokenDispenserForFinally::kFallThroughToken)); | |
| 1556 __ Push(result_register()); | |
| 1557 } | |
| 1558 | |
| 1559 void FullCodeGenerator::DeferredCommands::EmitJumpToFinally(TokenId token) { | |
| 1560 __ Push(Smi::FromInt(token)); | |
| 1561 __ Push(result_register()); | |
| 1562 __ jmp(finally_entry_); | |
| 1563 } | |
| 1495 | 1564 |
| 1496 bool FullCodeGenerator::TryLiteralCompare(CompareOperation* expr) { | 1565 bool FullCodeGenerator::TryLiteralCompare(CompareOperation* expr) { |
| 1497 Expression* sub_expr; | 1566 Expression* sub_expr; |
| 1498 Handle<String> check; | 1567 Handle<String> check; |
| 1499 if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) { | 1568 if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) { |
| 1500 EmitLiteralCompareTypeof(expr, sub_expr, check); | 1569 EmitLiteralCompareTypeof(expr, sub_expr, check); |
| 1501 return true; | 1570 return true; |
| 1502 } | 1571 } |
| 1503 | 1572 |
| 1504 if (expr->IsLiteralCompareUndefined(&sub_expr, isolate())) { | 1573 if (expr->IsLiteralCompareUndefined(&sub_expr, isolate())) { |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1709 return var->mode() == CONST_LEGACY || var->scope()->is_nonlinear() || | 1778 return var->mode() == CONST_LEGACY || var->scope()->is_nonlinear() || |
| 1710 var->initializer_position() >= proxy->position(); | 1779 var->initializer_position() >= proxy->position(); |
| 1711 } | 1780 } |
| 1712 | 1781 |
| 1713 | 1782 |
| 1714 #undef __ | 1783 #undef __ |
| 1715 | 1784 |
| 1716 | 1785 |
| 1717 } // namespace internal | 1786 } // namespace internal |
| 1718 } // namespace v8 | 1787 } // namespace v8 |
| OLD | NEW |