LCOV - code coverage report
Current view: top level - js/src/jit - IonBuilder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1639 6348 25.8 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
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/IonBuilder.h"
       8             : 
       9             : #include "mozilla/DebugOnly.h"
      10             : #include "mozilla/ScopeExit.h"
      11             : 
      12             : #include "builtin/Eval.h"
      13             : #include "builtin/TypedObject.h"
      14             : #include "frontend/SourceNotes.h"
      15             : #include "jit/BaselineFrame.h"
      16             : #include "jit/BaselineInspector.h"
      17             : #include "jit/Ion.h"
      18             : #include "jit/IonControlFlow.h"
      19             : #include "jit/IonOptimizationLevels.h"
      20             : #include "jit/JitSpewer.h"
      21             : #include "jit/Lowering.h"
      22             : #include "jit/MIRGraph.h"
      23             : #include "vm/ArgumentsObject.h"
      24             : #include "vm/Opcodes.h"
      25             : #include "vm/RegExpStatics.h"
      26             : #include "vm/TraceLogging.h"
      27             : 
      28             : #include "gc/Nursery-inl.h"
      29             : #include "jit/CompileInfo-inl.h"
      30             : #include "jit/shared/Lowering-shared-inl.h"
      31             : #include "vm/BytecodeUtil-inl.h"
      32             : #include "vm/EnvironmentObject-inl.h"
      33             : #include "vm/JSScript-inl.h"
      34             : #include "vm/NativeObject-inl.h"
      35             : #include "vm/ObjectGroup-inl.h"
      36             : #include "vm/UnboxedObject-inl.h"
      37             : 
      38             : using namespace js;
      39             : using namespace js::jit;
      40             : 
      41             : using mozilla::AssertedCast;
      42             : using mozilla::DebugOnly;
      43             : using mozilla::Maybe;
      44             : using mozilla::Nothing;
      45             : 
      46             : using JS::TrackedStrategy;
      47             : using JS::TrackedOutcome;
      48             : using JS::TrackedTypeSite;
      49             : 
      50             : class jit::BaselineFrameInspector
      51             : {
      52             :   public:
      53             :     TypeSet::Type thisType;
      54             :     JSObject* singletonEnvChain;
      55             : 
      56             :     Vector<TypeSet::Type, 4, JitAllocPolicy> argTypes;
      57             :     Vector<TypeSet::Type, 4, JitAllocPolicy> varTypes;
      58             : 
      59             :     explicit BaselineFrameInspector(TempAllocator* temp)
      60           0 :       : thisType(TypeSet::UndefinedType()),
      61             :         singletonEnvChain(nullptr),
      62             :         argTypes(*temp),
      63           0 :         varTypes(*temp)
      64             :     {}
      65             : };
      66             : 
      67             : BaselineFrameInspector*
      68           0 : jit::NewBaselineFrameInspector(TempAllocator* temp, BaselineFrame* frame)
      69             : {
      70           0 :     MOZ_ASSERT(frame);
      71             : 
      72           0 :     BaselineFrameInspector* inspector = temp->lifoAlloc()->new_<BaselineFrameInspector>(temp);
      73           0 :     if (!inspector)
      74             :         return nullptr;
      75             : 
      76             :     // Note: copying the actual values into a temporary structure for use
      77             :     // during compilation could capture nursery pointers, so the values' types
      78             :     // are recorded instead.
      79             : 
      80           0 :     if (frame->isFunctionFrame())
      81           0 :         inspector->thisType = TypeSet::GetMaybeUntrackedValueType(frame->thisArgument());
      82             : 
      83           0 :     if (frame->environmentChain()->isSingleton())
      84           0 :         inspector->singletonEnvChain = frame->environmentChain();
      85             : 
      86           0 :     JSScript* script = frame->script();
      87             : 
      88           0 :     if (script->functionNonDelazifying()) {
      89           0 :         if (!inspector->argTypes.reserve(frame->numFormalArgs()))
      90             :             return nullptr;
      91           0 :         for (size_t i = 0; i < frame->numFormalArgs(); i++) {
      92           0 :             if (script->formalIsAliased(i)) {
      93           0 :                 inspector->argTypes.infallibleAppend(TypeSet::UndefinedType());
      94           0 :             } else if (!script->argsObjAliasesFormals()) {
      95             :                 TypeSet::Type type =
      96           0 :                     TypeSet::GetMaybeUntrackedValueType(frame->unaliasedFormal(i));
      97           0 :                 inspector->argTypes.infallibleAppend(type);
      98           0 :             } else if (frame->hasArgsObj()) {
      99             :                 TypeSet::Type type =
     100           0 :                     TypeSet::GetMaybeUntrackedValueType(frame->argsObj().arg(i));
     101           0 :                 inspector->argTypes.infallibleAppend(type);
     102             :             } else {
     103           0 :                 inspector->argTypes.infallibleAppend(TypeSet::UndefinedType());
     104             :             }
     105             :         }
     106             :     }
     107             : 
     108           0 :     if (!inspector->varTypes.reserve(frame->numValueSlots()))
     109             :         return nullptr;
     110           0 :     for (size_t i = 0; i < frame->numValueSlots(); i++) {
     111           0 :         TypeSet::Type type = TypeSet::GetMaybeUntrackedValueType(*frame->valueSlot(i));
     112           0 :         inspector->varTypes.infallibleAppend(type);
     113             :     }
     114             : 
     115             :     return inspector;
     116             : }
     117             : 
     118           0 : IonBuilder::IonBuilder(JSContext* analysisContext, CompileRealm* realm,
     119             :                        const JitCompileOptions& options, TempAllocator* temp,
     120             :                        MIRGraph* graph, CompilerConstraintList* constraints,
     121             :                        BaselineInspector* inspector, CompileInfo* info,
     122             :                        const OptimizationInfo* optimizationInfo,
     123             :                        BaselineFrameInspector* baselineFrame, size_t inliningDepth,
     124           0 :                        uint32_t loopDepth)
     125             :   : MIRGenerator(realm, options, temp, graph, info, optimizationInfo),
     126             :     backgroundCodegen_(nullptr),
     127             :     actionableAbortScript_(nullptr),
     128             :     actionableAbortPc_(nullptr),
     129             :     actionableAbortMessage_(nullptr),
     130             :     rootList_(nullptr),
     131             :     analysisContext(analysisContext),
     132             :     baselineFrame_(baselineFrame),
     133             :     constraints_(constraints),
     134             :     thisTypes(nullptr),
     135             :     argTypes(nullptr),
     136             :     typeArray(nullptr),
     137             :     typeArrayHint(0),
     138             :     bytecodeTypeMap(nullptr),
     139             :     loopDepth_(loopDepth),
     140             :     blockWorklist(*temp),
     141             :     cfgCurrent(nullptr),
     142             :     cfg(nullptr),
     143             :     trackedOptimizationSites_(*temp),
     144             :     lexicalCheck_(nullptr),
     145             :     callerResumePoint_(nullptr),
     146             :     callerBuilder_(nullptr),
     147             :     iterators_(*temp),
     148             :     loopHeaders_(*temp),
     149             :     loopHeaderStack_(*temp),
     150             : #ifdef DEBUG
     151             :     cfgLoopHeaderStack_(*temp),
     152             : #endif
     153             :     inspector(inspector),
     154             :     inliningDepth_(inliningDepth),
     155             :     inlinedBytecodeLength_(0),
     156             :     numLoopRestarts_(0),
     157           0 :     failedBoundsCheck_(info->script()->failedBoundsCheck()),
     158           0 :     failedShapeGuard_(info->script()->failedShapeGuard()),
     159           0 :     failedLexicalCheck_(info->script()->failedLexicalCheck()),
     160             : #ifdef DEBUG
     161             :     hasLazyArguments_(false),
     162             : #endif
     163             :     inlineCallInfo_(nullptr),
     164           0 :     maybeFallbackFunctionGetter_(nullptr)
     165             : {
     166           0 :     script_ = info->script();
     167           0 :     scriptHasIonScript_ = script_->hasIonScript();
     168           0 :     pc = info->startPC();
     169             : 
     170           0 :     MOZ_ASSERT(script()->hasBaselineScript() == (info->analysisMode() != Analysis_ArgumentsUsage));
     171           0 :     MOZ_ASSERT(!!analysisContext == (info->analysisMode() == Analysis_DefiniteProperties));
     172           0 :     MOZ_ASSERT(script_->nTypeSets() < UINT16_MAX);
     173             : 
     174           0 :     if (!info->isAnalysis())
     175           0 :         script()->baselineScript()->setIonCompiledOrInlined();
     176           0 : }
     177             : 
     178             : void
     179           0 : IonBuilder::clearForBackEnd()
     180             : {
     181           0 :     MOZ_ASSERT(!analysisContext);
     182           0 :     baselineFrame_ = nullptr;
     183             : 
     184             :     // The caches below allocate data from the malloc heap. Release this before
     185             :     // later phases of compilation to avoid leaks, as the top level IonBuilder
     186             :     // is not explicitly destroyed. Note that builders for inner scripts are
     187             :     // constructed on the stack and will release this memory on destruction.
     188           0 :     envCoordinateNameCache.purge();
     189           0 : }
     190             : 
     191             : mozilla::GenericErrorResult<AbortReason>
     192           0 : IonBuilder::abort(AbortReason r)
     193             : {
     194           0 :     auto res = this->MIRGenerator::abort(r);
     195             : # ifdef DEBUG
     196           0 :     JitSpew(JitSpew_IonAbort, "aborted @ %s:%d", script()->filename(), PCToLineNumber(script(), pc));
     197             : # else
     198             :     JitSpew(JitSpew_IonAbort, "aborted @ %s", script()->filename());
     199             : # endif
     200           0 :     return res;
     201             : }
     202             : 
     203             : mozilla::GenericErrorResult<AbortReason>
     204           0 : IonBuilder::abort(AbortReason r, const char* message, ...)
     205             : {
     206             :     // Don't call PCToLineNumber in release builds.
     207             :     va_list ap;
     208           0 :     va_start(ap, message);
     209           0 :     auto res = this->MIRGenerator::abortFmt(r, message, ap);
     210           0 :     va_end(ap);
     211             : # ifdef DEBUG
     212           0 :     JitSpew(JitSpew_IonAbort, "aborted @ %s:%d", script()->filename(), PCToLineNumber(script(), pc));
     213             : # else
     214             :     JitSpew(JitSpew_IonAbort, "aborted @ %s", script()->filename());
     215             : # endif
     216           0 :     trackActionableAbort(message);
     217           0 :     return res;
     218             : }
     219             : 
     220             : IonBuilder*
     221           0 : IonBuilder::outermostBuilder()
     222             : {
     223           0 :     IonBuilder* builder = this;
     224           0 :     while (builder->callerBuilder_)
     225             :         builder = builder->callerBuilder_;
     226           0 :     return builder;
     227             : }
     228             : 
     229             : void
     230           0 : IonBuilder::trackActionableAbort(const char* message)
     231             : {
     232           0 :     if (!isOptimizationTrackingEnabled())
     233             :         return;
     234             : 
     235           0 :     IonBuilder* topBuilder = outermostBuilder();
     236           0 :     if (topBuilder->hadActionableAbort())
     237             :         return;
     238             : 
     239           0 :     topBuilder->actionableAbortScript_ = script();
     240           0 :     topBuilder->actionableAbortPc_ = pc;
     241           0 :     topBuilder->actionableAbortMessage_ = message;
     242             : }
     243             : 
     244             : void
     245           0 : IonBuilder::spew(const char* message)
     246             : {
     247             :     // Don't call PCToLineNumber in release builds.
     248             : #ifdef DEBUG
     249           0 :     JitSpew(JitSpew_IonMIR, "%s @ %s:%d", message, script()->filename(), PCToLineNumber(script(), pc));
     250             : #endif
     251           0 : }
     252             : 
     253             : JSFunction*
     254           0 : IonBuilder::getSingleCallTarget(TemporaryTypeSet* calleeTypes)
     255             : {
     256           0 :     if (!calleeTypes)
     257             :         return nullptr;
     258             : 
     259           0 :     TemporaryTypeSet::ObjectKey* key = calleeTypes->maybeSingleObject();
     260           0 :     if (!key || key->clasp() != &JSFunction::class_)
     261             :         return nullptr;
     262             : 
     263             :     if (key->isSingleton())
     264           0 :         return &key->singleton()->as<JSFunction>();
     265             : 
     266           0 :     if (JSFunction* fun = key->group()->maybeInterpretedFunction())
     267             :         return fun;
     268             : 
     269           0 :     return nullptr;
     270             : }
     271             : 
     272             : AbortReasonOr<Ok>
     273           0 : IonBuilder::getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
     274             :                                InliningTargets& targets, uint32_t maxTargets)
     275             : {
     276           0 :     MOZ_ASSERT(targets.empty());
     277             : 
     278           0 :     if (!calleeTypes)
     279           0 :         return Ok();
     280             : 
     281           0 :     if (calleeTypes->baseFlags() != 0)
     282           0 :         return Ok();
     283             : 
     284           0 :     unsigned objCount = calleeTypes->getObjectCount();
     285             : 
     286           0 :     if (objCount == 0 || objCount > maxTargets)
     287           0 :         return Ok();
     288             : 
     289           0 :     if (!targets.reserve(objCount))
     290           0 :         return abort(AbortReason::Alloc);
     291           0 :     for (unsigned i = 0; i < objCount; i++) {
     292           0 :         JSObject* obj = calleeTypes->getSingleton(i);
     293           0 :         ObjectGroup* group = nullptr;
     294           0 :         if (obj) {
     295           0 :             MOZ_ASSERT(obj->isSingleton());
     296             :         } else {
     297           0 :             group = calleeTypes->getGroup(i);
     298           0 :             if (!group)
     299             :                 continue;
     300             : 
     301           0 :             obj = group->maybeInterpretedFunction();
     302           0 :             if (!obj) {
     303           0 :                 targets.clear();
     304           0 :                 return Ok();
     305             :             }
     306             : 
     307           0 :             MOZ_ASSERT(!obj->isSingleton());
     308             :         }
     309             : 
     310             :         // Don't optimize if the callee is not callable or constructable per
     311             :         // the manner it is being invoked, so that CallKnown does not have to
     312             :         // handle these cases (they will always throw).
     313           0 :         if (constructing ? !obj->isConstructor() : !obj->isCallable()) {
     314           0 :             targets.clear();
     315           0 :             return Ok();
     316             :         }
     317             : 
     318           0 :         targets.infallibleAppend(InliningTarget(obj, group));
     319             :     }
     320             : 
     321           0 :     return Ok();
     322             : }
     323             : 
     324             : IonBuilder::InliningDecision
     325           0 : IonBuilder::DontInline(JSScript* targetScript, const char* reason)
     326             : {
     327           0 :     if (targetScript) {
     328           0 :         JitSpew(JitSpew_Inlining, "Cannot inline %s:%u: %s",
     329           0 :                 targetScript->filename(), targetScript->lineno(), reason);
     330             :     } else {
     331           0 :         JitSpew(JitSpew_Inlining, "Cannot inline: %s", reason);
     332             :     }
     333             : 
     334           0 :     return InliningDecision_DontInline;
     335             : }
     336             : 
     337             : /*
     338             :  * |hasCommonInliningPath| determines whether the current inlining path has been
     339             :  * seen before based on the sequence of scripts in the chain of |IonBuilder|s.
     340             :  *
     341             :  * An inlining path for a function |f| is the sequence of functions whose
     342             :  * inlinings precede |f| up to any previous occurrences of |f|.
     343             :  * So, if we have the chain of inlinings
     344             :  *
     345             :  *          f1 -> f2 -> f -> f3 -> f4 -> f5 -> f
     346             :  *          --------         --------------
     347             :  *
     348             :  * the inlining paths for |f| are [f2, f1] and [f5, f4, f3].
     349             :  * When attempting to inline |f|, we find all existing inlining paths for |f|
     350             :  * and check whether they share a common prefix with the path created were |f|
     351             :  * inlined.
     352             :  *
     353             :  * For example, given mutually recursive functions |f| and |g|, a possible
     354             :  * inlining is
     355             :  *
     356             :  *                           +---- Inlining stopped here...
     357             :  *                           |
     358             :  *                           v
     359             :  *          a -> f -> g -> f \ -> g -> f -> g -> ...
     360             :  *
     361             :  * where the vertical bar denotes the termination of inlining.
     362             :  * Inlining is terminated because we have already observed the inlining path
     363             :  * [f] when inlining function |g|. Note that this will inline recursive
     364             :  * functions such as |fib| only one level, as |fib| has a zero length inlining
     365             :  * path which trivially prefixes all inlining paths.
     366             :  *
     367             :  */
     368             : bool
     369           0 : IonBuilder::hasCommonInliningPath(const JSScript* scriptToInline)
     370             : {
     371             :     // Find all previous inlinings of the |scriptToInline| and check for common
     372             :     // inlining paths with the top of the inlining stack.
     373           0 :     for (IonBuilder* it = this->callerBuilder_; it; it = it->callerBuilder_) {
     374           0 :         if (it->script() != scriptToInline)
     375             :             continue;
     376             : 
     377             :         // This only needs to check the top of each stack for a match,
     378             :         // as a match of length one ensures a common prefix.
     379           0 :         IonBuilder* path = it->callerBuilder_;
     380           0 :         if (!path || this->script() == path->script())
     381             :             return true;
     382             :     }
     383             : 
     384             :     return false;
     385             : }
     386             : 
     387             : IonBuilder::InliningDecision
     388           0 : IonBuilder::canInlineTarget(JSFunction* target, CallInfo& callInfo)
     389             : {
     390           0 :     if (!optimizationInfo().inlineInterpreted()) {
     391             :         trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
     392             :         return InliningDecision_DontInline;
     393             :     }
     394             : 
     395           0 :     if (TraceLogTextIdEnabled(TraceLogger_InlinedScripts)) {
     396             :         return DontInline(nullptr, "Tracelogging of inlined scripts is enabled"
     397           0 :                                    "but Tracelogger cannot do that yet.");
     398             :     }
     399             : 
     400           0 :     if (!target->isInterpreted()) {
     401           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNotInterpreted);
     402           0 :         return DontInline(nullptr, "Non-interpreted target");
     403             :     }
     404             : 
     405           0 :     if (info().analysisMode() != Analysis_DefiniteProperties) {
     406             :         // If |this| or an argument has an empty resultTypeSet, don't bother
     407             :         // inlining, as the call is currently unreachable due to incomplete type
     408             :         // information. This does not apply to the definite properties analysis,
     409             :         // in that case we want to inline anyway.
     410             : 
     411           0 :         if (callInfo.thisArg()->emptyResultTypeSet()) {
     412           0 :             trackOptimizationOutcome(TrackedOutcome::CantInlineUnreachable);
     413           0 :             return DontInline(nullptr, "Empty TypeSet for |this|");
     414             :         }
     415             : 
     416           0 :         for (size_t i = 0; i < callInfo.argc(); i++) {
     417           0 :             if (callInfo.getArg(i)->emptyResultTypeSet()) {
     418           0 :                 trackOptimizationOutcome(TrackedOutcome::CantInlineUnreachable);
     419           0 :                 return DontInline(nullptr, "Empty TypeSet for argument");
     420             :             }
     421             :         }
     422             :     }
     423             : 
     424             :     // Allow constructing lazy scripts when performing the definite properties
     425             :     // analysis, as baseline has not been used to warm the caller up yet.
     426           0 :     if (target->isInterpreted() && info().analysisMode() == Analysis_DefiniteProperties) {
     427           0 :         RootedFunction fun(analysisContext, target);
     428           0 :         RootedScript script(analysisContext, JSFunction::getOrCreateScript(analysisContext, fun));
     429           0 :         if (!script)
     430           0 :             return InliningDecision_Error;
     431             : 
     432           0 :         if (!script->hasBaselineScript() && script->canBaselineCompile()) {
     433           0 :             MethodStatus status = BaselineCompile(analysisContext, script);
     434           0 :             if (status == Method_Error)
     435             :                 return InliningDecision_Error;
     436           0 :             if (status != Method_Compiled) {
     437             :                 trackOptimizationOutcome(TrackedOutcome::CantInlineNoBaseline);
     438             :                 return InliningDecision_DontInline;
     439             :             }
     440             :         }
     441             :     }
     442             : 
     443           0 :     if (!target->hasScript()) {
     444           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineLazy);
     445           0 :         return DontInline(nullptr, "Lazy script");
     446             :     }
     447             : 
     448           0 :     JSScript* inlineScript = target->nonLazyScript();
     449           0 :     if (callInfo.constructing() && !target->isConstructor()) {
     450           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNotConstructor);
     451           0 :         return DontInline(inlineScript, "Callee is not a constructor");
     452             :     }
     453             : 
     454           0 :     if (!callInfo.constructing() && target->isClassConstructor()) {
     455           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineClassConstructor);
     456           0 :         return DontInline(inlineScript, "Not constructing class constructor");
     457             :     }
     458             : 
     459           0 :     if (!CanIonInlineScript(inlineScript)) {
     460           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
     461           0 :         return DontInline(inlineScript, "Disabled Ion compilation");
     462             :     }
     463             : 
     464             :     // Don't inline functions which don't have baseline scripts.
     465           0 :     if (!inlineScript->hasBaselineScript()) {
     466           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNoBaseline);
     467           0 :         return DontInline(inlineScript, "No baseline jitcode");
     468             :     }
     469             : 
     470           0 :     if (TooManyFormalArguments(target->nargs())) {
     471           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineTooManyArgs);
     472           0 :         return DontInline(inlineScript, "Too many args");
     473             :     }
     474             : 
     475             :     // We check the number of actual arguments against the maximum number of
     476             :     // formal arguments as we do not want to encode all actual arguments in the
     477             :     // callerResumePoint.
     478           0 :     if (TooManyFormalArguments(callInfo.argc())) {
     479           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineTooManyArgs);
     480           0 :         return DontInline(inlineScript, "Too many actual args");
     481             :     }
     482             : 
     483           0 :     if (hasCommonInliningPath(inlineScript)) {
     484           0 :         trackOptimizationOutcome(TrackedOutcome::HasCommonInliningPath);
     485           0 :         return DontInline(inlineScript, "Common inlining path");
     486             :     }
     487             : 
     488           0 :     if (inlineScript->uninlineable()) {
     489           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
     490           0 :         return DontInline(inlineScript, "Uninlineable script");
     491             :     }
     492             : 
     493           0 :     if (inlineScript->needsArgsObj()) {
     494           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNeedsArgsObj);
     495           0 :         return DontInline(inlineScript, "Script that needs an arguments object");
     496             :     }
     497             : 
     498           0 :     if (inlineScript->isDebuggee()) {
     499           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineDebuggee);
     500           0 :         return DontInline(inlineScript, "Script is debuggee");
     501             :     }
     502             : 
     503             :     return InliningDecision_Inline;
     504             : }
     505             : 
     506             : AbortReasonOr<Ok>
     507           0 : IonBuilder::analyzeNewLoopTypes(const CFGBlock* loopEntryBlock)
     508             : {
     509           0 :     CFGLoopEntry* loopEntry = loopEntryBlock->stopIns()->toLoopEntry();
     510           0 :     CFGBlock* cfgBlock = loopEntry->successor();
     511           0 :     MBasicBlock* entry = blockWorklist[cfgBlock->id()];
     512           0 :     MOZ_ASSERT(!entry->isDead());
     513             : 
     514             :     // The phi inputs at the loop head only reflect types for variables that
     515             :     // were present at the start of the loop. If the variable changes to a new
     516             :     // type within the loop body, and that type is carried around to the loop
     517             :     // head, then we need to know about the new type up front.
     518             :     //
     519             :     // Since SSA information hasn't been constructed for the loop body yet, we
     520             :     // need a separate analysis to pick out the types that might flow around
     521             :     // the loop header. This is a best-effort analysis that may either over-
     522             :     // or under-approximate the set of such types.
     523             :     //
     524             :     // Over-approximating the types may lead to inefficient generated code, and
     525             :     // under-approximating the types will cause the loop body to be analyzed
     526             :     // multiple times as the correct types are deduced (see finishLoop).
     527             : 
     528             :     // If we restarted processing of an outer loop then get loop header types
     529             :     // directly from the last time we have previously processed this loop. This
     530             :     // both avoids repeated work from the bytecode traverse below, and will
     531             :     // also pick up types discovered while previously building the loop body.
     532             :     bool foundEntry = false;
     533           0 :     for (size_t i = 0; i < loopHeaders_.length(); i++) {
     534           0 :         if (loopHeaders_[i].pc == cfgBlock->startPc()) {
     535           0 :             MBasicBlock* oldEntry = loopHeaders_[i].header;
     536             : 
     537             :             // If this block has been discarded, its resume points will have
     538             :             // already discarded their operands.
     539           0 :             if (oldEntry->isDead()) {
     540           0 :                 loopHeaders_[i].header = entry;
     541           0 :                 foundEntry = true;
     542           0 :                 break;
     543             :             }
     544             : 
     545           0 :             MResumePoint* oldEntryRp = oldEntry->entryResumePoint();
     546           0 :             size_t stackDepth = oldEntryRp->stackDepth();
     547           0 :             for (size_t slot = 0; slot < stackDepth; slot++) {
     548           0 :                 MDefinition* oldDef = oldEntryRp->getOperand(slot);
     549           0 :                 if (!oldDef->isPhi()) {
     550           0 :                     MOZ_ASSERT(oldDef->block()->id() < oldEntry->id());
     551           0 :                     MOZ_ASSERT(oldDef == entry->getSlot(slot));
     552             :                     continue;
     553             :                 }
     554           0 :                 MPhi* oldPhi = oldDef->toPhi();
     555           0 :                 MPhi* newPhi = entry->getSlot(slot)->toPhi();
     556           0 :                 if (!newPhi->addBackedgeType(alloc(), oldPhi->type(), oldPhi->resultTypeSet()))
     557           0 :                     return abort(AbortReason::Alloc);
     558             :             }
     559             : 
     560             :             // Update the most recent header for this loop encountered, in case
     561             :             // new types flow to the phis and the loop is processed at least
     562             :             // three times.
     563           0 :             loopHeaders_[i].header = entry;
     564           0 :             return Ok();
     565             :         }
     566             :     }
     567           0 :     if (!foundEntry) {
     568           0 :         if (!loopHeaders_.append(LoopHeader(cfgBlock->startPc(), entry)))
     569           0 :             return abort(AbortReason::Alloc);
     570             :     }
     571             : 
     572           0 :     if (loopEntry->isForIn()) {
     573             :         // The backedge will have MIteratorMore with MIRType::Value. This slot
     574             :         // is initialized to MIRType::Undefined before the loop. Add
     575             :         // MIRType::Value to avoid unnecessary loop restarts.
     576             : 
     577           0 :         MPhi* phi = entry->getSlot(entry->stackDepth() - 1)->toPhi();
     578           0 :         MOZ_ASSERT(phi->getOperand(0)->type() == MIRType::Undefined);
     579             : 
     580           0 :         if (!phi->addBackedgeType(alloc(), MIRType::Value, nullptr))
     581           0 :             return abort(AbortReason::Alloc);
     582             :     }
     583             : 
     584             :     // Get the start and end pc of this loop.
     585           0 :     jsbytecode* start = loopEntryBlock->stopPc();
     586           0 :     start += GetBytecodeLength(start);
     587           0 :     jsbytecode* end = loopEntry->loopStopPc();
     588             : 
     589             :     // Iterate the bytecode quickly to seed possible types in the loopheader.
     590           0 :     jsbytecode* last = nullptr;
     591           0 :     jsbytecode* earlier = nullptr;
     592           0 :     for (jsbytecode* pc = start; pc != end; earlier = last, last = pc, pc += GetBytecodeLength(pc)) {
     593             :         uint32_t slot;
     594           0 :         if (*pc == JSOP_SETLOCAL)
     595           0 :             slot = info().localSlot(GET_LOCALNO(pc));
     596           0 :         else if (*pc == JSOP_SETARG)
     597           0 :             slot = info().argSlotUnchecked(GET_ARGNO(pc));
     598             :         else
     599             :             continue;
     600           0 :         if (slot >= info().firstStackSlot())
     601             :             continue;
     602           0 :         if (!last)
     603             :             continue;
     604             : 
     605           0 :         MPhi* phi = entry->getSlot(slot)->toPhi();
     606             : 
     607           0 :         if (*last == JSOP_POS)
     608           0 :             last = earlier;
     609             : 
     610           0 :         if (CodeSpec[*last].format & JOF_TYPESET) {
     611           0 :             TemporaryTypeSet* typeSet = bytecodeTypes(last);
     612           0 :             if (!typeSet->empty()) {
     613           0 :                 MIRType type = typeSet->getKnownMIRType();
     614           0 :                 if (!phi->addBackedgeType(alloc(), type, typeSet))
     615           0 :                     return abort(AbortReason::Alloc);
     616             :             }
     617           0 :         } else if (*last == JSOP_GETLOCAL || *last == JSOP_GETARG) {
     618             :             uint32_t slot = (*last == JSOP_GETLOCAL)
     619           0 :                             ? info().localSlot(GET_LOCALNO(last))
     620           0 :                             : info().argSlotUnchecked(GET_ARGNO(last));
     621           0 :             if (slot < info().firstStackSlot()) {
     622           0 :                 MPhi* otherPhi = entry->getSlot(slot)->toPhi();
     623           0 :                 if (otherPhi->hasBackedgeType()) {
     624           0 :                     if (!phi->addBackedgeType(alloc(), otherPhi->type(), otherPhi->resultTypeSet()))
     625           0 :                         return abort(AbortReason::Alloc);
     626             :                 }
     627             :             }
     628             :         } else {
     629           0 :             MIRType type = MIRType::None;
     630           0 :             switch (*last) {
     631             :               case JSOP_VOID:
     632             :               case JSOP_UNDEFINED:
     633           0 :                 type = MIRType::Undefined;
     634           0 :                 break;
     635             :               case JSOP_GIMPLICITTHIS:
     636           0 :                 if (!script()->hasNonSyntacticScope())
     637           0 :                     type = MIRType::Undefined;
     638             :                 break;
     639             :               case JSOP_NULL:
     640           0 :                 type = MIRType::Null;
     641           0 :                 break;
     642             :               case JSOP_ZERO:
     643             :               case JSOP_ONE:
     644             :               case JSOP_INT8:
     645             :               case JSOP_INT32:
     646             :               case JSOP_UINT16:
     647             :               case JSOP_UINT24:
     648             :               case JSOP_BITAND:
     649             :               case JSOP_BITOR:
     650             :               case JSOP_BITXOR:
     651             :               case JSOP_BITNOT:
     652             :               case JSOP_RSH:
     653             :               case JSOP_LSH:
     654             :               case JSOP_URSH:
     655           0 :                 type = MIRType::Int32;
     656           0 :                 break;
     657             :               case JSOP_FALSE:
     658             :               case JSOP_TRUE:
     659             :               case JSOP_EQ:
     660             :               case JSOP_NE:
     661             :               case JSOP_LT:
     662             :               case JSOP_LE:
     663             :               case JSOP_GT:
     664             :               case JSOP_GE:
     665             :               case JSOP_NOT:
     666             :               case JSOP_STRICTEQ:
     667             :               case JSOP_STRICTNE:
     668             :               case JSOP_IN:
     669             :               case JSOP_INSTANCEOF:
     670             :               case JSOP_HASOWN:
     671           0 :                 type = MIRType::Boolean;
     672           0 :                 break;
     673             :               case JSOP_DOUBLE:
     674           0 :                 type = MIRType::Double;
     675           0 :                 break;
     676             :               case JSOP_ITERNEXT:
     677             :               case JSOP_STRING:
     678             :               case JSOP_TOSTRING:
     679             :               case JSOP_TYPEOF:
     680             :               case JSOP_TYPEOFEXPR:
     681           0 :                 type = MIRType::String;
     682           0 :                 break;
     683             :               case JSOP_SYMBOL:
     684           0 :                 type = MIRType::Symbol;
     685           0 :                 break;
     686             :               case JSOP_ADD:
     687             :               case JSOP_SUB:
     688             :               case JSOP_MUL:
     689             :               case JSOP_DIV:
     690             :               case JSOP_MOD:
     691             :               case JSOP_NEG:
     692           0 :                 type = inspector->expectedResultType(last);
     693           0 :                 break;
     694             :               default:
     695             :                 break;
     696             :             }
     697           0 :             if (type != MIRType::None) {
     698           0 :                 if (!phi->addBackedgeType(alloc(), type, nullptr))
     699           0 :                     return abort(AbortReason::Alloc);
     700             :             }
     701             :         }
     702             :     }
     703           0 :     return Ok();
     704             : }
     705             : 
     706             : AbortReasonOr<Ok>
     707           0 : IonBuilder::init()
     708             : {
     709             :     {
     710           0 :         LifoAlloc::AutoFallibleScope fallibleAllocator(alloc().lifoAlloc());
     711           0 :         if (!TypeScript::FreezeTypeSets(constraints(), script(), &thisTypes, &argTypes, &typeArray))
     712           0 :             return abort(AbortReason::Alloc);
     713             :     }
     714             : 
     715           0 :     if (!alloc().ensureBallast())
     716           0 :         return abort(AbortReason::Alloc);
     717             : 
     718           0 :     if (inlineCallInfo_) {
     719             :         // If we're inlining, the actual this/argument types are not necessarily
     720             :         // a subset of the script's observed types. |argTypes| is never accessed
     721             :         // for inlined scripts, so we just null it.
     722           0 :         thisTypes = inlineCallInfo_->thisArg()->resultTypeSet();
     723           0 :         argTypes = nullptr;
     724             :     }
     725             : 
     726             :     // The baseline script normally has the bytecode type map, but compute
     727             :     // it ourselves if we do not have a baseline script.
     728           0 :     if (script()->hasBaselineScript()) {
     729           0 :         bytecodeTypeMap = script()->baselineScript()->bytecodeTypeMap();
     730             :     } else {
     731           0 :         bytecodeTypeMap = alloc_->lifoAlloc()->newArrayUninitialized<uint32_t>(script()->nTypeSets());
     732           0 :         if (!bytecodeTypeMap)
     733           0 :             return abort(AbortReason::Alloc);
     734           0 :         FillBytecodeTypeMap(script(), bytecodeTypeMap);
     735             :     }
     736             : 
     737           0 :     return Ok();
     738             : }
     739             : 
     740             : AbortReasonOr<Ok>
     741           0 : IonBuilder::build()
     742             : {
     743           0 :     MOZ_TRY(init());
     744             : 
     745           0 :     if (script()->hasBaselineScript())
     746           0 :         script()->baselineScript()->resetMaxInliningDepth();
     747             : 
     748             :     MBasicBlock* entry;
     749           0 :     MOZ_TRY_VAR(entry, newBlock(info().firstStackSlot(), pc));
     750           0 :     MOZ_TRY(setCurrentAndSpecializePhis(entry));
     751             : 
     752             : #ifdef JS_JITSPEW
     753           0 :     if (info().isAnalysis()) {
     754           0 :         JitSpew(JitSpew_IonScripts, "Analyzing script %s:%u (%p) %s",
     755           0 :                 script()->filename(), script()->lineno(), (void*)script(),
     756           0 :                 AnalysisModeString(info().analysisMode()));
     757             :     } else {
     758           0 :         JitSpew(JitSpew_IonScripts, "%sompiling script %s:%u (%p) (warmup-counter=%" PRIu32 ", level=%s)",
     759           0 :                 (script()->hasIonScript() ? "Rec" : "C"),
     760           0 :                 script()->filename(), script()->lineno(), (void*)script(),
     761           0 :                 script()->getWarmUpCount(), OptimizationLevelString(optimizationInfo().level()));
     762             :     }
     763             : #endif
     764             : 
     765           0 :     MOZ_TRY(initParameters());
     766           0 :     initLocals();
     767             : 
     768             :     // Initialize something for the env chain. We can bail out before the
     769             :     // start instruction, but the snapshot is encoded *at* the start
     770             :     // instruction, which means generating any code that could load into
     771             :     // registers is illegal.
     772           0 :     MInstruction* env = MConstant::New(alloc(), UndefinedValue());
     773           0 :     current->add(env);
     774           0 :     current->initSlot(info().environmentChainSlot(), env);
     775             : 
     776             :     // Initialize the return value.
     777           0 :     MInstruction* returnValue = MConstant::New(alloc(), UndefinedValue());
     778           0 :     current->add(returnValue);
     779           0 :     current->initSlot(info().returnValueSlot(), returnValue);
     780             : 
     781             :     // Initialize the arguments object slot to undefined if necessary.
     782           0 :     if (info().hasArguments()) {
     783           0 :         MInstruction* argsObj = MConstant::New(alloc(), UndefinedValue());
     784           0 :         current->add(argsObj);
     785           0 :         current->initSlot(info().argsObjSlot(), argsObj);
     786             :     }
     787             : 
     788             :     // Emit the start instruction, so we can begin real instructions.
     789           0 :     current->add(MStart::New(alloc()));
     790             : 
     791             :     // Guard against over-recursion. Do this before we start unboxing, since
     792             :     // this will create an OSI point that will read the incoming argument
     793             :     // values, which is nice to do before their last real use, to minimize
     794             :     // register/stack pressure.
     795           0 :     MCheckOverRecursed* check = MCheckOverRecursed::New(alloc());
     796           0 :     current->add(check);
     797           0 :     MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
     798           0 :     if (!entryRpCopy)
     799           0 :         return abort(AbortReason::Alloc);
     800           0 :     check->setResumePoint(entryRpCopy);
     801             : 
     802             :     // Parameters have been checked to correspond to the typeset, now we unbox
     803             :     // what we can in an infallible manner.
     804           0 :     MOZ_TRY(rewriteParameters());
     805             : 
     806             :     // Check for redeclaration errors for global scripts.
     807           0 :     if (!info().funMaybeLazy() && !info().module() &&
     808           0 :         script()->bodyScope()->is<GlobalScope>() &&
     809           0 :         script()->bodyScope()->as<GlobalScope>().hasBindings())
     810             :     {
     811           0 :         MGlobalNameConflictsCheck* redeclCheck = MGlobalNameConflictsCheck::New(alloc());
     812           0 :         current->add(redeclCheck);
     813           0 :         MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
     814           0 :         if (!entryRpCopy)
     815           0 :             return abort(AbortReason::Alloc);
     816           0 :         redeclCheck->setResumePoint(entryRpCopy);
     817             :     }
     818             : 
     819             :     // It's safe to start emitting actual IR, so now build the env chain.
     820           0 :     MOZ_TRY(initEnvironmentChain());
     821           0 :     if (info().needsArgsObj())
     822           0 :         initArgumentsObject();
     823             : 
     824             :     // The type analysis phase attempts to insert unbox operations near
     825             :     // definitions of values. It also attempts to replace uses in resume points
     826             :     // with the narrower, unboxed variants. However, we must prevent this
     827             :     // replacement from happening on values in the entry snapshot. Otherwise we
     828             :     // could get this:
     829             :     //
     830             :     //       v0 = MParameter(0)
     831             :     //       v1 = MParameter(1)
     832             :     //       --   ResumePoint(v2, v3)
     833             :     //       v2 = Unbox(v0, INT32)
     834             :     //       v3 = Unbox(v1, INT32)
     835             :     //
     836             :     // So we attach the initial resume point to each parameter, which the type
     837             :     // analysis explicitly checks (this is the same mechanism used for
     838             :     // effectful operations).
     839           0 :     for (uint32_t i = 0; i < info().endArgSlot(); i++) {
     840           0 :         MInstruction* ins = current->getEntrySlot(i)->toInstruction();
     841           0 :         if (ins->type() != MIRType::Value)
     842             :             continue;
     843             : 
     844           0 :         MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
     845           0 :         if (!entryRpCopy)
     846           0 :             return abort(AbortReason::Alloc);
     847           0 :         ins->setResumePoint(entryRpCopy);
     848             :     }
     849             : 
     850             : #ifdef DEBUG
     851             :     // lazyArguments should never be accessed in |argsObjAliasesFormals| scripts.
     852           0 :     if (info().hasArguments() && !info().argsObjAliasesFormals())
     853           0 :         hasLazyArguments_ = true;
     854             : #endif
     855             : 
     856           0 :     insertRecompileCheck();
     857             : 
     858             :     auto clearLastPriorResumePoint = mozilla::MakeScopeExit([&] {
     859             :         // Discard unreferenced & pre-allocated resume points.
     860           0 :         replaceMaybeFallbackFunctionGetter(nullptr);
     861           0 :     });
     862             : 
     863           0 :     MOZ_TRY(traverseBytecode());
     864             : 
     865           0 :     if (script_->hasBaselineScript() &&
     866           0 :         inlinedBytecodeLength_ > script_->baselineScript()->inlinedBytecodeLength())
     867             :     {
     868           0 :         script_->baselineScript()->setInlinedBytecodeLength(inlinedBytecodeLength_);
     869             :     }
     870             : 
     871           0 :     MOZ_TRY(maybeAddOsrTypeBarriers());
     872           0 :     MOZ_TRY(processIterators());
     873             : 
     874           0 :     if (!info().isAnalysis() && !abortedPreliminaryGroups().empty())
     875           0 :         return abort(AbortReason::PreliminaryObjects);
     876             : 
     877           0 :     MOZ_ASSERT(loopDepth_ == 0);
     878           0 :     return Ok();
     879             : }
     880             : 
     881             : AbortReasonOr<Ok>
     882           0 : IonBuilder::processIterators()
     883             : {
     884             :     // Find and mark phis that must transitively hold an iterator live.
     885             : 
     886           0 :     Vector<MDefinition*, 8, SystemAllocPolicy> worklist;
     887             : 
     888           0 :     for (size_t i = 0; i < iterators_.length(); i++) {
     889           0 :         MDefinition* iter = iterators_[i];
     890           0 :         if (!iter->isInWorklist()) {
     891           0 :             if (!worklist.append(iter))
     892           0 :                 return abort(AbortReason::Alloc);
     893           0 :             iter->setInWorklist();
     894             :         }
     895             :     }
     896             : 
     897           0 :     while (!worklist.empty()) {
     898           0 :         MDefinition* def = worklist.popCopy();
     899           0 :         def->setNotInWorklist();
     900             : 
     901           0 :         if (def->isPhi()) {
     902           0 :             MPhi* phi = def->toPhi();
     903             :             phi->setIterator();
     904           0 :             phi->setImplicitlyUsedUnchecked();
     905             :         }
     906             : 
     907           0 :         for (MUseDefIterator iter(def); iter; iter++) {
     908           0 :             MDefinition* use = iter.def();
     909           0 :             if (!use->isInWorklist() && (!use->isPhi() || !use->toPhi()->isIterator())) {
     910           0 :                 if (!worklist.append(use))
     911           0 :                     return abort(AbortReason::Alloc);
     912           0 :                 use->setInWorklist();
     913             :             }
     914             :         }
     915             :     }
     916             : 
     917           0 :     return Ok();
     918             : }
     919             : 
     920             : AbortReasonOr<Ok>
     921           0 : IonBuilder::buildInline(IonBuilder* callerBuilder, MResumePoint* callerResumePoint,
     922             :                         CallInfo& callInfo)
     923             : {
     924           0 :     inlineCallInfo_ = &callInfo;
     925             : 
     926           0 :     MOZ_TRY(init());
     927             : 
     928           0 :     JitSpew(JitSpew_IonScripts, "Inlining script %s:%u (%p)",
     929           0 :             script()->filename(), script()->lineno(), (void*)script());
     930             : 
     931           0 :     callerBuilder_ = callerBuilder;
     932           0 :     callerResumePoint_ = callerResumePoint;
     933             : 
     934           0 :     if (callerBuilder->failedBoundsCheck_)
     935           0 :         failedBoundsCheck_ = true;
     936             : 
     937           0 :     if (callerBuilder->failedShapeGuard_)
     938           0 :         failedShapeGuard_ = true;
     939             : 
     940           0 :     if (callerBuilder->failedLexicalCheck_)
     941           0 :         failedLexicalCheck_ = true;
     942             : 
     943           0 :     safeForMinorGC_ = callerBuilder->safeForMinorGC_;
     944             : 
     945             :     // Generate single entrance block.
     946             :     MBasicBlock* entry;
     947           0 :     MOZ_TRY_VAR(entry, newBlock(info().firstStackSlot(), pc));
     948           0 :     MOZ_TRY(setCurrentAndSpecializePhis(entry));
     949             : 
     950           0 :     current->setCallerResumePoint(callerResumePoint);
     951             : 
     952             :     // Connect the entrance block to the last block in the caller's graph.
     953           0 :     MBasicBlock* predecessor = callerBuilder->current;
     954           0 :     MOZ_ASSERT(predecessor == callerResumePoint->block());
     955             : 
     956           0 :     predecessor->end(MGoto::New(alloc(), current));
     957           0 :     if (!current->addPredecessorWithoutPhis(predecessor))
     958           0 :         return abort(AbortReason::Alloc);
     959             : 
     960             :     // Initialize env chain slot to Undefined.  It's set later by
     961             :     // |initEnvironmentChain|.
     962           0 :     MInstruction* env = MConstant::New(alloc(), UndefinedValue());
     963           0 :     current->add(env);
     964           0 :     current->initSlot(info().environmentChainSlot(), env);
     965             : 
     966             :     // Initialize |return value| slot.
     967           0 :     MInstruction* returnValue = MConstant::New(alloc(), UndefinedValue());
     968           0 :     current->add(returnValue);
     969           0 :     current->initSlot(info().returnValueSlot(), returnValue);
     970             : 
     971             :     // Initialize |arguments| slot.
     972           0 :     if (info().hasArguments()) {
     973           0 :         MInstruction* argsObj = MConstant::New(alloc(), UndefinedValue());
     974           0 :         current->add(argsObj);
     975           0 :         current->initSlot(info().argsObjSlot(), argsObj);
     976             :     }
     977             : 
     978             :     // Initialize |this| slot.
     979           0 :     current->initSlot(info().thisSlot(), callInfo.thisArg());
     980             : 
     981           0 :     JitSpew(JitSpew_Inlining, "Initializing %u arg slots", info().nargs());
     982             : 
     983             :     // NB: Ion does not inline functions which |needsArgsObj|.  So using argSlot()
     984             :     // instead of argSlotUnchecked() below is OK
     985           0 :     MOZ_ASSERT(!info().needsArgsObj());
     986             : 
     987             :     // Initialize actually set arguments.
     988           0 :     uint32_t existing_args = Min<uint32_t>(callInfo.argc(), info().nargs());
     989           0 :     for (size_t i = 0; i < existing_args; ++i) {
     990           0 :         MDefinition* arg = callInfo.getArg(i);
     991           0 :         current->initSlot(info().argSlot(i), arg);
     992             :     }
     993             : 
     994             :     // Pass Undefined for missing arguments
     995           0 :     for (size_t i = callInfo.argc(); i < info().nargs(); ++i) {
     996           0 :         MConstant* arg = MConstant::New(alloc(), UndefinedValue());
     997           0 :         current->add(arg);
     998           0 :         current->initSlot(info().argSlot(i), arg);
     999             :     }
    1000             : 
    1001           0 :     JitSpew(JitSpew_Inlining, "Initializing %u locals", info().nlocals());
    1002             : 
    1003           0 :     initLocals();
    1004             : 
    1005           0 :     JitSpew(JitSpew_Inlining, "Inline entry block MResumePoint %p, %u stack slots",
    1006           0 :             (void*) current->entryResumePoint(), current->entryResumePoint()->stackDepth());
    1007             : 
    1008             :     // +2 for the env chain and |this|, maybe another +1 for arguments object slot.
    1009           0 :     MOZ_ASSERT(current->entryResumePoint()->stackDepth() == info().totalSlots());
    1010             : 
    1011             : #ifdef DEBUG
    1012           0 :     if (script_->argumentsHasVarBinding())
    1013           0 :         hasLazyArguments_ = true;
    1014             : #endif
    1015             : 
    1016           0 :     insertRecompileCheck();
    1017             : 
    1018             :     // Initialize the env chain now that all resume points operands are
    1019             :     // initialized.
    1020           0 :     MOZ_TRY(initEnvironmentChain(callInfo.fun()));
    1021             : 
    1022             :     auto clearLastPriorResumePoint = mozilla::MakeScopeExit([&] {
    1023             :         // Discard unreferenced & pre-allocated resume points.
    1024           0 :         replaceMaybeFallbackFunctionGetter(nullptr);
    1025           0 :     });
    1026             : 
    1027           0 :     MOZ_TRY(traverseBytecode());
    1028             : 
    1029             : 
    1030           0 :     MOZ_ASSERT(iterators_.empty(), "Iterators should be added to outer builder");
    1031             : 
    1032           0 :     if (!info().isAnalysis() && !abortedPreliminaryGroups().empty())
    1033           0 :         return abort(AbortReason::PreliminaryObjects);
    1034             : 
    1035           0 :     return Ok();
    1036             : }
    1037             : 
    1038             : void
    1039           0 : IonBuilder::rewriteParameter(uint32_t slotIdx, MDefinition* param)
    1040             : {
    1041           0 :     MOZ_ASSERT(param->isParameter() || param->isGetArgumentsObjectArg());
    1042             : 
    1043           0 :     TemporaryTypeSet* types = param->resultTypeSet();
    1044           0 :     MDefinition* actual = ensureDefiniteType(param, types->getKnownMIRType());
    1045           0 :     if (actual == param)
    1046             :         return;
    1047             : 
    1048             :     // Careful! We leave the original MParameter in the entry resume point. The
    1049             :     // arguments still need to be checked unless proven otherwise at the call
    1050             :     // site, and these checks can bailout. We can end up:
    1051             :     //   v0 = Parameter(0)
    1052             :     //   v1 = Unbox(v0, INT32)
    1053             :     //   --   ResumePoint(v0)
    1054             :     //
    1055             :     // As usual, it would be invalid for v1 to be captured in the initial
    1056             :     // resume point, rather than v0.
    1057           0 :     current->rewriteSlot(slotIdx, actual);
    1058             : }
    1059             : 
    1060             : // Apply Type Inference information to parameters early on, unboxing them if
    1061             : // they have a definitive type. The actual guards will be emitted by the code
    1062             : // generator, explicitly, as part of the function prologue.
    1063             : AbortReasonOr<Ok>
    1064           0 : IonBuilder::rewriteParameters()
    1065             : {
    1066           0 :     MOZ_ASSERT(info().environmentChainSlot() == 0);
    1067             : 
    1068             :     // If this JSScript is not the code of a function, then skip the
    1069             :     // initialization of function parameters.
    1070           0 :     if (!info().funMaybeLazy())
    1071           0 :         return Ok();
    1072             : 
    1073           0 :     for (uint32_t i = info().startArgSlot(); i < info().endArgSlot(); i++) {
    1074           0 :         if (!alloc().ensureBallast())
    1075           0 :             return abort(AbortReason::Alloc);
    1076           0 :         MDefinition* param = current->getSlot(i);
    1077           0 :         rewriteParameter(i, param);
    1078             :     }
    1079             : 
    1080           0 :     return Ok();
    1081             : }
    1082             : 
    1083             : AbortReasonOr<Ok>
    1084           0 : IonBuilder::initParameters()
    1085             : {
    1086             :     // If this JSScript is not the code of a function, then skip the
    1087             :     // initialization of function parameters.
    1088           0 :     if (!info().funMaybeLazy())
    1089           0 :         return Ok();
    1090             : 
    1091             :     // If we are doing OSR on a frame which initially executed in the
    1092             :     // interpreter and didn't accumulate type information, try to use that OSR
    1093             :     // frame to determine possible initial types for 'this' and parameters.
    1094             : 
    1095           0 :     if (thisTypes->empty() && baselineFrame_) {
    1096           0 :         TypeSet::Type type = baselineFrame_->thisType;
    1097           0 :         if (type.isSingletonUnchecked())
    1098           0 :             checkNurseryObject(type.singleton());
    1099           0 :         thisTypes->addType(type, alloc_->lifoAlloc());
    1100             :     }
    1101             : 
    1102           0 :     MParameter* param = MParameter::New(alloc(), MParameter::THIS_SLOT, thisTypes);
    1103           0 :     current->add(param);
    1104           0 :     current->initSlot(info().thisSlot(), param);
    1105             : 
    1106           0 :     for (uint32_t i = 0; i < info().nargs(); i++) {
    1107           0 :         TemporaryTypeSet* types = &argTypes[i];
    1108           0 :         if (types->empty() && baselineFrame_ &&
    1109           0 :             !script_->baselineScript()->modifiesArguments())
    1110             :         {
    1111           0 :             TypeSet::Type type = baselineFrame_->argTypes[i];
    1112           0 :             if (type.isSingletonUnchecked())
    1113           0 :                 checkNurseryObject(type.singleton());
    1114           0 :             types->addType(type, alloc_->lifoAlloc());
    1115             :         }
    1116             : 
    1117           0 :         param = MParameter::New(alloc().fallible(), i, types);
    1118           0 :         if (!param)
    1119           0 :             return abort(AbortReason::Alloc);
    1120           0 :         current->add(param);
    1121           0 :         current->initSlot(info().argSlotUnchecked(i), param);
    1122             :     }
    1123             : 
    1124           0 :     return Ok();
    1125             : }
    1126             : 
    1127             : void
    1128           0 : IonBuilder::initLocals()
    1129             : {
    1130             :     // Initialize all frame slots to undefined. Lexical bindings are temporal
    1131             :     // dead zoned in bytecode.
    1132             : 
    1133           0 :     if (info().nlocals() == 0)
    1134             :         return;
    1135             : 
    1136           0 :     MConstant* undef = MConstant::New(alloc(), UndefinedValue());
    1137           0 :     current->add(undef);
    1138             : 
    1139           0 :     for (uint32_t i = 0; i < info().nlocals(); i++)
    1140           0 :         current->initSlot(info().localSlot(i), undef);
    1141             : }
    1142             : 
    1143             : bool
    1144           0 : IonBuilder::usesEnvironmentChain()
    1145             : {
    1146             :     // We don't have a BaselineScript if we're running the arguments analysis,
    1147             :     // but it's fine to assume we always use the environment chain in this case.
    1148           0 :     if (info().analysisMode() == Analysis_ArgumentsUsage)
    1149             :         return true;
    1150           0 :     return script()->baselineScript()->usesEnvironmentChain();
    1151             : }
    1152             : 
    1153             : AbortReasonOr<Ok>
    1154           0 : IonBuilder::initEnvironmentChain(MDefinition* callee)
    1155             : {
    1156           0 :     MInstruction* env = nullptr;
    1157             : 
    1158             :     // If the script doesn't use the envchain, then it's already initialized
    1159             :     // from earlier.  However, always make a env chain when |needsArgsObj| is true
    1160             :     // for the script, since arguments object construction requires the env chain
    1161             :     // to be passed in.
    1162           0 :     if (!info().needsArgsObj() && !usesEnvironmentChain())
    1163           0 :         return Ok();
    1164             : 
    1165             :     // The env chain is only tracked in scripts that have NAME opcodes which
    1166             :     // will try to access the env. For other scripts, the env instructions
    1167             :     // will be held live by resume points and code will still be generated for
    1168             :     // them, so just use a constant undefined value.
    1169             : 
    1170           0 :     if (JSFunction* fun = info().funMaybeLazy()) {
    1171           0 :         if (!callee) {
    1172           0 :             MCallee* calleeIns = MCallee::New(alloc());
    1173           0 :             current->add(calleeIns);
    1174           0 :             callee = calleeIns;
    1175             :         }
    1176           0 :         env = MFunctionEnvironment::New(alloc(), callee);
    1177           0 :         current->add(env);
    1178             : 
    1179             :         // This reproduce what is done in CallObject::createForFunction. Skip
    1180             :         // this for the arguments analysis, as the script might not have a
    1181             :         // baseline script with template objects yet.
    1182           0 :         if (fun->needsSomeEnvironmentObject() &&
    1183           0 :             info().analysisMode() != Analysis_ArgumentsUsage)
    1184             :         {
    1185           0 :             if (fun->needsNamedLambdaEnvironment())
    1186           0 :                 env = createNamedLambdaObject(callee, env);
    1187             : 
    1188             :             // TODO: Parameter expression-induced extra var environment not
    1189             :             // yet handled.
    1190           0 :             if (fun->needsExtraBodyVarEnvironment())
    1191           0 :                 return abort(AbortReason::Disable, "Extra var environment unsupported");
    1192             : 
    1193           0 :             if (fun->needsCallObject())
    1194           0 :                 MOZ_TRY_VAR(env, createCallObject(callee, env));
    1195             :         }
    1196           0 :     } else if (ModuleObject* module = info().module()) {
    1197             :         // Modules use a pre-created env object.
    1198           0 :         env = constant(ObjectValue(module->initialEnvironment()));
    1199             :     } else {
    1200             :         // For global scripts without a non-syntactic global scope, the env
    1201             :         // chain is the global lexical env.
    1202           0 :         MOZ_ASSERT(!script()->isForEval());
    1203           0 :         MOZ_ASSERT(!script()->hasNonSyntacticScope());
    1204           0 :         env = constant(ObjectValue(script()->global().lexicalEnvironment()));
    1205             :     }
    1206             : 
    1207             :     // Update the environment slot from UndefinedValue only after initial
    1208             :     // environment is created so that bailout doesn't see a partial env.
    1209             :     // See: |InitFromBailout|
    1210           0 :     current->setEnvironmentChain(env);
    1211           0 :     return Ok();
    1212             : }
    1213             : 
    1214             : void
    1215           0 : IonBuilder::initArgumentsObject()
    1216             : {
    1217           0 :     JitSpew(JitSpew_IonMIR, "%s:%u - Emitting code to initialize arguments object! block=%p",
    1218           0 :             script()->filename(), script()->lineno(), current);
    1219           0 :     MOZ_ASSERT(info().needsArgsObj());
    1220             : 
    1221           0 :     bool mapped = script()->hasMappedArgsObj();
    1222           0 :     ArgumentsObject* templateObj = script()->realm()->maybeArgumentsTemplateObject(mapped);
    1223             : 
    1224             :     MCreateArgumentsObject* argsObj =
    1225           0 :         MCreateArgumentsObject::New(alloc(), current->environmentChain(), templateObj);
    1226           0 :     current->add(argsObj);
    1227           0 :     current->setArgumentsObject(argsObj);
    1228           0 : }
    1229             : 
    1230             : AbortReasonOr<Ok>
    1231           0 : IonBuilder::addOsrValueTypeBarrier(uint32_t slot, MInstruction** def_,
    1232             :                                    MIRType type, TemporaryTypeSet* typeSet)
    1233             : {
    1234           0 :     MInstruction*& def = *def_;
    1235           0 :     MBasicBlock* osrBlock = def->block();
    1236             : 
    1237             :     // Clear bogus type information added in newOsrPreheader().
    1238           0 :     def->setResultType(MIRType::Value);
    1239           0 :     def->setResultTypeSet(nullptr);
    1240             : 
    1241           0 :     if (typeSet && !typeSet->unknown()) {
    1242           0 :         MInstruction* barrier = MTypeBarrier::New(alloc(), def, typeSet);
    1243           0 :         osrBlock->insertBefore(osrBlock->lastIns(), barrier);
    1244           0 :         osrBlock->rewriteSlot(slot, barrier);
    1245           0 :         def = barrier;
    1246             : 
    1247             :         // If the TypeSet is more precise than |type|, adjust |type| for the
    1248             :         // code below.
    1249           0 :         if (type == MIRType::Value)
    1250           0 :             type = barrier->type();
    1251           0 :     } else if (type == MIRType::Null ||
    1252           0 :                type == MIRType::Undefined ||
    1253           0 :                type == MIRType::MagicOptimizedArguments)
    1254             :     {
    1255             :         // No unbox instruction will be added below, so check the type by
    1256             :         // adding a type barrier for a singleton type set.
    1257           0 :         TypeSet::Type ntype = TypeSet::PrimitiveType(ValueTypeFromMIRType(type));
    1258           0 :         LifoAlloc* lifoAlloc = alloc().lifoAlloc();
    1259           0 :         typeSet = lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc, ntype);
    1260           0 :         if (!typeSet)
    1261           0 :             return abort(AbortReason::Alloc);
    1262           0 :         MInstruction* barrier = MTypeBarrier::New(alloc(), def, typeSet);
    1263           0 :         osrBlock->insertBefore(osrBlock->lastIns(), barrier);
    1264           0 :         osrBlock->rewriteSlot(slot, barrier);
    1265           0 :         def = barrier;
    1266             :     }
    1267             : 
    1268           0 :     switch (type) {
    1269             :       case MIRType::Boolean:
    1270             :       case MIRType::Int32:
    1271             :       case MIRType::Double:
    1272             :       case MIRType::String:
    1273             :       case MIRType::Symbol:
    1274             :       case MIRType::Object:
    1275           0 :         if (type != def->type()) {
    1276           0 :             MUnbox* unbox = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
    1277           0 :             osrBlock->insertBefore(osrBlock->lastIns(), unbox);
    1278           0 :             osrBlock->rewriteSlot(slot, unbox);
    1279           0 :             def = unbox;
    1280             :         }
    1281             :         break;
    1282             : 
    1283             :       case MIRType::Null:
    1284             :       {
    1285           0 :         MConstant* c = MConstant::New(alloc(), NullValue());
    1286           0 :         osrBlock->insertBefore(osrBlock->lastIns(), c);
    1287           0 :         osrBlock->rewriteSlot(slot, c);
    1288           0 :         def = c;
    1289           0 :         break;
    1290             :       }
    1291             : 
    1292             :       case MIRType::Undefined:
    1293             :       {
    1294           0 :         MConstant* c = MConstant::New(alloc(), UndefinedValue());
    1295           0 :         osrBlock->insertBefore(osrBlock->lastIns(), c);
    1296           0 :         osrBlock->rewriteSlot(slot, c);
    1297           0 :         def = c;
    1298           0 :         break;
    1299             :       }
    1300             : 
    1301             :       case MIRType::MagicOptimizedArguments:
    1302             :       {
    1303           0 :         MOZ_ASSERT(hasLazyArguments_);
    1304           0 :         MConstant* lazyArg = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
    1305           0 :         osrBlock->insertBefore(osrBlock->lastIns(), lazyArg);
    1306           0 :         osrBlock->rewriteSlot(slot, lazyArg);
    1307           0 :         def = lazyArg;
    1308           0 :         break;
    1309             :       }
    1310             : 
    1311             :       default:
    1312             :         break;
    1313             :     }
    1314             : 
    1315           0 :     MOZ_ASSERT(def == osrBlock->getSlot(slot));
    1316           0 :     return Ok();
    1317             : }
    1318             : 
    1319             : AbortReasonOr<Ok>
    1320           0 : IonBuilder::maybeAddOsrTypeBarriers()
    1321             : {
    1322           0 :     if (!info().osrPc())
    1323           0 :         return Ok();
    1324             : 
    1325             :     // The loop has successfully been processed, and the loop header phis
    1326             :     // have their final type. Add unboxes and type barriers in the OSR
    1327             :     // block to check that the values have the appropriate type, and update
    1328             :     // the types in the preheader.
    1329             : 
    1330           0 :     MBasicBlock* osrBlock = graph().osrBlock();
    1331           0 :     if (!osrBlock) {
    1332             :         // Because IonBuilder does not compile catch blocks, it's possible to
    1333             :         // end up without an OSR block if the OSR pc is only reachable via a
    1334             :         // break-statement inside the catch block. For instance:
    1335             :         //
    1336             :         //   for (;;) {
    1337             :         //       try {
    1338             :         //           throw 3;
    1339             :         //       } catch(e) {
    1340             :         //           break;
    1341             :         //       }
    1342             :         //   }
    1343             :         //   while (..) { } // <= OSR here, only reachable via catch block.
    1344             :         //
    1345             :         // For now we just abort in this case.
    1346           0 :         MOZ_ASSERT(graph().hasTryBlock());
    1347           0 :         return abort(AbortReason::Disable, "OSR block only reachable through catch block");
    1348             :     }
    1349             : 
    1350           0 :     MBasicBlock* preheader = osrBlock->getSuccessor(0);
    1351           0 :     MBasicBlock* header = preheader->getSuccessor(0);
    1352             :     static const size_t OSR_PHI_POSITION = 1;
    1353           0 :     MOZ_ASSERT(preheader->getPredecessor(OSR_PHI_POSITION) == osrBlock);
    1354             : 
    1355           0 :     MResumePoint* headerRp = header->entryResumePoint();
    1356           0 :     size_t stackDepth = headerRp->stackDepth();
    1357           0 :     MOZ_ASSERT(stackDepth == osrBlock->stackDepth());
    1358           0 :     for (uint32_t slot = info().startArgSlot(); slot < stackDepth; slot++) {
    1359             :         // Aliased slots are never accessed, since they need to go through
    1360             :         // the callobject. The typebarriers are added there and can be
    1361             :         // discarded here.
    1362           0 :         if (info().isSlotAliased(slot))
    1363           0 :             continue;
    1364             : 
    1365           0 :         if (!alloc().ensureBallast())
    1366           0 :             return abort(AbortReason::Alloc);
    1367             : 
    1368           0 :         MInstruction* def = osrBlock->getSlot(slot)->toInstruction();
    1369           0 :         MPhi* preheaderPhi = preheader->getSlot(slot)->toPhi();
    1370           0 :         MPhi* headerPhi = headerRp->getOperand(slot)->toPhi();
    1371             : 
    1372           0 :         MIRType type = headerPhi->type();
    1373           0 :         TemporaryTypeSet* typeSet = headerPhi->resultTypeSet();
    1374             : 
    1375           0 :         MOZ_TRY(addOsrValueTypeBarrier(slot, &def, type, typeSet));
    1376             : 
    1377           0 :         preheaderPhi->replaceOperand(OSR_PHI_POSITION, def);
    1378           0 :         preheaderPhi->setResultType(type);
    1379           0 :         preheaderPhi->setResultTypeSet(typeSet);
    1380             :     }
    1381             : 
    1382           0 :     return Ok();
    1383             : }
    1384             : 
    1385             : enum class CFGState : uint32_t {
    1386             :     Alloc = 0,
    1387             :     Abort = 1,
    1388             :     Success = 2
    1389             : };
    1390             : 
    1391             : static CFGState
    1392           0 : GetOrCreateControlFlowGraph(TempAllocator& tempAlloc, JSScript* script,
    1393             :                             const ControlFlowGraph** cfgOut)
    1394             : {
    1395           0 :     if (script->hasBaselineScript() && script->baselineScript()->controlFlowGraph()) {
    1396           0 :         *cfgOut = script->baselineScript()->controlFlowGraph();
    1397           0 :         return CFGState::Success;
    1398             :     }
    1399             : 
    1400           0 :     ControlFlowGenerator cfgenerator(tempAlloc, script);
    1401           0 :     if (!cfgenerator.traverseBytecode()) {
    1402           0 :         if (cfgenerator.aborted())
    1403             :             return CFGState::Abort;
    1404           0 :         return CFGState::Alloc;
    1405             :     }
    1406             : 
    1407             :     // If possible cache the control flow graph on the baseline script.
    1408           0 :     TempAllocator* graphAlloc = nullptr;
    1409           0 :     if (script->hasBaselineScript()) {
    1410           0 :         LifoAlloc& lifoAlloc = script->zone()->jitZone()->cfgSpace()->lifoAlloc();
    1411           0 :         LifoAlloc::AutoFallibleScope fallibleAllocator(&lifoAlloc);
    1412           0 :         graphAlloc = lifoAlloc.new_<TempAllocator>(&lifoAlloc);
    1413           0 :         if (!graphAlloc)
    1414           0 :             return CFGState::Alloc;
    1415             :     } else {
    1416             :         graphAlloc = &tempAlloc;
    1417             :     }
    1418             : 
    1419           0 :     ControlFlowGraph* cfg = cfgenerator.getGraph(*graphAlloc);
    1420           0 :     if (!cfg)
    1421             :         return CFGState::Alloc;
    1422             : 
    1423           0 :     if (script->hasBaselineScript()) {
    1424           0 :         MOZ_ASSERT(!script->baselineScript()->controlFlowGraph());
    1425           0 :         script->baselineScript()->setControlFlowGraph(cfg);
    1426             :     }
    1427             : 
    1428           0 :     if (JitSpewEnabled(JitSpew_CFG)) {
    1429           0 :         JitSpew(JitSpew_CFG, "Generating graph for %s:%u",
    1430           0 :                              script->filename(), script->lineno());
    1431           0 :         Fprinter& print = JitSpewPrinter();
    1432           0 :         cfg->dump(print, script);
    1433             :     }
    1434             : 
    1435           0 :     *cfgOut = cfg;
    1436           0 :     return CFGState::Success;
    1437             : }
    1438             : 
    1439             : // We traverse the bytecode using the control flow graph. This structure contains
    1440             : // a graph of CFGBlocks in RPO order.
    1441             : //
    1442             : // Per CFGBlock we take the corresponding MBasicBlock and start iterating the
    1443             : // bytecode of that CFGBlock. Each basic block has a mapping of local slots to
    1444             : // instructions, as well as a stack depth. As we encounter instructions we
    1445             : // mutate this mapping in the current block.
    1446             : //
    1447             : // Afterwards we visit the control flow instruction. There we add the ending ins
    1448             : // of the MBasicBlock and create new MBasicBlocks for the successors. That means
    1449             : // adding phi nodes for diamond join points, making sure to propagate types
    1450             : // around loops ...
    1451             : //
    1452             : // We keep a link between a CFGBlock and the entry MBasicBlock (in blockWorklist).
    1453             : // That vector only contains the MBasicBlocks that correspond with a CFGBlock.
    1454             : // We can create new MBasicBlocks that don't correspond to a CFGBlock.
    1455             : AbortReasonOr<Ok>
    1456           0 : IonBuilder::traverseBytecode()
    1457             : {
    1458           0 :     CFGState state = GetOrCreateControlFlowGraph(alloc(), info().script(), &cfg);
    1459           0 :     MOZ_ASSERT_IF(cfg && info().script()->hasBaselineScript(),
    1460             :                   info().script()->baselineScript()->controlFlowGraph() == cfg);
    1461           0 :     if (state == CFGState::Alloc)
    1462           0 :         return abort(AbortReason::Alloc);
    1463           0 :     if (state == CFGState::Abort)
    1464           0 :         return abort(AbortReason::Disable, "Couldn't create the CFG of script");
    1465             : 
    1466           0 :     if (!blockWorklist.growBy(cfg->numBlocks()))
    1467           0 :         return abort(AbortReason::Alloc);
    1468           0 :     blockWorklist[0] = current;
    1469             : 
    1470           0 :     size_t i = 0;
    1471           0 :     while (i < cfg->numBlocks()) {
    1472           0 :         if (!alloc().ensureBallast())
    1473           0 :             return abort(AbortReason::Alloc);
    1474             : 
    1475           0 :         bool restarted = false;
    1476           0 :         const CFGBlock* cfgblock = cfg->block(i);
    1477           0 :         MBasicBlock* mblock = blockWorklist[i];
    1478           0 :         MOZ_ASSERT(mblock && !mblock->isDead());
    1479             : 
    1480           0 :         MOZ_TRY(visitBlock(cfgblock, mblock));
    1481           0 :         MOZ_TRY(visitControlInstruction(cfgblock->stopIns(), &restarted));
    1482             : 
    1483           0 :         if (restarted) {
    1484             :             // Move back to the start of the loop.
    1485           0 :             while (!blockWorklist[i] || blockWorklist[i]->isDead()) {
    1486           0 :                 MOZ_ASSERT(i > 0);
    1487           0 :                 i--;
    1488             :             }
    1489           0 :             MOZ_ASSERT(cfgblock->stopIns()->isBackEdge());
    1490           0 :             MOZ_ASSERT(loopHeaderStack_.back() == blockWorklist[i]);
    1491             :         } else {
    1492           0 :             i++;
    1493             :         }
    1494             :     }
    1495             : 
    1496             : #ifdef DEBUG
    1497           0 :     MOZ_ASSERT(graph().numBlocks() >= blockWorklist.length());
    1498           0 :     for (i = 0; i < cfg->numBlocks(); i++) {
    1499           0 :         MOZ_ASSERT(blockWorklist[i]);
    1500           0 :         MOZ_ASSERT(!blockWorklist[i]->isDead());
    1501           0 :         MOZ_ASSERT_IF(i != 0, blockWorklist[i]->id() != 0);
    1502             :     }
    1503             : #endif
    1504             : 
    1505           0 :     cfg = nullptr;
    1506             : 
    1507           0 :     blockWorklist.clear();
    1508           0 :     return Ok();
    1509             : }
    1510             : 
    1511             : AbortReasonOr<Ok>
    1512           0 : IonBuilder::visitBlock(const CFGBlock* cfgblock, MBasicBlock* mblock)
    1513             : {
    1514           0 :     mblock->setLoopDepth(loopDepth_);
    1515             : 
    1516           0 :     cfgCurrent = cfgblock;
    1517           0 :     pc = cfgblock->startPc();
    1518             : 
    1519           0 :     if (mblock->pc() && script()->hasScriptCounts())
    1520           0 :         mblock->setHitCount(script()->getHitCount(mblock->pc()));
    1521             : 
    1522             :     // Optimization to move a predecessor that only has this block as successor
    1523             :     // just before this block.  Skip this optimization if the previous block is
    1524             :     // not part of the same function, as we might have to backtrack on inlining
    1525             :     // failures.
    1526           0 :     if (mblock->numPredecessors() == 1 && mblock->getPredecessor(0)->numSuccessors() == 1 &&
    1527           0 :         !mblock->getPredecessor(0)->outerResumePoint())
    1528             :     {
    1529           0 :         graph().removeBlockFromList(mblock->getPredecessor(0));
    1530           0 :         graph().addBlock(mblock->getPredecessor(0));
    1531             :     }
    1532             : 
    1533           0 :     MOZ_TRY(setCurrentAndSpecializePhis(mblock));
    1534           0 :     graph().addBlock(mblock);
    1535             : 
    1536           0 :     while (pc < cfgblock->stopPc()) {
    1537           0 :         if (!alloc().ensureBallast())
    1538           0 :             return abort(AbortReason::Alloc);
    1539             : 
    1540             : #ifdef DEBUG
    1541             :         // In debug builds, after compiling this op, check that all values
    1542             :         // popped by this opcode either:
    1543             :         //
    1544             :         //   (1) Have the ImplicitlyUsed flag set on them.
    1545             :         //   (2) Have more uses than before compiling this op (the value is
    1546             :         //       used as operand of a new MIR instruction).
    1547             :         //
    1548             :         // This is used to catch problems where IonBuilder pops a value without
    1549             :         // adding any SSA uses and doesn't call setImplicitlyUsedUnchecked on it.
    1550           0 :         Vector<MDefinition*, 4, JitAllocPolicy> popped(alloc());
    1551           0 :         Vector<size_t, 4, JitAllocPolicy> poppedUses(alloc());
    1552           0 :         unsigned nuses = GetUseCount(pc);
    1553             : 
    1554           0 :         for (unsigned i = 0; i < nuses; i++) {
    1555           0 :             MDefinition* def = current->peek(-int32_t(i + 1));
    1556           0 :             if (!popped.append(def) || !poppedUses.append(def->defUseCount()))
    1557           0 :                 return abort(AbortReason::Alloc);
    1558             :         }
    1559             : #endif
    1560             : 
    1561             :         // Nothing in inspectOpcode() is allowed to advance the pc.
    1562           0 :         JSOp op = JSOp(*pc);
    1563           0 :         MOZ_TRY(inspectOpcode(op));
    1564             : 
    1565             : #ifdef DEBUG
    1566           0 :         for (size_t i = 0; i < popped.length(); i++) {
    1567           0 :             switch (op) {
    1568             :               case JSOP_POP:
    1569             :               case JSOP_POPN:
    1570             :               case JSOP_DUPAT:
    1571             :               case JSOP_DUP:
    1572             :               case JSOP_DUP2:
    1573             :               case JSOP_PICK:
    1574             :               case JSOP_UNPICK:
    1575             :               case JSOP_SWAP:
    1576             :               case JSOP_SETARG:
    1577             :               case JSOP_SETLOCAL:
    1578             :               case JSOP_INITLEXICAL:
    1579             :               case JSOP_SETRVAL:
    1580             :               case JSOP_VOID:
    1581             :                 // Don't require SSA uses for values popped by these ops.
    1582             :                 break;
    1583             : 
    1584             :               case JSOP_POS:
    1585             :               case JSOP_TOID:
    1586             :               case JSOP_TOSTRING:
    1587             :                 // These ops may leave their input on the stack without setting
    1588             :                 // the ImplicitlyUsed flag. If this value will be popped immediately,
    1589             :                 // we may replace it with |undefined|, but the difference is
    1590             :                 // not observable.
    1591           0 :                 MOZ_ASSERT(i == 0);
    1592           0 :                 if (current->peek(-1) == popped[0])
    1593             :                     break;
    1594             :                 MOZ_FALLTHROUGH;
    1595             : 
    1596             :               default:
    1597           0 :                 MOZ_ASSERT(popped[i]->isImplicitlyUsed() ||
    1598             : 
    1599             :                            // MNewDerivedTypedObject instances are
    1600             :                            // often dead unless they escape from the
    1601             :                            // fn. See IonBuilder::loadTypedObjectData()
    1602             :                            // for more details.
    1603             :                            popped[i]->isNewDerivedTypedObject() ||
    1604             : 
    1605             :                            popped[i]->defUseCount() > poppedUses[i]);
    1606             :                 break;
    1607             :             }
    1608             :         }
    1609             : #endif
    1610             : 
    1611           0 :         pc += CodeSpec[op].length;
    1612           0 :         current->updateTrackedSite(bytecodeSite(pc));
    1613             :     }
    1614           0 :     return Ok();
    1615             : }
    1616             : 
    1617             : bool
    1618           0 : IonBuilder::blockIsOSREntry(const CFGBlock* block, const CFGBlock* predecessor)
    1619             : {
    1620           0 :     jsbytecode* entryPc = block->startPc();
    1621             : 
    1622           0 :     if (!info().osrPc())
    1623             :         return false;
    1624             : 
    1625           0 :     if (entryPc == predecessor->startPc()) {
    1626             :         // The predecessor is the actual osr entry block. Since it is empty
    1627             :         // the current block also starts a the osr pc. But it isn't the osr entry.
    1628           0 :         MOZ_ASSERT(predecessor->stopPc() == predecessor->startPc());
    1629             :         return false;
    1630             :     }
    1631             : 
    1632           0 :     if (block->stopPc() == block->startPc() && block->stopIns()->isBackEdge()) {
    1633             :         // An empty block with only a backedge can never be a loop entry.
    1634             :         return false;
    1635             :     }
    1636             : 
    1637           0 :     MOZ_ASSERT(*info().osrPc() == JSOP_LOOPENTRY);
    1638          17 :     return info().osrPc() == entryPc;
    1639             : }
    1640             : 
    1641             : AbortReasonOr<Ok>
    1642        1947 : IonBuilder::visitGoto(CFGGoto* ins)
    1643             : {
    1644             :     // Test if this potentially was a fake loop and create OSR entry if that is
    1645             :     // the case.
    1646        3894 :     const CFGBlock* successor = ins->getSuccessor(0);
    1647           0 :     if (blockIsOSREntry(successor, cfgCurrent)) {
    1648             :         MBasicBlock* preheader;
    1649           0 :         MOZ_TRY_VAR(preheader, newOsrPreheader(current, successor->startPc(), pc));
    1650           0 :         current->end(MGoto::New(alloc(), preheader));
    1651           0 :         MOZ_TRY(setCurrentAndSpecializePhis(preheader));
    1652             :     }
    1653             : 
    1654        1947 :     size_t id = successor->id();
    1655           0 :     bool create = !blockWorklist[id] || blockWorklist[id]->isDead();
    1656             : 
    1657        1947 :     current->popn(ins->popAmount());
    1658             : 
    1659        1947 :     if (create)
    1660           0 :         MOZ_TRY_VAR(blockWorklist[id], newBlock(current, successor->startPc()));
    1661             : 
    1662        1947 :     MBasicBlock* succ = blockWorklist[id];
    1663           0 :     current->end(MGoto::New(alloc(), succ));
    1664             : 
    1665        1947 :     if (!create) {
    1666           0 :         if (!succ->addPredecessor(alloc(), current))
    1667           0 :             return abort(AbortReason::Alloc);
    1668             :     }
    1669             : 
    1670        1947 :     return Ok();
    1671             : }
    1672             : 
    1673             : AbortReasonOr<Ok>
    1674         223 : IonBuilder::visitBackEdge(CFGBackEdge* ins, bool* restarted)
    1675             : {
    1676         223 :     loopDepth_--;
    1677             : 
    1678         446 :     MBasicBlock* loopEntry = blockWorklist[ins->getSuccessor(0)->id()];
    1679           0 :     current->end(MGoto::New(alloc(), loopEntry));
    1680             : 
    1681         446 :     MOZ_ASSERT(ins->getSuccessor(0) == cfgLoopHeaderStack_.back());
    1682             : 
    1683             :     // Compute phis in the loop header and propagate them throughout the loop,
    1684             :     // including the successor.
    1685         223 :     AbortReason r = loopEntry->setBackedge(alloc(), current);
    1686           0 :     switch (r) {
    1687             :       case AbortReason::NoAbort:
    1688         196 :         loopHeaderStack_.popBack();
    1689             : #ifdef DEBUG
    1690         196 :         cfgLoopHeaderStack_.popBack();
    1691             : #endif
    1692         196 :         return Ok();
    1693             : 
    1694             :       case AbortReason::Disable:
    1695             :         // If there are types for variables on the backedge that were not
    1696             :         // present at the original loop header, then uses of the variables'
    1697             :         // phis may have generated incorrect nodes. The new types have been
    1698             :         // incorporated into the header phis, so remove all blocks for the
    1699             :         // loop body and restart with the new types.
    1700          27 :         *restarted = true;
    1701           0 :         MOZ_TRY(restartLoop(ins->getSuccessor(0)));
    1702           0 :         return Ok();
    1703             : 
    1704             :       default:
    1705           0 :         return abort(r);
    1706             :     }
    1707             : }
    1708             : 
    1709             : AbortReasonOr<Ok>
    1710         196 : IonBuilder::visitLoopEntry(CFGLoopEntry* loopEntry)
    1711             : {
    1712         196 :     unsigned stackPhiCount = loopEntry->stackPhiCount();
    1713           0 :     const CFGBlock* successor = loopEntry->getSuccessor(0);
    1714           0 :     bool osr = blockIsOSREntry(successor, cfgCurrent);
    1715           0 :     if (osr) {
    1716           0 :         MOZ_ASSERT(loopEntry->canOsr());
    1717             :         MBasicBlock* preheader;
    1718           4 :         MOZ_TRY_VAR(preheader, newOsrPreheader(current, successor->startPc(), pc));
    1719           0 :         current->end(MGoto::New(alloc(), preheader));
    1720           0 :         MOZ_TRY(setCurrentAndSpecializePhis(preheader));
    1721             :     }
    1722             : 
    1723         196 :     loopDepth_++;
    1724             :     MBasicBlock* header;
    1725         784 :     MOZ_TRY_VAR(header, newPendingLoopHeader(current, successor->startPc(), osr,
    1726             :                                              loopEntry->canOsr(), stackPhiCount));
    1727         196 :     blockWorklist[successor->id()] = header;
    1728             : 
    1729         196 :     current->end(MGoto::New(alloc(), header));
    1730             : 
    1731         196 :     if (!loopHeaderStack_.append(header))
    1732           0 :         return abort(AbortReason::Alloc);
    1733             : #ifdef DEBUG
    1734         196 :     if (!cfgLoopHeaderStack_.append(successor))
    1735           0 :         return abort(AbortReason::Alloc);
    1736             : #endif
    1737             : 
    1738         588 :     MOZ_TRY(analyzeNewLoopTypes(cfgCurrent));
    1739             : 
    1740         392 :     setCurrent(header);
    1741           0 :     pc = header->pc();
    1742             : 
    1743         196 :     return Ok();
    1744             : }
    1745             : 
    1746             : AbortReasonOr<Ok>
    1747         223 : IonBuilder::jsop_loopentry()
    1748             : {
    1749           0 :     MOZ_ASSERT(*pc == JSOP_LOOPENTRY);
    1750             : 
    1751           0 :     current->add(MInterruptCheck::New(alloc()));
    1752           0 :     insertRecompileCheck();
    1753             : 
    1754           0 :     return Ok();
    1755             : }
    1756             : 
    1757             : AbortReasonOr<Ok>
    1758           0 : IonBuilder::visitControlInstruction(CFGControlInstruction* ins, bool* restarted)
    1759             : {
    1760           0 :     switch (ins->type()) {
    1761             :       case CFGControlInstruction::Type_Test:
    1762           0 :         return visitTest(ins->toTest());
    1763             :       case CFGControlInstruction::Type_Compare:
    1764           0 :         return visitCompare(ins->toCompare());
    1765             :       case CFGControlInstruction::Type_Goto:
    1766           0 :         return visitGoto(ins->toGoto());
    1767             :       case CFGControlInstruction::Type_BackEdge:
    1768           0 :         return visitBackEdge(ins->toBackEdge(), restarted);
    1769             :       case CFGControlInstruction::Type_LoopEntry:
    1770           0 :         return visitLoopEntry(ins->toLoopEntry());
    1771             :       case CFGControlInstruction::Type_Return:
    1772             :       case CFGControlInstruction::Type_RetRVal:
    1773           0 :         return visitReturn(ins);
    1774             :       case CFGControlInstruction::Type_Try:
    1775           0 :         return visitTry(ins->toTry());
    1776             :       case CFGControlInstruction::Type_Throw:
    1777           0 :         return visitThrow(ins->toThrow());
    1778             :       case CFGControlInstruction::Type_TableSwitch:
    1779           0 :         return visitTableSwitch(ins->toTableSwitch());
    1780             :     }
    1781           0 :     MOZ_CRASH("Unknown Control Instruction");
    1782             : }
    1783             : 
    1784             : AbortReasonOr<Ok>
    1785           0 : IonBuilder::inspectOpcode(JSOp op)
    1786             : {
    1787             :     // Add not yet implemented opcodes at the bottom of the switch!
    1788           0 :     switch (op) {
    1789             :       case JSOP_NOP:
    1790             :       case JSOP_NOP_DESTRUCTURING:
    1791             :       case JSOP_TRY_DESTRUCTURING_ITERCLOSE:
    1792             :       case JSOP_LINENO:
    1793             :       case JSOP_JUMPTARGET:
    1794             :       case JSOP_LABEL:
    1795           0 :         return Ok();
    1796             : 
    1797             :       case JSOP_UNDEFINED:
    1798             :         // If this ever changes, change what JSOP_GIMPLICITTHIS does too.
    1799           0 :         pushConstant(UndefinedValue());
    1800           0 :         return Ok();
    1801             : 
    1802             :       case JSOP_IFNE:
    1803             :       case JSOP_IFEQ:
    1804             :       case JSOP_RETURN:
    1805             :       case JSOP_RETRVAL:
    1806             :       case JSOP_AND:
    1807             :       case JSOP_OR:
    1808             :       case JSOP_TRY:
    1809             :       case JSOP_THROW:
    1810             :       case JSOP_GOTO:
    1811             :       case JSOP_CONDSWITCH:
    1812             :       case JSOP_TABLESWITCH:
    1813             :       case JSOP_CASE:
    1814             :       case JSOP_DEFAULT:
    1815             :         // Control flow opcodes should be handled in the ControlFlowGenerator.
    1816           0 :         MOZ_CRASH("Shouldn't encounter this opcode.");
    1817             : 
    1818             :       case JSOP_BITNOT:
    1819           0 :         return jsop_bitnot();
    1820             : 
    1821             :       case JSOP_BITAND:
    1822             :       case JSOP_BITOR:
    1823             :       case JSOP_BITXOR:
    1824             :       case JSOP_LSH:
    1825             :       case JSOP_RSH:
    1826             :       case JSOP_URSH:
    1827          60 :         return jsop_bitop(op);
    1828             : 
    1829             :       case JSOP_ADD:
    1830             :       case JSOP_SUB:
    1831             :       case JSOP_MUL:
    1832             :       case JSOP_DIV:
    1833             :       case JSOP_MOD:
    1834         332 :         return jsop_binary_arith(op);
    1835             : 
    1836             :       case JSOP_POW:
    1837           0 :         return jsop_pow();
    1838             : 
    1839             :       case JSOP_POS:
    1840          85 :         return jsop_pos();
    1841             : 
    1842             :       case JSOP_NEG:
    1843           0 :         return jsop_neg();
    1844             : 
    1845             :       case JSOP_TOSTRING:
    1846          32 :         return jsop_tostring();
    1847             : 
    1848             :       case JSOP_DEFVAR:
    1849           0 :         return jsop_defvar(GET_UINT32_INDEX(pc));
    1850             : 
    1851             :       case JSOP_DEFLET:
    1852             :       case JSOP_DEFCONST:
    1853           0 :         return jsop_deflexical(GET_UINT32_INDEX(pc));
    1854             : 
    1855             :       case JSOP_DEFFUN:
    1856           0 :         return jsop_deffun();
    1857             : 
    1858             :       case JSOP_EQ:
    1859             :       case JSOP_NE:
    1860             :       case JSOP_STRICTEQ:
    1861             :       case JSOP_STRICTNE:
    1862             :       case JSOP_LT:
    1863             :       case JSOP_LE:
    1864             :       case JSOP_GT:
    1865             :       case JSOP_GE:
    1866         989 :         return jsop_compare(op);
    1867             : 
    1868             :       case JSOP_DOUBLE:
    1869         154 :         pushConstant(info().getConst(pc));
    1870           0 :         return Ok();
    1871             : 
    1872             :       case JSOP_STRING:
    1873        1710 :         pushConstant(StringValue(info().getAtom(pc)));
    1874           0 :         return Ok();
    1875             : 
    1876             :       case JSOP_SYMBOL: {
    1877          78 :         unsigned which = GET_UINT8(pc);
    1878           0 :         JS::Symbol* sym = realm->runtime()->wellKnownSymbols().get(which);
    1879           0 :         pushConstant(SymbolValue(sym));
    1880           0 :         return Ok();
    1881             :       }
    1882             : 
    1883             :       case JSOP_ZERO:
    1884         823 :         pushConstant(Int32Value(0));
    1885           0 :         return Ok();
    1886             : 
    1887             :       case JSOP_ONE:
    1888         575 :         pushConstant(Int32Value(1));
    1889           0 :         return Ok();
    1890             : 
    1891             :       case JSOP_NULL:
    1892         408 :         pushConstant(NullValue());
    1893           0 :         return Ok();
    1894             : 
    1895             :       case JSOP_VOID:
    1896          39 :         current->pop();
    1897           0 :         pushConstant(UndefinedValue());
    1898           0 :         return Ok();
    1899             : 
    1900             :       case JSOP_HOLE:
    1901           0 :         pushConstant(MagicValue(JS_ELEMENTS_HOLE));
    1902           0 :         return Ok();
    1903             : 
    1904             :       case JSOP_FALSE:
    1905         282 :         pushConstant(BooleanValue(false));
    1906           0 :         return Ok();
    1907             : 
    1908             :       case JSOP_TRUE:
    1909         195 :         pushConstant(BooleanValue(true));
    1910           0 :         return Ok();
    1911             : 
    1912             :       case JSOP_ARGUMENTS:
    1913          55 :         return jsop_arguments();
    1914             : 
    1915             :       case JSOP_RUNONCE:
    1916           0 :         return jsop_runonce();
    1917             : 
    1918             :       case JSOP_REST:
    1919           1 :         return jsop_rest();
    1920             : 
    1921             :       case JSOP_GETARG:
    1922        2186 :         if (info().argsObjAliasesFormals()) {
    1923           0 :             MGetArgumentsObjectArg* getArg = MGetArgumentsObjectArg::New(alloc(),
    1924           0 :                                                                          current->argumentsObject(),
    1925           0 :                                                                          GET_ARGNO(pc));
    1926           0 :             current->add(getArg);
    1927           0 :             current->push(getArg);
    1928             :         } else {
    1929        2184 :             current->pushArg(GET_ARGNO(pc));
    1930             :         }
    1931        1093 :         return Ok();
    1932             : 
    1933             :       case JSOP_SETARG:
    1934         304 :         return jsop_setarg(GET_ARGNO(pc));
    1935             : 
    1936             :       case JSOP_GETLOCAL:
    1937       15531 :         current->pushLocal(GET_LOCALNO(pc));
    1938           0 :         return Ok();
    1939             : 
    1940             :       case JSOP_SETLOCAL:
    1941        4863 :         current->setLocal(GET_LOCALNO(pc));
    1942           0 :         return Ok();
    1943             : 
    1944             :       case JSOP_THROWSETCONST:
    1945             :       case JSOP_THROWSETALIASEDCONST:
    1946             :       case JSOP_THROWSETCALLEE:
    1947           0 :         return jsop_throwsetconst();
    1948             : 
    1949             :       case JSOP_CHECKLEXICAL:
    1950           0 :         return jsop_checklexical();
    1951             : 
    1952             :       case JSOP_INITLEXICAL:
    1953         834 :         current->setLocal(GET_LOCALNO(pc));
    1954           0 :         return Ok();
    1955             : 
    1956             :       case JSOP_INITGLEXICAL: {
    1957           0 :         MOZ_ASSERT(!script()->hasNonSyntacticScope());
    1958           0 :         MDefinition* value = current->pop();
    1959           0 :         current->push(constant(ObjectValue(script()->global().lexicalEnvironment())));
    1960           0 :         current->push(value);
    1961           0 :         return jsop_setprop(info().getAtom(pc)->asPropertyName());
    1962             :       }
    1963             : 
    1964             :       case JSOP_CHECKALIASEDLEXICAL:
    1965           3 :         return jsop_checkaliasedlexical(EnvironmentCoordinate(pc));
    1966             : 
    1967             :       case JSOP_INITALIASEDLEXICAL:
    1968           1 :         return jsop_setaliasedvar(EnvironmentCoordinate(pc));
    1969             : 
    1970             :       case JSOP_UNINITIALIZED:
    1971         102 :         pushConstant(MagicValue(JS_UNINITIALIZED_LEXICAL));
    1972           0 :         return Ok();
    1973             : 
    1974             :       case JSOP_POP: {
    1975        3620 :         MDefinition* def = current->pop();
    1976             : 
    1977             :         // POP opcodes frequently appear where values are killed, e.g. after
    1978             :         // SET* opcodes. Place a resume point afterwards to avoid capturing
    1979             :         // the dead value in later snapshots, except in places where that
    1980             :         // resume point is obviously unnecessary.
    1981        3620 :         if (pc[JSOP_POP_LENGTH] == JSOP_POP)
    1982           0 :             return Ok();
    1983           0 :         if (def->isConstant())
    1984           0 :             return Ok();
    1985           0 :         return maybeInsertResume();
    1986             :       }
    1987             : 
    1988             :       case JSOP_POPN:
    1989         150 :         for (uint32_t i = 0, n = GET_UINT16(pc); i < n; i++)
    1990           0 :             current->pop();
    1991           0 :         return Ok();
    1992             : 
    1993             :       case JSOP_DUPAT:
    1994         351 :         current->pushSlot(current->stackDepth() - 1 - GET_UINT24(pc));
    1995           0 :         return Ok();
    1996             : 
    1997             :       case JSOP_NEWINIT:
    1998           2 :         if (GET_UINT8(pc) == JSProto_Array)
    1999           0 :             return jsop_newarray(0);
    2000           1 :         return jsop_newobject();
    2001             : 
    2002             :       case JSOP_NEWARRAY:
    2003         216 :         return jsop_newarray(GET_UINT32(pc));
    2004             : 
    2005             :       case JSOP_NEWARRAY_COPYONWRITE:
    2006           0 :         return jsop_newarray_copyonwrite();
    2007             : 
    2008             :       case JSOP_NEWOBJECT:
    2009         140 :         return jsop_newobject();
    2010             : 
    2011             :       case JSOP_INITELEM:
    2012             :       case JSOP_INITHIDDENELEM:
    2013          15 :         return jsop_initelem();
    2014             : 
    2015             :       case JSOP_INITELEM_INC:
    2016          12 :         return jsop_initelem_inc();
    2017             : 
    2018             :       case JSOP_INITELEM_ARRAY:
    2019         166 :         return jsop_initelem_array();
    2020             : 
    2021             :       case JSOP_INITPROP:
    2022             :       case JSOP_INITLOCKEDPROP:
    2023             :       case JSOP_INITHIDDENPROP:
    2024             :       {
    2025         522 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2026           0 :         return jsop_initprop(name);
    2027             :       }
    2028             : 
    2029             :       case JSOP_MUTATEPROTO:
    2030             :       {
    2031           0 :         return jsop_mutateproto();
    2032             :       }
    2033             : 
    2034             :       case JSOP_INITPROP_GETTER:
    2035             :       case JSOP_INITHIDDENPROP_GETTER:
    2036             :       case JSOP_INITPROP_SETTER:
    2037             :       case JSOP_INITHIDDENPROP_SETTER: {
    2038           0 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2039           0 :         return jsop_initprop_getter_setter(name);
    2040             :       }
    2041             : 
    2042             :       case JSOP_INITELEM_GETTER:
    2043             :       case JSOP_INITHIDDENELEM_GETTER:
    2044             :       case JSOP_INITELEM_SETTER:
    2045             :       case JSOP_INITHIDDENELEM_SETTER:
    2046           0 :         return jsop_initelem_getter_setter();
    2047             : 
    2048             :       case JSOP_FUNCALL:
    2049           4 :         return jsop_funcall(GET_ARGC(pc));
    2050             : 
    2051             :       case JSOP_FUNAPPLY:
    2052          30 :         return jsop_funapply(GET_ARGC(pc));
    2053             : 
    2054             :       case JSOP_SPREADCALL:
    2055           1 :         return jsop_spreadcall();
    2056             : 
    2057             :       case JSOP_CALL:
    2058             :       case JSOP_CALL_IGNORES_RV:
    2059             :       case JSOP_CALLITER:
    2060             :       case JSOP_NEW:
    2061       10060 :         MOZ_TRY(jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL,
    2062             :                           (JSOp)*pc == JSOP_CALL_IGNORES_RV));
    2063        2515 :         if (op == JSOP_CALLITER) {
    2064           0 :             if (!outermostBuilder()->iterators_.append(current->peek(-1)))
    2065           0 :                 return abort(AbortReason::Alloc);
    2066             :         }
    2067        2515 :         return Ok();
    2068             : 
    2069             :       case JSOP_EVAL:
    2070             :       case JSOP_STRICTEVAL:
    2071           0 :         return jsop_eval(GET_ARGC(pc));
    2072             : 
    2073             :       case JSOP_INT8:
    2074        1263 :         pushConstant(Int32Value(GET_INT8(pc)));
    2075           0 :         return Ok();
    2076             : 
    2077             :       case JSOP_UINT16:
    2078         252 :         pushConstant(Int32Value(GET_UINT16(pc)));
    2079           0 :         return Ok();
    2080             : 
    2081             :       case JSOP_GETGNAME:
    2082             :       {
    2083         116 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2084           0 :         if (!script()->hasNonSyntacticScope())
    2085           0 :             return jsop_getgname(name);
    2086           0 :         return jsop_getname(name);
    2087             :       }
    2088             : 
    2089             :       case JSOP_SETGNAME:
    2090             :       case JSOP_STRICTSETGNAME:
    2091             :       {
    2092           0 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2093           0 :         JSObject* obj = nullptr;
    2094           0 :         if (!script()->hasNonSyntacticScope())
    2095           0 :             obj = testGlobalLexicalBinding(name);
    2096           0 :         if (obj)
    2097           0 :             return setStaticName(obj, name);
    2098           0 :         return jsop_setprop(name);
    2099             :       }
    2100             : 
    2101             :       case JSOP_GETNAME:
    2102             :       {
    2103         240 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2104           0 :         return jsop_getname(name);
    2105             :       }
    2106             : 
    2107             :       case JSOP_GETINTRINSIC:
    2108             :       {
    2109        4084 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2110           0 :         return jsop_intrinsic(name);
    2111             :       }
    2112             : 
    2113             :       case JSOP_GETIMPORT:
    2114             :       {
    2115           0 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2116           0 :         return jsop_getimport(name);
    2117             :       }
    2118             : 
    2119             :       case JSOP_BINDGNAME:
    2120           0 :         if (!script()->hasNonSyntacticScope()) {
    2121           0 :             if (JSObject* env = testGlobalLexicalBinding(info().getName(pc))) {
    2122           0 :                 pushConstant(ObjectValue(*env));
    2123           0 :                 return Ok();
    2124             :             }
    2125             :         }
    2126             :         // Fall through to JSOP_BINDNAME
    2127             :         MOZ_FALLTHROUGH;
    2128             :       case JSOP_BINDNAME:
    2129           0 :         return jsop_bindname(info().getName(pc));
    2130             : 
    2131             :       case JSOP_BINDVAR:
    2132           0 :         return jsop_bindvar();
    2133             : 
    2134             :       case JSOP_DUP:
    2135        1396 :         current->pushSlot(current->stackDepth() - 1);
    2136           0 :         return Ok();
    2137             : 
    2138             :       case JSOP_DUP2:
    2139          36 :         return jsop_dup2();
    2140             : 
    2141             :       case JSOP_SWAP:
    2142         492 :         current->swapAt(-1);
    2143           0 :         return Ok();
    2144             : 
    2145             :       case JSOP_PICK:
    2146         108 :         current->pick(-GET_INT8(pc));
    2147           0 :         return Ok();
    2148             : 
    2149             :       case JSOP_UNPICK:
    2150          66 :         current->unpick(-GET_INT8(pc));
    2151           0 :         return Ok();
    2152             : 
    2153             :       case JSOP_GETALIASEDVAR:
    2154         200 :         return jsop_getaliasedvar(EnvironmentCoordinate(pc));
    2155             : 
    2156             :       case JSOP_SETALIASEDVAR:
    2157           8 :         return jsop_setaliasedvar(EnvironmentCoordinate(pc));
    2158             : 
    2159             :       case JSOP_UINT24:
    2160           0 :         pushConstant(Int32Value(GET_UINT24(pc)));
    2161           0 :         return Ok();
    2162             : 
    2163             :       case JSOP_INT32:
    2164           0 :         pushConstant(Int32Value(GET_INT32(pc)));
    2165           0 :         return Ok();
    2166             : 
    2167             :       case JSOP_LOOPHEAD:
    2168             :         // JSOP_LOOPHEAD is handled when processing the loop header.
    2169           0 :         MOZ_CRASH("JSOP_LOOPHEAD outside loop");
    2170             : 
    2171             :       case JSOP_GETELEM:
    2172             :       case JSOP_CALLELEM:
    2173        1692 :         MOZ_TRY(jsop_getelem());
    2174           0 :         if (op == JSOP_CALLELEM)
    2175           0 :             MOZ_TRY(improveThisTypesForCall());
    2176           0 :         return Ok();
    2177             : 
    2178             :       case JSOP_SETELEM:
    2179             :       case JSOP_STRICTSETELEM:
    2180          71 :         return jsop_setelem();
    2181             : 
    2182             :       case JSOP_LENGTH:
    2183         244 :         return jsop_length();
    2184             : 
    2185             :       case JSOP_NOT:
    2186         386 :         return jsop_not();
    2187             : 
    2188             :       case JSOP_FUNCTIONTHIS:
    2189         206 :         return jsop_functionthis();
    2190             : 
    2191             :       case JSOP_GLOBALTHIS:
    2192           0 :         return jsop_globalthis();
    2193             : 
    2194             :       case JSOP_CALLEE: {
    2195           6 :          MDefinition* callee = getCallee();
    2196           0 :          current->push(callee);
    2197           0 :          return Ok();
    2198             :       }
    2199             : 
    2200             :       case JSOP_SUPERBASE:
    2201           2 :         return jsop_superbase();
    2202             : 
    2203             :       case JSOP_GETPROP_SUPER:
    2204             :       {
    2205           4 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2206           0 :         return jsop_getprop_super(name);
    2207             :       }
    2208             : 
    2209             :       case JSOP_GETELEM_SUPER:
    2210           0 :         return jsop_getelem_super();
    2211             : 
    2212             :       case JSOP_GETPROP:
    2213             :       case JSOP_CALLPROP:
    2214             :       {
    2215        1854 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2216           0 :         MOZ_TRY(jsop_getprop(name));
    2217           0 :         if (op == JSOP_CALLPROP)
    2218           0 :             MOZ_TRY(improveThisTypesForCall());
    2219           0 :         return Ok();
    2220             :       }
    2221             : 
    2222             :       case JSOP_SETPROP:
    2223             :       case JSOP_STRICTSETPROP:
    2224             :       case JSOP_SETNAME:
    2225             :       case JSOP_STRICTSETNAME:
    2226             :       {
    2227         950 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2228           0 :         return jsop_setprop(name);
    2229             :       }
    2230             : 
    2231             :       case JSOP_DELPROP:
    2232             :       case JSOP_STRICTDELPROP:
    2233             :       {
    2234           0 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2235           0 :         return jsop_delprop(name);
    2236             :       }
    2237             : 
    2238             :       case JSOP_DELELEM:
    2239             :       case JSOP_STRICTDELELEM:
    2240           0 :         return jsop_delelem();
    2241             : 
    2242             :       case JSOP_REGEXP:
    2243           2 :         return jsop_regexp(info().getRegExp(pc));
    2244             : 
    2245             :       case JSOP_CALLSITEOBJ:
    2246           0 :         pushConstant(ObjectValue(*info().getObject(pc)));
    2247           0 :         return Ok();
    2248             : 
    2249             :       case JSOP_OBJECT:
    2250           0 :         return jsop_object(info().getObject(pc));
    2251             : 
    2252             :       case JSOP_CLASSCONSTRUCTOR:
    2253           0 :         return jsop_classconstructor();
    2254             : 
    2255             :       case JSOP_TYPEOF:
    2256             :       case JSOP_TYPEOFEXPR:
    2257          45 :         return jsop_typeof();
    2258             : 
    2259             :       case JSOP_TOASYNC:
    2260           0 :         return jsop_toasync();
    2261             : 
    2262             :       case JSOP_TOASYNCGEN:
    2263           0 :         return jsop_toasyncgen();
    2264             : 
    2265             :       case JSOP_TOASYNCITER:
    2266           0 :         return jsop_toasynciter();
    2267             : 
    2268             :       case JSOP_TOID:
    2269           1 :         return jsop_toid();
    2270             : 
    2271             :       case JSOP_ITERNEXT:
    2272           0 :         return jsop_iternext();
    2273             : 
    2274             :       case JSOP_LAMBDA:
    2275          34 :         return jsop_lambda(info().getFunction(pc));
    2276             : 
    2277             :       case JSOP_LAMBDA_ARROW:
    2278           2 :         return jsop_lambda_arrow(info().getFunction(pc));
    2279             : 
    2280             :       case JSOP_SETFUNNAME:
    2281           0 :         return jsop_setfunname(GET_UINT8(pc));
    2282             : 
    2283             :       case JSOP_PUSHLEXICALENV:
    2284           2 :         return jsop_pushlexicalenv(GET_UINT32_INDEX(pc));
    2285             : 
    2286             :       case JSOP_POPLEXICALENV:
    2287           2 :         current->setEnvironmentChain(walkEnvironmentChain(1));
    2288           0 :         return Ok();
    2289             : 
    2290             :       case JSOP_FRESHENLEXICALENV:
    2291           0 :         return jsop_copylexicalenv(true);
    2292             : 
    2293             :       case JSOP_RECREATELEXICALENV:
    2294           0 :         return jsop_copylexicalenv(false);
    2295             : 
    2296             :       case JSOP_ITER:
    2297           0 :         return jsop_iter();
    2298             : 
    2299             :       case JSOP_MOREITER:
    2300           0 :         return jsop_itermore();
    2301             : 
    2302             :       case JSOP_ISNOITER:
    2303           0 :         return jsop_isnoiter();
    2304             : 
    2305             :       case JSOP_ENDITER:
    2306           0 :         return jsop_iterend();
    2307             : 
    2308             :       case JSOP_IN:
    2309          47 :         return jsop_in();
    2310             : 
    2311             :       case JSOP_HASOWN:
    2312           1 :         return jsop_hasown();
    2313             : 
    2314             :       case JSOP_SETRVAL:
    2315          88 :         MOZ_ASSERT(!script()->noScriptRval());
    2316           0 :         current->setSlot(info().returnValueSlot(), current->pop());
    2317           0 :         return Ok();
    2318             : 
    2319             :       case JSOP_INSTANCEOF:
    2320          11 :         return jsop_instanceof();
    2321             : 
    2322             :       case JSOP_DEBUGLEAVELEXICALENV:
    2323         104 :         return Ok();
    2324             : 
    2325             :       case JSOP_DEBUGGER:
    2326           0 :         return jsop_debugger();
    2327             : 
    2328             :       case JSOP_GIMPLICITTHIS:
    2329          40 :         if (!script()->hasNonSyntacticScope()) {
    2330           0 :             pushConstant(UndefinedValue());
    2331           0 :             return Ok();
    2332             :         }
    2333             :         // Fallthrough to IMPLICITTHIS in non-syntactic scope case
    2334             :         MOZ_FALLTHROUGH;
    2335             :       case JSOP_IMPLICITTHIS:
    2336             :       {
    2337          40 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2338           0 :         return jsop_implicitthis(name);
    2339             :       }
    2340             : 
    2341             :       case JSOP_NEWTARGET:
    2342           7 :         return jsop_newtarget();
    2343             : 
    2344             :       case JSOP_CHECKISOBJ:
    2345         282 :         return jsop_checkisobj(GET_UINT8(pc));
    2346             : 
    2347             :       case JSOP_CHECKISCALLABLE:
    2348           0 :         return jsop_checkiscallable(GET_UINT8(pc));
    2349             : 
    2350             :       case JSOP_CHECKOBJCOERCIBLE:
    2351           3 :         return jsop_checkobjcoercible();
    2352             : 
    2353             :       case JSOP_DEBUGCHECKSELFHOSTED:
    2354             :       {
    2355             : #ifdef DEBUG
    2356         204 :         MDebugCheckSelfHosted* check = MDebugCheckSelfHosted::New(alloc(), current->pop());
    2357           0 :         current->add(check);
    2358           0 :         current->push(check);
    2359           0 :         MOZ_TRY(resumeAfter(check));
    2360             : #endif
    2361         102 :         return Ok();
    2362             :       }
    2363             : 
    2364             :       case JSOP_IS_CONSTRUCTING:
    2365          87 :         pushConstant(MagicValue(JS_IS_CONSTRUCTING));
    2366           0 :         return Ok();
    2367             : 
    2368             :       case JSOP_OPTIMIZE_SPREADCALL:
    2369             :       {
    2370             :         // Assuming optimization isn't available doesn't affect correctness.
    2371             :         // TODO: Investigate dynamic checks.
    2372           0 :         MDefinition* arr = current->peek(-1);
    2373           0 :         arr->setImplicitlyUsedUnchecked();
    2374           0 :         pushConstant(BooleanValue(false));
    2375           0 :         return Ok();
    2376             :       }
    2377             : 
    2378             :       case JSOP_IMPORTMETA:
    2379           0 :         return jsop_importmeta();
    2380             : 
    2381             :       case JSOP_LOOPENTRY:
    2382         223 :         return jsop_loopentry();
    2383             : 
    2384             :       // ===== NOT Yet Implemented =====
    2385             :       // Read below!
    2386             : 
    2387             :       // With
    2388             :       case JSOP_ENTERWITH:
    2389             :       case JSOP_LEAVEWITH:
    2390             : 
    2391             :       // Spread
    2392             :       case JSOP_SPREADNEW:
    2393             :       case JSOP_SPREADEVAL:
    2394             :       case JSOP_STRICTSPREADEVAL:
    2395             : 
    2396             :       // Classes
    2397             :       case JSOP_CHECKCLASSHERITAGE:
    2398             :       case JSOP_FUNWITHPROTO:
    2399             :       case JSOP_OBJWITHPROTO:
    2400             :       case JSOP_BUILTINPROTO:
    2401             :       case JSOP_INITHOMEOBJECT:
    2402             :       case JSOP_DERIVEDCONSTRUCTOR:
    2403             :       case JSOP_CHECKTHIS:
    2404             :       case JSOP_CHECKRETURN:
    2405             :       case JSOP_CHECKTHISREINIT:
    2406             : 
    2407             :       // Super
    2408             :       case JSOP_SETPROP_SUPER:
    2409             :       case JSOP_SETELEM_SUPER:
    2410             :       case JSOP_STRICTSETPROP_SUPER:
    2411             :       case JSOP_STRICTSETELEM_SUPER:
    2412             :       case JSOP_SUPERFUN:
    2413             :       // Most of the infrastructure for these exists in Ion, but needs review
    2414             :       // and testing before these are enabled. Once other opcodes that are used
    2415             :       // in derived classes are supported in Ion, this can be better validated
    2416             :       // with testcases. Pay special attention to bailout and other areas where
    2417             :       // JSOP_NEW has special handling.
    2418             :       case JSOP_SPREADSUPERCALL:
    2419             :       case JSOP_SUPERCALL:
    2420             : 
    2421             :       // Environments (bug 1366470)
    2422             :       case JSOP_PUSHVARENV:
    2423             :       case JSOP_POPVARENV:
    2424             : 
    2425             :       // Compound assignment
    2426             :       case JSOP_GETBOUNDNAME:
    2427             : 
    2428             :       // Generators / Async (bug 1317690)
    2429             :       case JSOP_EXCEPTION:
    2430             :       case JSOP_THROWING:
    2431             :       case JSOP_ISGENCLOSING:
    2432             :       case JSOP_INITIALYIELD:
    2433             :       case JSOP_YIELD:
    2434             :       case JSOP_FINALYIELDRVAL:
    2435             :       case JSOP_RESUME:
    2436             :       case JSOP_DEBUGAFTERYIELD:
    2437             :       case JSOP_AWAIT:
    2438             :       case JSOP_GENERATOR:
    2439             : 
    2440             :       // Misc
    2441             :       case JSOP_DELNAME:
    2442             :       case JSOP_FINALLY:
    2443             :       case JSOP_GETRVAL:
    2444             :       case JSOP_GOSUB:
    2445             :       case JSOP_RETSUB:
    2446             :       case JSOP_SETINTRINSIC:
    2447             :       case JSOP_THROWMSG:
    2448             :         // === !! WARNING WARNING WARNING !! ===
    2449             :         // Do you really want to sacrifice performance by not implementing this
    2450             :         // operation in the optimizing compiler?
    2451             :         break;
    2452             : 
    2453             :       case JSOP_FORCEINTERPRETER:
    2454             :         // Intentionally not implemented.
    2455             :         break;
    2456             : 
    2457             :       case JSOP_UNUSED126:
    2458             :       case JSOP_UNUSED206:
    2459             :       case JSOP_UNUSED223:
    2460             :       case JSOP_LIMIT:
    2461             :         break;
    2462             :     }
    2463             : 
    2464             :     // Track a simpler message, since the actionable abort message is a
    2465             :     // static string, and the internal opcode name isn't an actionable
    2466             :     // thing anyways.
    2467           0 :     trackActionableAbort("Unsupported bytecode");
    2468             : #ifdef DEBUG
    2469          18 :     return abort(AbortReason::Disable, "Unsupported opcode: %s", CodeName[op]);
    2470             : #else
    2471             :     return abort(AbortReason::Disable, "Unsupported opcode: %d", op);
    2472             : #endif
    2473             : }
    2474             : 
    2475             : AbortReasonOr<Ok>
    2476           0 : IonBuilder::restartLoop(const CFGBlock* cfgHeader)
    2477             : {
    2478           0 :     AutoTraceLog logCompile(traceLogger(), TraceLogger_IonBuilderRestartLoop);
    2479             : 
    2480           0 :     spew("New types at loop header, restarting loop body");
    2481             : 
    2482           1 :     if (JitOptions.limitScriptSize) {
    2483          27 :         if (++numLoopRestarts_ >= MAX_LOOP_RESTARTS)
    2484           0 :             return abort(AbortReason::Disable, "Aborted while processing control flow");
    2485             :     }
    2486             : 
    2487          27 :     MBasicBlock* header = blockWorklist[cfgHeader->id()];
    2488             : 
    2489             :     // Discard unreferenced & pre-allocated resume points.
    2490          27 :     replaceMaybeFallbackFunctionGetter(nullptr);
    2491             : 
    2492             :     // Remove all blocks in the loop body other than the header, which has phis
    2493             :     // of the appropriate type and incoming edges to preserve.
    2494           0 :     if (!graph().removeSuccessorBlocks(header))
    2495           0 :         return abort(AbortReason::Alloc);
    2496          54 :     graph().removeBlockFromList(header);
    2497             : 
    2498             :     // Remove all instructions from the header itself, and all resume points
    2499             :     // except the entry resume point.
    2500           0 :     header->discardAllInstructions();
    2501          27 :     header->discardAllResumePoints(/* discardEntry = */ false);
    2502           0 :     header->setStackDepth(header->getPredecessor(0)->stackDepth());
    2503             : 
    2504          27 :     loopDepth_ = header->loopDepth();
    2505             : 
    2506             :     // Don't specializePhis(), as the header has been visited before and the
    2507             :     // phis have already had their type set.
    2508          54 :     setCurrent(header);
    2509           0 :     pc = header->pc();
    2510             : 
    2511          27 :     return Ok();
    2512             : }
    2513             : 
    2514             : AbortReasonOr<Ok>
    2515        2786 : IonBuilder::replaceTypeSet(MDefinition* subject, TemporaryTypeSet* type, MTest* test)
    2516             : {
    2517           1 :     if (type->unknown())
    2518           0 :         return Ok();
    2519             : 
    2520             :     // Don't emit MFilterTypeSet if it doesn't improve the typeset.
    2521           0 :     if (subject->resultTypeSet()) {
    2522           0 :         if (subject->resultTypeSet()->equals(type))
    2523        1101 :             return Ok();
    2524             :     } else {
    2525           0 :         TemporaryTypeSet oldTypes(alloc_->lifoAlloc(), subject->type());
    2526           0 :         if (oldTypes.equals(type))
    2527        1463 :             return Ok();
    2528             :     }
    2529             : 
    2530             :     MInstruction* replace = nullptr;
    2531             :     MDefinition* ins;
    2532             : 
    2533           0 :     for (uint32_t i = 0; i < current->stackDepth(); i++) {
    2534        2246 :         ins = current->getSlot(i);
    2535             : 
    2536             :         // Instead of creating a new MFilterTypeSet, try to update the old one.
    2537           0 :         if (ins->isFilterTypeSet() && ins->getOperand(0) == subject &&
    2538           0 :             ins->dependency() == test)
    2539             :         {
    2540             :             TemporaryTypeSet* intersect =
    2541           0 :                 TypeSet::intersectSets(ins->resultTypeSet(), type, alloc_->lifoAlloc());
    2542           0 :             if (!intersect)
    2543           0 :                 return abort(AbortReason::Alloc);
    2544             : 
    2545           0 :             ins->toFilterTypeSet()->setResultType(intersect->getKnownMIRType());
    2546           0 :             ins->toFilterTypeSet()->setResultTypeSet(intersect);
    2547             : 
    2548           0 :             if (ins->type() == MIRType::Undefined)
    2549           0 :                 current->setSlot(i, constant(UndefinedValue()));
    2550           0 :             else if (ins->type() == MIRType::Null)
    2551           0 :                 current->setSlot(i, constant(NullValue()));
    2552           0 :             else if (ins->type() == MIRType::MagicOptimizedArguments)
    2553           0 :                 current->setSlot(i, constant(MagicValue(JS_OPTIMIZED_ARGUMENTS)));
    2554             :             else
    2555           0 :                 MOZ_ASSERT(!IsMagicType(ins->type()));
    2556             :             continue;
    2557             :         }
    2558             : 
    2559           0 :         if (ins == subject) {
    2560           0 :             if (!replace) {
    2561           0 :                 replace = MFilterTypeSet::New(alloc(), subject, type);
    2562         210 :                 current->add(replace);
    2563             : 
    2564             :                 // Make sure we don't hoist it above the MTest, we can use the
    2565             :                 // 'dependency' of an MInstruction. This is normally used by
    2566             :                 // Alias Analysis, but won't get overwritten, since this
    2567             :                 // instruction doesn't have an AliasSet.
    2568         210 :                 replace->setDependency(test);
    2569             : 
    2570           0 :                 if (replace->type() == MIRType::Undefined)
    2571           0 :                     replace = constant(UndefinedValue());
    2572           0 :                 else if (replace->type() == MIRType::Null)
    2573           0 :                     replace = constant(NullValue());
    2574           1 :                 else if (replace->type() == MIRType::MagicOptimizedArguments)
    2575           0 :                     replace = constant(MagicValue(JS_OPTIMIZED_ARGUMENTS));
    2576             :                 else
    2577         396 :                     MOZ_ASSERT(!IsMagicType(ins->type()));
    2578             :             }
    2579         239 :             current->setSlot(i, replace);
    2580             :         }
    2581             :     }
    2582         222 :     return Ok();
    2583             : }
    2584             : 
    2585             : bool
    2586         490 : IonBuilder::detectAndOrStructure(MPhi* ins, bool* branchIsAnd)
    2587             : {
    2588             :     // Look for a triangle pattern:
    2589             :     //
    2590             :     //       initialBlock
    2591             :     //         /     |
    2592             :     // branchBlock   |
    2593             :     //         \     |
    2594             :     //        testBlock
    2595             :     //
    2596             :     // Where ins is a phi from testBlock which combines two values
    2597             :     // pushed onto the stack by initialBlock and branchBlock.
    2598             : 
    2599         490 :     if (ins->numOperands() != 2)
    2600             :         return false;
    2601             : 
    2602           0 :     MBasicBlock* testBlock = ins->block();
    2603         458 :     MOZ_ASSERT(testBlock->numPredecessors() == 2);
    2604             : 
    2605             :     MBasicBlock* initialBlock;
    2606             :     MBasicBlock* branchBlock;
    2607           1 :     if (testBlock->getPredecessor(0)->lastIns()->isTest()) {
    2608           0 :         initialBlock = testBlock->getPredecessor(0);
    2609           0 :         branchBlock = testBlock->getPredecessor(1);
    2610           1 :     } else if (testBlock->getPredecessor(1)->lastIns()->isTest()) {
    2611           0 :         initialBlock = testBlock->getPredecessor(1);
    2612           0 :         branchBlock = testBlock->getPredecessor(0);
    2613             :     } else {
    2614             :         return false;
    2615             :     }
    2616             : 
    2617           0 :     if (branchBlock->numSuccessors() != 1)
    2618             :         return false;
    2619             : 
    2620           0 :     if (branchBlock->numPredecessors() != 1 || branchBlock->getPredecessor(0) != initialBlock)
    2621             :         return false;
    2622             : 
    2623           0 :     if (initialBlock->numSuccessors() != 2)
    2624             :         return false;
    2625             : 
    2626           0 :     MDefinition* branchResult = ins->getOperand(testBlock->indexForPredecessor(branchBlock));
    2627           0 :     MDefinition* initialResult = ins->getOperand(testBlock->indexForPredecessor(initialBlock));
    2628             : 
    2629           0 :     if (branchBlock->stackDepth() != initialBlock->stackDepth())
    2630             :         return false;
    2631           0 :     if (branchBlock->stackDepth() != testBlock->stackDepth() + 1)
    2632             :         return false;
    2633           0 :     if (branchResult != branchBlock->peek(-1) || initialResult != initialBlock->peek(-1))
    2634             :         return false;
    2635             : 
    2636           0 :     MTest* initialTest = initialBlock->lastIns()->toTest();
    2637           0 :     bool branchIsTrue = branchBlock == initialTest->ifTrue();
    2638           0 :     if (initialTest->input() == ins->getOperand(0))
    2639           0 :         *branchIsAnd = branchIsTrue != (testBlock->getPredecessor(0) == branchBlock);
    2640           0 :     else if (initialTest->input() == ins->getOperand(1))
    2641           0 :         *branchIsAnd = branchIsTrue != (testBlock->getPredecessor(1) == branchBlock);
    2642             :     else
    2643             :         return false;
    2644             : 
    2645             :     return true;
    2646             : }
    2647             : 
    2648             : AbortReasonOr<Ok>
    2649        1412 : IonBuilder::improveTypesAtCompare(MCompare* ins, bool trueBranch, MTest* test)
    2650             : {
    2651           0 :     if (ins->compareType() == MCompare::Compare_Undefined ||
    2652        1292 :         ins->compareType() == MCompare::Compare_Null)
    2653             :     {
    2654         292 :         return improveTypesAtNullOrUndefinedCompare(ins, trueBranch, test);
    2655             :     }
    2656             : 
    2657           0 :     if ((ins->lhs()->isTypeOf() || ins->rhs()->isTypeOf()) &&
    2658         360 :         (ins->lhs()->isConstant() || ins->rhs()->isConstant()))
    2659             :     {
    2660          60 :         return improveTypesAtTypeOfCompare(ins, trueBranch, test);
    2661             :     }
    2662             : 
    2663        1060 :     return Ok();
    2664             : }
    2665             : 
    2666             : AbortReasonOr<Ok>
    2667          60 : IonBuilder::improveTypesAtTypeOfCompare(MCompare* ins, bool trueBranch, MTest* test)
    2668             : {
    2669           0 :     MTypeOf* typeOf = ins->lhs()->isTypeOf() ? ins->lhs()->toTypeOf() : ins->rhs()->toTypeOf();
    2670         300 :     MConstant* constant = ins->lhs()->isConstant() ? ins->lhs()->toConstant() : ins->rhs()->toConstant();
    2671             : 
    2672           0 :     if (constant->type() != MIRType::String)
    2673           0 :         return Ok();
    2674             : 
    2675           0 :     bool equal = ins->jsop() == JSOP_EQ || ins->jsop() == JSOP_STRICTEQ;
    2676          60 :     bool notEqual = ins->jsop() == JSOP_NE || ins->jsop() == JSOP_STRICTNE;
    2677             : 
    2678           0 :     if (notEqual)
    2679          28 :         trueBranch = !trueBranch;
    2680             : 
    2681             :     // Relational compares not supported.
    2682           1 :     if (!equal && !notEqual)
    2683           0 :         return Ok();
    2684             : 
    2685           0 :     MDefinition* subject = typeOf->input();
    2686          60 :     TemporaryTypeSet* inputTypes = subject->resultTypeSet();
    2687             : 
    2688             :     // Create temporary typeset equal to the type if there is no resultTypeSet.
    2689           0 :     TemporaryTypeSet tmp;
    2690           0 :     if (!inputTypes) {
    2691           0 :         if (subject->type() == MIRType::Value)
    2692           0 :             return Ok();
    2693           0 :         inputTypes = &tmp;
    2694           4 :         tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(subject->type())), alloc_->lifoAlloc());
    2695             :     }
    2696             : 
    2697           1 :     if (inputTypes->unknown())
    2698           0 :         return Ok();
    2699             : 
    2700             :     // Note: we cannot remove the AnyObject type in the false branch,
    2701             :     // since there are multiple ways to get an object. That is the reason
    2702             :     // for the 'trueBranch' test.
    2703           0 :     TemporaryTypeSet filter;
    2704           0 :     const JSAtomState& names = runtime->names();
    2705           0 :     if (constant->toString() == TypeName(JSTYPE_UNDEFINED, names)) {
    2706           0 :         filter.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
    2707           0 :         if (typeOf->inputMaybeCallableOrEmulatesUndefined() && trueBranch)
    2708           0 :             filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
    2709           0 :     } else if (constant->toString() == TypeName(JSTYPE_BOOLEAN, names)) {
    2710           0 :         filter.addType(TypeSet::BooleanType(), alloc_->lifoAlloc());
    2711           0 :     } else if (constant->toString() == TypeName(JSTYPE_NUMBER, names)) {
    2712           0 :         filter.addType(TypeSet::Int32Type(), alloc_->lifoAlloc());
    2713           0 :         filter.addType(TypeSet::DoubleType(), alloc_->lifoAlloc());
    2714           0 :     } else if (constant->toString() == TypeName(JSTYPE_STRING, names)) {
    2715           0 :         filter.addType(TypeSet::StringType(), alloc_->lifoAlloc());
    2716           0 :     } else if (constant->toString() == TypeName(JSTYPE_SYMBOL, names)) {
    2717           0 :         filter.addType(TypeSet::SymbolType(), alloc_->lifoAlloc());
    2718           0 :     } else if (constant->toString() == TypeName(JSTYPE_OBJECT, names)) {
    2719           0 :         filter.addType(TypeSet::NullType(), alloc_->lifoAlloc());
    2720           0 :         if (trueBranch)
    2721           0 :             filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
    2722           0 :     } else if (constant->toString() == TypeName(JSTYPE_FUNCTION, names)) {
    2723           0 :         if (typeOf->inputMaybeCallableOrEmulatesUndefined() && trueBranch)
    2724           0 :             filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
    2725             :     } else {
    2726           0 :         return Ok();
    2727             :     }
    2728             : 
    2729             :     TemporaryTypeSet* type;
    2730           0 :     if (trueBranch)
    2731          60 :         type = TypeSet::intersectSets(&filter, inputTypes, alloc_->lifoAlloc());
    2732             :     else
    2733          60 :         type = TypeSet::removeSet(inputTypes, &filter, alloc_->lifoAlloc());
    2734             : 
    2735           1 :     if (!type)
    2736           0 :         return abort(AbortReason::Alloc);
    2737             : 
    2738          60 :     return replaceTypeSet(subject, type, test);
    2739             : }
    2740             : 
    2741             : AbortReasonOr<Ok>
    2742         292 : IonBuilder::improveTypesAtNullOrUndefinedCompare(MCompare* ins, bool trueBranch, MTest* test)
    2743             : {
    2744         292 :     MOZ_ASSERT(ins->compareType() == MCompare::Compare_Undefined ||
    2745             :                ins->compareType() == MCompare::Compare_Null);
    2746             : 
    2747             :     // altersUndefined/Null represents if we can filter/set Undefined/Null.
    2748             :     bool altersUndefined, altersNull;
    2749         292 :     JSOp op = ins->jsop();
    2750             : 
    2751         292 :     switch(op) {
    2752             :       case JSOP_STRICTNE:
    2753             :       case JSOP_STRICTEQ:
    2754           0 :         altersUndefined = ins->compareType() == MCompare::Compare_Undefined;
    2755           0 :         altersNull = ins->compareType() == MCompare::Compare_Null;
    2756         292 :         break;
    2757             :       case JSOP_NE:
    2758             :       case JSOP_EQ:
    2759             :         altersUndefined = altersNull = true;
    2760             :         break;
    2761             :       default:
    2762           0 :         MOZ_CRASH("Relational compares not supported");
    2763             :     }
    2764             : 
    2765           0 :     MDefinition* subject = ins->lhs();
    2766         292 :     TemporaryTypeSet* inputTypes = subject->resultTypeSet();
    2767             : 
    2768         876 :     MOZ_ASSERT(IsNullOrUndefined(ins->rhs()->type()));
    2769             : 
    2770             :     // Create temporary typeset equal to the type if there is no resultTypeSet.
    2771           0 :     TemporaryTypeSet tmp;
    2772           0 :     if (!inputTypes) {
    2773           0 :         if (subject->type() == MIRType::Value)
    2774           0 :             return Ok();
    2775           0 :         inputTypes = &tmp;
    2776         100 :         tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(subject->type())), alloc_->lifoAlloc());
    2777             :     }
    2778             : 
    2779           0 :     if (inputTypes->unknown())
    2780           0 :         return Ok();
    2781             : 
    2782             :     TemporaryTypeSet* type;
    2783             : 
    2784             :     // Decide if we need to filter the type or set it.
    2785         288 :     if ((op == JSOP_STRICTEQ || op == JSOP_EQ) ^ trueBranch) {
    2786             :         // Remove undefined/null
    2787           0 :         TemporaryTypeSet remove;
    2788           0 :         if (altersUndefined)
    2789           0 :             remove.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
    2790           0 :         if (altersNull)
    2791         258 :             remove.addType(TypeSet::NullType(), alloc_->lifoAlloc());
    2792             : 
    2793         288 :         type = TypeSet::removeSet(inputTypes, &remove, alloc_->lifoAlloc());
    2794             :     } else {
    2795             :         // Set undefined/null.
    2796           0 :         TemporaryTypeSet base;
    2797           0 :         if (altersUndefined) {
    2798         174 :             base.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
    2799             :             // If TypeSet emulates undefined, then we cannot filter the objects.
    2800           0 :             if (inputTypes->maybeEmulatesUndefined(constraints()))
    2801           9 :                 base.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
    2802             :         }
    2803             : 
    2804           0 :         if (altersNull)
    2805         258 :             base.addType(TypeSet::NullType(), alloc_->lifoAlloc());
    2806             : 
    2807         288 :         type = TypeSet::intersectSets(&base, inputTypes, alloc_->lifoAlloc());
    2808             :     }
    2809             : 
    2810           1 :     if (!type)
    2811           0 :         return abort(AbortReason::Alloc);
    2812             : 
    2813         288 :     return replaceTypeSet(subject, type, test);
    2814             : }
    2815             : 
    2816             : AbortReasonOr<Ok>
    2817        4748 : IonBuilder::improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test)
    2818             : {
    2819             :     // We explore the test condition to try and deduce as much type information
    2820             :     // as possible.
    2821             : 
    2822             :     // All branches of this switch that don't want to fall through to the
    2823             :     // default behavior must return.  The default behavior assumes that a true
    2824             :     // test means the incoming ins is not null or undefined and that a false
    2825             :     // tests means it's one of null, undefined, false, 0, "", and objects
    2826             :     // emulating undefined
    2827        4748 :     switch (ins->op()) {
    2828             :       case MDefinition::Opcode::Not:
    2829        1796 :         return improveTypesAtTest(ins->toNot()->getOperand(0), !trueBranch, test);
    2830             :       case MDefinition::Opcode::IsObject: {
    2831           0 :         MDefinition* subject = ins->getOperand(0);
    2832          12 :         TemporaryTypeSet* oldType = subject->resultTypeSet();
    2833             : 
    2834             :         // Create temporary typeset equal to the type if there is no resultTypeSet.
    2835           0 :         TemporaryTypeSet tmp;
    2836           0 :         if (!oldType) {
    2837           0 :             if (subject->type() == MIRType::Value)
    2838           0 :                 return Ok();
    2839           0 :             oldType = &tmp;
    2840           0 :             tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(subject->type())), alloc_->lifoAlloc());
    2841             :         }
    2842             : 
    2843           1 :         if (oldType->unknown())
    2844           0 :             return Ok();
    2845             : 
    2846           0 :         TemporaryTypeSet* type = nullptr;
    2847           0 :         if (trueBranch)
    2848          12 :             type = oldType->cloneObjectsOnly(alloc_->lifoAlloc());
    2849             :         else
    2850          12 :             type = oldType->cloneWithoutObjects(alloc_->lifoAlloc());
    2851             : 
    2852           1 :         if (!type)
    2853           0 :             return abort(AbortReason::Alloc);
    2854             : 
    2855          12 :         return replaceTypeSet(subject, type, test);
    2856             :       }
    2857             :       case MDefinition::Opcode::Phi: {
    2858           0 :         bool branchIsAnd = true;
    2859         490 :         if (!detectAndOrStructure(ins->toPhi(), &branchIsAnd)) {
    2860             :             // Just fall through to the default behavior.
    2861             :             break;
    2862             :         }
    2863             : 
    2864             :         // Now we have detected the triangular structure and determined if it
    2865             :         // was an AND or an OR.
    2866           0 :         if (branchIsAnd) {
    2867           0 :             if (trueBranch) {
    2868           0 :                 MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(0), true, test));
    2869           0 :                 MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(1), true, test));
    2870             :             }
    2871             :         } else {
    2872             :             /*
    2873             :              * if (a || b) {
    2874             :              *    ...
    2875             :              * } else {
    2876             :              *    ...
    2877             :              * }
    2878             :              *
    2879             :              * If we have a statements like the one described above,
    2880             :              * And we are in the else branch of it. It amounts to:
    2881             :              * if (!(a || b)) and being in the true branch.
    2882             :              *
    2883             :              * Simplifying, we have (!a && !b)
    2884             :              * In this case we can use the same logic we use for branchIsAnd
    2885             :              *
    2886             :              */
    2887           0 :             if (!trueBranch) {
    2888           0 :                 MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(0), false, test));
    2889           0 :                 MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(1), false, test));
    2890             :             }
    2891             :         }
    2892           0 :         return Ok();
    2893             :       }
    2894             : 
    2895             :       case MDefinition::Opcode::Compare:
    2896        2824 :         return improveTypesAtCompare(ins->toCompare(), trueBranch, test);
    2897             : 
    2898             :       default:
    2899             :         break;
    2900             :     }
    2901             : 
    2902             :     // By default MTest tests ToBoolean(input). As a result in the true branch we can filter
    2903             :     // undefined and null. In false branch we can only encounter undefined, null, false, 0, ""
    2904             :     // and objects that emulate undefined.
    2905             : 
    2906        2426 :     TemporaryTypeSet* oldType = ins->resultTypeSet();
    2907             :     TemporaryTypeSet* type;
    2908             : 
    2909             :     // Create temporary typeset equal to the type if there is no resultTypeSet.
    2910           0 :     TemporaryTypeSet tmp;
    2911           0 :     if (!oldType) {
    2912           0 :         if (ins->type() == MIRType::Value)
    2913           0 :             return Ok();
    2914           0 :         oldType = &tmp;
    2915        2900 :         tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(ins->type())), alloc_->lifoAlloc());
    2916             :     }
    2917             : 
    2918             :     // If ins does not have a typeset we return as we cannot optimize.
    2919           0 :     if (oldType->unknown())
    2920           0 :         return Ok();
    2921             : 
    2922             :     // Decide either to set or remove.
    2923           0 :     if (trueBranch) {
    2924           0 :         TemporaryTypeSet remove;
    2925           0 :         remove.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
    2926           0 :         remove.addType(TypeSet::NullType(), alloc_->lifoAlloc());
    2927        2426 :         type = TypeSet::removeSet(oldType, &remove, alloc_->lifoAlloc());
    2928             :     } else {
    2929           0 :         TemporaryTypeSet base;
    2930           0 :         base.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc()); // ToBoolean(undefined) == false
    2931           0 :         base.addType(TypeSet::NullType(), alloc_->lifoAlloc()); // ToBoolean(null) == false
    2932           0 :         base.addType(TypeSet::BooleanType(), alloc_->lifoAlloc()); // ToBoolean(false) == false
    2933           0 :         base.addType(TypeSet::Int32Type(), alloc_->lifoAlloc()); // ToBoolean(0) == false
    2934           0 :         base.addType(TypeSet::DoubleType(), alloc_->lifoAlloc()); // ToBoolean(0.0) == false
    2935        3639 :         base.addType(TypeSet::StringType(), alloc_->lifoAlloc()); // ToBoolean("") == false
    2936             : 
    2937             :         // If the typeset does emulate undefined, then we cannot filter out
    2938             :         // objects.
    2939           0 :         if (oldType->maybeEmulatesUndefined(constraints()))
    2940         135 :             base.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
    2941             : 
    2942        2426 :         type = TypeSet::intersectSets(&base, oldType, alloc_->lifoAlloc());
    2943             :     }
    2944             : 
    2945           1 :     if (!type)
    2946           0 :         return abort(AbortReason::Alloc);
    2947             : 
    2948        2426 :     return replaceTypeSet(ins, type, test);
    2949             : }
    2950             : 
    2951             : AbortReasonOr<Ok>
    2952          36 : IonBuilder::jsop_dup2()
    2953             : {
    2954           0 :     uint32_t lhsSlot = current->stackDepth() - 2;
    2955           0 :     uint32_t rhsSlot = current->stackDepth() - 1;
    2956           0 :     current->pushSlot(lhsSlot);
    2957           0 :     current->pushSlot(rhsSlot);
    2958          36 :     return Ok();
    2959             : }
    2960             : 
    2961             : AbortReasonOr<Ok>
    2962        1925 : IonBuilder::visitTest(CFGTest* test)
    2963             : {
    2964        1925 :     MDefinition* ins = test->mustKeepCondition() ? current->peek(-1) : current->pop();
    2965             : 
    2966             :     // Create true and false branches.
    2967             :     MBasicBlock* ifTrue;
    2968        9625 :     MOZ_TRY_VAR(ifTrue, newBlock(current, test->trueBranch()->startPc()));
    2969             :     MBasicBlock* ifFalse;
    2970        9625 :     MOZ_TRY_VAR(ifFalse, newBlock(current, test->falseBranch()->startPc()));
    2971             : 
    2972           0 :     MTest* mir = newTest(ins, ifTrue, ifFalse);
    2973        1925 :     current->end(mir);
    2974             : 
    2975             :     // Filter the types in the true branch.
    2976           0 :     MOZ_TRY(setCurrentAndSpecializePhis(ifTrue));
    2977        5775 :     MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ true, mir));
    2978             : 
    2979        1925 :     blockWorklist[test->trueBranch()->id()] = ifTrue;
    2980             : 
    2981             :     // Filter the types in the false branch.
    2982             :     // Note: sometimes the false branch is used as merge point. As a result
    2983             :     // reuse the ifFalse block as a type improvement block and create a new
    2984             :     // ifFalse which we can use for the merge.
    2985           0 :     MBasicBlock* filterBlock = ifFalse;
    2986           0 :     ifFalse = nullptr;
    2987        1925 :     graph().addBlock(filterBlock);
    2988             : 
    2989           0 :     MOZ_TRY(setCurrentAndSpecializePhis(filterBlock));
    2990        5775 :     MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ false, mir));
    2991             : 
    2992           0 :     MOZ_TRY_VAR(ifFalse, newBlock(filterBlock, test->falseBranch()->startPc()));
    2993        1925 :     filterBlock->end(MGoto::New(alloc(), ifFalse));
    2994             : 
    2995           0 :     if (filterBlock->pc() && script()->hasScriptCounts())
    2996        1925 :         filterBlock->setHitCount(script()->getHitCount(filterBlock->pc()));
    2997             : 
    2998        1925 :     blockWorklist[test->falseBranch()->id()] = ifFalse;
    2999             : 
    3000        1925 :     current = nullptr;
    3001             : 
    3002        1925 :     return Ok();
    3003             : }
    3004             : 
    3005             : AbortReasonOr<Ok>
    3006           0 : IonBuilder::visitCompare(CFGCompare* compare)
    3007             : {
    3008           0 :     MDefinition* rhs = current->peek(-1);
    3009           0 :     MDefinition* lhs = current->peek(-2);
    3010             : 
    3011             :     // Execute the compare operation.
    3012           0 :     MOZ_TRY(jsop_compare(JSOP_STRICTEQ));
    3013           0 :     MInstruction* cmpResult = current->pop()->toInstruction();
    3014           0 :     MOZ_ASSERT(!cmpResult->isEffectful());
    3015             : 
    3016             :     // Put the rhs/lhs again on the stack.
    3017           0 :     current->push(lhs);
    3018           0 :     current->push(rhs);
    3019             : 
    3020             :     // Create true and false branches.
    3021             :     MBasicBlock* ifTrue;
    3022           0 :     MOZ_TRY_VAR(ifTrue, newBlockPopN(current, compare->trueBranch()->startPc(),
    3023             :                                      compare->truePopAmount()));
    3024             :     MBasicBlock* ifFalse;
    3025           0 :     MOZ_TRY_VAR(ifFalse, newBlockPopN(current, compare->falseBranch()->startPc(),
    3026             :                                       compare->falsePopAmount()));
    3027             : 
    3028           0 :     blockWorklist[compare->trueBranch()->id()] = ifTrue;
    3029           0 :     blockWorklist[compare->falseBranch()->id()] = ifFalse;
    3030             : 
    3031           0 :     MTest* mir = newTest(cmpResult, ifTrue, ifFalse);
    3032           0 :     current->end(mir);
    3033             : 
    3034             :     // Filter the types in the true branch.
    3035           0 :     MOZ_TRY(setCurrentAndSpecializePhis(ifTrue));
    3036           0 :     MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ true, mir));
    3037             : 
    3038             :     // Filter the types in the false branch.
    3039           0 :     MOZ_TRY(setCurrentAndSpecializePhis(ifFalse));
    3040           0 :     MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ false, mir));
    3041             : 
    3042           0 :     current = nullptr;
    3043             : 
    3044           0 :     return Ok();
    3045             : }
    3046             : 
    3047             : AbortReasonOr<Ok>
    3048          36 : IonBuilder::visitTry(CFGTry* try_)
    3049             : {
    3050             :     // We don't support try-finally. The ControlFlowGenerator should have
    3051             :     // aborted compilation in this case.
    3052             : 
    3053             :     // Try-catch within inline frames is not yet supported.
    3054          72 :     MOZ_ASSERT(!isInlineBuilder());
    3055             : 
    3056             :     // Try-catch during the arguments usage analysis is not yet supported. Code
    3057             :     // accessing the arguments within the 'catch' block is not accounted for.
    3058           0 :     if (info().analysisMode() == Analysis_ArgumentsUsage)
    3059           0 :         return abort(AbortReason::Disable, "Try-catch during arguments usage analysis");
    3060             : 
    3061          36 :     graph().setHasTryBlock();
    3062             : 
    3063             :     MBasicBlock* tryBlock;
    3064         216 :     MOZ_TRY_VAR(tryBlock, newBlock(current, try_->tryBlock()->startPc()));
    3065             : 
    3066          72 :     blockWorklist[try_->tryBlock()->id()] = tryBlock;
    3067             : 
    3068             :     // Connect the code after the try-catch to the graph with an MGotoWithFake
    3069             :     // instruction that always jumps to the try block. This ensures the
    3070             :     // successor block always has a predecessor.
    3071             :     MBasicBlock* successor;
    3072         180 :     MOZ_TRY_VAR(successor, newBlock(current, try_->getSuccessor(1)->startPc()));
    3073             : 
    3074          72 :     blockWorklist[try_->afterTryCatchBlock()->id()] = successor;
    3075             : 
    3076          36 :     current->end(MGotoWithFake::New(alloc(), tryBlock, successor));
    3077             : 
    3078             :     // The baseline compiler should not attempt to enter the catch block
    3079             :     // via OSR.
    3080          40 :     MOZ_ASSERT(info().osrPc() < try_->catchStartPc() ||
    3081             :                info().osrPc() >= try_->afterTryCatchBlock()->startPc());
    3082             : 
    3083          36 :     return Ok();
    3084             : }
    3085             : 
    3086             : AbortReasonOr<Ok>
    3087         941 : IonBuilder::visitReturn(CFGControlInstruction* control)
    3088             : {
    3089             :     MDefinition* def;
    3090         941 :     switch (control->type()) {
    3091             :       case CFGControlInstruction::Type_Return:
    3092             :         // Return the last instruction.
    3093           0 :         def = current->pop();
    3094         874 :         break;
    3095             : 
    3096             :       case CFGControlInstruction::Type_RetRVal:
    3097             :         // Return undefined eagerly if script doesn't use return value.
    3098           0 :         if (script()->noScriptRval()) {
    3099           0 :             MInstruction* ins = MConstant::New(alloc(), UndefinedValue());
    3100           0 :             current->add(ins);
    3101           0 :             def = ins;
    3102           0 :             break;
    3103             :         }
    3104             : 
    3105           0 :         def = current->getSlot(info().returnValueSlot());
    3106          67 :         break;
    3107             : 
    3108             :       default:
    3109           0 :         def = nullptr;
    3110           0 :         MOZ_CRASH("unknown return op");
    3111             :     }
    3112             : 
    3113           0 :     MReturn* ret = MReturn::New(alloc(), def);
    3114         941 :     current->end(ret);
    3115             : 
    3116           0 :     if (!graph().addReturn(current))
    3117           0 :         return abort(AbortReason::Alloc);
    3118             : 
    3119             :     // Make sure no one tries to use this block now.
    3120           0 :     setCurrent(nullptr);
    3121         941 :     return Ok();
    3122             : }
    3123             : 
    3124             : AbortReasonOr<Ok>
    3125          27 : IonBuilder::visitThrow(CFGThrow *cfgIns)
    3126             : {
    3127          27 :     MDefinition* def = current->pop();
    3128             : 
    3129             :     // MThrow is not marked as effectful. This means when it throws and we
    3130             :     // are inside a try block, we could use an earlier resume point and this
    3131             :     // resume point may not be up-to-date, for example:
    3132             :     //
    3133             :     // (function() {
    3134             :     //     try {
    3135             :     //         var x = 1;
    3136             :     //         foo(); // resume point
    3137             :     //         x = 2;
    3138             :     //         throw foo;
    3139             :     //     } catch(e) {
    3140             :     //         print(x);
    3141             :     //     }
    3142             :     // ])();
    3143             :     //
    3144             :     // If we use the resume point after the call, this will print 1 instead
    3145             :     // of 2. To fix this, we create a resume point right before the MThrow.
    3146             :     //
    3147             :     // Note that this is not a problem for instructions other than MThrow
    3148             :     // because they are either marked as effectful (have their own resume
    3149             :     // point) or cannot throw a catchable exception.
    3150             :     //
    3151             :     // We always install this resume point (instead of only when the function
    3152             :     // has a try block) in order to handle the Debugger onExceptionUnwind
    3153             :     // hook. When we need to handle the hook, we bail out to baseline right
    3154             :     // after the throw and propagate the exception when debug mode is on. This
    3155             :     // is opposed to the normal behavior of resuming directly in the
    3156             :     // associated catch block.
    3157           0 :     MNop* nop = MNop::New(alloc());
    3158          27 :     current->add(nop);
    3159             : 
    3160          81 :     MOZ_TRY(resumeAfter(nop));
    3161             : 
    3162           0 :     MThrow* ins = MThrow::New(alloc(), def);
    3163          27 :     current->end(ins);
    3164             : 
    3165          27 :     return Ok();
    3166             : }
    3167             : 
    3168             : AbortReasonOr<Ok>
    3169          12 : IonBuilder::visitTableSwitch(CFGTableSwitch* cfgIns)
    3170             : {
    3171             :     // Pop input.
    3172          12 :     MDefinition* ins = current->pop();
    3173             : 
    3174             :     // Create MIR instruction
    3175          12 :     MTableSwitch* tableswitch = MTableSwitch::New(alloc(), ins, cfgIns->low(), cfgIns->high());
    3176             : 
    3177             : #ifdef DEBUG
    3178           0 :     MOZ_ASSERT(cfgIns->defaultCase() == cfgIns->getSuccessor(0));
    3179           0 :     for (size_t i = 1; i < cfgIns->numSuccessors(); i++) {
    3180         144 :         MOZ_ASSERT(cfgIns->getCase(i-1) == cfgIns->getSuccessor(i));
    3181             :     }
    3182             : #endif
    3183             : 
    3184             :     // Create the cases
    3185           0 :     for (size_t i = 0; i < cfgIns->numSuccessors(); i++) {
    3186          84 :         const CFGBlock* cfgblock = cfgIns->getSuccessor(i);
    3187             : 
    3188             :         MBasicBlock* caseBlock;
    3189         420 :         MOZ_TRY_VAR(caseBlock, newBlock(current, cfgblock->startPc()));
    3190             : 
    3191          84 :         blockWorklist[cfgblock->id()] = caseBlock;
    3192             : 
    3193             :         size_t index;
    3194           0 :         if (i == 0) {
    3195           1 :             if (!tableswitch->addDefault(caseBlock, &index))
    3196           0 :                 return abort(AbortReason::Alloc);
    3197             : 
    3198             :         } else {
    3199           1 :             if (!tableswitch->addSuccessor(caseBlock, &index))
    3200           0 :                 return abort(AbortReason::Alloc);
    3201             : 
    3202           1 :             if (!tableswitch->addCase(index))
    3203           0 :                 return abort(AbortReason::Alloc);
    3204             : 
    3205             :             // If this is an actual case statement, optimize by replacing the
    3206             :             // input to the switch case with the actual number of the case.
    3207           0 :             MConstant* constant = MConstant::New(alloc(), Int32Value(i - 1 + tableswitch->low()));
    3208           0 :             caseBlock->add(constant);
    3209           0 :             for (uint32_t j = 0; j < caseBlock->stackDepth(); j++) {
    3210         504 :                 if (ins != caseBlock->getSlot(j))
    3211             :                     continue;
    3212             : 
    3213           0 :                 constant->setDependency(ins);
    3214           0 :                 caseBlock->setSlot(j, constant);
    3215             :             }
    3216          72 :             graph().addBlock(caseBlock);
    3217             : 
    3218           0 :             if (caseBlock->pc() && script()->hasScriptCounts())
    3219          72 :                 caseBlock->setHitCount(script()->getHitCount(caseBlock->pc()));
    3220             : 
    3221             :             MBasicBlock* merge;
    3222           0 :             MOZ_TRY_VAR(merge, newBlock(caseBlock, cfgblock->startPc()));
    3223           1 :             if (!merge)
    3224           0 :                 return abort(AbortReason::Alloc);
    3225             : 
    3226           0 :             caseBlock->end(MGoto::New(alloc(), merge));
    3227          72 :             blockWorklist[cfgblock->id()] = merge;
    3228             :         }
    3229             : 
    3230          84 :         MOZ_ASSERT(index == i);
    3231             :     }
    3232             : 
    3233             :     // Save the MIR instruction as last instruction of this block.
    3234           0 :     current->end(tableswitch);
    3235          12 :     return Ok();
    3236             : }
    3237             : 
    3238             : void
    3239        7431 : IonBuilder::pushConstant(const Value& v)
    3240             : {
    3241           0 :     current->push(constant(v));
    3242        7431 : }
    3243             : 
    3244             : AbortReasonOr<Ok>
    3245           0 : IonBuilder::bitnotTrySpecialized(bool* emitted, MDefinition* input)
    3246             : {
    3247           0 :     MOZ_ASSERT(*emitted == false);
    3248             : 
    3249             :     // Try to emit a specialized bitnot instruction based on the input type
    3250             :     // of the operand.
    3251             : 
    3252           0 :     if (input->mightBeType(MIRType::Object) || input->mightBeType(MIRType::Symbol))
    3253           0 :         return Ok();
    3254             : 
    3255           0 :     MBitNot* ins = MBitNot::New(alloc(), input);
    3256           0 :     ins->setSpecialization(MIRType::Int32);
    3257             : 
    3258           0 :     current->add(ins);
    3259           0 :     current->push(ins);
    3260             : 
    3261           0 :     *emitted = true;
    3262           0 :     return Ok();
    3263             : }
    3264             : 
    3265             : AbortReasonOr<Ok>
    3266           0 : IonBuilder::jsop_bitnot()
    3267             : {
    3268           0 :     bool emitted = false;
    3269             : 
    3270           0 :     MDefinition* input = current->pop();
    3271             : 
    3272           0 :     if (!forceInlineCaches()) {
    3273           0 :         MOZ_TRY(bitnotTrySpecialized(&emitted, input));
    3274           0 :         if(emitted)
    3275           0 :             return Ok();
    3276             :     }
    3277             : 
    3278           0 :     MOZ_TRY(arithTrySharedStub(&emitted, JSOP_BITNOT, nullptr, input));
    3279           0 :     if (emitted)
    3280           0 :         return Ok();
    3281             : 
    3282             :     // Not possible to optimize. Do a slow vm call.
    3283           0 :     MBitNot* ins = MBitNot::New(alloc(), input);
    3284             : 
    3285           0 :     current->add(ins);
    3286           0 :     current->push(ins);
    3287           0 :     MOZ_ASSERT(ins->isEffectful());
    3288             :     return resumeAfter(ins);
    3289             : }
    3290             : 
    3291             : AbortReasonOr<Ok>
    3292          60 : IonBuilder::jsop_bitop(JSOp op)
    3293             : {
    3294             :     // Pop inputs.
    3295           0 :     MDefinition* right = current->pop();
    3296          60 :     MDefinition* left = current->pop();
    3297             : 
    3298             :     MBinaryBitwiseInstruction* ins;
    3299          60 :     switch (op) {
    3300             :       case JSOP_BITAND:
    3301           0 :         ins = MBitAnd::New(alloc(), left, right);
    3302          17 :         break;
    3303             : 
    3304             :       case JSOP_BITOR:
    3305           0 :         ins = MBitOr::New(alloc(), left, right);
    3306          42 :         break;
    3307             : 
    3308             :       case JSOP_BITXOR:
    3309           0 :         ins = MBitXor::New(alloc(), left, right);
    3310           0 :         break;
    3311             : 
    3312             :       case JSOP_LSH:
    3313           0 :         ins = MLsh::New(alloc(), left, right);
    3314           0 :         break;
    3315             : 
    3316             :       case JSOP_RSH:
    3317           0 :         ins = MRsh::New(alloc(), left, right);
    3318           1 :         break;
    3319             : 
    3320             :       case JSOP_URSH:
    3321           0 :         ins = MUrsh::New(alloc(), left, right);
    3322           0 :         break;
    3323             : 
    3324             :       default:
    3325           0 :         MOZ_CRASH("unexpected bitop");
    3326             :     }
    3327             : 
    3328           0 :     current->add(ins);
    3329          60 :     ins->infer(inspector, pc);
    3330             : 
    3331           0 :     current->push(ins);
    3332           0 :     if (ins->isEffectful())
    3333           0 :         MOZ_TRY(resumeAfter(ins));
    3334             : 
    3335          60 :     return Ok();
    3336             : }
    3337             : 
    3338             : MDefinition::Opcode
    3339         149 : JSOpToMDefinition(JSOp op)
    3340             : {
    3341         149 :     switch (op) {
    3342             :       case JSOP_ADD:
    3343             :         return MDefinition::Opcode::Add;
    3344             :       case JSOP_SUB:
    3345           2 :         return MDefinition::Opcode::Sub;
    3346             :       case JSOP_MUL:
    3347           4 :         return MDefinition::Opcode::Mul;
    3348             :       case JSOP_DIV:
    3349           0 :         return MDefinition::Opcode::Div;
    3350             :       case JSOP_MOD:
    3351           0 :         return MDefinition::Opcode::Mod;
    3352             :       default:
    3353           0 :         MOZ_CRASH("unexpected binary opcode");
    3354             :     }
    3355             : }
    3356             : 
    3357             : AbortReasonOr<Ok>
    3358         336 : IonBuilder::binaryArithTryConcat(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
    3359             : {
    3360         336 :     MOZ_ASSERT(*emitted == false);
    3361             : 
    3362             :     // Try to convert an addition into a concat operation if the inputs
    3363             :     // indicate this might be a concatenation.
    3364             : 
    3365             :     // Only try to replace this with concat when we have an addition.
    3366           0 :     if (op != JSOP_ADD)
    3367          12 :         return Ok();
    3368             : 
    3369         324 :     trackOptimizationAttempt(TrackedStrategy::BinaryArith_Concat);
    3370             : 
    3371             :     // Make sure one of the inputs is a string.
    3372           0 :     if (left->type() != MIRType::String && right->type() != MIRType::String) {
    3373           0 :         trackOptimizationOutcome(TrackedOutcome::OperandNotString);
    3374         158 :         return Ok();
    3375             :     }
    3376             : 
    3377             :     // The non-string input (if present) should be atleast easily coercible to string.
    3378           0 :     if (right->type() != MIRType::String &&
    3379           0 :         (right->mightBeType(MIRType::Symbol) || right->mightBeType(MIRType::Object) ||
    3380          79 :          right->mightBeMagicType()))
    3381             :     {
    3382           0 :         trackOptimizationOutcome(TrackedOutcome::OperandNotEasilyCoercibleToString);
    3383           0 :         return Ok();
    3384             :     }
    3385           0 :     if (left->type() != MIRType::String &&
    3386           0 :         (left->mightBeType(MIRType::Symbol) || left->mightBeType(MIRType::Object) ||
    3387           0 :          left->mightBeMagicType()))
    3388             :     {
    3389           0 :         trackOptimizationOutcome(TrackedOutcome::OperandNotEasilyCoercibleToString);
    3390           0 :         return Ok();
    3391             :     }
    3392             : 
    3393           0 :     MConcat* ins = MConcat::New(alloc(), left, right);
    3394           0 :     current->add(ins);
    3395         166 :     current->push(ins);
    3396             : 
    3397         498 :     MOZ_TRY(maybeInsertResume());
    3398             : 
    3399           0 :     trackOptimizationSuccess();
    3400           0 :     *emitted = true;
    3401         166 :     return Ok();
    3402             : }
    3403             : 
    3404             : AbortReasonOr<Ok>
    3405           0 : IonBuilder::powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* power,
    3406             :                               MIRType outputType)
    3407             : {
    3408             :     // Typechecking.
    3409           0 :     MDefinition* output = nullptr;
    3410           0 :     MIRType baseType = base->type();
    3411           0 :     MIRType powerType = power->type();
    3412             : 
    3413           0 :     if (outputType != MIRType::Int32 && outputType != MIRType::Double)
    3414           0 :         return Ok();
    3415           0 :     if (!IsNumberType(baseType))
    3416           0 :         return Ok();
    3417           0 :     if (!IsNumberType(powerType))
    3418           0 :         return Ok();
    3419             : 
    3420           0 :     if (powerType == MIRType::Float32)
    3421           0 :         powerType = MIRType::Double;
    3422             : 
    3423           0 :     MPow* pow = MPow::New(alloc(), base, power, powerType);
    3424           0 :     current->add(pow);
    3425           0 :     output = pow;
    3426             : 
    3427             :     // Cast to the right type
    3428           0 :     if (outputType == MIRType::Int32 && output->type() != MIRType::Int32) {
    3429           0 :         auto* toInt = MToNumberInt32::New(alloc(), output);
    3430           0 :         current->add(toInt);
    3431           0 :         output = toInt;
    3432             :     }
    3433           0 :     if (outputType == MIRType::Double && output->type() != MIRType::Double) {
    3434           0 :         MToDouble* toDouble = MToDouble::New(alloc(), output);
    3435           0 :         current->add(toDouble);
    3436           0 :         output = toDouble;
    3437             :     }
    3438             : 
    3439           0 :     current->push(output);
    3440           0 :     *emitted = true;
    3441           0 :     return Ok();
    3442             : }
    3443             : 
    3444             : static inline bool
    3445         320 : SimpleArithOperand(MDefinition* op)
    3446             : {
    3447           0 :     return !op->emptyResultTypeSet()
    3448           0 :         && !op->mightBeType(MIRType::Object)
    3449           0 :         && !op->mightBeType(MIRType::String)
    3450           0 :         && !op->mightBeType(MIRType::Symbol)
    3451           0 :         && !op->mightBeType(MIRType::MagicOptimizedArguments)
    3452           0 :         && !op->mightBeType(MIRType::MagicHole)
    3453         615 :         && !op->mightBeType(MIRType::MagicIsConstructing);
    3454             : }
    3455             : 
    3456             : AbortReasonOr<Ok>
    3457         170 : IonBuilder::binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
    3458             : {
    3459         170 :     MOZ_ASSERT(*emitted == false);
    3460             : 
    3461             :     // Try to emit a specialized binary instruction based on the input types
    3462             :     // of the operands.
    3463             : 
    3464         170 :     trackOptimizationAttempt(TrackedStrategy::BinaryArith_SpecializedTypes);
    3465             : 
    3466             :     // Anything complex - strings, symbols, and objects - are not specialized
    3467           0 :     if (!SimpleArithOperand(left) || !SimpleArithOperand(right)) {
    3468           0 :         trackOptimizationOutcome(TrackedOutcome::OperandNotSimpleArith);
    3469          25 :         return Ok();
    3470             :     }
    3471             : 
    3472             :     // One of the inputs need to be a number.
    3473           0 :     if (!IsNumberType(left->type()) && !IsNumberType(right->type())) {
    3474           0 :         trackOptimizationOutcome(TrackedOutcome::OperandNotNumber);
    3475           0 :         return Ok();
    3476             :     }
    3477             : 
    3478           0 :     MDefinition::Opcode defOp = JSOpToMDefinition(op);
    3479           0 :     MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
    3480         145 :     ins->setNumberSpecialization(alloc(), inspector, pc);
    3481             : 
    3482           0 :     if (op == JSOP_ADD || op == JSOP_MUL)
    3483         143 :         ins->setCommutative();
    3484             : 
    3485           0 :     current->add(ins);
    3486         145 :     current->push(ins);
    3487             : 
    3488           0 :     MOZ_ASSERT(!ins->isEffectful());
    3489         435 :     MOZ_TRY(maybeInsertResume());
    3490             : 
    3491           0 :     trackOptimizationSuccess();
    3492           0 :     *emitted = true;
    3493         145 :     return Ok();
    3494             : }
    3495             : 
    3496             : AbortReasonOr<Ok>
    3497          25 : IonBuilder::binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
    3498             :                                                          MDefinition* left, MDefinition* right)
    3499             : {
    3500          25 :     MOZ_ASSERT(*emitted == false);
    3501             : 
    3502             :     // Try to emit a specialized binary instruction speculating the
    3503             :     // type using the baseline caches.
    3504             : 
    3505          25 :     trackOptimizationAttempt(TrackedStrategy::BinaryArith_SpecializedOnBaselineTypes);
    3506             : 
    3507           0 :     MIRType specialization = inspector->expectedBinaryArithSpecialization(pc);
    3508           0 :     if (specialization == MIRType::None) {
    3509           0 :         trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
    3510          25 :         return Ok();
    3511             :     }
    3512             : 
    3513           0 :     MDefinition::Opcode def_op = JSOpToMDefinition(op);
    3514           0 :     MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
    3515           0 :     ins->setSpecialization(specialization);
    3516             : 
    3517           0 :     current->add(ins);
    3518           0 :     current->push(ins);
    3519             : 
    3520           0 :     MOZ_ASSERT(!ins->isEffectful());
    3521           0 :     MOZ_TRY(maybeInsertResume());
    3522             : 
    3523           0 :     trackOptimizationSuccess();
    3524           0 :     *emitted = true;
    3525           0 :     return Ok();
    3526             : }
    3527             : 
    3528             : AbortReasonOr<Ok>
    3529          25 : IonBuilder::arithTrySharedStub(bool* emitted, JSOp op,
    3530             :                                MDefinition* left, MDefinition* right)
    3531             : {
    3532           0 :     MOZ_ASSERT(*emitted == false);
    3533          25 :     JSOp actualOp = JSOp(*pc);
    3534             : 
    3535             :     // Try to emit a shared stub cache.
    3536             : 
    3537           1 :     if (JitOptions.disableSharedStubs)
    3538           0 :         return Ok();
    3539             : 
    3540             :     // The actual jsop 'jsop_pos' is not supported yet.
    3541           0 :     if (actualOp == JSOP_POS)
    3542           4 :         return Ok();
    3543             : 
    3544             : 
    3545           0 :     MInstruction* stub = nullptr;
    3546          21 :     switch (actualOp) {
    3547             :       case JSOP_NEG:
    3548             :       case JSOP_BITNOT:
    3549           0 :         MOZ_ASSERT_IF(op == JSOP_MUL,
    3550             :                       left->maybeConstantValue() && left->maybeConstantValue()->toInt32() == -1);
    3551           0 :         MOZ_ASSERT_IF(op != JSOP_MUL, !left);
    3552           0 :         stub = MUnaryCache::New(alloc(), right);
    3553           0 :         break;
    3554             :       case JSOP_ADD:
    3555             :       case JSOP_SUB:
    3556             :       case JSOP_MUL:
    3557             :       case JSOP_DIV:
    3558             :       case JSOP_MOD:
    3559             :       case JSOP_POW:
    3560           0 :         stub = MBinarySharedStub::New(alloc(), left, right);
    3561          21 :         break;
    3562             :       default:
    3563           0 :         MOZ_CRASH("unsupported arith");
    3564             :     }
    3565             : 
    3566           0 :     current->add(stub);
    3567          21 :     current->push(stub);
    3568             : 
    3569             :     // Decrease type from 'any type' to 'empty type' when one of the operands
    3570             :     // is 'empty typed'.
    3571          21 :     maybeMarkEmpty(stub);
    3572             : 
    3573          63 :     MOZ_TRY(resumeAfter(stub));
    3574             : 
    3575           0 :     *emitted = true;
    3576          21 :     return Ok();
    3577             : }
    3578             : 
    3579             : AbortReasonOr<Ok>
    3580         336 : IonBuilder::jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right)
    3581             : {
    3582         336 :     bool emitted = false;
    3583             : 
    3584         336 :     startTrackingOptimizations();
    3585             : 
    3586           0 :     trackTypeInfo(TrackedTypeSite::Operand, left->type(), left->resultTypeSet());
    3587         672 :     trackTypeInfo(TrackedTypeSite::Operand, right->type(), right->resultTypeSet());
    3588             : 
    3589           0 :     if (!forceInlineCaches()) {
    3590           0 :         MOZ_TRY(binaryArithTryConcat(&emitted, op, left, right));
    3591           0 :         if (emitted)
    3592         166 :             return Ok();
    3593             : 
    3594           0 :         MOZ_TRY(binaryArithTrySpecialized(&emitted, op, left, right));
    3595           0 :         if (emitted)
    3596         145 :             return Ok();
    3597             : 
    3598           0 :         MOZ_TRY(binaryArithTrySpecializedOnBaselineInspector(&emitted, op, left, right));
    3599           0 :         if (emitted)
    3600           0 :             return Ok();
    3601             :     }
    3602             : 
    3603           0 :     MOZ_TRY(arithTrySharedStub(&emitted, op, left, right));
    3604           0 :     if (emitted)
    3605          21 :         return Ok();
    3606             : 
    3607             :     // Not possible to optimize. Do a slow vm call.
    3608           0 :     trackOptimizationAttempt(TrackedStrategy::BinaryArith_Call);
    3609           4 :     trackOptimizationSuccess();
    3610             : 
    3611           0 :     MDefinition::Opcode def_op = JSOpToMDefinition(op);
    3612           4 :     MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
    3613             : 
    3614             :     // Decrease type from 'any type' to 'empty type' when one of the operands
    3615             :     // is 'empty typed'.
    3616           4 :     maybeMarkEmpty(ins);
    3617             : 
    3618           0 :     current->add(ins);
    3619           0 :     current->push(ins);
    3620           8 :     MOZ_ASSERT(ins->isEffectful());
    3621             :     return resumeAfter(ins);
    3622             : }
    3623             : 
    3624             : AbortReasonOr<Ok>
    3625         332 : IonBuilder::jsop_binary_arith(JSOp op)
    3626             : {
    3627           0 :     MDefinition* right = current->pop();
    3628         332 :     MDefinition* left = current->pop();
    3629             : 
    3630         332 :     return jsop_binary_arith(op, left, right);
    3631             : }
    3632             : 
    3633             : 
    3634             : AbortReasonOr<Ok>
    3635           0 : IonBuilder::jsop_pow()
    3636             : {
    3637           0 :     MDefinition* exponent = current->pop();
    3638           0 :     MDefinition* base = current->pop();
    3639             : 
    3640           0 :     bool emitted = false;
    3641             : 
    3642           0 :     if (!forceInlineCaches()) {
    3643           0 :         MOZ_TRY(powTrySpecialized(&emitted, base, exponent, MIRType::Double));
    3644           0 :         if (emitted)
    3645           0 :             return Ok();
    3646             :     }
    3647             : 
    3648           0 :     MOZ_TRY(arithTrySharedStub(&emitted, JSOP_POW, base, exponent));
    3649           0 :     if (emitted)
    3650           0 :         return Ok();
    3651             : 
    3652             :     // For now, use MIRType::None as a safe cover-all. See bug 1188079.
    3653           0 :     MPow* pow = MPow::New(alloc(), base, exponent, MIRType::None);
    3654           0 :     current->add(pow);
    3655           0 :     current->push(pow);
    3656           0 :     MOZ_ASSERT(pow->isEffectful());
    3657             :     return resumeAfter(pow);
    3658             : }
    3659             : 
    3660             : AbortReasonOr<Ok>
    3661          85 : IonBuilder::jsop_pos()
    3662             : {
    3663         170 :     if (IsNumberType(current->peek(-1)->type())) {
    3664             :         // Already int32 or double. Set the operand as implicitly used so it
    3665             :         // doesn't get optimized out if it has no other uses, as we could bail
    3666             :         // out.
    3667           0 :         current->peek(-1)->setImplicitlyUsedUnchecked();
    3668          81 :         return Ok();
    3669             :     }
    3670             : 
    3671             :     // Compile +x as x * 1.
    3672           0 :     MDefinition* value = current->pop();
    3673           0 :     MConstant* one = MConstant::New(alloc(), Int32Value(1));
    3674           4 :     current->add(one);
    3675             : 
    3676           4 :     return jsop_binary_arith(JSOP_MUL, value, one);
    3677             : }
    3678             : 
    3679             : AbortReasonOr<Ok>
    3680           0 : IonBuilder::jsop_neg()
    3681             : {
    3682             :     // Since JSOP_NEG does not use a slot, we cannot push the MConstant.
    3683             :     // The MConstant is therefore passed to JSOP_MUL without slot traffic.
    3684           0 :     MConstant* negator = MConstant::New(alloc(), Int32Value(-1));
    3685           0 :     current->add(negator);
    3686             : 
    3687           0 :     MDefinition* right = current->pop();
    3688             : 
    3689           0 :     return jsop_binary_arith(JSOP_MUL, negator, right);
    3690             : }
    3691             : 
    3692             : AbortReasonOr<Ok>
    3693          32 : IonBuilder::jsop_tostring()
    3694             : {
    3695           0 :     if (current->peek(-1)->type() == MIRType::String)
    3696          14 :         return Ok();
    3697             : 
    3698           0 :     MDefinition* value = current->pop();
    3699           0 :     MToString* ins = MToString::New(alloc(), value);
    3700           0 :     current->add(ins);
    3701           0 :     current->push(ins);
    3702           0 :     MOZ_ASSERT(!ins->isEffectful());
    3703          18 :     return Ok();
    3704             : }
    3705             : 
    3706             : class AutoAccumulateReturns
    3707             : {
    3708             :     MIRGraph& graph_;
    3709             :     MIRGraphReturns* prev_;
    3710             : 
    3711             :   public:
    3712             :     AutoAccumulateReturns(MIRGraph& graph, MIRGraphReturns& returns)
    3713         291 :       : graph_(graph)
    3714             :     {
    3715           0 :         prev_ = graph_.returnAccumulator();
    3716         582 :         graph_.setReturnAccumulator(&returns);
    3717             :     }
    3718             :     ~AutoAccumulateReturns() {
    3719         582 :         graph_.setReturnAccumulator(prev_);
    3720             :     }
    3721             : };
    3722             : 
    3723             : IonBuilder::InliningResult
    3724         291 : IonBuilder::inlineScriptedCall(CallInfo& callInfo, JSFunction* target)
    3725             : {
    3726           0 :     MOZ_ASSERT(target->hasScript());
    3727         291 :     MOZ_ASSERT(IsIonInlinablePC(pc));
    3728             : 
    3729           0 :     MBasicBlock::BackupPoint backup(current);
    3730           1 :     if (!backup.init(alloc()))
    3731           0 :         return abort(AbortReason::Alloc);
    3732             : 
    3733         291 :     callInfo.setImplicitlyUsedUnchecked();
    3734             : 
    3735             :     // Create new |this| on the caller-side for inlined constructors.
    3736           0 :     if (callInfo.constructing()) {
    3737           0 :         MDefinition* thisDefn = createThis(target, callInfo.fun(), callInfo.getNewTarget());
    3738           1 :         if (!thisDefn)
    3739           0 :             return abort(AbortReason::Alloc);
    3740           4 :         callInfo.setThis(thisDefn);
    3741             :     }
    3742             : 
    3743             :     // Capture formals in the outer resume point.
    3744         873 :     MOZ_TRY(callInfo.pushCallStack(this, current));
    3745             : 
    3746             :     MResumePoint* outerResumePoint =
    3747           0 :         MResumePoint::New(alloc(), current, pc, MResumePoint::Outer);
    3748           1 :     if (!outerResumePoint)
    3749           0 :         return abort(AbortReason::Alloc);
    3750         291 :     current->setOuterResumePoint(outerResumePoint);
    3751             : 
    3752             :     // Pop formals again, except leave |fun| on stack for duration of call.
    3753           0 :     callInfo.popCallStack(current);
    3754         291 :     current->push(callInfo.fun());
    3755             : 
    3756           0 :     JSScript* calleeScript = target->nonLazyScript();
    3757         291 :     BaselineInspector inspector(calleeScript);
    3758             : 
    3759             :     // Improve type information of |this| when not set.
    3760           0 :     if (callInfo.constructing() &&
    3761           4 :         !callInfo.thisArg()->resultTypeSet())
    3762             :     {
    3763           0 :         StackTypeSet* types = TypeScript::ThisTypes(calleeScript);
    3764           0 :         if (types && !types->unknown()) {
    3765           0 :             TemporaryTypeSet* clonedTypes = types->clone(alloc_->lifoAlloc());
    3766           0 :             if (!clonedTypes)
    3767           0 :                 return abort(AbortReason::Alloc);
    3768           0 :             MTypeBarrier* barrier = MTypeBarrier::New(alloc(), callInfo.thisArg(), clonedTypes);
    3769           0 :             current->add(barrier);
    3770           0 :             if (barrier->type() == MIRType::Undefined)
    3771           0 :                 callInfo.setThis(constant(UndefinedValue()));
    3772           0 :             else if (barrier->type() == MIRType::Null)
    3773           0 :                 callInfo.setThis(constant(NullValue()));
    3774             :             else
    3775           0 :                 callInfo.setThis(barrier);
    3776             :         }
    3777             :     }
    3778             : 
    3779             :     // Start inlining.
    3780         582 :     LifoAlloc* lifoAlloc = alloc_->lifoAlloc();
    3781             :     InlineScriptTree* inlineScriptTree =
    3782           0 :         info().inlineScriptTree()->addCallee(alloc_, pc, calleeScript);
    3783           0 :     if (!inlineScriptTree)
    3784           0 :         return abort(AbortReason::Alloc);
    3785        1164 :     CompileInfo* info = lifoAlloc->new_<CompileInfo>(runtime, calleeScript, target,
    3786             :                                                      (jsbytecode*)nullptr,
    3787         582 :                                                      this->info().analysisMode(),
    3788             :                                                      /* needsArgsObj = */ false,
    3789           0 :                                                      inlineScriptTree);
    3790           1 :     if (!info)
    3791           0 :         return abort(AbortReason::Alloc);
    3792             : 
    3793           0 :     MIRGraphReturns returns(alloc());
    3794         873 :     AutoAccumulateReturns aar(graph(), returns);
    3795             : 
    3796             :     // Build the graph.
    3797           0 :     IonBuilder inlineBuilder(analysisContext, realm, options, &alloc(), &graph(), constraints(),
    3798           0 :                              &inspector, info, &optimizationInfo(), nullptr, inliningDepth_ + 1,
    3799           0 :                              loopDepth_);
    3800           0 :     AbortReasonOr<Ok> result = inlineBuilder.buildInline(this, outerResumePoint, callInfo);
    3801           0 :     if (result.isErr()) {
    3802           0 :         if (analysisContext && analysisContext->isExceptionPending()) {
    3803           0 :             JitSpew(JitSpew_IonAbort, "Inline builder raised exception.");
    3804           0 :             MOZ_ASSERT(result.unwrapErr() == AbortReason::Error);
    3805           0 :             return Err(result.unwrapErr());
    3806             :         }
    3807             : 
    3808             :         // Inlining the callee failed. Mark the callee as uninlineable only if
    3809             :         // the inlining was aborted for a non-exception reason.
    3810           0 :         switch (result.unwrapErr()) {
    3811             :           case AbortReason::Disable:
    3812           0 :             calleeScript->setUninlineable();
    3813           0 :             if (!JitOptions.disableInlineBacktracking) {
    3814           0 :                 current = backup.restore();
    3815           0 :                 if (!current)
    3816           0 :                     return abort(AbortReason::Alloc);
    3817           0 :                 return InliningStatus_NotInlined;
    3818             :             }
    3819           0 :             return abort(AbortReason::Inlining);
    3820             : 
    3821             :           case AbortReason::PreliminaryObjects: {
    3822           0 :             const ObjectGroupVector& groups = inlineBuilder.abortedPreliminaryGroups();
    3823           0 :             MOZ_ASSERT(!groups.empty());
    3824           0 :             for (size_t i = 0; i < groups.length(); i++)
    3825           0 :                 addAbortedPreliminaryGroup(groups[i]);
    3826           0 :             return Err(result.unwrapErr());
    3827             :           }
    3828             : 
    3829             :           case AbortReason::Alloc:
    3830             :           case AbortReason::Inlining:
    3831             :           case AbortReason::Error:
    3832           0 :             return Err(result.unwrapErr());
    3833             : 
    3834             :           case AbortReason::NoAbort:
    3835           0 :             MOZ_CRASH("Abort with AbortReason::NoAbort");
    3836             :             return abort(AbortReason::Error);
    3837             :         }
    3838             :     }
    3839             : 
    3840         291 :     if (returns.empty()) {
    3841             :         // Inlining of functions that have no exit is not supported.
    3842           0 :         calleeScript->setUninlineable();
    3843           0 :         if (!JitOptions.disableInlineBacktracking) {
    3844           0 :             current = backup.restore();
    3845           0 :             if (!current)
    3846           0 :                 return abort(AbortReason::Alloc);
    3847           0 :             return InliningStatus_NotInlined;
    3848             :         }
    3849           0 :         return abort(AbortReason::Inlining);
    3850             :     }
    3851             : 
    3852             :     // Create return block.
    3853         582 :     jsbytecode* postCall = GetNextPc(pc);
    3854             :     MBasicBlock* returnBlock;
    3855           0 :     MOZ_TRY_VAR(returnBlock, newBlock(current->stackDepth(), postCall));
    3856           0 :     graph().addBlock(returnBlock);
    3857         582 :     returnBlock->setCallerResumePoint(callerResumePoint_);
    3858             : 
    3859             :     // Inherit the slots from current and pop |fun|.
    3860           0 :     returnBlock->inheritSlots(current);
    3861         291 :     returnBlock->pop();
    3862             : 
    3863             :     // Accumulate return values.
    3864           0 :     MDefinition* retvalDefn = patchInlinedReturns(callInfo, returns, returnBlock);
    3865           0 :     if (!retvalDefn)
    3866           0 :         return abort(AbortReason::Alloc);
    3867         291 :     returnBlock->push(retvalDefn);
    3868             : 
    3869             :     // Initialize entry slots now that the stack has been fixed up.
    3870           0 :     if (!returnBlock->initEntrySlots(alloc()))
    3871           0 :         return abort(AbortReason::Alloc);
    3872             : 
    3873         873 :     MOZ_TRY(setCurrentAndSpecializePhis(returnBlock));
    3874             : 
    3875         291 :     return InliningStatus_Inlined;
    3876             : }
    3877             : 
    3878             : MDefinition*
    3879         661 : IonBuilder::patchInlinedReturn(CallInfo& callInfo, MBasicBlock* exit, MBasicBlock* bottom)
    3880             : {
    3881             :     // Replaces the MReturn in the exit block with an MGoto.
    3882           0 :     MDefinition* rdef = exit->lastIns()->toReturn()->input();
    3883         661 :     exit->discardLastIns();
    3884             : 
    3885             :     // Constructors must be patched by the caller to always return an object.
    3886           0 :     if (callInfo.constructing()) {
    3887           4 :         if (rdef->type() == MIRType::Value) {
    3888             :             // Unknown return: dynamically detect objects.
    3889           0 :             MReturnFromCtor* filter = MReturnFromCtor::New(alloc(), rdef, callInfo.thisArg());
    3890           0 :             exit->add(filter);
    3891           0 :             rdef = filter;
    3892           4 :         } else if (rdef->type() != MIRType::Object) {
    3893             :             // Known non-object return: force |this|.
    3894           4 :             rdef = callInfo.thisArg();
    3895             :         }
    3896         657 :     } else if (callInfo.isSetter()) {
    3897             :         // Setters return their argument, not whatever value is returned.
    3898           0 :         rdef = callInfo.getArg(0);
    3899             :     }
    3900             : 
    3901           0 :     if (!callInfo.isSetter())
    3902         661 :         rdef = specializeInlinedReturn(rdef, exit);
    3903             : 
    3904           0 :     MGoto* replacement = MGoto::New(alloc(), bottom);
    3905           0 :     exit->end(replacement);
    3906         661 :     if (!bottom->addPredecessorWithoutPhis(exit))
    3907             :         return nullptr;
    3908             : 
    3909         661 :     return rdef;
    3910             : }
    3911             : 
    3912             : MDefinition*
    3913         661 : IonBuilder::specializeInlinedReturn(MDefinition* rdef, MBasicBlock* exit)
    3914             : {
    3915             :     // Remove types from the return definition that weren't observed.
    3916        1322 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    3917             : 
    3918             :     // The observed typeset doesn't contain extra information.
    3919        1983 :     if (types->empty() || types->unknown())
    3920             :         return rdef;
    3921             : 
    3922             :     // Decide if specializing is needed using the result typeset if available,
    3923             :     // else use the result type.
    3924             : 
    3925         661 :     if (rdef->resultTypeSet()) {
    3926             :         // Don't specialize if return typeset is a subset of the
    3927             :         // observed typeset. The return typeset is already more specific.
    3928         552 :         if (rdef->resultTypeSet()->isSubset(types))
    3929             :             return rdef;
    3930             :     } else {
    3931         109 :         MIRType observedType = types->getKnownMIRType();
    3932             : 
    3933             :         // Don't specialize if type is MIRType::Float32 and TI reports
    3934             :         // MIRType::Double. Float is more specific than double.
    3935         109 :         if (observedType == MIRType::Double && rdef->type() == MIRType::Float32)
    3936             :             return rdef;
    3937             : 
    3938             :         // Don't specialize if types are inaccordance, except for MIRType::Value
    3939             :         // and MIRType::Object (when not unknown object), since the typeset
    3940             :         // contains more specific information.
    3941           0 :         if (observedType == rdef->type() &&
    3942           0 :             observedType != MIRType::Value &&
    3943          14 :             (observedType != MIRType::Object || types->unknownObject()))
    3944             :         {
    3945             :             return rdef;
    3946             :         }
    3947             :     }
    3948             : 
    3949          96 :     setCurrent(exit);
    3950             : 
    3951           0 :     MTypeBarrier* barrier = nullptr;
    3952           0 :     rdef = addTypeBarrier(rdef, types, BarrierKind::TypeSet, &barrier);
    3953           0 :     if (barrier)
    3954          48 :         barrier->setNotMovable();
    3955             : 
    3956             :     return rdef;
    3957             : }
    3958             : 
    3959             : MDefinition*
    3960         291 : IonBuilder::patchInlinedReturns(CallInfo& callInfo, MIRGraphReturns& returns, MBasicBlock* bottom)
    3961             : {
    3962             :     // Replaces MReturns with MGotos, returning the MDefinition
    3963             :     // representing the return value, or nullptr.
    3964         291 :     MOZ_ASSERT(returns.length() > 0);
    3965             : 
    3966           0 :     if (returns.length() == 1)
    3967         179 :         return patchInlinedReturn(callInfo, returns[0], bottom);
    3968             : 
    3969             :     // Accumulate multiple returns with a phi.
    3970           0 :     MPhi* phi = MPhi::New(alloc());
    3971         224 :     if (!phi->reserveLength(returns.length()))
    3972             :         return nullptr;
    3973             : 
    3974           0 :     for (size_t i = 0; i < returns.length(); i++) {
    3975           0 :         MDefinition* rdef = patchInlinedReturn(callInfo, returns[i], bottom);
    3976         482 :         if (!rdef)
    3977             :             return nullptr;
    3978         482 :         phi->addInput(rdef);
    3979             :     }
    3980             : 
    3981           0 :     bottom->addPhi(phi);
    3982         112 :     return phi;
    3983             : }
    3984             : 
    3985             : IonBuilder::InliningDecision
    3986        1588 : IonBuilder::makeInliningDecision(JSObject* targetArg, CallInfo& callInfo)
    3987             : {
    3988             :     // When there is no target, inlining is impossible.
    3989        1588 :     if (targetArg == nullptr) {
    3990             :         trackOptimizationOutcome(TrackedOutcome::CantInlineNoTarget);
    3991             :         return InliningDecision_DontInline;
    3992             :     }
    3993             : 
    3994             :     // Inlining non-function targets is handled by inlineNonFunctionCall().
    3995        1588 :     if (!targetArg->is<JSFunction>())
    3996             :         return InliningDecision_Inline;
    3997             : 
    3998        1588 :     JSFunction* target = &targetArg->as<JSFunction>();
    3999             : 
    4000             :     // Never inline during the arguments usage analysis.
    4001        1588 :     if (info().analysisMode() == Analysis_ArgumentsUsage)
    4002             :         return InliningDecision_DontInline;
    4003             : 
    4004             :     // Native functions provide their own detection in inlineNativeCall().
    4005        1514 :     if (target->isNative())
    4006             :         return InliningDecision_Inline;
    4007             : 
    4008             :     // Determine whether inlining is possible at callee site
    4009           0 :     InliningDecision decision = canInlineTarget(target, callInfo);
    4010         336 :     if (decision != InliningDecision_Inline)
    4011             :         return decision;
    4012             : 
    4013             :     // Heuristics!
    4014         312 :     JSScript* targetScript = target->nonLazyScript();
    4015             : 
    4016             :     // Callee must not be excessively large.
    4017             :     // This heuristic also applies to the callsite as a whole.
    4018           0 :     bool offThread = options.offThreadCompilationAvailable();
    4019           0 :     if (targetScript->length() > optimizationInfo().inlineMaxBytecodePerCallSite(offThread)) {
    4020           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineBigCallee);
    4021           0 :         return DontInline(targetScript, "Vetoed: callee excessively large");
    4022             :     }
    4023             : 
    4024             :     // Callee must have been called a few times to have somewhat stable
    4025             :     // type information, except for definite properties analysis,
    4026             :     // as the caller has not run yet.
    4027           0 :     if (targetScript->getWarmUpCount() < optimizationInfo().inliningWarmUpThreshold() &&
    4028           0 :         !targetScript->baselineScript()->ionCompiledOrInlined() &&
    4029          29 :         info().analysisMode() != Analysis_DefiniteProperties)
    4030             :     {
    4031           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNotHot);
    4032           0 :         JitSpew(JitSpew_Inlining, "Cannot inline %s:%u: callee is insufficiently hot.",
    4033           0 :                 targetScript->filename(), targetScript->lineno());
    4034          19 :         return InliningDecision_WarmUpCountTooLow;
    4035             :     }
    4036             : 
    4037             :     // Don't inline if the callee is known to inline a lot of code, to avoid
    4038             :     // huge MIR graphs.
    4039           0 :     uint32_t inlinedBytecodeLength = targetScript->baselineScript()->inlinedBytecodeLength();
    4040           0 :     if (inlinedBytecodeLength > optimizationInfo().inlineMaxCalleeInlinedBytecodeLength()) {
    4041           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineBigCalleeInlinedBytecodeLength);
    4042           0 :         return DontInline(targetScript, "Vetoed: callee inlinedBytecodeLength is too big");
    4043             :     }
    4044             : 
    4045         293 :     IonBuilder* outerBuilder = outermostBuilder();
    4046             : 
    4047             :     // Cap the total bytecode length we inline under a single script, to avoid
    4048             :     // excessive inlining in pathological cases.
    4049           0 :     size_t totalBytecodeLength = outerBuilder->inlinedBytecodeLength_ + targetScript->length();
    4050           0 :     if (totalBytecodeLength > optimizationInfo().inlineMaxTotalBytecodeLength()) {
    4051           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineExceededTotalBytecodeLength);
    4052           0 :         return DontInline(targetScript, "Vetoed: exceeding max total bytecode length");
    4053             :     }
    4054             : 
    4055             :     // Cap the inlining depth.
    4056             : 
    4057             :     uint32_t maxInlineDepth;
    4058           0 :     if (JitOptions.isSmallFunction(targetScript)) {
    4059         163 :         maxInlineDepth = optimizationInfo().smallFunctionMaxInlineDepth();
    4060             :     } else {
    4061         130 :         maxInlineDepth = optimizationInfo().maxInlineDepth();
    4062             : 
    4063             :         // Caller must not be excessively large.
    4064           0 :         if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength()) {
    4065           0 :             trackOptimizationOutcome(TrackedOutcome::CantInlineBigCaller);
    4066           0 :             return DontInline(targetScript, "Vetoed: caller excessively large");
    4067             :         }
    4068             :     }
    4069             : 
    4070           0 :     BaselineScript* outerBaseline = outermostBuilder()->script()->baselineScript();
    4071         293 :     if (inliningDepth_ >= maxInlineDepth) {
    4072             :         // We hit the depth limit and won't inline this function. Give the
    4073             :         // outermost script a max inlining depth of 0, so that it won't be
    4074             :         // inlined in other scripts. This heuristic is currently only used
    4075             :         // when we're inlining scripts with loops, see the comment below.
    4076           0 :         outerBaseline->setMaxInliningDepth(0);
    4077             : 
    4078           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth);
    4079           0 :         return DontInline(targetScript, "Vetoed: exceeding allowed inline depth");
    4080             :     }
    4081             : 
    4082             :     // Inlining functions with loops can be complicated. For instance, if we're
    4083             :     // close to the inlining depth limit and we inline the function f below, we
    4084             :     // can no longer inline the call to g:
    4085             :     //
    4086             :     //   function f() {
    4087             :     //      while (cond) {
    4088             :     //          g();
    4089             :     //      }
    4090             :     //   }
    4091             :     //
    4092             :     // If the loop has many iterations, it's more efficient to call f and inline
    4093             :     // g in f.
    4094             :     //
    4095             :     // To avoid this problem, we record a separate max inlining depth for each
    4096             :     // script, indicating at which depth we won't be able to inline all functions
    4097             :     // we inlined this time. This solves the issue above, because we will only
    4098             :     // inline f if it means we can also inline g.
    4099           0 :     if (targetScript->hasLoops() &&
    4100          92 :         inliningDepth_ >= targetScript->baselineScript()->maxInliningDepth())
    4101             :     {
    4102           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth);
    4103           0 :         return DontInline(targetScript, "Vetoed: exceeding allowed script inline depth");
    4104             :     }
    4105             : 
    4106             :     // Update the max depth at which we can inline the outer script.
    4107           0 :     MOZ_ASSERT(maxInlineDepth > inliningDepth_);
    4108           0 :     uint32_t scriptInlineDepth = maxInlineDepth - inliningDepth_ - 1;
    4109           0 :     if (scriptInlineDepth < outerBaseline->maxInliningDepth())
    4110          78 :         outerBaseline->setMaxInliningDepth(scriptInlineDepth);
    4111             : 
    4112             :     // End of heuristics, we will inline this function.
    4113             : 
    4114         293 :     outerBuilder->inlinedBytecodeLength_ += targetScript->length();
    4115             : 
    4116         293 :     return InliningDecision_Inline;
    4117             : }
    4118             : 
    4119             : AbortReasonOr<Ok>
    4120          11 : IonBuilder::selectInliningTargets(const InliningTargets& targets, CallInfo& callInfo,
    4121             :                                   BoolVector& choiceSet, uint32_t* numInlineable)
    4122             : {
    4123           0 :     *numInlineable = 0;
    4124          11 :     uint32_t totalSize = 0;
    4125             : 
    4126             :     // For each target, ask whether it may be inlined.
    4127           1 :     if (!choiceSet.reserve(targets.length()))
    4128           0 :         return abort(AbortReason::Alloc);
    4129             : 
    4130             :     // Don't inline polymorphic sites during the definite properties analysis.
    4131             :     // AddClearDefiniteFunctionUsesInScript depends on this for correctness.
    4132           0 :     if (info().analysisMode() == Analysis_DefiniteProperties && targets.length() > 1)
    4133           0 :         return Ok();
    4134             : 
    4135           0 :     for (size_t i = 0; i < targets.length(); i++) {
    4136          27 :         JSObject* target = targets[i].target;
    4137             : 
    4138           0 :         trackOptimizationAttempt(TrackedStrategy::Call_Inline);
    4139          27 :         trackTypeInfo(TrackedTypeSite::Call_Target, target);
    4140             : 
    4141             :         bool inlineable;
    4142           0 :         InliningDecision decision = makeInliningDecision(target, callInfo);
    4143          27 :         switch (decision) {
    4144             :           case InliningDecision_Error:
    4145           0 :             return abort(AbortReason::Error);
    4146             :           case InliningDecision_DontInline:
    4147             :           case InliningDecision_WarmUpCountTooLow:
    4148           0 :             inlineable = false;
    4149           3 :             break;
    4150             :           case InliningDecision_Inline:
    4151           0 :             inlineable = true;
    4152          24 :             break;
    4153             :           default:
    4154           0 :             MOZ_CRASH("Unhandled InliningDecision value!");
    4155             :         }
    4156             : 
    4157          27 :         if (target->is<JSFunction>()) {
    4158             :             // Enforce a maximum inlined bytecode limit at the callsite.
    4159           0 :             if (inlineable && target->as<JSFunction>().isInterpreted()) {
    4160           0 :                 totalSize += target->as<JSFunction>().nonLazyScript()->length();
    4161           0 :                 bool offThread = options.offThreadCompilationAvailable();
    4162           0 :                 if (totalSize > optimizationInfo().inlineMaxBytecodePerCallSite(offThread))
    4163           2 :                     inlineable = false;
    4164             :             }
    4165             :         } else {
    4166             :             // Non-function targets are not supported by polymorphic inlining.
    4167           0 :             inlineable = false;
    4168             :         }
    4169             : 
    4170           0 :         choiceSet.infallibleAppend(inlineable);
    4171           0 :         if (inlineable)
    4172          22 :             *numInlineable += 1;
    4173             :     }
    4174             : 
    4175             :     // If optimization tracking is turned on and one of the inlineable targets
    4176             :     // is a native, track the type info of the call. Most native inlinings
    4177             :     // depend on the types of the arguments and the return value.
    4178           1 :     if (isOptimizationTrackingEnabled()) {
    4179           0 :         for (size_t i = 0; i < targets.length(); i++) {
    4180           0 :             if (choiceSet[i] && targets[i].target->as<JSFunction>().isNative()) {
    4181             :                 trackTypeInfo(callInfo);
    4182             :                 break;
    4183             :             }
    4184             :         }
    4185             :     }
    4186             : 
    4187           0 :     MOZ_ASSERT(choiceSet.length() == targets.length());
    4188          11 :     return Ok();
    4189             : }
    4190             : 
    4191             : static bool
    4192           0 : CanInlineGetPropertyCache(MGetPropertyCache* cache, MDefinition* thisDef)
    4193             : {
    4194           0 :     if (cache->value()->type() != MIRType::Object)
    4195             :         return false;
    4196             : 
    4197           0 :     if (cache->value() != thisDef)
    4198             :         return false;
    4199             : 
    4200           0 :     InlinePropertyTable* table = cache->propTable();
    4201           0 :     if (!table)
    4202             :         return false;
    4203           0 :     if (table->numEntries() == 0)
    4204             :         return false;
    4205           0 :     return true;
    4206             : }
    4207             : 
    4208             : class WrapMGetPropertyCache
    4209             : {
    4210             :     MGetPropertyCache* cache_;
    4211             : 
    4212             :   private:
    4213           0 :     void discardPriorResumePoint() {
    4214        3261 :         if (!cache_)
    4215             :             return;
    4216             : 
    4217           0 :         InlinePropertyTable* propTable = cache_->propTable();
    4218          19 :         if (!propTable)
    4219             :             return;
    4220           0 :         MResumePoint* rp = propTable->takePriorResumePoint();
    4221           0 :         if (!rp)
    4222             :             return;
    4223           0 :         cache_->block()->discardPreAllocatedResumePoint(rp);
    4224             :     }
    4225             : 
    4226             :   public:
    4227             :     explicit WrapMGetPropertyCache(MGetPropertyCache* cache)
    4228        3261 :       : cache_(cache)
    4229             :     { }
    4230             : 
    4231             :     ~WrapMGetPropertyCache() {
    4232        3261 :         discardPriorResumePoint();
    4233             :     }
    4234             : 
    4235             :     MGetPropertyCache* get() {
    4236          11 :         return cache_;
    4237             :     }
    4238             :     MGetPropertyCache* operator->() {
    4239             :         return get();
    4240             :     }
    4241             : 
    4242             :     // This function returns the cache given to the constructor if the
    4243             :     // GetPropertyCache can be moved into the ObjectGroup fallback path.
    4244          19 :     MGetPropertyCache* moveableCache(bool hasTypeBarrier, MDefinition* thisDef) {
    4245             :         // If we have unhandled uses of the MGetPropertyCache, then we cannot
    4246             :         // move it to the ObjectGroup fallback path.
    4247           0 :         if (!hasTypeBarrier) {
    4248           8 :             if (cache_->hasUses())
    4249             :                 return nullptr;
    4250             :         } else {
    4251             :             // There is the TypeBarrier consumer, so we check that this is the
    4252             :             // only consumer.
    4253           0 :             MOZ_ASSERT(cache_->hasUses());
    4254          15 :             if (!cache_->hasOneUse())
    4255             :                 return nullptr;
    4256             :         }
    4257             : 
    4258             :         // If the this-object is not identical to the object of the
    4259             :         // MGetPropertyCache, then we cannot use the InlinePropertyTable, or if
    4260             :         // we do not yet have enough information from the ObjectGroup.
    4261           0 :         if (!CanInlineGetPropertyCache(cache_, thisDef))
    4262             :             return nullptr;
    4263             : 
    4264           0 :         MGetPropertyCache* ret = cache_;
    4265           0 :         cache_ = nullptr;
    4266           0 :         return ret;
    4267             :     }
    4268             : };
    4269             : 
    4270             : MGetPropertyCache*
    4271        1572 : IonBuilder::getInlineableGetPropertyCache(CallInfo& callInfo)
    4272             : {
    4273        1572 :     if (callInfo.constructing())
    4274             :         return nullptr;
    4275             : 
    4276           0 :     MDefinition* thisDef = callInfo.thisArg();
    4277        1553 :     if (thisDef->type() != MIRType::Object)
    4278             :         return nullptr;
    4279             : 
    4280           0 :     MDefinition* funcDef = callInfo.fun();
    4281         231 :     if (funcDef->type() != MIRType::Object)
    4282             :         return nullptr;
    4283             : 
    4284             :     // MGetPropertyCache with no uses may be optimized away.
    4285           0 :     if (funcDef->isGetPropertyCache()) {
    4286           0 :         WrapMGetPropertyCache cache(funcDef->toGetPropertyCache());
    4287           4 :         return cache.moveableCache(/* hasTypeBarrier = */ false, thisDef);
    4288             :     }
    4289             : 
    4290             :     // Optimize away the following common pattern:
    4291             :     // MTypeBarrier[MIRType::Object] <- MGetPropertyCache
    4292           0 :     if (funcDef->isTypeBarrier()) {
    4293           0 :         MTypeBarrier* barrier = funcDef->toTypeBarrier();
    4294          60 :         if (barrier->hasUses())
    4295             :             return nullptr;
    4296          30 :         if (barrier->type() != MIRType::Object)
    4297             :             return nullptr;
    4298          90 :         if (!barrier->input()->isGetPropertyCache())
    4299             :             return nullptr;
    4300             : 
    4301           0 :         WrapMGetPropertyCache cache(barrier->input()->toGetPropertyCache());
    4302          15 :         return cache.moveableCache(/* hasTypeBarrier = */ true, thisDef);
    4303             :     }
    4304             : 
    4305             :     return nullptr;
    4306             : }
    4307             : 
    4308             : IonBuilder::InliningResult
    4309        1469 : IonBuilder::inlineSingleCall(CallInfo& callInfo, JSObject* targetArg)
    4310             : {
    4311             :     InliningStatus status;
    4312           0 :     if (!targetArg->is<JSFunction>()) {
    4313           0 :         MOZ_TRY_VAR(status, inlineNonFunctionCall(callInfo, targetArg));
    4314           0 :         trackInlineSuccess(status);
    4315           0 :         return status;
    4316             :     }
    4317             : 
    4318           0 :     JSFunction* target = &targetArg->as<JSFunction>();
    4319           0 :     if (target->isNative()) {
    4320           0 :         MOZ_TRY_VAR(status, inlineNativeCall(callInfo, target));
    4321           0 :         trackInlineSuccess(status);
    4322        2356 :         return status;
    4323             :     }
    4324             : 
    4325             :     // Track success now, as inlining a scripted call makes a new return block
    4326             :     // which has a different pc than the current call pc.
    4327           0 :     trackInlineSuccess();
    4328         291 :     return inlineScriptedCall(callInfo, target);
    4329             : }
    4330             : 
    4331             : IonBuilder::InliningResult
    4332        2515 : IonBuilder::inlineCallsite(const InliningTargets& targets, CallInfo& callInfo)
    4333             : {
    4334           0 :     if (targets.empty()) {
    4335           0 :         trackOptimizationAttempt(TrackedStrategy::Call_Inline);
    4336           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNoTarget);
    4337         943 :         return InliningStatus_NotInlined;
    4338             :     }
    4339             : 
    4340             :     // Is the function provided by an MGetPropertyCache?
    4341             :     // If so, the cache may be movable to a fallback path, with a dispatch
    4342             :     // instruction guarding on the incoming ObjectGroup.
    4343           0 :     WrapMGetPropertyCache propCache(getInlineableGetPropertyCache(callInfo));
    4344        3144 :     keepFallbackFunctionGetter(propCache.get());
    4345             : 
    4346             :     // Inline single targets -- unless they derive from a cache, in which case
    4347             :     // avoiding the cache and guarding is still faster.
    4348           0 :     if (!propCache.get() && targets.length() == 1) {
    4349        1561 :         JSObject* target = targets[0].target;
    4350             : 
    4351           0 :         trackOptimizationAttempt(TrackedStrategy::Call_Inline);
    4352        1561 :         trackTypeInfo(TrackedTypeSite::Call_Target, target);
    4353             : 
    4354           0 :         InliningDecision decision = makeInliningDecision(target, callInfo);
    4355        1561 :         switch (decision) {
    4356             :           case InliningDecision_Error:
    4357           0 :             return abort(AbortReason::Error);
    4358             :           case InliningDecision_DontInline:
    4359          96 :             return InliningStatus_NotInlined;
    4360             :           case InliningDecision_WarmUpCountTooLow:
    4361          18 :             return InliningStatus_WarmUpCountTooLow;
    4362             :           case InliningDecision_Inline:
    4363             :             break;
    4364             :         }
    4365             : 
    4366             :         // Inlining will elminate uses of the original callee, but it needs to
    4367             :         // be preserved in phis if we bail out.  Mark the old callee definition as
    4368             :         // implicitly used to ensure this happens.
    4369        2894 :         callInfo.fun()->setImplicitlyUsedUnchecked();
    4370             : 
    4371             :         // If the callee is not going to be a lambda (which may vary across
    4372             :         // different invocations), then the callee definition can be replaced by a
    4373             :         // constant.
    4374        1447 :         if (target->isSingleton()) {
    4375             :             // Replace the function with an MConstant.
    4376           0 :             MConstant* constFun = constant(ObjectValue(*target));
    4377           0 :             if (callInfo.constructing() && callInfo.getNewTarget() == callInfo.fun())
    4378           0 :                 callInfo.setNewTarget(constFun);
    4379        1405 :             callInfo.setFun(constFun);
    4380             :         }
    4381             : 
    4382        1447 :         return inlineSingleCall(callInfo, target);
    4383             :     }
    4384             : 
    4385             :     // Choose a subset of the targets for polymorphic inlining.
    4386          33 :     BoolVector choiceSet(alloc());
    4387             :     uint32_t numInlined;
    4388           0 :     MOZ_TRY(selectInliningTargets(targets, callInfo, choiceSet, &numInlined));
    4389           0 :     if (numInlined == 0)
    4390           0 :         return InliningStatus_NotInlined;
    4391             : 
    4392             :     // Perform a polymorphic dispatch.
    4393          33 :     MOZ_TRY(inlineCalls(callInfo, targets, choiceSet, propCache.get()));
    4394             : 
    4395          11 :     return InliningStatus_Inlined;
    4396             : }
    4397             : 
    4398             : AbortReasonOr<Ok>
    4399           3 : IonBuilder::inlineGenericFallback(const Maybe<CallTargets>& targets, CallInfo& callInfo,
    4400             :                                   MBasicBlock* dispatchBlock)
    4401             : {
    4402             :     // Generate a new block with all arguments on-stack.
    4403             :     MBasicBlock* fallbackBlock;
    4404           0 :     MOZ_TRY_VAR(fallbackBlock, newBlock(dispatchBlock, pc));
    4405           3 :     graph().addBlock(fallbackBlock);
    4406             : 
    4407             :     // Create a new CallInfo to track modified state within this block.
    4408           0 :     CallInfo fallbackInfo(alloc(), pc, callInfo.constructing(), callInfo.ignoresReturnValue());
    4409           1 :     if (!fallbackInfo.init(callInfo))
    4410           0 :         return abort(AbortReason::Alloc);
    4411           6 :     fallbackInfo.popCallStack(fallbackBlock);
    4412             : 
    4413             :     // Generate an MCall, which uses stateful |current|.
    4414           0 :     MOZ_TRY(setCurrentAndSpecializePhis(fallbackBlock));
    4415           9 :     MOZ_TRY(makeCall(targets, fallbackInfo));
    4416             : 
    4417             :     // Pass return block to caller as |current|.
    4418           3 :     return Ok();
    4419             : }
    4420             : 
    4421             : AbortReasonOr<Ok>
    4422           0 : IonBuilder::inlineObjectGroupFallback(const Maybe<CallTargets>& targets,
    4423             :                                       CallInfo& callInfo, MBasicBlock* dispatchBlock,
    4424             :                                       MObjectGroupDispatch* dispatch, MGetPropertyCache* cache,
    4425             :                                       MBasicBlock** fallbackTarget)
    4426             : {
    4427             :     // Getting here implies the following:
    4428             :     // 1. The call function is an MGetPropertyCache, or an MGetPropertyCache
    4429             :     //    followed by an MTypeBarrier.
    4430           0 :     MOZ_ASSERT(callInfo.fun()->isGetPropertyCache() || callInfo.fun()->isTypeBarrier());
    4431             : 
    4432             :     // 2. The MGetPropertyCache has inlineable cases by guarding on the ObjectGroup.
    4433           0 :     MOZ_ASSERT(dispatch->numCases() > 0);
    4434             : 
    4435             :     // 3. The MGetPropertyCache (and, if applicable, MTypeBarrier) only
    4436             :     //    have at most a single use.
    4437           0 :     MOZ_ASSERT_IF(callInfo.fun()->isGetPropertyCache(), !cache->hasUses());
    4438           0 :     MOZ_ASSERT_IF(callInfo.fun()->isTypeBarrier(), cache->hasOneUse());
    4439             : 
    4440             :     // This means that no resume points yet capture the MGetPropertyCache,
    4441             :     // so everything from the MGetPropertyCache up until the call is movable.
    4442             :     // We now move the MGetPropertyCache and friends into a fallback path.
    4443           0 :     MOZ_ASSERT(cache->idempotent());
    4444             : 
    4445             :     // Create a new CallInfo to track modified state within the fallback path.
    4446           0 :     CallInfo fallbackInfo(alloc(), pc, callInfo.constructing(), callInfo.ignoresReturnValue());
    4447           0 :     if (!fallbackInfo.init(callInfo))
    4448           0 :         return abort(AbortReason::Alloc);
    4449             : 
    4450             :     // Capture stack prior to the call operation. This captures the function.
    4451             :     MResumePoint* preCallResumePoint =
    4452           0 :         MResumePoint::New(alloc(), dispatchBlock, pc, MResumePoint::ResumeAt);
    4453           0 :     if (!preCallResumePoint)
    4454           0 :         return abort(AbortReason::Alloc);
    4455             : 
    4456           0 :     DebugOnly<size_t> preCallFuncIndex = preCallResumePoint->stackDepth() - callInfo.numFormals();
    4457           0 :     MOZ_ASSERT(preCallResumePoint->getOperand(preCallFuncIndex) == fallbackInfo.fun());
    4458             : 
    4459             :     // In the dispatch block, replace the function's slot entry with Undefined.
    4460           0 :     MConstant* undefined = MConstant::New(alloc(), UndefinedValue());
    4461           0 :     dispatchBlock->add(undefined);
    4462           0 :     dispatchBlock->rewriteAtDepth(-int(callInfo.numFormals()), undefined);
    4463             : 
    4464             :     // Construct a block that does nothing but remove formals from the stack.
    4465             :     // This is effectively changing the entry resume point of the later fallback block.
    4466             :     MBasicBlock* prepBlock;
    4467           0 :     MOZ_TRY_VAR(prepBlock, newBlock(dispatchBlock, pc));
    4468           0 :     graph().addBlock(prepBlock);
    4469           0 :     fallbackInfo.popCallStack(prepBlock);
    4470             : 
    4471             :     // Construct a block into which the MGetPropertyCache can be moved.
    4472             :     // This is subtle: the pc and resume point are those of the MGetPropertyCache!
    4473           0 :     InlinePropertyTable* propTable = cache->propTable();
    4474           0 :     MResumePoint* priorResumePoint = propTable->takePriorResumePoint();
    4475           0 :     MOZ_ASSERT(propTable->pc() != nullptr);
    4476           0 :     MOZ_ASSERT(priorResumePoint != nullptr);
    4477             :     MBasicBlock* getPropBlock;
    4478           0 :     MOZ_TRY_VAR(getPropBlock, newBlock(prepBlock, propTable->pc(), priorResumePoint));
    4479           0 :     graph().addBlock(getPropBlock);
    4480             : 
    4481           0 :     prepBlock->end(MGoto::New(alloc(), getPropBlock));
    4482             : 
    4483             :     // Since the getPropBlock inherited the stack from right before the MGetPropertyCache,
    4484             :     // the target of the MGetPropertyCache is still on the stack.
    4485           0 :     DebugOnly<MDefinition*> checkObject = getPropBlock->pop();
    4486           0 :     MOZ_ASSERT(checkObject == cache->value());
    4487             : 
    4488             :     // Move the MGetPropertyCache and friends into the getPropBlock.
    4489           0 :     if (fallbackInfo.fun()->isGetPropertyCache()) {
    4490           0 :         MOZ_ASSERT(fallbackInfo.fun()->toGetPropertyCache() == cache);
    4491           0 :         getPropBlock->addFromElsewhere(cache);
    4492           0 :         getPropBlock->push(cache);
    4493             :     } else {
    4494           0 :         MTypeBarrier* barrier = callInfo.fun()->toTypeBarrier();
    4495           0 :         MOZ_ASSERT(barrier->type() == MIRType::Object);
    4496           0 :         MOZ_ASSERT(barrier->input()->isGetPropertyCache());
    4497           0 :         MOZ_ASSERT(barrier->input()->toGetPropertyCache() == cache);
    4498             : 
    4499           0 :         getPropBlock->addFromElsewhere(cache);
    4500           0 :         getPropBlock->addFromElsewhere(barrier);
    4501           0 :         getPropBlock->push(barrier);
    4502             :     }
    4503             : 
    4504             :     // Construct an end block with the correct resume point.
    4505             :     MBasicBlock* preCallBlock;
    4506           0 :     MOZ_TRY_VAR(preCallBlock, newBlock(getPropBlock, pc, preCallResumePoint));
    4507           0 :     graph().addBlock(preCallBlock);
    4508           0 :     getPropBlock->end(MGoto::New(alloc(), preCallBlock));
    4509             : 
    4510             :     // Now inline the MCallGeneric, using preCallBlock as the dispatch point.
    4511           0 :     MOZ_TRY(inlineGenericFallback(targets, fallbackInfo, preCallBlock));
    4512             : 
    4513             :     // inlineGenericFallback() set the return block as |current|.
    4514           0 :     preCallBlock->end(MGoto::New(alloc(), current));
    4515           0 :     *fallbackTarget = prepBlock;
    4516           0 :     return Ok();
    4517             : }
    4518             : 
    4519             : AbortReasonOr<Ok>
    4520          11 : IonBuilder::inlineCalls(CallInfo& callInfo, const InliningTargets& targets, BoolVector& choiceSet,
    4521             :                         MGetPropertyCache* maybeCache)
    4522             : {
    4523             :     // Only handle polymorphic inlining.
    4524           0 :     MOZ_ASSERT(IsIonInlinablePC(pc));
    4525           0 :     MOZ_ASSERT(choiceSet.length() == targets.length());
    4526           0 :     MOZ_ASSERT_IF(!maybeCache, targets.length() >= 2);
    4527           0 :     MOZ_ASSERT_IF(maybeCache, targets.length() >= 1);
    4528          11 :     MOZ_ASSERT_IF(maybeCache, maybeCache->value()->type() == MIRType::Object);
    4529             : 
    4530           0 :     MBasicBlock* dispatchBlock = current;
    4531           0 :     callInfo.setImplicitlyUsedUnchecked();
    4532          33 :     MOZ_TRY(callInfo.pushCallStack(this, dispatchBlock));
    4533             : 
    4534             :     // Patch any InlinePropertyTable to only contain functions that are
    4535             :     // inlineable. The InlinePropertyTable will also be patched at the end to
    4536             :     // exclude native functions that vetoed inlining.
    4537           0 :     if (maybeCache) {
    4538           0 :         InlinePropertyTable* propTable = maybeCache->propTable();
    4539           0 :         propTable->trimToTargets(targets);
    4540           0 :         if (propTable->numEntries() == 0)
    4541           0 :             maybeCache = nullptr;
    4542             :     }
    4543             : 
    4544             :     // Generate a dispatch based on guard kind.
    4545             :     MDispatchInstruction* dispatch;
    4546           0 :     if (maybeCache) {
    4547           0 :         dispatch = MObjectGroupDispatch::New(alloc(), maybeCache->value(), maybeCache->propTable());
    4548           0 :         callInfo.fun()->setImplicitlyUsedUnchecked();
    4549             :     } else {
    4550          11 :         dispatch = MFunctionDispatch::New(alloc(), callInfo.fun());
    4551             :     }
    4552             : 
    4553           0 :     MOZ_ASSERT(dispatchBlock->stackDepth() >= callInfo.numFormals());
    4554          22 :     uint32_t stackDepth = dispatchBlock->stackDepth() - callInfo.numFormals() + 1;
    4555             : 
    4556             :     // Generate a return block to host the rval-collecting MPhi.
    4557          22 :     jsbytecode* postCall = GetNextPc(pc);
    4558             :     MBasicBlock* returnBlock;
    4559           0 :     MOZ_TRY_VAR(returnBlock, newBlock(stackDepth, postCall));
    4560           0 :     graph().addBlock(returnBlock);
    4561          22 :     returnBlock->setCallerResumePoint(callerResumePoint_);
    4562             : 
    4563             :     // Set up stack, used to manually create a post-call resume point.
    4564           0 :     returnBlock->inheritSlots(dispatchBlock);
    4565          22 :     callInfo.popCallStack(returnBlock);
    4566             : 
    4567           0 :     MPhi* retPhi = MPhi::New(alloc());
    4568           0 :     returnBlock->addPhi(retPhi);
    4569          11 :     returnBlock->push(retPhi);
    4570             : 
    4571             :     // Create a resume point from current stack state.
    4572           1 :     if (!returnBlock->initEntrySlots(alloc()))
    4573           0 :         return abort(AbortReason::Alloc);
    4574             : 
    4575             :     // Reserve the capacity for the phi.
    4576             :     // Note: this is an upperbound. Unreachable targets and uninlineable natives are also counted.
    4577             :     uint32_t count = 1; // Possible fallback block.
    4578           0 :     for (uint32_t i = 0; i < targets.length(); i++) {
    4579           0 :         if (choiceSet[i])
    4580          22 :             count++;
    4581             :     }
    4582           1 :     if (!retPhi->reserveLength(count))
    4583           0 :         return abort(AbortReason::Alloc);
    4584             : 
    4585             :     // Inline each of the inlineable targets.
    4586          65 :     for (uint32_t i = 0; i < targets.length(); i++) {
    4587             :         // Target must be inlineable.
    4588           0 :         if (!choiceSet[i])
    4589           5 :             continue;
    4590             : 
    4591             :         // Even though we made one round of inline decisions already, we may
    4592             :         // be amending them below.
    4593          22 :         amendOptimizationAttempt(i);
    4594             : 
    4595             :         // Target must be reachable by the MDispatchInstruction.
    4596           0 :         JSFunction* target = &targets[i].target->as<JSFunction>();
    4597           0 :         if (maybeCache && !maybeCache->propTable()->hasFunction(target)) {
    4598           0 :             choiceSet[i] = false;
    4599             :             trackOptimizationOutcome(TrackedOutcome::CantInlineNotInDispatch);
    4600             :             continue;
    4601             :         }
    4602             : 
    4603             :         MBasicBlock* inlineBlock;
    4604           0 :         MOZ_TRY_VAR(inlineBlock, newBlock(dispatchBlock, pc));
    4605          22 :         graph().addBlock(inlineBlock);
    4606             : 
    4607             :         // Create a function MConstant to use in the entry ResumePoint. If we
    4608             :         // can't use a constant, add a no-op MPolyInlineGuard, to prevent
    4609             :         // hoisting env chain gets above the dispatch instruction.
    4610             :         MInstruction* funcDef;
    4611           0 :         if (target->isSingleton())
    4612          44 :             funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints());
    4613             :         else
    4614           0 :             funcDef = MPolyInlineGuard::New(alloc(), callInfo.fun());
    4615             : 
    4616           0 :         funcDef->setImplicitlyUsedUnchecked();
    4617          22 :         dispatchBlock->add(funcDef);
    4618             : 
    4619             :         // Use the inlined callee in the inline resume point and on stack.
    4620           0 :         int funIndex = inlineBlock->entryResumePoint()->stackDepth() - callInfo.numFormals();
    4621           0 :         inlineBlock->entryResumePoint()->replaceOperand(funIndex, funcDef);
    4622          44 :         inlineBlock->rewriteSlot(funIndex, funcDef);
    4623             : 
    4624             :         // Create a new CallInfo to track modified state within the inline block.
    4625           0 :         CallInfo inlineInfo(alloc(), pc, callInfo.constructing(), callInfo.ignoresReturnValue());
    4626           1 :         if (!inlineInfo.init(callInfo))
    4627           0 :             return abort(AbortReason::Alloc);
    4628           0 :         inlineInfo.popCallStack(inlineBlock);
    4629          44 :         inlineInfo.setFun(funcDef);
    4630             : 
    4631          22 :         if (maybeCache) {
    4632             :             // Assign the 'this' value a TypeSet specialized to the groups that
    4633             :             // can generate this inlining target.
    4634           0 :             MOZ_ASSERT(callInfo.thisArg() == maybeCache->value());
    4635             :             TemporaryTypeSet* thisTypes =
    4636           0 :                 maybeCache->propTable()->buildTypeSetForFunction(alloc(), target);
    4637           0 :             if (!thisTypes)
    4638           0 :                 return abort(AbortReason::Alloc);
    4639             : 
    4640           0 :             MFilterTypeSet* filter = MFilterTypeSet::New(alloc(), inlineInfo.thisArg(), thisTypes);
    4641           0 :             inlineBlock->add(filter);
    4642           0 :             inlineInfo.setThis(filter);
    4643             :         }
    4644             : 
    4645             :         // Inline the call into the inlineBlock.
    4646          66 :         MOZ_TRY(setCurrentAndSpecializePhis(inlineBlock));
    4647             :         InliningStatus status;
    4648          44 :         MOZ_TRY_VAR(status, inlineSingleCall(inlineInfo, target));
    4649             : 
    4650             :         // Natives may veto inlining.
    4651           0 :         if (status == InliningStatus_NotInlined) {
    4652           0 :             MOZ_ASSERT(current == inlineBlock);
    4653           0 :             graph().removeBlock(inlineBlock);
    4654           0 :             choiceSet[i] = false;
    4655             :             continue;
    4656             :         }
    4657             : 
    4658             :         // inlineSingleCall() changed |current| to the inline return block.
    4659           0 :         MBasicBlock* inlineReturnBlock = current;
    4660          44 :         setCurrent(dispatchBlock);
    4661             : 
    4662             :         // Connect the inline path to the returnBlock.
    4663           1 :         if (!dispatch->addCase(target, targets[i].group, inlineBlock))
    4664           0 :             return abort(AbortReason::Alloc);
    4665             : 
    4666           0 :         MDefinition* retVal = inlineReturnBlock->peek(-1);
    4667           0 :         retPhi->addInput(retVal);
    4668           0 :         inlineReturnBlock->end(MGoto::New(alloc(), returnBlock));
    4669           1 :         if (!returnBlock->addPredecessorWithoutPhis(inlineReturnBlock))
    4670           0 :             return abort(AbortReason::Alloc);
    4671             :     }
    4672             : 
    4673             :     // Patch the InlinePropertyTable to not dispatch to vetoed paths.
    4674             :     bool useFallback;
    4675           0 :     if (maybeCache) {
    4676           0 :         InlinePropertyTable* propTable = maybeCache->propTable();
    4677           0 :         propTable->trimTo(targets, choiceSet);
    4678             : 
    4679           0 :         if (propTable->numEntries() == 0 || !propTable->hasPriorResumePoint()) {
    4680             :             // Output a generic fallback path.
    4681           0 :             MOZ_ASSERT_IF(propTable->numEntries() == 0, dispatch->numCases() == 0);
    4682             :             maybeCache = nullptr;
    4683             :             useFallback = true;
    4684             :         } else {
    4685             :             // We need a fallback path if the ObjectGroup dispatch does not
    4686             :             // handle all incoming objects.
    4687           0 :             useFallback = false;
    4688           0 :             TemporaryTypeSet* objectTypes = maybeCache->value()->resultTypeSet();
    4689           0 :             for (uint32_t i = 0; i < objectTypes->getObjectCount(); i++) {
    4690           0 :                 TypeSet::ObjectKey* obj = objectTypes->getObject(i);
    4691           0 :                 if (!obj)
    4692             :                     continue;
    4693             : 
    4694           0 :                 if (!obj->isGroup()) {
    4695             :                     useFallback = true;
    4696             :                     break;
    4697             :                 }
    4698             : 
    4699           0 :                 if (!propTable->hasObjectGroup(obj->group())) {
    4700             :                     useFallback = true;
    4701             :                     break;
    4702             :                 }
    4703             :             }
    4704             : 
    4705           0 :             if (!useFallback) {
    4706             :                 // The object group dispatch handles all possible incoming
    4707             :                 // objects, so the cache and barrier will not be reached and
    4708             :                 // can be eliminated.
    4709           0 :                 if (callInfo.fun()->isGetPropertyCache()) {
    4710           0 :                     MOZ_ASSERT(callInfo.fun() == maybeCache);
    4711             :                 } else {
    4712           0 :                     MTypeBarrier* barrier = callInfo.fun()->toTypeBarrier();
    4713           0 :                     MOZ_ASSERT(!barrier->hasUses());
    4714           0 :                     MOZ_ASSERT(barrier->type() == MIRType::Object);
    4715           0 :                     MOZ_ASSERT(barrier->input()->isGetPropertyCache());
    4716           0 :                     MOZ_ASSERT(barrier->input()->toGetPropertyCache() == maybeCache);
    4717           0 :                     barrier->block()->discard(barrier);
    4718             :                 }
    4719             : 
    4720           0 :                 MOZ_ASSERT(!maybeCache->hasUses());
    4721           0 :                 maybeCache->block()->discard(maybeCache);
    4722             :             }
    4723             :         }
    4724             :     } else {
    4725          11 :         useFallback = dispatch->numCases() < targets.length();
    4726             :     }
    4727             : 
    4728             :     // If necessary, generate a fallback path.
    4729          11 :     if (useFallback) {
    4730             :         // Annotate the fallback call with the target information.
    4731           0 :         Maybe<CallTargets> remainingTargets;
    4732           0 :         remainingTargets.emplace(alloc());
    4733           0 :         for (uint32_t i = 0; i < targets.length(); i++) {
    4734          10 :             if (!maybeCache && choiceSet[i])
    4735             :                 continue;
    4736             : 
    4737           0 :             JSObject* target = targets[i].target;
    4738           0 :             if (!target->is<JSFunction>()) {
    4739           0 :                 remainingTargets = Nothing();
    4740           0 :                 break;
    4741             :             }
    4742           1 :             if (!remainingTargets->append(&target->as<JSFunction>()))
    4743           0 :                 return abort(AbortReason::Alloc);
    4744             :         }
    4745             : 
    4746             :         // Generate fallback blocks, and set |current| to the fallback return block.
    4747           3 :         if (maybeCache) {
    4748             :             MBasicBlock* fallbackTarget;
    4749           0 :             MOZ_TRY(inlineObjectGroupFallback(remainingTargets, callInfo, dispatchBlock,
    4750             :                                               dispatch->toObjectGroupDispatch(),
    4751             :                                               maybeCache, &fallbackTarget));
    4752           0 :             dispatch->addFallback(fallbackTarget);
    4753             :         } else {
    4754           0 :             MOZ_TRY(inlineGenericFallback(remainingTargets, callInfo, dispatchBlock));
    4755           3 :             dispatch->addFallback(current);
    4756             :         }
    4757             : 
    4758           3 :         MBasicBlock* fallbackReturnBlock = current;
    4759             : 
    4760             :         // Connect fallback case to return infrastructure.
    4761           0 :         MDefinition* retVal = fallbackReturnBlock->peek(-1);
    4762           0 :         retPhi->addInput(retVal);
    4763           0 :         fallbackReturnBlock->end(MGoto::New(alloc(), returnBlock));
    4764           1 :         if (!returnBlock->addPredecessorWithoutPhis(fallbackReturnBlock))
    4765           0 :             return abort(AbortReason::Alloc);
    4766             :     }
    4767             : 
    4768             :     // Finally add the dispatch instruction.
    4769             :     // This must be done at the end so that add() may be called above.
    4770          11 :     dispatchBlock->end(dispatch);
    4771             : 
    4772             :     // Check the depth change: +1 for retval
    4773          22 :     MOZ_ASSERT(returnBlock->stackDepth() == dispatchBlock->stackDepth() - callInfo.numFormals() + 1);
    4774             : 
    4775           0 :     graph().moveBlockToEnd(returnBlock);
    4776          11 :     return setCurrentAndSpecializePhis(returnBlock);
    4777             : }
    4778             : 
    4779             : MInstruction*
    4780           0 : IonBuilder::createNamedLambdaObject(MDefinition* callee, MDefinition* env)
    4781             : {
    4782             :     // Get a template CallObject that we'll use to generate inline object
    4783             :     // creation.
    4784           0 :     LexicalEnvironmentObject* templateObj = inspector->templateNamedLambdaObject();
    4785             : 
    4786             :     // One field is added to the function to handle its name.  This cannot be a
    4787             :     // dynamic slot because there is still plenty of room on the NamedLambda object.
    4788           0 :     MOZ_ASSERT(!templateObj->hasDynamicSlots());
    4789             : 
    4790             :     // Allocate the actual object. It is important that no intervening
    4791             :     // instructions could potentially bailout, thus leaking the dynamic slots
    4792             :     // pointer.
    4793           0 :     MInstruction* declEnvObj = MNewNamedLambdaObject::New(alloc(), templateObj);
    4794           0 :     current->add(declEnvObj);
    4795             : 
    4796             :     // Initialize the object's reserved slots. No post barrier is needed here:
    4797             :     // the object will be allocated in the nursery if possible, and if the
    4798             :     // tenured heap is used instead, a minor collection will have been performed
    4799             :     // that moved env/callee to the tenured heap.
    4800           0 :     current->add(MStoreFixedSlot::New(alloc(), declEnvObj,
    4801           0 :                                       NamedLambdaObject::enclosingEnvironmentSlot(), env));
    4802           0 :     current->add(MStoreFixedSlot::New(alloc(), declEnvObj,
    4803           0 :                                       NamedLambdaObject::lambdaSlot(), callee));
    4804             : 
    4805           0 :     return declEnvObj;
    4806             : }
    4807             : 
    4808             : AbortReasonOr<MInstruction*>
    4809          15 : IonBuilder::createCallObject(MDefinition* callee, MDefinition* env)
    4810             : {
    4811             :     // Get a template CallObject that we'll use to generate inline object
    4812             :     // creation.
    4813           0 :     CallObject* templateObj = inspector->templateCallObject();
    4814           0 :     MConstant* templateCst = MConstant::NewConstraintlessObject(alloc(), templateObj);
    4815          15 :     current->add(templateCst);
    4816             : 
    4817             :     // Allocate the object. Run-once scripts need a singleton type, so always do
    4818             :     // a VM call in such cases.
    4819             :     MNewCallObjectBase* callObj;
    4820           0 :     if (script()->treatAsRunOnce() || templateObj->isSingleton())
    4821           0 :         callObj = MNewSingletonCallObject::New(alloc(), templateCst);
    4822             :     else
    4823           0 :         callObj = MNewCallObject::New(alloc(), templateCst);
    4824          15 :     current->add(callObj);
    4825             : 
    4826             :     // Initialize the object's reserved slots. No post barrier is needed here,
    4827             :     // for the same reason as in createNamedLambdaObject.
    4828           0 :     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::enclosingEnvironmentSlot(), env));
    4829          15 :     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::calleeSlot(), callee));
    4830             : 
    4831             :     //if (!script()->functionHasParameterExprs()) {
    4832             : 
    4833             :     // Copy closed-over argument slots if there aren't parameter expressions.
    4834           0 :     MSlots* slots = nullptr;
    4835           0 :     for (PositionalFormalParameterIter fi(script()); fi; fi++) {
    4836          42 :         if (!fi.closedOver())
    4837             :             continue;
    4838             : 
    4839           1 :         if (!alloc().ensureBallast())
    4840           0 :             return abort(AbortReason::Alloc);
    4841             : 
    4842           0 :         unsigned slot = fi.location().slot();
    4843           0 :         unsigned formal = fi.argumentSlot();
    4844           4 :         unsigned numFixedSlots = templateObj->numFixedSlots();
    4845             :         MDefinition* param;
    4846           0 :         if (script()->functionHasParameterExprs())
    4847           0 :             param = constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
    4848             :         else
    4849           0 :             param = current->getSlot(info().argSlotUnchecked(formal));
    4850           0 :         if (slot >= numFixedSlots) {
    4851           0 :             if (!slots) {
    4852           0 :                 slots = MSlots::New(alloc(), callObj);
    4853           0 :                 current->add(slots);
    4854             :             }
    4855           0 :             current->add(MStoreSlot::New(alloc(), slots, slot - numFixedSlots, param));
    4856             :         } else {
    4857           2 :             current->add(MStoreFixedSlot::New(alloc(), callObj, slot, param));
    4858             :         }
    4859             :     }
    4860             : 
    4861          15 :     return AbortReasonOr<MInstruction*>(callObj);
    4862             : }
    4863             : 
    4864             : MDefinition*
    4865           0 : IonBuilder::createThisScripted(MDefinition* callee, MDefinition* newTarget)
    4866             : {
    4867             :     // Get callee.prototype.
    4868             :     //
    4869             :     // This instruction MUST be idempotent: since it does not correspond to an
    4870             :     // explicit operation in the bytecode, we cannot use resumeAfter().
    4871             :     // Getters may not override |prototype| fetching, so this operation is indeed idempotent.
    4872             :     // - First try an idempotent property cache.
    4873             :     // - Upon failing idempotent property cache, we can't use a non-idempotent cache,
    4874             :     //   therefore we fallback to CallGetProperty
    4875             :     //
    4876             :     // Note: both CallGetProperty and GetPropertyCache can trigger a GC,
    4877             :     //       and thus invalidation.
    4878             :     MInstruction* getProto;
    4879           0 :     if (!invalidatedIdempotentCache()) {
    4880           0 :         MConstant* id = constant(StringValue(names().prototype));
    4881           0 :         MGetPropertyCache* getPropCache = MGetPropertyCache::New(alloc(), newTarget, id,
    4882           0 :                                                                  /* monitored = */ false);
    4883           0 :         getPropCache->setIdempotent();
    4884           0 :         getProto = getPropCache;
    4885             :     } else {
    4886           0 :         MCallGetProperty* callGetProp = MCallGetProperty::New(alloc(), newTarget, names().prototype);
    4887             :         callGetProp->setIdempotent();
    4888           0 :         getProto = callGetProp;
    4889             :     }
    4890           0 :     current->add(getProto);
    4891             : 
    4892             :     // Create this from prototype
    4893           0 :     MCreateThisWithProto* createThis = MCreateThisWithProto::New(alloc(), callee, newTarget, getProto);
    4894           0 :     current->add(createThis);
    4895             : 
    4896           0 :     return createThis;
    4897             : }
    4898             : 
    4899             : JSObject*
    4900           4 : IonBuilder::getSingletonPrototype(JSFunction* target)
    4901             : {
    4902           0 :     TypeSet::ObjectKey* targetKey = TypeSet::ObjectKey::get(target);
    4903           4 :     if (targetKey->unknownProperties())
    4904             :         return nullptr;
    4905             : 
    4906           0 :     jsid protoid = NameToId(names().prototype);
    4907           4 :     HeapTypeSetKey protoProperty = targetKey->property(protoid);
    4908             : 
    4909           4 :     return protoProperty.singleton(constraints());
    4910             : }
    4911             : 
    4912             : MDefinition*
    4913           4 : IonBuilder::createThisScriptedSingleton(JSFunction* target)
    4914             : {
    4915           4 :     if (!target->hasScript())
    4916             :         return nullptr;
    4917             : 
    4918             :     // Get the singleton prototype (if exists)
    4919           0 :     JSObject* proto = getSingletonPrototype(target);
    4920           4 :     if (!proto)
    4921             :         return nullptr;
    4922             : 
    4923           0 :     JSObject* templateObject = inspector->getTemplateObject(pc);
    4924           0 :     if (!templateObject)
    4925             :         return nullptr;
    4926           0 :     if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
    4927             :         return nullptr;
    4928           0 :     if (templateObject->staticPrototype() != proto)
    4929             :         return nullptr;
    4930             : 
    4931           0 :     TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group());
    4932           0 :     if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED))
    4933             :         return nullptr;
    4934             : 
    4935           0 :     StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript());
    4936           0 :     if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject)))
    4937             :         return nullptr;
    4938             : 
    4939             :     // Generate an inline path to create a new |this| object with
    4940             :     // the given singleton prototype.
    4941           0 :     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    4942             :     MCreateThisWithTemplate* createThis =
    4943           0 :         MCreateThisWithTemplate::New(alloc(), constraints(), templateConst,
    4944           0 :                                      templateObject->group()->initialHeap(constraints()));
    4945           0 :     current->add(templateConst);
    4946           0 :     current->add(createThis);
    4947             : 
    4948           0 :     return createThis;
    4949             : }
    4950             : 
    4951             : MDefinition*
    4952          72 : IonBuilder::createThisScriptedBaseline(MDefinition* callee)
    4953             : {
    4954             :     // Try to inline |this| creation based on Baseline feedback.
    4955             : 
    4956           0 :     JSFunction* target = inspector->getSingleCallee(pc);
    4957          77 :     if (!target || !target->hasScript())
    4958             :         return nullptr;
    4959             : 
    4960           5 :     if (target->isBoundFunction() || target->isDerivedClassConstructor())
    4961             :         return nullptr;
    4962             : 
    4963           0 :     JSObject* templateObject = inspector->getTemplateObject(pc);
    4964           4 :     if (!templateObject)
    4965             :         return nullptr;
    4966           4 :     if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
    4967             :         return nullptr;
    4968             : 
    4969           0 :     Shape* shape = target->lookupPure(realm->runtime()->names().prototype);
    4970           4 :     if (!shape || !shape->isDataProperty())
    4971             :         return nullptr;
    4972             : 
    4973           0 :     Value protov = target->getSlot(shape->slot());
    4974           4 :     if (!protov.isObject())
    4975             :         return nullptr;
    4976             : 
    4977           0 :     JSObject* proto = checkNurseryObject(&protov.toObject());
    4978           4 :     if (proto != templateObject->staticPrototype())
    4979             :         return nullptr;
    4980             : 
    4981           0 :     TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group());
    4982           4 :     if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED))
    4983             :         return nullptr;
    4984             : 
    4985           0 :     StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript());
    4986           4 :     if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject)))
    4987             :         return nullptr;
    4988             : 
    4989             :     // Shape guard.
    4990           4 :     callee = addShapeGuard(callee, target->lastProperty(), Bailout_ShapeGuard);
    4991             : 
    4992             :     // Guard callee.prototype == proto.
    4993           0 :     MOZ_ASSERT(shape->numFixedSlots() == 0, "Must be a dynamic slot");
    4994           0 :     MSlots* slots = MSlots::New(alloc(), callee);
    4995           0 :     current->add(slots);
    4996           0 :     MLoadSlot* prototype = MLoadSlot::New(alloc(), slots, shape->slot());
    4997           0 :     current->add(prototype);
    4998           0 :     MDefinition* protoConst = constant(ObjectValue(*proto));
    4999           0 :     MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst,
    5000           0 :                                                             /* bailOnEquality = */ false);
    5001           4 :     current->add(guard);
    5002             : 
    5003             :     // Generate an inline path to create a new |this| object with
    5004             :     // the given prototype.
    5005           4 :     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    5006             :     MCreateThisWithTemplate* createThis =
    5007           0 :         MCreateThisWithTemplate::New(alloc(), constraints(), templateConst,
    5008           0 :                                      templateObject->group()->initialHeap(constraints()));
    5009           0 :     current->add(templateConst);
    5010           4 :     current->add(createThis);
    5011             : 
    5012           4 :     return createThis;
    5013             : }
    5014             : 
    5015             : MDefinition*
    5016          86 : IonBuilder::createThis(JSFunction* target, MDefinition* callee, MDefinition* newTarget)
    5017             : {
    5018             :     // Create |this| for unknown target.
    5019           0 :     if (!target) {
    5020          68 :         if (MDefinition* createThis = createThisScriptedBaseline(callee))
    5021             :             return createThis;
    5022             : 
    5023           0 :         MCreateThis* createThis = MCreateThis::New(alloc(), callee, newTarget);
    5024           0 :         current->add(createThis);
    5025          68 :         return createThis;
    5026             :     }
    5027             : 
    5028             :     // Native constructors build the new Object themselves.
    5029           0 :     if (target->isNative()) {
    5030          14 :         if (!target->isConstructor())
    5031             :             return nullptr;
    5032             : 
    5033          14 :         if (target->isNativeWithJitEntry()) {
    5034             :             // Do not bother inlining constructor calls to asm.js, since it is
    5035             :             // not used much in practice.
    5036           0 :             MOZ_ASSERT(target->isWasmOptimized());
    5037             :             return nullptr;
    5038             :         }
    5039             : 
    5040           0 :         MConstant* magic = MConstant::New(alloc(), MagicValue(JS_IS_CONSTRUCTING));
    5041           0 :         current->add(magic);
    5042          14 :         return magic;
    5043             :     }
    5044             : 
    5045           1 :     if (target->isBoundFunction())
    5046           0 :         return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
    5047             : 
    5048           0 :     if (target->isDerivedClassConstructor()) {
    5049           0 :         MOZ_ASSERT(target->isClassConstructor());
    5050           0 :         return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
    5051             :     }
    5052             : 
    5053             :     // Try baking in the prototype.
    5054           4 :     if (MDefinition* createThis = createThisScriptedSingleton(target))
    5055             :         return createThis;
    5056             : 
    5057           4 :     if (MDefinition* createThis = createThisScriptedBaseline(callee))
    5058             :         return createThis;
    5059             : 
    5060           0 :     return createThisScripted(callee, newTarget);
    5061             : }
    5062             : 
    5063             : AbortReasonOr<Ok>
    5064           2 : IonBuilder::jsop_funcall(uint32_t argc)
    5065             : {
    5066             :     // Stack for JSOP_FUNCALL:
    5067             :     // 1:      arg0
    5068             :     // ...
    5069             :     // argc:   argN
    5070             :     // argc+1: JSFunction*, the 'f' in |f.call()|, in |this| position.
    5071             :     // argc+2: The native 'call' function.
    5072             : 
    5073           0 :     int calleeDepth = -((int)argc + 2);
    5074           2 :     int funcDepth = -((int)argc + 1);
    5075             : 
    5076             :     // If |Function.prototype.call| may be overridden, don't optimize callsite.
    5077           0 :     TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
    5078           0 :     JSFunction* native = getSingleCallTarget(calleeTypes);
    5079           2 :     if (!native || !native->isNative() || native->native() != &fun_call) {
    5080             :         CallInfo callInfo(alloc(), pc, /* constructing = */ false,
    5081           0 :                           /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5082           1 :         if (!callInfo.init(current, argc))
    5083           0 :             return abort(AbortReason::Alloc);
    5084           2 :         return makeCall(native, callInfo);
    5085             :     }
    5086           0 :     current->peek(calleeDepth)->setImplicitlyUsedUnchecked();
    5087             : 
    5088             :     // Extract call target.
    5089           0 :     TemporaryTypeSet* funTypes = current->peek(funcDepth)->resultTypeSet();
    5090           0 :     JSFunction* target = getSingleCallTarget(funTypes);
    5091             : 
    5092             :     // Shimmy the slots down to remove the native 'call' function.
    5093           0 :     current->shimmySlots(funcDepth - 1);
    5094             : 
    5095           0 :     bool zeroArguments = (argc == 0);
    5096             : 
    5097             :     // If no |this| argument was provided, explicitly pass Undefined.
    5098             :     // Pushing is safe here, since one stack slot has been removed.
    5099           0 :     if (zeroArguments) {
    5100           0 :         pushConstant(UndefinedValue());
    5101             :     } else {
    5102             :         // |this| becomes implicit in the call.
    5103           0 :         argc -= 1;
    5104             :     }
    5105             : 
    5106             :     CallInfo callInfo(alloc(), pc, /* constructing = */ false,
    5107           0 :                       /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5108           0 :     if (!callInfo.init(current, argc))
    5109           0 :         return abort(AbortReason::Alloc);
    5110             : 
    5111             :     // Try to inline the call.
    5112           0 :     if (!zeroArguments) {
    5113           0 :         InliningDecision decision = makeInliningDecision(target, callInfo);
    5114           0 :         switch (decision) {
    5115             :           case InliningDecision_Error:
    5116           0 :             return abort(AbortReason::Error);
    5117             :           case InliningDecision_DontInline:
    5118             :           case InliningDecision_WarmUpCountTooLow:
    5119             :             break;
    5120             :           case InliningDecision_Inline: {
    5121             :             InliningStatus status;
    5122           0 :             MOZ_TRY_VAR(status, inlineSingleCall(callInfo, target));
    5123           0 :             if (status == InliningStatus_Inlined)
    5124           0 :                 return Ok();
    5125             :             break;
    5126             :           }
    5127             :         }
    5128             :     }
    5129             : 
    5130             :     // Call without inlining.
    5131           0 :     return makeCall(target, callInfo);
    5132             : }
    5133             : 
    5134             : AbortReasonOr<Ok>
    5135          15 : IonBuilder::jsop_funapply(uint32_t argc)
    5136             : {
    5137          15 :     int calleeDepth = -((int)argc + 2);
    5138             : 
    5139           0 :     TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
    5140           0 :     JSFunction* native = getSingleCallTarget(calleeTypes);
    5141          15 :     if (argc != 2 || info().analysisMode() == Analysis_ArgumentsUsage) {
    5142             :         CallInfo callInfo(alloc(), pc, /* constructing = */ false,
    5143           0 :                           /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5144           1 :         if (!callInfo.init(current, argc))
    5145           0 :             return abort(AbortReason::Alloc);
    5146          15 :         return makeCall(native, callInfo);
    5147             :     }
    5148             : 
    5149             :     // Disable compilation if the second argument to |apply| cannot be guaranteed
    5150             :     // to be either definitely |arguments| or definitely not |arguments|.
    5151           0 :     MDefinition* argument = current->peek(-1);
    5152           0 :     if (script()->argumentsHasVarBinding() &&
    5153           0 :         argument->mightBeType(MIRType::MagicOptimizedArguments) &&
    5154           0 :         argument->type() != MIRType::MagicOptimizedArguments)
    5155             :     {
    5156           0 :         return abort(AbortReason::Disable, "fun.apply with MaybeArguments");
    5157             :     }
    5158             : 
    5159             :     // Fallback to regular call if arg 2 is not definitely |arguments|.
    5160           0 :     if (argument->type() != MIRType::MagicOptimizedArguments) {
    5161             :         // Optimize fun.apply(self, array) if the length is sane and there are no holes.
    5162           0 :         TemporaryTypeSet* objTypes = argument->resultTypeSet();
    5163           0 :         if (native && native->isNative() && native->native() == fun_apply &&
    5164           0 :             objTypes &&
    5165           0 :             objTypes->getKnownClass(constraints()) == &ArrayObject::class_ &&
    5166           0 :             !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW) &&
    5167           0 :             ElementAccessIsPacked(constraints(), argument))
    5168             :         {
    5169           0 :             return jsop_funapplyarray(argc);
    5170             :         }
    5171             : 
    5172             :         CallInfo callInfo(alloc(), pc, /* constructing = */ false,
    5173           0 :                           /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5174           0 :         if (!callInfo.init(current, argc))
    5175           0 :             return abort(AbortReason::Alloc);
    5176           0 :         return makeCall(native, callInfo);
    5177             :     }
    5178             : 
    5179           0 :     if ((!native || !native->isNative() ||
    5180           0 :         native->native() != fun_apply) &&
    5181           0 :         info().analysisMode() != Analysis_DefiniteProperties)
    5182             :     {
    5183           0 :         return abort(AbortReason::Disable, "fun.apply speculation failed");
    5184             :     }
    5185             : 
    5186             :     // Use funapply that definitely uses |arguments|
    5187           0 :     return jsop_funapplyarguments(argc);
    5188             : }
    5189             : 
    5190             : AbortReasonOr<Ok>
    5191           1 : IonBuilder::jsop_spreadcall()
    5192             : {
    5193             :     // The arguments array is constructed by a JSOP_NEWARRAY and not
    5194             :     // leaked to user. The complications of spread call iterator behaviour are
    5195             :     // handled when the user objects are expanded and copied into this hidden
    5196             :     // array.
    5197             : 
    5198             : #ifdef DEBUG
    5199             :     // If we know class, ensure it is what we expected
    5200           0 :     MDefinition* argument = current->peek(-1);
    5201           0 :     if (TemporaryTypeSet* objTypes = argument->resultTypeSet())
    5202           0 :         if (const Class* clasp = objTypes->getKnownClass(constraints()))
    5203           0 :             MOZ_ASSERT(clasp == &ArrayObject::class_);
    5204             : #endif
    5205             : 
    5206           0 :     MDefinition* argArr = current->pop();
    5207           0 :     MDefinition* argThis = current->pop();
    5208           1 :     MDefinition* argFunc = current->pop();
    5209             : 
    5210             :     // Extract call target.
    5211           0 :     TemporaryTypeSet* funTypes = argFunc->resultTypeSet();
    5212           0 :     JSFunction* target = getSingleCallTarget(funTypes);
    5213           1 :     WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
    5214             : 
    5215             :     // Dense elements of argument array
    5216           0 :     MElements* elements = MElements::New(alloc(), argArr);
    5217           1 :     current->add(elements);
    5218             : 
    5219           0 :     MApplyArray* apply = MApplyArray::New(alloc(), wrappedTarget, argFunc, elements, argThis);
    5220           0 :     current->add(apply);
    5221           0 :     current->push(apply);
    5222           3 :     MOZ_TRY(resumeAfter(apply));
    5223             : 
    5224             :     // TypeBarrier the call result
    5225           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    5226           1 :     return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
    5227             : }
    5228             : 
    5229             : AbortReasonOr<Ok>
    5230           0 : IonBuilder::jsop_funapplyarray(uint32_t argc)
    5231             : {
    5232           0 :     MOZ_ASSERT(argc == 2);
    5233             : 
    5234           0 :     int funcDepth = -((int)argc + 1);
    5235             : 
    5236             :     // Extract call target.
    5237           0 :     TemporaryTypeSet* funTypes = current->peek(funcDepth)->resultTypeSet();
    5238           0 :     JSFunction* target = getSingleCallTarget(funTypes);
    5239             : 
    5240             :     // Pop the array agument
    5241           0 :     MDefinition* argObj = current->pop();
    5242             : 
    5243           0 :     MElements* elements = MElements::New(alloc(), argObj);
    5244           0 :     current->add(elements);
    5245             : 
    5246             :     // Pop the |this| argument.
    5247           0 :     MDefinition* argThis = current->pop();
    5248             : 
    5249             :     // Unwrap the (JSFunction *) parameter.
    5250           0 :     MDefinition* argFunc = current->pop();
    5251             : 
    5252             :     // Pop apply function.
    5253           0 :     MDefinition* nativeFunc = current->pop();
    5254           0 :     nativeFunc->setImplicitlyUsedUnchecked();
    5255             : 
    5256           0 :     WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
    5257           0 :     MApplyArray* apply = MApplyArray::New(alloc(), wrappedTarget, argFunc, elements, argThis);
    5258           0 :     current->add(apply);
    5259           0 :     current->push(apply);
    5260           0 :     MOZ_TRY(resumeAfter(apply));
    5261             : 
    5262           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    5263           0 :     return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
    5264             : }
    5265             : 
    5266             : AbortReasonOr<Ok>
    5267           0 : CallInfo::savePriorCallStack(MIRGenerator* mir, MBasicBlock* current, size_t peekDepth)
    5268             : {
    5269           0 :     MOZ_ASSERT(priorArgs_.empty());
    5270           0 :     if (!priorArgs_.reserve(peekDepth))
    5271           0 :         return mir->abort(AbortReason::Alloc);
    5272           0 :     while (peekDepth) {
    5273           0 :         priorArgs_.infallibleAppend(current->peek(0 - int32_t(peekDepth)));
    5274           0 :         peekDepth--;
    5275             :     }
    5276           0 :     return Ok();
    5277             : }
    5278             : 
    5279             : 
    5280             : AbortReasonOr<Ok>
    5281           0 : IonBuilder::jsop_funapplyarguments(uint32_t argc)
    5282             : {
    5283             :     // Stack for JSOP_FUNAPPLY:
    5284             :     // 1:      Vp
    5285             :     // 2:      This
    5286             :     // argc+1: JSFunction*, the 'f' in |f.call()|, in |this| position.
    5287             :     // argc+2: The native 'apply' function.
    5288             : 
    5289           0 :     int funcDepth = -((int)argc + 1);
    5290             : 
    5291             :     // Extract call target.
    5292           0 :     TemporaryTypeSet* funTypes = current->peek(funcDepth)->resultTypeSet();
    5293           0 :     JSFunction* target = getSingleCallTarget(funTypes);
    5294             : 
    5295             :     // When this script isn't inlined, use MApplyArgs,
    5296             :     // to copy the arguments from the stack and call the function
    5297           0 :     if (inliningDepth_ == 0 && info().analysisMode() != Analysis_DefiniteProperties) {
    5298             :         // The array argument corresponds to the arguments object. As the JIT
    5299             :         // is implicitly reading the arguments object in the next instruction,
    5300             :         // we need to prevent the deletion of the arguments object from resume
    5301             :         // points, so that Baseline will behave correctly after a bailout.
    5302           0 :         MDefinition* vp = current->pop();
    5303           0 :         vp->setImplicitlyUsedUnchecked();
    5304             : 
    5305           0 :         MDefinition* argThis = current->pop();
    5306             : 
    5307             :         // Unwrap the (JSFunction*) parameter.
    5308           0 :         MDefinition* argFunc = current->pop();
    5309             : 
    5310             :         // Pop apply function.
    5311           0 :         MDefinition* nativeFunc = current->pop();
    5312           0 :         nativeFunc->setImplicitlyUsedUnchecked();
    5313             : 
    5314           0 :         MArgumentsLength* numArgs = MArgumentsLength::New(alloc());
    5315           0 :         current->add(numArgs);
    5316             : 
    5317           0 :         WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
    5318           0 :         MApplyArgs* apply = MApplyArgs::New(alloc(), wrappedTarget, argFunc, numArgs, argThis);
    5319           0 :         current->add(apply);
    5320           0 :         current->push(apply);
    5321           0 :         MOZ_TRY(resumeAfter(apply));
    5322             : 
    5323           0 :         TemporaryTypeSet* types = bytecodeTypes(pc);
    5324           0 :         return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
    5325             :     }
    5326             : 
    5327             :     // When inlining we have the arguments the function gets called with
    5328             :     // and can optimize even more, by just calling the functions with the args.
    5329             :     // We also try this path when doing the definite properties analysis, as we
    5330             :     // can inline the apply() target and don't care about the actual arguments
    5331             :     // that were passed in.
    5332             : 
    5333             :     CallInfo callInfo(alloc(), pc, /* constructing = */ false,
    5334           0 :                       /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5335           0 :     MOZ_TRY(callInfo.savePriorCallStack(this, current, 4));
    5336             : 
    5337             :     // Vp
    5338           0 :     MDefinition* vp = current->pop();
    5339           0 :     vp->setImplicitlyUsedUnchecked();
    5340             : 
    5341             :     // Arguments
    5342           0 :     if (inliningDepth_) {
    5343           0 :         if (!callInfo.setArgs(inlineCallInfo_->argv()))
    5344           0 :             return abort(AbortReason::Alloc);
    5345             :     }
    5346             : 
    5347             :     // This
    5348           0 :     MDefinition* argThis = current->pop();
    5349           0 :     callInfo.setThis(argThis);
    5350             : 
    5351             :     // Pop function parameter.
    5352           0 :     MDefinition* argFunc = current->pop();
    5353           0 :     callInfo.setFun(argFunc);
    5354             : 
    5355             :     // Pop apply function.
    5356           0 :     MDefinition* nativeFunc = current->pop();
    5357           0 :     nativeFunc->setImplicitlyUsedUnchecked();
    5358             : 
    5359             :     // Try to inline the call.
    5360           0 :     InliningDecision decision = makeInliningDecision(target, callInfo);
    5361           0 :     switch (decision) {
    5362             :       case InliningDecision_Error:
    5363           0 :         return abort(AbortReason::Error);
    5364             :       case InliningDecision_DontInline:
    5365             :       case InliningDecision_WarmUpCountTooLow:
    5366             :         break;
    5367             :       case InliningDecision_Inline: {
    5368             :         InliningStatus status;
    5369           0 :         MOZ_TRY_VAR(status, inlineSingleCall(callInfo, target));
    5370           0 :         if (status == InliningStatus_Inlined)
    5371           0 :             return Ok();
    5372             :       }
    5373             :     }
    5374             : 
    5375           0 :     return makeCall(target, callInfo);
    5376             : }
    5377             : 
    5378             : AbortReasonOr<Ok>
    5379        2515 : IonBuilder::jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue)
    5380             : {
    5381        2515 :     startTrackingOptimizations();
    5382             : 
    5383             :     // If this call has never executed, try to seed the observed type set
    5384             :     // based on how the call result is used.
    5385           0 :     TemporaryTypeSet* observed = bytecodeTypes(pc);
    5386           0 :     if (observed->empty()) {
    5387           0 :         if (BytecodeFlowsToBitop(pc)) {
    5388           0 :             observed->addType(TypeSet::Int32Type(), alloc_->lifoAlloc());
    5389        1846 :         } else if (*GetNextPc(pc) == JSOP_POS) {
    5390             :             // Note: this is lame, overspecialized on the code patterns used
    5391             :             // by asm.js and should be replaced by a more general mechanism.
    5392             :             // See bug 870847.
    5393           0 :             observed->addType(TypeSet::DoubleType(), alloc_->lifoAlloc());
    5394             :         }
    5395             :     }
    5396             : 
    5397        2515 :     int calleeDepth = -((int)argc + 2 + constructing);
    5398             : 
    5399             :     // Acquire known call target if existent.
    5400           0 :     InliningTargets targets(alloc());
    5401           0 :     TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
    5402           0 :     if (calleeTypes)
    5403        7488 :         MOZ_TRY(getPolyCallTargets(calleeTypes, constructing, targets, 4));
    5404             : 
    5405           0 :     CallInfo callInfo(alloc(), pc, constructing, ignoresReturnValue);
    5406           1 :     if (!callInfo.init(current, argc))
    5407           0 :         return abort(AbortReason::Alloc);
    5408             : 
    5409             :     // Try inlining
    5410             :     InliningStatus status;
    5411           0 :     MOZ_TRY_VAR(status, inlineCallsite(targets, callInfo));
    5412           0 :     if (status == InliningStatus_Inlined)
    5413        1280 :         return Ok();
    5414             : 
    5415             :     // Discard unreferenced & pre-allocated resume points.
    5416        1235 :     replaceMaybeFallbackFunctionGetter(nullptr);
    5417             : 
    5418             :     // No inline, just make the call.
    5419           0 :     Maybe<CallTargets> callTargets;
    5420           0 :     if (!targets.empty()) {
    5421           0 :         callTargets.emplace(alloc());
    5422           0 :         for (const InliningTarget& target : targets) {
    5423           0 :             if (!target.target->is<JSFunction>()) {
    5424           0 :                 callTargets = Nothing();
    5425           0 :                 break;
    5426             :             }
    5427           1 :             if (!callTargets->append(&target.target->as<JSFunction>()))
    5428           0 :                 return abort(AbortReason::Alloc);
    5429             :         }
    5430             :     }
    5431             : 
    5432           0 :     if (status == InliningStatus_WarmUpCountTooLow &&
    5433           0 :         callTargets &&
    5434          18 :         callTargets->length() == 1)
    5435             :     {
    5436          18 :         JSFunction* target = callTargets.ref()[0];
    5437             :         MRecompileCheck* check =
    5438           0 :             MRecompileCheck::New(alloc(), target->nonLazyScript(),
    5439           0 :                                  optimizationInfo().inliningRecompileThreshold(),
    5440           0 :                                  MRecompileCheck::RecompileCheck_Inlining);
    5441          18 :         current->add(check);
    5442             :     }
    5443             : 
    5444        1235 :     return makeCall(callTargets, callInfo);
    5445             : }
    5446             : 
    5447             : AbortReasonOr<bool>
    5448           0 : IonBuilder::testShouldDOMCall(TypeSet* inTypes, JSFunction* func, JSJitInfo::OpType opType)
    5449             : {
    5450           0 :     if (!func->isNative() || !func->hasJitInfo())
    5451           0 :         return false;
    5452             : 
    5453             :     // If all the DOM objects flowing through are legal with this
    5454             :     // property, we can bake in a call to the bottom half of the DOM
    5455             :     // accessor
    5456             :     DOMInstanceClassHasProtoAtDepth instanceChecker =
    5457           0 :         realm->runtime()->DOMcallbacks()->instanceClassMatchesProto;
    5458             : 
    5459           0 :     const JSJitInfo* jinfo = func->jitInfo();
    5460           0 :     if (jinfo->type() != opType)
    5461           0 :         return false;
    5462             : 
    5463           0 :     for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
    5464           0 :         TypeSet::ObjectKey* key = inTypes->getObject(i);
    5465           0 :         if (!key)
    5466             :             continue;
    5467             : 
    5468           0 :         if (!alloc().ensureBallast())
    5469           0 :             return abort(AbortReason::Alloc);
    5470             : 
    5471           0 :         if (!key->hasStableClassAndProto(constraints()))
    5472           0 :             return false;
    5473             : 
    5474           0 :         if (!instanceChecker(key->clasp(), jinfo->protoID, jinfo->depth))
    5475           0 :             return false;
    5476             :     }
    5477             : 
    5478           0 :     return true;
    5479             : }
    5480             : 
    5481             : static bool
    5482         114 : ArgumentTypesMatch(MDefinition* def, StackTypeSet* calleeTypes)
    5483             : {
    5484         114 :     if (!calleeTypes)
    5485             :         return false;
    5486             : 
    5487           0 :     if (def->resultTypeSet()) {
    5488           0 :         MOZ_ASSERT(def->type() == MIRType::Value || def->mightBeType(def->type()));
    5489          66 :         return def->resultTypeSet()->isSubset(calleeTypes);
    5490             :     }
    5491             : 
    5492          41 :     if (def->type() == MIRType::Value)
    5493             :         return false;
    5494             : 
    5495           0 :     if (def->type() == MIRType::Object)
    5496           0 :         return calleeTypes->unknownObject();
    5497             : 
    5498          32 :     return calleeTypes->mightBeMIRType(def->type());
    5499             : }
    5500             : 
    5501             : bool
    5502         308 : IonBuilder::testNeedsArgumentCheck(JSFunction* target, CallInfo& callInfo)
    5503             : {
    5504             :     // If we have a known target, check if the caller arg types are a subset of callee.
    5505             :     // Since typeset accumulates and can't decrease that means we don't need to check
    5506             :     // the arguments anymore.
    5507             : 
    5508         308 :     if (target->isNative())
    5509             :         return false;
    5510             : 
    5511          68 :     if (!target->hasScript())
    5512             :         return true;
    5513             : 
    5514          64 :     JSScript* targetScript = target->nonLazyScript();
    5515             : 
    5516          64 :     if (!ArgumentTypesMatch(callInfo.thisArg(), TypeScript::ThisTypes(targetScript)))
    5517             :         return true;
    5518           0 :     uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->nargs());
    5519           0 :     for (size_t i = 0; i < expected_args; i++) {
    5520          50 :         if (!ArgumentTypesMatch(callInfo.getArg(i), TypeScript::ArgTypes(targetScript, i)))
    5521             :             return true;
    5522             :     }
    5523           0 :     for (size_t i = callInfo.argc(); i < target->nargs(); i++) {
    5524           1 :         if (!TypeScript::ArgTypes(targetScript, i)->mightBeMIRType(MIRType::Undefined))
    5525             :             return true;
    5526             :     }
    5527             : 
    5528             :     return false;
    5529             : }
    5530             : 
    5531             : AbortReasonOr<MCall*>
    5532        1257 : IonBuilder::makeCallHelper(const Maybe<CallTargets>& targets, CallInfo& callInfo)
    5533             : {
    5534             :     // This function may be called with mutated stack.
    5535             :     // Querying TI for popped types is invalid.
    5536             : 
    5537        1565 :     MOZ_ASSERT_IF(targets, !targets->empty());
    5538             : 
    5539           0 :     JSFunction* target = nullptr;
    5540           0 :     if (targets && targets->length() == 1)
    5541         306 :         target = targets.ref()[0];
    5542             : 
    5543        1257 :     uint32_t targetArgs = callInfo.argc();
    5544             : 
    5545             :     // Collect number of missing arguments provided that the target is
    5546             :     // scripted. Native functions are passed an explicit 'argc' parameter.
    5547           0 :     if (target && !target->isNativeWithCppEntry())
    5548          66 :         targetArgs = Max<uint32_t>(target->nargs(), callInfo.argc());
    5549             : 
    5550           0 :     bool isDOMCall = false;
    5551           0 :     DOMObjectKind objKind = DOMObjectKind::Unknown;
    5552        1563 :     if (target && !callInfo.constructing()) {
    5553             :         // We know we have a single call target.  Check whether the "this" types
    5554             :         // are DOM types and our function a DOM function, and if so flag the
    5555             :         // MCall accordingly.
    5556           0 :         TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
    5557           0 :         if (thisTypes &&
    5558           0 :             thisTypes->getKnownMIRType() == MIRType::Object &&
    5559          63 :             thisTypes->isDOMClass(constraints(), &objKind))
    5560             :         {
    5561           0 :             MOZ_TRY_VAR(isDOMCall, testShouldDOMCall(thisTypes, target, JSJitInfo::Method));
    5562             :         }
    5563             :     }
    5564             : 
    5565           0 :     MCall* call = MCall::New(alloc(), target, targetArgs + 1 + callInfo.constructing(),
    5566           0 :                              callInfo.argc(), callInfo.constructing(),
    5567           0 :                              callInfo.ignoresReturnValue(), isDOMCall, objKind);
    5568           0 :     if (!call)
    5569           0 :         return abort(AbortReason::Alloc);
    5570             : 
    5571           0 :     if (callInfo.constructing())
    5572          82 :         call->addArg(targetArgs + 1, callInfo.getNewTarget());
    5573             : 
    5574             :     // Explicitly pad any missing arguments with |undefined|.
    5575             :     // This permits skipping the argumentsRectifier.
    5576           0 :     MOZ_ASSERT_IF(target && targetArgs > callInfo.argc(), !target->isNativeWithCppEntry());
    5577           0 :     for (int i = targetArgs; i > (int)callInfo.argc(); i--) {
    5578           0 :         MConstant* undef = constant(UndefinedValue());
    5579           1 :         if (!alloc().ensureBallast())
    5580           0 :             return abort(AbortReason::Alloc);
    5581           4 :         call->addArg(i, undef);
    5582             :     }
    5583             : 
    5584             :     // Add explicit arguments.
    5585             :     // Skip addArg(0) because it is reserved for this
    5586           0 :     for (int32_t i = callInfo.argc() - 1; i >= 0; i--)
    5587        1669 :         call->addArg(i + 1, callInfo.getArg(i));
    5588             : 
    5589             :     // Now that we've told it about all the args, compute whether it's movable
    5590        1257 :     call->computeMovable();
    5591             : 
    5592             :     // Inline the constructor on the caller-side.
    5593           0 :     if (callInfo.constructing()) {
    5594           0 :         MDefinition* create = createThis(target, callInfo.fun(), callInfo.getNewTarget());
    5595           1 :         if (!create)
    5596           0 :             return abort(AbortReason::Disable, "Failure inlining constructor for call.");
    5597             : 
    5598           0 :         callInfo.thisArg()->setImplicitlyUsedUnchecked();
    5599          82 :         callInfo.setThis(create);
    5600             :     }
    5601             : 
    5602             :     // Pass |this| and function.
    5603           0 :     MDefinition* thisArg = callInfo.thisArg();
    5604        1257 :     call->addArg(0, thisArg);
    5605             : 
    5606        1257 :     if (targets) {
    5607             :         // The callee must be one of the target JSFunctions, so we don't need a
    5608             :         // Class check.
    5609         308 :         call->disableClassCheck();
    5610             : 
    5611             :         // Determine whether we can skip the callee's prologue type checks.
    5612           0 :         bool needArgCheck = false;
    5613           0 :         for (JSFunction* target : targets.ref()) {
    5614         308 :             if (testNeedsArgumentCheck(target, callInfo)) {
    5615             :                 needArgCheck = true;
    5616             :                 break;
    5617             :             }
    5618             :         }
    5619         308 :         if (!needArgCheck)
    5620             :             call->disableArgCheck();
    5621             :     }
    5622             : 
    5623        1257 :     call->initFunction(callInfo.fun());
    5624             : 
    5625           0 :     current->add(call);
    5626        1257 :     return call;
    5627             : }
    5628             : 
    5629             : static bool
    5630           0 : DOMCallNeedsBarrier(const JSJitInfo* jitinfo, TemporaryTypeSet* types)
    5631             : {
    5632           0 :     MOZ_ASSERT(jitinfo->type() != JSJitInfo::InlinableNative);
    5633             : 
    5634             :     // If the return type of our DOM native is in "types" already, we don't
    5635             :     // actually need a barrier.
    5636           0 :     if (jitinfo->returnType() == JSVAL_TYPE_UNKNOWN)
    5637             :         return true;
    5638             : 
    5639             :     // JSVAL_TYPE_OBJECT doesn't tell us much; we still have to barrier on the
    5640             :     // actual type of the object.
    5641           0 :     if (jitinfo->returnType() == JSVAL_TYPE_OBJECT)
    5642             :         return true;
    5643             : 
    5644             :     // No need for a barrier if we're already expecting the type we'll produce.
    5645           0 :     return MIRTypeFromValueType(jitinfo->returnType()) != types->getKnownMIRType();
    5646             : }
    5647             : 
    5648             : AbortReasonOr<Ok>
    5649        1257 : IonBuilder::makeCall(const Maybe<CallTargets>& targets, CallInfo& callInfo)
    5650             : {
    5651             : #ifdef DEBUG
    5652             :     // Constructor calls to non-constructors should throw. We don't want to use
    5653             :     // CallKnown in this case.
    5654           0 :     if (callInfo.constructing() && targets) {
    5655           0 :         for (JSFunction* target : targets.ref())
    5656          14 :             MOZ_ASSERT(target->isConstructor());
    5657             :     }
    5658             : #endif
    5659             : 
    5660             :     MCall* call;
    5661        5028 :     MOZ_TRY_VAR(call, makeCallHelper(targets, callInfo));
    5662             : 
    5663           0 :     current->push(call);
    5664           0 :     if (call->isEffectful())
    5665        5028 :         MOZ_TRY(resumeAfter(call));
    5666             : 
    5667        2514 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    5668             : 
    5669           0 :     if (call->isCallDOMNative())
    5670           0 :         return pushDOMTypeBarrier(call, types, call->getSingleTarget()->rawJSFunction());
    5671             : 
    5672        1257 :     return pushTypeBarrier(call, types, BarrierKind::TypeSet);
    5673             : }
    5674             : 
    5675             : AbortReasonOr<Ok>
    5676          19 : IonBuilder::makeCall(JSFunction* target, CallInfo& callInfo)
    5677             : {
    5678           0 :     Maybe<CallTargets> targets;
    5679           0 :     if (target) {
    5680           0 :         targets.emplace(alloc());
    5681           1 :         if (!targets->append(target))
    5682           0 :             return abort(AbortReason::Alloc);
    5683             :     }
    5684          19 :     return makeCall(targets, callInfo);
    5685             : }
    5686             : 
    5687             : AbortReasonOr<Ok>
    5688           0 : IonBuilder::jsop_eval(uint32_t argc)
    5689             : {
    5690           0 :     int calleeDepth = -((int)argc + 2);
    5691           0 :     TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
    5692             : 
    5693             :     // Emit a normal call if the eval has never executed. This keeps us from
    5694             :     // disabling compilation for the script when testing with --ion-eager.
    5695           0 :     if (calleeTypes && calleeTypes->empty())
    5696           0 :         return jsop_call(argc, /* constructing = */ false, false);
    5697             : 
    5698           0 :     JSFunction* target = getSingleCallTarget(calleeTypes);
    5699           0 :     if (!target)
    5700           0 :         return abort(AbortReason::Disable, "No single callee for eval()");
    5701             : 
    5702           0 :     if (script()->global().valueIsEval(ObjectValue(*target))) {
    5703           0 :         if (argc != 1)
    5704           0 :             return abort(AbortReason::Disable, "Direct eval with more than one argument");
    5705             : 
    5706           0 :         if (!info().funMaybeLazy())
    5707           0 :             return abort(AbortReason::Disable, "Direct eval in global code");
    5708             : 
    5709           0 :         if (info().funMaybeLazy()->isArrow())
    5710           0 :             return abort(AbortReason::Disable, "Direct eval from arrow function");
    5711             : 
    5712             :         CallInfo callInfo(alloc(), pc, /* constructing = */ false,
    5713           0 :                           /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5714           0 :         if (!callInfo.init(current, argc))
    5715           0 :             return abort(AbortReason::Alloc);
    5716           0 :         callInfo.setImplicitlyUsedUnchecked();
    5717             : 
    5718           0 :         callInfo.fun()->setImplicitlyUsedUnchecked();
    5719             : 
    5720           0 :         MDefinition* envChain = current->environmentChain();
    5721           0 :         MDefinition* string = callInfo.getArg(0);
    5722             : 
    5723             :         // Direct eval acts as identity on non-string types according to
    5724             :         // ES5 15.1.2.1 step 1.
    5725           0 :         if (!string->mightBeType(MIRType::String)) {
    5726           0 :             current->push(string);
    5727           0 :             TemporaryTypeSet* types = bytecodeTypes(pc);
    5728           0 :             return pushTypeBarrier(string, types, BarrierKind::TypeSet);
    5729             :         }
    5730             : 
    5731           0 :         MOZ_TRY(jsop_newtarget());
    5732           0 :         MDefinition* newTargetValue = current->pop();
    5733             : 
    5734             :         // Try to pattern match 'eval(v + "()")'. In this case v is likely a
    5735             :         // name on the env chain and the eval is performing a call on that
    5736             :         // value. Use an env chain lookup rather than a full eval.
    5737           0 :         if (string->isConcat() &&
    5738           0 :             string->getOperand(1)->type() == MIRType::String &&
    5739           0 :             string->getOperand(1)->maybeConstantValue())
    5740             :         {
    5741           0 :             JSAtom* atom = &string->getOperand(1)->maybeConstantValue()->toString()->asAtom();
    5742             : 
    5743           0 :             if (StringEqualsAscii(atom, "()")) {
    5744           0 :                 MDefinition* name = string->getOperand(0);
    5745           0 :                 MInstruction* dynamicName = MGetDynamicName::New(alloc(), envChain, name);
    5746           0 :                 current->add(dynamicName);
    5747             : 
    5748           0 :                 current->push(dynamicName);
    5749           0 :                 current->push(constant(UndefinedValue())); // thisv
    5750             : 
    5751             :                 CallInfo evalCallInfo(alloc(), pc, /* constructing = */ false,
    5752           0 :                                       /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5753           0 :                 if (!evalCallInfo.init(current, /* argc = */ 0))
    5754           0 :                     return abort(AbortReason::Alloc);
    5755             : 
    5756           0 :                 return makeCall(nullptr, evalCallInfo);
    5757             :             }
    5758             :         }
    5759             : 
    5760           0 :         MInstruction* ins = MCallDirectEval::New(alloc(), envChain, string,
    5761           0 :                                                  newTargetValue, pc);
    5762           0 :         current->add(ins);
    5763           0 :         current->push(ins);
    5764             : 
    5765           0 :         TemporaryTypeSet* types = bytecodeTypes(pc);
    5766           0 :         MOZ_TRY(resumeAfter(ins));
    5767           0 :         return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
    5768             :     }
    5769             : 
    5770           0 :     return jsop_call(argc, /* constructing = */ false, false);
    5771             : }
    5772             : 
    5773             : AbortReasonOr<Ok>
    5774         989 : IonBuilder::jsop_compare(JSOp op)
    5775             : {
    5776           0 :     MDefinition* right = current->pop();
    5777         989 :     MDefinition* left = current->pop();
    5778             : 
    5779         989 :     return jsop_compare(op, left, right);
    5780             : }
    5781             : 
    5782             : AbortReasonOr<Ok>
    5783         989 : IonBuilder::jsop_compare(JSOp op, MDefinition* left, MDefinition* right)
    5784             : {
    5785           0 :     bool emitted = false;
    5786         989 :     startTrackingOptimizations();
    5787             : 
    5788           0 :     if (!forceInlineCaches()) {
    5789           0 :         MOZ_TRY(compareTrySpecialized(&emitted, op, left, right, true));
    5790           0 :         if (emitted)
    5791           0 :             return Ok();
    5792           0 :         MOZ_TRY(compareTryBitwise(&emitted, op, left, right));
    5793           0 :         if (emitted)
    5794           0 :             return Ok();
    5795           0 :         MOZ_TRY(compareTrySpecializedOnBaselineInspector(&emitted, op, left, right));
    5796           0 :         if (emitted)
    5797           0 :             return Ok();
    5798             :     }
    5799             : 
    5800           0 :     MOZ_TRY(compareTrySharedStub(&emitted, left, right));
    5801           0 :     if (emitted)
    5802          95 :         return Ok();
    5803             : 
    5804           0 :     trackOptimizationAttempt(TrackedStrategy::Compare_Call);
    5805             : 
    5806             :     // Not possible to optimize. Do a slow vm call.
    5807           0 :     MCompare* ins = MCompare::New(alloc(), left, right, op);
    5808           0 :     ins->cacheOperandMightEmulateUndefined(constraints());
    5809             : 
    5810           0 :     current->add(ins);
    5811           0 :     current->push(ins);
    5812           0 :     if (ins->isEffectful())
    5813           0 :         MOZ_TRY(resumeAfter(ins));
    5814             : 
    5815           0 :     trackOptimizationSuccess();
    5816           0 :     return Ok();
    5817             : }
    5818             : 
    5819             : static bool
    5820         213 : ObjectOrSimplePrimitive(MDefinition* op)
    5821             : {
    5822             :     // Return true if op is either undefined/null/boolean/int32/symbol or an object.
    5823           0 :     return !op->mightBeType(MIRType::String)
    5824           0 :         && !op->mightBeType(MIRType::Double)
    5825           0 :         && !op->mightBeType(MIRType::Float32)
    5826           0 :         && !op->mightBeType(MIRType::MagicOptimizedArguments)
    5827           0 :         && !op->mightBeType(MIRType::MagicHole)
    5828         419 :         && !op->mightBeType(MIRType::MagicIsConstructing);
    5829             : }
    5830             : 
    5831             : AbortReasonOr<Ok>
    5832         989 : IonBuilder::compareTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right,
    5833             :                                   bool canTrackOptimization)
    5834             : {
    5835           0 :     MOZ_ASSERT(*emitted == false);
    5836         989 :     if (canTrackOptimization)
    5837             :         trackOptimizationAttempt(TrackedStrategy::Compare_SpecializedTypes);
    5838             : 
    5839             :     // Try to emit an compare based on the input types.
    5840             : 
    5841           0 :     MCompare::CompareType type = MCompare::determineCompareType(op, left, right);
    5842           0 :     if (type == MCompare::Compare_Unknown) {
    5843         198 :         if (canTrackOptimization)
    5844             :             trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
    5845         198 :         return Ok();
    5846             :     }
    5847             : 
    5848           0 :     MCompare* ins = MCompare::New(alloc(), left, right, op);
    5849           0 :     ins->setCompareType(type);
    5850         791 :     ins->cacheOperandMightEmulateUndefined(constraints());
    5851             : 
    5852             :     // Some compare types need to have the specific type in the rhs.
    5853             :     // Swap operands if that is not the case.
    5854           0 :     if (type == MCompare::Compare_StrictString && right->type() != MIRType::String)
    5855           0 :         ins->swapOperands();
    5856           0 :     else if (type == MCompare::Compare_Null && right->type() != MIRType::Null)
    5857           0 :         ins->swapOperands();
    5858           0 :     else if (type == MCompare::Compare_Undefined && right->type() != MIRType::Undefined)
    5859           0 :         ins->swapOperands();
    5860           0 :     else if (type == MCompare::Compare_Boolean && right->type() != MIRType::Boolean)
    5861           0 :         ins->swapOperands();
    5862             : 
    5863             :     // Replace inputs with unsigned variants if needed.
    5864           0 :     if (type == MCompare::Compare_UInt32)
    5865           0 :         ins->replaceWithUnsignedOperands();
    5866             : 
    5867           0 :     current->add(ins);
    5868         791 :     current->push(ins);
    5869             : 
    5870           0 :     MOZ_ASSERT(!ins->isEffectful());
    5871         791 :     if (canTrackOptimization)
    5872             :         trackOptimizationSuccess();
    5873           0 :     *emitted = true;
    5874         791 :     return Ok();
    5875             : }
    5876             : 
    5877             : AbortReasonOr<Ok>
    5878         198 : IonBuilder::compareTryBitwise(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
    5879             : {
    5880           0 :     MOZ_ASSERT(*emitted == false);
    5881         198 :     trackOptimizationAttempt(TrackedStrategy::Compare_Bitwise);
    5882             : 
    5883             :     // Try to emit a bitwise compare. Check if a bitwise compare equals the wanted
    5884             :     // result for all observed operand types.
    5885             : 
    5886             :     // Only allow loose and strict equality.
    5887           0 :     if (op != JSOP_EQ && op != JSOP_NE && op != JSOP_STRICTEQ && op != JSOP_STRICTNE) {
    5888           0 :         trackOptimizationOutcome(TrackedOutcome::RelationalCompare);
    5889          88 :         return Ok();
    5890             :     }
    5891             : 
    5892             :     // Only primitive (not double/string) or objects are supported.
    5893             :     // I.e. Undefined/Null/Boolean/Int32/Symbol and Object
    5894           0 :     if (!ObjectOrSimplePrimitive(left) || !ObjectOrSimplePrimitive(right)) {
    5895           0 :         trackOptimizationOutcome(TrackedOutcome::OperandTypeNotBitwiseComparable);
    5896           7 :         return Ok();
    5897             :     }
    5898             : 
    5899             :     // Objects that emulate undefined are not supported.
    5900           0 :     if (left->maybeEmulatesUndefined(constraints()) ||
    5901         103 :         right->maybeEmulatesUndefined(constraints()))
    5902             :     {
    5903           0 :         trackOptimizationOutcome(TrackedOutcome::OperandMaybeEmulatesUndefined);
    5904           0 :         return Ok();
    5905             :     }
    5906             : 
    5907             :     // In the loose comparison more values could be the same,
    5908             :     // but value comparison reporting otherwise.
    5909         103 :     if (op == JSOP_EQ || op == JSOP_NE) {
    5910             : 
    5911             :         // Undefined compared loosy to Null is not supported,
    5912             :         // because tag is different, but value can be the same (undefined == null).
    5913           0 :         if ((left->mightBeType(MIRType::Undefined) && right->mightBeType(MIRType::Null)) ||
    5914           3 :             (left->mightBeType(MIRType::Null) && right->mightBeType(MIRType::Undefined)))
    5915             :         {
    5916           0 :             trackOptimizationOutcome(TrackedOutcome::LoosyUndefinedNullCompare);
    5917           0 :             return Ok();
    5918             :         }
    5919             : 
    5920             :         // Int32 compared loosy to Boolean is not supported,
    5921             :         // because tag is different, but value can be the same (1 == true).
    5922           0 :         if ((left->mightBeType(MIRType::Int32) && right->mightBeType(MIRType::Boolean)) ||
    5923           3 :             (left->mightBeType(MIRType::Boolean) && right->mightBeType(MIRType::Int32)))
    5924             :         {
    5925           0 :             trackOptimizationOutcome(TrackedOutcome::LoosyInt32BooleanCompare);
    5926           0 :             return Ok();
    5927             :         }
    5928             : 
    5929             :         // For loosy comparison of an object with a Boolean/Number/String/Symbol
    5930             :         // the valueOf the object is taken. Therefore not supported.
    5931           0 :         bool simpleLHS = left->mightBeType(MIRType::Boolean) ||
    5932           0 :                          left->mightBeType(MIRType::Int32) ||
    5933           0 :                          left->mightBeType(MIRType::Symbol);
    5934           0 :         bool simpleRHS = right->mightBeType(MIRType::Boolean) ||
    5935           0 :                          right->mightBeType(MIRType::Int32) ||
    5936           0 :                          right->mightBeType(MIRType::Symbol);
    5937           0 :         if ((left->mightBeType(MIRType::Object) && simpleRHS) ||
    5938           3 :             (right->mightBeType(MIRType::Object) && simpleLHS))
    5939             :         {
    5940           0 :             trackOptimizationOutcome(TrackedOutcome::CallsValueOf);
    5941           0 :             return Ok();
    5942             :         }
    5943             :     }
    5944             : 
    5945           0 :     MCompare* ins = MCompare::New(alloc(), left, right, op);
    5946           0 :     ins->setCompareType(MCompare::Compare_Bitwise);
    5947         103 :     ins->cacheOperandMightEmulateUndefined(constraints());
    5948             : 
    5949           0 :     current->add(ins);
    5950         103 :     current->push(ins);
    5951             : 
    5952           0 :     MOZ_ASSERT(!ins->isEffectful());
    5953           0 :     trackOptimizationSuccess();
    5954           0 :     *emitted = true;
    5955         103 :     return Ok();
    5956             : }
    5957             : 
    5958             : AbortReasonOr<Ok>
    5959          95 : IonBuilder::compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
    5960             :                                                      MDefinition* right)
    5961             : {
    5962           0 :     MOZ_ASSERT(*emitted == false);
    5963          95 :     trackOptimizationAttempt(TrackedStrategy::Compare_SpecializedOnBaselineTypes);
    5964             : 
    5965             :     // Try to specialize based on any baseline caches that have been generated
    5966             :     // for the opcode. These will cause the instruction's type policy to insert
    5967             :     // fallible unboxes to the appropriate input types.
    5968             : 
    5969             :     // Strict equality isn't supported.
    5970           0 :     if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE) {
    5971           0 :         trackOptimizationOutcome(TrackedOutcome::StrictCompare);
    5972           7 :         return Ok();
    5973             :     }
    5974             : 
    5975           0 :     MCompare::CompareType type = inspector->expectedCompareType(pc);
    5976           0 :     if (type == MCompare::Compare_Unknown) {
    5977           0 :         trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
    5978          88 :         return Ok();
    5979             :     }
    5980             : 
    5981           0 :     MCompare* ins = MCompare::New(alloc(), left, right, op);
    5982           0 :     ins->setCompareType(type);
    5983           0 :     ins->cacheOperandMightEmulateUndefined(constraints());
    5984             : 
    5985           0 :     current->add(ins);
    5986           0 :     current->push(ins);
    5987             : 
    5988           0 :     MOZ_ASSERT(!ins->isEffectful());
    5989           0 :     trackOptimizationSuccess();
    5990           0 :     *emitted = true;
    5991           0 :     return Ok();
    5992             : }
    5993             : 
    5994             : AbortReasonOr<Ok>
    5995          95 : IonBuilder::compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* right)
    5996             : {
    5997          95 :     MOZ_ASSERT(*emitted == false);
    5998             : 
    5999             :     // Try to emit a shared stub cache.
    6000             : 
    6001           1 :     if (JitOptions.disableSharedStubs)
    6002           0 :         return Ok();
    6003             : 
    6004           0 :     if (JSOp(*pc) == JSOP_CASE)
    6005           0 :         return Ok();
    6006             : 
    6007          95 :     trackOptimizationAttempt(TrackedStrategy::Compare_SharedCache);
    6008             : 
    6009           0 :     MBinarySharedStub* stub = MBinarySharedStub::New(alloc(), left, right);
    6010           0 :     current->add(stub);
    6011           0 :     current->push(stub);
    6012         285 :     MOZ_TRY(resumeAfter(stub));
    6013             : 
    6014           0 :     MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Boolean, MUnbox::Infallible);
    6015           0 :     current->add(unbox);
    6016          95 :     current->push(unbox);
    6017             : 
    6018           0 :     trackOptimizationSuccess();
    6019           0 :     *emitted = true;
    6020          95 :     return Ok();
    6021             : }
    6022             : 
    6023             : AbortReasonOr<Ok>
    6024         109 : IonBuilder::newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, uint32_t length)
    6025             : {
    6026         109 :     MOZ_ASSERT(*emitted == false);
    6027             : 
    6028             :     // TODO: Support tracking optimizations for inlining a call and regular
    6029             :     // optimization tracking at the same time. Currently just drop optimization
    6030             :     // tracking when that happens.
    6031         218 :     bool canTrackOptimization = !IsCallPC(pc);
    6032             : 
    6033         109 :     if (canTrackOptimization)
    6034             :         trackOptimizationAttempt(TrackedStrategy::NewArray_TemplateObject);
    6035             : 
    6036           0 :     if (!templateObject) {
    6037          16 :         if (canTrackOptimization)
    6038             :             trackOptimizationOutcome(TrackedOutcome::NoTemplateObject);
    6039          16 :         return Ok();
    6040             :     }
    6041             : 
    6042          93 :     MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT);
    6043             : 
    6044             :     size_t arraySlots =
    6045         186 :         gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER;
    6046             : 
    6047           0 :     if (length > arraySlots) {
    6048           0 :         if (canTrackOptimization)
    6049             :             trackOptimizationOutcome(TrackedOutcome::LengthTooBig);
    6050           0 :         return Ok();
    6051             :     }
    6052             : 
    6053             :     // Emit fastpath.
    6054             : 
    6055           0 :     gc::InitialHeap heap = templateObject->group()->initialHeap(constraints());
    6056           0 :     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    6057          93 :     current->add(templateConst);
    6058             : 
    6059           0 :     MNewArray* ins = MNewArray::New(alloc(), constraints(), length, templateConst, heap, pc);
    6060           0 :     current->add(ins);
    6061          93 :     current->push(ins);
    6062             : 
    6063          93 :     if (canTrackOptimization)
    6064             :         trackOptimizationSuccess();
    6065           0 :     *emitted = true;
    6066          93 :     return Ok();
    6067             : }
    6068             : 
    6069             : AbortReasonOr<Ok>
    6070          16 : IonBuilder::newArrayTrySharedStub(bool* emitted)
    6071             : {
    6072          16 :     MOZ_ASSERT(*emitted == false);
    6073             : 
    6074             :     // TODO: Support tracking optimizations for inlining a call and regular
    6075             :     // optimization tracking at the same time. Currently just drop optimization
    6076             :     // tracking when that happens.
    6077          32 :     bool canTrackOptimization = !IsCallPC(pc);
    6078             : 
    6079             :     // Try to emit a shared stub cache.
    6080             : 
    6081           1 :     if (JitOptions.disableSharedStubs)
    6082           0 :         return Ok();
    6083             : 
    6084           0 :     if (*pc != JSOP_NEWINIT && *pc != JSOP_NEWARRAY)
    6085           0 :         return Ok();
    6086             : 
    6087          16 :     if (canTrackOptimization)
    6088             :         trackOptimizationAttempt(TrackedStrategy::NewArray_SharedCache);
    6089             : 
    6090           0 :     MInstruction* stub = MNullarySharedStub::New(alloc());
    6091           0 :     current->add(stub);
    6092          16 :     current->push(stub);
    6093             : 
    6094          48 :     MOZ_TRY(resumeAfter(stub));
    6095             : 
    6096           0 :     MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Object, MUnbox::Infallible);
    6097           0 :     current->add(unbox);
    6098          16 :     current->push(unbox);
    6099             : 
    6100          16 :     if (canTrackOptimization)
    6101             :         trackOptimizationSuccess();
    6102             : 
    6103           0 :     *emitted = true;
    6104          16 :     return Ok();
    6105             : }
    6106             : 
    6107             : AbortReasonOr<Ok>
    6108           0 : IonBuilder::newArrayTryVM(bool* emitted, JSObject* templateObject, uint32_t length)
    6109             : {
    6110           0 :     MOZ_ASSERT(*emitted == false);
    6111             : 
    6112             :     // TODO: Support tracking optimizations for inlining a call and regular
    6113             :     // optimization tracking at the same time. Currently just drop optimization
    6114             :     // tracking when that happens.
    6115           0 :     bool canTrackOptimization = !IsCallPC(pc);
    6116             : 
    6117             :     // Emit a VM call.
    6118           0 :     if (canTrackOptimization)
    6119             :         trackOptimizationAttempt(TrackedStrategy::NewArray_Call);
    6120             : 
    6121           0 :     gc::InitialHeap heap = gc::DefaultHeap;
    6122           0 :     MConstant* templateConst = MConstant::New(alloc(), NullValue());
    6123             : 
    6124           0 :     if (templateObject) {
    6125           0 :         heap = templateObject->group()->initialHeap(constraints());
    6126           0 :         templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    6127             :     }
    6128             : 
    6129           0 :     current->add(templateConst);
    6130             : 
    6131           0 :     MNewArray* ins = MNewArray::NewVM(alloc(), constraints(), length, templateConst, heap, pc);
    6132           0 :     current->add(ins);
    6133           0 :     current->push(ins);
    6134             : 
    6135           0 :     if (canTrackOptimization)
    6136             :         trackOptimizationSuccess();
    6137           0 :     *emitted = true;
    6138           0 :     return Ok();
    6139             : }
    6140             : 
    6141             : AbortReasonOr<Ok>
    6142         108 : IonBuilder::jsop_newarray(uint32_t length)
    6143             : {
    6144           0 :     JSObject* templateObject = inspector->getTemplateObject(pc);
    6145         324 :     MOZ_TRY(jsop_newarray(templateObject, length));
    6146             : 
    6147             :     // Improve resulting typeset.
    6148           0 :     ObjectGroup* templateGroup = inspector->getTemplateObjectGroup(pc);
    6149           0 :     if (templateGroup) {
    6150           0 :         TemporaryTypeSet* types = MakeSingletonTypeSet(alloc(), constraints(), templateGroup);
    6151         107 :         current->peek(-1)->setResultTypeSet(types);
    6152             :     }
    6153             : 
    6154         108 :     return Ok();
    6155             : }
    6156             : 
    6157             : AbortReasonOr<Ok>
    6158         109 : IonBuilder::jsop_newarray(JSObject* templateObject, uint32_t length)
    6159             : {
    6160             :     // TODO: Support tracking optimizations for inlining a call and regular
    6161             :     // optimization tracking at the same time. Currently just drop optimization
    6162             :     // tracking when that happens.
    6163         218 :     bool canTrackOptimization = !IsCallPC(pc);
    6164             : 
    6165           0 :     bool emitted = false;
    6166           0 :     if (canTrackOptimization)
    6167         108 :         startTrackingOptimizations();
    6168             : 
    6169           0 :     if (!forceInlineCaches()) {
    6170           0 :         MOZ_TRY(newArrayTryTemplateObject(&emitted, templateObject, length));
    6171           0 :         if (emitted)
    6172          93 :             return Ok();
    6173             :     }
    6174             : 
    6175           0 :     MOZ_TRY(newArrayTrySharedStub(&emitted));
    6176           0 :     if (emitted)
    6177          16 :         return Ok();
    6178             : 
    6179           0 :     MOZ_TRY(newArrayTryVM(&emitted, templateObject, length));
    6180           0 :     if (emitted)
    6181           0 :         return Ok();
    6182             : 
    6183           0 :     MOZ_CRASH("newarray should have been emited");
    6184             : }
    6185             : 
    6186             : AbortReasonOr<Ok>
    6187           0 : IonBuilder::jsop_newarray_copyonwrite()
    6188             : {
    6189           0 :     ArrayObject* templateObject = ObjectGroup::getCopyOnWriteObject(script(), pc);
    6190             : 
    6191             :     // The baseline compiler should have ensured the template object has a type
    6192             :     // with the copy on write flag set already. During the arguments usage
    6193             :     // analysis the baseline compiler hasn't run yet, however, though in this
    6194             :     // case the template object's type doesn't matter.
    6195           0 :     ObjectGroup* group = templateObject->group();
    6196           0 :     MOZ_ASSERT_IF(info().analysisMode() != Analysis_ArgumentsUsage,
    6197             :                   group->hasAllFlagsDontCheckGeneration(OBJECT_FLAG_COPY_ON_WRITE));
    6198             : 
    6199             : 
    6200           0 :     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    6201           0 :     current->add(templateConst);
    6202             : 
    6203             :     MNewArrayCopyOnWrite* ins =
    6204           0 :         MNewArrayCopyOnWrite::New(alloc(), constraints(), templateConst,
    6205           0 :                                   group->initialHeap(constraints()));
    6206             : 
    6207           0 :     current->add(ins);
    6208           0 :     current->push(ins);
    6209             : 
    6210           0 :     return Ok();
    6211             : }
    6212             : 
    6213             : AbortReasonOr<Ok>
    6214         141 : IonBuilder::newObjectTryTemplateObject(bool* emitted, JSObject* templateObject)
    6215             : {
    6216         141 :     MOZ_ASSERT(*emitted == false);
    6217             : 
    6218             :     // TODO: Support tracking optimizations for inlining a call and regular
    6219             :     // optimization tracking at the same time. Currently just drop optimization
    6220             :     // tracking when that happens.
    6221         282 :     bool canTrackOptimization = !IsCallPC(pc);
    6222             : 
    6223         141 :     if (canTrackOptimization)
    6224             :         trackOptimizationAttempt(TrackedStrategy::NewObject_TemplateObject);
    6225           0 :     if (!templateObject) {
    6226          10 :         if (canTrackOptimization)
    6227             :             trackOptimizationOutcome(TrackedOutcome::NoTemplateObject);
    6228          10 :         return Ok();
    6229             :     }
    6230             : 
    6231           0 :     if (templateObject->is<PlainObject>() && templateObject->as<PlainObject>().hasDynamicSlots()) {
    6232           0 :         if (canTrackOptimization)
    6233             :             trackOptimizationOutcome(TrackedOutcome::TemplateObjectIsPlainObjectWithDynamicSlots);
    6234           0 :         return Ok();
    6235             :     }
    6236             : 
    6237             :     // Emit fastpath.
    6238             : 
    6239             :     MNewObject::Mode mode;
    6240         131 :     if (JSOp(*pc) == JSOP_NEWOBJECT || JSOp(*pc) == JSOP_NEWINIT)
    6241             :         mode = MNewObject::ObjectLiteral;
    6242             :     else
    6243           0 :         mode = MNewObject::ObjectCreate;
    6244             : 
    6245           0 :     gc::InitialHeap heap = templateObject->group()->initialHeap(constraints());
    6246           0 :     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    6247         131 :     current->add(templateConst);
    6248             : 
    6249           0 :     MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, heap, mode);
    6250           0 :     current->add(ins);
    6251         131 :     current->push(ins);
    6252             : 
    6253         393 :     MOZ_TRY(resumeAfter(ins));
    6254             : 
    6255         131 :     if (canTrackOptimization)
    6256             :         trackOptimizationSuccess();
    6257           0 :     *emitted = true;
    6258         131 :     return Ok();
    6259             : }
    6260             : 
    6261             : AbortReasonOr<Ok>
    6262          10 : IonBuilder::newObjectTrySharedStub(bool* emitted)
    6263             : {
    6264          10 :     MOZ_ASSERT(*emitted == false);
    6265             : 
    6266             :     // TODO: Support tracking optimizations for inlining a call and regular
    6267             :     // optimization tracking at the same time. Currently just drop optimization
    6268             :     // tracking when that happens.
    6269          20 :     bool canTrackOptimization = !IsCallPC(pc);
    6270             : 
    6271             :     // Try to emit a shared stub cache.
    6272             : 
    6273           1 :     if (JitOptions.disableSharedStubs)
    6274           0 :         return Ok();
    6275             : 
    6276          10 :     if (canTrackOptimization)
    6277             :         trackOptimizationAttempt(TrackedStrategy::NewObject_SharedCache);
    6278             : 
    6279           0 :     MInstruction* stub = MNullarySharedStub::New(alloc());
    6280           0 :     current->add(stub);
    6281          10 :     current->push(stub);
    6282             : 
    6283          30 :     MOZ_TRY(resumeAfter(stub));
    6284             : 
    6285           0 :     MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Object, MUnbox::Infallible);
    6286           0 :     current->add(unbox);
    6287          10 :     current->push(unbox);
    6288             : 
    6289          10 :     if (canTrackOptimization)
    6290             :         trackOptimizationSuccess();
    6291           0 :     *emitted = true;
    6292          10 :     return Ok();
    6293             : }
    6294             : 
    6295             : AbortReasonOr<Ok>
    6296           0 : IonBuilder::newObjectTryVM(bool* emitted, JSObject* templateObject)
    6297             : {
    6298             :     // Emit a VM call.
    6299           0 :     MOZ_ASSERT(JSOp(*pc) == JSOP_NEWOBJECT || JSOp(*pc) == JSOP_NEWINIT);
    6300             : 
    6301           0 :     trackOptimizationAttempt(TrackedStrategy::NewObject_Call);
    6302             : 
    6303           0 :     gc::InitialHeap heap = gc::DefaultHeap;
    6304           0 :     MConstant* templateConst = MConstant::New(alloc(), NullValue());
    6305             : 
    6306           0 :     if (templateObject) {
    6307           0 :         heap = templateObject->group()->initialHeap(constraints());
    6308           0 :         templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    6309             :     }
    6310             : 
    6311           0 :     current->add(templateConst);
    6312             : 
    6313           0 :     MNewObject* ins = MNewObject::NewVM(alloc(), constraints(), templateConst, heap,
    6314           0 :                                         MNewObject::ObjectLiteral);
    6315           0 :     current->add(ins);
    6316           0 :     current->push(ins);
    6317             : 
    6318           0 :     MOZ_TRY(resumeAfter(ins));
    6319             : 
    6320           0 :     trackOptimizationSuccess();
    6321           0 :     *emitted = true;
    6322           0 :     return Ok();
    6323             : }
    6324             : 
    6325             : AbortReasonOr<Ok>
    6326         141 : IonBuilder::jsop_newobject()
    6327             : {
    6328           0 :     bool emitted = false;
    6329         141 :     startTrackingOptimizations();
    6330             : 
    6331         141 :     JSObject* templateObject = inspector->getTemplateObject(pc);
    6332             : 
    6333           0 :     if (!forceInlineCaches()) {
    6334           0 :         MOZ_TRY(newObjectTryTemplateObject(&emitted, templateObject));
    6335           0 :         if (emitted)
    6336         131 :             return Ok();
    6337             :     }
    6338           0 :     MOZ_TRY(newObjectTrySharedStub(&emitted));
    6339           0 :     if (emitted)
    6340          10 :         return Ok();
    6341             : 
    6342           0 :     MOZ_TRY(newObjectTryVM(&emitted, templateObject));
    6343           0 :     if (emitted)
    6344           0 :         return Ok();
    6345             : 
    6346           0 :     MOZ_CRASH("newobject should have been emited");
    6347             : }
    6348             : 
    6349             : AbortReasonOr<Ok>
    6350          15 : IonBuilder::jsop_initelem()
    6351             : {
    6352          15 :     MOZ_ASSERT(*pc == JSOP_INITELEM || *pc == JSOP_INITHIDDENELEM);
    6353             : 
    6354           0 :     MDefinition* value = current->pop();
    6355           0 :     MDefinition* id = current->pop();
    6356          15 :     MDefinition* obj = current->peek(-1);
    6357             : 
    6358          15 :     bool emitted = false;
    6359             : 
    6360           0 :     if (!forceInlineCaches() && *pc == JSOP_INITELEM) {
    6361           0 :         MOZ_TRY(initOrSetElemTryDense(&emitted, obj, id, value, /* writeHole = */ true));
    6362           0 :         if (emitted)
    6363           1 :             return Ok();
    6364             :     }
    6365             : 
    6366           0 :     MOZ_TRY(initOrSetElemTryCache(&emitted, obj, id, value));
    6367           0 :     if (emitted)
    6368           5 :         return Ok();
    6369             : 
    6370           0 :     MInitElem* initElem = MInitElem::New(alloc(), obj, id, value);
    6371           9 :     current->add(initElem);
    6372             : 
    6373             :     return resumeAfter(initElem);
    6374             : }
    6375             : 
    6376             : AbortReasonOr<Ok>
    6377          12 : IonBuilder::jsop_initelem_inc()
    6378             : {
    6379           0 :     MDefinition* value = current->pop();
    6380           0 :     MDefinition* id = current->pop();
    6381          12 :     MDefinition* obj = current->peek(-1);
    6382             : 
    6383          12 :     bool emitted = false;
    6384             : 
    6385           0 :     MAdd* nextId = MAdd::New(alloc(), id, constantInt(1), MIRType::Int32);
    6386           0 :     current->add(nextId);
    6387          12 :     current->push(nextId);
    6388             : 
    6389           0 :     if (!forceInlineCaches()) {
    6390           0 :         MOZ_TRY(initOrSetElemTryDense(&emitted, obj, id, value, /* writeHole = */ true));
    6391           0 :         if (emitted)
    6392          11 :             return Ok();
    6393             :     }
    6394             : 
    6395           0 :     MOZ_TRY(initOrSetElemTryCache(&emitted, obj, id, value));
    6396           0 :     if (emitted)
    6397           1 :         return Ok();
    6398             : 
    6399           0 :     MCallInitElementArray* initElem = MCallInitElementArray::New(alloc(), obj, id, value);
    6400           0 :     current->add(initElem);
    6401             : 
    6402             :     return resumeAfter(initElem);
    6403             : }
    6404             : 
    6405             : AbortReasonOr<Ok>
    6406         166 : IonBuilder::jsop_initelem_array()
    6407             : {
    6408           0 :     MDefinition* value = current->pop();
    6409         166 :     MDefinition* obj = current->peek(-1);
    6410             : 
    6411             :     // Make sure that arrays have the type being written to them by the
    6412             :     // intializer, and that arrays are marked as non-packed when writing holes
    6413             :     // to them during initialization.
    6414           0 :     bool needStub = false;
    6415         166 :     if (shouldAbortOnPreliminaryGroups(obj)) {
    6416             :         needStub = true;
    6417           0 :     } else if (!obj->resultTypeSet() ||
    6418           0 :                obj->resultTypeSet()->unknownObject() ||
    6419         166 :                obj->resultTypeSet()->getObjectCount() != 1)
    6420             :     {
    6421             :         needStub = true;
    6422             :     } else {
    6423           0 :         MOZ_ASSERT(obj->resultTypeSet()->getObjectCount() == 1);
    6424           0 :         TypeSet::ObjectKey* initializer = obj->resultTypeSet()->getObject(0);
    6425           0 :         if (value->type() == MIRType::MagicHole) {
    6426           0 :             if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED))
    6427           0 :                 needStub = true;
    6428           0 :         } else if (!initializer->unknownProperties()) {
    6429           0 :             HeapTypeSetKey elemTypes = initializer->property(JSID_VOID);
    6430           0 :             if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
    6431           0 :                 elemTypes.freeze(constraints());
    6432           0 :                 needStub = true;
    6433             :             }
    6434             :         }
    6435             :     }
    6436             : 
    6437           0 :     uint32_t index = GET_UINT32(pc);
    6438           0 :     if (needStub) {
    6439           0 :         MOZ_ASSERT(index <= INT32_MAX,
    6440             :                    "the bytecode emitter must fail to compile code that would "
    6441             :                    "produce JSOP_INITELEM_ARRAY with an index exceeding "
    6442             :                    "int32_t range");
    6443           0 :         MCallInitElementArray* store = MCallInitElementArray::New(alloc(), obj,
    6444           0 :                                                                   constantInt(index), value);
    6445           0 :         current->add(store);
    6446             :         return resumeAfter(store);
    6447             :     }
    6448             : 
    6449         166 :     return initializeArrayElement(obj, index, value, /* addResumePoint = */ true);
    6450             : }
    6451             : 
    6452             : AbortReasonOr<Ok>
    6453         166 : IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
    6454             :                                    bool addResumePointAndIncrementInitializedLength)
    6455             : {
    6456           0 :     MConstant* id = MConstant::New(alloc(), Int32Value(index));
    6457         166 :     current->add(id);
    6458             : 
    6459             :     // Get the elements vector.
    6460           0 :     MElements* elements = MElements::New(alloc(), obj);
    6461         166 :     current->add(elements);
    6462             : 
    6463           0 :     if (needsPostBarrier(value))
    6464          27 :         current->add(MPostWriteBarrier::New(alloc(), obj, value));
    6465             : 
    6466           0 :     if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) ||
    6467           1 :         (obj->isNullarySharedStub() &&
    6468           0 :          obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles))
    6469             :     {
    6470           0 :         MInstruction* valueDouble = MToDouble::New(alloc(), value);
    6471           0 :         current->add(valueDouble);
    6472           0 :         value = valueDouble;
    6473             :     }
    6474             : 
    6475             :     // Store the value.
    6476           0 :     MStoreElement* store = MStoreElement::New(alloc(), elements, id, value,
    6477           0 :                                               /* needsHoleCheck = */ false);
    6478         166 :     current->add(store);
    6479             : 
    6480         166 :     if (addResumePointAndIncrementInitializedLength) {
    6481             :         // Update the initialized length. (The template object for this
    6482             :         // array has the array's ultimate length, so the length field is
    6483             :         // already correct: no updating needed.)
    6484           0 :         MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id);
    6485         166 :         current->add(initLength);
    6486             : 
    6487         498 :         MOZ_TRY(resumeAfter(initLength));
    6488             :     }
    6489             : 
    6490         166 :     return Ok();
    6491             : }
    6492             : 
    6493             : AbortReasonOr<Ok>
    6494           0 : IonBuilder::jsop_mutateproto()
    6495             : {
    6496           0 :     MDefinition* value = current->pop();
    6497           0 :     MDefinition* obj = current->peek(-1);
    6498             : 
    6499           0 :     MMutateProto* mutate = MMutateProto::New(alloc(), obj, value);
    6500           0 :     current->add(mutate);
    6501           0 :     return resumeAfter(mutate);
    6502             : }
    6503             : 
    6504             : AbortReasonOr<Ok>
    6505         261 : IonBuilder::jsop_initprop(PropertyName* name)
    6506             : {
    6507         261 :     bool useFastPath = false;
    6508             : 
    6509           0 :     MDefinition* obj = current->peek(-2);
    6510           0 :     if (obj->isNewObject()) {
    6511           0 :         if (JSObject* templateObject = obj->toNewObject()->templateObject()) {
    6512           0 :             if (templateObject->is<PlainObject>()) {
    6513           0 :                 if (templateObject->as<PlainObject>().containsPure(name))
    6514         201 :                     useFastPath = true;
    6515             :             } else {
    6516          46 :                 MOZ_ASSERT(templateObject->as<UnboxedPlainObject>().layout().lookup(name));
    6517             :                 useFastPath = true;
    6518             :             }
    6519             :         }
    6520             :     }
    6521         522 :     MInstructionReverseIterator last = current->rbegin();
    6522             : 
    6523         508 :     if (useFastPath && !forceInlineCaches()) {
    6524             :         // This is definitely initializing an 'own' property of the object, treat
    6525             :         // it as an assignment.
    6526         741 :         MOZ_TRY(jsop_setprop(name));
    6527             :     } else {
    6528           0 :         MDefinition* value = current->pop();
    6529          14 :         MDefinition* obj = current->pop();
    6530             : 
    6531           0 :         bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
    6532          14 :                                                  /* canModify = */ true);
    6533             : 
    6534           0 :         bool emitted = false;
    6535           0 :         MOZ_TRY(setPropTryCache(&emitted, obj, name, value, barrier));
    6536          14 :         MOZ_ASSERT(emitted == true);
    6537             :     }
    6538             : 
    6539             :     // SETPROP pushed the value, instead of the object. Fix this on the stack,
    6540             :     // and check the most recent resume point to see if it needs updating too.
    6541           0 :     current->pop();
    6542           0 :     current->push(obj);
    6543           0 :     for (MInstructionReverseIterator riter = current->rbegin(); riter != last; riter++) {
    6544           0 :         if (MResumePoint* resumePoint = riter->resumePoint()) {
    6545           0 :             MOZ_ASSERT(resumePoint->pc() == pc);
    6546           0 :             if (resumePoint->mode() == MResumePoint::ResumeAfter) {
    6547         261 :                 size_t index = resumePoint->numOperands() - 1;
    6548             :                 resumePoint->replaceOperand(index, obj);
    6549             :             }
    6550             :             break;
    6551             :         }
    6552             :     }
    6553             : 
    6554         261 :     return Ok();
    6555             : }
    6556             : 
    6557             : AbortReasonOr<Ok>
    6558           0 : IonBuilder::jsop_initprop_getter_setter(PropertyName* name)
    6559             : {
    6560           0 :     MDefinition* value = current->pop();
    6561           0 :     MDefinition* obj = current->peek(-1);
    6562             : 
    6563           0 :     MInitPropGetterSetter* init = MInitPropGetterSetter::New(alloc(), obj, name, value);
    6564           0 :     current->add(init);
    6565           0 :     return resumeAfter(init);
    6566             : }
    6567             : 
    6568             : AbortReasonOr<Ok>
    6569           0 : IonBuilder::jsop_initelem_getter_setter()
    6570             : {
    6571           0 :     MDefinition* value = current->pop();
    6572           0 :     MDefinition* id = current->pop();
    6573           0 :     MDefinition* obj = current->peek(-1);
    6574             : 
    6575           0 :     MInitElemGetterSetter* init = MInitElemGetterSetter::New(alloc(), obj, id, value);
    6576           0 :     current->add(init);
    6577           0 :     return resumeAfter(init);
    6578             : }
    6579             : 
    6580             : AbortReasonOr<MBasicBlock*>
    6581        7463 : IonBuilder::newBlock(size_t stackDepth, jsbytecode* pc, MBasicBlock* maybePredecessor)
    6582             : {
    6583        7463 :     MOZ_ASSERT_IF(maybePredecessor, maybePredecessor->stackDepth() == stackDepth);
    6584             : 
    6585           0 :     MBasicBlock* block = MBasicBlock::New(graph(), stackDepth, info(), maybePredecessor,
    6586           0 :                                           bytecodeSite(pc), MBasicBlock::NORMAL);
    6587           0 :     if (!block)
    6588           0 :         return abort(AbortReason::Alloc);
    6589             : 
    6590           0 :     block->setLoopDepth(loopDepth_);
    6591        7463 :     return block;
    6592             : }
    6593             : 
    6594             : AbortReasonOr<MBasicBlock*>
    6595           0 : IonBuilder::newBlock(MBasicBlock* predecessor, jsbytecode* pc, MResumePoint* priorResumePoint)
    6596             : {
    6597           0 :     MBasicBlock* block = MBasicBlock::NewWithResumePoint(graph(), info(), predecessor,
    6598           0 :                                                          bytecodeSite(pc), priorResumePoint);
    6599           0 :     if (!block)
    6600           0 :         return abort(AbortReason::Alloc);
    6601             : 
    6602           0 :     block->setLoopDepth(loopDepth_);
    6603           0 :     return block;
    6604             : }
    6605             : 
    6606             : AbortReasonOr<MBasicBlock*>
    6607           0 : IonBuilder::newBlockPopN(MBasicBlock* predecessor, jsbytecode* pc, uint32_t popped)
    6608             : {
    6609           0 :     MBasicBlock* block = MBasicBlock::NewPopN(graph(), info(), predecessor, bytecodeSite(pc),
    6610           0 :                                               MBasicBlock::NORMAL, popped);
    6611           0 :     if (!block)
    6612           0 :         return abort(AbortReason::Alloc);
    6613             : 
    6614           0 :     block->setLoopDepth(loopDepth_);
    6615           0 :     return block;
    6616             : }
    6617             : 
    6618             : AbortReasonOr<MBasicBlock*>
    6619           1 : IonBuilder::newBlockAfter(MBasicBlock* at, size_t stackDepth, jsbytecode* pc,
    6620             :                           MBasicBlock* maybePredecessor)
    6621             : {
    6622           1 :     MOZ_ASSERT_IF(maybePredecessor, maybePredecessor->stackDepth() == stackDepth);
    6623             : 
    6624           0 :     MBasicBlock* block = MBasicBlock::New(graph(), stackDepth, info(), maybePredecessor,
    6625           0 :                                           bytecodeSite(pc), MBasicBlock::NORMAL);
    6626           0 :     if (!block)
    6627           0 :         return abort(AbortReason::Alloc);
    6628             : 
    6629           0 :     block->setLoopDepth(loopDepth_);
    6630           0 :     block->setHitCount(0); // osr block
    6631           0 :     graph().insertBlockAfter(at, block);
    6632           1 :     return block;
    6633             : }
    6634             : 
    6635             : AbortReasonOr<MBasicBlock*>
    6636           1 : IonBuilder::newOsrPreheader(MBasicBlock* predecessor, jsbytecode* loopEntry,
    6637             :                             jsbytecode* beforeLoopEntry)
    6638             : {
    6639           1 :     MOZ_ASSERT(JSOp(*loopEntry) == JSOP_LOOPENTRY);
    6640           1 :     MOZ_ASSERT(loopEntry == info().osrPc());
    6641             : 
    6642             :     // Create two blocks: one for the OSR entry with no predecessors, one for
    6643             :     // the preheader, which has the OSR entry block as a predecessor. The
    6644             :     // OSR block is always the second block (with id 1).
    6645             :     MBasicBlock* osrBlock;
    6646           0 :     MOZ_TRY_VAR(osrBlock, newBlockAfter(*graph().begin(), predecessor->stackDepth(), loopEntry));
    6647             :     MBasicBlock* preheader;
    6648           0 :     MOZ_TRY_VAR(preheader, newBlock(predecessor, loopEntry));
    6649             : 
    6650           1 :     graph().addBlock(preheader);
    6651             : 
    6652             :     // Give the pre-header the same hit count as the code before the loop.
    6653           2 :     if (script()->hasScriptCounts())
    6654           0 :         preheader->setHitCount(script()->getHitCount(beforeLoopEntry));
    6655             : 
    6656           1 :     MOsrEntry* entry = MOsrEntry::New(alloc());
    6657           1 :     osrBlock->add(entry);
    6658             : 
    6659             :     // Initialize |envChain|.
    6660             :     {
    6661           1 :         uint32_t slot = info().environmentChainSlot();
    6662             : 
    6663             :         MInstruction* envv;
    6664           1 :         if (usesEnvironmentChain()) {
    6665           2 :             envv = MOsrEnvironmentChain::New(alloc(), entry);
    6666             :         } else {
    6667             :             // Use an undefined value if the script does not need its env
    6668             :             // chain, to match the type that is already being tracked for the
    6669             :             // slot.
    6670           0 :             envv = MConstant::New(alloc(), UndefinedValue());
    6671             :         }
    6672             : 
    6673           1 :         osrBlock->add(envv);
    6674           1 :         osrBlock->initSlot(slot, envv);
    6675             :     }
    6676             :     // Initialize |return value|
    6677             :     {
    6678             :         MInstruction* returnValue;
    6679           2 :         if (!script()->noScriptRval())
    6680           0 :             returnValue = MOsrReturnValue::New(alloc(), entry);
    6681             :         else
    6682           0 :             returnValue = MConstant::New(alloc(), UndefinedValue());
    6683           1 :         osrBlock->add(returnValue);
    6684           1 :         osrBlock->initSlot(info().returnValueSlot(), returnValue);
    6685             :     }
    6686             : 
    6687             :     // Initialize arguments object.
    6688           0 :     bool needsArgsObj = info().needsArgsObj();
    6689           0 :     MInstruction* argsObj = nullptr;
    6690           0 :     if (info().hasArguments()) {
    6691           0 :         if (needsArgsObj)
    6692           0 :             argsObj = MOsrArgumentsObject::New(alloc(), entry);
    6693             :         else
    6694           0 :             argsObj = MConstant::New(alloc(), UndefinedValue());
    6695           0 :         osrBlock->add(argsObj);
    6696           0 :         osrBlock->initSlot(info().argsObjSlot(), argsObj);
    6697             :     }
    6698             : 
    6699           0 :     if (info().funMaybeLazy()) {
    6700             :         // Initialize |this| parameter.
    6701           0 :         MParameter* thisv = MParameter::New(alloc(), MParameter::THIS_SLOT, nullptr);
    6702           1 :         osrBlock->add(thisv);
    6703           1 :         osrBlock->initSlot(info().thisSlot(), thisv);
    6704             : 
    6705             :         // Initialize arguments.
    6706           3 :         for (uint32_t i = 0; i < info().nargs(); i++) {
    6707           2 :             uint32_t slot = needsArgsObj ? info().argSlotUnchecked(i) : info().argSlot(i);
    6708             : 
    6709             :             // Only grab arguments from the arguments object if the arguments object
    6710             :             // aliases formals.  If the argsobj does not alias formals, then the
    6711             :             // formals may have been assigned to during interpretation, and that change
    6712             :             // will not be reflected in the argsobj.
    6713           2 :             if (needsArgsObj && info().argsObjAliasesFormals()) {
    6714           0 :                 MOZ_ASSERT(argsObj && argsObj->isOsrArgumentsObject());
    6715             :                 // If this is an aliased formal, then the arguments object
    6716             :                 // contains a hole at this index.  Any references to this
    6717             :                 // variable in the jitcode will come from JSOP_*ALIASEDVAR
    6718             :                 // opcodes, so the slot itself can be set to undefined.  If
    6719             :                 // it's not aliased, it must be retrieved from the arguments
    6720             :                 // object.
    6721             :                 MInstruction* osrv;
    6722           0 :                 if (script()->formalIsAliased(i))
    6723           0 :                     osrv = MConstant::New(alloc(), UndefinedValue());
    6724             :                 else
    6725           0 :                     osrv = MGetArgumentsObjectArg::New(alloc(), argsObj, i);
    6726             : 
    6727           0 :                 osrBlock->add(osrv);
    6728           0 :                 osrBlock->initSlot(slot, osrv);
    6729             :             } else {
    6730           0 :                 MParameter* arg = MParameter::New(alloc(), i, nullptr);
    6731           2 :                 osrBlock->add(arg);
    6732           2 :                 osrBlock->initSlot(slot, arg);
    6733             :             }
    6734             :         }
    6735             :     }
    6736             : 
    6737             :     // Initialize locals.
    6738           0 :     for (uint32_t i = 0; i < info().nlocals(); i++) {
    6739           6 :         uint32_t slot = info().localSlot(i);
    6740           0 :         ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(i);
    6741             : 
    6742           1 :         MOsrValue* osrv = MOsrValue::New(alloc().fallible(), entry, offset);
    6743           0 :         if (!osrv)
    6744           0 :             return abort(AbortReason::Alloc);
    6745           3 :         osrBlock->add(osrv);
    6746           3 :         osrBlock->initSlot(slot, osrv);
    6747             :     }
    6748             : 
    6749             :     // Initialize stack.
    6750           0 :     uint32_t numStackSlots = preheader->stackDepth() - info().firstStackSlot();
    6751           0 :     for (uint32_t i = 0; i < numStackSlots; i++) {
    6752           6 :         uint32_t slot = info().stackSlot(i);
    6753           0 :         ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(info().nlocals() + i);
    6754             : 
    6755           1 :         MOsrValue* osrv = MOsrValue::New(alloc().fallible(), entry, offset);
    6756           0 :         if (!osrv)
    6757           0 :             return abort(AbortReason::Alloc);
    6758           3 :         osrBlock->add(osrv);
    6759           3 :         osrBlock->initSlot(slot, osrv);
    6760             :     }
    6761             : 
    6762             :     // Create an MStart to hold the first valid MResumePoint.
    6763           1 :     MStart* start = MStart::New(alloc());
    6764           1 :     osrBlock->add(start);
    6765             : 
    6766             :     // MOsrValue instructions are infallible, so the first MResumePoint must
    6767             :     // occur after they execute, at the point of the MStart.
    6768           3 :     MOZ_TRY(resumeAt(start, loopEntry));
    6769             : 
    6770             :     // Link the same MResumePoint from the MStart to each MOsrValue.
    6771             :     // This causes logic in ShouldSpecializeInput() to not replace Uses with
    6772             :     // Unboxes in the MResumePiont, so that the MStart always sees Values.
    6773           1 :     if (!osrBlock->linkOsrValues(start))
    6774           0 :         return abort(AbortReason::Alloc);
    6775             : 
    6776             :     // Clone types of the other predecessor of the pre-header to the osr block,
    6777             :     // such as pre-header phi's won't discard specialized type of the
    6778             :     // predecessor.
    6779           1 :     MOZ_ASSERT(predecessor->stackDepth() == osrBlock->stackDepth());
    6780           1 :     MOZ_ASSERT(info().environmentChainSlot() == 0);
    6781             : 
    6782             :     // Treat the OSR values as having the same type as the existing values
    6783             :     // coming in to the loop. These will be fixed up with appropriate
    6784             :     // unboxing and type barriers in finishLoop, once the possible types
    6785             :     // at the loop header are known.
    6786           0 :     for (uint32_t i = info().startArgSlot(); i < osrBlock->stackDepth(); i++) {
    6787           0 :         MDefinition* existing = current->getSlot(i);
    6788           9 :         MDefinition* def = osrBlock->getSlot(i);
    6789           9 :         MOZ_ASSERT_IF(!needsArgsObj || !info().isSlotAliased(i), def->type() == MIRType::Value);
    6790             : 
    6791             :         // Aliased slots are never accessed, since they need to go through
    6792             :         // the callobject. No need to type them here.
    6793           9 :         if (info().isSlotAliased(i))
    6794             :             continue;
    6795             : 
    6796          18 :         def->setResultType(existing->type());
    6797           9 :         def->setResultTypeSet(existing->resultTypeSet());
    6798             :     }
    6799             : 
    6800             :     // Finish the osrBlock.
    6801           1 :     osrBlock->end(MGoto::New(alloc(), preheader));
    6802           0 :     if (!preheader->addPredecessor(alloc(), osrBlock))
    6803           0 :         return abort(AbortReason::Alloc);
    6804           0 :     graph().setOsrBlock(osrBlock);
    6805             : 
    6806           1 :     return preheader;
    6807             : }
    6808             : 
    6809             : AbortReasonOr<MBasicBlock*>
    6810         196 : IonBuilder::newPendingLoopHeader(MBasicBlock* predecessor, jsbytecode* pc, bool osr, bool canOsr,
    6811             :                                  unsigned stackPhiCount)
    6812             : {
    6813             :     // If this site can OSR, all values on the expression stack are part of the loop.
    6814         196 :     if (canOsr)
    6815           0 :         stackPhiCount = predecessor->stackDepth() - info().firstStackSlot();
    6816             : 
    6817           0 :     MBasicBlock* block = MBasicBlock::NewPendingLoopHeader(graph(), info(), predecessor,
    6818           0 :                                                            bytecodeSite(pc), stackPhiCount);
    6819         196 :     if (!block)
    6820           0 :         return abort(AbortReason::Alloc);
    6821             : 
    6822         196 :     if (osr) {
    6823             :         // Incorporate type information from the OSR frame into the loop
    6824             :         // header. The OSR frame may have unexpected types due to type changes
    6825             :         // within the loop body or due to incomplete profiling information,
    6826             :         // in which case this may avoid restarts of loop analysis or bailouts
    6827             :         // during the OSR itself.
    6828             : 
    6829           0 :         MOZ_ASSERT(info().firstLocalSlot() - info().firstArgSlot() ==
    6830             :                    baselineFrame_->argTypes.length());
    6831           1 :         MOZ_ASSERT(block->stackDepth() - info().firstLocalSlot() ==
    6832             :                    baselineFrame_->varTypes.length());
    6833             : 
    6834             :         // Unbox the MOsrValue if it is known to be unboxable.
    6835          10 :         for (uint32_t i = info().startArgSlot(); i < block->stackDepth(); i++) {
    6836             : 
    6837             :             // The value of aliased args and slots are in the callobject. So we can't
    6838             :             // the value from the baseline frame.
    6839           9 :             if (info().isSlotAliased(i))
    6840           0 :                 continue;
    6841             : 
    6842          18 :             MPhi* phi = block->getSlot(i)->toPhi();
    6843             : 
    6844             :             // Get the type from the baseline frame.
    6845           0 :             TypeSet::Type existingType = TypeSet::UndefinedType();
    6846           0 :             uint32_t arg = i - info().firstArgSlot();
    6847           0 :             uint32_t var = i - info().firstLocalSlot();
    6848           0 :             if (info().funMaybeLazy() && i == info().thisSlot())
    6849           0 :                 existingType = baselineFrame_->thisType;
    6850           8 :             else if (arg < info().nargs())
    6851           0 :                 existingType = baselineFrame_->argTypes[arg];
    6852             :             else
    6853           0 :                 existingType = baselineFrame_->varTypes[var];
    6854             : 
    6855           9 :             if (existingType.isSingletonUnchecked())
    6856           2 :                 checkNurseryObject(existingType.singleton());
    6857             : 
    6858             :             // Extract typeset from value.
    6859           0 :             LifoAlloc* lifoAlloc = alloc().lifoAlloc();
    6860             :             TemporaryTypeSet* typeSet =
    6861           1 :                 lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc, existingType);
    6862           0 :             if (!typeSet)
    6863           0 :                 return abort(AbortReason::Alloc);
    6864           1 :             MIRType type = typeSet->getKnownMIRType();
    6865           9 :             if (!phi->addBackedgeType(alloc(), type, typeSet))
    6866           0 :                 return abort(AbortReason::Alloc);
    6867             :         }
    6868             :     }
    6869             : 
    6870         196 :     return block;
    6871             : }
    6872             : 
    6873             : MTest*
    6874           0 : IonBuilder::newTest(MDefinition* ins, MBasicBlock* ifTrue, MBasicBlock* ifFalse)
    6875             : {
    6876           1 :     MTest* test = MTest::New(alloc(), ins, ifTrue, ifFalse);
    6877        1925 :     test->cacheOperandMightEmulateUndefined(constraints());
    6878           0 :     return test;
    6879             : }
    6880             : 
    6881             : // A resume point is a mapping of stack slots to MDefinitions. It is used to
    6882             : // capture the environment such that if a guard fails, and IonMonkey needs
    6883             : // to exit back to the interpreter, the interpreter state can be
    6884             : // reconstructed.
    6885             : //
    6886             : // We capture stack state at critical points:
    6887             : //   * (1) At the beginning of every basic block.
    6888             : //   * (2) After every effectful operation.
    6889             : //
    6890             : // As long as these two properties are maintained, instructions can
    6891             : // be moved, hoisted, or, eliminated without problems, and ops without side
    6892             : // effects do not need to worry about capturing state at precisely the
    6893             : // right point in time.
    6894             : //
    6895             : // Effectful instructions, of course, need to capture state after completion,
    6896             : // where the interpreter will not attempt to repeat the operation. For this,
    6897             : // ResumeAfter must be used. The state is attached directly to the effectful
    6898             : // instruction to ensure that no intermediate instructions could be injected
    6899             : // in between by a future analysis pass.
    6900             : //
    6901             : // During LIR construction, if an instruction can bail back to the interpreter,
    6902             : // we create an LSnapshot, which uses the last known resume point to request
    6903             : // register/stack assignments for every live value.
    6904             : AbortReasonOr<Ok>
    6905           0 : IonBuilder::resume(MInstruction* ins, jsbytecode* pc, MResumePoint::Mode mode)
    6906             : {
    6907           0 :     MOZ_ASSERT(ins->isEffectful() || !ins->isMovable());
    6908             : 
    6909           0 :     MResumePoint* resumePoint = MResumePoint::New(alloc(), ins->block(), pc,
    6910           0 :                                                   mode);
    6911           0 :     if (!resumePoint)
    6912           0 :         return abort(AbortReason::Alloc);
    6913        7066 :     ins->setResumePoint(resumePoint);
    6914        7066 :     return Ok();
    6915             : }
    6916             : 
    6917             : AbortReasonOr<Ok>
    6918           0 : IonBuilder::resumeAt(MInstruction* ins, jsbytecode* pc)
    6919             : {
    6920           1 :     return resume(ins, pc, MResumePoint::ResumeAt);
    6921             : }
    6922             : 
    6923             : AbortReasonOr<Ok>
    6924           0 : IonBuilder::resumeAfter(MInstruction* ins)
    6925             : {
    6926        7065 :     return resume(ins, pc, MResumePoint::ResumeAfter);
    6927             : }
    6928             : 
    6929             : AbortReasonOr<Ok>
    6930        3283 : IonBuilder::maybeInsertResume()
    6931             : {
    6932             :     // Create a resume point at the current position, without an existing
    6933             :     // effectful instruction. This resume point is not necessary for correct
    6934             :     // behavior (see above), but is added to avoid holding any values from the
    6935             :     // previous resume point which are now dead. This shortens the live ranges
    6936             :     // of such values and improves register allocation.
    6937             :     //
    6938             :     // This optimization is not performed outside of loop bodies, where good
    6939             :     // register allocation is not as critical, in order to avoid creating
    6940             :     // excessive resume points.
    6941             : 
    6942        3283 :     if (loopDepth_ == 0)
    6943           0 :         return Ok();
    6944             : 
    6945        2214 :     MNop* ins = MNop::New(alloc());
    6946        2214 :     current->add(ins);
    6947             : 
    6948             :     return resumeAfter(ins);
    6949             : }
    6950             : 
    6951             : void
    6952           0 : IonBuilder::maybeMarkEmpty(MDefinition* ins)
    6953             : {
    6954          25 :     MOZ_ASSERT(ins->type() == MIRType::Value);
    6955             : 
    6956             :     // When one of the operands has no type information, mark the output
    6957             :     // as having no possible types too. This is to avoid degrading
    6958             :     // subsequent analysis.
    6959          35 :     for (size_t i = 0; i < ins->numOperands(); i++) {
    6960          30 :         if (!ins->getOperand(i)->emptyResultTypeSet())
    6961             :             continue;
    6962             : 
    6963           0 :         TemporaryTypeSet* types = alloc().lifoAlloc()->new_<TemporaryTypeSet>();
    6964          25 :         if (types) {
    6965          25 :             ins->setResultTypeSet(types);
    6966             :             return;
    6967             :         }
    6968             :     }
    6969             : }
    6970             : 
    6971             : // Return whether property lookups can be performed effectlessly on clasp.
    6972             : static bool
    6973           0 : ClassHasEffectlessLookup(const Class* clasp)
    6974             : {
    6975           0 :     return (clasp == &UnboxedPlainObject::class_) ||
    6976        1668 :            IsTypedObjectClass(clasp) ||
    6977        1668 :            (clasp->isNative() && !clasp->getOpsLookupProperty());
    6978             : }
    6979             : 
    6980             : // Return whether an object might have a property for name which is not
    6981             : // accounted for by type information.
    6982             : static bool
    6983         346 : ObjectHasExtraOwnProperty(CompileRealm* realm, TypeSet::ObjectKey* object, jsid id)
    6984             : {
    6985             :     // Some typed object properties are not reflected in type information.
    6986         902 :     if (object->isGroup() && object->group()->maybeTypeDescr())
    6987           0 :         return object->group()->typeDescr().hasProperty(realm->runtime()->names(), id);
    6988             : 
    6989         346 :     const Class* clasp = object->clasp();
    6990             : 
    6991             :     // Array |length| properties are not reflected in type information.
    6992         346 :     if (clasp == &ArrayObject::class_)
    6993         204 :         return JSID_IS_ATOM(id, realm->runtime()->names().length);
    6994             : 
    6995             :     // Resolve hooks can install new properties on objects on demand.
    6996         346 :     JSObject* singleton = object->isSingleton() ? object->singleton() : nullptr;
    6997         278 :     return ClassMayResolveId(realm->runtime()->names(), clasp, id, singleton);
    6998             : }
    6999             : 
    7000             : void
    7001         631 : IonBuilder::insertRecompileCheck()
    7002             : {
    7003             :     // No need for recompile checks if this is the highest optimization level.
    7004         631 :     OptimizationLevel curLevel = optimizationInfo().level();
    7005         631 :     if (IonOptimizations.isLastLevel(curLevel))
    7006             :         return;
    7007             : 
    7008             :     // Add recompile check.
    7009             : 
    7010             :     // Get the topmost builder. The topmost script will get recompiled when
    7011             :     // warm-up counter is high enough to justify a higher optimization level.
    7012           0 :     IonBuilder* topBuilder = outermostBuilder();
    7013             : 
    7014             :     // Add recompile check to recompile when the warm-up count reaches the
    7015             :     // threshold of the next optimization level.
    7016           0 :     OptimizationLevel nextLevel = IonOptimizations.nextLevel(curLevel);
    7017           0 :     const OptimizationInfo* info = IonOptimizations.get(nextLevel);
    7018           0 :     uint32_t warmUpThreshold = info->compilerWarmUpThreshold(topBuilder->script());
    7019           0 :     MRecompileCheck* check = MRecompileCheck::New(alloc(), topBuilder->script(), warmUpThreshold,
    7020           0 :                                 MRecompileCheck::RecompileCheck_OptimizationLevel);
    7021           0 :     current->add(check);
    7022             : }
    7023             : 
    7024             : JSObject*
    7025         213 : IonBuilder::testSingletonProperty(JSObject* obj, jsid id)
    7026             : {
    7027             :     // We would like to completely no-op property/global accesses which can
    7028             :     // produce only a particular JSObject. When indicating the access result is
    7029             :     // definitely an object, type inference does not account for the
    7030             :     // possibility that the property is entirely missing from the input object
    7031             :     // and its prototypes (if this happens, a semantic trigger would be hit and
    7032             :     // the pushed types updated, even if there is no type barrier).
    7033             :     //
    7034             :     // If the access definitely goes through obj, either directly or on the
    7035             :     // prototype chain, and the object has singleton type, then the type
    7036             :     // information for that property reflects the value that will definitely be
    7037             :     // read on accesses to the object. If the property is later deleted or
    7038             :     // reconfigured as a getter/setter then the type information for the
    7039             :     // property will change and trigger invalidation.
    7040             : 
    7041           0 :     while (obj) {
    7042         219 :         if (!ClassHasEffectlessLookup(obj->getClass()))
    7043           0 :             return nullptr;
    7044             : 
    7045           0 :         TypeSet::ObjectKey* objKey = TypeSet::ObjectKey::get(obj);
    7046         219 :         if (analysisContext)
    7047           0 :             objKey->ensureTrackedProperty(analysisContext, id);
    7048             : 
    7049         219 :         if (objKey->unknownProperties())
    7050             :             return nullptr;
    7051             : 
    7052           0 :         HeapTypeSetKey property = objKey->property(id);
    7053           0 :         if (property.isOwnProperty(constraints())) {
    7054         210 :             if (obj->isSingleton())
    7055         209 :                 return property.singleton(constraints());
    7056             :             return nullptr;
    7057             :         }
    7058             : 
    7059           9 :         if (ObjectHasExtraOwnProperty(realm, objKey, id))
    7060             :             return nullptr;
    7061             : 
    7062           9 :         obj = checkNurseryObject(obj->staticPrototype());
    7063             :     }
    7064             : 
    7065             :     return nullptr;
    7066             : }
    7067             : 
    7068             : JSObject*
    7069         508 : IonBuilder::testSingletonPropertyTypes(MDefinition* obj, jsid id)
    7070             : {
    7071             :     // As for TestSingletonProperty, but the input is any value in a type set
    7072             :     // rather than a specific object.
    7073             : 
    7074         508 :     TemporaryTypeSet* types = obj->resultTypeSet();
    7075        1016 :     if (types && types->unknownObject())
    7076             :         return nullptr;
    7077             : 
    7078           0 :     JSObject* objectSingleton = types ? types->maybeSingleton() : nullptr;
    7079         312 :     if (objectSingleton)
    7080           0 :         return testSingletonProperty(objectSingleton, id);
    7081             : 
    7082           0 :     MIRType objType = obj->type();
    7083         288 :     if (objType == MIRType::Value && types)
    7084          42 :         objType = types->getKnownMIRType();
    7085             : 
    7086             :     JSProtoKey key;
    7087         288 :     switch (objType) {
    7088             :       case MIRType::String:
    7089             :         key = JSProto_String;
    7090             :         break;
    7091             : 
    7092             :       case MIRType::Symbol:
    7093           0 :         key = JSProto_Symbol;
    7094           0 :         break;
    7095             : 
    7096             :       case MIRType::Int32:
    7097             :       case MIRType::Double:
    7098           0 :         key = JSProto_Number;
    7099           0 :         break;
    7100             : 
    7101             :       case MIRType::Boolean:
    7102           0 :         key = JSProto_Boolean;
    7103           0 :         break;
    7104             : 
    7105             :       case MIRType::Object: {
    7106         242 :         if (!types)
    7107             :             return nullptr;
    7108             : 
    7109             :         // For property accesses which may be on many objects, we just need to
    7110             :         // find a prototype common to all the objects; if that prototype
    7111             :         // has the singleton property, the access will not be on a missing property.
    7112             :         JSObject* singleton = nullptr;
    7113           0 :         for (unsigned i = 0; i < types->getObjectCount(); i++) {
    7114           0 :             TypeSet::ObjectKey* key = types->getObject(i);
    7115           0 :             if (!key)
    7116           0 :                 continue;
    7117         255 :             if (analysisContext)
    7118           0 :                 key->ensureTrackedProperty(analysisContext, id);
    7119             : 
    7120           0 :             const Class* clasp = key->clasp();
    7121           0 :             if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(realm, key, id))
    7122         120 :                 return nullptr;
    7123           0 :             if (key->unknownProperties())
    7124             :                 return nullptr;
    7125         253 :             HeapTypeSetKey property = key->property(id);
    7126         253 :             if (property.isOwnProperty(constraints()))
    7127             :                 return nullptr;
    7128             : 
    7129           0 :             if (JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull())) {
    7130             :                 // Test this type.
    7131         185 :                 JSObject* thisSingleton = testSingletonProperty(proto, id);
    7132           0 :                 if (!thisSingleton)
    7133             :                     return nullptr;
    7134         144 :                 if (singleton) {
    7135          13 :                     if (thisSingleton != singleton)
    7136             :                         return nullptr;
    7137             :                 } else {
    7138             :                     singleton = thisSingleton;
    7139             :                 }
    7140             :             } else {
    7141             :                 // Can't be on the prototype chain with no prototypes...
    7142             :                 return nullptr;
    7143             :             }
    7144             :         }
    7145             :         return singleton;
    7146             :       }
    7147             :       default:
    7148             :         return nullptr;
    7149             :     }
    7150             : 
    7151           8 :     if (JSObject* proto = script()->global().maybeGetPrototype(key))
    7152           4 :         return testSingletonProperty(proto, id);
    7153             : 
    7154             :     return nullptr;
    7155             : }
    7156             : 
    7157             : AbortReasonOr<bool>
    7158           0 : IonBuilder::testNotDefinedProperty(MDefinition* obj, jsid id, bool ownProperty /* = false */)
    7159             : {
    7160           0 :     TemporaryTypeSet* types = obj->resultTypeSet();
    7161          96 :     if (!types || types->unknownObject() || types->getKnownMIRType() != MIRType::Object)
    7162           0 :         return false;
    7163             : 
    7164           0 :     for (unsigned i = 0, count = types->getObjectCount(); i < count; i++) {
    7165          25 :         TypeSet::ObjectKey* key = types->getObject(i);
    7166          25 :         if (!key)
    7167             :             continue;
    7168             : 
    7169             :         while (true) {
    7170          82 :             if (!alloc().ensureBallast())
    7171           0 :                 return abort(AbortReason::Alloc);
    7172             : 
    7173          82 :             if (!key->hasStableClassAndProto(constraints()) || key->unknownProperties())
    7174           0 :                 return false;
    7175             : 
    7176           0 :             const Class* clasp = key->clasp();
    7177          82 :             if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(realm, key, id))
    7178           0 :                 return false;
    7179             : 
    7180             :             // If the object is a singleton, we can do a lookup now to avoid
    7181             :             // unnecessary invalidations later on, in case the property types
    7182             :             // have not yet been instantiated.
    7183           0 :             if (key->isSingleton() &&
    7184         253 :                 key->singleton()->is<NativeObject>() &&
    7185           0 :                 key->singleton()->as<NativeObject>().lookupPure(id))
    7186             :             {
    7187           0 :                 return false;
    7188             :             }
    7189             : 
    7190           0 :             HeapTypeSetKey property = key->property(id);
    7191          82 :             if (property.isOwnProperty(constraints()))
    7192           6 :                 return false;
    7193             : 
    7194             :             // If we only care about own properties don't check the proto.
    7195          76 :             if (ownProperty)
    7196             :                 break;
    7197             : 
    7198          76 :             JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
    7199           0 :             if (!proto)
    7200             :                 break;
    7201          57 :             key = TypeSet::ObjectKey::get(proto);
    7202          57 :         }
    7203             :     }
    7204             : 
    7205          19 :     return true;
    7206             : }
    7207             : 
    7208             : AbortReasonOr<Ok>
    7209           0 : IonBuilder::pushTypeBarrier(MDefinition* def, TemporaryTypeSet* observed, BarrierKind kind)
    7210             : {
    7211           0 :     MOZ_ASSERT(def == current->peek(-1));
    7212             : 
    7213           1 :     MDefinition* replace = addTypeBarrier(current->pop(), observed, kind);
    7214        3972 :     if (!replace)
    7215           0 :         return abort(AbortReason::Alloc);
    7216             : 
    7217        3972 :     current->push(replace);
    7218        3972 :     return Ok();
    7219             : }
    7220             : 
    7221             : // Given an observed type set, annotates the IR as much as possible:
    7222             : // (1) If no type information is provided, the given value is returned.
    7223             : // (2) If a single type definitely exists, and no type barrier is needed,
    7224             : //     then an infallible unbox instruction is returned.
    7225             : // (3) If a type barrier is needed, but has an unknown type set, the given
    7226             : //     value is returned.
    7227             : // (4) Lastly, a type barrier instruction is added and returned.
    7228             : MDefinition*
    7229        4020 : IonBuilder::addTypeBarrier(MDefinition* def, TemporaryTypeSet* observed, BarrierKind kind,
    7230             :                            MTypeBarrier** pbarrier)
    7231             : {
    7232             :     // Barriers are never needed for instructions whose result will not be used.
    7233        8040 :     if (BytecodeIsPopped(pc))
    7234             :         return def;
    7235             : 
    7236             :     // If the instruction has no side effects, we'll resume the entire operation.
    7237             :     // The actual type barrier will occur in the interpreter. If the
    7238             :     // instruction is effectful, even if it has a singleton type, there
    7239             :     // must be a resume point capturing the original def, and resuming
    7240             :     // to that point will explicitly monitor the new type.
    7241           0 :     if (kind == BarrierKind::NoBarrier) {
    7242           0 :         MDefinition* replace = ensureDefiniteType(def, observed->getKnownMIRType());
    7243         354 :         replace->setResultTypeSet(observed);
    7244         177 :         return replace;
    7245             :     }
    7246             : 
    7247        7086 :     if (observed->unknown())
    7248             :         return def;
    7249             : 
    7250        7086 :     MTypeBarrier* barrier = MTypeBarrier::New(alloc(), def, observed, kind);
    7251           0 :     current->add(barrier);
    7252             : 
    7253        3543 :     if (pbarrier)
    7254           0 :         *pbarrier = barrier;
    7255             : 
    7256           0 :     if (barrier->type() == MIRType::Undefined)
    7257           0 :         return constant(UndefinedValue());
    7258        3534 :     if (barrier->type() == MIRType::Null)
    7259           3 :         return constant(NullValue());
    7260             : 
    7261             :     return barrier;
    7262             : }
    7263             : 
    7264             : AbortReasonOr<Ok>
    7265           0 : IonBuilder::pushDOMTypeBarrier(MInstruction* ins, TemporaryTypeSet* observed, JSFunction* func)
    7266             : {
    7267           0 :     MOZ_ASSERT(func && func->isNative() && func->hasJitInfo());
    7268             : 
    7269           0 :     const JSJitInfo* jitinfo = func->jitInfo();
    7270           0 :     bool barrier = DOMCallNeedsBarrier(jitinfo, observed);
    7271             :     // Need to be a bit careful: if jitinfo->returnType is JSVAL_TYPE_DOUBLE but
    7272             :     // types->getKnownMIRType() is MIRType::Int32, then don't unconditionally
    7273             :     // unbox as a double.  Instead, go ahead and barrier on having an int type,
    7274             :     // since we know we need a barrier anyway due to the type mismatch.  This is
    7275             :     // the only situation in which TI actually has more information about the
    7276             :     // JSValueType than codegen can, short of jitinfo->returnType just being
    7277             :     // JSVAL_TYPE_UNKNOWN.
    7278           0 :     MDefinition* replace = ins;
    7279           0 :     if (jitinfo->returnType() != JSVAL_TYPE_DOUBLE ||
    7280           0 :         observed->getKnownMIRType() != MIRType::Int32) {
    7281           0 :         replace = ensureDefiniteType(ins, MIRTypeFromValueType(jitinfo->returnType()));
    7282           0 :         if (replace != ins) {
    7283           0 :             current->pop();
    7284           0 :             current->push(replace);
    7285             :         }
    7286             :     } else {
    7287           0 :         MOZ_ASSERT(barrier);
    7288             :     }
    7289             : 
    7290             :     return pushTypeBarrier(replace, observed,
    7291           0 :                            barrier ? BarrierKind::TypeSet : BarrierKind::NoBarrier);
    7292             : }
    7293             : 
    7294             : MDefinition*
    7295         461 : IonBuilder::ensureDefiniteType(MDefinition* def, MIRType definiteType)
    7296             : {
    7297             :     MInstruction* replace;
    7298           0 :     switch (definiteType) {
    7299             :       case MIRType::Undefined:
    7300           0 :         def->setImplicitlyUsedUnchecked();
    7301          32 :         replace = MConstant::New(alloc(), UndefinedValue());
    7302          32 :         break;
    7303             : 
    7304             :       case MIRType::Null:
    7305           0 :         def->setImplicitlyUsedUnchecked();
    7306           3 :         replace = MConstant::New(alloc(), NullValue());
    7307           3 :         break;
    7308             : 
    7309             :       case MIRType::Value:
    7310             :         return def;
    7311             : 
    7312             :       default: {
    7313           0 :         if (def->type() != MIRType::Value) {
    7314           0 :             if (def->type() == MIRType::Int32 && definiteType == MIRType::Double) {
    7315           0 :                 replace = MToDouble::New(alloc(), def);
    7316           0 :                 break;
    7317             :             }
    7318             :             return def;
    7319             :         }
    7320         136 :         replace = MUnbox::New(alloc(), def, definiteType, MUnbox::Infallible);
    7321         136 :         break;
    7322             :       }
    7323             :     }
    7324             : 
    7325         171 :     current->add(replace);
    7326         171 :     return replace;
    7327             : }
    7328             : 
    7329             : static size_t
    7330           0 : NumFixedSlots(JSObject* object)
    7331             : {
    7332             :     // Note: we can't use object->numFixedSlots() here, as this will read the
    7333             :     // shape and can race with the main thread if we are building off thread.
    7334             :     // The allocation kind and object class (which goes through the type) can
    7335             :     // be read freely, however.
    7336           0 :     gc::AllocKind kind = object->asTenured().getAllocKind();
    7337           0 :     return gc::GetGCKindSlots(kind, object->getClass());
    7338             : }
    7339             : 
    7340             : static bool
    7341           0 : IsUninitializedGlobalLexicalSlot(JSObject* obj, PropertyName* name)
    7342             : {
    7343           0 :     LexicalEnvironmentObject &globalLexical = obj->as<LexicalEnvironmentObject>();
    7344           0 :     MOZ_ASSERT(globalLexical.isGlobal());
    7345           0 :     Shape* shape = globalLexical.lookupPure(name);
    7346           0 :     if (!shape)
    7347             :         return false;
    7348           0 :     return globalLexical.getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL);
    7349             : }
    7350             : 
    7351             : AbortReasonOr<Ok>
    7352           5 : IonBuilder::getStaticName(bool* emitted, JSObject* staticObject, PropertyName* name,
    7353             :                           MDefinition* lexicalCheck)
    7354             : {
    7355           0 :     MOZ_ASSERT(*emitted == false);
    7356             : 
    7357           0 :     jsid id = NameToId(name);
    7358             : 
    7359           0 :     bool isGlobalLexical = staticObject->is<LexicalEnvironmentObject>() &&
    7360           5 :                            staticObject->as<LexicalEnvironmentObject>().isGlobal();
    7361          10 :     MOZ_ASSERT(isGlobalLexical ||
    7362             :                staticObject->is<GlobalObject>() ||
    7363             :                staticObject->is<CallObject>() ||
    7364             :                staticObject->is<ModuleEnvironmentObject>());
    7365           5 :     MOZ_ASSERT(staticObject->isSingleton());
    7366             : 
    7367             :     // Always emit the lexical check. This could be optimized, but is
    7368             :     // currently not for simplicity's sake.
    7369           5 :     if (lexicalCheck)
    7370           0 :         return Ok();
    7371             : 
    7372           0 :     TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(staticObject);
    7373           5 :     if (analysisContext)
    7374           0 :         staticKey->ensureTrackedProperty(analysisContext, NameToId(name));
    7375             : 
    7376           5 :     if (staticKey->unknownProperties())
    7377           0 :         return Ok();
    7378             : 
    7379           0 :     HeapTypeSetKey property = staticKey->property(id);
    7380           0 :     if (!property.maybeTypes() ||
    7381           5 :         !property.maybeTypes()->definiteProperty() ||
    7382           0 :         property.nonData(constraints()))
    7383             :     {
    7384             :         // The property has been reconfigured as non-configurable, non-enumerable
    7385             :         // or non-writable.
    7386           5 :         return Ok();
    7387             :     }
    7388             : 
    7389             :     // Don't optimize global lexical bindings if they aren't initialized at
    7390             :     // compile time.
    7391           0 :     if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name))
    7392           0 :         return Ok();
    7393             : 
    7394           0 :     *emitted = true;
    7395             : 
    7396           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    7397           0 :     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
    7398             :                                                        staticKey, name, types,
    7399           0 :                                                        /* updateObserved = */ true);
    7400             : 
    7401           0 :     if (barrier == BarrierKind::NoBarrier) {
    7402             :         // Try to inline properties holding a known constant object.
    7403           0 :         JSObject* singleton = types->maybeSingleton();
    7404           0 :         if (singleton) {
    7405           0 :             if (testSingletonProperty(staticObject, id) == singleton) {
    7406           0 :                 pushConstant(ObjectValue(*singleton));
    7407           0 :                 return Ok();
    7408             :             }
    7409             :         }
    7410             : 
    7411             :         // Try to inline properties that have never been overwritten.
    7412           0 :         Value constantValue;
    7413           0 :         if (property.constant(constraints(), &constantValue)) {
    7414           0 :             pushConstant(constantValue);
    7415           0 :             return Ok();
    7416             :         }
    7417             :     }
    7418             : 
    7419           0 :     MOZ_TRY(loadStaticSlot(staticObject, barrier, types, property.maybeTypes()->definiteSlot()));
    7420             : 
    7421           0 :     return Ok();
    7422             : }
    7423             : 
    7424             : AbortReasonOr<Ok>
    7425           0 : IonBuilder::loadStaticSlot(JSObject* staticObject, BarrierKind barrier, TemporaryTypeSet* types,
    7426             :                            uint32_t slot)
    7427             : {
    7428           0 :     if (barrier == BarrierKind::NoBarrier) {
    7429             :         // Try to inline properties that can only have one value.
    7430           0 :         MIRType knownType = types->getKnownMIRType();
    7431           0 :         if (knownType == MIRType::Undefined) {
    7432           0 :             pushConstant(UndefinedValue());
    7433           0 :             return Ok();
    7434             :         }
    7435           0 :         if (knownType == MIRType::Null) {
    7436           0 :             pushConstant(NullValue());
    7437           0 :             return Ok();
    7438             :         }
    7439             :     }
    7440             : 
    7441           0 :     MInstruction* obj = constant(ObjectValue(*staticObject));
    7442             : 
    7443           0 :     MIRType rvalType = types->getKnownMIRType();
    7444           0 :     if (barrier != BarrierKind::NoBarrier)
    7445           0 :         rvalType = MIRType::Value;
    7446             : 
    7447           0 :     return loadSlot(obj, slot, NumFixedSlots(staticObject), rvalType, barrier, types);
    7448             : }
    7449             : 
    7450             : // Whether a write of the given value may need a post-write barrier for GC purposes.
    7451             : bool
    7452           0 : IonBuilder::needsPostBarrier(MDefinition* value)
    7453             : {
    7454        1158 :     CompileZone* zone = realm->zone();
    7455           0 :     if (!zone->nurseryExists())
    7456             :         return false;
    7457           0 :     if (value->mightBeType(MIRType::Object))
    7458             :         return true;
    7459           0 :     if (value->mightBeType(MIRType::String) && zone->canNurseryAllocateStrings())
    7460             :         return true;
    7461         886 :     return false;
    7462             : }
    7463             : 
    7464             : AbortReasonOr<Ok>
    7465           0 : IonBuilder::setStaticName(JSObject* staticObject, PropertyName* name)
    7466             : {
    7467           0 :     jsid id = NameToId(name);
    7468             : 
    7469           0 :     bool isGlobalLexical = staticObject->is<LexicalEnvironmentObject>() &&
    7470           0 :                            staticObject->as<LexicalEnvironmentObject>().isGlobal();
    7471           0 :     MOZ_ASSERT(isGlobalLexical ||
    7472             :                staticObject->is<GlobalObject>() ||
    7473             :                staticObject->is<CallObject>());
    7474             : 
    7475           0 :     MDefinition* value = current->peek(-1);
    7476             : 
    7477           0 :     TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(staticObject);
    7478           0 :     if (staticKey->unknownProperties())
    7479           0 :         return jsop_setprop(name);
    7480             : 
    7481           0 :     HeapTypeSetKey property = staticKey->property(id);
    7482           0 :     if (!property.maybeTypes() ||
    7483           0 :         !property.maybeTypes()->definiteProperty() ||
    7484           0 :         property.nonData(constraints()) ||
    7485           0 :         property.nonWritable(constraints()))
    7486             :     {
    7487             :         // The property has been reconfigured as non-configurable, non-enumerable
    7488             :         // or non-writable.
    7489           0 :         return jsop_setprop(name);
    7490             :     }
    7491             : 
    7492           0 :     if (!CanWriteProperty(alloc(), constraints(), property, value))
    7493           0 :         return jsop_setprop(name);
    7494             : 
    7495             :     // Don't optimize global lexical bindings if they aren't initialized at
    7496             :     // compile time.
    7497           0 :     if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name))
    7498           0 :         return jsop_setprop(name);
    7499             : 
    7500           0 :     current->pop();
    7501             : 
    7502             :     // Pop the bound object on the stack.
    7503           0 :     MDefinition* obj = current->pop();
    7504           0 :     MOZ_ASSERT(&obj->toConstant()->toObject() == staticObject);
    7505             : 
    7506           0 :     if (needsPostBarrier(value))
    7507           0 :         current->add(MPostWriteBarrier::New(alloc(), obj, value));
    7508             : 
    7509             :     // If the property has a known type, we may be able to optimize typed stores by not
    7510             :     // storing the type tag.
    7511           0 :     MIRType slotType = MIRType::None;
    7512           0 :     MIRType knownType = property.knownMIRType(constraints());
    7513           0 :     if (knownType != MIRType::Value)
    7514           0 :         slotType = knownType;
    7515             : 
    7516           0 :     bool needsPreBarrier = property.needsBarrier(constraints());
    7517           0 :     return storeSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
    7518           0 :                      value, needsPreBarrier, slotType);
    7519             : }
    7520             : 
    7521             : JSObject*
    7522           0 : IonBuilder::testGlobalLexicalBinding(PropertyName* name)
    7523             : {
    7524           5 :     MOZ_ASSERT(JSOp(*pc) == JSOP_BINDGNAME ||
    7525             :                JSOp(*pc) == JSOP_GETGNAME ||
    7526             :                JSOp(*pc) == JSOP_SETGNAME ||
    7527             :                JSOp(*pc) == JSOP_STRICTSETGNAME);
    7528             : 
    7529             :     // The global isn't the global lexical env's prototype, but its enclosing
    7530             :     // env. Test for the existence of |name| manually on the global lexical
    7531             :     // env. If it is not found, look for it on the global itself.
    7532             : 
    7533           0 :     NativeObject* obj = &script()->global().lexicalEnvironment();
    7534           0 :     TypeSet::ObjectKey* lexicalKey = TypeSet::ObjectKey::get(obj);
    7535           0 :     jsid id = NameToId(name);
    7536           5 :     if (analysisContext)
    7537           0 :         lexicalKey->ensureTrackedProperty(analysisContext, id);
    7538             : 
    7539             :     // If the property is not found on the global lexical env but it is found
    7540             :     // on the global and is configurable, try to freeze the typeset for its
    7541             :     // non-existence.  If we don't have type information then fail.
    7542             :     //
    7543             :     // In the case that it is found on the global but is non-configurable,
    7544             :     // the binding cannot be shadowed by a global lexical binding.
    7545           0 :     Maybe<HeapTypeSetKey> lexicalProperty;
    7546           0 :     if (!lexicalKey->unknownProperties())
    7547           0 :         lexicalProperty.emplace(lexicalKey->property(id));
    7548           0 :     Shape* shape = obj->lookupPure(name);
    7549           0 :     if (shape) {
    7550           0 :         if ((JSOp(*pc) != JSOP_GETGNAME && !shape->writable()) ||
    7551           0 :             obj->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
    7552             :         {
    7553             :             return nullptr;
    7554             :         }
    7555             :     } else {
    7556           0 :         shape = script()->global().lookupPure(name);
    7557           0 :         if (!shape || shape->configurable()) {
    7558           3 :             if (lexicalProperty.isSome())
    7559           3 :                 MOZ_ALWAYS_FALSE(lexicalProperty->isOwnProperty(constraints()));
    7560             :             else
    7561             :                 return nullptr;
    7562             :         }
    7563          10 :         obj = &script()->global();
    7564             :     }
    7565             : 
    7566             :     return obj;
    7567             : }
    7568             : 
    7569             : AbortReasonOr<Ok>
    7570           5 : IonBuilder::jsop_getgname(PropertyName* name)
    7571             : {
    7572             :     // Optimize undefined/NaN/Infinity first. We must ensure we handle these
    7573             :     // cases *exactly* like Baseline, because it's invalid to add an Ion IC or
    7574             :     // VM call (that might trigger invalidation) if there's no Baseline IC for
    7575             :     // this op.
    7576           0 :     if (name == names().undefined) {
    7577           0 :         pushConstant(UndefinedValue());
    7578           0 :         return Ok();
    7579             :     }
    7580           0 :     if (name == names().NaN) {
    7581           0 :         pushConstant(realm->runtime()->NaNValue());
    7582           0 :         return Ok();
    7583             :     }
    7584           0 :     if (name == names().Infinity) {
    7585           0 :         pushConstant(realm->runtime()->positiveInfinityValue());
    7586           0 :         return Ok();
    7587             :     }
    7588             : 
    7589           0 :     if (JSObject* obj = testGlobalLexicalBinding(name)) {
    7590           0 :         bool emitted = false;
    7591           0 :         MOZ_TRY(getStaticName(&emitted, obj, name));
    7592           5 :         if (emitted)
    7593           0 :             return Ok();
    7594             : 
    7595           0 :         if (!forceInlineCaches() && obj->is<GlobalObject>()) {
    7596           0 :             TemporaryTypeSet* types = bytecodeTypes(pc);
    7597           0 :             MDefinition* globalObj = constant(ObjectValue(*obj));
    7598           0 :             MOZ_TRY(getPropTryCommonGetter(&emitted, globalObj, name, types));
    7599           5 :             if (emitted)
    7600           0 :                 return Ok();
    7601             :         }
    7602             :     }
    7603             : 
    7604           5 :     return jsop_getname(name);
    7605             : }
    7606             : 
    7607             : AbortReasonOr<Ok>
    7608         178 : IonBuilder::jsop_getname(PropertyName* name)
    7609             : {
    7610             :     MDefinition* object;
    7611         414 :     if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope())
    7612           0 :         object = constant(ObjectValue(script()->global().lexicalEnvironment()));
    7613             :     else
    7614           0 :         object = current->environmentChain();
    7615             : 
    7616           0 :     MGetNameCache* ins = MGetNameCache::New(alloc(), object);
    7617         178 :     current->add(ins);
    7618           0 :     current->push(ins);
    7619             : 
    7620           0 :     MOZ_TRY(resumeAfter(ins));
    7621             : 
    7622         356 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    7623         178 :     return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
    7624             : }
    7625             : 
    7626             : AbortReasonOr<Ok>
    7627           0 : IonBuilder::jsop_intrinsic(PropertyName* name)
    7628             : {
    7629           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    7630             : 
    7631        2042 :     Value vp = UndefinedValue();
    7632             :     // If the intrinsic value doesn't yet exist, we haven't executed this
    7633             :     // opcode yet, so we need to get it and monitor the result.
    7634        4084 :     if (!script()->global().maybeExistingIntrinsicValue(name, &vp)) {
    7635           0 :         MCallGetIntrinsicValue* ins = MCallGetIntrinsicValue::New(alloc(), name);
    7636             : 
    7637         558 :         current->add(ins);
    7638           0 :         current->push(ins);
    7639             : 
    7640           0 :         MOZ_TRY(resumeAfter(ins));
    7641             : 
    7642         558 :         return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
    7643             :     }
    7644             : 
    7645        2968 :     if (types->empty())
    7646         252 :         types->addType(TypeSet::GetValueType(vp), alloc().lifoAlloc());
    7647             : 
    7648             :     // Bake in the intrinsic, guaranteed to exist because a non-empty typeset
    7649             :     // means the intrinsic was successfully gotten in the VM call above.
    7650             :     // Assert that TI agrees with us on the type.
    7651           0 :     MOZ_ASSERT(types->hasType(TypeSet::GetValueType(vp)));
    7652             : 
    7653        1484 :     pushConstant(vp);
    7654        1484 :     return Ok();
    7655             : }
    7656             : 
    7657             : AbortReasonOr<Ok>
    7658           0 : IonBuilder::jsop_getimport(PropertyName* name)
    7659             : {
    7660           0 :     ModuleEnvironmentObject* env = GetModuleEnvironmentForScript(script());
    7661           0 :     MOZ_ASSERT(env);
    7662             : 
    7663             :     Shape* shape;
    7664             :     ModuleEnvironmentObject* targetEnv;
    7665           0 :     MOZ_ALWAYS_TRUE(env->lookupImport(NameToId(name), &targetEnv, &shape));
    7666             : 
    7667           0 :     PropertyName* localName = JSID_TO_STRING(shape->propid())->asAtom().asPropertyName();
    7668           0 :     bool emitted = false;
    7669           0 :     MOZ_TRY(getStaticName(&emitted, targetEnv, localName));
    7670             : 
    7671           0 :     if (!emitted) {
    7672             :         // This can happen if we don't have type information.
    7673           0 :         TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(targetEnv);
    7674           0 :         TemporaryTypeSet* types = bytecodeTypes(pc);
    7675           0 :         BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
    7676             :                                                            staticKey, name, types,
    7677           0 :                                                            /* updateObserved = */ true);
    7678             : 
    7679           0 :         MOZ_TRY(loadStaticSlot(targetEnv, barrier, types, shape->slot()));
    7680             :     }
    7681             : 
    7682             :     // In the rare case where this import hasn't been initialized already (we
    7683             :     // have an import cycle where modules reference each other's imports), emit
    7684             :     // a check.
    7685           0 :     if (targetEnv->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) {
    7686             :         MDefinition* checked;
    7687           0 :         MOZ_TRY_VAR(checked, addLexicalCheck(current->pop()));
    7688           0 :         current->push(checked);
    7689             :     }
    7690             : 
    7691           0 :     return Ok();
    7692             : }
    7693             : 
    7694             : AbortReasonOr<Ok>
    7695           0 : IonBuilder::jsop_bindname(PropertyName* name)
    7696             : {
    7697             :     MDefinition* envChain;
    7698           0 :     if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope())
    7699           0 :         envChain = constant(ObjectValue(script()->global().lexicalEnvironment()));
    7700             :     else
    7701           0 :         envChain = current->environmentChain();
    7702             : 
    7703           0 :     MBindNameCache* ins = MBindNameCache::New(alloc(), envChain, name, script(), pc);
    7704           0 :     current->add(ins);
    7705           0 :     current->push(ins);
    7706             : 
    7707           0 :     return resumeAfter(ins);
    7708             : }
    7709             : 
    7710             : AbortReasonOr<Ok>
    7711           0 : IonBuilder::jsop_bindvar()
    7712             : {
    7713           0 :     MOZ_ASSERT(usesEnvironmentChain());
    7714           0 :     MCallBindVar* ins = MCallBindVar::New(alloc(), current->environmentChain());
    7715           0 :     current->add(ins);
    7716           0 :     current->push(ins);
    7717           0 :     return Ok();
    7718             : }
    7719             : 
    7720             : static MIRType
    7721             : GetElemKnownType(bool needsHoleCheck, TemporaryTypeSet* types)
    7722             : {
    7723           0 :     MIRType knownType = types->getKnownMIRType();
    7724             : 
    7725             :     // Null and undefined have no payload so they can't be specialized.
    7726             :     // Since folding null/undefined while building SSA is not safe (see the
    7727             :     // comment in IsPhiObservable), we just add an untyped load instruction
    7728             :     // and rely on pushTypeBarrier and DCE to replace it with a null/undefined
    7729             :     // constant.
    7730           0 :     if (knownType == MIRType::Undefined || knownType == MIRType::Null)
    7731           0 :         knownType = MIRType::Value;
    7732             : 
    7733             :     // Different architectures may want typed element reads which require
    7734             :     // hole checks to be done as either value or typed reads.
    7735           0 :     if (needsHoleCheck && !LIRGenerator::allowTypedElementHoleCheck())
    7736           0 :         knownType = MIRType::Value;
    7737             : 
    7738             :     return knownType;
    7739             : }
    7740             : 
    7741             : AbortReasonOr<Ok>
    7742           0 : IonBuilder::jsop_getelem()
    7743             : {
    7744           0 :     startTrackingOptimizations();
    7745             : 
    7746         564 :     MDefinition* index = current->pop();
    7747           0 :     MDefinition* obj = current->pop();
    7748             : 
    7749        1128 :     trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet());
    7750        1128 :     trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet());
    7751             : 
    7752             :     // Always use a call if we are performing analysis and not actually
    7753             :     // emitting code, to simplify later analysis.
    7754         564 :     if (info().isAnalysis() || shouldAbortOnPreliminaryGroups(obj)) {
    7755           0 :         MInstruction* ins = MCallGetElement::New(alloc(), obj, index);
    7756             : 
    7757         267 :         current->add(ins);
    7758           0 :         current->push(ins);
    7759             : 
    7760           0 :         MOZ_TRY(resumeAfter(ins));
    7761             : 
    7762         534 :         TemporaryTypeSet* types = bytecodeTypes(pc);
    7763         267 :         return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
    7764             :     }
    7765             : 
    7766         297 :     bool emitted = false;
    7767             : 
    7768             :     // Handle lazy-arguments first. We have to do this even if forceInlineCaches
    7769             :     // is true (lazy arguments cannot escape to the IC). Like the code in
    7770             :     // IonBuilder::jsop_getprop, we only do this if we're not in analysis mode,
    7771             :     // to avoid unnecessary analysis aborts.
    7772           0 :     if (obj->mightBeType(MIRType::MagicOptimizedArguments) && !info().isAnalysis()) {
    7773           0 :         trackOptimizationAttempt(TrackedStrategy::GetElem_Arguments);
    7774           0 :         MOZ_TRY(getElemTryArguments(&emitted, obj, index));
    7775           2 :         if (emitted)
    7776           0 :             return Ok();
    7777             : 
    7778           0 :         trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlinedConstant);
    7779           0 :         MOZ_TRY(getElemTryArgumentsInlinedConstant(&emitted, obj, index));
    7780           0 :         if (emitted)
    7781           0 :             return Ok();
    7782             : 
    7783           0 :         trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlinedSwitch);
    7784           0 :         MOZ_TRY(getElemTryArgumentsInlinedIndex(&emitted, obj, index));
    7785           0 :         if (emitted)
    7786           0 :             return Ok();
    7787             : 
    7788           0 :         if (script()->argumentsHasVarBinding()) {
    7789           0 :             trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
    7790           0 :             return abort(AbortReason::Disable, "Type is not definitely lazy arguments.");
    7791             :         }
    7792             :     }
    7793             : 
    7794           0 :     obj = maybeUnboxForPropertyAccess(obj);
    7795         295 :     if (obj->type() == MIRType::Object)
    7796           0 :         obj = convertUnboxedObjects(obj);
    7797             : 
    7798         590 :     if (!forceInlineCaches()) {
    7799             :         // Note: no trackOptimizationAttempt call is needed, getElemTryGetProp
    7800             :         // will call it.
    7801           0 :         MOZ_TRY(getElemTryGetProp(&emitted, obj, index));
    7802         295 :         if (emitted)
    7803           0 :             return Ok();
    7804             : 
    7805           0 :         trackOptimizationAttempt(TrackedStrategy::GetElem_CallSiteObject);
    7806           0 :         MOZ_TRY(getElemTryCallSiteObject(&emitted, obj, index));
    7807         271 :         if (emitted)
    7808           0 :             return Ok();
    7809             : 
    7810           0 :         trackOptimizationAttempt(TrackedStrategy::GetElem_Dense);
    7811           0 :         MOZ_TRY(getElemTryDense(&emitted, obj, index));
    7812         271 :         if (emitted)
    7813           0 :             return Ok();
    7814             : 
    7815           0 :         trackOptimizationAttempt(TrackedStrategy::GetElem_TypedArray);
    7816           0 :         MOZ_TRY(getElemTryTypedArray(&emitted, obj, index));
    7817         203 :         if (emitted)
    7818           0 :             return Ok();
    7819             : 
    7820           0 :         trackOptimizationAttempt(TrackedStrategy::GetElem_String);
    7821           0 :         MOZ_TRY(getElemTryString(&emitted, obj, index));
    7822         203 :         if (emitted)
    7823           0 :             return Ok();
    7824             : 
    7825           0 :         trackOptimizationAttempt(TrackedStrategy::GetElem_TypedObject);
    7826           0 :         MOZ_TRY(getElemTryTypedObject(&emitted, obj, index));
    7827         203 :         if (emitted)
    7828           0 :             return Ok();
    7829             :     }
    7830             : 
    7831         203 :     trackOptimizationAttempt(TrackedStrategy::GetElem_InlineCache);
    7832         203 :     return getElemAddCache(obj, index);
    7833             : }
    7834             : 
    7835             : AbortReasonOr<Ok>
    7836           0 : IonBuilder::getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index)
    7837             : {
    7838         203 :     MOZ_ASSERT(*emitted == false);
    7839             : 
    7840             :     // The next several failures are all due to types not predicting that we
    7841             :     // are definitely doing a getelem access on a typed object.
    7842           0 :     trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject);
    7843             : 
    7844           0 :     TypedObjectPrediction objPrediction = typedObjectPrediction(obj);
    7845         203 :     if (objPrediction.isUseless())
    7846           0 :         return Ok();
    7847             : 
    7848           0 :     if (!objPrediction.ofArrayKind())
    7849           0 :         return Ok();
    7850             : 
    7851           0 :     TypedObjectPrediction elemPrediction = objPrediction.arrayElementType();
    7852           0 :     if (elemPrediction.isUseless())
    7853           0 :         return Ok();
    7854             : 
    7855             :     uint32_t elemSize;
    7856           0 :     if (!elemPrediction.hasKnownSize(&elemSize))
    7857           0 :         return Ok();
    7858             : 
    7859           0 :     switch (elemPrediction.kind()) {
    7860             :       case type::Simd:
    7861             :         // FIXME (bug 894105): load into a MIRType::float32x4 etc
    7862           0 :         trackOptimizationOutcome(TrackedOutcome::GenericFailure);
    7863           0 :         return Ok();
    7864             : 
    7865             :       case type::Struct:
    7866             :       case type::Array:
    7867             :         return getElemTryComplexElemOfTypedObject(emitted,
    7868             :                                                   obj,
    7869             :                                                   index,
    7870             :                                                   objPrediction,
    7871             :                                                   elemPrediction,
    7872           0 :                                                   elemSize);
    7873             :       case type::Scalar:
    7874             :         return getElemTryScalarElemOfTypedObject(emitted,
    7875             :                                                  obj,
    7876             :                                                  index,
    7877             :                                                  objPrediction,
    7878             :                                                  elemPrediction,
    7879           0 :                                                  elemSize);
    7880             : 
    7881             :       case type::Reference:
    7882             :         return getElemTryReferenceElemOfTypedObject(emitted,
    7883             :                                                     obj,
    7884             :                                                     index,
    7885             :                                                     objPrediction,
    7886           0 :                                                     elemPrediction);
    7887             :     }
    7888             : 
    7889           0 :     MOZ_CRASH("Bad kind");
    7890             : }
    7891             : 
    7892             : bool
    7893           0 : IonBuilder::checkTypedObjectIndexInBounds(uint32_t elemSize,
    7894             :                                           MDefinition* index,
    7895             :                                           TypedObjectPrediction objPrediction,
    7896             :                                           LinearSum* indexAsByteOffset)
    7897             : {
    7898             :     // Ensure index is an integer.
    7899           0 :     MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
    7900           0 :     current->add(idInt32);
    7901             : 
    7902             :     // If we know the length statically from the type, just embed it.
    7903             :     // Otherwise, load it from the appropriate reserved slot on the
    7904             :     // typed object.  We know it's an int32, so we can convert from
    7905             :     // Value to int32 using truncation.
    7906             :     int32_t lenOfAll;
    7907             :     MDefinition* length;
    7908           0 :     if (objPrediction.hasKnownArrayLength(&lenOfAll)) {
    7909           0 :         length = constantInt(lenOfAll);
    7910             : 
    7911             :         // If we are not loading the length from the object itself, only
    7912             :         // optimize if the array buffer can never be a detached array buffer.
    7913           0 :         TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
    7914           0 :         if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER)) {
    7915             :             trackOptimizationOutcome(TrackedOutcome::TypedObjectHasDetachedBuffer);
    7916             :             return false;
    7917             :         }
    7918             :     } else {
    7919             :         trackOptimizationOutcome(TrackedOutcome::TypedObjectArrayRange);
    7920             :         return false;
    7921             :     }
    7922             : 
    7923           0 :     index = addBoundsCheck(idInt32, length);
    7924             : 
    7925           0 :     return indexAsByteOffset->add(index, AssertedCast<int32_t>(elemSize));
    7926             : }
    7927             : 
    7928             : AbortReasonOr<Ok>
    7929           0 : IonBuilder::getElemTryScalarElemOfTypedObject(bool* emitted,
    7930             :                                               MDefinition* obj,
    7931             :                                               MDefinition* index,
    7932             :                                               TypedObjectPrediction objPrediction,
    7933             :                                               TypedObjectPrediction elemPrediction,
    7934             :                                               uint32_t elemSize)
    7935             : {
    7936           0 :     MOZ_ASSERT(objPrediction.ofArrayKind());
    7937             : 
    7938             :     // Must always be loading the same scalar type
    7939           0 :     ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
    7940           0 :     MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
    7941             : 
    7942           0 :     LinearSum indexAsByteOffset(alloc());
    7943           0 :     if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
    7944           0 :         return Ok();
    7945             : 
    7946           0 :     trackOptimizationSuccess();
    7947           0 :     *emitted = true;
    7948             : 
    7949           0 :     return pushScalarLoadFromTypedObject(obj, indexAsByteOffset, elemType);
    7950             : }
    7951             : 
    7952             : AbortReasonOr<Ok>
    7953           0 : IonBuilder::getElemTryReferenceElemOfTypedObject(bool* emitted,
    7954             :                                                  MDefinition* obj,
    7955             :                                                  MDefinition* index,
    7956             :                                                  TypedObjectPrediction objPrediction,
    7957             :                                                  TypedObjectPrediction elemPrediction)
    7958             : {
    7959           0 :     MOZ_ASSERT(objPrediction.ofArrayKind());
    7960             : 
    7961           0 :     ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
    7962           0 :     uint32_t elemSize = ReferenceTypeDescr::size(elemType);
    7963             : 
    7964           0 :     LinearSum indexAsByteOffset(alloc());
    7965           0 :     if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
    7966           0 :         return Ok();
    7967             : 
    7968           0 :     trackOptimizationSuccess();
    7969           0 :     *emitted = true;
    7970             : 
    7971           0 :     return pushReferenceLoadFromTypedObject(obj, indexAsByteOffset, elemType, nullptr);
    7972             : }
    7973             : 
    7974             : AbortReasonOr<Ok>
    7975           0 : IonBuilder::pushScalarLoadFromTypedObject(MDefinition* obj,
    7976             :                                           const LinearSum& byteOffset,
    7977             :                                           ScalarTypeDescr::Type elemType)
    7978             : {
    7979           0 :     uint32_t size = ScalarTypeDescr::size(elemType);
    7980           0 :     MOZ_ASSERT(size == ScalarTypeDescr::alignment(elemType));
    7981             : 
    7982             :     // Find location within the owner object.
    7983             :     MDefinition* elements;
    7984             :     MDefinition* scaledOffset;
    7985             :     int32_t adjustment;
    7986           0 :     MOZ_TRY(loadTypedObjectElements(obj, byteOffset, size, &elements, &scaledOffset, &adjustment));
    7987             : 
    7988             :     // Load the element.
    7989           0 :     MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset,
    7990             :                                                        elemType,
    7991             :                                                        DoesNotRequireMemoryBarrier,
    7992           0 :                                                        adjustment);
    7993           0 :     current->add(load);
    7994           0 :     current->push(load);
    7995             : 
    7996             :     // If we are reading in-bounds elements, we can use knowledge about
    7997             :     // the array type to determine the result type, even if the opcode has
    7998             :     // never executed. The known pushed type is only used to distinguish
    7999             :     // uint32 reads that may produce either doubles or integers.
    8000           0 :     TemporaryTypeSet* resultTypes = bytecodeTypes(pc);
    8001           0 :     bool allowDouble = resultTypes->hasType(TypeSet::DoubleType());
    8002             : 
    8003             :     // Note: knownType is not necessarily in resultTypes; e.g. if we
    8004             :     // have only observed integers coming out of float array.
    8005           0 :     MIRType knownType = MIRTypeForTypedArrayRead(elemType, allowDouble);
    8006             : 
    8007             :     // Note: we can ignore the type barrier here, we know the type must
    8008             :     // be valid and unbarriered. Also, need not set resultTypeSet,
    8009             :     // because knownType is scalar and a resultTypeSet would provide
    8010             :     // no useful additional info.
    8011           0 :     load->setResultType(knownType);
    8012             : 
    8013           0 :     return Ok();
    8014             : }
    8015             : 
    8016             : AbortReasonOr<Ok>
    8017           0 : IonBuilder::pushReferenceLoadFromTypedObject(MDefinition* typedObj,
    8018             :                                              const LinearSum& byteOffset,
    8019             :                                              ReferenceTypeDescr::Type type,
    8020             :                                              PropertyName* name)
    8021             : {
    8022             :     // Find location within the owner object.
    8023             :     MDefinition* elements;
    8024             :     MDefinition* scaledOffset;
    8025             :     int32_t adjustment;
    8026           0 :     uint32_t alignment = ReferenceTypeDescr::alignment(type);
    8027           0 :     MOZ_TRY(loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset, &adjustment));
    8028             : 
    8029           0 :     TemporaryTypeSet* observedTypes = bytecodeTypes(pc);
    8030             : 
    8031           0 :     MInstruction* load = nullptr;  // initialize to silence GCC warning
    8032           0 :     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
    8033           0 :                                                        typedObj, name, observedTypes);
    8034             : 
    8035           0 :     switch (type) {
    8036             :       case ReferenceTypeDescr::TYPE_ANY: {
    8037             :         // Make sure the barrier reflects the possibility of reading undefined.
    8038           0 :         bool bailOnUndefined = barrier == BarrierKind::NoBarrier &&
    8039           0 :                                !observedTypes->hasType(TypeSet::UndefinedType());
    8040           0 :         if (bailOnUndefined)
    8041           0 :             barrier = BarrierKind::TypeTagOnly;
    8042           0 :         load = MLoadElement::New(alloc(), elements, scaledOffset, false, false, adjustment);
    8043           0 :         break;
    8044             :       }
    8045             :       case ReferenceTypeDescr::TYPE_OBJECT: {
    8046             :         // Make sure the barrier reflects the possibility of reading null. When
    8047             :         // there is no other barrier needed we include the null bailout with
    8048             :         // MLoadUnboxedObjectOrNull, which avoids the need to box the result
    8049             :         // for a type barrier instruction.
    8050             :         MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
    8051           0 :         if (barrier == BarrierKind::NoBarrier && !observedTypes->hasType(TypeSet::NullType()))
    8052             :             nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull;
    8053             :         else
    8054           0 :             nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
    8055           0 :         load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, nullBehavior,
    8056           0 :                                              adjustment);
    8057             :         break;
    8058             :       }
    8059             :       case ReferenceTypeDescr::TYPE_STRING: {
    8060           0 :         load = MLoadUnboxedString::New(alloc(), elements, scaledOffset, adjustment);
    8061           0 :         observedTypes->addType(TypeSet::StringType(), alloc().lifoAlloc());
    8062           0 :         break;
    8063             :       }
    8064             :     }
    8065             : 
    8066           0 :     current->add(load);
    8067           0 :     current->push(load);
    8068             : 
    8069           0 :     return pushTypeBarrier(load, observedTypes, barrier);
    8070             : }
    8071             : 
    8072             : AbortReasonOr<Ok>
    8073           0 : IonBuilder::getElemTryComplexElemOfTypedObject(bool* emitted,
    8074             :                                                MDefinition* obj,
    8075             :                                                MDefinition* index,
    8076             :                                                TypedObjectPrediction objPrediction,
    8077             :                                                TypedObjectPrediction elemPrediction,
    8078             :                                                uint32_t elemSize)
    8079             : {
    8080           0 :     MOZ_ASSERT(objPrediction.ofArrayKind());
    8081             : 
    8082           0 :     MDefinition* type = loadTypedObjectType(obj);
    8083           0 :     MDefinition* elemTypeObj = typeObjectForElementFromArrayStructType(type);
    8084             : 
    8085           0 :     LinearSum indexAsByteOffset(alloc());
    8086           0 :     if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
    8087           0 :         return Ok();
    8088             : 
    8089             :     return pushDerivedTypedObject(emitted, obj, indexAsByteOffset,
    8090           0 :                                   elemPrediction, elemTypeObj);
    8091             : }
    8092             : 
    8093             : AbortReasonOr<Ok>
    8094           0 : IonBuilder::pushDerivedTypedObject(bool* emitted,
    8095             :                                    MDefinition* obj,
    8096             :                                    const LinearSum& baseByteOffset,
    8097             :                                    TypedObjectPrediction derivedPrediction,
    8098             :                                    MDefinition* derivedTypeObj)
    8099             : {
    8100             :     // Find location within the owner object.
    8101             :     MDefinition* owner;
    8102           0 :     LinearSum ownerByteOffset(alloc());
    8103           0 :     MOZ_TRY(loadTypedObjectData(obj, &owner, &ownerByteOffset));
    8104           0 :     if (!ownerByteOffset.add(baseByteOffset, 1))
    8105           0 :         return abort(AbortReason::Disable, "Overflow/underflow on type object offset.");
    8106             : 
    8107           0 :     MDefinition* offset = ConvertLinearSum(alloc(), current, ownerByteOffset,
    8108           0 :                                            /* convertConstant = */ true);
    8109             : 
    8110             :     // Create the derived typed object.
    8111           0 :     MInstruction* derivedTypedObj = MNewDerivedTypedObject::New(alloc(),
    8112             :                                                                 derivedPrediction,
    8113             :                                                                 derivedTypeObj,
    8114             :                                                                 owner,
    8115           0 :                                                                 offset);
    8116           0 :     current->add(derivedTypedObj);
    8117           0 :     current->push(derivedTypedObj);
    8118             : 
    8119             :     // Determine (if possible) the class/proto that `derivedTypedObj` will
    8120             :     // have. For derived typed objects, the opacity will be the same as the
    8121             :     // incoming object from which the derived typed object is, well, derived.
    8122             :     // The prototype will be determined based on the type descriptor (and is
    8123             :     // immutable).
    8124           0 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
    8125           0 :     const Class* expectedClass = nullptr;
    8126           0 :     if (const Class* objClass = objTypes ? objTypes->getKnownClass(constraints()) : nullptr) {
    8127           0 :         MOZ_ASSERT(IsTypedObjectClass(objClass));
    8128           0 :         expectedClass = GetOutlineTypedObjectClass(IsOpaqueTypedObjectClass(objClass));
    8129             :     }
    8130           0 :     const TypedProto* expectedProto = derivedPrediction.getKnownPrototype();
    8131           0 :     MOZ_ASSERT_IF(expectedClass, IsTypedObjectClass(expectedClass));
    8132             : 
    8133             :     // Determine (if possible) the class/proto that the observed type set
    8134             :     // describes.
    8135           0 :     TemporaryTypeSet* observedTypes = bytecodeTypes(pc);
    8136           0 :     const Class* observedClass = observedTypes->getKnownClass(constraints());
    8137             : 
    8138             :     // If expectedClass/expectedProto are both non-null (and hence known), we
    8139             :     // can predict precisely what object group derivedTypedObj will have.
    8140             :     // Therefore, if we observe that this group is already contained in the set
    8141             :     // of observedTypes, we can skip the barrier.
    8142             :     //
    8143             :     // Barriers still wind up being needed in some relatively
    8144             :     // rare cases:
    8145             :     //
    8146             :     // - if multiple kinds of typed objects flow into this point,
    8147             :     //   in which case we will not be able to predict expectedClass
    8148             :     //   nor expectedProto.
    8149             :     //
    8150             :     // - if the code has never executed, in which case the set of
    8151             :     //   observed types will be incomplete.
    8152             :     //
    8153             :     // Barriers are particularly expensive here because they prevent
    8154             :     // us from optimizing the MNewDerivedTypedObject away.
    8155             :     JSObject* observedProto;
    8156           0 :     if (observedTypes->getCommonPrototype(constraints(), &observedProto) &&
    8157           0 :         observedClass && observedProto && observedClass == expectedClass &&
    8158             :         observedProto == expectedProto)
    8159             :     {
    8160           0 :         derivedTypedObj->setResultTypeSet(observedTypes);
    8161             :     } else {
    8162           0 :         MOZ_TRY(pushTypeBarrier(derivedTypedObj, observedTypes, BarrierKind::TypeSet));
    8163             :     }
    8164             : 
    8165           0 :     trackOptimizationSuccess();
    8166           0 :     *emitted = true;
    8167           0 :     return Ok();
    8168             : }
    8169             : 
    8170             : AbortReasonOr<Ok>
    8171         295 : IonBuilder::getElemTryGetProp(bool* emitted, MDefinition* obj, MDefinition* index)
    8172             : {
    8173             :     // If index is a constant string or symbol, try to optimize this GETELEM
    8174             :     // as a GETPROP.
    8175             : 
    8176           0 :     MOZ_ASSERT(*emitted == false);
    8177             : 
    8178           0 :     MConstant* indexConst = index->maybeConstantValue();
    8179           0 :     jsid id;
    8180         295 :     if (!indexConst || !ValueToIdPure(indexConst->toJSValue(), &id))
    8181           0 :         return Ok();
    8182             : 
    8183         230 :     if (id != IdToTypeId(id))
    8184           0 :         return Ok();
    8185             : 
    8186           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    8187             : 
    8188           0 :     trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
    8189           0 :     MOZ_TRY(getPropTryConstant(emitted, obj, id, types) );
    8190           0 :     if (*emitted) {
    8191          24 :         index->setImplicitlyUsedUnchecked();
    8192          24 :         return Ok();
    8193             :     }
    8194             : 
    8195           0 :     trackOptimizationAttempt(TrackedStrategy::GetProp_NotDefined);
    8196           0 :     MOZ_TRY(getPropTryNotDefined(emitted, obj, id, types) );
    8197           0 :     if (*emitted) {
    8198           0 :         index->setImplicitlyUsedUnchecked();
    8199           0 :         return Ok();
    8200             :     }
    8201             : 
    8202          19 :     return Ok();
    8203             : }
    8204             : 
    8205             : AbortReasonOr<Ok>
    8206           0 : IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
    8207             : {
    8208           0 :     MOZ_ASSERT(*emitted == false);
    8209             : 
    8210           0 :     if (!ElementAccessIsDenseNative(constraints(), obj, index)) {
    8211         203 :         trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
    8212         203 :         return Ok();
    8213             :     }
    8214             : 
    8215             :     // Don't generate a fast path if there have been bounds check failures
    8216             :     // and this access might be on a sparse property.
    8217             :     bool hasExtraIndexedProperty;
    8218           0 :     MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
    8219           0 :     if (hasExtraIndexedProperty && failedBoundsCheck_) {
    8220           0 :         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
    8221           0 :         return Ok();
    8222             :     }
    8223             : 
    8224             :     // Don't generate a fast path if this pc has seen negative indexes accessed,
    8225             :     // which will not appear to be extra indexed properties.
    8226           0 :     if (inspector->hasSeenNegativeIndexGetElement(pc)) {
    8227           0 :         trackOptimizationOutcome(TrackedOutcome::ArraySeenNegativeIndex);
    8228           0 :         return Ok();
    8229             :     }
    8230             : 
    8231           0 :     MOZ_TRY(jsop_getelem_dense(obj, index));
    8232             : 
    8233           0 :     trackOptimizationSuccess();
    8234          68 :     *emitted = true;
    8235          68 :     return Ok();
    8236             : }
    8237             : 
    8238             : AbortReasonOr<Ok>
    8239           0 : IonBuilder::getElemTryTypedArray(bool* emitted, MDefinition* obj, MDefinition* index)
    8240             : {
    8241         203 :     MOZ_ASSERT(*emitted == false);
    8242             : 
    8243             :     Scalar::Type arrayType;
    8244           0 :     if (!ElementAccessIsTypedArray(constraints(), obj, index, &arrayType)) {
    8245         203 :         trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray);
    8246         203 :         return Ok();
    8247             :     }
    8248             : 
    8249             :     // Emit typed getelem variant.
    8250           0 :     MOZ_TRY(jsop_getelem_typed(obj, index, arrayType));
    8251             : 
    8252           0 :     trackOptimizationSuccess();
    8253           0 :     *emitted = true;
    8254           0 :     return Ok();
    8255             : }
    8256             : 
    8257             : AbortReasonOr<Ok>
    8258           0 : IonBuilder::getElemTryCallSiteObject(bool* emitted, MDefinition* obj, MDefinition* index)
    8259             : {
    8260           0 :     MOZ_ASSERT(*emitted == false);
    8261             : 
    8262           0 :     if (!obj->isConstant() || obj->type() != MIRType::Object) {
    8263         266 :         trackOptimizationOutcome(TrackedOutcome::NotObject);
    8264         266 :         return Ok();
    8265             :     }
    8266             : 
    8267           0 :     if (!index->isConstant() || index->type() != MIRType::Int32) {
    8268           5 :         trackOptimizationOutcome(TrackedOutcome::IndexType);
    8269           5 :         return Ok();
    8270             :     }
    8271             : 
    8272           0 :     JSObject* cst = &obj->toConstant()->toObject();
    8273           0 :     if (!cst->is<ArrayObject>()) {
    8274           0 :         trackOptimizationOutcome(TrackedOutcome::GenericFailure);
    8275           0 :         return Ok();
    8276             :     }
    8277             : 
    8278             :     // Technically this code would work with any kind of frozen array,
    8279             :     // in pratice it is usually a CallSiteObject.
    8280             : 
    8281           0 :     ArrayObject* array = &cst->as<ArrayObject>();
    8282           0 :     if (array->lengthIsWritable() || array->hasEmptyElements() || !array->denseElementsAreFrozen()) {
    8283           0 :         trackOptimizationOutcome(TrackedOutcome::GenericFailure);
    8284           0 :         return Ok();
    8285             :     }
    8286             : 
    8287           0 :     int32_t idx = index->toConstant()->toInt32();
    8288           0 :     if (idx < 0 || !array->containsDenseElement(uint32_t(idx))) {
    8289           0 :         trackOptimizationOutcome(TrackedOutcome::OutOfBounds);
    8290           0 :         return Ok();
    8291             :     }
    8292             : 
    8293           0 :     const Value& v = array->getDenseElement(uint32_t(idx));
    8294             :     // Strings should have been atomized by the parser.
    8295           0 :     if (!v.isString() || !v.toString()->isAtom())
    8296           0 :         return Ok();
    8297             : 
    8298           0 :     obj->setImplicitlyUsedUnchecked();
    8299           0 :     index->setImplicitlyUsedUnchecked();
    8300             : 
    8301           0 :     pushConstant(v);
    8302             : 
    8303           0 :     trackOptimizationSuccess();
    8304           0 :     *emitted = true;
    8305           0 :     return Ok();
    8306             : }
    8307             : 
    8308             : AbortReasonOr<Ok>
    8309           0 : IonBuilder::getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index)
    8310             : {
    8311           0 :     MOZ_ASSERT(*emitted == false);
    8312             : 
    8313           0 :     if (obj->type() != MIRType::String || !IsNumberType(index->type())) {
    8314         203 :         trackOptimizationOutcome(TrackedOutcome::AccessNotString);
    8315         203 :         return Ok();
    8316             :     }
    8317             : 
    8318             :     // If the index is expected to be out-of-bounds, don't optimize to avoid
    8319             :     // frequent bailouts.
    8320           0 :     if (bytecodeTypes(pc)->hasType(TypeSet::UndefinedType())) {
    8321           0 :         trackOptimizationOutcome(TrackedOutcome::OutOfBounds);
    8322           0 :         return Ok();
    8323             :     }
    8324             : 
    8325             :     // Emit fast path for string[index].
    8326           0 :     MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
    8327           0 :     current->add(idInt32);
    8328           0 :     index = idInt32;
    8329             : 
    8330           0 :     MStringLength* length = MStringLength::New(alloc(), obj);
    8331           0 :     current->add(length);
    8332             : 
    8333           0 :     index = addBoundsCheck(index, length);
    8334             : 
    8335           0 :     MCharCodeAt* charCode = MCharCodeAt::New(alloc(), obj, index);
    8336           0 :     current->add(charCode);
    8337             : 
    8338           0 :     MFromCharCode* result = MFromCharCode::New(alloc(), charCode);
    8339           0 :     current->add(result);
    8340           0 :     current->push(result);
    8341             : 
    8342           0 :     trackOptimizationSuccess();
    8343           0 :     *emitted = true;
    8344           0 :     return Ok();
    8345             : }
    8346             : 
    8347             : AbortReasonOr<Ok>
    8348           0 : IonBuilder::getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* index)
    8349             : {
    8350           0 :     MOZ_ASSERT(*emitted == false);
    8351             : 
    8352           2 :     if (inliningDepth_ > 0)
    8353           0 :         return Ok();
    8354             : 
    8355           2 :     if (obj->type() != MIRType::MagicOptimizedArguments)
    8356           0 :         return Ok();
    8357             : 
    8358             :     // Emit GetFrameArgument.
    8359             : 
    8360           4 :     MOZ_ASSERT(!info().argsObjAliasesFormals());
    8361             : 
    8362             :     // Type Inference has guaranteed this is an optimized arguments object.
    8363           2 :     obj->setImplicitlyUsedUnchecked();
    8364             : 
    8365             :     // To ensure that we are not looking above the number of actual arguments.
    8366           2 :     MArgumentsLength* length = MArgumentsLength::New(alloc());
    8367           2 :     current->add(length);
    8368             : 
    8369             :     // Ensure index is an integer.
    8370           0 :     MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
    8371           2 :     current->add(idInt32);
    8372           2 :     index = idInt32;
    8373             : 
    8374             :     // Bailouts if we read more than the number of actual arguments.
    8375           2 :     index = addBoundsCheck(index, length);
    8376             : 
    8377             :     // Load the argument from the actual arguments.
    8378           0 :     bool modifiesArgs = script()->baselineScript()->modifiesArguments();
    8379           0 :     MGetFrameArgument* load = MGetFrameArgument::New(alloc(), index, modifiesArgs);
    8380           2 :     current->add(load);
    8381           0 :     current->push(load);
    8382             : 
    8383           4 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    8384           0 :     MOZ_TRY(pushTypeBarrier(load, types, BarrierKind::TypeSet));
    8385             : 
    8386           0 :     trackOptimizationSuccess();
    8387           2 :     *emitted = true;
    8388           2 :     return Ok();
    8389             : }
    8390             : 
    8391             : AbortReasonOr<Ok>
    8392           0 : IonBuilder::getElemTryArgumentsInlinedConstant(bool* emitted, MDefinition* obj, MDefinition* index)
    8393             : {
    8394           0 :     MOZ_ASSERT(*emitted == false);
    8395             : 
    8396           0 :     if (inliningDepth_ == 0)
    8397           0 :         return Ok();
    8398             : 
    8399           0 :     if (obj->type() != MIRType::MagicOptimizedArguments)
    8400           0 :         return Ok();
    8401             : 
    8402           0 :     MConstant* indexConst = index->maybeConstantValue();
    8403           0 :     if (!indexConst || indexConst->type() != MIRType::Int32)
    8404           0 :         return Ok();
    8405             : 
    8406             :     // Emit inlined arguments.
    8407           0 :     obj->setImplicitlyUsedUnchecked();
    8408             : 
    8409           0 :     MOZ_ASSERT(!info().argsObjAliasesFormals());
    8410             : 
    8411             :     // When the id is constant, we can just return the corresponding inlined argument
    8412           0 :     MOZ_ASSERT(inliningDepth_ > 0);
    8413             : 
    8414           0 :     int32_t id = indexConst->toInt32();
    8415           0 :     index->setImplicitlyUsedUnchecked();
    8416             : 
    8417           0 :     if (id < (int32_t)inlineCallInfo_->argc() && id >= 0)
    8418           0 :         current->push(inlineCallInfo_->getArg(id));
    8419             :     else
    8420           0 :         pushConstant(UndefinedValue());
    8421             : 
    8422           0 :     trackOptimizationSuccess();
    8423           0 :     *emitted = true;
    8424           0 :     return Ok();
    8425             : }
    8426             : 
    8427             : AbortReasonOr<Ok>
    8428           0 : IonBuilder::getElemTryArgumentsInlinedIndex(bool* emitted, MDefinition* obj, MDefinition* index)
    8429             : {
    8430           0 :     MOZ_ASSERT(*emitted == false);
    8431             : 
    8432           0 :     if (inliningDepth_ == 0)
    8433           0 :         return Ok();
    8434             : 
    8435           0 :     if (obj->type() != MIRType::MagicOptimizedArguments)
    8436           0 :         return Ok();
    8437             : 
    8438           0 :     if (!IsNumberType(index->type()))
    8439           0 :         return Ok();
    8440             : 
    8441             :     // Currently, we do not support any arguments vector larger than 10, as this
    8442             :     // is being translated into code at the call site, and it would be better to
    8443             :     // store the arguments contiguously on the stack.
    8444           0 :     if (inlineCallInfo_->argc() > 10) {
    8445           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineBound);
    8446           0 :         return abort(AbortReason::Disable, "NYI get argument element with too many arguments");
    8447             :     }
    8448             : 
    8449             :     // Emit inlined arguments.
    8450           0 :     obj->setImplicitlyUsedUnchecked();
    8451             : 
    8452           0 :     MOZ_ASSERT(!info().argsObjAliasesFormals());
    8453             : 
    8454             :     // Ensure index is an integer.
    8455           0 :     MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
    8456           0 :     current->add(idInt32);
    8457           0 :     index = idInt32;
    8458             : 
    8459             :     // Bailout if we read more than the number of actual arguments. This bailout
    8460             :     // cannot re-enter because reading out of bounds arguments will disable the
    8461             :     // lazy arguments optimization for this script, when this code would be
    8462             :     // executed in Baseline. (see GetElemOptimizedArguments)
    8463           0 :     index = addBoundsCheck(index, constantInt(inlineCallInfo_->argc()));
    8464             : 
    8465             :     // Get an instruction to represent the state of the argument vector.
    8466           0 :     MInstruction* args = MArgumentState::New(alloc().fallible(), inlineCallInfo_->argv());
    8467           0 :     if (!args)
    8468           0 :         return abort(AbortReason::Alloc);
    8469           0 :     current->add(args);
    8470             : 
    8471             :     // Select a value to pick from a vector.
    8472           0 :     MInstruction* load = MLoadElementFromState::New(alloc(), args, index);
    8473           0 :     current->add(load);
    8474           0 :     current->push(load);
    8475             : 
    8476           0 :     trackOptimizationSuccess();
    8477           0 :     *emitted = true;
    8478           0 :     return Ok();
    8479             : }
    8480             : 
    8481             : AbortReasonOr<Ok>
    8482         203 : IonBuilder::getElemAddCache(MDefinition* obj, MDefinition* index)
    8483             : {
    8484             :     // Emit GetPropertyCache.
    8485             : 
    8486         406 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    8487             : 
    8488             :     BarrierKind barrier;
    8489         203 :     if (obj->type() == MIRType::Object) {
    8490             :         // Always add a barrier if the index is not an int32 value, so we can
    8491             :         // attach stubs for particular properties.
    8492           0 :         if (index->type() == MIRType::Int32) {
    8493         130 :             barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(), obj,
    8494         130 :                                                    nullptr, types);
    8495             :         } else {
    8496             :             barrier = BarrierKind::TypeSet;
    8497             :         }
    8498             :     } else {
    8499             :         // PropertyReadNeedsTypeBarrier only accounts for object types, so for
    8500             :         // now always insert a barrier if the input is not known to be an
    8501             :         // object.
    8502             :         barrier = BarrierKind::TypeSet;
    8503             :     }
    8504             : 
    8505             :     // Ensure we insert a type barrier for reads from typed objects, as type
    8506             :     // information does not account for the initial undefined/null types.
    8507           0 :     if (barrier != BarrierKind::TypeSet && !types->unknown()) {
    8508           0 :         MOZ_ASSERT(obj->resultTypeSet());
    8509           0 :         switch (obj->resultTypeSet()->forAllClasses(constraints(), IsTypedObjectClass)) {
    8510             :           case TemporaryTypeSet::ForAllResult::ALL_FALSE:
    8511             :           case TemporaryTypeSet::ForAllResult::EMPTY:
    8512             :             break;
    8513             :           case TemporaryTypeSet::ForAllResult::ALL_TRUE:
    8514             :           case TemporaryTypeSet::ForAllResult::MIXED:
    8515           0 :             barrier = BarrierKind::TypeSet;
    8516           0 :             break;
    8517             :         }
    8518             :     }
    8519             : 
    8520           0 :     MGetPropertyCache* ins = MGetPropertyCache::New(alloc(), obj, index,
    8521           0 :                                                     barrier == BarrierKind::TypeSet);
    8522         203 :     current->add(ins);
    8523           0 :     current->push(ins);
    8524             : 
    8525         609 :     MOZ_TRY(resumeAfter(ins));
    8526             : 
    8527             :     // Spice up type information.
    8528           0 :     if (index->type() == MIRType::Int32 && barrier == BarrierKind::NoBarrier) {
    8529           0 :         bool needHoleCheck = !ElementAccessIsPacked(constraints(), obj);
    8530           0 :         MIRType knownType = GetElemKnownType(needHoleCheck, types);
    8531             : 
    8532           0 :         if (knownType != MIRType::Value && knownType != MIRType::Double)
    8533           0 :             ins->setResultType(knownType);
    8534             :     }
    8535             : 
    8536           0 :     MOZ_TRY(pushTypeBarrier(ins, types, barrier));
    8537             : 
    8538         203 :     trackOptimizationSuccess();
    8539         203 :     return Ok();
    8540             : }
    8541             : 
    8542             : TemporaryTypeSet*
    8543           0 : IonBuilder::computeHeapType(const TemporaryTypeSet* objTypes, const jsid id)
    8544             : {
    8545         136 :     if (objTypes->unknownObject() || objTypes->getObjectCount() == 0)
    8546             :         return nullptr;
    8547             : 
    8548          68 :     TemporaryTypeSet* acc = nullptr;
    8549           0 :     LifoAlloc* lifoAlloc = alloc().lifoAlloc();
    8550             : 
    8551         136 :     Vector<HeapTypeSetKey, 4, SystemAllocPolicy> properties;
    8552          68 :     if (!properties.reserve(objTypes->getObjectCount()))
    8553             :         return nullptr;
    8554             : 
    8555          76 :     for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
    8556           0 :         TypeSet::ObjectKey* key = objTypes->getObject(i);
    8557             : 
    8558          68 :         if (key->unknownProperties())
    8559           0 :             return nullptr;
    8560             : 
    8561          68 :         HeapTypeSetKey property = key->property(id);
    8562           0 :         HeapTypeSet* currentSet = property.maybeTypes();
    8563             : 
    8564         136 :         if (!currentSet || currentSet->unknown())
    8565             :             return nullptr;
    8566             : 
    8567           0 :         properties.infallibleAppend(property);
    8568             : 
    8569           4 :         if (acc) {
    8570           0 :             acc = TypeSet::unionSets(acc, currentSet, lifoAlloc);
    8571             :         } else {
    8572           4 :             TemporaryTypeSet empty;
    8573           4 :             acc = TypeSet::unionSets(&empty, currentSet, lifoAlloc);
    8574             :         }
    8575             : 
    8576           4 :         if (!acc)
    8577             :             return nullptr;
    8578             :     }
    8579             : 
    8580             :     // Freeze all the properties associated with the refined type set.
    8581          16 :     for (HeapTypeSetKey* i = properties.begin(); i != properties.end(); i++)
    8582           4 :         i->freeze(constraints());
    8583             : 
    8584             :     return acc;
    8585             : }
    8586             : 
    8587             : AbortReasonOr<Ok>
    8588           0 : IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
    8589             : {
    8590           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    8591             : 
    8592           0 :     MOZ_ASSERT(index->type() == MIRType::Int32 || index->type() == MIRType::Double);
    8593             : 
    8594           0 :     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
    8595          68 :                                                        obj, nullptr, types);
    8596          68 :     bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
    8597             : 
    8598             :     // Reads which are on holes in the object do not have to bail out if
    8599             :     // undefined values have been observed at this access site and the access
    8600             :     // cannot hit another indexed property on the object or its prototypes.
    8601          68 :     bool readOutOfBounds = false;
    8602           0 :     if (types->hasType(TypeSet::UndefinedType())) {
    8603             :         bool hasExtraIndexedProperty;
    8604           0 :         MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
    8605           0 :         readOutOfBounds = !hasExtraIndexedProperty;
    8606             :     }
    8607             : 
    8608          68 :     MIRType knownType = MIRType::Value;
    8609          68 :     if (barrier == BarrierKind::NoBarrier)
    8610             :         knownType = GetElemKnownType(needsHoleCheck, types);
    8611             : 
    8612             :     // Ensure index is an integer.
    8613           0 :     MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
    8614          68 :     current->add(idInt32);
    8615          68 :     index = idInt32;
    8616             : 
    8617             :     // Get the elements vector.
    8618          68 :     MInstruction* elements = MElements::New(alloc(), obj);
    8619          68 :     current->add(elements);
    8620             : 
    8621             :     // Note: to help GVN, use the original MElements instruction and not
    8622             :     // MConvertElementsToDoubles as operand. This is fine because converting
    8623             :     // elements to double does not change the initialized length.
    8624          68 :     MInstruction* initLength = initializedLength(elements);
    8625             : 
    8626             :     // If we can load the element as a definite double, make sure to check that
    8627             :     // the array has been converted to homogenous doubles first.
    8628          68 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
    8629           0 :     bool inBounds = !readOutOfBounds && !needsHoleCheck;
    8630             : 
    8631           0 :     if (inBounds) {
    8632           0 :         TemporaryTypeSet* heapTypes = computeHeapType(objTypes, JSID_VOID);
    8633           0 :         if (heapTypes && heapTypes->isSubset(types)) {
    8634           0 :             knownType = heapTypes->getKnownMIRType();
    8635           0 :             types = heapTypes;
    8636             :         }
    8637             :     }
    8638             : 
    8639             :     bool loadDouble =
    8640           0 :         barrier == BarrierKind::NoBarrier &&
    8641           0 :         loopDepth_ &&
    8642           0 :         inBounds &&
    8643           0 :         knownType == MIRType::Double &&
    8644           0 :         objTypes &&
    8645           0 :         objTypes->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles;
    8646          68 :     if (loadDouble)
    8647           0 :         elements = addConvertElementsToDoubles(elements);
    8648             : 
    8649             :     MInstruction* load;
    8650             : 
    8651          68 :     if (!readOutOfBounds) {
    8652             :         // This load should not return undefined, so likely we're reading
    8653             :         // in-bounds elements, and the array is packed or its holes are not
    8654             :         // read. This is the best case: we can separate the bounds check for
    8655             :         // hoisting.
    8656           0 :         index = addBoundsCheck(index, initLength);
    8657             : 
    8658          68 :         load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble);
    8659          68 :         current->add(load);
    8660             :     } else {
    8661             :         // This load may return undefined, so assume that we *can* read holes,
    8662             :         // or that we can read out-of-bounds accesses. In this case, the bounds
    8663             :         // check is part of the opcode.
    8664           0 :         load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck);
    8665           0 :         current->add(load);
    8666             : 
    8667             :         // If maybeUndefined was true, the typeset must have undefined, and
    8668             :         // then either additional types or a barrier. This means we should
    8669             :         // never have a typed version of LoadElementHole.
    8670           0 :         MOZ_ASSERT(knownType == MIRType::Value);
    8671             :     }
    8672             : 
    8673           0 :     if (knownType != MIRType::Value) {
    8674           0 :         load->setResultType(knownType);
    8675           0 :         load->setResultTypeSet(types);
    8676             :     }
    8677             : 
    8678          68 :     current->push(load);
    8679          68 :     return pushTypeBarrier(load, types, barrier);
    8680             : }
    8681             : 
    8682             : MInstruction*
    8683           0 : IonBuilder::addArrayBufferByteLength(MDefinition* obj)
    8684             : {
    8685           0 :     MLoadFixedSlot* ins = MLoadFixedSlot::New(alloc(), obj, size_t(ArrayBufferObject::BYTE_LENGTH_SLOT));
    8686           0 :     current->add(ins);
    8687           0 :     ins->setResultType(MIRType::Int32);
    8688           0 :     return ins;
    8689             : }
    8690             : 
    8691             : void
    8692           0 : IonBuilder::addTypedArrayLengthAndData(MDefinition* obj,
    8693             :                                        BoundsChecking checking,
    8694             :                                        MDefinition** index,
    8695             :                                        MInstruction** length, MInstruction** elements)
    8696             : {
    8697           0 :     MOZ_ASSERT((index != nullptr) == (elements != nullptr));
    8698             : 
    8699           0 :     JSObject* tarr = nullptr;
    8700             : 
    8701           0 :     if (MConstant* objConst = obj->maybeConstantValue()) {
    8702           0 :         if (objConst->type() == MIRType::Object)
    8703           0 :             tarr = &objConst->toObject();
    8704           0 :     } else if (TemporaryTypeSet* types = obj->resultTypeSet()) {
    8705           0 :         tarr = types->maybeSingleton();
    8706             :     }
    8707             : 
    8708           0 :     if (tarr) {
    8709           0 :         SharedMem<void*> data = tarr->as<TypedArrayObject>().viewDataEither();
    8710             :         // Bug 979449 - Optimistically embed the elements and use TI to
    8711             :         //              invalidate if we move them.
    8712           0 :         bool isTenured = !tarr->runtimeFromMainThread()->gc.nursery().isInside(data);
    8713           0 :         if (isTenured && tarr->isSingleton()) {
    8714             :             // The 'data' pointer of TypedArrayObject can change in rare circumstances
    8715             :             // (ArrayBufferObject::changeContents).
    8716           0 :             TypeSet::ObjectKey* tarrKey = TypeSet::ObjectKey::get(tarr);
    8717           0 :             if (!tarrKey->unknownProperties()) {
    8718           0 :                 if (tarr->is<TypedArrayObject>())
    8719           0 :                     tarrKey->watchStateChangeForTypedArrayData(constraints());
    8720             : 
    8721           0 :                 obj->setImplicitlyUsedUnchecked();
    8722             : 
    8723           0 :                 int32_t len = AssertedCast<int32_t>(tarr->as<TypedArrayObject>().length());
    8724           0 :                 *length = MConstant::New(alloc(), Int32Value(len));
    8725           0 :                 current->add(*length);
    8726             : 
    8727           0 :                 if (index) {
    8728           0 :                     if (checking == DoBoundsCheck)
    8729           0 :                         *index = addBoundsCheck(*index, *length);
    8730             : 
    8731           0 :                     *elements = MConstantElements::New(alloc(), data);
    8732           0 :                     current->add(*elements);
    8733             :                 }
    8734           0 :                 return;
    8735             :             }
    8736             :         }
    8737             :     }
    8738             : 
    8739           0 :     *length = MTypedArrayLength::New(alloc(), obj);
    8740           0 :     current->add(*length);
    8741             : 
    8742           0 :     if (index) {
    8743           0 :         if (checking == DoBoundsCheck)
    8744           0 :             *index = addBoundsCheck(*index, *length);
    8745             : 
    8746           0 :         *elements = MTypedArrayElements::New(alloc(), obj);
    8747           0 :         current->add(*elements);
    8748             :     }
    8749             : }
    8750             : 
    8751             : AbortReasonOr<Ok>
    8752           0 : IonBuilder::jsop_getelem_typed(MDefinition* obj, MDefinition* index,
    8753             :                                Scalar::Type arrayType)
    8754             : {
    8755           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    8756             : 
    8757           0 :     bool maybeUndefined = types->hasType(TypeSet::UndefinedType());
    8758             : 
    8759             :     // Reading from an Uint32Array will result in a double for values
    8760             :     // that don't fit in an int32. We have to bailout if this happens
    8761             :     // and the instruction is not known to return a double.
    8762           0 :     bool allowDouble = types->hasType(TypeSet::DoubleType());
    8763             : 
    8764             :     // Ensure id is an integer.
    8765           0 :     MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
    8766           0 :     current->add(idInt32);
    8767           0 :     index = idInt32;
    8768             : 
    8769           0 :     if (!maybeUndefined) {
    8770             :         // Assume the index is in range, so that we can hoist the length,
    8771             :         // elements vector and bounds check.
    8772             : 
    8773             :         // If we are reading in-bounds elements, we can use knowledge about
    8774             :         // the array type to determine the result type, even if the opcode has
    8775             :         // never executed. The known pushed type is only used to distinguish
    8776             :         // uint32 reads that may produce either doubles or integers.
    8777           0 :         MIRType knownType = MIRTypeForTypedArrayRead(arrayType, allowDouble);
    8778             : 
    8779             :         // Get length, bounds-check, then get elements, and add all instructions.
    8780             :         MInstruction* length;
    8781             :         MInstruction* elements;
    8782           0 :         addTypedArrayLengthAndData(obj, DoBoundsCheck, &index, &length, &elements);
    8783             : 
    8784             :         // Load the element.
    8785           0 :         MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
    8786           0 :         current->add(load);
    8787           0 :         current->push(load);
    8788             : 
    8789             :         // Note: we can ignore the type barrier here, we know the type must
    8790             :         // be valid and unbarriered.
    8791           0 :         load->setResultType(knownType);
    8792           0 :         return Ok();
    8793             :     } else {
    8794             :         // We need a type barrier if the array's element type has never been
    8795             :         // observed (we've only read out-of-bounds values). Note that for
    8796             :         // Uint32Array, we only check for int32: if allowDouble is false we
    8797             :         // will bailout when we read a double.
    8798           0 :         BarrierKind barrier = BarrierKind::TypeSet;
    8799             :         switch (arrayType) {
    8800             :           case Scalar::Int8:
    8801             :           case Scalar::Uint8:
    8802             :           case Scalar::Uint8Clamped:
    8803             :           case Scalar::Int16:
    8804             :           case Scalar::Uint16:
    8805             :           case Scalar::Int32:
    8806             :           case Scalar::Uint32:
    8807           0 :             if (types->hasType(TypeSet::Int32Type()))
    8808           0 :                 barrier = BarrierKind::NoBarrier;
    8809             :             break;
    8810             :           case Scalar::Float32:
    8811             :           case Scalar::Float64:
    8812           0 :             if (allowDouble)
    8813           0 :                 barrier = BarrierKind::NoBarrier;
    8814             :             break;
    8815             :           default:
    8816           0 :             MOZ_CRASH("Unknown typed array type");
    8817             :         }
    8818             : 
    8819             :         // Assume we will read out-of-bound values. In this case the
    8820             :         // bounds check will be part of the instruction, and the instruction
    8821             :         // will always return a Value.
    8822             :         MLoadTypedArrayElementHole* load =
    8823           0 :             MLoadTypedArrayElementHole::New(alloc(), obj, index, arrayType, allowDouble);
    8824           0 :         current->add(load);
    8825           0 :         current->push(load);
    8826             : 
    8827           0 :         return pushTypeBarrier(load, types, barrier);
    8828             :     }
    8829             : }
    8830             : 
    8831             : AbortReasonOr<Ok>
    8832           0 : IonBuilder::jsop_setelem()
    8833             : {
    8834          71 :     bool emitted = false;
    8835           0 :     startTrackingOptimizations();
    8836             : 
    8837           0 :     MDefinition* value = current->pop();
    8838          71 :     MDefinition* index = current->pop();
    8839           0 :     MDefinition* object = convertUnboxedObjects(current->pop());
    8840             : 
    8841           0 :     trackTypeInfo(TrackedTypeSite::Receiver, object->type(), object->resultTypeSet());
    8842         142 :     trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet());
    8843           0 :     trackTypeInfo(TrackedTypeSite::Value, value->type(), value->resultTypeSet());
    8844             : 
    8845           0 :     if (shouldAbortOnPreliminaryGroups(object)) {
    8846           0 :         MInstruction* ins = MCallSetElement::New(alloc(), object, index, value, IsStrictSetPC(pc));
    8847           0 :         current->add(ins);
    8848           0 :         current->push(value);
    8849             :         return resumeAfter(ins);
    8850             :     }
    8851             : 
    8852           0 :     if (!forceInlineCaches()) {
    8853           0 :         trackOptimizationAttempt(TrackedStrategy::SetElem_TypedArray);
    8854           0 :         MOZ_TRY(setElemTryTypedArray(&emitted, object, index, value));
    8855          71 :         if (emitted)
    8856           0 :             return Ok();
    8857             : 
    8858           0 :         trackOptimizationAttempt(TrackedStrategy::SetElem_Dense);
    8859           0 :         SetElemICInspector icInspect(inspector->setElemICInspector(pc));
    8860           0 :         bool writeHole = icInspect.sawOOBDenseWrite();
    8861           0 :         MOZ_TRY(initOrSetElemTryDense(&emitted, object, index, value, writeHole));
    8862          71 :         if (emitted)
    8863           0 :             return Ok();
    8864             : 
    8865           0 :         trackOptimizationAttempt(TrackedStrategy::SetElem_Arguments);
    8866           1 :         MOZ_TRY(setElemTryArguments(&emitted, object));
    8867          43 :         if (emitted)
    8868           0 :             return Ok();
    8869             : 
    8870           0 :         trackOptimizationAttempt(TrackedStrategy::SetElem_TypedObject);
    8871           0 :         MOZ_TRY(setElemTryTypedObject(&emitted, object, index, value));
    8872          43 :         if (emitted)
    8873           0 :             return Ok();
    8874             :     }
    8875             : 
    8876           0 :     if (script()->argumentsHasVarBinding() &&
    8877          43 :         object->mightBeType(MIRType::MagicOptimizedArguments) &&
    8878           0 :         info().analysisMode() != Analysis_ArgumentsUsage)
    8879             :     {
    8880           0 :         return abort(AbortReason::Disable, "Type is not definitely lazy arguments.");
    8881             :     }
    8882             : 
    8883           0 :     trackOptimizationAttempt(TrackedStrategy::SetElem_InlineCache);
    8884           0 :     MOZ_TRY(initOrSetElemTryCache(&emitted, object, index, value));
    8885          43 :     if (emitted)
    8886          40 :         return Ok();
    8887             : 
    8888             :     // Emit call.
    8889           0 :     MInstruction* ins = MCallSetElement::New(alloc(), object, index, value, IsStrictSetPC(pc));
    8890           3 :     current->add(ins);
    8891           3 :     current->push(value);
    8892             : 
    8893             :     return resumeAfter(ins);
    8894             : }
    8895             : 
    8896             : AbortReasonOr<Ok>
    8897          43 : IonBuilder::setElemTryTypedObject(bool* emitted, MDefinition* obj,
    8898             :                                   MDefinition* index, MDefinition* value)
    8899             : {
    8900          43 :     MOZ_ASSERT(*emitted == false);
    8901             : 
    8902             :     // The next several failures are all due to types not predicting that we
    8903             :     // are definitely doing a getelem access on a typed object.
    8904           0 :     trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject);
    8905             : 
    8906           0 :     TypedObjectPrediction objPrediction = typedObjectPrediction(obj);
    8907          43 :     if (objPrediction.isUseless())
    8908           0 :         return Ok();
    8909             : 
    8910           0 :     if (!objPrediction.ofArrayKind())
    8911           0 :         return Ok();
    8912             : 
    8913           0 :     TypedObjectPrediction elemPrediction = objPrediction.arrayElementType();
    8914           0 :     if (elemPrediction.isUseless())
    8915           0 :         return Ok();
    8916             : 
    8917             :     uint32_t elemSize;
    8918           0 :     if (!elemPrediction.hasKnownSize(&elemSize))
    8919           0 :         return Ok();
    8920             : 
    8921           0 :     switch (elemPrediction.kind()) {
    8922             :       case type::Simd:
    8923             :         // FIXME (bug 894105): store a MIRType::float32x4 etc
    8924           0 :         trackOptimizationOutcome(TrackedOutcome::GenericFailure);
    8925           0 :         return Ok();
    8926             : 
    8927             :       case type::Reference:
    8928             :         return setElemTryReferenceElemOfTypedObject(emitted, obj, index,
    8929           0 :                                                     objPrediction, value, elemPrediction);
    8930             : 
    8931             :       case type::Scalar:
    8932             :         return setElemTryScalarElemOfTypedObject(emitted,
    8933             :                                                  obj,
    8934             :                                                  index,
    8935             :                                                  objPrediction,
    8936             :                                                  value,
    8937             :                                                  elemPrediction,
    8938           0 :                                                  elemSize);
    8939             : 
    8940             :       case type::Struct:
    8941             :       case type::Array:
    8942             :         // Not yet optimized.
    8943           0 :         trackOptimizationOutcome(TrackedOutcome::GenericFailure);
    8944           0 :         return Ok();
    8945             :     }
    8946             : 
    8947           0 :     MOZ_CRASH("Bad kind");
    8948             : }
    8949             : 
    8950             : AbortReasonOr<Ok>
    8951           0 : IonBuilder::setElemTryReferenceElemOfTypedObject(bool* emitted,
    8952             :                                                  MDefinition* obj,
    8953             :                                                  MDefinition* index,
    8954             :                                                  TypedObjectPrediction objPrediction,
    8955             :                                                  MDefinition* value,
    8956             :                                                  TypedObjectPrediction elemPrediction)
    8957             : {
    8958           0 :     ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
    8959           0 :     uint32_t elemSize = ReferenceTypeDescr::size(elemType);
    8960             : 
    8961           0 :     LinearSum indexAsByteOffset(alloc());
    8962           0 :     if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
    8963           0 :         return Ok();
    8964             : 
    8965             :     return setPropTryReferenceTypedObjectValue(emitted, obj, indexAsByteOffset,
    8966           0 :                                                elemType, value, nullptr);
    8967             : }
    8968             : 
    8969             : AbortReasonOr<Ok>
    8970           0 : IonBuilder::setElemTryScalarElemOfTypedObject(bool* emitted,
    8971             :                                               MDefinition* obj,
    8972             :                                               MDefinition* index,
    8973             :                                               TypedObjectPrediction objPrediction,
    8974             :                                               MDefinition* value,
    8975             :                                               TypedObjectPrediction elemPrediction,
    8976             :                                               uint32_t elemSize)
    8977             : {
    8978             :     // Must always be loading the same scalar type
    8979           0 :     ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
    8980           0 :     MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
    8981             : 
    8982           0 :     LinearSum indexAsByteOffset(alloc());
    8983           0 :     if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
    8984           0 :         return Ok();
    8985             : 
    8986           0 :     return setPropTryScalarTypedObjectValue(emitted, obj, indexAsByteOffset, elemType, value);
    8987             : }
    8988             : 
    8989             : AbortReasonOr<Ok>
    8990          71 : IonBuilder::setElemTryTypedArray(bool* emitted, MDefinition* object,
    8991             :                                  MDefinition* index, MDefinition* value)
    8992             : {
    8993          71 :     MOZ_ASSERT(*emitted == false);
    8994             : 
    8995             :     Scalar::Type arrayType;
    8996           0 :     if (!ElementAccessIsTypedArray(constraints(), object, index, &arrayType)) {
    8997          71 :         trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray);
    8998          71 :         return Ok();
    8999             :     }
    9000             : 
    9001             :     // Emit typed setelem variant.
    9002           0 :     MOZ_TRY(jsop_setelem_typed(arrayType, object, index, value));
    9003             : 
    9004           0 :     trackOptimizationSuccess();
    9005           0 :     *emitted = true;
    9006           0 :     return Ok();
    9007             : }
    9008             : 
    9009             : AbortReasonOr<Ok>
    9010          98 : IonBuilder::initOrSetElemTryDense(bool* emitted, MDefinition* object,
    9011             :                                   MDefinition* index, MDefinition* value, bool writeHole)
    9012             : {
    9013           0 :     MOZ_ASSERT(*emitted == false);
    9014             : 
    9015           0 :     if (value->type() == MIRType::MagicHole)
    9016             :     {
    9017           0 :         trackOptimizationOutcome(TrackedOutcome::InitHole);
    9018           0 :         return Ok();
    9019             :     }
    9020             : 
    9021           0 :     if (!ElementAccessIsDenseNative(constraints(), object, index)) {
    9022          56 :         trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
    9023          56 :         return Ok();
    9024             :     }
    9025             : 
    9026          42 :     if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
    9027             :                                       &object, nullptr, &value, /* canModify = */ true))
    9028             :     {
    9029           0 :         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
    9030           0 :         return Ok();
    9031             :     }
    9032             : 
    9033           0 :     if (!object->resultTypeSet()) {
    9034           2 :         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
    9035           2 :         return Ok();
    9036             :     }
    9037             : 
    9038             :     TemporaryTypeSet::DoubleConversion conversion =
    9039          40 :         object->resultTypeSet()->convertDoubleElements(constraints());
    9040             : 
    9041             :     // If AmbiguousDoubleConversion, only handle int32 values for now.
    9042          40 :     if (conversion == TemporaryTypeSet::AmbiguousDoubleConversion &&
    9043           0 :         value->type() != MIRType::Int32)
    9044             :     {
    9045           0 :         trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion);
    9046           0 :         return Ok();
    9047             :     }
    9048             : 
    9049             :     // Don't generate a fast path if there have been bounds check failures
    9050             :     // and this access might be on a sparse property.
    9051             :     bool hasExtraIndexedProperty;
    9052           0 :     MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, object));
    9053           0 :     if (hasExtraIndexedProperty && failedBoundsCheck_) {
    9054           0 :         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
    9055           0 :         return Ok();
    9056             :     }
    9057             : 
    9058             :     // Emit dense setelem variant.
    9059           0 :     MOZ_TRY(initOrSetElemDense(conversion, object, index, value, writeHole, emitted));
    9060             : 
    9061           0 :     if (!*emitted) {
    9062           0 :         trackOptimizationOutcome(TrackedOutcome::NonWritableProperty);
    9063           0 :         return Ok();
    9064             :     }
    9065             : 
    9066          40 :     trackOptimizationSuccess();
    9067          40 :     return Ok();
    9068             : }
    9069             : 
    9070             : AbortReasonOr<Ok>
    9071           0 : IonBuilder::setElemTryArguments(bool* emitted, MDefinition* object)
    9072             : {
    9073           0 :     MOZ_ASSERT(*emitted == false);
    9074             : 
    9075          43 :     if (object->type() != MIRType::MagicOptimizedArguments)
    9076          43 :         return Ok();
    9077             : 
    9078             :     // Arguments are not supported yet.
    9079           0 :     return abort(AbortReason::Disable, "NYI arguments[]=");
    9080             : }
    9081             : 
    9082             : AbortReasonOr<Ok>
    9083          58 : IonBuilder::initOrSetElemTryCache(bool* emitted, MDefinition* object,
    9084             :                                   MDefinition* index, MDefinition* value)
    9085             : {
    9086           0 :     MOZ_ASSERT(*emitted == false);
    9087             : 
    9088           0 :     if (!object->mightBeType(MIRType::Object)) {
    9089          12 :         trackOptimizationOutcome(TrackedOutcome::NotObject);
    9090          12 :         return Ok();
    9091             :     }
    9092             : 
    9093           0 :     if (value->type() == MIRType::MagicHole)
    9094             :     {
    9095           0 :         trackOptimizationOutcome(TrackedOutcome::InitHole);
    9096           0 :         return Ok();
    9097             :     }
    9098             : 
    9099           0 :     bool barrier = true;
    9100          51 :     if (index->type() == MIRType::Int32 &&
    9101           5 :         !PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
    9102             :                                        &object, nullptr, &value, /* canModify = */ true))
    9103             :     {
    9104           2 :         barrier = false;
    9105             :     }
    9106             : 
    9107             :     // We can avoid worrying about holes in the IC if we know a priori we are safe
    9108             :     // from them. If TI can guard that there are no indexed properties on the prototype
    9109             :     // chain, we know that we anen't missing any setters by overwriting the hole with
    9110             :     // another value.
    9111             :     bool guardHoles;
    9112          92 :     MOZ_TRY_VAR(guardHoles, ElementAccessHasExtraIndexedProperty(this, object));
    9113             : 
    9114             :     // Make sure the object being written to doesn't have copy on write elements.
    9115           0 :     const Class* clasp = object->resultTypeSet() ? object->resultTypeSet()->getKnownClass(constraints()) : nullptr;
    9116          80 :     bool checkNative = !clasp || !clasp->isNative();
    9117          46 :     object = addMaybeCopyElementsForWrite(object, checkNative);
    9118             : 
    9119             :     // Emit SetPropertyCache.
    9120           0 :     bool strict = JSOp(*pc) == JSOP_STRICTSETELEM;
    9121             :     MSetPropertyCache* ins =
    9122           0 :         MSetPropertyCache::New(alloc(), object, index, value, strict, needsPostBarrier(value),
    9123          46 :                                barrier, guardHoles);
    9124          46 :     current->add(ins);
    9125             : 
    9126             :     // Push value back onto stack. Init ops keep their object on stack.
    9127          92 :     if (!IsPropertyInitOp(JSOp(*pc)))
    9128           0 :         current->push(value);
    9129             : 
    9130           0 :     MOZ_TRY(resumeAfter(ins));
    9131             : 
    9132           0 :     trackOptimizationSuccess();
    9133          46 :     *emitted = true;
    9134          46 :     return Ok();
    9135             : }
    9136             : 
    9137             : AbortReasonOr<Ok>
    9138          40 : IonBuilder::initOrSetElemDense(TemporaryTypeSet::DoubleConversion conversion,
    9139             :                                MDefinition* obj, MDefinition* id, MDefinition* value,
    9140             :                                bool writeHole, bool* emitted)
    9141             : {
    9142           0 :     MOZ_ASSERT(*emitted == false);
    9143             : 
    9144          40 :     MIRType elementType = DenseNativeElementType(constraints(), obj);
    9145          40 :     bool packed = ElementAccessIsPacked(constraints(), obj);
    9146             : 
    9147             :     // Writes which are on holes in the object do not have to bail out if they
    9148             :     // cannot hit another indexed property on the object or its prototypes.
    9149             :     bool hasExtraIndexedProperty;
    9150           0 :     MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
    9151             : 
    9152           0 :     bool mayBeNonExtensible = ElementAccessMightBeNonExtensible(constraints(), obj);
    9153             : 
    9154          40 :     if (mayBeNonExtensible && hasExtraIndexedProperty) {
    9155             :         // FallibleStoreElement does not know how to deal with extra indexed
    9156             :         // properties on the prototype. This case should be rare so we fall back
    9157             :         // to an IC.
    9158           0 :         return Ok();
    9159             :     }
    9160             : 
    9161          40 :     *emitted = true;
    9162             : 
    9163             :     // Ensure id is an integer.
    9164           0 :     MInstruction* idInt32 = MToNumberInt32::New(alloc(), id);
    9165          40 :     current->add(idInt32);
    9166           0 :     id = idInt32;
    9167             : 
    9168          40 :     if (needsPostBarrier(value))
    9169           1 :         current->add(MPostWriteElementBarrier::New(alloc(), obj, value, id));
    9170             : 
    9171             :     // Copy the elements vector if necessary.
    9172          40 :     obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
    9173             : 
    9174             :     // Get the elements vector.
    9175          40 :     MElements* elements = MElements::New(alloc(), obj);
    9176          40 :     current->add(elements);
    9177             : 
    9178             :     // Ensure the value is a double, if double conversion might be needed.
    9179          40 :     MDefinition* newValue = value;
    9180          40 :     switch (conversion) {
    9181             :       case TemporaryTypeSet::AlwaysConvertToDoubles:
    9182             :       case TemporaryTypeSet::MaybeConvertToDoubles: {
    9183           0 :         MInstruction* valueDouble = MToDouble::New(alloc(), value);
    9184           0 :         current->add(valueDouble);
    9185           0 :         newValue = valueDouble;
    9186           0 :         break;
    9187             :       }
    9188             : 
    9189             :       case TemporaryTypeSet::AmbiguousDoubleConversion: {
    9190           0 :         MOZ_ASSERT(value->type() == MIRType::Int32);
    9191           0 :         MInstruction* maybeDouble = MMaybeToDoubleElement::New(alloc(), elements, value);
    9192           0 :         current->add(maybeDouble);
    9193           0 :         newValue = maybeDouble;
    9194           0 :         break;
    9195             :       }
    9196             : 
    9197             :       case TemporaryTypeSet::DontConvertToDoubles:
    9198             :         break;
    9199             : 
    9200             :       default:
    9201           0 :         MOZ_CRASH("Unknown double conversion");
    9202             :     }
    9203             : 
    9204             :     // Use MStoreElementHole if this SETELEM has written to out-of-bounds
    9205             :     // indexes in the past. Otherwise, use MStoreElement so that we can hoist
    9206             :     // the initialized length and bounds check.
    9207             :     // If an object may have been made non-extensible, no previous expectations
    9208             :     // hold and we fallback to MFallibleStoreElement.
    9209             :     MInstruction* store;
    9210           0 :     MStoreElementCommon* common = nullptr;
    9211           0 :     if (writeHole && !hasExtraIndexedProperty && !mayBeNonExtensible) {
    9212           0 :         MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue);
    9213          12 :         store = ins;
    9214           0 :         common = ins;
    9215             : 
    9216           0 :         current->add(ins);
    9217          28 :     } else if (mayBeNonExtensible) {
    9218           0 :         MOZ_ASSERT(!hasExtraIndexedProperty,
    9219             :                    "FallibleStoreElement codegen assumes no extra indexed properties");
    9220             : 
    9221           0 :         bool needsHoleCheck = !packed;
    9222           0 :         MFallibleStoreElement* ins = MFallibleStoreElement::New(alloc(), obj, elements, id,
    9223           0 :                                                                 newValue, needsHoleCheck);
    9224           0 :         store = ins;
    9225           0 :         common = ins;
    9226             : 
    9227           0 :         current->add(ins);
    9228             :     } else {
    9229           0 :         MInstruction* initLength = initializedLength(elements);
    9230             : 
    9231          28 :         id = addBoundsCheck(id, initLength);
    9232           0 :         bool needsHoleCheck = !packed && hasExtraIndexedProperty;
    9233             : 
    9234           0 :         MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
    9235          28 :         store = ins;
    9236           0 :         common = ins;
    9237             : 
    9238          28 :         current->add(store);
    9239             :     }
    9240             : 
    9241             :     // Push value back onto stack. Init ops keep their object on stack.
    9242          80 :     if (!IsPropertyInitOp(JSOp(*pc)))
    9243           0 :         current->push(value);
    9244             : 
    9245           0 :     MOZ_TRY(resumeAfter(store));
    9246             : 
    9247           0 :     if (common) {
    9248             :         // Determine whether a write barrier is required.
    9249          40 :         if (obj->resultTypeSet()->propertyNeedsBarrier(constraints(), JSID_VOID))
    9250             :             common->setNeedsBarrier();
    9251             : 
    9252          40 :         if (elementType != MIRType::None && packed)
    9253          40 :             common->setElementType(elementType);
    9254             :     }
    9255             : 
    9256          40 :     return Ok();
    9257             : }
    9258             : 
    9259             : 
    9260             : AbortReasonOr<Ok>
    9261           0 : IonBuilder::jsop_setelem_typed(Scalar::Type arrayType,
    9262             :                                MDefinition* obj, MDefinition* id, MDefinition* value)
    9263             : {
    9264           0 :     SetElemICInspector icInspect(inspector->setElemICInspector(pc));
    9265           0 :     bool expectOOB = icInspect.sawOOBTypedArrayWrite();
    9266             : 
    9267           0 :     if (expectOOB)
    9268           0 :         spew("Emitting OOB TypedArray SetElem");
    9269             : 
    9270             :     // Ensure id is an integer.
    9271           0 :     MInstruction* idInt32 = MToNumberInt32::New(alloc(), id);
    9272           0 :     current->add(idInt32);
    9273           0 :     id = idInt32;
    9274             : 
    9275             :     // Get length, bounds-check, then get elements, and add all instructions.
    9276             :     MInstruction* length;
    9277             :     MInstruction* elements;
    9278           0 :     BoundsChecking checking = expectOOB ? SkipBoundsCheck : DoBoundsCheck;
    9279           0 :     addTypedArrayLengthAndData(obj, checking, &id, &length, &elements);
    9280             : 
    9281             :     // Clamp value to [0, 255] for Uint8ClampedArray.
    9282           0 :     MDefinition* toWrite = value;
    9283           0 :     if (arrayType == Scalar::Uint8Clamped) {
    9284           0 :         toWrite = MClampToUint8::New(alloc(), value);
    9285           0 :         current->add(toWrite->toInstruction());
    9286             :     }
    9287             : 
    9288             :     // Store the value.
    9289             :     MInstruction* ins;
    9290           0 :     if (expectOOB) {
    9291           0 :         ins = MStoreTypedArrayElementHole::New(alloc(), elements, length, id, toWrite, arrayType);
    9292             :     } else {
    9293             :         MStoreUnboxedScalar* store =
    9294           0 :             MStoreUnboxedScalar::New(alloc(), elements, id, toWrite, arrayType,
    9295           0 :                                      MStoreUnboxedScalar::TruncateInput);
    9296           0 :         ins = store;
    9297             :     }
    9298             : 
    9299           0 :     current->add(ins);
    9300           0 :     current->push(value);
    9301             : 
    9302           0 :     return resumeAfter(ins);
    9303             : }
    9304             : 
    9305             : AbortReasonOr<Ok>
    9306           0 : IonBuilder::jsop_length()
    9307             : {
    9308         244 :     if (jsop_length_fastPath())
    9309           0 :         return Ok();
    9310             : 
    9311         464 :     PropertyName* name = info().getAtom(pc)->asPropertyName();
    9312         232 :     return jsop_getprop(name);
    9313             : }
    9314             : 
    9315             : bool
    9316           0 : IonBuilder::jsop_length_fastPath()
    9317             : {
    9318           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    9319             : 
    9320         244 :     if (types->getKnownMIRType() != MIRType::Int32)
    9321             :         return false;
    9322             : 
    9323           0 :     MDefinition* obj = current->peek(-1);
    9324             : 
    9325         112 :     if (shouldAbortOnPreliminaryGroups(obj))
    9326             :         return false;
    9327             : 
    9328         112 :     if (obj->mightBeType(MIRType::String)) {
    9329           0 :         if (obj->mightBeType(MIRType::Object))
    9330             :             return false;
    9331           0 :         current->pop();
    9332           0 :         MStringLength* ins = MStringLength::New(alloc(), obj);
    9333           0 :         current->add(ins);
    9334           0 :         current->push(ins);
    9335           0 :         return true;
    9336             :     }
    9337             : 
    9338         112 :     if (obj->mightBeType(MIRType::Object)) {
    9339         108 :         TemporaryTypeSet* objTypes = obj->resultTypeSet();
    9340             : 
    9341             :         // Compute the length for array objects.
    9342           0 :         if (objTypes &&
    9343         120 :             objTypes->getKnownClass(constraints()) == &ArrayObject::class_ &&
    9344           0 :             !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW))
    9345             :         {
    9346           0 :             current->pop();
    9347          12 :             MElements* elements = MElements::New(alloc(), obj);
    9348          12 :             current->add(elements);
    9349             : 
    9350             :             // Read length.
    9351           0 :             MArrayLength* length = MArrayLength::New(alloc(), elements);
    9352          12 :             current->add(length);
    9353          12 :             current->push(length);
    9354             :             return true;
    9355             :         }
    9356             : 
    9357             :         // Compute the length for array typed objects.
    9358           0 :         TypedObjectPrediction prediction = typedObjectPrediction(obj);
    9359           0 :         if (!prediction.isUseless()) {
    9360           0 :             TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
    9361           0 :             if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
    9362             :                 return false;
    9363             : 
    9364             :             MInstruction* length;
    9365             :             int32_t sizedLength;
    9366           0 :             if (prediction.hasKnownArrayLength(&sizedLength)) {
    9367           0 :                 obj->setImplicitlyUsedUnchecked();
    9368           0 :                 length = MConstant::New(alloc(), Int32Value(sizedLength));
    9369             :             } else {
    9370             :                 return false;
    9371             :             }
    9372             : 
    9373           0 :             current->pop();
    9374           0 :             current->add(length);
    9375           0 :             current->push(length);
    9376           0 :             return true;
    9377             :         }
    9378             :     }
    9379             : 
    9380             :     return false;
    9381             : }
    9382             : 
    9383             : AbortReasonOr<Ok>
    9384           0 : IonBuilder::jsop_arguments()
    9385             : {
    9386           0 :     if (info().needsArgsObj()) {
    9387          53 :         current->push(current->argumentsObject());
    9388          53 :         return Ok();
    9389             :     }
    9390             : 
    9391           0 :     MOZ_ASSERT(hasLazyArguments_);
    9392           0 :     MConstant* lazyArg = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
    9393           0 :     current->add(lazyArg);
    9394           2 :     current->push(lazyArg);
    9395           2 :     return Ok();
    9396             : }
    9397             : 
    9398             : AbortReasonOr<Ok>
    9399           0 : IonBuilder::jsop_newtarget()
    9400             : {
    9401           1 :     if (!info().funMaybeLazy()) {
    9402           0 :         MOZ_ASSERT(!info().script()->isForEval());
    9403           0 :         pushConstant(NullValue());
    9404           0 :         return Ok();
    9405             :     }
    9406             : 
    9407           0 :     MOZ_ASSERT(info().funMaybeLazy());
    9408             : 
    9409           0 :     if (info().funMaybeLazy()->isArrow()) {
    9410           0 :         MArrowNewTarget* arrowNewTarget = MArrowNewTarget::New(alloc(), getCallee());
    9411           0 :         current->add(arrowNewTarget);
    9412           0 :         current->push(arrowNewTarget);
    9413           0 :         return Ok();
    9414             :     }
    9415             : 
    9416           0 :     if (inliningDepth_ == 0) {
    9417           0 :         MNewTarget* newTarget = MNewTarget::New(alloc());
    9418           0 :         current->add(newTarget);
    9419           7 :         current->push(newTarget);
    9420           7 :         return Ok();
    9421             :     }
    9422             : 
    9423           0 :     if (!inlineCallInfo_->constructing()) {
    9424           0 :         pushConstant(UndefinedValue());
    9425           0 :         return Ok();
    9426             :     }
    9427             : 
    9428           0 :     current->push(inlineCallInfo_->getNewTarget());
    9429           0 :     return Ok();
    9430             : }
    9431             : 
    9432             : AbortReasonOr<Ok>
    9433           0 : IonBuilder::jsop_rest()
    9434             : {
    9435           1 :     if (info().analysisMode() == Analysis_ArgumentsUsage) {
    9436             :         // There's no BaselineScript with the template object. Just push a
    9437             :         // dummy value, it does not affect the arguments analysis.
    9438           0 :         MUnknownValue* unknown = MUnknownValue::New(alloc());
    9439           0 :         current->add(unknown);
    9440           0 :         current->push(unknown);
    9441           0 :         return Ok();
    9442             :     }
    9443             : 
    9444           0 :     ArrayObject* templateObject = &inspector->getTemplateObject(pc)->as<ArrayObject>();
    9445             : 
    9446           0 :     if (inliningDepth_ == 0) {
    9447             :         // We don't know anything about the callee.
    9448           1 :         MArgumentsLength* numActuals = MArgumentsLength::New(alloc());
    9449           1 :         current->add(numActuals);
    9450             : 
    9451             :         // Pass in the number of actual arguments, the number of formals (not
    9452             :         // including the rest parameter slot itself), and the template object.
    9453           0 :         MRest* rest = MRest::New(alloc(), constraints(), numActuals, info().nargs() - 1,
    9454           0 :                                  templateObject);
    9455           0 :         current->add(rest);
    9456           1 :         current->push(rest);
    9457           1 :         return Ok();
    9458             :     }
    9459             : 
    9460             :     // We know the exact number of arguments the callee pushed.
    9461           0 :     unsigned numActuals = inlineCallInfo_->argc();
    9462           0 :     unsigned numFormals = info().nargs() - 1;
    9463           0 :     unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
    9464             : 
    9465           0 :     MOZ_TRY(jsop_newarray(numRest));
    9466             : 
    9467           0 :     if (numRest == 0) {
    9468             :         // No more updating to do. (Note that in this one case the length from
    9469             :         // the template object is already correct.)
    9470           0 :         return Ok();
    9471             :     }
    9472             : 
    9473           0 :     MDefinition *array = current->peek(-1);
    9474           0 :     MElements* elements = MElements::New(alloc(), array);
    9475           0 :     current->add(elements);
    9476             : 
    9477             :     // Unroll the argument copy loop. We don't need to do any bounds or hole
    9478             :     // checking here.
    9479           0 :     MConstant* index = nullptr;
    9480           0 :     for (unsigned i = numFormals; i < numActuals; i++) {
    9481           0 :         if (!alloc().ensureBallast())
    9482           0 :             return abort(AbortReason::Alloc);
    9483             : 
    9484           0 :         index = MConstant::New(alloc(), Int32Value(i - numFormals));
    9485           0 :         current->add(index);
    9486             : 
    9487           0 :         MDefinition* arg = inlineCallInfo_->argv()[i];
    9488           0 :         MStoreElement* store = MStoreElement::New(alloc(), elements, index, arg,
    9489           0 :                                                   /* needsHoleCheck = */ false);
    9490           0 :         current->add(store);
    9491             : 
    9492           0 :         if (needsPostBarrier(arg))
    9493           0 :             current->add(MPostWriteBarrier::New(alloc(), array, arg));
    9494             :     }
    9495             : 
    9496             :     // The array's length is incorrectly 0 now, from the template object
    9497             :     // created by BaselineCompiler::emit_JSOP_REST() before the actual argument
    9498             :     // count was known. Set the correct length now that we know that count.
    9499           0 :     MSetArrayLength* length = MSetArrayLength::New(alloc(), elements, index);
    9500           0 :     current->add(length);
    9501             : 
    9502             :     // Update the initialized length for all the (necessarily non-hole)
    9503             :     // elements added.
    9504           0 :     MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, index);
    9505           0 :     current->add(initLength);
    9506             : 
    9507           0 :     return Ok();
    9508             : }
    9509             : 
    9510             : AbortReasonOr<Ok>
    9511           0 : IonBuilder::jsop_checkisobj(uint8_t kind)
    9512             : {
    9513           0 :     MDefinition* toCheck = current->peek(-1);
    9514             : 
    9515           0 :     if (toCheck->type() == MIRType::Object) {
    9516          98 :         toCheck->setImplicitlyUsedUnchecked();
    9517          98 :         return Ok();
    9518             :     }
    9519             : 
    9520           0 :     MCheckIsObj* check = MCheckIsObj::New(alloc(), current->pop(), kind);
    9521           0 :     current->add(check);
    9522          43 :     current->push(check);
    9523          43 :     return Ok();
    9524             : }
    9525             : 
    9526             : AbortReasonOr<Ok>
    9527           0 : IonBuilder::jsop_checkiscallable(uint8_t kind)
    9528             : {
    9529           0 :     MCheckIsCallable* check = MCheckIsCallable::New(alloc(), current->pop(), kind);
    9530           0 :     current->add(check);
    9531           0 :     current->push(check);
    9532           0 :     return Ok();
    9533             : }
    9534             : 
    9535             : AbortReasonOr<Ok>
    9536           0 : IonBuilder::jsop_checkobjcoercible()
    9537             : {
    9538           0 :     MDefinition* toCheck = current->peek(-1);
    9539             : 
    9540           6 :     if (!toCheck->mightBeType(MIRType::Undefined) &&
    9541           0 :         !toCheck->mightBeType(MIRType::Null))
    9542             :     {
    9543           3 :         toCheck->setImplicitlyUsedUnchecked();
    9544           3 :         return Ok();
    9545             :     }
    9546             : 
    9547           0 :     MOZ_ASSERT(toCheck->type() == MIRType::Value ||
    9548             :                toCheck->type() == MIRType::Null  ||
    9549             :                toCheck->type() == MIRType::Undefined);
    9550             : 
    9551             :     // If we want to squeeze more perf here, we can throw without checking,
    9552             :     // if IsNullOrUndefined(toCheck->type()). Since this is a failure case,
    9553             :     // it should be OK.
    9554           0 :     MCheckObjCoercible* check = MCheckObjCoercible::New(alloc(), current->pop());
    9555           0 :     current->add(check);
    9556           0 :     current->push(check);
    9557             :     return resumeAfter(check);
    9558             : }
    9559             : 
    9560             : uint32_t
    9561           0 : IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, jsid id, uint32_t* pnfixed)
    9562             : {
    9563        2514 :     if (!types || types->unknownObject() || !types->objectOrSentinel()) {
    9564             :         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
    9565             :         return UINT32_MAX;
    9566             :     }
    9567             : 
    9568             :     uint32_t slot = UINT32_MAX;
    9569             : 
    9570           0 :     for (size_t i = 0; i < types->getObjectCount(); i++) {
    9571           0 :         TypeSet::ObjectKey* key = types->getObject(i);
    9572         895 :         if (!key)
    9573           0 :             continue;
    9574             : 
    9575           0 :         if (key->unknownProperties()) {
    9576             :             trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
    9577         188 :             return UINT32_MAX;
    9578             :         }
    9579             : 
    9580         895 :         if (key->isSingleton()) {
    9581             :             trackOptimizationOutcome(TrackedOutcome::Singleton);
    9582             :             return UINT32_MAX;
    9583             :         }
    9584             : 
    9585           0 :         HeapTypeSetKey property = key->property(id);
    9586           0 :         if (!property.maybeTypes() ||
    9587        1570 :             !property.maybeTypes()->definiteProperty() ||
    9588         713 :             property.nonData(constraints()))
    9589             :         {
    9590             :             trackOptimizationOutcome(TrackedOutcome::NotFixedSlot);
    9591             :             return UINT32_MAX;
    9592             :         }
    9593             : 
    9594             :         // Definite slots will always be fixed slots when they are in the
    9595             :         // allowable range for fixed slots, except for objects which were
    9596             :         // converted from unboxed objects and have a smaller allocation size.
    9597           0 :         size_t nfixed = NativeObject::MAX_FIXED_SLOTS;
    9598           0 :         if (ObjectGroup* group = key->group()->maybeOriginalUnboxedGroup()) {
    9599          12 :             AutoSweepObjectGroup sweepGroup(group);
    9600           6 :             nfixed = gc::GetGCKindSlots(group->unboxedLayout(sweepGroup).getAllocKind());
    9601             :         }
    9602             : 
    9603           0 :         uint32_t propertySlot = property.maybeTypes()->definiteSlot();
    9604           0 :         if (slot == UINT32_MAX) {
    9605           0 :             slot = propertySlot;
    9606         691 :             *pnfixed = nfixed;
    9607          22 :         } else if (slot != propertySlot || nfixed != *pnfixed) {
    9608             :             trackOptimizationOutcome(TrackedOutcome::InconsistentFixedSlot);
    9609             :             return UINT32_MAX;
    9610             :         }
    9611             :     }
    9612             : 
    9613             :     return slot;
    9614             : }
    9615             : 
    9616             : uint32_t
    9617           0 : IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, jsid id, JSValueType* punboxedType)
    9618             : {
    9619        2302 :     if (!types || types->unknownObject() || !types->objectOrSentinel()) {
    9620             :         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
    9621             :         return UINT32_MAX;
    9622             :     }
    9623             : 
    9624             :     uint32_t offset = UINT32_MAX;
    9625             : 
    9626           0 :     for (size_t i = 0; i < types->getObjectCount(); i++) {
    9627           0 :         TypeSet::ObjectKey* key = types->getObject(i);
    9628         767 :         if (!key)
    9629           0 :             continue;
    9630             : 
    9631           0 :         if (key->unknownProperties()) {
    9632             :             trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
    9633         727 :             return UINT32_MAX;
    9634             :         }
    9635             : 
    9636         767 :         if (key->isSingleton()) {
    9637             :             trackOptimizationOutcome(TrackedOutcome::Singleton);
    9638             :             return UINT32_MAX;
    9639             :         }
    9640             : 
    9641           0 :         AutoSweepObjectGroup sweep(key->group());
    9642         729 :         UnboxedLayout* layout = key->group()->maybeUnboxedLayout(sweep);
    9643           0 :         if (!layout) {
    9644             :             trackOptimizationOutcome(TrackedOutcome::NotUnboxed);
    9645         689 :             return UINT32_MAX;
    9646             :         }
    9647             : 
    9648          40 :         const UnboxedLayout::Property* property = layout->lookup(id);
    9649          40 :         if (!property) {
    9650             :             trackOptimizationOutcome(TrackedOutcome::StructNoField);
    9651             :             return UINT32_MAX;
    9652             :         }
    9653             : 
    9654          40 :         if (layout->nativeGroup()) {
    9655             :             trackOptimizationOutcome(TrackedOutcome::UnboxedConvertedToNative);
    9656             :             return UINT32_MAX;
    9657             :         }
    9658             : 
    9659           0 :         key->watchStateChangeForUnboxedConvertedToNative(constraints());
    9660             : 
    9661           0 :         if (offset == UINT32_MAX) {
    9662           0 :             offset = property->offset;
    9663          40 :             *punboxedType = property->type;
    9664           0 :         } else if (offset != property->offset) {
    9665             :             trackOptimizationOutcome(TrackedOutcome::InconsistentFieldOffset);
    9666             :             return UINT32_MAX;
    9667           0 :         } else if (*punboxedType != property->type) {
    9668             :             trackOptimizationOutcome(TrackedOutcome::InconsistentFieldType);
    9669             :             return UINT32_MAX;
    9670             :         }
    9671             :     }
    9672             : 
    9673             :     return offset;
    9674             : }
    9675             : 
    9676             : AbortReasonOr<Ok>
    9677           0 : IonBuilder::jsop_runonce()
    9678             : {
    9679           0 :     MRunOncePrologue* ins = MRunOncePrologue::New(alloc());
    9680           0 :     current->add(ins);
    9681           0 :     return resumeAfter(ins);
    9682             : }
    9683             : 
    9684             : AbortReasonOr<Ok>
    9685           0 : IonBuilder::jsop_not()
    9686             : {
    9687           0 :     MDefinition* value = current->pop();
    9688             : 
    9689           0 :     MNot* ins = MNot::New(alloc(), value, constraints());
    9690           0 :     current->add(ins);
    9691         386 :     current->push(ins);
    9692         386 :     return Ok();
    9693             : }
    9694             : 
    9695             : AbortReasonOr<Ok>
    9696           0 : IonBuilder::jsop_superbase()
    9697             : {
    9698           1 :     JSFunction* fun = info().funMaybeLazy();
    9699           4 :     if (!fun || !fun->allowSuperProperty())
    9700           0 :         return abort(AbortReason::Disable, "super only supported directly in methods");
    9701             : 
    9702           4 :     auto* homeObject = MHomeObject::New(alloc(), getCallee());
    9703           0 :     current->add(homeObject);
    9704             : 
    9705           0 :     auto* superBase = MHomeObjectSuperBase::New(alloc(), homeObject);
    9706           2 :     current->add(superBase);
    9707           0 :     current->push(superBase);
    9708             : 
    9709           6 :     MOZ_TRY(resumeAfter(superBase));
    9710           2 :     return Ok();
    9711             : }
    9712             : 
    9713             : 
    9714             : AbortReasonOr<Ok>
    9715           0 : IonBuilder::jsop_getprop_super(PropertyName* name)
    9716             : {
    9717           2 :     MDefinition* obj = current->pop();
    9718           0 :     MDefinition* receiver = current->pop();
    9719             : 
    9720           0 :     MConstant* id = constant(StringValue(name));
    9721           0 :     auto* ins = MGetPropSuperCache::New(alloc(), obj, receiver, id);
    9722           2 :     current->add(ins);
    9723           0 :     current->push(ins);
    9724             : 
    9725           0 :     MOZ_TRY(resumeAfter(ins));
    9726             : 
    9727           4 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    9728           2 :     return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
    9729             : }
    9730             : 
    9731             : AbortReasonOr<Ok>
    9732           0 : IonBuilder::jsop_getelem_super()
    9733             : {
    9734           0 :     MDefinition* obj = current->pop();
    9735           0 :     MDefinition* receiver = current->pop();
    9736           0 :     MDefinition* id = current->pop();
    9737             : 
    9738             : #if defined(JS_CODEGEN_X86)
    9739             :     if (instrumentedProfiling())
    9740             :         return abort(AbortReason::Disable, "profiling functions with GETELEM_SUPER is disabled on x86");
    9741             : #endif
    9742             : 
    9743           0 :     auto* ins = MGetPropSuperCache::New(alloc(), obj, receiver, id);
    9744           0 :     current->add(ins);
    9745           0 :     current->push(ins);
    9746             : 
    9747           0 :     MOZ_TRY(resumeAfter(ins));
    9748             : 
    9749           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    9750           0 :     return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
    9751             : }
    9752             : 
    9753             : NativeObject*
    9754           0 : IonBuilder::commonPrototypeWithGetterSetter(TemporaryTypeSet* types, PropertyName* name,
    9755             :                                             bool isGetter, JSFunction* getterOrSetter,
    9756             :                                             bool* guardGlobal)
    9757             : {
    9758             :     // If there's a single object on the proto chain of all objects in |types|
    9759             :     // that contains a property |name| with |getterOrSetter| getter or setter
    9760             :     // function, return that object.
    9761             : 
    9762             :     // No sense looking if we don't know what's going on.
    9763           0 :     if (!types || types->unknownObject())
    9764             :         return nullptr;
    9765           0 :     *guardGlobal = false;
    9766             : 
    9767           0 :     NativeObject* foundProto = nullptr;
    9768           0 :     for (unsigned i = 0; i < types->getObjectCount(); i++) {
    9769           0 :         TypeSet::ObjectKey* key = types->getObject(i);
    9770           0 :         if (!key)
    9771             :             continue;
    9772             : 
    9773           0 :         while (key) {
    9774           0 :             if (key->unknownProperties())
    9775           0 :                 return nullptr;
    9776             : 
    9777           0 :             const Class* clasp = key->clasp();
    9778           0 :             if (!ClassHasEffectlessLookup(clasp))
    9779             :                 return nullptr;
    9780           0 :             JSObject* singleton = key->isSingleton() ? key->singleton() : nullptr;
    9781           0 :             if (ObjectHasExtraOwnProperty(realm, key, NameToId(name))) {
    9782           0 :                 if (!singleton || !singleton->is<GlobalObject>())
    9783             :                     return nullptr;
    9784           0 :                 *guardGlobal = true;
    9785             :             }
    9786             : 
    9787             :             // Look for a getter/setter on the class itself which may need
    9788             :             // to be called.
    9789           0 :             if (isGetter && clasp->getOpsGetProperty())
    9790             :                 return nullptr;
    9791           0 :             if (!isGetter && clasp->getOpsSetProperty())
    9792             :                 return nullptr;
    9793             : 
    9794             :             // If we have a singleton, check if it contains the getter or
    9795             :             // setter we're looking for. Note that we don't need to add any
    9796             :             // type constraints as the caller will add a Shape guard for the
    9797             :             // holder and type constraints for other objects on the proto
    9798             :             // chain.
    9799             :             //
    9800             :             // If the object is not a singleton, we fall back to the code below
    9801             :             // and check whether the property is missing. That's fine because
    9802             :             // we're looking for a getter or setter on the proto chain and
    9803             :             // these objects are singletons.
    9804           0 :             if (singleton) {
    9805           0 :                 if (!singleton->is<NativeObject>())
    9806             :                     return nullptr;
    9807             : 
    9808           0 :                 NativeObject* singletonNative = &singleton->as<NativeObject>();
    9809           0 :                 if (Shape* propShape = singletonNative->lookupPure(name)) {
    9810             :                     // We found a property. Check if it's the getter or setter
    9811             :                     // we're looking for.
    9812           0 :                     Value getterSetterVal = ObjectValue(*getterOrSetter);
    9813           0 :                     if (isGetter) {
    9814           0 :                         if (propShape->getterOrUndefined() != getterSetterVal)
    9815             :                             return nullptr;
    9816             :                     } else {
    9817           0 :                         if (propShape->setterOrUndefined() != getterSetterVal)
    9818             :                             return nullptr;
    9819             :                     }
    9820             : 
    9821           0 :                     if (!foundProto)
    9822             :                         foundProto = singletonNative;
    9823           0 :                     else if (foundProto != singletonNative)
    9824             :                         return nullptr;
    9825             :                     break;
    9826             :                 }
    9827             :             }
    9828             : 
    9829             :             // Test for isOwnProperty() without freezing. If we end up
    9830             :             // optimizing, freezePropertiesForCommonPropFunc will freeze the
    9831             :             // property type sets later on.
    9832           0 :             HeapTypeSetKey property = key->property(NameToId(name));
    9833           0 :             if (TypeSet* types = property.maybeTypes()) {
    9834           0 :                 if (!types->empty() || types->nonDataProperty())
    9835             :                     return nullptr;
    9836             :             }
    9837           0 :             if (singleton) {
    9838           0 :                 if (CanHaveEmptyPropertyTypesForOwnProperty(singleton)) {
    9839           0 :                     MOZ_ASSERT(singleton->is<GlobalObject>());
    9840           0 :                     *guardGlobal = true;
    9841             :                 }
    9842             :             }
    9843             : 
    9844           0 :             JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
    9845           0 :             if (foundProto && proto == foundProto) {
    9846             :                 // We found an object on the proto chain that's known to have
    9847             :                 // the getter or setter property, so we can stop looking.
    9848             :                 break;
    9849             :             }
    9850             : 
    9851           0 :             if (!proto) {
    9852             :                 // The getter or setter being searched for did not show up on
    9853             :                 // the object's prototype chain.
    9854             :                 return nullptr;
    9855             :             }
    9856           0 :             key = TypeSet::ObjectKey::get(proto);
    9857             :         }
    9858             :     }
    9859             : 
    9860             :     return foundProto;
    9861             : }
    9862             : 
    9863             : void
    9864           0 : IonBuilder::freezePropertiesForCommonPrototype(TemporaryTypeSet* types, PropertyName* name,
    9865             :                                                JSObject* foundProto,
    9866             :                                                bool allowEmptyTypesforGlobal/* = false*/)
    9867             : {
    9868           0 :     for (unsigned i = 0; i < types->getObjectCount(); i++) {
    9869             :         // If we found a Singleton object's own-property, there's nothing to
    9870             :         // freeze.
    9871           0 :         if (types->getSingleton(i) == foundProto)
    9872             :             continue;
    9873             : 
    9874           0 :         TypeSet::ObjectKey* key = types->getObject(i);
    9875           0 :         if (!key)
    9876             :             continue;
    9877             : 
    9878             :         while (true) {
    9879           0 :             HeapTypeSetKey property = key->property(NameToId(name));
    9880           0 :             MOZ_ALWAYS_TRUE(!property.isOwnProperty(constraints(), allowEmptyTypesforGlobal));
    9881             : 
    9882             :             // Don't mark the proto. It will be held down by the shape
    9883             :             // guard. This allows us to use properties found on prototypes
    9884             :             // with properties unknown to TI.
    9885           0 :             if (key->proto() == TaggedProto(foundProto))
    9886             :                 break;
    9887           0 :             key = TypeSet::ObjectKey::get(key->proto().toObjectOrNull());
    9888           0 :         }
    9889             :     }
    9890           0 : }
    9891             : 
    9892             : bool
    9893           0 : IonBuilder::testCommonGetterSetter(TemporaryTypeSet* types, PropertyName* name,
    9894             :                                    bool isGetter, JSFunction* getterOrSetter,
    9895             :                                    MDefinition** guard,
    9896             :                                    Shape* globalShape/* = nullptr*/,
    9897             :                                    MDefinition** globalGuard/* = nullptr */)
    9898             : {
    9899           0 :     MOZ_ASSERT(getterOrSetter);
    9900           0 :     MOZ_ASSERT_IF(globalShape, globalGuard);
    9901             :     bool guardGlobal;
    9902             : 
    9903             :     // Check if all objects being accessed will lookup the name through foundProto.
    9904             :     NativeObject* foundProto =
    9905           0 :         commonPrototypeWithGetterSetter(types, name, isGetter, getterOrSetter, &guardGlobal);
    9906           0 :     if (!foundProto || (guardGlobal && !globalShape)) {
    9907             :         trackOptimizationOutcome(TrackedOutcome::MultiProtoPaths);
    9908             :         return false;
    9909             :     }
    9910             : 
    9911             :     // We can optimize the getter/setter, so freeze all involved properties to
    9912             :     // ensure there isn't a lower shadowing getter or setter installed in the
    9913             :     // future.
    9914           0 :     freezePropertiesForCommonPrototype(types, name, foundProto, guardGlobal);
    9915             : 
    9916             :     // Add a shape guard on the prototype we found the property on. The rest of
    9917             :     // the prototype chain is guarded by TI freezes, except when name is a global
    9918             :     // name. In this case, we also have to guard on the globals shape to be able
    9919             :     // to optimize, because the way global property sets are handled means
    9920             :     // freezing doesn't work for what we want here. Note that a shape guard is
    9921             :     // good enough here, even in the proxy case, because we have ensured there
    9922             :     // are no lookup hooks for this property.
    9923           0 :     if (guardGlobal) {
    9924           0 :         JSObject* obj = &script()->global();
    9925           0 :         MDefinition* globalObj = constant(ObjectValue(*obj));
    9926           0 :         *globalGuard = addShapeGuard(globalObj, globalShape, Bailout_ShapeGuard);
    9927             :     }
    9928             : 
    9929             :     // If the getter/setter is not configurable we don't have to guard on the
    9930             :     // proto's shape.
    9931           0 :     Shape* propShape = foundProto->lookupPure(name);
    9932           0 :     MOZ_ASSERT_IF(isGetter, propShape->getterObject() == getterOrSetter);
    9933           0 :     MOZ_ASSERT_IF(!isGetter, propShape->setterObject() == getterOrSetter);
    9934           0 :     if (propShape && !propShape->configurable())
    9935             :         return true;
    9936             : 
    9937           0 :     MInstruction* wrapper = constant(ObjectValue(*foundProto));
    9938           0 :     *guard = addShapeGuard(wrapper, foundProto->lastProperty(), Bailout_ShapeGuard);
    9939           0 :     return true;
    9940             : }
    9941             : 
    9942             : void
    9943           0 : IonBuilder::replaceMaybeFallbackFunctionGetter(MGetPropertyCache* cache)
    9944             : {
    9945             :     // Discard the last prior resume point of the previous MGetPropertyCache.
    9946           1 :     WrapMGetPropertyCache rai(maybeFallbackFunctionGetter_);
    9947        1670 :     maybeFallbackFunctionGetter_ = cache;
    9948           0 : }
    9949             : 
    9950             : AbortReasonOr<Ok>
    9951           1 : IonBuilder::annotateGetPropertyCache(MDefinition* obj, PropertyName* name,
    9952             :                                      MGetPropertyCache* getPropCache, TemporaryTypeSet* objTypes,
    9953             :                                      TemporaryTypeSet* pushedTypes)
    9954             : {
    9955             :     // Ensure every pushed value is a singleton.
    9956           3 :     if (pushedTypes->unknownObject() || pushedTypes->baseFlags() != 0)
    9957           0 :         return Ok();
    9958             : 
    9959           0 :     for (unsigned i = 0; i < pushedTypes->getObjectCount(); i++) {
    9960           1 :         if (pushedTypes->getGroup(i) != nullptr)
    9961           1 :             return Ok();
    9962             :     }
    9963             : 
    9964             :     // Object's typeset should be a proper object
    9965           0 :     if (!objTypes || objTypes->baseFlags() || objTypes->unknownObject())
    9966           0 :         return Ok();
    9967             : 
    9968           0 :     unsigned int objCount = objTypes->getObjectCount();
    9969           0 :     if (objCount == 0)
    9970           0 :         return Ok();
    9971             : 
    9972           0 :     InlinePropertyTable* inlinePropTable = getPropCache->initInlinePropertyTable(alloc(), pc);
    9973           0 :     if (!inlinePropTable)
    9974           0 :         return abort(AbortReason::Alloc);
    9975             : 
    9976             :     // Ensure that the relevant property typeset for each group is
    9977             :     // is a single-object typeset containing a JSFunction
    9978           0 :     for (unsigned int i = 0; i < objCount; i++) {
    9979           0 :         ObjectGroup* group = objTypes->getGroup(i);
    9980           0 :         if (!group)
    9981           0 :             continue;
    9982           0 :         TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(group);
    9983           0 :         if (key->unknownProperties() || !key->proto().isObject())
    9984             :             continue;
    9985           0 :         JSObject* proto = checkNurseryObject(key->proto().toObject());
    9986             : 
    9987           0 :         const Class* clasp = key->clasp();
    9988           0 :         if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(realm, key, NameToId(name)))
    9989             :             continue;
    9990             : 
    9991           0 :         HeapTypeSetKey ownTypes = key->property(NameToId(name));
    9992           0 :         if (ownTypes.isOwnProperty(constraints()))
    9993             :             continue;
    9994             : 
    9995           0 :         JSObject* singleton = testSingletonProperty(proto, NameToId(name));
    9996           0 :         if (!singleton || !singleton->is<JSFunction>())
    9997             :             continue;
    9998             : 
    9999             :         // Don't add cases corresponding to non-observed pushes
   10000           0 :         if (!pushedTypes->hasType(TypeSet::ObjectType(singleton)))
   10001             :             continue;
   10002             : 
   10003           0 :         if (!inlinePropTable->addEntry(alloc(), group, &singleton->as<JSFunction>()))
   10004           0 :             return abort(AbortReason::Alloc);
   10005             :     }
   10006             : 
   10007           0 :     if (inlinePropTable->numEntries() == 0) {
   10008           0 :         getPropCache->clearInlinePropertyTable();
   10009           0 :         return Ok();
   10010             :     }
   10011             : 
   10012             : #ifdef JS_JITSPEW
   10013           0 :     if (inlinePropTable->numEntries() > 0)
   10014           0 :         JitSpew(JitSpew_Inlining, "Annotated GetPropertyCache with %d/%d inline cases",
   10015           0 :                                     (int) inlinePropTable->numEntries(), (int) objCount);
   10016             : #endif
   10017             : 
   10018             :     // If we successfully annotated the GetPropertyCache and there are inline cases,
   10019             :     // then keep a resume point of the state right before this instruction for use
   10020             :     // later when we have to bail out to this point in the fallback case of a
   10021             :     // PolyInlineDispatch.
   10022           0 :     if (inlinePropTable->numEntries() > 0) {
   10023             :         // Push the object back onto the stack temporarily to capture the resume point.
   10024           0 :         current->push(obj);
   10025           0 :         MResumePoint* resumePoint = MResumePoint::New(alloc(), current, pc,
   10026           0 :                                                       MResumePoint::ResumeAt);
   10027           0 :         if (!resumePoint)
   10028           0 :             return abort(AbortReason::Alloc);
   10029           0 :         inlinePropTable->setPriorResumePoint(resumePoint);
   10030           0 :         replaceMaybeFallbackFunctionGetter(getPropCache);
   10031           0 :         current->pop();
   10032             :     }
   10033           0 :     return Ok();
   10034             : }
   10035             : 
   10036             : // Returns true if an idempotent cache has ever invalidated this script
   10037             : // or an outer script.
   10038             : bool
   10039           0 : IonBuilder::invalidatedIdempotentCache()
   10040             : {
   10041           0 :     IonBuilder* builder = this;
   10042             :     do {
   10043           0 :         if (builder->script()->invalidatedIdempotentCache())
   10044             :             return true;
   10045         696 :         builder = builder->callerBuilder_;
   10046         696 :     } while (builder);
   10047             : 
   10048             :     return false;
   10049             : }
   10050             : 
   10051             : AbortReasonOr<Ok>
   10052          78 : IonBuilder::loadSlot(MDefinition* obj, size_t slot, size_t nfixed, MIRType rvalType,
   10053             :                      BarrierKind barrier, TemporaryTypeSet* types)
   10054             : {
   10055           0 :     if (slot < nfixed) {
   10056           0 :         MLoadFixedSlot* load = MLoadFixedSlot::New(alloc(), obj, slot);
   10057          66 :         current->add(load);
   10058           0 :         current->push(load);
   10059             : 
   10060         132 :         load->setResultType(rvalType);
   10061          66 :         return pushTypeBarrier(load, types, barrier);
   10062             :     }
   10063             : 
   10064          24 :     MSlots* slots = MSlots::New(alloc(), obj);
   10065           0 :     current->add(slots);
   10066             : 
   10067           0 :     MLoadSlot* load = MLoadSlot::New(alloc(), slots, slot - nfixed);
   10068          12 :     current->add(load);
   10069           0 :     current->push(load);
   10070             : 
   10071          24 :     load->setResultType(rvalType);
   10072          12 :     return pushTypeBarrier(load, types, barrier);
   10073             : }
   10074             : 
   10075             : AbortReasonOr<Ok>
   10076          78 : IonBuilder::loadSlot(MDefinition* obj, Shape* shape, MIRType rvalType,
   10077             :                      BarrierKind barrier, TemporaryTypeSet* types)
   10078             : {
   10079         156 :     return loadSlot(obj, shape->slot(), shape->numFixedSlots(), rvalType, barrier, types);
   10080             : }
   10081             : 
   10082             : AbortReasonOr<Ok>
   10083           1 : IonBuilder::storeSlot(MDefinition* obj, size_t slot, size_t nfixed,
   10084             :                       MDefinition* value, bool needsBarrier,
   10085             :                       MIRType slotType /* = MIRType::None */)
   10086             : {
   10087           0 :     if (slot < nfixed) {
   10088           0 :         MStoreFixedSlot* store = MStoreFixedSlot::New(alloc(), obj, slot, value);
   10089           0 :         current->add(store);
   10090           1 :         current->push(value);
   10091           1 :         if (needsBarrier)
   10092             :             store->setNeedsBarrier();
   10093             :         return resumeAfter(store);
   10094             :     }
   10095             : 
   10096           0 :     MSlots* slots = MSlots::New(alloc(), obj);
   10097           0 :     current->add(slots);
   10098             : 
   10099           0 :     MStoreSlot* store = MStoreSlot::New(alloc(), slots, slot - nfixed, value);
   10100           0 :     current->add(store);
   10101           0 :     current->push(value);
   10102           0 :     if (needsBarrier)
   10103           0 :         store->setNeedsBarrier();
   10104           0 :     if (slotType != MIRType::None)
   10105           0 :         store->setSlotType(slotType);
   10106             :     return resumeAfter(store);
   10107             : }
   10108             : 
   10109             : AbortReasonOr<Ok>
   10110           1 : IonBuilder::storeSlot(MDefinition* obj, Shape* shape, MDefinition* value, bool needsBarrier,
   10111             :                       MIRType slotType /* = MIRType::None */)
   10112             : {
   10113           1 :     MOZ_ASSERT(shape->writable());
   10114           2 :     return storeSlot(obj, shape->slot(), shape->numFixedSlots(), value, needsBarrier, slotType);
   10115             : }
   10116             : 
   10117             : bool
   10118        5123 : IonBuilder::shouldAbortOnPreliminaryGroups(MDefinition *obj)
   10119             : {
   10120             :     // Watch for groups which still have preliminary object information and
   10121             :     // have not had the new script properties or unboxed layout analyses
   10122             :     // performed. Normally this is done after a small number of the objects
   10123             :     // have been created, but if only a few have been created we can still
   10124             :     // perform the analysis with a smaller object population. The analysis can
   10125             :     // have side effects so we will end up aborting compilation after building
   10126             :     // finishes and retrying later.
   10127        5123 :     TemporaryTypeSet *types = obj->resultTypeSet();
   10128        8215 :     if (!types || types->unknownObject())
   10129             :         return false;
   10130             : 
   10131             :     bool preliminary = false;
   10132           0 :     for (size_t i = 0; i < types->getObjectCount(); i++) {
   10133        2325 :         TypeSet::ObjectKey* key = types->getObject(i);
   10134        2325 :         if (!key)
   10135             :             continue;
   10136             : 
   10137           0 :         if (ObjectGroup* group = key->maybeGroup()) {
   10138           0 :             if (group->hasUnanalyzedPreliminaryObjects()) {
   10139           7 :                 addAbortedPreliminaryGroup(group);
   10140           7 :                 preliminary = true;
   10141             :             }
   10142             :         }
   10143             :     }
   10144             : 
   10145             :     return preliminary;
   10146             : }
   10147             : 
   10148             : MDefinition*
   10149           0 : IonBuilder::maybeUnboxForPropertyAccess(MDefinition* def)
   10150             : {
   10151        1450 :     if (def->type() != MIRType::Value)
   10152             :         return def;
   10153             : 
   10154         236 :     MIRType type = inspector->expectedPropertyAccessInputType(pc);
   10155         236 :     if (type == MIRType::Value || !def->mightBeType(type))
   10156             :         return def;
   10157             : 
   10158           1 :     MUnbox* unbox = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
   10159           1 :     current->add(unbox);
   10160             : 
   10161             :     // Fixup type information for a common case where a property call
   10162             :     // is converted to the following bytecodes
   10163             :     //
   10164             :     //      a.foo()
   10165             :     // ================= Compiles to ================
   10166             :     //      LOAD "a"
   10167             :     //      DUP
   10168             :     //      CALLPROP "foo"
   10169             :     //      SWAP
   10170             :     //      CALL 0
   10171             :     //
   10172             :     // If we have better type information to unbox the first copy going into
   10173             :     // the CALLPROP operation, we can replace the duplicated copy on the
   10174             :     // stack as well.
   10175           0 :     if (*pc == JSOP_CALLPROP || *pc == JSOP_CALLELEM) {
   10176           0 :         uint32_t idx = current->stackDepth() - 1;
   10177           1 :         MOZ_ASSERT(current->getSlot(idx) == def);
   10178           1 :         current->setSlot(idx, unbox);
   10179             :     }
   10180             : 
   10181             :     return unbox;
   10182             : }
   10183             : 
   10184             : AbortReasonOr<Ok>
   10185           0 : IonBuilder::jsop_getprop(PropertyName* name)
   10186             : {
   10187        1159 :     bool emitted = false;
   10188           0 :     startTrackingOptimizations();
   10189             : 
   10190        1159 :     MDefinition* obj = current->pop();
   10191           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
   10192             : 
   10193           0 :     trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet());
   10194             : 
   10195        1159 :     if (!info().isAnalysis()) {
   10196             :         // The calls below can abort compilation, so we only try this if we're
   10197             :         // not analyzing.
   10198             :         // Try to optimize arguments.length.
   10199           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_ArgumentsLength);
   10200           0 :         MOZ_TRY(getPropTryArgumentsLength(&emitted, obj));
   10201         815 :         if (emitted)
   10202           4 :             return Ok();
   10203             : 
   10204             :         // Try to optimize arguments.callee.
   10205           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_ArgumentsCallee);
   10206           0 :         MOZ_TRY(getPropTryArgumentsCallee(&emitted, obj, name));
   10207         811 :         if (emitted)
   10208           0 :             return Ok();
   10209             :     }
   10210             : 
   10211           0 :     obj = maybeUnboxForPropertyAccess(obj);
   10212        1155 :     if (obj->type() == MIRType::Object)
   10213           0 :         obj = convertUnboxedObjects(obj);
   10214             : 
   10215        1155 :     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
   10216        1155 :                                                        obj, name, types);
   10217             : 
   10218             :     // Try to optimize to a specific constant.
   10219           0 :     trackOptimizationAttempt(TrackedStrategy::GetProp_InferredConstant);
   10220           0 :     if (barrier == BarrierKind::NoBarrier) {
   10221           0 :         MOZ_TRY(getPropTryInferredConstant(&emitted, obj, name, types));
   10222         570 :         if (emitted)
   10223           8 :             return Ok();
   10224             :     } else {
   10225             :         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
   10226             :     }
   10227             : 
   10228             :     // Always use a call if we are performing analysis and
   10229             :     // not actually emitting code, to simplify later analysis. Also skip deeper
   10230             :     // analysis if there are no known types for this operation, as it will
   10231             :     // always invalidate when executing.
   10232        1950 :     if (info().isAnalysis() || types->empty() || shouldAbortOnPreliminaryGroups(obj)) {
   10233         834 :         if (types->empty()) {
   10234             :             // Since no further optimizations will be tried, use the IC
   10235             :             // strategy, which would have been the last one to be tried, as a
   10236             :             // sentinel value for why everything failed.
   10237         312 :             trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
   10238             :             trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
   10239             :         }
   10240             : 
   10241         834 :         MCallGetProperty* call = MCallGetProperty::New(alloc(), obj, name);
   10242         417 :         current->add(call);
   10243             : 
   10244             :         // During the definite properties analysis we can still try to bake in
   10245             :         // constants read off the prototype chain, to allow inlining later on.
   10246             :         // In this case we still need the getprop call so that the later
   10247             :         // analysis knows when the |this| value has been read from.
   10248           0 :         if (info().isAnalysis()) {
   10249           0 :             MOZ_TRY(getPropTryConstant(&emitted, obj, NameToId(name), types));
   10250         344 :             if (emitted)
   10251           6 :                 return Ok();
   10252             :         }
   10253             : 
   10254           0 :         current->push(call);
   10255        1233 :         MOZ_TRY(resumeAfter(call));
   10256         411 :         return pushTypeBarrier(call, types, BarrierKind::TypeSet);
   10257             :     }
   10258             : 
   10259             :     // Try to optimize accesses on outer window proxies, for example window.foo.
   10260             :     // This needs to come before the various strategies getPropTryInnerize tries
   10261             :     // internally, since some of those strategies will "succeed" in silly ways
   10262             :     // even for an outer object.
   10263           0 :     trackOptimizationAttempt(TrackedStrategy::GetProp_Innerize);
   10264           0 :     MOZ_TRY(getPropTryInnerize(&emitted, obj, name, types));
   10265         730 :     if (emitted)
   10266           0 :         return Ok();
   10267             : 
   10268           0 :     if (!forceInlineCaches()) {
   10269             :         // Try to hardcode known constants.
   10270           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
   10271           0 :         MOZ_TRY(getPropTryConstant(&emitted, obj, NameToId(name), types));
   10272         730 :         if (emitted)
   10273          99 :             return Ok();
   10274             : 
   10275             :         // Try to hardcode known not-defined
   10276           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_NotDefined);
   10277           0 :         MOZ_TRY(getPropTryNotDefined(&emitted, obj, NameToId(name), types));
   10278         631 :         if (emitted)
   10279          19 :             return Ok();
   10280             : 
   10281             :         // Try to emit loads from definite slots.
   10282           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_DefiniteSlot);
   10283           0 :         MOZ_TRY(getPropTryDefiniteSlot(&emitted, obj, name, barrier, types));
   10284         612 :         if (emitted)
   10285         138 :             return Ok();
   10286             : 
   10287             :         // Try to emit loads from unboxed objects.
   10288           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_Unboxed);
   10289           0 :         MOZ_TRY(getPropTryUnboxed(&emitted, obj, name, barrier, types));
   10290         474 :         if (emitted)
   10291           8 :             return Ok();
   10292             : 
   10293             :         // Try to inline a common property getter, or make a call.
   10294           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
   10295           0 :         MOZ_TRY(getPropTryCommonGetter(&emitted, obj, name, types));
   10296         466 :         if (emitted)
   10297           2 :             return Ok();
   10298             : 
   10299             :         // Try to emit a monomorphic/polymorphic access based on baseline caches.
   10300           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_InlineAccess);
   10301           0 :         MOZ_TRY(getPropTryInlineAccess(&emitted, obj, name, barrier, types));
   10302         464 :         if (emitted)
   10303          56 :             return Ok();
   10304             : 
   10305             :         // Try to emit a property access on the prototype based on baseline
   10306             :         // caches.
   10307           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_InlineProtoAccess);
   10308           0 :         MOZ_TRY(getPropTryInlineProtoAccess(&emitted, obj, name, types));
   10309         408 :         if (emitted)
   10310          35 :             return Ok();
   10311             : 
   10312             :         // Try to emit loads from a module namespace.
   10313           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_ModuleNamespace);
   10314           0 :         MOZ_TRY(getPropTryModuleNamespace(&emitted, obj, name, barrier, types));
   10315         373 :         if (emitted)
   10316           0 :             return Ok();
   10317             : 
   10318             :         // Try to emit loads from known binary data blocks
   10319           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_TypedObject);
   10320           0 :         MOZ_TRY(getPropTryTypedObject(&emitted, obj, name));
   10321         373 :         if (emitted)
   10322           0 :             return Ok();
   10323             :     }
   10324             : 
   10325             :     // Emit a polymorphic cache.
   10326         373 :     trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
   10327         373 :     return getPropAddCache(obj, name, barrier, types);
   10328             : }
   10329             : 
   10330             : AbortReasonOr<Ok>
   10331         396 : IonBuilder::improveThisTypesForCall()
   10332             : {
   10333             :     // After a CALLPROP (or CALLELEM) for obj.prop(), the this-value and callee
   10334             :     // for the call are on top of the stack:
   10335             :     //
   10336             :     // ... [this: obj], [callee: obj.prop]
   10337             :     //
   10338             :     // If obj is null or undefined, obj.prop would have thrown an exception so
   10339             :     // at this point we can remove null and undefined from obj's TypeSet, to
   10340             :     // improve type information for the call that will follow.
   10341             : 
   10342         396 :     MOZ_ASSERT(*pc == JSOP_CALLPROP || *pc == JSOP_CALLELEM);
   10343             : 
   10344             :     // Ensure |this| has types {object, null/undefined}.
   10345           0 :     MDefinition* thisDef = current->peek(-2);
   10346           0 :     if (thisDef->type() != MIRType::Value ||
   10347           0 :         !thisDef->mightBeType(MIRType::Object) ||
   10348         396 :         !thisDef->resultTypeSet() ||
   10349           0 :         !thisDef->resultTypeSet()->objectOrSentinel())
   10350             :     {
   10351         396 :         return Ok();
   10352             :     }
   10353             : 
   10354             :     // Remove null/undefined from the TypeSet.
   10355           0 :     TemporaryTypeSet* types = thisDef->resultTypeSet()->cloneObjectsOnly(alloc_->lifoAlloc());
   10356           0 :     if (!types)
   10357           0 :         return abort(AbortReason::Alloc);
   10358             : 
   10359           0 :     MFilterTypeSet* filter = MFilterTypeSet::New(alloc(), thisDef, types);
   10360           0 :     current->add(filter);
   10361           0 :     current->rewriteAtDepth(-2, filter);
   10362             : 
   10363             :     // FilterTypeSetPolicy::adjustInputs will insert an infallible Unbox(Object)
   10364             :     // for the input. Don't hoist this unbox above the getprop or getelem
   10365             :     // operation.
   10366           0 :     filter->setDependency(current->peek(-1)->toInstruction());
   10367           0 :     return Ok();
   10368             : }
   10369             : 
   10370             : AbortReasonOr<Ok>
   10371           0 : IonBuilder::checkIsDefinitelyOptimizedArguments(MDefinition* obj, bool* isOptimizedArgs)
   10372             : {
   10373           0 :     if (obj->type() != MIRType::MagicOptimizedArguments) {
   10374         222 :         if (script()->argumentsHasVarBinding() &&
   10375           0 :             obj->mightBeType(MIRType::MagicOptimizedArguments))
   10376             :         {
   10377           0 :             return abort(AbortReason::Disable, "Type is not definitely lazy arguments.");
   10378             :         }
   10379             : 
   10380         110 :         *isOptimizedArgs = false;
   10381         110 :         return Ok();
   10382             :     }
   10383             : 
   10384           4 :     *isOptimizedArgs = true;
   10385           4 :     return Ok();
   10386             : }
   10387             : 
   10388             : AbortReasonOr<Ok>
   10389         570 : IonBuilder::getPropTryInferredConstant(bool* emitted, MDefinition* obj, PropertyName* name,
   10390             :                                        TemporaryTypeSet* types)
   10391             : {
   10392         570 :     MOZ_ASSERT(*emitted == false);
   10393             : 
   10394             :     // Need a result typeset to optimize.
   10395           0 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
   10396           0 :     if (!objTypes) {
   10397           0 :         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
   10398           0 :         return Ok();
   10399             :     }
   10400             : 
   10401           0 :     JSObject* singleton = objTypes->maybeSingleton();
   10402           0 :     if (!singleton) {
   10403         544 :         trackOptimizationOutcome(TrackedOutcome::NotSingleton);
   10404         544 :         return Ok();
   10405             :     }
   10406             : 
   10407           1 :     TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(singleton);
   10408           1 :     if (key->unknownProperties()) {
   10409           0 :         trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
   10410           0 :         return Ok();
   10411             :     }
   10412             : 
   10413           0 :     HeapTypeSetKey property = key->property(NameToId(name));
   10414             : 
   10415           0 :     Value constantValue = UndefinedValue();
   10416           0 :     if (property.constant(constraints(), &constantValue)) {
   10417           0 :         spew("Optimized constant property");
   10418           0 :         obj->setImplicitlyUsedUnchecked();
   10419           0 :         pushConstant(constantValue);
   10420           0 :         types->addType(TypeSet::GetValueType(constantValue), alloc_->lifoAlloc());
   10421           8 :         trackOptimizationSuccess();
   10422           8 :         *emitted = true;
   10423             :     }
   10424             : 
   10425          26 :     return Ok();
   10426             : }
   10427             : 
   10428             : AbortReasonOr<Ok>
   10429           0 : IonBuilder::getPropTryArgumentsLength(bool* emitted, MDefinition* obj)
   10430             : {
   10431           0 :     MOZ_ASSERT(*emitted == false);
   10432             : 
   10433         815 :     if (JSOp(*pc) != JSOP_LENGTH)
   10434           0 :         return Ok();
   10435             : 
   10436           0 :     bool isOptimizedArgs = false;
   10437           0 :     MOZ_TRY(checkIsDefinitelyOptimizedArguments(obj, &isOptimizedArgs));
   10438         114 :     if (!isOptimizedArgs)
   10439           0 :         return Ok();
   10440             : 
   10441           4 :     trackOptimizationSuccess();
   10442           0 :     *emitted = true;
   10443             : 
   10444           4 :     obj->setImplicitlyUsedUnchecked();
   10445             : 
   10446             :     // We don't know anything from the callee
   10447           0 :     if (inliningDepth_ == 0) {
   10448           0 :         MInstruction* ins = MArgumentsLength::New(alloc());
   10449           0 :         current->add(ins);
   10450           4 :         current->push(ins);
   10451           4 :         return Ok();
   10452             :     }
   10453             : 
   10454             :     // We are inlining and know the number of arguments the callee pushed
   10455           0 :     pushConstant(Int32Value(inlineCallInfo_->argv().length()));
   10456           0 :     return Ok();
   10457             : }
   10458             : 
   10459             : AbortReasonOr<Ok>
   10460           0 : IonBuilder::getPropTryArgumentsCallee(bool* emitted, MDefinition* obj, PropertyName* name)
   10461             : {
   10462           0 :     MOZ_ASSERT(*emitted == false);
   10463             : 
   10464        2433 :     if (name != names().callee)
   10465           0 :         return Ok();
   10466             : 
   10467           0 :     bool isOptimizedArgs = false;
   10468           0 :     MOZ_TRY(checkIsDefinitelyOptimizedArguments(obj, &isOptimizedArgs));
   10469           0 :     if (!isOptimizedArgs)
   10470           0 :         return Ok();
   10471             : 
   10472           0 :     MOZ_ASSERT(script()->hasMappedArgsObj());
   10473             : 
   10474           0 :     obj->setImplicitlyUsedUnchecked();
   10475           0 :     current->push(getCallee());
   10476             : 
   10477           0 :     trackOptimizationSuccess();
   10478           0 :     *emitted = true;
   10479           0 :     return Ok();
   10480             : }
   10481             : 
   10482             : AbortReasonOr<Ok>
   10483           0 : IonBuilder::getPropTryConstant(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types)
   10484             : {
   10485           0 :     MOZ_ASSERT(*emitted == false);
   10486             : 
   10487        1117 :     if (!types->mightBeMIRType(MIRType::Object)) {
   10488             :         // If we have not observed an object result here, don't look for a
   10489             :         // singleton constant.
   10490         609 :         trackOptimizationOutcome(TrackedOutcome::NotObject);
   10491         609 :         return Ok();
   10492             :     }
   10493             : 
   10494           0 :     JSObject* singleton = testSingletonPropertyTypes(obj, id);
   10495           0 :     if (!singleton) {
   10496         379 :         trackOptimizationOutcome(TrackedOutcome::NotSingleton);
   10497         379 :         return Ok();
   10498             :     }
   10499             : 
   10500             :     // Property access is a known constant -- safe to emit.
   10501           0 :     obj->setImplicitlyUsedUnchecked();
   10502             : 
   10503           0 :     pushConstant(ObjectValue(*singleton));
   10504             : 
   10505           0 :     trackOptimizationSuccess();
   10506         129 :     *emitted = true;
   10507         129 :     return Ok();
   10508             : }
   10509             : 
   10510             : AbortReasonOr<Ok>
   10511           0 : IonBuilder::getPropTryNotDefined(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types)
   10512             : {
   10513           0 :     MOZ_ASSERT(*emitted == false);
   10514             : 
   10515           0 :     if (!types->mightBeMIRType(MIRType::Undefined)) {
   10516             :         // Only optimize if we expect this property access to return undefined.
   10517         617 :         trackOptimizationOutcome(TrackedOutcome::NotUndefined);
   10518         617 :         return Ok();
   10519             :     }
   10520             : 
   10521             :     bool res;
   10522           0 :     MOZ_TRY_VAR(res, testNotDefinedProperty(obj, id));
   10523           0 :     if (!res) {
   10524          14 :         trackOptimizationOutcome(TrackedOutcome::GenericFailure);
   10525          14 :         return Ok();
   10526             :     }
   10527             : 
   10528          19 :     obj->setImplicitlyUsedUnchecked();
   10529           0 :     pushConstant(UndefinedValue());
   10530             : 
   10531           0 :     trackOptimizationSuccess();
   10532          19 :     *emitted = true;
   10533          19 :     return Ok();
   10534             : }
   10535             : 
   10536             : AbortReasonOr<Ok>
   10537         373 : IonBuilder::getPropTryTypedObject(bool* emitted,
   10538             :                                   MDefinition* obj,
   10539             :                                   PropertyName* name)
   10540             : {
   10541         373 :     TypedObjectPrediction fieldPrediction;
   10542             :     size_t fieldOffset;
   10543             :     size_t fieldIndex;
   10544         373 :     if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
   10545           0 :         return Ok();
   10546             : 
   10547           0 :     switch (fieldPrediction.kind()) {
   10548             :       case type::Simd:
   10549             :         // FIXME (bug 894104): load into a MIRType::float32x4 etc
   10550           0 :         return Ok();
   10551             : 
   10552             :       case type::Struct:
   10553             :       case type::Array:
   10554             :         return getPropTryComplexPropOfTypedObject(emitted,
   10555             :                                                   obj,
   10556             :                                                   fieldOffset,
   10557             :                                                   fieldPrediction,
   10558           0 :                                                   fieldIndex);
   10559             : 
   10560             :       case type::Reference:
   10561             :         return getPropTryReferencePropOfTypedObject(emitted,
   10562             :                                                     obj,
   10563             :                                                     fieldOffset,
   10564             :                                                     fieldPrediction,
   10565           0 :                                                     name);
   10566             : 
   10567             :       case type::Scalar:
   10568             :         return getPropTryScalarPropOfTypedObject(emitted,
   10569             :                                                  obj,
   10570             :                                                  fieldOffset,
   10571           0 :                                                  fieldPrediction);
   10572             :     }
   10573             : 
   10574           0 :     MOZ_CRASH("Bad kind");
   10575             : }
   10576             : 
   10577             : AbortReasonOr<Ok>
   10578           0 : IonBuilder::getPropTryScalarPropOfTypedObject(bool* emitted, MDefinition* typedObj,
   10579             :                                               int32_t fieldOffset,
   10580             :                                               TypedObjectPrediction fieldPrediction)
   10581             : {
   10582             :     // Must always be loading the same scalar type
   10583           0 :     Scalar::Type fieldType = fieldPrediction.scalarType();
   10584             : 
   10585             :     // Don't optimize if the typed object's underlying buffer may be detached.
   10586           0 :     TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
   10587           0 :     if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
   10588           0 :         return Ok();
   10589             : 
   10590           0 :     trackOptimizationSuccess();
   10591           0 :     *emitted = true;
   10592             : 
   10593           0 :     LinearSum byteOffset(alloc());
   10594           0 :     if (!byteOffset.add(fieldOffset))
   10595           0 :         return abort(AbortReason::Disable, "Overflow of field offsets.");
   10596             : 
   10597           0 :     return pushScalarLoadFromTypedObject(typedObj, byteOffset, fieldType);
   10598             : }
   10599             : 
   10600             : AbortReasonOr<Ok>
   10601           0 : IonBuilder::getPropTryReferencePropOfTypedObject(bool* emitted, MDefinition* typedObj,
   10602             :                                                  int32_t fieldOffset,
   10603             :                                                  TypedObjectPrediction fieldPrediction,
   10604             :                                                  PropertyName* name)
   10605             : {
   10606           0 :     ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
   10607             : 
   10608           0 :     TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
   10609           0 :     if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
   10610           0 :         return Ok();
   10611             : 
   10612           0 :     trackOptimizationSuccess();
   10613           0 :     *emitted = true;
   10614             : 
   10615           0 :     LinearSum byteOffset(alloc());
   10616           0 :     if (!byteOffset.add(fieldOffset))
   10617           0 :         return abort(AbortReason::Disable, "Overflow of field offsets.");
   10618             : 
   10619           0 :     return pushReferenceLoadFromTypedObject(typedObj, byteOffset, fieldType, name);
   10620             : }
   10621             : 
   10622             : AbortReasonOr<Ok>
   10623           0 : IonBuilder::getPropTryComplexPropOfTypedObject(bool* emitted,
   10624             :                                                MDefinition* typedObj,
   10625             :                                                int32_t fieldOffset,
   10626             :                                                TypedObjectPrediction fieldPrediction,
   10627             :                                                size_t fieldIndex)
   10628             : {
   10629             :     // Don't optimize if the typed object's underlying buffer may be detached.
   10630           0 :     TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
   10631           0 :     if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
   10632           0 :         return Ok();
   10633             : 
   10634             :     // OK, perform the optimization
   10635             : 
   10636             :     // Identify the type object for the field.
   10637           0 :     MDefinition* type = loadTypedObjectType(typedObj);
   10638           0 :     MDefinition* fieldTypeObj = typeObjectForFieldFromStructType(type, fieldIndex);
   10639             : 
   10640           0 :     LinearSum byteOffset(alloc());
   10641           0 :     if (!byteOffset.add(fieldOffset))
   10642           0 :         return abort(AbortReason::Disable, "Overflow of field offsets.");
   10643             : 
   10644             :     return pushDerivedTypedObject(emitted, typedObj, byteOffset,
   10645           0 :                                   fieldPrediction, fieldTypeObj);
   10646             : }
   10647             : 
   10648             : MDefinition*
   10649        2068 : IonBuilder::convertUnboxedObjects(MDefinition* obj)
   10650             : {
   10651             :     // If obj might be in any particular unboxed group which should be
   10652             :     // converted to a native representation, perform that conversion. This does
   10653             :     // not guarantee the object will not have such a group afterwards, if the
   10654             :     // object's possible groups are not precisely known.
   10655        2068 :     TemporaryTypeSet* types = obj->resultTypeSet();
   10656        4055 :     if (!types || types->unknownObject() || !types->objectOrSentinel())
   10657             :         return obj;
   10658             : 
   10659           0 :     BaselineInspector::ObjectGroupVector list(alloc());
   10660           0 :     for (size_t i = 0; i < types->getObjectCount(); i++) {
   10661           0 :         TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i);
   10662        2908 :         if (!key || !key->isGroup())
   10663           0 :             continue;
   10664             : 
   10665           0 :         AutoSweepObjectGroup sweep(key->group());
   10666           0 :         if (UnboxedLayout* layout = key->group()->maybeUnboxedLayout(sweep)) {
   10667           1 :             AutoEnterOOMUnsafeRegion oomUnsafe;
   10668         130 :             if (layout->nativeGroup() && !list.append(key->group()))
   10669           0 :                 oomUnsafe.crash("IonBuilder::convertUnboxedObjects");
   10670             :         }
   10671             :     }
   10672             : 
   10673        1336 :     return convertUnboxedObjects(obj, list);
   10674             : }
   10675             : 
   10676             : MDefinition*
   10677        1430 : IonBuilder::convertUnboxedObjects(MDefinition* obj,
   10678             :                                   const BaselineInspector::ObjectGroupVector& list)
   10679             : {
   10680           0 :     for (size_t i = 0; i < list.length(); i++) {
   10681           0 :         ObjectGroup* group = list[i];
   10682          12 :         if (TemporaryTypeSet* types = obj->resultTypeSet()) {
   10683          12 :             if (!types->hasType(TypeSet::ObjectType(group)))
   10684             :                 continue;
   10685             :         }
   10686           6 :         obj = MConvertUnboxedObjectToNative::New(alloc(), obj, group);
   10687           0 :         current->add(obj->toInstruction());
   10688             :     }
   10689        1430 :     return obj;
   10690             : }
   10691             : 
   10692             : AbortReasonOr<Ok>
   10693         612 : IonBuilder::getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name,
   10694             :                                    BarrierKind barrier, TemporaryTypeSet* types)
   10695             : {
   10696         612 :     MOZ_ASSERT(*emitted == false);
   10697             : 
   10698             :     uint32_t nfixed;
   10699           0 :     uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), NameToId(name), &nfixed);
   10700         612 :     if (slot == UINT32_MAX)
   10701           0 :         return Ok();
   10702             : 
   10703           0 :     if (obj->type() != MIRType::Object) {
   10704           0 :         MGuardObject* guard = MGuardObject::New(alloc(), obj);
   10705           0 :         current->add(guard);
   10706           0 :         obj = guard;
   10707             :     }
   10708             : 
   10709             :     MInstruction* load;
   10710         138 :     if (slot < nfixed) {
   10711           0 :         load = MLoadFixedSlot::New(alloc(), obj, slot);
   10712             :     } else {
   10713           0 :         MInstruction* slots = MSlots::New(alloc(), obj);
   10714           0 :         current->add(slots);
   10715             : 
   10716           0 :         load = MLoadSlot::New(alloc(), slots, slot - nfixed);
   10717             :     }
   10718             : 
   10719         138 :     if (barrier == BarrierKind::NoBarrier)
   10720           0 :         load->setResultType(types->getKnownMIRType());
   10721             : 
   10722         138 :     current->add(load);
   10723           0 :     current->push(load);
   10724             : 
   10725           0 :     MOZ_TRY(pushTypeBarrier(load, types, barrier));
   10726             : 
   10727           0 :     trackOptimizationSuccess();
   10728         138 :     *emitted = true;
   10729         138 :     return Ok();
   10730             : }
   10731             : 
   10732             : AbortReasonOr<Ok>
   10733         373 : IonBuilder::getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name,
   10734             :                                       BarrierKind barrier, TemporaryTypeSet* types)
   10735             : {
   10736           0 :     MOZ_ASSERT(*emitted == false);
   10737             : 
   10738           0 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
   10739           0 :     if (!objTypes) {
   10740           0 :         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
   10741           0 :         return Ok();
   10742             :     }
   10743             : 
   10744           0 :     JSObject* singleton = objTypes->maybeSingleton();
   10745           0 :     if (!singleton) {
   10746         373 :         trackOptimizationOutcome(TrackedOutcome::NotSingleton);
   10747         373 :         return Ok();
   10748             :     }
   10749             : 
   10750           0 :     if (!singleton->is<ModuleNamespaceObject>()) {
   10751           0 :         trackOptimizationOutcome(TrackedOutcome::NotModuleNamespace);
   10752           0 :         return Ok();
   10753             :     }
   10754             : 
   10755           0 :     ModuleNamespaceObject* ns = &singleton->as<ModuleNamespaceObject>();
   10756             :     ModuleEnvironmentObject* env;
   10757             :     Shape* shape;
   10758           0 :     if (!ns->bindings().lookup(NameToId(name), &env, &shape)) {
   10759           0 :         trackOptimizationOutcome(TrackedOutcome::UnknownProperty);
   10760           0 :         return Ok();
   10761             :     }
   10762             : 
   10763           0 :     obj->setImplicitlyUsedUnchecked();
   10764           0 :     MConstant* envConst = constant(ObjectValue(*env));
   10765           0 :     uint32_t slot = shape->slot();
   10766           0 :     uint32_t nfixed = env->numFixedSlots();
   10767             : 
   10768           0 :     MIRType rvalType = types->getKnownMIRType();
   10769           0 :     if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
   10770           0 :         rvalType = MIRType::Value;
   10771             : 
   10772           0 :     MOZ_TRY(loadSlot(envConst, slot, nfixed, rvalType, barrier, types));
   10773             : 
   10774           0 :     trackOptimizationSuccess();
   10775           0 :     *emitted = true;
   10776           0 :     return Ok();
   10777             : }
   10778             : 
   10779             : MInstruction*
   10780           8 : IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
   10781             :                                 BarrierKind barrier, TemporaryTypeSet* types)
   10782             : {
   10783             :     // loadUnboxedValue is designed to load any value as if it were contained in
   10784             :     // an array. Thus a property offset is converted to an index, when the
   10785             :     // object is reinterpreted as an array of properties of the same size.
   10786           0 :     size_t index = offset / UnboxedTypeSize(unboxedType);
   10787          16 :     MInstruction* indexConstant = MConstant::New(alloc(), Int32Value(index));
   10788           8 :     current->add(indexConstant);
   10789             : 
   10790             :     return loadUnboxedValue(obj, UnboxedPlainObject::offsetOfData(),
   10791           8 :                             indexConstant, unboxedType, barrier, types);
   10792             : }
   10793             : 
   10794             : MInstruction*
   10795           8 : IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset,
   10796             :                              MDefinition* index, JSValueType unboxedType,
   10797             :                              BarrierKind barrier, TemporaryTypeSet* types)
   10798             : {
   10799             :     MInstruction* load;
   10800           0 :     switch (unboxedType) {
   10801             :       case JSVAL_TYPE_BOOLEAN:
   10802           0 :         load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Uint8,
   10803           0 :                                        DoesNotRequireMemoryBarrier, elementsOffset);
   10804           0 :         load->setResultType(MIRType::Boolean);
   10805             :         break;
   10806             : 
   10807             :       case JSVAL_TYPE_INT32:
   10808           0 :         load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Int32,
   10809           0 :                                        DoesNotRequireMemoryBarrier, elementsOffset);
   10810           0 :         load->setResultType(MIRType::Int32);
   10811             :         break;
   10812             : 
   10813             :       case JSVAL_TYPE_DOUBLE:
   10814           0 :         load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Float64,
   10815             :                                        DoesNotRequireMemoryBarrier, elementsOffset,
   10816           0 :                                        /* canonicalizeDoubles = */ false);
   10817           0 :         load->setResultType(MIRType::Double);
   10818             :         break;
   10819             : 
   10820             :       case JSVAL_TYPE_STRING:
   10821          16 :         load = MLoadUnboxedString::New(alloc(), elements, index, elementsOffset);
   10822           8 :         break;
   10823             : 
   10824             :       case JSVAL_TYPE_OBJECT: {
   10825             :         MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
   10826           0 :         if (types->hasType(TypeSet::NullType()))
   10827             :             nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
   10828           0 :         else if (barrier != BarrierKind::NoBarrier)
   10829             :             nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull;
   10830             :         else
   10831           0 :             nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible;
   10832           0 :         load = MLoadUnboxedObjectOrNull::New(alloc(), elements, index, nullBehavior,
   10833           0 :                                              elementsOffset);
   10834             :         break;
   10835             :       }
   10836             : 
   10837             :       default:
   10838           0 :         MOZ_CRASH();
   10839             :     }
   10840             : 
   10841           8 :     current->add(load);
   10842           8 :     return load;
   10843             : }
   10844             : 
   10845             : AbortReasonOr<Ok>
   10846         474 : IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name,
   10847             :                               BarrierKind barrier, TemporaryTypeSet* types)
   10848             : {
   10849         474 :     MOZ_ASSERT(*emitted == false);
   10850             : 
   10851             :     JSValueType unboxedType;
   10852           0 :     uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), NameToId(name), &unboxedType);
   10853         474 :     if (offset == UINT32_MAX)
   10854           0 :         return Ok();
   10855             : 
   10856           0 :     if (obj->type() != MIRType::Object) {
   10857           0 :         MGuardObject* guard = MGuardObject::New(alloc(), obj);
   10858           0 :         current->add(guard);
   10859           0 :         obj = guard;
   10860             :     }
   10861             : 
   10862           8 :     MInstruction* load = loadUnboxedProperty(obj, offset, unboxedType, barrier, types);
   10863           0 :     current->push(load);
   10864             : 
   10865           0 :     MOZ_TRY(pushTypeBarrier(load, types, barrier));
   10866             : 
   10867           0 :     trackOptimizationSuccess();
   10868           8 :     *emitted = true;
   10869           8 :     return Ok();
   10870             : }
   10871             : 
   10872             : MDefinition*
   10873           2 : IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
   10874             :                 const BaselineInspector::ReceiverVector& receivers,
   10875             :                 const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
   10876             :                 bool isOwnProperty)
   10877             : {
   10878           2 :     MOZ_ASSERT(isOwnProperty == !holder);
   10879           0 :     MOZ_ASSERT(holderShape);
   10880             : 
   10881           0 :     obj = convertUnboxedObjects(obj, convertUnboxedGroups);
   10882             : 
   10883           0 :     if (isOwnProperty) {
   10884           2 :         MOZ_ASSERT(receivers.empty());
   10885           2 :         return addShapeGuard(obj, holderShape, Bailout_ShapeGuard);
   10886             :     }
   10887             : 
   10888           0 :     MDefinition* holderDef = constant(ObjectValue(*holder));
   10889           0 :     addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard);
   10890             : 
   10891           0 :     return addGuardReceiverPolymorphic(obj, receivers);
   10892             : }
   10893             : 
   10894             : AbortReasonOr<Ok>
   10895         471 : IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name,
   10896             :                                    TemporaryTypeSet* types, bool innerized)
   10897             : {
   10898           0 :     MOZ_ASSERT(*emitted == false);
   10899             : 
   10900           0 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
   10901             : 
   10902           0 :     JSFunction* commonGetter = nullptr;
   10903         471 :     MDefinition* guard = nullptr;
   10904         471 :     MDefinition* globalGuard = nullptr;
   10905             : 
   10906             :     {
   10907           0 :         Shape* lastProperty = nullptr;
   10908           0 :         Shape* globalShape = nullptr;
   10909           0 :         JSObject* foundProto = nullptr;
   10910           0 :         bool isOwnProperty = false;
   10911           0 :         BaselineInspector::ReceiverVector receivers(alloc());
   10912         944 :         BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
   10913         471 :         if (inspector->commonGetPropFunction(pc, innerized, &foundProto, &lastProperty, &commonGetter,
   10914             :                                               &globalShape, &isOwnProperty,
   10915             :                                               receivers, convertUnboxedGroups))
   10916             :         {
   10917           2 :             bool canUseTIForGetter = false;
   10918           2 :             if (!isOwnProperty) {
   10919             :                 // If it's not an own property, try to use TI to avoid shape guards.
   10920             :                 // For own properties we use the path below.
   10921           0 :                 canUseTIForGetter = testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
   10922             :                                                            commonGetter, &guard,
   10923           0 :                                                            globalShape, &globalGuard);
   10924             :             }
   10925           2 :             if (!canUseTIForGetter) {
   10926             :                 // If it's an own property or type information is bad, we can still
   10927             :                 // optimize the getter if we shape guard.
   10928           2 :                 obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
   10929             :                                                     receivers, convertUnboxedGroups,
   10930             :                                                     isOwnProperty);
   10931           2 :                 if (!obj)
   10932           0 :                     return abort(AbortReason::Alloc);
   10933             :             }
   10934         469 :         } else if (inspector->megamorphicGetterSetterFunction(pc, /* isGetter = */ true,
   10935             :                                                               &commonGetter))
   10936             :         {
   10937             :             // Try to use TI to guard on this getter.
   10938           0 :             if (!testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
   10939             :                                         commonGetter, &guard))
   10940             :             {
   10941           0 :                 return Ok();
   10942             :             }
   10943             :         } else {
   10944             :             // The Baseline IC didn't have any information we can use.
   10945         469 :             return Ok();
   10946             :         }
   10947             :     }
   10948             : 
   10949           0 :     DOMObjectKind objKind = DOMObjectKind::Unknown;
   10950           0 :     bool isDOM = objTypes && objTypes->isDOMClass(constraints(), &objKind);
   10951           2 :     if (isDOM)
   10952           0 :         MOZ_TRY_VAR(isDOM, testShouldDOMCall(objTypes, commonGetter, JSJitInfo::Getter));
   10953             : 
   10954           2 :     if (isDOM) {
   10955           0 :         const JSJitInfo* jitinfo = commonGetter->jitInfo();
   10956             :         // We don't support MGetDOMProperty/MGetDOMMember on things that might
   10957             :         // be proxies when the value might be in a slot, because the
   10958             :         // CodeGenerator code for LGetDOMProperty/LGetDOMMember doesn't handle
   10959             :         // that case correctly.
   10960           0 :         if (objKind == DOMObjectKind::Native ||
   10961           0 :             (!jitinfo->isAlwaysInSlot && !jitinfo->isLazilyCachedInSlot))
   10962             :         {
   10963             :             MInstruction* get;
   10964           0 :             if (jitinfo->isAlwaysInSlot) {
   10965             :                 // If our object is a singleton and we know the property is
   10966             :                 // constant (which is true if and only if the get doesn't alias
   10967             :                 // anything), we can just read the slot here and use that
   10968             :                 // constant.
   10969           0 :                 JSObject* singleton = objTypes->maybeSingleton();
   10970           0 :                 if (singleton && jitinfo->aliasSet() == JSJitInfo::AliasNone) {
   10971           0 :                     size_t slot = jitinfo->slotIndex;
   10972           0 :                     *emitted = true;
   10973           0 :                     pushConstant(GetReservedSlot(singleton, slot));
   10974           0 :                     return Ok();
   10975             :                 }
   10976             : 
   10977             :                 // We can't use MLoadFixedSlot here because it might not have
   10978             :                 // the right aliasing behavior; we want to alias DOM setters as
   10979             :                 // needed.
   10980           0 :                 get = MGetDOMMember::New(alloc(), jitinfo, obj, guard, globalGuard);
   10981             :             } else {
   10982           0 :                 get = MGetDOMProperty::New(alloc(), jitinfo, objKind, obj, guard, globalGuard);
   10983             :             }
   10984           0 :             if (!get)
   10985           0 :                 return abort(AbortReason::Alloc);
   10986           0 :             current->add(get);
   10987           0 :             current->push(get);
   10988             : 
   10989           0 :             if (get->isEffectful())
   10990           0 :                 MOZ_TRY(resumeAfter(get));
   10991             : 
   10992           0 :             MOZ_TRY(pushDOMTypeBarrier(get, types, commonGetter));
   10993             : 
   10994           0 :             trackOptimizationOutcome(TrackedOutcome::DOM);
   10995           0 :             *emitted = true;
   10996           0 :             return Ok();
   10997             :         }
   10998             :     }
   10999             : 
   11000             :     // Don't call the getter with a primitive value.
   11001           1 :     if (obj->type() != MIRType::Object) {
   11002           0 :         MGuardObject* guardObj = MGuardObject::New(alloc(), obj);
   11003           0 :         current->add(guardObj);
   11004           0 :         obj = guardObj;
   11005             :     }
   11006             : 
   11007             :     // Spoof stack to expected state for call.
   11008             : 
   11009             :     // Make sure there's enough room
   11010           0 :     if (!current->ensureHasSlots(2))
   11011           0 :         return abort(AbortReason::Alloc);
   11012           0 :     current->push(constant(ObjectValue(*commonGetter)));
   11013             : 
   11014           2 :     current->push(obj);
   11015             : 
   11016             :     CallInfo callInfo(alloc(), pc, /* constructing = */ false,
   11017           1 :                       /* ignoresReturnValue = */ BytecodeIsPopped(pc));
   11018           2 :     if (!callInfo.init(current, 0))
   11019           0 :         return abort(AbortReason::Alloc);
   11020             : 
   11021           0 :     if (commonGetter->isNative()) {
   11022             :         InliningStatus status;
   11023           4 :         MOZ_TRY_VAR(status, inlineNativeGetter(callInfo, commonGetter));
   11024           2 :         switch (status) {
   11025             :           case InliningStatus_WarmUpCountTooLow:
   11026             :           case InliningStatus_NotInlined:
   11027             :             break;
   11028             :           case InliningStatus_Inlined:
   11029           0 :             trackOptimizationOutcome(TrackedOutcome::Inlined);
   11030           0 :             *emitted = true;
   11031           0 :             return Ok();
   11032             :         }
   11033             :     }
   11034             : 
   11035             :     // Inline if we can, otherwise, forget it and just generate a call.
   11036           0 :     if (commonGetter->isInterpreted()) {
   11037           0 :         InliningDecision decision = makeInliningDecision(commonGetter, callInfo);
   11038           0 :         switch (decision) {
   11039             :           case InliningDecision_Error:
   11040           0 :             return abort(AbortReason::Error);
   11041             :           case InliningDecision_DontInline:
   11042             :           case InliningDecision_WarmUpCountTooLow:
   11043             :             break;
   11044             :           case InliningDecision_Inline: {
   11045             :             InliningStatus status;
   11046           0 :             MOZ_TRY_VAR(status, inlineScriptedCall(callInfo, commonGetter));
   11047           0 :             if (status == InliningStatus_Inlined) {
   11048           0 :                 *emitted = true;
   11049           0 :                 return Ok();
   11050             :             }
   11051             :             break;
   11052             :           }
   11053             :         }
   11054             :     }
   11055             : 
   11056           6 :     MOZ_TRY(makeCall(commonGetter, callInfo));
   11057             : 
   11058             :     // If the getter could have been inlined, don't track success. The call to
   11059             :     // makeInliningDecision above would have tracked a specific reason why we
   11060             :     // couldn't inline.
   11061           4 :     if (!commonGetter->isInterpreted())
   11062             :         trackOptimizationSuccess();
   11063             : 
   11064           2 :     *emitted = true;
   11065           2 :     return Ok();
   11066             : }
   11067             : 
   11068             : bool
   11069           0 : IonBuilder::canInlinePropertyOpShapes(const BaselineInspector::ReceiverVector& receivers)
   11070             : {
   11071         955 :     if (receivers.empty()) {
   11072             :         trackOptimizationOutcome(TrackedOutcome::NoShapeInfo);
   11073             :         return false;
   11074             :     }
   11075             : 
   11076         320 :     for (size_t i = 0; i < receivers.length(); i++) {
   11077             :         // We inline the property access as long as the shape is not in
   11078             :         // dictionary mode. We cannot be sure that the shape is still a
   11079             :         // lastProperty, and calling Shape::search() on dictionary mode
   11080             :         // shapes that aren't lastProperty is invalid.
   11081         224 :         if (receivers[i].shape && receivers[i].shape->inDictionary()) {
   11082             :             trackOptimizationOutcome(TrackedOutcome::InDictionaryMode);
   11083             :             return false;
   11084             :         }
   11085             :     }
   11086             : 
   11087             :     return true;
   11088             : }
   11089             : 
   11090             : static Shape*
   11091           0 : PropertyShapesHaveSameSlot(const BaselineInspector::ReceiverVector& receivers, jsid id)
   11092             : {
   11093           0 :     Shape* firstShape = nullptr;
   11094          27 :     for (size_t i = 0; i < receivers.length(); i++) {
   11095          27 :         if (receivers[i].group)
   11096             :             return nullptr;
   11097             : 
   11098          46 :         Shape* shape = receivers[i].shape->searchLinear(id);
   11099           0 :         MOZ_ASSERT(shape);
   11100             : 
   11101           0 :         if (i == 0) {
   11102             :             firstShape = shape;
   11103          17 :         } else if (shape->slot() != firstShape->slot() ||
   11104          14 :                    shape->numFixedSlots() != firstShape->numFixedSlots())
   11105             :         {
   11106             :             return nullptr;
   11107             :         }
   11108             :     }
   11109             : 
   11110             :     return firstShape;
   11111             : }
   11112             : 
   11113             : AbortReasonOr<Ok>
   11114         464 : IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name,
   11115             :                                    BarrierKind barrier, TemporaryTypeSet* types)
   11116             : {
   11117           0 :     MOZ_ASSERT(*emitted == false);
   11118             : 
   11119           0 :     BaselineInspector::ReceiverVector receivers(alloc());
   11120           1 :     BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
   11121         464 :     if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
   11122           0 :         return abort(AbortReason::Alloc);
   11123             : 
   11124         464 :     if (!canInlinePropertyOpShapes(receivers))
   11125           0 :         return Ok();
   11126             : 
   11127           0 :     obj = convertUnboxedObjects(obj, convertUnboxedGroups);
   11128             : 
   11129           0 :     MIRType rvalType = types->getKnownMIRType();
   11130         105 :     if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
   11131           0 :         rvalType = MIRType::Value;
   11132             : 
   11133          56 :     if (receivers.length() == 1) {
   11134           0 :         if (!receivers[0].group) {
   11135             :             // Monomorphic load from a native object.
   11136           0 :             spew("Inlining monomorphic native GETPROP");
   11137             : 
   11138           0 :             obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
   11139             : 
   11140          86 :             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
   11141           0 :             MOZ_ASSERT(shape);
   11142             : 
   11143           0 :             MOZ_TRY(loadSlot(obj, shape, rvalType, barrier, types));
   11144             : 
   11145           0 :             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11146          43 :             *emitted = true;
   11147          43 :             return Ok();
   11148             :         }
   11149             : 
   11150           0 :         if (receivers[0].shape) {
   11151             :             // Monomorphic load from an unboxed object expando.
   11152           0 :             spew("Inlining monomorphic unboxed expando GETPROP");
   11153             : 
   11154           0 :             obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
   11155           0 :             obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard);
   11156             : 
   11157           0 :             MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
   11158           0 :             current->add(expando);
   11159             : 
   11160           0 :             expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
   11161             : 
   11162           0 :             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
   11163           0 :             MOZ_ASSERT(shape);
   11164             : 
   11165           0 :             MOZ_TRY(loadSlot(expando, shape, rvalType, barrier, types));
   11166             : 
   11167           0 :             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11168           0 :             *emitted = true;
   11169           0 :             return Ok();
   11170             :         }
   11171             : 
   11172             :         // Monomorphic load from an unboxed object.
   11173           0 :         ObjectGroup* group = receivers[0].group;
   11174           0 :         if (obj->resultTypeSet() && !obj->resultTypeSet()->hasType(TypeSet::ObjectType(group)))
   11175           0 :             return Ok();
   11176             : 
   11177           0 :         obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
   11178             : 
   11179           0 :         AutoSweepObjectGroup sweep(group);
   11180           0 :         const UnboxedLayout::Property* property = group->unboxedLayout(sweep).lookup(name);
   11181           0 :         MInstruction* load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types);
   11182           0 :         current->push(load);
   11183             : 
   11184           0 :         MOZ_TRY(pushTypeBarrier(load, types, barrier));
   11185             : 
   11186           0 :         trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11187           0 :         *emitted = true;
   11188           0 :         return Ok();
   11189             :     }
   11190             : 
   11191          13 :     MOZ_ASSERT(receivers.length() > 1);
   11192           0 :     spew("Inlining polymorphic GETPROP");
   11193             : 
   11194           0 :     if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) {
   11195           0 :         obj = addGuardReceiverPolymorphic(obj, receivers);
   11196           0 :         if (!obj)
   11197           0 :             return abort(AbortReason::Alloc);
   11198             : 
   11199           0 :         MOZ_TRY(loadSlot(obj, propShape, rvalType, barrier, types));
   11200             : 
   11201           0 :         trackOptimizationOutcome(TrackedOutcome::Polymorphic);
   11202           0 :         *emitted = true;
   11203           0 :         return Ok();
   11204             :     }
   11205             : 
   11206           0 :     MGetPropertyPolymorphic* load = MGetPropertyPolymorphic::New(alloc(), obj, name);
   11207          13 :     current->add(load);
   11208           0 :     current->push(load);
   11209             : 
   11210           0 :     for (size_t i = 0; i < receivers.length(); i++) {
   11211           0 :         Shape* propShape = nullptr;
   11212           0 :         if (receivers[i].shape) {
   11213          58 :             propShape = receivers[i].shape->searchLinear(NameToId(name));
   11214           0 :             MOZ_ASSERT(propShape);
   11215             :         }
   11216          66 :         if (!load->addReceiver(receivers[i], propShape))
   11217           0 :             return abort(AbortReason::Alloc);
   11218             :     }
   11219             : 
   11220          13 :     if (failedShapeGuard_)
   11221           0 :         load->setNotMovable();
   11222             : 
   11223          26 :     load->setResultType(rvalType);
   11224           0 :     MOZ_TRY(pushTypeBarrier(load, types, barrier));
   11225             : 
   11226           0 :     trackOptimizationOutcome(TrackedOutcome::Polymorphic);
   11227          13 :     *emitted = true;
   11228          13 :     return Ok();
   11229             : }
   11230             : 
   11231             : AbortReasonOr<Ok>
   11232         408 : IonBuilder::getPropTryInlineProtoAccess(bool* emitted, MDefinition* obj, PropertyName* name,
   11233             :                                         TemporaryTypeSet* types)
   11234             : {
   11235           0 :     MOZ_ASSERT(*emitted == false);
   11236             : 
   11237           0 :     BaselineInspector::ReceiverVector receivers(alloc());
   11238           0 :     BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
   11239           1 :     JSObject* holder = nullptr;
   11240         408 :     if (!inspector->maybeInfoForProtoReadSlot(pc, receivers, convertUnboxedGroups, &holder))
   11241           0 :         return abort(AbortReason::Alloc);
   11242             : 
   11243         408 :     if (!canInlinePropertyOpShapes(receivers))
   11244           0 :         return Ok();
   11245             : 
   11246          35 :     MOZ_ASSERT(holder);
   11247          35 :     holder = checkNurseryObject(holder);
   11248             : 
   11249             :     BarrierKind barrier;
   11250           0 :     MOZ_TRY_VAR(barrier, PropertyReadOnPrototypeNeedsTypeBarrier(this, obj, name, types));
   11251             : 
   11252           0 :     MIRType rvalType = types->getKnownMIRType();
   11253          65 :     if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
   11254           5 :         rvalType = MIRType::Value;
   11255             : 
   11256             :     // Guard on the receiver shapes/groups.
   11257           0 :     obj = convertUnboxedObjects(obj, convertUnboxedGroups);
   11258           1 :     obj = addGuardReceiverPolymorphic(obj, receivers);
   11259          35 :     if (!obj)
   11260           0 :         return abort(AbortReason::Alloc);
   11261             : 
   11262             :     // Guard on the holder's shape.
   11263           0 :     MInstruction* holderDef = constant(ObjectValue(*holder));
   11264          70 :     Shape* holderShape = holder->as<NativeObject>().shape();
   11265           0 :     holderDef = addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard);
   11266             : 
   11267          70 :     Shape* propShape = holderShape->searchLinear(NameToId(name));
   11268           0 :     MOZ_ASSERT(propShape);
   11269             : 
   11270           0 :     MOZ_TRY(loadSlot(holderDef, propShape, rvalType, barrier, types));
   11271             : 
   11272           0 :     trackOptimizationSuccess();
   11273          35 :     *emitted = true;
   11274          35 :     return Ok();
   11275             : }
   11276             : 
   11277             : AbortReasonOr<Ok>
   11278         373 : IonBuilder::getPropAddCache(MDefinition* obj, PropertyName* name,
   11279             :                             BarrierKind barrier, TemporaryTypeSet* types)
   11280             : {
   11281             :     // PropertyReadNeedsTypeBarrier only accounts for object types, so for now
   11282             :     // always insert a barrier if the input is not known to be an object.
   11283         373 :     if (obj->type() != MIRType::Object)
   11284          28 :         barrier = BarrierKind::TypeSet;
   11285             : 
   11286             :     // Since getters have no guaranteed return values, we must barrier in order to be
   11287             :     // able to attach stubs for them.
   11288         373 :     if (inspector->hasSeenAccessedGetter(pc))
   11289           7 :         barrier = BarrierKind::TypeSet;
   11290             : 
   11291             :     // Caches can read values from prototypes, so update the barrier to
   11292             :     // reflect such possible values.
   11293           0 :     if (barrier != BarrierKind::TypeSet) {
   11294             :         BarrierKind protoBarrier;
   11295           0 :         MOZ_TRY_VAR(protoBarrier, PropertyReadOnPrototypeNeedsTypeBarrier(this, obj, name, types));
   11296          12 :         if (protoBarrier != BarrierKind::NoBarrier) {
   11297           0 :             MOZ_ASSERT(barrier <= protoBarrier);
   11298             :             barrier = protoBarrier;
   11299             :         }
   11300             :     }
   11301             : 
   11302             :     // Ensure we insert a type barrier for reads from typed objects, as type
   11303             :     // information does not account for the initial undefined/null types.
   11304           0 :     if (barrier != BarrierKind::TypeSet && !types->unknown()) {
   11305          12 :         MOZ_ASSERT(obj->resultTypeSet());
   11306          12 :         switch (obj->resultTypeSet()->forAllClasses(constraints(), IsTypedObjectClass)) {
   11307             :           case TemporaryTypeSet::ForAllResult::ALL_FALSE:
   11308             :           case TemporaryTypeSet::ForAllResult::EMPTY:
   11309             :             break;
   11310             :           case TemporaryTypeSet::ForAllResult::ALL_TRUE:
   11311             :           case TemporaryTypeSet::ForAllResult::MIXED:
   11312           0 :             barrier = BarrierKind::TypeSet;
   11313           0 :             break;
   11314             :         }
   11315             :     }
   11316             : 
   11317           0 :     MConstant* id = constant(StringValue(name));
   11318         373 :     MGetPropertyCache* load = MGetPropertyCache::New(alloc(), obj, id,
   11319        1119 :                                                      barrier == BarrierKind::TypeSet);
   11320             : 
   11321             :     // Try to mark the cache as idempotent.
   11322         718 :     if (obj->type() == MIRType::Object && !invalidatedIdempotentCache()) {
   11323         345 :         if (PropertyReadIsIdempotent(constraints(), obj, name))
   11324             :             load->setIdempotent();
   11325             :     }
   11326             : 
   11327             :     // When we are in the context of making a call from the value returned from
   11328             :     // a property, we query the typeObject for the given property name to fill
   11329             :     // the InlinePropertyTable of the GetPropertyCache.  This information is
   11330             :     // then used in inlineCallsite and inlineCalls, if the "this" definition is
   11331             :     // matching the "object" definition of the GetPropertyCache (see
   11332             :     // CanInlineGetPropertyCache).
   11333             :     //
   11334             :     // If this GetPropertyCache is idempotent, then we can dispatch to the right
   11335             :     // function only by checking the typed object, instead of querying the value
   11336             :     // of the property.  Thus this GetPropertyCache can be moved into the
   11337             :     // fallback path (see inlineObjectGroupFallback).  Otherwise, we always have
   11338             :     // to do the GetPropertyCache, and we can dispatch based on the JSFunction
   11339             :     // value.
   11340         500 :     if (JSOp(*pc) == JSOP_CALLPROP && load->idempotent())
   11341           0 :         MOZ_TRY(annotateGetPropertyCache(obj, name, load, obj->resultTypeSet(), types));
   11342             : 
   11343         373 :     current->add(load);
   11344           0 :     current->push(load);
   11345             : 
   11346         746 :     if (load->isEffectful())
   11347           0 :         MOZ_TRY(resumeAfter(load));
   11348             : 
   11349         373 :     MIRType rvalType = types->getKnownMIRType();
   11350         373 :     if (barrier != BarrierKind::NoBarrier) {
   11351             :         rvalType = MIRType::Value;
   11352             :     } else {
   11353           0 :         load->setResultTypeSet(types);
   11354          12 :         if (IsNullOrUndefined(rvalType))
   11355           0 :             rvalType = MIRType::Value;
   11356             :     }
   11357           0 :     load->setResultType(rvalType);
   11358             : 
   11359         500 :     if (*pc != JSOP_CALLPROP || !IsNullOrUndefined(obj->type())) {
   11360             :         // Due to inlining, it's possible the observed TypeSet is non-empty,
   11361             :         // even though we know |obj| is null/undefined and the MCallGetProperty
   11362             :         // will throw. Don't push a TypeBarrier in this case, to avoid
   11363             :         // inlining the following (unreachable) JSOP_CALL.
   11364        1119 :         MOZ_TRY(pushTypeBarrier(load, types, barrier));
   11365             :     }
   11366             : 
   11367         373 :     trackOptimizationSuccess();
   11368         373 :     return Ok();
   11369             : }
   11370             : 
   11371             : MDefinition*
   11372         730 : IonBuilder::tryInnerizeWindow(MDefinition* obj)
   11373             : {
   11374             :     // Try to optimize accesses on outer window proxies (window.foo, for
   11375             :     // example) to go directly to the inner window, the global.
   11376             :     //
   11377             :     // Callers should be careful not to pass the inner object to getters or
   11378             :     // setters that require outerization.
   11379             : 
   11380         730 :     if (obj->type() != MIRType::Object)
   11381             :         return obj;
   11382             : 
   11383         698 :     TemporaryTypeSet* types = obj->resultTypeSet();
   11384         698 :     if (!types)
   11385             :         return obj;
   11386             : 
   11387         698 :     JSObject* singleton = types->maybeSingleton();
   11388         698 :     if (!singleton)
   11389             :         return obj;
   11390             : 
   11391          18 :     if (!IsWindowProxy(singleton))
   11392             :         return obj;
   11393             : 
   11394             :     // This must be a WindowProxy for the current Window/global. Else it'd be
   11395             :     // a cross-compartment wrapper and IsWindowProxy returns false for those.
   11396           0 :     MOZ_ASSERT(ToWindowIfWindowProxy(singleton) == &script()->global());
   11397             : 
   11398             :     // When we navigate, the WindowProxy is brain transplanted and we'll mark
   11399             :     // its ObjectGroup as having unknown properties. The type constraint we add
   11400             :     // here will invalidate JIT code when this happens.
   11401           0 :     TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(singleton);
   11402           0 :     if (key->hasFlags(constraints(), OBJECT_FLAG_UNKNOWN_PROPERTIES))
   11403             :         return obj;
   11404             : 
   11405           0 :     obj->setImplicitlyUsedUnchecked();
   11406           0 :     return constant(ObjectValue(script()->global()));
   11407             : }
   11408             : 
   11409             : AbortReasonOr<Ok>
   11410         730 : IonBuilder::getPropTryInnerize(bool* emitted, MDefinition* obj, PropertyName* name,
   11411             :                                TemporaryTypeSet* types)
   11412             : {
   11413             :     // See the comment in tryInnerizeWindow for how this works.
   11414             : 
   11415             :     // Note that it's important that we do this _before_ we'd try to
   11416             :     // do the optimizations below on obj normally, since some of those
   11417             :     // optimizations have fallback paths that are slower than the path
   11418             :     // we'd produce here.
   11419             : 
   11420           0 :     MOZ_ASSERT(*emitted == false);
   11421             : 
   11422           0 :     MDefinition* inner = tryInnerizeWindow(obj);
   11423         730 :     if (inner == obj)
   11424           0 :         return Ok();
   11425             : 
   11426           0 :     if (!forceInlineCaches()) {
   11427           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
   11428           0 :         MOZ_TRY(getPropTryConstant(emitted, inner, NameToId(name), types));
   11429           0 :         if (*emitted)
   11430           0 :             return Ok();
   11431             : 
   11432           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_StaticName);
   11433           0 :         MOZ_TRY(getStaticName(emitted, &script()->global(), name));
   11434           0 :         if (*emitted)
   11435           0 :             return Ok();
   11436             : 
   11437           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
   11438           0 :         MOZ_TRY(getPropTryCommonGetter(emitted, inner, name, types, /* innerized = */true));
   11439           0 :         if (*emitted)
   11440           0 :             return Ok();
   11441             :     }
   11442             : 
   11443             :     // Passing the inner object to GetProperty IC is safe, see the
   11444             :     // needsOuterizedThisObject check in IsCacheableGetPropCallNative.
   11445           0 :     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
   11446           0 :                                                        inner, name, types);
   11447           0 :     trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
   11448           0 :     MOZ_TRY(getPropAddCache(inner, name, barrier, types));
   11449             : 
   11450           0 :     *emitted = true;
   11451           0 :     return Ok();
   11452             : }
   11453             : 
   11454             : AbortReasonOr<Ok>
   11455           0 : IonBuilder::jsop_setprop(PropertyName* name)
   11456             : {
   11457         722 :     MDefinition* value = current->pop();
   11458           0 :     MDefinition* obj = convertUnboxedObjects(current->pop());
   11459             : 
   11460           0 :     bool emitted = false;
   11461           0 :     startTrackingOptimizations();
   11462        1444 :     trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet());
   11463        1444 :     trackTypeInfo(TrackedTypeSite::Value, value->type(), value->resultTypeSet());
   11464             : 
   11465             :     // Always use a call if we are doing the definite properties analysis and
   11466             :     // not actually emitting code, to simplify later analysis.
   11467           0 :     if (info().isAnalysis() || shouldAbortOnPreliminaryGroups(obj)) {
   11468           0 :         bool strict = IsStrictSetPC(pc);
   11469           0 :         MInstruction* ins = MCallSetProperty::New(alloc(), obj, value, name, strict);
   11470           0 :         current->add(ins);
   11471          59 :         current->push(value);
   11472          59 :         return resumeAfter(ins);
   11473             :     }
   11474             : 
   11475           0 :     if (!forceInlineCaches()) {
   11476             :         // Try to inline a common property setter, or make a call.
   11477           0 :         trackOptimizationAttempt(TrackedStrategy::SetProp_CommonSetter);
   11478           0 :         MOZ_TRY(setPropTryCommonSetter(&emitted, obj, name, value));
   11479         663 :         if (emitted)
   11480           0 :             return Ok();
   11481             : 
   11482             :         // Try to emit stores to known binary data blocks
   11483           0 :         trackOptimizationAttempt(TrackedStrategy::SetProp_TypedObject);
   11484           0 :         MOZ_TRY(setPropTryTypedObject(&emitted, obj, name, value));
   11485         663 :         if (emitted)
   11486           0 :             return Ok();
   11487             :     }
   11488             : 
   11489           0 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
   11490         663 :     bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
   11491           0 :                                                  /* canModify = */ true);
   11492             : 
   11493           0 :     if (!forceInlineCaches()) {
   11494             :         // Try to emit stores to unboxed objects.
   11495           0 :         trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed);
   11496           0 :         MOZ_TRY(setPropTryUnboxed(&emitted, obj, name, value, barrier));
   11497         663 :         if (emitted)
   11498          32 :             return Ok();
   11499             :     }
   11500             : 
   11501           0 :     if (!forceInlineCaches()) {
   11502             :         // Try to emit store from definite slots.
   11503           0 :         trackOptimizationAttempt(TrackedStrategy::SetProp_DefiniteSlot);
   11504           0 :         MOZ_TRY(setPropTryDefiniteSlot(&emitted, obj, name, value, barrier));
   11505         631 :         if (emitted)
   11506         547 :             return Ok();
   11507             : 
   11508             :         // Try to emit a monomorphic/polymorphic store based on baseline caches.
   11509           0 :         trackOptimizationAttempt(TrackedStrategy::SetProp_InlineAccess);
   11510           0 :         MOZ_TRY(setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes));
   11511          84 :         if (emitted)
   11512           1 :             return Ok();
   11513             :     }
   11514             : 
   11515             :     // Emit a polymorphic cache.
   11516           0 :     trackOptimizationAttempt(TrackedStrategy::SetProp_InlineCache);
   11517           0 :     MOZ_TRY(setPropTryCache(&emitted, obj, name, value, barrier));
   11518          83 :     MOZ_ASSERT(emitted == true);
   11519          83 :     return Ok();
   11520             : }
   11521             : 
   11522             : AbortReasonOr<Ok>
   11523         663 : IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj,
   11524             :                                    PropertyName* name, MDefinition* value)
   11525             : {
   11526           0 :     MOZ_ASSERT(*emitted == false);
   11527             : 
   11528           0 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
   11529         663 :     JSFunction* commonSetter = nullptr;
   11530         663 :     MDefinition* guard = nullptr;
   11531             : 
   11532             :     {
   11533         663 :         Shape* lastProperty = nullptr;
   11534           0 :         JSObject* foundProto = nullptr;
   11535             :         bool isOwnProperty;
   11536           0 :         BaselineInspector::ReceiverVector receivers(alloc());
   11537        1326 :         BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
   11538         663 :         if (inspector->commonSetPropFunction(pc, &foundProto, &lastProperty, &commonSetter,
   11539             :                                               &isOwnProperty,
   11540             :                                               receivers, convertUnboxedGroups))
   11541             :         {
   11542           0 :             bool canUseTIForSetter = false;
   11543           0 :             if (!isOwnProperty) {
   11544             :                 // If it's not an own property, try to use TI to avoid shape guards.
   11545             :                 // For own properties we use the path below.
   11546           0 :                 canUseTIForSetter = testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
   11547           0 :                                                            commonSetter, &guard);
   11548             :             }
   11549           0 :             if (!canUseTIForSetter) {
   11550             :                 // If it's an own property or type information is bad, we can still
   11551             :                 // optimize the setter if we shape guard.
   11552           0 :                 obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
   11553             :                                                     receivers, convertUnboxedGroups,
   11554             :                                                     isOwnProperty);
   11555           0 :                 if (!obj)
   11556           0 :                     return abort(AbortReason::Alloc);
   11557             :             }
   11558         663 :         } else if (inspector->megamorphicGetterSetterFunction(pc, /* isGetter = */ false,
   11559             :                                                               &commonSetter))
   11560             :         {
   11561             :             // Try to use TI to guard on this setter.
   11562           0 :             if (!testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
   11563             :                                         commonSetter, &guard))
   11564             :             {
   11565           0 :                 return Ok();
   11566             :             }
   11567             :         } else {
   11568             :             // The Baseline IC didn't have any information we can use.
   11569         663 :             return Ok();
   11570             :         }
   11571             :     }
   11572             : 
   11573             :     // Emit common setter.
   11574             : 
   11575             :     // Setters can be called even if the property write needs a type
   11576             :     // barrier, as calling the setter does not actually write any data
   11577             :     // properties.
   11578             : 
   11579             :     // Try emitting dom call.
   11580           0 :     MOZ_TRY(setPropTryCommonDOMSetter(emitted, obj, value, commonSetter, objTypes));
   11581           0 :     if (*emitted) {
   11582           0 :         trackOptimizationOutcome(TrackedOutcome::DOM);
   11583           0 :         return Ok();
   11584             :     }
   11585             : 
   11586             :     // Don't call the setter with a primitive value.
   11587           0 :     if (obj->type() != MIRType::Object) {
   11588           0 :         MGuardObject* guardObj = MGuardObject::New(alloc(), obj);
   11589           0 :         current->add(guardObj);
   11590           0 :         obj = guardObj;
   11591             :     }
   11592             : 
   11593             :     // Dummy up the stack, as in getprop. We are pushing an extra value, so
   11594             :     // ensure there is enough space.
   11595           0 :     if (!current->ensureHasSlots(3))
   11596           0 :         return abort(AbortReason::Alloc);
   11597             : 
   11598           0 :     current->push(constant(ObjectValue(*commonSetter)));
   11599           0 :     current->push(obj);
   11600           0 :     current->push(value);
   11601             : 
   11602             :     // Call the setter. Note that we have to push the original value, not
   11603             :     // the setter's return value.
   11604             :     CallInfo callInfo(alloc(), pc, /* constructing = */ false,
   11605           0 :                       /* ignoresReturnValue = */ BytecodeIsPopped(pc));
   11606           0 :     if (!callInfo.init(current, 1))
   11607           0 :         return abort(AbortReason::Alloc);
   11608             : 
   11609             :     // Ensure that we know we are calling a setter in case we inline it.
   11610           0 :     callInfo.markAsSetter();
   11611             : 
   11612             :     // Inline the setter if we can.
   11613           0 :     if (commonSetter->isInterpreted()) {
   11614           0 :         InliningDecision decision = makeInliningDecision(commonSetter, callInfo);
   11615           0 :         switch (decision) {
   11616             :           case InliningDecision_Error:
   11617           0 :             return abort(AbortReason::Error);
   11618             :           case InliningDecision_DontInline:
   11619             :           case InliningDecision_WarmUpCountTooLow:
   11620             :             break;
   11621             :           case InliningDecision_Inline: {
   11622             :             InliningStatus status;
   11623           0 :             MOZ_TRY_VAR(status, inlineScriptedCall(callInfo, commonSetter));
   11624           0 :             if (status == InliningStatus_Inlined) {
   11625           0 :                 *emitted = true;
   11626           0 :                 return Ok();
   11627             :             }
   11628             :           }
   11629             :         }
   11630             :     }
   11631             : 
   11632           0 :     Maybe<CallTargets> targets;
   11633           0 :     targets.emplace(alloc());
   11634           0 :     if (!targets->append(commonSetter))
   11635           0 :         return abort(AbortReason::Alloc);
   11636             :     MCall* call;
   11637           0 :     MOZ_TRY_VAR(call, makeCallHelper(targets, callInfo));
   11638             : 
   11639           0 :     current->push(value);
   11640           0 :     MOZ_TRY(resumeAfter(call));
   11641             : 
   11642             :     // If the setter could have been inlined, don't track success. The call to
   11643             :     // makeInliningDecision above would have tracked a specific reason why we
   11644             :     // couldn't inline.
   11645           0 :     if (!commonSetter->isInterpreted())
   11646             :         trackOptimizationSuccess();
   11647             : 
   11648           0 :     *emitted = true;
   11649           0 :     return Ok();
   11650             : }
   11651             : 
   11652             : AbortReasonOr<Ok>
   11653           0 : IonBuilder::setPropTryCommonDOMSetter(bool* emitted, MDefinition* obj,
   11654             :                                       MDefinition* value, JSFunction* setter,
   11655             :                                       TemporaryTypeSet* objTypes)
   11656             : {
   11657           0 :     MOZ_ASSERT(*emitted == false);
   11658             : 
   11659           0 :     DOMObjectKind objKind = DOMObjectKind::Unknown;
   11660           0 :     if (!objTypes || !objTypes->isDOMClass(constraints(), &objKind))
   11661           0 :         return Ok();
   11662             : 
   11663           0 :     bool isDOM = false;
   11664           0 :     MOZ_TRY_VAR(isDOM, testShouldDOMCall(objTypes, setter, JSJitInfo::Setter));
   11665           0 :     if (!isDOM)
   11666           0 :         return Ok();
   11667             : 
   11668             :     // Emit SetDOMProperty.
   11669           0 :     MOZ_ASSERT(setter->jitInfo()->type() == JSJitInfo::Setter);
   11670           0 :     MSetDOMProperty* set = MSetDOMProperty::New(alloc(), setter->jitInfo()->setter, objKind,
   11671           0 :                                                 obj, value);
   11672             : 
   11673           0 :     current->add(set);
   11674           0 :     current->push(value);
   11675             : 
   11676           0 :     MOZ_TRY(resumeAfter(set));
   11677             : 
   11678           0 :     *emitted = true;
   11679           0 :     return Ok();
   11680             : }
   11681             : 
   11682             : AbortReasonOr<Ok>
   11683         663 : IonBuilder::setPropTryTypedObject(bool* emitted, MDefinition* obj,
   11684             :                                   PropertyName* name, MDefinition* value)
   11685             : {
   11686         663 :     TypedObjectPrediction fieldPrediction;
   11687             :     size_t fieldOffset;
   11688             :     size_t fieldIndex;
   11689         663 :     if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
   11690           0 :         return Ok();
   11691             : 
   11692           0 :     switch (fieldPrediction.kind()) {
   11693             :       case type::Simd:
   11694             :         // FIXME (bug 894104): store into a MIRType::float32x4 etc
   11695           0 :         return Ok();
   11696             : 
   11697             :       case type::Reference:
   11698             :         return setPropTryReferencePropOfTypedObject(emitted, obj, fieldOffset,
   11699           0 :                                                     value, fieldPrediction, name);
   11700             : 
   11701             :       case type::Scalar:
   11702             :         return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset,
   11703           0 :                                                  value, fieldPrediction);
   11704             : 
   11705             :       case type::Struct:
   11706             :       case type::Array:
   11707           0 :         return Ok();
   11708             :     }
   11709             : 
   11710           0 :     MOZ_CRASH("Unknown kind");
   11711             : }
   11712             : 
   11713             : AbortReasonOr<Ok>
   11714           0 : IonBuilder::setPropTryReferencePropOfTypedObject(bool* emitted,
   11715             :                                                  MDefinition* obj,
   11716             :                                                  int32_t fieldOffset,
   11717             :                                                  MDefinition* value,
   11718             :                                                  TypedObjectPrediction fieldPrediction,
   11719             :                                                  PropertyName* name)
   11720             : {
   11721           0 :     ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
   11722             : 
   11723           0 :     TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
   11724           0 :     if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
   11725           0 :         return Ok();
   11726             : 
   11727           0 :     LinearSum byteOffset(alloc());
   11728           0 :     if (!byteOffset.add(fieldOffset))
   11729           0 :         return abort(AbortReason::Disable, "Overflow of field offset.");
   11730             : 
   11731           0 :     return setPropTryReferenceTypedObjectValue(emitted, obj, byteOffset, fieldType, value, name);
   11732             : }
   11733             : 
   11734             : AbortReasonOr<Ok>
   11735           0 : IonBuilder::setPropTryScalarPropOfTypedObject(bool* emitted,
   11736             :                                               MDefinition* obj,
   11737             :                                               int32_t fieldOffset,
   11738             :                                               MDefinition* value,
   11739             :                                               TypedObjectPrediction fieldPrediction)
   11740             : {
   11741             :     // Must always be loading the same scalar type
   11742           0 :     Scalar::Type fieldType = fieldPrediction.scalarType();
   11743             : 
   11744             :     // Don't optimize if the typed object's underlying buffer may be detached.
   11745           0 :     TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
   11746           0 :     if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
   11747           0 :         return Ok();
   11748             : 
   11749           0 :     LinearSum byteOffset(alloc());
   11750           0 :     if (!byteOffset.add(fieldOffset))
   11751           0 :         return abort(AbortReason::Disable, "Overflow of field offet.");
   11752             : 
   11753           0 :     return setPropTryScalarTypedObjectValue(emitted, obj, byteOffset, fieldType, value);
   11754             : }
   11755             : 
   11756             : AbortReasonOr<Ok>
   11757         631 : IonBuilder::setPropTryDefiniteSlot(bool* emitted, MDefinition* obj,
   11758             :                                    PropertyName* name, MDefinition* value,
   11759             :                                    bool barrier)
   11760             : {
   11761           0 :     MOZ_ASSERT(*emitted == false);
   11762             : 
   11763           0 :     if (barrier) {
   11764           1 :         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
   11765           1 :         return Ok();
   11766             :     }
   11767             : 
   11768             :     uint32_t nfixed;
   11769           0 :     uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), NameToId(name), &nfixed);
   11770         630 :     if (slot == UINT32_MAX)
   11771          83 :         return Ok();
   11772             : 
   11773             :     bool writeBarrier = false;
   11774           0 :     for (size_t i = 0; i < obj->resultTypeSet()->getObjectCount(); i++) {
   11775           1 :         TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i);
   11776         555 :         if (!key)
   11777           0 :             continue;
   11778             : 
   11779           0 :         HeapTypeSetKey property = key->property(NameToId(name));
   11780           0 :         if (property.nonWritable(constraints())) {
   11781           0 :             trackOptimizationOutcome(TrackedOutcome::NonWritableProperty);
   11782           0 :             return Ok();
   11783             :         }
   11784         555 :         writeBarrier |= property.needsBarrier(constraints());
   11785             :     }
   11786             : 
   11787         547 :     if (needsPostBarrier(value))
   11788         146 :         current->add(MPostWriteBarrier::New(alloc(), obj, value));
   11789             : 
   11790             :     MInstruction* store;
   11791           0 :     if (slot < nfixed) {
   11792           0 :         store = MStoreFixedSlot::New(alloc(), obj, slot, value);
   11793         547 :         if (writeBarrier)
   11794           0 :             store->toStoreFixedSlot()->setNeedsBarrier();
   11795             :     } else {
   11796           0 :         MInstruction* slots = MSlots::New(alloc(), obj);
   11797           0 :         current->add(slots);
   11798             : 
   11799           0 :         store = MStoreSlot::New(alloc(), slots, slot - nfixed, value);
   11800           0 :         if (writeBarrier)
   11801           0 :             store->toStoreSlot()->setNeedsBarrier();
   11802             :     }
   11803             : 
   11804         547 :     current->add(store);
   11805           0 :     current->push(value);
   11806             : 
   11807           0 :     MOZ_TRY(resumeAfter(store));
   11808             : 
   11809           0 :     trackOptimizationSuccess();
   11810         547 :     *emitted = true;
   11811         547 :     return Ok();
   11812             : }
   11813             : 
   11814             : MInstruction*
   11815          32 : IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
   11816             :                                  MDefinition* value)
   11817             : {
   11818           0 :     size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType);
   11819          64 :     MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant));
   11820          32 :     current->add(scaledOffset);
   11821             : 
   11822             :     return storeUnboxedValue(obj, obj, UnboxedPlainObject::offsetOfData(),
   11823          32 :                              scaledOffset, unboxedType, value);
   11824             : }
   11825             : 
   11826             : MInstruction*
   11827          32 : IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset,
   11828             :                               MDefinition* scaledOffset, JSValueType unboxedType,
   11829             :                               MDefinition* value, bool preBarrier /* = true */)
   11830             : {
   11831             :     MInstruction* store;
   11832           0 :     switch (unboxedType) {
   11833             :       case JSVAL_TYPE_BOOLEAN:
   11834           0 :         store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8,
   11835             :                                          MStoreUnboxedScalar::DontTruncateInput,
   11836           0 :                                          DoesNotRequireMemoryBarrier, elementsOffset);
   11837           0 :         break;
   11838             : 
   11839             :       case JSVAL_TYPE_INT32:
   11840           0 :         store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32,
   11841             :                                          MStoreUnboxedScalar::DontTruncateInput,
   11842           0 :                                          DoesNotRequireMemoryBarrier, elementsOffset);
   11843           0 :         break;
   11844             : 
   11845             :       case JSVAL_TYPE_DOUBLE:
   11846           0 :         store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64,
   11847             :                                          MStoreUnboxedScalar::DontTruncateInput,
   11848           0 :                                          DoesNotRequireMemoryBarrier, elementsOffset);
   11849           0 :         break;
   11850             : 
   11851             :       case JSVAL_TYPE_STRING:
   11852           0 :         store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, obj,
   11853          64 :                                          elementsOffset, preBarrier);
   11854          32 :         break;
   11855             : 
   11856             :       case JSVAL_TYPE_OBJECT:
   11857           0 :         MOZ_ASSERT(value->type() == MIRType::Object ||
   11858             :                    value->type() == MIRType::Null ||
   11859             :                    value->type() == MIRType::Value);
   11860           0 :         MOZ_ASSERT(!value->mightBeType(MIRType::Undefined),
   11861             :                    "MToObjectOrNull slow path is invalid for unboxed objects");
   11862           0 :         store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj,
   11863           0 :                                                elementsOffset, preBarrier);
   11864           0 :         break;
   11865             : 
   11866             :       default:
   11867           0 :         MOZ_CRASH();
   11868             :     }
   11869             : 
   11870          32 :     current->add(store);
   11871          32 :     return store;
   11872             : }
   11873             : 
   11874             : AbortReasonOr<Ok>
   11875         663 : IonBuilder::setPropTryUnboxed(bool* emitted, MDefinition* obj,
   11876             :                               PropertyName* name, MDefinition* value,
   11877             :                               bool barrier)
   11878             : {
   11879           0 :     MOZ_ASSERT(*emitted == false);
   11880             : 
   11881           0 :     if (barrier) {
   11882           1 :         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
   11883           1 :         return Ok();
   11884             :     }
   11885             : 
   11886             :     JSValueType unboxedType;
   11887           0 :     uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), NameToId(name), &unboxedType);
   11888         662 :     if (offset == UINT32_MAX)
   11889           0 :         return Ok();
   11890             : 
   11891           1 :     if (obj->type() != MIRType::Object) {
   11892           0 :         MGuardObject* guard = MGuardObject::New(alloc(), obj);
   11893           0 :         current->add(guard);
   11894           0 :         obj = guard;
   11895             :     }
   11896             : 
   11897           0 :     MInstruction* store = storeUnboxedProperty(obj, offset, unboxedType, value);
   11898             : 
   11899           0 :     current->push(value);
   11900             : 
   11901           0 :     MOZ_TRY(resumeAfter(store));
   11902             : 
   11903          32 :     *emitted = true;
   11904          32 :     return Ok();
   11905             : }
   11906             : 
   11907             : AbortReasonOr<Ok>
   11908          84 : IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj,
   11909             :                                    PropertyName* name, MDefinition* value,
   11910             :                                    bool barrier, TemporaryTypeSet* objTypes)
   11911             : {
   11912           0 :     MOZ_ASSERT(*emitted == false);
   11913             : 
   11914           0 :     if (barrier) {
   11915           1 :         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
   11916           1 :         return Ok();
   11917             :     }
   11918             : 
   11919           0 :     BaselineInspector::ReceiverVector receivers(alloc());
   11920           1 :     BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
   11921          83 :     if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
   11922           0 :         return abort(AbortReason::Alloc);
   11923             : 
   11924          83 :     if (!canInlinePropertyOpShapes(receivers))
   11925           0 :         return Ok();
   11926             : 
   11927           0 :     obj = convertUnboxedObjects(obj, convertUnboxedGroups);
   11928             : 
   11929           1 :     if (receivers.length() == 1) {
   11930           0 :         if (!receivers[0].group) {
   11931             :             // Monomorphic store to a native object.
   11932           0 :             spew("Inlining monomorphic native SETPROP");
   11933             : 
   11934           0 :             obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
   11935             : 
   11936           2 :             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
   11937           0 :             MOZ_ASSERT(shape);
   11938             : 
   11939           1 :             if (needsPostBarrier(value))
   11940           0 :                 current->add(MPostWriteBarrier::New(alloc(), obj, value));
   11941             : 
   11942           1 :             bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
   11943           0 :             MOZ_TRY(storeSlot(obj, shape, value, needsPreBarrier));
   11944             : 
   11945           0 :             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11946           1 :             *emitted = true;
   11947           1 :             return Ok();
   11948             :         }
   11949             : 
   11950           0 :         if (receivers[0].shape) {
   11951             :             // Monomorphic store to an unboxed object expando.
   11952           0 :             spew("Inlining monomorphic unboxed expando SETPROP");
   11953             : 
   11954           0 :             obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
   11955           0 :             obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard);
   11956             : 
   11957           0 :             MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
   11958           0 :             current->add(expando);
   11959             : 
   11960           0 :             expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
   11961             : 
   11962           0 :             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
   11963           0 :             MOZ_ASSERT(shape);
   11964             : 
   11965           0 :             if (needsPostBarrier(value))
   11966           0 :                 current->add(MPostWriteBarrier::New(alloc(), obj, value));
   11967             : 
   11968           0 :             bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
   11969           0 :             MOZ_TRY(storeSlot(expando, shape, value, needsPreBarrier));
   11970             : 
   11971           0 :             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11972           0 :             *emitted = true;
   11973           0 :             return Ok();
   11974             :         }
   11975             : 
   11976             :         // Monomorphic store to an unboxed object.
   11977           0 :         spew("Inlining monomorphic unboxed SETPROP");
   11978             : 
   11979           0 :         ObjectGroup* group = receivers[0].group;
   11980           0 :         if (!objTypes->hasType(TypeSet::ObjectType(group)))
   11981           0 :             return Ok();
   11982             : 
   11983           0 :         obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
   11984             : 
   11985           0 :         if (needsPostBarrier(value))
   11986           0 :             current->add(MPostWriteBarrier::New(alloc(), obj, value));
   11987             : 
   11988           0 :         AutoSweepObjectGroup sweep(group);
   11989           0 :         const UnboxedLayout::Property* property = group->unboxedLayout(sweep).lookup(name);
   11990           0 :         MInstruction* store = storeUnboxedProperty(obj, property->offset, property->type, value);
   11991             : 
   11992           0 :         current->push(value);
   11993             : 
   11994           0 :         MOZ_TRY(resumeAfter(store));
   11995             : 
   11996           0 :         trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11997           0 :         *emitted = true;
   11998           0 :         return Ok();
   11999             :     }
   12000             : 
   12001           0 :     MOZ_ASSERT(receivers.length() > 1);
   12002           0 :     spew("Inlining polymorphic SETPROP");
   12003             : 
   12004           0 :     if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) {
   12005           0 :         obj = addGuardReceiverPolymorphic(obj, receivers);
   12006           0 :         if (!obj)
   12007           0 :             return abort(AbortReason::Alloc);
   12008             : 
   12009           0 :         if (needsPostBarrier(value))
   12010           0 :             current->add(MPostWriteBarrier::New(alloc(), obj, value));
   12011             : 
   12012           0 :         bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
   12013           0 :         MOZ_TRY(storeSlot(obj, propShape, value, needsPreBarrier));
   12014             : 
   12015           0 :         trackOptimizationOutcome(TrackedOutcome::Polymorphic);
   12016           0 :         *emitted = true;
   12017           0 :         return Ok();
   12018             :     }
   12019             : 
   12020           0 :     if (needsPostBarrier(value))
   12021           0 :         current->add(MPostWriteBarrier::New(alloc(), obj, value));
   12022             : 
   12023           0 :     MSetPropertyPolymorphic* ins = MSetPropertyPolymorphic::New(alloc(), obj, value, name);
   12024           0 :     current->add(ins);
   12025           0 :     current->push(value);
   12026             : 
   12027           0 :     for (size_t i = 0; i < receivers.length(); i++) {
   12028           0 :         Shape* propShape = nullptr;
   12029           0 :         if (receivers[i].shape) {
   12030           0 :             propShape = receivers[i].shape->searchLinear(NameToId(name));
   12031           0 :             MOZ_ASSERT(propShape);
   12032             :         }
   12033           0 :         if (!ins->addReceiver(receivers[i], propShape))
   12034           0 :             return abort(AbortReason::Alloc);
   12035             :     }
   12036             : 
   12037           0 :     if (objTypes->propertyNeedsBarrier(constraints(), NameToId(name)))
   12038           0 :         ins->setNeedsBarrier();
   12039             : 
   12040           0 :     MOZ_TRY(resumeAfter(ins));
   12041             : 
   12042           0 :     trackOptimizationOutcome(TrackedOutcome::Polymorphic);
   12043           0 :     *emitted = true;
   12044           0 :     return Ok();
   12045             : }
   12046             : 
   12047             : AbortReasonOr<Ok>
   12048          97 : IonBuilder::setPropTryCache(bool* emitted, MDefinition* obj,
   12049             :                             PropertyName* name, MDefinition* value,
   12050             :                             bool barrier)
   12051             : {
   12052           0 :     MOZ_ASSERT(*emitted == false);
   12053             : 
   12054           0 :     bool strict = IsStrictSetPC(pc);
   12055             : 
   12056           0 :     MConstant* id = constant(StringValue(name));
   12057           0 :     MSetPropertyCache* ins = MSetPropertyCache::New(alloc(), obj, id, value, strict,
   12058           0 :                                                     needsPostBarrier(value), barrier,
   12059           0 :                                                     /* guardHoles = */ false);
   12060          97 :     current->add(ins);
   12061           0 :     current->push(value);
   12062             : 
   12063           0 :     MOZ_TRY(resumeAfter(ins));
   12064             : 
   12065           0 :     trackOptimizationSuccess();
   12066          97 :     *emitted = true;
   12067          97 :     return Ok();
   12068             : }
   12069             : 
   12070             : AbortReasonOr<Ok>
   12071           0 : IonBuilder::jsop_delprop(PropertyName* name)
   12072             : {
   12073           0 :     MDefinition* obj = current->pop();
   12074             : 
   12075           0 :     bool strict = JSOp(*pc) == JSOP_STRICTDELPROP;
   12076           0 :     MInstruction* ins = MDeleteProperty::New(alloc(), obj, name, strict);
   12077             : 
   12078           0 :     current->add(ins);
   12079           0 :     current->push(ins);
   12080             : 
   12081           0 :     return resumeAfter(ins);
   12082             : }
   12083             : 
   12084             : AbortReasonOr<Ok>
   12085           0 : IonBuilder::jsop_delelem()
   12086             : {
   12087           0 :     MDefinition* index = current->pop();
   12088           0 :     MDefinition* obj = current->pop();
   12089             : 
   12090           0 :     bool strict = JSOp(*pc) == JSOP_STRICTDELELEM;
   12091           0 :     MDeleteElement* ins = MDeleteElement::New(alloc(), obj, index, strict);
   12092           0 :     current->add(ins);
   12093           0 :     current->push(ins);
   12094             : 
   12095           0 :     return resumeAfter(ins);
   12096             : }
   12097             : 
   12098             : AbortReasonOr<Ok>
   12099           0 : IonBuilder::jsop_regexp(RegExpObject* reobj)
   12100             : {
   12101           1 :     MOZ_ASSERT(!IsInsideNursery(reobj));
   12102             : 
   12103             :     // Determine this while we're still on the main thread to avoid races.
   12104           0 :     bool hasShared = reobj->hasShared();
   12105             : 
   12106           0 :     MRegExp* regexp = MRegExp::New(alloc(), constraints(), reobj, hasShared);
   12107           1 :     current->add(regexp);
   12108           0 :     current->push(regexp);
   12109             : 
   12110           1 :     return Ok();
   12111             : }
   12112             : 
   12113             : AbortReasonOr<Ok>
   12114           0 : IonBuilder::jsop_object(JSObject* obj)
   12115             : {
   12116           0 :     if (options.cloneSingletons()) {
   12117           0 :         MCloneLiteral* clone = MCloneLiteral::New(alloc(), constant(ObjectValue(*obj)));
   12118           0 :         current->add(clone);
   12119           0 :         current->push(clone);
   12120             :         return resumeAfter(clone);
   12121             :     }
   12122             : 
   12123           0 :     realm->setSingletonsAsValues();
   12124           0 :     pushConstant(ObjectValue(*obj));
   12125           0 :     return Ok();
   12126             : }
   12127             : 
   12128             : AbortReasonOr<Ok>
   12129           0 : IonBuilder::jsop_classconstructor()
   12130             : {
   12131           0 :     MClassConstructor* constructor = MClassConstructor::New(alloc(), pc);
   12132           0 :     current->add(constructor);
   12133           0 :     current->push(constructor);
   12134           0 :     return resumeAfter(constructor);
   12135             : }
   12136             : 
   12137             : AbortReasonOr<Ok>
   12138           0 : IonBuilder::jsop_lambda(JSFunction* fun)
   12139             : {
   12140          17 :     MOZ_ASSERT(usesEnvironmentChain());
   12141           0 :     MOZ_ASSERT(!fun->isArrow());
   12142             : 
   12143          17 :     if (IsAsmJSModule(fun))
   12144           0 :         return abort(AbortReason::Disable, "Lambda is an asm.js module function");
   12145             : 
   12146           0 :     MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
   12147           0 :     current->add(cst);
   12148           0 :     MLambda* ins = MLambda::New(alloc(), constraints(), current->environmentChain(), cst);
   12149          17 :     current->add(ins);
   12150          17 :     current->push(ins);
   12151             : 
   12152             :     return resumeAfter(ins);
   12153             : }
   12154             : 
   12155             : AbortReasonOr<Ok>
   12156           0 : IonBuilder::jsop_lambda_arrow(JSFunction* fun)
   12157             : {
   12158           0 :     MOZ_ASSERT(usesEnvironmentChain());
   12159           1 :     MOZ_ASSERT(fun->isArrow());
   12160           0 :     MOZ_ASSERT(!fun->isNative());
   12161             : 
   12162           0 :     MDefinition* newTargetDef = current->pop();
   12163           0 :     MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
   12164           0 :     current->add(cst);
   12165           0 :     MLambdaArrow* ins = MLambdaArrow::New(alloc(), constraints(), current->environmentChain(),
   12166           0 :                                           newTargetDef, cst);
   12167           1 :     current->add(ins);
   12168           0 :     current->push(ins);
   12169             : 
   12170           1 :     return resumeAfter(ins);
   12171             : }
   12172             : 
   12173             : AbortReasonOr<Ok>
   12174           0 : IonBuilder::jsop_setfunname(uint8_t prefixKind)
   12175             : {
   12176           0 :     MDefinition* name = current->pop();
   12177           0 :     MDefinition* fun = current->pop();
   12178           0 :     MOZ_ASSERT(fun->type() == MIRType::Object);
   12179             : 
   12180           0 :     MSetFunName* ins = MSetFunName::New(alloc(), fun, name, prefixKind);
   12181             : 
   12182           0 :     current->add(ins);
   12183           0 :     current->push(fun);
   12184             : 
   12185           0 :     return resumeAfter(ins);
   12186             : }
   12187             : 
   12188             : AbortReasonOr<Ok>
   12189           0 : IonBuilder::jsop_pushlexicalenv(uint32_t index)
   12190             : {
   12191           0 :     MOZ_ASSERT(usesEnvironmentChain());
   12192             : 
   12193           0 :     LexicalScope* scope = &script()->getScope(index)->as<LexicalScope>();
   12194             :     MNewLexicalEnvironmentObject* ins =
   12195           0 :         MNewLexicalEnvironmentObject::New(alloc(), current->environmentChain(), scope);
   12196             : 
   12197           1 :     current->add(ins);
   12198           0 :     current->setEnvironmentChain(ins);
   12199             : 
   12200           1 :     return Ok();
   12201             : }
   12202             : 
   12203             : AbortReasonOr<Ok>
   12204           0 : IonBuilder::jsop_copylexicalenv(bool copySlots)
   12205             : {
   12206           0 :     MOZ_ASSERT(usesEnvironmentChain());
   12207             : 
   12208             :     MCopyLexicalEnvironmentObject* ins =
   12209           0 :         MCopyLexicalEnvironmentObject::New(alloc(), current->environmentChain(), copySlots);
   12210             : 
   12211           0 :     current->add(ins);
   12212           0 :     current->setEnvironmentChain(ins);
   12213             : 
   12214           0 :     return Ok();
   12215             : }
   12216             : 
   12217             : AbortReasonOr<Ok>
   12218         152 : IonBuilder::jsop_setarg(uint32_t arg)
   12219             : {
   12220             :     // To handle this case, we should spill the arguments to the space where
   12221             :     // actual arguments are stored. The tricky part is that if we add a MIR
   12222             :     // to wrap the spilling action, we don't want the spilling to be
   12223             :     // captured by the GETARG and by the resume point, only by
   12224             :     // MGetFrameArgument.
   12225           0 :     MOZ_ASSERT_IF(script()->hasBaselineScript(),
   12226             :                   script()->baselineScript()->modifiesArguments());
   12227         152 :     MDefinition* val = current->peek(-1);
   12228             : 
   12229             :     // If an arguments object is in use, and it aliases formals, then all SETARGs
   12230             :     // must go through the arguments object.
   12231           0 :     if (info().argsObjAliasesFormals()) {
   12232           0 :         if (needsPostBarrier(val))
   12233           0 :             current->add(MPostWriteBarrier::New(alloc(), current->argumentsObject(), val));
   12234           0 :         current->add(MSetArgumentsObjectArg::New(alloc(), current->argumentsObject(),
   12235           0 :                                                  GET_ARGNO(pc), val));
   12236           0 :         return Ok();
   12237             :     }
   12238             : 
   12239             :     // :TODO: if hasArguments() is true, and the script has a JSOP_SETARG, then
   12240             :     // convert all arg accesses to go through the arguments object. (see Bug 957475)
   12241         304 :     if (info().hasArguments())
   12242           0 :         return abort(AbortReason::Disable, "NYI: arguments & setarg.");
   12243             : 
   12244             :     // Otherwise, if a magic arguments is in use, and it aliases formals, and there exist
   12245             :     // arguments[...] GETELEM expressions in the script, then SetFrameArgument must be used.
   12246             :     // If no arguments[...] GETELEM expressions are in the script, and an argsobj is not
   12247             :     // required, then it means that any aliased argument set can never be observed, and
   12248             :     // the frame does not actually need to be updated with the new arg value.
   12249         304 :     if (info().argumentsAliasesFormals()) {
   12250             :         // JSOP_SETARG with magic arguments within inline frames is not yet supported.
   12251             :         MOZ_ASSERT(script()->uninlineable() && !isInlineBuilder());
   12252             : 
   12253             :         MSetFrameArgument* store = MSetFrameArgument::New(alloc(), arg, val);
   12254             :         modifiesFrameArguments_ = true;
   12255             :         current->add(store);
   12256             :         current->setArg(arg);
   12257             :         return Ok();
   12258             :     }
   12259             : 
   12260             :     // If this assignment is at the start of the function and is coercing
   12261             :     // the original value for the argument which was passed in, loosen
   12262             :     // the type information for that original argument if it is currently
   12263             :     // empty due to originally executing in the interpreter.
   12264         307 :     if (graph().numBlocks() == 1 &&
   12265           0 :         (val->isBitOr() || val->isBitAnd() || val->isMul() /* for JSOP_POS */))
   12266             :      {
   12267           0 :          for (size_t i = 0; i < val->numOperands(); i++) {
   12268           0 :             MDefinition* op = val->getOperand(i);
   12269           0 :             if (op->isParameter() &&
   12270           0 :                 op->toParameter()->index() == (int32_t)arg &&
   12271           0 :                 op->resultTypeSet() &&
   12272           0 :                 op->resultTypeSet()->empty())
   12273             :             {
   12274           0 :                 bool otherUses = false;
   12275           0 :                 for (MUseDefIterator iter(op); iter; iter++) {
   12276           0 :                     MDefinition* def = iter.def();
   12277           0 :                     if (def == val)
   12278             :                         continue;
   12279           0 :                     otherUses = true;
   12280             :                 }
   12281           0 :                 if (!otherUses) {
   12282           0 :                     MOZ_ASSERT(op->resultTypeSet() == &argTypes[arg]);
   12283           0 :                     argTypes[arg].addType(TypeSet::UnknownType(), alloc_->lifoAlloc());
   12284           0 :                     if (val->isMul()) {
   12285           0 :                         val->setResultType(MIRType::Double);
   12286           0 :                         val->toMul()->setSpecialization(MIRType::Double);
   12287             :                     } else {
   12288           0 :                         MOZ_ASSERT(val->type() == MIRType::Int32);
   12289             :                     }
   12290           0 :                     val->setResultTypeSet(nullptr);
   12291             :                 }
   12292             :             }
   12293             :         }
   12294             :     }
   12295             : 
   12296         152 :     current->setArg(arg);
   12297         152 :     return Ok();
   12298             : }
   12299             : 
   12300             : AbortReasonOr<Ok>
   12301           0 : IonBuilder::jsop_defvar(uint32_t index)
   12302             : {
   12303           0 :     MOZ_ASSERT(JSOp(*pc) == JSOP_DEFVAR);
   12304             : 
   12305           0 :     PropertyName* name = script()->getName(index);
   12306             : 
   12307             :     // Bake in attrs.
   12308           0 :     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
   12309           0 :     MOZ_ASSERT(!script()->isForEval());
   12310             : 
   12311             :     // Pass the EnvironmentChain.
   12312           0 :     MOZ_ASSERT(usesEnvironmentChain());
   12313             : 
   12314             :     // Bake the name pointer into the MDefVar.
   12315           0 :     MDefVar* defvar = MDefVar::New(alloc(), name, attrs, current->environmentChain());
   12316           0 :     current->add(defvar);
   12317             : 
   12318           0 :     return resumeAfter(defvar);
   12319             : }
   12320             : 
   12321             : AbortReasonOr<Ok>
   12322           0 : IonBuilder::jsop_deflexical(uint32_t index)
   12323             : {
   12324           0 :     MOZ_ASSERT(!script()->hasNonSyntacticScope());
   12325           0 :     MOZ_ASSERT(JSOp(*pc) == JSOP_DEFLET || JSOp(*pc) == JSOP_DEFCONST);
   12326             : 
   12327           0 :     PropertyName* name = script()->getName(index);
   12328           0 :     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
   12329           0 :     if (JSOp(*pc) == JSOP_DEFCONST)
   12330           0 :         attrs |= JSPROP_READONLY;
   12331             : 
   12332           0 :     MDefLexical* deflex = MDefLexical::New(alloc(), name, attrs);
   12333           0 :     current->add(deflex);
   12334             : 
   12335           0 :     return resumeAfter(deflex);
   12336             : }
   12337             : 
   12338             : AbortReasonOr<Ok>
   12339           0 : IonBuilder::jsop_deffun()
   12340             : {
   12341           0 :     MOZ_ASSERT(usesEnvironmentChain());
   12342             : 
   12343           0 :     MDefFun* deffun = MDefFun::New(alloc(), current->pop(), current->environmentChain());
   12344           0 :     current->add(deffun);
   12345             : 
   12346           0 :     return resumeAfter(deffun);
   12347             : }
   12348             : 
   12349             : AbortReasonOr<Ok>
   12350           0 : IonBuilder::jsop_throwsetconst()
   12351             : {
   12352           0 :     current->peek(-1)->setImplicitlyUsedUnchecked();
   12353           0 :     MInstruction* lexicalError = MThrowRuntimeLexicalError::New(alloc(), JSMSG_BAD_CONST_ASSIGN);
   12354           0 :     current->add(lexicalError);
   12355           0 :     return resumeAfter(lexicalError);
   12356             : }
   12357             : 
   12358             : AbortReasonOr<Ok>
   12359           0 : IonBuilder::jsop_checklexical()
   12360             : {
   12361           0 :     uint32_t slot = info().localSlot(GET_LOCALNO(pc));
   12362             :     MDefinition* lexical;
   12363           0 :     MOZ_TRY_VAR(lexical, addLexicalCheck(current->getSlot(slot)));
   12364           0 :     current->setSlot(slot, lexical);
   12365           0 :     return Ok();
   12366             : }
   12367             : 
   12368             : AbortReasonOr<Ok>
   12369           3 : IonBuilder::jsop_checkaliasedlexical(EnvironmentCoordinate ec)
   12370             : {
   12371             :     MDefinition* let;
   12372           0 :     MOZ_TRY_VAR(let, addLexicalCheck(getAliasedVar(ec)));
   12373             : 
   12374           3 :     jsbytecode* nextPc = pc + JSOP_CHECKALIASEDLEXICAL_LENGTH;
   12375           3 :     MOZ_ASSERT(JSOp(*nextPc) == JSOP_GETALIASEDVAR ||
   12376             :                JSOp(*nextPc) == JSOP_SETALIASEDVAR ||
   12377             :                JSOp(*nextPc) == JSOP_THROWSETALIASEDCONST);
   12378           3 :     MOZ_ASSERT(ec == EnvironmentCoordinate(nextPc));
   12379             : 
   12380             :     // If we are checking for a load, push the checked let so that the load
   12381             :     // can use it.
   12382           3 :     if (JSOp(*nextPc) == JSOP_GETALIASEDVAR)
   12383           0 :         setLexicalCheck(let);
   12384             : 
   12385           3 :     return Ok();
   12386             : }
   12387             : 
   12388             : AbortReasonOr<Ok>
   12389           0 : IonBuilder::jsop_functionthis()
   12390             : {
   12391         206 :     MOZ_ASSERT(info().funMaybeLazy());
   12392           0 :     MOZ_ASSERT(!info().funMaybeLazy()->isArrow());
   12393             : 
   12394           0 :     if (script()->strict() || info().funMaybeLazy()->isSelfHostedBuiltin()) {
   12395             :         // No need to wrap primitive |this| in strict mode or self-hosted code.
   12396         410 :         current->pushSlot(info().thisSlot());
   12397         205 :         return Ok();
   12398             :     }
   12399             : 
   12400           3 :     if (thisTypes && (thisTypes->getKnownMIRType() == MIRType::Object ||
   12401           3 :         (thisTypes->empty() && baselineFrame_ && baselineFrame_->thisType.isSomeObject())))
   12402             :     {
   12403             :         // This is safe, because if the entry type of |this| is an object, it
   12404             :         // will necessarily be an object throughout the entire function. OSR
   12405             :         // can introduce a phi, but this phi will be specialized.
   12406           0 :         current->pushSlot(info().thisSlot());
   12407           0 :         return Ok();
   12408             :     }
   12409             : 
   12410             :     // If we are doing an analysis, we might not yet know the type of |this|.
   12411             :     // Instead of bailing out just push the |this| slot, as this code won't
   12412             :     // actually execute and it does not matter whether |this| is primitive.
   12413           0 :     if (info().isAnalysis()) {
   12414           2 :         current->pushSlot(info().thisSlot());
   12415           1 :         return Ok();
   12416             :     }
   12417             : 
   12418             :     // Hard case: |this| may be a primitive we have to wrap.
   12419           0 :     MDefinition* def = current->getSlot(info().thisSlot());
   12420             : 
   12421           0 :     if (def->type() == MIRType::Object) {
   12422           0 :         current->push(def);
   12423           0 :         return Ok();
   12424             :     }
   12425             : 
   12426             :     // Beyond this point we may need to access non-syntactic global. Ion doesn't
   12427             :     // currently support this so just abort.
   12428           0 :     if (script()->hasNonSyntacticScope())
   12429           0 :         return abort(AbortReason::Disable, "JSOP_FUNCTIONTHIS would need non-syntactic global");
   12430             : 
   12431           0 :     if (IsNullOrUndefined(def->type())) {
   12432           0 :         LexicalEnvironmentObject* globalLexical = &script()->global().lexicalEnvironment();
   12433           0 :         pushConstant(globalLexical->thisValue());
   12434           0 :         return Ok();
   12435             :     }
   12436             : 
   12437           0 :     MComputeThis* thisObj = MComputeThis::New(alloc(), def);
   12438           0 :     current->add(thisObj);
   12439           0 :     current->push(thisObj);
   12440             : 
   12441             :     return resumeAfter(thisObj);
   12442             : }
   12443             : 
   12444             : AbortReasonOr<Ok>
   12445           0 : IonBuilder::jsop_globalthis()
   12446             : {
   12447           0 :     if (script()->hasNonSyntacticScope()) {
   12448             :         // Ion does not compile global scripts with a non-syntactic scope, but
   12449             :         // we can end up here when we're compiling an arrow function.
   12450           0 :         return abort(AbortReason::Disable, "JSOP_GLOBALTHIS in script with non-syntactic scope");
   12451             :     }
   12452             : 
   12453           0 :     LexicalEnvironmentObject* globalLexical = &script()->global().lexicalEnvironment();
   12454           0 :     pushConstant(globalLexical->thisValue());
   12455           0 :     return Ok();
   12456             : }
   12457             : 
   12458             : AbortReasonOr<Ok>
   12459           0 : IonBuilder::jsop_typeof()
   12460             : {
   12461          45 :     MDefinition* input = current->pop();
   12462           0 :     MTypeOf* ins = MTypeOf::New(alloc(), input, input->type());
   12463             : 
   12464           0 :     ins->cacheInputMaybeCallableOrEmulatesUndefined(constraints());
   12465             : 
   12466          45 :     current->add(ins);
   12467           0 :     current->push(ins);
   12468             : 
   12469          45 :     return Ok();
   12470             : }
   12471             : 
   12472             : AbortReasonOr<Ok>
   12473           0 : IonBuilder::jsop_toasync()
   12474             : {
   12475           0 :     MDefinition* unwrapped = current->pop();
   12476           0 :     MOZ_ASSERT(unwrapped->type() == MIRType::Object);
   12477             : 
   12478           0 :     MToAsync* ins = MToAsync::New(alloc(), unwrapped);
   12479             : 
   12480           0 :     current->add(ins);
   12481           0 :     current->push(ins);
   12482             : 
   12483           0 :     return resumeAfter(ins);
   12484             : }
   12485             : 
   12486             : AbortReasonOr<Ok>
   12487           0 : IonBuilder::jsop_toasyncgen()
   12488             : {
   12489           0 :     MDefinition* unwrapped = current->pop();
   12490           0 :     MOZ_ASSERT(unwrapped->type() == MIRType::Object);
   12491             : 
   12492           0 :     MToAsyncGen* ins = MToAsyncGen::New(alloc(), unwrapped);
   12493             : 
   12494           0 :     current->add(ins);
   12495           0 :     current->push(ins);
   12496             : 
   12497           0 :     return resumeAfter(ins);
   12498             : }
   12499             : 
   12500             : AbortReasonOr<Ok>
   12501           0 : IonBuilder::jsop_toasynciter()
   12502             : {
   12503           0 :     MDefinition* nextMethod = current->pop();
   12504           0 :     MDefinition* iterator = current->pop();
   12505           0 :     MOZ_ASSERT(iterator->type() == MIRType::Object);
   12506             : 
   12507           0 :     MToAsyncIter* ins = MToAsyncIter::New(alloc(), iterator, nextMethod);
   12508             : 
   12509           0 :     current->add(ins);
   12510           0 :     current->push(ins);
   12511             : 
   12512           0 :     return resumeAfter(ins);
   12513             : }
   12514             : 
   12515             : AbortReasonOr<Ok>
   12516           1 : IonBuilder::jsop_toid()
   12517             : {
   12518             :     // No-op if the index is trivally convertable to an id.
   12519           0 :     MIRType type = current->peek(-1)->type();
   12520           1 :     if (type == MIRType::Int32 || type == MIRType::String || type == MIRType::Symbol)
   12521           0 :         return Ok();
   12522             : 
   12523           0 :     MDefinition* index = current->pop();
   12524           0 :     MToId* ins = MToId::New(alloc(), index);
   12525             : 
   12526           0 :     current->add(ins);
   12527           0 :     current->push(ins);
   12528             : 
   12529             :     return resumeAfter(ins);
   12530             : }
   12531             : 
   12532             : AbortReasonOr<Ok>
   12533           0 : IonBuilder::jsop_iter()
   12534             : {
   12535           0 :     MDefinition* obj = current->pop();
   12536           0 :     MInstruction* ins = MGetIteratorCache::New(alloc(), obj);
   12537             : 
   12538           0 :     if (!outermostBuilder()->iterators_.append(ins))
   12539           0 :         return abort(AbortReason::Alloc);
   12540             : 
   12541           0 :     current->add(ins);
   12542           0 :     current->push(ins);
   12543             : 
   12544           0 :     return resumeAfter(ins);
   12545             : }
   12546             : 
   12547             : AbortReasonOr<Ok>
   12548           0 : IonBuilder::jsop_itermore()
   12549             : {
   12550           0 :     MDefinition* iter = current->peek(-1);
   12551           0 :     MInstruction* ins = MIteratorMore::New(alloc(), iter);
   12552             : 
   12553           0 :     current->add(ins);
   12554           0 :     current->push(ins);
   12555             : 
   12556           0 :     return resumeAfter(ins);
   12557             : }
   12558             : 
   12559             : AbortReasonOr<Ok>
   12560           0 : IonBuilder::jsop_isnoiter()
   12561             : {
   12562           0 :     MDefinition* def = current->peek(-1);
   12563           0 :     MOZ_ASSERT(def->isIteratorMore());
   12564             : 
   12565           0 :     MInstruction* ins = MIsNoIter::New(alloc(), def);
   12566           0 :     current->add(ins);
   12567           0 :     current->push(ins);
   12568             : 
   12569           0 :     return Ok();
   12570             : }
   12571             : 
   12572             : AbortReasonOr<Ok>
   12573           0 : IonBuilder::jsop_iterend()
   12574             : {
   12575           0 :     MDefinition* iter = current->pop();
   12576           0 :     MInstruction* ins = MIteratorEnd::New(alloc(), iter);
   12577             : 
   12578           0 :     current->add(ins);
   12579             : 
   12580           0 :     return resumeAfter(ins);
   12581             : }
   12582             : 
   12583             : AbortReasonOr<Ok>
   12584           0 : IonBuilder::jsop_iternext()
   12585             : {
   12586           0 :     MDefinition* def = current->pop();
   12587           0 :     MOZ_ASSERT(def->type() == MIRType::Value);
   12588             : 
   12589             :     // The value must be a string.
   12590           0 :     MInstruction* unbox = MUnbox::New(alloc(), def, MIRType::String, MUnbox::Infallible);
   12591           0 :     current->add(unbox);
   12592           0 :     current->push(unbox);
   12593             : 
   12594           0 :     return Ok();
   12595             : }
   12596             : 
   12597             : MDefinition*
   12598           0 : IonBuilder::walkEnvironmentChain(unsigned hops)
   12599             : {
   12600           0 :     MDefinition* env = current->getSlot(info().environmentChainSlot());
   12601             : 
   12602           0 :     for (unsigned i = 0; i < hops; i++) {
   12603           0 :         MInstruction* ins = MEnclosingEnvironment::New(alloc(), env);
   12604          11 :         current->add(ins);
   12605          11 :         env = ins;
   12606             :     }
   12607             : 
   12608         211 :     return env;
   12609             : }
   12610             : 
   12611             : bool
   12612           0 : IonBuilder::hasStaticEnvironmentObject(JSObject** pcall)
   12613             : {
   12614         209 :     JSScript* outerScript = EnvironmentCoordinateFunctionScript(script(), pc);
   12615         415 :     if (!outerScript || !outerScript->treatAsRunOnce())
   12616             :         return false;
   12617             : 
   12618             :     TypeSet::ObjectKey* funKey =
   12619           0 :         TypeSet::ObjectKey::get(outerScript->functionNonDelazifying());
   12620           0 :     if (funKey->hasFlags(constraints(), OBJECT_FLAG_RUNONCE_INVALIDATED))
   12621             :         return false;
   12622             : 
   12623             :     // The script this aliased var operation is accessing will run only once,
   12624             :     // so there will be only one call object and the aliased var access can be
   12625             :     // compiled in the same manner as a global access. We still need to find
   12626             :     // the call object though.
   12627             : 
   12628             :     // Look for the call object on the current script's function's env chain.
   12629             :     // If the current script is inner to the outer script and the function has
   12630             :     // singleton type then it should show up here.
   12631             : 
   12632           0 :     MDefinition* envDef = current->getSlot(info().environmentChainSlot());
   12633           0 :     envDef->setImplicitlyUsedUnchecked();
   12634             : 
   12635           0 :     JSObject* environment = script()->functionNonDelazifying()->environment();
   12636           0 :     while (environment && !environment->is<GlobalObject>()) {
   12637           0 :         if (environment->is<CallObject>() &&
   12638           0 :             environment->as<CallObject>().callee().nonLazyScript() == outerScript)
   12639             :         {
   12640           0 :             MOZ_ASSERT(environment->isSingleton());
   12641           0 :             *pcall = environment;
   12642           0 :             return true;
   12643             :         }
   12644           0 :         environment = environment->enclosingEnvironment();
   12645             :     }
   12646             : 
   12647             :     // Look for the call object on the current frame, if we are compiling the
   12648             :     // outer script itself. Don't do this if we are at entry to the outer
   12649             :     // script, as the call object we see will not be the real one --- after
   12650             :     // entering the Ion code a different call object will be created.
   12651             : 
   12652           0 :     if (script() == outerScript && baselineFrame_ && info().osrPc()) {
   12653           0 :         JSObject* singletonScope = baselineFrame_->singletonEnvChain;
   12654           0 :         if (singletonScope &&
   12655           0 :             singletonScope->is<CallObject>() &&
   12656           0 :             singletonScope->as<CallObject>().callee().nonLazyScript() == outerScript)
   12657             :         {
   12658           0 :             MOZ_ASSERT(singletonScope->isSingleton());
   12659           0 :             *pcall = singletonScope;
   12660           0 :             return true;
   12661             :         }
   12662             :     }
   12663             : 
   12664             :     return true;
   12665             : }
   12666             : 
   12667             : MDefinition*
   12668           0 : IonBuilder::getAliasedVar(EnvironmentCoordinate ec)
   12669             : {
   12670           0 :     MDefinition* obj = walkEnvironmentChain(ec.hops());
   12671             : 
   12672         200 :     Shape* shape = EnvironmentCoordinateToEnvironmentShape(script(), pc);
   12673             : 
   12674             :     MInstruction* load;
   12675           0 :     if (shape->numFixedSlots() <= ec.slot()) {
   12676           0 :         MInstruction* slots = MSlots::New(alloc(), obj);
   12677           0 :         current->add(slots);
   12678             : 
   12679           0 :         load = MLoadSlot::New(alloc(), slots, ec.slot() - shape->numFixedSlots());
   12680             :     } else {
   12681         400 :         load = MLoadFixedSlot::New(alloc(), obj, ec.slot());
   12682             :     }
   12683             : 
   12684         200 :     current->add(load);
   12685         200 :     return load;
   12686             : }
   12687             : 
   12688             : AbortReasonOr<Ok>
   12689           0 : IonBuilder::jsop_getaliasedvar(EnvironmentCoordinate ec)
   12690             : {
   12691           0 :     JSObject* call = nullptr;
   12692           0 :     if (hasStaticEnvironmentObject(&call) && call) {
   12693           0 :         PropertyName* name = EnvironmentCoordinateName(envCoordinateNameCache, script(), pc);
   12694           0 :         bool emitted = false;
   12695           0 :         MOZ_TRY(getStaticName(&emitted, call, name, takeLexicalCheck()));
   12696           0 :         if (emitted)
   12697           0 :             return Ok();
   12698             :     }
   12699             : 
   12700             :     // See jsop_checkaliasedlexical.
   12701           0 :     MDefinition* load = takeLexicalCheck();
   12702           0 :     if (!load)
   12703         197 :         load = getAliasedVar(ec);
   12704           0 :     current->push(load);
   12705             : 
   12706         400 :     TemporaryTypeSet* types = bytecodeTypes(pc);
   12707         200 :     return pushTypeBarrier(load, types, BarrierKind::TypeSet);
   12708             : }
   12709             : 
   12710             : AbortReasonOr<Ok>
   12711           0 : IonBuilder::jsop_setaliasedvar(EnvironmentCoordinate ec)
   12712             : {
   12713           0 :     JSObject* call = nullptr;
   12714           0 :     if (hasStaticEnvironmentObject(&call)) {
   12715           0 :         uint32_t depth = current->stackDepth() + 1;
   12716           0 :         if (depth > current->nslots()) {
   12717           0 :             if (!current->increaseSlots(depth - current->nslots()))
   12718           0 :                 return abort(AbortReason::Alloc);
   12719             :         }
   12720           0 :         MDefinition* value = current->pop();
   12721           0 :         PropertyName* name = EnvironmentCoordinateName(envCoordinateNameCache, script(), pc);
   12722             : 
   12723           0 :         if (call) {
   12724             :             // Push the object on the stack to match the bound object expected in
   12725             :             // the global and property set cases.
   12726           0 :             pushConstant(ObjectValue(*call));
   12727           0 :             current->push(value);
   12728           0 :             return setStaticName(call, name);
   12729             :         }
   12730             : 
   12731             :         // The call object has type information we need to respect but we
   12732             :         // couldn't find it. Just do a normal property assign.
   12733           0 :         MDefinition* obj = walkEnvironmentChain(ec.hops());
   12734           0 :         current->push(obj);
   12735           0 :         current->push(value);
   12736           0 :         return jsop_setprop(name);
   12737             :     }
   12738             : 
   12739           9 :     MDefinition* rval = current->peek(-1);
   12740           0 :     MDefinition* obj = walkEnvironmentChain(ec.hops());
   12741             : 
   12742           0 :     Shape* shape = EnvironmentCoordinateToEnvironmentShape(script(), pc);
   12743             : 
   12744           9 :     if (needsPostBarrier(rval))
   12745           4 :         current->add(MPostWriteBarrier::New(alloc(), obj, rval));
   12746             : 
   12747             :     MInstruction* store;
   12748           0 :     if (shape->numFixedSlots() <= ec.slot()) {
   12749           0 :         MInstruction* slots = MSlots::New(alloc(), obj);
   12750           0 :         current->add(slots);
   12751             : 
   12752           0 :         store = MStoreSlot::NewBarriered(alloc(), slots, ec.slot() - shape->numFixedSlots(), rval);
   12753             :     } else {
   12754           9 :         store = MStoreFixedSlot::NewBarriered(alloc(), obj, ec.slot(), rval);
   12755             :     }
   12756             : 
   12757           9 :     current->add(store);
   12758             :     return resumeAfter(store);
   12759             : }
   12760             : 
   12761             : AbortReasonOr<Ok>
   12762           0 : IonBuilder::jsop_in()
   12763             : {
   12764          47 :     MDefinition* obj = convertUnboxedObjects(current->pop());
   12765           0 :     MDefinition* id = current->pop();
   12766             : 
   12767          94 :     if (!forceInlineCaches()) {
   12768           0 :         bool emitted = false;
   12769             : 
   12770           0 :         MOZ_TRY(inTryDense(&emitted, obj, id));
   12771          47 :         if (emitted)
   12772           0 :             return Ok();
   12773             : 
   12774           0 :         MOZ_TRY(hasTryNotDefined(&emitted, obj, id, /* ownProperty = */ false));
   12775          47 :         if (emitted)
   12776           0 :             return Ok();
   12777             : 
   12778           0 :         MOZ_TRY(hasTryDefiniteSlotOrUnboxed(&emitted, obj, id));
   12779          47 :         if (emitted)
   12780           0 :             return Ok();
   12781             :     }
   12782             : 
   12783           0 :     MInCache* ins = MInCache::New(alloc(), id, obj);
   12784             : 
   12785          47 :     current->add(ins);
   12786          47 :     current->push(ins);
   12787             : 
   12788             :     return resumeAfter(ins);
   12789             : }
   12790             : 
   12791             : AbortReasonOr<Ok>
   12792           0 : IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
   12793             : {
   12794           0 :     MOZ_ASSERT(!*emitted);
   12795             : 
   12796          47 :     if (shouldAbortOnPreliminaryGroups(obj))
   12797           0 :         return Ok();
   12798             : 
   12799          46 :     if (!ElementAccessIsDenseNative(constraints(), obj, id))
   12800          46 :         return Ok();
   12801             : 
   12802             :     bool hasExtraIndexedProperty;
   12803           0 :     MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
   12804           0 :     if (hasExtraIndexedProperty)
   12805           0 :         return Ok();
   12806             : 
   12807           0 :     *emitted = true;
   12808             : 
   12809           0 :     bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
   12810             : 
   12811             :     // Ensure id is an integer.
   12812           0 :     MInstruction* idInt32 = MToNumberInt32::New(alloc(), id);
   12813           0 :     current->add(idInt32);
   12814           0 :     id = idInt32;
   12815             : 
   12816             :     // Get the elements vector.
   12817           0 :     MElements* elements = MElements::New(alloc(), obj);
   12818           0 :     current->add(elements);
   12819             : 
   12820           0 :     MInstruction* initLength = initializedLength(elements);
   12821             : 
   12822             :     // If there are no holes, speculate the InArray check will not fail.
   12823           0 :     if (!needsHoleCheck && !failedBoundsCheck_) {
   12824           0 :         addBoundsCheck(idInt32, initLength);
   12825           0 :         pushConstant(BooleanValue(true));
   12826           0 :         return Ok();
   12827             :     }
   12828             : 
   12829             :     // Check if id < initLength and elem[id] not a hole.
   12830           0 :     MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck);
   12831             : 
   12832           0 :     current->add(ins);
   12833           0 :     current->push(ins);
   12834             : 
   12835           0 :     return Ok();
   12836             : }
   12837             : 
   12838             : AbortReasonOr<Ok>
   12839          48 : IonBuilder::hasTryNotDefined(bool* emitted, MDefinition* obj, MDefinition* id, bool ownProperty)
   12840             : {
   12841             :     // Fold |id in obj| to |false|, if we know the object (or an object on its
   12842             :     // prototype chain) does not have this property.
   12843             : 
   12844           0 :     MOZ_ASSERT(!*emitted);
   12845             : 
   12846           0 :     MConstant* idConst = id->maybeConstantValue();
   12847           0 :     jsid propId;
   12848          48 :     if (!idConst || !ValueToIdPure(idConst->toJSValue(), &propId))
   12849           0 :         return Ok();
   12850             : 
   12851          30 :     if (propId != IdToTypeId(propId))
   12852           0 :         return Ok();
   12853             : 
   12854             :     bool res;
   12855           0 :     MOZ_TRY_VAR(res, testNotDefinedProperty(obj, propId, ownProperty));
   12856          15 :     if (!res)
   12857           0 :         return Ok();
   12858             : 
   12859           0 :     *emitted = true;
   12860             : 
   12861           0 :     pushConstant(BooleanValue(false));
   12862           0 :     obj->setImplicitlyUsedUnchecked();
   12863           0 :     id->setImplicitlyUsedUnchecked();
   12864           0 :     return Ok();
   12865             : }
   12866             : 
   12867             : AbortReasonOr<Ok>
   12868          48 : IonBuilder::hasTryDefiniteSlotOrUnboxed(bool* emitted, MDefinition* obj, MDefinition* id)
   12869             : {
   12870             :     // Fold |id in obj| to |true|, when obj definitely contains a property with
   12871             :     // that name.
   12872           0 :     MOZ_ASSERT(!*emitted);
   12873             : 
   12874          48 :     if (obj->type() != MIRType::Object)
   12875           0 :         return Ok();
   12876             : 
   12877           0 :     MConstant* idConst = id->maybeConstantValue();
   12878           0 :     jsid propId;
   12879          19 :     if (!idConst || !ValueToIdPure(idConst->toJSValue(), &propId))
   12880           0 :         return Ok();
   12881             : 
   12882          30 :     if (propId != IdToTypeId(propId))
   12883           0 :         return Ok();
   12884             : 
   12885             :     // Try finding a native definite slot first.
   12886             :     uint32_t nfixed;
   12887          15 :     uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), propId, &nfixed);
   12888          15 :     if (slot == UINT32_MAX) {
   12889             :         // Check for unboxed object properties next.
   12890             :         JSValueType unboxedType;
   12891           0 :         uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), propId, &unboxedType);
   12892          15 :         if (offset == UINT32_MAX)
   12893          15 :             return Ok();
   12894             :     }
   12895             : 
   12896           0 :     *emitted = true;
   12897             : 
   12898           0 :     pushConstant(BooleanValue(true));
   12899           0 :     obj->setImplicitlyUsedUnchecked();
   12900           0 :     id->setImplicitlyUsedUnchecked();
   12901           0 :     return Ok();
   12902             : }
   12903             : 
   12904             : AbortReasonOr<Ok>
   12905           0 : IonBuilder::jsop_hasown()
   12906             : {
   12907           1 :     MDefinition* obj = convertUnboxedObjects(current->pop());
   12908           0 :     MDefinition* id = current->pop();
   12909             : 
   12910           2 :     if (!forceInlineCaches()) {
   12911           0 :         bool emitted = false;
   12912             : 
   12913           0 :         MOZ_TRY(hasTryNotDefined(&emitted, obj, id, /* ownProperty = */ true));
   12914           1 :         if (emitted)
   12915           0 :             return Ok();
   12916             : 
   12917           0 :         MOZ_TRY(hasTryDefiniteSlotOrUnboxed(&emitted, obj, id));
   12918           1 :         if (emitted)
   12919           0 :             return Ok();
   12920             :     }
   12921             : 
   12922           0 :     MHasOwnCache* ins = MHasOwnCache::New(alloc(), obj, id);
   12923           1 :     current->add(ins);
   12924           0 :     current->push(ins);
   12925             : 
   12926           3 :     MOZ_TRY(resumeAfter(ins));
   12927           1 :     return Ok();
   12928             : }
   12929             : 
   12930             : AbortReasonOr<bool>
   12931           0 : IonBuilder::hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* onProto)
   12932             : {
   12933           6 :     MOZ_ASSERT(protoObject);
   12934             : 
   12935             :     while (true) {
   12936           6 :         if (!alloc().ensureBallast())
   12937           0 :             return abort(AbortReason::Alloc);
   12938             : 
   12939          12 :         if (!key->hasStableClassAndProto(constraints()) || !key->clasp()->isNative())
   12940           0 :             return false;
   12941             : 
   12942           0 :         JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
   12943           0 :         if (!proto) {
   12944           0 :             *onProto = false;
   12945           0 :             return true;
   12946             :         }
   12947             : 
   12948           0 :         if (proto == protoObject) {
   12949           6 :             *onProto = true;
   12950           6 :             return true;
   12951             :         }
   12952             : 
   12953           0 :         key = TypeSet::ObjectKey::get(proto);
   12954           0 :     }
   12955             : 
   12956             :     MOZ_CRASH("Unreachable");
   12957             : }
   12958             : 
   12959             : AbortReasonOr<Ok>
   12960           6 : IonBuilder::tryFoldInstanceOf(bool* emitted, MDefinition* lhs, JSObject* protoObject)
   12961             : {
   12962             :     // Try to fold the js::IsDelegate part of the instanceof operation.
   12963           0 :     MOZ_ASSERT(*emitted == false);
   12964             : 
   12965           0 :     if (!lhs->mightBeType(MIRType::Object)) {
   12966             :         // If the lhs is a primitive, the result is false.
   12967           0 :         lhs->setImplicitlyUsedUnchecked();
   12968           0 :         pushConstant(BooleanValue(false));
   12969           0 :         *emitted = true;
   12970           0 :         return Ok();
   12971             :     }
   12972             : 
   12973           0 :     TemporaryTypeSet* lhsTypes = lhs->resultTypeSet();
   12974          12 :     if (!lhsTypes || lhsTypes->unknownObject())
   12975           0 :         return Ok();
   12976             : 
   12977             :     // We can fold if either all objects have protoObject on their proto chain
   12978             :     // or none have.
   12979             :     bool isFirst = true;
   12980             :     bool knownIsInstance = false;
   12981             : 
   12982           0 :     for (unsigned i = 0; i < lhsTypes->getObjectCount(); i++) {
   12983           1 :         TypeSet::ObjectKey* key = lhsTypes->getObject(i);
   12984           6 :         if (!key)
   12985           0 :             continue;
   12986             : 
   12987             :         bool checkSucceeded;
   12988             :         bool isInstance;
   12989           0 :         MOZ_TRY_VAR(checkSucceeded, hasOnProtoChain(key, protoObject, &isInstance));
   12990           6 :         if (!checkSucceeded)
   12991           0 :             return Ok();
   12992             : 
   12993           0 :         if (isFirst) {
   12994           0 :             knownIsInstance = isInstance;
   12995           6 :             isFirst = false;
   12996           0 :         } else if (knownIsInstance != isInstance) {
   12997             :             // Some of the objects have protoObject on their proto chain and
   12998             :             // others don't, so we can't optimize this.
   12999           0 :             return Ok();
   13000             :         }
   13001             :     }
   13002             : 
   13003           6 :     if (knownIsInstance && lhsTypes->getKnownMIRType() != MIRType::Object) {
   13004             :         // The result is true for all objects, but the lhs might be a primitive.
   13005             :         // We can't fold this completely but we can use a much faster IsObject
   13006             :         // test.
   13007           0 :         MIsObject* isObject = MIsObject::New(alloc(), lhs);
   13008           0 :         current->add(isObject);
   13009           0 :         current->push(isObject);
   13010           6 :         *emitted = true;
   13011           6 :         return Ok();
   13012             :     }
   13013             : 
   13014           0 :     lhs->setImplicitlyUsedUnchecked();
   13015           0 :     pushConstant(BooleanValue(knownIsInstance));
   13016           0 :     *emitted = true;
   13017           0 :     return Ok();
   13018             : }
   13019             : 
   13020             : AbortReasonOr<Ok>
   13021           0 : IonBuilder::jsop_instanceof()
   13022             : {
   13023           0 :     MDefinition* rhs = current->pop();
   13024          11 :     MDefinition* obj = current->pop();
   13025          11 :     bool emitted = false;
   13026             : 
   13027             :     // If this is an 'x instanceof function' operation and we can determine the
   13028             :     // exact function and prototype object being tested for, use a typed path.
   13029             :     do {
   13030           0 :         TemporaryTypeSet* rhsTypes = rhs->resultTypeSet();
   13031          11 :         JSObject* rhsObject = rhsTypes ? rhsTypes->maybeSingleton() : nullptr;
   13032          17 :         if (!rhsObject || !rhsObject->is<JSFunction>() || rhsObject->isBoundFunction())
   13033             :             break;
   13034             : 
   13035             :         // Refuse to optimize anything whose [[Prototype]] isn't Function.prototype
   13036             :         // since we can't guarantee that it uses the default @@hasInstance method.
   13037          12 :         if (rhsObject->hasUncacheableProto() || !rhsObject->hasStaticPrototype())
   13038             :             break;
   13039             : 
   13040          12 :         Value funProto = script()->global().getPrototype(JSProto_Function);
   13041           6 :         if (!funProto.isObject() || rhsObject->staticPrototype() != &funProto.toObject())
   13042             :             break;
   13043             : 
   13044             :         // If the user has supplied their own @@hasInstance method we shouldn't
   13045             :         // clobber it.
   13046           0 :         JSFunction* fun = &rhsObject->as<JSFunction>();
   13047           6 :         const WellKnownSymbols* symbols = &realm->runtime()->wellKnownSymbols();
   13048           6 :         if (!js::FunctionHasDefaultHasInstance(fun, *symbols))
   13049             :             break;
   13050             : 
   13051             :         // Ensure that we will bail if the @@hasInstance property or [[Prototype]]
   13052             :         // change.
   13053           6 :         TypeSet::ObjectKey* rhsKey = TypeSet::ObjectKey::get(rhsObject);
   13054           6 :         if (!rhsKey->hasStableClassAndProto(constraints()))
   13055             :             break;
   13056             : 
   13057           6 :         if (rhsKey->unknownProperties())
   13058             :             break;
   13059             : 
   13060             :         HeapTypeSetKey hasInstanceObject =
   13061          12 :             rhsKey->property(SYMBOL_TO_JSID(symbols->hasInstance));
   13062           6 :         if (hasInstanceObject.isOwnProperty(constraints()))
   13063             :             break;
   13064             : 
   13065             :         HeapTypeSetKey protoProperty =
   13066           0 :             rhsKey->property(NameToId(names().prototype));
   13067           6 :         JSObject* protoObject = protoProperty.singleton(constraints());
   13068           6 :         if (!protoObject)
   13069             :             break;
   13070             : 
   13071           0 :         rhs->setImplicitlyUsedUnchecked();
   13072             : 
   13073           0 :         MOZ_TRY(tryFoldInstanceOf(&emitted, obj, protoObject));
   13074           6 :         if (emitted)
   13075           0 :             return Ok();
   13076             : 
   13077           0 :         MInstanceOf* ins = MInstanceOf::New(alloc(), obj, protoObject);
   13078             : 
   13079           0 :         current->add(ins);
   13080           0 :         current->push(ins);
   13081             : 
   13082             :         return resumeAfter(ins);
   13083             :     } while (false);
   13084             : 
   13085             :     // Try to inline a fast path based on Baseline ICs.
   13086             :     do {
   13087             :         Shape* shape;
   13088             :         uint32_t slot;
   13089             :         JSObject* protoObject;
   13090           5 :         if (!inspector->instanceOfData(pc, &shape, &slot, &protoObject))
   13091             :             break;
   13092             : 
   13093             :         // Shape guard.
   13094           0 :         rhs = addShapeGuard(rhs, shape, Bailout_ShapeGuard);
   13095             : 
   13096             :         // Guard .prototype == protoObject.
   13097           0 :         MOZ_ASSERT(shape->numFixedSlots() == 0, "Must be a dynamic slot");
   13098           0 :         MSlots* slots = MSlots::New(alloc(), rhs);
   13099           0 :         current->add(slots);
   13100           0 :         MLoadSlot* prototype = MLoadSlot::New(alloc(), slots, slot);
   13101           0 :         current->add(prototype);
   13102           0 :         MConstant* protoConst = MConstant::NewConstraintlessObject(alloc(), protoObject);
   13103           0 :         current->add(protoConst);
   13104           0 :         MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst,
   13105           0 :                                                                 /* bailOnEquality = */ false);
   13106           0 :         current->add(guard);
   13107             : 
   13108           0 :         MOZ_TRY(tryFoldInstanceOf(&emitted, obj, protoObject));
   13109           0 :         if (emitted)
   13110           0 :             return Ok();
   13111             : 
   13112           0 :         MInstanceOf* ins = MInstanceOf::New(alloc(), obj, protoObject);
   13113           0 :         current->add(ins);
   13114           0 :         current->push(ins);
   13115             :         return resumeAfter(ins);
   13116             :     } while (false);
   13117             : 
   13118           0 :     MInstanceOfCache* ins = MInstanceOfCache::New(alloc(), obj, rhs);
   13119             : 
   13120           5 :     current->add(ins);
   13121           5 :     current->push(ins);
   13122             : 
   13123             :     return resumeAfter(ins);
   13124             : }
   13125             : 
   13126             : AbortReasonOr<Ok>
   13127           0 : IonBuilder::jsop_debugger()
   13128             : {
   13129           0 :     MDebugger* debugger = MDebugger::New(alloc());
   13130           0 :     current->add(debugger);
   13131             : 
   13132             :     // The |debugger;| statement will always bail out to baseline if
   13133             :     // cx->compartment()->isDebuggee(). Resume in-place and have baseline
   13134             :     // handle the details.
   13135           0 :     return resumeAt(debugger, pc);
   13136             : }
   13137             : 
   13138             : AbortReasonOr<Ok>
   13139           0 : IonBuilder::jsop_implicitthis(PropertyName* name)
   13140             : {
   13141           0 :     MOZ_ASSERT(usesEnvironmentChain());
   13142             : 
   13143           0 :     MImplicitThis* implicitThis = MImplicitThis::New(alloc(), current->environmentChain(), name);
   13144          20 :     current->add(implicitThis);
   13145           0 :     current->push(implicitThis);
   13146             : 
   13147          20 :     return resumeAfter(implicitThis);
   13148             : }
   13149             : 
   13150             : AbortReasonOr<Ok>
   13151           0 : IonBuilder::jsop_importmeta()
   13152             : {
   13153           0 :     ModuleObject* module = GetModuleObjectForScript(script());
   13154           0 :     MOZ_ASSERT(module);
   13155             : 
   13156             :     // The object must have been created already when we compiled for baseline.
   13157           0 :     JSObject* metaObject = module->metaObject();
   13158           0 :     MOZ_ASSERT(metaObject);
   13159             : 
   13160           0 :     pushConstant(ObjectValue(*metaObject));
   13161             : 
   13162           0 :     return Ok();
   13163             : }
   13164             : 
   13165             : MInstruction*
   13166           0 : IonBuilder::addConvertElementsToDoubles(MDefinition* elements)
   13167             : {
   13168           0 :     MInstruction* convert = MConvertElementsToDoubles::New(alloc(), elements);
   13169           0 :     current->add(convert);
   13170           0 :     return convert;
   13171             : }
   13172             : 
   13173             : MDefinition*
   13174           0 : IonBuilder::addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
   13175             : {
   13176           0 :     if (!ElementAccessMightBeCopyOnWrite(constraints(), object))
   13177             :         return object;
   13178           0 :     MInstruction* copy = MMaybeCopyElementsForWrite::New(alloc(), object, checkNative);
   13179          12 :     current->add(copy);
   13180          12 :     return copy;
   13181             : }
   13182             : 
   13183             : MInstruction*
   13184           0 : IonBuilder::addBoundsCheck(MDefinition* index, MDefinition* length)
   13185             : {
   13186         196 :     MInstruction* check = MBoundsCheck::New(alloc(), index, length);
   13187          98 :     current->add(check);
   13188             : 
   13189             :     // If a bounds check failed in the past, don't optimize bounds checks.
   13190          98 :     if (failedBoundsCheck_)
   13191           0 :         check->setNotMovable();
   13192             : 
   13193          98 :     if (JitOptions.spectreIndexMasking) {
   13194             :         // Use a separate MIR instruction for the index masking. Doing this as
   13195             :         // part of MBoundsCheck would be unsound because bounds checks can be
   13196             :         // optimized or eliminated completely. Consider this:
   13197             :         //
   13198             :         //   for (var i = 0; i < x; i++)
   13199             :         //        res = arr[i];
   13200             :         //
   13201             :         // If we can prove |x < arr.length|, we are able to eliminate the bounds
   13202             :         // check, but we should not get rid of the index masking because the
   13203             :         // |i < x| branch could still be mispredicted.
   13204             :         //
   13205             :         // Using a separate instruction lets us eliminate the bounds check
   13206             :         // without affecting the index masking.
   13207         196 :         check = MSpectreMaskIndex::New(alloc(), check, length);
   13208          98 :         current->add(check);
   13209             :     }
   13210             : 
   13211          98 :     return check;
   13212             : }
   13213             : 
   13214             : MInstruction*
   13215           0 : IonBuilder::addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind)
   13216             : {
   13217         236 :     MGuardShape* guard = MGuardShape::New(alloc(), obj, shape, bailoutKind);
   13218         118 :     current->add(guard);
   13219             : 
   13220             :     // If a shape guard failed in the past, don't optimize shape guard.
   13221         118 :     if (failedShapeGuard_)
   13222           0 :         guard->setNotMovable();
   13223             : 
   13224         118 :     return guard;
   13225             : }
   13226             : 
   13227             : MInstruction*
   13228           0 : IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind)
   13229             : {
   13230           0 :     MGuardObjectGroup* guard = MGuardObjectGroup::New(alloc(), obj, group,
   13231           0 :                                                       /* bailOnEquality = */ false, bailoutKind);
   13232           0 :     current->add(guard);
   13233             : 
   13234             :     // If a shape guard failed in the past, don't optimize group guards.
   13235           0 :     if (failedShapeGuard_)
   13236           0 :         guard->setNotMovable();
   13237             : 
   13238           0 :     LifoAlloc* lifoAlloc = alloc().lifoAlloc();
   13239           0 :     guard->setResultTypeSet(lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc,
   13240           0 :                                                             TypeSet::ObjectType(group)));
   13241             : 
   13242           0 :     return guard;
   13243             : }
   13244             : 
   13245             : MInstruction*
   13246           0 : IonBuilder::addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind)
   13247             : {
   13248           0 :     MGuardUnboxedExpando* guard = MGuardUnboxedExpando::New(alloc(), obj, hasExpando, bailoutKind);
   13249           0 :     current->add(guard);
   13250             : 
   13251             :     // If a shape guard failed in the past, don't optimize group guards.
   13252           0 :     if (failedShapeGuard_)
   13253           0 :         guard->setNotMovable();
   13254             : 
   13255           0 :     return guard;
   13256             : }
   13257             : 
   13258             : MInstruction*
   13259          35 : IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj,
   13260             :                                         const BaselineInspector::ReceiverVector& receivers)
   13261             : {
   13262          35 :     if (receivers.length() == 1) {
   13263           0 :         if (!receivers[0].group) {
   13264             :             // Monomorphic guard on a native object.
   13265          33 :             return addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
   13266             :         }
   13267             : 
   13268           0 :         if (!receivers[0].shape) {
   13269             :             // Guard on an unboxed object that does not have an expando.
   13270           0 :             obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
   13271           0 :             return addUnboxedExpandoGuard(obj, /* hasExpando = */ false, Bailout_ShapeGuard);
   13272             :         }
   13273             : 
   13274             :         // Monomorphic receiver guards are not yet supported when the receiver
   13275             :         // is an unboxed object with an expando.
   13276             :     }
   13277             : 
   13278           2 :     MGuardReceiverPolymorphic* guard = MGuardReceiverPolymorphic::New(alloc(), obj);
   13279           0 :     current->add(guard);
   13280             : 
   13281           2 :     if (failedShapeGuard_)
   13282           0 :         guard->setNotMovable();
   13283             : 
   13284          10 :     for (size_t i = 0; i < receivers.length(); i++) {
   13285           8 :         if (!guard->addReceiver(receivers[i]))
   13286             :             return nullptr;
   13287             :     }
   13288             : 
   13289             :     return guard;
   13290             : }
   13291             : 
   13292             : MInstruction*
   13293           0 : IonBuilder::addSharedTypedArrayGuard(MDefinition* obj)
   13294             : {
   13295           0 :     MGuardSharedTypedArray* guard = MGuardSharedTypedArray::New(alloc(), obj);
   13296           0 :     current->add(guard);
   13297           0 :     return guard;
   13298             : }
   13299             : 
   13300             : TemporaryTypeSet*
   13301           0 : IonBuilder::bytecodeTypes(jsbytecode* pc)
   13302             : {
   13303       10170 :     return TypeScript::BytecodeTypes(script(), pc, bytecodeTypeMap, &typeArrayHint, typeArray);
   13304             : }
   13305             : 
   13306             : TypedObjectPrediction
   13307        1378 : IonBuilder::typedObjectPrediction(MDefinition* typedObj)
   13308             : {
   13309             :     // Extract TypedObjectPrediction directly if we can
   13310        1378 :     if (typedObj->isNewDerivedTypedObject()) {
   13311           0 :         return typedObj->toNewDerivedTypedObject()->prediction();
   13312             :     }
   13313             : 
   13314        1378 :     TemporaryTypeSet* types = typedObj->resultTypeSet();
   13315        1378 :     return typedObjectPrediction(types);
   13316             : }
   13317             : 
   13318             : TypedObjectPrediction
   13319        1378 : IonBuilder::typedObjectPrediction(TemporaryTypeSet* types)
   13320             : {
   13321             :     // Type set must be known to be an object.
   13322        1378 :     if (!types || types->getKnownMIRType() != MIRType::Object)
   13323             :         return TypedObjectPrediction();
   13324             : 
   13325             :     // And only known objects.
   13326        2680 :     if (types->unknownObject())
   13327             :         return TypedObjectPrediction();
   13328             : 
   13329           0 :     TypedObjectPrediction out;
   13330           0 :     for (uint32_t i = 0; i < types->getObjectCount(); i++) {
   13331         750 :         ObjectGroup* group = types->getGroup(i);
   13332        1468 :         if (!group || !IsTypedObjectClass(group->clasp()))
   13333             :             return TypedObjectPrediction();
   13334             : 
   13335           0 :         if (!TypeSet::ObjectKey::get(group)->hasStableClassAndProto(constraints()))
   13336             :             return TypedObjectPrediction();
   13337             : 
   13338           0 :         out.addDescr(group->typeDescr());
   13339             :     }
   13340             : 
   13341           0 :     return out;
   13342             : }
   13343             : 
   13344             : MDefinition*
   13345           0 : IonBuilder::loadTypedObjectType(MDefinition* typedObj)
   13346             : {
   13347             :     // Shortcircuit derived type objects, meaning the intermediate
   13348             :     // objects created to represent `a.b` in an expression like
   13349             :     // `a.b.c`. In that case, the type object can be simply pulled
   13350             :     // from the operands of that instruction.
   13351           0 :     if (typedObj->isNewDerivedTypedObject())
   13352           0 :         return typedObj->toNewDerivedTypedObject()->type();
   13353             : 
   13354           0 :     MInstruction* descr = MTypedObjectDescr::New(alloc(), typedObj);
   13355           0 :     current->add(descr);
   13356             : 
   13357           0 :     return descr;
   13358             : }
   13359             : 
   13360             : // Given a typed object `typedObj` and an offset `offset` into that
   13361             : // object's data, returns another typed object and adusted offset
   13362             : // where the data can be found. Often, these returned values are the
   13363             : // same as the inputs, but in cases where intermediate derived type
   13364             : // objects have been created, the return values will remove
   13365             : // intermediate layers (often rendering those derived type objects
   13366             : // into dead code).
   13367             : AbortReasonOr<Ok>
   13368           0 : IonBuilder::loadTypedObjectData(MDefinition* typedObj,
   13369             :                                 MDefinition** owner,
   13370             :                                 LinearSum* ownerOffset)
   13371             : {
   13372           0 :     MOZ_ASSERT(typedObj->type() == MIRType::Object);
   13373             : 
   13374             :     // Shortcircuit derived type objects, meaning the intermediate
   13375             :     // objects created to represent `a.b` in an expression like
   13376             :     // `a.b.c`. In that case, the owned and a base offset can be
   13377             :     // pulled from the operands of the instruction and combined with
   13378             :     // `offset`.
   13379           0 :     if (typedObj->isNewDerivedTypedObject()) {
   13380           0 :         MNewDerivedTypedObject* ins = typedObj->toNewDerivedTypedObject();
   13381             : 
   13382           0 :         SimpleLinearSum base = ExtractLinearSum(ins->offset());
   13383           0 :         if (!ownerOffset->add(base))
   13384           0 :             return abort(AbortReason::Disable, "Overflow/underflow on type object offset.");
   13385             : 
   13386           0 :         *owner = ins->owner();
   13387           0 :         return Ok();
   13388             :     }
   13389             : 
   13390           0 :     *owner = typedObj;
   13391           0 :     return Ok();
   13392             : }
   13393             : 
   13394             : // Takes as input a typed object, an offset into that typed object's
   13395             : // memory, and the type repr of the data found at that offset. Returns
   13396             : // the elements pointer and a scaled offset. The scaled offset is
   13397             : // expressed in units of `unit`; when working with typed array MIR,
   13398             : // this is typically the alignment.
   13399             : AbortReasonOr<Ok>
   13400           0 : IonBuilder::loadTypedObjectElements(MDefinition* typedObj,
   13401             :                                     const LinearSum& baseByteOffset,
   13402             :                                     uint32_t scale,
   13403             :                                     MDefinition** ownerElements,
   13404             :                                     MDefinition** ownerScaledOffset,
   13405             :                                     int32_t* ownerByteAdjustment)
   13406             : {
   13407             :     MDefinition* owner;
   13408           0 :     LinearSum ownerByteOffset(alloc());
   13409           0 :     MOZ_TRY(loadTypedObjectData(typedObj, &owner, &ownerByteOffset));
   13410             : 
   13411           0 :     if (!ownerByteOffset.add(baseByteOffset))
   13412           0 :         return abort(AbortReason::Disable, "Overflow after adding the base offset.");
   13413             : 
   13414           0 :     TemporaryTypeSet* ownerTypes = owner->resultTypeSet();
   13415           0 :     const Class* clasp = ownerTypes ? ownerTypes->getKnownClass(constraints()) : nullptr;
   13416           0 :     if (clasp && IsInlineTypedObjectClass(clasp)) {
   13417             :         // Perform the load directly from the owner pointer.
   13418           0 :         if (!ownerByteOffset.add(InlineTypedObject::offsetOfDataStart()))
   13419           0 :             return abort(AbortReason::Disable, "Overflow after adding the data start.");
   13420           0 :         *ownerElements = owner;
   13421             :     } else {
   13422           0 :         bool definitelyOutline = clasp && IsOutlineTypedObjectClass(clasp);
   13423           0 :         *ownerElements = MTypedObjectElements::New(alloc(), owner, definitelyOutline);
   13424           0 :         current->add((*ownerElements)->toInstruction());
   13425             :     }
   13426             : 
   13427             :     // Extract the constant adjustment from the byte offset.
   13428           0 :     *ownerByteAdjustment = ownerByteOffset.constant();
   13429             :     int32_t negativeAdjustment;
   13430           0 :     if (!SafeSub(0, *ownerByteAdjustment, &negativeAdjustment))
   13431           0 :         return abort(AbortReason::Disable);
   13432           0 :     if (!ownerByteOffset.add(negativeAdjustment))
   13433           0 :         return abort(AbortReason::Disable);
   13434             : 
   13435             :     // Scale the byte offset if required by the MIR node which will access the
   13436             :     // typed object. In principle we should always be able to cleanly divide
   13437             :     // the terms in this lienar sum due to alignment restrictions, but due to
   13438             :     // limitations of ExtractLinearSum when applied to the terms in derived
   13439             :     // typed objects this isn't always be possible. In these cases, fall back
   13440             :     // on an explicit division operation.
   13441           0 :     if (ownerByteOffset.divide(scale)) {
   13442           0 :         *ownerScaledOffset = ConvertLinearSum(alloc(), current, ownerByteOffset);
   13443             :     } else {
   13444           0 :         MDefinition* unscaledOffset = ConvertLinearSum(alloc(), current, ownerByteOffset);
   13445           0 :         *ownerScaledOffset = MDiv::New(alloc(), unscaledOffset, constantInt(scale),
   13446             :                                        MIRType::Int32, /* unsigned = */ false);
   13447           0 :         current->add((*ownerScaledOffset)->toInstruction());
   13448             :     }
   13449           0 :     return Ok();
   13450             : }
   13451             : 
   13452             : // Looks up the offset/type-repr-set of the field `id`, given the type
   13453             : // set `objTypes` of the field owner. If a field is found, returns true
   13454             : // and sets *fieldOffset, *fieldPrediction, and *fieldIndex. Returns false
   13455             : // otherwise. Infallible.
   13456             : bool
   13457        1036 : IonBuilder::typedObjectHasField(MDefinition* typedObj,
   13458             :                                 PropertyName* name,
   13459             :                                 size_t* fieldOffset,
   13460             :                                 TypedObjectPrediction* fieldPrediction,
   13461             :                                 size_t* fieldIndex)
   13462             : {
   13463        1036 :     TypedObjectPrediction objPrediction = typedObjectPrediction(typedObj);
   13464        1036 :     if (objPrediction.isUseless()) {
   13465             :         trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject);
   13466             :         return false;
   13467             :     }
   13468             : 
   13469             :     // Must be accessing a struct.
   13470           0 :     if (objPrediction.kind() != type::Struct) {
   13471             :         trackOptimizationOutcome(TrackedOutcome::NotStruct);
   13472             :         return false;
   13473             :     }
   13474             : 
   13475             :     // Determine the type/offset of the field `name`, if any.
   13476           0 :     if (!objPrediction.hasFieldNamed(NameToId(name), fieldOffset,
   13477             :                                      fieldPrediction, fieldIndex))
   13478             :     {
   13479             :         trackOptimizationOutcome(TrackedOutcome::StructNoField);
   13480             :         return false;
   13481             :     }
   13482             : 
   13483             :     return true;
   13484             : }
   13485             : 
   13486             : MDefinition*
   13487           0 : IonBuilder::typeObjectForElementFromArrayStructType(MDefinition* typeObj)
   13488             : {
   13489           0 :     MInstruction* elemType = MLoadFixedSlot::New(alloc(), typeObj, JS_DESCR_SLOT_ARRAY_ELEM_TYPE);
   13490           0 :     current->add(elemType);
   13491             : 
   13492           0 :     MInstruction* unboxElemType = MUnbox::New(alloc(), elemType, MIRType::Object, MUnbox::Infallible);
   13493           0 :     current->add(unboxElemType);
   13494             : 
   13495           0 :     return unboxElemType;
   13496             : }
   13497             : 
   13498             : MDefinition*
   13499           0 : IonBuilder::typeObjectForFieldFromStructType(MDefinition* typeObj,
   13500             :                                              size_t fieldIndex)
   13501             : {
   13502             :     // Load list of field type objects.
   13503             : 
   13504           0 :     MInstruction* fieldTypes = MLoadFixedSlot::New(alloc(), typeObj, JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
   13505           0 :     current->add(fieldTypes);
   13506             : 
   13507           0 :     MInstruction* unboxFieldTypes = MUnbox::New(alloc(), fieldTypes, MIRType::Object, MUnbox::Infallible);
   13508           0 :     current->add(unboxFieldTypes);
   13509             : 
   13510             :     // Index into list with index of field.
   13511             : 
   13512           0 :     MInstruction* fieldTypesElements = MElements::New(alloc(), unboxFieldTypes);
   13513           0 :     current->add(fieldTypesElements);
   13514             : 
   13515           0 :     MConstant* fieldIndexDef = constantInt(fieldIndex);
   13516             : 
   13517           0 :     MInstruction* fieldType = MLoadElement::New(alloc(), fieldTypesElements, fieldIndexDef, false, false);
   13518           0 :     current->add(fieldType);
   13519             : 
   13520           0 :     MInstruction* unboxFieldType = MUnbox::New(alloc(), fieldType, MIRType::Object, MUnbox::Infallible);
   13521           0 :     current->add(unboxFieldType);
   13522             : 
   13523           0 :     return unboxFieldType;
   13524             : }
   13525             : 
   13526             : AbortReasonOr<Ok>
   13527           0 : IonBuilder::setPropTryScalarTypedObjectValue(bool* emitted,
   13528             :                                              MDefinition* typedObj,
   13529             :                                              const LinearSum& byteOffset,
   13530             :                                              ScalarTypeDescr::Type type,
   13531             :                                              MDefinition* value)
   13532             : {
   13533           0 :     MOZ_ASSERT(!*emitted);
   13534             : 
   13535             :     // Find location within the owner object.
   13536             :     MDefinition* elements;
   13537             :     MDefinition* scaledOffset;
   13538             :     int32_t adjustment;
   13539           0 :     uint32_t alignment = ScalarTypeDescr::alignment(type);
   13540           0 :     MOZ_TRY(loadTypedObjectElements(typedObj, byteOffset, alignment,
   13541             :                                     &elements, &scaledOffset, &adjustment));
   13542             : 
   13543             :     // Clamp value to [0, 255] when type is Uint8Clamped
   13544           0 :     MDefinition* toWrite = value;
   13545           0 :     if (type == Scalar::Uint8Clamped) {
   13546           0 :         toWrite = MClampToUint8::New(alloc(), value);
   13547           0 :         current->add(toWrite->toInstruction());
   13548             :     }
   13549             : 
   13550             :     MStoreUnboxedScalar* store =
   13551           0 :         MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, toWrite,
   13552             :                                  type, MStoreUnboxedScalar::TruncateInput,
   13553           0 :                                  DoesNotRequireMemoryBarrier, adjustment);
   13554           0 :     current->add(store);
   13555           0 :     current->push(value);
   13556             : 
   13557           0 :     trackOptimizationSuccess();
   13558           0 :     *emitted = true;
   13559             :     return resumeAfter(store);
   13560             : }
   13561             : 
   13562             : AbortReasonOr<Ok>
   13563           0 : IonBuilder::setPropTryReferenceTypedObjectValue(bool* emitted,
   13564             :                                                 MDefinition* typedObj,
   13565             :                                                 const LinearSum& byteOffset,
   13566             :                                                 ReferenceTypeDescr::Type type,
   13567             :                                                 MDefinition* value,
   13568             :                                                 PropertyName* name)
   13569             : {
   13570           0 :     MOZ_ASSERT(!*emitted);
   13571             : 
   13572             :     // Make sure we aren't adding new type information for writes of object and value
   13573             :     // references.
   13574           0 :     if (type != ReferenceTypeDescr::TYPE_STRING) {
   13575           0 :         MOZ_ASSERT(type == ReferenceTypeDescr::TYPE_ANY ||
   13576             :                    type == ReferenceTypeDescr::TYPE_OBJECT);
   13577             :         MIRType implicitType =
   13578           0 :             (type == ReferenceTypeDescr::TYPE_ANY) ? MIRType::Undefined : MIRType::Null;
   13579             : 
   13580           0 :         if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &typedObj, name, &value,
   13581             :                                           /* canModify = */ true, implicitType))
   13582             :         {
   13583           0 :             trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
   13584           0 :             return Ok();
   13585             :         }
   13586             :     }
   13587             : 
   13588             :     // Find location within the owner object.
   13589             :     MDefinition* elements;
   13590             :     MDefinition* scaledOffset;
   13591             :     int32_t adjustment;
   13592           0 :     uint32_t alignment = ReferenceTypeDescr::alignment(type);
   13593           0 :     MOZ_TRY(loadTypedObjectElements(typedObj, byteOffset, alignment,
   13594             :                                     &elements, &scaledOffset, &adjustment));
   13595             : 
   13596           0 :     MInstruction* store = nullptr;  // initialize to silence GCC warning
   13597           0 :     switch (type) {
   13598             :       case ReferenceTypeDescr::TYPE_ANY:
   13599           0 :         if (needsPostBarrier(value))
   13600           0 :             current->add(MPostWriteBarrier::New(alloc(), typedObj, value));
   13601           0 :         store = MStoreElement::New(alloc(), elements, scaledOffset, value, false, adjustment);
   13602           0 :         store->toStoreElement()->setNeedsBarrier();
   13603             :         break;
   13604             :       case ReferenceTypeDescr::TYPE_OBJECT:
   13605             :         // Note: We cannot necessarily tell at this point whether a post
   13606             :         // barrier is needed, because the type policy may insert ToObjectOrNull
   13607             :         // instructions later, and those may require a post barrier. Therefore,
   13608             :         // defer the insertion of post barriers to the type policy.
   13609           0 :         store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, typedObj, adjustment);
   13610           0 :         break;
   13611             :       case ReferenceTypeDescr::TYPE_STRING:
   13612             :         // See previous comment. The StoreUnboxedString type policy may insert
   13613             :         // ToString instructions that require a post barrier.
   13614           0 :         store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, typedObj, adjustment);
   13615           0 :         break;
   13616             :     }
   13617             : 
   13618           0 :     current->add(store);
   13619           0 :     current->push(value);
   13620             : 
   13621           0 :     trackOptimizationSuccess();
   13622           0 :     *emitted = true;
   13623             :     return resumeAfter(store);
   13624             : }
   13625             : 
   13626             : JSObject*
   13627        3836 : IonBuilder::checkNurseryObject(JSObject* obj)
   13628             : {
   13629             :     // If we try to use any nursery pointers during compilation, make sure that
   13630             :     // the main thread will cancel this compilation before performing a minor
   13631             :     // GC. All constants used during compilation should either go through this
   13632             :     // function or should come from a type set (which has a similar barrier).
   13633           0 :     if (obj && IsInsideNursery(obj)) {
   13634           0 :         realm->zone()->setMinorGCShouldCancelIonCompilations();
   13635           0 :         IonBuilder* builder = this;
   13636           0 :         while (builder) {
   13637           0 :             builder->setNotSafeForMinorGC();
   13638           0 :             builder = builder->callerBuilder_;
   13639             :         }
   13640             :     }
   13641             : 
   13642        3836 :     return obj;
   13643             : }
   13644             : 
   13645             : MConstant*
   13646           0 : IonBuilder::constant(const Value& v)
   13647             : {
   13648       10441 :     MOZ_ASSERT(!v.isString() || v.toString()->isAtom(),
   13649             :                "Handle non-atomized strings outside IonBuilder.");
   13650             : 
   13651        9399 :     if (v.isObject())
   13652           0 :         checkNurseryObject(&v.toObject());
   13653             : 
   13654           0 :     MConstant* c = MConstant::New(alloc(), v, constraints());
   13655        9399 :     current->add(c);
   13656        9399 :     return c;
   13657             : }
   13658             : 
   13659             : MConstant*
   13660           0 : IonBuilder::constantInt(int32_t i)
   13661             : {
   13662          12 :     return constant(Int32Value(i));
   13663             : }
   13664             : 
   13665             : MInstruction*
   13666           0 : IonBuilder::initializedLength(MDefinition* elements)
   13667             : {
   13668           0 :     MInstruction* res = MInitializedLength::New(alloc(), elements);
   13669          96 :     current->add(res);
   13670          96 :     return res;
   13671             : }
   13672             : 
   13673             : MInstruction*
   13674           0 : IonBuilder::setInitializedLength(MDefinition* obj, size_t count)
   13675             : {
   13676           0 :     MOZ_ASSERT(count);
   13677             : 
   13678             :     // MSetInitializedLength takes the index of the last element, rather
   13679             :     // than the count itself.
   13680           0 :     MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false);
   13681           0 :     current->add(elements);
   13682             :     MInstruction* res =
   13683           0 :         MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1)));
   13684           0 :     current->add(res);
   13685           0 :     return res;
   13686             : }
   13687             : 
   13688             : MDefinition*
   13689           0 : IonBuilder::getCallee()
   13690             : {
   13691           0 :     if (inliningDepth_ == 0) {
   13692           0 :         MInstruction* callee = MCallee::New(alloc());
   13693           8 :         current->add(callee);
   13694           8 :         return callee;
   13695             :     }
   13696             : 
   13697           0 :     return inlineCallInfo_->fun();
   13698             : }
   13699             : 
   13700             : AbortReasonOr<MDefinition*>
   13701           0 : IonBuilder::addLexicalCheck(MDefinition* input)
   13702             : {
   13703           3 :     MOZ_ASSERT(JSOp(*pc) == JSOP_CHECKLEXICAL ||
   13704             :                JSOp(*pc) == JSOP_CHECKALIASEDLEXICAL ||
   13705             :                JSOp(*pc) == JSOP_GETIMPORT);
   13706             : 
   13707             :     MInstruction* lexicalCheck;
   13708             : 
   13709             :     // If we're guaranteed to not be JS_UNINITIALIZED_LEXICAL, no need to check.
   13710           3 :     if (input->type() == MIRType::MagicUninitializedLexical) {
   13711             :         // Mark the input as implicitly used so the JS_UNINITIALIZED_LEXICAL
   13712             :         // magic value will be preserved on bailout.
   13713           0 :         input->setImplicitlyUsedUnchecked();
   13714           0 :         lexicalCheck = MThrowRuntimeLexicalError::New(alloc(), JSMSG_UNINITIALIZED_LEXICAL);
   13715           0 :         current->add(lexicalCheck);
   13716           0 :         MOZ_TRY(resumeAfter(lexicalCheck));
   13717           0 :         return constant(UndefinedValue());
   13718             :     }
   13719             : 
   13720           0 :     if (input->type() == MIRType::Value) {
   13721           0 :         lexicalCheck = MLexicalCheck::New(alloc(), input);
   13722           0 :         current->add(lexicalCheck);
   13723           0 :         if (failedLexicalCheck_)
   13724           0 :             lexicalCheck->setNotMovableUnchecked();
   13725           3 :         return lexicalCheck;
   13726             :     }
   13727             : 
   13728           0 :     return input;
   13729             : }
   13730             : 
   13731             : MDefinition*
   13732           0 : IonBuilder::convertToBoolean(MDefinition* input)
   13733             : {
   13734             :     // Convert to bool with the '!!' idiom
   13735           0 :     MNot* resultInverted = MNot::New(alloc(), input, constraints());
   13736           0 :     current->add(resultInverted);
   13737           0 :     MNot* result = MNot::New(alloc(), resultInverted, constraints());
   13738           0 :     current->add(result);
   13739             : 
   13740           0 :     return result;
   13741             : }
   13742             : 
   13743             : void
   13744           0 : IonBuilder::trace(JSTracer* trc)
   13745             : {
   13746           0 :     if (!realm->runtime()->runtimeMatches(trc->runtime()))
   13747             :         return;
   13748             : 
   13749           0 :     MOZ_ASSERT(rootList_);
   13750           0 :     rootList_->trace(trc);
   13751             : }
   13752             : 
   13753             : size_t
   13754           0 : IonBuilder::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   13755             : {
   13756             :     // See js::jit::FreeIonBuilder.
   13757             :     // The IonBuilder and most of its contents live in the LifoAlloc we point
   13758             :     // to. Note that this is only true for background IonBuilders.
   13759             : 
   13760           0 :     size_t result = alloc_->lifoAlloc()->sizeOfIncludingThis(mallocSizeOf);
   13761             : 
   13762           0 :     if (backgroundCodegen_)
   13763           0 :         result += mallocSizeOf(backgroundCodegen_);
   13764             : 
   13765             :     return result;
   13766             : }

Generated by: LCOV version 1.13-14-ga5dd952