OLD | NEW |
1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 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 3533 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3544 frame_->EmitPush(r0); | 3544 frame_->EmitPush(r0); |
3545 } | 3545 } |
3546 | 3546 |
3547 | 3547 |
3548 void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) { | 3548 void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) { |
3549 ASSERT_EQ(2, args->length()); | 3549 ASSERT_EQ(2, args->length()); |
3550 | 3550 |
3551 Load(args->at(0)); | 3551 Load(args->at(0)); |
3552 Load(args->at(1)); | 3552 Load(args->at(1)); |
3553 | 3553 |
3554 frame_->CallRuntime(Runtime::kStringAdd, 2); | 3554 StringAddStub stub(NO_STRING_ADD_FLAGS); |
| 3555 frame_->CallStub(&stub, 2); |
3555 frame_->EmitPush(r0); | 3556 frame_->EmitPush(r0); |
3556 } | 3557 } |
3557 | 3558 |
3558 | 3559 |
3559 void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) { | 3560 void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) { |
3560 ASSERT_EQ(3, args->length()); | 3561 ASSERT_EQ(3, args->length()); |
3561 | 3562 |
3562 Load(args->at(0)); | 3563 Load(args->at(0)); |
3563 Load(args->at(1)); | 3564 Load(args->at(1)); |
3564 Load(args->at(2)); | 3565 Load(args->at(2)); |
(...skipping 1760 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5325 | 5326 |
5326 // Push arguments to the stack | 5327 // Push arguments to the stack |
5327 __ push(r1); | 5328 __ push(r1); |
5328 __ push(r0); | 5329 __ push(r0); |
5329 | 5330 |
5330 if (Token::ADD == operation) { | 5331 if (Token::ADD == operation) { |
5331 // Test for string arguments before calling runtime. | 5332 // Test for string arguments before calling runtime. |
5332 // r1 : first argument | 5333 // r1 : first argument |
5333 // r0 : second argument | 5334 // r0 : second argument |
5334 // sp[0] : second argument | 5335 // sp[0] : second argument |
5335 // sp[1] : first argument | 5336 // sp[4] : first argument |
5336 | 5337 |
5337 Label not_strings, not_string1, string1; | 5338 Label not_strings, not_string1, string1; |
5338 __ tst(r1, Operand(kSmiTagMask)); | 5339 __ tst(r1, Operand(kSmiTagMask)); |
5339 __ b(eq, ¬_string1); | 5340 __ b(eq, ¬_string1); |
5340 __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE); | 5341 __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE); |
5341 __ b(ge, ¬_string1); | 5342 __ b(ge, ¬_string1); |
5342 | 5343 |
5343 // First argument is a a string, test second. | 5344 // First argument is a a string, test second. |
5344 __ tst(r0, Operand(kSmiTagMask)); | 5345 __ tst(r0, Operand(kSmiTagMask)); |
5345 __ b(eq, &string1); | 5346 __ b(eq, &string1); |
5346 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE); | 5347 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE); |
5347 __ b(ge, &string1); | 5348 __ b(ge, &string1); |
5348 | 5349 |
5349 // First and second argument are strings. | 5350 // First and second argument are strings. |
5350 __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1); | 5351 StringAddStub stub(NO_STRING_CHECK_IN_STUB); |
| 5352 __ TailCallStub(&stub); |
5351 | 5353 |
5352 // Only first argument is a string. | 5354 // Only first argument is a string. |
5353 __ bind(&string1); | 5355 __ bind(&string1); |
5354 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS); | 5356 __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS); |
5355 | 5357 |
5356 // First argument was not a string, test second. | 5358 // First argument was not a string, test second. |
5357 __ bind(¬_string1); | 5359 __ bind(¬_string1); |
5358 __ tst(r0, Operand(kSmiTagMask)); | 5360 __ tst(r0, Operand(kSmiTagMask)); |
5359 __ b(eq, ¬_strings); | 5361 __ b(eq, ¬_strings); |
5360 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE); | 5362 __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE); |
5361 __ b(ge, ¬_strings); | 5363 __ b(ge, ¬_strings); |
5362 | 5364 |
5363 // Only second argument is a string. | 5365 // Only second argument is a string. |
5364 __ b(¬_strings); | |
5365 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS); | 5366 __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS); |
5366 | 5367 |
5367 __ bind(¬_strings); | 5368 __ bind(¬_strings); |
5368 } | 5369 } |
5369 | 5370 |
5370 __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return. | 5371 __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return. |
5371 | 5372 |
5372 // We branch here if at least one of r0 and r1 is not a Smi. | 5373 // We branch here if at least one of r0 and r1 is not a Smi. |
5373 __ bind(not_smi); | 5374 __ bind(not_smi); |
5374 if (mode == NO_OVERWRITE) { | 5375 if (mode == NO_OVERWRITE) { |
(...skipping 1459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6834 // This loop just copies one character at a time, as it is only used for very | 6835 // This loop just copies one character at a time, as it is only used for very |
6835 // short strings. | 6836 // short strings. |
6836 if (!ascii) { | 6837 if (!ascii) { |
6837 __ add(count, count, Operand(count), SetCC); | 6838 __ add(count, count, Operand(count), SetCC); |
6838 } else { | 6839 } else { |
6839 __ cmp(count, Operand(0)); | 6840 __ cmp(count, Operand(0)); |
6840 } | 6841 } |
6841 __ b(eq, &done); | 6842 __ b(eq, &done); |
6842 | 6843 |
6843 __ bind(&loop); | 6844 __ bind(&loop); |
| 6845 __ ldrb(scratch, MemOperand(src, 1, PostIndex)); |
| 6846 // Perform sub between load and dependent store to get the load time to |
| 6847 // complete. |
6844 __ sub(count, count, Operand(1), SetCC); | 6848 __ sub(count, count, Operand(1), SetCC); |
6845 __ ldrb(scratch, MemOperand(src, count), pl); | 6849 __ strb(scratch, MemOperand(dest, 1, PostIndex)); |
6846 // Move branch between load and dependent store to not waste the cycle for | |
6847 // each iteration of the loop. It does cost an extra instruction on the | |
6848 // last iteration. | 6850 // last iteration. |
6849 __ b(mi, &done); | 6851 __ b(gt, &loop); |
6850 __ strb(scratch, MemOperand(dest, count)); | 6852 |
6851 __ b(&loop); | |
6852 __ bind(&done); | 6853 __ bind(&done); |
6853 } | 6854 } |
6854 | 6855 |
6855 | 6856 |
6856 enum CopyCharactersFlags { | 6857 enum CopyCharactersFlags { |
6857 COPY_ASCII = 1, | 6858 COPY_ASCII = 1, |
6858 DEST_ALWAYS_ALIGNED = 2 | 6859 DEST_ALWAYS_ALIGNED = 2 |
6859 }; | 6860 }; |
6860 | 6861 |
6861 | 6862 |
(...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7211 __ mov(r0, Operand(Smi::FromInt(GREATER)), LeaveCC, gt); | 7212 __ mov(r0, Operand(Smi::FromInt(GREATER)), LeaveCC, gt); |
7212 __ mov(r0, Operand(Smi::FromInt(LESS)), LeaveCC, lt); | 7213 __ mov(r0, Operand(Smi::FromInt(LESS)), LeaveCC, lt); |
7213 __ Ret(); | 7214 __ Ret(); |
7214 } | 7215 } |
7215 | 7216 |
7216 | 7217 |
7217 void StringCompareStub::Generate(MacroAssembler* masm) { | 7218 void StringCompareStub::Generate(MacroAssembler* masm) { |
7218 Label runtime; | 7219 Label runtime; |
7219 | 7220 |
7220 // Stack frame on entry. | 7221 // Stack frame on entry. |
7221 // sp[0]: return address | 7222 // sp[0]: right string |
7222 // sp[4]: right string | 7223 // sp[4]: left string |
7223 // sp[8]: left string | 7224 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // left |
7224 | 7225 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // right |
7225 __ ldr(r0, MemOperand(sp, 2 * kPointerSize)); // left | |
7226 __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); // right | |
7227 | 7226 |
7228 Label not_same; | 7227 Label not_same; |
7229 __ cmp(r0, r1); | 7228 __ cmp(r0, r1); |
7230 __ b(ne, ¬_same); | 7229 __ b(ne, ¬_same); |
7231 ASSERT_EQ(0, EQUAL); | 7230 ASSERT_EQ(0, EQUAL); |
7232 ASSERT_EQ(0, kSmiTag); | 7231 ASSERT_EQ(0, kSmiTag); |
7233 __ mov(r0, Operand(Smi::FromInt(EQUAL))); | 7232 __ mov(r0, Operand(Smi::FromInt(EQUAL))); |
7234 __ IncrementCounter(&Counters::string_compare_native, 1, r1, r2); | 7233 __ IncrementCounter(&Counters::string_compare_native, 1, r1, r2); |
7235 __ add(sp, sp, Operand(2 * kPointerSize)); | 7234 __ add(sp, sp, Operand(2 * kPointerSize)); |
7236 __ Ret(); | 7235 __ Ret(); |
7237 | 7236 |
7238 __ bind(¬_same); | 7237 __ bind(¬_same); |
7239 | 7238 |
7240 // Check that both objects are sequential ascii strings. | 7239 // Check that both objects are sequential ascii strings. |
7241 __ JumpIfNotBothSequentialAsciiStrings(r0, r1, r2, r3, &runtime); | 7240 __ JumpIfNotBothSequentialAsciiStrings(r0, r1, r2, r3, &runtime); |
7242 | 7241 |
7243 // Compare flat ascii strings natively. Remove arguments from stack first. | 7242 // Compare flat ascii strings natively. Remove arguments from stack first. |
7244 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3); | 7243 __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3); |
7245 __ add(sp, sp, Operand(2 * kPointerSize)); | 7244 __ add(sp, sp, Operand(2 * kPointerSize)); |
7246 GenerateCompareFlatAsciiStrings(masm, r0, r1, r2, r3, r4, r5); | 7245 GenerateCompareFlatAsciiStrings(masm, r0, r1, r2, r3, r4, r5); |
7247 | 7246 |
7248 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater) | 7247 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater) |
7249 // tagged as a small integer. | 7248 // tagged as a small integer. |
7250 __ bind(&runtime); | 7249 __ bind(&runtime); |
7251 __ TailCallRuntime(ExternalReference(Runtime::kStringCompare), 2, 1); | 7250 __ TailCallRuntime(ExternalReference(Runtime::kStringCompare), 2, 1); |
7252 } | 7251 } |
7253 | 7252 |
7254 | 7253 |
| 7254 void StringAddStub::Generate(MacroAssembler* masm) { |
| 7255 Label string_add_runtime; |
| 7256 // Stack on entry: |
| 7257 // sp[0]: second argument. |
| 7258 // sp[4]: first argument. |
| 7259 |
| 7260 // Load the two arguments. |
| 7261 __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument. |
| 7262 __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument. |
| 7263 |
| 7264 // Make sure that both arguments are strings if not known in advance. |
| 7265 if (string_check_) { |
| 7266 ASSERT_EQ(0, kSmiTag); |
| 7267 __ JumpIfEitherSmi(r0, r1, &string_add_runtime); |
| 7268 // Load instance types. |
| 7269 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); |
| 7270 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset)); |
| 7271 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset)); |
| 7272 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset)); |
| 7273 ASSERT_EQ(0, kStringTag); |
| 7274 // If either is not a string, go to runtime. |
| 7275 __ tst(r4, Operand(kIsNotStringMask)); |
| 7276 __ tst(r5, Operand(kIsNotStringMask), eq); |
| 7277 __ b(ne, &string_add_runtime); |
| 7278 } |
| 7279 |
| 7280 // Both arguments are strings. |
| 7281 // r0: first string |
| 7282 // r1: second string |
| 7283 // r4: first string instance type (if string_check_) |
| 7284 // r5: second string instance type (if string_check_) |
| 7285 { |
| 7286 Label strings_not_empty; |
| 7287 // Check if either of the strings are empty. In that case return the other. |
| 7288 __ ldr(r2, FieldMemOperand(r0, String::kLengthOffset)); |
| 7289 __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset)); |
| 7290 __ cmp(r2, Operand(0)); // Test if first string is empty. |
| 7291 __ mov(r0, Operand(r1), LeaveCC, eq); // If first is empty, return second. |
| 7292 __ cmp(r3, Operand(0), ne); // Else test if second string is empty. |
| 7293 __ b(ne, &strings_not_empty); // If either string was empty, return r0. |
| 7294 |
| 7295 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3); |
| 7296 __ add(sp, sp, Operand(2 * kPointerSize)); |
| 7297 __ Ret(); |
| 7298 |
| 7299 __ bind(&strings_not_empty); |
| 7300 } |
| 7301 |
| 7302 // Both strings are non-empty. |
| 7303 // r0: first string |
| 7304 // r1: second string |
| 7305 // r2: length of first string |
| 7306 // r3: length of second string |
| 7307 // r4: first string instance type (if string_check_) |
| 7308 // r5: second string instance type (if string_check_) |
| 7309 // Look at the length of the result of adding the two strings. |
| 7310 Label string_add_flat_result; |
| 7311 // Adding two lengths can't overflow. |
| 7312 ASSERT(String::kMaxLength * 2 > String::kMaxLength); |
| 7313 __ add(r6, r2, Operand(r3)); |
| 7314 // Use the runtime system when adding two one character strings, as it |
| 7315 // contains optimizations for this specific case using the symbol table. |
| 7316 __ cmp(r6, Operand(2)); |
| 7317 __ b(eq, &string_add_runtime); |
| 7318 // Check if resulting string will be flat. |
| 7319 __ cmp(r6, Operand(String::kMinNonFlatLength)); |
| 7320 __ b(lt, &string_add_flat_result); |
| 7321 // Handle exceptionally long strings in the runtime system. |
| 7322 ASSERT((String::kMaxLength & 0x80000000) == 0); |
| 7323 ASSERT(IsPowerOf2(String::kMaxLength + 1)); |
| 7324 // kMaxLength + 1 is representable as shifted literal, kMaxLength is not. |
| 7325 __ cmp(r6, Operand(String::kMaxLength + 1)); |
| 7326 __ b(hs, &string_add_runtime); |
| 7327 |
| 7328 // If result is not supposed to be flat, allocate a cons string object. |
| 7329 // If both strings are ascii the result is an ascii cons string. |
| 7330 if (!string_check_) { |
| 7331 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); |
| 7332 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset)); |
| 7333 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset)); |
| 7334 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset)); |
| 7335 } |
| 7336 Label non_ascii, allocated; |
| 7337 ASSERT_EQ(0, kTwoByteStringTag); |
| 7338 __ tst(r4, Operand(kStringEncodingMask)); |
| 7339 __ tst(r5, Operand(kStringEncodingMask), ne); |
| 7340 __ b(eq, &non_ascii); |
| 7341 |
| 7342 // Allocate an ASCII cons string. |
| 7343 __ AllocateAsciiConsString(r7, r6, r4, r5, &string_add_runtime); |
| 7344 __ bind(&allocated); |
| 7345 // Fill the fields of the cons string. |
| 7346 __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset)); |
| 7347 __ str(r1, FieldMemOperand(r7, ConsString::kSecondOffset)); |
| 7348 __ mov(r0, Operand(r7)); |
| 7349 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3); |
| 7350 __ add(sp, sp, Operand(2 * kPointerSize)); |
| 7351 __ Ret(); |
| 7352 |
| 7353 __ bind(&non_ascii); |
| 7354 // Allocate a two byte cons string. |
| 7355 __ AllocateTwoByteConsString(r7, r6, r4, r5, &string_add_runtime); |
| 7356 __ jmp(&allocated); |
| 7357 |
| 7358 // Handle creating a flat result. First check that both strings are |
| 7359 // sequential and that they have the same encoding. |
| 7360 // r0: first string |
| 7361 // r1: second string |
| 7362 // r2: length of first string |
| 7363 // r3: length of second string |
| 7364 // r4: first string instance type (if string_check_) |
| 7365 // r5: second string instance type (if string_check_) |
| 7366 // r6: sum of lengths. |
| 7367 __ bind(&string_add_flat_result); |
| 7368 if (!string_check_) { |
| 7369 __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); |
| 7370 __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset)); |
| 7371 __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset)); |
| 7372 __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset)); |
| 7373 } |
| 7374 // Check that both strings are sequential. |
| 7375 ASSERT_EQ(0, kSeqStringTag); |
| 7376 __ tst(r4, Operand(kStringRepresentationMask)); |
| 7377 __ tst(r5, Operand(kStringRepresentationMask), eq); |
| 7378 __ b(ne, &string_add_runtime); |
| 7379 // Now check if both strings have the same encoding (ASCII/Two-byte). |
| 7380 // r0: first string |
| 7381 // r1: second string |
| 7382 // r2: length of first string |
| 7383 // r3: length of second string |
| 7384 // r6: sum of lengths. |
| 7385 Label non_ascii_string_add_flat_result; |
| 7386 ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test. |
| 7387 __ eor(r7, r4, Operand(r5)); |
| 7388 __ tst(r7, Operand(kStringEncodingMask)); |
| 7389 __ b(ne, &string_add_runtime); |
| 7390 // And see if it's ASCII or two-byte. |
| 7391 __ tst(r4, Operand(kStringEncodingMask)); |
| 7392 __ b(eq, &non_ascii_string_add_flat_result); |
| 7393 |
| 7394 // Both strings are sequential ASCII strings. We also know that they are |
| 7395 // short (since the sum of the lengths is less than kMinNonFlatLength). |
| 7396 __ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime); |
| 7397 // Locate first character of result. |
| 7398 __ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 7399 // Locate first character of first argument. |
| 7400 __ add(r0, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 7401 // r0: first character of first string |
| 7402 // r1: second string |
| 7403 // r2: length of first string |
| 7404 // r3: length of second string |
| 7405 // r6: first character of result |
| 7406 // r7: result string |
| 7407 GenerateCopyCharacters(masm, r6, r0, r2, r4, true); |
| 7408 |
| 7409 // Load second argument and locate first character. |
| 7410 __ add(r1, r1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| 7411 // r1: first character of second string |
| 7412 // r3: length of second string |
| 7413 // r6: next character of result |
| 7414 // r7: result string |
| 7415 GenerateCopyCharacters(masm, r6, r1, r3, r4, true); |
| 7416 __ mov(r0, Operand(r7)); |
| 7417 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3); |
| 7418 __ add(sp, sp, Operand(2 * kPointerSize)); |
| 7419 __ Ret(); |
| 7420 |
| 7421 __ bind(&non_ascii_string_add_flat_result); |
| 7422 // Both strings are sequential two byte strings. |
| 7423 // r0: first character of first string |
| 7424 // r1: second string |
| 7425 // r2: length of first string |
| 7426 // r3: length of second string |
| 7427 // r6: sum of length of strings. |
| 7428 __ AllocateTwoByteString(r7, r6, r4, r5, r9, &string_add_runtime); |
| 7429 // r0: first string |
| 7430 // r1: second string |
| 7431 // r2: length of first string |
| 7432 // r3: length of second string |
| 7433 // r7: result string |
| 7434 |
| 7435 // Locate first character of result. |
| 7436 __ add(r6, r7, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); |
| 7437 // Locate first character of first argument. |
| 7438 __ add(r0, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); |
| 7439 |
| 7440 // r0: first character of first string |
| 7441 // r1: second string |
| 7442 // r2: length of first string |
| 7443 // r3: length of second string |
| 7444 // r6: first character of result |
| 7445 // r7: result string |
| 7446 GenerateCopyCharacters(masm, r6, r0, r2, r4, false); |
| 7447 |
| 7448 // Locate first character of second argument. |
| 7449 __ add(r1, r1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); |
| 7450 |
| 7451 // r1: first character of second string |
| 7452 // r3: length of second string |
| 7453 // r6: next character of result (after copy of first string) |
| 7454 // r7: result string |
| 7455 GenerateCopyCharacters(masm, r6, r1, r3, r4, false); |
| 7456 |
| 7457 __ mov(r0, Operand(r7)); |
| 7458 __ IncrementCounter(&Counters::string_add_native, 1, r2, r3); |
| 7459 __ add(sp, sp, Operand(2 * kPointerSize)); |
| 7460 __ Ret(); |
| 7461 |
| 7462 // Just jump to runtime to add the two strings. |
| 7463 __ bind(&string_add_runtime); |
| 7464 __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1); |
| 7465 } |
| 7466 |
| 7467 |
7255 #undef __ | 7468 #undef __ |
7256 | 7469 |
7257 } } // namespace v8::internal | 7470 } } // namespace v8::internal |
OLD | NEW |