LCOV - code coverage report
Current view: top level - js/src/jit - IonIC.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 78 229 34.1 %
Date: 2017-07-14 16:53:18 Functions: 7 13 53.8 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "jit/IonIC.h"
       8             : 
       9             : #include "mozilla/Maybe.h"
      10             : #include "mozilla/SizePrintfMacros.h"
      11             : 
      12             : #include "jit/CacheIRCompiler.h"
      13             : #include "jit/Linker.h"
      14             : 
      15             : #include "jit/MacroAssembler-inl.h"
      16             : #include "vm/Interpreter-inl.h"
      17             : 
      18             : using namespace js;
      19             : using namespace js::jit;
      20             : 
      21             : using mozilla::Maybe;
      22             : 
      23             : void
      24          14 : IonIC::updateBaseAddress(JitCode* code, MacroAssembler& masm)
      25             : {
      26          14 :     fallbackLabel_.repoint(code, &masm);
      27          14 :     rejoinLabel_.repoint(code, &masm);
      28             : 
      29          14 :     codeRaw_ = fallbackLabel_.raw();
      30          14 : }
      31             : 
      32             : Register
      33          67 : IonIC::scratchRegisterForEntryJump()
      34             : {
      35          67 :     switch (kind_) {
      36             :       case CacheKind::GetProp:
      37             :       case CacheKind::GetElem: {
      38          53 :         Register temp = asGetPropertyIC()->maybeTemp();
      39          53 :         if (temp != InvalidReg)
      40           1 :             return temp;
      41          52 :         TypedOrValueRegister output = asGetPropertyIC()->output();
      42          52 :         return output.hasValue() ? output.valueReg().scratchReg() : output.typedReg().gpr();
      43             :       }
      44             :       case CacheKind::SetProp:
      45             :       case CacheKind::SetElem:
      46           6 :         return asSetPropertyIC()->temp();
      47             :       case CacheKind::GetName:
      48           4 :         return asGetNameIC()->temp();
      49             :       case CacheKind::BindName:
      50           0 :         return asBindNameIC()->temp();
      51             :       case CacheKind::In:
      52           4 :         return asInIC()->temp();
      53             :       case CacheKind::HasOwn:
      54           0 :         return asHasOwnIC()->output();
      55             :       case CacheKind::Call:
      56             :       case CacheKind::Compare:
      57             :       case CacheKind::TypeOf:
      58             :       case CacheKind::GetPropSuper:
      59             :       case CacheKind::GetElemSuper:
      60           0 :         MOZ_CRASH("Unsupported IC");
      61             :     }
      62             : 
      63           0 :     MOZ_CRASH("Invalid kind");
      64             : }
      65             : 
      66             : void
      67           1 : IonIC::discardStubs(Zone* zone)
      68             : {
      69           1 :     if (firstStub_ && zone->needsIncrementalBarrier()) {
      70             :         // We are removing edges from IonIC to gcthings. Perform one final trace
      71             :         // of the stub for incremental GC, as it must know about those edges.
      72           0 :         trace(zone->barrierTracer());
      73             :     }
      74             : 
      75             : #ifdef JS_CRASH_DIAGNOSTICS
      76           1 :     IonICStub* stub = firstStub_;
      77          13 :     while (stub) {
      78           6 :         IonICStub* next = stub->next();
      79           6 :         stub->poison();
      80           6 :         stub = next;
      81             :     }
      82             : #endif
      83             : 
      84           1 :     firstStub_ = nullptr;
      85           1 :     codeRaw_ = fallbackLabel_.raw();
      86           1 :     state_.trackUnlinkedAllStubs();
      87           1 : }
      88             : 
      89             : void
      90           0 : IonIC::reset(Zone* zone)
      91             : {
      92           0 :     discardStubs(zone);
      93           0 :     state_.reset();
      94           0 : }
      95             : 
      96             : void
      97           0 : IonIC::trace(JSTracer* trc)
      98             : {
      99           0 :     if (script_)
     100           0 :         TraceManuallyBarrieredEdge(trc, &script_, "IonIC::script_");
     101             : 
     102           0 :     uint8_t* nextCodeRaw = codeRaw_;
     103           0 :     for (IonICStub* stub = firstStub_; stub; stub = stub->next()) {
     104           0 :         JitCode* code = JitCode::FromExecutable(nextCodeRaw);
     105           0 :         TraceManuallyBarrieredEdge(trc, &code, "ion-ic-code");
     106             : 
     107           0 :         TraceCacheIRStub(trc, stub, stub->stubInfo());
     108             : 
     109           0 :         nextCodeRaw = stub->nextCodeRaw();
     110             :     }
     111             : 
     112           0 :     MOZ_ASSERT(nextCodeRaw == fallbackLabel_.raw());
     113           0 : }
     114             : 
     115             : /* static */ bool
     116          20 : IonGetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonGetPropertyIC* ic,
     117             :                          HandleValue val, HandleValue idVal, MutableHandleValue res)
     118             : {
     119             :     // Override the return value if we are invalidated (bug 728188).
     120          20 :     IonScript* ionScript = outerScript->ionScript();
     121          40 :     AutoDetectInvalidation adi(cx, res, ionScript);
     122             : 
     123             :     // If the IC is idempotent, we will redo the op in the interpreter.
     124          20 :     if (ic->idempotent())
     125           0 :         adi.disable();
     126             : 
     127          20 :     if (ic->state().maybeTransition())
     128           1 :         ic->discardStubs(cx->zone());
     129             : 
     130          20 :     bool attached = false;
     131          20 :     if (ic->state().canAttachStub()) {
     132             :         // IonBuilder calls PropertyReadNeedsTypeBarrier to determine if it
     133             :         // needs a type barrier. Unfortunately, PropertyReadNeedsTypeBarrier
     134             :         // does not account for getters, so we should only attach a getter
     135             :         // stub if we inserted a type barrier.
     136             :         CanAttachGetter canAttachGetter =
     137          20 :             ic->monitoredResult() ? CanAttachGetter::Yes : CanAttachGetter::No;
     138          20 :         jsbytecode* pc = ic->idempotent() ? nullptr : ic->pc();
     139          20 :         bool isTemporarilyUnoptimizable = false;
     140          20 :         GetPropIRGenerator gen(cx, outerScript, pc, ic->kind(), ic->state().mode(),
     141          40 :                                &isTemporarilyUnoptimizable, val, idVal, val, canAttachGetter);
     142          20 :         if (ic->idempotent() ? gen.tryAttachIdempotentStub() : gen.tryAttachStub())
     143          20 :             ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
     144             : 
     145          20 :         if (!attached && !isTemporarilyUnoptimizable)
     146           0 :             ic->state().trackNotAttached();
     147             :     }
     148             : 
     149          20 :     if (!attached && ic->idempotent()) {
     150             :         // Invalidate the cache if the property was not found, or was found on
     151             :         // a non-native object. This ensures:
     152             :         // 1) The property read has no observable side-effects.
     153             :         // 2) There's no need to dynamically monitor the return type. This would
     154             :         //    be complicated since (due to GVN) there can be multiple pc's
     155             :         //    associated with a single idempotent cache.
     156           0 :         JitSpew(JitSpew_IonIC, "Invalidating from idempotent cache %s:%" PRIuSIZE,
     157           0 :                 outerScript->filename(), outerScript->lineno());
     158             : 
     159           0 :         outerScript->setInvalidatedIdempotentCache();
     160             : 
     161             :         // Do not re-invalidate if the lookup already caused invalidation.
     162           0 :         if (outerScript->hasIonScript())
     163           0 :             Invalidate(cx, outerScript);
     164             : 
     165             :         // We will redo the potentially effectful lookup in Baseline.
     166           0 :         return true;
     167             :     }
     168             : 
     169          20 :     if (ic->kind() == CacheKind::GetProp) {
     170          10 :         RootedPropertyName name(cx, idVal.toString()->asAtom().asPropertyName());
     171           5 :         if (!GetProperty(cx, val, name, res))
     172           0 :             return false;
     173             :     } else {
     174          15 :         MOZ_ASSERT(ic->kind() == CacheKind::GetElem);
     175          15 :         if (!GetElementOperation(cx, JSOp(*ic->pc()), val, idVal, res))
     176           0 :             return false;
     177             :     }
     178             : 
     179          20 :     if (!ic->idempotent()) {
     180             :         // Monitor changes to cache entry.
     181          20 :         if (!ic->monitoredResult())
     182           0 :             TypeScript::Monitor(cx, ic->script(), ic->pc(), res);
     183             :     }
     184             : 
     185          20 :     return true;
     186             : }
     187             : 
     188             : /* static */ bool
     189           0 : IonSetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonSetPropertyIC* ic,
     190             :                          HandleObject obj, HandleValue idVal, HandleValue rhs)
     191             : {
     192           0 :     RootedShape oldShape(cx);
     193           0 :     RootedObjectGroup oldGroup(cx);
     194           0 :     IonScript* ionScript = outerScript->ionScript();
     195             : 
     196           0 :     bool attached = false;
     197           0 :     bool isTemporarilyUnoptimizable = false;
     198             : 
     199           0 :     if (ic->state().maybeTransition())
     200           0 :         ic->discardStubs(cx->zone());
     201             : 
     202           0 :     if (ic->state().canAttachStub()) {
     203           0 :         oldShape = obj->maybeShape();
     204           0 :         oldGroup = JSObject::getGroup(cx, obj);
     205           0 :         if (!oldGroup)
     206           0 :             return false;
     207           0 :         if (obj->is<UnboxedPlainObject>()) {
     208           0 :             MOZ_ASSERT(!oldShape);
     209           0 :             if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
     210           0 :                 oldShape = expando->lastProperty();
     211             :         }
     212             : 
     213           0 :         RootedValue objv(cx, ObjectValue(*obj));
     214           0 :         RootedScript script(cx, ic->script());
     215           0 :         jsbytecode* pc = ic->pc();
     216           0 :         SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state().mode(),
     217             :                                &isTemporarilyUnoptimizable,
     218           0 :                                objv, idVal, rhs, ic->needsTypeBarrier(), ic->guardHoles());
     219           0 :         if (gen.tryAttachStub()) {
     220           0 :             ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached,
     221           0 :                                   gen.typeCheckInfo());
     222             :         }
     223             :     }
     224             : 
     225           0 :     jsbytecode* pc = ic->pc();
     226           0 :     if (ic->kind() == CacheKind::SetElem) {
     227           0 :         if (IsPropertyInitOp(JSOp(*pc))) {
     228           0 :             if (!InitElemOperation(cx, pc, obj, idVal, rhs))
     229           0 :                 return false;
     230             :         } else {
     231           0 :             MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
     232           0 :             if (!SetObjectElement(cx, obj, idVal, rhs, ic->strict()))
     233           0 :                 return false;
     234             :         }
     235             :     } else {
     236           0 :         MOZ_ASSERT(ic->kind() == CacheKind::SetProp);
     237             : 
     238           0 :         if (*pc == JSOP_INITGLEXICAL) {
     239           0 :             RootedScript script(cx, ic->script());
     240           0 :             MOZ_ASSERT(!script->hasNonSyntacticScope());
     241           0 :             InitGlobalLexicalOperation(cx, &cx->global()->lexicalEnvironment(), script, pc, rhs);
     242             :         } else {
     243           0 :             RootedPropertyName name(cx, idVal.toString()->asAtom().asPropertyName());
     244           0 :             if (!SetProperty(cx, obj, name, rhs, ic->strict(), pc))
     245           0 :                 return false;
     246             :         }
     247             :     }
     248             : 
     249           0 :     if (attached)
     250           0 :         return true;
     251             : 
     252             :     // The SetProperty call might have entered this IC recursively, so try
     253             :     // to transition.
     254           0 :     if (ic->state().maybeTransition())
     255           0 :         ic->discardStubs(cx->zone());
     256             : 
     257           0 :     if (ic->state().canAttachStub()) {
     258           0 :         RootedValue objv(cx, ObjectValue(*obj));
     259           0 :         RootedScript script(cx, ic->script());
     260           0 :         jsbytecode* pc = ic->pc();
     261           0 :         SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state().mode(),
     262             :                                &isTemporarilyUnoptimizable,
     263           0 :                                objv, idVal, rhs, ic->needsTypeBarrier(), ic->guardHoles());
     264           0 :         if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
     265           0 :             ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached,
     266           0 :                                   gen.typeCheckInfo());
     267             :         } else {
     268           0 :             gen.trackNotAttached();
     269             :         }
     270             : 
     271           0 :         if (!attached && !isTemporarilyUnoptimizable)
     272           0 :             ic->state().trackNotAttached();
     273             :     }
     274             : 
     275           0 :     return true;
     276             : }
     277             : 
     278             : /* static */ bool
     279           0 : IonGetNameIC::update(JSContext* cx, HandleScript outerScript, IonGetNameIC* ic,
     280             :                      HandleObject envChain, MutableHandleValue res)
     281             : {
     282           0 :     IonScript* ionScript = outerScript->ionScript();
     283           0 :     jsbytecode* pc = ic->pc();
     284           0 :     RootedPropertyName name(cx, ic->script()->getName(pc));
     285             : 
     286           0 :     if (ic->state().maybeTransition())
     287           0 :         ic->discardStubs(cx->zone());
     288             : 
     289           0 :     if (ic->state().canAttachStub()) {
     290           0 :         bool attached = false;
     291           0 :         RootedScript script(cx, ic->script());
     292           0 :         GetNameIRGenerator gen(cx, script, pc, ic->state().mode(), envChain, name);
     293           0 :         if (gen.tryAttachStub())
     294           0 :             ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
     295             : 
     296           0 :         if (!attached)
     297           0 :             ic->state().trackNotAttached();
     298             :     }
     299             : 
     300           0 :     RootedObject obj(cx);
     301           0 :     RootedObject holder(cx);
     302           0 :     Rooted<PropertyResult> prop(cx);
     303           0 :     if (!LookupName(cx, name, envChain, &obj, &holder, &prop))
     304           0 :         return false;
     305             : 
     306           0 :     if (*GetNextPc(pc) == JSOP_TYPEOF) {
     307           0 :         if (!FetchName<GetNameMode::TypeOf>(cx, obj, holder, name, prop, res))
     308           0 :             return false;
     309             :     } else {
     310           0 :         if (!FetchName<GetNameMode::Normal>(cx, obj, holder, name, prop, res))
     311           0 :             return false;
     312             :     }
     313             : 
     314             :     // No need to call TypeScript::Monitor, IonBuilder always inserts a type
     315             :     // barrier after GetName ICs.
     316             : 
     317           0 :     return true;
     318             : }
     319             : 
     320             : /* static */ JSObject*
     321           0 : IonBindNameIC::update(JSContext* cx, HandleScript outerScript, IonBindNameIC* ic,
     322             :                       HandleObject envChain)
     323             : {
     324           0 :     IonScript* ionScript = outerScript->ionScript();
     325           0 :     jsbytecode* pc = ic->pc();
     326           0 :     RootedPropertyName name(cx, ic->script()->getName(pc));
     327             : 
     328           0 :     if (ic->state().maybeTransition())
     329           0 :         ic->discardStubs(cx->zone());
     330             : 
     331           0 :     if (ic->state().canAttachStub()) {
     332           0 :         bool attached = false;
     333           0 :         RootedScript script(cx, ic->script());
     334           0 :         BindNameIRGenerator gen(cx, script, pc, ic->state().mode(), envChain, name);
     335           0 :         if (gen.tryAttachStub())
     336           0 :             ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
     337             : 
     338           0 :         if (!attached)
     339           0 :             ic->state().trackNotAttached();
     340             :     }
     341             : 
     342           0 :     RootedObject holder(cx);
     343           0 :     if (!LookupNameUnqualified(cx, name, envChain, &holder))
     344           0 :         return nullptr;
     345             : 
     346           0 :     return holder;
     347             : }
     348             : 
     349             : /* static */ bool
     350           0 : IonHasOwnIC::update(JSContext* cx, HandleScript outerScript, IonHasOwnIC* ic,
     351             :                     HandleValue val, HandleValue idVal, int32_t* res)
     352             : {
     353           0 :     IonScript* ionScript = outerScript->ionScript();
     354             : 
     355           0 :     if (ic->state().maybeTransition())
     356           0 :         ic->discardStubs(cx->zone());
     357             : 
     358           0 :     jsbytecode* pc = ic->pc();
     359             : 
     360           0 :     if (ic->state().canAttachStub()) {
     361           0 :         bool attached = false;
     362           0 :         RootedScript script(cx, ic->script());
     363           0 :         HasPropIRGenerator gen(cx, script, pc, CacheKind::HasOwn, ic->state().mode(), idVal, val);
     364           0 :         if (gen.tryAttachStub())
     365           0 :             ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
     366             : 
     367           0 :         if (!attached)
     368           0 :             ic->state().trackNotAttached();
     369             :     }
     370             : 
     371             :     bool found;
     372           0 :     if (!HasOwnProperty(cx, val, idVal, &found))
     373           0 :         return false;
     374             : 
     375           0 :     *res = found;
     376           0 :     return true;
     377             : }
     378             : 
     379             : /* static */ bool
     380           1 : IonInIC::update(JSContext* cx, HandleScript outerScript, IonInIC* ic,
     381             :                 HandleValue key, HandleObject obj, bool* res)
     382             : {
     383           1 :     IonScript* ionScript = outerScript->ionScript();
     384             : 
     385           1 :     if (ic->state().maybeTransition())
     386           0 :         ic->discardStubs(cx->zone());
     387             : 
     388           1 :     if (ic->state().canAttachStub()) {
     389           1 :         bool attached = false;
     390           2 :         RootedScript script(cx, ic->script());
     391           2 :         RootedValue objV(cx, ObjectValue(*obj));
     392           1 :         jsbytecode* pc = ic->pc();
     393           2 :         HasPropIRGenerator gen(cx, script, pc, CacheKind::In, ic->state().mode(), key, objV);
     394           1 :         if (gen.tryAttachStub())
     395           1 :             ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
     396             : 
     397           1 :         if (!attached)
     398           0 :             ic->state().trackNotAttached();
     399             :     }
     400             : 
     401           1 :     return OperatorIn(cx, key, obj, res);
     402             : }
     403             : 
     404             : uint8_t*
     405          36 : IonICStub::stubDataStart()
     406             : {
     407          36 :     return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
     408             : }
     409             : 
     410             : void
     411          21 : IonIC::attachStub(IonICStub* newStub, JitCode* code)
     412             : {
     413          21 :     MOZ_ASSERT(newStub);
     414          21 :     MOZ_ASSERT(code);
     415             : 
     416          21 :     if (firstStub_) {
     417           6 :         IonICStub* last = firstStub_;
     418          16 :         while (IonICStub* next = last->next())
     419          10 :             last = next;
     420           6 :         last->setNext(newStub, code);
     421             :     } else {
     422          15 :         firstStub_ = newStub;
     423          15 :         codeRaw_ = code->raw();
     424             :     }
     425             : 
     426          21 :     state_.trackAttached();
     427          21 : }

Generated by: LCOV version 1.13