| OLD | NEW |
| 1 // Copyright 2006-2008 the V8 project authors. All rights reserved. | 1 // Copyright 2006-2008 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 147 Result answer = frame_->CallStub(&stub, 3); | 147 Result answer = frame_->CallStub(&stub, 3); |
| 148 frame_->Push(&answer); | 148 frame_->Push(&answer); |
| 149 } | 149 } |
| 150 | 150 |
| 151 if (scope_->num_heap_slots() > 0) { | 151 if (scope_->num_heap_slots() > 0) { |
| 152 Comment cmnt(masm_, "[ allocate local context"); | 152 Comment cmnt(masm_, "[ allocate local context"); |
| 153 // Allocate local context. | 153 // Allocate local context. |
| 154 // Get outer context and create a new context based on it. | 154 // Get outer context and create a new context based on it. |
| 155 frame_->PushFunction(); | 155 frame_->PushFunction(); |
| 156 Result context = frame_->CallRuntime(Runtime::kNewContext, 1); | 156 Result context = frame_->CallRuntime(Runtime::kNewContext, 1); |
| 157 |
| 157 // Update context local. | 158 // Update context local. |
| 158 frame_->SaveContextRegister(); | 159 frame_->SaveContextRegister(); |
| 159 | 160 |
| 160 if (kDebug) { | 161 // Verify that the runtime call result and esi agree. |
| 161 JumpTarget verified_true(this); | 162 if (FLAG_debug_code) { |
| 162 // Verify eax and esi are the same in debug mode | |
| 163 __ cmp(context.reg(), Operand(esi)); | 163 __ cmp(context.reg(), Operand(esi)); |
| 164 context.Unuse(); | 164 __ Assert(equal, "Runtime::NewContext should end up in esi"); |
| 165 verified_true.Branch(equal); | |
| 166 frame_->SpillAll(); | |
| 167 __ int3(); | |
| 168 verified_true.Bind(); | |
| 169 } | 165 } |
| 170 } | 166 } |
| 171 | 167 |
| 172 // TODO(1241774): Improve this code: | 168 // TODO(1241774): Improve this code: |
| 173 // 1) only needed if we have a context | 169 // 1) only needed if we have a context |
| 174 // 2) no need to recompute context ptr every single time | 170 // 2) no need to recompute context ptr every single time |
| 175 // 3) don't copy parameter operand code from SlotOperand! | 171 // 3) don't copy parameter operand code from SlotOperand! |
| 176 { | 172 { |
| 177 Comment cmnt2(masm_, "[ copy context parameters into .context"); | 173 Comment cmnt2(masm_, "[ copy context parameters into .context"); |
| 178 | 174 |
| (...skipping 550 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 729 return OpBits::encode(op_) | | 725 return OpBits::encode(op_) | |
| 730 ModeBits::encode(mode_) | | 726 ModeBits::encode(mode_) | |
| 731 FlagBits::encode(flags_); | 727 FlagBits::encode(flags_); |
| 732 } | 728 } |
| 733 void Generate(MacroAssembler* masm); | 729 void Generate(MacroAssembler* masm); |
| 734 }; | 730 }; |
| 735 | 731 |
| 736 | 732 |
| 737 const char* GenericBinaryOpStub::GetName() { | 733 const char* GenericBinaryOpStub::GetName() { |
| 738 switch (op_) { | 734 switch (op_) { |
| 739 case Token::ADD: return "GenericBinaryOpStub_ADD"; | 735 case Token::ADD: return "GenericBinaryOpStub_ADD"; |
| 740 case Token::SUB: return "GenericBinaryOpStub_SUB"; | 736 case Token::SUB: return "GenericBinaryOpStub_SUB"; |
| 741 case Token::MUL: return "GenericBinaryOpStub_MUL"; | 737 case Token::MUL: return "GenericBinaryOpStub_MUL"; |
| 742 case Token::DIV: return "GenericBinaryOpStub_DIV"; | 738 case Token::DIV: return "GenericBinaryOpStub_DIV"; |
| 743 case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR"; | 739 case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR"; |
| 744 case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND"; | 740 case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND"; |
| 745 case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR"; | 741 case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR"; |
| 746 case Token::SAR: return "GenericBinaryOpStub_SAR"; | 742 case Token::SAR: return "GenericBinaryOpStub_SAR"; |
| 747 case Token::SHL: return "GenericBinaryOpStub_SHL"; | 743 case Token::SHL: return "GenericBinaryOpStub_SHL"; |
| 748 case Token::SHR: return "GenericBinaryOpStub_SHR"; | 744 case Token::SHR: return "GenericBinaryOpStub_SHR"; |
| 749 default: return "GenericBinaryOpStub"; | 745 default: return "GenericBinaryOpStub"; |
| 750 } | 746 } |
| 751 } | 747 } |
| 752 | 748 |
| 753 | 749 |
| 754 class DeferredInlineBinaryOperation: public DeferredCode { | 750 class DeferredInlineBinaryOperation: public DeferredCode { |
| 755 public: | 751 public: |
| 756 DeferredInlineBinaryOperation(CodeGenerator* generator, | 752 DeferredInlineBinaryOperation(CodeGenerator* generator, |
| 757 Token::Value op, | 753 Token::Value op, |
| 758 OverwriteMode mode, | 754 OverwriteMode mode, |
| 759 GenericBinaryFlags flags) | 755 GenericBinaryFlags flags) |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 833 GenericBinaryOpStub stub(op, overwrite_mode, flags); | 829 GenericBinaryOpStub stub(op, overwrite_mode, flags); |
| 834 Result answer = frame_->CallStub(&stub, 2); | 830 Result answer = frame_->CallStub(&stub, 2); |
| 835 frame_->Push(&answer); | 831 frame_->Push(&answer); |
| 836 } | 832 } |
| 837 } | 833 } |
| 838 | 834 |
| 839 | 835 |
| 840 class DeferredInlineSmiOperation: public DeferredCode { | 836 class DeferredInlineSmiOperation: public DeferredCode { |
| 841 public: | 837 public: |
| 842 DeferredInlineSmiOperation(CodeGenerator* generator, | 838 DeferredInlineSmiOperation(CodeGenerator* generator, |
| 843 Token::Value op, | 839 Token::Value op, |
| 844 Smi* value, | 840 Smi* value, |
| 845 OverwriteMode overwrite_mode) | 841 OverwriteMode overwrite_mode) |
| 846 : DeferredCode(generator), | 842 : DeferredCode(generator), |
| 847 op_(op), | 843 op_(op), |
| 848 value_(value), | 844 value_(value), |
| 849 overwrite_mode_(overwrite_mode) { | 845 overwrite_mode_(overwrite_mode) { |
| 850 set_comment("[ DeferredInlineSmiOperation"); | 846 set_comment("[ DeferredInlineSmiOperation"); |
| 851 } | 847 } |
| 852 | 848 |
| 853 virtual void Generate(); | 849 virtual void Generate(); |
| 854 | 850 |
| 855 private: | 851 private: |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1116 if (reversed) { | 1112 if (reversed) { |
| 1117 Result top = frame_->Pop(); | 1113 Result top = frame_->Pop(); |
| 1118 frame_->Push(value); | 1114 frame_->Push(value); |
| 1119 frame_->Push(&top); | 1115 frame_->Push(&top); |
| 1120 GenericBinaryOperation(op, type, overwrite_mode); | 1116 GenericBinaryOperation(op, type, overwrite_mode); |
| 1121 } else { | 1117 } else { |
| 1122 // Only the least significant 5 bits of the shift value are used. | 1118 // Only the least significant 5 bits of the shift value are used. |
| 1123 // In the slow case, this masking is done inside the runtime call. | 1119 // In the slow case, this masking is done inside the runtime call. |
| 1124 int shift_value = int_value & 0x1f; | 1120 int shift_value = int_value & 0x1f; |
| 1125 DeferredCode* deferred = | 1121 DeferredCode* deferred = |
| 1126 new DeferredInlineSmiOperation(this, Token::SAR, smi_value, | 1122 new DeferredInlineSmiOperation(this, Token::SAR, smi_value, |
| 1127 overwrite_mode); | 1123 overwrite_mode); |
| 1128 Result result = frame_->Pop(); | 1124 Result result = frame_->Pop(); |
| 1129 result.ToRegister(); | 1125 result.ToRegister(); |
| 1130 __ test(result.reg(), Immediate(kSmiTagMask)); | 1126 __ test(result.reg(), Immediate(kSmiTagMask)); |
| 1131 deferred->enter()->Branch(not_zero, &result, not_taken); | 1127 deferred->enter()->Branch(not_zero, &result, not_taken); |
| 1132 frame_->Spill(result.reg()); | 1128 frame_->Spill(result.reg()); |
| 1133 __ sar(result.reg(), shift_value); | 1129 __ sar(result.reg(), shift_value); |
| 1134 __ and_(result.reg(), ~kSmiTagMask); | 1130 __ and_(result.reg(), ~kSmiTagMask); |
| 1135 deferred->BindExit(&result); | 1131 deferred->BindExit(&result); |
| 1136 frame_->Push(&result); | 1132 frame_->Push(&result); |
| 1137 } | 1133 } |
| 1138 break; | 1134 break; |
| 1139 } | 1135 } |
| 1140 | 1136 |
| 1141 case Token::SHR: { | 1137 case Token::SHR: { |
| 1142 if (reversed) { | 1138 if (reversed) { |
| 1143 Result top = frame_->Pop(); | 1139 Result top = frame_->Pop(); |
| 1144 frame_->Push(value); | 1140 frame_->Push(value); |
| 1145 frame_->Push(&top); | 1141 frame_->Push(&top); |
| 1146 GenericBinaryOperation(op, type, overwrite_mode); | 1142 GenericBinaryOperation(op, type, overwrite_mode); |
| 1147 } else { | 1143 } else { |
| 1148 // Only the least significant 5 bits of the shift value are used. | 1144 // Only the least significant 5 bits of the shift value are used. |
| 1149 // In the slow case, this masking is done inside the runtime call. | 1145 // In the slow case, this masking is done inside the runtime call. |
| 1150 int shift_value = int_value & 0x1f; | 1146 int shift_value = int_value & 0x1f; |
| 1151 DeferredCode* deferred = | 1147 DeferredCode* deferred = |
| 1152 new DeferredInlineSmiOperation(this, Token::SHR, smi_value, | 1148 new DeferredInlineSmiOperation(this, Token::SHR, smi_value, |
| 1153 overwrite_mode); | 1149 overwrite_mode); |
| 1154 Result operand = frame_->Pop(); | 1150 Result operand = frame_->Pop(); |
| 1155 operand.ToRegister(); | 1151 operand.ToRegister(); |
| 1156 __ test(operand.reg(), Immediate(kSmiTagMask)); | 1152 __ test(operand.reg(), Immediate(kSmiTagMask)); |
| 1157 deferred->enter()->Branch(not_zero, &operand, not_taken); | 1153 deferred->enter()->Branch(not_zero, &operand, not_taken); |
| 1158 Result answer = allocator()->Allocate(); | 1154 Result answer = allocator()->Allocate(); |
| 1159 ASSERT(answer.is_valid()); | 1155 ASSERT(answer.is_valid()); |
| 1160 __ mov(answer.reg(), Operand(operand.reg())); | 1156 __ mov(answer.reg(), Operand(operand.reg())); |
| 1161 __ sar(answer.reg(), kSmiTagSize); | 1157 __ sar(answer.reg(), kSmiTagSize); |
| 1162 __ shr(answer.reg(), shift_value); | 1158 __ shr(answer.reg(), shift_value); |
| 1163 // A negative Smi shifted right two is in the positive Smi range. | 1159 // A negative Smi shifted right two is in the positive Smi range. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 1179 if (reversed) { | 1175 if (reversed) { |
| 1180 Result top = frame_->Pop(); | 1176 Result top = frame_->Pop(); |
| 1181 frame_->Push(value); | 1177 frame_->Push(value); |
| 1182 frame_->Push(&top); | 1178 frame_->Push(&top); |
| 1183 GenericBinaryOperation(op, type, overwrite_mode); | 1179 GenericBinaryOperation(op, type, overwrite_mode); |
| 1184 } else { | 1180 } else { |
| 1185 // Only the least significant 5 bits of the shift value are used. | 1181 // Only the least significant 5 bits of the shift value are used. |
| 1186 // In the slow case, this masking is done inside the runtime call. | 1182 // In the slow case, this masking is done inside the runtime call. |
| 1187 int shift_value = int_value & 0x1f; | 1183 int shift_value = int_value & 0x1f; |
| 1188 DeferredCode* deferred = | 1184 DeferredCode* deferred = |
| 1189 new DeferredInlineSmiOperation(this, Token::SHL, smi_value, | 1185 new DeferredInlineSmiOperation(this, Token::SHL, smi_value, |
| 1190 overwrite_mode); | 1186 overwrite_mode); |
| 1191 Result operand = frame_->Pop(); | 1187 Result operand = frame_->Pop(); |
| 1192 operand.ToRegister(); | 1188 operand.ToRegister(); |
| 1193 __ test(operand.reg(), Immediate(kSmiTagMask)); | 1189 __ test(operand.reg(), Immediate(kSmiTagMask)); |
| 1194 deferred->enter()->Branch(not_zero, &operand, not_taken); | 1190 deferred->enter()->Branch(not_zero, &operand, not_taken); |
| 1195 Result answer = allocator()->Allocate(); | 1191 Result answer = allocator()->Allocate(); |
| 1196 ASSERT(answer.is_valid()); | 1192 ASSERT(answer.is_valid()); |
| 1197 __ mov(answer.reg(), Operand(operand.reg())); | 1193 __ mov(answer.reg(), Operand(operand.reg())); |
| 1198 ASSERT(kSmiTag == 0); // adjust code if not the case | 1194 ASSERT(kSmiTag == 0); // adjust code if not the case |
| 1199 // We do no shifts, only the Smi conversion, if shift_value is 1. | 1195 // We do no shifts, only the Smi conversion, if shift_value is 1. |
| 1200 if (shift_value == 0) { | 1196 if (shift_value == 0) { |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1303 Result right_side(this); | 1299 Result right_side(this); |
| 1304 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order. | 1300 // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order. |
| 1305 if (cc == greater || cc == less_equal) { | 1301 if (cc == greater || cc == less_equal) { |
| 1306 cc = ReverseCondition(cc); | 1302 cc = ReverseCondition(cc); |
| 1307 left_side = frame_->Pop(); | 1303 left_side = frame_->Pop(); |
| 1308 right_side = frame_->Pop(); | 1304 right_side = frame_->Pop(); |
| 1309 } else { | 1305 } else { |
| 1310 right_side = frame_->Pop(); | 1306 right_side = frame_->Pop(); |
| 1311 left_side = frame_->Pop(); | 1307 left_side = frame_->Pop(); |
| 1312 } | 1308 } |
| 1309 ASSERT(cc == less || cc == equal || cc == greater_equal); |
| 1310 |
| 1313 // If either side is a constant smi, optimize the comparison. | 1311 // If either side is a constant smi, optimize the comparison. |
| 1314 bool left_side_constant_smi = | 1312 bool left_side_constant_smi = |
| 1315 left_side.is_constant() && left_side.handle()->IsSmi(); | 1313 left_side.is_constant() && left_side.handle()->IsSmi(); |
| 1316 bool right_side_constant_smi = | 1314 bool right_side_constant_smi = |
| 1317 right_side.is_constant() && right_side.handle()->IsSmi(); | 1315 right_side.is_constant() && right_side.handle()->IsSmi(); |
| 1318 bool left_side_constant_null = | 1316 bool left_side_constant_null = |
| 1319 left_side.is_constant() && left_side.handle()->IsNull(); | 1317 left_side.is_constant() && left_side.handle()->IsNull(); |
| 1320 bool right_side_constant_null = | 1318 bool right_side_constant_null = |
| 1321 right_side.is_constant() && right_side.handle()->IsNull(); | 1319 right_side.is_constant() && right_side.handle()->IsNull(); |
| 1322 | 1320 |
| 1323 if (left_side_constant_smi || right_side_constant_smi) { | 1321 if (left_side_constant_smi || right_side_constant_smi) { |
| 1324 if (left_side_constant_smi && right_side_constant_smi) { | 1322 if (left_side_constant_smi && right_side_constant_smi) { |
| 1325 // Trivial case, comparing two constants. | 1323 // Trivial case, comparing two constants. |
| 1326 int left_value = Smi::cast(*left_side.handle())->value(); | 1324 int left_value = Smi::cast(*left_side.handle())->value(); |
| 1327 int right_value = Smi::cast(*right_side.handle())->value(); | 1325 int right_value = Smi::cast(*right_side.handle())->value(); |
| 1328 if (left_value < right_value && | 1326 switch (cc) { |
| 1329 (cc == less || cc == less_equal || cc == not_equal) || | 1327 case less: |
| 1330 left_value == right_value && | 1328 dest->Goto(left_value < right_value); |
| 1331 (cc == less_equal || cc == equal || cc == greater_equal) || | 1329 break; |
| 1332 left_value > right_value && | 1330 case equal: |
| 1333 (cc == greater || cc == greater_equal || cc == not_equal)) { | 1331 dest->Goto(left_value == right_value); |
| 1334 // The comparison is unconditionally true. | 1332 break; |
| 1335 dest->Goto(true); | 1333 case greater_equal: |
| 1336 } else { | 1334 dest->Goto(left_value >= right_value); |
| 1337 // The comparison is unconditionally false. | 1335 break; |
| 1338 dest->Goto(false); | 1336 default: |
| 1337 UNREACHABLE(); |
| 1339 } | 1338 } |
| 1340 } else { // Only one side is a constant Smi. | 1339 } else { // Only one side is a constant Smi. |
| 1341 // If left side is a constant Smi, reverse the operands. | 1340 // If left side is a constant Smi, reverse the operands. |
| 1342 // Since one side is a constant Smi, conversion order does not matter. | 1341 // Since one side is a constant Smi, conversion order does not matter. |
| 1343 if (left_side_constant_smi) { | 1342 if (left_side_constant_smi) { |
| 1344 Result temp = left_side; | 1343 Result temp = left_side; |
| 1345 left_side = right_side; | 1344 left_side = right_side; |
| 1346 right_side = temp; | 1345 right_side = temp; |
| 1347 cc = ReverseCondition(cc); | 1346 cc = ReverseCondition(cc); |
| 1348 // This may reintroduce greater or less_equal as the value of cc. | 1347 // This may reintroduce greater or less_equal as the value of cc. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 1369 dest->false_target()->Jump(); | 1368 dest->false_target()->Jump(); |
| 1370 | 1369 |
| 1371 is_smi.Bind(&left_side, &right_side); | 1370 is_smi.Bind(&left_side, &right_side); |
| 1372 left_side.ToRegister(); | 1371 left_side.ToRegister(); |
| 1373 // Test smi equality and comparison by signed int comparison. | 1372 // Test smi equality and comparison by signed int comparison. |
| 1374 if (IsUnsafeSmi(right_side.handle())) { | 1373 if (IsUnsafeSmi(right_side.handle())) { |
| 1375 right_side.ToRegister(); | 1374 right_side.ToRegister(); |
| 1376 ASSERT(right_side.is_valid()); | 1375 ASSERT(right_side.is_valid()); |
| 1377 __ cmp(left_side.reg(), Operand(right_side.reg())); | 1376 __ cmp(left_side.reg(), Operand(right_side.reg())); |
| 1378 } else { | 1377 } else { |
| 1379 __ cmp(Operand(left_side.reg()), Immediate(right_side.handle())); | 1378 __ cmp(Operand(left_side.reg()), Immediate(right_side.handle())); |
| 1380 } | 1379 } |
| 1381 left_side.Unuse(); | 1380 left_side.Unuse(); |
| 1382 right_side.Unuse(); | 1381 right_side.Unuse(); |
| 1383 dest->Split(cc); | 1382 dest->Split(cc); |
| 1384 } | 1383 } |
| 1385 } else if (cc == equal && | 1384 } else if (cc == equal && |
| 1386 (left_side_constant_null || right_side_constant_null)) { | 1385 (left_side_constant_null || right_side_constant_null)) { |
| 1387 // To make null checks efficient, we check if either the left side or | 1386 // To make null checks efficient, we check if either the left side or |
| 1388 // the right side is the constant 'null'. | 1387 // the right side is the constant 'null'. |
| 1389 // If so, we optimize the code by inlining a null check instead of | 1388 // If so, we optimize the code by inlining a null check instead of |
| (...skipping 24 matching lines...) Expand all Loading... |
| 1414 __ movzx_b(temp.reg(), | 1413 __ movzx_b(temp.reg(), |
| 1415 FieldOperand(temp.reg(), Map::kBitFieldOffset)); | 1414 FieldOperand(temp.reg(), Map::kBitFieldOffset)); |
| 1416 __ test(temp.reg(), Immediate(1 << Map::kIsUndetectable)); | 1415 __ test(temp.reg(), Immediate(1 << Map::kIsUndetectable)); |
| 1417 temp.Unuse(); | 1416 temp.Unuse(); |
| 1418 operand.Unuse(); | 1417 operand.Unuse(); |
| 1419 dest->Split(not_zero); | 1418 dest->Split(not_zero); |
| 1420 } | 1419 } |
| 1421 } else { // Neither side is a constant Smi or null. | 1420 } else { // Neither side is a constant Smi or null. |
| 1422 // If either side is a non-smi constant, skip the smi check. | 1421 // If either side is a non-smi constant, skip the smi check. |
| 1423 bool known_non_smi = | 1422 bool known_non_smi = |
| 1424 left_side.is_constant() && !left_side.handle()->IsSmi() || | 1423 (left_side.is_constant() && !left_side.handle()->IsSmi()) || |
| 1425 right_side.is_constant() && !right_side.handle()->IsSmi(); | 1424 (right_side.is_constant() && !right_side.handle()->IsSmi()); |
| 1426 left_side.ToRegister(); | 1425 left_side.ToRegister(); |
| 1427 right_side.ToRegister(); | 1426 right_side.ToRegister(); |
| 1428 JumpTarget is_smi(this); | 1427 JumpTarget is_smi(this); |
| 1429 if (!known_non_smi) { | 1428 if (!known_non_smi) { |
| 1430 // Check for the smi case. | 1429 // Check for the smi case. |
| 1431 Result temp = allocator_->Allocate(); | 1430 Result temp = allocator_->Allocate(); |
| 1432 ASSERT(temp.is_valid()); | 1431 ASSERT(temp.is_valid()); |
| 1433 __ mov(temp.reg(), left_side.reg()); | 1432 __ mov(temp.reg(), left_side.reg()); |
| 1434 __ or_(temp.reg(), Operand(right_side.reg())); | 1433 __ or_(temp.reg(), Operand(right_side.reg())); |
| 1435 __ test(temp.reg(), Immediate(kSmiTagMask)); | 1434 __ test(temp.reg(), Immediate(kSmiTagMask)); |
| (...skipping 427 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1863 Result context(this); | 1862 Result context(this); |
| 1864 if (node->is_catch_block()) { | 1863 if (node->is_catch_block()) { |
| 1865 context = frame_->CallRuntime(Runtime::kPushCatchContext, 1); | 1864 context = frame_->CallRuntime(Runtime::kPushCatchContext, 1); |
| 1866 } else { | 1865 } else { |
| 1867 context = frame_->CallRuntime(Runtime::kPushContext, 1); | 1866 context = frame_->CallRuntime(Runtime::kPushContext, 1); |
| 1868 } | 1867 } |
| 1869 | 1868 |
| 1870 // Update context local. | 1869 // Update context local. |
| 1871 frame_->SaveContextRegister(); | 1870 frame_->SaveContextRegister(); |
| 1872 | 1871 |
| 1873 if (kDebug) { | 1872 // Verify that the runtime call result and esi agree. |
| 1874 JumpTarget verified_true(this); | 1873 if (FLAG_debug_code) { |
| 1875 // Verify that the result of the runtime call and the esi register are | |
| 1876 // the same in debug mode. | |
| 1877 __ cmp(context.reg(), Operand(esi)); | 1874 __ cmp(context.reg(), Operand(esi)); |
| 1878 context.Unuse(); | 1875 __ Assert(equal, "Runtime::NewContext should end up in esi"); |
| 1879 verified_true.Branch(equal); | |
| 1880 frame_->SpillAll(); | |
| 1881 __ int3(); | |
| 1882 verified_true.Bind(); | |
| 1883 } | 1876 } |
| 1884 } | 1877 } |
| 1885 | 1878 |
| 1886 | 1879 |
| 1887 void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) { | 1880 void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) { |
| 1888 ASSERT(!in_spilled_code()); | 1881 ASSERT(!in_spilled_code()); |
| 1889 Comment cmnt(masm_, "[ WithExitStatement"); | 1882 Comment cmnt(masm_, "[ WithExitStatement"); |
| 1890 CodeForStatementPosition(node); | 1883 CodeForStatementPosition(node); |
| 1891 // Pop context. | 1884 // Pop context. |
| 1892 __ mov(esi, ContextOperand(esi, Context::PREVIOUS_INDEX)); | 1885 __ mov(esi, ContextOperand(esi, Context::PREVIOUS_INDEX)); |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2024 } | 2017 } |
| 2025 | 2018 |
| 2026 | 2019 |
| 2027 void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { | 2020 void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { |
| 2028 ASSERT(!in_spilled_code()); | 2021 ASSERT(!in_spilled_code()); |
| 2029 Comment cmnt(masm_, "[ SwitchStatement"); | 2022 Comment cmnt(masm_, "[ SwitchStatement"); |
| 2030 CodeForStatementPosition(node); | 2023 CodeForStatementPosition(node); |
| 2031 node->set_break_stack_height(break_stack_height_); | 2024 node->set_break_stack_height(break_stack_height_); |
| 2032 node->break_target()->Initialize(this); | 2025 node->break_target()->Initialize(this); |
| 2033 | 2026 |
| 2027 // Compile the switch value. |
| 2034 Load(node->tag()); | 2028 Load(node->tag()); |
| 2029 |
| 2035 if (TryGenerateFastCaseSwitchStatement(node)) { | 2030 if (TryGenerateFastCaseSwitchStatement(node)) { |
| 2036 return; | 2031 return; |
| 2037 } | 2032 } |
| 2038 | 2033 |
| 2039 JumpTarget next_test(this); | |
| 2040 JumpTarget fall_through(this); | |
| 2041 JumpTarget default_entry(this); | |
| 2042 JumpTarget default_exit(this, JumpTarget::BIDIRECTIONAL); | |
| 2043 ZoneList<CaseClause*>* cases = node->cases(); | 2034 ZoneList<CaseClause*>* cases = node->cases(); |
| 2044 int length = cases->length(); | 2035 int length = cases->length(); |
| 2045 CaseClause* default_clause = NULL; | 2036 CaseClause* default_clause = NULL; |
| 2046 | 2037 |
| 2047 // Loop over the cases, compiling tests and bodies. Skip the | 2038 JumpTarget next_test(this); |
| 2048 // default if found and compile it at the end. Exit early if an | 2039 // Compile the case label expressions and comparisons. Exit early |
| 2049 // unconditionally true match occurs (which can happen, eg, in the | 2040 // if a comparison is unconditionally true. The target next_test is |
| 2050 // event the switch value is a compile-time constant). | 2041 // bound before the loop in order to indicate control flow to the |
| 2051 // | 2042 // first comparison. |
| 2052 // Bind the next_test target before entering the loop so we can use | |
| 2053 // its state to detect whether the switch value needs to be dropped | |
| 2054 // from the frame. | |
| 2055 next_test.Bind(); | 2043 next_test.Bind(); |
| 2056 int index = 0; | 2044 for (int i = 0; i < length && !next_test.is_unused(); i++) { |
| 2057 for (; index < length; index++) { | 2045 CaseClause* clause = cases->at(i); |
| 2058 CaseClause* clause = cases->at(index); | 2046 clause->body_target()->Initialize(this); |
| 2047 // The default is not a test, but remember it for later. |
| 2059 if (clause->is_default()) { | 2048 if (clause->is_default()) { |
| 2060 // Remember the default clause and compile it at the end. | |
| 2061 default_clause = clause; | 2049 default_clause = clause; |
| 2062 continue; | 2050 continue; |
| 2063 } | 2051 } |
| 2064 | 2052 |
| 2065 // Compile each non-default clause. | 2053 Comment cmnt(masm_, "[ Case comparison"); |
| 2066 Comment cmnt(masm_, "[ Case clause"); | 2054 // We recycle the same target next_test for each test. Bind it if |
| 2067 // Recycle the same target for each test. | 2055 // the previous test has not done so and then unuse it for the |
| 2068 if (!next_test.is_unused()) { | 2056 // loop. |
| 2069 // The next test target may be linked (as the target of a | 2057 if (next_test.is_linked()) { |
| 2070 // previous match failure) or bound (if the previous comparison | 2058 next_test.Bind(); |
| 2071 // was unconditionally false or this is the first non-default | |
| 2072 // comparison). | |
| 2073 if (next_test.is_linked()) { | |
| 2074 next_test.Bind(); | |
| 2075 } | |
| 2076 next_test.Unuse(); | |
| 2077 } | 2059 } |
| 2060 next_test.Unuse(); |
| 2078 | 2061 |
| 2079 // Duplicate the switch value. | 2062 // Duplicate the switch value. |
| 2080 frame_->Dup(); | 2063 frame_->Dup(); |
| 2081 | 2064 |
| 2082 // Compile the clause's label expression. | 2065 // Compile the label expression. |
| 2083 Load(clause->label()); | 2066 Load(clause->label()); |
| 2084 | 2067 |
| 2085 // Compare and branch to the body if true and to the next test if | 2068 // Compare and branch to the body if true or the next test if |
| 2086 // false. | 2069 // false. Prefer the next test as a fall through. |
| 2087 JumpTarget enter_body(this); | 2070 ControlDestination dest(clause->body_target(), &next_test, false); |
| 2088 ControlDestination dest(&enter_body, &next_test, true); | |
| 2089 Comparison(equal, true, &dest); | 2071 Comparison(equal, true, &dest); |
| 2090 | 2072 |
| 2091 bool previous_was_default = | 2073 // If the comparison fell through to the true target, jump to the |
| 2092 index > 0 && cases->at(index - 1)->is_default(); | 2074 // actual body. |
| 2093 if (dest.false_was_fall_through()) { | 2075 if (dest.true_was_fall_through()) { |
| 2094 // The false target next_test was bound as the fall-through. | 2076 clause->body_target()->Unuse(); |
| 2095 // This may indicate that the comparison was unconditionally | 2077 clause->body_target()->Jump(); |
| 2096 // false if there are no dangling jumps to enter_body. Even | |
| 2097 // then we may still need to compile the body if it is reachable | |
| 2098 // as a fall through. | |
| 2099 | |
| 2100 // We do not need to compile the body if control cannot reach | |
| 2101 // it. Control could reach the body (1) from the comparison by | |
| 2102 // a branch to enter_body, (2) as the fall through of some | |
| 2103 // previous case, or (3) possibly via a backward jump from the | |
| 2104 // default. | |
| 2105 if (!enter_body.is_linked() && | |
| 2106 !fall_through.is_linked() && | |
| 2107 !previous_was_default) { | |
| 2108 continue; | |
| 2109 } | |
| 2110 | |
| 2111 // We will compile the body and we have to jump around it on | |
| 2112 // this path where the comparison failed. | |
| 2113 next_test.Unuse(); | |
| 2114 next_test.Jump(); | |
| 2115 if (enter_body.is_linked()) { | |
| 2116 enter_body.Bind(); | |
| 2117 } | |
| 2118 } | |
| 2119 | |
| 2120 // The body entry target may have been bound, indicating control | |
| 2121 // flow can reach the body via the comparison. | |
| 2122 if (enter_body.is_bound()) { | |
| 2123 // The switch value is no longer needed. | |
| 2124 frame_->Drop(); | |
| 2125 } else { | |
| 2126 // The test was unconditionally false but we will compile the | |
| 2127 // body as a fall through. | |
| 2128 ASSERT(!has_valid_frame()); | |
| 2129 } | |
| 2130 | |
| 2131 // Label the body if needed for fall through. | |
| 2132 if (previous_was_default) { | |
| 2133 // Because the default is compiled last, there is always a potential | |
| 2134 // backwards edge to here, falling through from the default. | |
| 2135 default_exit.Bind(); | |
| 2136 } else { | |
| 2137 // Recycle the same target for each fall through. | |
| 2138 fall_through.Bind(); | |
| 2139 fall_through.Unuse(); | |
| 2140 } | |
| 2141 | |
| 2142 // Compile the body. | |
| 2143 ASSERT(has_valid_frame()); | |
| 2144 { Comment body_cmnt(masm_, "[ Case body"); | |
| 2145 VisitStatements(clause->statements()); | |
| 2146 } | |
| 2147 | |
| 2148 // The test may have been unconditionally true, which is indicated | |
| 2149 // by the absence of any control flow to the next_test target. In | |
| 2150 // that case, exit this loop and stop compiling both tests and | |
| 2151 // bodies (and begin compiling only bodies if necessary). | |
| 2152 | |
| 2153 // Otherwise, if control flow can fall off the end of the body | |
| 2154 // jump to the body of the next case as fall through unless this | |
| 2155 // is the last non-default case. | |
| 2156 if (!next_test.is_linked()) { | |
| 2157 index++; | |
| 2158 break; | |
| 2159 } else if (has_valid_frame()) { | |
| 2160 if (index < length - 2 && // There are at least two cases after this | |
| 2161 cases->at(index + 1)->is_default()) { // The next is the default. | |
| 2162 default_entry.Jump(); | |
| 2163 } else if (index < length - 1) { // This is not the last case. | |
| 2164 fall_through.Jump(); | |
| 2165 } | |
| 2166 } | 2078 } |
| 2167 } | 2079 } |
| 2168 | 2080 |
| 2169 // If we did not compile all the cases then we must have hit one | 2081 // If there was control flow to a next test from the last one |
| 2170 // that was unconditionally true. We do not need to compile any | 2082 // compiled, compile a jump to the default or break target. |
| 2171 // more tests but we may have (and continue to have) fall through. | 2083 if (!next_test.is_unused()) { |
| 2172 for (; index < length && has_valid_frame(); index++) { | |
| 2173 Comment cmnt(masm_, "[ Case fall-through"); | |
| 2174 VisitStatements(cases->at(index)->statements()); | |
| 2175 } | |
| 2176 | |
| 2177 // Complete the switch statement based on the compilation state of | |
| 2178 // the last case that was compiled. | |
| 2179 if (next_test.is_unused()) { | |
| 2180 // The last test compiled was unconditionally true. We still need | |
| 2181 // to compile the default if we found one and it can be targeted | |
| 2182 // by fall through. | |
| 2183 if (default_clause != NULL) { | |
| 2184 bool was_only_clause = length == 1 && cases->at(0) == default_clause; | |
| 2185 if (was_only_clause || default_entry.is_linked()) { | |
| 2186 Comment cmnt(masm_, "[ Default clause"); | |
| 2187 default_entry.Bind(); | |
| 2188 VisitStatements(default_clause->statements()); | |
| 2189 // If control flow can fall off the end of the default and there | |
| 2190 // was a case after it, jump to that case's body. | |
| 2191 if (has_valid_frame() && default_exit.is_bound()) { | |
| 2192 default_exit.Jump(); | |
| 2193 } | |
| 2194 } | |
| 2195 } | |
| 2196 } else { | |
| 2197 // The switch value is still on the frame. We have to drop it and | |
| 2198 // possibly compile a default case. | |
| 2199 if (next_test.is_linked()) { | 2084 if (next_test.is_linked()) { |
| 2200 if (has_valid_frame()) { | |
| 2201 // We have fall through and thus need to jump around the code | |
| 2202 // to drop the switch value. | |
| 2203 fall_through.Jump(); | |
| 2204 } | |
| 2205 next_test.Bind(); | 2085 next_test.Bind(); |
| 2206 } | 2086 } |
| 2087 // Drop the switch value. |
| 2207 frame_->Drop(); | 2088 frame_->Drop(); |
| 2208 | |
| 2209 // If there was a default clause, compile it now. | |
| 2210 if (default_clause != NULL) { | 2089 if (default_clause != NULL) { |
| 2211 Comment cmnt(masm_, "[ Default clause"); | 2090 default_clause->body_target()->Jump(); |
| 2212 if (default_entry.is_linked()) { | 2091 } else { |
| 2213 default_entry.Bind(); | 2092 node->break_target()->Jump(); |
| 2214 } | |
| 2215 VisitStatements(default_clause->statements()); | |
| 2216 // If control flow can fall off the end of the default and there | |
| 2217 // was a case after it, jump to that case's body. | |
| 2218 if (has_valid_frame() && default_exit.is_bound()) { | |
| 2219 default_exit.Jump(); | |
| 2220 } | |
| 2221 } | 2093 } |
| 2222 } | 2094 } |
| 2223 | 2095 |
| 2224 if (fall_through.is_linked()) { | 2096 |
| 2225 fall_through.Bind(); | 2097 // The last instruction emitted was a jump, either to the default |
| 2098 // clause or the break target, or else to a case body from the loop |
| 2099 // that compiles the tests. |
| 2100 ASSERT(!has_valid_frame()); |
| 2101 // Compile case bodies as needed. |
| 2102 for (int i = 0; i < length; i++) { |
| 2103 CaseClause* clause = cases->at(i); |
| 2104 |
| 2105 // There are two ways to reach the body: from the corresponding |
| 2106 // test or as the fall through of the previous body. |
| 2107 if (!clause->body_target()->is_linked() && !has_valid_frame()) { |
| 2108 // If we have neither, skip this body. |
| 2109 continue; |
| 2110 } else if (clause->body_target()->is_linked() && has_valid_frame()) { |
| 2111 // If we have both, put a jump on the fall through path to avoid |
| 2112 // the dropping of the switch value on the test path. The |
| 2113 // exception is the default which has already had the switch |
| 2114 // value dropped. |
| 2115 if (clause->is_default()) { |
| 2116 clause->body_target()->Bind(); |
| 2117 } else { |
| 2118 JumpTarget body(this); |
| 2119 body.Jump(); |
| 2120 clause->body_target()->Bind(); |
| 2121 frame_->Drop(); |
| 2122 body.Bind(); |
| 2123 } |
| 2124 } else if (clause->body_target()->is_linked()) { |
| 2125 // No fall through to worry about. |
| 2126 clause->body_target()->Bind(); |
| 2127 if (!clause->is_default()) { |
| 2128 frame_->Drop(); |
| 2129 } |
| 2130 } else { |
| 2131 // Otherwise, we have only fall through. |
| 2132 ASSERT(has_valid_frame()); |
| 2133 } |
| 2134 |
| 2135 // We are now prepared to compile the body. |
| 2136 Comment cmnt(masm_, "[ Case body"); |
| 2137 VisitStatements(clause->statements()); |
| 2226 } | 2138 } |
| 2139 |
| 2140 // We may not have a valid frame here so bind the break target only |
| 2141 // if needed. |
| 2227 if (node->break_target()->is_linked()) { | 2142 if (node->break_target()->is_linked()) { |
| 2228 node->break_target()->Bind(); | 2143 node->break_target()->Bind(); |
| 2229 } | 2144 } |
| 2230 } | 2145 } |
| 2231 | 2146 |
| 2232 | 2147 |
| 2233 void CodeGenerator::VisitLoopStatement(LoopStatement* node) { | 2148 void CodeGenerator::VisitLoopStatement(LoopStatement* node) { |
| 2234 ASSERT(!in_spilled_code()); | 2149 ASSERT(!in_spilled_code()); |
| 2235 Comment cmnt(masm_, "[ LoopStatement"); | 2150 Comment cmnt(masm_, "[ LoopStatement"); |
| 2236 CodeForStatementPosition(node); | 2151 CodeForStatementPosition(node); |
| (...skipping 671 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2908 } else { | 2823 } else { |
| 2909 // Fake TOS for targets that shadowed breaks and continues. | 2824 // Fake TOS for targets that shadowed breaks and continues. |
| 2910 frame_->EmitPush(Immediate(Factory::undefined_value())); | 2825 frame_->EmitPush(Immediate(Factory::undefined_value())); |
| 2911 } | 2826 } |
| 2912 __ Set(ecx, Immediate(Smi::FromInt(JUMPING + i))); | 2827 __ Set(ecx, Immediate(Smi::FromInt(JUMPING + i))); |
| 2913 unlink.Jump(); | 2828 unlink.Jump(); |
| 2914 } | 2829 } |
| 2915 } | 2830 } |
| 2916 | 2831 |
| 2917 // Unlink from try chain; be careful not to destroy the TOS. | 2832 // Unlink from try chain; be careful not to destroy the TOS. |
| 2918 unlink.Bind(); | 2833 if (unlink.is_linked()) { |
| 2919 // Reload sp from the top handler, because some statements that we | 2834 unlink.Bind(); |
| 2920 // break from (eg, for...in) may have left stuff on the stack. | 2835 } |
| 2921 // Preserve the TOS in a register across stack manipulation. | |
| 2922 frame_->EmitPop(eax); | |
| 2923 ExternalReference handler_address(Top::k_handler_address); | |
| 2924 __ mov(edx, Operand::StaticVariable(handler_address)); | |
| 2925 const int kNextOffset = StackHandlerConstants::kNextOffset + | |
| 2926 StackHandlerConstants::kAddressDisplacement; | |
| 2927 __ lea(esp, Operand(edx, kNextOffset)); | |
| 2928 frame_->Forget(frame_->height() - handler_height); | |
| 2929 | 2836 |
| 2930 frame_->EmitPop(Operand::StaticVariable(handler_address)); | 2837 // Control can reach here via a jump to unlink or by falling off the |
| 2931 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); | 2838 // end of the try block (with no unlinks). |
| 2932 // Next_sp popped. | 2839 if (has_valid_frame()) { |
| 2933 frame_->EmitPush(eax); | 2840 // Reload sp from the top handler, because some statements that we |
| 2841 // break from (eg, for...in) may have left stuff on the stack. |
| 2842 // Preserve the TOS in a register across stack manipulation. |
| 2843 frame_->EmitPop(eax); |
| 2844 ExternalReference handler_address(Top::k_handler_address); |
| 2845 __ mov(edx, Operand::StaticVariable(handler_address)); |
| 2846 const int kNextOffset = StackHandlerConstants::kNextOffset + |
| 2847 StackHandlerConstants::kAddressDisplacement; |
| 2848 __ lea(esp, Operand(edx, kNextOffset)); |
| 2849 frame_->Forget(frame_->height() - handler_height); |
| 2850 |
| 2851 frame_->EmitPop(Operand::StaticVariable(handler_address)); |
| 2852 frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); |
| 2853 // Next_sp popped. |
| 2854 frame_->EmitPush(eax); |
| 2855 } |
| 2934 | 2856 |
| 2935 // --- Finally block --- | 2857 // --- Finally block --- |
| 2936 finally_block.Bind(); | 2858 finally_block.Bind(); |
| 2937 | 2859 |
| 2938 // Push the state on the stack. | 2860 // Push the state on the stack. |
| 2939 frame_->EmitPush(ecx); | 2861 frame_->EmitPush(ecx); |
| 2940 | 2862 |
| 2941 // We keep two elements on the stack - the (possibly faked) result | 2863 // We keep two elements on the stack - the (possibly faked) result |
| 2942 // and the state - while evaluating the finally block. Record it, so | 2864 // and the state - while evaluating the finally block. Record it, so |
| 2943 // that a break/continue crossing this statement can restore the | 2865 // that a break/continue crossing this statement can restore the |
| (...skipping 566 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3510 frame_->Push(&boilerplate); | 3432 frame_->Push(&boilerplate); |
| 3511 // Clone the boilerplate object. | 3433 // Clone the boilerplate object. |
| 3512 Result clone = | 3434 Result clone = |
| 3513 frame_->CallRuntime(Runtime::kCloneObjectLiteralBoilerplate, 1); | 3435 frame_->CallRuntime(Runtime::kCloneObjectLiteralBoilerplate, 1); |
| 3514 // Push the newly cloned literal object as the result. | 3436 // Push the newly cloned literal object as the result. |
| 3515 frame_->Push(&clone); | 3437 frame_->Push(&clone); |
| 3516 | 3438 |
| 3517 for (int i = 0; i < node->properties()->length(); i++) { | 3439 for (int i = 0; i < node->properties()->length(); i++) { |
| 3518 ObjectLiteral::Property* property = node->properties()->at(i); | 3440 ObjectLiteral::Property* property = node->properties()->at(i); |
| 3519 switch (property->kind()) { | 3441 switch (property->kind()) { |
| 3520 case ObjectLiteral::Property::CONSTANT: break; | 3442 case ObjectLiteral::Property::CONSTANT: |
| 3443 break; |
| 3444 case ObjectLiteral::Property::OBJECT_LITERAL: |
| 3445 if (property->value()->AsObjectLiteral()->is_simple()) break; |
| 3521 case ObjectLiteral::Property::COMPUTED: { | 3446 case ObjectLiteral::Property::COMPUTED: { |
| 3522 Handle<Object> key(property->key()->handle()); | 3447 Handle<Object> key(property->key()->handle()); |
| 3523 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | 3448 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); |
| 3524 if (key->IsSymbol()) { | 3449 if (key->IsSymbol()) { |
| 3525 // Duplicate the object as the IC receiver. | 3450 // Duplicate the object as the IC receiver. |
| 3526 frame_->Dup(); | 3451 frame_->Dup(); |
| 3527 Load(property->value()); | 3452 Load(property->value()); |
| 3528 Result value = frame_->Pop(); | 3453 Result value = frame_->Pop(); |
| 3529 value.ToRegister(eax); | 3454 value.ToRegister(eax); |
| 3530 | 3455 |
| (...skipping 3370 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6901 | 6826 |
| 6902 // Slow-case: Go through the JavaScript implementation. | 6827 // Slow-case: Go through the JavaScript implementation. |
| 6903 __ bind(&slow); | 6828 __ bind(&slow); |
| 6904 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); | 6829 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); |
| 6905 } | 6830 } |
| 6906 | 6831 |
| 6907 | 6832 |
| 6908 #undef __ | 6833 #undef __ |
| 6909 | 6834 |
| 6910 } } // namespace v8::internal | 6835 } } // namespace v8::internal |
| OLD | NEW |