js/src/jit/IonCaches.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "jit/IonCaches.h"
     9 #include "mozilla/TemplateLib.h"
    11 #include "jsproxy.h"
    12 #include "jstypes.h"
    14 #include "builtin/TypedObject.h"
    15 #include "jit/Ion.h"
    16 #include "jit/IonLinker.h"
    17 #include "jit/IonSpewer.h"
    18 #include "jit/Lowering.h"
    19 #ifdef JS_ION_PERF
    20 # include "jit/PerfSpewer.h"
    21 #endif
    22 #include "jit/ParallelFunctions.h"
    23 #include "jit/VMFunctions.h"
    24 #include "vm/Shape.h"
    26 #include "jit/IonFrames-inl.h"
    27 #include "vm/Interpreter-inl.h"
    28 #include "vm/Shape-inl.h"
    30 using namespace js;
    31 using namespace js::jit;
    33 using mozilla::tl::FloorLog2;
    35 void
    36 CodeLocationJump::repoint(JitCode *code, MacroAssembler *masm)
    37 {
    38     JS_ASSERT(state_ == Relative);
    39     size_t new_off = (size_t)raw_;
    40 #ifdef JS_SMALL_BRANCH
    41     size_t jumpTableEntryOffset = reinterpret_cast<size_t>(jumpTableEntry_);
    42 #endif
    43     if (masm != nullptr) {
    44 #ifdef JS_CODEGEN_X64
    45         JS_ASSERT((uint64_t)raw_ <= UINT32_MAX);
    46 #endif
    47         new_off = masm->actualOffset((uintptr_t)raw_);
    48 #ifdef JS_SMALL_BRANCH
    49         jumpTableEntryOffset = masm->actualIndex(jumpTableEntryOffset);
    50 #endif
    51     }
    52     raw_ = code->raw() + new_off;
    53 #ifdef JS_SMALL_BRANCH
    54     jumpTableEntry_ = Assembler::PatchableJumpAddress(code, (size_t) jumpTableEntryOffset);
    55 #endif
    56     setAbsolute();
    57 }
    59 void
    60 CodeLocationLabel::repoint(JitCode *code, MacroAssembler *masm)
    61 {
    62      JS_ASSERT(state_ == Relative);
    63      size_t new_off = (size_t)raw_;
    64      if (masm != nullptr) {
    65 #ifdef JS_CODEGEN_X64
    66         JS_ASSERT((uint64_t)raw_ <= UINT32_MAX);
    67 #endif
    68         new_off = masm->actualOffset((uintptr_t)raw_);
    69      }
    70      JS_ASSERT(new_off < code->instructionsSize());
    72      raw_ = code->raw() + new_off;
    73      setAbsolute();
    74 }
    76 void
    77 CodeOffsetLabel::fixup(MacroAssembler *masm)
    78 {
    79      offset_ = masm->actualOffset(offset_);
    80 }
    82 void
    83 CodeOffsetJump::fixup(MacroAssembler *masm)
    84 {
    85      offset_ = masm->actualOffset(offset_);
    86 #ifdef JS_SMALL_BRANCH
    87      jumpTableIndex_ = masm->actualIndex(jumpTableIndex_);
    88 #endif
    89 }
    91 const char *
    92 IonCache::CacheName(IonCache::Kind kind)
    93 {
    94     static const char * const names[] =
    95     {
    96 #define NAME(x) #x,
    97         IONCACHE_KIND_LIST(NAME)
    98 #undef NAME
    99     };
   100     return names[kind];
   101 }
   103 IonCache::LinkStatus
   104 IonCache::linkCode(JSContext *cx, MacroAssembler &masm, IonScript *ion, JitCode **code)
   105 {
   106     Linker linker(masm);
   107     *code = linker.newCode<CanGC>(cx, JSC::ION_CODE);
   108     if (!*code)
   109         return LINK_ERROR;
   111     if (ion->invalidated())
   112         return CACHE_FLUSHED;
   114     return LINK_GOOD;
   115 }
   117 const size_t IonCache::MAX_STUBS = 16;
   119 // Helper class which encapsulates logic to attach a stub to an IC by hooking
   120 // up rejoins and next stub jumps.
   121 //
   122 // The simplest stubs have a single jump to the next stub and look like the
   123 // following:
   124 //
   125 //    branch guard NEXTSTUB
   126 //    ... IC-specific code ...
   127 //    jump REJOIN
   128 //
   129 // This corresponds to:
   130 //
   131 //    attacher.branchNextStub(masm, ...);
   132 //    ... emit IC-specific code ...
   133 //    attacher.jumpRejoin(masm);
   134 //
   135 // Whether the stub needs multiple next stub jumps look like:
   136 //
   137 //   branch guard FAILURES
   138 //   ... IC-specific code ...
   139 //   branch another-guard FAILURES
   140 //   ... IC-specific code ...
   141 //   jump REJOIN
   142 //   FAILURES:
   143 //   jump NEXTSTUB
   144 //
   145 // This corresponds to:
   146 //
   147 //   Label failures;
   148 //   masm.branchX(..., &failures);
   149 //   ... emit IC-specific code ...
   150 //   masm.branchY(..., failures);
   151 //   ... emit more IC-specific code ...
   152 //   attacher.jumpRejoin(masm);
   153 //   masm.bind(&failures);
   154 //   attacher.jumpNextStub(masm);
   155 //
   156 // A convenience function |branchNextStubOrLabel| is provided in the case that
   157 // the stub sometimes has multiple next stub jumps and sometimes a single
   158 // one. If a non-nullptr label is passed in, a |branchPtr| will be made to
   159 // that label instead of a |branchPtrWithPatch| to the next stub.
   160 class IonCache::StubAttacher
   161 {
   162   protected:
   163     bool hasNextStubOffset_ : 1;
   164     bool hasStubCodePatchOffset_ : 1;
   166     CodeLocationLabel rejoinLabel_;
   167     CodeOffsetJump nextStubOffset_;
   168     CodeOffsetJump rejoinOffset_;
   169     CodeOffsetLabel stubCodePatchOffset_;
   171   public:
   172     StubAttacher(CodeLocationLabel rejoinLabel)
   173       : hasNextStubOffset_(false),
   174         hasStubCodePatchOffset_(false),
   175         rejoinLabel_(rejoinLabel),
   176         nextStubOffset_(),
   177         rejoinOffset_(),
   178         stubCodePatchOffset_()
   179     { }
   181     // Value used instead of the JitCode self-reference of generated
   182     // stubs. This value is needed for marking calls made inside stubs. This
   183     // value would be replaced by the attachStub function after the allocation
   184     // of the JitCode. The self-reference is used to keep the stub path alive
   185     // even if the IonScript is invalidated or if the IC is flushed.
   186     static const ImmPtr STUB_ADDR;
   188     template <class T1, class T2>
   189     void branchNextStub(MacroAssembler &masm, Assembler::Condition cond, T1 op1, T2 op2) {
   190         JS_ASSERT(!hasNextStubOffset_);
   191         RepatchLabel nextStub;
   192         nextStubOffset_ = masm.branchPtrWithPatch(cond, op1, op2, &nextStub);
   193         hasNextStubOffset_ = true;
   194         masm.bind(&nextStub);
   195     }
   197     template <class T1, class T2>
   198     void branchNextStubOrLabel(MacroAssembler &masm, Assembler::Condition cond, T1 op1, T2 op2,
   199                                Label *label)
   200     {
   201         if (label != nullptr)
   202             masm.branchPtr(cond, op1, op2, label);
   203         else
   204             branchNextStub(masm, cond, op1, op2);
   205     }
   207     void jumpRejoin(MacroAssembler &masm) {
   208         RepatchLabel rejoin;
   209         rejoinOffset_ = masm.jumpWithPatch(&rejoin);
   210         masm.bind(&rejoin);
   211     }
   213     void jumpNextStub(MacroAssembler &masm) {
   214         JS_ASSERT(!hasNextStubOffset_);
   215         RepatchLabel nextStub;
   216         nextStubOffset_ = masm.jumpWithPatch(&nextStub);
   217         hasNextStubOffset_ = true;
   218         masm.bind(&nextStub);
   219     }
   221     void pushStubCodePointer(MacroAssembler &masm) {
   222         // Push the JitCode pointer for the stub we're generating.
   223         // WARNING:
   224         // WARNING: If JitCode ever becomes relocatable, the following code is incorrect.
   225         // WARNING: Note that we're not marking the pointer being pushed as an ImmGCPtr.
   226         // WARNING: This location will be patched with the pointer of the generated stub,
   227         // WARNING: such as it can be marked when a call is made with this stub. Be aware
   228         // WARNING: that ICs are not marked and so this stub will only be kept alive iff
   229         // WARNING: it is on the stack at the time of the GC. No ImmGCPtr is needed as the
   230         // WARNING: stubs are flushed on GC.
   231         // WARNING:
   232         JS_ASSERT(!hasStubCodePatchOffset_);
   233         stubCodePatchOffset_ = masm.PushWithPatch(STUB_ADDR);
   234         hasStubCodePatchOffset_ = true;
   235     }
   237     void patchRejoinJump(MacroAssembler &masm, JitCode *code) {
   238         rejoinOffset_.fixup(&masm);
   239         CodeLocationJump rejoinJump(code, rejoinOffset_);
   240         PatchJump(rejoinJump, rejoinLabel_);
   241     }
   243     void patchStubCodePointer(MacroAssembler &masm, JitCode *code) {
   244         if (hasStubCodePatchOffset_) {
   245             stubCodePatchOffset_.fixup(&masm);
   246             Assembler::patchDataWithValueCheck(CodeLocationLabel(code, stubCodePatchOffset_),
   247                                                ImmPtr(code), STUB_ADDR);
   248         }
   249     }
   251     virtual void patchNextStubJump(MacroAssembler &masm, JitCode *code) = 0;
   252 };
   254 const ImmPtr IonCache::StubAttacher::STUB_ADDR = ImmPtr((void*)0xdeadc0de);
   256 class RepatchIonCache::RepatchStubAppender : public IonCache::StubAttacher
   257 {
   258     RepatchIonCache &cache_;
   260   public:
   261     RepatchStubAppender(RepatchIonCache &cache)
   262       : StubAttacher(cache.rejoinLabel()),
   263         cache_(cache)
   264     {
   265     }
   267     void patchNextStubJump(MacroAssembler &masm, JitCode *code) {
   268         // Patch the previous nextStubJump of the last stub, or the jump from the
   269         // codeGen, to jump into the newly allocated code.
   270         PatchJump(cache_.lastJump_, CodeLocationLabel(code));
   272         // If this path is not taken, we are producing an entry which can no
   273         // longer go back into the update function.
   274         if (hasNextStubOffset_) {
   275             nextStubOffset_.fixup(&masm);
   276             CodeLocationJump nextStubJump(code, nextStubOffset_);
   277             PatchJump(nextStubJump, cache_.fallbackLabel_);
   279             // When the last stub fails, it fallback to the ool call which can
   280             // produce a stub. Next time we generate a stub, we will patch the
   281             // nextStub jump to try the new stub.
   282             cache_.lastJump_ = nextStubJump;
   283         }
   284     }
   285 };
   287 void
   288 RepatchIonCache::reset()
   289 {
   290     IonCache::reset();
   291     PatchJump(initialJump_, fallbackLabel_);
   292     lastJump_ = initialJump_;
   293 }
   295 void
   296 RepatchIonCache::emitInitialJump(MacroAssembler &masm, AddCacheState &addState)
   297 {
   298     initialJump_ = masm.jumpWithPatch(&addState.repatchEntry);
   299     lastJump_ = initialJump_;
   300 }
   302 void
   303 RepatchIonCache::bindInitialJump(MacroAssembler &masm, AddCacheState &addState)
   304 {
   305     masm.bind(&addState.repatchEntry);
   306 }
   308 void
   309 RepatchIonCache::updateBaseAddress(JitCode *code, MacroAssembler &masm)
   310 {
   311     IonCache::updateBaseAddress(code, masm);
   312     initialJump_.repoint(code, &masm);
   313     lastJump_.repoint(code, &masm);
   314 }
   316 class DispatchIonCache::DispatchStubPrepender : public IonCache::StubAttacher
   317 {
   318     DispatchIonCache &cache_;
   320   public:
   321     DispatchStubPrepender(DispatchIonCache &cache)
   322       : StubAttacher(cache.rejoinLabel_),
   323         cache_(cache)
   324     {
   325     }
   327     void patchNextStubJump(MacroAssembler &masm, JitCode *code) {
   328         JS_ASSERT(hasNextStubOffset_);
   330         // Jump to the previous entry in the stub dispatch table. We
   331         // have not yet executed the code we're patching the jump in.
   332         nextStubOffset_.fixup(&masm);
   333         CodeLocationJump nextStubJump(code, nextStubOffset_);
   334         PatchJump(nextStubJump, CodeLocationLabel(cache_.firstStub_));
   336         // Update the dispatch table. Modification to jumps after the dispatch
   337         // table is updated is disallowed, lest we race on entry into an
   338         // unfinalized stub.
   339         cache_.firstStub_ = code->raw();
   340     }
   341 };
   343 void
   344 DispatchIonCache::reset()
   345 {
   346     IonCache::reset();
   347     firstStub_ = fallbackLabel_.raw();
   348 }
   349 void
   350 DispatchIonCache::emitInitialJump(MacroAssembler &masm, AddCacheState &addState)
   351 {
   352     Register scratch = addState.dispatchScratch;
   353     dispatchLabel_ = masm.movWithPatch(ImmPtr((void*)-1), scratch);
   354     masm.loadPtr(Address(scratch, 0), scratch);
   355     masm.jump(scratch);
   356     rejoinLabel_ = masm.labelForPatch();
   357 }
   359 void
   360 DispatchIonCache::bindInitialJump(MacroAssembler &masm, AddCacheState &addState)
   361 {
   362     // Do nothing.
   363 }
   365 void
   366 DispatchIonCache::updateBaseAddress(JitCode *code, MacroAssembler &masm)
   367 {
   368     // The address of firstStub_ should be pointer aligned.
   369     JS_ASSERT(uintptr_t(&firstStub_) % sizeof(uintptr_t) == 0);
   371     IonCache::updateBaseAddress(code, masm);
   372     dispatchLabel_.fixup(&masm);
   373     Assembler::patchDataWithValueCheck(CodeLocationLabel(code, dispatchLabel_),
   374                                        ImmPtr(&firstStub_),
   375                                        ImmPtr((void*)-1));
   376     firstStub_ = fallbackLabel_.raw();
   377     rejoinLabel_.repoint(code, &masm);
   378 }
   380 void
   381 IonCache::attachStub(MacroAssembler &masm, StubAttacher &attacher, Handle<JitCode *> code)
   382 {
   383     JS_ASSERT(canAttachStub());
   384     incrementStubCount();
   386     // Update the success path to continue after the IC initial jump.
   387     attacher.patchRejoinJump(masm, code);
   389     // Replace the STUB_ADDR constant by the address of the generated stub, such
   390     // as it can be kept alive even if the cache is flushed (see
   391     // MarkJitExitFrame).
   392     attacher.patchStubCodePointer(masm, code);
   394     // Update the failure path. Note it is this patch that makes the stub
   395     // accessible for parallel ICs so it should not be moved unless you really
   396     // know what is going on.
   397     attacher.patchNextStubJump(masm, code);
   398 }
   400 bool
   401 IonCache::linkAndAttachStub(JSContext *cx, MacroAssembler &masm, StubAttacher &attacher,
   402                             IonScript *ion, const char *attachKind)
   403 {
   404     Rooted<JitCode *> code(cx);
   405     {
   406         // Need to exit the AutoFlushICache context to flush the cache
   407         // before attaching the stub below.
   408         AutoFlushICache afc("IonCache");
   409         LinkStatus status = linkCode(cx, masm, ion, code.address());
   410         if (status != LINK_GOOD)
   411             return status != LINK_ERROR;
   412     }
   414     if (pc_) {
   415         IonSpew(IonSpew_InlineCaches, "Cache %p(%s:%d/%d) generated %s %s stub at %p",
   416                 this, script_->filename(), script_->lineno(), script_->pcToOffset(pc_),
   417                 attachKind, CacheName(kind()), code->raw());
   418     } else {
   419         IonSpew(IonSpew_InlineCaches, "Cache %p generated %s %s stub at %p",
   420                 this, attachKind, CacheName(kind()), code->raw());
   421     }
   423 #ifdef JS_ION_PERF
   424     writePerfSpewerJitCodeProfile(code, "IonCache");
   425 #endif
   427     attachStub(masm, attacher, code);
   429     return true;
   430 }
   432 void
   433 IonCache::updateBaseAddress(JitCode *code, MacroAssembler &masm)
   434 {
   435     fallbackLabel_.repoint(code, &masm);
   436 }
   438 void
   439 IonCache::initializeAddCacheState(LInstruction *ins, AddCacheState *addState)
   440 {
   441 }
   443 static bool
   444 IsCacheableDOMProxy(JSObject *obj)
   445 {
   446     if (!obj->is<ProxyObject>())
   447         return false;
   449     BaseProxyHandler *handler = obj->as<ProxyObject>().handler();
   451     if (handler->family() != GetDOMProxyHandlerFamily())
   452         return false;
   454     if (obj->numFixedSlots() <= GetDOMProxyExpandoSlot())
   455         return false;
   457     return true;
   458 }
   460 static void
   461 GeneratePrototypeGuards(JSContext *cx, IonScript *ion, MacroAssembler &masm, JSObject *obj,
   462                         JSObject *holder, Register objectReg, Register scratchReg,
   463                         Label *failures)
   464 {
   465     /* The guards here protect against the effects of TradeGuts(). If the prototype chain
   466      * is directly altered, then TI will toss the jitcode, so we don't have to worry about
   467      * it, and any other change to the holder, or adding a shadowing property will result
   468      * in reshaping the holder, and thus the failure of the shape guard.
   469      */
   470     JS_ASSERT(obj != holder);
   472     if (obj->hasUncacheableProto()) {
   473         // Note: objectReg and scratchReg may be the same register, so we cannot
   474         // use objectReg in the rest of this function.
   475         masm.loadPtr(Address(objectReg, JSObject::offsetOfType()), scratchReg);
   476         Address proto(scratchReg, types::TypeObject::offsetOfProto());
   477         masm.branchNurseryPtr(Assembler::NotEqual, proto,
   478                               ImmMaybeNurseryPtr(obj->getProto()), failures);
   479     }
   481     JSObject *pobj = IsCacheableDOMProxy(obj)
   482                      ? obj->getTaggedProto().toObjectOrNull()
   483                      : obj->getProto();
   484     if (!pobj)
   485         return;
   486     while (pobj != holder) {
   487         if (pobj->hasUncacheableProto()) {
   488             JS_ASSERT(!pobj->hasSingletonType());
   489             masm.moveNurseryPtr(ImmMaybeNurseryPtr(pobj), scratchReg);
   490             Address objType(scratchReg, JSObject::offsetOfType());
   491             masm.branchPtr(Assembler::NotEqual, objType, ImmGCPtr(pobj->type()), failures);
   492         }
   493         pobj = pobj->getProto();
   494     }
   495 }
   497 static bool
   498 IsCacheableProtoChain(JSObject *obj, JSObject *holder)
   499 {
   500     while (obj != holder) {
   501         /*
   502          * We cannot assume that we find the holder object on the prototype
   503          * chain and must check for null proto. The prototype chain can be
   504          * altered during the lookupProperty call.
   505          */
   506         JSObject *proto = obj->getProto();
   507         if (!proto || !proto->isNative())
   508             return false;
   509         obj = proto;
   510     }
   511     return true;
   512 }
   514 static bool
   515 IsCacheableGetPropReadSlot(JSObject *obj, JSObject *holder, Shape *shape)
   516 {
   517     if (!shape || !IsCacheableProtoChain(obj, holder))
   518         return false;
   520     if (!shape->hasSlot() || !shape->hasDefaultGetter())
   521         return false;
   523     return true;
   524 }
   526 static bool
   527 IsCacheableNoProperty(JSObject *obj, JSObject *holder, Shape *shape, jsbytecode *pc,
   528                       const TypedOrValueRegister &output)
   529 {
   530     if (shape)
   531         return false;
   533     JS_ASSERT(!holder);
   535     // Just because we didn't find the property on the object doesn't mean it
   536     // won't magically appear through various engine hacks:
   537     if (obj->getClass()->getProperty && obj->getClass()->getProperty != JS_PropertyStub)
   538         return false;
   540     // Don't generate missing property ICs if we skipped a non-native object, as
   541     // lookups may extend beyond the prototype chain (e.g.  for DOMProxy
   542     // proxies).
   543     JSObject *obj2 = obj;
   544     while (obj2) {
   545         if (!obj2->isNative())
   546             return false;
   547         obj2 = obj2->getProto();
   548     }
   550     // The pc is nullptr if the cache is idempotent. We cannot share missing
   551     // properties between caches because TI can only try to prove that a type is
   552     // contained, but does not attempts to check if something does not exists.
   553     // So the infered type of getprop would be missing and would not contain
   554     // undefined, as expected for missing properties.
   555     if (!pc)
   556         return false;
   558 #if JS_HAS_NO_SUCH_METHOD
   559     // The __noSuchMethod__ hook may substitute in a valid method.  Since,
   560     // if o.m is missing, o.m() will probably be an error, just mark all
   561     // missing callprops as uncacheable.
   562     if (JSOp(*pc) == JSOP_CALLPROP ||
   563         JSOp(*pc) == JSOP_CALLELEM)
   564     {
   565         return false;
   566     }
   567 #endif
   569     // TI has not yet monitored an Undefined value. The fallback path will
   570     // monitor and invalidate the script.
   571     if (!output.hasValue())
   572         return false;
   574     return true;
   575 }
   577 static bool
   578 IsOptimizableArgumentsObjectForLength(JSObject *obj)
   579 {
   580     if (!obj->is<ArgumentsObject>())
   581         return false;
   583     if (obj->as<ArgumentsObject>().hasOverriddenLength())
   584         return false;
   586     return true;
   587 }
   589 static bool
   590 IsOptimizableArgumentsObjectForGetElem(JSObject *obj, Value idval)
   591 {
   592     if (!IsOptimizableArgumentsObjectForLength(obj))
   593         return false;
   595     ArgumentsObject &argsObj = obj->as<ArgumentsObject>();
   597     if (argsObj.isAnyElementDeleted())
   598         return false;
   600     if (!idval.isInt32())
   601         return false;
   603     int32_t idint = idval.toInt32();
   604     if (idint < 0 || static_cast<uint32_t>(idint) >= argsObj.initialLength())
   605         return false;
   607     return true;
   608 }
   610 static bool
   611 IsCacheableGetPropCallNative(JSObject *obj, JSObject *holder, Shape *shape)
   612 {
   613     if (!shape || !IsCacheableProtoChain(obj, holder))
   614         return false;
   616     if (!shape->hasGetterValue() || !shape->getterValue().isObject())
   617         return false;
   619     if (!shape->getterValue().toObject().is<JSFunction>())
   620         return false;
   622     JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
   623     if (!getter.isNative())
   624         return false;
   626     // Check for a getter that has jitinfo and whose jitinfo says it's
   627     // OK with both inner and outer objects.
   628     if (getter.jitInfo() && !getter.jitInfo()->needsOuterizedThisObject())
   629         return true;
   631     // For getters that need an outerized this object, don't cache if
   632     // obj has an outerObject hook, since our cache will pass obj
   633     // itself without outerizing.
   634     return !obj->getClass()->ext.outerObject;
   635 }
   637 static bool
   638 IsCacheableGetPropCallPropertyOp(JSObject *obj, JSObject *holder, Shape *shape)
   639 {
   640     if (!shape || !IsCacheableProtoChain(obj, holder))
   641         return false;
   643     if (shape->hasSlot() || shape->hasGetterValue() || shape->hasDefaultGetter())
   644         return false;
   646     return true;
   647 }
   649 static inline void
   650 EmitLoadSlot(MacroAssembler &masm, JSObject *holder, Shape *shape, Register holderReg,
   651              TypedOrValueRegister output, Register scratchReg)
   652 {
   653     JS_ASSERT(holder);
   654     if (holder->isFixedSlot(shape->slot())) {
   655         Address addr(holderReg, JSObject::getFixedSlotOffset(shape->slot()));
   656         masm.loadTypedOrValue(addr, output);
   657     } else {
   658         masm.loadPtr(Address(holderReg, JSObject::offsetOfSlots()), scratchReg);
   660         Address addr(scratchReg, holder->dynamicSlotIndex(shape->slot()) * sizeof(Value));
   661         masm.loadTypedOrValue(addr, output);
   662     }
   663 }
   665 static void
   666 GenerateDOMProxyChecks(JSContext *cx, MacroAssembler &masm, JSObject *obj,
   667                        PropertyName *name, Register object, Label *stubFailure,
   668                        bool skipExpandoCheck = false)
   669 {
   670     JS_ASSERT(IsCacheableDOMProxy(obj));
   672     // Guard the following:
   673     //      1. The object is a DOMProxy.
   674     //      2. The object does not have expando properties, or has an expando
   675     //          which is known to not have the desired property.
   676     Address handlerAddr(object, ProxyObject::offsetOfHandler());
   677     Address expandoSlotAddr(object, JSObject::getFixedSlotOffset(GetDOMProxyExpandoSlot()));
   679     // Check that object is a DOMProxy.
   680     masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr,
   681                           ImmPtr(obj->as<ProxyObject>().handler()), stubFailure);
   683     if (skipExpandoCheck)
   684         return;
   686     // For the remaining code, we need to reserve some registers to load a value.
   687     // This is ugly, but unvaoidable.
   688     RegisterSet domProxyRegSet(RegisterSet::All());
   689     domProxyRegSet.take(AnyRegister(object));
   690     ValueOperand tempVal = domProxyRegSet.takeValueOperand();
   691     masm.pushValue(tempVal);
   693     Label failDOMProxyCheck;
   694     Label domProxyOk;
   696     Value expandoVal = obj->getFixedSlot(GetDOMProxyExpandoSlot());
   697     masm.loadValue(expandoSlotAddr, tempVal);
   699     if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
   700         masm.branchTestValue(Assembler::NotEqual, tempVal, expandoVal, &failDOMProxyCheck);
   702         ExpandoAndGeneration *expandoAndGeneration = (ExpandoAndGeneration*)expandoVal.toPrivate();
   703         masm.movePtr(ImmPtr(expandoAndGeneration), tempVal.scratchReg());
   705         masm.branch32(Assembler::NotEqual,
   706                       Address(tempVal.scratchReg(),
   707                               ExpandoAndGeneration::offsetOfGeneration()),
   708                       Imm32(expandoAndGeneration->generation),
   709                       &failDOMProxyCheck);
   711         expandoVal = expandoAndGeneration->expando;
   712         masm.loadValue(Address(tempVal.scratchReg(),
   713                                ExpandoAndGeneration::offsetOfExpando()),
   714                        tempVal);
   715     }
   717     // If the incoming object does not have an expando object then we're sure we're not
   718     // shadowing.
   719     masm.branchTestUndefined(Assembler::Equal, tempVal, &domProxyOk);
   721     if (expandoVal.isObject()) {
   722         JS_ASSERT(!expandoVal.toObject().nativeContains(cx, name));
   724         // Reference object has an expando object that doesn't define the name. Check that
   725         // the incoming object has an expando object with the same shape.
   726         masm.branchTestObject(Assembler::NotEqual, tempVal, &failDOMProxyCheck);
   727         masm.extractObject(tempVal, tempVal.scratchReg());
   728         masm.branchPtr(Assembler::Equal,
   729                        Address(tempVal.scratchReg(), JSObject::offsetOfShape()),
   730                        ImmGCPtr(expandoVal.toObject().lastProperty()),
   731                        &domProxyOk);
   732     }
   734     // Failure case: restore the tempVal registers and jump to failures.
   735     masm.bind(&failDOMProxyCheck);
   736     masm.popValue(tempVal);
   737     masm.jump(stubFailure);
   739     // Success case: restore the tempval and proceed.
   740     masm.bind(&domProxyOk);
   741     masm.popValue(tempVal);
   742 }
   744 static void
   745 GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
   746                  IonCache::StubAttacher &attacher, JSObject *obj, JSObject *holder,
   747                  Shape *shape, Register object, TypedOrValueRegister output,
   748                  Label *failures = nullptr)
   749 {
   750     JS_ASSERT(obj->isNative());
   751     // If there's a single jump to |failures|, we can patch the shape guard
   752     // jump directly. Otherwise, jump to the end of the stub, so there's a
   753     // common point to patch.
   754     bool multipleFailureJumps = (obj != holder) || (failures != nullptr && failures->used());
   756     // If we have multiple failure jumps but didn't get a label from the
   757     // outside, make one ourselves.
   758     Label failures_;
   759     if (multipleFailureJumps && !failures)
   760         failures = &failures_;
   762     // Guard on the shape of the object.
   763     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
   764                                    Address(object, JSObject::offsetOfShape()),
   765                                    ImmGCPtr(obj->lastProperty()),
   766                                    failures);
   768     // If we need a scratch register, use either an output register or the
   769     // object register. After this point, we cannot jump directly to
   770     // |failures| since we may still have to pop the object register.
   771     bool restoreScratch = false;
   772     Register scratchReg = Register::FromCode(0); // Quell compiler warning.
   774     if (obj != holder || !holder->isFixedSlot(shape->slot())) {
   775         if (output.hasValue()) {
   776             scratchReg = output.valueReg().scratchReg();
   777         } else if (output.type() == MIRType_Double) {
   778             scratchReg = object;
   779             masm.push(scratchReg);
   780             restoreScratch = true;
   781         } else {
   782             scratchReg = output.typedReg().gpr();
   783         }
   784     }
   786     // Fast path: single failure jump, no prototype guards.
   787     if (!multipleFailureJumps) {
   788         EmitLoadSlot(masm, holder, shape, object, output, scratchReg);
   789         if (restoreScratch)
   790             masm.pop(scratchReg);
   791         attacher.jumpRejoin(masm);
   792         return;
   793     }
   795     // Slow path: multiple jumps; generate prototype guards.
   796     Label prototypeFailures;
   797     Register holderReg;
   798     if (obj != holder) {
   799         // Note: this may clobber the object register if it's used as scratch.
   800         GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg,
   801                                 &prototypeFailures);
   803         if (holder) {
   804             // Guard on the holder's shape.
   805             holderReg = scratchReg;
   806             masm.moveNurseryPtr(ImmMaybeNurseryPtr(holder), holderReg);
   807             masm.branchPtr(Assembler::NotEqual,
   808                            Address(holderReg, JSObject::offsetOfShape()),
   809                            ImmGCPtr(holder->lastProperty()),
   810                            &prototypeFailures);
   811         } else {
   812             // The property does not exist. Guard on everything in the
   813             // prototype chain.
   814             JSObject *proto = obj->getTaggedProto().toObjectOrNull();
   815             Register lastReg = object;
   816             JS_ASSERT(scratchReg != object);
   817             while (proto) {
   818                 masm.loadObjProto(lastReg, scratchReg);
   820                 // Guard the shape of the current prototype.
   821                 masm.branchPtr(Assembler::NotEqual,
   822                                Address(scratchReg, JSObject::offsetOfShape()),
   823                                ImmGCPtr(proto->lastProperty()),
   824                                &prototypeFailures);
   826                 proto = proto->getProto();
   827                 lastReg = scratchReg;
   828             }
   830             holderReg = InvalidReg;
   831         }
   832     } else {
   833         holderReg = object;
   834     }
   836     // Slot access.
   837     if (holder)
   838         EmitLoadSlot(masm, holder, shape, holderReg, output, scratchReg);
   839     else
   840         masm.moveValue(UndefinedValue(), output.valueReg());
   842     // Restore scratch on success.
   843     if (restoreScratch)
   844         masm.pop(scratchReg);
   846     attacher.jumpRejoin(masm);
   848     masm.bind(&prototypeFailures);
   849     if (restoreScratch)
   850         masm.pop(scratchReg);
   851     masm.bind(failures);
   853     attacher.jumpNextStub(masm);
   855 }
   857 static bool
   858 EmitGetterCall(JSContext *cx, MacroAssembler &masm,
   859                IonCache::StubAttacher &attacher, JSObject *obj,
   860                JSObject *holder, HandleShape shape,
   861                RegisterSet liveRegs, Register object,
   862                Register scratchReg, TypedOrValueRegister output,
   863                void *returnAddr)
   864 {
   865     JS_ASSERT(output.hasValue());
   866     MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
   868     // Remaining registers should basically be free, but we need to use |object| still
   869     // so leave it alone.
   870     RegisterSet regSet(RegisterSet::All());
   871     regSet.take(AnyRegister(object));
   873     // This is a slower stub path, and we're going to be doing a call anyway.  Don't need
   874     // to try so hard to not use the stack.  Scratch regs are just taken from the register
   875     // set not including the input, current value saved on the stack, and restored when
   876     // we're done with it.
   877     scratchReg               = regSet.takeGeneral();
   878     Register argJSContextReg = regSet.takeGeneral();
   879     Register argUintNReg     = regSet.takeGeneral();
   880     Register argVpReg        = regSet.takeGeneral();
   882     // Shape has a getter function.
   883     bool callNative = IsCacheableGetPropCallNative(obj, holder, shape);
   884     JS_ASSERT_IF(!callNative, IsCacheableGetPropCallPropertyOp(obj, holder, shape));
   886     if (callNative) {
   887         JS_ASSERT(shape->hasGetterValue() && shape->getterValue().isObject() &&
   888                   shape->getterValue().toObject().is<JSFunction>());
   889         JSFunction *target = &shape->getterValue().toObject().as<JSFunction>();
   891         JS_ASSERT(target);
   892         JS_ASSERT(target->isNative());
   894         // Native functions have the signature:
   895         //  bool (*)(JSContext *, unsigned, Value *vp)
   896         // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
   897         // are the function arguments.
   899         // Construct vp array:
   900         // Push object value for |this|
   901         masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object)));
   902         // Push callee/outparam.
   903         masm.Push(ObjectValue(*target));
   905         // Preload arguments into registers.
   906         masm.loadJSContext(argJSContextReg);
   907         masm.move32(Imm32(0), argUintNReg);
   908         masm.movePtr(StackPointer, argVpReg);
   910         // Push marking data for later use.
   911         masm.Push(argUintNReg);
   912         attacher.pushStubCodePointer(masm);
   914         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
   915             return false;
   916         masm.enterFakeExitFrame(ION_FRAME_OOL_NATIVE);
   918         // Construct and execute call.
   919         masm.setupUnalignedABICall(3, scratchReg);
   920         masm.passABIArg(argJSContextReg);
   921         masm.passABIArg(argUintNReg);
   922         masm.passABIArg(argVpReg);
   923         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native()));
   925         // Test for failure.
   926         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
   928         // Load the outparam vp[0] into output register(s).
   929         Address outparam(StackPointer, IonOOLNativeExitFrameLayout::offsetOfResult());
   930         masm.loadTypedOrValue(outparam, output);
   932         // masm.leaveExitFrame & pop locals
   933         masm.adjustStack(IonOOLNativeExitFrameLayout::Size(0));
   934     } else {
   935         Register argObjReg       = argUintNReg;
   936         Register argIdReg        = regSet.takeGeneral();
   938         PropertyOp target = shape->getterOp();
   939         JS_ASSERT(target);
   941         // Push stubCode for marking.
   942         attacher.pushStubCodePointer(masm);
   944         // JSPropertyOp: bool fn(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
   946         // Push args on stack first so we can take pointers to make handles.
   947         masm.Push(UndefinedValue());
   948         masm.movePtr(StackPointer, argVpReg);
   950         // push canonical jsid from shape instead of propertyname.
   951         masm.Push(shape->propid(), scratchReg);
   952         masm.movePtr(StackPointer, argIdReg);
   954         masm.Push(object);
   955         masm.movePtr(StackPointer, argObjReg);
   957         masm.loadJSContext(argJSContextReg);
   959         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
   960             return false;
   961         masm.enterFakeExitFrame(ION_FRAME_OOL_PROPERTY_OP);
   963         // Make the call.
   964         masm.setupUnalignedABICall(4, scratchReg);
   965         masm.passABIArg(argJSContextReg);
   966         masm.passABIArg(argObjReg);
   967         masm.passABIArg(argIdReg);
   968         masm.passABIArg(argVpReg);
   969         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target));
   971         // Test for failure.
   972         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
   974         // Load the outparam vp[0] into output register(s).
   975         Address outparam(StackPointer, IonOOLPropertyOpExitFrameLayout::offsetOfResult());
   976         masm.loadTypedOrValue(outparam, output);
   978         // masm.leaveExitFrame & pop locals.
   979         masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
   980     }
   982     masm.icRestoreLive(liveRegs, aic);
   983     return true;
   984 }
   986 static bool
   987 GenerateCallGetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
   988                    IonCache::StubAttacher &attacher, JSObject *obj, PropertyName *name,
   989                    JSObject *holder, HandleShape shape, RegisterSet &liveRegs, Register object,
   990                    TypedOrValueRegister output, void *returnAddr, Label *failures = nullptr)
   991 {
   992     JS_ASSERT(obj->isNative());
   993     JS_ASSERT(output.hasValue());
   995     // Use the passed in label if there was one. Otherwise, we'll have to make our own.
   996     Label stubFailure;
   997     failures = failures ? failures : &stubFailure;
   999     // Initial shape check.
  1000     masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfShape()),
  1001                    ImmGCPtr(obj->lastProperty()), failures);
  1003     Register scratchReg = output.valueReg().scratchReg();
  1005     // Note: this may clobber the object register if it's used as scratch.
  1006     if (obj != holder)
  1007         GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, failures);
  1009     // Guard on the holder's shape.
  1010     Register holderReg = scratchReg;
  1011     masm.moveNurseryPtr(ImmMaybeNurseryPtr(holder), holderReg);
  1012     masm.branchPtr(Assembler::NotEqual,
  1013                    Address(holderReg, JSObject::offsetOfShape()),
  1014                    ImmGCPtr(holder->lastProperty()),
  1015                    failures);
  1017     // Now we're good to go to invoke the native call.
  1018     if (!EmitGetterCall(cx, masm, attacher, obj, holder, shape, liveRegs, object,
  1019                         scratchReg, output, returnAddr))
  1020         return false;
  1022     // Rejoin jump.
  1023     attacher.jumpRejoin(masm);
  1025     // Jump to next stub.
  1026     masm.bind(failures);
  1027     attacher.jumpNextStub(masm);
  1029     return true;
  1032 static bool
  1033 GenerateArrayLength(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
  1034                     JSObject *obj, Register object, TypedOrValueRegister output)
  1036     JS_ASSERT(obj->is<ArrayObject>());
  1038     Label failures;
  1040     // Guard object is a dense array.
  1041     RootedShape shape(cx, obj->lastProperty());
  1042     if (!shape)
  1043         return false;
  1044     masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
  1046     // Load length.
  1047     Register outReg;
  1048     if (output.hasValue()) {
  1049         outReg = output.valueReg().scratchReg();
  1050     } else {
  1051         JS_ASSERT(output.type() == MIRType_Int32);
  1052         outReg = output.typedReg().gpr();
  1055     masm.loadPtr(Address(object, JSObject::offsetOfElements()), outReg);
  1056     masm.load32(Address(outReg, ObjectElements::offsetOfLength()), outReg);
  1058     // The length is an unsigned int, but the value encodes a signed int.
  1059     JS_ASSERT(object != outReg);
  1060     masm.branchTest32(Assembler::Signed, outReg, outReg, &failures);
  1062     if (output.hasValue())
  1063         masm.tagValue(JSVAL_TYPE_INT32, outReg, output.valueReg());
  1065     /* Success. */
  1066     attacher.jumpRejoin(masm);
  1068     /* Failure. */
  1069     masm.bind(&failures);
  1070     attacher.jumpNextStub(masm);
  1072     return true;
  1075 static void
  1076 GenerateTypedArrayLength(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
  1077                          JSObject *obj, Register object, TypedOrValueRegister output)
  1079     JS_ASSERT(obj->is<TypedArrayObject>());
  1081     Label failures;
  1083     Register tmpReg;
  1084     if (output.hasValue()) {
  1085         tmpReg = output.valueReg().scratchReg();
  1086     } else {
  1087         JS_ASSERT(output.type() == MIRType_Int32);
  1088         tmpReg = output.typedReg().gpr();
  1090     JS_ASSERT(object != tmpReg);
  1092     // Implement the negated version of JSObject::isTypedArray predicate.
  1093     masm.loadObjClass(object, tmpReg);
  1094     masm.branchPtr(Assembler::Below, tmpReg, ImmPtr(&TypedArrayObject::classes[0]),
  1095                    &failures);
  1096     masm.branchPtr(Assembler::AboveOrEqual, tmpReg,
  1097                    ImmPtr(&TypedArrayObject::classes[ScalarTypeDescr::TYPE_MAX]),
  1098                    &failures);
  1100     // Load length.
  1101     masm.loadTypedOrValue(Address(object, TypedArrayObject::lengthOffset()), output);
  1103     /* Success. */
  1104     attacher.jumpRejoin(masm);
  1106     /* Failure. */
  1107     masm.bind(&failures);
  1108     attacher.jumpNextStub(masm);
  1111 static bool
  1112 IsCacheableArrayLength(JSContext *cx, HandleObject obj, HandlePropertyName name,
  1113                        TypedOrValueRegister output)
  1115     if (!obj->is<ArrayObject>())
  1116         return false;
  1118     if (output.type() != MIRType_Value && output.type() != MIRType_Int32) {
  1119         // The stub assumes that we always output Int32, so make sure our output
  1120         // is equipped to handle that.
  1121         return false;
  1124     return true;
  1127 template <class GetPropCache>
  1128 static GetPropertyIC::NativeGetPropCacheability
  1129 CanAttachNativeGetProp(typename GetPropCache::Context cx, const GetPropCache &cache,
  1130                        HandleObject obj, HandlePropertyName name,
  1131                        MutableHandleObject holder, MutableHandleShape shape,
  1132                        bool skipArrayLen = false)
  1134     if (!obj || !obj->isNative())
  1135         return GetPropertyIC::CanAttachNone;
  1137     // The lookup needs to be universally pure, otherwise we risk calling hooks out
  1138     // of turn. We don't mind doing this even when purity isn't required, because we
  1139     // only miss out on shape hashification, which is only a temporary perf cost.
  1140     // The limits were arbitrarily set, anyways.
  1141     if (!LookupPropertyPure(obj, NameToId(name), holder.address(), shape.address()))
  1142         return GetPropertyIC::CanAttachNone;
  1144     RootedScript script(cx);
  1145     jsbytecode *pc;
  1146     cache.getScriptedLocation(&script, &pc);
  1147     if (IsCacheableGetPropReadSlot(obj, holder, shape) ||
  1148         IsCacheableNoProperty(obj, holder, shape, pc, cache.output()))
  1150         return GetPropertyIC::CanAttachReadSlot;
  1153     // |length| is a non-configurable getter property on ArrayObjects. Any time this
  1154     // check would have passed, we can install a getter stub instead. Allow people to
  1155     // make that decision themselves with skipArrayLen
  1156     if (!skipArrayLen && cx->names().length == name && cache.allowArrayLength(cx, obj) &&
  1157         IsCacheableArrayLength(cx, obj, name, cache.output()))
  1159         // The array length property is non-configurable, which means both that
  1160         // checking the class of the object and the name of the property is enough
  1161         // and that we don't need to worry about monitoring, since we know the
  1162         // return type statically.
  1163         return GetPropertyIC::CanAttachArrayLength;
  1166     // IonBuilder guarantees that it's impossible to generate a GetPropertyIC with
  1167     // allowGetters() true and cache.output().hasValue() false. If this isn't true,
  1168     // we will quickly assert during stub generation.
  1169     if (cache.allowGetters() &&
  1170         (IsCacheableGetPropCallNative(obj, holder, shape) ||
  1171          IsCacheableGetPropCallPropertyOp(obj, holder, shape)))
  1173         // Don't enable getter call if cache is parallel or idempotent, since
  1174         // they can be effectful. This is handled by allowGetters()
  1175         return GetPropertyIC::CanAttachCallGetter;
  1178     return GetPropertyIC::CanAttachNone;
  1181 bool
  1182 GetPropertyIC::allowArrayLength(Context cx, HandleObject obj) const
  1184     if (!idempotent())
  1185         return true;
  1187     uint32_t locationIndex, numLocations;
  1188     getLocationInfo(&locationIndex, &numLocations);
  1190     IonScript *ion = GetTopIonJSScript(cx)->ionScript();
  1191     CacheLocation *locs = ion->getCacheLocs(locationIndex);
  1192     for (size_t i = 0; i < numLocations; i++) {
  1193         CacheLocation &curLoc = locs[i];
  1194         types::StackTypeSet *bcTypes =
  1195             types::TypeScript::BytecodeTypes(curLoc.script, curLoc.pc);
  1197         if (!bcTypes->hasType(types::Type::Int32Type()))
  1198             return false;
  1201     return true;
  1204 bool
  1205 GetPropertyIC::tryAttachNative(JSContext *cx, IonScript *ion, HandleObject obj,
  1206                                HandlePropertyName name, void *returnAddr, bool *emitted)
  1208     JS_ASSERT(canAttachStub());
  1209     JS_ASSERT(!*emitted);
  1211     RootedShape shape(cx);
  1212     RootedObject holder(cx);
  1214     NativeGetPropCacheability type =
  1215         CanAttachNativeGetProp(cx, *this, obj, name, &holder, &shape);
  1216     if (type == CanAttachNone)
  1217         return true;
  1219     *emitted = true;
  1221     MacroAssembler masm(cx, ion, script_, pc_);
  1223     RepatchStubAppender attacher(*this);
  1224     const char *attachKind;
  1226     switch (type) {
  1227       case CanAttachReadSlot:
  1228         GenerateReadSlot(cx, ion, masm, attacher, obj, holder,
  1229                          shape, object(), output());
  1230         attachKind = idempotent() ? "idempotent reading"
  1231                                     : "non idempotent reading";
  1232         break;
  1233       case CanAttachCallGetter:
  1234         if (!GenerateCallGetter(cx, ion, masm, attacher, obj, name, holder, shape,
  1235                                 liveRegs_, object(), output(), returnAddr))
  1237             return false;
  1239         attachKind = "getter call";
  1240         break;
  1241       case CanAttachArrayLength:
  1242         if (!GenerateArrayLength(cx, masm, attacher, obj, object(), output()))
  1243             return false;
  1245         attachKind = "array length";
  1246         break;
  1247       default:
  1248         MOZ_ASSUME_UNREACHABLE("Bad NativeGetPropCacheability");
  1250     return linkAndAttachStub(cx, masm, attacher, ion, attachKind);
  1253 bool
  1254 GetPropertyIC::tryAttachTypedArrayLength(JSContext *cx, IonScript *ion, HandleObject obj,
  1255                                          HandlePropertyName name, bool *emitted)
  1257     JS_ASSERT(canAttachStub());
  1258     JS_ASSERT(!*emitted);
  1260     if (!obj->is<TypedArrayObject>())
  1261         return true;
  1263     if (cx->names().length != name)
  1264         return true;
  1266     if (hasTypedArrayLengthStub())
  1267         return true;
  1269     if (output().type() != MIRType_Value && output().type() != MIRType_Int32) {
  1270         // The next execution should cause an invalidation because the type
  1271         // does not fit.
  1272         return true;
  1275     if (idempotent())
  1276         return true;
  1278     *emitted = true;
  1280     MacroAssembler masm(cx, ion);
  1281     RepatchStubAppender attacher(*this);
  1282     GenerateTypedArrayLength(cx, masm, attacher, obj, object(), output());
  1284     JS_ASSERT(!hasTypedArrayLengthStub_);
  1285     hasTypedArrayLengthStub_ = true;
  1286     return linkAndAttachStub(cx, masm, attacher, ion, "typed array length");
  1290 static bool
  1291 EmitCallProxyGet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
  1292                  PropertyName *name, RegisterSet liveRegs, Register object,
  1293                  TypedOrValueRegister output, jsbytecode *pc, void *returnAddr)
  1295     JS_ASSERT(output.hasValue());
  1296     MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
  1298     // Remaining registers should be free, but we need to use |object| still
  1299     // so leave it alone.
  1300     RegisterSet regSet(RegisterSet::All());
  1301     regSet.take(AnyRegister(object));
  1303     // Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
  1304     //            MutableHandleValue vp)
  1305     Register argJSContextReg = regSet.takeGeneral();
  1306     Register argProxyReg     = regSet.takeGeneral();
  1307     Register argIdReg        = regSet.takeGeneral();
  1308     Register argVpReg        = regSet.takeGeneral();
  1310     Register scratch         = regSet.takeGeneral();
  1312     void *getFunction = JSOp(*pc) == JSOP_CALLPROP                      ?
  1313                             JS_FUNC_TO_DATA_PTR(void *, Proxy::callProp) :
  1314                             JS_FUNC_TO_DATA_PTR(void *, Proxy::get);
  1316     // Push stubCode for marking.
  1317     attacher.pushStubCodePointer(masm);
  1319     // Push args on stack first so we can take pointers to make handles.
  1320     masm.Push(UndefinedValue());
  1321     masm.movePtr(StackPointer, argVpReg);
  1323     RootedId propId(cx, AtomToId(name));
  1324     masm.Push(propId, scratch);
  1325     masm.movePtr(StackPointer, argIdReg);
  1327     // Pushing object and receiver.  Both are the same, so Handle to one is equivalent to
  1328     // handle to other.
  1329     masm.Push(object);
  1330     masm.Push(object);
  1331     masm.movePtr(StackPointer, argProxyReg);
  1333     masm.loadJSContext(argJSContextReg);
  1335     if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
  1336         return false;
  1337     masm.enterFakeExitFrame(ION_FRAME_OOL_PROXY);
  1339     // Make the call.
  1340     masm.setupUnalignedABICall(5, scratch);
  1341     masm.passABIArg(argJSContextReg);
  1342     masm.passABIArg(argProxyReg);
  1343     masm.passABIArg(argProxyReg);
  1344     masm.passABIArg(argIdReg);
  1345     masm.passABIArg(argVpReg);
  1346     masm.callWithABI(getFunction);
  1348     // Test for failure.
  1349     masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
  1351     // Load the outparam vp[0] into output register(s).
  1352     Address outparam(StackPointer, IonOOLProxyExitFrameLayout::offsetOfResult());
  1353     masm.loadTypedOrValue(outparam, output);
  1355     // masm.leaveExitFrame & pop locals
  1356     masm.adjustStack(IonOOLProxyExitFrameLayout::Size());
  1358     masm.icRestoreLive(liveRegs, aic);
  1359     return true;
  1362 bool
  1363 GetPropertyIC::tryAttachDOMProxyShadowed(JSContext *cx, IonScript *ion,
  1364                                          HandleObject obj, void *returnAddr,
  1365                                          bool *emitted)
  1367     JS_ASSERT(canAttachStub());
  1368     JS_ASSERT(!*emitted);
  1369     JS_ASSERT(IsCacheableDOMProxy(obj));
  1370     JS_ASSERT(monitoredResult());
  1371     JS_ASSERT(output().hasValue());
  1373     if (idempotent())
  1374         return true;
  1376     *emitted = true;
  1378     Label failures;
  1379     MacroAssembler masm(cx, ion, script_, pc_);
  1380     RepatchStubAppender attacher(*this);
  1382     // Guard on the shape of the object.
  1383     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
  1384                                    Address(object(), JSObject::offsetOfShape()),
  1385                                    ImmGCPtr(obj->lastProperty()),
  1386                                    &failures);
  1388     // Make sure object is a DOMProxy
  1389     GenerateDOMProxyChecks(cx, masm, obj, name(), object(), &failures,
  1390                            /*skipExpandoCheck=*/true);
  1392     if (!EmitCallProxyGet(cx, masm, attacher, name(), liveRegs_, object(), output(),
  1393                           pc(), returnAddr))
  1395         return false;
  1398     // Success.
  1399     attacher.jumpRejoin(masm);
  1401     // Failure.
  1402     masm.bind(&failures);
  1403     attacher.jumpNextStub(masm);
  1405     return linkAndAttachStub(cx, masm, attacher, ion, "list base shadowed get");
  1408 bool
  1409 GetPropertyIC::tryAttachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj,
  1410                                            HandlePropertyName name, bool resetNeeded,
  1411                                            void *returnAddr, bool *emitted)
  1413     JS_ASSERT(canAttachStub());
  1414     JS_ASSERT(!*emitted);
  1415     JS_ASSERT(IsCacheableDOMProxy(obj));
  1416     JS_ASSERT(monitoredResult());
  1417     JS_ASSERT(output().hasValue());
  1419     RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull());
  1420     RootedObject holder(cx);
  1421     RootedShape shape(cx);
  1423     NativeGetPropCacheability canCache =
  1424         CanAttachNativeGetProp(cx, *this, checkObj, name, &holder, &shape,
  1425                                /* skipArrayLen = */true);
  1426     JS_ASSERT(canCache != CanAttachArrayLength);
  1428     if (canCache == CanAttachNone)
  1429         return true;
  1431     // Make sure we observe our invariants if we're gonna deoptimize.
  1432     if (!holder && idempotent())
  1433         return true;
  1435     *emitted = true;
  1437     if (resetNeeded) {
  1438         // If we know that we have a DoesntShadowUnique object, then
  1439         // we reset the cache to clear out an existing IC for the object
  1440         // (if there is one). The generation is a constant in the generated
  1441         // code and we will not have the same generation again for this
  1442         // object, so the generation check in the existing IC would always
  1443         // fail anyway.
  1444         reset();
  1447     Label failures;
  1448     MacroAssembler masm(cx, ion, script_, pc_);
  1449     RepatchStubAppender attacher(*this);
  1451     // Guard on the shape of the object.
  1452     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
  1453                                    Address(object(), JSObject::offsetOfShape()),
  1454                                    ImmGCPtr(obj->lastProperty()),
  1455                                    &failures);
  1457     // Make sure object is a DOMProxy proxy
  1458     GenerateDOMProxyChecks(cx, masm, obj, name, object(), &failures);
  1460     if (holder) {
  1461         // Found the property on the prototype chain. Treat it like a native
  1462         // getprop.
  1463         Register scratchReg = output().valueReg().scratchReg();
  1464         GeneratePrototypeGuards(cx, ion, masm, obj, holder, object(), scratchReg, &failures);
  1466         // Rename scratch for clarity.
  1467         Register holderReg = scratchReg;
  1469         // Guard on the holder of the property
  1470         masm.moveNurseryPtr(ImmMaybeNurseryPtr(holder), holderReg);
  1471         masm.branchPtr(Assembler::NotEqual,
  1472                     Address(holderReg, JSObject::offsetOfShape()),
  1473                     ImmGCPtr(holder->lastProperty()),
  1474                     &failures);
  1476         if (canCache == CanAttachReadSlot) {
  1477             EmitLoadSlot(masm, holder, shape, holderReg, output(), scratchReg);
  1478         } else {
  1479             // EmitGetterCall() expects |obj| to be the object the property is
  1480             // on to do some checks. Since we actually looked at checkObj, and
  1481             // no extra guards will be generated, we can just pass that instead.
  1482             JS_ASSERT(canCache == CanAttachCallGetter);
  1483             JS_ASSERT(!idempotent());
  1484             if (!EmitGetterCall(cx, masm, attacher, checkObj, holder, shape, liveRegs_,
  1485                                 object(), scratchReg, output(), returnAddr))
  1487                 return false;
  1490     } else {
  1491         // Property was not found on the prototype chain. Deoptimize down to
  1492         // proxy get call
  1493         JS_ASSERT(!idempotent());
  1494         if (!EmitCallProxyGet(cx, masm, attacher, name, liveRegs_, object(), output(),
  1495                               pc(), returnAddr))
  1497             return false;
  1501     attacher.jumpRejoin(masm);
  1502     masm.bind(&failures);
  1503     attacher.jumpNextStub(masm);
  1505     return linkAndAttachStub(cx, masm, attacher, ion, "unshadowed proxy get");
  1508 bool
  1509 GetPropertyIC::tryAttachProxy(JSContext *cx, IonScript *ion, HandleObject obj,
  1510                               HandlePropertyName name, void *returnAddr,
  1511                               bool *emitted)
  1513     JS_ASSERT(canAttachStub());
  1514     JS_ASSERT(!*emitted);
  1516     if (!obj->is<ProxyObject>())
  1517         return true;
  1519     // TI can't be sure about our properties, so make sure anything
  1520     // we return can be monitored directly.
  1521     if (!monitoredResult())
  1522         return true;
  1524     // Skim off DOM proxies.
  1525     if (IsCacheableDOMProxy(obj)) {
  1526         RootedId id(cx, NameToId(name));
  1527         DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, obj, id);
  1528         if (shadows == ShadowCheckFailed)
  1529             return false;
  1530         if (shadows == Shadows)
  1531             return tryAttachDOMProxyShadowed(cx, ion, obj, returnAddr, emitted);
  1533         return tryAttachDOMProxyUnshadowed(cx, ion, obj, name, shadows == DoesntShadowUnique,
  1534                                            returnAddr, emitted);
  1537     return tryAttachGenericProxy(cx, ion, obj, name, returnAddr, emitted);
  1540 static void
  1541 GenerateProxyClassGuards(MacroAssembler &masm, Register object, Register scratchReg,
  1542                          Label *failures)
  1544     masm.loadObjClass(object, scratchReg);
  1545     masm.branchTest32(Assembler::Zero,
  1546                       Address(scratchReg, Class::offsetOfFlags()),
  1547                       Imm32(JSCLASS_IS_PROXY), failures);
  1550 bool
  1551 GetPropertyIC::tryAttachGenericProxy(JSContext *cx, IonScript *ion, HandleObject obj,
  1552                                      HandlePropertyName name, void *returnAddr,
  1553                                      bool *emitted)
  1555     JS_ASSERT(canAttachStub());
  1556     JS_ASSERT(!*emitted);
  1557     JS_ASSERT(obj->is<ProxyObject>());
  1558     JS_ASSERT(monitoredResult());
  1559     JS_ASSERT(output().hasValue());
  1561     if (hasGenericProxyStub())
  1562         return true;
  1564     if (idempotent())
  1565         return true;
  1567     *emitted = true;
  1569     Label failures;
  1570     MacroAssembler masm(cx, ion, script_, pc_);
  1571     RepatchStubAppender attacher(*this);
  1573     Register scratchReg = output().valueReg().scratchReg();
  1575     GenerateProxyClassGuards(masm, object(), scratchReg, &failures);
  1577     // Ensure that the incoming object is not a DOM proxy, so that we can get to
  1578     // the specialized stubs
  1579     masm.branchTestProxyHandlerFamily(Assembler::Equal, object(), scratchReg,
  1580                                       GetDOMProxyHandlerFamily(), &failures);
  1582     if (!EmitCallProxyGet(cx, masm, attacher, name, liveRegs_, object(), output(),
  1583                           pc(), returnAddr))
  1585         return false;
  1588     attacher.jumpRejoin(masm);
  1590     masm.bind(&failures);
  1591     attacher.jumpNextStub(masm);
  1593     JS_ASSERT(!hasGenericProxyStub_);
  1594     hasGenericProxyStub_ = true;
  1596     return linkAndAttachStub(cx, masm, attacher, ion, "Generic Proxy get");
  1599 bool
  1600 GetPropertyIC::tryAttachArgumentsLength(JSContext *cx, IonScript *ion, HandleObject obj,
  1601                                         HandlePropertyName name, bool *emitted)
  1603     JS_ASSERT(canAttachStub());
  1604     JS_ASSERT(!*emitted);
  1606     if (name != cx->names().length)
  1607         return true;
  1608     if (!IsOptimizableArgumentsObjectForLength(obj))
  1609         return true;
  1611     MIRType outputType = output().type();
  1612     if (!(outputType == MIRType_Value || outputType == MIRType_Int32))
  1613         return true;
  1615     if (hasArgumentsLengthStub(obj->is<StrictArgumentsObject>()))
  1616         return true;
  1618     *emitted = true;
  1620     JS_ASSERT(!idempotent());
  1622     Label failures;
  1623     MacroAssembler masm(cx, ion);
  1624     RepatchStubAppender attacher(*this);
  1626     Register tmpReg;
  1627     if (output().hasValue()) {
  1628         tmpReg = output().valueReg().scratchReg();
  1629     } else {
  1630         JS_ASSERT(output().type() == MIRType_Int32);
  1631         tmpReg = output().typedReg().gpr();
  1633     JS_ASSERT(object() != tmpReg);
  1635     const Class *clasp = obj->is<StrictArgumentsObject>() ? &StrictArgumentsObject::class_
  1636                                                           : &NormalArgumentsObject::class_;
  1638     masm.branchTestObjClass(Assembler::NotEqual, object(), tmpReg, clasp, &failures);
  1640     // Get initial ArgsObj length value, test if length has been overridden.
  1641     masm.unboxInt32(Address(object(), ArgumentsObject::getInitialLengthSlotOffset()), tmpReg);
  1642     masm.branchTest32(Assembler::NonZero, tmpReg, Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
  1643                       &failures);
  1645     masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), tmpReg);
  1647     // If output is Int32, result is already in right place, otherwise box it into output.
  1648     if (output().hasValue())
  1649         masm.tagValue(JSVAL_TYPE_INT32, tmpReg, output().valueReg());
  1651     // Success.
  1652     attacher.jumpRejoin(masm);
  1654     // Failure.
  1655     masm.bind(&failures);
  1656     attacher.jumpNextStub(masm);
  1658     if (obj->is<StrictArgumentsObject>()) {
  1659         JS_ASSERT(!hasStrictArgumentsLengthStub_);
  1660         hasStrictArgumentsLengthStub_ = true;
  1661         return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj length (strict)");
  1664     JS_ASSERT(!hasNormalArgumentsLengthStub_);
  1665     hasNormalArgumentsLengthStub_ = true;
  1666     return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj length (normal)");
  1669 bool
  1670 GetPropertyIC::tryAttachStub(JSContext *cx, IonScript *ion, HandleObject obj,
  1671                              HandlePropertyName name, void *returnAddr, bool *emitted)
  1673     JS_ASSERT(!*emitted);
  1675     if (!canAttachStub())
  1676         return true;
  1678     if (!*emitted && !tryAttachArgumentsLength(cx, ion, obj, name, emitted))
  1679         return false;
  1681     if (!*emitted && !tryAttachProxy(cx, ion, obj, name, returnAddr, emitted))
  1682         return false;
  1684     if (!*emitted && !tryAttachNative(cx, ion, obj, name, returnAddr, emitted))
  1685         return false;
  1687     if (!*emitted && !tryAttachTypedArrayLength(cx, ion, obj, name, emitted))
  1688         return false;
  1690     return true;
  1693 /* static */ bool
  1694 GetPropertyIC::update(JSContext *cx, size_t cacheIndex,
  1695                       HandleObject obj, MutableHandleValue vp)
  1697     void *returnAddr;
  1698     RootedScript topScript(cx, GetTopIonJSScript(cx, &returnAddr));
  1699     IonScript *ion = topScript->ionScript();
  1701     GetPropertyIC &cache = ion->getCache(cacheIndex).toGetProperty();
  1702     RootedPropertyName name(cx, cache.name());
  1704     // Override the return value if we are invalidated (bug 728188).
  1705     AutoDetectInvalidation adi(cx, vp.address(), ion);
  1707     // If the cache is idempotent, we will redo the op in the interpreter.
  1708     if (cache.idempotent())
  1709         adi.disable();
  1711     // For now, just stop generating new stubs once we hit the stub count
  1712     // limit. Once we can make calls from within generated stubs, a new call
  1713     // stub will be generated instead and the previous stubs unlinked.
  1714     bool emitted = false;
  1715     if (!cache.tryAttachStub(cx, ion, obj, name, returnAddr, &emitted))
  1716         return false;
  1718     if (cache.idempotent() && !emitted) {
  1719         // Invalidate the cache if the property was not found, or was found on
  1720         // a non-native object. This ensures:
  1721         // 1) The property read has no observable side-effects.
  1722         // 2) There's no need to dynamically monitor the return type. This would
  1723         //    be complicated since (due to GVN) there can be multiple pc's
  1724         //    associated with a single idempotent cache.
  1725         IonSpew(IonSpew_InlineCaches, "Invalidating from idempotent cache %s:%d",
  1726                 topScript->filename(), topScript->lineno());
  1728         topScript->setInvalidatedIdempotentCache();
  1730         // Do not re-invalidate if the lookup already caused invalidation.
  1731         if (!topScript->hasIonScript())
  1732             return true;
  1734         return Invalidate(cx, topScript);
  1737     RootedId id(cx, NameToId(name));
  1738     if (!JSObject::getGeneric(cx, obj, obj, id, vp))
  1739         return false;
  1741     if (!cache.idempotent()) {
  1742         RootedScript script(cx);
  1743         jsbytecode *pc;
  1744         cache.getScriptedLocation(&script, &pc);
  1746         // If the cache is idempotent, the property exists so we don't have to
  1747         // call __noSuchMethod__.
  1749 #if JS_HAS_NO_SUCH_METHOD
  1750         // Handle objects with __noSuchMethod__.
  1751         if (JSOp(*pc) == JSOP_CALLPROP && MOZ_UNLIKELY(vp.isUndefined())) {
  1752             if (!OnUnknownMethod(cx, obj, IdToValue(id), vp))
  1753                 return false;
  1755 #endif
  1757         // Monitor changes to cache entry.
  1758         if (!cache.monitoredResult())
  1759             types::TypeScript::Monitor(cx, script, pc, vp);
  1762     return true;
  1765 void
  1766 GetPropertyIC::reset()
  1768     RepatchIonCache::reset();
  1769     hasTypedArrayLengthStub_ = false;
  1770     hasStrictArgumentsLengthStub_ = false;
  1771     hasNormalArgumentsLengthStub_ = false;
  1772     hasGenericProxyStub_ = false;
  1775 bool
  1776 ParallelIonCache::initStubbedShapes(JSContext *cx)
  1778     JS_ASSERT(isAllocated());
  1779     if (!stubbedShapes_) {
  1780         stubbedShapes_ = cx->new_<ShapeSet>(cx);
  1781         return stubbedShapes_ && stubbedShapes_->init();
  1783     return true;
  1786 bool
  1787 ParallelIonCache::hasOrAddStubbedShape(LockedJSContext &cx, Shape *shape, bool *alreadyStubbed)
  1789     // Check if we have already stubbed the current object to avoid
  1790     // attaching a duplicate stub.
  1791     if (!initStubbedShapes(cx))
  1792         return false;
  1793     ShapeSet::AddPtr p = stubbedShapes_->lookupForAdd(shape);
  1794     if ((*alreadyStubbed = !!p))
  1795         return true;
  1796     return stubbedShapes_->add(p, shape);
  1799 void
  1800 ParallelIonCache::reset()
  1802     DispatchIonCache::reset();
  1803     if (stubbedShapes_)
  1804         stubbedShapes_->clear();
  1807 void
  1808 ParallelIonCache::destroy()
  1810     DispatchIonCache::destroy();
  1811     js_delete(stubbedShapes_);
  1814 void
  1815 GetPropertyParIC::reset()
  1817     ParallelIonCache::reset();
  1818     hasTypedArrayLengthStub_ = false;
  1821 bool
  1822 GetPropertyParIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj,
  1823                                  JSObject *holder, Shape *shape)
  1825     // Ready to generate the read slot stub.
  1826     DispatchStubPrepender attacher(*this);
  1827     MacroAssembler masm(cx, ion);
  1828     GenerateReadSlot(cx, ion, masm, attacher, obj, holder, shape, object(), output());
  1830     return linkAndAttachStub(cx, masm, attacher, ion, "parallel reading");
  1833 bool
  1834 GetPropertyParIC::attachArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj)
  1836     MacroAssembler masm(cx, ion);
  1837     DispatchStubPrepender attacher(*this);
  1838     if (!GenerateArrayLength(cx, masm, attacher, obj, object(), output()))
  1839         return false;
  1841     return linkAndAttachStub(cx, masm, attacher, ion, "parallel array length");
  1844 bool
  1845 GetPropertyParIC::attachTypedArrayLength(LockedJSContext &cx, IonScript *ion, JSObject *obj)
  1847     MacroAssembler masm(cx, ion);
  1848     DispatchStubPrepender attacher(*this);
  1849     GenerateTypedArrayLength(cx, masm, attacher, obj, object(), output());
  1851     JS_ASSERT(!hasTypedArrayLengthStub_);
  1852     hasTypedArrayLengthStub_ = true;
  1853     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array length");
  1856 bool
  1857 GetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex,
  1858                          HandleObject obj, MutableHandleValue vp)
  1860     IonScript *ion = GetTopIonJSScript(cx)->parallelIonScript();
  1861     GetPropertyParIC &cache = ion->getCache(cacheIndex).toGetPropertyPar();
  1863     // Grab the property early, as the pure path is fast anyways and doesn't
  1864     // need a lock. If we can't do it purely, bail out of parallel execution.
  1865     if (!GetPropertyPure(cx, obj, NameToId(cache.name()), vp.address()))
  1866         return false;
  1868     // Avoid unnecessary locking if cannot attach stubs.
  1869     if (!cache.canAttachStub())
  1870         return true;
  1873         // Lock the context before mutating the cache. Ideally we'd like to do
  1874         // finer-grained locking, with one lock per cache. However, generating
  1875         // new jitcode uses a global ExecutableAllocator tied to the runtime.
  1876         LockedJSContext ncx(cx);
  1878         if (cache.canAttachStub()) {
  1879             bool alreadyStubbed;
  1880             if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
  1881                 return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  1882             if (alreadyStubbed)
  1883                 return true;
  1885             // See note about the stub limit in GetPropertyCache.
  1886             bool attachedStub = false;
  1889                 RootedShape shape(ncx);
  1890                 RootedObject holder(ncx);
  1891                 RootedPropertyName name(ncx, cache.name());
  1893                 GetPropertyIC::NativeGetPropCacheability canCache =
  1894                     CanAttachNativeGetProp(ncx, cache, obj, name, &holder, &shape);
  1896                 if (canCache == GetPropertyIC::CanAttachReadSlot) {
  1897                     if (!cache.attachReadSlot(ncx, ion, obj, holder, shape))
  1898                         return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  1899                     attachedStub = true;
  1902                 if (!attachedStub && canCache == GetPropertyIC::CanAttachArrayLength) {
  1903                     if (!cache.attachArrayLength(ncx, ion, obj))
  1904                         return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  1905                     attachedStub = true;
  1909             if (!attachedStub && !cache.hasTypedArrayLengthStub() &&
  1910                 obj->is<TypedArrayObject>() && cx->names().length == cache.name() &&
  1911                 (cache.output().type() == MIRType_Value || cache.output().type() == MIRType_Int32))
  1913                 if (!cache.attachTypedArrayLength(ncx, ion, obj))
  1914                     return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  1915                 attachedStub = true;
  1920     return true;
  1923 void
  1924 IonCache::disable()
  1926     reset();
  1927     this->disabled_ = 1;
  1930 void
  1931 IonCache::reset()
  1933     this->stubCount_ = 0;
  1936 void
  1937 IonCache::destroy()
  1941 static void
  1942 GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
  1943                 JSObject *obj, Shape *shape, Register object, ConstantOrRegister value,
  1944                 bool needsTypeBarrier, bool checkTypeset)
  1946     JS_ASSERT(obj->isNative());
  1948     Label failures, barrierFailure;
  1949     masm.branchPtr(Assembler::NotEqual,
  1950                    Address(object, JSObject::offsetOfShape()),
  1951                    ImmGCPtr(obj->lastProperty()), &failures);
  1953     // Guard that the incoming value is in the type set for the property
  1954     // if a type barrier is required.
  1955     if (needsTypeBarrier) {
  1956         // We can't do anything that would change the HeapTypeSet, so
  1957         // just guard that it's already there.
  1959         // Obtain and guard on the TypeObject of the object.
  1960         types::TypeObject *type = obj->type();
  1961         masm.branchPtr(Assembler::NotEqual,
  1962                        Address(object, JSObject::offsetOfType()),
  1963                        ImmGCPtr(type), &failures);
  1965         if (checkTypeset) {
  1966             TypedOrValueRegister valReg = value.reg();
  1967             types::HeapTypeSet *propTypes = type->maybeGetProperty(shape->propid());
  1968             JS_ASSERT(propTypes);
  1969             JS_ASSERT(!propTypes->unknown());
  1971             Register scratchReg = object;
  1972             masm.push(scratchReg);
  1974             masm.guardTypeSet(valReg, propTypes, scratchReg, &barrierFailure);
  1975             masm.pop(object);
  1979     if (obj->isFixedSlot(shape->slot())) {
  1980         Address addr(object, JSObject::getFixedSlotOffset(shape->slot()));
  1982         if (cx->zone()->needsBarrier())
  1983             masm.callPreBarrier(addr, MIRType_Value);
  1985         masm.storeConstantOrRegister(value, addr);
  1986     } else {
  1987         Register slotsReg = object;
  1988         masm.loadPtr(Address(object, JSObject::offsetOfSlots()), slotsReg);
  1990         Address addr(slotsReg, obj->dynamicSlotIndex(shape->slot()) * sizeof(Value));
  1992         if (cx->zone()->needsBarrier())
  1993             masm.callPreBarrier(addr, MIRType_Value);
  1995         masm.storeConstantOrRegister(value, addr);
  1998     attacher.jumpRejoin(masm);
  2000     if (barrierFailure.used()) {
  2001         masm.bind(&barrierFailure);
  2002         masm.pop(object);
  2005     masm.bind(&failures);
  2006     attacher.jumpNextStub(masm);
  2009 bool
  2010 SetPropertyIC::attachSetSlot(JSContext *cx, IonScript *ion, HandleObject obj,
  2011                              HandleShape shape, bool checkTypeset)
  2013     MacroAssembler masm(cx, ion);
  2014     RepatchStubAppender attacher(*this);
  2015     GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(),
  2016                     checkTypeset);
  2017     return linkAndAttachStub(cx, masm, attacher, ion, "setting");
  2020 static bool
  2021 IsCacheableSetPropCallNative(HandleObject obj, HandleObject holder, HandleShape shape)
  2023     JS_ASSERT(obj->isNative());
  2025     if (!shape || !IsCacheableProtoChain(obj, holder))
  2026         return false;
  2028     return shape->hasSetterValue() && shape->setterObject() &&
  2029            shape->setterObject()->is<JSFunction>() &&
  2030            shape->setterObject()->as<JSFunction>().isNative();
  2033 static bool
  2034 IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, HandleShape shape)
  2036     JS_ASSERT(obj->isNative());
  2038     if (!shape)
  2039         return false;
  2041     if (!IsCacheableProtoChain(obj, holder))
  2042         return false;
  2044     if (shape->hasSlot())
  2045         return false;
  2047     if (shape->hasDefaultSetter())
  2048         return false;
  2050     if (shape->hasSetterValue())
  2051         return false;
  2053     // Despite the vehement claims of Shape.h that writable() is only
  2054     // relevant for data descriptors, some PropertyOp setters care
  2055     // desperately about its value. The flag should be always true, apart
  2056     // from these rare instances.
  2057     if (!shape->writable())
  2058         return false;
  2060     return true;
  2063 static bool
  2064 EmitCallProxySet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
  2065                  HandleId propId, RegisterSet liveRegs, Register object,
  2066                  ConstantOrRegister value, void *returnAddr, bool strict)
  2068     MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
  2070     // Remaining registers should be free, but we need to use |object| still
  2071     // so leave it alone.
  2072     RegisterSet regSet(RegisterSet::All());
  2073     regSet.take(AnyRegister(object));
  2075     // Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
  2076     //            bool strict, MutableHandleValue vp)
  2077     Register argJSContextReg = regSet.takeGeneral();
  2078     Register argProxyReg     = regSet.takeGeneral();
  2079     Register argIdReg        = regSet.takeGeneral();
  2080     Register argVpReg        = regSet.takeGeneral();
  2081     Register argStrictReg    = regSet.takeGeneral();
  2083     Register scratch         = regSet.takeGeneral();
  2085     // Push stubCode for marking.
  2086     attacher.pushStubCodePointer(masm);
  2088     // Push args on stack first so we can take pointers to make handles.
  2089     masm.Push(value);
  2090     masm.movePtr(StackPointer, argVpReg);
  2092     masm.Push(propId, scratch);
  2093     masm.movePtr(StackPointer, argIdReg);
  2095     // Pushing object and receiver.  Both are the same, so Handle to one is equivalent to
  2096     // handle to other.
  2097     masm.Push(object);
  2098     masm.Push(object);
  2099     masm.movePtr(StackPointer, argProxyReg);
  2101     masm.loadJSContext(argJSContextReg);
  2102     masm.move32(Imm32(strict? 1 : 0), argStrictReg);
  2104     if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
  2105         return false;
  2106     masm.enterFakeExitFrame(ION_FRAME_OOL_PROXY);
  2108     // Make the call.
  2109     masm.setupUnalignedABICall(6, scratch);
  2110     masm.passABIArg(argJSContextReg);
  2111     masm.passABIArg(argProxyReg);
  2112     masm.passABIArg(argProxyReg);
  2113     masm.passABIArg(argIdReg);
  2114     masm.passABIArg(argStrictReg);
  2115     masm.passABIArg(argVpReg);
  2116     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Proxy::set));
  2118     // Test for failure.
  2119     masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
  2121     // masm.leaveExitFrame & pop locals
  2122     masm.adjustStack(IonOOLProxyExitFrameLayout::Size());
  2124     masm.icRestoreLive(liveRegs, aic);
  2125     return true;
  2128 bool
  2129 SetPropertyIC::attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAddr)
  2131     JS_ASSERT(!hasGenericProxyStub());
  2133     MacroAssembler masm(cx, ion, script_, pc_);
  2134     RepatchStubAppender attacher(*this);
  2136     Label failures;
  2138         Label proxyFailures;
  2139         Label proxySuccess;
  2141         RegisterSet regSet(RegisterSet::All());
  2142         regSet.take(AnyRegister(object()));
  2143         if (!value().constant())
  2144             regSet.takeUnchecked(value().reg());
  2146         Register scratch = regSet.takeGeneral();
  2147         masm.push(scratch);
  2149         GenerateProxyClassGuards(masm, object(), scratch, &proxyFailures);
  2151         // Remove the DOM proxies. They'll take care of themselves so this stub doesn't
  2152         // catch too much. The failure case is actually Equal. Fall through to the failure code.
  2153         masm.branchTestProxyHandlerFamily(Assembler::NotEqual, object(), scratch,
  2154                                           GetDOMProxyHandlerFamily(), &proxySuccess);
  2156         masm.bind(&proxyFailures);
  2157         masm.pop(scratch);
  2158         // Unify the point of failure to allow for later DOM proxy handling.
  2159         masm.jump(&failures);
  2161         masm.bind(&proxySuccess);
  2162         masm.pop(scratch);
  2165     RootedId propId(cx, AtomToId(name()));
  2166     if (!EmitCallProxySet(cx, masm, attacher, propId, liveRegs_, object(), value(),
  2167                           returnAddr, strict()))
  2169         return false;
  2172     attacher.jumpRejoin(masm);
  2174     masm.bind(&failures);
  2175     attacher.jumpNextStub(masm);
  2177     JS_ASSERT(!hasGenericProxyStub_);
  2178     hasGenericProxyStub_ = true;
  2180     return linkAndAttachStub(cx, masm, attacher, ion, "generic proxy set");
  2183 bool
  2184 SetPropertyIC::attachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObject obj,
  2185                                         void *returnAddr)
  2187     JS_ASSERT(IsCacheableDOMProxy(obj));
  2189     Label failures;
  2190     MacroAssembler masm(cx, ion, script_, pc_);
  2191     RepatchStubAppender attacher(*this);
  2193     // Guard on the shape of the object.
  2194     masm.branchPtr(Assembler::NotEqual,
  2195                    Address(object(), JSObject::offsetOfShape()),
  2196                    ImmGCPtr(obj->lastProperty()), &failures);
  2198     // Make sure object is a DOMProxy
  2199     GenerateDOMProxyChecks(cx, masm, obj, name(), object(), &failures,
  2200                            /*skipExpandoCheck=*/true);
  2202     RootedId propId(cx, AtomToId(name()));
  2203     if (!EmitCallProxySet(cx, masm, attacher, propId, liveRegs_, object(),
  2204                           value(), returnAddr, strict()))
  2206         return false;
  2209     // Success.
  2210     attacher.jumpRejoin(masm);
  2212     // Failure.
  2213     masm.bind(&failures);
  2214     attacher.jumpNextStub(masm);
  2216     return linkAndAttachStub(cx, masm, attacher, ion, "DOM proxy shadowed set");
  2219 static bool
  2220 GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
  2221                    IonCache::StubAttacher &attacher, HandleObject obj,
  2222                    HandleObject holder, HandleShape shape, bool strict, Register object,
  2223                    ConstantOrRegister value, Label *failure, RegisterSet liveRegs,
  2224                    void *returnAddr)
  2226     // Generate prototype guards if needed.
  2227     // Take a scratch register for use, save on stack.
  2229         RegisterSet regSet(RegisterSet::All());
  2230         regSet.take(AnyRegister(object));
  2231         if (!value.constant())
  2232             regSet.takeUnchecked(value.reg());
  2233         Register scratchReg = regSet.takeGeneral();
  2234         masm.push(scratchReg);
  2236         Label protoFailure;
  2237         Label protoSuccess;
  2239         // Generate prototype/shape guards.
  2240         if (obj != holder)
  2241             GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, &protoFailure);
  2243         masm.moveNurseryPtr(ImmMaybeNurseryPtr(holder), scratchReg);
  2244         masm.branchPtr(Assembler::NotEqual,
  2245                        Address(scratchReg, JSObject::offsetOfShape()),
  2246                        ImmGCPtr(holder->lastProperty()),
  2247                        &protoFailure);
  2249         masm.jump(&protoSuccess);
  2251         masm.bind(&protoFailure);
  2252         masm.pop(scratchReg);
  2253         masm.jump(failure);
  2255         masm.bind(&protoSuccess);
  2256         masm.pop(scratchReg);
  2259     // Good to go for invoking setter.
  2261     MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
  2263     // Remaining registers should basically be free, but we need to use |object| still
  2264     // so leave it alone.
  2265     RegisterSet regSet(RegisterSet::All());
  2266     regSet.take(AnyRegister(object));
  2268     // This is a slower stub path, and we're going to be doing a call anyway.  Don't need
  2269     // to try so hard to not use the stack.  Scratch regs are just taken from the register
  2270     // set not including the input, current value saved on the stack, and restored when
  2271     // we're done with it.
  2272     //
  2273     // Be very careful not to use any of these before value is pushed, since they
  2274     // might shadow.
  2275     Register scratchReg     = regSet.takeGeneral();
  2276     Register argJSContextReg = regSet.takeGeneral();
  2277     Register argVpReg        = regSet.takeGeneral();
  2279     bool callNative = IsCacheableSetPropCallNative(obj, holder, shape);
  2280     JS_ASSERT_IF(!callNative, IsCacheableSetPropCallPropertyOp(obj, holder, shape));
  2282     if (callNative) {
  2283         JS_ASSERT(shape->hasSetterValue() && shape->setterObject() &&
  2284                   shape->setterObject()->is<JSFunction>());
  2285         JSFunction *target = &shape->setterObject()->as<JSFunction>();
  2287         JS_ASSERT(target->isNative());
  2289         Register argUintNReg = regSet.takeGeneral();
  2291         // Set up the call:
  2292         //  bool (*)(JSContext *, unsigned, Value *vp)
  2293         // vp[0] is callee/outparam
  2294         // vp[1] is |this|
  2295         // vp[2] is the value
  2297         // Build vp and move the base into argVpReg.
  2298         masm.Push(value);
  2299         masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object)));
  2300         masm.Push(ObjectValue(*target));
  2301         masm.movePtr(StackPointer, argVpReg);
  2303         // Preload other regs
  2304         masm.loadJSContext(argJSContextReg);
  2305         masm.move32(Imm32(1), argUintNReg);
  2307         // Push data for GC marking
  2308         masm.Push(argUintNReg);
  2309         attacher.pushStubCodePointer(masm);
  2311         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
  2312             return false;
  2313         masm.enterFakeExitFrame(ION_FRAME_OOL_NATIVE);
  2315         // Make the call
  2316         masm.setupUnalignedABICall(3, scratchReg);
  2317         masm.passABIArg(argJSContextReg);
  2318         masm.passABIArg(argUintNReg);
  2319         masm.passABIArg(argVpReg);
  2320         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native()));
  2322         // Test for failure.
  2323         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
  2325         // masm.leaveExitFrame & pop locals.
  2326         masm.adjustStack(IonOOLNativeExitFrameLayout::Size(1));
  2327     } else {
  2328         Register argObjReg       = regSet.takeGeneral();
  2329         Register argIdReg        = regSet.takeGeneral();
  2330         Register argStrictReg    = regSet.takeGeneral();
  2332         attacher.pushStubCodePointer(masm);
  2334         StrictPropertyOp target = shape->setterOp();
  2335         JS_ASSERT(target);
  2336         // JSStrictPropertyOp: bool fn(JSContext *cx, HandleObject obj,
  2337         //                               HandleId id, bool strict, MutableHandleValue vp);
  2339         // Push args on stack first so we can take pointers to make handles.
  2340         if (value.constant())
  2341             masm.Push(value.value());
  2342         else
  2343             masm.Push(value.reg());
  2344         masm.movePtr(StackPointer, argVpReg);
  2346         masm.move32(Imm32(strict ? 1 : 0), argStrictReg);
  2348         // push canonical jsid from shape instead of propertyname.
  2349         masm.Push(shape->propid(), argIdReg);
  2350         masm.movePtr(StackPointer, argIdReg);
  2352         masm.Push(object);
  2353         masm.movePtr(StackPointer, argObjReg);
  2355         masm.loadJSContext(argJSContextReg);
  2357         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
  2358             return false;
  2359         masm.enterFakeExitFrame(ION_FRAME_OOL_PROPERTY_OP);
  2361         // Make the call.
  2362         masm.setupUnalignedABICall(5, scratchReg);
  2363         masm.passABIArg(argJSContextReg);
  2364         masm.passABIArg(argObjReg);
  2365         masm.passABIArg(argIdReg);
  2366         masm.passABIArg(argStrictReg);
  2367         masm.passABIArg(argVpReg);
  2368         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target));
  2370         // Test for failure.
  2371         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
  2373         // masm.leaveExitFrame & pop locals.
  2374         masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
  2377     masm.icRestoreLive(liveRegs, aic);
  2378     return true;
  2381 static bool
  2382 IsCacheableDOMProxyUnshadowedSetterCall(JSContext *cx, HandleObject obj, HandlePropertyName name,
  2383                                         MutableHandleObject holder, MutableHandleShape shape,
  2384                                         bool *isSetter)
  2386     JS_ASSERT(IsCacheableDOMProxy(obj));
  2388     *isSetter = false;
  2390     RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull());
  2391     if (!checkObj)
  2392         return true;
  2394     if (!JSObject::lookupProperty(cx, obj, name, holder, shape))
  2395         return false;
  2397     if (!holder)
  2398         return true;
  2400     if (!IsCacheableSetPropCallNative(checkObj, holder, shape) &&
  2401         !IsCacheableSetPropCallPropertyOp(checkObj, holder, shape))
  2403         return true;
  2406     *isSetter = true;
  2407     return true;
  2410 bool
  2411 SetPropertyIC::attachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObject obj,
  2412                                         void *returnAddr)
  2414     JS_ASSERT(IsCacheableDOMProxy(obj));
  2416     Label failures;
  2417     MacroAssembler masm(cx, ion, script_, pc_);
  2418     RepatchStubAppender attacher(*this);
  2420     // Guard on the shape of the object.
  2421     masm.branchPtr(Assembler::NotEqual,
  2422                    Address(object(), JSObject::offsetOfShape()),
  2423                    ImmGCPtr(obj->lastProperty()), &failures);
  2425     // Make sure object is a DOMProxy
  2426     GenerateDOMProxyChecks(cx, masm, obj, name(), object(), &failures);
  2428     RootedPropertyName propName(cx, name());
  2429     RootedObject holder(cx);
  2430     RootedShape shape(cx);
  2431     bool isSetter;
  2432     if (!IsCacheableDOMProxyUnshadowedSetterCall(cx, obj, propName, &holder,
  2433                                                  &shape, &isSetter))
  2435         return false;
  2438     if (isSetter) {
  2439         if (!GenerateCallSetter(cx, ion, masm, attacher, obj, holder, shape, strict(),
  2440                                 object(), value(), &failures, liveRegs_, returnAddr))
  2442             return false;
  2444     } else {
  2445         // Either there was no proto, or the property wasn't appropriately found on it.
  2446         // Drop back to just a call to Proxy::set().
  2447         RootedId propId(cx, AtomToId(name()));
  2448         if (!EmitCallProxySet(cx, masm, attacher, propId, liveRegs_, object(),
  2449                             value(), returnAddr, strict()))
  2451             return false;
  2455     // Success.
  2456     attacher.jumpRejoin(masm);
  2458     // Failure.
  2459     masm.bind(&failures);
  2460     attacher.jumpNextStub(masm);
  2462     return linkAndAttachStub(cx, masm, attacher, ion, "DOM proxy unshadowed set");
  2465 bool
  2466 SetPropertyIC::attachCallSetter(JSContext *cx, IonScript *ion,
  2467                                 HandleObject obj, HandleObject holder, HandleShape shape,
  2468                                 void *returnAddr)
  2470     JS_ASSERT(obj->isNative());
  2472     MacroAssembler masm(cx, ion, script_, pc_);
  2473     RepatchStubAppender attacher(*this);
  2475     Label failure;
  2476     masm.branchPtr(Assembler::NotEqual,
  2477                    Address(object(), JSObject::offsetOfShape()),
  2478                    ImmGCPtr(obj->lastProperty()),
  2479                    &failure);
  2481     if (!GenerateCallSetter(cx, ion, masm, attacher, obj, holder, shape, strict(),
  2482                             object(), value(), &failure, liveRegs_, returnAddr))
  2484         return false;
  2487     // Rejoin jump.
  2488     attacher.jumpRejoin(masm);
  2490     // Jump to next stub.
  2491     masm.bind(&failure);
  2492     attacher.jumpNextStub(masm);
  2494     return linkAndAttachStub(cx, masm, attacher, ion, "setter call");
  2497 static void
  2498 GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
  2499                 JSObject *obj, Shape *oldShape, Register object, ConstantOrRegister value,
  2500                 bool checkTypeset)
  2502     JS_ASSERT(obj->isNative());
  2504     Label failures;
  2506     // Guard the type of the object
  2507     masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfType()),
  2508                    ImmGCPtr(obj->type()), &failures);
  2510     // Guard shapes along prototype chain.
  2511     masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, &failures);
  2513     Label failuresPopObject;
  2514     masm.push(object);    // save object reg because we clobber it
  2516     // Guard that the incoming value is in the type set for the property
  2517     // if a type barrier is required.
  2518     if (checkTypeset) {
  2519         TypedOrValueRegister valReg = value.reg();
  2520         types::TypeObject *type = obj->type();
  2521         types::HeapTypeSet *propTypes = type->maybeGetProperty(obj->lastProperty()->propid());
  2522         JS_ASSERT(propTypes);
  2523         JS_ASSERT(!propTypes->unknown());
  2525         Register scratchReg = object;
  2526         masm.guardTypeSet(valReg, propTypes, scratchReg, &failuresPopObject);
  2527         masm.loadPtr(Address(StackPointer, 0), object);
  2530     JSObject *proto = obj->getProto();
  2531     Register protoReg = object;
  2532     while (proto) {
  2533         Shape *protoShape = proto->lastProperty();
  2535         // load next prototype
  2536         masm.loadObjProto(protoReg, protoReg);
  2538         // Ensure that its shape matches.
  2539         masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, &failuresPopObject);
  2541         proto = proto->getProto();
  2544     masm.pop(object);     // restore object reg
  2546     // Changing object shape.  Write the object's new shape.
  2547     Shape *newShape = obj->lastProperty();
  2548     Address shapeAddr(object, JSObject::offsetOfShape());
  2549     if (cx->zone()->needsBarrier())
  2550         masm.callPreBarrier(shapeAddr, MIRType_Shape);
  2551     masm.storePtr(ImmGCPtr(newShape), shapeAddr);
  2553     // Set the value on the object. Since this is an add, obj->lastProperty()
  2554     // must be the shape of the property we are adding.
  2555     if (obj->isFixedSlot(newShape->slot())) {
  2556         Address addr(object, JSObject::getFixedSlotOffset(newShape->slot()));
  2557         masm.storeConstantOrRegister(value, addr);
  2558     } else {
  2559         Register slotsReg = object;
  2561         masm.loadPtr(Address(object, JSObject::offsetOfSlots()), slotsReg);
  2563         Address addr(slotsReg, obj->dynamicSlotIndex(newShape->slot()) * sizeof(Value));
  2564         masm.storeConstantOrRegister(value, addr);
  2567     // Success.
  2568     attacher.jumpRejoin(masm);
  2570     // Failure.
  2571     masm.bind(&failuresPopObject);
  2572     masm.pop(object);
  2573     masm.bind(&failures);
  2575     attacher.jumpNextStub(masm);
  2578 bool
  2579 SetPropertyIC::attachAddSlot(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldShape,
  2580                              bool checkTypeset)
  2582     JS_ASSERT_IF(!needsTypeBarrier(), !checkTypeset);
  2584     MacroAssembler masm(cx, ion);
  2585     RepatchStubAppender attacher(*this);
  2586     GenerateAddSlot(cx, masm, attacher, obj, oldShape, object(), value(), checkTypeset);
  2587     return linkAndAttachStub(cx, masm, attacher, ion, "adding");
  2590 static bool
  2591 CanInlineSetPropTypeCheck(JSObject *obj, jsid id, ConstantOrRegister val, bool *checkTypeset)
  2593     bool shouldCheck = false;
  2594     types::TypeObject *type = obj->type();
  2595     if (!type->unknownProperties()) {
  2596         types::HeapTypeSet *propTypes = type->maybeGetProperty(id);
  2597         if (!propTypes)
  2598             return false;
  2599         if (!propTypes->unknown()) {
  2600             shouldCheck = true;
  2601             if (val.constant()) {
  2602                 // If the input is a constant, then don't bother if the barrier will always fail.
  2603                 if (!propTypes->hasType(types::GetValueType(val.value())))
  2604                     return false;
  2605                 shouldCheck = false;
  2606             } else {
  2607                 TypedOrValueRegister reg = val.reg();
  2608                 // We can do the same trick as above for primitive types of specialized registers.
  2609                 // TIs handling of objects is complicated enough to warrant a runtime
  2610                 // check, as we can't statically handle the case where the typeset
  2611                 // contains the specific object, but doesn't have ANYOBJECT set.
  2612                 if (reg.hasTyped() && reg.type() != MIRType_Object) {
  2613                     JSValueType valType = ValueTypeFromMIRType(reg.type());
  2614                     if (!propTypes->hasType(types::Type::PrimitiveType(valType)))
  2615                         return false;
  2616                     shouldCheck = false;
  2622     *checkTypeset = shouldCheck;
  2623     return true;
  2626 static bool
  2627 IsPropertySetInlineable(HandleObject obj, HandleId id, MutableHandleShape pshape,
  2628                         ConstantOrRegister val, bool needsTypeBarrier, bool *checkTypeset)
  2630     JS_ASSERT(obj->isNative());
  2632     // Do a pure non-proto chain climbing lookup. See note in
  2633     // CanAttachNativeGetProp.
  2634     pshape.set(obj->nativeLookupPure(id));
  2636     if (!pshape)
  2637         return false;
  2639     if (!pshape->hasSlot())
  2640         return false;
  2642     if (!pshape->hasDefaultSetter())
  2643         return false;
  2645     if (!pshape->writable())
  2646         return false;
  2648     if (needsTypeBarrier)
  2649         return CanInlineSetPropTypeCheck(obj, id, val, checkTypeset);
  2651     return true;
  2654 static bool
  2655 IsPropertyAddInlineable(HandleObject obj, HandleId id, ConstantOrRegister val, uint32_t oldSlots,
  2656                         HandleShape oldShape, bool needsTypeBarrier, bool *checkTypeset)
  2658     JS_ASSERT(obj->isNative());
  2660     // If the shape of the object did not change, then this was not an add.
  2661     if (obj->lastProperty() == oldShape)
  2662         return false;
  2664     Shape *shape = obj->nativeLookupPure(id);
  2665     if (!shape || shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter())
  2666         return false;
  2668     // If we have a shape at this point and the object's shape changed, then
  2669     // the shape must be the one we just added.
  2670     JS_ASSERT(shape == obj->lastProperty());
  2672     // If object has a non-default resolve hook, don't inline
  2673     if (obj->getClass()->resolve != JS_ResolveStub)
  2674         return false;
  2676     // Likewise for a non-default addProperty hook, since we'll need
  2677     // to invoke it.
  2678     if (obj->getClass()->addProperty != JS_PropertyStub)
  2679         return false;
  2681     if (!obj->nonProxyIsExtensible() || !shape->writable())
  2682         return false;
  2684     // Walk up the object prototype chain and ensure that all prototypes
  2685     // are native, and that all prototypes have no getter or setter
  2686     // defined on the property
  2687     for (JSObject *proto = obj->getProto(); proto; proto = proto->getProto()) {
  2688         // If prototype is non-native, don't optimize
  2689         if (!proto->isNative())
  2690             return false;
  2692         // If prototype defines this property in a non-plain way, don't optimize
  2693         Shape *protoShape = proto->nativeLookupPure(id);
  2694         if (protoShape && !protoShape->hasDefaultSetter())
  2695             return false;
  2697         // Otherwise, if there's no such property, watch out for a resolve
  2698         // hook that would need to be invoked and thus prevent inlining of
  2699         // property addition.
  2700         if (proto->getClass()->resolve != JS_ResolveStub)
  2701              return false;
  2704     // Only add a IC entry if the dynamic slots didn't change when the shapes
  2705     // changed.  Need to ensure that a shape change for a subsequent object
  2706     // won't involve reallocating the slot array.
  2707     if (obj->numDynamicSlots() != oldSlots)
  2708         return false;
  2710     if (needsTypeBarrier)
  2711         return CanInlineSetPropTypeCheck(obj, id, val, checkTypeset);
  2713     *checkTypeset = false;
  2714     return true;
  2717 static SetPropertyIC::NativeSetPropCacheability
  2718 CanAttachNativeSetProp(HandleObject obj, HandleId id, ConstantOrRegister val,
  2719                        bool needsTypeBarrier, MutableHandleObject holder,
  2720                        MutableHandleShape shape, bool *checkTypeset)
  2722     if (!obj->isNative())
  2723         return SetPropertyIC::CanAttachNone;
  2725     // See if the property exists on the object.
  2726     if (IsPropertySetInlineable(obj, id, shape, val, needsTypeBarrier, checkTypeset))
  2727         return SetPropertyIC::CanAttachSetSlot;
  2729     // If we couldn't find the property on the object itself, do a full, but
  2730     // still pure lookup for setters.
  2731     if (!LookupPropertyPure(obj, id, holder.address(), shape.address()))
  2732         return SetPropertyIC::CanAttachNone;
  2734     // If the object doesn't have the property, we don't know if we can attach
  2735     // a stub to add the property until we do the VM call to add. If the
  2736     // property exists as a data property on the prototype, we should add
  2737     // a new, shadowing property.
  2738     if (!shape || (obj != holder && shape->hasDefaultSetter() && shape->hasSlot()))
  2739         return SetPropertyIC::MaybeCanAttachAddSlot;
  2741     if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) ||
  2742         IsCacheableSetPropCallNative(obj, holder, shape))
  2744         return SetPropertyIC::CanAttachCallSetter;
  2747     return SetPropertyIC::CanAttachNone;
  2750 bool
  2751 SetPropertyIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
  2752                       HandleValue value)
  2754     void *returnAddr;
  2755     RootedScript script(cx, GetTopIonJSScript(cx, &returnAddr));
  2756     IonScript *ion = script->ionScript();
  2757     SetPropertyIC &cache = ion->getCache(cacheIndex).toSetProperty();
  2758     RootedPropertyName name(cx, cache.name());
  2759     RootedId id(cx, AtomToId(name));
  2761     // Stop generating new stubs once we hit the stub count limit, see
  2762     // GetPropertyCache.
  2763     bool inlinable = cache.canAttachStub() && !obj->watched();
  2764     NativeSetPropCacheability canCache = CanAttachNone;
  2765     bool addedSetterStub = false;
  2766     if (inlinable) {
  2767         if (!addedSetterStub && obj->is<ProxyObject>()) {
  2768             if (IsCacheableDOMProxy(obj)) {
  2769                 DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, obj, id);
  2770                 if (shadows == ShadowCheckFailed)
  2771                     return false;
  2772                 if (shadows == Shadows) {
  2773                     if (!cache.attachDOMProxyShadowed(cx, ion, obj, returnAddr))
  2774                         return false;
  2775                     addedSetterStub = true;
  2776                 } else {
  2777                     JS_ASSERT(shadows == DoesntShadow || shadows == DoesntShadowUnique);
  2778                     if (shadows == DoesntShadowUnique)
  2779                         cache.reset();
  2780                     if (!cache.attachDOMProxyUnshadowed(cx, ion, obj, returnAddr))
  2781                         return false;
  2782                     addedSetterStub = true;
  2786             if (!addedSetterStub && !cache.hasGenericProxyStub()) {
  2787                 if (!cache.attachGenericProxy(cx, ion, returnAddr))
  2788                     return false;
  2789                 addedSetterStub = true;
  2793         // Make sure the object de-lazifies its type. We do this here so that
  2794         // the parallel IC can share code that assumes that native objects all
  2795         // have a type object.
  2796         if (obj->isNative() && !obj->getType(cx))
  2797             return false;
  2799         RootedShape shape(cx);
  2800         RootedObject holder(cx);
  2801         bool checkTypeset;
  2802         canCache = CanAttachNativeSetProp(obj, id, cache.value(), cache.needsTypeBarrier(),
  2803                                           &holder, &shape, &checkTypeset);
  2805         if (!addedSetterStub && canCache == CanAttachSetSlot) {
  2806             if (!cache.attachSetSlot(cx, ion, obj, shape, checkTypeset))
  2807                 return false;
  2808             addedSetterStub = true;
  2811         if (!addedSetterStub && canCache == CanAttachCallSetter) {
  2812             if (!cache.attachCallSetter(cx, ion, obj, holder, shape, returnAddr))
  2813                 return false;
  2814             addedSetterStub = true;
  2818     uint32_t oldSlots = obj->numDynamicSlots();
  2819     RootedShape oldShape(cx, obj->lastProperty());
  2821     // Set/Add the property on the object, the inlined cache are setup for the next execution.
  2822     if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
  2823         return false;
  2825     // The property did not exist before, now we can try to inline the property add.
  2826     bool checkTypeset;
  2827     if (!addedSetterStub && canCache == MaybeCanAttachAddSlot &&
  2828         IsPropertyAddInlineable(obj, id, cache.value(), oldSlots, oldShape, cache.needsTypeBarrier(),
  2829                                 &checkTypeset))
  2831         if (!cache.attachAddSlot(cx, ion, obj, oldShape, checkTypeset))
  2832             return false;
  2835     return true;
  2838 void
  2839 SetPropertyIC::reset()
  2841     RepatchIonCache::reset();
  2842     hasGenericProxyStub_ = false;
  2845 bool
  2846 SetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
  2847                          HandleValue value)
  2849     JS_ASSERT(cx->isThreadLocal(obj));
  2851     IonScript *ion = GetTopIonJSScript(cx)->parallelIonScript();
  2852     SetPropertyParIC &cache = ion->getCache(cacheIndex).toSetPropertyPar();
  2854     RootedValue v(cx, value);
  2855     RootedId id(cx, AtomToId(cache.name()));
  2857     // Avoid unnecessary locking if cannot attach stubs.
  2858     if (!cache.canAttachStub()) {
  2859         return baseops::SetPropertyHelper<ParallelExecution>(
  2860             cx, obj, obj, id, baseops::Qualified, &v, cache.strict());
  2863     SetPropertyIC::NativeSetPropCacheability canCache = SetPropertyIC::CanAttachNone;
  2864     bool attachedStub = false;
  2867         // See note about locking context in GetPropertyParIC::update.
  2868         LockedJSContext ncx(cx);
  2870         if (cache.canAttachStub()) {
  2871             bool alreadyStubbed;
  2872             if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
  2873                 return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  2874             if (alreadyStubbed) {
  2875                 return baseops::SetPropertyHelper<ParallelExecution>(
  2876                     cx, obj, obj, id, baseops::Qualified, &v, cache.strict());
  2879             // If the object has a lazy type, we need to de-lazify it, but
  2880             // this is not safe in parallel.
  2881             if (obj->hasLazyType())
  2882                 return false;
  2885                 RootedShape shape(cx);
  2886                 RootedObject holder(cx);
  2887                 bool checkTypeset;
  2888                 canCache = CanAttachNativeSetProp(obj, id, cache.value(), cache.needsTypeBarrier(),
  2889                                                   &holder, &shape, &checkTypeset);
  2891                 if (canCache == SetPropertyIC::CanAttachSetSlot) {
  2892                     if (!cache.attachSetSlot(ncx, ion, obj, shape, checkTypeset))
  2893                         return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  2894                     attachedStub = true;
  2900     uint32_t oldSlots = obj->numDynamicSlots();
  2901     RootedShape oldShape(cx, obj->lastProperty());
  2903     if (!baseops::SetPropertyHelper<ParallelExecution>(cx, obj, obj, id, baseops::Qualified, &v,
  2904                                                        cache.strict()))
  2906         return false;
  2909     bool checkTypeset;
  2910     if (!attachedStub && canCache == SetPropertyIC::MaybeCanAttachAddSlot &&
  2911         IsPropertyAddInlineable(obj, id, cache.value(), oldSlots, oldShape, cache.needsTypeBarrier(),
  2912                                 &checkTypeset))
  2914         LockedJSContext ncx(cx);
  2915         if (cache.canAttachStub() && !cache.attachAddSlot(ncx, ion, obj, oldShape, checkTypeset))
  2916             return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  2919     return true;
  2922 bool
  2923 SetPropertyParIC::attachSetSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *shape,
  2924                                 bool checkTypeset)
  2926     MacroAssembler masm(cx, ion);
  2927     DispatchStubPrepender attacher(*this);
  2928     GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(),
  2929                     checkTypeset);
  2930     return linkAndAttachStub(cx, masm, attacher, ion, "parallel setting");
  2933 bool
  2934 SetPropertyParIC::attachAddSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj, Shape *oldShape,
  2935                                 bool checkTypeset)
  2937     JS_ASSERT_IF(!needsTypeBarrier(), !checkTypeset);
  2939     MacroAssembler masm(cx, ion);
  2940     DispatchStubPrepender attacher(*this);
  2941     GenerateAddSlot(cx, masm, attacher, obj, oldShape, object(), value(), checkTypeset);
  2942     return linkAndAttachStub(cx, masm, attacher, ion, "parallel adding");
  2945 const size_t GetElementIC::MAX_FAILED_UPDATES = 16;
  2947 /* static */ bool
  2948 GetElementIC::canAttachGetProp(JSObject *obj, const Value &idval, jsid id)
  2950     uint32_t dummy;
  2951     return (obj->isNative() &&
  2952             idval.isString() &&
  2953             JSID_IS_ATOM(id) &&
  2954             !JSID_TO_ATOM(id)->isIndex(&dummy));
  2957 static bool
  2958 EqualStringsHelper(JSString *str1, JSString *str2)
  2960     JS_ASSERT(str1->isAtom());
  2961     JS_ASSERT(!str2->isAtom());
  2962     JS_ASSERT(str1->length() == str2->length());
  2964     const jschar *chars = str2->getChars(nullptr);
  2965     if (!chars)
  2966         return false;
  2967     return mozilla::PodEqual(str1->asAtom().chars(), chars, str1->length());
  2970 bool
  2971 GetElementIC::attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj,
  2972                             const Value &idval, HandlePropertyName name,
  2973                             void *returnAddr)
  2975     JS_ASSERT(index().reg().hasValue());
  2977     RootedObject holder(cx);
  2978     RootedShape shape(cx);
  2980     GetPropertyIC::NativeGetPropCacheability canCache =
  2981         CanAttachNativeGetProp(cx, *this, obj, name, &holder, &shape,
  2982                                /* skipArrayLen =*/true);
  2984     bool cacheable = canCache == GetPropertyIC::CanAttachReadSlot ||
  2985                      (canCache == GetPropertyIC::CanAttachCallGetter &&
  2986                       output().hasValue());
  2988     if (!cacheable) {
  2989         IonSpew(IonSpew_InlineCaches, "GETELEM uncacheable property");
  2990         return true;
  2993     JS_ASSERT(idval.isString());
  2994     JS_ASSERT(idval.toString()->length() == name->length());
  2996     Label failures;
  2997     MacroAssembler masm(cx, ion);
  2999     // Ensure the index is a string.
  3000     ValueOperand val = index().reg().valueReg();
  3001     masm.branchTestString(Assembler::NotEqual, val, &failures);
  3003     Register scratch = output().valueReg().scratchReg();
  3004     masm.unboxString(val, scratch);
  3006     Label equal;
  3007     masm.branchPtr(Assembler::Equal, scratch, ImmGCPtr(name), &equal);
  3009     // The pointers are not equal, so if the input string is also an atom it
  3010     // must be a different string.
  3011     masm.loadPtr(Address(scratch, JSString::offsetOfLengthAndFlags()), scratch);
  3012     masm.branchTest32(Assembler::NonZero, scratch, Imm32(JSString::ATOM_BIT), &failures);
  3014     // Check the length.
  3015     masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), scratch);
  3016     masm.branch32(Assembler::NotEqual, scratch, Imm32(name->length()), &failures);
  3018     // We have a non-atomized string with the same length. For now call a helper
  3019     // function to do the comparison.
  3020     RegisterSet volatileRegs = RegisterSet::Volatile();
  3021     masm.PushRegsInMask(volatileRegs);
  3023     Register objReg = object();
  3024     JS_ASSERT(objReg != scratch);
  3026     if (!volatileRegs.has(objReg))
  3027         masm.push(objReg);
  3029     masm.setupUnalignedABICall(2, scratch);
  3030     masm.movePtr(ImmGCPtr(name), objReg);
  3031     masm.passABIArg(objReg);
  3032     masm.unboxString(val, scratch);
  3033     masm.passABIArg(scratch);
  3034     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, EqualStringsHelper));
  3035     masm.mov(ReturnReg, scratch);
  3037     if (!volatileRegs.has(objReg))
  3038         masm.pop(objReg);
  3040     RegisterSet ignore = RegisterSet();
  3041     ignore.add(scratch);
  3042     masm.PopRegsInMaskIgnore(volatileRegs, ignore);
  3044     masm.branchIfFalseBool(scratch, &failures);
  3045     masm.bind(&equal);
  3047     RepatchStubAppender attacher(*this);
  3048     if (canCache == GetPropertyIC::CanAttachReadSlot) {
  3049         GenerateReadSlot(cx, ion, masm, attacher, obj, holder, shape, object(), output(),
  3050                          &failures);
  3051     } else {
  3052         JS_ASSERT(canCache == GetPropertyIC::CanAttachCallGetter);
  3053         // Set the frame for bailout safety of the OOL call.
  3054         if (!GenerateCallGetter(cx, ion, masm, attacher, obj, name, holder, shape, liveRegs_,
  3055                                 object(), output(), returnAddr, &failures))
  3057             return false;
  3061     return linkAndAttachStub(cx, masm, attacher, ion, "property");
  3064 /* static */ bool
  3065 GetElementIC::canAttachDenseElement(JSObject *obj, const Value &idval)
  3067     return obj->isNative() && idval.isInt32();
  3070 static bool
  3071 GenerateDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
  3072                      JSObject *obj, const Value &idval, Register object,
  3073                      ConstantOrRegister index, TypedOrValueRegister output)
  3075     JS_ASSERT(GetElementIC::canAttachDenseElement(obj, idval));
  3077     Label failures;
  3079     // Guard object's shape.
  3080     RootedShape shape(cx, obj->lastProperty());
  3081     if (!shape)
  3082         return false;
  3083     masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
  3085     // Ensure the index is an int32 value.
  3086     Register indexReg = InvalidReg;
  3088     if (index.reg().hasValue()) {
  3089         indexReg = output.scratchReg().gpr();
  3090         JS_ASSERT(indexReg != InvalidReg);
  3091         ValueOperand val = index.reg().valueReg();
  3093         masm.branchTestInt32(Assembler::NotEqual, val, &failures);
  3095         // Unbox the index.
  3096         masm.unboxInt32(val, indexReg);
  3097     } else {
  3098         JS_ASSERT(!index.reg().typedReg().isFloat());
  3099         indexReg = index.reg().typedReg().gpr();
  3102     // Load elements vector.
  3103     masm.push(object);
  3104     masm.loadPtr(Address(object, JSObject::offsetOfElements()), object);
  3106     Label hole;
  3108     // Guard on the initialized length.
  3109     Address initLength(object, ObjectElements::offsetOfInitializedLength());
  3110     masm.branch32(Assembler::BelowOrEqual, initLength, indexReg, &hole);
  3112     // Check for holes & load the value.
  3113     masm.loadElementTypedOrValue(BaseIndex(object, indexReg, TimesEight),
  3114                                  output, true, &hole);
  3116     masm.pop(object);
  3117     attacher.jumpRejoin(masm);
  3119     // All failures flow to here.
  3120     masm.bind(&hole);
  3121     masm.pop(object);
  3122     masm.bind(&failures);
  3124     attacher.jumpNextStub(masm);
  3126     return true;
  3129 bool
  3130 GetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval)
  3132     MacroAssembler masm(cx, ion);
  3133     RepatchStubAppender attacher(*this);
  3134     if (!GenerateDenseElement(cx, masm, attacher, obj, idval, object(), index(), output()))
  3135         return false;
  3137     setHasDenseStub();
  3138     return linkAndAttachStub(cx, masm, attacher, ion, "dense array");
  3141 /* static */ bool
  3142 GetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval,
  3143                                          TypedOrValueRegister output)
  3145     if (!obj->is<TypedArrayObject>())
  3146         return false;
  3148     if (!idval.isInt32() && !idval.isString())
  3149         return false;
  3152     // Don't emit a stub if the access is out of bounds. We make to make
  3153     // certain that we monitor the type coming out of the typed array when
  3154     // we generate the stub. Out of bounds accesses will hit the fallback
  3155     // path.
  3156     uint32_t index;
  3157     if (idval.isInt32()) {
  3158         index = idval.toInt32();
  3159     } else {
  3160         index = GetIndexFromString(idval.toString());
  3161         if (index == UINT32_MAX)
  3162             return false;
  3164     if (index >= obj->as<TypedArrayObject>().length())
  3165         return false;
  3167     // The output register is not yet specialized as a float register, the only
  3168     // way to accept float typed arrays for now is to return a Value type.
  3169     uint32_t arrayType = obj->as<TypedArrayObject>().type();
  3170     if (arrayType == ScalarTypeDescr::TYPE_FLOAT32 ||
  3171         arrayType == ScalarTypeDescr::TYPE_FLOAT64)
  3173         return output.hasValue();
  3176     return output.hasValue() || !output.typedReg().isFloat();
  3179 static void
  3180 GenerateGetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
  3181                              TypedArrayObject *tarr, const Value &idval, Register object,
  3182                              ConstantOrRegister index, TypedOrValueRegister output,
  3183                              bool allowDoubleResult)
  3185     JS_ASSERT(GetElementIC::canAttachTypedArrayElement(tarr, idval, output));
  3187     Label failures;
  3189     // The array type is the object within the table of typed array classes.
  3190     int arrayType = tarr->type();
  3192     // Guard on the shape.
  3193     Shape *shape = tarr->lastProperty();
  3194     masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
  3196     // Decide to what type index the stub should be optimized
  3197     Register tmpReg = output.scratchReg().gpr();
  3198     JS_ASSERT(tmpReg != InvalidReg);
  3199     Register indexReg = tmpReg;
  3200     JS_ASSERT(!index.constant());
  3201     if (idval.isString()) {
  3202         JS_ASSERT(GetIndexFromString(idval.toString()) != UINT32_MAX);
  3204         // Part 1: Get the string into a register
  3205         Register str;
  3206         if (index.reg().hasValue()) {
  3207             ValueOperand val = index.reg().valueReg();
  3208             masm.branchTestString(Assembler::NotEqual, val, &failures);
  3210             str = masm.extractString(val, indexReg);
  3211         } else {
  3212             JS_ASSERT(!index.reg().typedReg().isFloat());
  3213             str = index.reg().typedReg().gpr();
  3216         // Part 2: Call to translate the str into index
  3217         RegisterSet regs = RegisterSet::Volatile();
  3218         masm.PushRegsInMask(regs);
  3219         regs.takeUnchecked(str);
  3221         Register temp = regs.takeGeneral();
  3223         masm.setupUnalignedABICall(1, temp);
  3224         masm.passABIArg(str);
  3225         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, GetIndexFromString));
  3226         masm.mov(ReturnReg, indexReg);
  3228         RegisterSet ignore = RegisterSet();
  3229         ignore.add(indexReg);
  3230         masm.PopRegsInMaskIgnore(RegisterSet::Volatile(), ignore);
  3232         masm.branch32(Assembler::Equal, indexReg, Imm32(UINT32_MAX), &failures);
  3234     } else {
  3235         JS_ASSERT(idval.isInt32());
  3237         if (index.reg().hasValue()) {
  3238             ValueOperand val = index.reg().valueReg();
  3239             masm.branchTestInt32(Assembler::NotEqual, val, &failures);
  3241             // Unbox the index.
  3242             masm.unboxInt32(val, indexReg);
  3243         } else {
  3244             JS_ASSERT(!index.reg().typedReg().isFloat());
  3245             indexReg = index.reg().typedReg().gpr();
  3249     // Guard on the initialized length.
  3250     Address length(object, TypedArrayObject::lengthOffset());
  3251     masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
  3253     // Save the object register on the stack in case of failure.
  3254     Label popAndFail;
  3255     Register elementReg = object;
  3256     masm.push(object);
  3258     // Load elements vector.
  3259     masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg);
  3261     // Load the value. We use an invalid register because the destination
  3262     // register is necessary a non double register.
  3263     int width = TypedArrayObject::slotWidth(arrayType);
  3264     BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
  3265     if (output.hasValue()) {
  3266         masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
  3267                                 elementReg, &popAndFail);
  3268     } else {
  3269         masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popAndFail);
  3272     masm.pop(object);
  3273     attacher.jumpRejoin(masm);
  3275     // Restore the object before continuing to the next stub.
  3276     masm.bind(&popAndFail);
  3277     masm.pop(object);
  3278     masm.bind(&failures);
  3280     attacher.jumpNextStub(masm);
  3283 bool
  3284 GetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr,
  3285                                       const Value &idval)
  3287     MacroAssembler masm(cx, ion);
  3288     RepatchStubAppender attacher(*this);
  3289     GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output(),
  3290                                  allowDoubleResult());
  3291     return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
  3294 bool
  3295 GetElementIC::attachArgumentsElement(JSContext *cx, IonScript *ion, JSObject *obj)
  3297     JS_ASSERT(obj->is<ArgumentsObject>());
  3299     Label failures;
  3300     MacroAssembler masm(cx, ion);
  3301     RepatchStubAppender attacher(*this);
  3303     Register tmpReg = output().scratchReg().gpr();
  3304     JS_ASSERT(tmpReg != InvalidReg);
  3306     const Class *clasp = obj->is<StrictArgumentsObject>() ? &StrictArgumentsObject::class_
  3307                                                           : &NormalArgumentsObject::class_;
  3309     masm.branchTestObjClass(Assembler::NotEqual, object(), tmpReg, clasp, &failures);
  3311     // Get initial ArgsObj length value, test if length has been overridden.
  3312     masm.unboxInt32(Address(object(), ArgumentsObject::getInitialLengthSlotOffset()), tmpReg);
  3313     masm.branchTest32(Assembler::NonZero, tmpReg, Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
  3314                       &failures);
  3315     masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), tmpReg);
  3317     // Decide to what type index the stub should be optimized
  3318     Register indexReg;
  3319     JS_ASSERT(!index().constant());
  3321     // Check index against length.
  3322     Label failureRestoreIndex;
  3323     if (index().reg().hasValue()) {
  3324         ValueOperand val = index().reg().valueReg();
  3325         masm.branchTestInt32(Assembler::NotEqual, val, &failures);
  3326         indexReg = val.scratchReg();
  3328         masm.unboxInt32(val, indexReg);
  3329         masm.branch32(Assembler::AboveOrEqual, indexReg, tmpReg, &failureRestoreIndex);
  3330     } else {
  3331         JS_ASSERT(index().reg().type() == MIRType_Int32);
  3332         indexReg = index().reg().typedReg().gpr();
  3333         masm.branch32(Assembler::AboveOrEqual, indexReg, tmpReg, &failures);
  3335     // Save indexReg because it needs to be clobbered to check deleted bit.
  3336     Label failurePopIndex;
  3337     masm.push(indexReg);
  3339     // Check if property was deleted on arguments object.
  3340     masm.loadPrivate(Address(object(), ArgumentsObject::getDataSlotOffset()), tmpReg);
  3341     masm.loadPtr(Address(tmpReg, offsetof(ArgumentsData, deletedBits)), tmpReg);
  3343     // In tempReg, calculate index of word containing bit: (idx >> logBitsPerWord)
  3344     const uint32_t shift = FloorLog2<(sizeof(size_t) * JS_BITS_PER_BYTE)>::value;
  3345     JS_ASSERT(shift == 5 || shift == 6);
  3346     masm.rshiftPtr(Imm32(shift), indexReg);
  3347     masm.loadPtr(BaseIndex(tmpReg, indexReg, ScaleFromElemWidth(sizeof(size_t))), tmpReg);
  3349     // Don't bother testing specific bit, if any bit is set in the word, fail.
  3350     masm.branchPtr(Assembler::NotEqual, tmpReg, ImmPtr(nullptr), &failurePopIndex);
  3352     // Get the address to load from into tmpReg
  3353     masm.loadPrivate(Address(object(), ArgumentsObject::getDataSlotOffset()), tmpReg);
  3354     masm.addPtr(Imm32(ArgumentsData::offsetOfArgs()), tmpReg);
  3356     // Restore original index register value, to use for indexing element.
  3357     masm.pop(indexReg);
  3358     BaseIndex elemIdx(tmpReg, indexReg, ScaleFromElemWidth(sizeof(Value)));
  3360     // Ensure result is not magic value, and type-check result.
  3361     masm.branchTestMagic(Assembler::Equal, elemIdx, &failureRestoreIndex);
  3363     if (output().hasTyped()) {
  3364         JS_ASSERT(!output().typedReg().isFloat());
  3365         JS_ASSERT(index().reg().type() == MIRType_Boolean ||
  3366                   index().reg().type() == MIRType_Int32 ||
  3367                   index().reg().type() == MIRType_String ||
  3368                   index().reg().type() == MIRType_Object);
  3369         masm.branchTestMIRType(Assembler::NotEqual, elemIdx, index().reg().type(),
  3370                                &failureRestoreIndex);
  3373     masm.loadTypedOrValue(elemIdx, output());
  3375     // indexReg may need to be reconstructed if it was originally a value.
  3376     if (index().reg().hasValue())
  3377         masm.tagValue(JSVAL_TYPE_INT32, indexReg, index().reg().valueReg());
  3379     // Success.
  3380     attacher.jumpRejoin(masm);
  3382     // Restore the object before continuing to the next stub.
  3383     masm.bind(&failurePopIndex);
  3384     masm.pop(indexReg);
  3385     masm.bind(&failureRestoreIndex);
  3386     if (index().reg().hasValue())
  3387         masm.tagValue(JSVAL_TYPE_INT32, indexReg, index().reg().valueReg());
  3388     masm.bind(&failures);
  3389     attacher.jumpNextStub(masm);
  3392     if (obj->is<StrictArgumentsObject>()) {
  3393         JS_ASSERT(!hasStrictArgumentsStub_);
  3394         hasStrictArgumentsStub_ = true;
  3395         return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj element (strict)");
  3398     JS_ASSERT(!hasNormalArgumentsStub_);
  3399     hasNormalArgumentsStub_ = true;
  3400     return linkAndAttachStub(cx, masm, attacher, ion, "ArgsObj element (normal)");
  3403 bool
  3404 GetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
  3405                      HandleValue idval, MutableHandleValue res)
  3407     void *returnAddr;
  3408     IonScript *ion = GetTopIonJSScript(cx, &returnAddr)->ionScript();
  3409     GetElementIC &cache = ion->getCache(cacheIndex).toGetElement();
  3410     RootedScript script(cx);
  3411     jsbytecode *pc;
  3412     cache.getScriptedLocation(&script, &pc);
  3414     // Override the return value when the script is invalidated (bug 728188).
  3415     AutoDetectInvalidation adi(cx, res.address(), ion);
  3417     if (cache.isDisabled()) {
  3418         if (!GetObjectElementOperation(cx, JSOp(*pc), obj, /* wasObject = */true, idval, res))
  3419             return false;
  3420         if (!cache.monitoredResult())
  3421             types::TypeScript::Monitor(cx, script, pc, res);
  3422         return true;
  3425     RootedId id(cx);
  3426     if (!ValueToId<CanGC>(cx, idval, &id))
  3427         return false;
  3429     bool attachedStub = false;
  3430     if (cache.canAttachStub()) {
  3431         if (IsOptimizableArgumentsObjectForGetElem(obj, idval) &&
  3432             !cache.hasArgumentsStub(obj->is<StrictArgumentsObject>()) &&
  3433             !cache.index().constant() &&
  3434             (cache.index().reg().hasValue() ||
  3435              cache.index().reg().type() == MIRType_Int32) &&
  3436             (cache.output().hasValue() || !cache.output().typedReg().isFloat()))
  3438             if (!cache.attachArgumentsElement(cx, ion, obj))
  3439                 return false;
  3440             attachedStub = true;
  3442         if (!attachedStub && cache.monitoredResult() && canAttachGetProp(obj, idval, id)) {
  3443             RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
  3444             if (!cache.attachGetProp(cx, ion, obj, idval, name, returnAddr))
  3445                 return false;
  3446             attachedStub = true;
  3448         if (!attachedStub && !cache.hasDenseStub() && canAttachDenseElement(obj, idval)) {
  3449             if (!cache.attachDenseElement(cx, ion, obj, idval))
  3450                 return false;
  3451             attachedStub = true;
  3453         if (!attachedStub && canAttachTypedArrayElement(obj, idval, cache.output())) {
  3454             Rooted<TypedArrayObject*> tarr(cx, &obj->as<TypedArrayObject>());
  3455             if (!cache.attachTypedArrayElement(cx, ion, tarr, idval))
  3456                 return false;
  3457             attachedStub = true;
  3461     if (!GetObjectElementOperation(cx, JSOp(*pc), obj, /* wasObject = */true, idval, res))
  3462         return false;
  3464     // Disable cache when we reach max stubs or update failed too much.
  3465     if (!attachedStub) {
  3466         cache.incFailedUpdates();
  3467         if (cache.shouldDisable()) {
  3468             IonSpew(IonSpew_InlineCaches, "Disable inline cache");
  3469             cache.disable();
  3471     } else {
  3472         cache.resetFailedUpdates();
  3475     if (!cache.monitoredResult())
  3476         types::TypeScript::Monitor(cx, script, pc, res);
  3477     return true;
  3480 void
  3481 GetElementIC::reset()
  3483     RepatchIonCache::reset();
  3484     hasDenseStub_ = false;
  3485     hasStrictArgumentsStub_ = false;
  3486     hasNormalArgumentsStub_ = false;
  3489 static bool
  3490 IsDenseElementSetInlineable(JSObject *obj, const Value &idval)
  3492     if (!obj->is<ArrayObject>())
  3493         return false;
  3495     if (obj->watched())
  3496         return false;
  3498     if (!idval.isInt32())
  3499         return false;
  3501     // The object may have a setter definition,
  3502     // either directly, or via a prototype, or via the target object for a prototype
  3503     // which is a proxy, that handles a particular integer write.
  3504     // Scan the prototype and shape chain to make sure that this is not the case.
  3505     JSObject *curObj = obj;
  3506     while (curObj) {
  3507         // Ensure object is native.
  3508         if (!curObj->isNative())
  3509             return false;
  3511         // Ensure all indexed properties are stored in dense elements.
  3512         if (curObj->isIndexed())
  3513             return false;
  3515         curObj = curObj->getProto();
  3518     return true;
  3521 static bool
  3522 IsTypedArrayElementSetInlineable(JSObject *obj, const Value &idval, const Value &value)
  3524     // Don't bother attaching stubs for assigning strings and objects.
  3525     return (obj->is<TypedArrayObject>() && idval.isInt32() &&
  3526             !value.isString() && !value.isObject());
  3529 static void
  3530 StoreDenseElement(MacroAssembler &masm, ConstantOrRegister value, Register elements,
  3531                   BaseIndex target)
  3533     // If the ObjectElements::CONVERT_DOUBLE_ELEMENTS flag is set, int32 values
  3534     // have to be converted to double first. If the value is not int32, it can
  3535     // always be stored directly.
  3537     Address elementsFlags(elements, ObjectElements::offsetOfFlags());
  3538     if (value.constant()) {
  3539         Value v = value.value();
  3540         Label done;
  3541         if (v.isInt32()) {
  3542             Label dontConvert;
  3543             masm.branchTest32(Assembler::Zero, elementsFlags,
  3544                               Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
  3545                               &dontConvert);
  3546             masm.storeValue(DoubleValue(v.toInt32()), target);
  3547             masm.jump(&done);
  3548             masm.bind(&dontConvert);
  3550         masm.storeValue(v, target);
  3551         masm.bind(&done);
  3552         return;
  3555     TypedOrValueRegister reg = value.reg();
  3556     if (reg.hasTyped() && reg.type() != MIRType_Int32) {
  3557         masm.storeTypedOrValue(reg, target);
  3558         return;
  3561     Label convert, storeValue, done;
  3562     masm.branchTest32(Assembler::NonZero, elementsFlags,
  3563                       Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
  3564                       &convert);
  3565     masm.bind(&storeValue);
  3566     masm.storeTypedOrValue(reg, target);
  3567     masm.jump(&done);
  3569     masm.bind(&convert);
  3570     if (reg.hasValue()) {
  3571         masm.branchTestInt32(Assembler::NotEqual, reg.valueReg(), &storeValue);
  3572         masm.int32ValueToDouble(reg.valueReg(), ScratchFloatReg);
  3573         masm.storeDouble(ScratchFloatReg, target);
  3574     } else {
  3575         JS_ASSERT(reg.type() == MIRType_Int32);
  3576         masm.convertInt32ToDouble(reg.typedReg().gpr(), ScratchFloatReg);
  3577         masm.storeDouble(ScratchFloatReg, target);
  3580     masm.bind(&done);
  3583 static bool
  3584 GenerateSetDenseElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
  3585                         JSObject *obj, const Value &idval, bool guardHoles, Register object,
  3586                         ValueOperand indexVal, ConstantOrRegister value, Register tempToUnboxIndex,
  3587                         Register temp)
  3589     JS_ASSERT(obj->isNative());
  3590     JS_ASSERT(idval.isInt32());
  3592     Label failures;
  3593     Label outOfBounds; // index represents a known hole, or an illegal append
  3595     Label markElem, storeElement; // used if TI protects us from worrying about holes.
  3597     // Guard object is a dense array.
  3598     Shape *shape = obj->lastProperty();
  3599     if (!shape)
  3600         return false;
  3601     masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
  3603     // Ensure the index is an int32 value.
  3604     masm.branchTestInt32(Assembler::NotEqual, indexVal, &failures);
  3606     // Unbox the index.
  3607     Register index = masm.extractInt32(indexVal, tempToUnboxIndex);
  3610         // Load obj->elements.
  3611         Register elements = temp;
  3612         masm.loadPtr(Address(object, JSObject::offsetOfElements()), elements);
  3614         // Compute the location of the element.
  3615         BaseIndex target(elements, index, TimesEight);
  3617         // If TI cannot help us deal with HOLES by preventing indexed properties
  3618         // on the prototype chain, we have to be very careful to check for ourselves
  3619         // to avoid stomping on what should be a setter call. Start by only allowing things
  3620         // within the initialized length.
  3621         if (guardHoles) {
  3622             Address initLength(elements, ObjectElements::offsetOfInitializedLength());
  3623             masm.branch32(Assembler::BelowOrEqual, initLength, index, &outOfBounds);
  3624         } else {
  3625             // Guard that we can increase the initialized length.
  3626             Address capacity(elements, ObjectElements::offsetOfCapacity());
  3627             masm.branch32(Assembler::BelowOrEqual, capacity, index, &outOfBounds);
  3629             // Guard on the initialized length.
  3630             Address initLength(elements, ObjectElements::offsetOfInitializedLength());
  3631             masm.branch32(Assembler::Below, initLength, index, &outOfBounds);
  3633             // if (initLength == index)
  3634             masm.branch32(Assembler::NotEqual, initLength, index, &markElem);
  3636                 // Increase initialize length.
  3637                 Int32Key newLength(index);
  3638                 masm.bumpKey(&newLength, 1);
  3639                 masm.storeKey(newLength, initLength);
  3641                 // Increase length if needed.
  3642                 Label bumpedLength;
  3643                 Address length(elements, ObjectElements::offsetOfLength());
  3644                 masm.branch32(Assembler::AboveOrEqual, length, index, &bumpedLength);
  3645                 masm.storeKey(newLength, length);
  3646                 masm.bind(&bumpedLength);
  3648                 // Restore the index.
  3649                 masm.bumpKey(&newLength, -1);
  3650                 masm.jump(&storeElement);
  3652             // else
  3653             masm.bind(&markElem);
  3656         if (cx->zone()->needsBarrier())
  3657             masm.callPreBarrier(target, MIRType_Value);
  3659         // Store the value.
  3660         if (guardHoles)
  3661             masm.branchTestMagic(Assembler::Equal, target, &failures);
  3662         else
  3663             masm.bind(&storeElement);
  3664         StoreDenseElement(masm, value, elements, target);
  3666     attacher.jumpRejoin(masm);
  3668     // All failures flow to here.
  3669     masm.bind(&outOfBounds);
  3670     masm.bind(&failures);
  3671     attacher.jumpNextStub(masm);
  3673     return true;
  3676 bool
  3677 SetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval)
  3679     MacroAssembler masm(cx, ion);
  3680     RepatchStubAppender attacher(*this);
  3681     if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval,
  3682                                  guardHoles(), object(), index(),
  3683                                  value(), tempToUnboxIndex(),
  3684                                  temp()))
  3686         return false;
  3689     setHasDenseStub();
  3690     const char *message = guardHoles()            ?
  3691                             "dense array (holes)" :
  3692                             "dense array";
  3693     return linkAndAttachStub(cx, masm, attacher, ion, message);
  3696 static bool
  3697 GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
  3698                              TypedArrayObject *tarr, Register object,
  3699                              ValueOperand indexVal, ConstantOrRegister value,
  3700                              Register tempUnbox, Register temp, FloatRegister tempFloat)
  3702     Label failures, done, popObjectAndFail;
  3704     // Guard on the shape.
  3705     Shape *shape = tarr->lastProperty();
  3706     if (!shape)
  3707         return false;
  3708     masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
  3710     // Ensure the index is an int32.
  3711     masm.branchTestInt32(Assembler::NotEqual, indexVal, &failures);
  3712     Register index = masm.extractInt32(indexVal, tempUnbox);
  3714     // Guard on the length.
  3715     Address length(object, TypedArrayObject::lengthOffset());
  3716     masm.unboxInt32(length, temp);
  3717     masm.branch32(Assembler::BelowOrEqual, temp, index, &done);
  3719     // Load the elements vector.
  3720     Register elements = temp;
  3721     masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elements);
  3723     // Set the value.
  3724     int arrayType = tarr->type();
  3725     int width = TypedArrayObject::slotWidth(arrayType);
  3726     BaseIndex target(elements, index, ScaleFromElemWidth(width));
  3728     if (arrayType == ScalarTypeDescr::TYPE_FLOAT32) {
  3729         if (LIRGenerator::allowFloat32Optimizations()) {
  3730             if (!masm.convertConstantOrRegisterToFloat(cx, value, tempFloat, &failures))
  3731                 return false;
  3732         } else {
  3733             if (!masm.convertConstantOrRegisterToDouble(cx, value, tempFloat, &failures))
  3734                 return false;
  3736         masm.storeToTypedFloatArray(arrayType, tempFloat, target);
  3737     } else if (arrayType == ScalarTypeDescr::TYPE_FLOAT64) {
  3738         if (!masm.convertConstantOrRegisterToDouble(cx, value, tempFloat, &failures))
  3739             return false;
  3740         masm.storeToTypedFloatArray(arrayType, tempFloat, target);
  3741     } else {
  3742         // On x86 we only have 6 registers available to use, so reuse the object
  3743         // register to compute the intermediate value to store and restore it
  3744         // afterwards.
  3745         masm.push(object);
  3747         if (arrayType == ScalarTypeDescr::TYPE_UINT8_CLAMPED) {
  3748             if (!masm.clampConstantOrRegisterToUint8(cx, value, tempFloat, object,
  3749                                                      &popObjectAndFail))
  3751                 return false;
  3753         } else {
  3754             if (!masm.truncateConstantOrRegisterToInt32(cx, value, tempFloat, object,
  3755                                                         &popObjectAndFail))
  3757                 return false;
  3760         masm.storeToTypedIntArray(arrayType, object, target);
  3762         masm.pop(object);
  3765     // Out-of-bound writes jump here as they are no-ops.
  3766     masm.bind(&done);
  3767     attacher.jumpRejoin(masm);
  3769     if (popObjectAndFail.used()) {
  3770         masm.bind(&popObjectAndFail);
  3771         masm.pop(object);
  3774     masm.bind(&failures);
  3775     attacher.jumpNextStub(masm);
  3776     return true;
  3779 bool
  3780 SetElementIC::attachTypedArrayElement(JSContext *cx, IonScript *ion, TypedArrayObject *tarr)
  3782     MacroAssembler masm(cx, ion);
  3783     RepatchStubAppender attacher(*this);
  3784     if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr,
  3785                                       object(), index(), value(),
  3786                                       tempToUnboxIndex(), temp(), tempFloat()))
  3788         return false;
  3791     return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
  3794 bool
  3795 SetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
  3796                      HandleValue idval, HandleValue value)
  3798     IonScript *ion = GetTopIonJSScript(cx)->ionScript();
  3799     SetElementIC &cache = ion->getCache(cacheIndex).toSetElement();
  3801     bool attachedStub = false;
  3802     if (cache.canAttachStub()) {
  3803         if (!cache.hasDenseStub() && IsDenseElementSetInlineable(obj, idval)) {
  3804             if (!cache.attachDenseElement(cx, ion, obj, idval))
  3805                 return false;
  3806             attachedStub = true;
  3808         if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) {
  3809             TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
  3810             if (!cache.attachTypedArrayElement(cx, ion, tarr))
  3811                 return false;
  3815     if (!SetObjectElement(cx, obj, idval, value, cache.strict()))
  3816         return false;
  3817     return true;
  3820 void
  3821 SetElementIC::reset()
  3823     RepatchIonCache::reset();
  3824     hasDenseStub_ = false;
  3827 bool
  3828 SetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj,
  3829                                     const Value &idval)
  3831     MacroAssembler masm(cx, ion);
  3832     DispatchStubPrepender attacher(*this);
  3833     if (!GenerateSetDenseElement(cx, masm, attacher, obj, idval,
  3834                                  guardHoles(), object(), index(),
  3835                                  value(), tempToUnboxIndex(),
  3836                                  temp()))
  3838         return false;
  3841     const char *message = guardHoles()                     ?
  3842                             "parallel dense array (holes)" :
  3843                             "parallel dense array";
  3845     return linkAndAttachStub(cx, masm, attacher, ion, message);
  3848 bool
  3849 SetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
  3850                                          TypedArrayObject *tarr)
  3852     MacroAssembler masm(cx, ion);
  3853     DispatchStubPrepender attacher(*this);
  3854     if (!GenerateSetTypedArrayElement(cx, masm, attacher, tarr,
  3855                                       object(), index(), value(),
  3856                                       tempToUnboxIndex(), temp(), tempFloat()))
  3858         return false;
  3861     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
  3864 bool
  3865 SetElementParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
  3866                         HandleValue idval, HandleValue value)
  3868     IonScript *ion = GetTopIonJSScript(cx)->parallelIonScript();
  3869     SetElementParIC &cache = ion->getCache(cacheIndex).toSetElementPar();
  3871     // Avoid unnecessary locking if cannot attach stubs.
  3872     if (!cache.canAttachStub())
  3873         return SetElementPar(cx, obj, idval, value, cache.strict());
  3876         LockedJSContext ncx(cx);
  3878         if (cache.canAttachStub()) {
  3879             bool alreadyStubbed;
  3880             if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
  3881                 return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  3882             if (alreadyStubbed)
  3883                 return SetElementPar(cx, obj, idval, value, cache.strict());
  3885             bool attachedStub = false;
  3886             if (IsDenseElementSetInlineable(obj, idval)) {
  3887                 if (!cache.attachDenseElement(ncx, ion, obj, idval))
  3888                     return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  3889                 attachedStub = true;
  3891             if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) {
  3892                 TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
  3893                 if (!cache.attachTypedArrayElement(ncx, ion, tarr))
  3894                     return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  3899     return SetElementPar(cx, obj, idval, value, cache.strict());
  3902 bool
  3903 GetElementParIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, JSObject *obj,
  3904                                 const Value &idval, PropertyName *name, JSObject *holder,
  3905                                 Shape *shape)
  3907     MacroAssembler masm(cx, ion);
  3908     DispatchStubPrepender attacher(*this);
  3910     // Guard on the index value.
  3911     Label failures;
  3912     ValueOperand val = index().reg().valueReg();
  3913     masm.branchTestValue(Assembler::NotEqual, val, idval, &failures);
  3915     GenerateReadSlot(cx, ion, masm, attacher, obj, holder, shape, object(), output(),
  3916                      &failures);
  3918     return linkAndAttachStub(cx, masm, attacher, ion, "parallel getelem reading");
  3921 bool
  3922 GetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, JSObject *obj,
  3923                                     const Value &idval)
  3925     MacroAssembler masm(cx, ion);
  3926     DispatchStubPrepender attacher(*this);
  3927     if (!GenerateDenseElement(cx, masm, attacher, obj, idval, object(), index(), output()))
  3928         return false;
  3930     return linkAndAttachStub(cx, masm, attacher, ion, "parallel dense element");
  3933 bool
  3934 GetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
  3935                                          TypedArrayObject *tarr, const Value &idval)
  3937     MacroAssembler masm(cx, ion);
  3938     DispatchStubPrepender attacher(*this);
  3939     GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output(),
  3940                                  allowDoubleResult());
  3941     return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array");
  3944 bool
  3945 GetElementParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
  3946                         HandleValue idval, MutableHandleValue vp)
  3948     IonScript *ion = GetTopIonJSScript(cx)->parallelIonScript();
  3949     GetElementParIC &cache = ion->getCache(cacheIndex).toGetElementPar();
  3951     // Try to get the element early, as the pure path doesn't need a lock. If
  3952     // we can't do it purely, bail out of parallel execution.
  3953     if (!GetObjectElementOperationPure(cx, obj, idval, vp.address()))
  3954         return false;
  3956     // Avoid unnecessary locking if cannot attach stubs.
  3957     if (!cache.canAttachStub())
  3958         return true;
  3961         // See note about locking context in GetPropertyParIC::update.
  3962         LockedJSContext ncx(cx);
  3964         if (cache.canAttachStub()) {
  3965             bool alreadyStubbed;
  3966             if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
  3967                 return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  3968             if (alreadyStubbed)
  3969                 return true;
  3971             jsid id;
  3972             if (!ValueToIdPure(idval, &id))
  3973                 return false;
  3975             bool attachedStub = false;
  3976             if (cache.monitoredResult() &&
  3977                 GetElementIC::canAttachGetProp(obj, idval, id))
  3979                 RootedShape shape(ncx);
  3980                 RootedObject holder(ncx);
  3981                 RootedPropertyName name(ncx, JSID_TO_ATOM(id)->asPropertyName());
  3983                 GetPropertyIC::NativeGetPropCacheability canCache =
  3984                     CanAttachNativeGetProp(ncx, cache, obj, name, &holder, &shape);
  3986                 if (canCache == GetPropertyIC::CanAttachReadSlot)
  3988                     if (!cache.attachReadSlot(ncx, ion, obj, idval, name, holder, shape))
  3989                         return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  3990                     attachedStub = true;
  3993             if (!attachedStub &&
  3994                 GetElementIC::canAttachDenseElement(obj, idval))
  3996                 if (!cache.attachDenseElement(ncx, ion, obj, idval))
  3997                     return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  3998                 attachedStub = true;
  4000             if (!attachedStub &&
  4001                 GetElementIC::canAttachTypedArrayElement(obj, idval, cache.output()))
  4003                 if (!cache.attachTypedArrayElement(ncx, ion, &obj->as<TypedArrayObject>(), idval))
  4004                     return cx->setPendingAbortFatal(ParallelBailoutFailedIC);
  4005                 attachedStub = true;
  4010     return true;
  4013 bool
  4014 BindNameIC::attachGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain)
  4016     JS_ASSERT(scopeChain->is<GlobalObject>());
  4018     MacroAssembler masm(cx, ion);
  4019     RepatchStubAppender attacher(*this);
  4021     // Guard on the scope chain.
  4022     attacher.branchNextStub(masm, Assembler::NotEqual, scopeChainReg(),
  4023                             ImmGCPtr(scopeChain));
  4024     masm.movePtr(ImmGCPtr(scopeChain), outputReg());
  4026     attacher.jumpRejoin(masm);
  4028     return linkAndAttachStub(cx, masm, attacher, ion, "global");
  4031 static inline void
  4032 GenerateScopeChainGuard(MacroAssembler &masm, JSObject *scopeObj,
  4033                         Register scopeObjReg, Shape *shape, Label *failures)
  4035     if (scopeObj->is<CallObject>()) {
  4036         // We can skip a guard on the call object if the script's bindings are
  4037         // guaranteed to be immutable (and thus cannot introduce shadowing
  4038         // variables).
  4039         CallObject *callObj = &scopeObj->as<CallObject>();
  4040         if (!callObj->isForEval()) {
  4041             JSFunction *fun = &callObj->callee();
  4042             // The function might have been relazified under rare conditions.
  4043             // In that case, we pessimistically create the guard, as we'd
  4044             // need to root various pointers to delazify,
  4045             if (fun->hasScript()) {
  4046                 JSScript *script = fun->nonLazyScript();
  4047                 if (!script->funHasExtensibleScope())
  4048                     return;
  4051     } else if (scopeObj->is<GlobalObject>()) {
  4052         // If this is the last object on the scope walk, and the property we've
  4053         // found is not configurable, then we don't need a shape guard because
  4054         // the shape cannot be removed.
  4055         if (shape && !shape->configurable())
  4056             return;
  4059     Address shapeAddr(scopeObjReg, JSObject::offsetOfShape());
  4060     masm.branchPtr(Assembler::NotEqual, shapeAddr, ImmGCPtr(scopeObj->lastProperty()), failures);
  4063 static void
  4064 GenerateScopeChainGuards(MacroAssembler &masm, JSObject *scopeChain, JSObject *holder,
  4065                          Register outputReg, Label *failures, bool skipLastGuard = false)
  4067     JSObject *tobj = scopeChain;
  4069     // Walk up the scope chain. Note that IsCacheableScopeChain guarantees the
  4070     // |tobj == holder| condition terminates the loop.
  4071     while (true) {
  4072         JS_ASSERT(IsCacheableNonGlobalScope(tobj) || tobj->is<GlobalObject>());
  4074         if (skipLastGuard && tobj == holder)
  4075             break;
  4077         GenerateScopeChainGuard(masm, tobj, outputReg, nullptr, failures);
  4079         if (tobj == holder)
  4080             break;
  4082         // Load the next link.
  4083         tobj = &tobj->as<ScopeObject>().enclosingScope();
  4084         masm.extractObject(Address(outputReg, ScopeObject::offsetOfEnclosingScope()), outputReg);
  4088 bool
  4089 BindNameIC::attachNonGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain, JSObject *holder)
  4091     JS_ASSERT(IsCacheableNonGlobalScope(scopeChain));
  4093     MacroAssembler masm(cx, ion);
  4094     RepatchStubAppender attacher(*this);
  4096     // Guard on the shape of the scope chain.
  4097     Label failures;
  4098     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
  4099                                    Address(scopeChainReg(), JSObject::offsetOfShape()),
  4100                                    ImmGCPtr(scopeChain->lastProperty()),
  4101                                    holder != scopeChain ? &failures : nullptr);
  4103     if (holder != scopeChain) {
  4104         JSObject *parent = &scopeChain->as<ScopeObject>().enclosingScope();
  4105         masm.extractObject(Address(scopeChainReg(), ScopeObject::offsetOfEnclosingScope()), outputReg());
  4107         GenerateScopeChainGuards(masm, parent, holder, outputReg(), &failures);
  4108     } else {
  4109         masm.movePtr(scopeChainReg(), outputReg());
  4112     // At this point outputReg holds the object on which the property
  4113     // was found, so we're done.
  4114     attacher.jumpRejoin(masm);
  4116     // All failures flow to here, so there is a common point to patch.
  4117     if (holder != scopeChain) {
  4118         masm.bind(&failures);
  4119         attacher.jumpNextStub(masm);
  4122     return linkAndAttachStub(cx, masm, attacher, ion, "non-global");
  4125 static bool
  4126 IsCacheableScopeChain(JSObject *scopeChain, JSObject *holder)
  4128     while (true) {
  4129         if (!IsCacheableNonGlobalScope(scopeChain)) {
  4130             IonSpew(IonSpew_InlineCaches, "Non-cacheable object on scope chain");
  4131             return false;
  4134         if (scopeChain == holder)
  4135             return true;
  4137         scopeChain = &scopeChain->as<ScopeObject>().enclosingScope();
  4138         if (!scopeChain) {
  4139             IonSpew(IonSpew_InlineCaches, "Scope chain indirect hit");
  4140             return false;
  4144     MOZ_ASSUME_UNREACHABLE("Invalid scope chain");
  4147 JSObject *
  4148 BindNameIC::update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain)
  4150     IonScript *ion = GetTopIonJSScript(cx)->ionScript();
  4151     BindNameIC &cache = ion->getCache(cacheIndex).toBindName();
  4152     HandlePropertyName name = cache.name();
  4154     RootedObject holder(cx);
  4155     if (scopeChain->is<GlobalObject>()) {
  4156         holder = scopeChain;
  4157     } else {
  4158         if (!LookupNameWithGlobalDefault(cx, name, scopeChain, &holder))
  4159             return nullptr;
  4162     // Stop generating new stubs once we hit the stub count limit, see
  4163     // GetPropertyCache.
  4164     if (cache.canAttachStub()) {
  4165         if (scopeChain->is<GlobalObject>()) {
  4166             if (!cache.attachGlobal(cx, ion, scopeChain))
  4167                 return nullptr;
  4168         } else if (IsCacheableScopeChain(scopeChain, holder)) {
  4169             if (!cache.attachNonGlobal(cx, ion, scopeChain, holder))
  4170                 return nullptr;
  4171         } else {
  4172             IonSpew(IonSpew_InlineCaches, "BINDNAME uncacheable scope chain");
  4176     return holder;
  4179 bool
  4180 NameIC::attachReadSlot(JSContext *cx, IonScript *ion, HandleObject scopeChain,
  4181                        HandleObject holderBase, HandleObject holder,
  4182                        HandleShape shape)
  4184     MacroAssembler masm(cx, ion);
  4185     Label failures;
  4186     RepatchStubAppender attacher(*this);
  4188     Register scratchReg = outputReg().valueReg().scratchReg();
  4190     // Don't guard the base of the proto chain the name was found on. It will be guarded
  4191     // by GenerateReadSlot().
  4192     masm.mov(scopeChainReg(), scratchReg);
  4193     GenerateScopeChainGuards(masm, scopeChain, holderBase, scratchReg, &failures,
  4194                              /* skipLastGuard = */true);
  4196     // GenerateScopeChain leaves the last scope chain in scrachReg, even though it
  4197     // doesn't generate the extra guard.
  4198     GenerateReadSlot(cx, ion, masm, attacher, holderBase, holder, shape, scratchReg,
  4199                      outputReg(), failures.used() ? &failures : nullptr);
  4201     return linkAndAttachStub(cx, masm, attacher, ion, "generic");
  4204 static bool
  4205 IsCacheableNameReadSlot(JSContext *cx, HandleObject scopeChain, HandleObject obj,
  4206                         HandleObject holder, HandleShape shape, jsbytecode *pc,
  4207                         const TypedOrValueRegister &output)
  4209     if (!shape)
  4210         return false;
  4211     if (!obj->isNative())
  4212         return false;
  4214     if (obj->is<GlobalObject>()) {
  4215         // Support only simple property lookups.
  4216         if (!IsCacheableGetPropReadSlot(obj, holder, shape) &&
  4217             !IsCacheableNoProperty(obj, holder, shape, pc, output))
  4218             return false;
  4219     } else if (obj->is<CallObject>()) {
  4220         JS_ASSERT(obj == holder);
  4221         if (!shape->hasDefaultGetter())
  4222             return false;
  4223     } else {
  4224         // We don't yet support lookups on Block or DeclEnv objects.
  4225         return false;
  4228     RootedObject obj2(cx, scopeChain);
  4229     while (obj2) {
  4230         if (!IsCacheableNonGlobalScope(obj2) && !obj2->is<GlobalObject>())
  4231             return false;
  4233         // Stop once we hit the global or target obj.
  4234         if (obj2->is<GlobalObject>() || obj2 == obj)
  4235             break;
  4237         obj2 = obj2->enclosingScope();
  4240     return obj == obj2;
  4243 bool
  4244 NameIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
  4245                          HandleShape shape, void *returnAddr)
  4247     MacroAssembler masm(cx, ion, script_, pc_);
  4249     RepatchStubAppender attacher(*this);
  4250     if (!GenerateCallGetter(cx, ion, masm, attacher, obj, name(), holder, shape, liveRegs_,
  4251                             scopeChainReg(), outputReg(), returnAddr))
  4253          return false;
  4256     const char *attachKind = "name getter";
  4257     return linkAndAttachStub(cx, masm, attacher, ion, attachKind);
  4260 static bool
  4261 IsCacheableNameCallGetter(JSObject *scopeChain, JSObject *obj, JSObject *holder, Shape *shape)
  4263     if (obj != scopeChain)
  4264         return false;
  4266     if (!obj->is<GlobalObject>())
  4267         return false;
  4269     return IsCacheableGetPropCallNative(obj, holder, shape) ||
  4270         IsCacheableGetPropCallPropertyOp(obj, holder, shape);
  4273 bool
  4274 NameIC::update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain,
  4275                MutableHandleValue vp)
  4277     void *returnAddr;
  4278     IonScript *ion = GetTopIonJSScript(cx, &returnAddr)->ionScript();
  4280     NameIC &cache = ion->getCache(cacheIndex).toName();
  4281     RootedPropertyName name(cx, cache.name());
  4283     RootedScript script(cx);
  4284     jsbytecode *pc;
  4285     cache.getScriptedLocation(&script, &pc);
  4287     RootedObject obj(cx);
  4288     RootedObject holder(cx);
  4289     RootedShape shape(cx);
  4290     if (!LookupName(cx, name, scopeChain, &obj, &holder, &shape))
  4291         return false;
  4293     if (cache.canAttachStub()) {
  4294         if (IsCacheableNameReadSlot(cx, scopeChain, obj, holder, shape, pc, cache.outputReg())) {
  4295             if (!cache.attachReadSlot(cx, ion, scopeChain, obj, holder, shape))
  4296                 return false;
  4297         } else if (IsCacheableNameCallGetter(scopeChain, obj, holder, shape)) {
  4298             if (!cache.attachCallGetter(cx, ion, obj, holder, shape, returnAddr))
  4299                 return false;
  4303     if (cache.isTypeOf()) {
  4304         if (!FetchName<true>(cx, obj, holder, name, shape, vp))
  4305             return false;
  4306     } else {
  4307         if (!FetchName<false>(cx, obj, holder, name, shape, vp))
  4308             return false;
  4311     // Monitor changes to cache entry.
  4312     types::TypeScript::Monitor(cx, script, pc, vp);
  4314     return true;
  4317 bool
  4318 CallsiteCloneIC::attach(JSContext *cx, IonScript *ion, HandleFunction original,
  4319                         HandleFunction clone)
  4321     MacroAssembler masm(cx, ion);
  4322     RepatchStubAppender attacher(*this);
  4324     // Guard against object identity on the original.
  4325     attacher.branchNextStub(masm, Assembler::NotEqual, calleeReg(), ImmGCPtr(original));
  4327     // Load the clone.
  4328     masm.movePtr(ImmGCPtr(clone), outputReg());
  4330     attacher.jumpRejoin(masm);
  4332     return linkAndAttachStub(cx, masm, attacher, ion, "generic");
  4335 JSObject *
  4336 CallsiteCloneIC::update(JSContext *cx, size_t cacheIndex, HandleObject callee)
  4338     // Act as the identity for functions that are not clone-at-callsite, as we
  4339     // generate this cache as long as some callees are clone-at-callsite.
  4340     RootedFunction fun(cx, &callee->as<JSFunction>());
  4341     if (!fun->hasScript() || !fun->nonLazyScript()->shouldCloneAtCallsite())
  4342         return fun;
  4344     IonScript *ion = GetTopIonJSScript(cx)->ionScript();
  4345     CallsiteCloneIC &cache = ion->getCache(cacheIndex).toCallsiteClone();
  4347     RootedFunction clone(cx, CloneFunctionAtCallsite(cx, fun, cache.callScript(), cache.callPc()));
  4348     if (!clone)
  4349         return nullptr;
  4351     if (cache.canAttachStub()) {
  4352         if (!cache.attach(cx, ion, fun, clone))
  4353             return nullptr;
  4356     return clone;

mercurial