OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 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 619 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
630 // operation result to the caller of the stub. | 630 // operation result to the caller of the stub. |
631 __ TailCallExternalReference( | 631 __ TailCallExternalReference( |
632 ExternalReference(IC_Utility(IC::kToBoolean_Patch), masm->isolate()), | 632 ExternalReference(IC_Utility(IC::kToBoolean_Patch), masm->isolate()), |
633 3, | 633 3, |
634 1); | 634 1); |
635 } | 635 } |
636 | 636 |
637 | 637 |
638 class FloatingPointHelper : public AllStatic { | 638 class FloatingPointHelper : public AllStatic { |
639 public: | 639 public: |
| 640 enum ConvertUndefined { |
| 641 CONVERT_UNDEFINED_TO_ZERO, |
| 642 BAILOUT_ON_UNDEFINED |
| 643 }; |
640 // Load the operands from rdx and rax into xmm0 and xmm1, as doubles. | 644 // Load the operands from rdx and rax into xmm0 and xmm1, as doubles. |
641 // If the operands are not both numbers, jump to not_numbers. | 645 // If the operands are not both numbers, jump to not_numbers. |
642 // Leaves rdx and rax unchanged. SmiOperands assumes both are smis. | 646 // Leaves rdx and rax unchanged. SmiOperands assumes both are smis. |
643 // NumberOperands assumes both are smis or heap numbers. | 647 // NumberOperands assumes both are smis or heap numbers. |
644 static void LoadSSE2SmiOperands(MacroAssembler* masm); | 648 static void LoadSSE2SmiOperands(MacroAssembler* masm); |
645 static void LoadSSE2NumberOperands(MacroAssembler* masm); | 649 static void LoadSSE2NumberOperands(MacroAssembler* masm); |
646 static void LoadSSE2UnknownOperands(MacroAssembler* masm, | 650 static void LoadSSE2UnknownOperands(MacroAssembler* masm, |
647 Label* not_numbers); | 651 Label* not_numbers); |
648 | 652 |
649 // Takes the operands in rdx and rax and loads them as integers in rax | 653 // Takes the operands in rdx and rax and loads them as integers in rax |
(...skipping 15 matching lines...) Expand all Loading... |
665 // is NULL). | 669 // is NULL). |
666 // On success, both first and second holds Smi tagged values. | 670 // On success, both first and second holds Smi tagged values. |
667 // One of first or second must be non-Smi when entering. | 671 // One of first or second must be non-Smi when entering. |
668 static void NumbersToSmis(MacroAssembler* masm, | 672 static void NumbersToSmis(MacroAssembler* masm, |
669 Register first, | 673 Register first, |
670 Register second, | 674 Register second, |
671 Register scratch1, | 675 Register scratch1, |
672 Register scratch2, | 676 Register scratch2, |
673 Register scratch3, | 677 Register scratch3, |
674 Label* on_success, | 678 Label* on_success, |
675 Label* on_not_smis); | 679 Label* on_not_smis, |
| 680 ConvertUndefined convert_undefined); |
676 }; | 681 }; |
677 | 682 |
678 | 683 |
679 // Get the integer part of a heap number. | 684 // Get the integer part of a heap number. |
680 // Overwrites the contents of rdi, rbx and rcx. Result cannot be rdi or rbx. | 685 // Overwrites the contents of rdi, rbx and rcx. Result cannot be rdi or rbx. |
681 void IntegerConvert(MacroAssembler* masm, | 686 void IntegerConvert(MacroAssembler* masm, |
682 Register result, | 687 Register result, |
683 Register source) { | 688 Register source) { |
684 // Result may be rcx. If result and source are the same register, source will | 689 // Result may be rcx. If result and source are the same register, source will |
685 // be overwritten. | 690 // be overwritten. |
(...skipping 304 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
990 case UNARY_NO_OVERWRITE: overwrite_name = "Alloc"; break; | 995 case UNARY_NO_OVERWRITE: overwrite_name = "Alloc"; break; |
991 case UNARY_OVERWRITE: overwrite_name = "Overwrite"; break; | 996 case UNARY_OVERWRITE: overwrite_name = "Overwrite"; break; |
992 } | 997 } |
993 stream->Add("UnaryOpStub_%s_%s_%s", | 998 stream->Add("UnaryOpStub_%s_%s_%s", |
994 op_name, | 999 op_name, |
995 overwrite_name, | 1000 overwrite_name, |
996 UnaryOpIC::GetName(operand_type_)); | 1001 UnaryOpIC::GetName(operand_type_)); |
997 } | 1002 } |
998 | 1003 |
999 | 1004 |
| 1005 void BinaryOpStub::Initialize() {} |
| 1006 |
| 1007 |
1000 void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { | 1008 void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { |
1001 __ pop(rcx); // Save return address. | 1009 __ pop(rcx); // Save return address. |
1002 __ push(rdx); | 1010 __ push(rdx); |
1003 __ push(rax); | 1011 __ push(rax); |
1004 // Left and right arguments are now on top. | 1012 // Left and right arguments are now on top. |
1005 // Push this stub's key. Although the operation and the type info are | |
1006 // encoded into the key, the encoding is opaque, so push them too. | |
1007 __ Push(Smi::FromInt(MinorKey())); | 1013 __ Push(Smi::FromInt(MinorKey())); |
1008 __ Push(Smi::FromInt(op_)); | |
1009 __ Push(Smi::FromInt(operands_type_)); | |
1010 | 1014 |
1011 __ push(rcx); // Push return address. | 1015 __ push(rcx); // Push return address. |
1012 | 1016 |
1013 // Patch the caller to an appropriate specialized stub and return the | 1017 // Patch the caller to an appropriate specialized stub and return the |
1014 // operation result to the caller of the stub. | 1018 // operation result to the caller of the stub. |
1015 __ TailCallExternalReference( | 1019 __ TailCallExternalReference( |
1016 ExternalReference(IC_Utility(IC::kBinaryOp_Patch), | 1020 ExternalReference(IC_Utility(IC::kBinaryOp_Patch), |
1017 masm->isolate()), | 1021 masm->isolate()), |
1018 5, | 1022 3, |
1019 1); | 1023 1); |
1020 } | 1024 } |
1021 | 1025 |
1022 | 1026 |
1023 void BinaryOpStub::Generate(MacroAssembler* masm) { | 1027 static void BinaryOpStub_GenerateSmiCode( |
1024 // Explicitly allow generation of nested stubs. It is safe here because | |
1025 // generation code does not use any raw pointers. | |
1026 AllowStubCallsScope allow_stub_calls(masm, true); | |
1027 | |
1028 switch (operands_type_) { | |
1029 case BinaryOpIC::UNINITIALIZED: | |
1030 GenerateTypeTransition(masm); | |
1031 break; | |
1032 case BinaryOpIC::SMI: | |
1033 GenerateSmiStub(masm); | |
1034 break; | |
1035 case BinaryOpIC::INT32: | |
1036 UNREACHABLE(); | |
1037 // The int32 case is identical to the Smi case. We avoid creating this | |
1038 // ic state on x64. | |
1039 break; | |
1040 case BinaryOpIC::HEAP_NUMBER: | |
1041 GenerateHeapNumberStub(masm); | |
1042 break; | |
1043 case BinaryOpIC::ODDBALL: | |
1044 GenerateOddballStub(masm); | |
1045 break; | |
1046 case BinaryOpIC::BOTH_STRING: | |
1047 GenerateBothStringStub(masm); | |
1048 break; | |
1049 case BinaryOpIC::STRING: | |
1050 GenerateStringStub(masm); | |
1051 break; | |
1052 case BinaryOpIC::GENERIC: | |
1053 GenerateGeneric(masm); | |
1054 break; | |
1055 default: | |
1056 UNREACHABLE(); | |
1057 } | |
1058 } | |
1059 | |
1060 | |
1061 void BinaryOpStub::PrintName(StringStream* stream) { | |
1062 const char* op_name = Token::Name(op_); | |
1063 const char* overwrite_name; | |
1064 switch (mode_) { | |
1065 case NO_OVERWRITE: overwrite_name = "Alloc"; break; | |
1066 case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break; | |
1067 case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break; | |
1068 default: overwrite_name = "UnknownOverwrite"; break; | |
1069 } | |
1070 stream->Add("BinaryOpStub_%s_%s_%s", | |
1071 op_name, | |
1072 overwrite_name, | |
1073 BinaryOpIC::GetName(operands_type_)); | |
1074 } | |
1075 | |
1076 | |
1077 void BinaryOpStub::GenerateSmiCode( | |
1078 MacroAssembler* masm, | 1028 MacroAssembler* masm, |
1079 Label* slow, | 1029 Label* slow, |
1080 SmiCodeGenerateHeapNumberResults allow_heapnumber_results) { | 1030 BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results, |
| 1031 Token::Value op) { |
1081 | 1032 |
1082 // Arguments to BinaryOpStub are in rdx and rax. | 1033 // Arguments to BinaryOpStub are in rdx and rax. |
1083 const Register left = rdx; | 1034 const Register left = rdx; |
1084 const Register right = rax; | 1035 const Register right = rax; |
1085 | 1036 |
1086 // We only generate heapnumber answers for overflowing calculations | 1037 // We only generate heapnumber answers for overflowing calculations |
1087 // for the four basic arithmetic operations and logical right shift by 0. | 1038 // for the four basic arithmetic operations and logical right shift by 0. |
1088 bool generate_inline_heapnumber_results = | 1039 bool generate_inline_heapnumber_results = |
1089 (allow_heapnumber_results == ALLOW_HEAPNUMBER_RESULTS) && | 1040 (allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS) && |
1090 (op_ == Token::ADD || op_ == Token::SUB || | 1041 (op == Token::ADD || op == Token::SUB || |
1091 op_ == Token::MUL || op_ == Token::DIV || op_ == Token::SHR); | 1042 op == Token::MUL || op == Token::DIV || op == Token::SHR); |
1092 | 1043 |
1093 // Smi check of both operands. If op is BIT_OR, the check is delayed | 1044 // Smi check of both operands. If op is BIT_OR, the check is delayed |
1094 // until after the OR operation. | 1045 // until after the OR operation. |
1095 Label not_smis; | 1046 Label not_smis; |
1096 Label use_fp_on_smis; | 1047 Label use_fp_on_smis; |
1097 Label fail; | 1048 Label fail; |
1098 | 1049 |
1099 if (op_ != Token::BIT_OR) { | 1050 if (op != Token::BIT_OR) { |
1100 Comment smi_check_comment(masm, "-- Smi check arguments"); | 1051 Comment smi_check_comment(masm, "-- Smi check arguments"); |
1101 __ JumpIfNotBothSmi(left, right, ¬_smis); | 1052 __ JumpIfNotBothSmi(left, right, ¬_smis); |
1102 } | 1053 } |
1103 | 1054 |
1104 Label smi_values; | 1055 Label smi_values; |
1105 __ bind(&smi_values); | 1056 __ bind(&smi_values); |
1106 // Perform the operation. | 1057 // Perform the operation. |
1107 Comment perform_smi(masm, "-- Perform smi operation"); | 1058 Comment perform_smi(masm, "-- Perform smi operation"); |
1108 switch (op_) { | 1059 switch (op) { |
1109 case Token::ADD: | 1060 case Token::ADD: |
1110 ASSERT(right.is(rax)); | 1061 ASSERT(right.is(rax)); |
1111 __ SmiAdd(right, right, left, &use_fp_on_smis); // ADD is commutative. | 1062 __ SmiAdd(right, right, left, &use_fp_on_smis); // ADD is commutative. |
1112 break; | 1063 break; |
1113 | 1064 |
1114 case Token::SUB: | 1065 case Token::SUB: |
1115 __ SmiSub(left, left, right, &use_fp_on_smis); | 1066 __ SmiSub(left, left, right, &use_fp_on_smis); |
1116 __ movq(rax, left); | 1067 __ movq(rax, left); |
1117 break; | 1068 break; |
1118 | 1069 |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1170 } | 1121 } |
1171 | 1122 |
1172 // 5. Emit return of result in rax. Some operations have registers pushed. | 1123 // 5. Emit return of result in rax. Some operations have registers pushed. |
1173 __ ret(0); | 1124 __ ret(0); |
1174 | 1125 |
1175 if (use_fp_on_smis.is_linked()) { | 1126 if (use_fp_on_smis.is_linked()) { |
1176 // 6. For some operations emit inline code to perform floating point | 1127 // 6. For some operations emit inline code to perform floating point |
1177 // operations on known smis (e.g., if the result of the operation | 1128 // operations on known smis (e.g., if the result of the operation |
1178 // overflowed the smi range). | 1129 // overflowed the smi range). |
1179 __ bind(&use_fp_on_smis); | 1130 __ bind(&use_fp_on_smis); |
1180 if (op_ == Token::DIV || op_ == Token::MOD) { | 1131 if (op == Token::DIV || op == Token::MOD) { |
1181 // Restore left and right to rdx and rax. | 1132 // Restore left and right to rdx and rax. |
1182 __ movq(rdx, rcx); | 1133 __ movq(rdx, rcx); |
1183 __ movq(rax, rbx); | 1134 __ movq(rax, rbx); |
1184 } | 1135 } |
1185 | 1136 |
1186 if (generate_inline_heapnumber_results) { | 1137 if (generate_inline_heapnumber_results) { |
1187 __ AllocateHeapNumber(rcx, rbx, slow); | 1138 __ AllocateHeapNumber(rcx, rbx, slow); |
1188 Comment perform_float(masm, "-- Perform float operation on smis"); | 1139 Comment perform_float(masm, "-- Perform float operation on smis"); |
1189 if (op_ == Token::SHR) { | 1140 if (op == Token::SHR) { |
1190 __ SmiToInteger32(left, left); | 1141 __ SmiToInteger32(left, left); |
1191 __ cvtqsi2sd(xmm0, left); | 1142 __ cvtqsi2sd(xmm0, left); |
1192 } else { | 1143 } else { |
1193 FloatingPointHelper::LoadSSE2SmiOperands(masm); | 1144 FloatingPointHelper::LoadSSE2SmiOperands(masm); |
1194 switch (op_) { | 1145 switch (op) { |
1195 case Token::ADD: __ addsd(xmm0, xmm1); break; | 1146 case Token::ADD: __ addsd(xmm0, xmm1); break; |
1196 case Token::SUB: __ subsd(xmm0, xmm1); break; | 1147 case Token::SUB: __ subsd(xmm0, xmm1); break; |
1197 case Token::MUL: __ mulsd(xmm0, xmm1); break; | 1148 case Token::MUL: __ mulsd(xmm0, xmm1); break; |
1198 case Token::DIV: __ divsd(xmm0, xmm1); break; | 1149 case Token::DIV: __ divsd(xmm0, xmm1); break; |
1199 default: UNREACHABLE(); | 1150 default: UNREACHABLE(); |
1200 } | 1151 } |
1201 } | 1152 } |
1202 __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0); | 1153 __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0); |
1203 __ movq(rax, rcx); | 1154 __ movq(rax, rcx); |
1204 __ ret(0); | 1155 __ ret(0); |
1205 } else { | 1156 } else { |
1206 __ jmp(&fail); | 1157 __ jmp(&fail); |
1207 } | 1158 } |
1208 } | 1159 } |
1209 | 1160 |
1210 // 7. Non-smi operands reach the end of the code generated by | 1161 // 7. Non-smi operands reach the end of the code generated by |
1211 // GenerateSmiCode, and fall through to subsequent code, | 1162 // GenerateSmiCode, and fall through to subsequent code, |
1212 // with the operands in rdx and rax. | 1163 // with the operands in rdx and rax. |
1213 // But first we check if non-smi values are HeapNumbers holding | 1164 // But first we check if non-smi values are HeapNumbers holding |
1214 // values that could be smi. | 1165 // values that could be smi. |
1215 __ bind(¬_smis); | 1166 __ bind(¬_smis); |
1216 Comment done_comment(masm, "-- Enter non-smi code"); | 1167 Comment done_comment(masm, "-- Enter non-smi code"); |
| 1168 FloatingPointHelper::ConvertUndefined convert_undefined = |
| 1169 FloatingPointHelper::BAILOUT_ON_UNDEFINED; |
| 1170 // This list must be in sync with BinaryOpPatch() behavior in ic.cc. |
| 1171 if (op == Token::BIT_AND || |
| 1172 op == Token::BIT_OR || |
| 1173 op == Token::BIT_XOR || |
| 1174 op == Token::SAR || |
| 1175 op == Token::SHL || |
| 1176 op == Token::SHR) { |
| 1177 convert_undefined = FloatingPointHelper::CONVERT_UNDEFINED_TO_ZERO; |
| 1178 } |
1217 FloatingPointHelper::NumbersToSmis(masm, left, right, rbx, rdi, rcx, | 1179 FloatingPointHelper::NumbersToSmis(masm, left, right, rbx, rdi, rcx, |
1218 &smi_values, &fail); | 1180 &smi_values, &fail, convert_undefined); |
1219 __ jmp(&smi_values); | 1181 __ jmp(&smi_values); |
1220 __ bind(&fail); | 1182 __ bind(&fail); |
1221 } | 1183 } |
1222 | 1184 |
1223 | 1185 |
1224 void BinaryOpStub::GenerateFloatingPointCode(MacroAssembler* masm, | 1186 static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm, |
1225 Label* allocation_failure, | 1187 Label* alloc_failure, |
1226 Label* non_numeric_failure) { | 1188 OverwriteMode mode); |
1227 switch (op_) { | 1189 |
| 1190 |
| 1191 static void BinaryOpStub_GenerateFloatingPointCode(MacroAssembler* masm, |
| 1192 Label* allocation_failure, |
| 1193 Label* non_numeric_failure, |
| 1194 Token::Value op, |
| 1195 OverwriteMode mode) { |
| 1196 switch (op) { |
1228 case Token::ADD: | 1197 case Token::ADD: |
1229 case Token::SUB: | 1198 case Token::SUB: |
1230 case Token::MUL: | 1199 case Token::MUL: |
1231 case Token::DIV: { | 1200 case Token::DIV: { |
1232 FloatingPointHelper::LoadSSE2UnknownOperands(masm, non_numeric_failure); | 1201 FloatingPointHelper::LoadSSE2UnknownOperands(masm, non_numeric_failure); |
1233 | 1202 |
1234 switch (op_) { | 1203 switch (op) { |
1235 case Token::ADD: __ addsd(xmm0, xmm1); break; | 1204 case Token::ADD: __ addsd(xmm0, xmm1); break; |
1236 case Token::SUB: __ subsd(xmm0, xmm1); break; | 1205 case Token::SUB: __ subsd(xmm0, xmm1); break; |
1237 case Token::MUL: __ mulsd(xmm0, xmm1); break; | 1206 case Token::MUL: __ mulsd(xmm0, xmm1); break; |
1238 case Token::DIV: __ divsd(xmm0, xmm1); break; | 1207 case Token::DIV: __ divsd(xmm0, xmm1); break; |
1239 default: UNREACHABLE(); | 1208 default: UNREACHABLE(); |
1240 } | 1209 } |
1241 GenerateHeapResultAllocation(masm, allocation_failure); | 1210 BinaryOpStub_GenerateHeapResultAllocation( |
| 1211 masm, allocation_failure, mode); |
1242 __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0); | 1212 __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0); |
1243 __ ret(0); | 1213 __ ret(0); |
1244 break; | 1214 break; |
1245 } | 1215 } |
1246 case Token::MOD: { | 1216 case Token::MOD: { |
1247 // For MOD we jump to the allocation_failure label, to call runtime. | 1217 // For MOD we jump to the allocation_failure label, to call runtime. |
1248 __ jmp(allocation_failure); | 1218 __ jmp(allocation_failure); |
1249 break; | 1219 break; |
1250 } | 1220 } |
1251 case Token::BIT_OR: | 1221 case Token::BIT_OR: |
1252 case Token::BIT_AND: | 1222 case Token::BIT_AND: |
1253 case Token::BIT_XOR: | 1223 case Token::BIT_XOR: |
1254 case Token::SAR: | 1224 case Token::SAR: |
1255 case Token::SHL: | 1225 case Token::SHL: |
1256 case Token::SHR: { | 1226 case Token::SHR: { |
1257 Label non_smi_shr_result; | 1227 Label non_smi_shr_result; |
1258 Register heap_number_map = r9; | 1228 Register heap_number_map = r9; |
1259 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); | 1229 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); |
1260 FloatingPointHelper::LoadAsIntegers(masm, non_numeric_failure, | 1230 FloatingPointHelper::LoadAsIntegers(masm, non_numeric_failure, |
1261 heap_number_map); | 1231 heap_number_map); |
1262 switch (op_) { | 1232 switch (op) { |
1263 case Token::BIT_OR: __ orl(rax, rcx); break; | 1233 case Token::BIT_OR: __ orl(rax, rcx); break; |
1264 case Token::BIT_AND: __ andl(rax, rcx); break; | 1234 case Token::BIT_AND: __ andl(rax, rcx); break; |
1265 case Token::BIT_XOR: __ xorl(rax, rcx); break; | 1235 case Token::BIT_XOR: __ xorl(rax, rcx); break; |
1266 case Token::SAR: __ sarl_cl(rax); break; | 1236 case Token::SAR: __ sarl_cl(rax); break; |
1267 case Token::SHL: __ shll_cl(rax); break; | 1237 case Token::SHL: __ shll_cl(rax); break; |
1268 case Token::SHR: { | 1238 case Token::SHR: { |
1269 __ shrl_cl(rax); | 1239 __ shrl_cl(rax); |
1270 // Check if result is negative. This can only happen for a shift | 1240 // Check if result is negative. This can only happen for a shift |
1271 // by zero. | 1241 // by zero. |
1272 __ testl(rax, rax); | 1242 __ testl(rax, rax); |
1273 __ j(negative, &non_smi_shr_result); | 1243 __ j(negative, &non_smi_shr_result); |
1274 break; | 1244 break; |
1275 } | 1245 } |
1276 default: UNREACHABLE(); | 1246 default: UNREACHABLE(); |
1277 } | 1247 } |
1278 STATIC_ASSERT(kSmiValueSize == 32); | 1248 STATIC_ASSERT(kSmiValueSize == 32); |
1279 // Tag smi result and return. | 1249 // Tag smi result and return. |
1280 __ Integer32ToSmi(rax, rax); | 1250 __ Integer32ToSmi(rax, rax); |
1281 __ Ret(); | 1251 __ Ret(); |
1282 | 1252 |
1283 // Logical shift right can produce an unsigned int32 that is not | 1253 // Logical shift right can produce an unsigned int32 that is not |
1284 // an int32, and so is not in the smi range. Allocate a heap number | 1254 // an int32, and so is not in the smi range. Allocate a heap number |
1285 // in that case. | 1255 // in that case. |
1286 if (op_ == Token::SHR) { | 1256 if (op == Token::SHR) { |
1287 __ bind(&non_smi_shr_result); | 1257 __ bind(&non_smi_shr_result); |
1288 Label allocation_failed; | 1258 Label allocation_failed; |
1289 __ movl(rbx, rax); // rbx holds result value (uint32 value as int64). | 1259 __ movl(rbx, rax); // rbx holds result value (uint32 value as int64). |
1290 // Allocate heap number in new space. | 1260 // Allocate heap number in new space. |
1291 // Not using AllocateHeapNumber macro in order to reuse | 1261 // Not using AllocateHeapNumber macro in order to reuse |
1292 // already loaded heap_number_map. | 1262 // already loaded heap_number_map. |
1293 __ AllocateInNewSpace(HeapNumber::kSize, | 1263 __ AllocateInNewSpace(HeapNumber::kSize, |
1294 rax, | 1264 rax, |
1295 rdx, | 1265 rdx, |
1296 no_reg, | 1266 no_reg, |
(...skipping 16 matching lines...) Expand all Loading... |
1313 __ Integer32ToSmi(rdx, rbx); | 1283 __ Integer32ToSmi(rdx, rbx); |
1314 __ jmp(allocation_failure); | 1284 __ jmp(allocation_failure); |
1315 } | 1285 } |
1316 break; | 1286 break; |
1317 } | 1287 } |
1318 default: UNREACHABLE(); break; | 1288 default: UNREACHABLE(); break; |
1319 } | 1289 } |
1320 // No fall-through from this generated code. | 1290 // No fall-through from this generated code. |
1321 if (FLAG_debug_code) { | 1291 if (FLAG_debug_code) { |
1322 __ Abort("Unexpected fall-through in " | 1292 __ Abort("Unexpected fall-through in " |
1323 "BinaryStub::GenerateFloatingPointCode."); | 1293 "BinaryStub_GenerateFloatingPointCode."); |
1324 } | 1294 } |
1325 } | 1295 } |
1326 | 1296 |
1327 | 1297 |
1328 void BinaryOpStub::GenerateStringAddCode(MacroAssembler* masm) { | 1298 void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) { |
1329 ASSERT(op_ == Token::ADD); | 1299 ASSERT(op_ == Token::ADD); |
1330 Label left_not_string, call_runtime; | 1300 Label left_not_string, call_runtime; |
1331 | 1301 |
1332 // Registers containing left and right operands respectively. | 1302 // Registers containing left and right operands respectively. |
1333 Register left = rdx; | 1303 Register left = rdx; |
1334 Register right = rax; | 1304 Register right = rax; |
1335 | 1305 |
1336 // Test if left operand is a string. | 1306 // Test if left operand is a string. |
1337 __ JumpIfSmi(left, &left_not_string, Label::kNear); | 1307 __ JumpIfSmi(left, &left_not_string, Label::kNear); |
1338 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx); | 1308 __ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx); |
(...skipping 10 matching lines...) Expand all Loading... |
1349 | 1319 |
1350 StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB); | 1320 StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB); |
1351 GenerateRegisterArgsPush(masm); | 1321 GenerateRegisterArgsPush(masm); |
1352 __ TailCallStub(&string_add_right_stub); | 1322 __ TailCallStub(&string_add_right_stub); |
1353 | 1323 |
1354 // Neither argument is a string. | 1324 // Neither argument is a string. |
1355 __ bind(&call_runtime); | 1325 __ bind(&call_runtime); |
1356 } | 1326 } |
1357 | 1327 |
1358 | 1328 |
1359 void BinaryOpStub::GenerateCallRuntimeCode(MacroAssembler* masm) { | |
1360 GenerateRegisterArgsPush(masm); | |
1361 switch (op_) { | |
1362 case Token::ADD: | |
1363 __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION); | |
1364 break; | |
1365 case Token::SUB: | |
1366 __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION); | |
1367 break; | |
1368 case Token::MUL: | |
1369 __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION); | |
1370 break; | |
1371 case Token::DIV: | |
1372 __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION); | |
1373 break; | |
1374 case Token::MOD: | |
1375 __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION); | |
1376 break; | |
1377 case Token::BIT_OR: | |
1378 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION); | |
1379 break; | |
1380 case Token::BIT_AND: | |
1381 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION); | |
1382 break; | |
1383 case Token::BIT_XOR: | |
1384 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION); | |
1385 break; | |
1386 case Token::SAR: | |
1387 __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION); | |
1388 break; | |
1389 case Token::SHL: | |
1390 __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION); | |
1391 break; | |
1392 case Token::SHR: | |
1393 __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION); | |
1394 break; | |
1395 default: | |
1396 UNREACHABLE(); | |
1397 } | |
1398 } | |
1399 | |
1400 | |
1401 void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) { | 1329 void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) { |
1402 Label call_runtime; | 1330 Label call_runtime; |
1403 if (result_type_ == BinaryOpIC::UNINITIALIZED || | 1331 if (result_type_ == BinaryOpIC::UNINITIALIZED || |
1404 result_type_ == BinaryOpIC::SMI) { | 1332 result_type_ == BinaryOpIC::SMI) { |
1405 // Only allow smi results. | 1333 // Only allow smi results. |
1406 GenerateSmiCode(masm, NULL, NO_HEAPNUMBER_RESULTS); | 1334 BinaryOpStub_GenerateSmiCode(masm, NULL, NO_HEAPNUMBER_RESULTS, op_); |
1407 } else { | 1335 } else { |
1408 // Allow heap number result and don't make a transition if a heap number | 1336 // Allow heap number result and don't make a transition if a heap number |
1409 // cannot be allocated. | 1337 // cannot be allocated. |
1410 GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS); | 1338 BinaryOpStub_GenerateSmiCode( |
| 1339 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_); |
1411 } | 1340 } |
1412 | 1341 |
1413 // Code falls through if the result is not returned as either a smi or heap | 1342 // Code falls through if the result is not returned as either a smi or heap |
1414 // number. | 1343 // number. |
1415 GenerateTypeTransition(masm); | 1344 GenerateTypeTransition(masm); |
1416 | 1345 |
1417 if (call_runtime.is_linked()) { | 1346 if (call_runtime.is_linked()) { |
1418 __ bind(&call_runtime); | 1347 __ bind(&call_runtime); |
1419 GenerateCallRuntimeCode(masm); | 1348 GenerateRegisterArgsPush(masm); |
| 1349 GenerateCallRuntime(masm); |
1420 } | 1350 } |
1421 } | 1351 } |
1422 | 1352 |
1423 | 1353 |
1424 void BinaryOpStub::GenerateStringStub(MacroAssembler* masm) { | 1354 void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) { |
1425 ASSERT(operands_type_ == BinaryOpIC::STRING); | 1355 // The int32 case is identical to the Smi case. We avoid creating this |
1426 ASSERT(op_ == Token::ADD); | 1356 // ic state on x64. |
1427 GenerateStringAddCode(masm); | 1357 UNREACHABLE(); |
1428 // Try to add arguments as strings, otherwise, transition to the generic | |
1429 // BinaryOpIC type. | |
1430 GenerateTypeTransition(masm); | |
1431 } | 1358 } |
1432 | 1359 |
1433 | 1360 |
1434 void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) { | 1361 void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) { |
1435 Label call_runtime; | 1362 Label call_runtime; |
1436 ASSERT(operands_type_ == BinaryOpIC::BOTH_STRING); | 1363 ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING); |
1437 ASSERT(op_ == Token::ADD); | 1364 ASSERT(op_ == Token::ADD); |
1438 // If both arguments are strings, call the string add stub. | 1365 // If both arguments are strings, call the string add stub. |
1439 // Otherwise, do a transition. | 1366 // Otherwise, do a transition. |
1440 | 1367 |
1441 // Registers containing left and right operands respectively. | 1368 // Registers containing left and right operands respectively. |
1442 Register left = rdx; | 1369 Register left = rdx; |
1443 Register right = rax; | 1370 Register right = rax; |
1444 | 1371 |
1445 // Test if left operand is a string. | 1372 // Test if left operand is a string. |
1446 __ JumpIfSmi(left, &call_runtime); | 1373 __ JumpIfSmi(left, &call_runtime); |
(...skipping 13 matching lines...) Expand all Loading... |
1460 GenerateTypeTransition(masm); | 1387 GenerateTypeTransition(masm); |
1461 } | 1388 } |
1462 | 1389 |
1463 | 1390 |
1464 void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) { | 1391 void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) { |
1465 Label call_runtime; | 1392 Label call_runtime; |
1466 | 1393 |
1467 if (op_ == Token::ADD) { | 1394 if (op_ == Token::ADD) { |
1468 // Handle string addition here, because it is the only operation | 1395 // Handle string addition here, because it is the only operation |
1469 // that does not do a ToNumber conversion on the operands. | 1396 // that does not do a ToNumber conversion on the operands. |
1470 GenerateStringAddCode(masm); | 1397 GenerateAddStrings(masm); |
1471 } | 1398 } |
1472 | 1399 |
1473 // Convert oddball arguments to numbers. | 1400 // Convert oddball arguments to numbers. |
1474 Label check, done; | 1401 Label check, done; |
1475 __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex); | 1402 __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex); |
1476 __ j(not_equal, &check, Label::kNear); | 1403 __ j(not_equal, &check, Label::kNear); |
1477 if (Token::IsBitOp(op_)) { | 1404 if (Token::IsBitOp(op_)) { |
1478 __ xor_(rdx, rdx); | 1405 __ xor_(rdx, rdx); |
1479 } else { | 1406 } else { |
1480 __ LoadRoot(rdx, Heap::kNanValueRootIndex); | 1407 __ LoadRoot(rdx, Heap::kNanValueRootIndex); |
1481 } | 1408 } |
1482 __ jmp(&done, Label::kNear); | 1409 __ jmp(&done, Label::kNear); |
1483 __ bind(&check); | 1410 __ bind(&check); |
1484 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); | 1411 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); |
1485 __ j(not_equal, &done, Label::kNear); | 1412 __ j(not_equal, &done, Label::kNear); |
1486 if (Token::IsBitOp(op_)) { | 1413 if (Token::IsBitOp(op_)) { |
1487 __ xor_(rax, rax); | 1414 __ xor_(rax, rax); |
1488 } else { | 1415 } else { |
1489 __ LoadRoot(rax, Heap::kNanValueRootIndex); | 1416 __ LoadRoot(rax, Heap::kNanValueRootIndex); |
1490 } | 1417 } |
1491 __ bind(&done); | 1418 __ bind(&done); |
1492 | 1419 |
1493 GenerateHeapNumberStub(masm); | 1420 GenerateHeapNumberStub(masm); |
1494 } | 1421 } |
1495 | 1422 |
1496 | 1423 |
| 1424 static void BinaryOpStub_CheckSmiInput(MacroAssembler* masm, |
| 1425 Register input, |
| 1426 Label* fail) { |
| 1427 Label ok; |
| 1428 __ JumpIfSmi(input, &ok, Label::kNear); |
| 1429 Register heap_number_map = r8; |
| 1430 Register scratch1 = r9; |
| 1431 Register scratch2 = r10; |
| 1432 // HeapNumbers containing 32bit integer values are also allowed. |
| 1433 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); |
| 1434 __ cmpq(FieldOperand(input, HeapObject::kMapOffset), heap_number_map); |
| 1435 __ j(not_equal, fail); |
| 1436 __ movsd(xmm0, FieldOperand(input, HeapNumber::kValueOffset)); |
| 1437 // Convert, convert back, and compare the two doubles' bits. |
| 1438 __ cvttsd2siq(scratch2, xmm0); |
| 1439 __ cvtlsi2sd(xmm1, scratch2); |
| 1440 __ movq(scratch1, xmm0); |
| 1441 __ movq(scratch2, xmm1); |
| 1442 __ cmpq(scratch1, scratch2); |
| 1443 __ j(not_equal, fail); |
| 1444 __ bind(&ok); |
| 1445 } |
| 1446 |
| 1447 |
1497 void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) { | 1448 void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) { |
1498 Label gc_required, not_number; | 1449 Label gc_required, not_number; |
1499 GenerateFloatingPointCode(masm, &gc_required, ¬_number); | 1450 |
| 1451 // It could be that only SMIs have been seen at either the left |
| 1452 // or the right operand. For precise type feedback, patch the IC |
| 1453 // again if this changes. |
| 1454 if (left_type_ == BinaryOpIC::SMI) { |
| 1455 BinaryOpStub_CheckSmiInput(masm, rdx, ¬_number); |
| 1456 } |
| 1457 if (right_type_ == BinaryOpIC::SMI) { |
| 1458 BinaryOpStub_CheckSmiInput(masm, rax, ¬_number); |
| 1459 } |
| 1460 |
| 1461 BinaryOpStub_GenerateFloatingPointCode( |
| 1462 masm, &gc_required, ¬_number, op_, mode_); |
1500 | 1463 |
1501 __ bind(¬_number); | 1464 __ bind(¬_number); |
1502 GenerateTypeTransition(masm); | 1465 GenerateTypeTransition(masm); |
1503 | 1466 |
1504 __ bind(&gc_required); | 1467 __ bind(&gc_required); |
1505 GenerateCallRuntimeCode(masm); | 1468 GenerateRegisterArgsPush(masm); |
| 1469 GenerateCallRuntime(masm); |
1506 } | 1470 } |
1507 | 1471 |
1508 | 1472 |
1509 void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) { | 1473 void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) { |
1510 Label call_runtime, call_string_add_or_runtime; | 1474 Label call_runtime, call_string_add_or_runtime; |
1511 | 1475 |
1512 GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS); | 1476 BinaryOpStub_GenerateSmiCode( |
| 1477 masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_); |
1513 | 1478 |
1514 GenerateFloatingPointCode(masm, &call_runtime, &call_string_add_or_runtime); | 1479 BinaryOpStub_GenerateFloatingPointCode( |
| 1480 masm, &call_runtime, &call_string_add_or_runtime, op_, mode_); |
1515 | 1481 |
1516 __ bind(&call_string_add_or_runtime); | 1482 __ bind(&call_string_add_or_runtime); |
1517 if (op_ == Token::ADD) { | 1483 if (op_ == Token::ADD) { |
1518 GenerateStringAddCode(masm); | 1484 GenerateAddStrings(masm); |
1519 } | 1485 } |
1520 | 1486 |
1521 __ bind(&call_runtime); | 1487 __ bind(&call_runtime); |
1522 GenerateCallRuntimeCode(masm); | 1488 GenerateRegisterArgsPush(masm); |
| 1489 GenerateCallRuntime(masm); |
1523 } | 1490 } |
1524 | 1491 |
1525 | 1492 |
1526 void BinaryOpStub::GenerateHeapResultAllocation(MacroAssembler* masm, | 1493 static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm, |
1527 Label* alloc_failure) { | 1494 Label* alloc_failure, |
| 1495 OverwriteMode mode) { |
1528 Label skip_allocation; | 1496 Label skip_allocation; |
1529 OverwriteMode mode = mode_; | |
1530 switch (mode) { | 1497 switch (mode) { |
1531 case OVERWRITE_LEFT: { | 1498 case OVERWRITE_LEFT: { |
1532 // If the argument in rdx is already an object, we skip the | 1499 // If the argument in rdx is already an object, we skip the |
1533 // allocation of a heap number. | 1500 // allocation of a heap number. |
1534 __ JumpIfNotSmi(rdx, &skip_allocation); | 1501 __ JumpIfNotSmi(rdx, &skip_allocation); |
1535 // Allocate a heap number for the result. Keep eax and edx intact | 1502 // Allocate a heap number for the result. Keep eax and edx intact |
1536 // for the possible runtime call. | 1503 // for the possible runtime call. |
1537 __ AllocateHeapNumber(rbx, rcx, alloc_failure); | 1504 __ AllocateHeapNumber(rbx, rcx, alloc_failure); |
1538 // Now rdx can be overwritten losing one of the arguments as we are | 1505 // Now rdx can be overwritten losing one of the arguments as we are |
1539 // now done and will not need it any more. | 1506 // now done and will not need it any more. |
(...skipping 475 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2015 } | 1982 } |
2016 | 1983 |
2017 | 1984 |
2018 void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm, | 1985 void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm, |
2019 Register first, | 1986 Register first, |
2020 Register second, | 1987 Register second, |
2021 Register scratch1, | 1988 Register scratch1, |
2022 Register scratch2, | 1989 Register scratch2, |
2023 Register scratch3, | 1990 Register scratch3, |
2024 Label* on_success, | 1991 Label* on_success, |
2025 Label* on_not_smis) { | 1992 Label* on_not_smis, |
| 1993 ConvertUndefined convert_undefined) { |
2026 Register heap_number_map = scratch3; | 1994 Register heap_number_map = scratch3; |
2027 Register smi_result = scratch1; | 1995 Register smi_result = scratch1; |
2028 Label done; | 1996 Label done, maybe_undefined_first, maybe_undefined_second, first_done; |
2029 | 1997 |
2030 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); | 1998 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); |
2031 | 1999 |
2032 Label first_smi; | 2000 Label first_smi; |
2033 __ JumpIfSmi(first, &first_smi, Label::kNear); | 2001 __ JumpIfSmi(first, &first_smi, Label::kNear); |
2034 __ cmpq(FieldOperand(first, HeapObject::kMapOffset), heap_number_map); | 2002 __ cmpq(FieldOperand(first, HeapObject::kMapOffset), heap_number_map); |
2035 __ j(not_equal, on_not_smis); | 2003 __ j(not_equal, |
| 2004 (convert_undefined == CONVERT_UNDEFINED_TO_ZERO) |
| 2005 ? &maybe_undefined_first |
| 2006 : on_not_smis); |
2036 // Convert HeapNumber to smi if possible. | 2007 // Convert HeapNumber to smi if possible. |
2037 __ movsd(xmm0, FieldOperand(first, HeapNumber::kValueOffset)); | 2008 __ movsd(xmm0, FieldOperand(first, HeapNumber::kValueOffset)); |
2038 __ movq(scratch2, xmm0); | 2009 __ movq(scratch2, xmm0); |
2039 __ cvttsd2siq(smi_result, xmm0); | 2010 __ cvttsd2siq(smi_result, xmm0); |
2040 // Check if conversion was successful by converting back and | 2011 // Check if conversion was successful by converting back and |
2041 // comparing to the original double's bits. | 2012 // comparing to the original double's bits. |
2042 __ cvtlsi2sd(xmm1, smi_result); | 2013 __ cvtlsi2sd(xmm1, smi_result); |
2043 __ movq(kScratchRegister, xmm1); | 2014 __ movq(kScratchRegister, xmm1); |
2044 __ cmpq(scratch2, kScratchRegister); | 2015 __ cmpq(scratch2, kScratchRegister); |
2045 __ j(not_equal, on_not_smis); | 2016 __ j(not_equal, on_not_smis); |
2046 __ Integer32ToSmi(first, smi_result); | 2017 __ Integer32ToSmi(first, smi_result); |
2047 | 2018 |
| 2019 __ bind(&first_done); |
2048 __ JumpIfSmi(second, (on_success != NULL) ? on_success : &done); | 2020 __ JumpIfSmi(second, (on_success != NULL) ? on_success : &done); |
2049 __ bind(&first_smi); | 2021 __ bind(&first_smi); |
2050 __ AssertNotSmi(second); | 2022 __ AssertNotSmi(second); |
2051 __ cmpq(FieldOperand(second, HeapObject::kMapOffset), heap_number_map); | 2023 __ cmpq(FieldOperand(second, HeapObject::kMapOffset), heap_number_map); |
2052 __ j(not_equal, on_not_smis); | 2024 __ j(not_equal, |
| 2025 (convert_undefined == CONVERT_UNDEFINED_TO_ZERO) |
| 2026 ? &maybe_undefined_second |
| 2027 : on_not_smis); |
2053 // Convert second to smi, if possible. | 2028 // Convert second to smi, if possible. |
2054 __ movsd(xmm0, FieldOperand(second, HeapNumber::kValueOffset)); | 2029 __ movsd(xmm0, FieldOperand(second, HeapNumber::kValueOffset)); |
2055 __ movq(scratch2, xmm0); | 2030 __ movq(scratch2, xmm0); |
2056 __ cvttsd2siq(smi_result, xmm0); | 2031 __ cvttsd2siq(smi_result, xmm0); |
2057 __ cvtlsi2sd(xmm1, smi_result); | 2032 __ cvtlsi2sd(xmm1, smi_result); |
2058 __ movq(kScratchRegister, xmm1); | 2033 __ movq(kScratchRegister, xmm1); |
2059 __ cmpq(scratch2, kScratchRegister); | 2034 __ cmpq(scratch2, kScratchRegister); |
2060 __ j(not_equal, on_not_smis); | 2035 __ j(not_equal, on_not_smis); |
2061 __ Integer32ToSmi(second, smi_result); | 2036 __ Integer32ToSmi(second, smi_result); |
2062 if (on_success != NULL) { | 2037 if (on_success != NULL) { |
2063 __ jmp(on_success); | 2038 __ jmp(on_success); |
2064 } else { | 2039 } else { |
2065 __ bind(&done); | 2040 __ jmp(&done); |
2066 } | 2041 } |
| 2042 |
| 2043 __ bind(&maybe_undefined_first); |
| 2044 __ CompareRoot(first, Heap::kUndefinedValueRootIndex); |
| 2045 __ j(not_equal, on_not_smis); |
| 2046 __ xor_(first, first); |
| 2047 __ jmp(&first_done); |
| 2048 |
| 2049 __ bind(&maybe_undefined_second); |
| 2050 __ CompareRoot(second, Heap::kUndefinedValueRootIndex); |
| 2051 __ j(not_equal, on_not_smis); |
| 2052 __ xor_(second, second); |
| 2053 if (on_success != NULL) { |
| 2054 __ jmp(on_success); |
| 2055 } |
| 2056 // Else: fall through. |
| 2057 |
| 2058 __ bind(&done); |
2067 } | 2059 } |
2068 | 2060 |
2069 | 2061 |
2070 void MathPowStub::Generate(MacroAssembler* masm) { | 2062 void MathPowStub::Generate(MacroAssembler* masm) { |
2071 // Choose register conforming to calling convention (when bailing out). | 2063 // Choose register conforming to calling convention (when bailing out). |
2072 #ifdef _WIN64 | 2064 #ifdef _WIN64 |
2073 const Register exponent = rdx; | 2065 const Register exponent = rdx; |
2074 #else | 2066 #else |
2075 const Register exponent = rdi; | 2067 const Register exponent = rdi; |
2076 #endif | 2068 #endif |
(...skipping 1294 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3371 | 3363 |
3372 | 3364 |
3373 static int NegativeComparisonResult(Condition cc) { | 3365 static int NegativeComparisonResult(Condition cc) { |
3374 ASSERT(cc != equal); | 3366 ASSERT(cc != equal); |
3375 ASSERT((cc == less) || (cc == less_equal) | 3367 ASSERT((cc == less) || (cc == less_equal) |
3376 || (cc == greater) || (cc == greater_equal)); | 3368 || (cc == greater) || (cc == greater_equal)); |
3377 return (cc == greater || cc == greater_equal) ? LESS : GREATER; | 3369 return (cc == greater || cc == greater_equal) ? LESS : GREATER; |
3378 } | 3370 } |
3379 | 3371 |
3380 | 3372 |
3381 void CompareStub::Generate(MacroAssembler* masm) { | 3373 static void CheckInputType(MacroAssembler* masm, |
3382 ASSERT(lhs_.is(no_reg) && rhs_.is(no_reg)); | 3374 Register input, |
| 3375 CompareIC::State expected, |
| 3376 Label* fail) { |
| 3377 Label ok; |
| 3378 if (expected == CompareIC::SMI) { |
| 3379 __ JumpIfNotSmi(input, fail); |
| 3380 } else if (expected == CompareIC::HEAP_NUMBER) { |
| 3381 __ JumpIfSmi(input, &ok); |
| 3382 __ CompareMap(input, masm->isolate()->factory()->heap_number_map(), NULL); |
| 3383 __ j(not_equal, fail); |
| 3384 } |
| 3385 // We could be strict about symbol/string here, but as long as |
| 3386 // hydrogen doesn't care, the stub doesn't have to care either. |
| 3387 __ bind(&ok); |
| 3388 } |
3383 | 3389 |
| 3390 |
| 3391 static void BranchIfNonSymbol(MacroAssembler* masm, |
| 3392 Label* label, |
| 3393 Register object, |
| 3394 Register scratch) { |
| 3395 __ JumpIfSmi(object, label); |
| 3396 __ movq(scratch, FieldOperand(object, HeapObject::kMapOffset)); |
| 3397 __ movzxbq(scratch, |
| 3398 FieldOperand(scratch, Map::kInstanceTypeOffset)); |
| 3399 // Ensure that no non-strings have the symbol bit set. |
| 3400 STATIC_ASSERT(LAST_TYPE < kNotStringTag + kIsSymbolMask); |
| 3401 STATIC_ASSERT(kSymbolTag != 0); |
| 3402 __ testb(scratch, Immediate(kIsSymbolMask)); |
| 3403 __ j(zero, label); |
| 3404 } |
| 3405 |
| 3406 |
| 3407 void ICCompareStub::GenerateGeneric(MacroAssembler* masm) { |
3384 Label check_unequal_objects, done; | 3408 Label check_unequal_objects, done; |
| 3409 Condition cc = GetCondition(); |
3385 Factory* factory = masm->isolate()->factory(); | 3410 Factory* factory = masm->isolate()->factory(); |
3386 | 3411 |
3387 // Compare two smis if required. | 3412 Label miss; |
3388 if (include_smi_compare_) { | 3413 CheckInputType(masm, rdx, left_, &miss); |
3389 Label non_smi, smi_done; | 3414 CheckInputType(masm, rax, right_, &miss); |
3390 __ JumpIfNotBothSmi(rax, rdx, &non_smi); | 3415 |
3391 __ subq(rdx, rax); | 3416 // Compare two smis. |
3392 __ j(no_overflow, &smi_done); | 3417 Label non_smi, smi_done; |
3393 __ not_(rdx); // Correct sign in case of overflow. rdx cannot be 0 here. | 3418 __ JumpIfNotBothSmi(rax, rdx, &non_smi); |
3394 __ bind(&smi_done); | 3419 __ subq(rdx, rax); |
3395 __ movq(rax, rdx); | 3420 __ j(no_overflow, &smi_done); |
3396 __ ret(0); | 3421 __ not_(rdx); // Correct sign in case of overflow. rdx cannot be 0 here. |
3397 __ bind(&non_smi); | 3422 __ bind(&smi_done); |
3398 } else if (FLAG_debug_code) { | 3423 __ movq(rax, rdx); |
3399 Label ok; | 3424 __ ret(0); |
3400 __ JumpIfNotSmi(rdx, &ok); | 3425 __ bind(&non_smi); |
3401 __ JumpIfNotSmi(rax, &ok); | |
3402 __ Abort("CompareStub: smi operands"); | |
3403 __ bind(&ok); | |
3404 } | |
3405 | 3426 |
3406 // The compare stub returns a positive, negative, or zero 64-bit integer | 3427 // The compare stub returns a positive, negative, or zero 64-bit integer |
3407 // value in rax, corresponding to result of comparing the two inputs. | 3428 // value in rax, corresponding to result of comparing the two inputs. |
3408 // NOTICE! This code is only reached after a smi-fast-case check, so | 3429 // NOTICE! This code is only reached after a smi-fast-case check, so |
3409 // it is certain that at least one operand isn't a smi. | 3430 // it is certain that at least one operand isn't a smi. |
3410 | 3431 |
3411 // Two identical objects are equal unless they are both NaN or undefined. | 3432 // Two identical objects are equal unless they are both NaN or undefined. |
3412 { | 3433 { |
3413 Label not_identical; | 3434 Label not_identical; |
3414 __ cmpq(rax, rdx); | 3435 __ cmpq(rax, rdx); |
3415 __ j(not_equal, ¬_identical, Label::kNear); | 3436 __ j(not_equal, ¬_identical, Label::kNear); |
3416 | 3437 |
3417 if (cc_ != equal) { | 3438 if (cc != equal) { |
3418 // Check for undefined. undefined OP undefined is false even though | 3439 // Check for undefined. undefined OP undefined is false even though |
3419 // undefined == undefined. | 3440 // undefined == undefined. |
3420 Label check_for_nan; | 3441 Label check_for_nan; |
3421 __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex); | 3442 __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex); |
3422 __ j(not_equal, &check_for_nan, Label::kNear); | 3443 __ j(not_equal, &check_for_nan, Label::kNear); |
3423 __ Set(rax, NegativeComparisonResult(cc_)); | 3444 __ Set(rax, NegativeComparisonResult(cc)); |
3424 __ ret(0); | 3445 __ ret(0); |
3425 __ bind(&check_for_nan); | 3446 __ bind(&check_for_nan); |
3426 } | 3447 } |
3427 | 3448 |
3428 // Test for NaN. Sadly, we can't just compare to FACTORY->nan_value(), | 3449 // Test for NaN. Sadly, we can't just compare to FACTORY->nan_value(), |
3429 // so we do the second best thing - test it ourselves. | 3450 // so we do the second best thing - test it ourselves. |
3430 // Note: if cc_ != equal, never_nan_nan_ is not used. | 3451 Label heap_number; |
3431 // We cannot set rax to EQUAL until just before return because | 3452 // If it's not a heap number, then return equal for (in)equality operator. |
3432 // rax must be unchanged on jump to not_identical. | 3453 __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), |
3433 if (never_nan_nan_ && (cc_ == equal)) { | 3454 factory->heap_number_map()); |
3434 __ Set(rax, EQUAL); | 3455 __ j(equal, &heap_number, Label::kNear); |
3435 __ ret(0); | 3456 if (cc != equal) { |
3436 } else { | 3457 // Call runtime on identical objects. Otherwise return equal. |
3437 Label heap_number; | 3458 __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx); |
3438 // If it's not a heap number, then return equal for (in)equality operator. | 3459 __ j(above_equal, ¬_identical, Label::kNear); |
3439 __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), | 3460 } |
3440 factory->heap_number_map()); | 3461 __ Set(rax, EQUAL); |
3441 __ j(equal, &heap_number, Label::kNear); | 3462 __ ret(0); |
3442 if (cc_ != equal) { | |
3443 // Call runtime on identical objects. Otherwise return equal. | |
3444 __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx); | |
3445 __ j(above_equal, ¬_identical, Label::kNear); | |
3446 } | |
3447 __ Set(rax, EQUAL); | |
3448 __ ret(0); | |
3449 | 3463 |
3450 __ bind(&heap_number); | 3464 __ bind(&heap_number); |
3451 // It is a heap number, so return equal if it's not NaN. | 3465 // It is a heap number, so return equal if it's not NaN. |
3452 // For NaN, return 1 for every condition except greater and | 3466 // For NaN, return 1 for every condition except greater and |
3453 // greater-equal. Return -1 for them, so the comparison yields | 3467 // greater-equal. Return -1 for them, so the comparison yields |
3454 // false for all conditions except not-equal. | 3468 // false for all conditions except not-equal. |
3455 __ Set(rax, EQUAL); | 3469 __ Set(rax, EQUAL); |
3456 __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset)); | 3470 __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset)); |
3457 __ ucomisd(xmm0, xmm0); | 3471 __ ucomisd(xmm0, xmm0); |
3458 __ setcc(parity_even, rax); | 3472 __ setcc(parity_even, rax); |
3459 // rax is 0 for equal non-NaN heapnumbers, 1 for NaNs. | 3473 // rax is 0 for equal non-NaN heapnumbers, 1 for NaNs. |
3460 if (cc_ == greater_equal || cc_ == greater) { | 3474 if (cc == greater_equal || cc == greater) { |
3461 __ neg(rax); | 3475 __ neg(rax); |
3462 } | |
3463 __ ret(0); | |
3464 } | 3476 } |
| 3477 __ ret(0); |
3465 | 3478 |
3466 __ bind(¬_identical); | 3479 __ bind(¬_identical); |
3467 } | 3480 } |
3468 | 3481 |
3469 if (cc_ == equal) { // Both strict and non-strict. | 3482 if (cc == equal) { // Both strict and non-strict. |
3470 Label slow; // Fallthrough label. | 3483 Label slow; // Fallthrough label. |
3471 | 3484 |
3472 // If we're doing a strict equality comparison, we don't have to do | 3485 // If we're doing a strict equality comparison, we don't have to do |
3473 // type conversion, so we generate code to do fast comparison for objects | 3486 // type conversion, so we generate code to do fast comparison for objects |
3474 // and oddballs. Non-smi numbers and strings still go through the usual | 3487 // and oddballs. Non-smi numbers and strings still go through the usual |
3475 // slow-case code. | 3488 // slow-case code. |
3476 if (strict_) { | 3489 if (strict()) { |
3477 // If either is a Smi (we know that not both are), then they can only | 3490 // If either is a Smi (we know that not both are), then they can only |
3478 // be equal if the other is a HeapNumber. If so, use the slow case. | 3491 // be equal if the other is a HeapNumber. If so, use the slow case. |
3479 { | 3492 { |
3480 Label not_smis; | 3493 Label not_smis; |
3481 __ SelectNonSmi(rbx, rax, rdx, ¬_smis); | 3494 __ SelectNonSmi(rbx, rax, rdx, ¬_smis); |
3482 | 3495 |
3483 // Check if the non-smi operand is a heap number. | 3496 // Check if the non-smi operand is a heap number. |
3484 __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), | 3497 __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), |
3485 factory->heap_number_map()); | 3498 factory->heap_number_map()); |
3486 // If heap number, handle it in the slow case. | 3499 // If heap number, handle it in the slow case. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3518 // Check for oddballs: true, false, null, undefined. | 3531 // Check for oddballs: true, false, null, undefined. |
3519 __ CmpInstanceType(rcx, ODDBALL_TYPE); | 3532 __ CmpInstanceType(rcx, ODDBALL_TYPE); |
3520 __ j(equal, &return_not_equal); | 3533 __ j(equal, &return_not_equal); |
3521 | 3534 |
3522 // Fall through to the general case. | 3535 // Fall through to the general case. |
3523 } | 3536 } |
3524 __ bind(&slow); | 3537 __ bind(&slow); |
3525 } | 3538 } |
3526 | 3539 |
3527 // Generate the number comparison code. | 3540 // Generate the number comparison code. |
3528 if (include_number_compare_) { | 3541 Label non_number_comparison; |
3529 Label non_number_comparison; | 3542 Label unordered; |
3530 Label unordered; | 3543 FloatingPointHelper::LoadSSE2UnknownOperands(masm, &non_number_comparison); |
3531 FloatingPointHelper::LoadSSE2UnknownOperands(masm, &non_number_comparison); | 3544 __ xorl(rax, rax); |
3532 __ xorl(rax, rax); | 3545 __ xorl(rcx, rcx); |
3533 __ xorl(rcx, rcx); | 3546 __ ucomisd(xmm0, xmm1); |
3534 __ ucomisd(xmm0, xmm1); | |
3535 | 3547 |
3536 // Don't base result on EFLAGS when a NaN is involved. | 3548 // Don't base result on EFLAGS when a NaN is involved. |
3537 __ j(parity_even, &unordered, Label::kNear); | 3549 __ j(parity_even, &unordered, Label::kNear); |
3538 // Return a result of -1, 0, or 1, based on EFLAGS. | 3550 // Return a result of -1, 0, or 1, based on EFLAGS. |
3539 __ setcc(above, rax); | 3551 __ setcc(above, rax); |
3540 __ setcc(below, rcx); | 3552 __ setcc(below, rcx); |
3541 __ subq(rax, rcx); | 3553 __ subq(rax, rcx); |
3542 __ ret(0); | 3554 __ ret(0); |
3543 | 3555 |
3544 // If one of the numbers was NaN, then the result is always false. | 3556 // If one of the numbers was NaN, then the result is always false. |
3545 // The cc is never not-equal. | 3557 // The cc is never not-equal. |
3546 __ bind(&unordered); | 3558 __ bind(&unordered); |
3547 ASSERT(cc_ != not_equal); | 3559 ASSERT(cc != not_equal); |
3548 if (cc_ == less || cc_ == less_equal) { | 3560 if (cc == less || cc == less_equal) { |
3549 __ Set(rax, 1); | 3561 __ Set(rax, 1); |
3550 } else { | 3562 } else { |
3551 __ Set(rax, -1); | 3563 __ Set(rax, -1); |
3552 } | 3564 } |
3553 __ ret(0); | 3565 __ ret(0); |
3554 | 3566 |
3555 // The number comparison code did not provide a valid result. | 3567 // The number comparison code did not provide a valid result. |
3556 __ bind(&non_number_comparison); | 3568 __ bind(&non_number_comparison); |
3557 } | |
3558 | 3569 |
3559 // Fast negative check for symbol-to-symbol equality. | 3570 // Fast negative check for symbol-to-symbol equality. |
3560 Label check_for_strings; | 3571 Label check_for_strings; |
3561 if (cc_ == equal) { | 3572 if (cc == equal) { |
3562 BranchIfNonSymbol(masm, &check_for_strings, rax, kScratchRegister); | 3573 BranchIfNonSymbol(masm, &check_for_strings, rax, kScratchRegister); |
3563 BranchIfNonSymbol(masm, &check_for_strings, rdx, kScratchRegister); | 3574 BranchIfNonSymbol(masm, &check_for_strings, rdx, kScratchRegister); |
3564 | 3575 |
3565 // We've already checked for object identity, so if both operands | 3576 // We've already checked for object identity, so if both operands |
3566 // are symbols they aren't equal. Register eax (not rax) already holds a | 3577 // are symbols they aren't equal. Register eax (not rax) already holds a |
3567 // non-zero value, which indicates not equal, so just return. | 3578 // non-zero value, which indicates not equal, so just return. |
3568 __ ret(0); | 3579 __ ret(0); |
3569 } | 3580 } |
3570 | 3581 |
3571 __ bind(&check_for_strings); | 3582 __ bind(&check_for_strings); |
3572 | 3583 |
3573 __ JumpIfNotBothSequentialAsciiStrings( | 3584 __ JumpIfNotBothSequentialAsciiStrings( |
3574 rdx, rax, rcx, rbx, &check_unequal_objects); | 3585 rdx, rax, rcx, rbx, &check_unequal_objects); |
3575 | 3586 |
3576 // Inline comparison of ASCII strings. | 3587 // Inline comparison of ASCII strings. |
3577 if (cc_ == equal) { | 3588 if (cc == equal) { |
3578 StringCompareStub::GenerateFlatAsciiStringEquals(masm, | 3589 StringCompareStub::GenerateFlatAsciiStringEquals(masm, |
3579 rdx, | 3590 rdx, |
3580 rax, | 3591 rax, |
3581 rcx, | 3592 rcx, |
3582 rbx); | 3593 rbx); |
3583 } else { | 3594 } else { |
3584 StringCompareStub::GenerateCompareFlatAsciiStrings(masm, | 3595 StringCompareStub::GenerateCompareFlatAsciiStrings(masm, |
3585 rdx, | 3596 rdx, |
3586 rax, | 3597 rax, |
3587 rcx, | 3598 rcx, |
3588 rbx, | 3599 rbx, |
3589 rdi, | 3600 rdi, |
3590 r8); | 3601 r8); |
3591 } | 3602 } |
3592 | 3603 |
3593 #ifdef DEBUG | 3604 #ifdef DEBUG |
3594 __ Abort("Unexpected fall-through from string comparison"); | 3605 __ Abort("Unexpected fall-through from string comparison"); |
3595 #endif | 3606 #endif |
3596 | 3607 |
3597 __ bind(&check_unequal_objects); | 3608 __ bind(&check_unequal_objects); |
3598 if (cc_ == equal && !strict_) { | 3609 if (cc == equal && !strict()) { |
3599 // Not strict equality. Objects are unequal if | 3610 // Not strict equality. Objects are unequal if |
3600 // they are both JSObjects and not undetectable, | 3611 // they are both JSObjects and not undetectable, |
3601 // and their pointers are different. | 3612 // and their pointers are different. |
3602 Label not_both_objects, return_unequal; | 3613 Label not_both_objects, return_unequal; |
3603 // At most one is a smi, so we can test for smi by adding the two. | 3614 // At most one is a smi, so we can test for smi by adding the two. |
3604 // A smi plus a heap object has the low bit set, a heap object plus | 3615 // A smi plus a heap object has the low bit set, a heap object plus |
3605 // a heap object has the low bit clear. | 3616 // a heap object has the low bit clear. |
3606 STATIC_ASSERT(kSmiTag == 0); | 3617 STATIC_ASSERT(kSmiTag == 0); |
3607 STATIC_ASSERT(kSmiTagMask == 1); | 3618 STATIC_ASSERT(kSmiTagMask == 1); |
3608 __ lea(rcx, Operand(rax, rdx, times_1, 0)); | 3619 __ lea(rcx, Operand(rax, rdx, times_1, 0)); |
(...skipping 19 matching lines...) Expand all Loading... |
3628 __ bind(¬_both_objects); | 3639 __ bind(¬_both_objects); |
3629 } | 3640 } |
3630 | 3641 |
3631 // Push arguments below the return address to prepare jump to builtin. | 3642 // Push arguments below the return address to prepare jump to builtin. |
3632 __ pop(rcx); | 3643 __ pop(rcx); |
3633 __ push(rdx); | 3644 __ push(rdx); |
3634 __ push(rax); | 3645 __ push(rax); |
3635 | 3646 |
3636 // Figure out which native to call and setup the arguments. | 3647 // Figure out which native to call and setup the arguments. |
3637 Builtins::JavaScript builtin; | 3648 Builtins::JavaScript builtin; |
3638 if (cc_ == equal) { | 3649 if (cc == equal) { |
3639 builtin = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS; | 3650 builtin = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS; |
3640 } else { | 3651 } else { |
3641 builtin = Builtins::COMPARE; | 3652 builtin = Builtins::COMPARE; |
3642 __ Push(Smi::FromInt(NegativeComparisonResult(cc_))); | 3653 __ Push(Smi::FromInt(NegativeComparisonResult(cc))); |
3643 } | 3654 } |
3644 | 3655 |
3645 // Restore return address on the stack. | 3656 // Restore return address on the stack. |
3646 __ push(rcx); | 3657 __ push(rcx); |
3647 | 3658 |
3648 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater) | 3659 // Call the native; it returns -1 (less), 0 (equal), or 1 (greater) |
3649 // tagged as a small integer. | 3660 // tagged as a small integer. |
3650 __ InvokeBuiltin(builtin, JUMP_FUNCTION); | 3661 __ InvokeBuiltin(builtin, JUMP_FUNCTION); |
| 3662 |
| 3663 __ bind(&miss); |
| 3664 GenerateMiss(masm); |
3651 } | 3665 } |
3652 | 3666 |
3653 | 3667 |
3654 void CompareStub::BranchIfNonSymbol(MacroAssembler* masm, | |
3655 Label* label, | |
3656 Register object, | |
3657 Register scratch) { | |
3658 __ JumpIfSmi(object, label); | |
3659 __ movq(scratch, FieldOperand(object, HeapObject::kMapOffset)); | |
3660 __ movzxbq(scratch, | |
3661 FieldOperand(scratch, Map::kInstanceTypeOffset)); | |
3662 // Ensure that no non-strings have the symbol bit set. | |
3663 STATIC_ASSERT(LAST_TYPE < kNotStringTag + kIsSymbolMask); | |
3664 STATIC_ASSERT(kSymbolTag != 0); | |
3665 __ testb(scratch, Immediate(kIsSymbolMask)); | |
3666 __ j(zero, label); | |
3667 } | |
3668 | |
3669 | |
3670 void StackCheckStub::Generate(MacroAssembler* masm) { | 3668 void StackCheckStub::Generate(MacroAssembler* masm) { |
3671 __ TailCallRuntime(Runtime::kStackGuard, 0, 1); | 3669 __ TailCallRuntime(Runtime::kStackGuard, 0, 1); |
3672 } | 3670 } |
3673 | 3671 |
3674 | 3672 |
3675 void InterruptStub::Generate(MacroAssembler* masm) { | 3673 void InterruptStub::Generate(MacroAssembler* masm) { |
3676 __ TailCallRuntime(Runtime::kInterrupt, 0, 1); | 3674 __ TailCallRuntime(Runtime::kInterrupt, 0, 1); |
3677 } | 3675 } |
3678 | 3676 |
3679 | 3677 |
(...skipping 734 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4414 } | 4412 } |
4415 | 4413 |
4416 | 4414 |
4417 // Passing arguments in registers is not supported. | 4415 // Passing arguments in registers is not supported. |
4418 Register InstanceofStub::left() { return no_reg; } | 4416 Register InstanceofStub::left() { return no_reg; } |
4419 | 4417 |
4420 | 4418 |
4421 Register InstanceofStub::right() { return no_reg; } | 4419 Register InstanceofStub::right() { return no_reg; } |
4422 | 4420 |
4423 | 4421 |
4424 int CompareStub::MinorKey() { | |
4425 // Encode the three parameters in a unique 16 bit value. To avoid duplicate | |
4426 // stubs the never NaN NaN condition is only taken into account if the | |
4427 // condition is equals. | |
4428 ASSERT(static_cast<unsigned>(cc_) < (1 << 12)); | |
4429 ASSERT(lhs_.is(no_reg) && rhs_.is(no_reg)); | |
4430 return ConditionField::encode(static_cast<unsigned>(cc_)) | |
4431 | RegisterField::encode(false) // lhs_ and rhs_ are not used | |
4432 | StrictField::encode(strict_) | |
4433 | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false) | |
4434 | IncludeNumberCompareField::encode(include_number_compare_) | |
4435 | IncludeSmiCompareField::encode(include_smi_compare_); | |
4436 } | |
4437 | |
4438 | |
4439 // Unfortunately you have to run without snapshots to see most of these | |
4440 // names in the profile since most compare stubs end up in the snapshot. | |
4441 void CompareStub::PrintName(StringStream* stream) { | |
4442 ASSERT(lhs_.is(no_reg) && rhs_.is(no_reg)); | |
4443 const char* cc_name; | |
4444 switch (cc_) { | |
4445 case less: cc_name = "LT"; break; | |
4446 case greater: cc_name = "GT"; break; | |
4447 case less_equal: cc_name = "LE"; break; | |
4448 case greater_equal: cc_name = "GE"; break; | |
4449 case equal: cc_name = "EQ"; break; | |
4450 case not_equal: cc_name = "NE"; break; | |
4451 default: cc_name = "UnknownCondition"; break; | |
4452 } | |
4453 bool is_equality = cc_ == equal || cc_ == not_equal; | |
4454 stream->Add("CompareStub_%s", cc_name); | |
4455 if (strict_ && is_equality) stream->Add("_STRICT"); | |
4456 if (never_nan_nan_ && is_equality) stream->Add("_NO_NAN"); | |
4457 if (!include_number_compare_) stream->Add("_NO_NUMBER"); | |
4458 if (!include_smi_compare_) stream->Add("_NO_SMI"); | |
4459 } | |
4460 | |
4461 | |
4462 // ------------------------------------------------------------------------- | 4422 // ------------------------------------------------------------------------- |
4463 // StringCharCodeAtGenerator | 4423 // StringCharCodeAtGenerator |
4464 | 4424 |
4465 void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { | 4425 void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { |
4466 Label flat_string; | 4426 Label flat_string; |
4467 Label ascii_string; | 4427 Label ascii_string; |
4468 Label got_char_code; | 4428 Label got_char_code; |
4469 Label sliced_string; | 4429 Label sliced_string; |
4470 | 4430 |
4471 // If the receiver is a smi trigger the non-string case. | 4431 // If the receiver is a smi trigger the non-string case. |
(...skipping 1089 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5561 GenerateCompareFlatAsciiStrings(masm, rdx, rax, rcx, rbx, rdi, r8); | 5521 GenerateCompareFlatAsciiStrings(masm, rdx, rax, rcx, rbx, rdi, r8); |
5562 | 5522 |
5563 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater) | 5523 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater) |
5564 // tagged as a small integer. | 5524 // tagged as a small integer. |
5565 __ bind(&runtime); | 5525 __ bind(&runtime); |
5566 __ TailCallRuntime(Runtime::kStringCompare, 2, 1); | 5526 __ TailCallRuntime(Runtime::kStringCompare, 2, 1); |
5567 } | 5527 } |
5568 | 5528 |
5569 | 5529 |
5570 void ICCompareStub::GenerateSmis(MacroAssembler* masm) { | 5530 void ICCompareStub::GenerateSmis(MacroAssembler* masm) { |
5571 ASSERT(state_ == CompareIC::SMIS); | 5531 ASSERT(state_ == CompareIC::SMI); |
5572 Label miss; | 5532 Label miss; |
5573 __ JumpIfNotBothSmi(rdx, rax, &miss, Label::kNear); | 5533 __ JumpIfNotBothSmi(rdx, rax, &miss, Label::kNear); |
5574 | 5534 |
5575 if (GetCondition() == equal) { | 5535 if (GetCondition() == equal) { |
5576 // For equality we do not care about the sign of the result. | 5536 // For equality we do not care about the sign of the result. |
5577 __ subq(rax, rdx); | 5537 __ subq(rax, rdx); |
5578 } else { | 5538 } else { |
5579 Label done; | 5539 Label done; |
5580 __ subq(rdx, rax); | 5540 __ subq(rdx, rax); |
5581 __ j(no_overflow, &done, Label::kNear); | 5541 __ j(no_overflow, &done, Label::kNear); |
5582 // Correct sign of result in case of overflow. | 5542 // Correct sign of result in case of overflow. |
5583 __ SmiNot(rdx, rdx); | 5543 __ SmiNot(rdx, rdx); |
5584 __ bind(&done); | 5544 __ bind(&done); |
5585 __ movq(rax, rdx); | 5545 __ movq(rax, rdx); |
5586 } | 5546 } |
5587 __ ret(0); | 5547 __ ret(0); |
5588 | 5548 |
5589 __ bind(&miss); | 5549 __ bind(&miss); |
5590 GenerateMiss(masm); | 5550 GenerateMiss(masm); |
5591 } | 5551 } |
5592 | 5552 |
5593 | 5553 |
5594 void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) { | 5554 void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) { |
5595 ASSERT(state_ == CompareIC::HEAP_NUMBERS); | 5555 ASSERT(state_ == CompareIC::HEAP_NUMBER); |
5596 | 5556 |
5597 Label generic_stub; | 5557 Label generic_stub; |
5598 Label unordered, maybe_undefined1, maybe_undefined2; | 5558 Label unordered, maybe_undefined1, maybe_undefined2; |
5599 Label miss; | 5559 Label miss; |
5600 Condition either_smi = masm->CheckEitherSmi(rax, rdx); | |
5601 __ j(either_smi, &generic_stub, Label::kNear); | |
5602 | 5560 |
5603 __ CmpObjectType(rax, HEAP_NUMBER_TYPE, rcx); | 5561 if (left_ == CompareIC::SMI) { |
| 5562 __ JumpIfNotSmi(rdx, &miss); |
| 5563 } |
| 5564 if (right_ == CompareIC::SMI) { |
| 5565 __ JumpIfNotSmi(rax, &miss); |
| 5566 } |
| 5567 |
| 5568 // Load left and right operand. |
| 5569 Label done, left, left_smi, right_smi; |
| 5570 __ JumpIfSmi(rax, &right_smi, Label::kNear); |
| 5571 __ CompareMap(rax, masm->isolate()->factory()->heap_number_map(), NULL); |
5604 __ j(not_equal, &maybe_undefined1, Label::kNear); | 5572 __ j(not_equal, &maybe_undefined1, Label::kNear); |
5605 __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx); | 5573 __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset)); |
| 5574 __ jmp(&left, Label::kNear); |
| 5575 __ bind(&right_smi); |
| 5576 __ SmiToInteger32(rcx, rax); // Can't clobber rax yet. |
| 5577 __ cvtlsi2sd(xmm1, rcx); |
| 5578 |
| 5579 __ bind(&left); |
| 5580 __ JumpIfSmi(rdx, &left_smi, Label::kNear); |
| 5581 __ CompareMap(rdx, masm->isolate()->factory()->heap_number_map(), NULL); |
5606 __ j(not_equal, &maybe_undefined2, Label::kNear); | 5582 __ j(not_equal, &maybe_undefined2, Label::kNear); |
| 5583 __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset)); |
| 5584 __ jmp(&done); |
| 5585 __ bind(&left_smi); |
| 5586 __ SmiToInteger32(rcx, rdx); // Can't clobber rdx yet. |
| 5587 __ cvtlsi2sd(xmm0, rcx); |
5607 | 5588 |
5608 // Load left and right operand | 5589 __ bind(&done); |
5609 __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset)); | |
5610 __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset)); | |
5611 | |
5612 // Compare operands | 5590 // Compare operands |
5613 __ ucomisd(xmm0, xmm1); | 5591 __ ucomisd(xmm0, xmm1); |
5614 | 5592 |
5615 // Don't base result on EFLAGS when a NaN is involved. | 5593 // Don't base result on EFLAGS when a NaN is involved. |
5616 __ j(parity_even, &unordered, Label::kNear); | 5594 __ j(parity_even, &unordered, Label::kNear); |
5617 | 5595 |
5618 // Return a result of -1, 0, or 1, based on EFLAGS. | 5596 // Return a result of -1, 0, or 1, based on EFLAGS. |
5619 // Performing mov, because xor would destroy the flag register. | 5597 // Performing mov, because xor would destroy the flag register. |
5620 __ movl(rax, Immediate(0)); | 5598 __ movl(rax, Immediate(0)); |
5621 __ movl(rcx, Immediate(0)); | 5599 __ movl(rcx, Immediate(0)); |
5622 __ setcc(above, rax); // Add one to zero if carry clear and not equal. | 5600 __ setcc(above, rax); // Add one to zero if carry clear and not equal. |
5623 __ sbbq(rax, rcx); // Subtract one if below (aka. carry set). | 5601 __ sbbq(rax, rcx); // Subtract one if below (aka. carry set). |
5624 __ ret(0); | 5602 __ ret(0); |
5625 | 5603 |
5626 __ bind(&unordered); | 5604 __ bind(&unordered); |
5627 CompareStub stub(GetCondition(), strict(), NO_COMPARE_FLAGS); | |
5628 __ bind(&generic_stub); | 5605 __ bind(&generic_stub); |
| 5606 ICCompareStub stub(op_, CompareIC::GENERIC, CompareIC::GENERIC, |
| 5607 CompareIC::GENERIC); |
5629 __ jmp(stub.GetCode(), RelocInfo::CODE_TARGET); | 5608 __ jmp(stub.GetCode(), RelocInfo::CODE_TARGET); |
5630 | 5609 |
5631 __ bind(&maybe_undefined1); | 5610 __ bind(&maybe_undefined1); |
5632 if (Token::IsOrderedRelationalCompareOp(op_)) { | 5611 if (Token::IsOrderedRelationalCompareOp(op_)) { |
5633 __ Cmp(rax, masm->isolate()->factory()->undefined_value()); | 5612 __ Cmp(rax, masm->isolate()->factory()->undefined_value()); |
5634 __ j(not_equal, &miss); | 5613 __ j(not_equal, &miss); |
| 5614 __ JumpIfSmi(rdx, &unordered); |
5635 __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx); | 5615 __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx); |
5636 __ j(not_equal, &maybe_undefined2, Label::kNear); | 5616 __ j(not_equal, &maybe_undefined2, Label::kNear); |
5637 __ jmp(&unordered); | 5617 __ jmp(&unordered); |
5638 } | 5618 } |
5639 | 5619 |
5640 __ bind(&maybe_undefined2); | 5620 __ bind(&maybe_undefined2); |
5641 if (Token::IsOrderedRelationalCompareOp(op_)) { | 5621 if (Token::IsOrderedRelationalCompareOp(op_)) { |
5642 __ Cmp(rdx, masm->isolate()->factory()->undefined_value()); | 5622 __ Cmp(rdx, masm->isolate()->factory()->undefined_value()); |
5643 __ j(equal, &unordered); | 5623 __ j(equal, &unordered); |
5644 } | 5624 } |
5645 | 5625 |
5646 __ bind(&miss); | 5626 __ bind(&miss); |
5647 GenerateMiss(masm); | 5627 GenerateMiss(masm); |
5648 } | 5628 } |
5649 | 5629 |
5650 | 5630 |
5651 void ICCompareStub::GenerateSymbols(MacroAssembler* masm) { | 5631 void ICCompareStub::GenerateSymbols(MacroAssembler* masm) { |
5652 ASSERT(state_ == CompareIC::SYMBOLS); | 5632 ASSERT(state_ == CompareIC::SYMBOL); |
5653 ASSERT(GetCondition() == equal); | 5633 ASSERT(GetCondition() == equal); |
5654 | 5634 |
5655 // Registers containing left and right operands respectively. | 5635 // Registers containing left and right operands respectively. |
5656 Register left = rdx; | 5636 Register left = rdx; |
5657 Register right = rax; | 5637 Register right = rax; |
5658 Register tmp1 = rcx; | 5638 Register tmp1 = rcx; |
5659 Register tmp2 = rbx; | 5639 Register tmp2 = rbx; |
5660 | 5640 |
5661 // Check that both operands are heap objects. | 5641 // Check that both operands are heap objects. |
5662 Label miss; | 5642 Label miss; |
(...skipping 22 matching lines...) Expand all Loading... |
5685 __ Move(rax, Smi::FromInt(EQUAL)); | 5665 __ Move(rax, Smi::FromInt(EQUAL)); |
5686 __ bind(&done); | 5666 __ bind(&done); |
5687 __ ret(0); | 5667 __ ret(0); |
5688 | 5668 |
5689 __ bind(&miss); | 5669 __ bind(&miss); |
5690 GenerateMiss(masm); | 5670 GenerateMiss(masm); |
5691 } | 5671 } |
5692 | 5672 |
5693 | 5673 |
5694 void ICCompareStub::GenerateStrings(MacroAssembler* masm) { | 5674 void ICCompareStub::GenerateStrings(MacroAssembler* masm) { |
5695 ASSERT(state_ == CompareIC::STRINGS); | 5675 ASSERT(state_ == CompareIC::STRING); |
5696 Label miss; | 5676 Label miss; |
5697 | 5677 |
5698 bool equality = Token::IsEqualityOp(op_); | 5678 bool equality = Token::IsEqualityOp(op_); |
5699 | 5679 |
5700 // Registers containing left and right operands respectively. | 5680 // Registers containing left and right operands respectively. |
5701 Register left = rdx; | 5681 Register left = rdx; |
5702 Register right = rax; | 5682 Register right = rax; |
5703 Register tmp1 = rcx; | 5683 Register tmp1 = rcx; |
5704 Register tmp2 = rbx; | 5684 Register tmp2 = rbx; |
5705 Register tmp3 = rdi; | 5685 Register tmp3 = rdi; |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5771 } else { | 5751 } else { |
5772 __ TailCallRuntime(Runtime::kStringCompare, 2, 1); | 5752 __ TailCallRuntime(Runtime::kStringCompare, 2, 1); |
5773 } | 5753 } |
5774 | 5754 |
5775 __ bind(&miss); | 5755 __ bind(&miss); |
5776 GenerateMiss(masm); | 5756 GenerateMiss(masm); |
5777 } | 5757 } |
5778 | 5758 |
5779 | 5759 |
5780 void ICCompareStub::GenerateObjects(MacroAssembler* masm) { | 5760 void ICCompareStub::GenerateObjects(MacroAssembler* masm) { |
5781 ASSERT(state_ == CompareIC::OBJECTS); | 5761 ASSERT(state_ == CompareIC::OBJECT); |
5782 Label miss; | 5762 Label miss; |
5783 Condition either_smi = masm->CheckEitherSmi(rdx, rax); | 5763 Condition either_smi = masm->CheckEitherSmi(rdx, rax); |
5784 __ j(either_smi, &miss, Label::kNear); | 5764 __ j(either_smi, &miss, Label::kNear); |
5785 | 5765 |
5786 __ CmpObjectType(rax, JS_OBJECT_TYPE, rcx); | 5766 __ CmpObjectType(rax, JS_OBJECT_TYPE, rcx); |
5787 __ j(not_equal, &miss, Label::kNear); | 5767 __ j(not_equal, &miss, Label::kNear); |
5788 __ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx); | 5768 __ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx); |
5789 __ j(not_equal, &miss, Label::kNear); | 5769 __ j(not_equal, &miss, Label::kNear); |
5790 | 5770 |
5791 ASSERT(GetCondition() == equal); | 5771 ASSERT(GetCondition() == equal); |
(...skipping 707 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6499 #endif | 6479 #endif |
6500 | 6480 |
6501 __ Ret(); | 6481 __ Ret(); |
6502 } | 6482 } |
6503 | 6483 |
6504 #undef __ | 6484 #undef __ |
6505 | 6485 |
6506 } } // namespace v8::internal | 6486 } } // namespace v8::internal |
6507 | 6487 |
6508 #endif // V8_TARGET_ARCH_X64 | 6488 #endif // V8_TARGET_ARCH_X64 |
OLD | NEW |