| 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 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 59 descriptor->deoptimization_handler_ = NULL; | 59 descriptor->deoptimization_handler_ = NULL; |
| 60 } | 60 } |
| 61 | 61 |
| 62 | 62 |
| 63 void NumberToStringStub::InitializeInterfaceDescriptor( | 63 void NumberToStringStub::InitializeInterfaceDescriptor( |
| 64 Isolate* isolate, | 64 Isolate* isolate, |
| 65 CodeStubInterfaceDescriptor* descriptor) { | 65 CodeStubInterfaceDescriptor* descriptor) { |
| 66 static Register registers[] = { a0 }; | 66 static Register registers[] = { a0 }; |
| 67 descriptor->register_param_count_ = 1; | 67 descriptor->register_param_count_ = 1; |
| 68 descriptor->register_params_ = registers; | 68 descriptor->register_params_ = registers; |
| 69 descriptor->deoptimization_handler_ = NULL; | 69 descriptor->deoptimization_handler_ = |
| 70 Runtime::FunctionForId(Runtime::kNumberToString)->entry; |
| 70 } | 71 } |
| 71 | 72 |
| 72 | 73 |
| 73 void FastCloneShallowArrayStub::InitializeInterfaceDescriptor( | 74 void FastCloneShallowArrayStub::InitializeInterfaceDescriptor( |
| 74 Isolate* isolate, | 75 Isolate* isolate, |
| 75 CodeStubInterfaceDescriptor* descriptor) { | 76 CodeStubInterfaceDescriptor* descriptor) { |
| 76 static Register registers[] = { a3, a2, a1 }; | 77 static Register registers[] = { a3, a2, a1 }; |
| 77 descriptor->register_param_count_ = 3; | 78 descriptor->register_param_count_ = 3; |
| 78 descriptor->register_params_ = registers; | 79 descriptor->register_params_ = registers; |
| 79 descriptor->deoptimization_handler_ = | 80 descriptor->deoptimization_handler_ = |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 CodeStubInterfaceDescriptor* descriptor, | 175 CodeStubInterfaceDescriptor* descriptor, |
| 175 int constant_stack_parameter_count) { | 176 int constant_stack_parameter_count) { |
| 176 // register state | 177 // register state |
| 177 // a0 -- number of arguments | 178 // a0 -- number of arguments |
| 178 // a1 -- function | 179 // a1 -- function |
| 179 // a2 -- type info cell with elements kind | 180 // a2 -- type info cell with elements kind |
| 180 static Register registers[] = { a1, a2 }; | 181 static Register registers[] = { a1, a2 }; |
| 181 descriptor->register_param_count_ = 2; | 182 descriptor->register_param_count_ = 2; |
| 182 if (constant_stack_parameter_count != 0) { | 183 if (constant_stack_parameter_count != 0) { |
| 183 // stack param count needs (constructor pointer, and single argument) | 184 // stack param count needs (constructor pointer, and single argument) |
| 184 descriptor->stack_parameter_count_ = &a0; | 185 descriptor->stack_parameter_count_ = a0; |
| 185 } | 186 } |
| 186 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count; | 187 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count; |
| 187 descriptor->register_params_ = registers; | 188 descriptor->register_params_ = registers; |
| 188 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE; | 189 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE; |
| 189 descriptor->deoptimization_handler_ = | 190 descriptor->deoptimization_handler_ = |
| 190 Runtime::FunctionForId(Runtime::kArrayConstructor)->entry; | 191 Runtime::FunctionForId(Runtime::kArrayConstructor)->entry; |
| 191 } | 192 } |
| 192 | 193 |
| 193 | 194 |
| 194 static void InitializeInternalArrayConstructorDescriptor( | 195 static void InitializeInternalArrayConstructorDescriptor( |
| 195 Isolate* isolate, | 196 Isolate* isolate, |
| 196 CodeStubInterfaceDescriptor* descriptor, | 197 CodeStubInterfaceDescriptor* descriptor, |
| 197 int constant_stack_parameter_count) { | 198 int constant_stack_parameter_count) { |
| 198 // register state | 199 // register state |
| 199 // a0 -- number of arguments | 200 // a0 -- number of arguments |
| 200 // a1 -- constructor function | 201 // a1 -- constructor function |
| 201 static Register registers[] = { a1 }; | 202 static Register registers[] = { a1 }; |
| 202 descriptor->register_param_count_ = 1; | 203 descriptor->register_param_count_ = 1; |
| 203 | 204 |
| 204 if (constant_stack_parameter_count != 0) { | 205 if (constant_stack_parameter_count != 0) { |
| 205 // Stack param count needs (constructor pointer, and single argument). | 206 // Stack param count needs (constructor pointer, and single argument). |
| 206 descriptor->stack_parameter_count_ = &a0; | 207 descriptor->stack_parameter_count_ = a0; |
| 207 } | 208 } |
| 208 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count; | 209 descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count; |
| 209 descriptor->register_params_ = registers; | 210 descriptor->register_params_ = registers; |
| 210 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE; | 211 descriptor->function_mode_ = JS_FUNCTION_STUB_MODE; |
| 211 descriptor->deoptimization_handler_ = | 212 descriptor->deoptimization_handler_ = |
| 212 Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry; | 213 Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry; |
| 213 } | 214 } |
| 214 | 215 |
| 215 | 216 |
| 216 void ArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor( | 217 void ArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor( |
| (...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 539 int double_offset = offset(); | 540 int double_offset = offset(); |
| 540 // Account for saved regs if input is sp. | 541 // Account for saved regs if input is sp. |
| 541 if (input_reg.is(sp)) double_offset += 3 * kPointerSize; | 542 if (input_reg.is(sp)) double_offset += 3 * kPointerSize; |
| 542 | 543 |
| 543 Register scratch = | 544 Register scratch = |
| 544 GetRegisterThatIsNotOneOf(input_reg, result_reg); | 545 GetRegisterThatIsNotOneOf(input_reg, result_reg); |
| 545 Register scratch2 = | 546 Register scratch2 = |
| 546 GetRegisterThatIsNotOneOf(input_reg, result_reg, scratch); | 547 GetRegisterThatIsNotOneOf(input_reg, result_reg, scratch); |
| 547 Register scratch3 = | 548 Register scratch3 = |
| 548 GetRegisterThatIsNotOneOf(input_reg, result_reg, scratch, scratch2); | 549 GetRegisterThatIsNotOneOf(input_reg, result_reg, scratch, scratch2); |
| 549 DoubleRegister double_scratch = kLithiumScratchDouble.low(); | 550 DoubleRegister double_scratch = kLithiumScratchDouble; |
| 550 DoubleRegister double_input = f12; | |
| 551 | 551 |
| 552 __ Push(scratch, scratch2, scratch3); | 552 __ Push(scratch, scratch2, scratch3); |
| 553 | 553 |
| 554 __ ldc1(double_input, MemOperand(input_reg, double_offset)); | 554 if (!skip_fastpath()) { |
| 555 // Load double input. |
| 556 __ ldc1(double_scratch, MemOperand(input_reg, double_offset)); |
| 555 | 557 |
| 556 if (!skip_fastpath()) { | |
| 557 // Clear cumulative exception flags and save the FCSR. | 558 // Clear cumulative exception flags and save the FCSR. |
| 558 __ cfc1(scratch2, FCSR); | 559 __ cfc1(scratch2, FCSR); |
| 559 __ ctc1(zero_reg, FCSR); | 560 __ ctc1(zero_reg, FCSR); |
| 561 |
| 560 // Try a conversion to a signed integer. | 562 // Try a conversion to a signed integer. |
| 561 __ trunc_w_d(double_scratch, double_input); | 563 __ Trunc_w_d(double_scratch, double_scratch); |
| 564 // Move the converted value into the result register. |
| 562 __ mfc1(result_reg, double_scratch); | 565 __ mfc1(result_reg, double_scratch); |
| 566 |
| 563 // Retrieve and restore the FCSR. | 567 // Retrieve and restore the FCSR. |
| 564 __ cfc1(scratch, FCSR); | 568 __ cfc1(scratch, FCSR); |
| 565 __ ctc1(scratch2, FCSR); | 569 __ ctc1(scratch2, FCSR); |
| 570 |
| 566 // Check for overflow and NaNs. | 571 // Check for overflow and NaNs. |
| 567 __ And( | 572 __ And( |
| 568 scratch, scratch, | 573 scratch, scratch, |
| 569 kFCSROverflowFlagMask | kFCSRUnderflowFlagMask | 574 kFCSROverflowFlagMask | kFCSRUnderflowFlagMask |
| 570 | kFCSRInvalidOpFlagMask); | 575 | kFCSRInvalidOpFlagMask); |
| 571 // If we had no exceptions we are done. | 576 // If we had no exceptions we are done. |
| 572 __ Branch(&done, eq, scratch, Operand(zero_reg)); | 577 __ Branch(&done, eq, scratch, Operand(zero_reg)); |
| 573 } | 578 } |
| 574 | 579 |
| 575 // Load the double value and perform a manual truncation. | 580 // Load the double value and perform a manual truncation. |
| 576 Register input_high = scratch2; | 581 Register input_high = scratch2; |
| 577 Register input_low = scratch3; | 582 Register input_low = scratch3; |
| 578 __ Move(input_low, input_high, double_input); | 583 |
| 584 __ lw(input_low, MemOperand(input_reg, double_offset)); |
| 585 __ lw(input_high, MemOperand(input_reg, double_offset + kIntSize)); |
| 579 | 586 |
| 580 Label normal_exponent, restore_sign; | 587 Label normal_exponent, restore_sign; |
| 581 // Extract the biased exponent in result. | 588 // Extract the biased exponent in result. |
| 582 __ Ext(result_reg, | 589 __ Ext(result_reg, |
| 583 input_high, | 590 input_high, |
| 584 HeapNumber::kExponentShift, | 591 HeapNumber::kExponentShift, |
| 585 HeapNumber::kExponentBits); | 592 HeapNumber::kExponentBits); |
| 586 | 593 |
| 587 // Check for Infinity and NaNs, which should return 0. | 594 // Check for Infinity and NaNs, which should return 0. |
| 588 __ Subu(scratch, result_reg, HeapNumber::kExponentMask); | 595 __ Subu(scratch, result_reg, HeapNumber::kExponentMask); |
| (...skipping 631 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1220 argument_count); | 1227 argument_count); |
| 1221 if (save_doubles_ == kSaveFPRegs) { | 1228 if (save_doubles_ == kSaveFPRegs) { |
| 1222 __ MultiPopFPU(kCallerSavedFPU); | 1229 __ MultiPopFPU(kCallerSavedFPU); |
| 1223 } | 1230 } |
| 1224 | 1231 |
| 1225 __ MultiPop(kJSCallerSaved | ra.bit()); | 1232 __ MultiPop(kJSCallerSaved | ra.bit()); |
| 1226 __ Ret(); | 1233 __ Ret(); |
| 1227 } | 1234 } |
| 1228 | 1235 |
| 1229 | 1236 |
| 1230 // Generates code to call a C function to do a double operation. | 1237 void BinaryOpStub::InitializeInterfaceDescriptor( |
| 1231 // This code never falls through, but returns with a heap number containing | 1238 Isolate* isolate, |
| 1232 // the result in v0. | 1239 CodeStubInterfaceDescriptor* descriptor) { |
| 1233 // Register heap_number_result must be a heap number in which the | 1240 static Register registers[] = { a1, a0 }; |
| 1234 // result of the operation will be stored. | 1241 descriptor->register_param_count_ = 2; |
| 1235 // Requires the following layout on entry: | 1242 descriptor->register_params_ = registers; |
| 1236 // a0: Left value (least significant part of mantissa). | 1243 descriptor->deoptimization_handler_ = FUNCTION_ADDR(BinaryOpIC_Miss); |
| 1237 // a1: Left value (sign, exponent, top of mantissa). | 1244 descriptor->SetMissHandler( |
| 1238 // a2: Right value (least significant part of mantissa). | 1245 ExternalReference(IC_Utility(IC::kBinaryOpIC_Miss), isolate)); |
| 1239 // a3: Right value (sign, exponent, top of mantissa). | |
| 1240 static void CallCCodeForDoubleOperation(MacroAssembler* masm, | |
| 1241 Token::Value op, | |
| 1242 Register heap_number_result, | |
| 1243 Register scratch) { | |
| 1244 // Assert that heap_number_result is saved. | |
| 1245 // We currently always use s0 to pass it. | |
| 1246 ASSERT(heap_number_result.is(s0)); | |
| 1247 | |
| 1248 // Push the current return address before the C call. | |
| 1249 __ push(ra); | |
| 1250 __ PrepareCallCFunction(4, scratch); // Two doubles are 4 arguments. | |
| 1251 { | |
| 1252 AllowExternalCallThatCantCauseGC scope(masm); | |
| 1253 __ CallCFunction( | |
| 1254 ExternalReference::double_fp_operation(op, masm->isolate()), 0, 2); | |
| 1255 } | |
| 1256 // Store answer in the overwritable heap number. | |
| 1257 // Double returned in register f0. | |
| 1258 __ sdc1(f0, FieldMemOperand(heap_number_result, HeapNumber::kValueOffset)); | |
| 1259 // Place heap_number_result in v0 and return to the pushed return address. | |
| 1260 __ pop(ra); | |
| 1261 __ Ret(USE_DELAY_SLOT); | |
| 1262 __ mov(v0, heap_number_result); | |
| 1263 } | 1246 } |
| 1264 | 1247 |
| 1265 | 1248 |
| 1266 void BinaryOpStub::Initialize() { | |
| 1267 platform_specific_bit_ = true; // FPU is a base requirement for V8. | |
| 1268 } | |
| 1269 | |
| 1270 | |
| 1271 void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) { | |
| 1272 Label get_result; | |
| 1273 | |
| 1274 __ Push(a1, a0); | |
| 1275 | |
| 1276 __ li(a2, Operand(Smi::FromInt(MinorKey()))); | |
| 1277 __ push(a2); | |
| 1278 | |
| 1279 __ TailCallExternalReference( | |
| 1280 ExternalReference(IC_Utility(IC::kBinaryOp_Patch), | |
| 1281 masm->isolate()), | |
| 1282 3, | |
| 1283 1); | |
| 1284 } | |
| 1285 | |
| 1286 | |
| 1287 void BinaryOpStub::GenerateTypeTransitionWithSavedArgs( | |
| 1288 MacroAssembler* masm) { | |
| 1289 UNIMPLEMENTED(); | |
| 1290 } | |
| 1291 | |
| 1292 | |
| 1293 void BinaryOpStub_GenerateSmiSmiOperation(MacroAssembler* masm, | |
| 1294 Token::Value op) { | |
| 1295 Register left = a1; | |
| 1296 Register right = a0; | |
| 1297 | |
| 1298 Register scratch1 = t0; | |
| 1299 Register scratch2 = t1; | |
| 1300 | |
| 1301 ASSERT(right.is(a0)); | |
| 1302 STATIC_ASSERT(kSmiTag == 0); | |
| 1303 | |
| 1304 Label not_smi_result; | |
| 1305 switch (op) { | |
| 1306 case Token::ADD: | |
| 1307 __ AdduAndCheckForOverflow(v0, left, right, scratch1); | |
| 1308 __ RetOnNoOverflow(scratch1); | |
| 1309 // No need to revert anything - right and left are intact. | |
| 1310 break; | |
| 1311 case Token::SUB: | |
| 1312 __ SubuAndCheckForOverflow(v0, left, right, scratch1); | |
| 1313 __ RetOnNoOverflow(scratch1); | |
| 1314 // No need to revert anything - right and left are intact. | |
| 1315 break; | |
| 1316 case Token::MUL: { | |
| 1317 // Remove tag from one of the operands. This way the multiplication result | |
| 1318 // will be a smi if it fits the smi range. | |
| 1319 __ SmiUntag(scratch1, right); | |
| 1320 // Do multiplication. | |
| 1321 // lo = lower 32 bits of scratch1 * left. | |
| 1322 // hi = higher 32 bits of scratch1 * left. | |
| 1323 __ Mult(left, scratch1); | |
| 1324 // Check for overflowing the smi range - no overflow if higher 33 bits of | |
| 1325 // the result are identical. | |
| 1326 __ mflo(scratch1); | |
| 1327 __ mfhi(scratch2); | |
| 1328 __ sra(scratch1, scratch1, 31); | |
| 1329 __ Branch(¬_smi_result, ne, scratch1, Operand(scratch2)); | |
| 1330 // Go slow on zero result to handle -0. | |
| 1331 __ mflo(v0); | |
| 1332 __ Ret(ne, v0, Operand(zero_reg)); | |
| 1333 // We need -0 if we were multiplying a negative number with 0 to get 0. | |
| 1334 // We know one of them was zero. | |
| 1335 __ Addu(scratch2, right, left); | |
| 1336 Label skip; | |
| 1337 // ARM uses the 'pl' condition, which is 'ge'. | |
| 1338 // Negating it results in 'lt'. | |
| 1339 __ Branch(&skip, lt, scratch2, Operand(zero_reg)); | |
| 1340 ASSERT(Smi::FromInt(0) == 0); | |
| 1341 __ Ret(USE_DELAY_SLOT); | |
| 1342 __ mov(v0, zero_reg); // Return smi 0 if the non-zero one was positive. | |
| 1343 __ bind(&skip); | |
| 1344 // We fall through here if we multiplied a negative number with 0, because | |
| 1345 // that would mean we should produce -0. | |
| 1346 } | |
| 1347 break; | |
| 1348 case Token::DIV: { | |
| 1349 Label done; | |
| 1350 __ SmiUntag(scratch2, right); | |
| 1351 __ SmiUntag(scratch1, left); | |
| 1352 __ Div(scratch1, scratch2); | |
| 1353 // A minor optimization: div may be calculated asynchronously, so we check | |
| 1354 // for division by zero before getting the result. | |
| 1355 __ Branch(¬_smi_result, eq, scratch2, Operand(zero_reg)); | |
| 1356 // If the result is 0, we need to make sure the dividsor (right) is | |
| 1357 // positive, otherwise it is a -0 case. | |
| 1358 // Quotient is in 'lo', remainder is in 'hi'. | |
| 1359 // Check for no remainder first. | |
| 1360 __ mfhi(scratch1); | |
| 1361 __ Branch(¬_smi_result, ne, scratch1, Operand(zero_reg)); | |
| 1362 __ mflo(scratch1); | |
| 1363 __ Branch(&done, ne, scratch1, Operand(zero_reg)); | |
| 1364 __ Branch(¬_smi_result, lt, scratch2, Operand(zero_reg)); | |
| 1365 __ bind(&done); | |
| 1366 // Check that the signed result fits in a Smi. | |
| 1367 __ Addu(scratch2, scratch1, Operand(0x40000000)); | |
| 1368 __ Branch(¬_smi_result, lt, scratch2, Operand(zero_reg)); | |
| 1369 __ Ret(USE_DELAY_SLOT); // SmiTag emits one instruction in delay slot. | |
| 1370 __ SmiTag(v0, scratch1); | |
| 1371 } | |
| 1372 break; | |
| 1373 case Token::MOD: { | |
| 1374 Label done; | |
| 1375 __ SmiUntag(scratch2, right); | |
| 1376 __ SmiUntag(scratch1, left); | |
| 1377 __ Div(scratch1, scratch2); | |
| 1378 // A minor optimization: div may be calculated asynchronously, so we check | |
| 1379 // for division by 0 before calling mfhi. | |
| 1380 // Check for zero on the right hand side. | |
| 1381 __ Branch(¬_smi_result, eq, scratch2, Operand(zero_reg)); | |
| 1382 // If the result is 0, we need to make sure the dividend (left) is | |
| 1383 // positive (or 0), otherwise it is a -0 case. | |
| 1384 // Remainder is in 'hi'. | |
| 1385 __ mfhi(scratch2); | |
| 1386 __ Branch(&done, ne, scratch2, Operand(zero_reg)); | |
| 1387 __ Branch(¬_smi_result, lt, scratch1, Operand(zero_reg)); | |
| 1388 __ bind(&done); | |
| 1389 // Check that the signed result fits in a Smi. | |
| 1390 __ Addu(scratch1, scratch2, Operand(0x40000000)); | |
| 1391 __ Branch(¬_smi_result, lt, scratch1, Operand(zero_reg)); | |
| 1392 __ Ret(USE_DELAY_SLOT); // SmiTag emits one instruction in delay slot. | |
| 1393 __ SmiTag(v0, scratch2); | |
| 1394 } | |
| 1395 break; | |
| 1396 case Token::BIT_OR: | |
| 1397 __ Ret(USE_DELAY_SLOT); | |
| 1398 __ or_(v0, left, right); | |
| 1399 break; | |
| 1400 case Token::BIT_AND: | |
| 1401 __ Ret(USE_DELAY_SLOT); | |
| 1402 __ and_(v0, left, right); | |
| 1403 break; | |
| 1404 case Token::BIT_XOR: | |
| 1405 __ Ret(USE_DELAY_SLOT); | |
| 1406 __ xor_(v0, left, right); | |
| 1407 break; | |
| 1408 case Token::SAR: | |
| 1409 // Remove tags from right operand. | |
| 1410 __ GetLeastBitsFromSmi(scratch1, right, 5); | |
| 1411 __ srav(scratch1, left, scratch1); | |
| 1412 // Smi tag result. | |
| 1413 __ And(v0, scratch1, ~kSmiTagMask); | |
| 1414 __ Ret(); | |
| 1415 break; | |
| 1416 case Token::SHR: | |
| 1417 // Remove tags from operands. We can't do this on a 31 bit number | |
| 1418 // because then the 0s get shifted into bit 30 instead of bit 31. | |
| 1419 __ SmiUntag(scratch1, left); | |
| 1420 __ GetLeastBitsFromSmi(scratch2, right, 5); | |
| 1421 __ srlv(v0, scratch1, scratch2); | |
| 1422 // Unsigned shift is not allowed to produce a negative number, so | |
| 1423 // check the sign bit and the sign bit after Smi tagging. | |
| 1424 __ And(scratch1, v0, Operand(0xc0000000)); | |
| 1425 __ Branch(¬_smi_result, ne, scratch1, Operand(zero_reg)); | |
| 1426 // Smi tag result. | |
| 1427 __ Ret(USE_DELAY_SLOT); // SmiTag emits one instruction in delay slot. | |
| 1428 __ SmiTag(v0); | |
| 1429 break; | |
| 1430 case Token::SHL: | |
| 1431 // Remove tags from operands. | |
| 1432 __ SmiUntag(scratch1, left); | |
| 1433 __ GetLeastBitsFromSmi(scratch2, right, 5); | |
| 1434 __ sllv(scratch1, scratch1, scratch2); | |
| 1435 // Check that the signed result fits in a Smi. | |
| 1436 __ Addu(scratch2, scratch1, Operand(0x40000000)); | |
| 1437 __ Branch(¬_smi_result, lt, scratch2, Operand(zero_reg)); | |
| 1438 __ Ret(USE_DELAY_SLOT); | |
| 1439 __ SmiTag(v0, scratch1); // SmiTag emits one instruction in delay slot. | |
| 1440 break; | |
| 1441 default: | |
| 1442 UNREACHABLE(); | |
| 1443 } | |
| 1444 __ bind(¬_smi_result); | |
| 1445 } | |
| 1446 | |
| 1447 | |
| 1448 void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm, | |
| 1449 Register result, | |
| 1450 Register heap_number_map, | |
| 1451 Register scratch1, | |
| 1452 Register scratch2, | |
| 1453 Label* gc_required, | |
| 1454 OverwriteMode mode); | |
| 1455 | |
| 1456 | |
| 1457 void BinaryOpStub_GenerateFPOperation(MacroAssembler* masm, | |
| 1458 BinaryOpIC::TypeInfo left_type, | |
| 1459 BinaryOpIC::TypeInfo right_type, | |
| 1460 bool smi_operands, | |
| 1461 Label* not_numbers, | |
| 1462 Label* gc_required, | |
| 1463 Label* miss, | |
| 1464 Token::Value op, | |
| 1465 OverwriteMode mode) { | |
| 1466 Register left = a1; | |
| 1467 Register right = a0; | |
| 1468 Register scratch1 = t3; | |
| 1469 Register scratch2 = t5; | |
| 1470 | |
| 1471 ASSERT(smi_operands || (not_numbers != NULL)); | |
| 1472 if (smi_operands) { | |
| 1473 __ AssertSmi(left); | |
| 1474 __ AssertSmi(right); | |
| 1475 } | |
| 1476 if (left_type == BinaryOpIC::SMI) { | |
| 1477 __ JumpIfNotSmi(left, miss); | |
| 1478 } | |
| 1479 if (right_type == BinaryOpIC::SMI) { | |
| 1480 __ JumpIfNotSmi(right, miss); | |
| 1481 } | |
| 1482 | |
| 1483 Register heap_number_map = t2; | |
| 1484 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); | |
| 1485 | |
| 1486 switch (op) { | |
| 1487 case Token::ADD: | |
| 1488 case Token::SUB: | |
| 1489 case Token::MUL: | |
| 1490 case Token::DIV: | |
| 1491 case Token::MOD: { | |
| 1492 // Allocate new heap number for result. | |
| 1493 Register result = s0; | |
| 1494 BinaryOpStub_GenerateHeapResultAllocation( | |
| 1495 masm, result, heap_number_map, scratch1, scratch2, gc_required, mode); | |
| 1496 | |
| 1497 // Load left and right operands into f12 and f14. | |
| 1498 if (smi_operands) { | |
| 1499 __ SmiUntag(scratch1, a0); | |
| 1500 __ mtc1(scratch1, f14); | |
| 1501 __ cvt_d_w(f14, f14); | |
| 1502 __ SmiUntag(scratch1, a1); | |
| 1503 __ mtc1(scratch1, f12); | |
| 1504 __ cvt_d_w(f12, f12); | |
| 1505 } else { | |
| 1506 // Load right operand to f14. | |
| 1507 if (right_type == BinaryOpIC::INT32) { | |
| 1508 __ LoadNumberAsInt32Double( | |
| 1509 right, f14, heap_number_map, scratch1, scratch2, f2, miss); | |
| 1510 } else { | |
| 1511 Label* fail = (right_type == BinaryOpIC::NUMBER) ? miss : not_numbers; | |
| 1512 __ LoadNumber(right, f14, heap_number_map, scratch1, fail); | |
| 1513 } | |
| 1514 // Load left operand to f12 or a0/a1. This keeps a0/a1 intact if it | |
| 1515 // jumps to |miss|. | |
| 1516 if (left_type == BinaryOpIC::INT32) { | |
| 1517 __ LoadNumberAsInt32Double( | |
| 1518 left, f12, heap_number_map, scratch1, scratch2, f2, miss); | |
| 1519 } else { | |
| 1520 Label* fail = (left_type == BinaryOpIC::NUMBER) ? miss : not_numbers; | |
| 1521 __ LoadNumber(left, f12, heap_number_map, scratch1, fail); | |
| 1522 } | |
| 1523 } | |
| 1524 | |
| 1525 // Calculate the result. | |
| 1526 if (op != Token::MOD) { | |
| 1527 // Using FPU registers: | |
| 1528 // f12: Left value. | |
| 1529 // f14: Right value. | |
| 1530 switch (op) { | |
| 1531 case Token::ADD: | |
| 1532 __ add_d(f10, f12, f14); | |
| 1533 break; | |
| 1534 case Token::SUB: | |
| 1535 __ sub_d(f10, f12, f14); | |
| 1536 break; | |
| 1537 case Token::MUL: | |
| 1538 __ mul_d(f10, f12, f14); | |
| 1539 break; | |
| 1540 case Token::DIV: | |
| 1541 __ div_d(f10, f12, f14); | |
| 1542 break; | |
| 1543 default: | |
| 1544 UNREACHABLE(); | |
| 1545 } | |
| 1546 | |
| 1547 // ARM uses a workaround here because of the unaligned HeapNumber | |
| 1548 // kValueOffset. On MIPS this workaround is built into sdc1 so | |
| 1549 // there's no point in generating even more instructions. | |
| 1550 __ sdc1(f10, FieldMemOperand(result, HeapNumber::kValueOffset)); | |
| 1551 __ Ret(USE_DELAY_SLOT); | |
| 1552 __ mov(v0, result); | |
| 1553 } else { | |
| 1554 // Call the C function to handle the double operation. | |
| 1555 CallCCodeForDoubleOperation(masm, op, result, scratch1); | |
| 1556 if (FLAG_debug_code) { | |
| 1557 __ stop("Unreachable code."); | |
| 1558 } | |
| 1559 } | |
| 1560 break; | |
| 1561 } | |
| 1562 case Token::BIT_OR: | |
| 1563 case Token::BIT_XOR: | |
| 1564 case Token::BIT_AND: | |
| 1565 case Token::SAR: | |
| 1566 case Token::SHR: | |
| 1567 case Token::SHL: { | |
| 1568 if (smi_operands) { | |
| 1569 __ SmiUntag(a3, left); | |
| 1570 __ SmiUntag(a2, right); | |
| 1571 } else { | |
| 1572 // Convert operands to 32-bit integers. Right in a2 and left in a3. | |
| 1573 __ TruncateNumberToI(left, a3, heap_number_map, scratch1, not_numbers); | |
| 1574 __ TruncateNumberToI(right, a2, heap_number_map, scratch1, not_numbers); | |
| 1575 } | |
| 1576 Label result_not_a_smi; | |
| 1577 switch (op) { | |
| 1578 case Token::BIT_OR: | |
| 1579 __ Or(a2, a3, Operand(a2)); | |
| 1580 break; | |
| 1581 case Token::BIT_XOR: | |
| 1582 __ Xor(a2, a3, Operand(a2)); | |
| 1583 break; | |
| 1584 case Token::BIT_AND: | |
| 1585 __ And(a2, a3, Operand(a2)); | |
| 1586 break; | |
| 1587 case Token::SAR: | |
| 1588 // Use only the 5 least significant bits of the shift count. | |
| 1589 __ GetLeastBitsFromInt32(a2, a2, 5); | |
| 1590 __ srav(a2, a3, a2); | |
| 1591 break; | |
| 1592 case Token::SHR: | |
| 1593 // Use only the 5 least significant bits of the shift count. | |
| 1594 __ GetLeastBitsFromInt32(a2, a2, 5); | |
| 1595 __ srlv(a2, a3, a2); | |
| 1596 // SHR is special because it is required to produce a positive answer. | |
| 1597 // The code below for writing into heap numbers isn't capable of | |
| 1598 // writing the register as an unsigned int so we go to slow case if we | |
| 1599 // hit this case. | |
| 1600 __ Branch(&result_not_a_smi, lt, a2, Operand(zero_reg)); | |
| 1601 break; | |
| 1602 case Token::SHL: | |
| 1603 // Use only the 5 least significant bits of the shift count. | |
| 1604 __ GetLeastBitsFromInt32(a2, a2, 5); | |
| 1605 __ sllv(a2, a3, a2); | |
| 1606 break; | |
| 1607 default: | |
| 1608 UNREACHABLE(); | |
| 1609 } | |
| 1610 // Check that the *signed* result fits in a smi. | |
| 1611 __ Addu(a3, a2, Operand(0x40000000)); | |
| 1612 __ Branch(&result_not_a_smi, lt, a3, Operand(zero_reg)); | |
| 1613 __ Ret(USE_DELAY_SLOT); // SmiTag emits one instruction in delay slot. | |
| 1614 __ SmiTag(v0, a2); | |
| 1615 | |
| 1616 // Allocate new heap number for result. | |
| 1617 __ bind(&result_not_a_smi); | |
| 1618 Register result = t1; | |
| 1619 if (smi_operands) { | |
| 1620 __ AllocateHeapNumber( | |
| 1621 result, scratch1, scratch2, heap_number_map, gc_required); | |
| 1622 } else { | |
| 1623 BinaryOpStub_GenerateHeapResultAllocation( | |
| 1624 masm, result, heap_number_map, scratch1, scratch2, gc_required, | |
| 1625 mode); | |
| 1626 } | |
| 1627 | |
| 1628 // a2: Answer as signed int32. | |
| 1629 // t1: Heap number to write answer into. | |
| 1630 | |
| 1631 // Nothing can go wrong now, so move the heap number to v0, which is the | |
| 1632 // result. | |
| 1633 __ mov(v0, t1); | |
| 1634 // Convert the int32 in a2 to the heap number in a0. As | |
| 1635 // mentioned above SHR needs to always produce a positive result. | |
| 1636 __ mtc1(a2, f0); | |
| 1637 if (op == Token::SHR) { | |
| 1638 __ Cvt_d_uw(f0, f0, f22); | |
| 1639 } else { | |
| 1640 __ cvt_d_w(f0, f0); | |
| 1641 } | |
| 1642 // ARM uses a workaround here because of the unaligned HeapNumber | |
| 1643 // kValueOffset. On MIPS this workaround is built into sdc1 so | |
| 1644 // there's no point in generating even more instructions. | |
| 1645 __ sdc1(f0, FieldMemOperand(v0, HeapNumber::kValueOffset)); | |
| 1646 __ Ret(); | |
| 1647 break; | |
| 1648 } | |
| 1649 default: | |
| 1650 UNREACHABLE(); | |
| 1651 } | |
| 1652 } | |
| 1653 | |
| 1654 | |
| 1655 // Generate the smi code. If the operation on smis are successful this return is | |
| 1656 // generated. If the result is not a smi and heap number allocation is not | |
| 1657 // requested the code falls through. If number allocation is requested but a | |
| 1658 // heap number cannot be allocated the code jumps to the label gc_required. | |
| 1659 void BinaryOpStub_GenerateSmiCode( | |
| 1660 MacroAssembler* masm, | |
| 1661 Label* use_runtime, | |
| 1662 Label* gc_required, | |
| 1663 Token::Value op, | |
| 1664 BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results, | |
| 1665 OverwriteMode mode) { | |
| 1666 Label not_smis; | |
| 1667 | |
| 1668 Register left = a1; | |
| 1669 Register right = a0; | |
| 1670 Register scratch1 = t3; | |
| 1671 | |
| 1672 // Perform combined smi check on both operands. | |
| 1673 __ Or(scratch1, left, Operand(right)); | |
| 1674 STATIC_ASSERT(kSmiTag == 0); | |
| 1675 __ JumpIfNotSmi(scratch1, ¬_smis); | |
| 1676 | |
| 1677 // If the smi-smi operation results in a smi return is generated. | |
| 1678 BinaryOpStub_GenerateSmiSmiOperation(masm, op); | |
| 1679 | |
| 1680 // If heap number results are possible generate the result in an allocated | |
| 1681 // heap number. | |
| 1682 if (allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS) { | |
| 1683 BinaryOpStub_GenerateFPOperation( | |
| 1684 masm, BinaryOpIC::UNINITIALIZED, BinaryOpIC::UNINITIALIZED, true, | |
| 1685 use_runtime, gc_required, ¬_smis, op, mode); | |
| 1686 } | |
| 1687 __ bind(¬_smis); | |
| 1688 } | |
| 1689 | |
| 1690 | |
| 1691 void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) { | |
| 1692 Label right_arg_changed, call_runtime; | |
| 1693 | |
| 1694 if (op_ == Token::MOD && encoded_right_arg_.has_value) { | |
| 1695 // It is guaranteed that the value will fit into a Smi, because if it | |
| 1696 // didn't, we wouldn't be here, see BinaryOp_Patch. | |
| 1697 __ Branch(&right_arg_changed, | |
| 1698 ne, | |
| 1699 a0, | |
| 1700 Operand(Smi::FromInt(fixed_right_arg_value()))); | |
| 1701 } | |
| 1702 | |
| 1703 if (result_type_ == BinaryOpIC::UNINITIALIZED || | |
| 1704 result_type_ == BinaryOpIC::SMI) { | |
| 1705 // Only allow smi results. | |
| 1706 BinaryOpStub_GenerateSmiCode( | |
| 1707 masm, &call_runtime, NULL, op_, NO_HEAPNUMBER_RESULTS, mode_); | |
| 1708 } else { | |
| 1709 // Allow heap number result and don't make a transition if a heap number | |
| 1710 // cannot be allocated. | |
| 1711 BinaryOpStub_GenerateSmiCode( | |
| 1712 masm, &call_runtime, &call_runtime, op_, ALLOW_HEAPNUMBER_RESULTS, | |
| 1713 mode_); | |
| 1714 } | |
| 1715 | |
| 1716 // Code falls through if the result is not returned as either a smi or heap | |
| 1717 // number. | |
| 1718 __ bind(&right_arg_changed); | |
| 1719 GenerateTypeTransition(masm); | |
| 1720 | |
| 1721 __ bind(&call_runtime); | |
| 1722 { | |
| 1723 FrameScope scope(masm, StackFrame::INTERNAL); | |
| 1724 GenerateRegisterArgsPush(masm); | |
| 1725 GenerateCallRuntime(masm); | |
| 1726 } | |
| 1727 __ Ret(); | |
| 1728 } | |
| 1729 | |
| 1730 | |
| 1731 void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) { | |
| 1732 Label call_runtime; | |
| 1733 ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING); | |
| 1734 ASSERT(op_ == Token::ADD); | |
| 1735 // If both arguments are strings, call the string add stub. | |
| 1736 // Otherwise, do a transition. | |
| 1737 | |
| 1738 // Registers containing left and right operands respectively. | |
| 1739 Register left = a1; | |
| 1740 Register right = a0; | |
| 1741 | |
| 1742 // Test if left operand is a string. | |
| 1743 __ JumpIfSmi(left, &call_runtime); | |
| 1744 __ GetObjectType(left, a2, a2); | |
| 1745 __ Branch(&call_runtime, ge, a2, Operand(FIRST_NONSTRING_TYPE)); | |
| 1746 | |
| 1747 // Test if right operand is a string. | |
| 1748 __ JumpIfSmi(right, &call_runtime); | |
| 1749 __ GetObjectType(right, a2, a2); | |
| 1750 __ Branch(&call_runtime, ge, a2, Operand(FIRST_NONSTRING_TYPE)); | |
| 1751 | |
| 1752 StringAddStub string_add_stub( | |
| 1753 (StringAddFlags)(STRING_ADD_CHECK_NONE | STRING_ADD_ERECT_FRAME)); | |
| 1754 GenerateRegisterArgsPush(masm); | |
| 1755 __ TailCallStub(&string_add_stub); | |
| 1756 | |
| 1757 __ bind(&call_runtime); | |
| 1758 GenerateTypeTransition(masm); | |
| 1759 } | |
| 1760 | |
| 1761 | |
| 1762 void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) { | |
| 1763 ASSERT(Max(left_type_, right_type_) == BinaryOpIC::INT32); | |
| 1764 | |
| 1765 Register left = a1; | |
| 1766 Register right = a0; | |
| 1767 Register scratch1 = t3; | |
| 1768 Register scratch2 = t5; | |
| 1769 FPURegister double_scratch = f0; | |
| 1770 FPURegister single_scratch = f6; | |
| 1771 | |
| 1772 Register heap_number_result = no_reg; | |
| 1773 Register heap_number_map = t2; | |
| 1774 __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); | |
| 1775 | |
| 1776 Label call_runtime; | |
| 1777 // Labels for type transition, used for wrong input or output types. | |
| 1778 // Both label are currently actually bound to the same position. We use two | |
| 1779 // different label to differentiate the cause leading to type transition. | |
| 1780 Label transition; | |
| 1781 | |
| 1782 // Smi-smi fast case. | |
| 1783 Label skip; | |
| 1784 __ Or(scratch1, left, right); | |
| 1785 __ JumpIfNotSmi(scratch1, &skip); | |
| 1786 BinaryOpStub_GenerateSmiSmiOperation(masm, op_); | |
| 1787 // Fall through if the result is not a smi. | |
| 1788 __ bind(&skip); | |
| 1789 | |
| 1790 switch (op_) { | |
| 1791 case Token::ADD: | |
| 1792 case Token::SUB: | |
| 1793 case Token::MUL: | |
| 1794 case Token::DIV: | |
| 1795 case Token::MOD: { | |
| 1796 // It could be that only SMIs have been seen at either the left | |
| 1797 // or the right operand. For precise type feedback, patch the IC | |
| 1798 // again if this changes. | |
| 1799 if (left_type_ == BinaryOpIC::SMI) { | |
| 1800 __ JumpIfNotSmi(left, &transition); | |
| 1801 } | |
| 1802 if (right_type_ == BinaryOpIC::SMI) { | |
| 1803 __ JumpIfNotSmi(right, &transition); | |
| 1804 } | |
| 1805 // Load both operands and check that they are 32-bit integer. | |
| 1806 // Jump to type transition if they are not. The registers a0 and a1 (right | |
| 1807 // and left) are preserved for the runtime call. | |
| 1808 | |
| 1809 __ LoadNumberAsInt32Double( | |
| 1810 right, f14, heap_number_map, scratch1, scratch2, f2, &transition); | |
| 1811 __ LoadNumberAsInt32Double( | |
| 1812 left, f12, heap_number_map, scratch1, scratch2, f2, &transition); | |
| 1813 | |
| 1814 if (op_ != Token::MOD) { | |
| 1815 Label return_heap_number; | |
| 1816 switch (op_) { | |
| 1817 case Token::ADD: | |
| 1818 __ add_d(f10, f12, f14); | |
| 1819 break; | |
| 1820 case Token::SUB: | |
| 1821 __ sub_d(f10, f12, f14); | |
| 1822 break; | |
| 1823 case Token::MUL: | |
| 1824 __ mul_d(f10, f12, f14); | |
| 1825 break; | |
| 1826 case Token::DIV: | |
| 1827 __ div_d(f10, f12, f14); | |
| 1828 break; | |
| 1829 default: | |
| 1830 UNREACHABLE(); | |
| 1831 } | |
| 1832 | |
| 1833 if (result_type_ <= BinaryOpIC::INT32) { | |
| 1834 Register except_flag = scratch2; | |
| 1835 const FPURoundingMode kRoundingMode = op_ == Token::DIV ? | |
| 1836 kRoundToMinusInf : kRoundToZero; | |
| 1837 const CheckForInexactConversion kConversion = op_ == Token::DIV ? | |
| 1838 kCheckForInexactConversion : kDontCheckForInexactConversion; | |
| 1839 __ EmitFPUTruncate(kRoundingMode, | |
| 1840 scratch1, | |
| 1841 f10, | |
| 1842 at, | |
| 1843 f16, | |
| 1844 except_flag, | |
| 1845 kConversion); | |
| 1846 // If except_flag != 0, result does not fit in a 32-bit integer. | |
| 1847 __ Branch(&transition, ne, except_flag, Operand(zero_reg)); | |
| 1848 // Try to tag the result as a Smi, return heap number on overflow. | |
| 1849 __ SmiTagCheckOverflow(scratch1, scratch1, scratch2); | |
| 1850 __ Branch(&return_heap_number, lt, scratch2, Operand(zero_reg)); | |
| 1851 // Check for minus zero, transition in that case (because we need | |
| 1852 // to return a heap number). | |
| 1853 Label not_zero; | |
| 1854 ASSERT(kSmiTag == 0); | |
| 1855 __ Branch(¬_zero, ne, scratch1, Operand(zero_reg)); | |
| 1856 __ mfc1(scratch2, f11); | |
| 1857 __ And(scratch2, scratch2, HeapNumber::kSignMask); | |
| 1858 __ Branch(&transition, ne, scratch2, Operand(zero_reg)); | |
| 1859 __ bind(¬_zero); | |
| 1860 | |
| 1861 __ Ret(USE_DELAY_SLOT); | |
| 1862 __ mov(v0, scratch1); | |
| 1863 } | |
| 1864 | |
| 1865 __ bind(&return_heap_number); | |
| 1866 // Return a heap number, or fall through to type transition or runtime | |
| 1867 // call if we can't. | |
| 1868 // We are using FPU registers so s0 is available. | |
| 1869 heap_number_result = s0; | |
| 1870 BinaryOpStub_GenerateHeapResultAllocation(masm, | |
| 1871 heap_number_result, | |
| 1872 heap_number_map, | |
| 1873 scratch1, | |
| 1874 scratch2, | |
| 1875 &call_runtime, | |
| 1876 mode_); | |
| 1877 __ sdc1(f10, | |
| 1878 FieldMemOperand(heap_number_result, HeapNumber::kValueOffset)); | |
| 1879 __ Ret(USE_DELAY_SLOT); | |
| 1880 __ mov(v0, heap_number_result); | |
| 1881 | |
| 1882 // A DIV operation expecting an integer result falls through | |
| 1883 // to type transition. | |
| 1884 | |
| 1885 } else { | |
| 1886 if (encoded_right_arg_.has_value) { | |
| 1887 __ Move(f16, fixed_right_arg_value()); | |
| 1888 __ BranchF(&transition, NULL, ne, f14, f16); | |
| 1889 } | |
| 1890 | |
| 1891 Label pop_and_call_runtime; | |
| 1892 | |
| 1893 // Allocate a heap number to store the result. | |
| 1894 heap_number_result = s0; | |
| 1895 BinaryOpStub_GenerateHeapResultAllocation(masm, | |
| 1896 heap_number_result, | |
| 1897 heap_number_map, | |
| 1898 scratch1, | |
| 1899 scratch2, | |
| 1900 &pop_and_call_runtime, | |
| 1901 mode_); | |
| 1902 | |
| 1903 // Call the C function to handle the double operation. | |
| 1904 CallCCodeForDoubleOperation(masm, op_, heap_number_result, scratch1); | |
| 1905 if (FLAG_debug_code) { | |
| 1906 __ stop("Unreachable code."); | |
| 1907 } | |
| 1908 | |
| 1909 __ bind(&pop_and_call_runtime); | |
| 1910 __ Drop(2); | |
| 1911 __ Branch(&call_runtime); | |
| 1912 } | |
| 1913 | |
| 1914 break; | |
| 1915 } | |
| 1916 | |
| 1917 case Token::BIT_OR: | |
| 1918 case Token::BIT_XOR: | |
| 1919 case Token::BIT_AND: | |
| 1920 case Token::SAR: | |
| 1921 case Token::SHR: | |
| 1922 case Token::SHL: { | |
| 1923 Label return_heap_number; | |
| 1924 // Convert operands to 32-bit integers. Right in a2 and left in a3. The | |
| 1925 // registers a0 and a1 (right and left) are preserved for the runtime | |
| 1926 // call. | |
| 1927 __ LoadNumberAsInt32( | |
| 1928 left, a3, heap_number_map, scratch1, scratch2, f0, f2, &transition); | |
| 1929 __ LoadNumberAsInt32( | |
| 1930 right, a2, heap_number_map, scratch1, scratch2, f0, f2, &transition); | |
| 1931 | |
| 1932 // The ECMA-262 standard specifies that, for shift operations, only the | |
| 1933 // 5 least significant bits of the shift value should be used. | |
| 1934 switch (op_) { | |
| 1935 case Token::BIT_OR: | |
| 1936 __ Or(a2, a3, Operand(a2)); | |
| 1937 break; | |
| 1938 case Token::BIT_XOR: | |
| 1939 __ Xor(a2, a3, Operand(a2)); | |
| 1940 break; | |
| 1941 case Token::BIT_AND: | |
| 1942 __ And(a2, a3, Operand(a2)); | |
| 1943 break; | |
| 1944 case Token::SAR: | |
| 1945 __ And(a2, a2, Operand(0x1f)); | |
| 1946 __ srav(a2, a3, a2); | |
| 1947 break; | |
| 1948 case Token::SHR: | |
| 1949 __ And(a2, a2, Operand(0x1f)); | |
| 1950 __ srlv(a2, a3, a2); | |
| 1951 // SHR is special because it is required to produce a positive answer. | |
| 1952 // We only get a negative result if the shift value (a2) is 0. | |
| 1953 // This result cannot be respresented as a signed 32-bit integer, try | |
| 1954 // to return a heap number if we can. | |
| 1955 __ Branch((result_type_ <= BinaryOpIC::INT32) | |
| 1956 ? &transition | |
| 1957 : &return_heap_number, | |
| 1958 lt, | |
| 1959 a2, | |
| 1960 Operand(zero_reg)); | |
| 1961 break; | |
| 1962 case Token::SHL: | |
| 1963 __ And(a2, a2, Operand(0x1f)); | |
| 1964 __ sllv(a2, a3, a2); | |
| 1965 break; | |
| 1966 default: | |
| 1967 UNREACHABLE(); | |
| 1968 } | |
| 1969 | |
| 1970 // Check if the result fits in a smi. | |
| 1971 __ Addu(scratch1, a2, Operand(0x40000000)); | |
| 1972 // If not try to return a heap number. (We know the result is an int32.) | |
| 1973 __ Branch(&return_heap_number, lt, scratch1, Operand(zero_reg)); | |
| 1974 // Tag the result and return. | |
| 1975 __ Ret(USE_DELAY_SLOT); // SmiTag emits one instruction in delay slot. | |
| 1976 __ SmiTag(v0, a2); | |
| 1977 | |
| 1978 __ bind(&return_heap_number); | |
| 1979 heap_number_result = t1; | |
| 1980 BinaryOpStub_GenerateHeapResultAllocation(masm, | |
| 1981 heap_number_result, | |
| 1982 heap_number_map, | |
| 1983 scratch1, | |
| 1984 scratch2, | |
| 1985 &call_runtime, | |
| 1986 mode_); | |
| 1987 | |
| 1988 if (op_ != Token::SHR) { | |
| 1989 // Convert the result to a floating point value. | |
| 1990 __ mtc1(a2, double_scratch); | |
| 1991 __ cvt_d_w(double_scratch, double_scratch); | |
| 1992 } else { | |
| 1993 // The result must be interpreted as an unsigned 32-bit integer. | |
| 1994 __ mtc1(a2, double_scratch); | |
| 1995 __ Cvt_d_uw(double_scratch, double_scratch, single_scratch); | |
| 1996 } | |
| 1997 | |
| 1998 // Store the result. | |
| 1999 __ sdc1(double_scratch, | |
| 2000 FieldMemOperand(heap_number_result, HeapNumber::kValueOffset)); | |
| 2001 __ Ret(USE_DELAY_SLOT); | |
| 2002 __ mov(v0, heap_number_result); | |
| 2003 | |
| 2004 break; | |
| 2005 } | |
| 2006 | |
| 2007 default: | |
| 2008 UNREACHABLE(); | |
| 2009 } | |
| 2010 | |
| 2011 // We never expect DIV to yield an integer result, so we always generate | |
| 2012 // type transition code for DIV operations expecting an integer result: the | |
| 2013 // code will fall through to this type transition. | |
| 2014 if (transition.is_linked() || | |
| 2015 ((op_ == Token::DIV) && (result_type_ <= BinaryOpIC::INT32))) { | |
| 2016 __ bind(&transition); | |
| 2017 GenerateTypeTransition(masm); | |
| 2018 } | |
| 2019 | |
| 2020 __ bind(&call_runtime); | |
| 2021 { | |
| 2022 FrameScope scope(masm, StackFrame::INTERNAL); | |
| 2023 GenerateRegisterArgsPush(masm); | |
| 2024 GenerateCallRuntime(masm); | |
| 2025 } | |
| 2026 __ Ret(); | |
| 2027 } | |
| 2028 | |
| 2029 | |
| 2030 void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) { | |
| 2031 Label call_runtime; | |
| 2032 | |
| 2033 if (op_ == Token::ADD) { | |
| 2034 // Handle string addition here, because it is the only operation | |
| 2035 // that does not do a ToNumber conversion on the operands. | |
| 2036 GenerateAddStrings(masm); | |
| 2037 } | |
| 2038 | |
| 2039 // Convert oddball arguments to numbers. | |
| 2040 Label check, done; | |
| 2041 __ LoadRoot(t0, Heap::kUndefinedValueRootIndex); | |
| 2042 __ Branch(&check, ne, a1, Operand(t0)); | |
| 2043 if (Token::IsBitOp(op_)) { | |
| 2044 __ li(a1, Operand(Smi::FromInt(0))); | |
| 2045 } else { | |
| 2046 __ LoadRoot(a1, Heap::kNanValueRootIndex); | |
| 2047 } | |
| 2048 __ jmp(&done); | |
| 2049 __ bind(&check); | |
| 2050 __ LoadRoot(t0, Heap::kUndefinedValueRootIndex); | |
| 2051 __ Branch(&done, ne, a0, Operand(t0)); | |
| 2052 if (Token::IsBitOp(op_)) { | |
| 2053 __ li(a0, Operand(Smi::FromInt(0))); | |
| 2054 } else { | |
| 2055 __ LoadRoot(a0, Heap::kNanValueRootIndex); | |
| 2056 } | |
| 2057 __ bind(&done); | |
| 2058 | |
| 2059 GenerateNumberStub(masm); | |
| 2060 } | |
| 2061 | |
| 2062 | |
| 2063 void BinaryOpStub::GenerateNumberStub(MacroAssembler* masm) { | |
| 2064 Label call_runtime, transition; | |
| 2065 BinaryOpStub_GenerateFPOperation( | |
| 2066 masm, left_type_, right_type_, false, | |
| 2067 &transition, &call_runtime, &transition, op_, mode_); | |
| 2068 | |
| 2069 __ bind(&transition); | |
| 2070 GenerateTypeTransition(masm); | |
| 2071 | |
| 2072 __ bind(&call_runtime); | |
| 2073 { | |
| 2074 FrameScope scope(masm, StackFrame::INTERNAL); | |
| 2075 GenerateRegisterArgsPush(masm); | |
| 2076 GenerateCallRuntime(masm); | |
| 2077 } | |
| 2078 __ Ret(); | |
| 2079 } | |
| 2080 | |
| 2081 | |
| 2082 void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) { | |
| 2083 Label call_runtime, call_string_add_or_runtime, transition; | |
| 2084 | |
| 2085 BinaryOpStub_GenerateSmiCode( | |
| 2086 masm, &call_runtime, &call_runtime, op_, ALLOW_HEAPNUMBER_RESULTS, mode_); | |
| 2087 | |
| 2088 BinaryOpStub_GenerateFPOperation( | |
| 2089 masm, left_type_, right_type_, false, | |
| 2090 &call_string_add_or_runtime, &call_runtime, &transition, op_, mode_); | |
| 2091 | |
| 2092 __ bind(&transition); | |
| 2093 GenerateTypeTransition(masm); | |
| 2094 | |
| 2095 __ bind(&call_string_add_or_runtime); | |
| 2096 if (op_ == Token::ADD) { | |
| 2097 GenerateAddStrings(masm); | |
| 2098 } | |
| 2099 | |
| 2100 __ bind(&call_runtime); | |
| 2101 { | |
| 2102 FrameScope scope(masm, StackFrame::INTERNAL); | |
| 2103 GenerateRegisterArgsPush(masm); | |
| 2104 GenerateCallRuntime(masm); | |
| 2105 } | |
| 2106 __ Ret(); | |
| 2107 } | |
| 2108 | |
| 2109 | |
| 2110 void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) { | |
| 2111 ASSERT(op_ == Token::ADD); | |
| 2112 Label left_not_string, call_runtime; | |
| 2113 | |
| 2114 Register left = a1; | |
| 2115 Register right = a0; | |
| 2116 | |
| 2117 // Check if left argument is a string. | |
| 2118 __ JumpIfSmi(left, &left_not_string); | |
| 2119 __ GetObjectType(left, a2, a2); | |
| 2120 __ Branch(&left_not_string, ge, a2, Operand(FIRST_NONSTRING_TYPE)); | |
| 2121 | |
| 2122 StringAddStub string_add_left_stub( | |
| 2123 (StringAddFlags)(STRING_ADD_CHECK_RIGHT | STRING_ADD_ERECT_FRAME)); | |
| 2124 GenerateRegisterArgsPush(masm); | |
| 2125 __ TailCallStub(&string_add_left_stub); | |
| 2126 | |
| 2127 // Left operand is not a string, test right. | |
| 2128 __ bind(&left_not_string); | |
| 2129 __ JumpIfSmi(right, &call_runtime); | |
| 2130 __ GetObjectType(right, a2, a2); | |
| 2131 __ Branch(&call_runtime, ge, a2, Operand(FIRST_NONSTRING_TYPE)); | |
| 2132 | |
| 2133 StringAddStub string_add_right_stub( | |
| 2134 (StringAddFlags)(STRING_ADD_CHECK_LEFT | STRING_ADD_ERECT_FRAME)); | |
| 2135 GenerateRegisterArgsPush(masm); | |
| 2136 __ TailCallStub(&string_add_right_stub); | |
| 2137 | |
| 2138 // At least one argument is not a string. | |
| 2139 __ bind(&call_runtime); | |
| 2140 } | |
| 2141 | |
| 2142 | |
| 2143 void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm, | |
| 2144 Register result, | |
| 2145 Register heap_number_map, | |
| 2146 Register scratch1, | |
| 2147 Register scratch2, | |
| 2148 Label* gc_required, | |
| 2149 OverwriteMode mode) { | |
| 2150 // Code below will scratch result if allocation fails. To keep both arguments | |
| 2151 // intact for the runtime call result cannot be one of these. | |
| 2152 ASSERT(!result.is(a0) && !result.is(a1)); | |
| 2153 | |
| 2154 if (mode == OVERWRITE_LEFT || mode == OVERWRITE_RIGHT) { | |
| 2155 Label skip_allocation, allocated; | |
| 2156 Register overwritable_operand = mode == OVERWRITE_LEFT ? a1 : a0; | |
| 2157 // If the overwritable operand is already an object, we skip the | |
| 2158 // allocation of a heap number. | |
| 2159 __ JumpIfNotSmi(overwritable_operand, &skip_allocation); | |
| 2160 // Allocate a heap number for the result. | |
| 2161 __ AllocateHeapNumber( | |
| 2162 result, scratch1, scratch2, heap_number_map, gc_required); | |
| 2163 __ Branch(&allocated); | |
| 2164 __ bind(&skip_allocation); | |
| 2165 // Use object holding the overwritable operand for result. | |
| 2166 __ mov(result, overwritable_operand); | |
| 2167 __ bind(&allocated); | |
| 2168 } else { | |
| 2169 ASSERT(mode == NO_OVERWRITE); | |
| 2170 __ AllocateHeapNumber( | |
| 2171 result, scratch1, scratch2, heap_number_map, gc_required); | |
| 2172 } | |
| 2173 } | |
| 2174 | |
| 2175 | |
| 2176 void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) { | |
| 2177 __ Push(a1, a0); | |
| 2178 } | |
| 2179 | |
| 2180 | |
| 2181 | |
| 2182 void TranscendentalCacheStub::Generate(MacroAssembler* masm) { | 1249 void TranscendentalCacheStub::Generate(MacroAssembler* masm) { |
| 2183 // Untagged case: double input in f4, double result goes | 1250 // Untagged case: double input in f4, double result goes |
| 2184 // into f4. | 1251 // into f4. |
| 2185 // Tagged case: tagged input on top of stack and in a0, | 1252 // Tagged case: tagged input on top of stack and in a0, |
| 2186 // tagged result (heap number) goes into v0. | 1253 // tagged result (heap number) goes into v0. |
| 2187 | 1254 |
| 2188 Label input_not_smi; | 1255 Label input_not_smi; |
| 2189 Label loaded; | 1256 Label loaded; |
| 2190 Label calculate; | 1257 Label calculate; |
| 2191 Label invalid_cache; | 1258 Label invalid_cache; |
| (...skipping 449 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2641 | 1708 |
| 2642 | 1709 |
| 2643 void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { | 1710 void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { |
| 2644 CEntryStub::GenerateAheadOfTime(isolate); | 1711 CEntryStub::GenerateAheadOfTime(isolate); |
| 2645 WriteInt32ToHeapNumberStub::GenerateFixedRegStubsAheadOfTime(isolate); | 1712 WriteInt32ToHeapNumberStub::GenerateFixedRegStubsAheadOfTime(isolate); |
| 2646 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate); | 1713 StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate); |
| 2647 StubFailureTrampolineStub::GenerateAheadOfTime(isolate); | 1714 StubFailureTrampolineStub::GenerateAheadOfTime(isolate); |
| 2648 RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate); | 1715 RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate); |
| 2649 ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); | 1716 ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); |
| 2650 CreateAllocationSiteStub::GenerateAheadOfTime(isolate); | 1717 CreateAllocationSiteStub::GenerateAheadOfTime(isolate); |
| 1718 BinaryOpStub::GenerateAheadOfTime(isolate); |
| 2651 } | 1719 } |
| 2652 | 1720 |
| 2653 | 1721 |
| 2654 void CodeStub::GenerateFPStubs(Isolate* isolate) { | 1722 void CodeStub::GenerateFPStubs(Isolate* isolate) { |
| 2655 SaveFPRegsMode mode = kSaveFPRegs; | 1723 SaveFPRegsMode mode = kSaveFPRegs; |
| 2656 CEntryStub save_doubles(1, mode); | 1724 CEntryStub save_doubles(1, mode); |
| 2657 StoreBufferOverflowStub stub(mode); | 1725 StoreBufferOverflowStub stub(mode); |
| 2658 // These stubs might already be in the snapshot, detect that and don't | 1726 // These stubs might already be in the snapshot, detect that and don't |
| 2659 // regenerate, which would lead to code stub initialization state being messed | 1727 // regenerate, which would lead to code stub initialization state being messed |
| 2660 // up. | 1728 // up. |
| (...skipping 3045 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5706 // t2: first character of result. | 4774 // t2: first character of result. |
| 5707 StringHelper::GenerateCopyCharacters(masm, t2, t3, a2, t0, false); | 4775 StringHelper::GenerateCopyCharacters(masm, t2, t3, a2, t0, false); |
| 5708 // t2: next character of result. | 4776 // t2: next character of result. |
| 5709 StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, false); | 4777 StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, false); |
| 5710 | 4778 |
| 5711 __ IncrementCounter(counters->string_add_native(), 1, a2, a3); | 4779 __ IncrementCounter(counters->string_add_native(), 1, a2, a3); |
| 5712 __ DropAndRet(2); | 4780 __ DropAndRet(2); |
| 5713 | 4781 |
| 5714 // Just jump to runtime to add the two strings. | 4782 // Just jump to runtime to add the two strings. |
| 5715 __ bind(&call_runtime); | 4783 __ bind(&call_runtime); |
| 5716 if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) { | 4784 __ TailCallRuntime(Runtime::kStringAdd, 2, 1); |
| 5717 GenerateRegisterArgsPop(masm); | |
| 5718 // Build a frame. | |
| 5719 { | |
| 5720 FrameScope scope(masm, StackFrame::INTERNAL); | |
| 5721 GenerateRegisterArgsPush(masm); | |
| 5722 __ CallRuntime(Runtime::kStringAdd, 2); | |
| 5723 } | |
| 5724 __ Ret(); | |
| 5725 } else { | |
| 5726 __ TailCallRuntime(Runtime::kStringAdd, 2, 1); | |
| 5727 } | |
| 5728 | 4785 |
| 5729 if (call_builtin.is_linked()) { | 4786 if (call_builtin.is_linked()) { |
| 5730 __ bind(&call_builtin); | 4787 __ bind(&call_builtin); |
| 5731 if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) { | 4788 __ InvokeBuiltin(builtin_id, JUMP_FUNCTION); |
| 5732 GenerateRegisterArgsPop(masm); | |
| 5733 // Build a frame. | |
| 5734 { | |
| 5735 FrameScope scope(masm, StackFrame::INTERNAL); | |
| 5736 GenerateRegisterArgsPush(masm); | |
| 5737 __ InvokeBuiltin(builtin_id, CALL_FUNCTION); | |
| 5738 } | |
| 5739 __ Ret(); | |
| 5740 } else { | |
| 5741 __ InvokeBuiltin(builtin_id, JUMP_FUNCTION); | |
| 5742 } | |
| 5743 } | 4789 } |
| 5744 } | 4790 } |
| 5745 | 4791 |
| 5746 | 4792 |
| 5747 void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) { | 4793 void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) { |
| 5748 __ push(a0); | 4794 __ push(a0); |
| 5749 __ push(a1); | 4795 __ push(a1); |
| 5750 } | 4796 } |
| 5751 | 4797 |
| 5752 | 4798 |
| (...skipping 1417 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7170 __ bind(&fast_elements_case); | 6216 __ bind(&fast_elements_case); |
| 7171 GenerateCase(masm, FAST_ELEMENTS); | 6217 GenerateCase(masm, FAST_ELEMENTS); |
| 7172 } | 6218 } |
| 7173 | 6219 |
| 7174 | 6220 |
| 7175 #undef __ | 6221 #undef __ |
| 7176 | 6222 |
| 7177 } } // namespace v8::internal | 6223 } } // namespace v8::internal |
| 7178 | 6224 |
| 7179 #endif // V8_TARGET_ARCH_MIPS | 6225 #endif // V8_TARGET_ARCH_MIPS |
| OLD | NEW |