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 |