Bug 585341: fix non-default-getter on with in IC. (r=dvander)
[mozilla-central.git] / js / src / methodjit / PolyIC.cpp
blob309d7195c6e12c952c11f7debd757594cefe8dc6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
23 * Contributor(s):
24 * David Mandelin <dmandelin@mozilla.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
39 #include "PolyIC.h"
40 #include "StubCalls.h"
41 #include "CodeGenIncludes.h"
42 #include "StubCalls-inl.h"
43 #include "assembler/assembler/LinkBuffer.h"
44 #include "jsscope.h"
45 #include "jsnum.h"
46 #include "jsobjinlines.h"
47 #include "jsscopeinlines.h"
48 #include "jspropertycache.h"
49 #include "jspropertycacheinlines.h"
50 #include "jsautooplen.h"
52 #if defined JS_POLYIC
54 using namespace js;
55 using namespace js::mjit;
56 using namespace js::mjit::ic;
58 /* Rough over-estimate of how much memory we need to unprotect. */
59 static const uint32 INLINE_PATH_LENGTH = 64;
61 typedef JSC::FunctionPtr FunctionPtr;
62 typedef JSC::RepatchBuffer RepatchBuffer;
63 typedef JSC::CodeBlock CodeBlock;
64 typedef JSC::CodeLocationLabel CodeLocationLabel;
65 typedef JSC::JITCode JITCode;
66 typedef JSC::MacroAssembler::Jump Jump;
67 typedef JSC::MacroAssembler::RegisterID RegisterID;
68 typedef JSC::MacroAssembler::Label Label;
69 typedef JSC::MacroAssembler::Imm32 Imm32;
70 typedef JSC::MacroAssembler::ImmPtr ImmPtr;
71 typedef JSC::MacroAssembler::Address Address;
72 typedef JSC::ReturnAddressPtr ReturnAddressPtr;
73 typedef JSC::MacroAssemblerCodePtr MacroAssemblerCodePtr;
75 class AutoPropertyDropper
77 JSContext *cx;
78 JSObject *holder;
79 JSProperty *prop;
81 public:
82 AutoPropertyDropper(JSContext *cx, JSObject *obj, JSProperty *prop)
83 : cx(cx), holder(obj), prop(prop)
85 JS_ASSERT(prop);
88 ~AutoPropertyDropper()
90 holder->dropProperty(cx, prop);
94 class PICStubCompiler
96 protected:
97 const char *type;
98 VMFrame &f;
99 JSScript *script;
100 ic::PICInfo &pic;
102 public:
103 PICStubCompiler(const char *type, VMFrame &f, JSScript *script, ic::PICInfo &pic)
104 : type(type), f(f), script(script), pic(pic)
107 bool disable(const char *reason, VoidStub stub)
109 return disable(reason, JS_FUNC_TO_DATA_PTR(void *, stub));
112 bool disable(const char *reason, VoidStubUInt32 stub)
114 return disable(reason, JS_FUNC_TO_DATA_PTR(void *, stub));
117 bool disable(const char *reason, void *stub)
119 spew("disabled", reason);
120 JITCode jitCode(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
121 CodeBlock codeBlock(jitCode);
122 RepatchBuffer repatcher(&codeBlock);
123 ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress());
124 MacroAssemblerCodePtr target(stub);
125 repatcher.relinkCallerToTrampoline(retPtr, target);
126 return true;
129 JSC::ExecutablePool *getExecPool(size_t size)
131 mjit::ThreadData *jd = &JS_METHODJIT_DATA(f.cx);
132 return jd->execPool->poolForSize(size);
135 protected:
136 void spew(const char *event, const char *op)
138 JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n",
139 type, event, op, script->filename,
140 js_FramePCToLineNumber(f.cx, f.fp));
144 class PICRepatchBuffer : public JSC::RepatchBuffer
146 ic::PICInfo &pic;
147 JSC::CodeLocationLabel label;
149 public:
150 PICRepatchBuffer(ic::PICInfo &ic, JSC::CodeLocationLabel path)
151 : JSC::RepatchBuffer(path.executableAddress(), INLINE_PATH_LENGTH),
152 pic(ic), label(path)
155 void relink(int32 offset, JSC::CodeLocationLabel target) {
156 JSC::RepatchBuffer::relink(label.jumpAtOffset(offset), target);
160 class SetPropCompiler : public PICStubCompiler
162 JSObject *obj;
163 JSAtom *atom;
164 VoidStubUInt32 stub;
166 static int32 dslotsLoadOffset(ic::PICInfo &pic) {
167 #if defined JS_NUNBOX32
168 if (pic.u.vr.isConstant)
169 return SETPROP_DSLOTS_BEFORE_CONSTANT;
170 if (pic.u.vr.u.s.isTypeKnown)
171 return SETPROP_DSLOTS_BEFORE_KTYPE;
172 return SETPROP_DSLOTS_BEFORE_DYNAMIC;
173 #elif defined JS_PUNBOX64
174 return pic.labels.setprop.dslotsLoadOffset;
175 #endif
178 #if defined JS_NUNBOX32
179 inline int32 inlineTypeOffset() {
180 if (pic.u.vr.isConstant)
181 return SETPROP_INLINE_STORE_CONST_TYPE;
182 if (pic.u.vr.u.s.isTypeKnown)
183 return SETPROP_INLINE_STORE_KTYPE_TYPE;
184 return SETPROP_INLINE_STORE_DYN_TYPE;
186 #endif
188 #if defined JS_NUNBOX32
189 inline int32 inlineDataOffset() {
190 if (pic.u.vr.isConstant)
191 return SETPROP_INLINE_STORE_CONST_DATA;
192 if (pic.u.vr.u.s.isTypeKnown)
193 return SETPROP_INLINE_STORE_KTYPE_DATA;
194 return SETPROP_INLINE_STORE_DYN_DATA;
196 #endif
198 static int32 inlineShapeOffset(ic::PICInfo &pic) {
199 #if defined JS_NUNBOX32
200 return SETPROP_INLINE_SHAPE_OFFSET;
201 #elif defined JS_PUNBOX64
202 return pic.labels.setprop.inlineShapeOffset;
203 #endif
206 static int32 inlineShapeJump(ic::PICInfo &pic) {
207 #if defined JS_NUNBOX32
208 return SETPROP_INLINE_SHAPE_JUMP;
209 #elif defined JS_PUNBOX64
210 return inlineShapeOffset(pic) + SETPROP_INLINE_SHAPE_JUMP;
211 #endif
214 inline int32 dslotsLoadOffset() {
215 return dslotsLoadOffset(pic);
218 inline int32 inlineShapeOffset() {
219 return inlineShapeOffset(pic);
222 inline int32 inlineShapeJump() {
223 return inlineShapeJump(pic);
226 public:
227 SetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom,
228 VoidStubUInt32 stub)
229 : PICStubCompiler("setprop", f, script, pic), obj(obj), atom(atom), stub(stub)
232 bool disable(const char *reason)
234 return PICStubCompiler::disable(reason, stub);
237 static void reset(ic::PICInfo &pic)
239 RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
240 repatcher.repatchLEAToLoadPtr(pic.storeBack.instructionAtOffset(dslotsLoadOffset(pic)));
241 repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(
242 pic.shapeGuard + inlineShapeOffset(pic)),
243 int32(JSScope::INVALID_SHAPE));
244 repatcher.relink(pic.fastPathStart.jumpAtOffset(
245 pic.shapeGuard + inlineShapeJump(pic)),
246 pic.slowPathStart);
248 RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
249 ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress());
250 MacroAssemblerCodePtr target(JS_FUNC_TO_DATA_PTR(void *, ic::SetProp));
251 repatcher.relinkCallerToTrampoline(retPtr, target);
254 bool patchInline(JSScopeProperty *sprop)
256 JS_ASSERT(!pic.inlinePathPatched);
257 JaegerSpew(JSpew_PICs, "patch setprop inline at %p\n", pic.fastPathStart.executableAddress());
259 PICRepatchBuffer repatcher(pic, pic.fastPathStart);
261 int32 offset;
262 if (sprop->slot < JS_INITIAL_NSLOTS) {
263 JSC::CodeLocationInstruction istr;
264 istr = pic.storeBack.instructionAtOffset(dslotsLoadOffset());
265 repatcher.repatchLoadPtrToLEA(istr);
268 // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
269 // To: | lea fslots, [obj + DSLOTS_OFFSET]
271 // Because the offset is wrong, it's necessary to correct it
272 // below.
274 int32 diff = int32(offsetof(JSObject, fslots)) -
275 int32(offsetof(JSObject, dslots));
276 JS_ASSERT(diff != 0);
277 offset = (int32(sprop->slot) * sizeof(Value)) + diff;
278 } else {
279 offset = (sprop->slot - JS_INITIAL_NSLOTS) * sizeof(Value);
282 uint32 shapeOffs = pic.shapeGuard + inlineShapeOffset();
283 repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(shapeOffs), obj->shape());
284 #if defined JS_NUNBOX32
285 repatcher.repatch(pic.storeBack.dataLabel32AtOffset(inlineTypeOffset()), offset + 4);
286 repatcher.repatch(pic.storeBack.dataLabel32AtOffset(inlineDataOffset()), offset);
287 #elif defined JS_PUNBOX64
288 repatcher.repatch(pic.storeBack.dataLabel32AtOffset(SETPROP_INLINE_STORE_VALUE), offset);
289 #endif
291 pic.inlinePathPatched = true;
293 return true;
296 void patchPreviousToHere(PICRepatchBuffer &repatcher, CodeLocationLabel cs)
298 // Patch either the inline fast path or a generated stub. The stub
299 // omits the prefix of the inline fast path that loads the shape, so
300 // the offsets are different.
301 int shapeGuardJumpOffset;
302 if (pic.stubsGenerated)
303 #if defined JS_NUNBOX32
304 shapeGuardJumpOffset = SETPROP_STUB_SHAPE_JUMP;
305 #elif defined JS_PUNBOX64
306 shapeGuardJumpOffset = pic.labels.setprop.stubShapeJump;
307 #endif
308 else
309 shapeGuardJumpOffset = pic.shapeGuard + inlineShapeJump();
310 repatcher.relink(shapeGuardJumpOffset, cs);
313 // :TODO: x64 -- implement more efficient version.
314 void emitStore(Assembler &masm, Address address)
316 if (pic.u.vr.isConstant) {
317 masm.storeValue(Valueify(pic.u.vr.u.v), address);
318 } else {
319 if (pic.u.vr.u.s.isTypeKnown)
320 masm.storeTypeTag(ImmType(pic.u.vr.u.s.type.knownType), address);
321 else
322 masm.storeTypeTag(pic.u.vr.u.s.type.reg, address);
323 masm.storePayload(pic.u.vr.u.s.data, address);
327 bool generateStub(JSScopeProperty *sprop)
329 Assembler masm;
331 // Shape guard.
332 if (pic.shapeNeedsRemat()) {
333 masm.loadShape(pic.objReg, pic.shapeReg);
334 pic.shapeRegHasBaseShape = true;
337 Label start = masm.label();
338 Jump shapeMismatch = masm.branch32_force32(Assembler::NotEqual, pic.shapeReg,
339 Imm32(obj->shape()));
341 #if defined JS_NUNBOX32
342 DBGLABEL(dbgStubShapeJump);
343 JS_ASSERT(masm.differenceBetween(start, dbgStubShapeJump) == SETPROP_STUB_SHAPE_JUMP);
344 #elif defined JS_PUNBOX64
345 Label stubShapeJumpLabel = masm.label();
346 #endif
348 JSScope *scope = obj->scope();
349 JS_ASSERT_IF(!sprop->hasDefaultSetter(), obj->getClass() == &js_CallClass);
351 Jump rebrand;
352 Jump skipOver;
353 if (sprop->hasDefaultSetter()) {
354 Address address(pic.objReg, offsetof(JSObject, fslots) + sprop->slot * sizeof(Value));
355 if (sprop->slot >= JS_INITIAL_NSLOTS) {
356 masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg);
357 address = Address(pic.objReg, (sprop->slot - JS_INITIAL_NSLOTS) * sizeof(Value));
360 // If the scope is branded, or has a method barrier. It's now necessary
361 // to guard that we're not overwriting a function-valued property.
362 if (scope->brandedOrHasMethodBarrier()) {
363 masm.loadTypeTag(address, pic.shapeReg);
364 Jump skip = masm.testObject(Assembler::NotEqual, pic.shapeReg);
365 masm.loadPayload(address, pic.shapeReg);
366 rebrand = masm.testFunction(Assembler::Equal, pic.shapeReg);
367 skip.linkTo(masm.label(), &masm);
368 pic.shapeRegHasBaseShape = false;
371 emitStore(masm, address);
372 } else {
373 uint16 slot = uint16(sprop->shortid);
375 /* Guard that the call object has a frame. */
376 masm.loadFunctionPrivate(pic.objReg, pic.shapeReg);
377 Jump escapedFrame = masm.branchTestPtr(Assembler::Zero, pic.shapeReg, pic.shapeReg);
380 uint32 bias = 0;
381 if (sprop->setterOp() == SetCallArg)
382 masm.loadPtr(Address(pic.shapeReg, offsetof(JSStackFrame, argv)), pic.shapeReg);
383 else
384 bias = sizeof(JSStackFrame);
385 Address address(pic.shapeReg, bias + slot * sizeof(Value));
386 emitStore(masm, address);
387 skipOver = masm.jump();
390 escapedFrame.linkTo(masm.label(), &masm);
392 if (sprop->setterOp() == SetCallVar) {
393 JSFunction *fun = js_GetCallObjectFunction(obj);
394 slot += fun->nargs;
396 masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg);
398 Address dslot(pic.objReg, slot * sizeof(Value));
399 emitStore(masm, dslot);
402 pic.shapeRegHasBaseShape = false;
404 Jump done = masm.jump();
406 JSC::ExecutablePool *ep = getExecPool(masm.size());
407 if (!ep || !pic.execPools.append(ep)) {
408 if (ep)
409 ep->release();
410 js_ReportOutOfMemory(f.cx);
411 return false;
414 JSC::LinkBuffer buffer(&masm, ep);
415 buffer.link(shapeMismatch, pic.slowPathStart);
416 buffer.link(done, pic.storeBack);
417 if (sprop->hasDefaultSetter() && (scope->brandedOrHasMethodBarrier()))
418 buffer.link(rebrand, pic.slowPathStart);
419 if (!sprop->hasDefaultSetter())
420 buffer.link(skipOver, pic.storeBack);
421 CodeLocationLabel cs = buffer.finalizeCodeAddendum();
422 JaegerSpew(JSpew_PICs, "generate setprop stub %p %d %d at %p\n",
423 (void*)&pic,
424 obj->shape(),
425 pic.stubsGenerated,
426 cs.executableAddress());
428 PICRepatchBuffer repatcher(pic, pic.lastPathStart());
430 // This function can patch either the inline fast path for a generated
431 // stub. The stub omits the prefix of the inline fast path that loads
432 // the shape, so the offsets are different.
433 patchPreviousToHere(repatcher, cs);
435 pic.stubsGenerated++;
436 pic.lastStubStart = buffer.locationOf(start);
438 #if defined JS_PUNBOX64
439 pic.labels.setprop.stubShapeJump = masm.differenceBetween(start, stubShapeJumpLabel);
440 #endif
442 if (pic.stubsGenerated == MAX_PIC_STUBS)
443 disable("max stubs reached");
445 return true;
448 bool update()
450 if (!pic.hit) {
451 spew("first hit", "nop");
452 pic.hit = true;
453 return true;
456 JSObject *aobj = js_GetProtoIfDenseArray(obj);
457 if (!aobj->isNative())
458 return disable("non-native");
460 JSObject *holder;
461 JSProperty *prop = NULL;
462 if (!aobj->lookupProperty(f.cx, ATOM_TO_JSID(atom), &holder, &prop))
463 return false;
464 if (!prop)
465 return disable("property not found");
467 AutoPropertyDropper dropper(f.cx, holder, prop);
468 if (holder != obj)
469 return disable("property not on object");
471 JSScope *scope = obj->scope();
472 JSScopeProperty *sprop = (JSScopeProperty *)prop;
473 if (!sprop->writable())
474 return disable("readonly");
475 if (scope->sealed() && !sprop->hasSlot())
476 return disable("what does this even mean");
478 if (sprop->hasDefaultSetter()) {
479 if (!SPROP_HAS_VALID_SLOT(sprop, scope))
480 return disable("invalid slot");
481 } else {
482 if (sprop->hasSetterValue())
483 return disable("scripted setter");
484 if (sprop->setterOp() != SetCallArg &&
485 sprop->setterOp() != SetCallVar) {
486 return disable("setter");
490 JS_ASSERT(obj == holder);
491 if (!pic.inlinePathPatched &&
492 !scope->brandedOrHasMethodBarrier() &&
493 sprop->hasDefaultSetter() &&
494 !obj->isDenseArray()) {
495 return patchInline(sprop);
498 return generateStub(sprop);
502 class GetPropCompiler : public PICStubCompiler
504 JSObject *obj;
505 JSAtom *atom;
506 void *stub;
507 int lastStubSecondShapeGuard;
509 static int32 inlineShapeOffset(ic::PICInfo &pic) {
510 #if defined JS_NUNBOX32
511 return GETPROP_INLINE_SHAPE_OFFSET;
512 #elif defined JS_PUNBOX64
513 return pic.labels.getprop.inlineShapeOffset;
514 #endif
517 inline int32 inlineShapeOffset() {
518 return inlineShapeOffset(pic);
521 static int32 inlineShapeJump(ic::PICInfo &pic) {
522 #if defined JS_NUNBOX32
523 return GETPROP_INLINE_SHAPE_JUMP;
524 #elif defined JS_PUNBOX64
525 return inlineShapeOffset(pic) + GETPROP_INLINE_SHAPE_JUMP;
526 #endif
529 inline int32 inlineShapeJump() {
530 return inlineShapeJump(pic);
533 static int32 dslotsLoad(ic::PICInfo &pic) {
534 #if defined JS_NUNBOX32
535 return GETPROP_DSLOTS_LOAD;
536 #elif defined JS_PUNBOX64
537 return pic.labels.getprop.dslotsLoadOffset;
538 #endif
541 inline int32 dslotsLoad() {
542 return dslotsLoad(pic);
545 public:
546 GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom,
547 VoidStub stub)
548 : PICStubCompiler("getprop", f, script, pic), obj(obj), atom(atom),
549 stub(JS_FUNC_TO_DATA_PTR(void *, stub)),
550 lastStubSecondShapeGuard(pic.u.get.secondShapeGuard)
553 GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom,
554 VoidStubUInt32 stub)
555 : PICStubCompiler("callprop", f, script, pic), obj(obj), atom(atom),
556 stub(JS_FUNC_TO_DATA_PTR(void *, stub)),
557 lastStubSecondShapeGuard(pic.u.get.secondShapeGuard)
560 static void reset(ic::PICInfo &pic)
562 RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
563 repatcher.repatchLEAToLoadPtr(pic.storeBack.instructionAtOffset(dslotsLoad(pic)));
564 repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(
565 pic.shapeGuard + inlineShapeOffset(pic)),
566 int32(JSScope::INVALID_SHAPE));
567 repatcher.relink(pic.fastPathStart.jumpAtOffset(pic.shapeGuard + inlineShapeJump(pic)),
568 pic.slowPathStart);
570 if (pic.hasTypeCheck()) {
571 repatcher.relink(pic.fastPathStart.jumpAtOffset(GETPROP_INLINE_TYPE_GUARD),
572 pic.slowPathStart);
575 RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
576 ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress());
578 VoidStubUInt32 stub;
579 switch (pic.kind) {
580 case ic::PICInfo::GET:
581 stub = ic::GetProp;
582 break;
583 case ic::PICInfo::CALL:
584 stub = ic::CallProp;
585 break;
586 default:
587 JS_NOT_REACHED("invalid pic kind for GetPropCompiler::reset");
588 return;
591 MacroAssemblerCodePtr target(JS_FUNC_TO_DATA_PTR(void *, stub));
592 repatcher.relinkCallerToTrampoline(retPtr, target);
595 bool generateArgsLengthStub()
597 Assembler masm;
599 Address clasp(pic.objReg, offsetof(JSObject, clasp));
600 Jump notArgs = masm.branchPtr(Assembler::NotEqual, clasp, ImmPtr(&js_SlowArrayClass));
602 masm.load32(Address(pic.objReg, offsetof(JSObject, fslots) +
603 JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)),
604 pic.objReg);
605 masm.move(pic.objReg, pic.shapeReg);
606 masm.and32(Imm32(1), pic.shapeReg);
607 Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg, pic.shapeReg);
609 masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
610 Jump done = masm.jump();
612 JSC::ExecutablePool *ep = getExecPool(masm.size());
613 if (!ep || !pic.execPools.append(ep)) {
614 if (ep)
615 ep->release();
616 js_ReportOutOfMemory(f.cx);
617 return false;
620 JSC::LinkBuffer buffer(&masm, ep);
621 buffer.link(notArgs, pic.slowPathStart);
622 buffer.link(overridden, pic.slowPathStart);
623 buffer.link(done, pic.storeBack);
625 CodeLocationLabel start = buffer.finalizeCodeAddendum();
626 JaegerSpew(JSpew_PICs, "generate args length stub at %p\n",
627 start.executableAddress());
629 PICRepatchBuffer repatcher(pic, pic.lastPathStart());
630 patchPreviousToHere(repatcher, start);
632 disable("args length done");
634 return true;
637 bool generateArrayLengthStub()
639 Assembler masm;
641 masm.loadPtr(Address(pic.objReg, offsetof(JSObject, clasp)), pic.shapeReg);
642 Jump isDense = masm.branchPtr(Assembler::Equal, pic.shapeReg, ImmPtr(&js_ArrayClass));
643 Jump notArray = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
644 ImmPtr(&js_SlowArrayClass));
646 isDense.linkTo(masm.label(), &masm);
647 masm.load32(Address(pic.objReg, offsetof(JSObject, fslots) +
648 JSObject::JSSLOT_ARRAY_LENGTH * sizeof(Value)),
649 pic.objReg);
650 Jump oob = masm.branch32(Assembler::Above, pic.objReg, Imm32(JSVAL_INT_MAX));
651 masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
652 Jump done = masm.jump();
654 JSC::ExecutablePool *ep = getExecPool(masm.size());
655 if (!ep || !pic.execPools.append(ep)) {
656 if (ep)
657 ep->release();
658 js_ReportOutOfMemory(f.cx);
659 return false;
662 JSC::LinkBuffer buffer(&masm, ep);
663 buffer.link(notArray, pic.slowPathStart);
664 buffer.link(oob, pic.slowPathStart);
665 buffer.link(done, pic.storeBack);
667 CodeLocationLabel start = buffer.finalizeCodeAddendum();
668 JaegerSpew(JSpew_PICs, "generate array length stub at %p\n",
669 start.executableAddress());
671 PICRepatchBuffer repatcher(pic, pic.lastPathStart());
672 patchPreviousToHere(repatcher, start);
674 disable("array length done");
676 return true;
679 bool generateStringCallStub()
681 JS_ASSERT(pic.hasTypeCheck());
682 JS_ASSERT(pic.kind == ic::PICInfo::CALL);
684 if (!f.fp->script->compileAndGo)
685 return disable("String.prototype without compile-and-go");
687 mjit::ThreadData &jm = JS_METHODJIT_DATA(f.cx);
688 if (!jm.addScript(script)) {
689 js_ReportOutOfMemory(f.cx);
690 return false;
693 JSObject *holder;
694 JSProperty *prop;
695 if (!obj->lookupProperty(f.cx, ATOM_TO_JSID(atom), &holder, &prop))
696 return false;
697 if (!prop)
698 return disable("property not found");
700 AutoPropertyDropper dropper(f.cx, holder, prop);
701 JSScopeProperty *sprop = (JSScopeProperty *)prop;
702 if (holder != obj)
703 return disable("proto walk on String.prototype");
704 if (!sprop->hasDefaultGetterOrIsMethod())
705 return disable("getter");
706 if (!SPROP_HAS_VALID_SLOT(sprop, holder->scope()))
707 return disable("invalid slot");
709 JS_ASSERT(holder->isNative());
711 Assembler masm;
713 /* Only strings are allowed. */
714 Jump notString = masm.branchPtr(Assembler::NotEqual, pic.typeReg(),
715 ImmType(JSVAL_TYPE_STRING));
718 * Sink pic.objReg, since we're about to lose it. This is optimistic,
719 * we could reload it from objRemat if we wanted.
721 * Note: This is really hacky, and relies on f.regs.sp being set
722 * correctly in ic::CallProp. Should we just move the store higher
723 * up in the fast path, or put this offset in PICInfo?
725 uint32 thisvOffset = uint32(f.regs.sp - f.fp->slots()) - 1;
726 Address thisv(JSFrameReg, sizeof(JSStackFrame) + thisvOffset * sizeof(Value));
727 masm.storeTypeTag(ImmType(JSVAL_TYPE_STRING), thisv);
728 masm.storePayload(pic.objReg, thisv);
731 * Clobber objReg with String.prototype and do some PIC stuff. Well,
732 * really this is now a MIC, except it won't ever be patched, so we
733 * just disable the PIC at the end. :FIXME:? String.prototype probably
734 * does not get random shape changes.
736 masm.move(ImmPtr(obj), pic.objReg);
737 masm.loadShape(pic.objReg, pic.shapeReg);
738 Jump shapeMismatch = masm.branch32(Assembler::NotEqual, pic.shapeReg,
739 Imm32(obj->shape()));
740 masm.loadSlot(pic.objReg, pic.objReg, sprop->slot, pic.shapeReg, pic.objReg);
742 Jump done = masm.jump();
744 JSC::ExecutablePool *ep = getExecPool(masm.size());
745 if (!ep || !pic.execPools.append(ep)) {
746 if (ep)
747 ep->release();
748 js_ReportOutOfMemory(f.cx);
749 return false;
752 JSC::LinkBuffer patchBuffer(&masm, ep);
754 patchBuffer.link(notString, pic.slowPathStart.labelAtOffset(pic.u.get.typeCheckOffset));
755 patchBuffer.link(shapeMismatch, pic.slowPathStart);
756 patchBuffer.link(done, pic.storeBack);
758 CodeLocationLabel cs = patchBuffer.finalizeCodeAddendum();
759 JaegerSpew(JSpew_PICs, "generate string call stub at %p\n",
760 cs.executableAddress());
762 /* Patch the type check to jump here. */
763 if (pic.hasTypeCheck()) {
764 RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
765 repatcher.relink(pic.fastPathStart.jumpAtOffset(GETPROP_INLINE_TYPE_GUARD), cs);
768 /* Disable the PIC so we don't keep generating stubs on the above shape mismatch. */
769 disable("generated string call stub");
771 return true;
774 bool generateStringLengthStub()
776 JS_ASSERT(pic.hasTypeCheck());
778 Assembler masm;
779 Jump notString = masm.branchPtr(Assembler::NotEqual, pic.typeReg(),
780 ImmType(JSVAL_TYPE_STRING));
781 masm.loadPtr(Address(pic.objReg, offsetof(JSString, mLengthAndFlags)), pic.objReg);
782 // String length is guaranteed to be no more than 2**28, so the 32-bit operation is OK.
783 masm.urshift32(Imm32(JSString::FLAGS_LENGTH_SHIFT), pic.objReg);
784 masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
785 Jump done = masm.jump();
787 JSC::ExecutablePool *ep = getExecPool(masm.size());
788 if (!ep || !pic.execPools.append(ep)) {
789 if (ep)
790 ep->release();
791 js_ReportOutOfMemory(f.cx);
792 return false;
795 JSC::LinkBuffer patchBuffer(&masm, ep);
796 patchBuffer.link(notString, pic.slowPathStart.labelAtOffset(pic.u.get.typeCheckOffset));
797 patchBuffer.link(done, pic.storeBack);
799 CodeLocationLabel start = patchBuffer.finalizeCodeAddendum();
800 JaegerSpew(JSpew_PICs, "generate string length stub at %p\n",
801 start.executableAddress());
803 if (pic.hasTypeCheck()) {
804 RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
805 repatcher.relink(pic.fastPathStart.jumpAtOffset(GETPROP_INLINE_TYPE_GUARD), start);
808 disable("generated string length stub");
810 return true;
813 bool patchInline(JSObject *holder, JSScopeProperty *sprop)
815 spew("patch", "inline");
816 PICRepatchBuffer repatcher(pic, pic.fastPathStart);
818 mjit::ThreadData &jm = JS_METHODJIT_DATA(f.cx);
819 if (!jm.addScript(script)) {
820 js_ReportOutOfMemory(f.cx);
821 return false;
824 int32 offset;
825 if (sprop->slot < JS_INITIAL_NSLOTS) {
826 JSC::CodeLocationInstruction istr;
827 istr = pic.storeBack.instructionAtOffset(dslotsLoad());
828 repatcher.repatchLoadPtrToLEA(istr);
831 // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
832 // To: | lea fslots, [obj + DSLOTS_OFFSET]
834 // Because the offset is wrong, it's necessary to correct it
835 // below.
837 int32 diff = int32(offsetof(JSObject, fslots)) -
838 int32(offsetof(JSObject, dslots));
839 JS_ASSERT(diff != 0);
840 offset = (int32(sprop->slot) * sizeof(Value)) + diff;
841 } else {
842 offset = (sprop->slot - JS_INITIAL_NSLOTS) * sizeof(Value);
845 uint32 shapeOffs = pic.shapeGuard + inlineShapeOffset();
846 repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(shapeOffs), obj->shape());
847 #if defined JS_NUNBOX32
848 repatcher.repatch(pic.storeBack.dataLabel32AtOffset(GETPROP_TYPE_LOAD), offset + 4);
849 repatcher.repatch(pic.storeBack.dataLabel32AtOffset(GETPROP_DATA_LOAD), offset);
850 #elif defined JS_PUNBOX64
851 repatcher.repatch(pic.storeBack.dataLabel32AtOffset(pic.labels.getprop.inlineValueOffset), offset);
852 #endif
854 pic.inlinePathPatched = true;
856 return true;
859 bool generateStub(JSObject *holder, JSScopeProperty *sprop)
861 Vector<Jump, 8> shapeMismatches(f.cx);
863 Assembler masm;
865 if (pic.objNeedsRemat()) {
866 if (pic.objRemat() >= sizeof(JSStackFrame))
867 masm.load32(Address(JSFrameReg, pic.objRemat()), pic.objReg);
868 else
869 masm.move(RegisterID(pic.objRemat()), pic.objReg);
870 pic.u.get.objNeedsRemat = false;
873 Label start;
874 Jump shapeGuard;
875 Jump argsLenGuard;
876 if (obj->isDenseArray()) {
877 start = masm.label();
878 shapeGuard = masm.branchPtr(Assembler::NotEqual,
879 Address(pic.objReg, offsetof(JSObject, clasp)),
880 ImmPtr(obj->getClass()));
882 * No need to assert validity of GETPROP_STUB_SHAPE_JUMP in this case:
883 * the IC is disabled after a dense array hit, so no patching can occur.
885 } else {
886 if (pic.shapeNeedsRemat()) {
887 masm.loadShape(pic.objReg, pic.shapeReg);
888 pic.shapeRegHasBaseShape = true;
891 start = masm.label();
892 shapeGuard = masm.branch32_force32(Assembler::NotEqual, pic.shapeReg,
893 Imm32(obj->shape()));
894 #if defined JS_NUNBOX32
895 JS_ASSERT(masm.differenceBetween(start, shapeGuard) == GETPROP_STUB_SHAPE_JUMP);
896 #endif
899 #if defined JS_PUNBOX64
900 Label stubShapeJumpLabel = masm.label();
901 #endif
904 if (!shapeMismatches.append(shapeGuard))
905 return false;
907 if (obj != holder) {
908 // Emit code that walks the prototype chain.
909 JSObject *tempObj = obj;
910 Address proto(pic.objReg, offsetof(JSObject, proto));
911 do {
912 tempObj = tempObj->getProto();
913 // FIXME: we should find out why this condition occurs. It is probably
914 // related to PICs on globals.
915 if (!tempObj)
916 return disable("null object in prototype chain");
917 JS_ASSERT(tempObj);
920 * If there is a non-native along the prototype chain the shape is technically
921 * invalid.
923 if (!tempObj->isNative())
924 return disable("non-JS-native in prototype chain");
926 masm.loadPtr(proto, pic.objReg);
927 pic.shapeRegHasBaseShape = false;
928 pic.u.get.objNeedsRemat = true;
930 Jump j = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
931 if (!shapeMismatches.append(j))
932 return false;
933 } while (tempObj != holder);
935 // Load the shape out of the holder and check it.
936 masm.loadShape(pic.objReg, pic.shapeReg);
937 Jump j = masm.branch32_force32(Assembler::NotEqual, pic.shapeReg,
938 Imm32(holder->shape()));
939 if (!shapeMismatches.append(j))
940 return false;
941 pic.u.get.secondShapeGuard = masm.distanceOf(masm.label()) - masm.distanceOf(start);
942 } else {
943 JS_ASSERT(holder->isNative()); /* Precondition: already checked. */
944 pic.u.get.secondShapeGuard = 0;
947 /* Load the value out of the object. */
948 masm.loadSlot(pic.objReg, pic.objReg, sprop->slot, pic.shapeReg, pic.objReg);
949 Jump done = masm.jump();
951 JSC::ExecutablePool *ep = getExecPool(masm.size());
952 if (!ep) {
953 js_ReportOutOfMemory(f.cx);
954 return false;
957 // :TODO: this can OOM
958 JSC::LinkBuffer buffer(&masm, ep);
960 if (!pic.execPools.append(ep)) {
961 ep->release();
962 js_ReportOutOfMemory(f.cx);
963 return false;
966 // The guard exit jumps to the original slow case.
967 for (Jump *pj = shapeMismatches.begin(); pj != shapeMismatches.end(); ++pj)
968 buffer.link(*pj, pic.slowPathStart);
970 // The final exit jumps to the store-back in the inline stub.
971 buffer.link(done, pic.storeBack);
972 CodeLocationLabel cs = buffer.finalizeCodeAddendum();
973 JaegerSpew(JSpew_PICs, "generated %s stub at %p\n", type, cs.executableAddress());
975 PICRepatchBuffer repatcher(pic, pic.lastPathStart());
976 patchPreviousToHere(repatcher, cs);
978 pic.stubsGenerated++;
979 pic.lastStubStart = buffer.locationOf(start);
981 #if defined JS_PUNBOX64
982 pic.labels.getprop.stubShapeJump = masm.differenceBetween(start, stubShapeJumpLabel);
983 #endif
985 if (pic.stubsGenerated == MAX_PIC_STUBS)
986 disable("max stubs reached");
987 if (obj->isDenseArray())
988 disable("dense array");
990 return true;
993 void patchPreviousToHere(PICRepatchBuffer &repatcher, CodeLocationLabel cs)
995 // Patch either the inline fast path or a generated stub. The stub
996 // omits the prefix of the inline fast path that loads the shape, so
997 // the offsets are different.
998 int shapeGuardJumpOffset;
999 if (pic.stubsGenerated)
1000 #if defined JS_NUNBOX32
1001 shapeGuardJumpOffset = GETPROP_STUB_SHAPE_JUMP;
1002 #elif defined JS_PUNBOX64
1003 shapeGuardJumpOffset = pic.labels.getprop.stubShapeJump;
1004 #endif
1005 else
1006 shapeGuardJumpOffset = pic.shapeGuard + inlineShapeJump();
1007 repatcher.relink(shapeGuardJumpOffset, cs);
1008 if (lastStubSecondShapeGuard)
1009 repatcher.relink(lastStubSecondShapeGuard, cs);
1012 bool update()
1014 if (!pic.hit) {
1015 spew("first hit", "nop");
1016 pic.hit = true;
1017 return true;
1020 JSObject *aobj = js_GetProtoIfDenseArray(obj);
1021 if (!aobj->isNative())
1022 return disable("non-native");
1024 JSObject *holder;
1025 JSProperty *prop;
1026 if (!aobj->lookupProperty(f.cx, ATOM_TO_JSID(atom), &holder, &prop))
1027 return false;
1029 if (!prop)
1030 return disable("lookup failed");
1032 AutoPropertyDropper dropper(f.cx, holder, prop);
1034 if (!holder->isNative())
1035 return disable("non-native holder");
1037 JSScopeProperty *sprop = (JSScopeProperty *)prop;
1038 if (!sprop->hasDefaultGetterOrIsMethod())
1039 return disable("getter");
1040 if (!SPROP_HAS_VALID_SLOT(sprop, holder->scope()))
1041 return disable("invalid slot");
1043 if (obj == holder && !pic.inlinePathPatched)
1044 return patchInline(holder, sprop);
1046 return generateStub(holder, sprop);
1049 bool disable(const char *reason)
1051 return PICStubCompiler::disable(reason, stub);
1055 class GetElemCompiler : public PICStubCompiler
1057 JSObject *obj;
1058 JSString *id;
1059 void *stub;
1060 int lastStubSecondShapeGuard;
1062 static int32 dslotsLoad(ic::PICInfo &pic) {
1063 #if defined JS_NUNBOX32
1064 return GETELEM_DSLOTS_LOAD;
1065 #elif defined JS_PUNBOX64
1066 return pic.labels.getelem.dslotsLoadOffset;
1067 #endif
1070 inline int32 dslotsLoad() {
1071 return dslotsLoad(pic);
1074 static int32 inlineShapeOffset(ic::PICInfo &pic) {
1075 #if defined JS_NUNBOX32
1076 return GETELEM_INLINE_SHAPE_OFFSET;
1077 #elif defined JS_PUNBOX64
1078 return pic.labels.getelem.inlineShapeOffset;
1079 #endif
1082 inline int32 inlineShapeOffset() {
1083 return inlineShapeOffset(pic);
1086 static int32 inlineShapeJump(ic::PICInfo &pic) {
1087 #if defined JS_NUNBOX32
1088 return GETELEM_INLINE_SHAPE_JUMP;
1089 #elif defined JS_PUNBOX64
1090 return inlineShapeOffset(pic) + GETELEM_INLINE_SHAPE_JUMP;
1091 #endif
1094 inline int32 inlineShapeJump() {
1095 return inlineShapeJump(pic);
1098 static int32 inlineAtomOffset(ic::PICInfo &pic) {
1099 #if defined JS_NUNBOX32
1100 return GETELEM_INLINE_ATOM_OFFSET;
1101 #elif defined JS_PUNBOX64
1102 return pic.labels.getelem.inlineAtomOffset;
1103 #endif
1106 inline int32 inlineAtomOffset() {
1107 return inlineAtomOffset(pic);
1110 static int32 inlineAtomJump(ic::PICInfo &pic) {
1111 #if defined JS_NUNBOX32
1112 return GETELEM_INLINE_ATOM_JUMP;
1113 #elif defined JS_PUNBOX64
1114 return inlineAtomOffset(pic) + GETELEM_INLINE_ATOM_JUMP;
1115 #endif
1118 inline int32 inlineAtomJump() {
1119 return inlineAtomJump(pic);
1122 public:
1123 GetElemCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSString *id,
1124 VoidStub stub)
1125 : PICStubCompiler("getelem", f, script, pic), obj(obj), id(id),
1126 stub(JS_FUNC_TO_DATA_PTR(void *, stub)),
1127 lastStubSecondShapeGuard(pic.u.get.secondShapeGuard)
1130 static void reset(ic::PICInfo &pic)
1132 JS_ASSERT(pic.kind == ic::PICInfo::GETELEM);
1134 RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
1135 repatcher.repatchLEAToLoadPtr(pic.storeBack.instructionAtOffset(dslotsLoad(pic)));
1137 /* Only the shape needs to be patched to fail -- atom jump will never be taken. */
1138 repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(
1139 pic.shapeGuard + inlineShapeOffset(pic)),
1140 int32(JSScope::INVALID_SHAPE));
1141 repatcher.relink(pic.fastPathStart.jumpAtOffset(pic.shapeGuard + inlineShapeJump(pic)),
1142 pic.slowPathStart);
1144 RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
1145 ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress());
1147 MacroAssemblerCodePtr target(JS_FUNC_TO_DATA_PTR(void *, ic::GetElem));
1148 repatcher.relinkCallerToTrampoline(retPtr, target);
1151 bool patchInline(JSObject *holder, JSScopeProperty *sprop)
1153 spew("patch", "inline");
1154 PICRepatchBuffer repatcher(pic, pic.fastPathStart);
1156 int32 offset;
1157 if (sprop->slot < JS_INITIAL_NSLOTS) {
1158 JSC::CodeLocationInstruction istr = pic.storeBack.instructionAtOffset(dslotsLoad());
1159 repatcher.repatchLoadPtrToLEA(istr);
1162 // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
1163 // To: | lea fslots, [obj + DSLOTS_OFFSET]
1165 // Because the offset is wrong, it's necessary to correct it
1166 // below.
1168 int32 diff = int32(offsetof(JSObject, fslots)) - int32(offsetof(JSObject, dslots));
1169 JS_ASSERT(diff != 0);
1170 offset = (int32(sprop->slot) * sizeof(Value)) + diff;
1171 } else {
1172 offset = (sprop->slot - JS_INITIAL_NSLOTS) * sizeof(Value);
1175 uint32 shapeOffset = pic.shapeGuard + inlineShapeOffset();
1176 repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(shapeOffset), obj->shape());
1177 uint32 idOffset = pic.shapeGuard + inlineAtomOffset();
1178 repatcher.repatch(pic.fastPathStart.dataLabelPtrAtOffset(idOffset), id);
1179 #if defined JS_NUNBOX32
1180 repatcher.repatch(pic.storeBack.dataLabel32AtOffset(GETELEM_TYPE_LOAD), offset + 4);
1181 repatcher.repatch(pic.storeBack.dataLabel32AtOffset(GETELEM_DATA_LOAD), offset);
1182 #elif defined JS_PUNBOX64
1183 repatcher.repatch(pic.storeBack.dataLabel32AtOffset(pic.labels.getelem.inlineValueOffset), offset);
1184 #endif
1185 pic.inlinePathPatched = true;
1187 return true;
1190 void patchPreviousToHere(PICRepatchBuffer &repatcher, CodeLocationLabel cs)
1192 // Patch either the inline fast path or a generated stub. The stub
1193 // omits the prefix of the inline fast path that loads the shape, so
1194 // the offsets are different.
1195 int shapeGuardJumpOffset;
1196 int atomGuardJumpOffset;
1197 if (pic.stubsGenerated) {
1198 #if defined JS_NUNBOX32
1199 shapeGuardJumpOffset = GETELEM_STUB_SHAPE_JUMP;
1200 #elif defined JS_PUNBOX64
1201 shapeGuardJumpOffset = pic.labels.getelem.stubShapeJump;
1202 #endif
1203 atomGuardJumpOffset = GETELEM_STUB_ATOM_JUMP;
1204 } else {
1205 shapeGuardJumpOffset = pic.shapeGuard + inlineShapeJump();
1206 atomGuardJumpOffset = pic.shapeGuard + inlineAtomJump();
1208 repatcher.relink(shapeGuardJumpOffset, cs);
1209 repatcher.relink(atomGuardJumpOffset, cs);
1210 if (lastStubSecondShapeGuard)
1211 repatcher.relink(lastStubSecondShapeGuard, cs);
1214 bool generateStub(JSObject *holder, JSScopeProperty *sprop)
1216 JS_ASSERT(pic.u.get.idReg != pic.shapeReg);
1217 Vector<Jump, 8> shapeMismatches(f.cx);
1219 Assembler masm;
1221 if (pic.objNeedsRemat()) {
1222 if (pic.objRemat() >= sizeof(JSStackFrame))
1223 masm.load32(Address(JSFrameReg, pic.objRemat()), pic.objReg);
1224 else
1225 masm.move(RegisterID(pic.objRemat()), pic.objReg);
1226 pic.u.get.objNeedsRemat = false;
1229 if (pic.idNeedsRemat()) {
1230 if (pic.idRemat() >= sizeof(JSStackFrame))
1231 masm.load32(Address(JSFrameReg, pic.idRemat()), pic.u.get.idReg);
1232 else
1233 masm.move(RegisterID(pic.idRemat()), pic.u.get.idReg);
1234 pic.u.get.idNeedsRemat = false;
1237 if (pic.shapeNeedsRemat()) {
1238 masm.loadShape(pic.objReg, pic.shapeReg);
1239 pic.shapeRegHasBaseShape = true;
1242 Label start = masm.label();
1243 Jump atomGuard = masm.branchPtr(Assembler::NotEqual, pic.u.get.idReg, ImmPtr(id));
1244 DBGLABEL(dbgStubAtomJump);
1245 Jump shapeGuard = masm.branch32_force32(Assembler::NotEqual, pic.shapeReg,
1246 Imm32(obj->shape()));
1248 #if (defined JS_NUNBOX32 && defined DEBUG) || defined JS_PUNBOX64
1249 Label stubShapeJump = masm.label();
1250 #endif
1252 JS_ASSERT(masm.differenceBetween(start, dbgStubAtomJump) == GETELEM_STUB_ATOM_JUMP);
1253 #if defined JS_NUNBOX32
1254 JS_ASSERT(masm.differenceBetween(start, stubShapeJump) == GETELEM_STUB_SHAPE_JUMP);
1255 #endif
1257 if (!(shapeMismatches.append(shapeGuard) && shapeMismatches.append(atomGuard)))
1258 return false;
1260 if (obj != holder) {
1261 // Emit code that walks the prototype chain.
1262 JSObject *tempObj = obj;
1263 Address proto(pic.objReg, offsetof(JSObject, proto));
1264 do {
1265 tempObj = tempObj->getProto();
1266 // FIXME: we should find out why this condition occurs. It is probably
1267 // related to PICs on globals.
1268 if (!tempObj)
1269 return disable("null object in prototype chain");
1270 JS_ASSERT(tempObj);
1273 * If there is a non-native along the prototype chain the shape is technically
1274 * invalid.
1276 if (!tempObj->isNative())
1277 return disable("non-JS-native in prototype chain");
1279 masm.loadPtr(proto, pic.objReg);
1280 pic.shapeRegHasBaseShape = false;
1281 pic.u.get.objNeedsRemat = true;
1283 Jump j = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
1284 if (!shapeMismatches.append(j))
1285 return false;
1286 } while (tempObj != holder);
1288 // Load the shape out of the holder and check it.
1289 masm.loadShape(pic.objReg, pic.shapeReg);
1290 Jump j = masm.branch32_force32(Assembler::NotEqual, pic.shapeReg,
1291 Imm32(holder->shape()));
1292 if (!shapeMismatches.append(j))
1293 return false;
1294 pic.u.get.secondShapeGuard = masm.distanceOf(masm.label()) - masm.distanceOf(start);
1295 } else {
1296 JS_ASSERT(holder->isNative()); /* Precondition: already checked. */
1297 pic.u.get.secondShapeGuard = 0;
1300 /* Load the value out of the object. */
1301 masm.loadSlot(pic.objReg, pic.objReg, sprop->slot, pic.shapeReg, pic.objReg);
1302 Jump done = masm.jump();
1304 JSC::ExecutablePool *ep = getExecPool(masm.size());
1305 if (!ep) {
1306 js_ReportOutOfMemory(f.cx);
1307 return false;
1310 // :TODO: this can OOM
1311 JSC::LinkBuffer buffer(&masm, ep);
1313 if (!pic.execPools.append(ep)) {
1314 ep->release();
1315 js_ReportOutOfMemory(f.cx);
1316 return false;
1319 // The guard exit jumps to the original slow case.
1320 for (Jump *pj = shapeMismatches.begin(); pj != shapeMismatches.end(); ++pj)
1321 buffer.link(*pj, pic.slowPathStart);
1323 // The final exit jumps to the store-back in the inline stub.
1324 buffer.link(done, pic.storeBack);
1325 CodeLocationLabel cs = buffer.finalizeCodeAddendum();
1326 #if DEBUG
1327 char *chars = js_DeflateString(f.cx, id->chars(), id->length());
1328 JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom 0x%x (\"%s\") shape 0x%x (%s: %d)\n",
1329 type, cs.executableAddress(), id, chars, holder->shape(), script->filename,
1330 js_FramePCToLineNumber(f.cx, f.fp));
1331 f.cx->free(chars);
1332 #endif
1334 PICRepatchBuffer repatcher(pic, pic.lastPathStart());
1335 patchPreviousToHere(repatcher, cs);
1337 pic.stubsGenerated++;
1338 pic.lastStubStart = buffer.locationOf(start);
1340 #if defined JS_PUNBOX64
1341 pic.labels.getelem.stubShapeJump = masm.differenceBetween(start, stubShapeJump);
1342 #endif
1344 if (pic.stubsGenerated == MAX_PIC_STUBS)
1345 disable("max stubs reached");
1346 if (obj->isDenseArray())
1347 disable("dense array");
1349 return true;
1352 bool update()
1354 if (!pic.hit) {
1355 spew("first hit", "nop");
1356 pic.hit = true;
1357 return true;
1360 JSAtom *atom = js_AtomizeString(f.cx, id, 0);
1361 if (!atom)
1362 return false;
1363 JSObject *holder;
1364 JSProperty *prop;
1365 if (!obj->lookupProperty(f.cx, ATOM_TO_JSID(atom), &holder, &prop))
1366 return false;
1368 if (!prop)
1369 return disable("lookup failed");
1371 AutoPropertyDropper dropper(f.cx, holder, prop);
1373 if (!obj->isNative())
1374 return disable("non-native obj");
1375 if (!holder->isNative())
1376 return disable("non-native holder");
1378 JSScopeProperty *sprop = (JSScopeProperty *)prop;
1379 if (!sprop->hasDefaultGetterOrIsMethod())
1380 return disable("getter");
1381 if (!SPROP_HAS_VALID_SLOT(sprop, holder->scope()))
1382 return disable("invalid slot");
1384 if (obj == holder && !pic.inlinePathPatched)
1385 return patchInline(holder, sprop);
1387 return generateStub(holder, sprop);
1390 bool disable(const char *reason)
1392 return PICStubCompiler::disable(reason, stub);
1396 class ScopeNameCompiler : public PICStubCompiler
1398 JSObject *scopeChain;
1399 JSAtom *atom;
1400 void *stub;
1402 public:
1403 JSObject *obj;
1404 JSObject *holder;
1405 JSProperty *prop;
1406 JSScopeProperty *sprop;
1408 public:
1409 ScopeNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic,
1410 JSAtom *atom, VoidStubUInt32 stub)
1411 : PICStubCompiler("name", f, script, pic), scopeChain(scopeChain), atom(atom),
1412 stub(JS_FUNC_TO_DATA_PTR(void *, stub)), obj(NULL), holder(NULL), prop(NULL)
1415 bool disable(const char *reason)
1417 return PICStubCompiler::disable(reason, stub);
1420 static void reset(ic::PICInfo &pic)
1422 RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
1423 repatcher.relink(pic.fastPathStart.jumpAtOffset(SCOPENAME_JUMP_OFFSET),
1424 pic.slowPathStart);
1426 RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
1427 ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress());
1428 MacroAssemblerCodePtr target(JS_FUNC_TO_DATA_PTR(void *, ic::Name));
1429 repatcher.relinkCallerToTrampoline(retPtr, target);
1432 typedef Vector<Jump, 8, ContextAllocPolicy> JumpList;
1434 bool walkScopeChain(Assembler &masm, JumpList &fails, bool &found)
1436 /* Walk the scope chain. */
1437 JSObject *tobj = scopeChain;
1439 while (tobj && tobj != holder) {
1440 if (!js_IsCacheableNonGlobalScope(tobj))
1441 return disable("non-cacheable scope chain object");
1442 JS_ASSERT(tobj->isNative());
1444 if (tobj != scopeChain) {
1445 /* scopeChain will never be NULL, but parents can be NULL. */
1446 Jump j = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
1447 if (!fails.append(j))
1448 return false;
1451 /* Guard on intervening shapes. */
1452 masm.loadShape(pic.objReg, pic.shapeReg);
1453 Jump j = masm.branch32(Assembler::NotEqual, pic.shapeReg, Imm32(tobj->shape()));
1454 if (!fails.append(j))
1455 return false;
1457 /* Load the next link in the scope chain. */
1458 Address parent(pic.objReg, offsetof(JSObject, parent));
1459 masm.loadPtr(parent, pic.objReg);
1461 tobj = tobj->getParent();
1464 found = tobj == holder;
1466 return true;
1469 bool generateGlobalStub()
1471 Assembler masm;
1472 JumpList fails(f.cx);
1474 masm.loadPtr(Address(JSFrameReg, offsetof(JSStackFrame, scopeChain)), pic.objReg);
1476 JS_ASSERT(obj == holder);
1477 JS_ASSERT(holder == scopeChain->getGlobal());
1479 bool found = false;
1480 if (!walkScopeChain(masm, fails, found))
1481 return false;
1482 if (!found)
1483 return disable("scope chain walk terminated early");
1485 Jump finalNull = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
1486 masm.loadShape(pic.objReg, pic.shapeReg);
1487 Jump finalShape = masm.branch32(Assembler::NotEqual, pic.shapeReg, Imm32(holder->shape()));
1489 masm.loadSlot(pic.objReg, pic.objReg, sprop->slot, pic.shapeReg, pic.objReg);
1491 Jump done = masm.jump();
1493 // All failures flow to here, so there is a common point to patch.
1494 for (Jump *pj = fails.begin(); pj != fails.end(); ++pj)
1495 pj->linkTo(masm.label(), &masm);
1496 finalNull.linkTo(masm.label(), &masm);
1497 finalShape.linkTo(masm.label(), &masm);
1498 Label failLabel = masm.label();
1499 Jump failJump = masm.jump();
1500 DBGLABEL(dbgJumpOffset);
1502 JS_ASSERT(masm.differenceBetween(failLabel, dbgJumpOffset) == SCOPENAME_JUMP_OFFSET);
1504 JSC::ExecutablePool *ep = getExecPool(masm.size());
1505 if (!ep) {
1506 js_ReportOutOfMemory(f.cx);
1507 return false;
1510 // :TODO: this can OOM
1511 JSC::LinkBuffer buffer(&masm, ep);
1513 if (!pic.execPools.append(ep)) {
1514 ep->release();
1515 js_ReportOutOfMemory(f.cx);
1516 return false;
1519 buffer.link(failJump, pic.slowPathStart);
1520 buffer.link(done, pic.storeBack);
1521 CodeLocationLabel cs = buffer.finalizeCodeAddendum();
1522 JaegerSpew(JSpew_PICs, "generated %s global stub at %p\n", type, cs.executableAddress());
1523 spew("NAME stub", "global");
1525 PICRepatchBuffer repatcher(pic, pic.lastPathStart());
1526 repatcher.relink(SCOPENAME_JUMP_OFFSET, cs);
1528 pic.stubsGenerated++;
1529 pic.lastStubStart = buffer.locationOf(failLabel);
1531 if (pic.stubsGenerated == MAX_PIC_STUBS)
1532 disable("max stubs reached");
1534 return true;
1537 enum CallObjPropKind {
1538 ARG,
1542 bool generateCallStub()
1544 Assembler masm;
1545 Vector<Jump, 8, ContextAllocPolicy> fails(f.cx);
1547 masm.loadPtr(Address(JSFrameReg, offsetof(JSStackFrame, scopeChain)), pic.objReg);
1549 JS_ASSERT(obj == holder);
1550 JS_ASSERT(holder != scopeChain->getGlobal());
1552 CallObjPropKind kind;
1553 if (sprop->getterOp() == js_GetCallArg) {
1554 kind = ARG;
1555 } else if (sprop->getterOp() == js_GetCallVar) {
1556 kind = VAR;
1557 } else {
1558 return disable("unhandled callobj sprop getter");
1561 bool found = false;
1562 if (!walkScopeChain(masm, fails, found))
1563 return false;
1564 if (!found)
1565 return disable("scope chain walk terminated early");
1567 Jump finalNull = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
1568 masm.loadShape(pic.objReg, pic.shapeReg);
1569 Jump finalShape = masm.branch32(Assembler::NotEqual, pic.shapeReg, Imm32(holder->shape()));
1571 /* Get callobj's stack frame. */
1572 masm.loadFunctionPrivate(pic.objReg, pic.shapeReg);
1574 uint16 slot = uint16(sprop->shortid);
1576 Jump skipOver;
1577 Jump escapedFrame = masm.branchTestPtr(Assembler::Zero, pic.shapeReg, pic.shapeReg);
1579 /* Not-escaped case. */
1581 uint32 bias = 0;
1582 if (kind == ARG)
1583 masm.loadPtr(Address(pic.shapeReg, offsetof(JSStackFrame, argv)), pic.shapeReg);
1584 else
1585 bias = sizeof(JSStackFrame);
1586 Address addr(pic.shapeReg, bias + slot * sizeof(Value));
1587 masm.loadPayload(addr, pic.objReg);
1588 masm.loadTypeTag(addr, pic.shapeReg);
1589 skipOver = masm.jump();
1592 escapedFrame.linkTo(masm.label(), &masm);
1595 masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg);
1597 JSFunction *fun = js_GetCallObjectFunction(holder);
1598 if (kind == VAR)
1599 slot += fun->nargs;
1600 Address dslot(pic.objReg, slot * sizeof(Value));
1601 masm.loadTypeTag(dslot, pic.shapeReg);
1602 masm.loadPayload(dslot, pic.objReg);
1605 skipOver.linkTo(masm.label(), &masm);
1606 Jump done = masm.jump();
1608 // All failures flow to here, so there is a common point to patch.
1609 for (Jump *pj = fails.begin(); pj != fails.end(); ++pj)
1610 pj->linkTo(masm.label(), &masm);
1611 finalNull.linkTo(masm.label(), &masm);
1612 finalShape.linkTo(masm.label(), &masm);
1613 Label failLabel = masm.label();
1614 Jump failJump = masm.jump();
1616 JSC::ExecutablePool *ep = getExecPool(masm.size());
1617 if (!ep) {
1618 js_ReportOutOfMemory(f.cx);
1619 return false;
1622 // :TODO: this can OOM
1623 JSC::LinkBuffer buffer(&masm, ep);
1625 if (!pic.execPools.append(ep)) {
1626 ep->release();
1627 js_ReportOutOfMemory(f.cx);
1628 return false;
1631 buffer.link(failJump, pic.slowPathStart);
1632 buffer.link(done, pic.storeBack);
1633 CodeLocationLabel cs = buffer.finalizeCodeAddendum();
1634 JaegerSpew(JSpew_PICs, "generated %s call stub at %p\n", type, cs.executableAddress());
1636 PICRepatchBuffer repatcher(pic, pic.lastPathStart());
1637 repatcher.relink(SCOPENAME_JUMP_OFFSET, cs);
1639 pic.stubsGenerated++;
1640 pic.lastStubStart = buffer.locationOf(failLabel);
1642 if (pic.stubsGenerated == MAX_PIC_STUBS)
1643 disable("max stubs reached");
1645 return true;
1648 bool update()
1650 JSContext *cx = f.cx;
1652 if (!js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &holder, &prop))
1653 return false;
1655 if (!pic.hit) {
1656 spew("first hit", "nop");
1657 pic.hit = true;
1658 return true;
1661 if (!prop)
1662 return disable("property not found");
1663 if (!obj->isNative() || !holder->isNative())
1664 return disable("non-native scope object");
1665 if (obj != holder)
1666 return disable("property is on proto of a scope object");
1668 sprop = (JSScopeProperty *)prop;
1669 if (!sprop->hasDefaultGetterOrIsMethod())
1670 return disable("getter");
1671 if (!SPROP_HAS_VALID_SLOT(sprop, holder->scope()))
1672 return disable("invalid slot");
1674 if (obj->getClass() == &js_CallClass)
1675 return generateCallStub();
1677 if (!obj->getParent())
1678 return generateGlobalStub();
1680 return disable("scope object not handled yet");
1684 class BindNameCompiler : public PICStubCompiler
1686 JSObject *scopeChain;
1687 JSAtom *atom;
1688 void *stub;
1690 static int32 inlineJumpOffset(ic::PICInfo &pic) {
1691 #if defined JS_NUNBOX32
1692 return BINDNAME_INLINE_JUMP_OFFSET;
1693 #elif defined JS_PUNBOX64
1694 return pic.labels.bindname.inlineJumpOffset;
1695 #endif
1698 inline int32 inlineJumpOffset() {
1699 return inlineJumpOffset(pic);
1702 public:
1703 BindNameCompiler(VMFrame &f, JSScript *script, JSObject *scopeChain, ic::PICInfo &pic,
1704 JSAtom *atom, VoidStubUInt32 stub)
1705 : PICStubCompiler("bind", f, script, pic), scopeChain(scopeChain), atom(atom),
1706 stub(JS_FUNC_TO_DATA_PTR(void *, stub))
1709 bool disable(const char *reason)
1711 return PICStubCompiler::disable(reason, stub);
1714 static void reset(ic::PICInfo &pic)
1716 PICRepatchBuffer repatcher(pic, pic.fastPathStart);
1717 repatcher.relink(pic.shapeGuard + inlineJumpOffset(pic), pic.slowPathStart);
1719 RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
1720 ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress());
1721 MacroAssemblerCodePtr target(JS_FUNC_TO_DATA_PTR(void *, ic::BindName));
1722 repatcher.relinkCallerToTrampoline(retPtr, target);
1725 bool generateStub(JSObject *obj)
1727 Assembler masm;
1728 js::Vector<Jump, 8, ContextAllocPolicy> fails(f.cx);
1730 /* Guard on the shape of the scope chain. */
1731 masm.loadPtr(Address(JSFrameReg, offsetof(JSStackFrame, scopeChain)), pic.objReg);
1732 masm.loadShape(pic.objReg, pic.shapeReg);
1733 Jump firstShape = masm.branch32(Assembler::NotEqual, pic.shapeReg,
1734 Imm32(scopeChain->shape()));
1736 /* Walk up the scope chain. */
1737 JSObject *tobj = scopeChain;
1738 Address parent(pic.objReg, offsetof(JSObject, parent));
1739 while (tobj && tobj != obj) {
1740 if (!js_IsCacheableNonGlobalScope(tobj))
1741 return disable("non-cacheable obj in scope chain");
1742 masm.loadPtr(parent, pic.objReg);
1743 Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
1744 if (!fails.append(nullTest))
1745 return false;
1746 masm.loadShape(pic.objReg, pic.shapeReg);
1747 Jump shapeTest = masm.branch32(Assembler::NotEqual, pic.shapeReg,
1748 Imm32(tobj->shape()));
1749 tobj = tobj->getParent();
1751 if (tobj != obj)
1752 return disable("indirect hit");
1754 Jump done = masm.jump();
1756 // All failures flow to here, so there is a common point to patch.
1757 for (Jump *pj = fails.begin(); pj != fails.end(); ++pj)
1758 pj->linkTo(masm.label(), &masm);
1759 firstShape.linkTo(masm.label(), &masm);
1760 Label failLabel = masm.label();
1761 Jump failJump = masm.jump();
1762 DBGLABEL(dbgStubJumpOffset);
1764 JS_ASSERT(masm.differenceBetween(failLabel, dbgStubJumpOffset) == BINDNAME_STUB_JUMP_OFFSET);
1766 JSC::ExecutablePool *ep = getExecPool(masm.size());
1767 if (!ep) {
1768 js_ReportOutOfMemory(f.cx);
1769 return false;
1772 // :TODO: this can OOM
1773 JSC::LinkBuffer buffer(&masm, ep);
1775 if (!pic.execPools.append(ep)) {
1776 ep->release();
1777 js_ReportOutOfMemory(f.cx);
1778 return false;
1781 buffer.link(failJump, pic.slowPathStart);
1782 buffer.link(done, pic.storeBack);
1783 CodeLocationLabel cs = buffer.finalizeCodeAddendum();
1784 JaegerSpew(JSpew_PICs, "generated %s stub at %p\n", type, cs.executableAddress());
1786 PICRepatchBuffer repatcher(pic, pic.lastPathStart());
1787 if (!pic.stubsGenerated)
1788 repatcher.relink(pic.shapeGuard + inlineJumpOffset(), cs);
1789 else
1790 repatcher.relink(BINDNAME_STUB_JUMP_OFFSET, cs);
1792 pic.stubsGenerated++;
1793 pic.lastStubStart = buffer.locationOf(failLabel);
1795 if (pic.stubsGenerated == MAX_PIC_STUBS)
1796 disable("max stubs reached");
1798 return true;
1801 JSObject *update()
1803 JS_ASSERT(scopeChain->getParent());
1805 JSObject *obj = js_FindIdentifierBase(f.cx, scopeChain, ATOM_TO_JSID(atom));
1806 if (!obj)
1807 return obj;
1809 if (!pic.hit) {
1810 spew("first hit", "nop");
1811 pic.hit = true;
1812 return obj;
1815 if (!generateStub(obj))
1816 return NULL;
1818 return obj;
1822 void JS_FASTCALL
1823 ic::GetProp(VMFrame &f, uint32 index)
1825 JSScript *script = f.fp->script;
1826 PICInfo &pic = script->pics[index];
1828 JSAtom *atom = pic.atom;
1829 if (atom == f.cx->runtime->atomState.lengthAtom) {
1830 if (f.regs.sp[-1].isString()) {
1831 GetPropCompiler cc(f, script, NULL, pic, NULL, stubs::Length);
1832 if (!cc.generateStringLengthStub()) {
1833 cc.disable("error");
1834 THROW();
1836 JSString *str = f.regs.sp[-1].toString();
1837 f.regs.sp[-1].setInt32(str->length());
1838 return;
1839 } else if (!f.regs.sp[-1].isPrimitive()) {
1840 JSObject *obj = &f.regs.sp[-1].toObject();
1841 if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden())) {
1842 GetPropCompiler cc(f, script, obj, pic, NULL, stubs::Length);
1843 if (obj->isArray()) {
1844 if (!cc.generateArrayLengthStub()) {
1845 cc.disable("error");
1846 THROW();
1848 f.regs.sp[-1].setNumber(obj->getArrayLength());
1849 } else if (obj->isArguments()) {
1850 if (!cc.generateArgsLengthStub()) {
1851 cc.disable("error");
1852 THROW();
1854 f.regs.sp[-1].setInt32(int32_t(obj->getArgsLength()));
1856 return;
1859 atom = f.cx->runtime->atomState.lengthAtom;
1862 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
1863 if (!obj)
1864 THROW();
1866 if (pic.shouldGenerate()) {
1867 GetPropCompiler cc(f, script, obj, pic, atom, stubs::GetProp);
1868 if (!cc.update()) {
1869 cc.disable("error");
1870 THROW();
1874 Value v;
1875 if (!obj->getProperty(f.cx, ATOM_TO_JSID(atom), &v))
1876 THROW();
1877 f.regs.sp[-1] = v;
1880 void JS_FASTCALL
1881 ic::GetElem(VMFrame &f, uint32 picIndex)
1883 JSScript *script = f.fp->script;
1884 PICInfo &pic = script->pics[picIndex];
1886 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1887 if (!obj)
1888 THROW();
1890 Value idval = f.regs.sp[-1];
1891 JS_ASSERT(idval.isString());
1892 JSString *id = idval.toString();
1893 if (pic.shouldGenerate()) {
1894 GetElemCompiler cc(f, script, obj, pic, id, stubs::GetElem);
1895 if (!cc.update()) {
1896 cc.disable("error");
1897 THROW();
1901 JSAtom *atom = js_AtomizeString(f.cx, id, 0);
1902 if (!atom)
1903 THROW();
1904 Value v;
1905 if (!obj->getProperty(f.cx, ATOM_TO_JSID(atom), &v))
1906 THROW();
1907 f.regs.sp[-2] = v;
1910 static void JS_FASTCALL
1911 SetPropDumb(VMFrame &f, uint32 index)
1913 JSScript *script = f.fp->script;
1914 ic::PICInfo &pic = script->pics[index];
1915 JS_ASSERT(pic.kind == ic::PICInfo::SET);
1916 JSAtom *atom = pic.atom;
1918 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1919 if (!obj)
1920 THROW();
1921 Value rval = f.regs.sp[-1];
1922 if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1]))
1923 THROW();
1924 f.regs.sp[-2] = rval;
1927 static void JS_FASTCALL
1928 SetPropSlow(VMFrame &f, uint32 index)
1930 JSScript *script = f.fp->script;
1931 ic::PICInfo &pic = script->pics[index];
1932 JS_ASSERT(pic.kind == ic::PICInfo::SET);
1934 JSAtom *atom = pic.atom;
1935 stubs::SetName(f, atom);
1938 void JS_FASTCALL
1939 ic::SetProp(VMFrame &f, uint32 index)
1941 JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
1942 if (!obj)
1943 THROW();
1945 JSScript *script = f.fp->script;
1946 ic::PICInfo &pic = script->pics[index];
1947 JSAtom *atom = pic.atom;
1948 JS_ASSERT(pic.kind == ic::PICInfo::SET);
1951 // Important: We update the PIC before looking up the property so that the
1952 // PIC is updated only if the property already exists. The PIC doesn't try
1953 // to optimize adding new properties; that is for the slow case.
1955 // Also note, we can't use SetName for PROPINC PICs because the property
1956 // cache can't handle a GET and SET from the same scripted PC.
1959 VoidStubUInt32 stub;
1960 switch (JSOp(*f.regs.pc)) {
1961 case JSOP_PROPINC:
1962 case JSOP_PROPDEC:
1963 case JSOP_INCPROP:
1964 case JSOP_DECPROP:
1965 case JSOP_NAMEINC:
1966 case JSOP_NAMEDEC:
1967 case JSOP_INCNAME:
1968 case JSOP_DECNAME:
1969 stub = SetPropDumb;
1970 break;
1971 default:
1972 stub = SetPropSlow;
1973 break;
1976 SetPropCompiler cc(f, script, obj, pic, atom, stub);
1977 if (!cc.update()) {
1978 cc.disable("error");
1979 THROW();
1982 Value rval = f.regs.sp[-1];
1983 stub(f, index);
1986 static void JS_FASTCALL
1987 CallPropSlow(VMFrame &f, uint32 index)
1989 JSScript *script = f.fp->script;
1990 ic::PICInfo &pic = script->pics[index];
1991 stubs::CallProp(f, pic.atom);
1994 void JS_FASTCALL
1995 ic::CallProp(VMFrame &f, uint32 index)
1997 JSContext *cx = f.cx;
1998 JSFrameRegs &regs = f.regs;
2000 JSScript *script = f.fp->script;
2001 ic::PICInfo &pic = script->pics[index];
2002 JSAtom *origAtom = pic.atom;
2004 Value lval;
2005 lval = regs.sp[-1];
2007 Value objv;
2008 if (lval.isObject()) {
2009 objv = lval;
2010 } else {
2011 JSProtoKey protoKey;
2012 if (lval.isString()) {
2013 protoKey = JSProto_String;
2014 } else if (lval.isNumber()) {
2015 protoKey = JSProto_Number;
2016 } else if (lval.isBoolean()) {
2017 protoKey = JSProto_Boolean;
2018 } else {
2019 JS_ASSERT(lval.isNull() || lval.isUndefined());
2020 js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
2021 THROW();
2023 JSObject *pobj;
2024 if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
2025 THROW();
2026 objv.setObject(*pobj);
2029 JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject());
2030 Value rval;
2032 bool usePIC = true;
2034 PropertyCacheEntry *entry;
2035 JSObject *obj2;
2036 JSAtom *atom;
2037 JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
2038 if (!atom) {
2039 if (entry->vword.isFunObj()) {
2040 rval.setObject(entry->vword.toFunObj());
2041 } else if (entry->vword.isSlot()) {
2042 uint32 slot = entry->vword.toSlot();
2043 JS_ASSERT(slot < obj2->scope()->freeslot);
2044 rval = obj2->lockedGetSlot(slot);
2045 } else {
2046 JS_ASSERT(entry->vword.isSprop());
2047 JSScopeProperty *sprop = entry->vword.toSprop();
2048 NATIVE_GET(cx, &objv.toObject(), obj2, sprop, JSGET_NO_METHOD_BARRIER, &rval,
2049 THROW());
2051 regs.sp++;
2052 regs.sp[-2] = rval;
2053 regs.sp[-1] = lval;
2054 goto end_callprop;
2058 * Cache miss: use the immediate atom that was loaded for us under
2059 * PropertyCache::test.
2061 jsid id;
2062 id = ATOM_TO_JSID(origAtom);
2064 regs.sp++;
2065 regs.sp[-1].setNull();
2066 if (lval.isObject()) {
2067 if (!js_GetMethod(cx, &objv.toObject(), id,
2068 JS_LIKELY(!objv.toObject().getOps()->getProperty)
2069 ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
2070 : JSGET_NO_METHOD_BARRIER,
2071 &rval)) {
2072 THROW();
2074 regs.sp[-1] = objv;
2075 regs.sp[-2] = rval;
2076 } else {
2077 JS_ASSERT(!objv.toObject().getOps()->getProperty);
2078 if (!js_GetPropertyHelper(cx, &objv.toObject(), id,
2079 JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
2080 &rval)) {
2081 THROW();
2083 regs.sp[-1] = lval;
2084 regs.sp[-2] = rval;
2087 end_callprop:
2088 /* Wrap primitive lval in object clothing if necessary. */
2089 if (lval.isPrimitive()) {
2090 /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */
2091 JSObject *funobj;
2092 if (!IsFunctionObject(rval, &funobj) ||
2093 !PrimitiveThisTest(GET_FUNCTION_PRIVATE(cx, funobj), lval)) {
2094 if (!js_PrimitiveToObject(cx, &regs.sp[-1]))
2095 THROW();
2096 usePIC = false;
2100 GetPropCompiler cc(f, script, &objv.toObject(), pic, origAtom, CallPropSlow);
2101 if (usePIC) {
2102 if (lval.isObject()) {
2103 if (!cc.update()) {
2104 cc.disable("error");
2105 THROW();
2107 } else if (lval.isString()) {
2108 if (!cc.generateStringCallStub()) {
2109 cc.disable("error");
2110 THROW();
2112 } else {
2113 cc.disable("non-string primitive");
2115 } else {
2116 cc.disable("wrapped primitive");
2119 #if JS_HAS_NO_SUCH_METHOD
2120 if (JS_UNLIKELY(rval.isUndefined())) {
2121 regs.sp[-2].setString(ATOM_TO_STRING(origAtom));
2122 if (!js_OnUnknownMethod(cx, regs.sp - 2))
2123 THROW();
2125 #endif
2128 static void JS_FASTCALL
2129 SlowName(VMFrame &f, uint32 index)
2131 stubs::Name(f);
2134 void JS_FASTCALL
2135 ic::Name(VMFrame &f, uint32 index)
2137 JSScript *script = f.fp->script;
2138 ic::PICInfo &pic = script->pics[index];
2139 JSAtom *atom = pic.atom;
2141 ScopeNameCompiler cc(f, script, f.fp->scopeChain, pic, atom, SlowName);
2143 if (!cc.update()) {
2144 cc.disable("error");
2145 THROW();
2148 Value rval;
2149 if (cc.prop && (!cc.obj->isNative() || !cc.holder->isNative())) {
2150 cc.holder->dropProperty(f.cx, cc.prop);
2151 if (!cc.obj->getProperty(f.cx, ATOM_TO_JSID(atom), &rval))
2152 THROW();
2153 } else {
2154 if (!cc.prop) {
2155 /* Kludge to allow (typeof foo == "undefined") tests. */
2156 cc.disable("property not found");
2157 JSOp op2 = js_GetOpcode(f.cx, f.fp->script, f.regs.pc + JSOP_NAME_LENGTH);
2158 if (op2 == JSOP_TYPEOF) {
2159 f.regs.sp[0].setUndefined();
2160 return;
2162 ReportAtomNotDefined(f.cx, atom);
2163 THROW();
2165 JSScopeProperty *sprop = (JSScopeProperty *)cc.prop;
2166 JSObject *normalized = cc.obj;
2167 if (cc.obj->getClass() == &js_WithClass && !sprop->hasDefaultGetter())
2168 normalized = js_UnwrapWithObject(f.cx, cc.obj);
2169 NATIVE_GET(f.cx, normalized, cc.holder, sprop, JSGET_METHOD_BARRIER, &rval,
2170 THROW());
2171 JS_UNLOCK_OBJ(f.cx, cc.holder);
2174 f.regs.sp[0] = rval;
2177 static void JS_FASTCALL
2178 SlowBindName(VMFrame &f, uint32 index)
2180 stubs::BindName(f);
2183 void JS_FASTCALL
2184 ic::BindName(VMFrame &f, uint32 index)
2186 JSScript *script = f.fp->script;
2187 ic::PICInfo &pic = script->pics[index];
2188 JSAtom *atom = pic.atom;
2190 BindNameCompiler cc(f, script, f.fp->scopeChain, pic, atom, SlowBindName);
2192 JSObject *obj = cc.update();
2193 if (!obj) {
2194 cc.disable("error");
2195 THROW();
2198 f.regs.sp[0].setObject(*obj);
2201 void
2202 ic::PurgePICs(JSContext *cx, JSScript *script)
2204 uint32 npics = script->numPICs();
2205 for (uint32 i = 0; i < npics; i++) {
2206 ic::PICInfo &pic = script->pics[i];
2207 switch (pic.kind) {
2208 case ic::PICInfo::SET:
2209 SetPropCompiler::reset(pic);
2210 break;
2211 case ic::PICInfo::NAME:
2212 ScopeNameCompiler::reset(pic);
2213 break;
2214 case ic::PICInfo::BIND:
2215 BindNameCompiler::reset(pic);
2216 break;
2217 case ic::PICInfo::CALL: /* fall-through */
2218 case ic::PICInfo::GET:
2219 GetPropCompiler::reset(pic);
2220 break;
2221 case ic::PICInfo::GETELEM:
2222 GetElemCompiler::reset(pic);
2223 break;
2224 default:
2225 JS_NOT_REACHED("Unhandled PIC kind");
2226 break;
2228 pic.reset();
2232 #endif /* JS_POLYIC */