Chromium Code Reviews| Index: src/code-stub-assembler.cc |
| diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc |
| index a72e267f391581a840defb56e0dfe7e5b25a3dc7..c28f8fc50c6aefd63b1bcc877e00cffcb8999e3c 100644 |
| --- a/src/code-stub-assembler.cc |
| +++ b/src/code-stub-assembler.cc |
| @@ -71,6 +71,43 @@ Node* CodeStubAssembler::IntPtrOrSmiConstant(int value, ParameterMode mode) { |
| } |
| } |
| +Node* CodeStubAssembler::IntPtrAddFoldConstants(Node* left, Node* right) { |
| + int32_t left_constant; |
| + bool is_left_constant = ToInt32Constant(left, left_constant); |
| + int32_t right_constant; |
| + bool is_right_constant = ToInt32Constant(right, right_constant); |
| + if (is_left_constant) { |
| + if (is_right_constant) { |
| + return IntPtrConstant(left_constant + right_constant); |
| + } |
| + if (left_constant == 0) { |
| + return right; |
| + } |
| + } else if (is_right_constant) { |
| + if (right_constant == 0) { |
| + return left; |
| + } |
| + } |
| + return IntPtrAdd(left, right); |
| +} |
| + |
| +Node* CodeStubAssembler::IntPtrSubFoldConstants(Node* left, Node* right) { |
| + int32_t left_constant; |
| + bool is_left_constant = ToInt32Constant(left, left_constant); |
| + int32_t right_constant; |
| + bool is_right_constant = ToInt32Constant(right, right_constant); |
| + if (is_left_constant) { |
| + if (is_right_constant) { |
| + return IntPtrConstant(left_constant - right_constant); |
| + } |
| + } else if (is_right_constant) { |
| + if (right_constant == 0) { |
| + return left; |
| + } |
| + } |
| + return IntPtrSub(left, right); |
| +} |
| + |
| Node* CodeStubAssembler::Float64Round(Node* x) { |
| Node* one = Float64Constant(1.0); |
| Node* one_half = Float64Constant(0.5); |
| @@ -1212,8 +1249,10 @@ Node* CodeStubAssembler::AllocateHeapNumberWithValue(Node* value, |
| return result; |
| } |
| -Node* CodeStubAssembler::AllocateSeqOneByteString(int length) { |
| - Node* result = Allocate(SeqOneByteString::SizeFor(length)); |
| +Node* CodeStubAssembler::AllocateSeqOneByteString(int length, |
| + AllocationFlags flags) { |
| + Node* result = Allocate(SeqOneByteString::SizeFor(length), flags); |
| + DCHECK(Heap::RootIsImmortalImmovable(Heap::kOneByteStringMapRootIndex)); |
| StoreMapNoWriteBarrier(result, LoadRoot(Heap::kOneByteStringMapRootIndex)); |
| StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kLengthOffset, |
| SmiConstant(Smi::FromInt(length))); |
| @@ -1223,7 +1262,8 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(int length) { |
| return result; |
| } |
| -Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length) { |
| +Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length, |
| + AllocationFlags flags) { |
| Variable var_result(this, MachineRepresentation::kTagged); |
| // Compute the SeqOneByteString size and check if it fits into new space. |
| @@ -1240,7 +1280,8 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length) { |
| Bind(&if_sizeissmall); |
| { |
| // Just allocate the SeqOneByteString in new space. |
| - Node* result = Allocate(size); |
| + Node* result = Allocate(size, flags); |
| + DCHECK(Heap::RootIsImmortalImmovable(Heap::kOneByteStringMapRootIndex)); |
| StoreMapNoWriteBarrier(result, LoadRoot(Heap::kOneByteStringMapRootIndex)); |
| StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kLengthOffset, |
| SmiFromWord(length)); |
| @@ -1264,8 +1305,10 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length) { |
| return var_result.value(); |
| } |
| -Node* CodeStubAssembler::AllocateSeqTwoByteString(int length) { |
| - Node* result = Allocate(SeqTwoByteString::SizeFor(length)); |
| +Node* CodeStubAssembler::AllocateSeqTwoByteString(int length, |
| + AllocationFlags flags) { |
| + Node* result = Allocate(SeqTwoByteString::SizeFor(length), flags); |
| + DCHECK(Heap::RootIsImmortalImmovable(Heap::kStringMapRootIndex)); |
| StoreMapNoWriteBarrier(result, LoadRoot(Heap::kStringMapRootIndex)); |
| StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kLengthOffset, |
| SmiConstant(Smi::FromInt(length))); |
| @@ -1275,7 +1318,8 @@ Node* CodeStubAssembler::AllocateSeqTwoByteString(int length) { |
| return result; |
| } |
| -Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length) { |
| +Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length, |
| + AllocationFlags flags) { |
| Variable var_result(this, MachineRepresentation::kTagged); |
| // Compute the SeqTwoByteString size and check if it fits into new space. |
| @@ -1292,7 +1336,8 @@ Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length) { |
| Bind(&if_sizeissmall); |
| { |
| // Just allocate the SeqTwoByteString in new space. |
| - Node* result = Allocate(size); |
| + Node* result = Allocate(size, flags); |
| + DCHECK(Heap::RootIsImmortalImmovable(Heap::kStringMapRootIndex)); |
| StoreMapNoWriteBarrier(result, LoadRoot(Heap::kStringMapRootIndex)); |
| StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kLengthOffset, |
| SmiFromWord(length)); |
| @@ -1723,70 +1768,42 @@ void CodeStubAssembler::CopyStringCharacters(compiler::Node* from_string, |
| compiler::Node* from_index, |
| compiler::Node* to_index, |
| compiler::Node* character_count, |
| - String::Encoding encoding) { |
| - Label out(this); |
| - |
| - // Nothing to do for zero characters. |
| - |
| - GotoIf(SmiLessThanOrEqual(character_count, SmiConstant(Smi::kZero)), &out); |
| - |
| - // Calculate offsets into the strings. |
| - |
| - Node* from_offset; |
| - Node* limit_offset; |
| - Node* to_offset; |
| - |
| - { |
| - Node* byte_count = SmiUntag(character_count); |
| - Node* from_byte_index = SmiUntag(from_index); |
| - Node* to_byte_index = SmiUntag(to_index); |
| - if (encoding == String::ONE_BYTE_ENCODING) { |
| - const int offset = SeqOneByteString::kHeaderSize - kHeapObjectTag; |
| - from_offset = IntPtrAdd(IntPtrConstant(offset), from_byte_index); |
| - limit_offset = IntPtrAdd(from_offset, byte_count); |
| - to_offset = IntPtrAdd(IntPtrConstant(offset), to_byte_index); |
| - } else { |
| - STATIC_ASSERT(2 == sizeof(uc16)); |
| - byte_count = WordShl(byte_count, 1); |
| - from_byte_index = WordShl(from_byte_index, 1); |
| - to_byte_index = WordShl(to_byte_index, 1); |
| - |
| - const int offset = SeqTwoByteString::kHeaderSize - kHeapObjectTag; |
| - from_offset = IntPtrAdd(IntPtrConstant(offset), from_byte_index); |
| - limit_offset = IntPtrAdd(from_offset, byte_count); |
| - to_offset = IntPtrAdd(IntPtrConstant(offset), to_byte_index); |
| - } |
| - } |
| - |
| - Variable var_from_offset(this, MachineType::PointerRepresentation()); |
| - Variable var_to_offset(this, MachineType::PointerRepresentation()); |
| - |
| - var_from_offset.Bind(from_offset); |
| - var_to_offset.Bind(to_offset); |
| - |
| - Variable* vars[] = {&var_from_offset, &var_to_offset}; |
| - Label decrement(this, 2, vars); |
| - |
| - Label loop(this, 2, vars); |
| - Goto(&loop); |
| - Bind(&loop); |
| - { |
| - from_offset = var_from_offset.value(); |
| - to_offset = var_to_offset.value(); |
| - |
| - // TODO(jgruber): We could make this faster through larger copy unit sizes. |
| - Node* value = Load(MachineType::Uint8(), from_string, from_offset); |
| - StoreNoWriteBarrier(MachineRepresentation::kWord8, to_string, to_offset, |
| - value); |
| - |
| - Node* new_from_offset = IntPtrAdd(from_offset, IntPtrConstant(1)); |
| - var_from_offset.Bind(new_from_offset); |
| - var_to_offset.Bind(IntPtrAdd(to_offset, IntPtrConstant(1))); |
| - |
| - Branch(WordNotEqual(new_from_offset, limit_offset), &loop, &out); |
| - } |
| - |
| - Bind(&out); |
| + String::Encoding encoding, |
| + ParameterMode mode) { |
| + bool one_byte = encoding == String::ONE_BYTE_ENCODING; |
| + Comment(one_byte ? "CopyStringCharacters ONE_BYTE_ENCODING" |
| + : "CopyStringCharacters TWO_BYTE_ENCODING"); |
| + |
| + ElementsKind kind = one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS; |
| + int header_size = (one_byte ? SeqOneByteString::kHeaderSize |
| + : SeqTwoByteString::kHeaderSize) - |
| + kHeapObjectTag; |
| + Node* from_offset = ElementOffsetFromIndex(from_index, kind, mode); |
| + Node* to_offset = ElementOffsetFromIndex(to_index, kind, mode); |
| + Node* byte_count = ElementOffsetFromIndex(character_count, kind, mode); |
| + Node* limit_offset = IntPtrAddFoldConstants(from_offset, byte_count); |
| + |
| + // Prepare the fast loop |
| + MachineType type = one_byte ? MachineType::Uint8() : MachineType::Uint16(); |
| + MachineRepresentation rep = |
| + one_byte ? MachineRepresentation::kWord8 : MachineRepresentation::kWord16; |
| + int increment = -(1 << ElementsKindToShiftSize(kind)); |
| + |
| + Node* to_string_adjusted = IntPtrAddFoldConstants( |
| + to_string, IntPtrSubFoldConstants(to_offset, from_offset)); |
| + limit_offset = |
| + IntPtrAddFoldConstants(limit_offset, IntPtrConstant(header_size)); |
| + from_offset = |
| + IntPtrAddFoldConstants(from_offset, IntPtrConstant(header_size)); |
| + |
| + BuildFastLoop(MachineType::PointerRepresentation(), limit_offset, from_offset, |
| + [from_string, to_string_adjusted, type, rep]( |
| + CodeStubAssembler* assembler, Node* offset) { |
| + Node* value = assembler->Load(type, from_string, offset); |
| + assembler->StoreNoWriteBarrier(rep, to_string_adjusted, |
| + offset, value); |
| + }, |
| + increment); |
| } |
| Node* CodeStubAssembler::LoadElementAndPrepareForStore(Node* array, |
| @@ -2565,7 +2582,8 @@ Node* AllocAndCopyStringCharacters(CodeStubAssembler* a, Node* context, |
| Node* result = |
| a->AllocateSeqOneByteString(context, a->SmiToWord(character_count)); |
| a->CopyStringCharacters(from, result, from_index, smi_zero, character_count, |
| - String::ONE_BYTE_ENCODING); |
| + String::ONE_BYTE_ENCODING, |
| + CodeStubAssembler::SMI_PARAMETERS); |
| var_result.Bind(result); |
| a->Goto(&end); |
| @@ -2577,7 +2595,8 @@ Node* AllocAndCopyStringCharacters(CodeStubAssembler* a, Node* context, |
| Node* result = |
| a->AllocateSeqTwoByteString(context, a->SmiToWord(character_count)); |
| a->CopyStringCharacters(from, result, from_index, smi_zero, character_count, |
| - String::TWO_BYTE_ENCODING); |
| + String::TWO_BYTE_ENCODING, |
| + CodeStubAssembler::SMI_PARAMETERS); |
| var_result.Bind(result); |
| a->Goto(&end); |
| @@ -2849,9 +2868,9 @@ Node* CodeStubAssembler::StringConcat(Node* context, Node* first, |
| Node* result = AllocateSeqOneByteString(context, SmiToWord(length)); |
| CopyStringCharacters(first, result, smi_zero, smi_zero, first_length, |
| - String::ONE_BYTE_ENCODING); |
| + String::ONE_BYTE_ENCODING, SMI_PARAMETERS); |
| CopyStringCharacters(second, result, smi_zero, first_length, second_length, |
| - String::ONE_BYTE_ENCODING); |
| + String::ONE_BYTE_ENCODING, SMI_PARAMETERS); |
| var_result.Bind(result); |
| Goto(&out); |
| @@ -3275,6 +3294,247 @@ Node* CodeStubAssembler::ToNumber(Node* context, Node* input) { |
| return var_result.value(); |
| } |
| +Node* CodeStubAssembler::ToString(Node* context, Node* input) { |
| + Label is_number(this); |
| + Label runtime(this, Label::kDeferred); |
| + Variable result(this, MachineRepresentation::kTagged); |
| + Label done(this, &result); |
| + |
| + GotoIf(TaggedIsSmi(input), &is_number); |
| + |
| + Node* input_map = LoadMap(input); |
| + Node* input_instance_type = LoadMapInstanceType(input_map); |
| + |
| + result.Bind(input); |
| + GotoIf(IsStringInstanceType(input_instance_type), &done); |
| + |
| + Label not_heap_number(this); |
| + Branch(WordNotEqual(input_map, HeapNumberMapConstant()), ¬_heap_number, |
| + &is_number); |
| + |
| + Bind(&is_number); |
| + result.Bind(NumberToString(context, input)); |
| + Goto(&done); |
| + |
| + Bind(¬_heap_number); |
| + { |
| + GotoIf(Word32NotEqual(input_instance_type, Int32Constant(ODDBALL_TYPE)), |
| + &runtime); |
| + result.Bind(LoadObjectField(input, Oddball::kToStringOffset)); |
| + Goto(&done); |
| + } |
| + |
| + Bind(&runtime); |
| + { |
| + result.Bind(CallRuntime(Runtime::kToString, context, input)); |
| + Goto(&done); |
| + } |
| + |
| + Bind(&done); |
| + return result.value(); |
| +} |
| + |
| +Node* CodeStubAssembler::JSReceiverToPrimitive(Node* context, Node* input) { |
| + STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); |
| + Label if_isreceiver(this, Label::kDeferred), if_isnotreceiver(this); |
| + Variable result(this, MachineRepresentation::kTagged); |
| + Label done(this, &result); |
| + |
| + GotoIf(TaggedIsSmi(input), &if_isnotreceiver); |
| + |
| + Node* map = LoadMap(input); |
| + Node* instance_type = LoadMapInstanceType(map); |
| + Branch(IsJSReceiverInstanceType(instance_type), &if_isreceiver, |
| + &if_isnotreceiver); |
| + |
| + Bind(&if_isreceiver); |
| + { |
| + // Convert {input} to a primitive first passing Number hint. |
| + Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate()); |
| + result.Bind(CallStub(callable, context, input)); |
| + Goto(&done); |
| + } |
| + |
| + Bind(&if_isnotreceiver); |
| + { |
| + result.Bind(input); |
| + Goto(&done); |
| + } |
| + |
| + Bind(&done); |
| + return result.value(); |
| +} |
| + |
| +Node* CodeStubAssembler::NewConsString(Node* context, Node* length, Node* left, |
| + Node* right, AllocationFlags flags) { |
| + // Added string can be a cons string. |
| + Comment("Allocating ConsString"); |
| + Node* left_instance_type = LoadInstanceType(left); |
| + Node* right_instance_type = LoadInstanceType(right); |
| + |
| + // Allocate the cons string object. After the allocation we decide whether the |
| + // cons string is one-byte or two-byte and set the appropriate map. |
| + Node* cons_string = Allocate(IntPtrConstant(ConsString::kSize), flags); |
| + |
| + // Compute intersection and difference of instance types. |
| + Node* anded_instance_types = WordAnd(left_instance_type, right_instance_type); |
| + Node* xored_instance_types = WordXor(left_instance_type, right_instance_type); |
| + |
| + // We create a one-byte cons string if |
| + // 1. both strings are one-byte, or |
| + // 2. at least one of the strings is two-byte, but happens to contain only |
| + // one-byte characters. |
| + // To do this, we check |
| + // 1. if both strings are one-byte, or if the one-byte data hint is set in |
| + // both strings, or |
|
Igor Sheludko
2016/10/13 15:41:33
I wonder why does kOneByteStringTag not imply kOne
danno
2016/10/17 14:23:26
Good question, although before pulling on this str
|
| + // 2. if one of the strings has the one-byte data hint set and the other |
| + // string is one-byte. |
| + STATIC_ASSERT(kOneByteStringTag != 0); |
| + STATIC_ASSERT(kOneByteDataHintMask != 0); |
|
Igor Sheludko
2016/10/13 15:41:33
kOneByteDataHintTag != 0
danno
2016/10/17 14:23:26
Done.
|
| + Label one_byte_map(this); |
| + Label two_byte_map(this); |
| + Label map_done(this); |
| + GotoIf(WordNotEqual( |
| + WordAnd(anded_instance_types, |
| + IntPtrConstant(kOneByteStringTag | kOneByteDataHintTag)), |
| + IntPtrConstant(0)), |
| + &one_byte_map); |
| + Branch(WordNotEqual( |
| + WordAnd(xored_instance_types, |
| + IntPtrConstant(kOneByteStringTag | kOneByteDataHintMask)), |
|
Igor Sheludko
2016/10/13 15:41:33
kOneByteStringTag -> kStringEncodingMask
danno
2016/10/17 14:23:26
Done.
|
| + IntPtrConstant(kOneByteStringTag | kOneByteDataHintTag)), |
| + &two_byte_map, &one_byte_map); |
| + |
| + Bind(&one_byte_map); |
| + Comment("One-byte ConsString"); |
| + DCHECK(Heap::RootIsImmortalImmovable(Heap::kConsOneByteStringMapRootIndex)); |
| + StoreMapNoWriteBarrier(cons_string, |
| + LoadRoot(Heap::kConsOneByteStringMapRootIndex)); |
|
jgruber
2016/10/13 14:10:16
Would it make sense to reuse Allocate{One,Two}Byte
danno
2016/10/17 14:23:26
Yes! Good catch!
|
| + Goto(&map_done); |
| + |
| + Bind(&two_byte_map); |
| + Comment("Two-byte ConsString"); |
| + DCHECK(Heap::RootIsImmortalImmovable(Heap::kConsStringMapRootIndex)); |
| + StoreMapNoWriteBarrier(cons_string, LoadRoot(Heap::kConsStringMapRootIndex)); |
| + Goto(&map_done); |
| + |
| + Bind(&map_done); |
| + StoreObjectFieldNoWriteBarrier(cons_string, String::kHashFieldOffset, |
| + IntPtrConstant(String::kEmptyHashField)); |
| + StoreObjectFieldNoWriteBarrier(cons_string, String::kLengthOffset, |
| + SmiTag(length)); |
| + bool const new_space = !(flags & kPretenured); |
| + if (new_space) { |
| + StoreObjectFieldNoWriteBarrier(cons_string, ConsString::kFirstOffset, left); |
| + StoreObjectFieldNoWriteBarrier(cons_string, ConsString::kSecondOffset, |
| + right); |
| + } else { |
| + StoreObjectField(cons_string, ConsString::kFirstOffset, left); |
| + StoreObjectField(cons_string, ConsString::kSecondOffset, right); |
| + } |
| + |
| + return cons_string; |
| +} |
| + |
| +Node* CodeStubAssembler::StringAdd(Node* context, Node* left, Node* right, |
|
jgruber
2016/10/13 14:10:16
This is basically a more complete version of Strin
danno
2016/10/17 14:23:26
I totally missed that this existed when I ported f
|
| + AllocationFlags flags) { |
| + Label check_right(this); |
| + Label runtime(this, Label::kDeferred); |
| + Label cons(this); |
| + Label non_cons(this); |
| + Variable result(this, MachineRepresentation::kTagged); |
| + Label done(this, &result); |
| + Label done_native(this, &result); |
| + Counters* counters = isolate()->counters(); |
| + |
| + Node* left_length = LoadStringLength(left); |
| + GotoIf(WordNotEqual(IntPtrConstant(0), left_length), &check_right); |
| + result.Bind(right); |
| + Goto(&done_native); |
| + |
| + Bind(&check_right); |
| + Node* right_length = LoadStringLength(right); |
| + GotoIf(WordNotEqual(IntPtrConstant(0), right_length), &cons); |
| + result.Bind(left); |
| + Goto(&done_native); |
| + |
| + Bind(&cons); |
| + Node* new_length = IntPtrAdd(SmiUntag(left_length), SmiUntag(right_length)); |
| + GotoIf(IntPtrLessThan(new_length, IntPtrConstant(0)), &runtime); |
| + GotoIf(IntPtrGreaterThan(new_length, IntPtrConstant(String::kMaxLength)), |
|
Igor Sheludko
2016/10/13 15:41:33
How about doing one unsigned comparison instead of
danno
2016/10/17 14:23:26
Done.
|
| + &runtime); |
| + |
| + GotoIf(IntPtrLessThan(new_length, IntPtrConstant(ConsString::kMinLength)), |
| + &non_cons); |
| + |
| + result.Bind(NewConsString(context, new_length, left, right, flags)); |
| + Goto(&done_native); |
| + |
| + Bind(&non_cons); |
| + |
| + Comment("Full string concatenate"); |
| + Node* left_instance_type = LoadInstanceType(left); |
| + Node* right_instance_type = LoadInstanceType(right); |
| + // Compute intersection and difference of instance types. |
| + |
| + Node* ored_instance_types = WordOr(left_instance_type, right_instance_type); |
| + Node* xored_instance_types = WordXor(left_instance_type, right_instance_type); |
| + |
| + // Check if both strings have the same encoding and both are sequential. |
| + GotoIf(WordNotEqual( |
| + WordAnd(xored_instance_types, IntPtrConstant(kStringEncodingMask)), |
| + IntPtrConstant(0)), |
| + &runtime); |
| + GotoIf(WordNotEqual(WordAnd(ored_instance_types, |
| + IntPtrConstant(kStringRepresentationMask)), |
| + IntPtrConstant(0)), |
| + &runtime); |
| + |
| + Label two_byte(this); |
| + GotoIf(WordEqual( |
| + WordAnd(ored_instance_types, IntPtrConstant(kStringEncodingMask)), |
| + IntPtrConstant(0)), |
|
Igor Sheludko
2016/10/13 15:41:33
0 -> kTwoByteStringTag or STATIC_ASSERT
danno
2016/10/17 14:23:26
Done.
|
| + &two_byte); |
| + // One-byte sequential string case |
| + Node* new_string = AllocateSeqOneByteString(context, new_length); |
| + CopyStringCharacters(left, new_string, SmiConstant(Smi::kZero), |
| + SmiConstant(Smi::kZero), left_length, |
| + String::ONE_BYTE_ENCODING, SMI_PARAMETERS); |
| + CopyStringCharacters(right, new_string, SmiConstant(Smi::kZero), left_length, |
| + right_length, String::ONE_BYTE_ENCODING, SMI_PARAMETERS); |
| + result.Bind(new_string); |
| + Goto(&done_native); |
| + |
| + Bind(&two_byte); |
| + { |
| + // Two-byte sequential string case |
| + new_string = AllocateSeqTwoByteString(context, new_length); |
| + CopyStringCharacters(left, new_string, SmiConstant(Smi::kZero), |
| + SmiConstant(Smi::kZero), left_length, |
| + String::TWO_BYTE_ENCODING, SMI_PARAMETERS); |
| + CopyStringCharacters(right, new_string, SmiConstant(Smi::kZero), |
| + left_length, right_length, String::TWO_BYTE_ENCODING, |
| + SMI_PARAMETERS); |
| + result.Bind(new_string); |
| + Goto(&done_native); |
| + } |
| + |
| + Bind(&runtime); |
| + { |
| + result.Bind(CallRuntime(Runtime::kStringAdd, context, left, right)); |
| + Goto(&done); |
| + } |
| + |
| + Bind(&done_native); |
| + { |
| + IncrementCounter(counters->string_add_native(), 1); |
| + Goto(&done); |
| + } |
| + |
| + Bind(&done); |
| + return result.value(); |
| +} |
| + |
| Node* CodeStubAssembler::ToInteger(Node* context, Node* input, |
| ToIntegerTruncationMode mode) { |
| // We might need to loop once for ToNumber conversion. |
| @@ -4338,16 +4598,14 @@ compiler::Node* CodeStubAssembler::ElementOffsetFromIndex(Node* index_node, |
| if (Is64() && mode == INTEGER_PARAMETERS) { |
| index_node = ChangeInt32ToInt64(index_node); |
| } |
| - if (base_size == 0) { |
| - return (element_size_shift >= 0) |
| - ? WordShl(index_node, IntPtrConstant(element_size_shift)) |
| - : WordShr(index_node, IntPtrConstant(-element_size_shift)); |
| - } |
| - return IntPtrAdd( |
| - IntPtrConstant(base_size), |
| - (element_size_shift >= 0) |
| - ? WordShl(index_node, IntPtrConstant(element_size_shift)) |
| - : WordShr(index_node, IntPtrConstant(-element_size_shift))); |
| + |
| + Node* shifted_index = |
| + (element_size_shift == 0) |
| + ? index_node |
| + : ((element_size_shift > 0) |
| + ? WordShl(index_node, IntPtrConstant(element_size_shift)) |
| + : WordShr(index_node, IntPtrConstant(-element_size_shift))); |
| + return IntPtrAddFoldConstants(IntPtrConstant(base_size), shifted_index); |
| } |
| compiler::Node* CodeStubAssembler::LoadTypeFeedbackVectorForStub() { |