| 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 758 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 769 case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND"; | 769 case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND"; |
| 770 case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR"; | 770 case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR"; |
| 771 case Token::SAR: return "GenericBinaryOpStub_SAR"; | 771 case Token::SAR: return "GenericBinaryOpStub_SAR"; |
| 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 // A deferred code class implementing binary operations on likely smis. | 779 // Call the specialized stub for a binary operation. |
| 780 // This class generates both inline code and deferred code. | |
| 781 // The fastest path is implemented inline. Deferred code calls | |
| 782 // the GenericBinaryOpStub stub for slow cases. | |
| 783 class DeferredInlineBinaryOperation: public DeferredCode { | 780 class DeferredInlineBinaryOperation: public DeferredCode { |
| 784 public: | 781 public: |
| 785 DeferredInlineBinaryOperation(Token::Value op, | 782 DeferredInlineBinaryOperation(Token::Value op, |
| 786 OverwriteMode mode, | 783 OverwriteMode mode) |
| 787 GenericBinaryFlags flags) | 784 : op_(op), mode_(mode) { |
| 788 : stub_(op, mode, flags), op_(op) { | |
| 789 set_comment("[ DeferredInlineBinaryOperation"); | 785 set_comment("[ DeferredInlineBinaryOperation"); |
| 790 } | 786 } |
| 791 | 787 |
| 792 // Consumes its arguments, left and right, leaving them invalid. | |
| 793 Result GenerateInlineCode(Result* left, Result* right); | |
| 794 | |
| 795 virtual void Generate(); | 788 virtual void Generate(); |
| 796 | 789 |
| 797 private: | 790 private: |
| 798 GenericBinaryOpStub stub_; | |
| 799 Token::Value op_; | 791 Token::Value op_; |
| 792 OverwriteMode mode_; |
| 800 }; | 793 }; |
| 801 | 794 |
| 802 | 795 |
| 803 void DeferredInlineBinaryOperation::Generate() { | 796 void DeferredInlineBinaryOperation::Generate() { |
| 804 Result left; | 797 Result left; |
| 805 Result right; | 798 Result right; |
| 806 enter()->Bind(&left, &right); | 799 enter()->Bind(&left, &right); |
| 807 cgen()->frame()->Push(&left); | 800 cgen()->frame()->Push(&left); |
| 808 cgen()->frame()->Push(&right); | 801 cgen()->frame()->Push(&right); |
| 809 Result answer = cgen()->frame()->CallStub(&stub_, 2); | 802 GenericBinaryOpStub stub(op_, mode_, SMI_CODE_INLINED); |
| 803 Result answer = cgen()->frame()->CallStub(&stub, 2); |
| 810 exit_.Jump(&answer); | 804 exit_.Jump(&answer); |
| 811 } | 805 } |
| 812 | 806 |
| 813 | 807 |
| 814 void CodeGenerator::GenericBinaryOperation(Token::Value op, | 808 void CodeGenerator::GenericBinaryOperation(Token::Value op, |
| 815 SmiAnalysis* type, | 809 SmiAnalysis* type, |
| 816 OverwriteMode overwrite_mode) { | 810 OverwriteMode overwrite_mode) { |
| 817 Comment cmnt(masm_, "[ BinaryOperation"); | 811 Comment cmnt(masm_, "[ BinaryOperation"); |
| 818 Comment cmnt_token(masm_, Token::String(op)); | 812 Comment cmnt_token(masm_, Token::String(op)); |
| 819 | 813 |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1000 } | 994 } |
| 1001 frame_->Push(Handle<Object>(answer_object)); | 995 frame_->Push(Handle<Object>(answer_object)); |
| 1002 return true; | 996 return true; |
| 1003 } | 997 } |
| 1004 | 998 |
| 1005 | 999 |
| 1006 void CodeGenerator::LikelySmiBinaryOperation(Token::Value op, | 1000 void CodeGenerator::LikelySmiBinaryOperation(Token::Value op, |
| 1007 Result* left, | 1001 Result* left, |
| 1008 Result* right, | 1002 Result* right, |
| 1009 OverwriteMode overwrite_mode) { | 1003 OverwriteMode overwrite_mode) { |
| 1010 // Implements a binary operation using a deferred code object | 1004 // Implements a binary operation using a deferred code object and |
| 1011 // and some inline code to operate on smis quickly. | 1005 // some inline code to operate on smis quickly. |
| 1012 DeferredInlineBinaryOperation* deferred = | 1006 DeferredInlineBinaryOperation* deferred = |
| 1013 new DeferredInlineBinaryOperation(op, overwrite_mode, SMI_CODE_INLINED); | 1007 new DeferredInlineBinaryOperation(op, overwrite_mode); |
| 1014 // Generate the inline code that handles some smi operations, | 1008 |
| 1015 // and jumps to the deferred code for everything else. | 1009 // Special handling of div and mod because they use fixed registers. |
| 1016 Result answer = deferred->GenerateInlineCode(left, right); | 1010 if (op == Token::DIV || op == Token::MOD) { |
| 1011 // We need eax as the quotient register, edx as the remainder |
| 1012 // register, neither left nor right in eax or edx, and left copied |
| 1013 // to eax. |
| 1014 Result quotient; |
| 1015 Result remainder; |
| 1016 bool left_is_in_eax = false; |
| 1017 // Step 1: get eax for quotient. |
| 1018 if ((left->is_register() && left->reg().is(eax)) || |
| 1019 (right->is_register() && right->reg().is(eax))) { |
| 1020 // One or both is in eax. Use a fresh non-edx register for |
| 1021 // them. |
| 1022 Result fresh = allocator_->Allocate(); |
| 1023 ASSERT(fresh.is_valid()); |
| 1024 if (fresh.reg().is(edx)) { |
| 1025 remainder = fresh; |
| 1026 fresh = allocator_->Allocate(); |
| 1027 ASSERT(fresh.is_valid()); |
| 1028 } |
| 1029 if (left->is_register() && left->reg().is(eax)) { |
| 1030 quotient = *left; |
| 1031 *left = fresh; |
| 1032 left_is_in_eax = true; |
| 1033 } |
| 1034 if (right->is_register() && right->reg().is(eax)) { |
| 1035 quotient = *right; |
| 1036 *right = fresh; |
| 1037 } |
| 1038 __ mov(fresh.reg(), eax); |
| 1039 } else { |
| 1040 // Neither left nor right is in eax. |
| 1041 quotient = allocator_->Allocate(eax); |
| 1042 } |
| 1043 ASSERT(quotient.is_register() && quotient.reg().is(eax)); |
| 1044 ASSERT(!(left->is_register() && left->reg().is(eax))); |
| 1045 ASSERT(!(right->is_register() && right->reg().is(eax))); |
| 1046 |
| 1047 // Step 2: get edx for remainder if necessary. |
| 1048 if (!remainder.is_valid()) { |
| 1049 if ((left->is_register() && left->reg().is(edx)) || |
| 1050 (right->is_register() && right->reg().is(edx))) { |
| 1051 Result fresh = allocator_->Allocate(); |
| 1052 ASSERT(fresh.is_valid()); |
| 1053 if (left->is_register() && left->reg().is(edx)) { |
| 1054 remainder = *left; |
| 1055 *left = fresh; |
| 1056 } |
| 1057 if (right->is_register() && right->reg().is(edx)) { |
| 1058 remainder = *right; |
| 1059 *right = fresh; |
| 1060 } |
| 1061 __ mov(fresh.reg(), edx); |
| 1062 } else { |
| 1063 // Neither left nor right is in edx. |
| 1064 remainder = allocator_->Allocate(edx); |
| 1065 } |
| 1066 } |
| 1067 ASSERT(remainder.is_register() && remainder.reg().is(edx)); |
| 1068 ASSERT(!(left->is_register() && left->reg().is(edx))); |
| 1069 ASSERT(!(right->is_register() && right->reg().is(edx))); |
| 1070 |
| 1071 left->ToRegister(); |
| 1072 right->ToRegister(); |
| 1073 frame_->Spill(quotient.reg()); |
| 1074 frame_->Spill(remainder.reg()); |
| 1075 |
| 1076 // Check that left and right are smi tagged. |
| 1077 if (left->reg().is(right->reg())) { |
| 1078 __ test(left->reg(), Immediate(kSmiTagMask)); |
| 1079 } else { |
| 1080 // Use the quotient register as a scratch for the tag check. |
| 1081 if (!left_is_in_eax) __ mov(quotient.reg(), left->reg()); |
| 1082 left_is_in_eax = false; |
| 1083 __ or_(quotient.reg(), Operand(right->reg())); |
| 1084 ASSERT(kSmiTag == 0); // Adjust test if not the case. |
| 1085 __ test(quotient.reg(), Immediate(kSmiTagMask)); |
| 1086 } |
| 1087 deferred->SetEntryFrame(left, right); |
| 1088 deferred->enter()->Branch(not_zero, left, right); |
| 1089 |
| 1090 if (!left_is_in_eax) __ mov(quotient.reg(), left->reg()); |
| 1091 |
| 1092 // Sign extend eax into edx:eax. |
| 1093 __ cdq(); |
| 1094 // Check for 0 divisor. |
| 1095 __ test(right->reg(), Operand(right->reg())); |
| 1096 deferred->enter()->Branch(zero, left, right); |
| 1097 // Divide edx:eax by the right operand. |
| 1098 __ idiv(right->reg()); |
| 1099 |
| 1100 // Complete the operation. |
| 1101 if (op == Token::DIV) { |
| 1102 // Check for negative zero result. If result is zero, and divisor |
| 1103 // is negative, return a floating point negative zero. The |
| 1104 // virtual frame is unchanged in this block, so local control flow |
| 1105 // can use a Label rather than a JumpTarget. |
| 1106 Label non_zero_result; |
| 1107 __ test(left->reg(), Operand(left->reg())); |
| 1108 __ j(not_zero, &non_zero_result); |
| 1109 __ test(right->reg(), Operand(right->reg())); |
| 1110 deferred->enter()->Branch(negative, left, right); |
| 1111 __ bind(&non_zero_result); |
| 1112 // Check for the corner case of dividing the most negative smi by |
| 1113 // -1. We cannot use the overflow flag, since it is not set by |
| 1114 // idiv instruction. |
| 1115 ASSERT(kSmiTag == 0 && kSmiTagSize == 1); |
| 1116 __ cmp(quotient.reg(), 0x40000000); |
| 1117 deferred->enter()->Branch(equal, left, right); |
| 1118 // Check that the remainder is zero. |
| 1119 __ test(remainder.reg(), Operand(remainder.reg())); |
| 1120 remainder.Unuse(); |
| 1121 deferred->enter()->Branch(not_zero, left, right); |
| 1122 left->Unuse(); |
| 1123 right->Unuse(); |
| 1124 // Tag the result and store it in the quotient register. |
| 1125 ASSERT(kSmiTagSize == times_2); // adjust code if not the case |
| 1126 __ lea(quotient.reg(), |
| 1127 Operand(quotient.reg(), quotient.reg(), times_1, kSmiTag)); |
| 1128 deferred->BindExit("ient); |
| 1129 frame_->Push("ient); |
| 1130 } else { |
| 1131 ASSERT(op == Token::MOD); |
| 1132 quotient.Unuse(); |
| 1133 // Check for a negative zero result. If the result is zero, and |
| 1134 // the dividend is negative, return a floating point negative |
| 1135 // zero. The frame is unchanged in this block, so local control |
| 1136 // flow can use a Label rather than a JumpTarget. |
| 1137 Label non_zero_result; |
| 1138 __ test(remainder.reg(), Operand(remainder.reg())); |
| 1139 __ j(not_zero, &non_zero_result, taken); |
| 1140 __ test(left->reg(), Operand(left->reg())); |
| 1141 deferred->enter()->Branch(negative, left, right); |
| 1142 left->Unuse(); |
| 1143 right->Unuse(); |
| 1144 __ bind(&non_zero_result); |
| 1145 deferred->BindExit(&remainder); |
| 1146 frame_->Push(&remainder); |
| 1147 } |
| 1148 return; |
| 1149 } |
| 1150 |
| 1151 // Handle the other binary operations. |
| 1152 left->ToRegister(); |
| 1153 right->ToRegister(); |
| 1154 // A newly allocated register answer is used to hold the answer. The |
| 1155 // registers containing left and right are not modified in most cases, |
| 1156 // so they usually don't need to be spilled in the fast case. |
| 1157 Result answer = allocator_->Allocate(); |
| 1158 |
| 1159 ASSERT(answer.is_valid()); |
| 1160 // Perform the smi tag check. |
| 1161 if (left->reg().is(right->reg())) { |
| 1162 __ test(left->reg(), Immediate(kSmiTagMask)); |
| 1163 } else { |
| 1164 __ mov(answer.reg(), left->reg()); |
| 1165 __ or_(answer.reg(), Operand(right->reg())); |
| 1166 ASSERT(kSmiTag == 0); // Adjust test if not the case. |
| 1167 __ test(answer.reg(), Immediate(kSmiTagMask)); |
| 1168 } |
| 1169 switch (op) { |
| 1170 case Token::ADD: |
| 1171 deferred->SetEntryFrame(left, right); |
| 1172 deferred->enter()->Branch(not_zero, left, right, not_taken); |
| 1173 __ mov(answer.reg(), left->reg()); |
| 1174 __ add(answer.reg(), Operand(right->reg())); // Add optimistically. |
| 1175 deferred->enter()->Branch(overflow, left, right, not_taken); |
| 1176 break; |
| 1177 |
| 1178 case Token::SUB: |
| 1179 deferred->SetEntryFrame(left, right); |
| 1180 deferred->enter()->Branch(not_zero, left, right, not_taken); |
| 1181 __ mov(answer.reg(), left->reg()); |
| 1182 __ sub(answer.reg(), Operand(right->reg())); // Subtract optimistically. |
| 1183 deferred->enter()->Branch(overflow, left, right, not_taken); |
| 1184 break; |
| 1185 |
| 1186 case Token::MUL: { |
| 1187 deferred->SetEntryFrame(left, right); |
| 1188 deferred->enter()->Branch(not_zero, left, right, not_taken); |
| 1189 __ mov(answer.reg(), left->reg()); |
| 1190 // If the smi tag is 0 we can just leave the tag on one operand. |
| 1191 ASSERT(kSmiTag == 0); // Adjust code below if not the case. |
| 1192 // Remove smi tag from the left operand (but keep sign). |
| 1193 // Left-hand operand has been copied into answer. |
| 1194 __ sar(answer.reg(), kSmiTagSize); |
| 1195 // Do multiplication of smis, leaving result in answer. |
| 1196 __ imul(answer.reg(), Operand(right->reg())); |
| 1197 // Go slow on overflows. |
| 1198 deferred->enter()->Branch(overflow, left, right, not_taken); |
| 1199 // Check for negative zero result. If product is zero, and one |
| 1200 // argument is negative, go to slow case. The frame is unchanged |
| 1201 // in this block, so local control flow can use a Label rather |
| 1202 // than a JumpTarget. |
| 1203 Label non_zero_result; |
| 1204 __ test(answer.reg(), Operand(answer.reg())); |
| 1205 __ j(not_zero, &non_zero_result, taken); |
| 1206 __ mov(answer.reg(), left->reg()); |
| 1207 __ or_(answer.reg(), Operand(right->reg())); |
| 1208 deferred->enter()->Branch(negative, left, right, not_taken); |
| 1209 __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct. |
| 1210 __ bind(&non_zero_result); |
| 1211 break; |
| 1212 } |
| 1213 |
| 1214 case Token::BIT_OR: |
| 1215 deferred->enter()->Branch(not_zero, left, right, not_taken); |
| 1216 __ mov(answer.reg(), left->reg()); |
| 1217 __ or_(answer.reg(), Operand(right->reg())); |
| 1218 break; |
| 1219 |
| 1220 case Token::BIT_AND: |
| 1221 deferred->enter()->Branch(not_zero, left, right, not_taken); |
| 1222 __ mov(answer.reg(), left->reg()); |
| 1223 __ and_(answer.reg(), Operand(right->reg())); |
| 1224 break; |
| 1225 |
| 1226 case Token::BIT_XOR: |
| 1227 deferred->enter()->Branch(not_zero, left, right, not_taken); |
| 1228 __ mov(answer.reg(), left->reg()); |
| 1229 __ xor_(answer.reg(), Operand(right->reg())); |
| 1230 break; |
| 1231 |
| 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: |
| 1336 UNREACHABLE(); |
| 1337 break; |
| 1338 } |
| 1339 left->Unuse(); |
| 1340 right->Unuse(); |
| 1017 deferred->BindExit(&answer); | 1341 deferred->BindExit(&answer); |
| 1018 frame_->Push(&answer); | 1342 frame_->Push(&answer); |
| 1019 } | 1343 } |
| 1020 | 1344 |
| 1021 | 1345 |
| 1022 class DeferredInlineSmiOperation: public DeferredCode { | 1346 class DeferredInlineSmiOperation: public DeferredCode { |
| 1023 public: | 1347 public: |
| 1024 DeferredInlineSmiOperation(Token::Value op, | 1348 DeferredInlineSmiOperation(Token::Value op, |
| 1025 Smi* value, | 1349 Smi* value, |
| 1026 OverwriteMode overwrite_mode) | 1350 OverwriteMode overwrite_mode) |
| (...skipping 4698 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5725 // Return 1/0 for true/false in eax. | 6049 // Return 1/0 for true/false in eax. |
| 5726 __ bind(&true_result); | 6050 __ bind(&true_result); |
| 5727 __ mov(eax, 1); | 6051 __ mov(eax, 1); |
| 5728 __ ret(1 * kPointerSize); | 6052 __ ret(1 * kPointerSize); |
| 5729 __ bind(&false_result); | 6053 __ bind(&false_result); |
| 5730 __ mov(eax, 0); | 6054 __ mov(eax, 0); |
| 5731 __ ret(1 * kPointerSize); | 6055 __ ret(1 * kPointerSize); |
| 5732 } | 6056 } |
| 5733 | 6057 |
| 5734 | 6058 |
| 5735 Result DeferredInlineBinaryOperation::GenerateInlineCode(Result* left, | |
| 5736 Result* right) { | |
| 5737 MacroAssembler* masm = cgen()->masm(); | |
| 5738 | |
| 5739 // Special handling of div and mod because they use fixed registers. | |
| 5740 if (op_ == Token::DIV || op_ == Token::MOD) { | |
| 5741 // We need eax as the quotient register, edx as the remainder | |
| 5742 // register, neither left nor right in eax or edx, and left copied | |
| 5743 // to eax. | |
| 5744 Result quotient; | |
| 5745 Result remainder; | |
| 5746 bool left_is_in_eax = false; | |
| 5747 // Step 1: get eax for quotient. | |
| 5748 if ((left->is_register() && left->reg().is(eax)) || | |
| 5749 (right->is_register() && right->reg().is(eax))) { | |
| 5750 // One or both is in eax. Use a fresh non-edx register for | |
| 5751 // them. | |
| 5752 Result fresh = cgen()->allocator()->Allocate(); | |
| 5753 ASSERT(fresh.is_valid()); | |
| 5754 if (fresh.reg().is(edx)) { | |
| 5755 remainder = fresh; | |
| 5756 fresh = cgen()->allocator()->Allocate(); | |
| 5757 ASSERT(fresh.is_valid()); | |
| 5758 } | |
| 5759 if (left->is_register() && left->reg().is(eax)) { | |
| 5760 quotient = *left; | |
| 5761 *left = fresh; | |
| 5762 left_is_in_eax = true; | |
| 5763 } | |
| 5764 if (right->is_register() && right->reg().is(eax)) { | |
| 5765 quotient = *right; | |
| 5766 *right = fresh; | |
| 5767 } | |
| 5768 __ mov(fresh.reg(), eax); | |
| 5769 } else { | |
| 5770 // Neither left nor right is in eax. | |
| 5771 quotient = cgen()->allocator()->Allocate(eax); | |
| 5772 } | |
| 5773 ASSERT(quotient.is_register() && quotient.reg().is(eax)); | |
| 5774 ASSERT(!(left->is_register() && left->reg().is(eax))); | |
| 5775 ASSERT(!(right->is_register() && right->reg().is(eax))); | |
| 5776 | |
| 5777 // Step 2: get edx for remainder if necessary. | |
| 5778 if (!remainder.is_valid()) { | |
| 5779 if ((left->is_register() && left->reg().is(edx)) || | |
| 5780 (right->is_register() && right->reg().is(edx))) { | |
| 5781 Result fresh = cgen()->allocator()->Allocate(); | |
| 5782 ASSERT(fresh.is_valid()); | |
| 5783 if (left->is_register() && left->reg().is(edx)) { | |
| 5784 remainder = *left; | |
| 5785 *left = fresh; | |
| 5786 } | |
| 5787 if (right->is_register() && right->reg().is(edx)) { | |
| 5788 remainder = *right; | |
| 5789 *right = fresh; | |
| 5790 } | |
| 5791 __ mov(fresh.reg(), edx); | |
| 5792 } else { | |
| 5793 // Neither left nor right is in edx. | |
| 5794 remainder = cgen()->allocator()->Allocate(edx); | |
| 5795 } | |
| 5796 } | |
| 5797 ASSERT(remainder.is_register() && remainder.reg().is(edx)); | |
| 5798 ASSERT(!(left->is_register() && left->reg().is(edx))); | |
| 5799 ASSERT(!(right->is_register() && right->reg().is(edx))); | |
| 5800 | |
| 5801 left->ToRegister(); | |
| 5802 right->ToRegister(); | |
| 5803 cgen()->frame()->Spill(quotient.reg()); | |
| 5804 cgen()->frame()->Spill(remainder.reg()); | |
| 5805 | |
| 5806 // Check that left and right are smi tagged. | |
| 5807 if (left->reg().is(right->reg())) { | |
| 5808 __ test(left->reg(), Immediate(kSmiTagMask)); | |
| 5809 } else { | |
| 5810 // Use the quotient register as a scratch for the tag check. | |
| 5811 if (!left_is_in_eax) __ mov(quotient.reg(), left->reg()); | |
| 5812 left_is_in_eax = false; | |
| 5813 __ or_(quotient.reg(), Operand(right->reg())); | |
| 5814 ASSERT(kSmiTag == 0); // Adjust test if not the case. | |
| 5815 __ test(quotient.reg(), Immediate(kSmiTagMask)); | |
| 5816 } | |
| 5817 SetEntryFrame(left, right); | |
| 5818 enter()->Branch(not_zero, left, right); | |
| 5819 | |
| 5820 if (!left_is_in_eax) __ mov(quotient.reg(), left->reg()); | |
| 5821 | |
| 5822 // Sign extend eax into edx:eax. | |
| 5823 __ cdq(); | |
| 5824 // Check for 0 divisor. | |
| 5825 __ test(right->reg(), Operand(right->reg())); | |
| 5826 enter()->Branch(zero, left, right); | |
| 5827 // Divide edx:eax by the right operand. | |
| 5828 __ idiv(right->reg()); | |
| 5829 | |
| 5830 // Complete the operation. | |
| 5831 if (op_ == Token::DIV) { | |
| 5832 // Check for negative zero result. If result is zero, and divisor | |
| 5833 // is negative, return a floating point negative zero. The | |
| 5834 // virtual frame is unchanged in this block, so local control flow | |
| 5835 // can use a Label rather than a JumpTarget. | |
| 5836 Label non_zero_result; | |
| 5837 __ test(left->reg(), Operand(left->reg())); | |
| 5838 __ j(not_zero, &non_zero_result); | |
| 5839 __ test(right->reg(), Operand(right->reg())); | |
| 5840 enter()->Branch(negative, left, right); | |
| 5841 __ bind(&non_zero_result); | |
| 5842 // Check for the corner case of dividing the most negative smi by | |
| 5843 // -1. We cannot use the overflow flag, since it is not set by | |
| 5844 // idiv instruction. | |
| 5845 ASSERT(kSmiTag == 0 && kSmiTagSize == 1); | |
| 5846 __ cmp(quotient.reg(), 0x40000000); | |
| 5847 enter()->Branch(equal, left, right); | |
| 5848 // Check that the remainder is zero. | |
| 5849 __ test(remainder.reg(), Operand(remainder.reg())); | |
| 5850 enter()->Branch(not_zero, left, right); | |
| 5851 left->Unuse(); | |
| 5852 right->Unuse(); | |
| 5853 // Tag the result and store it in the quotient register. | |
| 5854 ASSERT(kSmiTagSize == times_2); // adjust code if not the case | |
| 5855 __ lea(quotient.reg(), | |
| 5856 Operand(quotient.reg(), quotient.reg(), times_1, kSmiTag)); | |
| 5857 return quotient; | |
| 5858 } else { | |
| 5859 ASSERT(op_ == Token::MOD); | |
| 5860 // Check for a negative zero result. If the result is zero, and | |
| 5861 // the dividend is negative, return a floating point negative | |
| 5862 // zero. The frame is unchanged in this block, so local control | |
| 5863 // flow can use a Label rather than a JumpTarget. | |
| 5864 Label non_zero_result; | |
| 5865 __ test(remainder.reg(), Operand(remainder.reg())); | |
| 5866 __ j(not_zero, &non_zero_result, taken); | |
| 5867 __ test(left->reg(), Operand(left->reg())); | |
| 5868 enter()->Branch(negative, left, right); | |
| 5869 left->Unuse(); | |
| 5870 right->Unuse(); | |
| 5871 __ bind(&non_zero_result); | |
| 5872 return remainder; | |
| 5873 } | |
| 5874 } | |
| 5875 | |
| 5876 // Handle the other binary operations. | |
| 5877 left->ToRegister(); | |
| 5878 right->ToRegister(); | |
| 5879 // A newly allocated register answer is used to hold the answer. The | |
| 5880 // registers containing left and right are not modified in most cases, | |
| 5881 // so they usually don't need to be spilled in the fast case. | |
| 5882 Result answer = cgen()->allocator()->Allocate(); | |
| 5883 | |
| 5884 ASSERT(answer.is_valid()); | |
| 5885 // Perform the smi tag check. | |
| 5886 if (left->reg().is(right->reg())) { | |
| 5887 __ test(left->reg(), Immediate(kSmiTagMask)); | |
| 5888 } else { | |
| 5889 __ mov(answer.reg(), left->reg()); | |
| 5890 __ or_(answer.reg(), Operand(right->reg())); | |
| 5891 ASSERT(kSmiTag == 0); // Adjust test if not the case. | |
| 5892 __ test(answer.reg(), Immediate(kSmiTagMask)); | |
| 5893 } | |
| 5894 switch (op_) { | |
| 5895 case Token::ADD: | |
| 5896 SetEntryFrame(left, right); | |
| 5897 enter()->Branch(not_zero, left, right, not_taken); | |
| 5898 __ mov(answer.reg(), left->reg()); | |
| 5899 __ add(answer.reg(), Operand(right->reg())); // Add optimistically. | |
| 5900 enter()->Branch(overflow, left, right, not_taken); | |
| 5901 break; | |
| 5902 | |
| 5903 case Token::SUB: | |
| 5904 SetEntryFrame(left, right); | |
| 5905 enter()->Branch(not_zero, left, right, not_taken); | |
| 5906 __ mov(answer.reg(), left->reg()); | |
| 5907 __ sub(answer.reg(), Operand(right->reg())); // Subtract optimistically. | |
| 5908 enter()->Branch(overflow, left, right, not_taken); | |
| 5909 break; | |
| 5910 | |
| 5911 case Token::MUL: { | |
| 5912 SetEntryFrame(left, right); | |
| 5913 enter()->Branch(not_zero, left, right, not_taken); | |
| 5914 __ mov(answer.reg(), left->reg()); | |
| 5915 // If the smi tag is 0 we can just leave the tag on one operand. | |
| 5916 ASSERT(kSmiTag == 0); // Adjust code below if not the case. | |
| 5917 // Remove smi tag from the left operand (but keep sign). | |
| 5918 // Left-hand operand has been copied into answer. | |
| 5919 __ sar(answer.reg(), kSmiTagSize); | |
| 5920 // Do multiplication of smis, leaving result in answer. | |
| 5921 __ imul(answer.reg(), Operand(right->reg())); | |
| 5922 // Go slow on overflows. | |
| 5923 enter()->Branch(overflow, left, right, not_taken); | |
| 5924 // Check for negative zero result. If product is zero, and one | |
| 5925 // argument is negative, go to slow case. The frame is unchanged | |
| 5926 // in this block, so local control flow can use a Label rather | |
| 5927 // than a JumpTarget. | |
| 5928 Label non_zero_result; | |
| 5929 __ test(answer.reg(), Operand(answer.reg())); | |
| 5930 __ j(not_zero, &non_zero_result, taken); | |
| 5931 __ mov(answer.reg(), left->reg()); | |
| 5932 __ or_(answer.reg(), Operand(right->reg())); | |
| 5933 enter()->Branch(negative, left, right, not_taken); | |
| 5934 __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct. | |
| 5935 __ bind(&non_zero_result); | |
| 5936 break; | |
| 5937 } | |
| 5938 | |
| 5939 case Token::BIT_OR: | |
| 5940 enter()->Branch(not_zero, left, right, not_taken); | |
| 5941 __ mov(answer.reg(), left->reg()); | |
| 5942 __ or_(answer.reg(), Operand(right->reg())); | |
| 5943 break; | |
| 5944 | |
| 5945 case Token::BIT_AND: | |
| 5946 enter()->Branch(not_zero, left, right, not_taken); | |
| 5947 __ mov(answer.reg(), left->reg()); | |
| 5948 __ and_(answer.reg(), Operand(right->reg())); | |
| 5949 break; | |
| 5950 | |
| 5951 case Token::BIT_XOR: | |
| 5952 enter()->Branch(not_zero, left, right, not_taken); | |
| 5953 __ mov(answer.reg(), left->reg()); | |
| 5954 __ xor_(answer.reg(), Operand(right->reg())); | |
| 5955 break; | |
| 5956 | |
| 5957 case Token::SHL: | |
| 5958 case Token::SHR: | |
| 5959 case Token::SAR: | |
| 5960 enter()->Branch(not_zero, left, right, not_taken); | |
| 5961 __ mov(answer.reg(), left->reg()); | |
| 5962 // Move right into ecx. | |
| 5963 // Left is in two registers already, so even if left or answer is ecx, | |
| 5964 // we can move right to it, and use the other one. | |
| 5965 // Right operand must be in register cl because x86 likes it that way. | |
| 5966 if (right->reg().is(ecx)) { | |
| 5967 // Right is already in the right place. Left may be in the | |
| 5968 // same register, which causes problems. Always use answer | |
| 5969 // instead of left, even if left is not ecx, since this avoids | |
| 5970 // spilling left. | |
| 5971 *left = answer; | |
| 5972 } else if (left->reg().is(ecx)) { | |
| 5973 cgen()->frame()->Spill(left->reg()); | |
| 5974 __ mov(left->reg(), right->reg()); | |
| 5975 *right = *left; | |
| 5976 *left = answer; // Use copy of left in answer as left. | |
| 5977 } else if (answer.reg().is(ecx)) { | |
| 5978 __ mov(answer.reg(), right->reg()); | |
| 5979 *right = answer; | |
| 5980 } else { | |
| 5981 Result reg_ecx = cgen()->allocator()->Allocate(ecx); | |
| 5982 ASSERT(reg_ecx.is_valid()); | |
| 5983 __ mov(ecx, right->reg()); | |
| 5984 *right = reg_ecx; | |
| 5985 // Answer and left both contain the left operand. Use answer, so | |
| 5986 // left is not spilled. | |
| 5987 *left = answer; | |
| 5988 } | |
| 5989 ASSERT(left->reg().is_valid()); | |
| 5990 ASSERT(!left->reg().is(ecx)); | |
| 5991 ASSERT(right->reg().is(ecx)); | |
| 5992 answer.Unuse(); // Answer may now be being used for left or right. | |
| 5993 // We will modify left and right, which we do not do in any other | |
| 5994 // binary operation. The exits to slow code need to restore the | |
| 5995 // original values of left and right, or at least values that give | |
| 5996 // the same answer. | |
| 5997 | |
| 5998 // We are modifying left and right. They must be spilled! | |
| 5999 cgen()->frame()->Spill(left->reg()); | |
| 6000 cgen()->frame()->Spill(right->reg()); | |
| 6001 | |
| 6002 // Remove tags from operands (but keep sign). | |
| 6003 __ sar(left->reg(), kSmiTagSize); | |
| 6004 __ sar(ecx, kSmiTagSize); | |
| 6005 // Perform the operation. | |
| 6006 switch (op_) { | |
| 6007 case Token::SAR: | |
| 6008 __ sar(left->reg()); | |
| 6009 // No checks of result necessary | |
| 6010 break; | |
| 6011 case Token::SHR: { | |
| 6012 __ shr(left->reg()); | |
| 6013 // Check that the *unsigned* result fits in a smi. | |
| 6014 // Neither of the two high-order bits can be set: | |
| 6015 // - 0x80000000: high bit would be lost when smi tagging. | |
| 6016 // - 0x40000000: this number would convert to negative when | |
| 6017 // Smi tagging these two cases can only happen with shifts | |
| 6018 // by 0 or 1 when handed a valid smi. | |
| 6019 // If the answer cannot be represented by a SMI, restore | |
| 6020 // the left and right arguments, and jump to slow case. | |
| 6021 // The low bit of the left argument may be lost, but only | |
| 6022 // in a case where it is dropped anyway. | |
| 6023 JumpTarget result_ok; | |
| 6024 __ test(left->reg(), Immediate(0xc0000000)); | |
| 6025 result_ok.Branch(zero, left, taken); | |
| 6026 __ shl(left->reg()); | |
| 6027 ASSERT(kSmiTag == 0); | |
| 6028 __ shl(left->reg(), kSmiTagSize); | |
| 6029 __ shl(right->reg(), kSmiTagSize); | |
| 6030 enter()->Jump(left, right); | |
| 6031 result_ok.Bind(left); | |
| 6032 break; | |
| 6033 } | |
| 6034 case Token::SHL: { | |
| 6035 __ shl(left->reg()); | |
| 6036 // Check that the *signed* result fits in a smi. | |
| 6037 JumpTarget result_ok; | |
| 6038 __ cmp(left->reg(), 0xc0000000); | |
| 6039 result_ok.Branch(positive, left, taken); | |
| 6040 | |
| 6041 __ shr(left->reg()); | |
| 6042 ASSERT(kSmiTag == 0); | |
| 6043 __ shl(left->reg(), kSmiTagSize); | |
| 6044 __ shl(right->reg(), kSmiTagSize); | |
| 6045 enter()->Jump(left, right); | |
| 6046 result_ok.Bind(left); | |
| 6047 break; | |
| 6048 } | |
| 6049 default: | |
| 6050 UNREACHABLE(); | |
| 6051 } | |
| 6052 // Smi-tag the result, in left, and make answer an alias for left-> | |
| 6053 answer = *left; | |
| 6054 answer.ToRegister(); | |
| 6055 ASSERT(kSmiTagSize == times_2); // adjust code if not the case | |
| 6056 __ lea(answer.reg(), | |
| 6057 Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); | |
| 6058 break; | |
| 6059 | |
| 6060 default: | |
| 6061 UNREACHABLE(); | |
| 6062 break; | |
| 6063 } | |
| 6064 left->Unuse(); | |
| 6065 right->Unuse(); | |
| 6066 return answer; | |
| 6067 } | |
| 6068 | |
| 6069 | |
| 6070 void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) { | 6059 void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) { |
| 6071 // Perform fast-case smi code for the operation (eax <op> ebx) and | 6060 // Perform fast-case smi code for the operation (eax <op> ebx) and |
| 6072 // leave result in register eax. | 6061 // leave result in register eax. |
| 6073 | 6062 |
| 6074 // Prepare the smi check of both operands by or'ing them together | 6063 // Prepare the smi check of both operands by or'ing them together |
| 6075 // before checking against the smi mask. | 6064 // before checking against the smi mask. |
| 6076 __ mov(ecx, Operand(ebx)); | 6065 __ mov(ecx, Operand(ebx)); |
| 6077 __ or_(ecx, Operand(eax)); | 6066 __ or_(ecx, Operand(eax)); |
| 6078 | 6067 |
| 6079 switch (op_) { | 6068 switch (op_) { |
| (...skipping 1221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7301 | 7290 |
| 7302 // Slow-case: Go through the JavaScript implementation. | 7291 // Slow-case: Go through the JavaScript implementation. |
| 7303 __ bind(&slow); | 7292 __ bind(&slow); |
| 7304 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); | 7293 __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); |
| 7305 } | 7294 } |
| 7306 | 7295 |
| 7307 | 7296 |
| 7308 #undef __ | 7297 #undef __ |
| 7309 | 7298 |
| 7310 } } // namespace v8::internal | 7299 } } // namespace v8::internal |
| OLD | NEW |