| OLD | NEW |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. | 1 // Copyright 2013 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 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 343 Isolate* isolate, | 343 Isolate* isolate, |
| 344 CodeStubInterfaceDescriptor* descriptor) { | 344 CodeStubInterfaceDescriptor* descriptor) { |
| 345 static Register registers[] = { rcx, rdx, rax }; | 345 static Register registers[] = { rcx, rdx, rax }; |
| 346 descriptor->register_param_count_ = 3; | 346 descriptor->register_param_count_ = 3; |
| 347 descriptor->register_params_ = registers; | 347 descriptor->register_params_ = registers; |
| 348 descriptor->deoptimization_handler_ = | 348 descriptor->deoptimization_handler_ = |
| 349 FUNCTION_ADDR(BinaryOpIC_MissWithAllocationSite); | 349 FUNCTION_ADDR(BinaryOpIC_MissWithAllocationSite); |
| 350 } | 350 } |
| 351 | 351 |
| 352 | 352 |
| 353 void NewStringAddStub::InitializeInterfaceDescriptor( | 353 void StringAddStub::InitializeInterfaceDescriptor( |
| 354 Isolate* isolate, | 354 Isolate* isolate, |
| 355 CodeStubInterfaceDescriptor* descriptor) { | 355 CodeStubInterfaceDescriptor* descriptor) { |
| 356 static Register registers[] = { rdx, rax }; | 356 static Register registers[] = { rdx, rax }; |
| 357 descriptor->register_param_count_ = 2; | 357 descriptor->register_param_count_ = 2; |
| 358 descriptor->register_params_ = registers; | 358 descriptor->register_params_ = registers; |
| 359 descriptor->deoptimization_handler_ = | 359 descriptor->deoptimization_handler_ = |
| 360 Runtime::FunctionForId(Runtime::kStringAdd)->entry; | 360 Runtime::FunctionForId(Runtime::kStringAdd)->entry; |
| 361 } | 361 } |
| 362 | 362 |
| 363 | 363 |
| (...skipping 2873 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3237 if (!result_.is(rax)) { | 3237 if (!result_.is(rax)) { |
| 3238 __ movp(result_, rax); | 3238 __ movp(result_, rax); |
| 3239 } | 3239 } |
| 3240 call_helper.AfterCall(masm); | 3240 call_helper.AfterCall(masm); |
| 3241 __ jmp(&exit_); | 3241 __ jmp(&exit_); |
| 3242 | 3242 |
| 3243 __ Abort(kUnexpectedFallthroughFromCharFromCodeSlowCase); | 3243 __ Abort(kUnexpectedFallthroughFromCharFromCodeSlowCase); |
| 3244 } | 3244 } |
| 3245 | 3245 |
| 3246 | 3246 |
| 3247 void StringAddStub::Generate(MacroAssembler* masm) { | |
| 3248 Label call_runtime, call_builtin; | |
| 3249 Builtins::JavaScript builtin_id = Builtins::ADD; | |
| 3250 | |
| 3251 // Load the two arguments. | |
| 3252 StackArgumentsAccessor args(rsp, 2, ARGUMENTS_DONT_CONTAIN_RECEIVER); | |
| 3253 __ movp(rax, args.GetArgumentOperand(0)); // First argument (left). | |
| 3254 __ movp(rdx, args.GetArgumentOperand(1)); // Second argument (right). | |
| 3255 | |
| 3256 // Make sure that both arguments are strings if not known in advance. | |
| 3257 // Otherwise, at least one of the arguments is definitely a string, | |
| 3258 // and we convert the one that is not known to be a string. | |
| 3259 if ((flags_ & STRING_ADD_CHECK_BOTH) == STRING_ADD_CHECK_BOTH) { | |
| 3260 ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT); | |
| 3261 ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT); | |
| 3262 __ JumpIfSmi(rax, &call_runtime); | |
| 3263 __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, r8); | |
| 3264 __ j(above_equal, &call_runtime); | |
| 3265 | |
| 3266 // First argument is a a string, test second. | |
| 3267 __ JumpIfSmi(rdx, &call_runtime); | |
| 3268 __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9); | |
| 3269 __ j(above_equal, &call_runtime); | |
| 3270 } else if ((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) { | |
| 3271 ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == 0); | |
| 3272 GenerateConvertArgument(masm, 2 * kPointerSize, rax, rbx, rcx, rdi, | |
| 3273 &call_builtin); | |
| 3274 builtin_id = Builtins::STRING_ADD_RIGHT; | |
| 3275 } else if ((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) { | |
| 3276 ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == 0); | |
| 3277 GenerateConvertArgument(masm, 1 * kPointerSize, rdx, rbx, rcx, rdi, | |
| 3278 &call_builtin); | |
| 3279 builtin_id = Builtins::STRING_ADD_LEFT; | |
| 3280 } | |
| 3281 | |
| 3282 // Both arguments are strings. | |
| 3283 // rax: first string | |
| 3284 // rdx: second string | |
| 3285 // Check if either of the strings are empty. In that case return the other. | |
| 3286 Label second_not_zero_length, both_not_zero_length; | |
| 3287 __ movp(rcx, FieldOperand(rdx, String::kLengthOffset)); | |
| 3288 __ SmiTest(rcx); | |
| 3289 __ j(not_zero, &second_not_zero_length, Label::kNear); | |
| 3290 // Second string is empty, result is first string which is already in rax. | |
| 3291 Counters* counters = masm->isolate()->counters(); | |
| 3292 __ IncrementCounter(counters->string_add_native(), 1); | |
| 3293 __ ret(2 * kPointerSize); | |
| 3294 __ bind(&second_not_zero_length); | |
| 3295 __ movp(rbx, FieldOperand(rax, String::kLengthOffset)); | |
| 3296 __ SmiTest(rbx); | |
| 3297 __ j(not_zero, &both_not_zero_length, Label::kNear); | |
| 3298 // First string is empty, result is second string which is in rdx. | |
| 3299 __ movp(rax, rdx); | |
| 3300 __ IncrementCounter(counters->string_add_native(), 1); | |
| 3301 __ ret(2 * kPointerSize); | |
| 3302 | |
| 3303 // Both strings are non-empty. | |
| 3304 // rax: first string | |
| 3305 // rbx: length of first string | |
| 3306 // rcx: length of second string | |
| 3307 // rdx: second string | |
| 3308 // r8: map of first string (if flags_ == NO_STRING_ADD_FLAGS) | |
| 3309 // r9: map of second string (if flags_ == NO_STRING_ADD_FLAGS) | |
| 3310 Label string_add_flat_result, longer_than_two; | |
| 3311 __ bind(&both_not_zero_length); | |
| 3312 | |
| 3313 // If arguments where known to be strings, maps are not loaded to r8 and r9 | |
| 3314 // by the code above. | |
| 3315 if ((flags_ & STRING_ADD_CHECK_BOTH) != STRING_ADD_CHECK_BOTH) { | |
| 3316 __ movp(r8, FieldOperand(rax, HeapObject::kMapOffset)); | |
| 3317 __ movp(r9, FieldOperand(rdx, HeapObject::kMapOffset)); | |
| 3318 } | |
| 3319 // Get the instance types of the two strings as they will be needed soon. | |
| 3320 __ movzxbl(r8, FieldOperand(r8, Map::kInstanceTypeOffset)); | |
| 3321 __ movzxbl(r9, FieldOperand(r9, Map::kInstanceTypeOffset)); | |
| 3322 | |
| 3323 // Look at the length of the result of adding the two strings. | |
| 3324 STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue / 2); | |
| 3325 __ SmiAdd(rbx, rbx, rcx); | |
| 3326 // Use the string table when adding two one character strings, as it | |
| 3327 // helps later optimizations to return an internalized string here. | |
| 3328 __ SmiCompare(rbx, Smi::FromInt(2)); | |
| 3329 __ j(not_equal, &longer_than_two); | |
| 3330 | |
| 3331 // Check that both strings are non-external ASCII strings. | |
| 3332 __ JumpIfBothInstanceTypesAreNotSequentialAscii(r8, r9, rbx, rcx, | |
| 3333 &call_runtime); | |
| 3334 | |
| 3335 // Get the two characters forming the sub string. | |
| 3336 __ movzxbq(rbx, FieldOperand(rax, SeqOneByteString::kHeaderSize)); | |
| 3337 __ movzxbq(rcx, FieldOperand(rdx, SeqOneByteString::kHeaderSize)); | |
| 3338 | |
| 3339 // Try to lookup two character string in string table. If it is not found | |
| 3340 // just allocate a new one. | |
| 3341 Label make_two_character_string, make_flat_ascii_string; | |
| 3342 StringHelper::GenerateTwoCharacterStringTableProbe( | |
| 3343 masm, rbx, rcx, r14, r11, rdi, r15, &make_two_character_string); | |
| 3344 __ IncrementCounter(counters->string_add_native(), 1); | |
| 3345 __ ret(2 * kPointerSize); | |
| 3346 | |
| 3347 __ bind(&make_two_character_string); | |
| 3348 __ Set(rdi, 2); | |
| 3349 __ AllocateAsciiString(rax, rdi, r8, r9, r11, &call_runtime); | |
| 3350 // rbx - first byte: first character | |
| 3351 // rbx - second byte: *maybe* second character | |
| 3352 // Make sure that the second byte of rbx contains the second character. | |
| 3353 __ movzxbq(rcx, FieldOperand(rdx, SeqOneByteString::kHeaderSize)); | |
| 3354 __ shll(rcx, Immediate(kBitsPerByte)); | |
| 3355 __ orl(rbx, rcx); | |
| 3356 // Write both characters to the new string. | |
| 3357 __ movw(FieldOperand(rax, SeqOneByteString::kHeaderSize), rbx); | |
| 3358 __ IncrementCounter(counters->string_add_native(), 1); | |
| 3359 __ ret(2 * kPointerSize); | |
| 3360 | |
| 3361 __ bind(&longer_than_two); | |
| 3362 // Check if resulting string will be flat. | |
| 3363 __ SmiCompare(rbx, Smi::FromInt(ConsString::kMinLength)); | |
| 3364 __ j(below, &string_add_flat_result); | |
| 3365 // Handle exceptionally long strings in the runtime system. | |
| 3366 STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0); | |
| 3367 __ SmiCompare(rbx, Smi::FromInt(String::kMaxLength)); | |
| 3368 __ j(above, &call_runtime); | |
| 3369 | |
| 3370 // If result is not supposed to be flat, allocate a cons string object. If | |
| 3371 // both strings are ASCII the result is an ASCII cons string. | |
| 3372 // rax: first string | |
| 3373 // rbx: length of resulting flat string | |
| 3374 // rdx: second string | |
| 3375 // r8: instance type of first string | |
| 3376 // r9: instance type of second string | |
| 3377 Label non_ascii, allocated, ascii_data; | |
| 3378 __ movl(rcx, r8); | |
| 3379 __ and_(rcx, r9); | |
| 3380 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0); | |
| 3381 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); | |
| 3382 __ testl(rcx, Immediate(kStringEncodingMask)); | |
| 3383 __ j(zero, &non_ascii); | |
| 3384 __ bind(&ascii_data); | |
| 3385 // Allocate an ASCII cons string. | |
| 3386 __ AllocateAsciiConsString(rcx, rdi, no_reg, &call_runtime); | |
| 3387 __ bind(&allocated); | |
| 3388 // Fill the fields of the cons string. | |
| 3389 __ movp(FieldOperand(rcx, ConsString::kLengthOffset), rbx); | |
| 3390 __ movp(FieldOperand(rcx, ConsString::kHashFieldOffset), | |
| 3391 Immediate(String::kEmptyHashField)); | |
| 3392 | |
| 3393 Label skip_write_barrier, after_writing; | |
| 3394 ExternalReference high_promotion_mode = ExternalReference:: | |
| 3395 new_space_high_promotion_mode_active_address(masm->isolate()); | |
| 3396 __ Load(rbx, high_promotion_mode); | |
| 3397 __ testb(rbx, Immediate(1)); | |
| 3398 __ j(zero, &skip_write_barrier); | |
| 3399 | |
| 3400 __ movp(FieldOperand(rcx, ConsString::kFirstOffset), rax); | |
| 3401 __ RecordWriteField(rcx, | |
| 3402 ConsString::kFirstOffset, | |
| 3403 rax, | |
| 3404 rbx, | |
| 3405 kDontSaveFPRegs); | |
| 3406 __ movp(FieldOperand(rcx, ConsString::kSecondOffset), rdx); | |
| 3407 __ RecordWriteField(rcx, | |
| 3408 ConsString::kSecondOffset, | |
| 3409 rdx, | |
| 3410 rbx, | |
| 3411 kDontSaveFPRegs); | |
| 3412 __ jmp(&after_writing); | |
| 3413 | |
| 3414 __ bind(&skip_write_barrier); | |
| 3415 __ movp(FieldOperand(rcx, ConsString::kFirstOffset), rax); | |
| 3416 __ movp(FieldOperand(rcx, ConsString::kSecondOffset), rdx); | |
| 3417 | |
| 3418 __ bind(&after_writing); | |
| 3419 | |
| 3420 __ movp(rax, rcx); | |
| 3421 __ IncrementCounter(counters->string_add_native(), 1); | |
| 3422 __ ret(2 * kPointerSize); | |
| 3423 __ bind(&non_ascii); | |
| 3424 // At least one of the strings is two-byte. Check whether it happens | |
| 3425 // to contain only one byte characters. | |
| 3426 // rcx: first instance type AND second instance type. | |
| 3427 // r8: first instance type. | |
| 3428 // r9: second instance type. | |
| 3429 __ testb(rcx, Immediate(kOneByteDataHintMask)); | |
| 3430 __ j(not_zero, &ascii_data); | |
| 3431 __ xor_(r8, r9); | |
| 3432 STATIC_ASSERT(kOneByteStringTag != 0 && kOneByteDataHintTag != 0); | |
| 3433 __ andb(r8, Immediate(kOneByteStringTag | kOneByteDataHintTag)); | |
| 3434 __ cmpb(r8, Immediate(kOneByteStringTag | kOneByteDataHintTag)); | |
| 3435 __ j(equal, &ascii_data); | |
| 3436 // Allocate a two byte cons string. | |
| 3437 __ AllocateTwoByteConsString(rcx, rdi, no_reg, &call_runtime); | |
| 3438 __ jmp(&allocated); | |
| 3439 | |
| 3440 // We cannot encounter sliced strings or cons strings here since: | |
| 3441 STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength); | |
| 3442 // Handle creating a flat result from either external or sequential strings. | |
| 3443 // Locate the first characters' locations. | |
| 3444 // rax: first string | |
| 3445 // rbx: length of resulting flat string as smi | |
| 3446 // rdx: second string | |
| 3447 // r8: instance type of first string | |
| 3448 // r9: instance type of first string | |
| 3449 Label first_prepared, second_prepared; | |
| 3450 Label first_is_sequential, second_is_sequential; | |
| 3451 __ bind(&string_add_flat_result); | |
| 3452 | |
| 3453 __ SmiToInteger32(r14, FieldOperand(rax, SeqString::kLengthOffset)); | |
| 3454 // r14: length of first string | |
| 3455 STATIC_ASSERT(kSeqStringTag == 0); | |
| 3456 __ testb(r8, Immediate(kStringRepresentationMask)); | |
| 3457 __ j(zero, &first_is_sequential, Label::kNear); | |
| 3458 // Rule out short external string and load string resource. | |
| 3459 STATIC_ASSERT(kShortExternalStringTag != 0); | |
| 3460 __ testb(r8, Immediate(kShortExternalStringMask)); | |
| 3461 __ j(not_zero, &call_runtime); | |
| 3462 __ movp(rcx, FieldOperand(rax, ExternalString::kResourceDataOffset)); | |
| 3463 __ jmp(&first_prepared, Label::kNear); | |
| 3464 __ bind(&first_is_sequential); | |
| 3465 STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize); | |
| 3466 __ lea(rcx, FieldOperand(rax, SeqOneByteString::kHeaderSize)); | |
| 3467 __ bind(&first_prepared); | |
| 3468 | |
| 3469 // Check whether both strings have same encoding. | |
| 3470 __ xorl(r8, r9); | |
| 3471 __ testb(r8, Immediate(kStringEncodingMask)); | |
| 3472 __ j(not_zero, &call_runtime); | |
| 3473 | |
| 3474 __ SmiToInteger32(r15, FieldOperand(rdx, SeqString::kLengthOffset)); | |
| 3475 // r15: length of second string | |
| 3476 STATIC_ASSERT(kSeqStringTag == 0); | |
| 3477 __ testb(r9, Immediate(kStringRepresentationMask)); | |
| 3478 __ j(zero, &second_is_sequential, Label::kNear); | |
| 3479 // Rule out short external string and load string resource. | |
| 3480 STATIC_ASSERT(kShortExternalStringTag != 0); | |
| 3481 __ testb(r9, Immediate(kShortExternalStringMask)); | |
| 3482 __ j(not_zero, &call_runtime); | |
| 3483 __ movp(rdx, FieldOperand(rdx, ExternalString::kResourceDataOffset)); | |
| 3484 __ jmp(&second_prepared, Label::kNear); | |
| 3485 __ bind(&second_is_sequential); | |
| 3486 STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize); | |
| 3487 __ lea(rdx, FieldOperand(rdx, SeqOneByteString::kHeaderSize)); | |
| 3488 __ bind(&second_prepared); | |
| 3489 | |
| 3490 Label non_ascii_string_add_flat_result; | |
| 3491 // r9: instance type of second string | |
| 3492 // First string and second string have the same encoding. | |
| 3493 STATIC_ASSERT(kTwoByteStringTag == 0); | |
| 3494 __ SmiToInteger32(rbx, rbx); | |
| 3495 __ testb(r9, Immediate(kStringEncodingMask)); | |
| 3496 __ j(zero, &non_ascii_string_add_flat_result); | |
| 3497 | |
| 3498 __ bind(&make_flat_ascii_string); | |
| 3499 // Both strings are ASCII strings. As they are short they are both flat. | |
| 3500 __ AllocateAsciiString(rax, rbx, rdi, r8, r9, &call_runtime); | |
| 3501 // rax: result string | |
| 3502 // Locate first character of result. | |
| 3503 __ lea(rbx, FieldOperand(rax, SeqOneByteString::kHeaderSize)); | |
| 3504 // rcx: first char of first string | |
| 3505 // rbx: first character of result | |
| 3506 // r14: length of first string | |
| 3507 StringHelper::GenerateCopyCharacters(masm, rbx, rcx, r14, true); | |
| 3508 // rbx: next character of result | |
| 3509 // rdx: first char of second string | |
| 3510 // r15: length of second string | |
| 3511 StringHelper::GenerateCopyCharacters(masm, rbx, rdx, r15, true); | |
| 3512 __ IncrementCounter(counters->string_add_native(), 1); | |
| 3513 __ ret(2 * kPointerSize); | |
| 3514 | |
| 3515 __ bind(&non_ascii_string_add_flat_result); | |
| 3516 // Both strings are ASCII strings. As they are short they are both flat. | |
| 3517 __ AllocateTwoByteString(rax, rbx, rdi, r8, r9, &call_runtime); | |
| 3518 // rax: result string | |
| 3519 // Locate first character of result. | |
| 3520 __ lea(rbx, FieldOperand(rax, SeqTwoByteString::kHeaderSize)); | |
| 3521 // rcx: first char of first string | |
| 3522 // rbx: first character of result | |
| 3523 // r14: length of first string | |
| 3524 StringHelper::GenerateCopyCharacters(masm, rbx, rcx, r14, false); | |
| 3525 // rbx: next character of result | |
| 3526 // rdx: first char of second string | |
| 3527 // r15: length of second string | |
| 3528 StringHelper::GenerateCopyCharacters(masm, rbx, rdx, r15, false); | |
| 3529 __ IncrementCounter(counters->string_add_native(), 1); | |
| 3530 __ ret(2 * kPointerSize); | |
| 3531 | |
| 3532 // Just jump to runtime to add the two strings. | |
| 3533 __ bind(&call_runtime); | |
| 3534 __ TailCallRuntime(Runtime::kStringAdd, 2, 1); | |
| 3535 | |
| 3536 if (call_builtin.is_linked()) { | |
| 3537 __ bind(&call_builtin); | |
| 3538 __ InvokeBuiltin(builtin_id, JUMP_FUNCTION); | |
| 3539 } | |
| 3540 } | |
| 3541 | |
| 3542 | |
| 3543 void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) { | |
| 3544 __ push(rax); | |
| 3545 __ push(rdx); | |
| 3546 } | |
| 3547 | |
| 3548 | |
| 3549 void StringAddStub::GenerateRegisterArgsPop(MacroAssembler* masm, | |
| 3550 Register temp) { | |
| 3551 __ PopReturnAddressTo(temp); | |
| 3552 __ pop(rdx); | |
| 3553 __ pop(rax); | |
| 3554 __ PushReturnAddressFrom(temp); | |
| 3555 } | |
| 3556 | |
| 3557 | |
| 3558 void StringAddStub::GenerateConvertArgument(MacroAssembler* masm, | |
| 3559 int stack_offset, | |
| 3560 Register arg, | |
| 3561 Register scratch1, | |
| 3562 Register scratch2, | |
| 3563 Register scratch3, | |
| 3564 Label* slow) { | |
| 3565 // First check if the argument is already a string. | |
| 3566 Label not_string, done; | |
| 3567 __ JumpIfSmi(arg, ¬_string); | |
| 3568 __ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1); | |
| 3569 __ j(below, &done); | |
| 3570 | |
| 3571 // Check the number to string cache. | |
| 3572 __ bind(¬_string); | |
| 3573 // Puts the cached result into scratch1. | |
| 3574 __ LookupNumberStringCache(arg, scratch1, scratch2, scratch3, slow); | |
| 3575 __ movp(arg, scratch1); | |
| 3576 __ movp(Operand(rsp, stack_offset), arg); | |
| 3577 __ bind(&done); | |
| 3578 } | |
| 3579 | |
| 3580 | |
| 3581 void StringHelper::GenerateCopyCharacters(MacroAssembler* masm, | |
| 3582 Register dest, | |
| 3583 Register src, | |
| 3584 Register count, | |
| 3585 bool ascii) { | |
| 3586 Label loop; | |
| 3587 __ bind(&loop); | |
| 3588 // This loop just copies one character at a time, as it is only used for very | |
| 3589 // short strings. | |
| 3590 if (ascii) { | |
| 3591 __ movb(kScratchRegister, Operand(src, 0)); | |
| 3592 __ movb(Operand(dest, 0), kScratchRegister); | |
| 3593 __ incq(src); | |
| 3594 __ incq(dest); | |
| 3595 } else { | |
| 3596 __ movzxwl(kScratchRegister, Operand(src, 0)); | |
| 3597 __ movw(Operand(dest, 0), kScratchRegister); | |
| 3598 __ addq(src, Immediate(2)); | |
| 3599 __ addq(dest, Immediate(2)); | |
| 3600 } | |
| 3601 __ decl(count); | |
| 3602 __ j(not_zero, &loop); | |
| 3603 } | |
| 3604 | |
| 3605 | |
| 3606 void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm, | 3247 void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm, |
| 3607 Register dest, | 3248 Register dest, |
| 3608 Register src, | 3249 Register src, |
| 3609 Register count, | 3250 Register count, |
| 3610 bool ascii) { | 3251 bool ascii) { |
| 3611 // Copy characters using rep movs of doublewords. Align destination on 4 byte | 3252 // Copy characters using rep movs of doublewords. Align destination on 4 byte |
| 3612 // boundary before starting rep movs. Copy remaining characters after running | 3253 // boundary before starting rep movs. Copy remaining characters after running |
| 3613 // rep movs. | 3254 // rep movs. |
| 3614 // Count is positive int32, dest and src are character pointers. | 3255 // Count is positive int32, dest and src are character pointers. |
| 3615 ASSERT(dest.is(rdi)); // rep movs destination | 3256 ASSERT(dest.is(rdi)); // rep movs destination |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3652 __ movb(kScratchRegister, Operand(src, 0)); | 3293 __ movb(kScratchRegister, Operand(src, 0)); |
| 3653 __ movb(Operand(dest, 0), kScratchRegister); | 3294 __ movb(Operand(dest, 0), kScratchRegister); |
| 3654 __ incq(src); | 3295 __ incq(src); |
| 3655 __ incq(dest); | 3296 __ incq(dest); |
| 3656 __ decl(count); | 3297 __ decl(count); |
| 3657 __ j(not_zero, &loop); | 3298 __ j(not_zero, &loop); |
| 3658 | 3299 |
| 3659 __ bind(&done); | 3300 __ bind(&done); |
| 3660 } | 3301 } |
| 3661 | 3302 |
| 3662 void StringHelper::GenerateTwoCharacterStringTableProbe(MacroAssembler* masm, | |
| 3663 Register c1, | |
| 3664 Register c2, | |
| 3665 Register scratch1, | |
| 3666 Register scratch2, | |
| 3667 Register scratch3, | |
| 3668 Register scratch4, | |
| 3669 Label* not_found) { | |
| 3670 // Register scratch3 is the general scratch register in this function. | |
| 3671 Register scratch = scratch3; | |
| 3672 | |
| 3673 // Make sure that both characters are not digits as such strings has a | |
| 3674 // different hash algorithm. Don't try to look for these in the string table. | |
| 3675 Label not_array_index; | |
| 3676 __ leal(scratch, Operand(c1, -'0')); | |
| 3677 __ cmpl(scratch, Immediate(static_cast<int>('9' - '0'))); | |
| 3678 __ j(above, ¬_array_index, Label::kNear); | |
| 3679 __ leal(scratch, Operand(c2, -'0')); | |
| 3680 __ cmpl(scratch, Immediate(static_cast<int>('9' - '0'))); | |
| 3681 __ j(below_equal, not_found); | |
| 3682 | |
| 3683 __ bind(¬_array_index); | |
| 3684 // Calculate the two character string hash. | |
| 3685 Register hash = scratch1; | |
| 3686 GenerateHashInit(masm, hash, c1, scratch); | |
| 3687 GenerateHashAddCharacter(masm, hash, c2, scratch); | |
| 3688 GenerateHashGetHash(masm, hash, scratch); | |
| 3689 | |
| 3690 // Collect the two characters in a register. | |
| 3691 Register chars = c1; | |
| 3692 __ shl(c2, Immediate(kBitsPerByte)); | |
| 3693 __ orl(chars, c2); | |
| 3694 | |
| 3695 // chars: two character string, char 1 in byte 0 and char 2 in byte 1. | |
| 3696 // hash: hash of two character string. | |
| 3697 | |
| 3698 // Load the string table. | |
| 3699 Register string_table = c2; | |
| 3700 __ LoadRoot(string_table, Heap::kStringTableRootIndex); | |
| 3701 | |
| 3702 // Calculate capacity mask from the string table capacity. | |
| 3703 Register mask = scratch2; | |
| 3704 __ SmiToInteger32(mask, | |
| 3705 FieldOperand(string_table, StringTable::kCapacityOffset)); | |
| 3706 __ decl(mask); | |
| 3707 | |
| 3708 Register map = scratch4; | |
| 3709 | |
| 3710 // Registers | |
| 3711 // chars: two character string, char 1 in byte 0 and char 2 in byte 1. | |
| 3712 // hash: hash of two character string (32-bit int) | |
| 3713 // string_table: string table | |
| 3714 // mask: capacity mask (32-bit int) | |
| 3715 // map: - | |
| 3716 // scratch: - | |
| 3717 | |
| 3718 // Perform a number of probes in the string table. | |
| 3719 static const int kProbes = 4; | |
| 3720 Label found_in_string_table; | |
| 3721 Label next_probe[kProbes]; | |
| 3722 Register candidate = scratch; // Scratch register contains candidate. | |
| 3723 for (int i = 0; i < kProbes; i++) { | |
| 3724 // Calculate entry in string table. | |
| 3725 __ movl(scratch, hash); | |
| 3726 if (i > 0) { | |
| 3727 __ addl(scratch, Immediate(StringTable::GetProbeOffset(i))); | |
| 3728 } | |
| 3729 __ andl(scratch, mask); | |
| 3730 | |
| 3731 // Load the entry from the string table. | |
| 3732 STATIC_ASSERT(StringTable::kEntrySize == 1); | |
| 3733 __ movp(candidate, | |
| 3734 FieldOperand(string_table, | |
| 3735 scratch, | |
| 3736 times_pointer_size, | |
| 3737 StringTable::kElementsStartOffset)); | |
| 3738 | |
| 3739 // If entry is undefined no string with this hash can be found. | |
| 3740 Label is_string; | |
| 3741 __ CmpObjectType(candidate, ODDBALL_TYPE, map); | |
| 3742 __ j(not_equal, &is_string, Label::kNear); | |
| 3743 | |
| 3744 __ CompareRoot(candidate, Heap::kUndefinedValueRootIndex); | |
| 3745 __ j(equal, not_found); | |
| 3746 // Must be the hole (deleted entry). | |
| 3747 if (FLAG_debug_code) { | |
| 3748 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); | |
| 3749 __ cmpq(kScratchRegister, candidate); | |
| 3750 __ Assert(equal, kOddballInStringTableIsNotUndefinedOrTheHole); | |
| 3751 } | |
| 3752 __ jmp(&next_probe[i]); | |
| 3753 | |
| 3754 __ bind(&is_string); | |
| 3755 | |
| 3756 // If length is not 2 the string is not a candidate. | |
| 3757 __ SmiCompare(FieldOperand(candidate, String::kLengthOffset), | |
| 3758 Smi::FromInt(2)); | |
| 3759 __ j(not_equal, &next_probe[i]); | |
| 3760 | |
| 3761 // We use kScratchRegister as a temporary register in assumption that | |
| 3762 // JumpIfInstanceTypeIsNotSequentialAscii does not use it implicitly | |
| 3763 Register temp = kScratchRegister; | |
| 3764 | |
| 3765 // Check that the candidate is a non-external ASCII string. | |
| 3766 __ movzxbl(temp, FieldOperand(map, Map::kInstanceTypeOffset)); | |
| 3767 __ JumpIfInstanceTypeIsNotSequentialAscii( | |
| 3768 temp, temp, &next_probe[i]); | |
| 3769 | |
| 3770 // Check if the two characters match. | |
| 3771 __ movl(temp, FieldOperand(candidate, SeqOneByteString::kHeaderSize)); | |
| 3772 __ andl(temp, Immediate(0x0000ffff)); | |
| 3773 __ cmpl(chars, temp); | |
| 3774 __ j(equal, &found_in_string_table); | |
| 3775 __ bind(&next_probe[i]); | |
| 3776 } | |
| 3777 | |
| 3778 // No matching 2 character string found by probing. | |
| 3779 __ jmp(not_found); | |
| 3780 | |
| 3781 // Scratch register contains result when we fall through to here. | |
| 3782 Register result = candidate; | |
| 3783 __ bind(&found_in_string_table); | |
| 3784 if (!result.is(rax)) { | |
| 3785 __ movp(rax, result); | |
| 3786 } | |
| 3787 } | |
| 3788 | |
| 3789 | 3303 |
| 3790 void StringHelper::GenerateHashInit(MacroAssembler* masm, | 3304 void StringHelper::GenerateHashInit(MacroAssembler* masm, |
| 3791 Register hash, | 3305 Register hash, |
| 3792 Register character, | 3306 Register character, |
| 3793 Register scratch) { | 3307 Register scratch) { |
| 3794 // hash = (seed + character) + ((seed + character) << 10); | 3308 // hash = (seed + character) + ((seed + character) << 10); |
| 3795 __ LoadRoot(scratch, Heap::kHashSeedRootIndex); | 3309 __ LoadRoot(scratch, Heap::kHashSeedRootIndex); |
| 3796 __ SmiToInteger32(scratch, scratch); | 3310 __ SmiToInteger32(scratch, scratch); |
| 3797 __ addl(scratch, character); | 3311 __ addl(scratch, character); |
| 3798 __ movl(hash, scratch); | 3312 __ movl(hash, scratch); |
| (...skipping 1925 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5724 __ bind(&fast_elements_case); | 5238 __ bind(&fast_elements_case); |
| 5725 GenerateCase(masm, FAST_ELEMENTS); | 5239 GenerateCase(masm, FAST_ELEMENTS); |
| 5726 } | 5240 } |
| 5727 | 5241 |
| 5728 | 5242 |
| 5729 #undef __ | 5243 #undef __ |
| 5730 | 5244 |
| 5731 } } // namespace v8::internal | 5245 } } // namespace v8::internal |
| 5732 | 5246 |
| 5733 #endif // V8_TARGET_ARCH_X64 | 5247 #endif // V8_TARGET_ARCH_X64 |
| OLD | NEW |