Chromium Code Reviews| 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 761 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 772 case Token::SHL: return "GenericBinaryOpStub_SHL"; | 772 case Token::SHL: return "GenericBinaryOpStub_SHL"; |
| 773 case Token::SHR: return "GenericBinaryOpStub_SHR"; | 773 case Token::SHR: return "GenericBinaryOpStub_SHR"; |
| 774 default: return "GenericBinaryOpStub"; | 774 default: return "GenericBinaryOpStub"; |
| 775 } | 775 } |
| 776 } | 776 } |
| 777 | 777 |
| 778 | 778 |
| 779 // Call the specialized stub for a binary operation. | 779 // Call the specialized stub for a binary operation. |
| 780 class DeferredInlineBinaryOperation: public DeferredCode { | 780 class DeferredInlineBinaryOperation: public DeferredCode { |
| 781 public: | 781 public: |
| 782 DeferredInlineBinaryOperation(Token::Value op, | 782 DeferredInlineBinaryOperation(Token::Value op, OverwriteMode mode) |
| 783 OverwriteMode mode) | |
| 784 : op_(op), mode_(mode) { | 783 : op_(op), mode_(mode) { |
| 785 set_comment("[ DeferredInlineBinaryOperation"); | 784 set_comment("[ DeferredInlineBinaryOperation"); |
| 786 } | 785 } |
| 787 | 786 |
| 788 virtual void Generate(); | 787 virtual void Generate(); |
| 789 | 788 |
| 790 private: | 789 private: |
| 791 Token::Value op_; | 790 Token::Value op_; |
| 792 OverwriteMode mode_; | 791 OverwriteMode mode_; |
| 793 }; | 792 }; |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 994 } | 993 } |
| 995 frame_->Push(Handle<Object>(answer_object)); | 994 frame_->Push(Handle<Object>(answer_object)); |
| 996 return true; | 995 return true; |
| 997 } | 996 } |
| 998 | 997 |
| 999 | 998 |
| 1000 void CodeGenerator::LikelySmiBinaryOperation(Token::Value op, | 999 void CodeGenerator::LikelySmiBinaryOperation(Token::Value op, |
| 1001 Result* left, | 1000 Result* left, |
| 1002 Result* right, | 1001 Result* right, |
| 1003 OverwriteMode overwrite_mode) { | 1002 OverwriteMode overwrite_mode) { |
| 1004 // Implements a binary operation using a deferred code object and | 1003 // Implements a binary operation using a deferred code object and some |
| 1005 // some inline code to operate on smis quickly. | 1004 // inline code to operate on smis quickly. |
| 1006 DeferredInlineBinaryOperation* deferred = | 1005 DeferredInlineBinaryOperation* deferred = |
| 1007 new DeferredInlineBinaryOperation(op, overwrite_mode); | 1006 new DeferredInlineBinaryOperation(op, overwrite_mode); |
| 1008 | 1007 |
| 1009 // Special handling of div and mod because they use fixed registers. | 1008 // Special handling of div and mod because they use fixed registers. |
| 1010 if (op == Token::DIV || op == Token::MOD) { | 1009 if (op == Token::DIV || op == Token::MOD) { |
| 1011 // We need eax as the quotient register, edx as the remainder | 1010 // We need eax as the quotient register, edx as the remainder |
| 1012 // register, neither left nor right in eax or edx, and left copied | 1011 // register, neither left nor right in eax or edx, and left copied |
| 1013 // to eax. | 1012 // to eax. |
| 1014 Result quotient; | 1013 Result quotient; |
| 1015 Result remainder; | 1014 Result remainder; |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1141 deferred->enter()->Branch(negative, left, right); | 1140 deferred->enter()->Branch(negative, left, right); |
| 1142 left->Unuse(); | 1141 left->Unuse(); |
| 1143 right->Unuse(); | 1142 right->Unuse(); |
| 1144 __ bind(&non_zero_result); | 1143 __ bind(&non_zero_result); |
| 1145 deferred->BindExit(&remainder); | 1144 deferred->BindExit(&remainder); |
| 1146 frame_->Push(&remainder); | 1145 frame_->Push(&remainder); |
| 1147 } | 1146 } |
| 1148 return; | 1147 return; |
| 1149 } | 1148 } |
| 1150 | 1149 |
| 1150 // Special handling of shift operations because they use fixed | |
| 1151 // registers. | |
| 1152 if (op == Token::SHL || op == Token::SHR || op == Token::SAR) { | |
| 1153 // Move left out of ecx if necessary. | |
| 1154 if (left->is_register() && left->reg().is(ecx)) { | |
| 1155 *left = allocator_->Allocate(); | |
| 1156 ASSERT(left->is_valid()); | |
| 1157 __ mov(left->reg(), ecx); | |
| 1158 } | |
| 1159 right->ToRegister(ecx); | |
| 1160 left->ToRegister(); | |
| 1161 ASSERT(left->is_register() && !left->reg().is(ecx)); | |
| 1162 ASSERT(right->is_register() && right->reg().is(ecx)); | |
| 1163 | |
| 1164 // We will modify right, it must be spilled. | |
| 1165 frame_->Spill(ecx); | |
| 1166 | |
| 1167 // Use a fresh answer register to avoid spilling the left operand. | |
| 1168 Result answer = allocator_->Allocate(); | |
| 1169 ASSERT(answer.is_valid()); | |
| 1170 // Check that both operands are smis using the answer register as a | |
| 1171 // temporary. | |
| 1172 __ mov(answer.reg(), left->reg()); | |
| 1173 __ or_(answer.reg(), Operand(ecx)); | |
| 1174 __ test(answer.reg(), Immediate(kSmiTagMask)); | |
| 1175 deferred->enter()->Branch(not_zero, left, right); | |
| 1176 | |
| 1177 // Untag both operands. | |
| 1178 __ mov(answer.reg(), left->reg()); | |
| 1179 __ sar(answer.reg(), kSmiTagSize); | |
| 1180 __ sar(ecx, kSmiTagSize); | |
| 1181 // Perform the operation. | |
| 1182 switch (op) { | |
| 1183 case Token::SAR: | |
| 1184 __ sar(answer.reg()); | |
| 1185 // No checks of result necessary | |
| 1186 break; | |
| 1187 case Token::SHR: { | |
| 1188 JumpTarget result_ok; | |
| 1189 __ shr(answer.reg()); | |
| 1190 // Check that the *unsigned* result fits in a smi. | |
| 1191 // Neither of the two high-order bits can be set: | |
| 1192 // - 0x80000000: high bit would be lost when smi tagging. | |
| 1193 // - 0x40000000: this number would convert to negative when | |
| 1194 // Smi tagging these two cases can only happen with shifts | |
|
William Hesse
2009/06/03 12:12:04
// Smi tagging. These two cases can..
| |
| 1195 // by 0 or 1 when handed a valid smi. | |
| 1196 // If the answer cannot be represented by a smi, restore | |
| 1197 // the left and right arguments, and jump to slow case. | |
| 1198 // The low bit of the left argument may be lost, but only | |
| 1199 // in a case where it is dropped anyway. | |
| 1200 __ test(answer.reg(), Immediate(0xc0000000)); | |
| 1201 result_ok.Branch(zero, &answer); | |
| 1202 ASSERT(kSmiTag == 0); | |
| 1203 __ shl(ecx, kSmiTagSize); | |
| 1204 answer.Unuse(); | |
| 1205 deferred->enter()->Jump(left, right); | |
| 1206 result_ok.Bind(&answer); | |
| 1207 break; | |
| 1208 } | |
| 1209 case Token::SHL: { | |
| 1210 JumpTarget result_ok; | |
| 1211 __ shl(answer.reg()); | |
| 1212 // Check that the *signed* result fits in a smi. | |
| 1213 __ cmp(answer.reg(), 0xc0000000); | |
| 1214 result_ok.Branch(positive, &answer); | |
| 1215 ASSERT(kSmiTag == 0); | |
| 1216 __ shl(ecx, kSmiTagSize); | |
| 1217 answer.Unuse(); | |
| 1218 deferred->enter()->Jump(left, right); | |
| 1219 result_ok.Bind(&answer); | |
| 1220 break; | |
| 1221 } | |
| 1222 default: | |
| 1223 UNREACHABLE(); | |
| 1224 } | |
| 1225 left->Unuse(); | |
| 1226 right->Unuse(); | |
| 1227 // Smi-tag the result in answer. | |
| 1228 ASSERT(kSmiTagSize == 1); // Adjust code if not the case. | |
| 1229 __ lea(answer.reg(), | |
| 1230 Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); | |
| 1231 deferred->BindExit(&answer); | |
| 1232 frame_->Push(&answer); | |
| 1233 return; | |
| 1234 } | |
| 1235 | |
| 1151 // Handle the other binary operations. | 1236 // Handle the other binary operations. |
| 1152 left->ToRegister(); | 1237 left->ToRegister(); |
| 1153 right->ToRegister(); | 1238 right->ToRegister(); |
| 1154 // A newly allocated register answer is used to hold the answer. The | 1239 // A newly allocated register answer is used to hold the answer. The |
| 1155 // registers containing left and right are not modified in most cases, | 1240 // registers containing left and right are not modified so they don't |
| 1156 // so they usually don't need to be spilled in the fast case. | 1241 // need to be spilled in the fast case. |
| 1157 Result answer = allocator_->Allocate(); | 1242 Result answer = allocator_->Allocate(); |
| 1158 | 1243 |
| 1159 ASSERT(answer.is_valid()); | 1244 ASSERT(answer.is_valid()); |
| 1160 // Perform the smi tag check. | 1245 // Perform the smi tag check. |
| 1161 if (left->reg().is(right->reg())) { | 1246 if (left->reg().is(right->reg())) { |
| 1162 __ test(left->reg(), Immediate(kSmiTagMask)); | 1247 __ test(left->reg(), Immediate(kSmiTagMask)); |
| 1163 } else { | 1248 } else { |
| 1164 __ mov(answer.reg(), left->reg()); | 1249 __ mov(answer.reg(), left->reg()); |
| 1165 __ or_(answer.reg(), Operand(right->reg())); | 1250 __ or_(answer.reg(), Operand(right->reg())); |
| 1166 ASSERT(kSmiTag == 0); // Adjust test if not the case. | 1251 ASSERT(kSmiTag == 0); // Adjust test if not the case. |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1222 __ mov(answer.reg(), left->reg()); | 1307 __ mov(answer.reg(), left->reg()); |
| 1223 __ and_(answer.reg(), Operand(right->reg())); | 1308 __ and_(answer.reg(), Operand(right->reg())); |
| 1224 break; | 1309 break; |
| 1225 | 1310 |
| 1226 case Token::BIT_XOR: | 1311 case Token::BIT_XOR: |
| 1227 deferred->enter()->Branch(not_zero, left, right, not_taken); | 1312 deferred->enter()->Branch(not_zero, left, right, not_taken); |
| 1228 __ mov(answer.reg(), left->reg()); | 1313 __ mov(answer.reg(), left->reg()); |
| 1229 __ xor_(answer.reg(), Operand(right->reg())); | 1314 __ xor_(answer.reg(), Operand(right->reg())); |
| 1230 break; | 1315 break; |
| 1231 | 1316 |
| 1232 case Token::SHL: | |
| 1233 case Token::SHR: | |
| 1234 case Token::SAR: | |
| 1235 deferred->enter()->Branch(not_zero, left, right, not_taken); | |
| 1236 __ mov(answer.reg(), left->reg()); | |
| 1237 // Move right into ecx. | |
| 1238 // Left is in two registers already, so even if left or answer is ecx, | |
| 1239 // we can move right to it, and use the other one. | |
| 1240 // Right operand must be in register cl because x86 likes it that way. | |
| 1241 if (right->reg().is(ecx)) { | |
| 1242 // Right is already in the right place. Left may be in the | |
| 1243 // same register, which causes problems. Always use answer | |
| 1244 // instead of left, even if left is not ecx, since this avoids | |
| 1245 // spilling left. | |
| 1246 *left = answer; | |
| 1247 } else if (left->reg().is(ecx)) { | |
| 1248 frame_->Spill(left->reg()); | |
| 1249 __ mov(left->reg(), right->reg()); | |
| 1250 *right = *left; | |
| 1251 *left = answer; // Use copy of left in answer as left. | |
| 1252 } else if (answer.reg().is(ecx)) { | |
| 1253 __ mov(answer.reg(), right->reg()); | |
| 1254 *right = answer; | |
| 1255 } else { | |
| 1256 Result reg_ecx = allocator_->Allocate(ecx); | |
| 1257 ASSERT(reg_ecx.is_valid()); | |
| 1258 __ mov(ecx, right->reg()); | |
| 1259 *right = reg_ecx; | |
| 1260 // Answer and left both contain the left operand. Use answer, so | |
| 1261 // left is not spilled. | |
| 1262 *left = answer; | |
| 1263 } | |
| 1264 ASSERT(left->reg().is_valid()); | |
| 1265 ASSERT(!left->reg().is(ecx)); | |
| 1266 ASSERT(right->reg().is(ecx)); | |
| 1267 answer.Unuse(); // Answer may now be being used for left or right. | |
| 1268 // We will modify left and right, which we do not do in any other | |
| 1269 // binary operation. The exits to slow code need to restore the | |
| 1270 // original values of left and right, or at least values that give | |
| 1271 // the same answer. | |
| 1272 | |
| 1273 // We are modifying left and right. They must be spilled! | |
| 1274 frame_->Spill(left->reg()); | |
| 1275 frame_->Spill(right->reg()); | |
| 1276 | |
| 1277 // Remove tags from operands (but keep sign). | |
| 1278 __ sar(left->reg(), kSmiTagSize); | |
| 1279 __ sar(ecx, kSmiTagSize); | |
| 1280 // Perform the operation. | |
| 1281 switch (op) { | |
| 1282 case Token::SAR: | |
| 1283 __ sar(left->reg()); | |
| 1284 // No checks of result necessary | |
| 1285 break; | |
| 1286 case Token::SHR: { | |
| 1287 __ shr(left->reg()); | |
| 1288 // Check that the *unsigned* result fits in a smi. | |
| 1289 // Neither of the two high-order bits can be set: | |
| 1290 // - 0x80000000: high bit would be lost when smi tagging. | |
| 1291 // - 0x40000000: this number would convert to negative when | |
| 1292 // Smi tagging these two cases can only happen with shifts | |
| 1293 // by 0 or 1 when handed a valid smi. | |
| 1294 // If the answer cannot be represented by a SMI, restore | |
| 1295 // the left and right arguments, and jump to slow case. | |
| 1296 // The low bit of the left argument may be lost, but only | |
| 1297 // in a case where it is dropped anyway. | |
| 1298 JumpTarget result_ok; | |
| 1299 __ test(left->reg(), Immediate(0xc0000000)); | |
| 1300 result_ok.Branch(zero, left, taken); | |
| 1301 __ shl(left->reg()); | |
| 1302 ASSERT(kSmiTag == 0); | |
| 1303 __ shl(left->reg(), kSmiTagSize); | |
| 1304 __ shl(right->reg(), kSmiTagSize); | |
| 1305 deferred->enter()->Jump(left, right); | |
| 1306 result_ok.Bind(left); | |
| 1307 break; | |
| 1308 } | |
| 1309 case Token::SHL: { | |
| 1310 __ shl(left->reg()); | |
| 1311 // Check that the *signed* result fits in a smi. | |
| 1312 JumpTarget result_ok; | |
| 1313 __ cmp(left->reg(), 0xc0000000); | |
| 1314 result_ok.Branch(positive, left, taken); | |
| 1315 | |
| 1316 __ shr(left->reg()); | |
| 1317 ASSERT(kSmiTag == 0); | |
| 1318 __ shl(left->reg(), kSmiTagSize); | |
| 1319 __ shl(right->reg(), kSmiTagSize); | |
| 1320 deferred->enter()->Jump(left, right); | |
| 1321 result_ok.Bind(left); | |
| 1322 break; | |
| 1323 } | |
| 1324 default: | |
| 1325 UNREACHABLE(); | |
| 1326 } | |
| 1327 // Smi-tag the result, in left, and make answer an alias for left-> | |
| 1328 answer = *left; | |
| 1329 answer.ToRegister(); | |
| 1330 ASSERT(kSmiTagSize == times_2); // adjust code if not the case | |
| 1331 __ lea(answer.reg(), | |
| 1332 Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); | |
| 1333 break; | |
| 1334 | |
| 1335 default: | 1317 default: |
| 1336 UNREACHABLE(); | 1318 UNREACHABLE(); |
| 1337 break; | 1319 break; |
| 1338 } | 1320 } |
| 1339 left->Unuse(); | 1321 left->Unuse(); |
| 1340 right->Unuse(); | 1322 right->Unuse(); |
| 1341 deferred->BindExit(&answer); | 1323 deferred->BindExit(&answer); |
| 1342 frame_->Push(&answer); | 1324 frame_->Push(&answer); |
| 1343 } | 1325 } |
| 1344 | 1326 |
| (...skipping 5945 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 7290 | 7272 |
| 7291 // Slow-case: Go through the JavaScript implementation. | 7273 // Slow-case: Go through the JavaScript implementation. |
| 7292 __ bind(&slow); | 7274 __ bind(&slow); |
| 7293 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); | 7275 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); |
| 7294 } | 7276 } |
| 7295 | 7277 |
| 7296 | 7278 |
| 7297 #undef __ | 7279 #undef __ |
| 7298 | 7280 |
| 7299 } } // namespace v8::internal | 7281 } } // namespace v8::internal |
| OLD | NEW |