blob: 62ab0f213e9302a7d182ffd406bcfb9ea0482b3e [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2006-2009 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 "accessors.h"
31#include "api.h"
32#include "arguments.h"
Ben Murdochb0fe1622011-05-05 13:52:32 +010033#include "codegen.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000034#include "execution.h"
35#include "ic-inl.h"
36#include "runtime.h"
37#include "stub-cache.h"
38
39namespace v8 {
40namespace internal {
41
42#ifdef DEBUG
43static char TransitionMarkFromState(IC::State state) {
44 switch (state) {
45 case UNINITIALIZED: return '0';
46 case PREMONOMORPHIC: return 'P';
47 case MONOMORPHIC: return '1';
48 case MONOMORPHIC_PROTOTYPE_FAILURE: return '^';
49 case MEGAMORPHIC: return 'N';
50
51 // We never see the debugger states here, because the state is
52 // computed from the original code - not the patched code. Let
53 // these cases fall through to the unreachable code below.
54 case DEBUG_BREAK: break;
55 case DEBUG_PREPARE_STEP_IN: break;
56 }
57 UNREACHABLE();
58 return 0;
59}
60
61void IC::TraceIC(const char* type,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010062 Handle<Object> name,
Steve Blocka7e24c12009-10-30 11:49:00 +000063 State old_state,
64 Code* new_target,
65 const char* extra_info) {
66 if (FLAG_trace_ic) {
Steve Block6ded16b2010-05-10 14:33:55 +010067 State new_state = StateFrom(new_target,
68 Heap::undefined_value(),
69 Heap::undefined_value());
Steve Blocka7e24c12009-10-30 11:49:00 +000070 PrintF("[%s (%c->%c)%s", type,
71 TransitionMarkFromState(old_state),
72 TransitionMarkFromState(new_state),
73 extra_info);
74 name->Print();
75 PrintF("]\n");
76 }
77}
78#endif
79
80
81IC::IC(FrameDepth depth) {
82 // To improve the performance of the (much used) IC code, we unfold
83 // a few levels of the stack frame iteration code. This yields a
84 // ~35% speedup when running DeltaBlue with the '--nouse-ic' flag.
85 const Address entry = Top::c_entry_fp(Top::GetCurrentThread());
86 Address* pc_address =
87 reinterpret_cast<Address*>(entry + ExitFrameConstants::kCallerPCOffset);
88 Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset);
89 // If there's another JavaScript frame on the stack, we need to look
90 // one frame further down the stack to find the frame pointer and
91 // the return address stack slot.
92 if (depth == EXTRA_CALL_FRAME) {
93 const int kCallerPCOffset = StandardFrameConstants::kCallerPCOffset;
94 pc_address = reinterpret_cast<Address*>(fp + kCallerPCOffset);
95 fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset);
96 }
97#ifdef DEBUG
98 StackFrameIterator it;
99 for (int i = 0; i < depth + 1; i++) it.Advance();
100 StackFrame* frame = it.frame();
101 ASSERT(fp == frame->fp() && pc_address == frame->pc_address());
102#endif
103 fp_ = fp;
104 pc_address_ = pc_address;
105}
106
107
108#ifdef ENABLE_DEBUGGER_SUPPORT
109Address IC::OriginalCodeAddress() {
110 HandleScope scope;
111 // Compute the JavaScript frame for the frame pointer of this IC
112 // structure. We need this to be able to find the function
113 // corresponding to the frame.
114 StackFrameIterator it;
115 while (it.frame()->fp() != this->fp()) it.Advance();
116 JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame());
117 // Find the function on the stack and both the active code for the
118 // function and the original code.
119 JSFunction* function = JSFunction::cast(frame->function());
120 Handle<SharedFunctionInfo> shared(function->shared());
121 Code* code = shared->code();
122 ASSERT(Debug::HasDebugInfo(shared));
123 Code* original_code = Debug::GetDebugInfo(shared)->original_code();
124 ASSERT(original_code->IsCode());
125 // Get the address of the call site in the active code. This is the
126 // place where the call to DebugBreakXXX is and where the IC
127 // normally would be.
128 Address addr = pc() - Assembler::kCallTargetAddressOffset;
129 // Return the address in the original code. This is the place where
130 // the call which has been overwritten by the DebugBreakXXX resides
131 // and the place where the inline cache system should look.
Steve Blockd0582a62009-12-15 09:54:21 +0000132 intptr_t delta =
133 original_code->instruction_start() - code->instruction_start();
Steve Blocka7e24c12009-10-30 11:49:00 +0000134 return addr + delta;
135}
136#endif
137
Steve Block8defd9f2010-07-08 12:39:36 +0100138
139static bool HasNormalObjectsInPrototypeChain(LookupResult* lookup,
140 Object* receiver) {
141 Object* end = lookup->IsProperty() ? lookup->holder() : Heap::null_value();
142 for (Object* current = receiver;
143 current != end;
144 current = current->GetPrototype()) {
145 if (current->IsJSObject() &&
146 !JSObject::cast(current)->HasFastProperties() &&
147 !current->IsJSGlobalProxy() &&
148 !current->IsJSGlobalObject()) {
149 return true;
150 }
151 }
152
153 return false;
154}
155
156
Ben Murdochb8e0da22011-05-16 14:20:40 +0100157static bool TryRemoveInvalidPrototypeDependentStub(Code* target,
158 Object* receiver,
159 Object* name) {
Steve Block8defd9f2010-07-08 12:39:36 +0100160 InlineCacheHolderFlag cache_holder =
161 Code::ExtractCacheHolderFromFlags(target->flags());
162
Steve Block8defd9f2010-07-08 12:39:36 +0100163 if (cache_holder == OWN_MAP && !receiver->IsJSObject()) {
164 // The stub was generated for JSObject but called for non-JSObject.
Kristian Monsen80d68ea2010-09-08 11:05:35 +0100165 // IC::GetCodeCacheHolder is not applicable.
Ben Murdochb8e0da22011-05-16 14:20:40 +0100166 return false;
Steve Block8defd9f2010-07-08 12:39:36 +0100167 } else if (cache_holder == PROTOTYPE_MAP &&
168 receiver->GetPrototype()->IsNull()) {
Kristian Monsen80d68ea2010-09-08 11:05:35 +0100169 // IC::GetCodeCacheHolder is not applicable.
Ben Murdochb8e0da22011-05-16 14:20:40 +0100170 return false;
Steve Block8defd9f2010-07-08 12:39:36 +0100171 }
Kristian Monsen80d68ea2010-09-08 11:05:35 +0100172 Map* map = IC::GetCodeCacheHolder(receiver, cache_holder)->map();
Steve Blocka7e24c12009-10-30 11:49:00 +0000173
174 // Decide whether the inline cache failed because of changes to the
175 // receiver itself or changes to one of its prototypes.
176 //
177 // If there are changes to the receiver itself, the map of the
178 // receiver will have changed and the current target will not be in
179 // the receiver map's code cache. Therefore, if the current target
180 // is in the receiver map's code cache, the inline cache failed due
181 // to prototype check failure.
Steve Block6ded16b2010-05-10 14:33:55 +0100182 int index = map->IndexInCodeCache(name, target);
Steve Blocka7e24c12009-10-30 11:49:00 +0000183 if (index >= 0) {
Steve Block6ded16b2010-05-10 14:33:55 +0100184 map->RemoveFromCodeCache(String::cast(name), target, index);
Ben Murdochb8e0da22011-05-16 14:20:40 +0100185 return true;
186 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000187
Ben Murdochb8e0da22011-05-16 14:20:40 +0100188 return false;
189}
190
191
192IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
193 IC::State state = target->ic_state();
194
195 if (state != MONOMORPHIC || !name->IsString()) return state;
196 if (receiver->IsUndefined() || receiver->IsNull()) return state;
197
198 // For keyed load/store/call, the most likely cause of cache failure is
199 // that the key has changed. We do not distinguish between
200 // prototype and non-prototype failures for keyed access.
201 Code::Kind kind = target->kind();
202 if (kind == Code::KEYED_LOAD_IC ||
203 kind == Code::KEYED_STORE_IC ||
204 kind == Code::KEYED_CALL_IC) {
205 return MONOMORPHIC;
206 }
207
208 // Remove the target from the code cache if it became invalid
209 // because of changes in the prototype chain to avoid hitting it
210 // again.
211 // Call stubs handle this later to allow extra IC state
212 // transitions.
213 if (kind != Code::CALL_IC &&
214 TryRemoveInvalidPrototypeDependentStub(target, receiver, name)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000215 return MONOMORPHIC_PROTOTYPE_FAILURE;
216 }
217
218 // The builtins object is special. It only changes when JavaScript
219 // builtins are loaded lazily. It is important to keep inline
220 // caches for the builtins object monomorphic. Therefore, if we get
221 // an inline cache miss for the builtins object after lazily loading
222 // JavaScript builtins, we return uninitialized as the state to
223 // force the inline cache back to monomorphic state.
224 if (receiver->IsJSBuiltinsObject()) {
225 return UNINITIALIZED;
226 }
227
228 return MONOMORPHIC;
229}
230
231
232RelocInfo::Mode IC::ComputeMode() {
233 Address addr = address();
234 Code* code = Code::cast(Heap::FindCodeObject(addr));
235 for (RelocIterator it(code, RelocInfo::kCodeTargetMask);
236 !it.done(); it.next()) {
237 RelocInfo* info = it.rinfo();
238 if (info->pc() == addr) return info->rmode();
239 }
240 UNREACHABLE();
241 return RelocInfo::NONE;
242}
243
244
245Failure* IC::TypeError(const char* type,
246 Handle<Object> object,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100247 Handle<Object> key) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000248 HandleScope scope;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100249 Handle<Object> args[2] = { key, object };
Steve Blocka7e24c12009-10-30 11:49:00 +0000250 Handle<Object> error = Factory::NewTypeError(type, HandleVector(args, 2));
251 return Top::Throw(*error);
252}
253
254
255Failure* IC::ReferenceError(const char* type, Handle<String> name) {
256 HandleScope scope;
257 Handle<Object> error =
258 Factory::NewReferenceError(type, HandleVector(&name, 1));
259 return Top::Throw(*error);
260}
261
262
263void IC::Clear(Address address) {
264 Code* target = GetTargetAtAddress(address);
265
266 // Don't clear debug break inline cache as it will remove the break point.
267 if (target->ic_state() == DEBUG_BREAK) return;
268
269 switch (target->kind()) {
270 case Code::LOAD_IC: return LoadIC::Clear(address, target);
271 case Code::KEYED_LOAD_IC: return KeyedLoadIC::Clear(address, target);
272 case Code::STORE_IC: return StoreIC::Clear(address, target);
273 case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target);
274 case Code::CALL_IC: return CallIC::Clear(address, target);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100275 case Code::KEYED_CALL_IC: return KeyedCallIC::Clear(address, target);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100276 case Code::BINARY_OP_IC:
277 case Code::TYPE_RECORDING_BINARY_OP_IC:
278 case Code::COMPARE_IC:
279 // Clearing these is tricky and does not
280 // make any performance difference.
281 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000282 default: UNREACHABLE();
283 }
284}
285
286
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100287void CallICBase::Clear(Address address, Code* target) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000288 State state = target->ic_state();
Steve Blocka7e24c12009-10-30 11:49:00 +0000289 if (state == UNINITIALIZED) return;
290 Code* code =
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100291 StubCache::FindCallInitialize(target->arguments_count(),
292 target->ic_in_loop(),
293 target->kind());
Steve Blocka7e24c12009-10-30 11:49:00 +0000294 SetTargetAtAddress(address, code);
295}
296
297
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100298void KeyedLoadIC::ClearInlinedVersion(Address address) {
299 // Insert null as the map to check for to make sure the map check fails
300 // sending control flow to the IC instead of the inlined version.
301 PatchInlinedLoad(address, Heap::null_value());
302}
303
304
Steve Blocka7e24c12009-10-30 11:49:00 +0000305void KeyedLoadIC::Clear(Address address, Code* target) {
306 if (target->ic_state() == UNINITIALIZED) return;
307 // Make sure to also clear the map used in inline fast cases. If we
308 // do not clear these maps, cached code can keep objects alive
309 // through the embedded maps.
310 ClearInlinedVersion(address);
311 SetTargetAtAddress(address, initialize_stub());
312}
313
314
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100315void LoadIC::ClearInlinedVersion(Address address) {
316 // Reset the map check of the inlined inobject property load (if
317 // present) to guarantee failure by holding an invalid map (the null
318 // value). The offset can be patched to anything.
319 PatchInlinedLoad(address, Heap::null_value(), 0);
Ben Murdochf87a2032010-10-22 12:50:53 +0100320 PatchInlinedContextualLoad(address,
321 Heap::null_value(),
322 Heap::null_value(),
323 true);
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100324}
325
326
Steve Blocka7e24c12009-10-30 11:49:00 +0000327void LoadIC::Clear(Address address, Code* target) {
328 if (target->ic_state() == UNINITIALIZED) return;
329 ClearInlinedVersion(address);
330 SetTargetAtAddress(address, initialize_stub());
331}
332
333
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100334void StoreIC::ClearInlinedVersion(Address address) {
335 // Reset the map check of the inlined inobject property store (if
336 // present) to guarantee failure by holding an invalid map (the null
337 // value). The offset can be patched to anything.
338 PatchInlinedStore(address, Heap::null_value(), 0);
339}
340
341
Steve Blocka7e24c12009-10-30 11:49:00 +0000342void StoreIC::Clear(Address address, Code* target) {
343 if (target->ic_state() == UNINITIALIZED) return;
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100344 ClearInlinedVersion(address);
Steve Block1e0659c2011-05-24 12:43:12 +0100345 SetTargetAtAddress(address,
346 target->extra_ic_state() == kStoreICStrict
347 ? initialize_stub_strict()
348 : initialize_stub());
Steve Blocka7e24c12009-10-30 11:49:00 +0000349}
350
351
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100352void KeyedStoreIC::ClearInlinedVersion(Address address) {
353 // Insert null as the elements map to check for. This will make
354 // sure that the elements fast-case map check fails so that control
355 // flows to the IC instead of the inlined version.
356 PatchInlinedStore(address, Heap::null_value());
357}
358
359
360void KeyedStoreIC::RestoreInlinedVersion(Address address) {
361 // Restore the fast-case elements map check so that the inlined
362 // version can be used again.
363 PatchInlinedStore(address, Heap::fixed_array_map());
364}
365
366
Steve Blocka7e24c12009-10-30 11:49:00 +0000367void KeyedStoreIC::Clear(Address address, Code* target) {
368 if (target->ic_state() == UNINITIALIZED) return;
369 SetTargetAtAddress(address, initialize_stub());
370}
371
372
373static bool HasInterceptorGetter(JSObject* object) {
374 return !object->GetNamedInterceptor()->getter()->IsUndefined();
375}
376
377
378static void LookupForRead(Object* object,
379 String* name,
380 LookupResult* lookup) {
381 AssertNoAllocation no_gc; // pointers must stay valid
382
383 // Skip all the objects with named interceptors, but
384 // without actual getter.
385 while (true) {
386 object->Lookup(name, lookup);
387 // Besides normal conditions (property not found or it's not
Andrei Popescu402d9372010-02-26 13:31:12 +0000388 // an interceptor), bail out if lookup is not cacheable: we won't
Steve Blocka7e24c12009-10-30 11:49:00 +0000389 // be able to IC it anyway and regular lookup should work fine.
Andrei Popescu402d9372010-02-26 13:31:12 +0000390 if (!lookup->IsFound()
391 || (lookup->type() != INTERCEPTOR)
392 || !lookup->IsCacheable()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000393 return;
394 }
395
396 JSObject* holder = lookup->holder();
397 if (HasInterceptorGetter(holder)) {
398 return;
399 }
400
401 holder->LocalLookupRealNamedProperty(name, lookup);
Andrei Popescu402d9372010-02-26 13:31:12 +0000402 if (lookup->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000403 ASSERT(lookup->type() != INTERCEPTOR);
404 return;
405 }
406
407 Object* proto = holder->GetPrototype();
408 if (proto->IsNull()) {
409 lookup->NotFound();
410 return;
411 }
412
413 object = proto;
414 }
415}
416
417
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100418Object* CallICBase::TryCallAsFunction(Object* object) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000419 HandleScope scope;
420 Handle<Object> target(object);
421 Handle<Object> delegate = Execution::GetFunctionDelegate(target);
422
423 if (delegate->IsJSFunction()) {
424 // Patch the receiver and use the delegate as the function to
425 // invoke. This is used for invoking objects as if they were
426 // functions.
427 const int argc = this->target()->arguments_count();
428 StackFrameLocator locator;
429 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
430 int index = frame->ComputeExpressionsCount() - (argc + 1);
431 frame->SetExpression(index, *target);
432 }
433
434 return *delegate;
435}
436
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100437
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100438void CallICBase::ReceiverToObject(Handle<Object> object) {
Leon Clarkee46be812010-01-19 14:06:41 +0000439 HandleScope scope;
440 Handle<Object> receiver(object);
441
442 // Change the receiver to the result of calling ToObject on it.
443 const int argc = this->target()->arguments_count();
444 StackFrameLocator locator;
445 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
446 int index = frame->ComputeExpressionsCount() - (argc + 1);
447 frame->SetExpression(index, *Factory::ToObject(object));
448}
449
Steve Blocka7e24c12009-10-30 11:49:00 +0000450
John Reck59135872010-11-02 12:39:01 -0700451MaybeObject* CallICBase::LoadFunction(State state,
Ben Murdochb8e0da22011-05-16 14:20:40 +0100452 Code::ExtraICState extra_ic_state,
John Reck59135872010-11-02 12:39:01 -0700453 Handle<Object> object,
454 Handle<String> name) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000455 // If the object is undefined or null it's illegal to try to get any
456 // of its properties; throw a TypeError in that case.
457 if (object->IsUndefined() || object->IsNull()) {
458 return TypeError("non_object_property_call", object, name);
459 }
460
Leon Clarkee46be812010-01-19 14:06:41 +0000461 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
462 ReceiverToObject(object);
463 }
464
Steve Blocka7e24c12009-10-30 11:49:00 +0000465 // Check if the name is trivially convertible to an index and get
466 // the element if so.
467 uint32_t index;
468 if (name->AsArrayIndex(&index)) {
John Reck59135872010-11-02 12:39:01 -0700469 Object* result;
470 { MaybeObject* maybe_result = object->GetElement(index);
471 if (!maybe_result->ToObject(&result)) return maybe_result;
472 }
473
Steve Blocka7e24c12009-10-30 11:49:00 +0000474 if (result->IsJSFunction()) return result;
475
476 // Try to find a suitable function delegate for the object at hand.
477 result = TryCallAsFunction(result);
478 if (result->IsJSFunction()) return result;
479
480 // Otherwise, it will fail in the lookup step.
481 }
482
483 // Lookup the property in the object.
484 LookupResult lookup;
485 LookupForRead(*object, *name, &lookup);
486
Andrei Popescu402d9372010-02-26 13:31:12 +0000487 if (!lookup.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000488 // If the object does not have the requested property, check which
489 // exception we need to throw.
Leon Clarkee46be812010-01-19 14:06:41 +0000490 if (IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000491 return ReferenceError("not_defined", name);
492 }
493 return TypeError("undefined_method", object, name);
494 }
495
496 // Lookup is valid: Update inline cache and stub cache.
Andrei Popescu31002712010-02-23 13:46:05 +0000497 if (FLAG_use_ic) {
Ben Murdochb8e0da22011-05-16 14:20:40 +0100498 UpdateCaches(&lookup, state, extra_ic_state, object, name);
Steve Blocka7e24c12009-10-30 11:49:00 +0000499 }
500
501 // Get the property.
502 PropertyAttributes attr;
John Reck59135872010-11-02 12:39:01 -0700503 Object* result;
504 { MaybeObject* maybe_result =
505 object->GetProperty(*object, &lookup, *name, &attr);
506 if (!maybe_result->ToObject(&result)) return maybe_result;
507 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000508 if (lookup.type() == INTERCEPTOR) {
509 // If the object does not have the requested property, check which
510 // exception we need to throw.
511 if (attr == ABSENT) {
Leon Clarkee46be812010-01-19 14:06:41 +0000512 if (IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000513 return ReferenceError("not_defined", name);
514 }
515 return TypeError("undefined_method", object, name);
516 }
517 }
518
519 ASSERT(result != Heap::the_hole_value());
520
521 if (result->IsJSFunction()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000522#ifdef ENABLE_DEBUGGER_SUPPORT
523 // Handle stepping into a function if step into is active.
524 if (Debug::StepInActive()) {
525 // Protect the result in a handle as the debugger can allocate and might
526 // cause GC.
527 HandleScope scope;
528 Handle<JSFunction> function(JSFunction::cast(result));
529 Debug::HandleStepIn(function, object, fp(), false);
530 return *function;
531 }
532#endif
533
534 return result;
535 }
536
537 // Try to find a suitable function delegate for the object at hand.
538 result = TryCallAsFunction(result);
John Reck59135872010-11-02 12:39:01 -0700539 MaybeObject* answer = result;
540 if (!result->IsJSFunction()) {
541 answer = TypeError("property_not_function", object, name);
542 }
543 return answer;
Steve Blocka7e24c12009-10-30 11:49:00 +0000544}
545
546
Ben Murdochb8e0da22011-05-16 14:20:40 +0100547bool CallICBase::TryUpdateExtraICState(LookupResult* lookup,
548 Handle<Object> object,
549 Code::ExtraICState* extra_ic_state) {
550 ASSERT(kind_ == Code::CALL_IC);
551 if (lookup->type() != CONSTANT_FUNCTION) return false;
552 JSFunction* function = lookup->GetConstantFunction();
553 if (!function->shared()->HasBuiltinFunctionId()) return false;
554
555 // Fetch the arguments passed to the called function.
556 const int argc = target()->arguments_count();
557 Address entry = Top::c_entry_fp(Top::GetCurrentThread());
558 Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset);
559 Arguments args(argc + 1,
560 &Memory::Object_at(fp +
561 StandardFrameConstants::kCallerSPOffset +
562 argc * kPointerSize));
563 switch (function->shared()->builtin_function_id()) {
564 case kStringCharCodeAt:
565 case kStringCharAt:
566 if (object->IsString()) {
567 String* string = String::cast(*object);
568 // Check that there's the right wrapper in the receiver slot.
569 ASSERT(string == JSValue::cast(args[0])->value());
570 // If we're in the default (fastest) state and the index is
571 // out of bounds, update the state to record this fact.
572 if (*extra_ic_state == DEFAULT_STRING_STUB &&
573 argc >= 1 && args[1]->IsNumber()) {
574 double index;
575 if (args[1]->IsSmi()) {
576 index = Smi::cast(args[1])->value();
577 } else {
578 ASSERT(args[1]->IsHeapNumber());
579 index = DoubleToInteger(HeapNumber::cast(args[1])->value());
580 }
581 if (index < 0 || index >= string->length()) {
582 *extra_ic_state = STRING_INDEX_OUT_OF_BOUNDS;
583 return true;
584 }
585 }
586 }
587 break;
588 default:
589 return false;
590 }
591 return false;
592}
593
594
595MaybeObject* CallICBase::ComputeMonomorphicStub(
596 LookupResult* lookup,
597 State state,
598 Code::ExtraICState extra_ic_state,
599 Handle<Object> object,
600 Handle<String> name) {
601 int argc = target()->arguments_count();
602 InLoopFlag in_loop = target()->ic_in_loop();
603 MaybeObject* maybe_code = NULL;
604 switch (lookup->type()) {
605 case FIELD: {
606 int index = lookup->GetFieldIndex();
607 maybe_code = StubCache::ComputeCallField(argc,
608 in_loop,
609 kind_,
610 *name,
611 *object,
612 lookup->holder(),
613 index);
614 break;
615 }
616 case CONSTANT_FUNCTION: {
617 // Get the constant function and compute the code stub for this
618 // call; used for rewriting to monomorphic state and making sure
619 // that the code stub is in the stub cache.
620 JSFunction* function = lookup->GetConstantFunction();
621 maybe_code = StubCache::ComputeCallConstant(argc,
622 in_loop,
623 kind_,
624 extra_ic_state,
625 *name,
626 *object,
627 lookup->holder(),
628 function);
629 break;
630 }
631 case NORMAL: {
632 if (!object->IsJSObject()) return NULL;
633 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
634
635 if (lookup->holder()->IsGlobalObject()) {
636 GlobalObject* global = GlobalObject::cast(lookup->holder());
637 JSGlobalPropertyCell* cell =
638 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
639 if (!cell->value()->IsJSFunction()) return NULL;
640 JSFunction* function = JSFunction::cast(cell->value());
641 maybe_code = StubCache::ComputeCallGlobal(argc,
642 in_loop,
643 kind_,
644 *name,
645 *receiver,
646 global,
647 cell,
648 function);
649 } else {
650 // There is only one shared stub for calling normalized
651 // properties. It does not traverse the prototype chain, so the
652 // property must be found in the receiver for the stub to be
653 // applicable.
654 if (lookup->holder() != *receiver) return NULL;
655 maybe_code = StubCache::ComputeCallNormal(argc,
656 in_loop,
657 kind_,
658 *name,
659 *receiver);
660 }
661 break;
662 }
663 case INTERCEPTOR: {
664 ASSERT(HasInterceptorGetter(lookup->holder()));
665 maybe_code = StubCache::ComputeCallInterceptor(argc,
666 kind_,
667 *name,
668 *object,
669 lookup->holder());
670 break;
671 }
672 default:
673 maybe_code = NULL;
674 break;
675 }
676 return maybe_code;
677}
678
679
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100680void CallICBase::UpdateCaches(LookupResult* lookup,
Steve Block8defd9f2010-07-08 12:39:36 +0100681 State state,
Ben Murdochb8e0da22011-05-16 14:20:40 +0100682 Code::ExtraICState extra_ic_state,
Steve Block8defd9f2010-07-08 12:39:36 +0100683 Handle<Object> object,
684 Handle<String> name) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000685 // Bail out if we didn't find a result.
Andrei Popescu402d9372010-02-26 13:31:12 +0000686 if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000687
Steve Block8defd9f2010-07-08 12:39:36 +0100688 if (lookup->holder() != *object &&
689 HasNormalObjectsInPrototypeChain(lookup, object->GetPrototype())) {
690 // Suppress optimization for prototype chains with slow properties objects
691 // in the middle.
692 return;
693 }
Steve Block8defd9f2010-07-08 12:39:36 +0100694
Steve Blocka7e24c12009-10-30 11:49:00 +0000695 // Compute the number of arguments.
696 int argc = target()->arguments_count();
697 InLoopFlag in_loop = target()->ic_in_loop();
John Reck59135872010-11-02 12:39:01 -0700698 MaybeObject* maybe_code = NULL;
Ben Murdochb8e0da22011-05-16 14:20:40 +0100699 bool had_proto_failure = false;
Steve Blocka7e24c12009-10-30 11:49:00 +0000700 if (state == UNINITIALIZED) {
701 // This is the first time we execute this inline cache.
702 // Set the target to the pre monomorphic stub to delay
703 // setting the monomorphic state.
John Reck59135872010-11-02 12:39:01 -0700704 maybe_code = StubCache::ComputeCallPreMonomorphic(argc, in_loop, kind_);
Steve Blocka7e24c12009-10-30 11:49:00 +0000705 } else if (state == MONOMORPHIC) {
Ben Murdochb8e0da22011-05-16 14:20:40 +0100706 if (kind_ == Code::CALL_IC &&
707 TryUpdateExtraICState(lookup, object, &extra_ic_state)) {
708 maybe_code = ComputeMonomorphicStub(lookup,
709 state,
710 extra_ic_state,
711 object,
712 name);
713 } else if (kind_ == Code::CALL_IC &&
714 TryRemoveInvalidPrototypeDependentStub(target(),
715 *object,
716 *name)) {
717 had_proto_failure = true;
718 maybe_code = ComputeMonomorphicStub(lookup,
719 state,
720 extra_ic_state,
721 object,
722 name);
723 } else {
724 maybe_code = StubCache::ComputeCallMegamorphic(argc, in_loop, kind_);
Steve Blocka7e24c12009-10-30 11:49:00 +0000725 }
Ben Murdochb8e0da22011-05-16 14:20:40 +0100726 } else {
727 maybe_code = ComputeMonomorphicStub(lookup,
728 state,
729 extra_ic_state,
730 object,
731 name);
Steve Blocka7e24c12009-10-30 11:49:00 +0000732 }
733
734 // If we're unable to compute the stub (not enough memory left), we
735 // simply avoid updating the caches.
Ben Murdochb8e0da22011-05-16 14:20:40 +0100736 Object* code;
John Reck59135872010-11-02 12:39:01 -0700737 if (maybe_code == NULL || !maybe_code->ToObject(&code)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000738
739 // Patch the call site depending on the state of the cache.
740 if (state == UNINITIALIZED ||
741 state == PREMONOMORPHIC ||
742 state == MONOMORPHIC ||
743 state == MONOMORPHIC_PROTOTYPE_FAILURE) {
744 set_target(Code::cast(code));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100745 } else if (state == MEGAMORPHIC) {
Steve Block8defd9f2010-07-08 12:39:36 +0100746 // Cache code holding map should be consistent with
747 // GenerateMonomorphicCacheProbe. It is not the map which holds the stub.
748 Map* map = JSObject::cast(object->IsJSObject() ? *object :
749 object->GetPrototype())->map();
750
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100751 // Update the stub cache.
Steve Block8defd9f2010-07-08 12:39:36 +0100752 StubCache::Set(*name, map, Code::cast(code));
Steve Blocka7e24c12009-10-30 11:49:00 +0000753 }
754
Ben Murdochb8e0da22011-05-16 14:20:40 +0100755 USE(had_proto_failure);
Steve Blocka7e24c12009-10-30 11:49:00 +0000756#ifdef DEBUG
Ben Murdochb8e0da22011-05-16 14:20:40 +0100757 if (had_proto_failure) state = MONOMORPHIC_PROTOTYPE_FAILURE;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100758 TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC",
759 name, state, target(), in_loop ? " (in-loop)" : "");
Steve Blocka7e24c12009-10-30 11:49:00 +0000760#endif
761}
762
763
John Reck59135872010-11-02 12:39:01 -0700764MaybeObject* KeyedCallIC::LoadFunction(State state,
765 Handle<Object> object,
766 Handle<Object> key) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100767 if (key->IsSymbol()) {
Ben Murdochb8e0da22011-05-16 14:20:40 +0100768 return CallICBase::LoadFunction(state,
769 Code::kNoExtraICState,
770 object,
771 Handle<String>::cast(key));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100772 }
773
774 if (object->IsUndefined() || object->IsNull()) {
775 return TypeError("non_object_property_call", object, key);
776 }
777
778 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
779 ReceiverToObject(object);
780 }
781
782 if (FLAG_use_ic && state != MEGAMORPHIC && !object->IsAccessCheckNeeded()) {
783 int argc = target()->arguments_count();
784 InLoopFlag in_loop = target()->ic_in_loop();
John Reck59135872010-11-02 12:39:01 -0700785 MaybeObject* maybe_code = StubCache::ComputeCallMegamorphic(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100786 argc, in_loop, Code::KEYED_CALL_IC);
John Reck59135872010-11-02 12:39:01 -0700787 Object* code;
788 if (maybe_code->ToObject(&code)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100789 set_target(Code::cast(code));
790#ifdef DEBUG
791 TraceIC(
792 "KeyedCallIC", key, state, target(), in_loop ? " (in-loop)" : "");
793#endif
794 }
795 }
John Reck59135872010-11-02 12:39:01 -0700796 Object* result;
797 { MaybeObject* maybe_result = Runtime::GetObjectProperty(object, key);
798 if (!maybe_result->ToObject(&result)) return maybe_result;
799 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100800 if (result->IsJSFunction()) return result;
801 result = TryCallAsFunction(result);
John Reck59135872010-11-02 12:39:01 -0700802 MaybeObject* answer = result;
803 if (!result->IsJSFunction()) {
804 answer = TypeError("property_not_function", object, key);
805 }
806 return answer;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100807}
808
809
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100810#ifdef DEBUG
811#define TRACE_IC_NAMED(msg, name) \
812 if (FLAG_trace_ic) PrintF(msg, *(name)->ToCString())
813#else
814#define TRACE_IC_NAMED(msg, name)
815#endif
816
817
John Reck59135872010-11-02 12:39:01 -0700818MaybeObject* LoadIC::Load(State state,
819 Handle<Object> object,
820 Handle<String> name) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000821 // If the object is undefined or null it's illegal to try to get any
822 // of its properties; throw a TypeError in that case.
823 if (object->IsUndefined() || object->IsNull()) {
824 return TypeError("non_object_property_load", object, name);
825 }
826
827 if (FLAG_use_ic) {
Steve Block1e0659c2011-05-24 12:43:12 +0100828 Code* non_monomorphic_stub =
829 (state == UNINITIALIZED) ? pre_monomorphic_stub() : megamorphic_stub();
830
Steve Blocka7e24c12009-10-30 11:49:00 +0000831 // Use specialized code for getting the length of strings and
832 // string wrapper objects. The length property of string wrapper
833 // objects is read-only and therefore always returns the length of
834 // the underlying string value. See ECMA-262 15.5.5.1.
835 if ((object->IsString() || object->IsStringWrapper()) &&
836 name->Equals(Heap::length_symbol())) {
837 HandleScope scope;
Steve Block1e0659c2011-05-24 12:43:12 +0100838#ifdef DEBUG
839 if (FLAG_trace_ic) PrintF("[LoadIC : +#length /string]\n");
840#endif
841 if (state == PREMONOMORPHIC) {
842 if (object->IsString()) {
843 Map* map = HeapObject::cast(*object)->map();
844 const int offset = String::kLengthOffset;
845 PatchInlinedLoad(address(), map, offset);
846 set_target(Builtins::builtin(Builtins::LoadIC_StringLength));
847 } else {
848 set_target(Builtins::builtin(Builtins::LoadIC_StringWrapperLength));
849 }
850 } else if (state == MONOMORPHIC && object->IsStringWrapper()) {
851 set_target(Builtins::builtin(Builtins::LoadIC_StringWrapperLength));
852 } else {
853 set_target(non_monomorphic_stub);
854 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000855 // Get the string if we have a string wrapper object.
856 if (object->IsJSValue()) {
857 object = Handle<Object>(Handle<JSValue>::cast(object)->value());
858 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000859 return Smi::FromInt(String::cast(*object)->length());
860 }
861
862 // Use specialized code for getting the length of arrays.
863 if (object->IsJSArray() && name->Equals(Heap::length_symbol())) {
864#ifdef DEBUG
865 if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n");
866#endif
Steve Block1e0659c2011-05-24 12:43:12 +0100867 if (state == PREMONOMORPHIC) {
868 Map* map = HeapObject::cast(*object)->map();
869 const int offset = JSArray::kLengthOffset;
870 PatchInlinedLoad(address(), map, offset);
871 set_target(Builtins::builtin(Builtins::LoadIC_ArrayLength));
872 } else {
873 set_target(non_monomorphic_stub);
874 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000875 return JSArray::cast(*object)->length();
876 }
877
878 // Use specialized code for getting prototype of functions.
Steve Block6ded16b2010-05-10 14:33:55 +0100879 if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) &&
880 JSFunction::cast(*object)->should_have_prototype()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000881#ifdef DEBUG
882 if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n");
883#endif
Steve Block1e0659c2011-05-24 12:43:12 +0100884 if (state == PREMONOMORPHIC) {
885 set_target(Builtins::builtin(Builtins::LoadIC_FunctionPrototype));
886 } else {
887 set_target(non_monomorphic_stub);
888 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000889 return Accessors::FunctionGetPrototype(*object, 0);
890 }
891 }
892
893 // Check if the name is trivially convertible to an index and get
894 // the element if so.
895 uint32_t index;
896 if (name->AsArrayIndex(&index)) return object->GetElement(index);
897
898 // Named lookup in the object.
899 LookupResult lookup;
900 LookupForRead(*object, *name, &lookup);
901
Andrei Popescu402d9372010-02-26 13:31:12 +0000902 // If we did not find a property, check if we need to throw an exception.
903 if (!lookup.IsProperty()) {
Leon Clarkee46be812010-01-19 14:06:41 +0000904 if (FLAG_strict || IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000905 return ReferenceError("not_defined", name);
906 }
907 LOG(SuspectReadEvent(*name, *object));
908 }
909
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100910 bool can_be_inlined_precheck =
Steve Blocka7e24c12009-10-30 11:49:00 +0000911 FLAG_use_ic &&
Andrei Popescu402d9372010-02-26 13:31:12 +0000912 lookup.IsProperty() &&
Steve Blocka7e24c12009-10-30 11:49:00 +0000913 lookup.IsCacheable() &&
914 lookup.holder() == *object &&
Steve Blocka7e24c12009-10-30 11:49:00 +0000915 !object->IsAccessCheckNeeded();
916
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100917 bool can_be_inlined =
918 can_be_inlined_precheck &&
919 state == PREMONOMORPHIC &&
920 lookup.type() == FIELD;
921
922 bool can_be_inlined_contextual =
923 can_be_inlined_precheck &&
924 state == UNINITIALIZED &&
925 lookup.holder()->IsGlobalObject() &&
926 lookup.type() == NORMAL;
927
Steve Blocka7e24c12009-10-30 11:49:00 +0000928 if (can_be_inlined) {
929 Map* map = lookup.holder()->map();
930 // Property's index in the properties array. If negative we have
931 // an inobject property.
932 int index = lookup.GetFieldIndex() - map->inobject_properties();
933 if (index < 0) {
934 // Index is an offset from the end of the object.
935 int offset = map->instance_size() + (index * kPointerSize);
936 if (PatchInlinedLoad(address(), map, offset)) {
937 set_target(megamorphic_stub());
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100938 TRACE_IC_NAMED("[LoadIC : inline patch %s]\n", name);
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100939 return lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100940 } else {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100941 TRACE_IC_NAMED("[LoadIC : no inline patch %s (patching failed)]\n",
942 name);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100943 }
944 } else {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100945 TRACE_IC_NAMED("[LoadIC : no inline patch %s (not inobject)]\n", name);
946 }
947 } else if (can_be_inlined_contextual) {
948 Map* map = lookup.holder()->map();
949 JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(
950 lookup.holder()->property_dictionary()->ValueAt(
951 lookup.GetDictionaryEntry()));
Ben Murdochf87a2032010-10-22 12:50:53 +0100952 if (PatchInlinedContextualLoad(address(),
953 map,
954 cell,
955 lookup.IsDontDelete())) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100956 set_target(megamorphic_stub());
957 TRACE_IC_NAMED("[LoadIC : inline contextual patch %s]\n", name);
958 ASSERT(cell->value() != Heap::the_hole_value());
959 return cell->value();
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100960 }
961 } else {
962 if (FLAG_use_ic && state == PREMONOMORPHIC) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100963 TRACE_IC_NAMED("[LoadIC : no inline patch %s (not inlinable)]\n", name);
Steve Blocka7e24c12009-10-30 11:49:00 +0000964 }
965 }
966
967 // Update inline cache and stub cache.
Andrei Popescu31002712010-02-23 13:46:05 +0000968 if (FLAG_use_ic) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000969 UpdateCaches(&lookup, state, object, name);
970 }
971
972 PropertyAttributes attr;
Andrei Popescu402d9372010-02-26 13:31:12 +0000973 if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000974 // Get the property.
John Reck59135872010-11-02 12:39:01 -0700975 Object* result;
976 { MaybeObject* maybe_result =
977 object->GetProperty(*object, &lookup, *name, &attr);
978 if (!maybe_result->ToObject(&result)) return maybe_result;
979 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000980 // If the property is not present, check if we need to throw an
981 // exception.
Leon Clarkee46be812010-01-19 14:06:41 +0000982 if (attr == ABSENT && IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000983 return ReferenceError("not_defined", name);
984 }
985 return result;
986 }
987
988 // Get the property.
989 return object->GetProperty(*object, &lookup, *name, &attr);
990}
991
992
993void LoadIC::UpdateCaches(LookupResult* lookup,
994 State state,
995 Handle<Object> object,
996 Handle<String> name) {
Steve Block6ded16b2010-05-10 14:33:55 +0100997 // Bail out if the result is not cacheable.
998 if (!lookup->IsCacheable()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000999
1000 // Loading properties from values is not common, so don't try to
1001 // deal with non-JS objects here.
1002 if (!object->IsJSObject()) return;
1003 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1004
Steve Block8defd9f2010-07-08 12:39:36 +01001005 if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
1006
Steve Blocka7e24c12009-10-30 11:49:00 +00001007 // Compute the code stub for this load.
John Reck59135872010-11-02 12:39:01 -07001008 MaybeObject* maybe_code = NULL;
1009 Object* code;
Steve Blocka7e24c12009-10-30 11:49:00 +00001010 if (state == UNINITIALIZED) {
1011 // This is the first time we execute this inline cache.
1012 // Set the target to the pre monomorphic stub to delay
1013 // setting the monomorphic state.
John Reck59135872010-11-02 12:39:01 -07001014 maybe_code = pre_monomorphic_stub();
Steve Block6ded16b2010-05-10 14:33:55 +01001015 } else if (!lookup->IsProperty()) {
1016 // Nonexistent property. The result is undefined.
John Reck59135872010-11-02 12:39:01 -07001017 maybe_code = StubCache::ComputeLoadNonexistent(*name, *receiver);
Steve Blocka7e24c12009-10-30 11:49:00 +00001018 } else {
1019 // Compute monomorphic stub.
1020 switch (lookup->type()) {
1021 case FIELD: {
John Reck59135872010-11-02 12:39:01 -07001022 maybe_code = StubCache::ComputeLoadField(*name, *receiver,
1023 lookup->holder(),
1024 lookup->GetFieldIndex());
Steve Blocka7e24c12009-10-30 11:49:00 +00001025 break;
1026 }
1027 case CONSTANT_FUNCTION: {
1028 Object* constant = lookup->GetConstantFunction();
John Reck59135872010-11-02 12:39:01 -07001029 maybe_code = StubCache::ComputeLoadConstant(*name, *receiver,
1030 lookup->holder(), constant);
Steve Blocka7e24c12009-10-30 11:49:00 +00001031 break;
1032 }
1033 case NORMAL: {
1034 if (lookup->holder()->IsGlobalObject()) {
1035 GlobalObject* global = GlobalObject::cast(lookup->holder());
1036 JSGlobalPropertyCell* cell =
1037 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
John Reck59135872010-11-02 12:39:01 -07001038 maybe_code = StubCache::ComputeLoadGlobal(*name,
1039 *receiver,
1040 global,
1041 cell,
1042 lookup->IsDontDelete());
Steve Blocka7e24c12009-10-30 11:49:00 +00001043 } else {
1044 // There is only one shared stub for loading normalized
1045 // properties. It does not traverse the prototype chain, so the
1046 // property must be found in the receiver for the stub to be
1047 // applicable.
1048 if (lookup->holder() != *receiver) return;
John Reck59135872010-11-02 12:39:01 -07001049 maybe_code = StubCache::ComputeLoadNormal();
Steve Blocka7e24c12009-10-30 11:49:00 +00001050 }
1051 break;
1052 }
1053 case CALLBACKS: {
1054 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
1055 AccessorInfo* callback =
1056 AccessorInfo::cast(lookup->GetCallbackObject());
1057 if (v8::ToCData<Address>(callback->getter()) == 0) return;
John Reck59135872010-11-02 12:39:01 -07001058 maybe_code = StubCache::ComputeLoadCallback(*name, *receiver,
1059 lookup->holder(), callback);
Steve Blocka7e24c12009-10-30 11:49:00 +00001060 break;
1061 }
1062 case INTERCEPTOR: {
1063 ASSERT(HasInterceptorGetter(lookup->holder()));
John Reck59135872010-11-02 12:39:01 -07001064 maybe_code = StubCache::ComputeLoadInterceptor(*name, *receiver,
1065 lookup->holder());
Steve Blocka7e24c12009-10-30 11:49:00 +00001066 break;
1067 }
1068 default:
1069 return;
1070 }
1071 }
1072
1073 // If we're unable to compute the stub (not enough memory left), we
1074 // simply avoid updating the caches.
John Reck59135872010-11-02 12:39:01 -07001075 if (maybe_code == NULL || !maybe_code->ToObject(&code)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001076
1077 // Patch the call site depending on the state of the cache.
1078 if (state == UNINITIALIZED || state == PREMONOMORPHIC ||
1079 state == MONOMORPHIC_PROTOTYPE_FAILURE) {
1080 set_target(Code::cast(code));
1081 } else if (state == MONOMORPHIC) {
1082 set_target(megamorphic_stub());
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01001083 } else if (state == MEGAMORPHIC) {
Steve Block8defd9f2010-07-08 12:39:36 +01001084 // Cache code holding map should be consistent with
1085 // GenerateMonomorphicCacheProbe.
1086 Map* map = JSObject::cast(object->IsJSObject() ? *object :
1087 object->GetPrototype())->map();
1088
1089 StubCache::Set(*name, map, Code::cast(code));
Steve Blocka7e24c12009-10-30 11:49:00 +00001090 }
1091
1092#ifdef DEBUG
1093 TraceIC("LoadIC", name, state, target());
1094#endif
1095}
1096
1097
John Reck59135872010-11-02 12:39:01 -07001098MaybeObject* KeyedLoadIC::Load(State state,
1099 Handle<Object> object,
1100 Handle<Object> key) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001101 if (key->IsSymbol()) {
1102 Handle<String> name = Handle<String>::cast(key);
1103
1104 // If the object is undefined or null it's illegal to try to get any
1105 // of its properties; throw a TypeError in that case.
1106 if (object->IsUndefined() || object->IsNull()) {
1107 return TypeError("non_object_property_load", object, name);
1108 }
1109
1110 if (FLAG_use_ic) {
Steve Block1e0659c2011-05-24 12:43:12 +01001111 // TODO(1073): don't ignore the current stub state.
1112
Steve Blocka7e24c12009-10-30 11:49:00 +00001113 // Use specialized code for getting the length of strings.
1114 if (object->IsString() && name->Equals(Heap::length_symbol())) {
1115 Handle<String> string = Handle<String>::cast(object);
1116 Object* code = NULL;
John Reck59135872010-11-02 12:39:01 -07001117 { MaybeObject* maybe_code =
1118 StubCache::ComputeKeyedLoadStringLength(*name, *string);
1119 if (!maybe_code->ToObject(&code)) return maybe_code;
1120 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001121 set_target(Code::cast(code));
1122#ifdef DEBUG
1123 TraceIC("KeyedLoadIC", name, state, target());
1124#endif // DEBUG
1125 return Smi::FromInt(string->length());
1126 }
1127
1128 // Use specialized code for getting the length of arrays.
1129 if (object->IsJSArray() && name->Equals(Heap::length_symbol())) {
1130 Handle<JSArray> array = Handle<JSArray>::cast(object);
John Reck59135872010-11-02 12:39:01 -07001131 Object* code;
1132 { MaybeObject* maybe_code =
1133 StubCache::ComputeKeyedLoadArrayLength(*name, *array);
1134 if (!maybe_code->ToObject(&code)) return maybe_code;
1135 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001136 set_target(Code::cast(code));
1137#ifdef DEBUG
1138 TraceIC("KeyedLoadIC", name, state, target());
1139#endif // DEBUG
1140 return JSArray::cast(*object)->length();
1141 }
1142
1143 // Use specialized code for getting prototype of functions.
Steve Block6ded16b2010-05-10 14:33:55 +01001144 if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) &&
1145 JSFunction::cast(*object)->should_have_prototype()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001146 Handle<JSFunction> function = Handle<JSFunction>::cast(object);
John Reck59135872010-11-02 12:39:01 -07001147 Object* code;
1148 { MaybeObject* maybe_code =
1149 StubCache::ComputeKeyedLoadFunctionPrototype(*name, *function);
1150 if (!maybe_code->ToObject(&code)) return maybe_code;
1151 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001152 set_target(Code::cast(code));
1153#ifdef DEBUG
1154 TraceIC("KeyedLoadIC", name, state, target());
1155#endif // DEBUG
1156 return Accessors::FunctionGetPrototype(*object, 0);
1157 }
1158 }
1159
1160 // Check if the name is trivially convertible to an index and get
1161 // the element or char if so.
1162 uint32_t index = 0;
1163 if (name->AsArrayIndex(&index)) {
1164 HandleScope scope;
1165 // Rewrite to the generic keyed load stub.
1166 if (FLAG_use_ic) set_target(generic_stub());
1167 return Runtime::GetElementOrCharAt(object, index);
1168 }
1169
1170 // Named lookup.
1171 LookupResult lookup;
1172 LookupForRead(*object, *name, &lookup);
1173
Andrei Popescu402d9372010-02-26 13:31:12 +00001174 // If we did not find a property, check if we need to throw an exception.
1175 if (!lookup.IsProperty()) {
Leon Clarkee46be812010-01-19 14:06:41 +00001176 if (FLAG_strict || IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001177 return ReferenceError("not_defined", name);
1178 }
1179 }
1180
Andrei Popescu31002712010-02-23 13:46:05 +00001181 if (FLAG_use_ic) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001182 UpdateCaches(&lookup, state, object, name);
1183 }
1184
1185 PropertyAttributes attr;
Andrei Popescu402d9372010-02-26 13:31:12 +00001186 if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001187 // Get the property.
John Reck59135872010-11-02 12:39:01 -07001188 Object* result;
1189 { MaybeObject* maybe_result =
1190 object->GetProperty(*object, &lookup, *name, &attr);
1191 if (!maybe_result->ToObject(&result)) return maybe_result;
1192 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001193 // If the property is not present, check if we need to throw an
1194 // exception.
Leon Clarkee46be812010-01-19 14:06:41 +00001195 if (attr == ABSENT && IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001196 return ReferenceError("not_defined", name);
1197 }
1198 return result;
1199 }
1200
1201 return object->GetProperty(*object, &lookup, *name, &attr);
1202 }
1203
1204 // Do not use ICs for objects that require access checks (including
1205 // the global object).
1206 bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded();
1207
1208 if (use_ic) {
Steve Block3ce2e202009-11-05 08:53:23 +00001209 Code* stub = generic_stub();
Steve Block1e0659c2011-05-24 12:43:12 +01001210 if (state == UNINITIALIZED) {
1211 if (object->IsString() && key->IsNumber()) {
1212 stub = string_stub();
1213 } else if (object->IsJSObject()) {
1214 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1215 if (receiver->HasExternalArrayElements()) {
1216 MaybeObject* probe =
1217 StubCache::ComputeKeyedLoadOrStoreExternalArray(*receiver,
1218 false);
1219 stub = probe->IsFailure() ?
1220 NULL : Code::cast(probe->ToObjectUnchecked());
1221 } else if (receiver->HasIndexedInterceptor()) {
1222 stub = indexed_interceptor_stub();
1223 } else if (receiver->HasPixelElements()) {
1224 MaybeObject* probe =
1225 StubCache::ComputeKeyedLoadPixelArray(*receiver);
1226 stub = probe->IsFailure() ?
1227 NULL : Code::cast(probe->ToObjectUnchecked());
1228 } else if (key->IsSmi() &&
1229 receiver->map()->has_fast_elements()) {
1230 MaybeObject* probe =
1231 StubCache::ComputeKeyedLoadSpecialized(*receiver);
1232 stub = probe->IsFailure() ?
1233 NULL : Code::cast(probe->ToObjectUnchecked());
1234 }
Steve Block3ce2e202009-11-05 08:53:23 +00001235 }
1236 }
Ben Murdochb0fe1622011-05-05 13:52:32 +01001237 if (stub != NULL) set_target(stub);
1238
1239#ifdef DEBUG
1240 TraceIC("KeyedLoadIC", key, state, target());
1241#endif // DEBUG
1242
Steve Block8defd9f2010-07-08 12:39:36 +01001243 // For JSObjects with fast elements that are not value wrappers
1244 // and that do not have indexed interceptors, we initialize the
1245 // inlined fast case (if present) by patching the inlined map
1246 // check.
Steve Blocka7e24c12009-10-30 11:49:00 +00001247 if (object->IsJSObject() &&
1248 !object->IsJSValue() &&
Steve Block8defd9f2010-07-08 12:39:36 +01001249 !JSObject::cast(*object)->HasIndexedInterceptor() &&
1250 JSObject::cast(*object)->HasFastElements()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001251 Map* map = JSObject::cast(*object)->map();
1252 PatchInlinedLoad(address(), map);
1253 }
1254 }
1255
1256 // Get the property.
1257 return Runtime::GetObjectProperty(object, key);
1258}
1259
1260
1261void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state,
1262 Handle<Object> object, Handle<String> name) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001263 // Bail out if we didn't find a result.
Andrei Popescu402d9372010-02-26 13:31:12 +00001264 if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001265
1266 if (!object->IsJSObject()) return;
1267 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1268
Steve Block8defd9f2010-07-08 12:39:36 +01001269 if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
1270
Steve Blocka7e24c12009-10-30 11:49:00 +00001271 // Compute the code stub for this load.
John Reck59135872010-11-02 12:39:01 -07001272 MaybeObject* maybe_code = NULL;
1273 Object* code;
Steve Blocka7e24c12009-10-30 11:49:00 +00001274
1275 if (state == UNINITIALIZED) {
1276 // This is the first time we execute this inline cache.
1277 // Set the target to the pre monomorphic stub to delay
1278 // setting the monomorphic state.
John Reck59135872010-11-02 12:39:01 -07001279 maybe_code = pre_monomorphic_stub();
Steve Blocka7e24c12009-10-30 11:49:00 +00001280 } else {
1281 // Compute a monomorphic stub.
1282 switch (lookup->type()) {
1283 case FIELD: {
John Reck59135872010-11-02 12:39:01 -07001284 maybe_code = StubCache::ComputeKeyedLoadField(*name, *receiver,
1285 lookup->holder(),
1286 lookup->GetFieldIndex());
Steve Blocka7e24c12009-10-30 11:49:00 +00001287 break;
1288 }
1289 case CONSTANT_FUNCTION: {
1290 Object* constant = lookup->GetConstantFunction();
John Reck59135872010-11-02 12:39:01 -07001291 maybe_code = StubCache::ComputeKeyedLoadConstant(*name,
1292 *receiver,
1293 lookup->holder(),
1294 constant);
Steve Blocka7e24c12009-10-30 11:49:00 +00001295 break;
1296 }
1297 case CALLBACKS: {
1298 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
1299 AccessorInfo* callback =
1300 AccessorInfo::cast(lookup->GetCallbackObject());
1301 if (v8::ToCData<Address>(callback->getter()) == 0) return;
John Reck59135872010-11-02 12:39:01 -07001302 maybe_code = StubCache::ComputeKeyedLoadCallback(*name,
1303 *receiver,
1304 lookup->holder(),
1305 callback);
Steve Blocka7e24c12009-10-30 11:49:00 +00001306 break;
1307 }
1308 case INTERCEPTOR: {
1309 ASSERT(HasInterceptorGetter(lookup->holder()));
John Reck59135872010-11-02 12:39:01 -07001310 maybe_code = StubCache::ComputeKeyedLoadInterceptor(*name, *receiver,
1311 lookup->holder());
Steve Blocka7e24c12009-10-30 11:49:00 +00001312 break;
1313 }
1314 default: {
1315 // Always rewrite to the generic case so that we do not
1316 // repeatedly try to rewrite.
John Reck59135872010-11-02 12:39:01 -07001317 maybe_code = generic_stub();
Steve Blocka7e24c12009-10-30 11:49:00 +00001318 break;
1319 }
1320 }
1321 }
1322
1323 // If we're unable to compute the stub (not enough memory left), we
1324 // simply avoid updating the caches.
John Reck59135872010-11-02 12:39:01 -07001325 if (maybe_code == NULL || !maybe_code->ToObject(&code)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001326
1327 // Patch the call site depending on the state of the cache. Make
1328 // sure to always rewrite from monomorphic to megamorphic.
1329 ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE);
1330 if (state == UNINITIALIZED || state == PREMONOMORPHIC) {
1331 set_target(Code::cast(code));
1332 } else if (state == MONOMORPHIC) {
1333 set_target(megamorphic_stub());
1334 }
1335
1336#ifdef DEBUG
1337 TraceIC("KeyedLoadIC", name, state, target());
1338#endif
1339}
1340
1341
1342static bool StoreICableLookup(LookupResult* lookup) {
1343 // Bail out if we didn't find a result.
Andrei Popescu402d9372010-02-26 13:31:12 +00001344 if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00001345
1346 // If the property is read-only, we leave the IC in its current
1347 // state.
1348 if (lookup->IsReadOnly()) return false;
1349
Steve Blocka7e24c12009-10-30 11:49:00 +00001350 return true;
1351}
1352
1353
1354static bool LookupForWrite(JSObject* object,
1355 String* name,
1356 LookupResult* lookup) {
1357 object->LocalLookup(name, lookup);
1358 if (!StoreICableLookup(lookup)) {
1359 return false;
1360 }
1361
1362 if (lookup->type() == INTERCEPTOR) {
1363 if (object->GetNamedInterceptor()->setter()->IsUndefined()) {
1364 object->LocalLookupRealNamedProperty(name, lookup);
1365 return StoreICableLookup(lookup);
1366 }
1367 }
1368
1369 return true;
1370}
1371
1372
John Reck59135872010-11-02 12:39:01 -07001373MaybeObject* StoreIC::Store(State state,
Steve Block1e0659c2011-05-24 12:43:12 +01001374 Code::ExtraICState extra_ic_state,
John Reck59135872010-11-02 12:39:01 -07001375 Handle<Object> object,
1376 Handle<String> name,
1377 Handle<Object> value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001378 // If the object is undefined or null it's illegal to try to set any
1379 // properties on it; throw a TypeError in that case.
1380 if (object->IsUndefined() || object->IsNull()) {
1381 return TypeError("non_object_property_store", object, name);
1382 }
1383
1384 // Ignore stores where the receiver is not a JSObject.
1385 if (!object->IsJSObject()) return *value;
1386 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1387
1388 // Check if the given name is an array index.
1389 uint32_t index;
1390 if (name->AsArrayIndex(&index)) {
1391 HandleScope scope;
1392 Handle<Object> result = SetElement(receiver, index, value);
1393 if (result.is_null()) return Failure::Exception();
1394 return *value;
1395 }
1396
Steve Block6ded16b2010-05-10 14:33:55 +01001397 // Use specialized code for setting the length of arrays.
1398 if (receiver->IsJSArray()
1399 && name->Equals(Heap::length_symbol())
1400 && receiver->AllowsSetElementsLength()) {
1401#ifdef DEBUG
1402 if (FLAG_trace_ic) PrintF("[StoreIC : +#length /array]\n");
1403#endif
Steve Block1e0659c2011-05-24 12:43:12 +01001404 Builtins::Name target = (extra_ic_state == kStoreICStrict)
1405 ? Builtins::StoreIC_ArrayLength_Strict
1406 : Builtins::StoreIC_ArrayLength;
1407 set_target(Builtins::builtin(target));
Steve Block6ded16b2010-05-10 14:33:55 +01001408 return receiver->SetProperty(*name, *value, NONE);
1409 }
1410
Steve Blocka7e24c12009-10-30 11:49:00 +00001411 // Lookup the property locally in the receiver.
1412 if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) {
1413 LookupResult lookup;
Kristian Monsen50ef84f2010-07-29 15:18:00 +01001414
Steve Blocka7e24c12009-10-30 11:49:00 +00001415 if (LookupForWrite(*receiver, *name, &lookup)) {
Kristian Monsen50ef84f2010-07-29 15:18:00 +01001416 bool can_be_inlined =
1417 state == UNINITIALIZED &&
1418 lookup.IsProperty() &&
1419 lookup.holder() == *receiver &&
1420 lookup.type() == FIELD &&
1421 !receiver->IsAccessCheckNeeded();
1422
1423 if (can_be_inlined) {
1424 Map* map = lookup.holder()->map();
1425 // Property's index in the properties array. If negative we have
1426 // an inobject property.
1427 int index = lookup.GetFieldIndex() - map->inobject_properties();
1428 if (index < 0) {
1429 // Index is an offset from the end of the object.
1430 int offset = map->instance_size() + (index * kPointerSize);
1431 if (PatchInlinedStore(address(), map, offset)) {
1432 set_target(megamorphic_stub());
1433#ifdef DEBUG
1434 if (FLAG_trace_ic) {
1435 PrintF("[StoreIC : inline patch %s]\n", *name->ToCString());
1436 }
1437#endif
1438 return receiver->SetProperty(*name, *value, NONE);
1439#ifdef DEBUG
1440
1441 } else {
1442 if (FLAG_trace_ic) {
1443 PrintF("[StoreIC : no inline patch %s (patching failed)]\n",
1444 *name->ToCString());
1445 }
1446 }
1447 } else {
1448 if (FLAG_trace_ic) {
1449 PrintF("[StoreIC : no inline patch %s (not inobject)]\n",
1450 *name->ToCString());
1451 }
1452 }
1453 } else {
1454 if (state == PREMONOMORPHIC) {
1455 if (FLAG_trace_ic) {
1456 PrintF("[StoreIC : no inline patch %s (not inlinable)]\n",
1457 *name->ToCString());
1458#endif
1459 }
1460 }
1461 }
1462
1463 // If no inlined store ic was patched, generate a stub for this
1464 // store.
Steve Block1e0659c2011-05-24 12:43:12 +01001465 UpdateCaches(&lookup, state, extra_ic_state, receiver, name, value);
1466 } else {
1467 // Strict mode doesn't allow setting non-existent global property.
1468 if (extra_ic_state == kStoreICStrict && IsContextual(object)) {
1469 return ReferenceError("not_defined", name);
1470 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001471 }
1472 }
1473
Ben Murdochb0fe1622011-05-05 13:52:32 +01001474 if (receiver->IsJSGlobalProxy()) {
1475 // Generate a generic stub that goes to the runtime when we see a global
1476 // proxy as receiver.
Steve Block1e0659c2011-05-24 12:43:12 +01001477 Code* stub = (extra_ic_state == kStoreICStrict)
1478 ? global_proxy_stub_strict()
1479 : global_proxy_stub();
1480 if (target() != stub) {
1481 set_target(stub);
Ben Murdochb0fe1622011-05-05 13:52:32 +01001482#ifdef DEBUG
1483 TraceIC("StoreIC", name, state, target());
1484#endif
1485 }
1486 }
1487
Steve Blocka7e24c12009-10-30 11:49:00 +00001488 // Set the property.
1489 return receiver->SetProperty(*name, *value, NONE);
1490}
1491
1492
1493void StoreIC::UpdateCaches(LookupResult* lookup,
1494 State state,
Steve Block1e0659c2011-05-24 12:43:12 +01001495 Code::ExtraICState extra_ic_state,
Steve Blocka7e24c12009-10-30 11:49:00 +00001496 Handle<JSObject> receiver,
1497 Handle<String> name,
1498 Handle<Object> value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001499 // Skip JSGlobalProxy.
1500 ASSERT(!receiver->IsJSGlobalProxy());
1501
1502 ASSERT(StoreICableLookup(lookup));
1503
1504 // If the property has a non-field type allowing map transitions
1505 // where there is extra room in the object, we leave the IC in its
1506 // current state.
1507 PropertyType type = lookup->type();
1508
1509 // Compute the code stub for this store; used for rewriting to
1510 // monomorphic state and making sure that the code stub is in the
1511 // stub cache.
John Reck59135872010-11-02 12:39:01 -07001512 MaybeObject* maybe_code = NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +00001513 Object* code = NULL;
1514 switch (type) {
1515 case FIELD: {
Steve Block1e0659c2011-05-24 12:43:12 +01001516 maybe_code = StubCache::ComputeStoreField(
1517 *name, *receiver, lookup->GetFieldIndex(), NULL, extra_ic_state);
Steve Blocka7e24c12009-10-30 11:49:00 +00001518 break;
1519 }
1520 case MAP_TRANSITION: {
1521 if (lookup->GetAttributes() != NONE) return;
1522 HandleScope scope;
1523 ASSERT(type == MAP_TRANSITION);
1524 Handle<Map> transition(lookup->GetTransitionMap());
1525 int index = transition->PropertyIndexFor(*name);
Steve Block1e0659c2011-05-24 12:43:12 +01001526 maybe_code = StubCache::ComputeStoreField(
1527 *name, *receiver, index, *transition, extra_ic_state);
Steve Blocka7e24c12009-10-30 11:49:00 +00001528 break;
1529 }
1530 case NORMAL: {
Steve Block8defd9f2010-07-08 12:39:36 +01001531 if (receiver->IsGlobalObject()) {
1532 // The stub generated for the global object picks the value directly
1533 // from the property cell. So the property must be directly on the
1534 // global object.
1535 Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
1536 JSGlobalPropertyCell* cell =
1537 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
Steve Block1e0659c2011-05-24 12:43:12 +01001538 maybe_code = StubCache::ComputeStoreGlobal(
1539 *name, *global, cell, extra_ic_state);
Steve Block8defd9f2010-07-08 12:39:36 +01001540 } else {
1541 if (lookup->holder() != *receiver) return;
Steve Block1e0659c2011-05-24 12:43:12 +01001542 maybe_code = StubCache::ComputeStoreNormal(extra_ic_state);
Steve Blocka7e24c12009-10-30 11:49:00 +00001543 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001544 break;
1545 }
1546 case CALLBACKS: {
1547 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
1548 AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject());
1549 if (v8::ToCData<Address>(callback->setter()) == 0) return;
Steve Block1e0659c2011-05-24 12:43:12 +01001550 maybe_code = StubCache::ComputeStoreCallback(
1551 *name, *receiver, callback, extra_ic_state);
Steve Blocka7e24c12009-10-30 11:49:00 +00001552 break;
1553 }
1554 case INTERCEPTOR: {
1555 ASSERT(!receiver->GetNamedInterceptor()->setter()->IsUndefined());
Steve Block1e0659c2011-05-24 12:43:12 +01001556 maybe_code = StubCache::ComputeStoreInterceptor(
1557 *name, *receiver, extra_ic_state);
Steve Blocka7e24c12009-10-30 11:49:00 +00001558 break;
1559 }
1560 default:
1561 return;
1562 }
1563
1564 // If we're unable to compute the stub (not enough memory left), we
1565 // simply avoid updating the caches.
John Reck59135872010-11-02 12:39:01 -07001566 if (maybe_code == NULL || !maybe_code->ToObject(&code)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001567
1568 // Patch the call site depending on the state of the cache.
1569 if (state == UNINITIALIZED || state == MONOMORPHIC_PROTOTYPE_FAILURE) {
1570 set_target(Code::cast(code));
1571 } else if (state == MONOMORPHIC) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01001572 // Only move to megamorphic if the target changes.
Steve Block1e0659c2011-05-24 12:43:12 +01001573 if (target() != Code::cast(code)) {
1574 set_target(extra_ic_state == kStoreICStrict
1575 ? megamorphic_stub_strict()
1576 : megamorphic_stub());
1577 }
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01001578 } else if (state == MEGAMORPHIC) {
1579 // Update the stub cache.
1580 StubCache::Set(*name, receiver->map(), Code::cast(code));
Steve Blocka7e24c12009-10-30 11:49:00 +00001581 }
1582
1583#ifdef DEBUG
1584 TraceIC("StoreIC", name, state, target());
1585#endif
1586}
1587
1588
John Reck59135872010-11-02 12:39:01 -07001589MaybeObject* KeyedStoreIC::Store(State state,
1590 Handle<Object> object,
1591 Handle<Object> key,
1592 Handle<Object> value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001593 if (key->IsSymbol()) {
1594 Handle<String> name = Handle<String>::cast(key);
1595
1596 // If the object is undefined or null it's illegal to try to set any
1597 // properties on it; throw a TypeError in that case.
1598 if (object->IsUndefined() || object->IsNull()) {
1599 return TypeError("non_object_property_store", object, name);
1600 }
1601
1602 // Ignore stores where the receiver is not a JSObject.
1603 if (!object->IsJSObject()) return *value;
1604 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1605
1606 // Check if the given name is an array index.
1607 uint32_t index;
1608 if (name->AsArrayIndex(&index)) {
1609 HandleScope scope;
1610 Handle<Object> result = SetElement(receiver, index, value);
1611 if (result.is_null()) return Failure::Exception();
1612 return *value;
1613 }
1614
1615 // Lookup the property locally in the receiver.
1616 LookupResult lookup;
1617 receiver->LocalLookup(*name, &lookup);
1618
1619 // Update inline cache and stub cache.
Andrei Popescu31002712010-02-23 13:46:05 +00001620 if (FLAG_use_ic) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001621 UpdateCaches(&lookup, state, receiver, name, value);
1622 }
1623
1624 // Set the property.
1625 return receiver->SetProperty(*name, *value, NONE);
1626 }
1627
1628 // Do not use ICs for objects that require access checks (including
1629 // the global object).
1630 bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded();
1631 ASSERT(!(use_ic && object->IsJSGlobalProxy()));
1632
Steve Block3ce2e202009-11-05 08:53:23 +00001633 if (use_ic) {
1634 Code* stub = generic_stub();
1635 if (object->IsJSObject()) {
1636 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1637 if (receiver->HasExternalArrayElements()) {
Steve Block1e0659c2011-05-24 12:43:12 +01001638 MaybeObject* probe =
1639 StubCache::ComputeKeyedLoadOrStoreExternalArray(*receiver, true);
1640 stub =
1641 probe->IsFailure() ? NULL : Code::cast(probe->ToObjectUnchecked());
Ben Murdochb0fe1622011-05-05 13:52:32 +01001642 } else if (state == UNINITIALIZED &&
1643 key->IsSmi() &&
1644 receiver->map()->has_fast_elements()) {
1645 MaybeObject* probe = StubCache::ComputeKeyedStoreSpecialized(*receiver);
1646 stub =
1647 probe->IsFailure() ? NULL : Code::cast(probe->ToObjectUnchecked());
Steve Block3ce2e202009-11-05 08:53:23 +00001648 }
1649 }
Ben Murdochb0fe1622011-05-05 13:52:32 +01001650 if (stub != NULL) set_target(stub);
Steve Block3ce2e202009-11-05 08:53:23 +00001651 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001652
1653 // Set the property.
1654 return Runtime::SetObjectProperty(object, key, value, NONE);
1655}
1656
1657
1658void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
1659 State state,
1660 Handle<JSObject> receiver,
1661 Handle<String> name,
1662 Handle<Object> value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001663 // Skip JSGlobalProxy.
1664 if (receiver->IsJSGlobalProxy()) return;
1665
1666 // Bail out if we didn't find a result.
Andrei Popescu402d9372010-02-26 13:31:12 +00001667 if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001668
1669 // If the property is read-only, we leave the IC in its current
1670 // state.
1671 if (lookup->IsReadOnly()) return;
1672
1673 // If the property has a non-field type allowing map transitions
1674 // where there is extra room in the object, we leave the IC in its
1675 // current state.
1676 PropertyType type = lookup->type();
1677
1678 // Compute the code stub for this store; used for rewriting to
1679 // monomorphic state and making sure that the code stub is in the
1680 // stub cache.
John Reck59135872010-11-02 12:39:01 -07001681 MaybeObject* maybe_code = NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +00001682 Object* code = NULL;
1683
1684 switch (type) {
1685 case FIELD: {
John Reck59135872010-11-02 12:39:01 -07001686 maybe_code = StubCache::ComputeKeyedStoreField(*name, *receiver,
1687 lookup->GetFieldIndex());
Steve Blocka7e24c12009-10-30 11:49:00 +00001688 break;
1689 }
1690 case MAP_TRANSITION: {
1691 if (lookup->GetAttributes() == NONE) {
1692 HandleScope scope;
1693 ASSERT(type == MAP_TRANSITION);
1694 Handle<Map> transition(lookup->GetTransitionMap());
1695 int index = transition->PropertyIndexFor(*name);
John Reck59135872010-11-02 12:39:01 -07001696 maybe_code = StubCache::ComputeKeyedStoreField(*name, *receiver,
1697 index, *transition);
Steve Blocka7e24c12009-10-30 11:49:00 +00001698 break;
1699 }
1700 // fall through.
1701 }
1702 default: {
1703 // Always rewrite to the generic case so that we do not
1704 // repeatedly try to rewrite.
John Reck59135872010-11-02 12:39:01 -07001705 maybe_code = generic_stub();
Steve Blocka7e24c12009-10-30 11:49:00 +00001706 break;
1707 }
1708 }
1709
1710 // If we're unable to compute the stub (not enough memory left), we
1711 // simply avoid updating the caches.
John Reck59135872010-11-02 12:39:01 -07001712 if (maybe_code == NULL || !maybe_code->ToObject(&code)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001713
1714 // Patch the call site depending on the state of the cache. Make
1715 // sure to always rewrite from monomorphic to megamorphic.
1716 ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE);
1717 if (state == UNINITIALIZED || state == PREMONOMORPHIC) {
1718 set_target(Code::cast(code));
1719 } else if (state == MONOMORPHIC) {
1720 set_target(megamorphic_stub());
1721 }
1722
1723#ifdef DEBUG
1724 TraceIC("KeyedStoreIC", name, state, target());
1725#endif
1726}
1727
1728
1729// ----------------------------------------------------------------------------
1730// Static IC stub generators.
1731//
1732
Ben Murdochf87a2032010-10-22 12:50:53 +01001733static JSFunction* CompileFunction(JSFunction* function,
1734 InLoopFlag in_loop) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001735 // Compile now with optimization.
1736 HandleScope scope;
Ben Murdochf87a2032010-10-22 12:50:53 +01001737 Handle<JSFunction> function_handle(function);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001738 if (in_loop == IN_LOOP) {
Ben Murdochf87a2032010-10-22 12:50:53 +01001739 CompileLazyInLoop(function_handle, CLEAR_EXCEPTION);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001740 } else {
Ben Murdochf87a2032010-10-22 12:50:53 +01001741 CompileLazy(function_handle, CLEAR_EXCEPTION);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001742 }
Ben Murdochf87a2032010-10-22 12:50:53 +01001743 return *function_handle;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001744}
1745
1746
1747// Used from ic-<arch>.cc.
John Reck59135872010-11-02 12:39:01 -07001748MUST_USE_RESULT MaybeObject* CallIC_Miss(Arguments args) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001749 NoHandleAllocation na;
1750 ASSERT(args.length() == 2);
1751 CallIC ic;
Steve Block6ded16b2010-05-10 14:33:55 +01001752 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
Ben Murdochb8e0da22011-05-16 14:20:40 +01001753 Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
1754 MaybeObject* maybe_result = ic.LoadFunction(state,
1755 extra_ic_state,
1756 args.at<Object>(0),
1757 args.at<String>(1));
John Reck59135872010-11-02 12:39:01 -07001758 Object* result;
Ben Murdochb8e0da22011-05-16 14:20:40 +01001759 if (!maybe_result->ToObject(&result)) return maybe_result;
Steve Blocka7e24c12009-10-30 11:49:00 +00001760
1761 // The first time the inline cache is updated may be the first time the
1762 // function it references gets called. If the function was lazily compiled
1763 // then the first call will trigger a compilation. We check for this case
1764 // and we do the compilation immediately, instead of waiting for the stub
1765 // currently attached to the JSFunction object to trigger compilation. We
1766 // do this in the case where we know that the inline cache is inside a loop,
1767 // because then we know that we want to optimize the function.
1768 if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
1769 return result;
1770 }
Ben Murdochf87a2032010-10-22 12:50:53 +01001771 return CompileFunction(JSFunction::cast(result), ic.target()->ic_in_loop());
Steve Blocka7e24c12009-10-30 11:49:00 +00001772}
1773
1774
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001775// Used from ic-<arch>.cc.
John Reck59135872010-11-02 12:39:01 -07001776MUST_USE_RESULT MaybeObject* KeyedCallIC_Miss(Arguments args) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001777 NoHandleAllocation na;
1778 ASSERT(args.length() == 2);
1779 KeyedCallIC ic;
1780 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
John Reck59135872010-11-02 12:39:01 -07001781 Object* result;
1782 { MaybeObject* maybe_result =
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001783 ic.LoadFunction(state, args.at<Object>(0), args.at<Object>(1));
John Reck59135872010-11-02 12:39:01 -07001784 if (!maybe_result->ToObject(&result)) return maybe_result;
1785 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001786
1787 if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
1788 return result;
1789 }
Ben Murdochf87a2032010-10-22 12:50:53 +01001790 return CompileFunction(JSFunction::cast(result), ic.target()->ic_in_loop());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001791}
1792
1793
1794// Used from ic-<arch>.cc.
John Reck59135872010-11-02 12:39:01 -07001795MUST_USE_RESULT MaybeObject* LoadIC_Miss(Arguments args) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001796 NoHandleAllocation na;
1797 ASSERT(args.length() == 2);
1798 LoadIC ic;
Steve Block6ded16b2010-05-10 14:33:55 +01001799 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001800 return ic.Load(state, args.at<Object>(0), args.at<String>(1));
1801}
1802
1803
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001804// Used from ic-<arch>.cc
John Reck59135872010-11-02 12:39:01 -07001805MUST_USE_RESULT MaybeObject* KeyedLoadIC_Miss(Arguments args) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001806 NoHandleAllocation na;
1807 ASSERT(args.length() == 2);
1808 KeyedLoadIC ic;
Steve Block6ded16b2010-05-10 14:33:55 +01001809 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001810 return ic.Load(state, args.at<Object>(0), args.at<Object>(1));
1811}
1812
1813
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001814// Used from ic-<arch>.cc.
John Reck59135872010-11-02 12:39:01 -07001815MUST_USE_RESULT MaybeObject* StoreIC_Miss(Arguments args) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001816 NoHandleAllocation na;
1817 ASSERT(args.length() == 3);
1818 StoreIC ic;
Steve Block6ded16b2010-05-10 14:33:55 +01001819 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
Steve Block1e0659c2011-05-24 12:43:12 +01001820 Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
1821 return ic.Store(state, extra_ic_state, args.at<Object>(0),
1822 args.at<String>(1), args.at<Object>(2));
Steve Blocka7e24c12009-10-30 11:49:00 +00001823}
1824
1825
John Reck59135872010-11-02 12:39:01 -07001826MUST_USE_RESULT MaybeObject* StoreIC_ArrayLength(Arguments args) {
Steve Block6ded16b2010-05-10 14:33:55 +01001827 NoHandleAllocation nha;
1828
1829 ASSERT(args.length() == 2);
1830 JSObject* receiver = JSObject::cast(args[0]);
1831 Object* len = args[1];
1832
John Reck59135872010-11-02 12:39:01 -07001833 // The generated code should filter out non-Smis before we get here.
1834 ASSERT(len->IsSmi());
1835
1836 Object* result;
1837 { MaybeObject* maybe_result = receiver->SetElementsLength(len);
1838 if (!maybe_result->ToObject(&result)) return maybe_result;
1839 }
Steve Block6ded16b2010-05-10 14:33:55 +01001840 return len;
1841}
1842
1843
Steve Blocka7e24c12009-10-30 11:49:00 +00001844// Extend storage is called in a store inline cache when
1845// it is necessary to extend the properties array of a
1846// JSObject.
John Reck59135872010-11-02 12:39:01 -07001847MUST_USE_RESULT MaybeObject* SharedStoreIC_ExtendStorage(Arguments args) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001848 NoHandleAllocation na;
1849 ASSERT(args.length() == 3);
1850
1851 // Convert the parameters
1852 JSObject* object = JSObject::cast(args[0]);
1853 Map* transition = Map::cast(args[1]);
1854 Object* value = args[2];
1855
1856 // Check the object has run out out property space.
1857 ASSERT(object->HasFastProperties());
1858 ASSERT(object->map()->unused_property_fields() == 0);
1859
1860 // Expand the properties array.
1861 FixedArray* old_storage = object->properties();
1862 int new_unused = transition->unused_property_fields();
1863 int new_size = old_storage->length() + new_unused + 1;
John Reck59135872010-11-02 12:39:01 -07001864 Object* result;
1865 { MaybeObject* maybe_result = old_storage->CopySize(new_size);
1866 if (!maybe_result->ToObject(&result)) return maybe_result;
1867 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001868 FixedArray* new_storage = FixedArray::cast(result);
1869 new_storage->set(old_storage->length(), value);
1870
1871 // Set the new property value and do the map transition.
1872 object->set_properties(new_storage);
1873 object->set_map(transition);
1874
1875 // Return the stored value.
1876 return value;
1877}
1878
1879
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001880// Used from ic-<arch>.cc.
John Reck59135872010-11-02 12:39:01 -07001881MUST_USE_RESULT MaybeObject* KeyedStoreIC_Miss(Arguments args) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001882 NoHandleAllocation na;
1883 ASSERT(args.length() == 3);
1884 KeyedStoreIC ic;
Steve Block6ded16b2010-05-10 14:33:55 +01001885 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001886 return ic.Store(state, args.at<Object>(0), args.at<Object>(1),
1887 args.at<Object>(2));
1888}
1889
1890
Steve Block6ded16b2010-05-10 14:33:55 +01001891void BinaryOpIC::patch(Code* code) {
1892 set_target(code);
1893}
1894
1895
1896const char* BinaryOpIC::GetName(TypeInfo type_info) {
1897 switch (type_info) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01001898 case UNINIT_OR_SMI: return "UninitOrSmi";
Steve Block6ded16b2010-05-10 14:33:55 +01001899 case DEFAULT: return "Default";
1900 case GENERIC: return "Generic";
1901 case HEAP_NUMBERS: return "HeapNumbers";
1902 case STRINGS: return "Strings";
1903 default: return "Invalid";
1904 }
1905}
1906
1907
1908BinaryOpIC::State BinaryOpIC::ToState(TypeInfo type_info) {
1909 switch (type_info) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01001910 case UNINIT_OR_SMI:
1911 return UNINITIALIZED;
1912 case DEFAULT:
1913 case HEAP_NUMBERS:
1914 case STRINGS:
1915 return MONOMORPHIC;
1916 case GENERIC:
1917 return MEGAMORPHIC;
Steve Block6ded16b2010-05-10 14:33:55 +01001918 }
Ben Murdochb0fe1622011-05-05 13:52:32 +01001919 UNREACHABLE();
1920 return UNINITIALIZED;
Steve Block6ded16b2010-05-10 14:33:55 +01001921}
1922
1923
1924BinaryOpIC::TypeInfo BinaryOpIC::GetTypeInfo(Object* left,
1925 Object* right) {
1926 if (left->IsSmi() && right->IsSmi()) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01001927 // If we have two smi inputs we can reach here because
1928 // of an overflow. Enter default state.
1929 return DEFAULT;
Steve Block6ded16b2010-05-10 14:33:55 +01001930 }
1931
1932 if (left->IsNumber() && right->IsNumber()) {
1933 return HEAP_NUMBERS;
1934 }
1935
1936 if (left->IsString() || right->IsString()) {
1937 // Patching for fast string ADD makes sense even if only one of the
1938 // arguments is a string.
1939 return STRINGS;
1940 }
1941
1942 return GENERIC;
1943}
1944
1945
Ben Murdochb0fe1622011-05-05 13:52:32 +01001946// defined in code-stubs-<arch>.cc
Steve Block6ded16b2010-05-10 14:33:55 +01001947Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info);
1948
1949
John Reck59135872010-11-02 12:39:01 -07001950MUST_USE_RESULT MaybeObject* BinaryOp_Patch(Arguments args) {
Leon Clarkeac952652010-07-15 11:15:24 +01001951 ASSERT(args.length() == 5);
Steve Block6ded16b2010-05-10 14:33:55 +01001952
Ben Murdochb0fe1622011-05-05 13:52:32 +01001953 HandleScope scope;
Steve Block6ded16b2010-05-10 14:33:55 +01001954 Handle<Object> left = args.at<Object>(0);
1955 Handle<Object> right = args.at<Object>(1);
Leon Clarkeac952652010-07-15 11:15:24 +01001956 int key = Smi::cast(args[2])->value();
1957 Token::Value op = static_cast<Token::Value>(Smi::cast(args[3])->value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01001958 BinaryOpIC::TypeInfo previous_type =
Leon Clarkeac952652010-07-15 11:15:24 +01001959 static_cast<BinaryOpIC::TypeInfo>(Smi::cast(args[4])->value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01001960
1961 BinaryOpIC::TypeInfo type = BinaryOpIC::GetTypeInfo(*left, *right);
1962 Handle<Code> code = GetBinaryOpStub(key, type);
1963 if (!code.is_null()) {
1964 BinaryOpIC ic;
1965 ic.patch(*code);
1966 if (FLAG_trace_ic) {
1967 PrintF("[BinaryOpIC (%s->%s)#%s]\n",
1968 BinaryOpIC::GetName(previous_type),
1969 BinaryOpIC::GetName(type),
1970 Token::Name(op));
Steve Block6ded16b2010-05-10 14:33:55 +01001971 }
1972 }
1973
Leon Clarkeac952652010-07-15 11:15:24 +01001974 Handle<JSBuiltinsObject> builtins = Top::builtins();
Leon Clarkeac952652010-07-15 11:15:24 +01001975 Object* builtin = NULL; // Initialization calms down the compiler.
Leon Clarkeac952652010-07-15 11:15:24 +01001976 switch (op) {
1977 case Token::ADD:
1978 builtin = builtins->javascript_builtin(Builtins::ADD);
1979 break;
1980 case Token::SUB:
1981 builtin = builtins->javascript_builtin(Builtins::SUB);
1982 break;
1983 case Token::MUL:
1984 builtin = builtins->javascript_builtin(Builtins::MUL);
1985 break;
1986 case Token::DIV:
1987 builtin = builtins->javascript_builtin(Builtins::DIV);
1988 break;
1989 case Token::MOD:
1990 builtin = builtins->javascript_builtin(Builtins::MOD);
1991 break;
1992 case Token::BIT_AND:
1993 builtin = builtins->javascript_builtin(Builtins::BIT_AND);
1994 break;
1995 case Token::BIT_OR:
1996 builtin = builtins->javascript_builtin(Builtins::BIT_OR);
1997 break;
1998 case Token::BIT_XOR:
1999 builtin = builtins->javascript_builtin(Builtins::BIT_XOR);
2000 break;
2001 case Token::SHR:
2002 builtin = builtins->javascript_builtin(Builtins::SHR);
2003 break;
2004 case Token::SAR:
2005 builtin = builtins->javascript_builtin(Builtins::SAR);
2006 break;
2007 case Token::SHL:
2008 builtin = builtins->javascript_builtin(Builtins::SHL);
2009 break;
2010 default:
2011 UNREACHABLE();
2012 }
2013
2014 Handle<JSFunction> builtin_function(JSFunction::cast(builtin));
2015
2016 bool caught_exception;
2017 Object** builtin_args[] = { right.location() };
2018 Handle<Object> result = Execution::Call(builtin_function,
2019 left,
2020 ARRAY_SIZE(builtin_args),
2021 builtin_args,
2022 &caught_exception);
2023 if (caught_exception) {
2024 return Failure::Exception();
2025 }
Steve Block6ded16b2010-05-10 14:33:55 +01002026 return *result;
2027}
2028
2029
Ben Murdochb0fe1622011-05-05 13:52:32 +01002030void TRBinaryOpIC::patch(Code* code) {
2031 set_target(code);
2032}
2033
2034
2035const char* TRBinaryOpIC::GetName(TypeInfo type_info) {
2036 switch (type_info) {
2037 case UNINITIALIZED: return "Uninitialized";
2038 case SMI: return "SMI";
2039 case INT32: return "Int32s";
2040 case HEAP_NUMBER: return "HeapNumbers";
2041 case STRING: return "Strings";
2042 case GENERIC: return "Generic";
2043 default: return "Invalid";
2044 }
2045}
2046
2047
2048TRBinaryOpIC::State TRBinaryOpIC::ToState(TypeInfo type_info) {
2049 switch (type_info) {
2050 case UNINITIALIZED:
2051 return ::v8::internal::UNINITIALIZED;
2052 case SMI:
2053 case INT32:
2054 case HEAP_NUMBER:
2055 case STRING:
2056 return MONOMORPHIC;
2057 case GENERIC:
2058 return MEGAMORPHIC;
2059 }
2060 UNREACHABLE();
2061 return ::v8::internal::UNINITIALIZED;
2062}
2063
2064
2065TRBinaryOpIC::TypeInfo TRBinaryOpIC::JoinTypes(TRBinaryOpIC::TypeInfo x,
2066 TRBinaryOpIC::TypeInfo y) {
2067 if (x == UNINITIALIZED) return y;
2068 if (y == UNINITIALIZED) return x;
2069 if (x == STRING && y == STRING) return STRING;
2070 if (x == STRING || y == STRING) return GENERIC;
2071 if (x >= y) return x;
2072 return y;
2073}
2074
2075TRBinaryOpIC::TypeInfo TRBinaryOpIC::GetTypeInfo(Handle<Object> left,
2076 Handle<Object> right) {
2077 ::v8::internal::TypeInfo left_type =
2078 ::v8::internal::TypeInfo::TypeFromValue(left);
2079 ::v8::internal::TypeInfo right_type =
2080 ::v8::internal::TypeInfo::TypeFromValue(right);
2081
2082 if (left_type.IsSmi() && right_type.IsSmi()) {
2083 return SMI;
2084 }
2085
2086 if (left_type.IsInteger32() && right_type.IsInteger32()) {
Steve Block1e0659c2011-05-24 12:43:12 +01002087 // Platforms with 32-bit Smis have no distinct INT32 type.
2088 if (kSmiValueSize == 32) return SMI;
Ben Murdochb0fe1622011-05-05 13:52:32 +01002089 return INT32;
2090 }
2091
2092 if (left_type.IsNumber() && right_type.IsNumber()) {
2093 return HEAP_NUMBER;
2094 }
2095
2096 if (left_type.IsString() || right_type.IsString()) {
2097 // Patching for fast string ADD makes sense even if only one of the
2098 // arguments is a string.
2099 return STRING;
2100 }
2101
2102 return GENERIC;
2103}
2104
2105
2106// defined in code-stubs-<arch>.cc
2107// Only needed to remove dependency of ic.cc on code-stubs-<arch>.h.
2108Handle<Code> GetTypeRecordingBinaryOpStub(int key,
2109 TRBinaryOpIC::TypeInfo type_info,
2110 TRBinaryOpIC::TypeInfo result_type);
2111
2112
2113MaybeObject* TypeRecordingBinaryOp_Patch(Arguments args) {
2114 ASSERT(args.length() == 5);
2115
2116 HandleScope scope;
2117 Handle<Object> left = args.at<Object>(0);
2118 Handle<Object> right = args.at<Object>(1);
2119 int key = Smi::cast(args[2])->value();
2120 Token::Value op = static_cast<Token::Value>(Smi::cast(args[3])->value());
2121 TRBinaryOpIC::TypeInfo previous_type =
2122 static_cast<TRBinaryOpIC::TypeInfo>(Smi::cast(args[4])->value());
2123
2124 TRBinaryOpIC::TypeInfo type = TRBinaryOpIC::GetTypeInfo(left, right);
2125 type = TRBinaryOpIC::JoinTypes(type, previous_type);
2126 TRBinaryOpIC::TypeInfo result_type = TRBinaryOpIC::UNINITIALIZED;
2127 if (type == TRBinaryOpIC::STRING && op != Token::ADD) {
2128 type = TRBinaryOpIC::GENERIC;
2129 }
2130 if (type == TRBinaryOpIC::SMI &&
2131 previous_type == TRBinaryOpIC::SMI) {
Steve Block1e0659c2011-05-24 12:43:12 +01002132 if (op == Token::DIV || op == Token::MUL || kSmiValueSize == 32) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01002133 // Arithmetic on two Smi inputs has yielded a heap number.
2134 // That is the only way to get here from the Smi stub.
Steve Block1e0659c2011-05-24 12:43:12 +01002135 // With 32-bit Smis, all overflows give heap numbers, but with
2136 // 31-bit Smis, most operations overflow to int32 results.
Ben Murdochb0fe1622011-05-05 13:52:32 +01002137 result_type = TRBinaryOpIC::HEAP_NUMBER;
2138 } else {
2139 // Other operations on SMIs that overflow yield int32s.
2140 result_type = TRBinaryOpIC::INT32;
2141 }
2142 }
2143 if (type == TRBinaryOpIC::INT32 &&
2144 previous_type == TRBinaryOpIC::INT32) {
2145 // We must be here because an operation on two INT32 types overflowed.
2146 result_type = TRBinaryOpIC::HEAP_NUMBER;
2147 }
2148
2149 Handle<Code> code = GetTypeRecordingBinaryOpStub(key, type, result_type);
2150 if (!code.is_null()) {
Ben Murdochb0fe1622011-05-05 13:52:32 +01002151 if (FLAG_trace_ic) {
2152 PrintF("[TypeRecordingBinaryOpIC (%s->(%s->%s))#%s]\n",
2153 TRBinaryOpIC::GetName(previous_type),
2154 TRBinaryOpIC::GetName(type),
2155 TRBinaryOpIC::GetName(result_type),
2156 Token::Name(op));
2157 }
Steve Block1e0659c2011-05-24 12:43:12 +01002158 TRBinaryOpIC ic;
2159 ic.patch(*code);
Ben Murdochb0fe1622011-05-05 13:52:32 +01002160
2161 // Activate inlined smi code.
2162 if (previous_type == TRBinaryOpIC::UNINITIALIZED) {
2163 PatchInlinedSmiCode(ic.address());
2164 }
2165 }
2166
2167 Handle<JSBuiltinsObject> builtins = Top::builtins();
2168 Object* builtin = NULL; // Initialization calms down the compiler.
2169 switch (op) {
2170 case Token::ADD:
2171 builtin = builtins->javascript_builtin(Builtins::ADD);
2172 break;
2173 case Token::SUB:
2174 builtin = builtins->javascript_builtin(Builtins::SUB);
2175 break;
2176 case Token::MUL:
2177 builtin = builtins->javascript_builtin(Builtins::MUL);
2178 break;
2179 case Token::DIV:
2180 builtin = builtins->javascript_builtin(Builtins::DIV);
2181 break;
2182 case Token::MOD:
2183 builtin = builtins->javascript_builtin(Builtins::MOD);
2184 break;
2185 case Token::BIT_AND:
2186 builtin = builtins->javascript_builtin(Builtins::BIT_AND);
2187 break;
2188 case Token::BIT_OR:
2189 builtin = builtins->javascript_builtin(Builtins::BIT_OR);
2190 break;
2191 case Token::BIT_XOR:
2192 builtin = builtins->javascript_builtin(Builtins::BIT_XOR);
2193 break;
2194 case Token::SHR:
2195 builtin = builtins->javascript_builtin(Builtins::SHR);
2196 break;
2197 case Token::SAR:
2198 builtin = builtins->javascript_builtin(Builtins::SAR);
2199 break;
2200 case Token::SHL:
2201 builtin = builtins->javascript_builtin(Builtins::SHL);
2202 break;
2203 default:
2204 UNREACHABLE();
2205 }
2206
2207 Handle<JSFunction> builtin_function(JSFunction::cast(builtin));
2208
2209 bool caught_exception;
2210 Object** builtin_args[] = { right.location() };
2211 Handle<Object> result = Execution::Call(builtin_function,
2212 left,
2213 ARRAY_SIZE(builtin_args),
2214 builtin_args,
2215 &caught_exception);
2216 if (caught_exception) {
2217 return Failure::Exception();
2218 }
2219 return *result;
2220}
2221
2222
2223Handle<Code> CompareIC::GetUninitialized(Token::Value op) {
2224 ICCompareStub stub(op, UNINITIALIZED);
2225 return stub.GetCode();
2226}
2227
2228
2229CompareIC::State CompareIC::ComputeState(Code* target) {
2230 int key = target->major_key();
2231 if (key == CodeStub::Compare) return GENERIC;
2232 ASSERT(key == CodeStub::CompareIC);
2233 return static_cast<State>(target->compare_state());
2234}
2235
2236
2237const char* CompareIC::GetStateName(State state) {
2238 switch (state) {
2239 case UNINITIALIZED: return "UNINITIALIZED";
2240 case SMIS: return "SMIS";
2241 case HEAP_NUMBERS: return "HEAP_NUMBERS";
2242 case OBJECTS: return "OBJECTS";
2243 case GENERIC: return "GENERIC";
2244 default:
2245 UNREACHABLE();
2246 return NULL;
2247 }
2248}
2249
2250
2251CompareIC::State CompareIC::TargetState(State state,
2252 bool has_inlined_smi_code,
2253 Handle<Object> x,
2254 Handle<Object> y) {
2255 if (!has_inlined_smi_code && state != UNINITIALIZED) return GENERIC;
2256 if (state == UNINITIALIZED && x->IsSmi() && y->IsSmi()) return SMIS;
2257 if ((state == UNINITIALIZED || (state == SMIS && has_inlined_smi_code)) &&
2258 x->IsNumber() && y->IsNumber()) return HEAP_NUMBERS;
2259 if (op_ != Token::EQ && op_ != Token::EQ_STRICT) return GENERIC;
2260 if (state == UNINITIALIZED &&
2261 x->IsJSObject() && y->IsJSObject()) return OBJECTS;
2262 return GENERIC;
2263}
2264
2265
2266// Used from ic_<arch>.cc.
2267Code* CompareIC_Miss(Arguments args) {
2268 NoHandleAllocation na;
2269 ASSERT(args.length() == 3);
2270 CompareIC ic(static_cast<Token::Value>(Smi::cast(args[2])->value()));
2271 ic.UpdateCaches(args.at<Object>(0), args.at<Object>(1));
2272 return ic.target();
2273}
2274
2275
Steve Blocka7e24c12009-10-30 11:49:00 +00002276static Address IC_utilities[] = {
2277#define ADDR(name) FUNCTION_ADDR(name),
2278 IC_UTIL_LIST(ADDR)
2279 NULL
2280#undef ADDR
2281};
2282
2283
2284Address IC::AddressFromUtilityId(IC::UtilityId id) {
2285 return IC_utilities[id];
2286}
2287
2288
2289} } // namespace v8::internal