Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / methodjit / MonoIC.cpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=4 sw=4 et tw=99:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18  * May 28, 2008.
19  *
20  * The Initial Developer of the Original Code is
21  *   Brendan Eich <brendan@mozilla.org>
22  *
23  * Contributor(s):
24  *   David Anderson <danderson@mozilla.com>
25  *   David Mandelin <dmandelin@mozilla.com>
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either of the GNU General Public License Version 2 or later (the "GPL"),
29  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40 #include "jsscope.h"
41 #include "jsnum.h"
42 #include "MonoIC.h"
43 #include "StubCalls.h"
44 #include "StubCalls-inl.h"
45 #include "assembler/assembler/LinkBuffer.h"
46 #include "assembler/assembler/MacroAssembler.h"
47 #include "assembler/assembler/CodeLocation.h"
48 #include "methodjit/CodeGenIncludes.h"
49 #include "methodjit/Compiler.h"
50 #include "methodjit/ICRepatcher.h"
51 #include "methodjit/PolyIC.h"
52 #include "InlineFrameAssembler.h"
53 #include "jsobj.h"
54
55 #include "jsinterpinlines.h"
56 #include "jsobjinlines.h"
57 #include "jsscopeinlines.h"
58 #include "jsscriptinlines.h"
59
60 using namespace js;
61 using namespace js::mjit;
62 using namespace js::mjit::ic;
63
64 typedef JSC::MacroAssembler::RegisterID RegisterID;
65 typedef JSC::MacroAssembler::Address Address;
66 typedef JSC::MacroAssembler::Jump Jump;
67 typedef JSC::MacroAssembler::Imm32 Imm32;
68 typedef JSC::MacroAssembler::ImmPtr ImmPtr;
69 typedef JSC::MacroAssembler::Call Call;
70 typedef JSC::MacroAssembler::Label Label;
71 typedef JSC::MacroAssembler::DataLabel32 DataLabel32;
72
73 #if defined JS_MONOIC
74
75 static void
76 PatchGetFallback(VMFrame &f, ic::GetGlobalNameIC *ic)
77 {
78     Repatcher repatch(f.jit());
79     JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::GetGlobalName));
80     repatch.relink(ic->slowPathCall, fptr);
81 }
82
83 void JS_FASTCALL
84 ic::GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic)
85 {
86     JSObject *obj = f.fp()->scopeChain().getGlobal();
87     JSAtom *atom = f.fp()->script()->getAtom(GET_INDEX(f.regs.pc));
88     jsid id = ATOM_TO_JSID(atom);
89
90     const Shape *shape = obj->nativeLookup(id);
91     if (!shape ||
92         !shape->hasDefaultGetterOrIsMethod() ||
93         !shape->hasSlot())
94     {
95         if (shape)
96             PatchGetFallback(f, ic);
97         stubs::GetGlobalName(f);
98         return;
99     }
100     uint32 slot = shape->slot;
101
102     /* Patch shape guard. */
103     Repatcher repatcher(f.jit());
104     repatcher.repatch(ic->fastPathStart.dataLabel32AtOffset(ic->shapeOffset), obj->shape());
105
106     /* Patch loads. */
107     JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset);
108     repatcher.patchAddressOffsetForValueLoad(label, slot * sizeof(Value));
109
110     /* Do load anyway... this time. */
111     stubs::GetGlobalName(f);
112 }
113
114 template <JSBool strict>
115 static void JS_FASTCALL
116 DisabledSetGlobal(VMFrame &f, ic::SetGlobalNameIC *ic)
117 {
118     JSScript *script = f.fp()->script();
119     JSAtom *atom = script->getAtom(GET_INDEX(f.regs.pc));
120     stubs::SetGlobalName<strict>(f, atom);
121 }
122
123 template void JS_FASTCALL DisabledSetGlobal<true>(VMFrame &f, ic::SetGlobalNameIC *ic);
124 template void JS_FASTCALL DisabledSetGlobal<false>(VMFrame &f, ic::SetGlobalNameIC *ic);
125
126 template <JSBool strict>
127 static void JS_FASTCALL
128 DisabledSetGlobalNoCache(VMFrame &f, ic::SetGlobalNameIC *ic)
129 {
130     JSScript *script = f.fp()->script();
131     JSAtom *atom = script->getAtom(GET_INDEX(f.regs.pc));
132     stubs::SetGlobalNameNoCache<strict>(f, atom);
133 }
134
135 template void JS_FASTCALL DisabledSetGlobalNoCache<true>(VMFrame &f, ic::SetGlobalNameIC *ic);
136 template void JS_FASTCALL DisabledSetGlobalNoCache<false>(VMFrame &f, ic::SetGlobalNameIC *ic);
137
138 static void
139 PatchSetFallback(VMFrame &f, ic::SetGlobalNameIC *ic)
140 {
141     JSScript *script = f.fp()->script();
142
143     Repatcher repatch(f.jit());
144     VoidStubSetGlobal stub = ic->usePropertyCache
145                              ? STRICT_VARIANT(DisabledSetGlobal)
146                              : STRICT_VARIANT(DisabledSetGlobalNoCache);
147     JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stub));
148     repatch.relink(ic->slowPathCall, fptr);
149 }
150
151 void
152 SetGlobalNameIC::patchExtraShapeGuard(Repatcher &repatcher, int32 shape)
153 {
154     JS_ASSERT(hasExtraStub);
155
156     JSC::CodeLocationLabel label(JSC::MacroAssemblerCodePtr(extraStub.start()));
157     repatcher.repatch(label.dataLabel32AtOffset(extraShapeGuard), shape);
158 }
159
160 void
161 SetGlobalNameIC::patchInlineShapeGuard(Repatcher &repatcher, int32 shape)
162 {
163     JSC::CodeLocationDataLabel32 label = fastPathStart.dataLabel32AtOffset(shapeOffset);
164     repatcher.repatch(label, shape);
165 }
166
167 static LookupStatus
168 UpdateSetGlobalNameStub(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, const Shape *shape)
169 {
170     Repatcher repatcher(ic->extraStub);
171
172     ic->patchExtraShapeGuard(repatcher, obj->shape());
173
174     JSC::CodeLocationLabel label(JSC::MacroAssemblerCodePtr(ic->extraStub.start()));
175     label = label.labelAtOffset(ic->extraStoreOffset);
176     repatcher.patchAddressOffsetForValueStore(label, shape->slot * sizeof(Value),
177                                               ic->vr.isTypeKnown());
178
179     return Lookup_Cacheable;
180 }
181
182 static LookupStatus
183 AttachSetGlobalNameStub(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, const Shape *shape)
184 {
185     Assembler masm;
186
187     Label start = masm.label();
188
189     DataLabel32 shapeLabel;
190     Jump guard = masm.branch32WithPatch(Assembler::NotEqual, ic->shapeReg, Imm32(obj->shape()),
191                                         shapeLabel);
192
193     /* A constant object needs rematerialization. */
194     if (ic->objConst)
195         masm.move(ImmPtr(obj), ic->objReg);
196
197     JS_ASSERT(obj->branded());
198
199     /*
200      * Load obj->slots. If ic->objConst, then this clobbers objReg, because
201      * ic->objReg == ic->shapeReg.
202      */
203     masm.loadPtr(Address(ic->objReg, offsetof(JSObject, slots)), ic->shapeReg);
204
205     /* Test if overwriting a function-tagged slot. */
206     Address slot(ic->shapeReg, sizeof(Value) * shape->slot);
207     Jump isNotObject = masm.testObject(Assembler::NotEqual, slot);
208
209     /* Now, test if the object is a function object. */
210     masm.loadPayload(slot, ic->shapeReg);
211     Jump isFun = masm.testFunction(Assembler::Equal, ic->shapeReg);
212
213     /* Restore shapeReg to obj->slots, since we clobbered it. */
214     if (ic->objConst)
215         masm.move(ImmPtr(obj), ic->objReg);
216     masm.loadPtr(Address(ic->objReg, offsetof(JSObject, slots)), ic->shapeReg);
217
218     /* If the object test fails, shapeReg is still obj->slots. */
219     isNotObject.linkTo(masm.label(), &masm);
220     DataLabel32 store = masm.storeValueWithAddressOffsetPatch(ic->vr, slot);
221
222     Jump done = masm.jump();
223
224     JITScript *jit = f.jit();
225     LinkerHelper linker(masm);
226     JSC::ExecutablePool *ep = linker.init(f.cx);
227     if (!ep)
228         return Lookup_Error;
229     if (!jit->execPools.append(ep)) {
230         ep->release();
231         js_ReportOutOfMemory(f.cx);
232         return Lookup_Error;
233     }
234
235     if (!linker.verifyRange(jit))
236         return Lookup_Uncacheable;
237
238     linker.link(done, ic->fastPathStart.labelAtOffset(ic->fastRejoinOffset));
239     linker.link(guard, ic->slowPathStart);
240     linker.link(isFun, ic->slowPathStart);
241
242     JSC::CodeLocationLabel cs = linker.finalize();
243     JaegerSpew(JSpew_PICs, "generated setgname stub at %p\n", cs.executableAddress());
244
245     Repatcher repatcher(f.jit());
246     repatcher.relink(ic->fastPathStart.jumpAtOffset(ic->inlineShapeJump), cs);
247
248     int offset = linker.locationOf(shapeLabel) - linker.locationOf(start);
249     ic->extraShapeGuard = offset;
250     JS_ASSERT(ic->extraShapeGuard == offset);
251
252     ic->extraStub = JSC::JITCode(cs.executableAddress(), linker.size());
253     offset = linker.locationOf(store) - linker.locationOf(start);
254     ic->extraStoreOffset = offset;
255     JS_ASSERT(ic->extraStoreOffset == offset);
256
257     ic->hasExtraStub = true;
258
259     return Lookup_Cacheable;
260 }
261
262 static LookupStatus
263 UpdateSetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, const Shape *shape)
264 {
265     /* Give globals a chance to appear. */
266     if (!shape)
267         return Lookup_Uncacheable;
268
269     if (shape->isMethod() ||
270         !shape->hasDefaultSetter() ||
271         !shape->writable() ||
272         !shape->hasSlot())
273     {
274         /* Disable the IC for weird shape attributes. */
275         PatchSetFallback(f, ic);
276         return Lookup_Uncacheable;
277     }
278
279     /* Branded sets must guard that they don't overwrite method-valued properties. */
280     if (obj->branded()) {
281         /*
282          * If this slot has a function valued property, the tail of this opcode
283          * could change the shape. Even if it doesn't, the IC is probably
284          * pointless, because it will always hit the function-test path and
285          * bail out. In these cases, don't bother building or updating the IC.
286          */
287         const Value &v = obj->getSlot(shape->slot);
288         if (v.isObject() && v.toObject().isFunction()) {
289             /*
290              * If we're going to rebrand, the object may unbrand, allowing this
291              * IC to come back to life. In that case, we don't disable the IC.
292              */
293             if (!ChangesMethodValue(v, f.regs.sp[-1]))
294                 PatchSetFallback(f, ic);
295             return Lookup_Uncacheable;
296         }
297
298         if (ic->hasExtraStub)
299             return UpdateSetGlobalNameStub(f, ic, obj, shape);
300
301         return AttachSetGlobalNameStub(f, ic, obj, shape);
302     }
303
304     /* Object is not branded, so we can use the inline path. */
305     Repatcher repatcher(f.jit());
306     ic->patchInlineShapeGuard(repatcher, obj->shape());
307
308     JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset);
309     repatcher.patchAddressOffsetForValueStore(label, shape->slot * sizeof(Value),
310                                               ic->vr.isTypeKnown());
311
312     return Lookup_Cacheable;
313 }
314
315 void JS_FASTCALL
316 ic::SetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic)
317 {
318     JSObject *obj = f.fp()->scopeChain().getGlobal();
319     JSScript *script = f.fp()->script();
320     JSAtom *atom = script->getAtom(GET_INDEX(f.regs.pc));
321     const Shape *shape = obj->nativeLookup(ATOM_TO_JSID(atom));
322
323     LookupStatus status = UpdateSetGlobalName(f, ic, obj, shape);
324     if (status == Lookup_Error)
325         THROW();
326
327     if (ic->usePropertyCache)
328         STRICT_VARIANT(stubs::SetGlobalName)(f, atom);
329     else
330         STRICT_VARIANT(stubs::SetGlobalNameNoCache)(f, atom);
331 }
332
333 class EqualityICLinker : public LinkerHelper
334 {
335     VMFrame &f;
336
337   public:
338     EqualityICLinker(Assembler &masm, VMFrame &f)
339         : LinkerHelper(masm), f(f)
340     { }
341
342     bool init(JSContext *cx) {
343         JSC::ExecutablePool *pool = LinkerHelper::init(cx);
344         if (!pool)
345             return false;
346         JSScript *script = f.fp()->script();
347         JITScript *jit = script->getJIT(f.fp()->isConstructing());
348         if (!jit->execPools.append(pool)) {
349             pool->release();
350             js_ReportOutOfMemory(cx);
351             return false;
352         }
353         return true;
354     }
355 };
356
357 /* Rough over-estimate of how much memory we need to unprotect. */
358 static const uint32 INLINE_PATH_LENGTH = 64;
359
360 class EqualityCompiler : public BaseCompiler
361 {
362     VMFrame &f;
363     EqualityICInfo &ic;
364
365     Vector<Jump, 4, SystemAllocPolicy> jumpList;
366     Jump trueJump;
367     Jump falseJump;
368     
369   public:
370     EqualityCompiler(VMFrame &f, EqualityICInfo &ic)
371         : BaseCompiler(f.cx), f(f), ic(ic), jumpList(SystemAllocPolicy())
372     {
373     }
374
375     void linkToStub(Jump j)
376     {
377         jumpList.append(j);
378     }
379
380     void linkTrue(Jump j)
381     {
382         trueJump = j;
383     }
384
385     void linkFalse(Jump j)
386     {
387         falseJump = j;
388     }
389     
390     void generateStringPath(Assembler &masm)
391     {
392         const ValueRemat &lvr = ic.lvr;
393         const ValueRemat &rvr = ic.rvr;
394
395         if (!lvr.isConstant() && !lvr.isType(JSVAL_TYPE_STRING)) {
396             Jump lhsFail = masm.testString(Assembler::NotEqual, lvr.typeReg());
397             linkToStub(lhsFail);
398         }
399         
400         if (!rvr.isConstant() && !rvr.isType(JSVAL_TYPE_STRING)) {
401             Jump rhsFail = masm.testString(Assembler::NotEqual, rvr.typeReg());
402             linkToStub(rhsFail);
403         }
404
405         RegisterID tmp = ic.tempReg;
406         
407         /* Test if lhs/rhs are atomized. */
408         Imm32 atomizedFlags(JSString::FLAT | JSString::ATOMIZED);
409         
410         masm.load32(Address(lvr.dataReg(), JSString::offsetOfLengthAndFlags()), tmp);
411         masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), tmp);
412         Jump lhsNotAtomized = masm.branch32(Assembler::NotEqual, tmp, atomizedFlags);
413         linkToStub(lhsNotAtomized);
414
415         if (!rvr.isConstant()) {
416             masm.load32(Address(rvr.dataReg(), JSString::offsetOfLengthAndFlags()), tmp);
417             masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), tmp);
418             Jump rhsNotAtomized = masm.branch32(Assembler::NotEqual, tmp, atomizedFlags);
419             linkToStub(rhsNotAtomized);
420         }
421
422         if (rvr.isConstant()) {
423             JSString *str = rvr.value().toString();
424             JS_ASSERT(str->isAtomized());
425             Jump test = masm.branchPtr(ic.cond, lvr.dataReg(), ImmPtr(str));
426             linkTrue(test);
427         } else {
428             Jump test = masm.branchPtr(ic.cond, lvr.dataReg(), rvr.dataReg());
429             linkTrue(test);
430         }
431
432         Jump fallthrough = masm.jump();
433         linkFalse(fallthrough);
434     }
435
436     void generateObjectPath(Assembler &masm)
437     {
438         ValueRemat &lvr = ic.lvr;
439         ValueRemat &rvr = ic.rvr;
440         
441         if (!lvr.isConstant() && !lvr.isType(JSVAL_TYPE_OBJECT)) {
442             Jump lhsFail = masm.testObject(Assembler::NotEqual, lvr.typeReg());
443             linkToStub(lhsFail);
444         }
445         
446         if (!rvr.isConstant() && !rvr.isType(JSVAL_TYPE_OBJECT)) {
447             Jump rhsFail = masm.testObject(Assembler::NotEqual, rvr.typeReg());
448             linkToStub(rhsFail);
449         }
450
451         Jump lhsHasEq = masm.branchTest32(Assembler::NonZero,
452                                           Address(lvr.dataReg(),
453                                                   offsetof(JSObject, flags)),
454                                           Imm32(JSObject::HAS_EQUALITY));
455         linkToStub(lhsHasEq);
456
457         if (rvr.isConstant()) {
458             JSObject *obj = &rvr.value().toObject();
459             Jump test = masm.branchPtr(ic.cond, lvr.dataReg(), ImmPtr(obj));
460             linkTrue(test);
461         } else {
462             Jump test = masm.branchPtr(ic.cond, lvr.dataReg(), rvr.dataReg());
463             linkTrue(test);
464         }
465
466         Jump fallthrough = masm.jump();
467         linkFalse(fallthrough);
468     }
469
470     bool linkForIC(Assembler &masm)
471     {
472         EqualityICLinker buffer(masm, f);
473         if (!buffer.init(cx))
474             return false;
475
476         Repatcher repatcher(f.jit());
477
478         /* Overwrite the call to the IC with a call to the stub. */
479         JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic.stub));
480         repatcher.relink(ic.stubCall, fptr);
481
482         // Silently fail, the IC is disabled now.
483         if (!buffer.verifyRange(f.jit()))
484             return true;
485
486         /* Set the targets of all type test failures to go to the stub. */
487         for (size_t i = 0; i < jumpList.length(); i++)
488             buffer.link(jumpList[i], ic.stubEntry);
489         jumpList.clear();
490
491         /* Set the targets for the the success and failure of the actual equality test. */
492         buffer.link(trueJump, ic.target);
493         buffer.link(falseJump, ic.fallThrough);
494
495         CodeLocationLabel cs = buffer.finalize();
496
497         /* Jump to the newly generated code instead of to the IC. */
498         repatcher.relink(ic.jumpToStub, cs);
499
500         return true;
501     }
502
503     bool update()
504     {
505         if (!ic.generated) {
506             Assembler masm;
507             Value rval = f.regs.sp[-1];
508             Value lval = f.regs.sp[-2];
509             
510             if (rval.isObject() && lval.isObject()) {
511                 generateObjectPath(masm);
512                 ic.generated = true;
513             } else if (rval.isString() && lval.isString()) {
514                 generateStringPath(masm);
515                 ic.generated = true;
516             } else {
517                 return true;
518             }
519
520             return linkForIC(masm);
521         }
522
523         return true;
524     }
525 };
526
527 JSBool JS_FASTCALL
528 ic::Equality(VMFrame &f, ic::EqualityICInfo *ic)
529 {
530     EqualityCompiler cc(f, *ic);
531     if (!cc.update())
532         THROWV(JS_FALSE);
533
534     return ic->stub(f);
535 }
536
537 static void * JS_FASTCALL
538 SlowCallFromIC(VMFrame &f, ic::CallICInfo *ic)
539 {
540     stubs::SlowCall(f, ic->frameSize.getArgc(f));
541     return NULL;
542 }
543
544 static void * JS_FASTCALL
545 SlowNewFromIC(VMFrame &f, ic::CallICInfo *ic)
546 {
547     stubs::SlowNew(f, ic->frameSize.staticArgc());
548     return NULL;
549 }
550
551 /*
552  * Calls have an inline path and an out-of-line path. The inline path is used
553  * in the fastest case: the method has JIT'd code, and |argc == nargs|.
554  * 
555  * The inline path and OOL path are separated by a guard on the identity of
556  * the callee object. This guard starts as NULL and always fails on the first
557  * hit. On the OOL path, the callee is verified to be both a function and a
558  * scripted function. If these conditions hold, |ic::Call| is invoked.
559  *
560  * |ic::Call| first ensures that the callee has JIT code. If it doesn't, the
561  * call to |ic::Call| is patched to a slow path. If it does have JIT'd code,
562  * the following cases can occur:
563  *
564  *   1) args != nargs: The call to |ic::Call| is patched with a dynamically
565  *      generated stub. This stub inlines a path that looks like:
566  *      ----
567  *      push frame
568  *      if (callee is not compiled) {
569  *          Compile(callee);
570  *      }
571  *      call callee->arityLabel
572  *
573  *      The arity label is a special entry point for correcting frames for
574  *      arity mismatches.
575  *
576  *   2) args == nargs, and the inline call site was not patched yet.
577  *      The guard dividing the two paths is patched to guard on the given
578  *      function object identity, and the proceeding call is patched to
579  *      directly call the JIT code.
580  *
581  *   3) args == nargs, and the inline call site was patched already.
582  *      A small stub is created which extends the original guard to also
583  *      guard on the JSFunction lying underneath the function object.
584  *
585  * If the OOL path does not have a scripted function, but does have a
586  * scripted native, then a small stub is generated which inlines the native
587  * invocation.
588  */
589 class CallCompiler : public BaseCompiler
590 {
591     VMFrame &f;
592     CallICInfo &ic;
593     bool callingNew;
594
595   public:
596     CallCompiler(VMFrame &f, CallICInfo &ic, bool callingNew)
597       : BaseCompiler(f.cx), f(f), ic(ic), callingNew(callingNew)
598     {
599     }
600
601     JSC::ExecutablePool *poolForSize(LinkerHelper &linker, CallICInfo::PoolIndex index)
602     {
603         JSC::ExecutablePool *ep = linker.init(f.cx);
604         if (!ep)
605             return NULL;
606         JS_ASSERT(!ic.pools[index]);
607         ic.pools[index] = ep;
608         return ep;
609     }
610
611     void disable(JITScript *jit)
612     {
613         JSC::CodeLocationCall oolCall = ic.slowPathStart.callAtOffset(ic.oolCallOffset);
614         Repatcher repatch(jit);
615         JSC::FunctionPtr fptr = callingNew
616                                 ? JSC::FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, SlowNewFromIC))
617                                 : JSC::FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, SlowCallFromIC));
618         repatch.relink(oolCall, fptr);
619     }
620
621     bool generateFullCallStub(JITScript *from, JSScript *script, uint32 flags)
622     {
623         /*
624          * Create a stub that works with arity mismatches. Like the fast-path,
625          * this allocates a frame on the caller side, but also performs extra
626          * checks for compilability. Perhaps this should be a separate, shared
627          * trampoline, but for now we generate it dynamically.
628          */
629         Assembler masm;
630         InlineFrameAssembler inlFrame(masm, ic, flags);
631         RegisterID t0 = inlFrame.tempRegs.takeAnyReg();
632
633         /* Generate the inline frame creation. */
634         inlFrame.assemble(ic.funGuard.labelAtOffset(ic.joinPointOffset).executableAddress());
635
636         /* funPtrReg is still valid. Check if a compilation is needed. */
637         Address scriptAddr(ic.funPtrReg, offsetof(JSFunction, u) +
638                            offsetof(JSFunction::U::Scripted, script));
639         masm.loadPtr(scriptAddr, t0);
640
641         /*
642          * Test if script->nmap is NULL - same as checking ncode, but faster
643          * here since ncode has two failure modes and we need to load out of
644          * nmap anyway.
645          */
646         size_t offset = callingNew
647                         ? offsetof(JSScript, jitArityCheckCtor)
648                         : offsetof(JSScript, jitArityCheckNormal);
649         masm.loadPtr(Address(t0, offset), t0);
650         Jump hasCode = masm.branchPtr(Assembler::Above, t0, ImmPtr(JS_UNJITTABLE_SCRIPT));
651
652         /* Try and compile. On success we get back the nmap pointer. */
653         masm.storePtr(JSFrameReg, FrameAddress(offsetof(VMFrame, regs.fp)));
654         void *compilePtr = JS_FUNC_TO_DATA_PTR(void *, stubs::CompileFunction);
655         if (ic.frameSize.isStatic()) {
656             masm.move(Imm32(ic.frameSize.staticArgc()), Registers::ArgReg1);
657             masm.fallibleVMCall(compilePtr, script->code, ic.frameSize.staticLocalSlots());
658         } else {
659             masm.load32(FrameAddress(offsetof(VMFrame, u.call.dynamicArgc)), Registers::ArgReg1);
660             masm.fallibleVMCall(compilePtr, script->code, -1);
661         }
662         masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.fp)), JSFrameReg);
663
664         Jump notCompiled = masm.branchTestPtr(Assembler::Zero, Registers::ReturnReg,
665                                               Registers::ReturnReg);
666
667         masm.jump(Registers::ReturnReg);
668
669         hasCode.linkTo(masm.label(), &masm);
670
671         /* Get nmap[ARITY], set argc, call. */
672         if (ic.frameSize.isStatic())
673             masm.move(Imm32(ic.frameSize.staticArgc()), JSParamReg_Argc);
674         else
675             masm.load32(FrameAddress(offsetof(VMFrame, u.call.dynamicArgc)), JSParamReg_Argc);
676         masm.jump(t0);
677
678         LinkerHelper linker(masm);
679         JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_ScriptStub);
680         if (!ep)
681             return false;
682
683         if (!linker.verifyRange(from)) {
684             disable(from);
685             return true;
686         }
687
688         linker.link(notCompiled, ic.slowPathStart.labelAtOffset(ic.slowJoinOffset));
689         JSC::CodeLocationLabel cs = linker.finalize();
690
691         JaegerSpew(JSpew_PICs, "generated CALL stub %p (%d bytes)\n", cs.executableAddress(),
692                    masm.size());
693
694         Repatcher repatch(from);
695         JSC::CodeLocationJump oolJump = ic.slowPathStart.jumpAtOffset(ic.oolJumpOffset);
696         repatch.relink(oolJump, cs);
697
698         return true;
699     }
700
701     bool patchInlinePath(JITScript *from, JSScript *script, JSObject *obj)
702     {
703         JS_ASSERT(ic.frameSize.isStatic());
704         JITScript *jit = script->getJIT(callingNew);
705
706         /* Very fast path. */
707         Repatcher repatch(from);
708
709         if (!repatch.canRelink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset),
710                                JSC::CodeLocationLabel(jit->fastEntry))) {
711             return false;
712         }
713
714         ic.fastGuardedObject = obj;
715
716         repatch.repatch(ic.funGuard, obj);
717         repatch.relink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset),
718                        JSC::CodeLocationLabel(jit->fastEntry));
719
720         JaegerSpew(JSpew_PICs, "patched CALL path %p (obj: %p)\n",
721                    ic.funGuard.executableAddress(), ic.fastGuardedObject);
722
723         return true;
724     }
725
726     bool generateStubForClosures(JITScript *from, JSObject *obj)
727     {
728         JS_ASSERT(ic.frameSize.isStatic());
729
730         /* Slightly less fast path - guard on fun->getFunctionPrivate() instead. */
731         Assembler masm;
732
733         Registers tempRegs;
734         tempRegs.takeReg(ic.funObjReg);
735
736         RegisterID t0 = tempRegs.takeAnyReg();
737
738         /* Guard that it's actually a function object. */
739         Jump claspGuard = masm.testObjClass(Assembler::NotEqual, ic.funObjReg, &js_FunctionClass);
740
741         /* Guard that it's the same function. */
742         JSFunction *fun = obj->getFunctionPrivate();
743         masm.loadObjPrivate(ic.funObjReg, t0);
744         Jump funGuard = masm.branchPtr(Assembler::NotEqual, t0, ImmPtr(fun));
745         Jump done = masm.jump();
746
747         LinkerHelper linker(masm);
748         JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_ClosureStub);
749         if (!ep)
750             return false;
751
752         ic.hasJsFunCheck = true;
753
754         if (!linker.verifyRange(from)) {
755             disable(from);
756             return true;
757         }
758
759         linker.link(claspGuard, ic.slowPathStart);
760         linker.link(funGuard, ic.slowPathStart);
761         linker.link(done, ic.funGuard.labelAtOffset(ic.hotPathOffset));
762         JSC::CodeLocationLabel cs = linker.finalize();
763
764         JaegerSpew(JSpew_PICs, "generated CALL closure stub %p (%d bytes)\n",
765                    cs.executableAddress(), masm.size());
766
767         Repatcher repatch(from);
768         repatch.relink(ic.funJump, cs);
769
770         return true;
771     }
772
773     bool generateNativeStub()
774     {
775         JITScript *jit = f.jit();
776
777         /* Snapshot the frameDepth before SplatApplyArgs modifies it. */
778         uintN initialFrameDepth = f.regs.sp - f.regs.fp->slots();
779
780         /*
781          * SplatApplyArgs has not been called, so we call it here before
782          * potentially touching f.u.call.dynamicArgc.
783          */
784         Value *vp;
785         if (ic.frameSize.isStatic()) {
786             JS_ASSERT(f.regs.sp - f.regs.fp->slots() == (int)ic.frameSize.staticLocalSlots());
787             vp = f.regs.sp - (2 + ic.frameSize.staticArgc());
788         } else {
789             JS_ASSERT(*f.regs.pc == JSOP_FUNAPPLY && GET_ARGC(f.regs.pc) == 2);
790             if (!ic::SplatApplyArgs(f))       /* updates regs.sp */
791                 THROWV(true);
792             vp = f.regs.sp - (2 + f.u.call.dynamicArgc);
793         }
794
795         JSObject *obj;
796         if (!IsFunctionObject(*vp, &obj))
797             return false;
798
799         JSFunction *fun = obj->getFunctionPrivate();
800         if ((!callingNew && !fun->isNative()) || (callingNew && !fun->isConstructor()))
801             return false;
802
803         if (callingNew)
804             vp[1].setMagicWithObjectOrNullPayload(NULL);
805
806         if (!CallJSNative(cx, fun->u.n.native, ic.frameSize.getArgc(f), vp))
807             THROWV(true);
808
809         /* Right now, take slow-path for IC misses or multiple stubs. */
810         if (ic.fastGuardedNative || ic.hasJsFunCheck)
811             return true;
812
813         /* Native MIC needs to warm up first. */
814         if (!ic.hit) {
815             ic.hit = true;
816             return true;
817         }
818
819         /* Generate fast-path for calling this native. */
820         Assembler masm;
821
822         /* Guard on the function object identity, for now. */
823         Jump funGuard = masm.branchPtr(Assembler::NotEqual, ic.funObjReg, ImmPtr(obj));
824
825         /* N.B. After this call, the frame will have a dynamic frame size. */
826         if (ic.frameSize.isDynamic()) {
827             masm.fallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, ic::SplatApplyArgs),
828                                 f.regs.pc, initialFrameDepth);
829         }
830
831         Registers tempRegs;
832 #ifndef JS_CPU_X86
833         tempRegs.takeReg(Registers::ArgReg0);
834         tempRegs.takeReg(Registers::ArgReg1);
835         tempRegs.takeReg(Registers::ArgReg2);
836 #endif
837         RegisterID t0 = tempRegs.takeAnyReg();
838
839         /* Store pc. */
840         masm.storePtr(ImmPtr(cx->regs->pc),
841                        FrameAddress(offsetof(VMFrame, regs.pc)));
842
843         /* Store sp (if not already set by ic::SplatApplyArgs). */
844         if (ic.frameSize.isStatic()) {
845             uint32 spOffset = sizeof(JSStackFrame) + initialFrameDepth * sizeof(Value);
846             masm.addPtr(Imm32(spOffset), JSFrameReg, t0);
847             masm.storePtr(t0, FrameAddress(offsetof(VMFrame, regs.sp)));
848         }
849
850         /* Store fp. */
851         masm.storePtr(JSFrameReg, FrameAddress(offsetof(VMFrame, regs.fp)));
852
853         /* Grab cx. */
854 #ifdef JS_CPU_X86
855         RegisterID cxReg = tempRegs.takeAnyReg();
856 #else
857         RegisterID cxReg = Registers::ArgReg0;
858 #endif
859         masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), cxReg);
860
861         /* Compute vp. */
862 #ifdef JS_CPU_X86
863         RegisterID vpReg = t0;
864 #else
865         RegisterID vpReg = Registers::ArgReg2;
866 #endif
867         MaybeRegisterID argcReg;
868         if (ic.frameSize.isStatic()) {
869             uint32 vpOffset = sizeof(JSStackFrame) + (vp - f.regs.fp->slots()) * sizeof(Value);
870             masm.addPtr(Imm32(vpOffset), JSFrameReg, vpReg);
871         } else {
872             argcReg = tempRegs.takeAnyReg();
873             masm.load32(FrameAddress(offsetof(VMFrame, u.call.dynamicArgc)), argcReg.reg());
874             masm.loadPtr(FrameAddress(offsetof(VMFrame, regs.sp)), vpReg);
875
876             /* vpOff = (argc + 2) * sizeof(Value) */
877             RegisterID vpOff = tempRegs.takeAnyReg();
878             masm.move(argcReg.reg(), vpOff);
879             masm.add32(Imm32(2), vpOff);  /* callee, this */
880             JS_STATIC_ASSERT(sizeof(Value) == 8);
881             masm.lshift32(Imm32(3), vpOff);
882             masm.subPtr(vpOff, vpReg);
883
884             tempRegs.putReg(vpOff);
885         }
886
887         /* Mark vp[1] as magic for |new|. */
888         if (callingNew) {
889             Value v;
890             v.setMagicWithObjectOrNullPayload(NULL);
891             masm.storeValue(v, Address(vpReg, sizeof(Value)));
892         }
893
894         masm.setupABICall(Registers::NormalCall, 3);
895         masm.storeArg(2, vpReg);
896         if (ic.frameSize.isStatic())
897             masm.storeArg(1, Imm32(ic.frameSize.staticArgc()));
898         else
899             masm.storeArg(1, argcReg.reg());
900         masm.storeArg(0, cxReg);
901         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, fun->u.n.native), false);
902
903         Jump hasException = masm.branchTest32(Assembler::Zero, Registers::ReturnReg,
904                                               Registers::ReturnReg);
905         
906
907         Jump done = masm.jump();
908
909         /* Move JaegerThrowpoline into register for very far jump on x64. */
910         hasException.linkTo(masm.label(), &masm);
911         masm.throwInJIT();
912
913         LinkerHelper linker(masm);
914         JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_NativeStub);
915         if (!ep)
916             THROWV(true);
917
918         ic.fastGuardedNative = obj;
919
920         if (!linker.verifyRange(jit)) {
921             disable(jit);
922             return true;
923         }
924
925         linker.link(done, ic.slowPathStart.labelAtOffset(ic.slowJoinOffset));
926         linker.link(funGuard, ic.slowPathStart);
927         JSC::CodeLocationLabel cs = linker.finalize();
928
929         JaegerSpew(JSpew_PICs, "generated native CALL stub %p (%d bytes)\n",
930                    cs.executableAddress(), masm.size());
931
932         Repatcher repatch(jit);
933         repatch.relink(ic.funJump, cs);
934
935         return true;
936     }
937
938     void *update()
939     {
940         JITScript *jit = f.jit();
941
942         stubs::UncachedCallResult ucr;
943         if (callingNew)
944             stubs::UncachedNewHelper(f, ic.frameSize.staticArgc(), &ucr);
945         else
946             stubs::UncachedCallHelper(f, ic.frameSize.getArgc(f), &ucr);
947
948         // If the function cannot be jitted (generally unjittable or empty script),
949         // patch this site to go to a slow path always.
950         if (!ucr.codeAddr) {
951             if (ucr.unjittable)
952                 disable(jit);
953             return NULL;
954         }
955             
956         JSFunction *fun = ucr.fun;
957         JS_ASSERT(fun);
958         JSScript *script = fun->script();
959         JS_ASSERT(script);
960         JSObject *callee = ucr.callee;
961         JS_ASSERT(callee);
962
963         uint32 flags = callingNew ? JSFRAME_CONSTRUCTING : 0;
964
965         if (!ic.hit) {
966             ic.hit = true;
967             return ucr.codeAddr;
968         }
969
970         if (!ic.frameSize.isStatic() || ic.frameSize.staticArgc() != fun->nargs) {
971             if (!generateFullCallStub(jit, script, flags))
972                 THROWV(NULL);
973         } else {
974             if (!ic.fastGuardedObject && patchInlinePath(jit, script, callee)) {
975                 // Nothing, done.
976             } else if (ic.fastGuardedObject &&
977                        !ic.hasJsFunCheck &&
978                        !ic.fastGuardedNative &&
979                        ic.fastGuardedObject->getFunctionPrivate() == fun) {
980                 /*
981                  * Note: Multiple "function guard" stubs are not yet
982                  * supported, thus the fastGuardedNative check.
983                  */
984                 if (!generateStubForClosures(jit, callee))
985                     THROWV(NULL);
986             } else {
987                 if (!generateFullCallStub(jit, script, flags))
988                     THROWV(NULL);
989             }
990         }
991
992         return ucr.codeAddr;
993     }
994 };
995
996 void * JS_FASTCALL
997 ic::Call(VMFrame &f, CallICInfo *ic)
998 {
999     CallCompiler cc(f, *ic, false);
1000     return cc.update();
1001 }
1002
1003 void * JS_FASTCALL
1004 ic::New(VMFrame &f, CallICInfo *ic)
1005 {
1006     CallCompiler cc(f, *ic, true);
1007     return cc.update();
1008 }
1009
1010 void JS_FASTCALL
1011 ic::NativeCall(VMFrame &f, CallICInfo *ic)
1012 {
1013     CallCompiler cc(f, *ic, false);
1014     if (!cc.generateNativeStub())
1015         stubs::SlowCall(f, ic->frameSize.getArgc(f));
1016 }
1017
1018 void JS_FASTCALL
1019 ic::NativeNew(VMFrame &f, CallICInfo *ic)
1020 {
1021     CallCompiler cc(f, *ic, true);
1022     if (!cc.generateNativeStub())
1023         stubs::SlowNew(f, ic->frameSize.staticArgc());
1024 }
1025
1026 static inline bool
1027 BumpStack(VMFrame &f, uintN inc)
1028 {
1029     static const unsigned MANY_ARGS = 1024;
1030     static const unsigned MIN_SPACE = 500;
1031
1032     /* If we are not passing many args, treat this as a normal call. */
1033     if (inc < MANY_ARGS) {
1034         if (f.regs.sp + inc < f.stackLimit)
1035             return true;
1036         StackSpace &stack = f.cx->stack();
1037         if (!stack.bumpCommitAndLimit(f.entryfp, f.regs.sp, inc, &f.stackLimit)) {
1038             js_ReportOverRecursed(f.cx);
1039             return false;
1040         }
1041         return true;
1042     }
1043
1044     /*
1045      * The purpose of f.stackLimit is to catch over-recursion based on
1046      * assumptions about the average frame size. 'apply' with a large number of
1047      * arguments breaks these assumptions and can result in premature "out of
1048      * script quota" errors. Normally, apply will go through js::Invoke, which
1049      * effectively starts a fresh stackLimit. Here, we bump f.stackLimit,
1050      * if necessary, to allow for this 'apply' call, and a reasonable number of
1051      * subsequent calls, to succeed without hitting the stackLimit. In theory,
1052      * this a recursive chain containing apply to circumvent the stackLimit.
1053      * However, since each apply call must consume at least MANY_ARGS slots,
1054      * this sequence will quickly reach the end of the stack and OOM.
1055      */
1056
1057     uintN incWithSpace = inc + MIN_SPACE;
1058     Value *bumpedWithSpace = f.regs.sp + incWithSpace;
1059     if (bumpedWithSpace < f.stackLimit)
1060         return true;
1061
1062     StackSpace &stack = f.cx->stack();
1063     if (stack.bumpCommitAndLimit(f.entryfp, f.regs.sp, incWithSpace, &f.stackLimit))
1064         return true;
1065
1066     if (!stack.ensureSpace(f.cx, f.regs.sp, incWithSpace))
1067         return false;
1068     f.stackLimit = bumpedWithSpace;
1069     return true;
1070 }
1071
1072 /*
1073  * SplatApplyArgs is only called for expressions of the form |f.apply(x, y)|.
1074  * Additionally, the callee has already been checked to be the native apply.
1075  * All successful paths through SplatApplyArgs must set f.u.call.dynamicArgc
1076  * and f.regs.sp.
1077  */
1078 JSBool JS_FASTCALL
1079 ic::SplatApplyArgs(VMFrame &f)
1080 {
1081     JSContext *cx = f.cx;
1082     JS_ASSERT(GET_ARGC(f.regs.pc) == 2);
1083
1084     /*
1085      * The lazyArgsObj flag indicates an optimized call |f.apply(x, arguments)|
1086      * where the args obj has not been created or pushed on the stack. Thus,
1087      * if lazyArgsObj is set, the stack for |f.apply(x, arguments)| is:
1088      *
1089      *  | Function.prototype.apply | f | x |
1090      *
1091      * Otherwise, if !lazyArgsObj, the stack is a normal 2-argument apply:
1092      *
1093      *  | Function.prototype.apply | f | x | arguments |
1094      */
1095     if (f.u.call.lazyArgsObj) {
1096         Value *vp = f.regs.sp - 3;
1097         JS_ASSERT(JS_CALLEE(cx, vp).toObject().getFunctionPrivate()->u.n.native == js_fun_apply);
1098
1099         JSStackFrame *fp = f.regs.fp;
1100         if (!fp->hasOverriddenArgs() &&
1101             (!fp->hasArgsObj() ||
1102              (fp->hasArgsObj() && !fp->argsObj().isArgsLengthOverridden() &&
1103               !js_PrototypeHasIndexedProperties(cx, &fp->argsObj())))) {
1104
1105             uintN n = fp->numActualArgs();
1106             if (!BumpStack(f, n))
1107                 THROWV(false);
1108             f.regs.sp += n;
1109
1110             Value *argv = JS_ARGV(cx, vp + 1 /* vp[1]'s argv */);
1111             if (fp->hasArgsObj())
1112                 fp->forEachCanonicalActualArg(CopyNonHoleArgsTo(&fp->argsObj(), argv));
1113             else
1114                 fp->forEachCanonicalActualArg(CopyTo(argv));
1115
1116             f.u.call.dynamicArgc = n;
1117             return true;
1118         }
1119
1120         /*
1121          * Can't optimize; push the arguments object so that the stack matches
1122          * the !lazyArgsObj stack state described above.
1123          */
1124         f.regs.sp++;
1125         if (!js_GetArgsValue(cx, fp, &vp[3]))
1126             THROWV(false);
1127     }
1128
1129     Value *vp = f.regs.sp - 4;
1130     JS_ASSERT(JS_CALLEE(cx, vp).toObject().getFunctionPrivate()->u.n.native == js_fun_apply);
1131
1132     /*
1133      * This stub should mimic the steps taken by js_fun_apply. Step 1 and part
1134      * of Step 2 have already been taken care of by calling jit code.
1135      */
1136
1137     /* Step 2 (part 2). */
1138     if (vp[3].isNullOrUndefined()) {
1139         f.regs.sp--;
1140         f.u.call.dynamicArgc = 0;
1141         return true;
1142     }
1143
1144     /* Step 3. */
1145     if (!vp[3].isObject()) {
1146         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, js_apply_str);
1147         THROWV(false);
1148     }
1149
1150     /* Steps 4-5. */
1151     JSObject *aobj = &vp[3].toObject();
1152     jsuint length;
1153     if (!js_GetLengthProperty(cx, aobj, &length))
1154         THROWV(false);
1155
1156     JS_ASSERT(!JS_ON_TRACE(cx));
1157
1158     /* Step 6. */
1159     uintN n = uintN(JS_MIN(length, JS_ARGS_LENGTH_MAX));
1160
1161     intN delta = n - 1;
1162     if (delta > 0 && !BumpStack(f, delta))
1163         THROWV(false);
1164     f.regs.sp += delta;
1165
1166     /* Steps 7-8. */
1167     if (!GetElements(cx, aobj, n, f.regs.sp - n))
1168         THROWV(false);
1169
1170     f.u.call.dynamicArgc = n;
1171     return true;
1172 }
1173
1174 void
1175 JITScript::purgeMICs()
1176 {
1177     if (!nGetGlobalNames || !nSetGlobalNames)
1178         return;
1179
1180     Repatcher repatch(this);
1181
1182     ic::GetGlobalNameIC *getGlobalNames_ = getGlobalNames();
1183     for (uint32 i = 0; i < nGetGlobalNames; i++) {
1184         ic::GetGlobalNameIC &ic = getGlobalNames_[i];
1185         JSC::CodeLocationDataLabel32 label = ic.fastPathStart.dataLabel32AtOffset(ic.shapeOffset);
1186         repatch.repatch(label, int(JSObjectMap::INVALID_SHAPE));
1187     }
1188
1189     ic::SetGlobalNameIC *setGlobalNames_ = setGlobalNames();
1190     for (uint32 i = 0; i < nSetGlobalNames; i++) {
1191         ic::SetGlobalNameIC &ic = setGlobalNames_[i];
1192         ic.patchInlineShapeGuard(repatch, int32(JSObjectMap::INVALID_SHAPE));
1193
1194         if (ic.hasExtraStub) {
1195             Repatcher repatcher(ic.extraStub);
1196             ic.patchExtraShapeGuard(repatcher, int32(JSObjectMap::INVALID_SHAPE));
1197         }
1198     }
1199 }
1200
1201 void
1202 ic::PurgeMICs(JSContext *cx, JSScript *script)
1203 {
1204     /* MICs are purged during GC to handle changing shapes. */
1205     JS_ASSERT(cx->runtime->gcRegenShapes);
1206
1207     if (script->jitNormal)
1208         script->jitNormal->purgeMICs();
1209     if (script->jitCtor)
1210         script->jitCtor->purgeMICs();
1211 }
1212
1213 void
1214 JITScript::nukeScriptDependentICs()
1215 {
1216     if (!nCallICs)
1217         return;
1218
1219     Repatcher repatcher(this);
1220
1221     ic::CallICInfo *callICs_ = callICs();
1222     for (uint32 i = 0; i < nCallICs; i++) {
1223         ic::CallICInfo &ic = callICs_[i];
1224         if (!ic.fastGuardedObject)
1225             continue;
1226         repatcher.repatch(ic.funGuard, NULL);
1227         repatcher.relink(ic.funJump, ic.slowPathStart);
1228         ic.releasePool(CallICInfo::Pool_ClosureStub);
1229         ic.fastGuardedObject = NULL;
1230         ic.hasJsFunCheck = false;
1231     }
1232 }
1233
1234 void
1235 JITScript::sweepCallICs(JSContext *cx, bool purgeAll)
1236 {
1237     Repatcher repatcher(this);
1238
1239     /*
1240      * If purgeAll is set, purge stubs in the script except those covered by PurgePICs
1241      * (which is always called during GC). We want to remove references which can keep
1242      * alive pools that we are trying to destroy (see JSCompartment::sweep).
1243      */
1244
1245     ic::CallICInfo *callICs_ = callICs();
1246     for (uint32 i = 0; i < nCallICs; i++) {
1247         ic::CallICInfo &ic = callICs_[i];
1248
1249         /*
1250          * If the object is unreachable, we're guaranteed not to be currently
1251          * executing a stub generated by a guard on that object. This lets us
1252          * precisely GC call ICs while keeping the identity guard safe.
1253          */
1254         bool fastFunDead = ic.fastGuardedObject &&
1255             (purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedObject));
1256         bool nativeDead = ic.fastGuardedNative &&
1257             (purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedNative));
1258
1259         /*
1260          * There are three conditions where we need to relink:
1261          * (1) purgeAll is true.
1262          * (2) The native is dead, since it always has a stub.
1263          * (3) The fastFun is dead *and* there is a closure stub.
1264          *
1265          * Note although both objects can be non-NULL, there can only be one
1266          * of [closure, native] stub per call IC.
1267          */
1268         if (purgeAll || nativeDead || (fastFunDead && ic.hasJsFunCheck)) {
1269             repatcher.relink(ic.funJump, ic.slowPathStart);
1270             ic.hit = false;
1271         }
1272
1273         if (fastFunDead) {
1274             repatcher.repatch(ic.funGuard, NULL);
1275             ic.releasePool(CallICInfo::Pool_ClosureStub);
1276             ic.hasJsFunCheck = false;
1277             ic.fastGuardedObject = NULL;
1278         }
1279
1280         if (nativeDead) {
1281             ic.releasePool(CallICInfo::Pool_NativeStub);
1282             ic.fastGuardedNative = NULL;
1283         }
1284
1285         if (purgeAll) {
1286             ic.releasePool(CallICInfo::Pool_ScriptStub);
1287             JSC::CodeLocationJump oolJump = ic.slowPathStart.jumpAtOffset(ic.oolJumpOffset);
1288             JSC::CodeLocationLabel icCall = ic.slowPathStart.labelAtOffset(ic.icCallOffset);
1289             repatcher.relink(oolJump, icCall);
1290         }
1291     }
1292
1293     if (purgeAll) {
1294         /* Purge ICs generating stubs into execPools. */
1295         uint32 released = 0;
1296
1297         ic::EqualityICInfo *equalityICs_ = equalityICs();
1298         for (uint32 i = 0; i < nEqualityICs; i++) {
1299             ic::EqualityICInfo &ic = equalityICs_[i];
1300             if (!ic.generated)
1301                 continue;
1302
1303             JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic::Equality));
1304             repatcher.relink(ic.stubCall, fptr);
1305             repatcher.relink(ic.jumpToStub, ic.stubEntry);
1306
1307             ic.generated = false;
1308             released++;
1309         }
1310
1311         ic::SetGlobalNameIC *setGlobalNames_ = setGlobalNames();
1312         for (uint32 i = 0; i < nSetGlobalNames; i ++) {
1313             ic::SetGlobalNameIC &ic = setGlobalNames_[i];
1314             if (!ic.hasExtraStub)
1315                 continue;
1316             repatcher.relink(ic.fastPathStart.jumpAtOffset(ic.inlineShapeJump), ic.slowPathStart);
1317             ic.hasExtraStub = false;
1318             released++;
1319         }
1320
1321         JS_ASSERT(released == execPools.length());
1322         for (uint32 i = 0; i < released; i++)
1323             execPools[i]->release();
1324         execPools.clear();
1325     }
1326 }
1327
1328 void
1329 ic::SweepCallICs(JSContext *cx, JSScript *script, bool purgeAll)
1330 {
1331     if (script->jitNormal)
1332         script->jitNormal->sweepCallICs(cx, purgeAll);
1333     if (script->jitCtor)
1334         script->jitCtor->sweepCallICs(cx, purgeAll);
1335 }
1336
1337 #endif /* JS_MONOIC */
1338