OLD | NEW |
| (Empty) |
1 // Copyright 2006-2008 the V8 project authors. All rights reserved. | |
2 // Redistribution and use in source and binary forms, with or without | |
3 // modification, are permitted provided that the following conditions are | |
4 // met: | |
5 // | |
6 // * Redistributions of source code must retain the above copyright | |
7 // notice, this list of conditions and the following disclaimer. | |
8 // * Redistributions in binary form must reproduce the above | |
9 // copyright notice, this list of conditions and the following | |
10 // disclaimer in the documentation and/or other materials provided | |
11 // with the distribution. | |
12 // * Neither the name of Google Inc. nor the names of its | |
13 // contributors may be used to endorse or promote products derived | |
14 // from this software without specific prior written permission. | |
15 // | |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 | |
28 #include "v8.h" | |
29 | |
30 #include "codegen-inl.h" | |
31 #include "ic-inl.h" | |
32 #include "runtime.h" | |
33 #include "stub-cache.h" | |
34 | |
35 namespace v8 { namespace internal { | |
36 | |
37 // ---------------------------------------------------------------------------- | |
38 // Static IC stub generators. | |
39 // | |
40 | |
41 #define __ ACCESS_MASM(masm) | |
42 | |
43 | |
44 // Helper function used to load a property from a dictionary backing storage. | |
45 static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label, | |
46 Register r0, Register r1, Register r2, | |
47 Register name) { | |
48 // Register use: | |
49 // | |
50 // r0 - used to hold the property dictionary. | |
51 // | |
52 // r1 - initially the receiver | |
53 // - used for the index into the property dictionary | |
54 // - holds the result on exit. | |
55 // | |
56 // r2 - used to hold the capacity of the property dictionary. | |
57 // | |
58 // name - holds the name of the property and is unchanges. | |
59 | |
60 Label done; | |
61 | |
62 // Check for the absence of an interceptor. | |
63 // Load the map into r0. | |
64 __ mov(r0, FieldOperand(r1, JSObject::kMapOffset)); | |
65 // Test the has_named_interceptor bit in the map. | |
66 __ test(FieldOperand(r0, Map::kInstanceAttributesOffset), | |
67 Immediate(1 << (Map::kHasNamedInterceptor + (3 * 8)))); | |
68 // Jump to miss if the interceptor bit is set. | |
69 __ j(not_zero, miss_label, not_taken); | |
70 | |
71 // Check that the properties array is a dictionary. | |
72 __ mov(r0, FieldOperand(r1, JSObject::kPropertiesOffset)); | |
73 __ cmp(FieldOperand(r0, HeapObject::kMapOffset), | |
74 Immediate(Factory::hash_table_map())); | |
75 __ j(not_equal, miss_label); | |
76 | |
77 // Compute the capacity mask. | |
78 const int kCapacityOffset = | |
79 Array::kHeaderSize + Dictionary::kCapacityIndex * kPointerSize; | |
80 __ mov(r2, FieldOperand(r0, kCapacityOffset)); | |
81 __ shr(r2, kSmiTagSize); // convert smi to int | |
82 __ dec(r2); | |
83 | |
84 // Generate an unrolled loop that performs a few probes before | |
85 // giving up. Measurements done on Gmail indicate that 2 probes | |
86 // cover ~93% of loads from dictionaries. | |
87 static const int kProbes = 4; | |
88 const int kElementsStartOffset = | |
89 Array::kHeaderSize + Dictionary::kElementsStartIndex * kPointerSize; | |
90 for (int i = 0; i < kProbes; i++) { | |
91 // Compute the masked index: (hash + i + i * i) & mask. | |
92 __ mov(r1, FieldOperand(name, String::kLengthOffset)); | |
93 __ shr(r1, String::kHashShift); | |
94 if (i > 0) { | |
95 __ add(Operand(r1), Immediate(Dictionary::GetProbeOffset(i))); | |
96 } | |
97 __ and_(r1, Operand(r2)); | |
98 | |
99 // Scale the index by multiplying by the element size. | |
100 ASSERT(Dictionary::kElementSize == 3); | |
101 __ lea(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3 | |
102 | |
103 // Check if the key is identical to the name. | |
104 __ cmp(name, | |
105 Operand(r0, r1, times_4, kElementsStartOffset - kHeapObjectTag)); | |
106 if (i != kProbes - 1) { | |
107 __ j(equal, &done, taken); | |
108 } else { | |
109 __ j(not_equal, miss_label, not_taken); | |
110 } | |
111 } | |
112 | |
113 // Check that the value is a normal property. | |
114 __ bind(&done); | |
115 const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; | |
116 __ test(Operand(r0, r1, times_4, kDetailsOffset - kHeapObjectTag), | |
117 Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize)); | |
118 __ j(not_zero, miss_label, not_taken); | |
119 | |
120 // Get the value at the masked, scaled index. | |
121 const int kValueOffset = kElementsStartOffset + kPointerSize; | |
122 __ mov(r1, Operand(r0, r1, times_4, kValueOffset - kHeapObjectTag)); | |
123 } | |
124 | |
125 | |
126 // Helper function used to check that a value is either not a function | |
127 // or is loaded if it is a function. | |
128 static void GenerateCheckNonFunctionOrLoaded(MacroAssembler* masm, Label* miss, | |
129 Register value, Register scratch) { | |
130 Label done; | |
131 // Check if the value is a Smi. | |
132 __ test(value, Immediate(kSmiTagMask)); | |
133 __ j(zero, &done, not_taken); | |
134 // Check if the value is a function. | |
135 __ CmpObjectType(value, JS_FUNCTION_TYPE, scratch); | |
136 __ j(not_equal, &done, taken); | |
137 // Check if the function has been loaded. | |
138 __ mov(scratch, FieldOperand(value, JSFunction::kSharedFunctionInfoOffset)); | |
139 __ mov(scratch, | |
140 FieldOperand(scratch, SharedFunctionInfo::kLazyLoadDataOffset)); | |
141 __ cmp(scratch, Factory::undefined_value()); | |
142 __ j(not_equal, miss, not_taken); | |
143 __ bind(&done); | |
144 } | |
145 | |
146 | |
147 void LoadIC::GenerateArrayLength(MacroAssembler* masm) { | |
148 // ----------- S t a t e ------------- | |
149 // -- ecx : name | |
150 // -- esp[0] : return address | |
151 // -- esp[4] : receiver | |
152 // ----------------------------------- | |
153 | |
154 Label miss; | |
155 | |
156 __ mov(eax, Operand(esp, kPointerSize)); | |
157 | |
158 StubCompiler::GenerateLoadArrayLength(masm, eax, edx, &miss); | |
159 __ bind(&miss); | |
160 StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); | |
161 } | |
162 | |
163 | |
164 void LoadIC::GenerateStringLength(MacroAssembler* masm) { | |
165 // ----------- S t a t e ------------- | |
166 // -- ecx : name | |
167 // -- esp[0] : return address | |
168 // -- esp[4] : receiver | |
169 // ----------------------------------- | |
170 | |
171 Label miss; | |
172 | |
173 __ mov(eax, Operand(esp, kPointerSize)); | |
174 | |
175 StubCompiler::GenerateLoadStringLength(masm, eax, edx, &miss); | |
176 __ bind(&miss); | |
177 StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); | |
178 } | |
179 | |
180 | |
181 void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) { | |
182 // ----------- S t a t e ------------- | |
183 // -- ecx : name | |
184 // -- esp[0] : return address | |
185 // -- esp[4] : receiver | |
186 // ----------------------------------- | |
187 | |
188 Label miss; | |
189 | |
190 __ mov(eax, Operand(esp, kPointerSize)); | |
191 | |
192 StubCompiler::GenerateLoadFunctionPrototype(masm, eax, edx, ebx, &miss); | |
193 __ bind(&miss); | |
194 StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); | |
195 } | |
196 | |
197 | |
198 #ifdef DEBUG | |
199 // For use in assert below. | |
200 static int TenToThe(int exponent) { | |
201 ASSERT(exponent <= 9); | |
202 ASSERT(exponent >= 1); | |
203 int answer = 10; | |
204 for (int i = 1; i < exponent; i++) answer *= 10; | |
205 return answer; | |
206 } | |
207 #endif | |
208 | |
209 | |
210 void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { | |
211 // ----------- S t a t e ------------- | |
212 // -- esp[0] : return address | |
213 // -- esp[4] : name | |
214 // -- esp[8] : receiver | |
215 // ----------------------------------- | |
216 Label slow, fast, check_string, index_int, index_string; | |
217 | |
218 // Load name and receiver. | |
219 __ mov(eax, (Operand(esp, kPointerSize))); | |
220 __ mov(ecx, (Operand(esp, 2 * kPointerSize))); | |
221 | |
222 // Check that the object isn't a smi. | |
223 __ test(ecx, Immediate(kSmiTagMask)); | |
224 __ j(zero, &slow, not_taken); | |
225 | |
226 // Get the map of the receiver. | |
227 __ mov(edx, FieldOperand(ecx, HeapObject::kMapOffset)); | |
228 // Check that the receiver does not require access checks. We need | |
229 // to check this explicitly since this generic stub does not perform | |
230 // map checks. | |
231 __ movzx_b(ebx, FieldOperand(edx, Map::kBitFieldOffset)); | |
232 __ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded)); | |
233 __ j(not_zero, &slow, not_taken); | |
234 // Check that the object is some kind of JS object EXCEPT JS Value type. | |
235 // In the case that the object is a value-wrapper object, | |
236 // we enter the runtime system to make sure that indexing | |
237 // into string objects work as intended. | |
238 ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); | |
239 __ movzx_b(edx, FieldOperand(edx, Map::kInstanceTypeOffset)); | |
240 __ cmp(edx, JS_OBJECT_TYPE); | |
241 __ j(less, &slow, not_taken); | |
242 // Check that the key is a smi. | |
243 __ test(eax, Immediate(kSmiTagMask)); | |
244 __ j(not_zero, &check_string, not_taken); | |
245 __ sar(eax, kSmiTagSize); | |
246 // Get the elements array of the object. | |
247 __ bind(&index_int); | |
248 __ mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset)); | |
249 // Check that the object is in fast mode (not dictionary). | |
250 __ cmp(FieldOperand(ecx, HeapObject::kMapOffset), | |
251 Immediate(Factory::hash_table_map())); | |
252 __ j(equal, &slow, not_taken); | |
253 // Check that the key (index) is within bounds. | |
254 __ cmp(eax, FieldOperand(ecx, Array::kLengthOffset)); | |
255 __ j(below, &fast, taken); | |
256 // Slow case: Load name and receiver from stack and jump to runtime. | |
257 __ bind(&slow); | |
258 __ IncrementCounter(&Counters::keyed_load_generic_slow, 1); | |
259 KeyedLoadIC::Generate(masm, ExternalReference(Runtime::kKeyedGetProperty)); | |
260 // Check if the key is a symbol that is not an array index. | |
261 __ bind(&check_string); | |
262 __ mov(ebx, FieldOperand(eax, String::kLengthOffset)); | |
263 __ test(ebx, Immediate(String::kIsArrayIndexMask)); | |
264 __ j(not_zero, &index_string, not_taken); | |
265 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); | |
266 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset)); | |
267 __ test(ebx, Immediate(kIsSymbolMask)); | |
268 __ j(not_zero, &slow, not_taken); | |
269 // Probe the dictionary leaving result in ecx. | |
270 GenerateDictionaryLoad(masm, &slow, ebx, ecx, edx, eax); | |
271 GenerateCheckNonFunctionOrLoaded(masm, &slow, ecx, edx); | |
272 __ mov(eax, Operand(ecx)); | |
273 __ IncrementCounter(&Counters::keyed_load_generic_symbol, 1); | |
274 __ ret(0); | |
275 // Array index string: If short enough use cache in length/hash field (ebx). | |
276 // We assert that there are enough bits in an int32_t after the hash shift | |
277 // bits have been subtracted to allow space for the length and the cached | |
278 // array index. | |
279 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) < | |
280 (1 << (String::kShortLengthShift - String::kHashShift))); | |
281 __ bind(&index_string); | |
282 const int kLengthFieldLimit = | |
283 (String::kMaxCachedArrayIndexLength + 1) << String::kShortLengthShift; | |
284 __ cmp(ebx, kLengthFieldLimit); | |
285 __ j(above_equal, &slow); | |
286 __ mov(eax, Operand(ebx)); | |
287 __ and_(eax, (1 << String::kShortLengthShift) - 1); | |
288 __ shr(eax, String::kLongLengthShift); | |
289 __ jmp(&index_int); | |
290 // Fast case: Do the load. | |
291 __ bind(&fast); | |
292 __ mov(eax, Operand(ecx, eax, times_4, Array::kHeaderSize - kHeapObjectTag)); | |
293 __ cmp(Operand(eax), Immediate(Factory::the_hole_value())); | |
294 // In case the loaded value is the_hole we have to consult GetProperty | |
295 // to ensure the prototype chain is searched. | |
296 __ j(equal, &slow, not_taken); | |
297 __ IncrementCounter(&Counters::keyed_load_generic_smi, 1); | |
298 __ ret(0); | |
299 } | |
300 | |
301 | |
302 void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { | |
303 // ----------- S t a t e ------------- | |
304 // -- eax : value | |
305 // -- esp[0] : return address | |
306 // -- esp[4] : key | |
307 // -- esp[8] : receiver | |
308 // ----------------------------------- | |
309 Label slow, fast, array, extra; | |
310 | |
311 // Get the receiver from the stack. | |
312 __ mov(edx, Operand(esp, 2 * kPointerSize)); // 2 ~ return address, key | |
313 // Check that the object isn't a smi. | |
314 __ test(edx, Immediate(kSmiTagMask)); | |
315 __ j(zero, &slow, not_taken); | |
316 // Get the map from the receiver. | |
317 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); | |
318 // Check that the receiver does not require access checks. We need | |
319 // to do this because this generic stub does not perform map checks. | |
320 __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset)); | |
321 __ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded)); | |
322 __ j(not_zero, &slow, not_taken); | |
323 // Get the key from the stack. | |
324 __ mov(ebx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address | |
325 // Check that the key is a smi. | |
326 __ test(ebx, Immediate(kSmiTagMask)); | |
327 __ j(not_zero, &slow, not_taken); | |
328 // Get the instance type from the map of the receiver. | |
329 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); | |
330 // Check if the object is a JS array or not. | |
331 __ cmp(ecx, JS_ARRAY_TYPE); | |
332 __ j(equal, &array); | |
333 // Check that the object is some kind of JS object. | |
334 __ cmp(ecx, FIRST_JS_OBJECT_TYPE); | |
335 __ j(less, &slow, not_taken); | |
336 | |
337 // Object case: Check key against length in the elements array. | |
338 // eax: value | |
339 // edx: JSObject | |
340 // ebx: index (as a smi) | |
341 __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset)); | |
342 // Check that the object is in fast mode (not dictionary). | |
343 __ cmp(FieldOperand(ecx, HeapObject::kMapOffset), | |
344 Immediate(Factory::hash_table_map())); | |
345 __ j(equal, &slow, not_taken); | |
346 // Untag the key (for checking against untagged length in the fixed array). | |
347 __ mov(edx, Operand(ebx)); | |
348 __ sar(edx, kSmiTagSize); // untag the index and use it for the comparison | |
349 __ cmp(edx, FieldOperand(ecx, Array::kLengthOffset)); | |
350 // eax: value | |
351 // ecx: FixedArray | |
352 // ebx: index (as a smi) | |
353 __ j(below, &fast, taken); | |
354 | |
355 | |
356 // Slow case: Push extra copies of the arguments (3). | |
357 __ bind(&slow); | |
358 __ pop(ecx); | |
359 __ push(Operand(esp, 1 * kPointerSize)); | |
360 __ push(Operand(esp, 1 * kPointerSize)); | |
361 __ push(eax); | |
362 __ push(ecx); | |
363 // Do tail-call to runtime routine. | |
364 __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3); | |
365 | |
366 | |
367 // Extra capacity case: Check if there is extra capacity to | |
368 // perform the store and update the length. Used for adding one | |
369 // element to the array by writing to array[array.length]. | |
370 __ bind(&extra); | |
371 // eax: value | |
372 // edx: JSArray | |
373 // ecx: FixedArray | |
374 // ebx: index (as a smi) | |
375 // flags: compare (ebx, edx.length()) | |
376 __ j(not_equal, &slow, not_taken); // do not leave holes in the array | |
377 __ sar(ebx, kSmiTagSize); // untag | |
378 __ cmp(ebx, FieldOperand(ecx, Array::kLengthOffset)); | |
379 __ j(above_equal, &slow, not_taken); | |
380 // Restore tag and increment. | |
381 __ lea(ebx, Operand(ebx, times_2, 1 << kSmiTagSize)); | |
382 __ mov(FieldOperand(edx, JSArray::kLengthOffset), ebx); | |
383 __ sub(Operand(ebx), Immediate(1 << kSmiTagSize)); // decrement ebx again | |
384 __ jmp(&fast); | |
385 | |
386 | |
387 // Array case: Get the length and the elements array from the JS | |
388 // array. Check that the array is in fast mode; if it is the | |
389 // length is always a smi. | |
390 __ bind(&array); | |
391 // eax: value | |
392 // edx: JSArray | |
393 // ebx: index (as a smi) | |
394 __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset)); | |
395 __ cmp(FieldOperand(ecx, HeapObject::kMapOffset), | |
396 Immediate(Factory::hash_table_map())); | |
397 __ j(equal, &slow, not_taken); | |
398 | |
399 // Check the key against the length in the array, compute the | |
400 // address to store into and fall through to fast case. | |
401 __ cmp(ebx, FieldOperand(edx, JSArray::kLengthOffset)); | |
402 __ j(above_equal, &extra, not_taken); | |
403 | |
404 | |
405 // Fast case: Do the store. | |
406 __ bind(&fast); | |
407 // eax: value | |
408 // ecx: FixedArray | |
409 // ebx: index (as a smi) | |
410 __ mov(Operand(ecx, ebx, times_2, Array::kHeaderSize - kHeapObjectTag), eax); | |
411 // Update write barrier for the elements array address. | |
412 __ mov(edx, Operand(eax)); | |
413 __ RecordWrite(ecx, 0, edx, ebx); | |
414 __ ret(0); | |
415 } | |
416 | |
417 | |
418 // Defined in ic.cc. | |
419 Object* CallIC_Miss(Arguments args); | |
420 | |
421 void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { | |
422 // ----------- S t a t e ------------- | |
423 // ----------------------------------- | |
424 Label number, non_number, non_string, boolean, probe, miss; | |
425 | |
426 // Get the receiver of the function from the stack; 1 ~ return address. | |
427 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); | |
428 // Get the name of the function from the stack; 2 ~ return address, receiver | |
429 __ mov(ecx, Operand(esp, (argc + 2) * kPointerSize)); | |
430 | |
431 // Probe the stub cache. | |
432 Code::Flags flags = | |
433 Code::ComputeFlags(Code::CALL_IC, MONOMORPHIC, NORMAL, argc); | |
434 StubCache::GenerateProbe(masm, flags, edx, ecx, ebx); | |
435 | |
436 // If the stub cache probing failed, the receiver might be a value. | |
437 // For value objects, we use the map of the prototype objects for | |
438 // the corresponding JSValue for the cache and that is what we need | |
439 // to probe. | |
440 // | |
441 // Check for number. | |
442 __ test(edx, Immediate(kSmiTagMask)); | |
443 __ j(zero, &number, not_taken); | |
444 __ CmpObjectType(edx, HEAP_NUMBER_TYPE, ebx); | |
445 __ j(not_equal, &non_number, taken); | |
446 __ bind(&number); | |
447 StubCompiler::GenerateLoadGlobalFunctionPrototype( | |
448 masm, Context::NUMBER_FUNCTION_INDEX, edx); | |
449 __ jmp(&probe); | |
450 | |
451 // Check for string. | |
452 __ bind(&non_number); | |
453 __ cmp(ebx, FIRST_NONSTRING_TYPE); | |
454 __ j(above_equal, &non_string, taken); | |
455 StubCompiler::GenerateLoadGlobalFunctionPrototype( | |
456 masm, Context::STRING_FUNCTION_INDEX, edx); | |
457 __ jmp(&probe); | |
458 | |
459 // Check for boolean. | |
460 __ bind(&non_string); | |
461 __ cmp(edx, Factory::true_value()); | |
462 __ j(equal, &boolean, not_taken); | |
463 __ cmp(edx, Factory::false_value()); | |
464 __ j(not_equal, &miss, taken); | |
465 __ bind(&boolean); | |
466 StubCompiler::GenerateLoadGlobalFunctionPrototype( | |
467 masm, Context::BOOLEAN_FUNCTION_INDEX, edx); | |
468 | |
469 // Probe the stub cache for the value object. | |
470 __ bind(&probe); | |
471 StubCache::GenerateProbe(masm, flags, edx, ecx, ebx); | |
472 | |
473 // Cache miss: Jump to runtime. | |
474 __ bind(&miss); | |
475 Generate(masm, argc, ExternalReference(IC_Utility(kCallIC_Miss))); | |
476 } | |
477 | |
478 | |
479 static void GenerateNormalHelper(MacroAssembler* masm, | |
480 int argc, | |
481 bool is_global_object, | |
482 Label* miss) { | |
483 // Search dictionary - put result in register edx. | |
484 GenerateDictionaryLoad(masm, miss, eax, edx, ebx, ecx); | |
485 | |
486 // Move the result to register edi and check that it isn't a smi. | |
487 __ mov(edi, Operand(edx)); | |
488 __ test(edx, Immediate(kSmiTagMask)); | |
489 __ j(zero, miss, not_taken); | |
490 | |
491 // Check that the value is a JavaScript function. | |
492 __ CmpObjectType(edx, JS_FUNCTION_TYPE, edx); | |
493 __ j(not_equal, miss, not_taken); | |
494 | |
495 // Check that the function has been loaded. | |
496 __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); | |
497 __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kLazyLoadDataOffset)); | |
498 __ cmp(edx, Factory::undefined_value()); | |
499 __ j(not_equal, miss, not_taken); | |
500 | |
501 // Patch the receiver with the global proxy if necessary. | |
502 if (is_global_object) { | |
503 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); | |
504 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); | |
505 __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); | |
506 } | |
507 | |
508 // Invoke the function. | |
509 ParameterCount actual(argc); | |
510 __ InvokeFunction(edi, actual, JUMP_FUNCTION); | |
511 } | |
512 | |
513 | |
514 void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { | |
515 // ----------- S t a t e ------------- | |
516 // ----------------------------------- | |
517 | |
518 Label miss, global_object, non_global_object; | |
519 | |
520 // Get the receiver of the function from the stack; 1 ~ return address. | |
521 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); | |
522 // Get the name of the function from the stack; 2 ~ return address, receiver. | |
523 __ mov(ecx, Operand(esp, (argc + 2) * kPointerSize)); | |
524 | |
525 // Check that the receiver isn't a smi. | |
526 __ test(edx, Immediate(kSmiTagMask)); | |
527 __ j(zero, &miss, not_taken); | |
528 | |
529 // Check that the receiver is a valid JS object. | |
530 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); | |
531 __ movzx_b(eax, FieldOperand(ebx, Map::kInstanceTypeOffset)); | |
532 __ cmp(eax, FIRST_JS_OBJECT_TYPE); | |
533 __ j(less, &miss, not_taken); | |
534 | |
535 // If this assert fails, we have to check upper bound too. | |
536 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); | |
537 | |
538 // Check for access to global object. | |
539 __ cmp(eax, JS_GLOBAL_OBJECT_TYPE); | |
540 __ j(equal, &global_object); | |
541 __ cmp(eax, JS_BUILTINS_OBJECT_TYPE); | |
542 __ j(not_equal, &non_global_object); | |
543 | |
544 // Accessing global object: Load and invoke. | |
545 __ bind(&global_object); | |
546 // Check that the global object does not require access checks. | |
547 __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset)); | |
548 __ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded)); | |
549 __ j(not_equal, &miss, not_taken); | |
550 GenerateNormalHelper(masm, argc, true, &miss); | |
551 | |
552 // Accessing non-global object: Check for access to global proxy. | |
553 Label global_proxy, invoke; | |
554 __ bind(&non_global_object); | |
555 __ cmp(eax, JS_GLOBAL_PROXY_TYPE); | |
556 __ j(equal, &global_proxy, not_taken); | |
557 // Check that the non-global, non-global-proxy object does not | |
558 // require access checks. | |
559 __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset)); | |
560 __ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded)); | |
561 __ j(not_equal, &miss, not_taken); | |
562 __ bind(&invoke); | |
563 GenerateNormalHelper(masm, argc, false, &miss); | |
564 | |
565 // Global object proxy access: Check access rights. | |
566 __ bind(&global_proxy); | |
567 __ CheckAccessGlobalProxy(edx, eax, &miss); | |
568 __ jmp(&invoke); | |
569 | |
570 // Cache miss: Jump to runtime. | |
571 __ bind(&miss); | |
572 Generate(masm, argc, ExternalReference(IC_Utility(kCallIC_Miss))); | |
573 } | |
574 | |
575 | |
576 void CallIC::Generate(MacroAssembler* masm, | |
577 int argc, | |
578 const ExternalReference& f) { | |
579 // ----------- S t a t e ------------- | |
580 // ----------------------------------- | |
581 | |
582 // Get the receiver of the function from the stack; 1 ~ return address. | |
583 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); | |
584 // Get the name of the function to call from the stack. | |
585 // 2 ~ receiver, return address. | |
586 __ mov(ebx, Operand(esp, (argc + 2) * kPointerSize)); | |
587 | |
588 // Enter an internal frame. | |
589 __ EnterInternalFrame(); | |
590 | |
591 // Push the receiver and the name of the function. | |
592 __ push(edx); | |
593 __ push(ebx); | |
594 | |
595 // Call the entry. | |
596 CEntryStub stub; | |
597 __ mov(eax, Immediate(2)); | |
598 __ mov(ebx, Immediate(f)); | |
599 __ CallStub(&stub); | |
600 | |
601 // Move result to edi and exit the internal frame. | |
602 __ mov(edi, eax); | |
603 __ LeaveInternalFrame(); | |
604 | |
605 // Check if the receiver is a global object of some sort. | |
606 Label invoke, global; | |
607 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); // receiver | |
608 __ test(edx, Immediate(kSmiTagMask)); | |
609 __ j(zero, &invoke, not_taken); | |
610 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); | |
611 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); | |
612 __ cmp(ecx, JS_GLOBAL_OBJECT_TYPE); | |
613 __ j(equal, &global); | |
614 __ cmp(ecx, JS_BUILTINS_OBJECT_TYPE); | |
615 __ j(not_equal, &invoke); | |
616 | |
617 // Patch the receiver on the stack. | |
618 __ bind(&global); | |
619 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); | |
620 __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); | |
621 | |
622 // Invoke the function. | |
623 ParameterCount actual(argc); | |
624 __ bind(&invoke); | |
625 __ InvokeFunction(edi, actual, JUMP_FUNCTION); | |
626 } | |
627 | |
628 | |
629 // Defined in ic.cc. | |
630 Object* LoadIC_Miss(Arguments args); | |
631 | |
632 void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { | |
633 // ----------- S t a t e ------------- | |
634 // -- ecx : name | |
635 // -- esp[0] : return address | |
636 // -- esp[4] : receiver | |
637 // ----------------------------------- | |
638 | |
639 __ mov(eax, Operand(esp, kPointerSize)); | |
640 | |
641 // Probe the stub cache. | |
642 Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC); | |
643 StubCache::GenerateProbe(masm, flags, eax, ecx, ebx); | |
644 | |
645 // Cache miss: Jump to runtime. | |
646 Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); | |
647 } | |
648 | |
649 | |
650 void LoadIC::GenerateNormal(MacroAssembler* masm) { | |
651 // ----------- S t a t e ------------- | |
652 // -- ecx : name | |
653 // -- esp[0] : return address | |
654 // -- esp[4] : receiver | |
655 // ----------------------------------- | |
656 | |
657 Label miss, probe, global; | |
658 | |
659 __ mov(eax, Operand(esp, kPointerSize)); | |
660 | |
661 // Check that the receiver isn't a smi. | |
662 __ test(eax, Immediate(kSmiTagMask)); | |
663 __ j(zero, &miss, not_taken); | |
664 | |
665 // Check that the receiver is a valid JS object. | |
666 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); | |
667 __ movzx_b(edx, FieldOperand(ebx, Map::kInstanceTypeOffset)); | |
668 __ cmp(edx, FIRST_JS_OBJECT_TYPE); | |
669 __ j(less, &miss, not_taken); | |
670 | |
671 // If this assert fails, we have to check upper bound too. | |
672 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); | |
673 | |
674 // Check for access to global object (unlikely). | |
675 __ cmp(edx, JS_GLOBAL_PROXY_TYPE); | |
676 __ j(equal, &global, not_taken); | |
677 | |
678 // Check for non-global object that requires access check. | |
679 __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset)); | |
680 __ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded)); | |
681 __ j(not_zero, &miss, not_taken); | |
682 | |
683 // Search the dictionary placing the result in eax. | |
684 __ bind(&probe); | |
685 GenerateDictionaryLoad(masm, &miss, edx, eax, ebx, ecx); | |
686 GenerateCheckNonFunctionOrLoaded(masm, &miss, eax, edx); | |
687 __ ret(0); | |
688 | |
689 // Global object access: Check access rights. | |
690 __ bind(&global); | |
691 __ CheckAccessGlobalProxy(eax, edx, &miss); | |
692 __ jmp(&probe); | |
693 | |
694 // Cache miss: Restore receiver from stack and jump to runtime. | |
695 __ bind(&miss); | |
696 __ mov(eax, Operand(esp, 1 * kPointerSize)); | |
697 Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); | |
698 } | |
699 | |
700 | |
701 void LoadIC::GenerateMiss(MacroAssembler* masm) { | |
702 // ----------- S t a t e ------------- | |
703 // -- ecx : name | |
704 // -- esp[0] : return address | |
705 // -- esp[4] : receiver | |
706 // ----------------------------------- | |
707 | |
708 Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); | |
709 } | |
710 | |
711 | |
712 void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) { | |
713 // ----------- S t a t e ------------- | |
714 // -- ecx : name | |
715 // -- esp[0] : return address | |
716 // -- esp[4] : receiver | |
717 // ----------------------------------- | |
718 | |
719 __ mov(eax, Operand(esp, kPointerSize)); | |
720 | |
721 // Move the return address below the arguments. | |
722 __ pop(ebx); | |
723 __ push(eax); | |
724 __ push(ecx); | |
725 __ push(ebx); | |
726 | |
727 // Perform tail call to the entry. | |
728 __ TailCallRuntime(f, 2); | |
729 } | |
730 | |
731 | |
732 // One byte opcode for test eax,0xXXXXXXXX. | |
733 static const byte kTestEaxByte = 0xA9; | |
734 | |
735 | |
736 bool KeyedLoadIC::HasInlinedVersion(Address address) { | |
737 Address test_instruction_address = address + 4; // 4 = stub address | |
738 return *test_instruction_address == kTestEaxByte; | |
739 } | |
740 | |
741 | |
742 void KeyedLoadIC::ClearInlinedVersion(Address address) { | |
743 // Insert null as the map to check for to make sure the map check fails | |
744 // sending control flow to the IC instead of the inlined version. | |
745 PatchInlinedMapCheck(address, Heap::null_value()); | |
746 } | |
747 | |
748 | |
749 void KeyedLoadIC::PatchInlinedMapCheck(Address address, Object* value) { | |
750 Address test_instruction_address = address + 4; // 4 = stub address | |
751 // The keyed load has a fast inlined case if the IC call instruction | |
752 // is immediately followed by a test instruction. | |
753 if (*test_instruction_address == kTestEaxByte) { | |
754 // Fetch the offset from the test instruction to the map cmp | |
755 // instruction. This offset is stored in the last 4 bytes of the | |
756 // 5 byte test instruction. | |
757 Address offset_address = test_instruction_address + 1; | |
758 int offset_value = *(reinterpret_cast<int*>(offset_address)); | |
759 // Compute the map address. The map address is in the last 4 | |
760 // bytes of the 7-byte operand-immediate compare instruction, so | |
761 // we add 3 to the offset to get the map address. | |
762 Address map_address = test_instruction_address + offset_value + 3; | |
763 // Patch the map check. | |
764 (*(reinterpret_cast<Object**>(map_address))) = value; | |
765 } | |
766 } | |
767 | |
768 | |
769 // Defined in ic.cc. | |
770 Object* KeyedLoadIC_Miss(Arguments args); | |
771 | |
772 | |
773 void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { | |
774 // ----------- S t a t e ------------- | |
775 // -- esp[0] : return address | |
776 // -- esp[4] : name | |
777 // -- esp[8] : receiver | |
778 // ----------------------------------- | |
779 | |
780 Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss))); | |
781 } | |
782 | |
783 | |
784 void KeyedLoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) { | |
785 // ----------- S t a t e ------------- | |
786 // -- esp[0] : return address | |
787 // -- esp[4] : name | |
788 // -- esp[8] : receiver | |
789 // ----------------------------------- | |
790 | |
791 __ mov(eax, Operand(esp, kPointerSize)); | |
792 __ mov(ecx, Operand(esp, 2 * kPointerSize)); | |
793 | |
794 // Move the return address below the arguments. | |
795 __ pop(ebx); | |
796 __ push(ecx); | |
797 __ push(eax); | |
798 __ push(ebx); | |
799 | |
800 // Perform tail call to the entry. | |
801 __ TailCallRuntime(f, 2); | |
802 } | |
803 | |
804 | |
805 void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { | |
806 // ----------- S t a t e ------------- | |
807 // -- eax : value | |
808 // -- ecx : name | |
809 // -- esp[0] : return address | |
810 // -- esp[4] : receiver | |
811 // ----------------------------------- | |
812 | |
813 // Get the receiver from the stack and probe the stub cache. | |
814 __ mov(edx, Operand(esp, 4)); | |
815 Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC); | |
816 StubCache::GenerateProbe(masm, flags, edx, ecx, ebx); | |
817 | |
818 // Cache miss: Jump to runtime. | |
819 Generate(masm, ExternalReference(IC_Utility(kStoreIC_Miss))); | |
820 } | |
821 | |
822 | |
823 void StoreIC::GenerateExtendStorage(MacroAssembler* masm) { | |
824 // ----------- S t a t e ------------- | |
825 // -- eax : value | |
826 // -- ecx : transition map | |
827 // -- esp[0] : return address | |
828 // -- esp[4] : receiver | |
829 // ----------------------------------- | |
830 | |
831 // Move the return address below the arguments. | |
832 __ pop(ebx); | |
833 __ push(Operand(esp, 0)); | |
834 __ push(ecx); | |
835 __ push(eax); | |
836 __ push(ebx); | |
837 // Perform tail call to the entry. | |
838 __ TailCallRuntime( | |
839 ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3); | |
840 } | |
841 | |
842 | |
843 void StoreIC::Generate(MacroAssembler* masm, const ExternalReference& f) { | |
844 // ----------- S t a t e ------------- | |
845 // -- eax : value | |
846 // -- ecx : name | |
847 // -- esp[0] : return address | |
848 // -- esp[4] : receiver | |
849 // ----------------------------------- | |
850 | |
851 // Move the return address below the arguments. | |
852 __ pop(ebx); | |
853 __ push(Operand(esp, 0)); | |
854 __ push(ecx); | |
855 __ push(eax); | |
856 __ push(ebx); | |
857 | |
858 // Perform tail call to the entry. | |
859 __ TailCallRuntime(f, 3); | |
860 } | |
861 | |
862 | |
863 // Defined in ic.cc. | |
864 Object* KeyedStoreIC_Miss(Arguments args); | |
865 | |
866 void KeyedStoreIC::Generate(MacroAssembler* masm, const ExternalReference& f) { | |
867 // ----------- S t a t e ------------- | |
868 // -- eax : value | |
869 // -- esp[0] : return address | |
870 // -- esp[4] : key | |
871 // -- esp[8] : receiver | |
872 // ----------------------------------- | |
873 | |
874 // Move the return address below the arguments. | |
875 __ pop(ecx); | |
876 __ push(Operand(esp, 1 * kPointerSize)); | |
877 __ push(Operand(esp, 1 * kPointerSize)); | |
878 __ push(eax); | |
879 __ push(ecx); | |
880 | |
881 // Do tail-call to runtime routine. | |
882 __ TailCallRuntime(f, 3); | |
883 } | |
884 | |
885 | |
886 void KeyedStoreIC::GenerateExtendStorage(MacroAssembler* masm) { | |
887 // ----------- S t a t e ------------- | |
888 // -- eax : value | |
889 // -- ecx : transition map | |
890 // -- esp[0] : return address | |
891 // -- esp[4] : key | |
892 // -- esp[8] : receiver | |
893 // ----------------------------------- | |
894 | |
895 // Move the return address below the arguments. | |
896 __ pop(ebx); | |
897 __ push(Operand(esp, 1 * kPointerSize)); | |
898 __ push(ecx); | |
899 __ push(eax); | |
900 __ push(ebx); | |
901 | |
902 // Do tail-call to runtime routine. | |
903 __ TailCallRuntime( | |
904 ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3); | |
905 } | |
906 | |
907 #undef __ | |
908 | |
909 | |
910 } } // namespace v8::internal | |
OLD | NEW |