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 |