| 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" |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 const int kValueOffset = kElementsStartOffset + kPointerSize; | 114 const int kValueOffset = kElementsStartOffset + kPointerSize; |
| 115 __ leap(scratch1, Operand(elements, scratch1, times_pointer_size, | 115 __ leap(scratch1, Operand(elements, scratch1, times_pointer_size, |
| 116 kValueOffset - kHeapObjectTag)); | 116 kValueOffset - kHeapObjectTag)); |
| 117 __ movp(Operand(scratch1, 0), value); | 117 __ movp(Operand(scratch1, 0), value); |
| 118 | 118 |
| 119 // Update write barrier. Make sure not to clobber the value. | 119 // Update write barrier. Make sure not to clobber the value. |
| 120 __ movp(scratch0, value); | 120 __ movp(scratch0, value); |
| 121 __ RecordWrite(elements, scratch1, scratch0, kDontSaveFPRegs); | 121 __ RecordWrite(elements, scratch1, scratch0, kDontSaveFPRegs); |
| 122 } | 122 } |
| 123 | 123 |
| 124 static void KeyedStoreGenerateMegamorphicHelper( | |
| 125 MacroAssembler* masm, Label* fast_object, Label* fast_double, Label* slow, | |
| 126 KeyedStoreCheckMap check_map, KeyedStoreIncrementLength increment_length) { | |
| 127 Label transition_smi_elements; | |
| 128 Label finish_object_store, non_double_value, transition_double_elements; | |
| 129 Label fast_double_without_map_check; | |
| 130 Register receiver = StoreDescriptor::ReceiverRegister(); | |
| 131 Register key = StoreDescriptor::NameRegister(); | |
| 132 Register value = StoreDescriptor::ValueRegister(); | |
| 133 DCHECK(receiver.is(rdx)); | |
| 134 DCHECK(key.is(rcx)); | |
| 135 DCHECK(value.is(rax)); | |
| 136 // Fast case: Do the store, could be either Object or double. | |
| 137 __ bind(fast_object); | |
| 138 // rbx: receiver's elements array (a FixedArray) | |
| 139 // receiver is a JSArray. | |
| 140 // r9: map of receiver | |
| 141 if (check_map == kCheckMap) { | |
| 142 __ movp(rdi, FieldOperand(rbx, HeapObject::kMapOffset)); | |
| 143 __ CompareRoot(rdi, Heap::kFixedArrayMapRootIndex); | |
| 144 __ j(not_equal, fast_double); | |
| 145 } | |
| 146 | |
| 147 // HOLECHECK: guards "A[i] = V" | |
| 148 // We have to go to the runtime if the current value is the hole because | |
| 149 // there may be a callback on the element | |
| 150 Label holecheck_passed1; | |
| 151 __ movp(kScratchRegister, | |
| 152 FieldOperand(rbx, key, times_pointer_size, FixedArray::kHeaderSize)); | |
| 153 __ CompareRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); | |
| 154 __ j(not_equal, &holecheck_passed1); | |
| 155 __ JumpIfDictionaryInPrototypeChain(receiver, rdi, kScratchRegister, slow); | |
| 156 | |
| 157 __ bind(&holecheck_passed1); | |
| 158 | |
| 159 // Smi stores don't require further checks. | |
| 160 Label non_smi_value; | |
| 161 __ JumpIfNotSmi(value, &non_smi_value); | |
| 162 if (increment_length == kIncrementLength) { | |
| 163 // Add 1 to receiver->length. | |
| 164 __ leal(rdi, Operand(key, 1)); | |
| 165 __ Integer32ToSmiField(FieldOperand(receiver, JSArray::kLengthOffset), rdi); | |
| 166 } | |
| 167 // It's irrelevant whether array is smi-only or not when writing a smi. | |
| 168 __ movp(FieldOperand(rbx, key, times_pointer_size, FixedArray::kHeaderSize), | |
| 169 value); | |
| 170 __ ret(0); | |
| 171 | |
| 172 __ bind(&non_smi_value); | |
| 173 // Writing a non-smi, check whether array allows non-smi elements. | |
| 174 // r9: receiver's map | |
| 175 __ CheckFastObjectElements(r9, &transition_smi_elements); | |
| 176 | |
| 177 __ bind(&finish_object_store); | |
| 178 if (increment_length == kIncrementLength) { | |
| 179 // Add 1 to receiver->length. | |
| 180 __ leal(rdi, Operand(key, 1)); | |
| 181 __ Integer32ToSmiField(FieldOperand(receiver, JSArray::kLengthOffset), rdi); | |
| 182 } | |
| 183 __ movp(FieldOperand(rbx, key, times_pointer_size, FixedArray::kHeaderSize), | |
| 184 value); | |
| 185 __ movp(rdx, value); // Preserve the value which is returned. | |
| 186 __ RecordWriteArray(rbx, rdx, key, kDontSaveFPRegs, EMIT_REMEMBERED_SET, | |
| 187 OMIT_SMI_CHECK); | |
| 188 __ ret(0); | |
| 189 | |
| 190 __ bind(fast_double); | |
| 191 if (check_map == kCheckMap) { | |
| 192 // Check for fast double array case. If this fails, call through to the | |
| 193 // runtime. | |
| 194 // rdi: elements array's map | |
| 195 __ CompareRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex); | |
| 196 __ j(not_equal, slow); | |
| 197 } | |
| 198 | |
| 199 // HOLECHECK: guards "A[i] double hole?" | |
| 200 // We have to see if the double version of the hole is present. If so | |
| 201 // go to the runtime. | |
| 202 uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32); | |
| 203 __ cmpl(FieldOperand(rbx, key, times_8, offset), Immediate(kHoleNanUpper32)); | |
| 204 __ j(not_equal, &fast_double_without_map_check); | |
| 205 __ JumpIfDictionaryInPrototypeChain(receiver, rdi, kScratchRegister, slow); | |
| 206 | |
| 207 __ bind(&fast_double_without_map_check); | |
| 208 __ StoreNumberToDoubleElements(value, rbx, key, kScratchDoubleReg, | |
| 209 &transition_double_elements); | |
| 210 if (increment_length == kIncrementLength) { | |
| 211 // Add 1 to receiver->length. | |
| 212 __ leal(rdi, Operand(key, 1)); | |
| 213 __ Integer32ToSmiField(FieldOperand(receiver, JSArray::kLengthOffset), rdi); | |
| 214 } | |
| 215 __ ret(0); | |
| 216 | |
| 217 __ bind(&transition_smi_elements); | |
| 218 __ movp(rbx, FieldOperand(receiver, HeapObject::kMapOffset)); | |
| 219 | |
| 220 // Transition the array appropriately depending on the value type. | |
| 221 __ movp(r9, FieldOperand(value, HeapObject::kMapOffset)); | |
| 222 __ CompareRoot(r9, Heap::kHeapNumberMapRootIndex); | |
| 223 __ j(not_equal, &non_double_value); | |
| 224 | |
| 225 // Value is a double. Transition FAST_SMI_ELEMENTS -> | |
| 226 // FAST_DOUBLE_ELEMENTS and complete the store. | |
| 227 __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, | |
| 228 FAST_DOUBLE_ELEMENTS, rbx, rdi, slow); | |
| 229 AllocationSiteMode mode = | |
| 230 AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS); | |
| 231 ElementsTransitionGenerator::GenerateSmiToDouble(masm, receiver, key, value, | |
| 232 rbx, mode, slow); | |
| 233 __ movp(rbx, FieldOperand(receiver, JSObject::kElementsOffset)); | |
| 234 __ jmp(&fast_double_without_map_check); | |
| 235 | |
| 236 __ bind(&non_double_value); | |
| 237 // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS | |
| 238 __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, rbx, | |
| 239 rdi, slow); | |
| 240 mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS); | |
| 241 ElementsTransitionGenerator::GenerateMapChangeElementsTransition( | |
| 242 masm, receiver, key, value, rbx, mode, slow); | |
| 243 __ movp(rbx, FieldOperand(receiver, JSObject::kElementsOffset)); | |
| 244 __ jmp(&finish_object_store); | |
| 245 | |
| 246 __ bind(&transition_double_elements); | |
| 247 // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a | |
| 248 // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and | |
| 249 // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS | |
| 250 __ movp(rbx, FieldOperand(receiver, HeapObject::kMapOffset)); | |
| 251 __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS, | |
| 252 rbx, rdi, slow); | |
| 253 mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS); | |
| 254 ElementsTransitionGenerator::GenerateDoubleToObject(masm, receiver, key, | |
| 255 value, rbx, mode, slow); | |
| 256 __ movp(rbx, FieldOperand(receiver, JSObject::kElementsOffset)); | |
| 257 __ jmp(&finish_object_store); | |
| 258 } | |
| 259 | |
| 260 | |
| 261 void KeyedStoreIC::GenerateMegamorphic(MacroAssembler* masm, | |
| 262 LanguageMode language_mode) { | |
| 263 // Return address is on the stack. | |
| 264 Label slow, slow_with_tagged_index, fast_object, fast_object_grow; | |
| 265 Label fast_double, fast_double_grow; | |
| 266 Label array, extra, check_if_double_array, maybe_name_key, miss; | |
| 267 Register receiver = StoreDescriptor::ReceiverRegister(); | |
| 268 Register key = StoreDescriptor::NameRegister(); | |
| 269 DCHECK(receiver.is(rdx)); | |
| 270 DCHECK(key.is(rcx)); | |
| 271 | |
| 272 // Check that the object isn't a smi. | |
| 273 __ JumpIfSmi(receiver, &slow_with_tagged_index); | |
| 274 // Get the map from the receiver. | |
| 275 __ movp(r9, FieldOperand(receiver, HeapObject::kMapOffset)); | |
| 276 // Check that the receiver does not require access checks. | |
| 277 // The generic stub does not perform map checks. | |
| 278 __ testb(FieldOperand(r9, Map::kBitFieldOffset), | |
| 279 Immediate(1 << Map::kIsAccessCheckNeeded)); | |
| 280 __ j(not_zero, &slow_with_tagged_index); | |
| 281 // Check that the key is a smi. | |
| 282 __ JumpIfNotSmi(key, &maybe_name_key); | |
| 283 __ SmiToInteger32(key, key); | |
| 284 | |
| 285 __ CmpInstanceType(r9, JS_ARRAY_TYPE); | |
| 286 __ j(equal, &array); | |
| 287 // Check that the object is some kind of JS object EXCEPT JS Value type. In | |
| 288 // the case that the object is a value-wrapper object, we enter the runtime | |
| 289 // system to make sure that indexing into string objects works as intended. | |
| 290 STATIC_ASSERT(JS_VALUE_TYPE < JS_OBJECT_TYPE); | |
| 291 __ CmpInstanceType(r9, JS_OBJECT_TYPE); | |
| 292 __ j(below, &slow); | |
| 293 | |
| 294 // Object case: Check key against length in the elements array. | |
| 295 __ movp(rbx, FieldOperand(receiver, JSObject::kElementsOffset)); | |
| 296 // Check array bounds. | |
| 297 __ SmiCompareInteger32(FieldOperand(rbx, FixedArray::kLengthOffset), key); | |
| 298 // rbx: FixedArray | |
| 299 __ j(above, &fast_object); | |
| 300 | |
| 301 // Slow case: call runtime. | |
| 302 __ bind(&slow); | |
| 303 __ Integer32ToSmi(key, key); | |
| 304 __ bind(&slow_with_tagged_index); | |
| 305 PropertyICCompiler::GenerateRuntimeSetProperty(masm, language_mode); | |
| 306 // Never returns to here. | |
| 307 | |
| 308 __ bind(&maybe_name_key); | |
| 309 __ movp(r9, FieldOperand(key, HeapObject::kMapOffset)); | |
| 310 __ movzxbp(r9, FieldOperand(r9, Map::kInstanceTypeOffset)); | |
| 311 __ JumpIfNotUniqueNameInstanceType(r9, &slow_with_tagged_index); | |
| 312 | |
| 313 Register vector = StoreWithVectorDescriptor::VectorRegister(); | |
| 314 Register slot = StoreWithVectorDescriptor::SlotRegister(); | |
| 315 // The handlers in the stub cache expect a vector and slot. Since we won't | |
| 316 // change the IC from any downstream misses, a dummy vector can be used. | |
| 317 Handle<TypeFeedbackVector> dummy_vector = | |
| 318 TypeFeedbackVector::DummyVector(masm->isolate()); | |
| 319 int slot_index = dummy_vector->GetIndex( | |
| 320 FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedStoreICSlot)); | |
| 321 __ Move(vector, dummy_vector); | |
| 322 __ Move(slot, Smi::FromInt(slot_index)); | |
| 323 | |
| 324 masm->isolate()->store_stub_cache()->GenerateProbe(masm, receiver, key, r9, | |
| 325 no_reg); | |
| 326 // Cache miss. | |
| 327 __ jmp(&miss); | |
| 328 | |
| 329 // Extra capacity case: Check if there is extra capacity to | |
| 330 // perform the store and update the length. Used for adding one | |
| 331 // element to the array by writing to array[array.length]. | |
| 332 __ bind(&extra); | |
| 333 // receiver is a JSArray. | |
| 334 // rbx: receiver's elements array (a FixedArray) | |
| 335 // flags: smicompare (receiver.length(), rbx) | |
| 336 __ j(not_equal, &slow); // do not leave holes in the array | |
| 337 __ SmiCompareInteger32(FieldOperand(rbx, FixedArray::kLengthOffset), key); | |
| 338 __ j(below_equal, &slow); | |
| 339 // Increment index to get new length. | |
| 340 __ movp(rdi, FieldOperand(rbx, HeapObject::kMapOffset)); | |
| 341 __ CompareRoot(rdi, Heap::kFixedArrayMapRootIndex); | |
| 342 __ j(not_equal, &check_if_double_array); | |
| 343 __ jmp(&fast_object_grow); | |
| 344 | |
| 345 __ bind(&check_if_double_array); | |
| 346 // rdi: elements array's map | |
| 347 __ CompareRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex); | |
| 348 __ j(not_equal, &slow); | |
| 349 __ jmp(&fast_double_grow); | |
| 350 | |
| 351 // Array case: Get the length and the elements array from the JS | |
| 352 // array. Check that the array is in fast mode (and writable); if it | |
| 353 // is the length is always a smi. | |
| 354 __ bind(&array); | |
| 355 // receiver is a JSArray. | |
| 356 __ movp(rbx, FieldOperand(receiver, JSObject::kElementsOffset)); | |
| 357 | |
| 358 // Check the key against the length in the array, compute the | |
| 359 // address to store into and fall through to fast case. | |
| 360 __ SmiCompareInteger32(FieldOperand(receiver, JSArray::kLengthOffset), key); | |
| 361 __ j(below_equal, &extra); | |
| 362 | |
| 363 KeyedStoreGenerateMegamorphicHelper(masm, &fast_object, &fast_double, &slow, | |
| 364 kCheckMap, kDontIncrementLength); | |
| 365 KeyedStoreGenerateMegamorphicHelper(masm, &fast_object_grow, | |
| 366 &fast_double_grow, &slow, kDontCheckMap, | |
| 367 kIncrementLength); | |
| 368 | |
| 369 __ bind(&miss); | |
| 370 GenerateMiss(masm); | |
| 371 } | |
| 372 | |
| 373 void LoadIC::GenerateNormal(MacroAssembler* masm) { | 124 void LoadIC::GenerateNormal(MacroAssembler* masm) { |
| 374 Register dictionary = rax; | 125 Register dictionary = rax; |
| 375 DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister())); | 126 DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister())); |
| 376 DCHECK(!dictionary.is(LoadDescriptor::NameRegister())); | 127 DCHECK(!dictionary.is(LoadDescriptor::NameRegister())); |
| 377 | 128 |
| 378 Label slow; | 129 Label slow; |
| 379 | 130 |
| 380 __ movp(dictionary, FieldOperand(LoadDescriptor::ReceiverRegister(), | 131 __ movp(dictionary, FieldOperand(LoadDescriptor::ReceiverRegister(), |
| 381 JSObject::kPropertiesOffset)); | 132 JSObject::kPropertiesOffset)); |
| 382 GenerateDictionaryLoad(masm, &slow, dictionary, | 133 GenerateDictionaryLoad(masm, &slow, dictionary, |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 597 Condition cc = | 348 Condition cc = |
| 598 (check == ENABLE_INLINED_SMI_CHECK) | 349 (check == ENABLE_INLINED_SMI_CHECK) |
| 599 ? (*jmp_address == Assembler::kJncShortOpcode ? not_zero : zero) | 350 ? (*jmp_address == Assembler::kJncShortOpcode ? not_zero : zero) |
| 600 : (*jmp_address == Assembler::kJnzShortOpcode ? not_carry : carry); | 351 : (*jmp_address == Assembler::kJnzShortOpcode ? not_carry : carry); |
| 601 *jmp_address = static_cast<byte>(Assembler::kJccShortPrefix | cc); | 352 *jmp_address = static_cast<byte>(Assembler::kJccShortPrefix | cc); |
| 602 } | 353 } |
| 603 } // namespace internal | 354 } // namespace internal |
| 604 } // namespace v8 | 355 } // namespace v8 |
| 605 | 356 |
| 606 #endif // V8_TARGET_ARCH_X64 | 357 #endif // V8_TARGET_ARCH_X64 |
| OLD | NEW |