Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(987)

Side by Side Diff: src/builtins/builtins-string-gen.cc

Issue 2814373002: [string] Widen StringIndexOf fast path (Closed)
Patch Set: Address comments Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/assembler.cc ('k') | src/compiler/code-assembler.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 the V8 project authors. All rights reserved. 1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/builtins/builtins-regexp-gen.h" 5 #include "src/builtins/builtins-regexp-gen.h"
6 #include "src/builtins/builtins-utils-gen.h" 6 #include "src/builtins/builtins-utils-gen.h"
7 #include "src/builtins/builtins.h" 7 #include "src/builtins/builtins.h"
8 #include "src/code-factory.h" 8 #include "src/code-factory.h"
9 #include "src/code-stub-assembler.h" 9 #include "src/code-stub-assembler.h"
10 #include "src/objects.h" 10 #include "src/objects.h"
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 Int32Constant(kShortExternalStringTag))); 52 Int32Constant(kShortExternalStringTag)));
53 var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset, 53 var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset,
54 MachineType::Pointer())); 54 MachineType::Pointer()));
55 Goto(&if_join); 55 Goto(&if_join);
56 } 56 }
57 57
58 BIND(&if_join); 58 BIND(&if_join);
59 return var_data.value(); 59 return var_data.value();
60 } 60 }
61 61
62 Node* LoadOneByteChar(Node* string, Node* index) { 62 void DispatchOnStringEncodings(Node* const lhs_instance_type,
63 return Load(MachineType::Uint8(), string, OneByteCharOffset(index)); 63 Node* const rhs_instance_type,
64 Label* if_one_one, Label* if_one_two,
65 Label* if_two_one, Label* if_two_two) {
66 STATIC_ASSERT(kStringEncodingMask == 0x8);
67 STATIC_ASSERT(kTwoByteStringTag == 0x0);
68 STATIC_ASSERT(kOneByteStringTag == 0x8);
69
70 // First combine the encodings.
71
72 Node* const encoding_mask = Int32Constant(kStringEncodingMask);
73 Node* const lhs_encoding = Word32And(lhs_instance_type, encoding_mask);
74 Node* const rhs_encoding = Word32And(rhs_instance_type, encoding_mask);
75
76 Node* const combined_encodings =
77 Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1));
78
79 // Then dispatch on the combined encoding.
80
81 Label unreachable(this, Label::kDeferred);
82
83 int32_t values[] = {
84 kOneByteStringTag | (kOneByteStringTag >> 1),
85 kOneByteStringTag | (kTwoByteStringTag >> 1),
86 kTwoByteStringTag | (kOneByteStringTag >> 1),
87 kTwoByteStringTag | (kTwoByteStringTag >> 1),
88 };
89 Label* labels[] = {
90 if_one_one, if_one_two, if_two_one, if_two_two,
91 };
92
93 STATIC_ASSERT(arraysize(values) == arraysize(labels));
94 Switch(combined_encodings, &unreachable, values, labels, arraysize(values));
95
96 BIND(&unreachable);
97 Unreachable();
64 } 98 }
65 99
66 Node* OneByteCharAddress(Node* string, Node* index) { 100 template <typename SubjectChar, typename PatternChar>
67 Node* offset = OneByteCharOffset(index); 101 Node* CallSearchStringRaw(Node* const subject_ptr, Node* const subject_length,
68 return IntPtrAdd(string, offset); 102 Node* const search_ptr, Node* const search_length,
103 Node* const start_position) {
104 Node* const function_addr = ExternalConstant(
105 ExternalReference::search_string_raw<SubjectChar, PatternChar>(
106 isolate()));
107 Node* const isolate_ptr =
108 ExternalConstant(ExternalReference::isolate_address(isolate()));
109
110 MachineType type_ptr = MachineType::Pointer();
111 MachineType type_intptr = MachineType::IntPtr();
112
113 Node* const result = CallCFunction6(
114 type_intptr, type_ptr, type_ptr, type_intptr, type_ptr, type_intptr,
115 type_intptr, function_addr, isolate_ptr, subject_ptr, subject_length,
116 search_ptr, search_length, start_position);
117
118 return result;
69 } 119 }
70 120
71 Node* OneByteCharOffset(Node* index) { 121 Node* PointerToStringDataAtIndex(Node* const string_data, Node* const index,
72 return CharOffset(String::ONE_BYTE_ENCODING, index); 122 String::Encoding encoding) {
73 } 123 const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
124 ? UINT8_ELEMENTS
125 : UINT16_ELEMENTS;
74 126
75 Node* CharOffset(String::Encoding encoding, Node* index) { 127 Node* const offset_in_bytes =
76 const int header = SeqOneByteString::kHeaderSize - kHeapObjectTag; 128 ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS);
77 Node* offset = index; 129 return IntPtrAdd(string_data, offset_in_bytes);
78 if (encoding == String::TWO_BYTE_ENCODING) {
79 offset = IntPtrAdd(offset, offset);
80 }
81 offset = IntPtrAdd(offset, IntPtrConstant(header));
82 return offset;
83 }
84
85 void DispatchOnStringInstanceType(Node* const instance_type,
86 Label* if_onebyte_sequential,
87 Label* if_onebyte_external,
88 Label* if_otherwise) {
89 const int kMask = kStringRepresentationMask | kStringEncodingMask;
90 Node* const encoding_and_representation =
91 Word32And(instance_type, Int32Constant(kMask));
92
93 int32_t values[] = {
94 kOneByteStringTag | kSeqStringTag,
95 kOneByteStringTag | kExternalStringTag,
96 };
97 Label* labels[] = {
98 if_onebyte_sequential, if_onebyte_external,
99 };
100 STATIC_ASSERT(arraysize(values) == arraysize(labels));
101
102 Switch(encoding_and_representation, if_otherwise, values, labels,
103 arraysize(values));
104 } 130 }
105 131
106 void GenerateStringEqual(Node* context, Node* left, Node* right); 132 void GenerateStringEqual(Node* context, Node* left, Node* right);
107 void GenerateStringRelationalComparison(Node* context, Node* left, 133 void GenerateStringRelationalComparison(Node* context, Node* left,
108 Node* right, 134 Node* right,
109 RelationalComparisonMode mode); 135 RelationalComparisonMode mode);
110 136
111 Node* ToSmiBetweenZeroAnd(Node* context, Node* value, Node* limit); 137 Node* ToSmiBetweenZeroAnd(Node* context, Node* value, Node* limit);
112 138
113 Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index, 139 Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index,
114 UnicodeEncoding encoding); 140 UnicodeEncoding encoding);
115 141
116 void StringIndexOf(Node* receiver, Node* instance_type, Node* search_string, 142 void StringIndexOf(Node* const subject_string,
117 Node* search_string_instance_type, Node* position, 143 Node* const subject_instance_type,
144 Node* const search_string,
145 Node* const search_instance_type, Node* const position,
118 std::function<void(Node*)> f_return); 146 std::function<void(Node*)> f_return);
119 147
120 Node* IndexOfDollarChar(Node* const context, Node* const string); 148 Node* IndexOfDollarChar(Node* const context, Node* const string);
121 149
122 Node* IsNullOrUndefined(Node* const value); 150 Node* IsNullOrUndefined(Node* const value);
123 void RequireObjectCoercible(Node* const context, Node* const value, 151 void RequireObjectCoercible(Node* const context, Node* const value,
124 const char* method_name); 152 const char* method_name);
125 153
126 Node* SmiIsNegative(Node* const value) { 154 Node* SmiIsNegative(Node* const value) {
127 return SmiLessThan(value, SmiConstant(0)); 155 return SmiLessThan(value, SmiConstant(0));
(...skipping 577 matching lines...) Expand 10 before | Expand all | Expand 10 after
705 CodeStubAssembler::VariableList({&var_result}, zone()), 733 CodeStubAssembler::VariableList({&var_result}, zone()),
706 [this, context, &var_result](Node* arg) { 734 [this, context, &var_result](Node* arg) {
707 arg = CallStub(CodeFactory::ToString(isolate()), context, arg); 735 arg = CallStub(CodeFactory::ToString(isolate()), context, arg);
708 var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context, 736 var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
709 var_result.value(), arg)); 737 var_result.value(), arg));
710 }); 738 });
711 arguments.PopAndReturn(var_result.value()); 739 arguments.PopAndReturn(var_result.value());
712 } 740 }
713 741
714 void StringBuiltinsAssembler::StringIndexOf( 742 void StringBuiltinsAssembler::StringIndexOf(
715 Node* receiver, Node* instance_type, Node* search_string, 743 Node* const subject_string, Node* const subject_instance_type,
716 Node* search_string_instance_type, Node* position, 744 Node* const search_string, Node* const search_instance_type,
717 std::function<void(Node*)> f_return) { 745 Node* const position, std::function<void(Node*)> f_return) {
718 CSA_ASSERT(this, IsString(receiver)); 746 CSA_ASSERT(this, IsString(subject_string));
719 CSA_ASSERT(this, IsString(search_string)); 747 CSA_ASSERT(this, IsString(search_string));
720 CSA_ASSERT(this, TaggedIsSmi(position)); 748 CSA_ASSERT(this, TaggedIsSmi(position));
721 749
722 Label zero_length_needle(this), 750 Node* const int_zero = IntPtrConstant(0);
723 call_runtime_unchecked(this, Label::kDeferred), return_minus_1(this),
724 check_search_string(this), continue_fast_path(this);
725 751
726 Node* const int_zero = IntPtrConstant(0);
727 VARIABLE(var_needle_byte, MachineType::PointerRepresentation(), int_zero); 752 VARIABLE(var_needle_byte, MachineType::PointerRepresentation(), int_zero);
728 VARIABLE(var_string_addr, MachineType::PointerRepresentation(), int_zero); 753 VARIABLE(var_string_addr, MachineType::PointerRepresentation(), int_zero);
729 754
730 Node* needle_length = SmiUntag(LoadStringLength(search_string)); 755 Node* const search_length = SmiUntag(LoadStringLength(search_string));
731 // Use faster/complex runtime fallback for long search strings. 756 Node* const subject_length = SmiUntag(LoadStringLength(subject_string));
732 GotoIf(IntPtrLessThan(IntPtrConstant(1), needle_length), 757 Node* const start_position = IntPtrMax(SmiUntag(position), int_zero);
733 &call_runtime_unchecked);
734 Node* string_length = SmiUntag(LoadStringLength(receiver));
735 Node* start_position = IntPtrMax(SmiUntag(position), int_zero);
736 758
737 GotoIf(IntPtrEqual(int_zero, needle_length), &zero_length_needle); 759 Label zero_length_needle(this), return_minus_1(this);
738 // Check that the needle fits in the start position. 760 {
739 GotoIfNot(IntPtrLessThanOrEqual(needle_length, 761 GotoIf(IntPtrEqual(int_zero, search_length), &zero_length_needle);
740 IntPtrSub(string_length, start_position)),
741 &return_minus_1);
742 762
743 // Load the string address. 763 // Check that the needle fits in the start position.
764 GotoIfNot(IntPtrLessThanOrEqual(search_length,
765 IntPtrSub(subject_length, start_position)),
766 &return_minus_1);
767 }
768
769 // Try to unpack subject and search strings. Bail to runtime if either needs
770 // to be flattened.
771 ToDirectStringAssembler subject_to_direct(state(), subject_string);
772 ToDirectStringAssembler search_to_direct(state(), search_string);
773
774 Label call_runtime_unchecked(this, Label::kDeferred);
775
776 subject_to_direct.TryToDirect(&call_runtime_unchecked);
777 search_to_direct.TryToDirect(&call_runtime_unchecked);
778
779 // Load pointers to string data.
780 Node* const subject_ptr =
781 subject_to_direct.PointerToData(&call_runtime_unchecked);
782 Node* const search_ptr =
783 search_to_direct.PointerToData(&call_runtime_unchecked);
784
785 Node* const subject_offset = subject_to_direct.offset();
786 Node* const search_offset = search_to_direct.offset();
787
788 // Like String::IndexOf, the actual matching is done by the optimized
789 // SearchString method in string-search.h. Dispatch based on string instance
790 // types, then call straight into C++ for matching.
791
792 CSA_ASSERT(this, IntPtrGreaterThan(search_length, int_zero));
793 CSA_ASSERT(this, IntPtrGreaterThanOrEqual(start_position, int_zero));
794 CSA_ASSERT(this, IntPtrGreaterThanOrEqual(subject_length, start_position));
795 CSA_ASSERT(this,
796 IntPtrLessThanOrEqual(search_length,
797 IntPtrSub(subject_length, start_position)));
798
799 Label one_one(this), one_two(this), two_one(this), two_two(this);
800 DispatchOnStringEncodings(subject_to_direct.instance_type(),
801 search_to_direct.instance_type(), &one_one,
802 &one_two, &two_one, &two_two);
803
804 typedef const uint8_t onebyte_t;
805 typedef const uc16 twobyte_t;
806
807 BIND(&one_one);
744 { 808 {
745 Label if_onebyte_sequential(this); 809 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
746 Label if_onebyte_external(this, Label::kDeferred); 810 subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
811 Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
812 search_ptr, search_offset, String::ONE_BYTE_ENCODING);
747 813
748 // Only support one-byte strings on the fast path. 814 Label direct_memchr_call(this), generic_fast_path(this);
749 DispatchOnStringInstanceType(instance_type, &if_onebyte_sequential, 815 Branch(IntPtrEqual(search_length, IntPtrConstant(1)), &direct_memchr_call,
750 &if_onebyte_external, &call_runtime_unchecked); 816 &generic_fast_path);
751 817
752 BIND(&if_onebyte_sequential); 818 // An additional fast path that calls directly into memchr for 1-length
819 // search strings.
820 BIND(&direct_memchr_call);
753 { 821 {
754 var_string_addr.Bind( 822 Node* const string_addr = IntPtrAdd(adjusted_subject_ptr, start_position);
755 OneByteCharAddress(BitcastTaggedToWord(receiver), start_position)); 823 Node* const search_length = IntPtrSub(subject_length, start_position);
756 Goto(&check_search_string); 824 Node* const search_byte =
825 ChangeInt32ToIntPtr(Load(MachineType::Uint8(), adjusted_search_ptr));
826
827 Node* const memchr =
828 ExternalConstant(ExternalReference::libc_memchr_function(isolate()));
829 Node* const result_address =
830 CallCFunction3(MachineType::Pointer(), MachineType::Pointer(),
831 MachineType::IntPtr(), MachineType::UintPtr(), memchr,
832 string_addr, search_byte, search_length);
833 GotoIf(WordEqual(result_address, int_zero), &return_minus_1);
834 Node* const result_index =
835 IntPtrAdd(IntPtrSub(result_address, string_addr), start_position);
836 f_return(SmiTag(result_index));
757 } 837 }
758 838
759 BIND(&if_onebyte_external); 839 BIND(&generic_fast_path);
760 { 840 {
761 Node* const unpacked = TryDerefExternalString(receiver, instance_type, 841 Node* const result = CallSearchStringRaw<onebyte_t, onebyte_t>(
762 &call_runtime_unchecked); 842 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
763 var_string_addr.Bind(OneByteCharAddress(unpacked, start_position)); 843 search_length, start_position);
764 Goto(&check_search_string); 844 f_return(SmiTag(result));
765 } 845 }
766 } 846 }
767 847
768 // Load the needle character. 848 BIND(&one_two);
769 BIND(&check_search_string);
770 { 849 {
771 Label if_onebyte_sequential(this); 850 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
772 Label if_onebyte_external(this, Label::kDeferred); 851 subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
852 Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
853 search_ptr, search_offset, String::TWO_BYTE_ENCODING);
773 854
774 DispatchOnStringInstanceType(search_string_instance_type, 855 Node* const result = CallSearchStringRaw<onebyte_t, twobyte_t>(
775 &if_onebyte_sequential, &if_onebyte_external, 856 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
776 &call_runtime_unchecked); 857 search_length, start_position);
777 858 f_return(SmiTag(result));
778 BIND(&if_onebyte_sequential);
779 {
780 var_needle_byte.Bind(
781 ChangeInt32ToIntPtr(LoadOneByteChar(search_string, int_zero)));
782 Goto(&continue_fast_path);
783 }
784
785 BIND(&if_onebyte_external);
786 {
787 Node* const unpacked = TryDerefExternalString(
788 search_string, search_string_instance_type, &call_runtime_unchecked);
789 var_needle_byte.Bind(
790 ChangeInt32ToIntPtr(LoadOneByteChar(unpacked, int_zero)));
791 Goto(&continue_fast_path);
792 }
793 } 859 }
794 860
795 BIND(&continue_fast_path); 861 BIND(&two_one);
796 { 862 {
797 Node* needle_byte = var_needle_byte.value(); 863 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
798 Node* string_addr = var_string_addr.value(); 864 subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
799 Node* search_length = IntPtrSub(string_length, start_position); 865 Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
800 // Call out to the highly optimized memchr to perform the actual byte 866 search_ptr, search_offset, String::ONE_BYTE_ENCODING);
801 // search. 867
802 Node* memchr = 868 Node* const result = CallSearchStringRaw<twobyte_t, onebyte_t>(
803 ExternalConstant(ExternalReference::libc_memchr_function(isolate())); 869 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
804 Node* result_address = 870 search_length, start_position);
805 CallCFunction3(MachineType::Pointer(), MachineType::Pointer(), 871 f_return(SmiTag(result));
806 MachineType::IntPtr(), MachineType::UintPtr(), memchr, 872 }
807 string_addr, needle_byte, search_length); 873
808 GotoIf(WordEqual(result_address, int_zero), &return_minus_1); 874 BIND(&two_two);
809 Node* result_index = 875 {
810 IntPtrAdd(IntPtrSub(result_address, string_addr), start_position); 876 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
811 f_return(SmiTag(result_index)); 877 subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
878 Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
879 search_ptr, search_offset, String::TWO_BYTE_ENCODING);
880
881 Node* const result = CallSearchStringRaw<twobyte_t, twobyte_t>(
882 adjusted_subject_ptr, subject_length, adjusted_search_ptr,
883 search_length, start_position);
884 f_return(SmiTag(result));
812 } 885 }
813 886
814 BIND(&return_minus_1); 887 BIND(&return_minus_1);
815 f_return(SmiConstant(-1)); 888 f_return(SmiConstant(-1));
816 889
817 BIND(&zero_length_needle); 890 BIND(&zero_length_needle);
818 { 891 {
819 Comment("0-length search_string"); 892 Comment("0-length search_string");
820 f_return(SmiTag(IntPtrMin(string_length, start_position))); 893 f_return(SmiTag(IntPtrMin(subject_length, start_position)));
821 } 894 }
822 895
823 BIND(&call_runtime_unchecked); 896 BIND(&call_runtime_unchecked);
824 { 897 {
825 // Simplified version of the runtime call where the types of the arguments 898 // Simplified version of the runtime call where the types of the arguments
826 // are already known due to type checks in this stub. 899 // are already known due to type checks in this stub.
827 Comment("Call Runtime Unchecked"); 900 Comment("Call Runtime Unchecked");
828 Node* result = CallRuntime(Runtime::kStringIndexOfUnchecked, SmiConstant(0), 901 Node* result = CallRuntime(Runtime::kStringIndexOfUnchecked, SmiConstant(0),
829 receiver, search_string, position); 902 subject_string, search_string, position);
830 f_return(result); 903 f_return(result);
831 } 904 }
832 } 905 }
833 906
834 // ES6 String.prototype.indexOf(searchString [, position]) 907 // ES6 String.prototype.indexOf(searchString [, position])
835 // #sec-string.prototype.indexof 908 // #sec-string.prototype.indexof
836 // Unchecked helper for builtins lowering. 909 // Unchecked helper for builtins lowering.
837 TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) { 910 TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) {
838 Node* receiver = Parameter(Descriptor::kReceiver); 911 Node* receiver = Parameter(Descriptor::kReceiver);
839 Node* search_string = Parameter(Descriptor::kSearchString); 912 Node* search_string = Parameter(Descriptor::kSearchString);
(...skipping 866 matching lines...) Expand 10 before | Expand all | Expand 10 after
1706 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, 1779 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
1707 HeapConstant(factory()->NewStringFromAsciiChecked( 1780 HeapConstant(factory()->NewStringFromAsciiChecked(
1708 "String Iterator.prototype.next", TENURED)), 1781 "String Iterator.prototype.next", TENURED)),
1709 iterator); 1782 iterator);
1710 Unreachable(); 1783 Unreachable();
1711 } 1784 }
1712 } 1785 }
1713 1786
1714 } // namespace internal 1787 } // namespace internal
1715 } // namespace v8 1788 } // namespace v8
OLDNEW
« no previous file with comments | « src/assembler.cc ('k') | src/compiler/code-assembler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698