OLD | NEW |
1 // Copyright 2006-2009 the V8 project authors. All rights reserved. | 1 // Copyright 2006-2009 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 24 matching lines...) Expand all Loading... |
35 #include "register-allocator-inl.h" | 35 #include "register-allocator-inl.h" |
36 #include "runtime.h" | 36 #include "runtime.h" |
37 #include "scopes.h" | 37 #include "scopes.h" |
38 | 38 |
39 namespace v8 { | 39 namespace v8 { |
40 namespace internal { | 40 namespace internal { |
41 | 41 |
42 #define __ ACCESS_MASM(masm_) | 42 #define __ ACCESS_MASM(masm_) |
43 | 43 |
44 // ------------------------------------------------------------------------- | 44 // ------------------------------------------------------------------------- |
| 45 // Platform-specific DeferredCode functions. |
| 46 |
| 47 void DeferredCode::SaveRegisters() { |
| 48 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { |
| 49 int action = registers_[i]; |
| 50 if (action == kPush) { |
| 51 __ push(RegisterAllocator::ToRegister(i)); |
| 52 } else if (action != kIgnore && (action & kSyncedFlag) == 0) { |
| 53 __ mov(Operand(ebp, action), RegisterAllocator::ToRegister(i)); |
| 54 } |
| 55 } |
| 56 } |
| 57 |
| 58 |
| 59 void DeferredCode::RestoreRegisters() { |
| 60 // Restore registers in reverse order due to the stack. |
| 61 for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) { |
| 62 int action = registers_[i]; |
| 63 if (action == kPush) { |
| 64 __ pop(RegisterAllocator::ToRegister(i)); |
| 65 } else if (action != kIgnore) { |
| 66 action &= ~kSyncedFlag; |
| 67 __ mov(RegisterAllocator::ToRegister(i), Operand(ebp, action)); |
| 68 } |
| 69 } |
| 70 } |
| 71 |
| 72 |
| 73 // ------------------------------------------------------------------------- |
45 // CodeGenState implementation. | 74 // CodeGenState implementation. |
46 | 75 |
47 CodeGenState::CodeGenState(CodeGenerator* owner) | 76 CodeGenState::CodeGenState(CodeGenerator* owner) |
48 : owner_(owner), | 77 : owner_(owner), |
49 typeof_state_(NOT_INSIDE_TYPEOF), | 78 typeof_state_(NOT_INSIDE_TYPEOF), |
50 destination_(NULL), | 79 destination_(NULL), |
51 previous_(NULL) { | 80 previous_(NULL) { |
52 owner_->set_state(this); | 81 owner_->set_state(this); |
53 } | 82 } |
54 | 83 |
(...skipping 11 matching lines...) Expand all Loading... |
66 | 95 |
67 CodeGenState::~CodeGenState() { | 96 CodeGenState::~CodeGenState() { |
68 ASSERT(owner_->state() == this); | 97 ASSERT(owner_->state() == this); |
69 owner_->set_state(previous_); | 98 owner_->set_state(previous_); |
70 } | 99 } |
71 | 100 |
72 | 101 |
73 // ------------------------------------------------------------------------- | 102 // ------------------------------------------------------------------------- |
74 // CodeGenerator implementation | 103 // CodeGenerator implementation |
75 | 104 |
76 CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script, | 105 CodeGenerator::CodeGenerator(int buffer_size, |
| 106 Handle<Script> script, |
77 bool is_eval) | 107 bool is_eval) |
78 : is_eval_(is_eval), | 108 : is_eval_(is_eval), |
79 script_(script), | 109 script_(script), |
80 deferred_(8), | 110 deferred_(8), |
81 masm_(new MacroAssembler(NULL, buffer_size)), | 111 masm_(new MacroAssembler(NULL, buffer_size)), |
82 scope_(NULL), | 112 scope_(NULL), |
83 frame_(NULL), | 113 frame_(NULL), |
84 allocator_(NULL), | 114 allocator_(NULL), |
85 state_(NULL), | 115 state_(NULL), |
86 loop_nesting_(0), | 116 loop_nesting_(0), |
(...skipping 685 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
772 case Token::SHL: return "GenericBinaryOpStub_SHL"; | 802 case Token::SHL: return "GenericBinaryOpStub_SHL"; |
773 case Token::SHR: return "GenericBinaryOpStub_SHR"; | 803 case Token::SHR: return "GenericBinaryOpStub_SHR"; |
774 default: return "GenericBinaryOpStub"; | 804 default: return "GenericBinaryOpStub"; |
775 } | 805 } |
776 } | 806 } |
777 | 807 |
778 | 808 |
779 // Call the specialized stub for a binary operation. | 809 // Call the specialized stub for a binary operation. |
780 class DeferredInlineBinaryOperation: public DeferredCode { | 810 class DeferredInlineBinaryOperation: public DeferredCode { |
781 public: | 811 public: |
782 DeferredInlineBinaryOperation(Token::Value op, OverwriteMode mode) | 812 DeferredInlineBinaryOperation(Token::Value op, |
783 : op_(op), mode_(mode) { | 813 Register dst, |
| 814 Register left, |
| 815 Register right, |
| 816 OverwriteMode mode) |
| 817 : op_(op), dst_(dst), left_(left), right_(right), mode_(mode) { |
784 set_comment("[ DeferredInlineBinaryOperation"); | 818 set_comment("[ DeferredInlineBinaryOperation"); |
785 } | 819 } |
786 | 820 |
787 virtual void Generate(); | 821 virtual void Generate(); |
788 | 822 |
789 private: | 823 private: |
790 Token::Value op_; | 824 Token::Value op_; |
| 825 Register dst_; |
| 826 Register left_; |
| 827 Register right_; |
791 OverwriteMode mode_; | 828 OverwriteMode mode_; |
792 }; | 829 }; |
793 | 830 |
794 | 831 |
795 void DeferredInlineBinaryOperation::Generate() { | 832 void DeferredInlineBinaryOperation::Generate() { |
796 Result left; | 833 __ push(left_); |
797 Result right; | 834 __ push(right_); |
798 enter()->Bind(&left, &right); | |
799 cgen()->frame()->Push(&left); | |
800 cgen()->frame()->Push(&right); | |
801 GenericBinaryOpStub stub(op_, mode_, SMI_CODE_INLINED); | 835 GenericBinaryOpStub stub(op_, mode_, SMI_CODE_INLINED); |
802 Result answer = cgen()->frame()->CallStub(&stub, 2); | 836 __ CallStub(&stub); |
803 exit_.Jump(&answer); | 837 if (!dst_.is(eax)) __ mov(dst_, eax); |
804 } | 838 } |
805 | 839 |
806 | 840 |
807 void CodeGenerator::GenericBinaryOperation(Token::Value op, | 841 void CodeGenerator::GenericBinaryOperation(Token::Value op, |
808 SmiAnalysis* type, | 842 SmiAnalysis* type, |
809 OverwriteMode overwrite_mode) { | 843 OverwriteMode overwrite_mode) { |
810 Comment cmnt(masm_, "[ BinaryOperation"); | 844 Comment cmnt(masm_, "[ BinaryOperation"); |
811 Comment cmnt_token(masm_, Token::String(op)); | 845 Comment cmnt_token(masm_, Token::String(op)); |
812 | 846 |
813 if (op == Token::COMMA) { | 847 if (op == Token::COMMA) { |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
989 break; | 1023 break; |
990 } | 1024 } |
991 if (answer_object == Heap::undefined_value()) { | 1025 if (answer_object == Heap::undefined_value()) { |
992 return false; | 1026 return false; |
993 } | 1027 } |
994 frame_->Push(Handle<Object>(answer_object)); | 1028 frame_->Push(Handle<Object>(answer_object)); |
995 return true; | 1029 return true; |
996 } | 1030 } |
997 | 1031 |
998 | 1032 |
| 1033 // Implements a binary operation using a deferred code object and some |
| 1034 // inline code to operate on smis quickly. |
999 void CodeGenerator::LikelySmiBinaryOperation(Token::Value op, | 1035 void CodeGenerator::LikelySmiBinaryOperation(Token::Value op, |
1000 Result* left, | 1036 Result* left, |
1001 Result* right, | 1037 Result* right, |
1002 OverwriteMode overwrite_mode) { | 1038 OverwriteMode overwrite_mode) { |
1003 // Implements a binary operation using a deferred code object and some | |
1004 // inline code to operate on smis quickly. | |
1005 DeferredInlineBinaryOperation* deferred = | |
1006 new DeferredInlineBinaryOperation(op, overwrite_mode); | |
1007 | |
1008 // Special handling of div and mod because they use fixed registers. | 1039 // Special handling of div and mod because they use fixed registers. |
1009 if (op == Token::DIV || op == Token::MOD) { | 1040 if (op == Token::DIV || op == Token::MOD) { |
1010 // We need eax as the quotient register, edx as the remainder | 1041 // We need eax as the quotient register, edx as the remainder |
1011 // register, neither left nor right in eax or edx, and left copied | 1042 // register, neither left nor right in eax or edx, and left copied |
1012 // to eax. | 1043 // to eax. |
1013 Result quotient; | 1044 Result quotient; |
1014 Result remainder; | 1045 Result remainder; |
1015 bool left_is_in_eax = false; | 1046 bool left_is_in_eax = false; |
1016 // Step 1: get eax for quotient. | 1047 // Step 1: get eax for quotient. |
1017 if ((left->is_register() && left->reg().is(eax)) || | 1048 if ((left->is_register() && left->reg().is(eax)) || |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1062 // Neither left nor right is in edx. | 1093 // Neither left nor right is in edx. |
1063 remainder = allocator_->Allocate(edx); | 1094 remainder = allocator_->Allocate(edx); |
1064 } | 1095 } |
1065 } | 1096 } |
1066 ASSERT(remainder.is_register() && remainder.reg().is(edx)); | 1097 ASSERT(remainder.is_register() && remainder.reg().is(edx)); |
1067 ASSERT(!(left->is_register() && left->reg().is(edx))); | 1098 ASSERT(!(left->is_register() && left->reg().is(edx))); |
1068 ASSERT(!(right->is_register() && right->reg().is(edx))); | 1099 ASSERT(!(right->is_register() && right->reg().is(edx))); |
1069 | 1100 |
1070 left->ToRegister(); | 1101 left->ToRegister(); |
1071 right->ToRegister(); | 1102 right->ToRegister(); |
1072 frame_->Spill(quotient.reg()); | 1103 frame_->Spill(eax); |
1073 frame_->Spill(remainder.reg()); | 1104 frame_->Spill(edx); |
1074 | 1105 |
1075 // Check that left and right are smi tagged. | 1106 // Check that left and right are smi tagged. |
| 1107 DeferredInlineBinaryOperation* deferred = |
| 1108 new DeferredInlineBinaryOperation(op, |
| 1109 (op == Token::DIV) ? eax : edx, |
| 1110 left->reg(), |
| 1111 right->reg(), |
| 1112 overwrite_mode); |
1076 if (left->reg().is(right->reg())) { | 1113 if (left->reg().is(right->reg())) { |
1077 __ test(left->reg(), Immediate(kSmiTagMask)); | 1114 __ test(left->reg(), Immediate(kSmiTagMask)); |
1078 } else { | 1115 } else { |
1079 // Use the quotient register as a scratch for the tag check. | 1116 // Use the quotient register as a scratch for the tag check. |
1080 if (!left_is_in_eax) __ mov(quotient.reg(), left->reg()); | 1117 if (!left_is_in_eax) __ mov(eax, left->reg()); |
1081 left_is_in_eax = false; | 1118 left_is_in_eax = false; // About to destroy the value in eax. |
1082 __ or_(quotient.reg(), Operand(right->reg())); | 1119 __ or_(eax, Operand(right->reg())); |
1083 ASSERT(kSmiTag == 0); // Adjust test if not the case. | 1120 ASSERT(kSmiTag == 0); // Adjust test if not the case. |
1084 __ test(quotient.reg(), Immediate(kSmiTagMask)); | 1121 __ test(eax, Immediate(kSmiTagMask)); |
1085 } | 1122 } |
1086 deferred->SetEntryFrame(left, right); | 1123 deferred->Branch(not_zero); |
1087 deferred->enter()->Branch(not_zero, left, right); | |
1088 | 1124 |
1089 if (!left_is_in_eax) __ mov(quotient.reg(), left->reg()); | 1125 if (!left_is_in_eax) __ mov(eax, left->reg()); |
1090 | |
1091 // Sign extend eax into edx:eax. | 1126 // Sign extend eax into edx:eax. |
1092 __ cdq(); | 1127 __ cdq(); |
1093 // Check for 0 divisor. | 1128 // Check for 0 divisor. |
1094 __ test(right->reg(), Operand(right->reg())); | 1129 __ test(right->reg(), Operand(right->reg())); |
1095 deferred->enter()->Branch(zero, left, right); | 1130 deferred->Branch(zero); |
1096 // Divide edx:eax by the right operand. | 1131 // Divide edx:eax by the right operand. |
1097 __ idiv(right->reg()); | 1132 __ idiv(right->reg()); |
1098 | 1133 |
1099 // Complete the operation. | 1134 // Complete the operation. |
1100 if (op == Token::DIV) { | 1135 if (op == Token::DIV) { |
1101 // Check for negative zero result. If result is zero, and divisor | 1136 // Check for negative zero result. If result is zero, and divisor |
1102 // is negative, return a floating point negative zero. The | 1137 // is negative, return a floating point negative zero. The |
1103 // virtual frame is unchanged in this block, so local control flow | 1138 // virtual frame is unchanged in this block, so local control flow |
1104 // can use a Label rather than a JumpTarget. | 1139 // can use a Label rather than a JumpTarget. |
1105 Label non_zero_result; | 1140 Label non_zero_result; |
1106 __ test(left->reg(), Operand(left->reg())); | 1141 __ test(left->reg(), Operand(left->reg())); |
1107 __ j(not_zero, &non_zero_result); | 1142 __ j(not_zero, &non_zero_result); |
1108 __ test(right->reg(), Operand(right->reg())); | 1143 __ test(right->reg(), Operand(right->reg())); |
1109 deferred->enter()->Branch(negative, left, right); | 1144 deferred->Branch(negative); |
1110 __ bind(&non_zero_result); | 1145 __ bind(&non_zero_result); |
1111 // Check for the corner case of dividing the most negative smi by | 1146 // Check for the corner case of dividing the most negative smi by |
1112 // -1. We cannot use the overflow flag, since it is not set by | 1147 // -1. We cannot use the overflow flag, since it is not set by |
1113 // idiv instruction. | 1148 // idiv instruction. |
1114 ASSERT(kSmiTag == 0 && kSmiTagSize == 1); | 1149 ASSERT(kSmiTag == 0 && kSmiTagSize == 1); |
1115 __ cmp(quotient.reg(), 0x40000000); | 1150 __ cmp(eax, 0x40000000); |
1116 deferred->enter()->Branch(equal, left, right); | 1151 deferred->Branch(equal); |
1117 // Check that the remainder is zero. | 1152 // Check that the remainder is zero. |
1118 __ test(remainder.reg(), Operand(remainder.reg())); | 1153 __ test(edx, Operand(edx)); |
1119 remainder.Unuse(); | 1154 deferred->Branch(not_zero); |
1120 deferred->enter()->Branch(not_zero, left, right); | 1155 // Tag the result and store it in the quotient register. |
| 1156 ASSERT(kSmiTagSize == times_2); // adjust code if not the case |
| 1157 __ lea(eax, Operand(eax, eax, times_1, kSmiTag)); |
| 1158 deferred->BindExit(); |
1121 left->Unuse(); | 1159 left->Unuse(); |
1122 right->Unuse(); | 1160 right->Unuse(); |
1123 // Tag the result and store it in the quotient register. | |
1124 ASSERT(kSmiTagSize == times_2); // adjust code if not the case | |
1125 __ lea(quotient.reg(), | |
1126 Operand(quotient.reg(), quotient.reg(), times_1, kSmiTag)); | |
1127 deferred->BindExit("ient); | |
1128 frame_->Push("ient); | 1161 frame_->Push("ient); |
1129 } else { | 1162 } else { |
1130 ASSERT(op == Token::MOD); | 1163 ASSERT(op == Token::MOD); |
1131 quotient.Unuse(); | |
1132 // Check for a negative zero result. If the result is zero, and | 1164 // Check for a negative zero result. If the result is zero, and |
1133 // the dividend is negative, return a floating point negative | 1165 // the dividend is negative, return a floating point negative |
1134 // zero. The frame is unchanged in this block, so local control | 1166 // zero. The frame is unchanged in this block, so local control |
1135 // flow can use a Label rather than a JumpTarget. | 1167 // flow can use a Label rather than a JumpTarget. |
1136 Label non_zero_result; | 1168 Label non_zero_result; |
1137 __ test(remainder.reg(), Operand(remainder.reg())); | 1169 __ test(edx, Operand(edx)); |
1138 __ j(not_zero, &non_zero_result, taken); | 1170 __ j(not_zero, &non_zero_result, taken); |
1139 __ test(left->reg(), Operand(left->reg())); | 1171 __ test(left->reg(), Operand(left->reg())); |
1140 deferred->enter()->Branch(negative, left, right); | 1172 deferred->Branch(negative); |
| 1173 __ bind(&non_zero_result); |
| 1174 deferred->BindExit(); |
1141 left->Unuse(); | 1175 left->Unuse(); |
1142 right->Unuse(); | 1176 right->Unuse(); |
1143 __ bind(&non_zero_result); | |
1144 deferred->BindExit(&remainder); | |
1145 frame_->Push(&remainder); | 1177 frame_->Push(&remainder); |
1146 } | 1178 } |
1147 return; | 1179 return; |
1148 } | 1180 } |
1149 | 1181 |
1150 // Special handling of shift operations because they use fixed | 1182 // Special handling of shift operations because they use fixed |
1151 // registers. | 1183 // registers. |
1152 if (op == Token::SHL || op == Token::SHR || op == Token::SAR) { | 1184 if (op == Token::SHL || op == Token::SHR || op == Token::SAR) { |
1153 // Move left out of ecx if necessary. | 1185 // Move left out of ecx if necessary. |
1154 if (left->is_register() && left->reg().is(ecx)) { | 1186 if (left->is_register() && left->reg().is(ecx)) { |
1155 *left = allocator_->Allocate(); | 1187 *left = allocator_->Allocate(); |
1156 ASSERT(left->is_valid()); | 1188 ASSERT(left->is_valid()); |
1157 __ mov(left->reg(), ecx); | 1189 __ mov(left->reg(), ecx); |
1158 } | 1190 } |
1159 right->ToRegister(ecx); | 1191 right->ToRegister(ecx); |
1160 left->ToRegister(); | 1192 left->ToRegister(); |
1161 ASSERT(left->is_register() && !left->reg().is(ecx)); | 1193 ASSERT(left->is_register() && !left->reg().is(ecx)); |
1162 ASSERT(right->is_register() && right->reg().is(ecx)); | 1194 ASSERT(right->is_register() && right->reg().is(ecx)); |
1163 | 1195 |
1164 // We will modify right, it must be spilled. | 1196 // We will modify right, it must be spilled. |
1165 frame_->Spill(ecx); | 1197 frame_->Spill(ecx); |
1166 | 1198 |
1167 // Use a fresh answer register to avoid spilling the left operand. | 1199 // Use a fresh answer register to avoid spilling the left operand. |
1168 Result answer = allocator_->Allocate(); | 1200 Result answer = allocator_->Allocate(); |
1169 ASSERT(answer.is_valid()); | 1201 ASSERT(answer.is_valid()); |
1170 // Check that both operands are smis using the answer register as a | 1202 // Check that both operands are smis using the answer register as a |
1171 // temporary. | 1203 // temporary. |
| 1204 DeferredInlineBinaryOperation* deferred = |
| 1205 new DeferredInlineBinaryOperation(op, |
| 1206 answer.reg(), |
| 1207 left->reg(), |
| 1208 ecx, |
| 1209 overwrite_mode); |
1172 __ mov(answer.reg(), left->reg()); | 1210 __ mov(answer.reg(), left->reg()); |
1173 __ or_(answer.reg(), Operand(ecx)); | 1211 __ or_(answer.reg(), Operand(ecx)); |
1174 __ test(answer.reg(), Immediate(kSmiTagMask)); | 1212 __ test(answer.reg(), Immediate(kSmiTagMask)); |
1175 deferred->enter()->Branch(not_zero, left, right); | 1213 deferred->Branch(not_zero); |
1176 | 1214 |
1177 // Untag both operands. | 1215 // Untag both operands. |
1178 __ mov(answer.reg(), left->reg()); | 1216 __ mov(answer.reg(), left->reg()); |
1179 __ sar(answer.reg(), kSmiTagSize); | 1217 __ sar(answer.reg(), kSmiTagSize); |
1180 __ sar(ecx, kSmiTagSize); | 1218 __ sar(ecx, kSmiTagSize); |
1181 // Perform the operation. | 1219 // Perform the operation. |
1182 switch (op) { | 1220 switch (op) { |
1183 case Token::SAR: | 1221 case Token::SAR: |
1184 __ sar(answer.reg()); | 1222 __ sar(answer.reg()); |
1185 // No checks of result necessary | 1223 // No checks of result necessary |
1186 break; | 1224 break; |
1187 case Token::SHR: { | 1225 case Token::SHR: { |
1188 JumpTarget result_ok; | 1226 Label result_ok; |
1189 __ shr(answer.reg()); | 1227 __ shr(answer.reg()); |
1190 // Check that the *unsigned* result fits in a smi. Neither of | 1228 // Check that the *unsigned* result fits in a smi. Neither of |
1191 // the two high-order bits can be set: | 1229 // the two high-order bits can be set: |
1192 // * 0x80000000: high bit would be lost when smi tagging. | 1230 // * 0x80000000: high bit would be lost when smi tagging. |
1193 // * 0x40000000: this number would convert to negative when smi | 1231 // * 0x40000000: this number would convert to negative when smi |
1194 // tagging. | 1232 // tagging. |
1195 // These two cases can only happen with shifts by 0 or 1 when | 1233 // These two cases can only happen with shifts by 0 or 1 when |
1196 // handed a valid smi. If the answer cannot be represented by a | 1234 // handed a valid smi. If the answer cannot be represented by a |
1197 // smi, restore the left and right arguments, and jump to slow | 1235 // smi, restore the left and right arguments, and jump to slow |
1198 // case. The low bit of the left argument may be lost, but only | 1236 // case. The low bit of the left argument may be lost, but only |
1199 // in a case where it is dropped anyway. | 1237 // in a case where it is dropped anyway. |
1200 __ test(answer.reg(), Immediate(0xc0000000)); | 1238 __ test(answer.reg(), Immediate(0xc0000000)); |
1201 result_ok.Branch(zero, &answer); | 1239 __ j(zero, &result_ok); |
1202 ASSERT(kSmiTag == 0); | 1240 ASSERT(kSmiTag == 0); |
1203 __ shl(ecx, kSmiTagSize); | 1241 __ shl(ecx, kSmiTagSize); |
1204 answer.Unuse(); | 1242 deferred->Jump(); |
1205 deferred->enter()->Jump(left, right); | 1243 __ bind(&result_ok); |
1206 result_ok.Bind(&answer); | |
1207 break; | 1244 break; |
1208 } | 1245 } |
1209 case Token::SHL: { | 1246 case Token::SHL: { |
1210 JumpTarget result_ok; | 1247 Label result_ok; |
1211 __ shl(answer.reg()); | 1248 __ shl(answer.reg()); |
1212 // Check that the *signed* result fits in a smi. | 1249 // Check that the *signed* result fits in a smi. |
1213 __ cmp(answer.reg(), 0xc0000000); | 1250 __ cmp(answer.reg(), 0xc0000000); |
1214 result_ok.Branch(positive, &answer); | 1251 __ j(positive, &result_ok); |
1215 ASSERT(kSmiTag == 0); | 1252 ASSERT(kSmiTag == 0); |
1216 __ shl(ecx, kSmiTagSize); | 1253 __ shl(ecx, kSmiTagSize); |
1217 answer.Unuse(); | 1254 deferred->Jump(); |
1218 deferred->enter()->Jump(left, right); | 1255 __ bind(&result_ok); |
1219 result_ok.Bind(&answer); | |
1220 break; | 1256 break; |
1221 } | 1257 } |
1222 default: | 1258 default: |
1223 UNREACHABLE(); | 1259 UNREACHABLE(); |
1224 } | 1260 } |
1225 left->Unuse(); | |
1226 right->Unuse(); | |
1227 // Smi-tag the result in answer. | 1261 // Smi-tag the result in answer. |
1228 ASSERT(kSmiTagSize == 1); // Adjust code if not the case. | 1262 ASSERT(kSmiTagSize == 1); // Adjust code if not the case. |
1229 __ lea(answer.reg(), | 1263 __ lea(answer.reg(), |
1230 Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); | 1264 Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); |
1231 deferred->BindExit(&answer); | 1265 deferred->BindExit(); |
| 1266 left->Unuse(); |
| 1267 right->Unuse(); |
1232 frame_->Push(&answer); | 1268 frame_->Push(&answer); |
1233 return; | 1269 return; |
1234 } | 1270 } |
1235 | 1271 |
1236 // Handle the other binary operations. | 1272 // Handle the other binary operations. |
1237 left->ToRegister(); | 1273 left->ToRegister(); |
1238 right->ToRegister(); | 1274 right->ToRegister(); |
1239 // A newly allocated register answer is used to hold the answer. The | 1275 // A newly allocated register answer is used to hold the answer. The |
1240 // registers containing left and right are not modified so they don't | 1276 // registers containing left and right are not modified so they don't |
1241 // need to be spilled in the fast case. | 1277 // need to be spilled in the fast case. |
1242 Result answer = allocator_->Allocate(); | 1278 Result answer = allocator_->Allocate(); |
| 1279 ASSERT(answer.is_valid()); |
1243 | 1280 |
1244 ASSERT(answer.is_valid()); | |
1245 // Perform the smi tag check. | 1281 // Perform the smi tag check. |
| 1282 DeferredInlineBinaryOperation* deferred = |
| 1283 new DeferredInlineBinaryOperation(op, |
| 1284 answer.reg(), |
| 1285 left->reg(), |
| 1286 right->reg(), |
| 1287 overwrite_mode); |
1246 if (left->reg().is(right->reg())) { | 1288 if (left->reg().is(right->reg())) { |
1247 __ test(left->reg(), Immediate(kSmiTagMask)); | 1289 __ test(left->reg(), Immediate(kSmiTagMask)); |
1248 } else { | 1290 } else { |
1249 __ mov(answer.reg(), left->reg()); | 1291 __ mov(answer.reg(), left->reg()); |
1250 __ or_(answer.reg(), Operand(right->reg())); | 1292 __ or_(answer.reg(), Operand(right->reg())); |
1251 ASSERT(kSmiTag == 0); // Adjust test if not the case. | 1293 ASSERT(kSmiTag == 0); // Adjust test if not the case. |
1252 __ test(answer.reg(), Immediate(kSmiTagMask)); | 1294 __ test(answer.reg(), Immediate(kSmiTagMask)); |
1253 } | 1295 } |
| 1296 deferred->Branch(not_zero); |
| 1297 __ mov(answer.reg(), left->reg()); |
1254 switch (op) { | 1298 switch (op) { |
1255 case Token::ADD: | 1299 case Token::ADD: |
1256 deferred->SetEntryFrame(left, right); | |
1257 deferred->enter()->Branch(not_zero, left, right, not_taken); | |
1258 __ mov(answer.reg(), left->reg()); | |
1259 __ add(answer.reg(), Operand(right->reg())); // Add optimistically. | 1300 __ add(answer.reg(), Operand(right->reg())); // Add optimistically. |
1260 deferred->enter()->Branch(overflow, left, right, not_taken); | 1301 deferred->Branch(overflow); |
1261 break; | 1302 break; |
1262 | 1303 |
1263 case Token::SUB: | 1304 case Token::SUB: |
1264 deferred->SetEntryFrame(left, right); | |
1265 deferred->enter()->Branch(not_zero, left, right, not_taken); | |
1266 __ mov(answer.reg(), left->reg()); | |
1267 __ sub(answer.reg(), Operand(right->reg())); // Subtract optimistically. | 1305 __ sub(answer.reg(), Operand(right->reg())); // Subtract optimistically. |
1268 deferred->enter()->Branch(overflow, left, right, not_taken); | 1306 deferred->Branch(overflow); |
1269 break; | 1307 break; |
1270 | 1308 |
1271 case Token::MUL: { | 1309 case Token::MUL: { |
1272 deferred->SetEntryFrame(left, right); | |
1273 deferred->enter()->Branch(not_zero, left, right, not_taken); | |
1274 __ mov(answer.reg(), left->reg()); | |
1275 // If the smi tag is 0 we can just leave the tag on one operand. | 1310 // If the smi tag is 0 we can just leave the tag on one operand. |
1276 ASSERT(kSmiTag == 0); // Adjust code below if not the case. | 1311 ASSERT(kSmiTag == 0); // Adjust code below if not the case. |
1277 // Remove smi tag from the left operand (but keep sign). | 1312 // Remove smi tag from the left operand (but keep sign). |
1278 // Left-hand operand has been copied into answer. | 1313 // Left-hand operand has been copied into answer. |
1279 __ sar(answer.reg(), kSmiTagSize); | 1314 __ sar(answer.reg(), kSmiTagSize); |
1280 // Do multiplication of smis, leaving result in answer. | 1315 // Do multiplication of smis, leaving result in answer. |
1281 __ imul(answer.reg(), Operand(right->reg())); | 1316 __ imul(answer.reg(), Operand(right->reg())); |
1282 // Go slow on overflows. | 1317 // Go slow on overflows. |
1283 deferred->enter()->Branch(overflow, left, right, not_taken); | 1318 deferred->Branch(overflow); |
1284 // Check for negative zero result. If product is zero, and one | 1319 // Check for negative zero result. If product is zero, and one |
1285 // argument is negative, go to slow case. The frame is unchanged | 1320 // argument is negative, go to slow case. The frame is unchanged |
1286 // in this block, so local control flow can use a Label rather | 1321 // in this block, so local control flow can use a Label rather |
1287 // than a JumpTarget. | 1322 // than a JumpTarget. |
1288 Label non_zero_result; | 1323 Label non_zero_result; |
1289 __ test(answer.reg(), Operand(answer.reg())); | 1324 __ test(answer.reg(), Operand(answer.reg())); |
1290 __ j(not_zero, &non_zero_result, taken); | 1325 __ j(not_zero, &non_zero_result, taken); |
1291 __ mov(answer.reg(), left->reg()); | 1326 __ mov(answer.reg(), left->reg()); |
1292 __ or_(answer.reg(), Operand(right->reg())); | 1327 __ or_(answer.reg(), Operand(right->reg())); |
1293 deferred->enter()->Branch(negative, left, right, not_taken); | 1328 deferred->Branch(negative); |
1294 __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct. | 1329 __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct. |
1295 __ bind(&non_zero_result); | 1330 __ bind(&non_zero_result); |
1296 break; | 1331 break; |
1297 } | 1332 } |
1298 | 1333 |
1299 case Token::BIT_OR: | 1334 case Token::BIT_OR: |
1300 deferred->enter()->Branch(not_zero, left, right, not_taken); | |
1301 __ mov(answer.reg(), left->reg()); | |
1302 __ or_(answer.reg(), Operand(right->reg())); | 1335 __ or_(answer.reg(), Operand(right->reg())); |
1303 break; | 1336 break; |
1304 | 1337 |
1305 case Token::BIT_AND: | 1338 case Token::BIT_AND: |
1306 deferred->enter()->Branch(not_zero, left, right, not_taken); | |
1307 __ mov(answer.reg(), left->reg()); | |
1308 __ and_(answer.reg(), Operand(right->reg())); | 1339 __ and_(answer.reg(), Operand(right->reg())); |
1309 break; | 1340 break; |
1310 | 1341 |
1311 case Token::BIT_XOR: | 1342 case Token::BIT_XOR: |
1312 deferred->enter()->Branch(not_zero, left, right, not_taken); | |
1313 __ mov(answer.reg(), left->reg()); | |
1314 __ xor_(answer.reg(), Operand(right->reg())); | 1343 __ xor_(answer.reg(), Operand(right->reg())); |
1315 break; | 1344 break; |
1316 | 1345 |
1317 default: | 1346 default: |
1318 UNREACHABLE(); | 1347 UNREACHABLE(); |
1319 break; | 1348 break; |
1320 } | 1349 } |
| 1350 deferred->BindExit(); |
1321 left->Unuse(); | 1351 left->Unuse(); |
1322 right->Unuse(); | 1352 right->Unuse(); |
1323 deferred->BindExit(&answer); | |
1324 frame_->Push(&answer); | 1353 frame_->Push(&answer); |
1325 } | 1354 } |
1326 | 1355 |
1327 | 1356 |
| 1357 // Call the appropriate binary operation stub to compute src op value |
| 1358 // and leave the result in dst. |
1328 class DeferredInlineSmiOperation: public DeferredCode { | 1359 class DeferredInlineSmiOperation: public DeferredCode { |
1329 public: | 1360 public: |
1330 DeferredInlineSmiOperation(Token::Value op, | 1361 DeferredInlineSmiOperation(Token::Value op, |
| 1362 Register dst, |
| 1363 Register src, |
1331 Smi* value, | 1364 Smi* value, |
1332 OverwriteMode overwrite_mode) | 1365 OverwriteMode overwrite_mode) |
1333 : op_(op), | 1366 : op_(op), |
| 1367 dst_(dst), |
| 1368 src_(src), |
1334 value_(value), | 1369 value_(value), |
1335 overwrite_mode_(overwrite_mode) { | 1370 overwrite_mode_(overwrite_mode) { |
1336 set_comment("[ DeferredInlineSmiOperation"); | 1371 set_comment("[ DeferredInlineSmiOperation"); |
1337 } | 1372 } |
1338 | 1373 |
1339 virtual void Generate(); | 1374 virtual void Generate(); |
1340 | 1375 |
1341 private: | 1376 private: |
1342 Token::Value op_; | 1377 Token::Value op_; |
| 1378 Register dst_; |
| 1379 Register src_; |
1343 Smi* value_; | 1380 Smi* value_; |
1344 OverwriteMode overwrite_mode_; | 1381 OverwriteMode overwrite_mode_; |
1345 }; | 1382 }; |
1346 | 1383 |
1347 | 1384 |
1348 void DeferredInlineSmiOperation::Generate() { | 1385 void DeferredInlineSmiOperation::Generate() { |
1349 Result left; | 1386 __ push(src_); |
1350 enter()->Bind(&left); | 1387 __ push(Immediate(value_)); |
1351 cgen()->frame()->Push(&left); | 1388 GenericBinaryOpStub stub(op_, overwrite_mode_, SMI_CODE_INLINED); |
1352 cgen()->frame()->Push(value_); | 1389 __ CallStub(&stub); |
1353 GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED); | 1390 if (!dst_.is(eax)) __ mov(dst_, eax); |
1354 Result answer = cgen()->frame()->CallStub(&igostub, 2); | |
1355 exit_.Jump(&answer); | |
1356 } | 1391 } |
1357 | 1392 |
1358 | 1393 |
| 1394 // Call the appropriate binary operation stub to compute value op src |
| 1395 // and leave the result in dst. |
1359 class DeferredInlineSmiOperationReversed: public DeferredCode { | 1396 class DeferredInlineSmiOperationReversed: public DeferredCode { |
1360 public: | 1397 public: |
1361 DeferredInlineSmiOperationReversed(Token::Value op, | 1398 DeferredInlineSmiOperationReversed(Token::Value op, |
| 1399 Register dst, |
1362 Smi* value, | 1400 Smi* value, |
| 1401 Register src, |
1363 OverwriteMode overwrite_mode) | 1402 OverwriteMode overwrite_mode) |
1364 : op_(op), | 1403 : op_(op), |
| 1404 dst_(dst), |
1365 value_(value), | 1405 value_(value), |
| 1406 src_(src), |
1366 overwrite_mode_(overwrite_mode) { | 1407 overwrite_mode_(overwrite_mode) { |
1367 set_comment("[ DeferredInlineSmiOperationReversed"); | 1408 set_comment("[ DeferredInlineSmiOperationReversed"); |
1368 } | 1409 } |
1369 | 1410 |
1370 virtual void Generate(); | 1411 virtual void Generate(); |
1371 | 1412 |
1372 private: | 1413 private: |
1373 Token::Value op_; | 1414 Token::Value op_; |
| 1415 Register dst_; |
1374 Smi* value_; | 1416 Smi* value_; |
| 1417 Register src_; |
1375 OverwriteMode overwrite_mode_; | 1418 OverwriteMode overwrite_mode_; |
1376 }; | 1419 }; |
1377 | 1420 |
1378 | 1421 |
1379 void DeferredInlineSmiOperationReversed::Generate() { | 1422 void DeferredInlineSmiOperationReversed::Generate() { |
1380 Result right; | 1423 __ push(Immediate(value_)); |
1381 enter()->Bind(&right); | 1424 __ push(src_); |
1382 cgen()->frame()->Push(value_); | |
1383 cgen()->frame()->Push(&right); | |
1384 GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED); | 1425 GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED); |
1385 Result answer = cgen()->frame()->CallStub(&igostub, 2); | 1426 __ CallStub(&igostub); |
1386 exit_.Jump(&answer); | 1427 if (!dst_.is(eax)) __ mov(dst_, eax); |
1387 } | 1428 } |
1388 | 1429 |
1389 | 1430 |
| 1431 // The result of src + value is in dst. It either overflowed or was not |
| 1432 // smi tagged. Undo the speculative addition and call the appropriate |
| 1433 // specialized stub for add. The result is left in dst. |
1390 class DeferredInlineSmiAdd: public DeferredCode { | 1434 class DeferredInlineSmiAdd: public DeferredCode { |
1391 public: | 1435 public: |
1392 DeferredInlineSmiAdd(Smi* value, | 1436 DeferredInlineSmiAdd(Register dst, |
| 1437 Smi* value, |
1393 OverwriteMode overwrite_mode) | 1438 OverwriteMode overwrite_mode) |
1394 : value_(value), | 1439 : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) { |
1395 overwrite_mode_(overwrite_mode) { | |
1396 set_comment("[ DeferredInlineSmiAdd"); | 1440 set_comment("[ DeferredInlineSmiAdd"); |
1397 } | 1441 } |
1398 | 1442 |
1399 virtual void Generate(); | 1443 virtual void Generate(); |
1400 | 1444 |
1401 private: | 1445 private: |
| 1446 Register dst_; |
1402 Smi* value_; | 1447 Smi* value_; |
1403 OverwriteMode overwrite_mode_; | 1448 OverwriteMode overwrite_mode_; |
1404 }; | 1449 }; |
1405 | 1450 |
1406 | 1451 |
| 1452 void DeferredInlineSmiAdd::Generate() { |
| 1453 // Undo the optimistic add operation and call the shared stub. |
| 1454 __ sub(Operand(dst_), Immediate(value_)); |
| 1455 __ push(dst_); |
| 1456 __ push(Immediate(value_)); |
| 1457 GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED); |
| 1458 __ CallStub(&igostub); |
| 1459 if (!dst_.is(eax)) __ mov(dst_, eax); |
| 1460 } |
| 1461 |
| 1462 |
| 1463 // The result of value + src is in dst. It either overflowed or was not |
| 1464 // smi tagged. Undo the speculative addition and call the appropriate |
| 1465 // specialized stub for add. The result is left in dst. |
1407 class DeferredInlineSmiAddReversed: public DeferredCode { | 1466 class DeferredInlineSmiAddReversed: public DeferredCode { |
1408 public: | 1467 public: |
1409 DeferredInlineSmiAddReversed(Smi* value, | 1468 DeferredInlineSmiAddReversed(Register dst, |
| 1469 Smi* value, |
1410 OverwriteMode overwrite_mode) | 1470 OverwriteMode overwrite_mode) |
1411 : value_(value), | 1471 : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) { |
1412 overwrite_mode_(overwrite_mode) { | |
1413 set_comment("[ DeferredInlineSmiAddReversed"); | 1472 set_comment("[ DeferredInlineSmiAddReversed"); |
1414 } | 1473 } |
1415 | 1474 |
1416 virtual void Generate(); | 1475 virtual void Generate(); |
1417 | 1476 |
1418 private: | 1477 private: |
| 1478 Register dst_; |
1419 Smi* value_; | 1479 Smi* value_; |
1420 OverwriteMode overwrite_mode_; | 1480 OverwriteMode overwrite_mode_; |
1421 }; | 1481 }; |
1422 | 1482 |
1423 | 1483 |
| 1484 void DeferredInlineSmiAddReversed::Generate() { |
| 1485 // Undo the optimistic add operation and call the shared stub. |
| 1486 __ sub(Operand(dst_), Immediate(value_)); |
| 1487 __ push(Immediate(value_)); |
| 1488 __ push(dst_); |
| 1489 GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED); |
| 1490 __ CallStub(&igostub); |
| 1491 if (!dst_.is(eax)) __ mov(dst_, eax); |
| 1492 } |
| 1493 |
| 1494 |
| 1495 // The result of src - value is in dst. It either overflowed or was not |
| 1496 // smi tagged. Undo the speculative subtraction and call the |
| 1497 // appropriate specialized stub for subtract. The result is left in |
| 1498 // dst. |
1424 class DeferredInlineSmiSub: public DeferredCode { | 1499 class DeferredInlineSmiSub: public DeferredCode { |
1425 public: | 1500 public: |
1426 DeferredInlineSmiSub(Smi* value, | 1501 DeferredInlineSmiSub(Register dst, |
| 1502 Smi* value, |
1427 OverwriteMode overwrite_mode) | 1503 OverwriteMode overwrite_mode) |
1428 : value_(value), | 1504 : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) { |
1429 overwrite_mode_(overwrite_mode) { | |
1430 set_comment("[ DeferredInlineSmiSub"); | 1505 set_comment("[ DeferredInlineSmiSub"); |
1431 } | 1506 } |
1432 | 1507 |
1433 virtual void Generate(); | 1508 virtual void Generate(); |
1434 | 1509 |
1435 private: | 1510 private: |
| 1511 Register dst_; |
1436 Smi* value_; | 1512 Smi* value_; |
1437 OverwriteMode overwrite_mode_; | 1513 OverwriteMode overwrite_mode_; |
1438 }; | 1514 }; |
1439 | 1515 |
1440 | 1516 |
1441 #undef __ | 1517 void DeferredInlineSmiSub::Generate() { |
1442 #define __ ACCESS_MASM(cgen()->masm()) | 1518 // Undo the optimistic sub operation and call the shared stub. |
1443 | 1519 __ add(Operand(dst_), Immediate(value_)); |
1444 | 1520 __ push(dst_); |
1445 void DeferredInlineSmiAdd::Generate() { | 1521 __ push(Immediate(value_)); |
1446 // Undo the optimistic add operation and call the shared stub. | 1522 GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED); |
1447 Result left; // Initially left + value_. | 1523 __ CallStub(&igostub); |
1448 enter()->Bind(&left); | 1524 if (!dst_.is(eax)) __ mov(dst_, eax); |
1449 left.ToRegister(); | |
1450 cgen()->frame()->Spill(left.reg()); | |
1451 __ sub(Operand(left.reg()), Immediate(value_)); | |
1452 cgen()->frame()->Push(&left); | |
1453 cgen()->frame()->Push(value_); | |
1454 GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED); | |
1455 Result answer = cgen()->frame()->CallStub(&igostub, 2); | |
1456 exit_.Jump(&answer); | |
1457 } | 1525 } |
1458 | 1526 |
1459 | 1527 |
1460 void DeferredInlineSmiAddReversed::Generate() { | |
1461 // Undo the optimistic add operation and call the shared stub. | |
1462 Result right; // Initially value_ + right. | |
1463 enter()->Bind(&right); | |
1464 right.ToRegister(); | |
1465 cgen()->frame()->Spill(right.reg()); | |
1466 __ sub(Operand(right.reg()), Immediate(value_)); | |
1467 cgen()->frame()->Push(value_); | |
1468 cgen()->frame()->Push(&right); | |
1469 GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED); | |
1470 Result answer = cgen()->frame()->CallStub(&igostub, 2); | |
1471 exit_.Jump(&answer); | |
1472 } | |
1473 | |
1474 | |
1475 void DeferredInlineSmiSub::Generate() { | |
1476 // Undo the optimistic sub operation and call the shared stub. | |
1477 Result left; // Initially left - value_. | |
1478 enter()->Bind(&left); | |
1479 left.ToRegister(); | |
1480 cgen()->frame()->Spill(left.reg()); | |
1481 __ add(Operand(left.reg()), Immediate(value_)); | |
1482 cgen()->frame()->Push(&left); | |
1483 cgen()->frame()->Push(value_); | |
1484 GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED); | |
1485 Result answer = cgen()->frame()->CallStub(&igostub, 2); | |
1486 exit_.Jump(&answer); | |
1487 } | |
1488 | |
1489 | |
1490 #undef __ | |
1491 #define __ ACCESS_MASM(masm_) | |
1492 | |
1493 | |
1494 class DeferredInlineSmiSubReversed: public DeferredCode { | |
1495 public: | |
1496 DeferredInlineSmiSubReversed(Smi* value, | |
1497 OverwriteMode overwrite_mode) | |
1498 : value_(value), | |
1499 overwrite_mode_(overwrite_mode) { | |
1500 set_comment("[ DeferredInlineSmiSubReversed"); | |
1501 } | |
1502 | |
1503 virtual void Generate(); | |
1504 | |
1505 private: | |
1506 Smi* value_; | |
1507 OverwriteMode overwrite_mode_; | |
1508 }; | |
1509 | |
1510 | |
1511 void DeferredInlineSmiSubReversed::Generate() { | |
1512 // Call the shared stub. | |
1513 Result right; | |
1514 enter()->Bind(&right); | |
1515 cgen()->frame()->Push(value_); | |
1516 cgen()->frame()->Push(&right); | |
1517 GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED); | |
1518 Result answer = cgen()->frame()->CallStub(&igostub, 2); | |
1519 exit_.Jump(&answer); | |
1520 } | |
1521 | |
1522 | |
1523 void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op, | 1528 void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op, |
1524 Result* operand, | 1529 Result* operand, |
1525 Handle<Object> value, | 1530 Handle<Object> value, |
1526 SmiAnalysis* type, | 1531 SmiAnalysis* type, |
1527 bool reversed, | 1532 bool reversed, |
1528 OverwriteMode overwrite_mode) { | 1533 OverwriteMode overwrite_mode) { |
1529 // NOTE: This is an attempt to inline (a bit) more of the code for | 1534 // NOTE: This is an attempt to inline (a bit) more of the code for |
1530 // some possible smi operations (like + and -) when (at least) one | 1535 // some possible smi operations (like + and -) when (at least) one |
1531 // of the operands is a constant smi. | 1536 // of the operands is a constant smi. |
1532 // Consumes the argument "operand". | 1537 // Consumes the argument "operand". |
(...skipping 14 matching lines...) Expand all Loading... |
1547 } | 1552 } |
1548 | 1553 |
1549 // Get the literal value. | 1554 // Get the literal value. |
1550 Smi* smi_value = Smi::cast(*value); | 1555 Smi* smi_value = Smi::cast(*value); |
1551 int int_value = smi_value->value(); | 1556 int int_value = smi_value->value(); |
1552 | 1557 |
1553 switch (op) { | 1558 switch (op) { |
1554 case Token::ADD: { | 1559 case Token::ADD: { |
1555 operand->ToRegister(); | 1560 operand->ToRegister(); |
1556 frame_->Spill(operand->reg()); | 1561 frame_->Spill(operand->reg()); |
1557 __ add(Operand(operand->reg()), Immediate(value)); | |
1558 | 1562 |
| 1563 // Optimistically add. Call the specialized add stub if the |
| 1564 // result is not a smi or overflows. |
1559 DeferredCode* deferred = NULL; | 1565 DeferredCode* deferred = NULL; |
1560 if (reversed) { | 1566 if (reversed) { |
1561 deferred = new DeferredInlineSmiAddReversed(smi_value, overwrite_mode); | 1567 deferred = new DeferredInlineSmiAddReversed(operand->reg(), |
| 1568 smi_value, |
| 1569 overwrite_mode); |
1562 } else { | 1570 } else { |
1563 deferred = new DeferredInlineSmiAdd(smi_value, overwrite_mode); | 1571 deferred = new DeferredInlineSmiAdd(operand->reg(), |
| 1572 smi_value, |
| 1573 overwrite_mode); |
1564 } | 1574 } |
1565 deferred->SetEntryFrame(operand); | 1575 __ add(Operand(operand->reg()), Immediate(value)); |
1566 deferred->enter()->Branch(overflow, operand, not_taken); | 1576 deferred->Branch(overflow); |
1567 __ test(operand->reg(), Immediate(kSmiTagMask)); | 1577 __ test(operand->reg(), Immediate(kSmiTagMask)); |
1568 deferred->enter()->Branch(not_zero, operand, not_taken); | 1578 deferred->Branch(not_zero); |
1569 deferred->BindExit(operand); | 1579 deferred->BindExit(); |
1570 frame_->Push(operand); | 1580 frame_->Push(operand); |
1571 break; | 1581 break; |
1572 } | 1582 } |
1573 | 1583 |
1574 case Token::SUB: { | 1584 case Token::SUB: { |
1575 DeferredCode* deferred = NULL; | 1585 DeferredCode* deferred = NULL; |
1576 Result answer; // Only allocate a new register if reversed. | 1586 Result answer; // Only allocate a new register if reversed. |
1577 if (reversed) { | 1587 if (reversed) { |
| 1588 // The reversed case is only hit when the right operand is not a |
| 1589 // constant. |
| 1590 ASSERT(operand->is_register()); |
1578 answer = allocator()->Allocate(); | 1591 answer = allocator()->Allocate(); |
1579 ASSERT(answer.is_valid()); | 1592 ASSERT(answer.is_valid()); |
1580 deferred = new DeferredInlineSmiSubReversed(smi_value, overwrite_mode); | |
1581 __ Set(answer.reg(), Immediate(value)); | 1593 __ Set(answer.reg(), Immediate(value)); |
1582 // We are in the reversed case so they can't both be Smi constants. | 1594 deferred = new DeferredInlineSmiOperationReversed(op, |
1583 ASSERT(operand->is_register()); | 1595 answer.reg(), |
| 1596 smi_value, |
| 1597 operand->reg(), |
| 1598 overwrite_mode); |
1584 __ sub(answer.reg(), Operand(operand->reg())); | 1599 __ sub(answer.reg(), Operand(operand->reg())); |
1585 } else { | 1600 } else { |
1586 operand->ToRegister(); | 1601 operand->ToRegister(); |
1587 frame_->Spill(operand->reg()); | 1602 frame_->Spill(operand->reg()); |
1588 deferred = new DeferredInlineSmiSub(smi_value, overwrite_mode); | 1603 answer = *operand; |
| 1604 deferred = new DeferredInlineSmiSub(operand->reg(), |
| 1605 smi_value, |
| 1606 overwrite_mode); |
1589 __ sub(Operand(operand->reg()), Immediate(value)); | 1607 __ sub(Operand(operand->reg()), Immediate(value)); |
1590 answer = *operand; | |
1591 } | 1608 } |
1592 deferred->SetEntryFrame(operand); | 1609 deferred->Branch(overflow); |
1593 deferred->enter()->Branch(overflow, operand, not_taken); | |
1594 __ test(answer.reg(), Immediate(kSmiTagMask)); | 1610 __ test(answer.reg(), Immediate(kSmiTagMask)); |
1595 deferred->enter()->Branch(not_zero, operand, not_taken); | 1611 deferred->Branch(not_zero); |
| 1612 deferred->BindExit(); |
1596 operand->Unuse(); | 1613 operand->Unuse(); |
1597 deferred->BindExit(&answer); | |
1598 frame_->Push(&answer); | 1614 frame_->Push(&answer); |
1599 break; | 1615 break; |
1600 } | 1616 } |
1601 | 1617 |
1602 case Token::SAR: { | 1618 case Token::SAR: |
1603 if (reversed) { | 1619 if (reversed) { |
1604 Result constant_operand(value); | 1620 Result constant_operand(value); |
1605 LikelySmiBinaryOperation(op, &constant_operand, operand, | 1621 LikelySmiBinaryOperation(op, &constant_operand, operand, |
1606 overwrite_mode); | 1622 overwrite_mode); |
1607 } else { | 1623 } else { |
1608 // Only the least significant 5 bits of the shift value are used. | 1624 // Only the least significant 5 bits of the shift value are used. |
1609 // In the slow case, this masking is done inside the runtime call. | 1625 // In the slow case, this masking is done inside the runtime call. |
1610 int shift_value = int_value & 0x1f; | 1626 int shift_value = int_value & 0x1f; |
1611 DeferredCode* deferred = | |
1612 new DeferredInlineSmiOperation(op, smi_value, overwrite_mode); | |
1613 operand->ToRegister(); | 1627 operand->ToRegister(); |
| 1628 frame_->Spill(operand->reg()); |
| 1629 DeferredInlineSmiOperation* deferred = |
| 1630 new DeferredInlineSmiOperation(op, |
| 1631 operand->reg(), |
| 1632 operand->reg(), |
| 1633 smi_value, |
| 1634 overwrite_mode); |
1614 __ test(operand->reg(), Immediate(kSmiTagMask)); | 1635 __ test(operand->reg(), Immediate(kSmiTagMask)); |
1615 deferred->enter()->Branch(not_zero, operand, not_taken); | 1636 deferred->Branch(not_zero); |
1616 if (shift_value > 0) { | 1637 if (shift_value > 0) { |
1617 frame_->Spill(operand->reg()); | |
1618 __ sar(operand->reg(), shift_value); | 1638 __ sar(operand->reg(), shift_value); |
1619 __ and_(operand->reg(), ~kSmiTagMask); | 1639 __ and_(operand->reg(), ~kSmiTagMask); |
1620 } | 1640 } |
1621 deferred->BindExit(operand); | 1641 deferred->BindExit(); |
1622 frame_->Push(operand); | 1642 frame_->Push(operand); |
1623 } | 1643 } |
1624 break; | 1644 break; |
1625 } | |
1626 | 1645 |
1627 case Token::SHR: { | 1646 case Token::SHR: |
1628 if (reversed) { | 1647 if (reversed) { |
1629 Result constant_operand(value); | 1648 Result constant_operand(value); |
1630 LikelySmiBinaryOperation(op, &constant_operand, operand, | 1649 LikelySmiBinaryOperation(op, &constant_operand, operand, |
1631 overwrite_mode); | 1650 overwrite_mode); |
1632 } else { | 1651 } else { |
1633 // Only the least significant 5 bits of the shift value are used. | 1652 // Only the least significant 5 bits of the shift value are used. |
1634 // In the slow case, this masking is done inside the runtime call. | 1653 // In the slow case, this masking is done inside the runtime call. |
1635 int shift_value = int_value & 0x1f; | 1654 int shift_value = int_value & 0x1f; |
1636 DeferredCode* deferred = | |
1637 new DeferredInlineSmiOperation(op, smi_value, overwrite_mode); | |
1638 operand->ToRegister(); | 1655 operand->ToRegister(); |
1639 __ test(operand->reg(), Immediate(kSmiTagMask)); | |
1640 deferred->enter()->Branch(not_zero, operand, not_taken); | |
1641 Result answer = allocator()->Allocate(); | 1656 Result answer = allocator()->Allocate(); |
1642 ASSERT(answer.is_valid()); | 1657 ASSERT(answer.is_valid()); |
| 1658 DeferredInlineSmiOperation* deferred = |
| 1659 new DeferredInlineSmiOperation(op, |
| 1660 answer.reg(), |
| 1661 operand->reg(), |
| 1662 smi_value, |
| 1663 overwrite_mode); |
| 1664 __ test(operand->reg(), Immediate(kSmiTagMask)); |
| 1665 deferred->Branch(not_zero); |
1643 __ mov(answer.reg(), operand->reg()); | 1666 __ mov(answer.reg(), operand->reg()); |
1644 __ sar(answer.reg(), kSmiTagSize); | 1667 __ sar(answer.reg(), kSmiTagSize); |
1645 __ shr(answer.reg(), shift_value); | 1668 __ shr(answer.reg(), shift_value); |
1646 // A negative Smi shifted right two is in the positive Smi range. | 1669 // A negative Smi shifted right two is in the positive Smi range. |
1647 if (shift_value < 2) { | 1670 if (shift_value < 2) { |
1648 __ test(answer.reg(), Immediate(0xc0000000)); | 1671 __ test(answer.reg(), Immediate(0xc0000000)); |
1649 deferred->enter()->Branch(not_zero, operand, not_taken); | 1672 deferred->Branch(not_zero); |
1650 } | 1673 } |
1651 operand->Unuse(); | 1674 operand->Unuse(); |
1652 ASSERT(kSmiTagSize == times_2); // Adjust the code if not true. | 1675 ASSERT(kSmiTagSize == times_2); // Adjust the code if not true. |
1653 __ lea(answer.reg(), | 1676 __ lea(answer.reg(), |
1654 Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); | 1677 Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); |
1655 deferred->BindExit(&answer); | 1678 deferred->BindExit(); |
1656 frame_->Push(&answer); | 1679 frame_->Push(&answer); |
1657 } | 1680 } |
1658 break; | 1681 break; |
1659 } | |
1660 | 1682 |
1661 case Token::SHL: { | 1683 case Token::SHL: |
1662 if (reversed) { | 1684 if (reversed) { |
1663 Result constant_operand(value); | 1685 Result constant_operand(value); |
1664 LikelySmiBinaryOperation(op, &constant_operand, operand, | 1686 LikelySmiBinaryOperation(op, &constant_operand, operand, |
1665 overwrite_mode); | 1687 overwrite_mode); |
1666 } else { | 1688 } else { |
1667 // Only the least significant 5 bits of the shift value are used. | 1689 // Only the least significant 5 bits of the shift value are used. |
1668 // In the slow case, this masking is done inside the runtime call. | 1690 // In the slow case, this masking is done inside the runtime call. |
1669 int shift_value = int_value & 0x1f; | 1691 int shift_value = int_value & 0x1f; |
1670 DeferredCode* deferred = | |
1671 new DeferredInlineSmiOperation(op, smi_value, overwrite_mode); | |
1672 operand->ToRegister(); | 1692 operand->ToRegister(); |
1673 __ test(operand->reg(), Immediate(kSmiTagMask)); | 1693 if (shift_value == 0) { |
1674 deferred->enter()->Branch(not_zero, operand, not_taken); | 1694 DeferredInlineSmiOperation* deferred = |
1675 if (shift_value != 0) { | 1695 new DeferredInlineSmiOperation(op, |
| 1696 operand->reg(), |
| 1697 operand->reg(), |
| 1698 smi_value, |
| 1699 overwrite_mode); |
| 1700 __ test(operand->reg(), Immediate(kSmiTagMask)); |
| 1701 deferred->Branch(not_zero); |
| 1702 deferred->BindExit(); |
| 1703 frame_->Push(operand); |
| 1704 } else { |
| 1705 // Use a fresh temporary for nonzero shift values. |
1676 Result answer = allocator()->Allocate(); | 1706 Result answer = allocator()->Allocate(); |
1677 ASSERT(answer.is_valid()); | 1707 ASSERT(answer.is_valid()); |
| 1708 DeferredInlineSmiOperation* deferred = |
| 1709 new DeferredInlineSmiOperation(op, |
| 1710 answer.reg(), |
| 1711 operand->reg(), |
| 1712 smi_value, |
| 1713 overwrite_mode); |
| 1714 __ test(operand->reg(), Immediate(kSmiTagMask)); |
| 1715 deferred->Branch(not_zero); |
1678 __ mov(answer.reg(), operand->reg()); | 1716 __ mov(answer.reg(), operand->reg()); |
1679 ASSERT(kSmiTag == 0); // adjust code if not the case | 1717 ASSERT(kSmiTag == 0); // adjust code if not the case |
1680 // We do no shifts, only the Smi conversion, if shift_value is 1. | 1718 // We do no shifts, only the Smi conversion, if shift_value is 1. |
1681 if (shift_value > 1) { | 1719 if (shift_value > 1) { |
1682 __ shl(answer.reg(), shift_value - 1); | 1720 __ shl(answer.reg(), shift_value - 1); |
1683 } | 1721 } |
1684 // Convert int result to Smi, checking that it is in int range. | 1722 // Convert int result to Smi, checking that it is in int range. |
1685 ASSERT(kSmiTagSize == times_2); // adjust code if not the case | 1723 ASSERT(kSmiTagSize == 1); // adjust code if not the case |
1686 __ add(answer.reg(), Operand(answer.reg())); | 1724 __ add(answer.reg(), Operand(answer.reg())); |
1687 deferred->enter()->Branch(overflow, operand, not_taken); | 1725 deferred->Branch(overflow); |
| 1726 deferred->BindExit(); |
1688 operand->Unuse(); | 1727 operand->Unuse(); |
1689 deferred->BindExit(&answer); | |
1690 frame_->Push(&answer); | 1728 frame_->Push(&answer); |
1691 } else { | |
1692 deferred->BindExit(operand); | |
1693 frame_->Push(operand); | |
1694 } | 1729 } |
1695 } | 1730 } |
1696 break; | 1731 break; |
1697 } | |
1698 | 1732 |
1699 case Token::BIT_OR: | 1733 case Token::BIT_OR: |
1700 case Token::BIT_XOR: | 1734 case Token::BIT_XOR: |
1701 case Token::BIT_AND: { | 1735 case Token::BIT_AND: { |
| 1736 operand->ToRegister(); |
| 1737 frame_->Spill(operand->reg()); |
1702 DeferredCode* deferred = NULL; | 1738 DeferredCode* deferred = NULL; |
1703 if (reversed) { | 1739 if (reversed) { |
1704 deferred = new DeferredInlineSmiOperationReversed(op, smi_value, | 1740 deferred = new DeferredInlineSmiOperationReversed(op, |
| 1741 operand->reg(), |
| 1742 smi_value, |
| 1743 operand->reg(), |
1705 overwrite_mode); | 1744 overwrite_mode); |
1706 } else { | 1745 } else { |
1707 deferred = new DeferredInlineSmiOperation(op, smi_value, | 1746 deferred = new DeferredInlineSmiOperation(op, |
| 1747 operand->reg(), |
| 1748 operand->reg(), |
| 1749 smi_value, |
1708 overwrite_mode); | 1750 overwrite_mode); |
1709 } | 1751 } |
1710 operand->ToRegister(); | |
1711 __ test(operand->reg(), Immediate(kSmiTagMask)); | 1752 __ test(operand->reg(), Immediate(kSmiTagMask)); |
1712 deferred->enter()->Branch(not_zero, operand, not_taken); | 1753 deferred->Branch(not_zero); |
1713 frame_->Spill(operand->reg()); | |
1714 if (op == Token::BIT_AND) { | 1754 if (op == Token::BIT_AND) { |
1715 __ and_(Operand(operand->reg()), Immediate(value)); | 1755 __ and_(Operand(operand->reg()), Immediate(value)); |
1716 } else if (op == Token::BIT_XOR) { | 1756 } else if (op == Token::BIT_XOR) { |
1717 if (int_value != 0) { | 1757 if (int_value != 0) { |
1718 __ xor_(Operand(operand->reg()), Immediate(value)); | 1758 __ xor_(Operand(operand->reg()), Immediate(value)); |
1719 } | 1759 } |
1720 } else { | 1760 } else { |
1721 ASSERT(op == Token::BIT_OR); | 1761 ASSERT(op == Token::BIT_OR); |
1722 if (int_value != 0) { | 1762 if (int_value != 0) { |
1723 __ or_(Operand(operand->reg()), Immediate(value)); | 1763 __ or_(Operand(operand->reg()), Immediate(value)); |
1724 } | 1764 } |
1725 } | 1765 } |
1726 deferred->BindExit(operand); | 1766 deferred->BindExit(); |
1727 frame_->Push(operand); | 1767 frame_->Push(operand); |
1728 break; | 1768 break; |
1729 } | 1769 } |
1730 | 1770 |
1731 default: { | 1771 default: { |
1732 Result constant_operand(value); | 1772 Result constant_operand(value); |
1733 if (reversed) { | 1773 if (reversed) { |
1734 LikelySmiBinaryOperation(op, &constant_operand, operand, | 1774 LikelySmiBinaryOperation(op, &constant_operand, operand, |
1735 overwrite_mode); | 1775 overwrite_mode); |
1736 } else { | 1776 } else { |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1983 Result answer = frame_->CallStub(&call_function, arg_count + 1); | 2023 Result answer = frame_->CallStub(&call_function, arg_count + 1); |
1984 // Restore context and replace function on the stack with the | 2024 // Restore context and replace function on the stack with the |
1985 // result of the stub invocation. | 2025 // result of the stub invocation. |
1986 frame_->RestoreContextRegister(); | 2026 frame_->RestoreContextRegister(); |
1987 frame_->SetElementAt(0, &answer); | 2027 frame_->SetElementAt(0, &answer); |
1988 } | 2028 } |
1989 | 2029 |
1990 | 2030 |
1991 class DeferredStackCheck: public DeferredCode { | 2031 class DeferredStackCheck: public DeferredCode { |
1992 public: | 2032 public: |
1993 explicit DeferredStackCheck() { | 2033 DeferredStackCheck() { |
1994 set_comment("[ DeferredStackCheck"); | 2034 set_comment("[ DeferredStackCheck"); |
1995 } | 2035 } |
1996 | 2036 |
1997 virtual void Generate(); | 2037 virtual void Generate(); |
1998 }; | 2038 }; |
1999 | 2039 |
2000 | 2040 |
2001 void DeferredStackCheck::Generate() { | 2041 void DeferredStackCheck::Generate() { |
2002 enter()->Bind(); | |
2003 StackCheckStub stub; | 2042 StackCheckStub stub; |
2004 Result ignored = cgen()->frame()->CallStub(&stub, 0); | 2043 __ CallStub(&stub); |
2005 ignored.Unuse(); | |
2006 exit_.Jump(); | |
2007 } | 2044 } |
2008 | 2045 |
2009 | 2046 |
2010 void CodeGenerator::CheckStack() { | 2047 void CodeGenerator::CheckStack() { |
2011 if (FLAG_check_stack) { | 2048 if (FLAG_check_stack) { |
2012 DeferredStackCheck* deferred = new DeferredStackCheck; | 2049 DeferredStackCheck* deferred = new DeferredStackCheck; |
2013 ExternalReference stack_guard_limit = | 2050 ExternalReference stack_guard_limit = |
2014 ExternalReference::address_of_stack_guard_limit(); | 2051 ExternalReference::address_of_stack_guard_limit(); |
2015 __ cmp(esp, Operand::StaticVariable(stack_guard_limit)); | 2052 __ cmp(esp, Operand::StaticVariable(stack_guard_limit)); |
2016 deferred->enter()->Branch(below, not_taken); | 2053 deferred->Branch(below); |
2017 deferred->BindExit(); | 2054 deferred->BindExit(); |
2018 } | 2055 } |
2019 } | 2056 } |
2020 | 2057 |
2021 | 2058 |
2022 void CodeGenerator::VisitAndSpill(Statement* statement) { | 2059 void CodeGenerator::VisitAndSpill(Statement* statement) { |
2023 ASSERT(in_spilled_code()); | 2060 ASSERT(in_spilled_code()); |
2024 set_in_spilled_code(false); | 2061 set_in_spilled_code(false); |
2025 Visit(statement); | 2062 Visit(statement); |
2026 if (frame_ != NULL) { | 2063 if (frame_ != NULL) { |
(...skipping 1831 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3858 } | 3895 } |
3859 | 3896 |
3860 | 3897 |
3861 bool CodeGenerator::IsUnsafeSmi(Handle<Object> value) { | 3898 bool CodeGenerator::IsUnsafeSmi(Handle<Object> value) { |
3862 if (!value->IsSmi()) return false; | 3899 if (!value->IsSmi()) return false; |
3863 int int_value = Smi::cast(*value)->value(); | 3900 int int_value = Smi::cast(*value)->value(); |
3864 return !is_intn(int_value, kMaxSmiInlinedBits); | 3901 return !is_intn(int_value, kMaxSmiInlinedBits); |
3865 } | 3902 } |
3866 | 3903 |
3867 | 3904 |
| 3905 // Materialize the regexp literal 'node' in the literals array |
| 3906 // 'literals' of the function. Leave the regexp boilerplate in |
| 3907 // 'boilerplate'. |
3868 class DeferredRegExpLiteral: public DeferredCode { | 3908 class DeferredRegExpLiteral: public DeferredCode { |
3869 public: | 3909 public: |
3870 explicit DeferredRegExpLiteral(RegExpLiteral* node) : node_(node) { | 3910 DeferredRegExpLiteral(Register boilerplate, |
| 3911 Register literals, |
| 3912 RegExpLiteral* node) |
| 3913 : boilerplate_(boilerplate), literals_(literals), node_(node) { |
3871 set_comment("[ DeferredRegExpLiteral"); | 3914 set_comment("[ DeferredRegExpLiteral"); |
3872 } | 3915 } |
3873 | 3916 |
3874 virtual void Generate(); | 3917 void Generate(); |
3875 | 3918 |
3876 private: | 3919 private: |
| 3920 Register boilerplate_; |
| 3921 Register literals_; |
3877 RegExpLiteral* node_; | 3922 RegExpLiteral* node_; |
3878 }; | 3923 }; |
3879 | 3924 |
3880 | 3925 |
3881 void DeferredRegExpLiteral::Generate() { | 3926 void DeferredRegExpLiteral::Generate() { |
3882 Result literals; | |
3883 enter()->Bind(&literals); | |
3884 // Since the entry is undefined we call the runtime system to | 3927 // Since the entry is undefined we call the runtime system to |
3885 // compute the literal. | 3928 // compute the literal. |
3886 | |
3887 VirtualFrame* frame = cgen()->frame(); | |
3888 // Literal array (0). | 3929 // Literal array (0). |
3889 frame->Push(&literals); | 3930 __ push(literals_); |
3890 // Literal index (1). | 3931 // Literal index (1). |
3891 frame->Push(Smi::FromInt(node_->literal_index())); | 3932 __ push(Immediate(Smi::FromInt(node_->literal_index()))); |
3892 // RegExp pattern (2). | 3933 // RegExp pattern (2). |
3893 frame->Push(node_->pattern()); | 3934 __ push(Immediate(node_->pattern())); |
3894 // RegExp flags (3). | 3935 // RegExp flags (3). |
3895 frame->Push(node_->flags()); | 3936 __ push(Immediate(node_->flags())); |
3896 Result boilerplate = | 3937 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); |
3897 frame->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); | 3938 if (!boilerplate_.is(eax)) __ mov(boilerplate_, eax); |
3898 exit_.Jump(&boilerplate); | |
3899 } | 3939 } |
3900 | 3940 |
3901 | 3941 |
3902 void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) { | 3942 void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) { |
3903 Comment cmnt(masm_, "[ RegExp Literal"); | 3943 Comment cmnt(masm_, "[ RegExp Literal"); |
3904 DeferredRegExpLiteral* deferred = new DeferredRegExpLiteral(node); | |
3905 | 3944 |
3906 // Retrieve the literals array and check the allocated entry. Begin | 3945 // Retrieve the literals array and check the allocated entry. Begin |
3907 // with a writable copy of the function of this activation in a | 3946 // with a writable copy of the function of this activation in a |
3908 // register. | 3947 // register. |
3909 frame_->PushFunction(); | 3948 frame_->PushFunction(); |
3910 Result literals = frame_->Pop(); | 3949 Result literals = frame_->Pop(); |
3911 literals.ToRegister(); | 3950 literals.ToRegister(); |
3912 frame_->Spill(literals.reg()); | 3951 frame_->Spill(literals.reg()); |
3913 | 3952 |
3914 // Load the literals array of the function. | 3953 // Load the literals array of the function. |
3915 __ mov(literals.reg(), | 3954 __ mov(literals.reg(), |
3916 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset)); | 3955 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset)); |
3917 | 3956 |
3918 // Load the literal at the ast saved index. | 3957 // Load the literal at the ast saved index. |
| 3958 Result boilerplate = allocator_->Allocate(); |
| 3959 ASSERT(boilerplate.is_valid()); |
3919 int literal_offset = | 3960 int literal_offset = |
3920 FixedArray::kHeaderSize + node->literal_index() * kPointerSize; | 3961 FixedArray::kHeaderSize + node->literal_index() * kPointerSize; |
3921 Result boilerplate = allocator_->Allocate(); | |
3922 ASSERT(boilerplate.is_valid()); | |
3923 __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset)); | 3962 __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset)); |
3924 | 3963 |
3925 // Check whether we need to materialize the RegExp object. If so, | 3964 // Check whether we need to materialize the RegExp object. If so, |
3926 // jump to the deferred code passing the literals array. | 3965 // jump to the deferred code passing the literals array. |
| 3966 DeferredRegExpLiteral* deferred = |
| 3967 new DeferredRegExpLiteral(boilerplate.reg(), literals.reg(), node); |
3927 __ cmp(boilerplate.reg(), Factory::undefined_value()); | 3968 __ cmp(boilerplate.reg(), Factory::undefined_value()); |
3928 deferred->enter()->Branch(equal, &literals, not_taken); | 3969 deferred->Branch(equal); |
3929 | 3970 deferred->BindExit(); |
3930 literals.Unuse(); | 3971 literals.Unuse(); |
3931 // The deferred code returns the boilerplate object. | |
3932 deferred->BindExit(&boilerplate); | |
3933 | 3972 |
3934 // Push the boilerplate object. | 3973 // Push the boilerplate object. |
3935 frame_->Push(&boilerplate); | 3974 frame_->Push(&boilerplate); |
3936 } | 3975 } |
3937 | 3976 |
3938 | 3977 |
3939 // This deferred code stub will be used for creating the boilerplate | 3978 // Materialize the object literal 'node' in the literals array |
3940 // by calling Runtime_CreateObjectLiteral. | 3979 // 'literals' of the function. Leave the object boilerplate in |
3941 // Each created boilerplate is stored in the JSFunction and they are | 3980 // 'boilerplate'. |
3942 // therefore context dependent. | |
3943 class DeferredObjectLiteral: public DeferredCode { | 3981 class DeferredObjectLiteral: public DeferredCode { |
3944 public: | 3982 public: |
3945 explicit DeferredObjectLiteral(ObjectLiteral* node) : node_(node) { | 3983 DeferredObjectLiteral(Register boilerplate, |
| 3984 Register literals, |
| 3985 ObjectLiteral* node) |
| 3986 : boilerplate_(boilerplate), literals_(literals), node_(node) { |
3946 set_comment("[ DeferredObjectLiteral"); | 3987 set_comment("[ DeferredObjectLiteral"); |
3947 } | 3988 } |
3948 | 3989 |
3949 virtual void Generate(); | 3990 void Generate(); |
3950 | 3991 |
3951 private: | 3992 private: |
| 3993 Register boilerplate_; |
| 3994 Register literals_; |
3952 ObjectLiteral* node_; | 3995 ObjectLiteral* node_; |
3953 }; | 3996 }; |
3954 | 3997 |
3955 | 3998 |
3956 void DeferredObjectLiteral::Generate() { | 3999 void DeferredObjectLiteral::Generate() { |
3957 Result literals; | |
3958 enter()->Bind(&literals); | |
3959 // Since the entry is undefined we call the runtime system to | 4000 // Since the entry is undefined we call the runtime system to |
3960 // compute the literal. | 4001 // compute the literal. |
3961 | |
3962 VirtualFrame* frame = cgen()->frame(); | |
3963 // Literal array (0). | 4002 // Literal array (0). |
3964 frame->Push(&literals); | 4003 __ push(literals_); |
3965 // Literal index (1). | 4004 // Literal index (1). |
3966 frame->Push(Smi::FromInt(node_->literal_index())); | 4005 __ push(Immediate(Smi::FromInt(node_->literal_index()))); |
3967 // Constant properties (2). | 4006 // Constant properties (2). |
3968 frame->Push(node_->constant_properties()); | 4007 __ push(Immediate(node_->constant_properties())); |
3969 Result boilerplate = | 4008 __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3); |
3970 frame->CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3); | 4009 if (!boilerplate_.is(eax)) __ mov(boilerplate_, eax); |
3971 exit_.Jump(&boilerplate); | |
3972 } | 4010 } |
3973 | 4011 |
3974 | 4012 |
3975 void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { | 4013 void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { |
3976 Comment cmnt(masm_, "[ ObjectLiteral"); | 4014 Comment cmnt(masm_, "[ ObjectLiteral"); |
3977 DeferredObjectLiteral* deferred = new DeferredObjectLiteral(node); | |
3978 | 4015 |
3979 // Retrieve the literals array and check the allocated entry. Begin | 4016 // Retrieve the literals array and check the allocated entry. Begin |
3980 // with a writable copy of the function of this activation in a | 4017 // with a writable copy of the function of this activation in a |
3981 // register. | 4018 // register. |
3982 frame_->PushFunction(); | 4019 frame_->PushFunction(); |
3983 Result literals = frame_->Pop(); | 4020 Result literals = frame_->Pop(); |
3984 literals.ToRegister(); | 4021 literals.ToRegister(); |
3985 frame_->Spill(literals.reg()); | 4022 frame_->Spill(literals.reg()); |
3986 | 4023 |
3987 // Load the literals array of the function. | 4024 // Load the literals array of the function. |
3988 __ mov(literals.reg(), | 4025 __ mov(literals.reg(), |
3989 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset)); | 4026 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset)); |
3990 | 4027 |
3991 // Load the literal at the ast saved index. | 4028 // Load the literal at the ast saved index. |
| 4029 Result boilerplate = allocator_->Allocate(); |
| 4030 ASSERT(boilerplate.is_valid()); |
3992 int literal_offset = | 4031 int literal_offset = |
3993 FixedArray::kHeaderSize + node->literal_index() * kPointerSize; | 4032 FixedArray::kHeaderSize + node->literal_index() * kPointerSize; |
3994 Result boilerplate = allocator_->Allocate(); | |
3995 ASSERT(boilerplate.is_valid()); | |
3996 __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset)); | 4033 __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset)); |
3997 | 4034 |
3998 // Check whether we need to materialize the object literal boilerplate. | 4035 // Check whether we need to materialize the object literal boilerplate. |
3999 // If so, jump to the deferred code passing the literals array. | 4036 // If so, jump to the deferred code passing the literals array. |
| 4037 DeferredObjectLiteral* deferred = |
| 4038 new DeferredObjectLiteral(boilerplate.reg(), literals.reg(), node); |
4000 __ cmp(boilerplate.reg(), Factory::undefined_value()); | 4039 __ cmp(boilerplate.reg(), Factory::undefined_value()); |
4001 deferred->enter()->Branch(equal, &literals, not_taken); | 4040 deferred->Branch(equal); |
4002 | 4041 deferred->BindExit(); |
4003 literals.Unuse(); | 4042 literals.Unuse(); |
4004 // The deferred code returns the boilerplate object. | |
4005 deferred->BindExit(&boilerplate); | |
4006 | 4043 |
4007 // Push the boilerplate object. | 4044 // Push the boilerplate object. |
4008 frame_->Push(&boilerplate); | 4045 frame_->Push(&boilerplate); |
4009 // Clone the boilerplate object. | 4046 // Clone the boilerplate object. |
4010 Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate; | 4047 Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate; |
4011 if (node->depth() == 1) { | 4048 if (node->depth() == 1) { |
4012 clone_function_id = Runtime::kCloneShallowLiteralBoilerplate; | 4049 clone_function_id = Runtime::kCloneShallowLiteralBoilerplate; |
4013 } | 4050 } |
4014 Result clone = frame_->CallRuntime(clone_function_id, 1); | 4051 Result clone = frame_->CallRuntime(clone_function_id, 1); |
4015 // Push the newly cloned literal object as the result. | 4052 // Push the newly cloned literal object as the result. |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4065 Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4); | 4102 Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4); |
4066 // Ignore the result. | 4103 // Ignore the result. |
4067 break; | 4104 break; |
4068 } | 4105 } |
4069 default: UNREACHABLE(); | 4106 default: UNREACHABLE(); |
4070 } | 4107 } |
4071 } | 4108 } |
4072 } | 4109 } |
4073 | 4110 |
4074 | 4111 |
4075 // This deferred code stub will be used for creating the boilerplate | 4112 // Materialize the array literal 'node' in the literals array 'literals' |
4076 // by calling Runtime_CreateArrayLiteralBoilerplate. | 4113 // of the function. Leave the array boilerplate in 'boilerplate'. |
4077 // Each created boilerplate is stored in the JSFunction and they are | |
4078 // therefore context dependent. | |
4079 class DeferredArrayLiteral: public DeferredCode { | 4114 class DeferredArrayLiteral: public DeferredCode { |
4080 public: | 4115 public: |
4081 explicit DeferredArrayLiteral(ArrayLiteral* node) : node_(node) { | 4116 DeferredArrayLiteral(Register boilerplate, |
| 4117 Register literals, |
| 4118 ArrayLiteral* node) |
| 4119 : boilerplate_(boilerplate), literals_(literals), node_(node) { |
4082 set_comment("[ DeferredArrayLiteral"); | 4120 set_comment("[ DeferredArrayLiteral"); |
4083 } | 4121 } |
4084 | 4122 |
4085 virtual void Generate(); | 4123 void Generate(); |
4086 | 4124 |
4087 private: | 4125 private: |
| 4126 Register boilerplate_; |
| 4127 Register literals_; |
4088 ArrayLiteral* node_; | 4128 ArrayLiteral* node_; |
4089 }; | 4129 }; |
4090 | 4130 |
4091 | 4131 |
4092 void DeferredArrayLiteral::Generate() { | 4132 void DeferredArrayLiteral::Generate() { |
4093 Result literals; | |
4094 enter()->Bind(&literals); | |
4095 // Since the entry is undefined we call the runtime system to | 4133 // Since the entry is undefined we call the runtime system to |
4096 // compute the literal. | 4134 // compute the literal. |
4097 | |
4098 VirtualFrame* frame = cgen()->frame(); | |
4099 // Literal array (0). | 4135 // Literal array (0). |
4100 frame->Push(&literals); | 4136 __ push(literals_); |
4101 // Literal index (1). | 4137 // Literal index (1). |
4102 frame->Push(Smi::FromInt(node_->literal_index())); | 4138 __ push(Immediate(Smi::FromInt(node_->literal_index()))); |
4103 // Constant properties (2). | 4139 // Constant properties (2). |
4104 frame->Push(node_->literals()); | 4140 __ push(Immediate(node_->literals())); |
4105 Result boilerplate = | 4141 __ CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3); |
4106 frame->CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3); | 4142 if (!boilerplate_.is(eax)) __ mov(boilerplate_, eax); |
4107 exit_.Jump(&boilerplate); | |
4108 } | 4143 } |
4109 | 4144 |
4110 | 4145 |
4111 void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { | 4146 void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { |
4112 Comment cmnt(masm_, "[ ArrayLiteral"); | 4147 Comment cmnt(masm_, "[ ArrayLiteral"); |
4113 DeferredArrayLiteral* deferred = new DeferredArrayLiteral(node); | |
4114 | 4148 |
4115 // Retrieve the literals array and check the allocated entry. Begin | 4149 // Retrieve the literals array and check the allocated entry. Begin |
4116 // with a writable copy of the function of this activation in a | 4150 // with a writable copy of the function of this activation in a |
4117 // register. | 4151 // register. |
4118 frame_->PushFunction(); | 4152 frame_->PushFunction(); |
4119 Result literals = frame_->Pop(); | 4153 Result literals = frame_->Pop(); |
4120 literals.ToRegister(); | 4154 literals.ToRegister(); |
4121 frame_->Spill(literals.reg()); | 4155 frame_->Spill(literals.reg()); |
4122 | 4156 |
4123 // Load the literals array of the function. | 4157 // Load the literals array of the function. |
4124 __ mov(literals.reg(), | 4158 __ mov(literals.reg(), |
4125 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset)); | 4159 FieldOperand(literals.reg(), JSFunction::kLiteralsOffset)); |
4126 | 4160 |
4127 // Load the literal at the ast saved index. | 4161 // Load the literal at the ast saved index. |
| 4162 Result boilerplate = allocator_->Allocate(); |
| 4163 ASSERT(boilerplate.is_valid()); |
4128 int literal_offset = | 4164 int literal_offset = |
4129 FixedArray::kHeaderSize + node->literal_index() * kPointerSize; | 4165 FixedArray::kHeaderSize + node->literal_index() * kPointerSize; |
4130 Result boilerplate = allocator_->Allocate(); | |
4131 ASSERT(boilerplate.is_valid()); | |
4132 __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset)); | 4166 __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset)); |
4133 | 4167 |
4134 // Check whether we need to materialize the object literal boilerplate. | 4168 // Check whether we need to materialize the object literal boilerplate. |
4135 // If so, jump to the deferred code passing the literals array. | 4169 // If so, jump to the deferred code passing the literals array. |
| 4170 DeferredArrayLiteral* deferred = |
| 4171 new DeferredArrayLiteral(boilerplate.reg(), literals.reg(), node); |
4136 __ cmp(boilerplate.reg(), Factory::undefined_value()); | 4172 __ cmp(boilerplate.reg(), Factory::undefined_value()); |
4137 deferred->enter()->Branch(equal, &literals, not_taken); | 4173 deferred->Branch(equal); |
| 4174 deferred->BindExit(); |
| 4175 literals.Unuse(); |
4138 | 4176 |
4139 literals.Unuse(); | 4177 // Push the resulting array literal boilerplate on the stack. |
4140 // The deferred code returns the boilerplate object. | |
4141 deferred->BindExit(&boilerplate); | |
4142 | |
4143 // Push the resulting array literal on the stack. | |
4144 frame_->Push(&boilerplate); | 4178 frame_->Push(&boilerplate); |
4145 | |
4146 // Clone the boilerplate object. | 4179 // Clone the boilerplate object. |
4147 Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate; | 4180 Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate; |
4148 if (node->depth() == 1) { | 4181 if (node->depth() == 1) { |
4149 clone_function_id = Runtime::kCloneShallowLiteralBoilerplate; | 4182 clone_function_id = Runtime::kCloneShallowLiteralBoilerplate; |
4150 } | 4183 } |
4151 Result clone = frame_->CallRuntime(clone_function_id, 1); | 4184 Result clone = frame_->CallRuntime(clone_function_id, 1); |
4152 // Push the newly cloned literal object as the result. | 4185 // Push the newly cloned literal object as the result. |
4153 frame_->Push(&clone); | 4186 frame_->Push(&clone); |
4154 | 4187 |
4155 // Generate code to set the elements in the array that are not | 4188 // Generate code to set the elements in the array that are not |
(...skipping 900 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5056 break; | 5089 break; |
5057 } | 5090 } |
5058 | 5091 |
5059 default: | 5092 default: |
5060 UNREACHABLE(); | 5093 UNREACHABLE(); |
5061 } | 5094 } |
5062 } | 5095 } |
5063 } | 5096 } |
5064 | 5097 |
5065 | 5098 |
5066 class DeferredCountOperation: public DeferredCode { | 5099 // The value in dst was optimistically incremented or decremented. The |
| 5100 // result overflowed or was not smi tagged. Undo the operation, call |
| 5101 // into the runtime to convert the argument to a number, and call the |
| 5102 // specialized add or subtract stub. The result is left in dst. |
| 5103 class DeferredPrefixCountOperation: public DeferredCode { |
5067 public: | 5104 public: |
5068 DeferredCountOperation(bool is_postfix, | 5105 DeferredPrefixCountOperation(Register dst, bool is_increment) |
5069 bool is_increment, | 5106 : dst_(dst), is_increment_(is_increment) { |
5070 int target_size) | |
5071 : is_postfix_(is_postfix), | |
5072 is_increment_(is_increment), | |
5073 target_size_(target_size) { | |
5074 set_comment("[ DeferredCountOperation"); | 5107 set_comment("[ DeferredCountOperation"); |
5075 } | 5108 } |
5076 | 5109 |
5077 virtual void Generate(); | 5110 virtual void Generate(); |
5078 | 5111 |
5079 private: | 5112 private: |
5080 bool is_postfix_; | 5113 Register dst_; |
5081 bool is_increment_; | 5114 bool is_increment_; |
5082 int target_size_; | |
5083 }; | 5115 }; |
5084 | 5116 |
5085 | 5117 |
5086 #undef __ | 5118 void DeferredPrefixCountOperation::Generate() { |
5087 #define __ ACCESS_MASM(cgen()->masm()) | |
5088 | |
5089 | |
5090 void DeferredCountOperation::Generate() { | |
5091 Result value; | |
5092 enter()->Bind(&value); | |
5093 VirtualFrame* frame = cgen()->frame(); | |
5094 // Undo the optimistic smi operation. | 5119 // Undo the optimistic smi operation. |
5095 value.ToRegister(); | |
5096 frame->Spill(value.reg()); | |
5097 if (is_increment_) { | 5120 if (is_increment_) { |
5098 __ sub(Operand(value.reg()), Immediate(Smi::FromInt(1))); | 5121 __ sub(Operand(dst_), Immediate(Smi::FromInt(1))); |
5099 } else { | 5122 } else { |
5100 __ add(Operand(value.reg()), Immediate(Smi::FromInt(1))); | 5123 __ add(Operand(dst_), Immediate(Smi::FromInt(1))); |
5101 } | 5124 } |
5102 frame->Push(&value); | 5125 __ push(dst_); |
5103 value = frame->InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION, 1); | 5126 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION); |
5104 frame->Push(&value); | 5127 __ push(eax); |
5105 if (is_postfix_) { // Fix up copy of old value with ToNumber(value). | 5128 __ push(Immediate(Smi::FromInt(1))); |
5106 // This is only safe because VisitCountOperation makes this frame slot | 5129 if (is_increment_) { |
5107 // beneath the reference a register, which is spilled at the above call. | 5130 __ CallRuntime(Runtime::kNumberAdd, 2); |
5108 // We cannot safely write to constants or copies below the water line. | 5131 } else { |
5109 frame->StoreToElementAt(target_size_ + 1); | 5132 __ CallRuntime(Runtime::kNumberSub, 2); |
5110 } | 5133 } |
5111 frame->Push(Smi::FromInt(1)); | 5134 if (!dst_.is(eax)) __ mov(dst_, eax); |
5112 if (is_increment_) { | |
5113 value = frame->CallRuntime(Runtime::kNumberAdd, 2); | |
5114 } else { | |
5115 value = frame->CallRuntime(Runtime::kNumberSub, 2); | |
5116 } | |
5117 exit_.Jump(&value); | |
5118 } | 5135 } |
5119 | 5136 |
5120 | 5137 |
5121 #undef __ | 5138 // The value in dst was optimistically incremented or decremented. The |
5122 #define __ ACCESS_MASM(masm_) | 5139 // result overflowed or was not smi tagged. Undo the operation and call |
| 5140 // into the runtime to convert the argument to a number. Update the |
| 5141 // original value in old. Call the specialized add or subtract stub. |
| 5142 // The result is left in dst. |
| 5143 class DeferredPostfixCountOperation: public DeferredCode { |
| 5144 public: |
| 5145 DeferredPostfixCountOperation(Register dst, Register old, bool is_increment) |
| 5146 : dst_(dst), old_(old), is_increment_(is_increment) { |
| 5147 set_comment("[ DeferredCountOperation"); |
| 5148 } |
| 5149 |
| 5150 virtual void Generate(); |
| 5151 |
| 5152 private: |
| 5153 Register dst_; |
| 5154 Register old_; |
| 5155 bool is_increment_; |
| 5156 }; |
| 5157 |
| 5158 |
| 5159 void DeferredPostfixCountOperation::Generate() { |
| 5160 // Undo the optimistic smi operation. |
| 5161 if (is_increment_) { |
| 5162 __ sub(Operand(dst_), Immediate(Smi::FromInt(1))); |
| 5163 } else { |
| 5164 __ add(Operand(dst_), Immediate(Smi::FromInt(1))); |
| 5165 } |
| 5166 __ push(dst_); |
| 5167 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION); |
| 5168 |
| 5169 // Save the result of ToNumber to use as the old value. |
| 5170 __ push(eax); |
| 5171 |
| 5172 // Call the runtime for the addition or subtraction. |
| 5173 __ push(eax); |
| 5174 __ push(Immediate(Smi::FromInt(1))); |
| 5175 if (is_increment_) { |
| 5176 __ CallRuntime(Runtime::kNumberAdd, 2); |
| 5177 } else { |
| 5178 __ CallRuntime(Runtime::kNumberSub, 2); |
| 5179 } |
| 5180 if (!dst_.is(eax)) __ mov(dst_, eax); |
| 5181 __ pop(old_); |
| 5182 } |
5123 | 5183 |
5124 | 5184 |
5125 void CodeGenerator::VisitCountOperation(CountOperation* node) { | 5185 void CodeGenerator::VisitCountOperation(CountOperation* node) { |
5126 Comment cmnt(masm_, "[ CountOperation"); | 5186 Comment cmnt(masm_, "[ CountOperation"); |
5127 | 5187 |
5128 bool is_postfix = node->is_postfix(); | 5188 bool is_postfix = node->is_postfix(); |
5129 bool is_increment = node->op() == Token::INC; | 5189 bool is_increment = node->op() == Token::INC; |
5130 | 5190 |
5131 Variable* var = node->expression()->AsVariableProxy()->AsVariable(); | 5191 Variable* var = node->expression()->AsVariableProxy()->AsVariable(); |
5132 bool is_const = (var != NULL && var->mode() == Variable::CONST); | 5192 bool is_const = (var != NULL && var->mode() == Variable::CONST); |
5133 | 5193 |
5134 // Postfix operators need a stack slot under the reference to hold | 5194 // Postfix operations need a stack slot under the reference to hold |
5135 // the old value while the new one is being stored. | 5195 // the old value while the new value is being stored. This is so that |
5136 if (is_postfix) { | 5196 // in the case that storing the new value requires a call, the old |
5137 frame_->Push(Smi::FromInt(0)); | 5197 // value will be in the frame to be spilled. |
5138 } | 5198 if (is_postfix) frame_->Push(Smi::FromInt(0)); |
5139 | 5199 |
5140 { Reference target(this, node->expression()); | 5200 { Reference target(this, node->expression()); |
5141 if (target.is_illegal()) { | 5201 if (target.is_illegal()) { |
5142 // Spoof the virtual frame to have the expected height (one higher | 5202 // Spoof the virtual frame to have the expected height (one higher |
5143 // than on entry). | 5203 // than on entry). |
5144 if (!is_postfix) { | 5204 if (!is_postfix) frame_->Push(Smi::FromInt(0)); |
5145 frame_->Push(Smi::FromInt(0)); | |
5146 } | |
5147 return; | 5205 return; |
5148 } | 5206 } |
5149 target.TakeValue(NOT_INSIDE_TYPEOF); | 5207 target.TakeValue(NOT_INSIDE_TYPEOF); |
5150 | 5208 |
5151 DeferredCountOperation* deferred = | 5209 Result new_value = frame_->Pop(); |
5152 new DeferredCountOperation(is_postfix, is_increment, target.size()); | 5210 new_value.ToRegister(); |
5153 | 5211 |
5154 Result value = frame_->Pop(); | 5212 Result old_value; // Only allocated in the postfix case. |
5155 value.ToRegister(); | 5213 if (is_postfix) { |
| 5214 // Allocate a temporary to preserve the old value. |
| 5215 old_value = allocator_->Allocate(); |
| 5216 ASSERT(old_value.is_valid()); |
| 5217 __ mov(old_value.reg(), new_value.reg()); |
| 5218 } |
| 5219 // Ensure the new value is writable. |
| 5220 frame_->Spill(new_value.reg()); |
5156 | 5221 |
5157 // Postfix: Store the old value as the result. | 5222 // In order to combine the overflow and the smi tag check, we need |
5158 if (is_postfix) { | 5223 // to be able to allocate a byte register. We attempt to do so |
5159 // Explicitly back the slot for the old value with a new register. | 5224 // without spilling. If we fail, we will generate separate overflow |
5160 // This improves performance in some cases. | 5225 // and smi tag checks. |
5161 Result old_value = allocator_->Allocate(); | |
5162 ASSERT(old_value.is_valid()); | |
5163 __ mov(old_value.reg(), value.reg()); | |
5164 // SetElement must not create a constant element or a copy in this slot, | |
5165 // since we will write to it, below the waterline, in deferred code. | |
5166 frame_->SetElementAt(target.size(), &old_value); | |
5167 } | |
5168 | |
5169 // Perform optimistic increment/decrement. Ensure the value is | |
5170 // writable. | |
5171 frame_->Spill(value.reg()); | |
5172 ASSERT(allocator_->count(value.reg()) == 1); | |
5173 | |
5174 // In order to combine the overflow and the smi check, we need to | |
5175 // be able to allocate a byte register. We attempt to do so | |
5176 // without spilling. If we fail, we will generate separate | |
5177 // overflow and smi checks. | |
5178 // | 5226 // |
5179 // We need to allocate and clear the temporary byte register | 5227 // We allocate and clear the temporary byte register before |
5180 // before performing the count operation since clearing the | 5228 // performing the count operation since clearing the register using |
5181 // register using xor will clear the overflow flag. | 5229 // xor will clear the overflow flag. |
5182 Result tmp = allocator_->AllocateByteRegisterWithoutSpilling(); | 5230 Result tmp = allocator_->AllocateByteRegisterWithoutSpilling(); |
5183 if (tmp.is_valid()) { | 5231 if (tmp.is_valid()) { |
5184 __ Set(tmp.reg(), Immediate(0)); | 5232 __ Set(tmp.reg(), Immediate(0)); |
5185 } | 5233 } |
5186 | 5234 |
5187 if (is_increment) { | 5235 DeferredCode* deferred = NULL; |
5188 __ add(Operand(value.reg()), Immediate(Smi::FromInt(1))); | 5236 if (is_postfix) { |
| 5237 deferred = new DeferredPostfixCountOperation(new_value.reg(), |
| 5238 old_value.reg(), |
| 5239 is_increment); |
5189 } else { | 5240 } else { |
5190 __ sub(Operand(value.reg()), Immediate(Smi::FromInt(1))); | 5241 deferred = new DeferredPrefixCountOperation(new_value.reg(), |
| 5242 is_increment); |
5191 } | 5243 } |
5192 | 5244 |
5193 // If the count operation didn't overflow and the result is a | 5245 if (is_increment) { |
5194 // valid smi, we're done. Otherwise, we jump to the deferred | 5246 __ add(Operand(new_value.reg()), Immediate(Smi::FromInt(1))); |
5195 // slow-case code. | 5247 } else { |
5196 // | 5248 __ sub(Operand(new_value.reg()), Immediate(Smi::FromInt(1))); |
5197 // We combine the overflow and the smi check if we could | 5249 } |
5198 // successfully allocate a temporary byte register. | 5250 |
| 5251 // If the count operation didn't overflow and the result is a valid |
| 5252 // smi, we're done. Otherwise, we jump to the deferred slow-case |
| 5253 // code. |
5199 if (tmp.is_valid()) { | 5254 if (tmp.is_valid()) { |
| 5255 // We combine the overflow and the smi tag check if we could |
| 5256 // successfully allocate a temporary byte register. |
5200 __ setcc(overflow, tmp.reg()); | 5257 __ setcc(overflow, tmp.reg()); |
5201 __ or_(Operand(tmp.reg()), value.reg()); | 5258 __ or_(Operand(tmp.reg()), new_value.reg()); |
5202 __ test(tmp.reg(), Immediate(kSmiTagMask)); | 5259 __ test(tmp.reg(), Immediate(kSmiTagMask)); |
5203 tmp.Unuse(); | 5260 tmp.Unuse(); |
5204 deferred->enter()->Branch(not_zero, &value, not_taken); | 5261 deferred->Branch(not_zero); |
5205 } else { // Otherwise we test separately for overflow and smi check. | 5262 } else { |
5206 deferred->SetEntryFrame(&value); | 5263 // Otherwise we test separately for overflow and smi tag. |
5207 deferred->enter()->Branch(overflow, &value, not_taken); | 5264 deferred->Branch(overflow); |
5208 __ test(value.reg(), Immediate(kSmiTagMask)); | 5265 __ test(new_value.reg(), Immediate(kSmiTagMask)); |
5209 deferred->enter()->Branch(not_zero, &value, not_taken); | 5266 deferred->Branch(not_zero); |
5210 } | 5267 } |
| 5268 deferred->BindExit(); |
5211 | 5269 |
5212 // Store the new value in the target if not const. | 5270 // Postfix: store the old value in the allocated slot under the |
5213 deferred->BindExit(&value); | 5271 // reference. |
5214 frame_->Push(&value); | 5272 if (is_postfix) frame_->SetElementAt(target.size(), &old_value); |
5215 if (!is_const) { | 5273 |
5216 target.SetValue(NOT_CONST_INIT); | 5274 frame_->Push(&new_value); |
5217 } | 5275 // Non-constant: update the reference. |
| 5276 if (!is_const) target.SetValue(NOT_CONST_INIT); |
5218 } | 5277 } |
5219 | 5278 |
5220 // Postfix: Discard the new value and use the old. | 5279 // Postfix: drop the new value and use the old. |
5221 if (is_postfix) { | 5280 if (is_postfix) frame_->Drop(); |
5222 frame_->Drop(); | |
5223 } | |
5224 } | 5281 } |
5225 | 5282 |
5226 | 5283 |
5227 void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { | 5284 void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { |
5228 // Note that due to an optimization in comparison operations (typeof | 5285 // Note that due to an optimization in comparison operations (typeof |
5229 // compared to a string literal), we can evaluate a binary expression such | 5286 // compared to a string literal), we can evaluate a binary expression such |
5230 // as AND or OR and not leave a value on the frame or in the cc register. | 5287 // as AND or OR and not leave a value on the frame or in the cc register. |
5231 Comment cmnt(masm_, "[ BinaryOperation"); | 5288 Comment cmnt(masm_, "[ BinaryOperation"); |
5232 Token::Value op = node->op(); | 5289 Token::Value op = node->op(); |
5233 | 5290 |
(...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5564 bool CodeGenerator::HasValidEntryRegisters() { | 5621 bool CodeGenerator::HasValidEntryRegisters() { |
5565 return (allocator()->count(eax) == (frame()->is_used(eax) ? 1 : 0)) | 5622 return (allocator()->count(eax) == (frame()->is_used(eax) ? 1 : 0)) |
5566 && (allocator()->count(ebx) == (frame()->is_used(ebx) ? 1 : 0)) | 5623 && (allocator()->count(ebx) == (frame()->is_used(ebx) ? 1 : 0)) |
5567 && (allocator()->count(ecx) == (frame()->is_used(ecx) ? 1 : 0)) | 5624 && (allocator()->count(ecx) == (frame()->is_used(ecx) ? 1 : 0)) |
5568 && (allocator()->count(edx) == (frame()->is_used(edx) ? 1 : 0)) | 5625 && (allocator()->count(edx) == (frame()->is_used(edx) ? 1 : 0)) |
5569 && (allocator()->count(edi) == (frame()->is_used(edi) ? 1 : 0)); | 5626 && (allocator()->count(edi) == (frame()->is_used(edi) ? 1 : 0)); |
5570 } | 5627 } |
5571 #endif | 5628 #endif |
5572 | 5629 |
5573 | 5630 |
| 5631 // Emit a LoadIC call to get the value from receiver and leave it in |
| 5632 // dst. The receiver register is restored after the call. |
5574 class DeferredReferenceGetNamedValue: public DeferredCode { | 5633 class DeferredReferenceGetNamedValue: public DeferredCode { |
5575 public: | 5634 public: |
5576 explicit DeferredReferenceGetNamedValue(Handle<String> name) : name_(name) { | 5635 DeferredReferenceGetNamedValue(Register dst, |
| 5636 Register receiver, |
| 5637 Handle<String> name) |
| 5638 : dst_(dst), receiver_(receiver), name_(name) { |
5577 set_comment("[ DeferredReferenceGetNamedValue"); | 5639 set_comment("[ DeferredReferenceGetNamedValue"); |
5578 } | 5640 } |
5579 | 5641 |
5580 virtual void Generate(); | 5642 virtual void Generate(); |
5581 | 5643 |
5582 Label* patch_site() { return &patch_site_; } | 5644 Label* patch_site() { return &patch_site_; } |
5583 | 5645 |
5584 private: | 5646 private: |
5585 Label patch_site_; | 5647 Label patch_site_; |
| 5648 Register dst_; |
| 5649 Register receiver_; |
5586 Handle<String> name_; | 5650 Handle<String> name_; |
5587 }; | 5651 }; |
5588 | 5652 |
5589 | 5653 |
| 5654 void DeferredReferenceGetNamedValue::Generate() { |
| 5655 __ push(receiver_); |
| 5656 __ Set(ecx, Immediate(name_)); |
| 5657 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); |
| 5658 __ call(ic, RelocInfo::CODE_TARGET); |
| 5659 // The call must be followed by a test eax instruction to indicate |
| 5660 // that the inobject property case was inlined. |
| 5661 // |
| 5662 // Store the delta to the map check instruction here in the test |
| 5663 // instruction. Use masm_-> instead of the __ macro since the |
| 5664 // latter can't return a value. |
| 5665 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site()); |
| 5666 // Here we use masm_-> instead of the __ macro because this is the |
| 5667 // instruction that gets patched and coverage code gets in the way. |
| 5668 masm_->test(eax, Immediate(-delta_to_patch_site)); |
| 5669 __ IncrementCounter(&Counters::named_load_inline_miss, 1); |
| 5670 |
| 5671 if (!dst_.is(eax)) __ mov(dst_, eax); |
| 5672 __ pop(receiver_); |
| 5673 } |
| 5674 |
| 5675 |
5590 class DeferredReferenceGetKeyedValue: public DeferredCode { | 5676 class DeferredReferenceGetKeyedValue: public DeferredCode { |
5591 public: | 5677 public: |
5592 explicit DeferredReferenceGetKeyedValue(bool is_global) | 5678 explicit DeferredReferenceGetKeyedValue(Register dst, |
5593 : is_global_(is_global) { | 5679 Register receiver, |
| 5680 Register key, |
| 5681 bool is_global) |
| 5682 : dst_(dst), receiver_(receiver), key_(key), is_global_(is_global) { |
5594 set_comment("[ DeferredReferenceGetKeyedValue"); | 5683 set_comment("[ DeferredReferenceGetKeyedValue"); |
5595 } | 5684 } |
5596 | 5685 |
5597 virtual void Generate(); | 5686 virtual void Generate(); |
5598 | 5687 |
5599 Label* patch_site() { return &patch_site_; } | 5688 Label* patch_site() { return &patch_site_; } |
5600 | 5689 |
5601 private: | 5690 private: |
5602 Label patch_site_; | 5691 Label patch_site_; |
| 5692 Register dst_; |
| 5693 Register receiver_; |
| 5694 Register key_; |
5603 bool is_global_; | 5695 bool is_global_; |
5604 }; | 5696 }; |
5605 | 5697 |
5606 | 5698 |
5607 #undef __ | |
5608 #define __ ACCESS_MASM(cgen()->masm()) | |
5609 | |
5610 | |
5611 void DeferredReferenceGetNamedValue::Generate() { | |
5612 Result receiver; | |
5613 enter()->Bind(&receiver); | |
5614 | |
5615 cgen()->frame()->Push(&receiver); | |
5616 cgen()->frame()->Push(name_); | |
5617 Result answer = cgen()->frame()->CallLoadIC(RelocInfo::CODE_TARGET); | |
5618 // The call must be followed by a test eax instruction to indicate | |
5619 // that the inobject property case was inlined. | |
5620 ASSERT(answer.is_register() && answer.reg().is(eax)); | |
5621 // Store the delta to the map check instruction here in the test | |
5622 // instruction. Use cgen()->masm()-> instead of the __ macro since | |
5623 // the latter can't return a value. | |
5624 int delta_to_patch_site = | |
5625 cgen()->masm()->SizeOfCodeGeneratedSince(patch_site()); | |
5626 // Here we use cgen()->masm()-> instead of the __ macro because this | |
5627 // is the instruction that gets patched and coverage code gets in the | |
5628 // way. | |
5629 cgen()->masm()->test(answer.reg(), Immediate(-delta_to_patch_site)); | |
5630 __ IncrementCounter(&Counters::named_load_inline_miss, 1); | |
5631 receiver = cgen()->frame()->Pop(); | |
5632 exit_.Jump(&receiver, &answer); | |
5633 } | |
5634 | |
5635 | |
5636 void DeferredReferenceGetKeyedValue::Generate() { | 5699 void DeferredReferenceGetKeyedValue::Generate() { |
5637 Result receiver; | 5700 __ push(receiver_); // First IC argument. |
5638 Result key; | 5701 __ push(key_); // Second IC argument. |
5639 enter()->Bind(&receiver, &key); | |
5640 cgen()->frame()->Push(&receiver); // First IC argument. | |
5641 cgen()->frame()->Push(&key); // Second IC argument. | |
5642 | 5702 |
5643 // Calculate the delta from the IC call instruction to the map check | 5703 // Calculate the delta from the IC call instruction to the map check |
5644 // cmp instruction in the inlined version. This delta is stored in | 5704 // cmp instruction in the inlined version. This delta is stored in |
5645 // a test(eax, delta) instruction after the call so that we can find | 5705 // a test(eax, delta) instruction after the call so that we can find |
5646 // it in the IC initialization code and patch the cmp instruction. | 5706 // it in the IC initialization code and patch the cmp instruction. |
5647 // This means that we cannot allow test instructions after calls to | 5707 // This means that we cannot allow test instructions after calls to |
5648 // KeyedLoadIC stubs in other places. | 5708 // KeyedLoadIC stubs in other places. |
| 5709 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); |
5649 RelocInfo::Mode mode = is_global_ | 5710 RelocInfo::Mode mode = is_global_ |
5650 ? RelocInfo::CODE_TARGET_CONTEXT | 5711 ? RelocInfo::CODE_TARGET_CONTEXT |
5651 : RelocInfo::CODE_TARGET; | 5712 : RelocInfo::CODE_TARGET; |
5652 Result value = cgen()->frame()->CallKeyedLoadIC(mode); | 5713 __ call(ic, mode); |
5653 // The result needs to be specifically the eax register because the | 5714 // The delta from the start of the map-compare instruction to the |
5654 // offset to the patch site will be expected in a test eax | 5715 // test instruction. We use masm_-> directly here instead of the __ |
5655 // instruction. | 5716 // macro because the macro sometimes uses macro expansion to turn |
5656 ASSERT(value.is_register() && value.reg().is(eax)); | 5717 // into something that can't return a value. This is encountered |
5657 // The delta from the start of the map-compare instruction to the test | 5718 // when doing generated code coverage tests. |
5658 // instruction. We use cgen()->masm() directly here instead of the __ | 5719 int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site()); |
5659 // macro because the macro sometimes uses macro expansion to turn into | 5720 // Here we use masm_-> instead of the __ macro because this is the |
5660 // something that can't return a value. This is encountered when | 5721 // instruction that gets patched and coverage code gets in the way. |
5661 // doing generated code coverage tests. | 5722 masm_->test(eax, Immediate(-delta_to_patch_site)); |
5662 int delta_to_patch_site = | |
5663 cgen()->masm()->SizeOfCodeGeneratedSince(patch_site()); | |
5664 // Here we use cgen()->masm()-> instead of the __ macro because this | |
5665 // is the instruction that gets patched and coverage code gets in the | |
5666 // way. | |
5667 cgen()->masm()->test(value.reg(), Immediate(-delta_to_patch_site)); | |
5668 __ IncrementCounter(&Counters::keyed_load_inline_miss, 1); | 5723 __ IncrementCounter(&Counters::keyed_load_inline_miss, 1); |
5669 | 5724 |
5670 // The receiver and key were spilled by the call, so their state as | 5725 if (!dst_.is(eax)) __ mov(dst_, eax); |
5671 // constants or copies has been changed. Thus, they need to be | 5726 __ pop(key_); |
5672 // "mergable" in the block at the exit label and are therefore | 5727 __ pop(receiver_); |
5673 // passed as return results here. | |
5674 key = cgen()->frame()->Pop(); | |
5675 receiver = cgen()->frame()->Pop(); | |
5676 exit_.Jump(&receiver, &key, &value); | |
5677 } | 5728 } |
5678 | 5729 |
5679 | 5730 |
5680 #undef __ | 5731 #undef __ |
5681 #define __ ACCESS_MASM(masm) | 5732 #define __ ACCESS_MASM(masm) |
5682 | 5733 |
5683 Handle<String> Reference::GetName() { | 5734 Handle<String> Reference::GetName() { |
5684 ASSERT(type_ == NAMED); | 5735 ASSERT(type_ == NAMED); |
5685 Property* property = expression_->AsProperty(); | 5736 Property* property = expression_->AsProperty(); |
5686 if (property == NULL) { | 5737 if (property == NULL) { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5737 : RelocInfo::CODE_TARGET; | 5788 : RelocInfo::CODE_TARGET; |
5738 Result answer = cgen_->frame()->CallLoadIC(mode); | 5789 Result answer = cgen_->frame()->CallLoadIC(mode); |
5739 // A test eax instruction following the call signals that the | 5790 // A test eax instruction following the call signals that the |
5740 // inobject property case was inlined. Ensure that there is not | 5791 // inobject property case was inlined. Ensure that there is not |
5741 // a test eax instruction here. | 5792 // a test eax instruction here. |
5742 __ nop(); | 5793 __ nop(); |
5743 cgen_->frame()->Push(&answer); | 5794 cgen_->frame()->Push(&answer); |
5744 } else { | 5795 } else { |
5745 // Inline the inobject property case. | 5796 // Inline the inobject property case. |
5746 Comment cmnt(masm, "[ Inlined named property load"); | 5797 Comment cmnt(masm, "[ Inlined named property load"); |
5747 DeferredReferenceGetNamedValue* deferred = | |
5748 new DeferredReferenceGetNamedValue(GetName()); | |
5749 Result receiver = cgen_->frame()->Pop(); | 5798 Result receiver = cgen_->frame()->Pop(); |
5750 receiver.ToRegister(); | 5799 receiver.ToRegister(); |
5751 | 5800 |
5752 // Try to preallocate the value register so that all frames | 5801 Result value = cgen_->allocator()->Allocate(); |
5753 // reaching the deferred code are identical. | 5802 ASSERT(value.is_valid()); |
5754 Result value = cgen_->allocator()->AllocateWithoutSpilling(); | 5803 DeferredReferenceGetNamedValue* deferred = |
5755 if (value.is_valid()) { | 5804 new DeferredReferenceGetNamedValue(value.reg(), |
5756 deferred->SetEntryFrame(&receiver); | 5805 receiver.reg(), |
5757 } | 5806 GetName()); |
5758 | 5807 |
5759 // Check that the receiver is a heap object. | 5808 // Check that the receiver is a heap object. |
5760 __ test(receiver.reg(), Immediate(kSmiTagMask)); | 5809 __ test(receiver.reg(), Immediate(kSmiTagMask)); |
5761 deferred->enter()->Branch(zero, &receiver, not_taken); | 5810 deferred->Branch(zero); |
5762 | |
5763 // Do not allocate the value register after binding the patch | |
5764 // site label. The distance from the patch site to the offset | |
5765 // must be constant. | |
5766 if (!value.is_valid()) { | |
5767 value = cgen_->allocator()->Allocate(); | |
5768 ASSERT(value.is_valid()); | |
5769 } | |
5770 | 5811 |
5771 __ bind(deferred->patch_site()); | 5812 __ bind(deferred->patch_site()); |
5772 // This is the map check instruction that will be patched (so we can't | 5813 // This is the map check instruction that will be patched (so we can't |
5773 // use the double underscore macro that may insert instructions). | 5814 // use the double underscore macro that may insert instructions). |
5774 // Initially use an invalid map to force a failure. | 5815 // Initially use an invalid map to force a failure. |
5775 masm->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset), | 5816 masm->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset), |
5776 Immediate(Factory::null_value())); | 5817 Immediate(Factory::null_value())); |
5777 // This branch is always a forwards branch so it's always a fixed | 5818 // This branch is always a forwards branch so it's always a fixed |
5778 // size which allows the assert below to succeed and patching to work. | 5819 // size which allows the assert below to succeed and patching to work. |
5779 deferred->enter()->Branch(not_equal, &receiver, not_taken); | 5820 deferred->Branch(not_equal); |
5780 | 5821 |
5781 // The delta from the patch label to the load offset must be | 5822 // The delta from the patch label to the load offset must be |
5782 // statically known. | 5823 // statically known. |
5783 ASSERT(masm->SizeOfCodeGeneratedSince(deferred->patch_site()) == | 5824 ASSERT(masm->SizeOfCodeGeneratedSince(deferred->patch_site()) == |
5784 LoadIC::kOffsetToLoadInstruction); | 5825 LoadIC::kOffsetToLoadInstruction); |
5785 // The initial (invalid) offset has to be large enough to force | 5826 // The initial (invalid) offset has to be large enough to force |
5786 // a 32-bit instruction encoding to allow patching with an | 5827 // a 32-bit instruction encoding to allow patching with an |
5787 // arbitrary offset. Use kMaxInt (minus kHeapObjectTag). | 5828 // arbitrary offset. Use kMaxInt (minus kHeapObjectTag). |
5788 int offset = kMaxInt; | 5829 int offset = kMaxInt; |
5789 masm->mov(value.reg(), FieldOperand(receiver.reg(), offset)); | 5830 masm->mov(value.reg(), FieldOperand(receiver.reg(), offset)); |
5790 | 5831 |
5791 __ IncrementCounter(&Counters::named_load_inline, 1); | 5832 __ IncrementCounter(&Counters::named_load_inline, 1); |
5792 deferred->BindExit(&receiver, &value); | 5833 deferred->BindExit(); |
5793 cgen_->frame()->Push(&receiver); | 5834 cgen_->frame()->Push(&receiver); |
5794 cgen_->frame()->Push(&value); | 5835 cgen_->frame()->Push(&value); |
5795 } | 5836 } |
5796 break; | 5837 break; |
5797 } | 5838 } |
5798 | 5839 |
5799 case KEYED: { | 5840 case KEYED: { |
5800 // TODO(1241834): Make sure that this it is safe to ignore the | 5841 // TODO(1241834): Make sure that this it is safe to ignore the |
5801 // distinction between expressions in a typeof and not in a typeof. | 5842 // distinction between expressions in a typeof and not in a typeof. |
5802 Comment cmnt(masm, "[ Load from keyed Property"); | 5843 Comment cmnt(masm, "[ Load from keyed Property"); |
5803 Variable* var = expression_->AsVariableProxy()->AsVariable(); | 5844 Variable* var = expression_->AsVariableProxy()->AsVariable(); |
5804 bool is_global = var != NULL; | 5845 bool is_global = var != NULL; |
5805 ASSERT(!is_global || var->is_global()); | 5846 ASSERT(!is_global || var->is_global()); |
5806 // Inline array load code if inside of a loop. We do not know | 5847 // Inline array load code if inside of a loop. We do not know |
5807 // the receiver map yet, so we initially generate the code with | 5848 // the receiver map yet, so we initially generate the code with |
5808 // a check against an invalid map. In the inline cache code, we | 5849 // a check against an invalid map. In the inline cache code, we |
5809 // patch the map check if appropriate. | 5850 // patch the map check if appropriate. |
5810 if (cgen_->loop_nesting() > 0) { | 5851 if (cgen_->loop_nesting() > 0) { |
5811 Comment cmnt(masm, "[ Inlined array index load"); | 5852 Comment cmnt(masm, "[ Inlined array index load"); |
5812 DeferredReferenceGetKeyedValue* deferred = | |
5813 new DeferredReferenceGetKeyedValue(is_global); | |
5814 | 5853 |
5815 Result key = cgen_->frame()->Pop(); | 5854 Result key = cgen_->frame()->Pop(); |
5816 Result receiver = cgen_->frame()->Pop(); | 5855 Result receiver = cgen_->frame()->Pop(); |
5817 key.ToRegister(); | 5856 key.ToRegister(); |
5818 receiver.ToRegister(); | 5857 receiver.ToRegister(); |
5819 | 5858 |
5820 // Try to preallocate the elements and index scratch registers | 5859 // Use a fresh temporary to load the elements without destroying |
5821 // so that all frames reaching the deferred code are identical. | 5860 // the receiver which is needed for the deferred slow case. |
5822 Result elements = cgen_->allocator()->AllocateWithoutSpilling(); | 5861 Result elements = cgen_->allocator()->Allocate(); |
5823 Result index = cgen_->allocator()->AllocateWithoutSpilling(); | 5862 ASSERT(elements.is_valid()); |
5824 if (elements.is_valid() && index.is_valid()) { | 5863 |
5825 deferred->SetEntryFrame(&receiver, &key); | 5864 // Use a fresh temporary for the index and later the loaded |
5826 } | 5865 // value. |
| 5866 Result index = cgen_->allocator()->Allocate(); |
| 5867 ASSERT(index.is_valid()); |
| 5868 |
| 5869 DeferredReferenceGetKeyedValue* deferred = |
| 5870 new DeferredReferenceGetKeyedValue(index.reg(), |
| 5871 receiver.reg(), |
| 5872 key.reg(), |
| 5873 is_global); |
5827 | 5874 |
5828 // Check that the receiver is not a smi (only needed if this | 5875 // Check that the receiver is not a smi (only needed if this |
5829 // is not a load from the global context) and that it has the | 5876 // is not a load from the global context) and that it has the |
5830 // expected map. | 5877 // expected map. |
5831 if (!is_global) { | 5878 if (!is_global) { |
5832 __ test(receiver.reg(), Immediate(kSmiTagMask)); | 5879 __ test(receiver.reg(), Immediate(kSmiTagMask)); |
5833 deferred->enter()->Branch(zero, &receiver, &key, not_taken); | 5880 deferred->Branch(zero); |
5834 } | 5881 } |
5835 | 5882 |
5836 // Initially, use an invalid map. The map is patched in the IC | 5883 // Initially, use an invalid map. The map is patched in the IC |
5837 // initialization code. | 5884 // initialization code. |
5838 __ bind(deferred->patch_site()); | 5885 __ bind(deferred->patch_site()); |
5839 // Use masm-> here instead of the double underscore macro since extra | 5886 // Use masm-> here instead of the double underscore macro since extra |
5840 // coverage code can interfere with the patching. | 5887 // coverage code can interfere with the patching. |
5841 masm->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset), | 5888 masm->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset), |
5842 Immediate(Factory::null_value())); | 5889 Immediate(Factory::null_value())); |
5843 deferred->enter()->Branch(not_equal, &receiver, &key, not_taken); | 5890 deferred->Branch(not_equal); |
5844 | 5891 |
5845 // Check that the key is a smi. | 5892 // Check that the key is a smi. |
5846 __ test(key.reg(), Immediate(kSmiTagMask)); | 5893 __ test(key.reg(), Immediate(kSmiTagMask)); |
5847 deferred->enter()->Branch(not_zero, &receiver, &key, not_taken); | 5894 deferred->Branch(not_zero); |
5848 | 5895 |
5849 // Get the elements array from the receiver and check that it | 5896 // Get the elements array from the receiver and check that it |
5850 // is not a dictionary. | 5897 // is not a dictionary. |
5851 if (!elements.is_valid()) { | |
5852 elements = cgen_->allocator()->Allocate(); | |
5853 ASSERT(elements.is_valid()); | |
5854 } | |
5855 __ mov(elements.reg(), | 5898 __ mov(elements.reg(), |
5856 FieldOperand(receiver.reg(), JSObject::kElementsOffset)); | 5899 FieldOperand(receiver.reg(), JSObject::kElementsOffset)); |
5857 __ cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset), | 5900 __ cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset), |
5858 Immediate(Factory::hash_table_map())); | 5901 Immediate(Factory::hash_table_map())); |
5859 deferred->enter()->Branch(equal, &receiver, &key, not_taken); | 5902 deferred->Branch(equal); |
5860 | 5903 |
5861 // Shift the key to get the actual index value and check that | 5904 // Shift the key to get the actual index value and check that |
5862 // it is within bounds. | 5905 // it is within bounds. |
5863 if (!index.is_valid()) { | |
5864 index = cgen_->allocator()->Allocate(); | |
5865 ASSERT(index.is_valid()); | |
5866 } | |
5867 __ mov(index.reg(), key.reg()); | 5906 __ mov(index.reg(), key.reg()); |
5868 __ sar(index.reg(), kSmiTagSize); | 5907 __ sar(index.reg(), kSmiTagSize); |
5869 __ cmp(index.reg(), | 5908 __ cmp(index.reg(), |
5870 FieldOperand(elements.reg(), Array::kLengthOffset)); | 5909 FieldOperand(elements.reg(), Array::kLengthOffset)); |
5871 deferred->enter()->Branch(above_equal, &receiver, &key, not_taken); | 5910 deferred->Branch(above_equal); |
5872 | 5911 |
5873 // Load and check that the result is not the hole. We could | 5912 // Load and check that the result is not the hole. We could |
5874 // reuse the index or elements register for the value. | 5913 // reuse the index or elements register for the value. |
5875 // | 5914 // |
5876 // TODO(206): Consider whether it makes sense to try some | 5915 // TODO(206): Consider whether it makes sense to try some |
5877 // heuristic about which register to reuse. For example, if | 5916 // heuristic about which register to reuse. For example, if |
5878 // one is eax, the we can reuse that one because the value | 5917 // one is eax, the we can reuse that one because the value |
5879 // coming from the deferred code will be in eax. | 5918 // coming from the deferred code will be in eax. |
5880 Result value = index; | 5919 Result value = index; |
5881 __ mov(value.reg(), Operand(elements.reg(), | 5920 __ mov(value.reg(), Operand(elements.reg(), |
5882 index.reg(), | 5921 index.reg(), |
5883 times_4, | 5922 times_4, |
5884 Array::kHeaderSize - kHeapObjectTag)); | 5923 Array::kHeaderSize - kHeapObjectTag)); |
5885 elements.Unuse(); | 5924 elements.Unuse(); |
5886 index.Unuse(); | 5925 index.Unuse(); |
5887 __ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value())); | 5926 __ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value())); |
5888 deferred->enter()->Branch(equal, &receiver, &key, not_taken); | 5927 deferred->Branch(equal); |
5889 __ IncrementCounter(&Counters::keyed_load_inline, 1); | 5928 __ IncrementCounter(&Counters::keyed_load_inline, 1); |
5890 | 5929 |
| 5930 deferred->BindExit(); |
5891 // Restore the receiver and key to the frame and push the | 5931 // Restore the receiver and key to the frame and push the |
5892 // result on top of it. | 5932 // result on top of it. |
5893 deferred->BindExit(&receiver, &key, &value); | |
5894 cgen_->frame()->Push(&receiver); | 5933 cgen_->frame()->Push(&receiver); |
5895 cgen_->frame()->Push(&key); | 5934 cgen_->frame()->Push(&key); |
5896 cgen_->frame()->Push(&value); | 5935 cgen_->frame()->Push(&value); |
5897 | 5936 |
5898 } else { | 5937 } else { |
5899 Comment cmnt(masm, "[ Load from keyed Property"); | 5938 Comment cmnt(masm, "[ Load from keyed Property"); |
5900 RelocInfo::Mode mode = is_global | 5939 RelocInfo::Mode mode = is_global |
5901 ? RelocInfo::CODE_TARGET_CONTEXT | 5940 ? RelocInfo::CODE_TARGET_CONTEXT |
5902 : RelocInfo::CODE_TARGET; | 5941 : RelocInfo::CODE_TARGET; |
5903 Result answer = cgen_->frame()->CallKeyedLoadIC(mode); | 5942 Result answer = cgen_->frame()->CallKeyedLoadIC(mode); |
(...skipping 1368 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7272 | 7311 |
7273 // Slow-case: Go through the JavaScript implementation. | 7312 // Slow-case: Go through the JavaScript implementation. |
7274 __ bind(&slow); | 7313 __ bind(&slow); |
7275 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); | 7314 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); |
7276 } | 7315 } |
7277 | 7316 |
7278 | 7317 |
7279 #undef __ | 7318 #undef __ |
7280 | 7319 |
7281 } } // namespace v8::internal | 7320 } } // namespace v8::internal |
OLD | NEW |