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 |