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 |