| OLD | NEW |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 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 #if V8_TARGET_ARCH_X64 | 5 #if V8_TARGET_ARCH_X64 |
| 6 | 6 |
| 7 #include "src/codegen.h" | 7 #include "src/codegen.h" |
| 8 #include "src/ic/ic.h" | 8 #include "src/ic/ic.h" |
| 9 #include "src/ic/ic-compiler.h" | 9 #include "src/ic/ic-compiler.h" |
| 10 #include "src/ic/stub-cache.h" | 10 #include "src/ic/stub-cache.h" |
| 11 | 11 |
| 12 namespace v8 { | 12 namespace v8 { |
| 13 namespace internal { | 13 namespace internal { |
| 14 | 14 |
| 15 // ---------------------------------------------------------------------------- | 15 // ---------------------------------------------------------------------------- |
| 16 // Static IC stub generators. | 16 // Static IC stub generators. |
| 17 // | 17 // |
| 18 | 18 |
| 19 #define __ ACCESS_MASM(masm) | 19 #define __ ACCESS_MASM(masm) |
| 20 | 20 |
| 21 | |
| 22 static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, Register type, | |
| 23 Label* global_object) { | |
| 24 // Register usage: | |
| 25 // type: holds the receiver instance type on entry. | |
| 26 __ cmpb(type, Immediate(JS_GLOBAL_OBJECT_TYPE)); | |
| 27 __ j(equal, global_object); | |
| 28 __ cmpb(type, Immediate(JS_GLOBAL_PROXY_TYPE)); | |
| 29 __ j(equal, global_object); | |
| 30 } | |
| 31 | |
| 32 | |
| 33 // Helper function used to load a property from a dictionary backing storage. | 21 // Helper function used to load a property from a dictionary backing storage. |
| 34 // This function may return false negatives, so miss_label | 22 // This function may return false negatives, so miss_label |
| 35 // must always call a backup property load that is complete. | 23 // must always call a backup property load that is complete. |
| 36 // This function is safe to call if name is not an internalized string, | 24 // This function is safe to call if name is not an internalized string, |
| 37 // and will jump to the miss_label in that case. | 25 // and will jump to the miss_label in that case. |
| 38 // The generated code assumes that the receiver has slow properties, | 26 // The generated code assumes that the receiver has slow properties, |
| 39 // is not a global object and does not have interceptors. | 27 // is not a global object and does not have interceptors. |
| 40 static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label, | 28 static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label, |
| 41 Register elements, Register name, | 29 Register elements, Register name, |
| 42 Register r0, Register r1, Register result) { | 30 Register r0, Register r1, Register result) { |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 126 const int kValueOffset = kElementsStartOffset + kPointerSize; | 114 const int kValueOffset = kElementsStartOffset + kPointerSize; |
| 127 __ leap(scratch1, Operand(elements, scratch1, times_pointer_size, | 115 __ leap(scratch1, Operand(elements, scratch1, times_pointer_size, |
| 128 kValueOffset - kHeapObjectTag)); | 116 kValueOffset - kHeapObjectTag)); |
| 129 __ movp(Operand(scratch1, 0), value); | 117 __ movp(Operand(scratch1, 0), value); |
| 130 | 118 |
| 131 // Update write barrier. Make sure not to clobber the value. | 119 // Update write barrier. Make sure not to clobber the value. |
| 132 __ movp(scratch0, value); | 120 __ movp(scratch0, value); |
| 133 __ RecordWrite(elements, scratch1, scratch0, kDontSaveFPRegs); | 121 __ RecordWrite(elements, scratch1, scratch0, kDontSaveFPRegs); |
| 134 } | 122 } |
| 135 | 123 |
| 136 | |
| 137 // Checks the receiver for special cases (value type, slow case bits). | |
| 138 // Falls through for regular JS object. | |
| 139 static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, | |
| 140 Register receiver, Register map, | |
| 141 int interceptor_bit, Label* slow) { | |
| 142 // Register use: | |
| 143 // receiver - holds the receiver and is unchanged. | |
| 144 // Scratch registers: | |
| 145 // map - used to hold the map of the receiver. | |
| 146 | |
| 147 // Check that the object isn't a smi. | |
| 148 __ JumpIfSmi(receiver, slow); | |
| 149 | |
| 150 // Check that the object is some kind of JS object EXCEPT JS Value type. | |
| 151 // In the case that the object is a value-wrapper object, | |
| 152 // we enter the runtime system to make sure that indexing | |
| 153 // into string objects work as intended. | |
| 154 DCHECK(JS_OBJECT_TYPE > JS_VALUE_TYPE); | |
| 155 __ CmpObjectType(receiver, JS_OBJECT_TYPE, map); | |
| 156 __ j(below, slow); | |
| 157 | |
| 158 // Check bit field. | |
| 159 __ testb( | |
| 160 FieldOperand(map, Map::kBitFieldOffset), | |
| 161 Immediate((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit))); | |
| 162 __ j(not_zero, slow); | |
| 163 } | |
| 164 | |
| 165 | |
| 166 // Loads an indexed element from a fast case array. | |
| 167 static void GenerateFastArrayLoad(MacroAssembler* masm, Register receiver, | |
| 168 Register key, Register elements, | |
| 169 Register scratch, Register result, | |
| 170 Label* slow) { | |
| 171 // Register use: | |
| 172 // | |
| 173 // receiver - holds the receiver on entry. | |
| 174 // Unchanged unless 'result' is the same register. | |
| 175 // | |
| 176 // key - holds the smi key on entry. | |
| 177 // Unchanged unless 'result' is the same register. | |
| 178 // | |
| 179 // result - holds the result on exit if the load succeeded. | |
| 180 // Allowed to be the the same as 'receiver' or 'key'. | |
| 181 // Unchanged on bailout so 'receiver' and 'key' can be safely | |
| 182 // used by further computation. | |
| 183 // | |
| 184 // Scratch registers: | |
| 185 // | |
| 186 // elements - holds the elements of the receiver and its prototypes. | |
| 187 // | |
| 188 // scratch - used to hold maps, prototypes, and the loaded value. | |
| 189 Label check_prototypes, check_next_prototype; | |
| 190 Label done, in_bounds, absent; | |
| 191 | |
| 192 __ movp(elements, FieldOperand(receiver, JSObject::kElementsOffset)); | |
| 193 __ AssertFastElements(elements); | |
| 194 // Check that the key (index) is within bounds. | |
| 195 __ SmiCompare(key, FieldOperand(elements, FixedArray::kLengthOffset)); | |
| 196 // Unsigned comparison rejects negative indices. | |
| 197 __ j(below, &in_bounds); | |
| 198 | |
| 199 // Out-of-bounds. Check the prototype chain to see if we can just return | |
| 200 // 'undefined'. | |
| 201 __ SmiCompare(key, Smi::kZero); | |
| 202 __ j(less, slow); // Negative keys can't take the fast OOB path. | |
| 203 __ bind(&check_prototypes); | |
| 204 __ movp(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); | |
| 205 __ bind(&check_next_prototype); | |
| 206 __ movp(scratch, FieldOperand(scratch, Map::kPrototypeOffset)); | |
| 207 // scratch: current prototype | |
| 208 __ CompareRoot(scratch, Heap::kNullValueRootIndex); | |
| 209 __ j(equal, &absent); | |
| 210 __ movp(elements, FieldOperand(scratch, JSObject::kElementsOffset)); | |
| 211 __ movp(scratch, FieldOperand(scratch, HeapObject::kMapOffset)); | |
| 212 // elements: elements of current prototype | |
| 213 // scratch: map of current prototype | |
| 214 __ CmpInstanceType(scratch, JS_OBJECT_TYPE); | |
| 215 __ j(below, slow); | |
| 216 __ testb(FieldOperand(scratch, Map::kBitFieldOffset), | |
| 217 Immediate((1 << Map::kIsAccessCheckNeeded) | | |
| 218 (1 << Map::kHasIndexedInterceptor))); | |
| 219 __ j(not_zero, slow); | |
| 220 __ CompareRoot(elements, Heap::kEmptyFixedArrayRootIndex); | |
| 221 __ j(not_equal, slow); | |
| 222 __ jmp(&check_next_prototype); | |
| 223 | |
| 224 __ bind(&absent); | |
| 225 __ LoadRoot(result, Heap::kUndefinedValueRootIndex); | |
| 226 __ jmp(&done); | |
| 227 | |
| 228 __ bind(&in_bounds); | |
| 229 // Fast case: Do the load. | |
| 230 SmiIndex index = masm->SmiToIndex(scratch, key, kPointerSizeLog2); | |
| 231 __ movp(scratch, FieldOperand(elements, index.reg, index.scale, | |
| 232 FixedArray::kHeaderSize)); | |
| 233 __ CompareRoot(scratch, Heap::kTheHoleValueRootIndex); | |
| 234 // In case the loaded value is the_hole we have to check the prototype chain. | |
| 235 __ j(equal, &check_prototypes); | |
| 236 __ Move(result, scratch); | |
| 237 __ bind(&done); | |
| 238 } | |
| 239 | |
| 240 | |
| 241 // Checks whether a key is an array index string or a unique name. | |
| 242 // Falls through if the key is a unique name. | |
| 243 static void GenerateKeyNameCheck(MacroAssembler* masm, Register key, | |
| 244 Register map, Register hash, | |
| 245 Label* index_string, Label* not_unique) { | |
| 246 // Register use: | |
| 247 // key - holds the key and is unchanged. Assumed to be non-smi. | |
| 248 // Scratch registers: | |
| 249 // map - used to hold the map of the key. | |
| 250 // hash - used to hold the hash of the key. | |
| 251 Label unique; | |
| 252 __ CmpObjectType(key, LAST_UNIQUE_NAME_TYPE, map); | |
| 253 __ j(above, not_unique); | |
| 254 STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE); | |
| 255 __ j(equal, &unique); | |
| 256 | |
| 257 // Is the string an array index, with cached numeric value? | |
| 258 __ movl(hash, FieldOperand(key, Name::kHashFieldOffset)); | |
| 259 __ testl(hash, Immediate(Name::kContainsCachedArrayIndexMask)); | |
| 260 __ j(zero, index_string); // The value in hash is used at jump target. | |
| 261 | |
| 262 // Is the string internalized? We already know it's a string so a single | |
| 263 // bit test is enough. | |
| 264 STATIC_ASSERT(kNotInternalizedTag != 0); | |
| 265 __ testb(FieldOperand(map, Map::kInstanceTypeOffset), | |
| 266 Immediate(kIsNotInternalizedMask)); | |
| 267 __ j(not_zero, not_unique); | |
| 268 | |
| 269 __ bind(&unique); | |
| 270 } | |
| 271 | |
| 272 void KeyedLoadIC::GenerateMegamorphic(MacroAssembler* masm) { | |
| 273 // The return address is on the stack. | |
| 274 Label slow, check_name, index_smi, index_name, property_array_property; | |
| 275 Label probe_dictionary, check_number_dictionary; | |
| 276 | |
| 277 Register receiver = LoadDescriptor::ReceiverRegister(); | |
| 278 Register key = LoadDescriptor::NameRegister(); | |
| 279 DCHECK(receiver.is(rdx)); | |
| 280 DCHECK(key.is(rcx)); | |
| 281 | |
| 282 // Check that the key is a smi. | |
| 283 __ JumpIfNotSmi(key, &check_name); | |
| 284 __ bind(&index_smi); | |
| 285 // Now the key is known to be a smi. This place is also jumped to from below | |
| 286 // where a numeric string is converted to a smi. | |
| 287 | |
| 288 GenerateKeyedLoadReceiverCheck(masm, receiver, rax, | |
| 289 Map::kHasIndexedInterceptor, &slow); | |
| 290 | |
| 291 // Check the receiver's map to see if it has fast elements. | |
| 292 __ CheckFastElements(rax, &check_number_dictionary); | |
| 293 | |
| 294 GenerateFastArrayLoad(masm, receiver, key, rax, rbx, rax, &slow); | |
| 295 Counters* counters = masm->isolate()->counters(); | |
| 296 __ IncrementCounter(counters->ic_keyed_load_generic_smi(), 1); | |
| 297 __ ret(0); | |
| 298 | |
| 299 __ bind(&check_number_dictionary); | |
| 300 __ SmiToInteger32(rbx, key); | |
| 301 __ movp(rax, FieldOperand(receiver, JSObject::kElementsOffset)); | |
| 302 | |
| 303 // Check whether the elements is a number dictionary. | |
| 304 // rbx: key as untagged int32 | |
| 305 // rax: elements | |
| 306 __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), | |
| 307 Heap::kHashTableMapRootIndex); | |
| 308 __ j(not_equal, &slow); | |
| 309 __ LoadFromNumberDictionary(&slow, rax, key, rbx, r9, rdi, rax); | |
| 310 __ ret(0); | |
| 311 | |
| 312 __ bind(&slow); | |
| 313 // Slow case: Jump to runtime. | |
| 314 __ IncrementCounter(counters->ic_keyed_load_generic_slow(), 1); | |
| 315 KeyedLoadIC::GenerateRuntimeGetProperty(masm); | |
| 316 | |
| 317 __ bind(&check_name); | |
| 318 GenerateKeyNameCheck(masm, key, rax, rbx, &index_name, &slow); | |
| 319 | |
| 320 GenerateKeyedLoadReceiverCheck(masm, receiver, rax, Map::kHasNamedInterceptor, | |
| 321 &slow); | |
| 322 | |
| 323 // If the receiver is a fast-case object, check the stub cache. Otherwise | |
| 324 // probe the dictionary. | |
| 325 __ movp(rbx, FieldOperand(receiver, JSObject::kPropertiesOffset)); | |
| 326 __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset), | |
| 327 Heap::kHashTableMapRootIndex); | |
| 328 __ j(equal, &probe_dictionary); | |
| 329 | |
| 330 Register megamorphic_scratch = rdi; | |
| 331 // The handlers in the stub cache expect a vector and slot. Since we won't | |
| 332 // change the IC from any downstream misses, a dummy vector can be used. | |
| 333 Register vector = LoadWithVectorDescriptor::VectorRegister(); | |
| 334 Register slot = LoadDescriptor::SlotRegister(); | |
| 335 DCHECK(!AreAliased(megamorphic_scratch, vector, slot)); | |
| 336 Handle<TypeFeedbackVector> dummy_vector = | |
| 337 TypeFeedbackVector::DummyVector(masm->isolate()); | |
| 338 int slot_index = dummy_vector->GetIndex( | |
| 339 FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedLoadICSlot)); | |
| 340 __ Move(vector, dummy_vector); | |
| 341 __ Move(slot, Smi::FromInt(slot_index)); | |
| 342 | |
| 343 masm->isolate()->load_stub_cache()->GenerateProbe( | |
| 344 masm, receiver, key, megamorphic_scratch, no_reg); | |
| 345 // Cache miss. | |
| 346 GenerateMiss(masm); | |
| 347 | |
| 348 // Do a quick inline probe of the receiver's dictionary, if it | |
| 349 // exists. | |
| 350 __ bind(&probe_dictionary); | |
| 351 // rbx: elements | |
| 352 | |
| 353 __ movp(rax, FieldOperand(receiver, JSObject::kMapOffset)); | |
| 354 __ movb(rax, FieldOperand(rax, Map::kInstanceTypeOffset)); | |
| 355 GenerateGlobalInstanceTypeCheck(masm, rax, &slow); | |
| 356 | |
| 357 GenerateDictionaryLoad(masm, &slow, rbx, key, rax, rdi, rax); | |
| 358 __ IncrementCounter(counters->ic_keyed_load_generic_symbol(), 1); | |
| 359 __ ret(0); | |
| 360 | |
| 361 __ bind(&index_name); | |
| 362 __ IndexFromHash(rbx, key); | |
| 363 __ jmp(&index_smi); | |
| 364 } | |
| 365 | |
| 366 | |
| 367 static void KeyedStoreGenerateMegamorphicHelper( | 124 static void KeyedStoreGenerateMegamorphicHelper( |
| 368 MacroAssembler* masm, Label* fast_object, Label* fast_double, Label* slow, | 125 MacroAssembler* masm, Label* fast_object, Label* fast_double, Label* slow, |
| 369 KeyedStoreCheckMap check_map, KeyedStoreIncrementLength increment_length) { | 126 KeyedStoreCheckMap check_map, KeyedStoreIncrementLength increment_length) { |
| 370 Label transition_smi_elements; | 127 Label transition_smi_elements; |
| 371 Label finish_object_store, non_double_value, transition_double_elements; | 128 Label finish_object_store, non_double_value, transition_double_elements; |
| 372 Label fast_double_without_map_check; | 129 Label fast_double_without_map_check; |
| 373 Register receiver = StoreDescriptor::ReceiverRegister(); | 130 Register receiver = StoreDescriptor::ReceiverRegister(); |
| 374 Register key = StoreDescriptor::NameRegister(); | 131 Register key = StoreDescriptor::NameRegister(); |
| 375 Register value = StoreDescriptor::ValueRegister(); | 132 Register value = StoreDescriptor::ValueRegister(); |
| 376 DCHECK(receiver.is(rdx)); | 133 DCHECK(receiver.is(rdx)); |
| (...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 840 Condition cc = | 597 Condition cc = |
| 841 (check == ENABLE_INLINED_SMI_CHECK) | 598 (check == ENABLE_INLINED_SMI_CHECK) |
| 842 ? (*jmp_address == Assembler::kJncShortOpcode ? not_zero : zero) | 599 ? (*jmp_address == Assembler::kJncShortOpcode ? not_zero : zero) |
| 843 : (*jmp_address == Assembler::kJnzShortOpcode ? not_carry : carry); | 600 : (*jmp_address == Assembler::kJnzShortOpcode ? not_carry : carry); |
| 844 *jmp_address = static_cast<byte>(Assembler::kJccShortPrefix | cc); | 601 *jmp_address = static_cast<byte>(Assembler::kJccShortPrefix | cc); |
| 845 } | 602 } |
| 846 } // namespace internal | 603 } // namespace internal |
| 847 } // namespace v8 | 604 } // namespace v8 |
| 848 | 605 |
| 849 #endif // V8_TARGET_ARCH_X64 | 606 #endif // V8_TARGET_ARCH_X64 |
| OLD | NEW |