| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 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 #include "src/code-stub-assembler.h" | 4 #include "src/code-stub-assembler.h" |
| 5 #include "src/code-factory.h" | 5 #include "src/code-factory.h" |
| 6 #include "src/frames-inl.h" | 6 #include "src/frames-inl.h" |
| 7 #include "src/frames.h" | 7 #include "src/frames.h" |
| 8 | 8 |
| 9 namespace v8 { | 9 namespace v8 { |
| 10 namespace internal { | 10 namespace internal { |
| (...skipping 2886 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2897 Int32Constant(kOneByteStringTag)); | 2897 Int32Constant(kOneByteStringTag)); |
| 2898 } | 2898 } |
| 2899 | 2899 |
| 2900 Node* CodeStubAssembler::IsSequentialStringInstanceType(Node* instance_type) { | 2900 Node* CodeStubAssembler::IsSequentialStringInstanceType(Node* instance_type) { |
| 2901 CSA_ASSERT(this, IsStringInstanceType(instance_type)); | 2901 CSA_ASSERT(this, IsStringInstanceType(instance_type)); |
| 2902 return Word32Equal( | 2902 return Word32Equal( |
| 2903 Word32And(instance_type, Int32Constant(kStringRepresentationMask)), | 2903 Word32And(instance_type, Int32Constant(kStringRepresentationMask)), |
| 2904 Int32Constant(kSeqStringTag)); | 2904 Int32Constant(kSeqStringTag)); |
| 2905 } | 2905 } |
| 2906 | 2906 |
| 2907 Node* CodeStubAssembler::IsExternalStringInstanceType(Node* instance_type) { |
| 2908 CSA_ASSERT(this, IsStringInstanceType(instance_type)); |
| 2909 return Word32Equal( |
| 2910 Word32And(instance_type, Int32Constant(kStringRepresentationMask)), |
| 2911 Int32Constant(kExternalStringTag)); |
| 2912 } |
| 2913 |
| 2914 Node* CodeStubAssembler::IsShortExternalStringInstanceType( |
| 2915 Node* instance_type) { |
| 2916 CSA_ASSERT(this, IsStringInstanceType(instance_type)); |
| 2917 STATIC_ASSERT(kShortExternalStringTag != 0); |
| 2918 return Word32NotEqual( |
| 2919 Word32And(instance_type, Int32Constant(kShortExternalStringMask)), |
| 2920 Int32Constant(0)); |
| 2921 } |
| 2922 |
| 2907 Node* CodeStubAssembler::IsJSReceiverInstanceType(Node* instance_type) { | 2923 Node* CodeStubAssembler::IsJSReceiverInstanceType(Node* instance_type) { |
| 2908 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); | 2924 STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); |
| 2909 return Int32GreaterThanOrEqual(instance_type, | 2925 return Int32GreaterThanOrEqual(instance_type, |
| 2910 Int32Constant(FIRST_JS_RECEIVER_TYPE)); | 2926 Int32Constant(FIRST_JS_RECEIVER_TYPE)); |
| 2911 } | 2927 } |
| 2912 | 2928 |
| 2913 Node* CodeStubAssembler::IsJSReceiver(Node* object) { | 2929 Node* CodeStubAssembler::IsJSReceiver(Node* object) { |
| 2914 STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); | 2930 STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); |
| 2915 return IsJSReceiverInstanceType(LoadInstanceType(object)); | 2931 return IsJSReceiverInstanceType(LoadInstanceType(object)); |
| 2916 } | 2932 } |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3006 LoadRoot(Heap::kUnseededNumberDictionaryMapRootIndex)); | 3022 LoadRoot(Heap::kUnseededNumberDictionaryMapRootIndex)); |
| 3007 } | 3023 } |
| 3008 | 3024 |
| 3009 Node* CodeStubAssembler::IsJSFunction(Node* object) { | 3025 Node* CodeStubAssembler::IsJSFunction(Node* object) { |
| 3010 return HasInstanceType(object, JS_FUNCTION_TYPE); | 3026 return HasInstanceType(object, JS_FUNCTION_TYPE); |
| 3011 } | 3027 } |
| 3012 | 3028 |
| 3013 Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index, | 3029 Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index, |
| 3014 ParameterMode parameter_mode) { | 3030 ParameterMode parameter_mode) { |
| 3015 CSA_ASSERT(this, IsString(string)); | 3031 CSA_ASSERT(this, IsString(string)); |
| 3032 |
| 3016 // Translate the {index} into a Word. | 3033 // Translate the {index} into a Word. |
| 3017 index = ParameterToWord(index, parameter_mode); | 3034 Node* const int_index = ParameterToWord(index, parameter_mode); |
| 3018 | 3035 |
| 3019 // We may need to loop in case of cons, thin, or sliced strings. | |
| 3020 Variable var_index(this, MachineType::PointerRepresentation(), index); | |
| 3021 Variable var_string(this, MachineRepresentation::kTagged, string); | |
| 3022 Variable var_result(this, MachineRepresentation::kWord32); | 3036 Variable var_result(this, MachineRepresentation::kWord32); |
| 3023 Variable* loop_vars[] = {&var_index, &var_string}; | 3037 |
| 3024 Label done_loop(this, &var_result), loop(this, 2, loop_vars); | 3038 Label out(this, &var_result), runtime_generic(this), runtime_external(this); |
| 3025 Goto(&loop); | 3039 |
| 3026 Bind(&loop); | 3040 ToDirectStringAssembler to_direct(state(), string); |
| 3041 Node* const direct_string = to_direct.TryToDirect(&runtime_generic); |
| 3042 Node* const offset = IntPtrAdd(int_index, to_direct.offset()); |
| 3043 Node* const instance_type = to_direct.instance_type(); |
| 3044 |
| 3045 Node* const string_data = to_direct.PointerToData(&runtime_external); |
| 3046 |
| 3047 // Check if the {string} is a TwoByteSeqString or a OneByteSeqString. |
| 3048 Label if_stringistwobyte(this), if_stringisonebyte(this); |
| 3049 Branch(IsOneByteStringInstanceType(instance_type), &if_stringisonebyte, |
| 3050 &if_stringistwobyte); |
| 3051 |
| 3052 Bind(&if_stringisonebyte); |
| 3027 { | 3053 { |
| 3028 // Load the current {index}. | 3054 var_result.Bind(Load(MachineType::Uint8(), string_data, offset)); |
| 3029 index = var_index.value(); | 3055 Goto(&out); |
| 3030 | |
| 3031 // Load the current {string}. | |
| 3032 string = var_string.value(); | |
| 3033 | |
| 3034 // Load the instance type of the {string}. | |
| 3035 Node* string_instance_type = LoadInstanceType(string); | |
| 3036 | |
| 3037 // Check if the {string} is a SeqString. | |
| 3038 Label if_stringissequential(this), if_stringisnotsequential(this); | |
| 3039 Branch(Word32Equal(Word32And(string_instance_type, | |
| 3040 Int32Constant(kStringRepresentationMask)), | |
| 3041 Int32Constant(kSeqStringTag)), | |
| 3042 &if_stringissequential, &if_stringisnotsequential); | |
| 3043 | |
| 3044 Bind(&if_stringissequential); | |
| 3045 { | |
| 3046 // Check if the {string} is a TwoByteSeqString or a OneByteSeqString. | |
| 3047 Label if_stringistwobyte(this), if_stringisonebyte(this); | |
| 3048 Branch(Word32Equal(Word32And(string_instance_type, | |
| 3049 Int32Constant(kStringEncodingMask)), | |
| 3050 Int32Constant(kTwoByteStringTag)), | |
| 3051 &if_stringistwobyte, &if_stringisonebyte); | |
| 3052 | |
| 3053 Bind(&if_stringisonebyte); | |
| 3054 { | |
| 3055 var_result.Bind( | |
| 3056 Load(MachineType::Uint8(), string, | |
| 3057 IntPtrAdd(index, IntPtrConstant(SeqOneByteString::kHeaderSize - | |
| 3058 kHeapObjectTag)))); | |
| 3059 Goto(&done_loop); | |
| 3060 } | |
| 3061 | |
| 3062 Bind(&if_stringistwobyte); | |
| 3063 { | |
| 3064 var_result.Bind( | |
| 3065 Load(MachineType::Uint16(), string, | |
| 3066 IntPtrAdd(WordShl(index, IntPtrConstant(1)), | |
| 3067 IntPtrConstant(SeqTwoByteString::kHeaderSize - | |
| 3068 kHeapObjectTag)))); | |
| 3069 Goto(&done_loop); | |
| 3070 } | |
| 3071 } | |
| 3072 | |
| 3073 Bind(&if_stringisnotsequential); | |
| 3074 { | |
| 3075 // Check if the {string} is a ConsString. | |
| 3076 Label if_stringiscons(this), if_stringisnotcons(this); | |
| 3077 Branch(Word32Equal(Word32And(string_instance_type, | |
| 3078 Int32Constant(kStringRepresentationMask)), | |
| 3079 Int32Constant(kConsStringTag)), | |
| 3080 &if_stringiscons, &if_stringisnotcons); | |
| 3081 | |
| 3082 Bind(&if_stringiscons); | |
| 3083 { | |
| 3084 // Check whether the right hand side is the empty string (i.e. if | |
| 3085 // this is really a flat string in a cons string). If that is not | |
| 3086 // the case we flatten the string first. | |
| 3087 Label if_rhsisempty(this), if_rhsisnotempty(this, Label::kDeferred); | |
| 3088 Node* rhs = LoadObjectField(string, ConsString::kSecondOffset); | |
| 3089 Branch(WordEqual(rhs, EmptyStringConstant()), &if_rhsisempty, | |
| 3090 &if_rhsisnotempty); | |
| 3091 | |
| 3092 Bind(&if_rhsisempty); | |
| 3093 { | |
| 3094 // Just operate on the left hand side of the {string}. | |
| 3095 var_string.Bind(LoadObjectField(string, ConsString::kFirstOffset)); | |
| 3096 Goto(&loop); | |
| 3097 } | |
| 3098 | |
| 3099 Bind(&if_rhsisnotempty); | |
| 3100 { | |
| 3101 // Flatten the {string} and lookup in the resulting string. | |
| 3102 var_string.Bind(CallRuntime(Runtime::kFlattenString, | |
| 3103 NoContextConstant(), string)); | |
| 3104 Goto(&loop); | |
| 3105 } | |
| 3106 } | |
| 3107 | |
| 3108 Bind(&if_stringisnotcons); | |
| 3109 { | |
| 3110 // Check if the {string} is an ExternalString. | |
| 3111 Label if_stringisexternal(this), if_stringisnotexternal(this); | |
| 3112 Branch(Word32Equal(Word32And(string_instance_type, | |
| 3113 Int32Constant(kStringRepresentationMask)), | |
| 3114 Int32Constant(kExternalStringTag)), | |
| 3115 &if_stringisexternal, &if_stringisnotexternal); | |
| 3116 | |
| 3117 Bind(&if_stringisexternal); | |
| 3118 { | |
| 3119 // Check if the {string} is a short external string. | |
| 3120 Label if_stringisnotshort(this), | |
| 3121 if_stringisshort(this, Label::kDeferred); | |
| 3122 Branch(Word32Equal(Word32And(string_instance_type, | |
| 3123 Int32Constant(kShortExternalStringMask)), | |
| 3124 Int32Constant(0)), | |
| 3125 &if_stringisnotshort, &if_stringisshort); | |
| 3126 | |
| 3127 Bind(&if_stringisnotshort); | |
| 3128 { | |
| 3129 // Load the actual resource data from the {string}. | |
| 3130 Node* string_resource_data = | |
| 3131 LoadObjectField(string, ExternalString::kResourceDataOffset, | |
| 3132 MachineType::Pointer()); | |
| 3133 | |
| 3134 // Check if the {string} is a TwoByteExternalString or a | |
| 3135 // OneByteExternalString. | |
| 3136 Label if_stringistwobyte(this), if_stringisonebyte(this); | |
| 3137 Branch(Word32Equal(Word32And(string_instance_type, | |
| 3138 Int32Constant(kStringEncodingMask)), | |
| 3139 Int32Constant(kTwoByteStringTag)), | |
| 3140 &if_stringistwobyte, &if_stringisonebyte); | |
| 3141 | |
| 3142 Bind(&if_stringisonebyte); | |
| 3143 { | |
| 3144 var_result.Bind( | |
| 3145 Load(MachineType::Uint8(), string_resource_data, index)); | |
| 3146 Goto(&done_loop); | |
| 3147 } | |
| 3148 | |
| 3149 Bind(&if_stringistwobyte); | |
| 3150 { | |
| 3151 var_result.Bind(Load(MachineType::Uint16(), string_resource_data, | |
| 3152 WordShl(index, IntPtrConstant(1)))); | |
| 3153 Goto(&done_loop); | |
| 3154 } | |
| 3155 } | |
| 3156 | |
| 3157 Bind(&if_stringisshort); | |
| 3158 { | |
| 3159 // The {string} might be compressed, call the runtime. | |
| 3160 var_result.Bind(SmiToWord32( | |
| 3161 CallRuntime(Runtime::kExternalStringGetChar, | |
| 3162 NoContextConstant(), string, SmiTag(index)))); | |
| 3163 Goto(&done_loop); | |
| 3164 } | |
| 3165 } | |
| 3166 | |
| 3167 Bind(&if_stringisnotexternal); | |
| 3168 { | |
| 3169 Label if_stringissliced(this), if_stringisthin(this); | |
| 3170 Branch( | |
| 3171 Word32Equal(Word32And(string_instance_type, | |
| 3172 Int32Constant(kStringRepresentationMask)), | |
| 3173 Int32Constant(kSlicedStringTag)), | |
| 3174 &if_stringissliced, &if_stringisthin); | |
| 3175 Bind(&if_stringissliced); | |
| 3176 { | |
| 3177 // The {string} is a SlicedString, continue with its parent. | |
| 3178 Node* string_offset = | |
| 3179 LoadAndUntagObjectField(string, SlicedString::kOffsetOffset); | |
| 3180 Node* string_parent = | |
| 3181 LoadObjectField(string, SlicedString::kParentOffset); | |
| 3182 var_index.Bind(IntPtrAdd(index, string_offset)); | |
| 3183 var_string.Bind(string_parent); | |
| 3184 Goto(&loop); | |
| 3185 } | |
| 3186 Bind(&if_stringisthin); | |
| 3187 { | |
| 3188 // The {string} is a ThinString, continue with its actual value. | |
| 3189 var_string.Bind(LoadObjectField(string, ThinString::kActualOffset)); | |
| 3190 Goto(&loop); | |
| 3191 } | |
| 3192 } | |
| 3193 } | |
| 3194 } | |
| 3195 } | 3056 } |
| 3196 | 3057 |
| 3197 Bind(&done_loop); | 3058 Bind(&if_stringistwobyte); |
| 3059 { |
| 3060 var_result.Bind(Load(MachineType::Uint16(), string_data, |
| 3061 WordShl(offset, IntPtrConstant(1)))); |
| 3062 Goto(&out); |
| 3063 } |
| 3064 |
| 3065 Bind(&runtime_generic); |
| 3066 { |
| 3067 Node* const smi_index = ParameterToTagged(index, parameter_mode); |
| 3068 Node* const result = CallRuntime(Runtime::kStringCharCodeAtRT, |
| 3069 NoContextConstant(), string, smi_index); |
| 3070 var_result.Bind(SmiToWord32(result)); |
| 3071 Goto(&out); |
| 3072 } |
| 3073 |
| 3074 Bind(&runtime_external); |
| 3075 { |
| 3076 Node* const result = |
| 3077 CallRuntime(Runtime::kExternalStringGetChar, NoContextConstant(), |
| 3078 direct_string, SmiTag(offset)); |
| 3079 var_result.Bind(SmiToWord32(result)); |
| 3080 Goto(&out); |
| 3081 } |
| 3082 |
| 3083 Bind(&out); |
| 3198 return var_result.value(); | 3084 return var_result.value(); |
| 3199 } | 3085 } |
| 3200 | 3086 |
| 3201 Node* CodeStubAssembler::StringFromCharCode(Node* code) { | 3087 Node* CodeStubAssembler::StringFromCharCode(Node* code) { |
| 3202 Variable var_result(this, MachineRepresentation::kTagged); | 3088 Variable var_result(this, MachineRepresentation::kTagged); |
| 3203 | 3089 |
| 3204 // Check if the {code} is a one-byte char code. | 3090 // Check if the {code} is a one-byte char code. |
| 3205 Label if_codeisonebyte(this), if_codeistwobyte(this, Label::kDeferred), | 3091 Label if_codeisonebyte(this), if_codeistwobyte(this, Label::kDeferred), |
| 3206 if_done(this); | 3092 if_done(this); |
| 3207 Branch(Int32LessThanOrEqual(code, Int32Constant(String::kMaxOneByteCharCode)), | 3093 Branch(Int32LessThanOrEqual(code, Int32Constant(String::kMaxOneByteCharCode)), |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3306 } | 3192 } |
| 3307 | 3193 |
| 3308 a->Bind(&end); | 3194 a->Bind(&end); |
| 3309 return var_result.value(); | 3195 return var_result.value(); |
| 3310 } | 3196 } |
| 3311 | 3197 |
| 3312 } // namespace | 3198 } // namespace |
| 3313 | 3199 |
| 3314 Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, | 3200 Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, |
| 3315 Node* to) { | 3201 Node* to) { |
| 3316 Label end(this); | 3202 Variable var_result(this, MachineRepresentation::kTagged); |
| 3317 Label runtime(this); | 3203 ToDirectStringAssembler to_direct(state(), string); |
| 3318 | 3204 Label end(this), runtime(this); |
| 3319 Node* const int_zero = Int32Constant(0); | |
| 3320 | |
| 3321 // Int32 variables. | |
| 3322 Variable var_instance_type(this, MachineRepresentation::kWord32, int_zero); | |
| 3323 Variable var_representation(this, MachineRepresentation::kWord32, int_zero); | |
| 3324 | |
| 3325 Variable var_from(this, MachineRepresentation::kTagged, from); // Smi. | |
| 3326 Variable var_string(this, MachineRepresentation::kTagged, string); // String. | |
| 3327 Variable var_result(this, MachineRepresentation::kTagged); // String. | |
| 3328 | 3205 |
| 3329 // Make sure first argument is a string. | 3206 // Make sure first argument is a string. |
| 3330 CSA_ASSERT(this, TaggedIsNotSmi(string)); | 3207 CSA_ASSERT(this, TaggedIsNotSmi(string)); |
| 3331 CSA_ASSERT(this, IsString(string)); | 3208 CSA_ASSERT(this, IsString(string)); |
| 3332 | 3209 |
| 3333 // Load the instance type of the {string}. | |
| 3334 Node* const instance_type = LoadInstanceType(string); | |
| 3335 var_instance_type.Bind(instance_type); | |
| 3336 | |
| 3337 // Make sure that both from and to are non-negative smis. | 3210 // Make sure that both from and to are non-negative smis. |
| 3338 | 3211 |
| 3339 GotoIfNot(TaggedIsPositiveSmi(from), &runtime); | 3212 GotoIfNot(TaggedIsPositiveSmi(from), &runtime); |
| 3340 GotoIfNot(TaggedIsPositiveSmi(to), &runtime); | 3213 GotoIfNot(TaggedIsPositiveSmi(to), &runtime); |
| 3341 | 3214 |
| 3342 Node* const substr_length = SmiSub(to, from); | 3215 Node* const substr_length = SmiSub(to, from); |
| 3343 Node* const string_length = LoadStringLength(string); | 3216 Node* const string_length = LoadStringLength(string); |
| 3344 | 3217 |
| 3345 // Begin dispatching based on substring length. | 3218 // Begin dispatching based on substring length. |
| 3346 | 3219 |
| 3347 Label original_string_or_invalid_length(this); | 3220 Label original_string_or_invalid_length(this); |
| 3348 GotoIf(SmiAboveOrEqual(substr_length, string_length), | 3221 GotoIf(SmiAboveOrEqual(substr_length, string_length), |
| 3349 &original_string_or_invalid_length); | 3222 &original_string_or_invalid_length); |
| 3350 | 3223 |
| 3351 // A real substring (substr_length < string_length). | 3224 // A real substring (substr_length < string_length). |
| 3352 | 3225 |
| 3353 Label single_char(this); | 3226 Label single_char(this); |
| 3354 GotoIf(SmiEqual(substr_length, SmiConstant(Smi::FromInt(1))), &single_char); | 3227 GotoIf(SmiEqual(substr_length, SmiConstant(Smi::FromInt(1))), &single_char); |
| 3355 | 3228 |
| 3356 // TODO(jgruber): Add an additional case for substring of length == 0? | 3229 // TODO(jgruber): Add an additional case for substring of length == 0? |
| 3357 | 3230 |
| 3358 // Deal with different string types: update the index if necessary | 3231 // Deal with different string types: update the index if necessary |
| 3359 // and put the underlying string into var_string. | 3232 // and extract the underlying string. |
| 3360 | 3233 |
| 3361 // If the string is not indirect, it can only be sequential or external. | 3234 Node* const direct_string = to_direct.TryToDirect(&runtime); |
| 3362 STATIC_ASSERT(kIsIndirectStringMask == | 3235 Node* const offset = SmiAdd(from, SmiTag(to_direct.offset())); |
| 3363 (kSlicedStringTag & kConsStringTag & kThinStringTag)); | 3236 Node* const instance_type = to_direct.instance_type(); |
| 3364 STATIC_ASSERT(kIsIndirectStringMask != 0); | |
| 3365 Label underlying_unpacked(this); | |
| 3366 GotoIf(Word32Equal( | |
| 3367 Word32And(instance_type, Int32Constant(kIsIndirectStringMask)), | |
| 3368 Int32Constant(0)), | |
| 3369 &underlying_unpacked); | |
| 3370 | |
| 3371 // The subject string is a sliced, cons, or thin string. | |
| 3372 | |
| 3373 Label thin_string(this), thin_or_sliced(this); | |
| 3374 var_representation.Bind( | |
| 3375 Word32And(instance_type, Int32Constant(kStringRepresentationMask))); | |
| 3376 GotoIf( | |
| 3377 Word32NotEqual(var_representation.value(), Int32Constant(kConsStringTag)), | |
| 3378 &thin_or_sliced); | |
| 3379 | |
| 3380 // Cons string. Check whether it is flat, then fetch first part. | |
| 3381 // Flat cons strings have an empty second part. | |
| 3382 { | |
| 3383 GotoIf(WordNotEqual(LoadObjectField(string, ConsString::kSecondOffset), | |
| 3384 EmptyStringConstant()), | |
| 3385 &runtime); | |
| 3386 | |
| 3387 Node* first_string_part = LoadObjectField(string, ConsString::kFirstOffset); | |
| 3388 var_string.Bind(first_string_part); | |
| 3389 var_instance_type.Bind(LoadInstanceType(first_string_part)); | |
| 3390 var_representation.Bind(Word32And( | |
| 3391 var_instance_type.value(), Int32Constant(kStringRepresentationMask))); | |
| 3392 | |
| 3393 // The loaded first part might be a thin string. | |
| 3394 Branch(Word32Equal(Word32And(var_instance_type.value(), | |
| 3395 Int32Constant(kIsIndirectStringMask)), | |
| 3396 Int32Constant(0)), | |
| 3397 &underlying_unpacked, &thin_string); | |
| 3398 } | |
| 3399 | |
| 3400 Bind(&thin_or_sliced); | |
| 3401 { | |
| 3402 GotoIf( | |
| 3403 Word32Equal(var_representation.value(), Int32Constant(kThinStringTag)), | |
| 3404 &thin_string); | |
| 3405 // Otherwise it's a sliced string. | |
| 3406 // Fetch parent and correct start index by offset. | |
| 3407 Node* sliced_offset = | |
| 3408 LoadObjectField(var_string.value(), SlicedString::kOffsetOffset); | |
| 3409 var_from.Bind(SmiAdd(from, sliced_offset)); | |
| 3410 | |
| 3411 Node* slice_parent = LoadObjectField(string, SlicedString::kParentOffset); | |
| 3412 var_string.Bind(slice_parent); | |
| 3413 | |
| 3414 Node* slice_parent_instance_type = LoadInstanceType(slice_parent); | |
| 3415 var_instance_type.Bind(slice_parent_instance_type); | |
| 3416 | |
| 3417 // The loaded parent might be a thin string. | |
| 3418 Branch(Word32Equal(Word32And(var_instance_type.value(), | |
| 3419 Int32Constant(kIsIndirectStringMask)), | |
| 3420 Int32Constant(0)), | |
| 3421 &underlying_unpacked, &thin_string); | |
| 3422 } | |
| 3423 | |
| 3424 Bind(&thin_string); | |
| 3425 { | |
| 3426 Node* actual_string = | |
| 3427 LoadObjectField(var_string.value(), ThinString::kActualOffset); | |
| 3428 var_string.Bind(actual_string); | |
| 3429 var_instance_type.Bind(LoadInstanceType(actual_string)); | |
| 3430 Goto(&underlying_unpacked); | |
| 3431 } | |
| 3432 | 3237 |
| 3433 // The subject string can only be external or sequential string of either | 3238 // The subject string can only be external or sequential string of either |
| 3434 // encoding at this point. | 3239 // encoding at this point. |
| 3435 Label external_string(this); | 3240 Label external_string(this); |
| 3436 Bind(&underlying_unpacked); | |
| 3437 { | 3241 { |
| 3438 if (FLAG_string_slices) { | 3242 if (FLAG_string_slices) { |
| 3439 Label copy_routine(this); | 3243 Label next(this); |
| 3440 | 3244 |
| 3441 // Short slice. Copy instead of slicing. | 3245 // Short slice. Copy instead of slicing. |
| 3442 GotoIf(SmiLessThan(substr_length, | 3246 GotoIf(SmiLessThan(substr_length, |
| 3443 SmiConstant(Smi::FromInt(SlicedString::kMinLength))), | 3247 SmiConstant(Smi::FromInt(SlicedString::kMinLength))), |
| 3444 ©_routine); | 3248 &next); |
| 3445 | 3249 |
| 3446 // Allocate new sliced string. | 3250 // Allocate new sliced string. |
| 3447 | 3251 |
| 3448 Label two_byte_slice(this); | |
| 3449 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0); | |
| 3450 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); | |
| 3451 | |
| 3452 Counters* counters = isolate()->counters(); | 3252 Counters* counters = isolate()->counters(); |
| 3453 IncrementCounter(counters->sub_string_native(), 1); | 3253 IncrementCounter(counters->sub_string_native(), 1); |
| 3454 | 3254 |
| 3455 GotoIf(Word32Equal(Word32And(var_instance_type.value(), | 3255 Label one_byte_slice(this), two_byte_slice(this); |
| 3456 Int32Constant(kStringEncodingMask)), | 3256 Branch(IsOneByteStringInstanceType(to_direct.instance_type()), |
| 3457 Int32Constant(0)), | 3257 &one_byte_slice, &two_byte_slice); |
| 3458 &two_byte_slice); | |
| 3459 | 3258 |
| 3460 var_result.Bind(AllocateSlicedOneByteString( | 3259 Bind(&one_byte_slice); |
| 3461 substr_length, var_string.value(), var_from.value())); | 3260 { |
| 3462 Goto(&end); | 3261 var_result.Bind( |
| 3262 AllocateSlicedOneByteString(substr_length, direct_string, offset)); |
| 3263 Goto(&end); |
| 3264 } |
| 3463 | 3265 |
| 3464 Bind(&two_byte_slice); | 3266 Bind(&two_byte_slice); |
| 3267 { |
| 3268 var_result.Bind( |
| 3269 AllocateSlicedTwoByteString(substr_length, direct_string, offset)); |
| 3270 Goto(&end); |
| 3271 } |
| 3465 | 3272 |
| 3466 var_result.Bind(AllocateSlicedTwoByteString( | 3273 Bind(&next); |
| 3467 substr_length, var_string.value(), var_from.value())); | |
| 3468 Goto(&end); | |
| 3469 | |
| 3470 Bind(©_routine); | |
| 3471 } | 3274 } |
| 3472 | 3275 |
| 3473 // The subject string can only be external or sequential string of either | 3276 // The subject string can only be external or sequential string of either |
| 3474 // encoding at this point. | 3277 // encoding at this point. |
| 3475 STATIC_ASSERT(kExternalStringTag != 0); | 3278 GotoIf(to_direct.is_external(), &external_string); |
| 3476 STATIC_ASSERT(kSeqStringTag == 0); | |
| 3477 GotoIfNot(Word32Equal(Word32And(var_instance_type.value(), | |
| 3478 Int32Constant(kExternalStringTag)), | |
| 3479 Int32Constant(0)), | |
| 3480 &external_string); | |
| 3481 | 3279 |
| 3482 var_result.Bind(AllocAndCopyStringCharacters( | 3280 var_result.Bind(AllocAndCopyStringCharacters( |
| 3483 this, context, var_string.value(), var_instance_type.value(), | 3281 this, context, direct_string, instance_type, offset, substr_length)); |
| 3484 var_from.value(), substr_length)); | |
| 3485 | 3282 |
| 3486 Counters* counters = isolate()->counters(); | 3283 Counters* counters = isolate()->counters(); |
| 3487 IncrementCounter(counters->sub_string_native(), 1); | 3284 IncrementCounter(counters->sub_string_native(), 1); |
| 3488 | 3285 |
| 3489 Goto(&end); | 3286 Goto(&end); |
| 3490 } | 3287 } |
| 3491 | 3288 |
| 3492 // Handle external string. | 3289 // Handle external string. |
| 3493 Bind(&external_string); | 3290 Bind(&external_string); |
| 3494 { | 3291 { |
| 3495 Node* const fake_sequential_string = TryDerefExternalString( | 3292 Node* const fake_sequential_string = to_direct.PointerToString(&runtime); |
| 3496 var_string.value(), var_instance_type.value(), &runtime); | |
| 3497 | 3293 |
| 3498 var_result.Bind(AllocAndCopyStringCharacters( | 3294 var_result.Bind( |
| 3499 this, context, fake_sequential_string, var_instance_type.value(), | 3295 AllocAndCopyStringCharacters(this, context, fake_sequential_string, |
| 3500 var_from.value(), substr_length)); | 3296 instance_type, offset, substr_length)); |
| 3501 | 3297 |
| 3502 Counters* counters = isolate()->counters(); | 3298 Counters* counters = isolate()->counters(); |
| 3503 IncrementCounter(counters->sub_string_native(), 1); | 3299 IncrementCounter(counters->sub_string_native(), 1); |
| 3504 | 3300 |
| 3505 Goto(&end); | 3301 Goto(&end); |
| 3506 } | 3302 } |
| 3507 | 3303 |
| 3508 // Substrings of length 1 are generated through CharCodeAt and FromCharCode. | 3304 // Substrings of length 1 are generated through CharCodeAt and FromCharCode. |
| 3509 Bind(&single_char); | 3305 Bind(&single_char); |
| 3510 { | 3306 { |
| 3511 Node* char_code = StringCharCodeAt(var_string.value(), var_from.value()); | 3307 Node* char_code = StringCharCodeAt(string, from); |
| 3512 var_result.Bind(StringFromCharCode(char_code)); | 3308 var_result.Bind(StringFromCharCode(char_code)); |
| 3513 Goto(&end); | 3309 Goto(&end); |
| 3514 } | 3310 } |
| 3515 | 3311 |
| 3516 Bind(&original_string_or_invalid_length); | 3312 Bind(&original_string_or_invalid_length); |
| 3517 { | 3313 { |
| 3518 // Longer than original string's length or negative: unsafe arguments. | 3314 // Longer than original string's length or negative: unsafe arguments. |
| 3519 GotoIf(SmiAbove(substr_length, string_length), &runtime); | 3315 GotoIf(SmiAbove(substr_length, string_length), &runtime); |
| 3520 | 3316 |
| 3521 // Equal length - check if {from, to} == {0, str.length}. | 3317 // Equal length - check if {from, to} == {0, str.length}. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 3535 { | 3331 { |
| 3536 var_result.Bind( | 3332 var_result.Bind( |
| 3537 CallRuntime(Runtime::kSubString, context, string, from, to)); | 3333 CallRuntime(Runtime::kSubString, context, string, from, to)); |
| 3538 Goto(&end); | 3334 Goto(&end); |
| 3539 } | 3335 } |
| 3540 | 3336 |
| 3541 Bind(&end); | 3337 Bind(&end); |
| 3542 return var_result.value(); | 3338 return var_result.value(); |
| 3543 } | 3339 } |
| 3544 | 3340 |
| 3545 namespace { | 3341 ToDirectStringAssembler::ToDirectStringAssembler( |
| 3342 compiler::CodeAssemblerState* state, Node* string) |
| 3343 : CodeStubAssembler(state), |
| 3344 var_string_(this, MachineRepresentation::kTagged, string), |
| 3345 var_instance_type_(this, MachineRepresentation::kWord32), |
| 3346 var_offset_(this, MachineType::PointerRepresentation()), |
| 3347 var_is_external_(this, MachineRepresentation::kWord32) { |
| 3348 CSA_ASSERT(this, TaggedIsNotSmi(string)); |
| 3349 CSA_ASSERT(this, IsString(string)); |
| 3546 | 3350 |
| 3547 Node* IsExternalStringInstanceType(CodeStubAssembler* a, | 3351 var_string_.Bind(string); |
| 3548 Node* const instance_type) { | 3352 var_offset_.Bind(IntPtrConstant(0)); |
| 3549 CSA_ASSERT(a, a->IsStringInstanceType(instance_type)); | 3353 var_instance_type_.Bind(LoadInstanceType(string)); |
| 3550 return a->Word32Equal( | 3354 var_is_external_.Bind(Int32Constant(0)); |
| 3551 a->Word32And(instance_type, a->Int32Constant(kStringRepresentationMask)), | |
| 3552 a->Int32Constant(kExternalStringTag)); | |
| 3553 } | 3355 } |
| 3554 | 3356 |
| 3555 Node* IsShortExternalStringInstanceType(CodeStubAssembler* a, | 3357 Node* ToDirectStringAssembler::TryToDirect(Label* if_bailout) { |
| 3556 Node* const instance_type) { | 3358 VariableList vars({&var_string_, &var_offset_, &var_instance_type_}, zone()); |
| 3557 CSA_ASSERT(a, a->IsStringInstanceType(instance_type)); | 3359 Label dispatch(this, vars); |
| 3558 STATIC_ASSERT(kShortExternalStringTag != 0); | 3360 Label if_iscons(this); |
| 3559 return a->Word32NotEqual( | 3361 Label if_isexternal(this); |
| 3560 a->Word32And(instance_type, a->Int32Constant(kShortExternalStringMask)), | 3362 Label if_issliced(this); |
| 3561 a->Int32Constant(0)); | 3363 Label if_isthin(this); |
| 3562 } | |
| 3563 | |
| 3564 } // namespace | |
| 3565 | |
| 3566 void CodeStubAssembler::TryUnpackString(Variable* var_string, | |
| 3567 Variable* var_offset, | |
| 3568 Variable* var_instance_type, | |
| 3569 Label* if_bailout) { | |
| 3570 DCHECK_EQ(var_string->rep(), MachineType::PointerRepresentation()); | |
| 3571 DCHECK_EQ(var_offset->rep(), MachineType::PointerRepresentation()); | |
| 3572 DCHECK_EQ(var_instance_type->rep(), MachineRepresentation::kWord32); | |
| 3573 CSA_ASSERT(this, IsString(var_string->value())); | |
| 3574 | |
| 3575 Label out(this); | 3364 Label out(this); |
| 3576 | 3365 |
| 3577 VariableList vars({var_string, var_offset, var_instance_type}, zone()); | |
| 3578 Label dispatch(this, vars); | |
| 3579 Label if_isdirect(this); | |
| 3580 Label if_iscons(this, Label::kDeferred); | |
| 3581 Label if_isexternal(this, Label::kDeferred); | |
| 3582 Label if_issliced(this, Label::kDeferred); | |
| 3583 Label if_isthin(this, Label::kDeferred); | |
| 3584 | |
| 3585 Goto(&dispatch); | 3366 Goto(&dispatch); |
| 3586 | 3367 |
| 3587 // Dispatch based on string representation. | 3368 // Dispatch based on string representation. |
| 3588 Bind(&dispatch); | 3369 Bind(&dispatch); |
| 3589 { | 3370 { |
| 3590 int32_t values[] = { | 3371 int32_t values[] = { |
| 3591 kSeqStringTag, kConsStringTag, kExternalStringTag, | 3372 kSeqStringTag, kConsStringTag, kExternalStringTag, |
| 3592 kSlicedStringTag, kThinStringTag, | 3373 kSlicedStringTag, kThinStringTag, |
| 3593 }; | 3374 }; |
| 3594 Label* labels[] = { | 3375 Label* labels[] = { |
| 3595 &if_isdirect, &if_iscons, &if_isexternal, &if_issliced, &if_isthin, | 3376 &out, &if_iscons, &if_isexternal, &if_issliced, &if_isthin, |
| 3596 }; | 3377 }; |
| 3597 STATIC_ASSERT(arraysize(values) == arraysize(labels)); | 3378 STATIC_ASSERT(arraysize(values) == arraysize(labels)); |
| 3598 | 3379 |
| 3599 Node* const representation = Word32And( | 3380 Node* const representation = Word32And( |
| 3600 var_instance_type->value(), Int32Constant(kStringRepresentationMask)); | 3381 var_instance_type_.value(), Int32Constant(kStringRepresentationMask)); |
| 3601 Switch(representation, if_bailout, values, labels, arraysize(values)); | 3382 Switch(representation, if_bailout, values, labels, arraysize(values)); |
| 3602 } | 3383 } |
| 3603 | 3384 |
| 3604 // Cons string. Check whether it is flat, then fetch first part. | 3385 // Cons string. Check whether it is flat, then fetch first part. |
| 3605 // Flat cons strings have an empty second part. | 3386 // Flat cons strings have an empty second part. |
| 3606 Bind(&if_iscons); | 3387 Bind(&if_iscons); |
| 3607 { | 3388 { |
| 3608 Node* const string = var_string->value(); | 3389 Node* const string = var_string_.value(); |
| 3609 GotoIfNot(IsEmptyString(LoadObjectField(string, ConsString::kSecondOffset)), | 3390 GotoIfNot(IsEmptyString(LoadObjectField(string, ConsString::kSecondOffset)), |
| 3610 if_bailout); | 3391 if_bailout); |
| 3611 | 3392 |
| 3612 Node* const lhs = LoadObjectField(string, ConsString::kFirstOffset); | 3393 Node* const lhs = LoadObjectField(string, ConsString::kFirstOffset); |
| 3613 var_string->Bind(BitcastTaggedToWord(lhs)); | 3394 var_string_.Bind(lhs); |
| 3614 var_instance_type->Bind(LoadInstanceType(lhs)); | 3395 var_instance_type_.Bind(LoadInstanceType(lhs)); |
| 3615 | 3396 |
| 3616 Goto(&dispatch); | 3397 Goto(&dispatch); |
| 3617 } | 3398 } |
| 3618 | 3399 |
| 3619 // Sliced string. Fetch parent and correct start index by offset. | 3400 // Sliced string. Fetch parent and correct start index by offset. |
| 3620 Bind(&if_issliced); | 3401 Bind(&if_issliced); |
| 3621 { | 3402 { |
| 3622 Node* const string = var_string->value(); | 3403 Node* const string = var_string_.value(); |
| 3623 Node* const sliced_offset = | 3404 Node* const sliced_offset = |
| 3624 LoadObjectField(string, SlicedString::kOffsetOffset); | 3405 LoadAndUntagObjectField(string, SlicedString::kOffsetOffset); |
| 3625 var_offset->Bind(IntPtrAdd(var_offset->value(), SmiUntag(sliced_offset))); | 3406 var_offset_.Bind(IntPtrAdd(var_offset_.value(), sliced_offset)); |
| 3626 | 3407 |
| 3627 Node* const parent = LoadObjectField(string, SlicedString::kParentOffset); | 3408 Node* const parent = LoadObjectField(string, SlicedString::kParentOffset); |
| 3628 var_string->Bind(BitcastTaggedToWord(parent)); | 3409 var_string_.Bind(parent); |
| 3629 var_instance_type->Bind(LoadInstanceType(parent)); | 3410 var_instance_type_.Bind(LoadInstanceType(parent)); |
| 3630 | 3411 |
| 3631 Goto(&dispatch); | 3412 Goto(&dispatch); |
| 3632 } | 3413 } |
| 3633 | 3414 |
| 3634 // Thin string. Fetch the actual string. | 3415 // Thin string. Fetch the actual string. |
| 3635 Bind(&if_isthin); | 3416 Bind(&if_isthin); |
| 3636 { | 3417 { |
| 3637 Node* const string = var_string->value(); | 3418 Node* const string = var_string_.value(); |
| 3638 Node* const actual_string = | 3419 Node* const actual_string = |
| 3639 LoadObjectField(string, ThinString::kActualOffset); | 3420 LoadObjectField(string, ThinString::kActualOffset); |
| 3640 Node* const actual_instance_type = LoadInstanceType(actual_string); | 3421 Node* const actual_instance_type = LoadInstanceType(actual_string); |
| 3641 | 3422 |
| 3642 var_string->Bind(BitcastTaggedToWord(actual_string)); | 3423 var_string_.Bind(actual_string); |
| 3643 var_instance_type->Bind(actual_instance_type); | 3424 var_instance_type_.Bind(actual_instance_type); |
| 3644 | 3425 |
| 3645 Goto(&dispatch); | 3426 Goto(&dispatch); |
| 3646 } | 3427 } |
| 3647 | 3428 |
| 3648 // External string. | 3429 // External string. |
| 3649 Bind(&if_isexternal); | 3430 Bind(&if_isexternal); |
| 3431 var_is_external_.Bind(Int32Constant(1)); |
| 3432 Goto(&out); |
| 3433 |
| 3434 Bind(&out); |
| 3435 return var_string_.value(); |
| 3436 } |
| 3437 |
| 3438 Node* ToDirectStringAssembler::TryToSequential(StringPointerKind ptr_kind, |
| 3439 Label* if_bailout) { |
| 3440 CHECK(ptr_kind == PTR_TO_DATA || ptr_kind == PTR_TO_STRING); |
| 3441 |
| 3442 Variable var_result(this, MachineType::PointerRepresentation()); |
| 3443 Label out(this), if_issequential(this), if_isexternal(this); |
| 3444 Branch(is_external(), &if_isexternal, &if_issequential); |
| 3445 |
| 3446 Bind(&if_issequential); |
| 3650 { | 3447 { |
| 3651 Node* const string = var_string->value(); | 3448 STATIC_ASSERT(SeqOneByteString::kHeaderSize == |
| 3652 Node* const faked_seq_string = | 3449 SeqTwoByteString::kHeaderSize); |
| 3653 TryDerefExternalString(string, var_instance_type->value(), if_bailout); | 3450 Node* result = BitcastTaggedToWord(var_string_.value()); |
| 3654 | 3451 if (ptr_kind == PTR_TO_DATA) { |
| 3655 STATIC_ASSERT(kSeqStringTag == 0x0); | 3452 result = IntPtrAdd(result, IntPtrConstant(SeqOneByteString::kHeaderSize - |
| 3656 Node* const faked_seq_instance_type = Word32Xor( | 3453 kHeapObjectTag)); |
| 3657 var_instance_type->value(), Int32Constant(kExternalStringTag)); | 3454 } |
| 3658 CSA_ASSERT(this, IsSequentialStringInstanceType(faked_seq_instance_type)); | 3455 var_result.Bind(result); |
| 3659 | 3456 Goto(&out); |
| 3660 var_string->Bind(faked_seq_string); | |
| 3661 var_instance_type->Bind(faked_seq_instance_type); | |
| 3662 | |
| 3663 Goto(&if_isdirect); | |
| 3664 } | 3457 } |
| 3665 | 3458 |
| 3666 Bind(&if_isdirect); | 3459 Bind(&if_isexternal); |
| 3460 { |
| 3461 GotoIf(IsShortExternalStringInstanceType(var_instance_type_.value()), |
| 3462 if_bailout); |
| 3463 |
| 3464 Node* const string = var_string_.value(); |
| 3465 Node* result = LoadObjectField(string, ExternalString::kResourceDataOffset, |
| 3466 MachineType::Pointer()); |
| 3467 if (ptr_kind == PTR_TO_STRING) { |
| 3468 result = IntPtrSub(result, IntPtrConstant(SeqOneByteString::kHeaderSize - |
| 3469 kHeapObjectTag)); |
| 3470 } |
| 3471 var_result.Bind(result); |
| 3472 Goto(&out); |
| 3473 } |
| 3474 |
| 3475 Bind(&out); |
| 3476 return var_result.value(); |
| 3667 } | 3477 } |
| 3668 | 3478 |
| 3669 Node* CodeStubAssembler::TryDerefExternalString(Node* const string, | 3479 Node* CodeStubAssembler::TryDerefExternalString(Node* const string, |
| 3670 Node* const instance_type, | 3480 Node* const instance_type, |
| 3671 Label* if_bailout) { | 3481 Label* if_bailout) { |
| 3672 Label out(this); | 3482 Label out(this); |
| 3673 | 3483 |
| 3674 USE(IsExternalStringInstanceType); | 3484 CSA_ASSERT(this, IsExternalStringInstanceType(instance_type)); |
| 3675 CSA_ASSERT(this, IsExternalStringInstanceType(this, instance_type)); | 3485 GotoIf(IsShortExternalStringInstanceType(instance_type), if_bailout); |
| 3676 GotoIf(IsShortExternalStringInstanceType(this, instance_type), if_bailout); | |
| 3677 | 3486 |
| 3678 // Move the pointer so that offset-wise, it looks like a sequential string. | 3487 // Move the pointer so that offset-wise, it looks like a sequential string. |
| 3679 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize); | 3488 STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize); |
| 3680 | 3489 |
| 3681 Node* resource_data = LoadObjectField( | 3490 Node* resource_data = LoadObjectField( |
| 3682 string, ExternalString::kResourceDataOffset, MachineType::Pointer()); | 3491 string, ExternalString::kResourceDataOffset, MachineType::Pointer()); |
| 3683 Node* const fake_sequential_string = | 3492 Node* const fake_sequential_string = |
| 3684 IntPtrSub(resource_data, | 3493 IntPtrSub(resource_data, |
| 3685 IntPtrConstant(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); | 3494 IntPtrConstant(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); |
| 3686 | 3495 |
| (...skipping 4776 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8463 formatted.c_str(), TENURED); | 8272 formatted.c_str(), TENURED); |
| 8464 CallRuntime(Runtime::kGlobalPrint, NoContextConstant(), | 8273 CallRuntime(Runtime::kGlobalPrint, NoContextConstant(), |
| 8465 HeapConstant(string)); | 8274 HeapConstant(string)); |
| 8466 } | 8275 } |
| 8467 CallRuntime(Runtime::kDebugPrint, NoContextConstant(), tagged_value); | 8276 CallRuntime(Runtime::kDebugPrint, NoContextConstant(), tagged_value); |
| 8468 #endif | 8277 #endif |
| 8469 } | 8278 } |
| 8470 | 8279 |
| 8471 } // namespace internal | 8280 } // namespace internal |
| 8472 } // namespace v8 | 8281 } // namespace v8 |
| OLD | NEW |