LCOV - code coverage report
Current view: top level - js/src/jit - IonBuilder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 3263 6684 48.8 %
Date: 2017-07-14 16:53:18 Functions: 221 337 65.6 %
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/SizePrintfMacros.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 "jsopcodeinlines.h"
      29             : #include "jsscriptinlines.h"
      30             : 
      31             : #include "jit/CompileInfo-inl.h"
      32             : #include "jit/shared/Lowering-shared-inl.h"
      33             : #include "vm/EnvironmentObject-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             : 
      45             : using JS::TrackedStrategy;
      46             : using JS::TrackedOutcome;
      47             : using JS::TrackedTypeSite;
      48             : 
      49             : class jit::BaselineFrameInspector
      50             : {
      51             :   public:
      52             :     TypeSet::Type thisType;
      53             :     JSObject* singletonEnvChain;
      54             : 
      55             :     Vector<TypeSet::Type, 4, JitAllocPolicy> argTypes;
      56             :     Vector<TypeSet::Type, 4, JitAllocPolicy> varTypes;
      57             : 
      58          10 :     explicit BaselineFrameInspector(TempAllocator* temp)
      59          10 :       : thisType(TypeSet::UndefinedType()),
      60             :         singletonEnvChain(nullptr),
      61             :         argTypes(*temp),
      62          10 :         varTypes(*temp)
      63          10 :     {}
      64             : };
      65             : 
      66             : BaselineFrameInspector*
      67          10 : jit::NewBaselineFrameInspector(TempAllocator* temp, BaselineFrame* frame, CompileInfo* info)
      68             : {
      69          10 :     MOZ_ASSERT(frame);
      70             : 
      71          10 :     BaselineFrameInspector* inspector = temp->lifoAlloc()->new_<BaselineFrameInspector>(temp);
      72          10 :     if (!inspector)
      73           0 :         return nullptr;
      74             : 
      75             :     // Note: copying the actual values into a temporary structure for use
      76             :     // during compilation could capture nursery pointers, so the values' types
      77             :     // are recorded instead.
      78             : 
      79          10 :     if (frame->isFunctionFrame())
      80          10 :         inspector->thisType = TypeSet::GetMaybeUntrackedValueType(frame->thisArgument());
      81             : 
      82          10 :     if (frame->environmentChain()->isSingleton())
      83           5 :         inspector->singletonEnvChain = frame->environmentChain();
      84             : 
      85          10 :     JSScript* script = frame->script();
      86             : 
      87          10 :     if (script->functionNonDelazifying()) {
      88          10 :         if (!inspector->argTypes.reserve(frame->numFormalArgs()))
      89           0 :             return nullptr;
      90          32 :         for (size_t i = 0; i < frame->numFormalArgs(); i++) {
      91          22 :             if (script->formalIsAliased(i)) {
      92           3 :                 inspector->argTypes.infallibleAppend(TypeSet::UndefinedType());
      93          19 :             } else if (!script->argsObjAliasesFormals()) {
      94             :                 TypeSet::Type type =
      95          19 :                     TypeSet::GetMaybeUntrackedValueType(frame->unaliasedFormal(i));
      96          19 :                 inspector->argTypes.infallibleAppend(type);
      97           0 :             } else if (frame->hasArgsObj()) {
      98             :                 TypeSet::Type type =
      99           0 :                     TypeSet::GetMaybeUntrackedValueType(frame->argsObj().arg(i));
     100           0 :                 inspector->argTypes.infallibleAppend(type);
     101             :             } else {
     102           0 :                 inspector->argTypes.infallibleAppend(TypeSet::UndefinedType());
     103             :             }
     104             :         }
     105             :     }
     106             : 
     107          10 :     if (!inspector->varTypes.reserve(frame->numValueSlots()))
     108           0 :         return nullptr;
     109          83 :     for (size_t i = 0; i < frame->numValueSlots(); i++) {
     110          73 :         TypeSet::Type type = TypeSet::GetMaybeUntrackedValueType(*frame->valueSlot(i));
     111          73 :         inspector->varTypes.infallibleAppend(type);
     112             :     }
     113             : 
     114          10 :     return inspector;
     115             : }
     116             : 
     117         179 : IonBuilder::IonBuilder(JSContext* analysisContext, CompileCompartment* comp,
     118             :                        const JitCompileOptions& options, TempAllocator* temp,
     119             :                        MIRGraph* graph, CompilerConstraintList* constraints,
     120             :                        BaselineInspector* inspector, CompileInfo* info,
     121             :                        const OptimizationInfo* optimizationInfo,
     122             :                        BaselineFrameInspector* baselineFrame, size_t inliningDepth,
     123         179 :                        uint32_t loopDepth)
     124             :   : MIRGenerator(comp, options, temp, graph, info, optimizationInfo),
     125             :     backgroundCodegen_(nullptr),
     126             :     actionableAbortScript_(nullptr),
     127             :     actionableAbortPc_(nullptr),
     128             :     actionableAbortMessage_(nullptr),
     129             :     rootList_(nullptr),
     130             :     analysisContext(analysisContext),
     131             :     baselineFrame_(baselineFrame),
     132             :     constraints_(constraints),
     133             :     analysis_(*temp, info->script()),
     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         179 :     failedBoundsCheck_(info->script()->failedBoundsCheck()),
     158         179 :     failedShapeGuard_(info->script()->failedShapeGuard()),
     159         179 :     failedLexicalCheck_(info->script()->failedLexicalCheck()),
     160             :     nonStringIteration_(false),
     161             : #ifdef DEBUG
     162             :     hasLazyArguments_(false),
     163             : #endif
     164             :     inlineCallInfo_(nullptr),
     165         716 :     maybeFallbackFunctionGetter_(nullptr)
     166             : {
     167         179 :     script_ = info->script();
     168         179 :     scriptHasIonScript_ = script_->hasIonScript();
     169         179 :     pc = info->startPC();
     170             : 
     171         179 :     MOZ_ASSERT(script()->hasBaselineScript() == (info->analysisMode() != Analysis_ArgumentsUsage));
     172         179 :     MOZ_ASSERT(!!analysisContext == (info->analysisMode() == Analysis_DefiniteProperties));
     173         179 :     MOZ_ASSERT(script_->nTypeSets() < UINT16_MAX);
     174             : 
     175         179 :     if (!info->isAnalysis())
     176          43 :         script()->baselineScript()->setIonCompiledOrInlined();
     177         179 : }
     178             : 
     179             : void
     180          10 : IonBuilder::clearForBackEnd()
     181             : {
     182          10 :     MOZ_ASSERT(!analysisContext);
     183          10 :     baselineFrame_ = nullptr;
     184             : 
     185             :     // The caches below allocate data from the malloc heap. Release this before
     186             :     // later phases of compilation to avoid leaks, as the top level IonBuilder
     187             :     // is not explicitly destroyed. Note that builders for inner scripts are
     188             :     // constructed on the stack and will release this memory on destruction.
     189          10 :     gsn.purge();
     190          10 :     envCoordinateNameCache.purge();
     191          10 : }
     192             : 
     193             : mozilla::GenericErrorResult<AbortReason>
     194           2 : IonBuilder::abort(AbortReason r)
     195             : {
     196           2 :     auto res = this->MIRGenerator::abort(r);
     197             : # ifdef DEBUG
     198           2 :     JitSpew(JitSpew_IonAbort, "aborted @ %s:%d", script()->filename(), PCToLineNumber(script(), pc));
     199             : # else
     200             :     JitSpew(JitSpew_IonAbort, "aborted @ %s", script()->filename());
     201             : # endif
     202           2 :     return res;
     203             : }
     204             : 
     205             : mozilla::GenericErrorResult<AbortReason>
     206           3 : IonBuilder::abort(AbortReason r, const char* message, ...)
     207             : {
     208             :     // Don't call PCToLineNumber in release builds.
     209             :     va_list ap;
     210           3 :     va_start(ap, message);
     211           3 :     auto res = this->MIRGenerator::abortFmt(r, message, ap);
     212           3 :     va_end(ap);
     213             : # ifdef DEBUG
     214           3 :     JitSpew(JitSpew_IonAbort, "aborted @ %s:%d", script()->filename(), PCToLineNumber(script(), pc));
     215             : # else
     216             :     JitSpew(JitSpew_IonAbort, "aborted @ %s", script()->filename());
     217             : # endif
     218           3 :     trackActionableAbort(message);
     219           3 :     return res;
     220             : }
     221             : 
     222             : IonBuilder*
     223          71 : IonBuilder::outermostBuilder()
     224             : {
     225          71 :     IonBuilder* builder = this;
     226         143 :     while (builder->callerBuilder_)
     227          36 :         builder = builder->callerBuilder_;
     228          71 :     return builder;
     229             : }
     230             : 
     231             : void
     232           3 : IonBuilder::trackActionableAbort(const char* message)
     233             : {
     234           3 :     if (!isOptimizationTrackingEnabled())
     235           3 :         return;
     236             : 
     237           0 :     IonBuilder* topBuilder = outermostBuilder();
     238           0 :     if (topBuilder->hadActionableAbort())
     239           0 :         return;
     240             : 
     241           0 :     topBuilder->actionableAbortScript_ = script();
     242           0 :     topBuilder->actionableAbortPc_ = pc;
     243           0 :     topBuilder->actionableAbortMessage_ = message;
     244             : }
     245             : 
     246             : void
     247          10 : IonBuilder::spew(const char* message)
     248             : {
     249             :     // Don't call PCToLineNumber in release builds.
     250             : #ifdef DEBUG
     251          10 :     JitSpew(JitSpew_IonMIR, "%s @ %s:%d", message, script()->filename(), PCToLineNumber(script(), pc));
     252             : #endif
     253          10 : }
     254             : 
     255             : JSFunction*
     256          46 : IonBuilder::getSingleCallTarget(TemporaryTypeSet* calleeTypes)
     257             : {
     258          46 :     if (!calleeTypes)
     259           0 :         return nullptr;
     260             : 
     261          46 :     TemporaryTypeSet::ObjectKey* key = calleeTypes->maybeSingleObject();
     262          46 :     if (!key || key->clasp() != &JSFunction::class_)
     263           5 :         return nullptr;
     264             : 
     265          41 :     if (key->isSingleton())
     266          41 :         return &key->singleton()->as<JSFunction>();
     267             : 
     268           0 :     if (JSFunction* fun = key->group()->maybeInterpretedFunction())
     269           0 :         return fun;
     270             : 
     271           0 :     return nullptr;
     272             : }
     273             : 
     274             : AbortReasonOr<Ok>
     275        1228 : IonBuilder::getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
     276             :                                InliningTargets& targets, uint32_t maxTargets)
     277             : {
     278        1228 :     MOZ_ASSERT(targets.empty());
     279             : 
     280        1228 :     if (!calleeTypes)
     281           0 :         return Ok();
     282             : 
     283        1228 :     if (calleeTypes->baseFlags() != 0)
     284           0 :         return Ok();
     285             : 
     286        1228 :     unsigned objCount = calleeTypes->getObjectCount();
     287             : 
     288        1228 :     if (objCount == 0 || objCount > maxTargets)
     289         871 :         return Ok();
     290             : 
     291         357 :     if (!targets.reserve(objCount))
     292           0 :         return abort(AbortReason::Alloc);
     293         720 :     for (unsigned i = 0; i < objCount; i++) {
     294         363 :         JSObject* obj = calleeTypes->getSingleton(i);
     295         363 :         ObjectGroup* group = nullptr;
     296         363 :         if (obj) {
     297         328 :             MOZ_ASSERT(obj->isSingleton());
     298             :         } else {
     299          35 :             group = calleeTypes->getGroup(i);
     300          35 :             if (!group)
     301           0 :                 continue;
     302             : 
     303          35 :             obj = group->maybeInterpretedFunction();
     304          35 :             if (!obj) {
     305           0 :                 targets.clear();
     306           0 :                 return Ok();
     307             :             }
     308             : 
     309          35 :             MOZ_ASSERT(!obj->isSingleton());
     310             :         }
     311             : 
     312             :         // Don't optimize if the callee is not callable or constructable per
     313             :         // the manner it is being invoked, so that CallKnown does not have to
     314             :         // handle these cases (they will always throw).
     315         363 :         if (constructing ? !obj->isConstructor() : !obj->isCallable()) {
     316           0 :             targets.clear();
     317           0 :             return Ok();
     318             :         }
     319             : 
     320         363 :         targets.infallibleAppend(InliningTarget(obj, group));
     321             :     }
     322             : 
     323         357 :     return Ok();
     324             : }
     325             : 
     326             : IonBuilder::InliningDecision
     327          18 : IonBuilder::DontInline(JSScript* targetScript, const char* reason)
     328             : {
     329          18 :     if (targetScript) {
     330          12 :         JitSpew(JitSpew_Inlining, "Cannot inline %s:%" PRIuSIZE ": %s",
     331          12 :                 targetScript->filename(), targetScript->lineno(), reason);
     332             :     } else {
     333           6 :         JitSpew(JitSpew_Inlining, "Cannot inline: %s", reason);
     334             :     }
     335             : 
     336          18 :     return InliningDecision_DontInline;
     337             : }
     338             : 
     339             : /*
     340             :  * |hasCommonInliningPath| determines whether the current inlining path has been
     341             :  * seen before based on the sequence of scripts in the chain of |IonBuilder|s.
     342             :  *
     343             :  * An inlining path for a function |f| is the sequence of functions whose
     344             :  * inlinings precede |f| up to any previous occurrences of |f|.
     345             :  * So, if we have the chain of inlinings
     346             :  *
     347             :  *          f1 -> f2 -> f -> f3 -> f4 -> f5 -> f
     348             :  *          --------         --------------
     349             :  *
     350             :  * the inlining paths for |f| are [f2, f1] and [f5, f4, f3].
     351             :  * When attempting to inline |f|, we find all existing inlining paths for |f|
     352             :  * and check whether they share a common prefix with the path created were |f|
     353             :  * inlined.
     354             :  *
     355             :  * For example, given mutually recursive functions |f| and |g|, a possible
     356             :  * inlining is
     357             :  *
     358             :  *                           +---- Inlining stopped here...
     359             :  *                           |
     360             :  *                           v
     361             :  *          a -> f -> g -> f \ -> g -> f -> g -> ...
     362             :  *
     363             :  * where the vertical bar denotes the termination of inlining.
     364             :  * Inlining is terminated because we have already observed the inlining path
     365             :  * [f] when inlining function |g|. Note that this will inline recursive
     366             :  * functions such as |fib| only one level, as |fib| has a zero length inlining
     367             :  * path which trivially prefixes all inlining paths.
     368             :  *
     369             :  */
     370             : bool
     371          45 : IonBuilder::hasCommonInliningPath(const JSScript* scriptToInline)
     372             : {
     373             :     // Find all previous inlinings of the |scriptToInline| and check for common
     374             :     // inlining paths with the top of the inlining stack.
     375          66 :     for (IonBuilder* it = this->callerBuilder_; it; it = it->callerBuilder_) {
     376          21 :         if (it->script() != scriptToInline)
     377          21 :             continue;
     378             : 
     379             :         // This only needs to check the top of each stack for a match,
     380             :         // as a match of length one ensures a common prefix.
     381           0 :         IonBuilder* path = it->callerBuilder_;
     382           0 :         if (!path || this->script() == path->script())
     383           0 :             return true;
     384             :     }
     385             : 
     386          45 :     return false;
     387             : }
     388             : 
     389             : IonBuilder::InliningDecision
     390          62 : IonBuilder::canInlineTarget(JSFunction* target, CallInfo& callInfo)
     391             : {
     392          62 :     if (!optimizationInfo().inlineInterpreted()) {
     393           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
     394           0 :         return InliningDecision_DontInline;
     395             :     }
     396             : 
     397          62 :     if (TraceLogTextIdEnabled(TraceLogger_InlinedScripts)) {
     398             :         return DontInline(nullptr, "Tracelogging of inlined scripts is enabled"
     399           0 :                                    "but Tracelogger cannot do that yet.");
     400             :     }
     401             : 
     402          62 :     if (!target->isInterpreted()) {
     403           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNotInterpreted);
     404           0 :         return DontInline(nullptr, "Non-interpreted target");
     405             :     }
     406             : 
     407          62 :     if (info().analysisMode() != Analysis_DefiniteProperties) {
     408             :         // If |this| or an argument has an empty resultTypeSet, don't bother
     409             :         // inlining, as the call is currently unreachable due to incomplete type
     410             :         // information. This does not apply to the definite properties analysis,
     411             :         // in that case we want to inline anyway.
     412             : 
     413          62 :         if (callInfo.thisArg()->emptyResultTypeSet()) {
     414           0 :             trackOptimizationOutcome(TrackedOutcome::CantInlineUnreachable);
     415           0 :             return DontInline(nullptr, "Empty TypeSet for |this|");
     416             :         }
     417             : 
     418         146 :         for (size_t i = 0; i < callInfo.argc(); i++) {
     419          90 :             if (callInfo.getArg(i)->emptyResultTypeSet()) {
     420           6 :                 trackOptimizationOutcome(TrackedOutcome::CantInlineUnreachable);
     421           6 :                 return DontInline(nullptr, "Empty TypeSet for argument");
     422             :             }
     423             :         }
     424             :     }
     425             : 
     426             :     // Allow constructing lazy scripts when performing the definite properties
     427             :     // analysis, as baseline has not been used to warm the caller up yet.
     428          56 :     if (target->isInterpreted() && info().analysisMode() == Analysis_DefiniteProperties) {
     429           0 :         RootedFunction fun(analysisContext, target);
     430           0 :         RootedScript script(analysisContext, JSFunction::getOrCreateScript(analysisContext, fun));
     431           0 :         if (!script)
     432           0 :             return InliningDecision_Error;
     433             : 
     434           0 :         if (!script->hasBaselineScript() && script->canBaselineCompile()) {
     435           0 :             MethodStatus status = BaselineCompile(analysisContext, script);
     436           0 :             if (status == Method_Error)
     437           0 :                 return InliningDecision_Error;
     438           0 :             if (status != Method_Compiled) {
     439           0 :                 trackOptimizationOutcome(TrackedOutcome::CantInlineNoBaseline);
     440           0 :                 return InliningDecision_DontInline;
     441             :             }
     442             :         }
     443             :     }
     444             : 
     445          56 :     if (!target->hasScript()) {
     446           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineLazy);
     447           0 :         return DontInline(nullptr, "Lazy script");
     448             :     }
     449             : 
     450          56 :     JSScript* inlineScript = target->nonLazyScript();
     451          56 :     if (callInfo.constructing() && !target->isConstructor()) {
     452           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNotConstructor);
     453           0 :         return DontInline(inlineScript, "Callee is not a constructor");
     454             :     }
     455             : 
     456          56 :     if (!callInfo.constructing() && target->isClassConstructor()) {
     457           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineClassConstructor);
     458           0 :         return DontInline(inlineScript, "Not constructing class constructor");
     459             :     }
     460             : 
     461          56 :     AnalysisMode analysisMode = info().analysisMode();
     462          56 :     if (!CanIonCompile(inlineScript, analysisMode)) {
     463           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
     464           0 :         return DontInline(inlineScript, "Disabled Ion compilation");
     465             :     }
     466             : 
     467             :     // Don't inline functions which don't have baseline scripts.
     468          56 :     if (!inlineScript->hasBaselineScript()) {
     469          11 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNoBaseline);
     470          11 :         return DontInline(inlineScript, "No baseline jitcode");
     471             :     }
     472             : 
     473          45 :     if (TooManyFormalArguments(target->nargs())) {
     474           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineTooManyArgs);
     475           0 :         return DontInline(inlineScript, "Too many args");
     476             :     }
     477             : 
     478             :     // We check the number of actual arguments against the maximum number of
     479             :     // formal arguments as we do not want to encode all actual arguments in the
     480             :     // callerResumePoint.
     481          45 :     if (TooManyFormalArguments(callInfo.argc())) {
     482           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineTooManyArgs);
     483           0 :         return DontInline(inlineScript, "Too many actual args");
     484             :     }
     485             : 
     486          45 :     if (hasCommonInliningPath(inlineScript)) {
     487           0 :         trackOptimizationOutcome(TrackedOutcome::HasCommonInliningPath);
     488           0 :         return DontInline(inlineScript, "Common inlining path");
     489             :     }
     490             : 
     491          45 :     if (inlineScript->uninlineable()) {
     492           1 :         trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
     493           1 :         return DontInline(inlineScript, "Uninlineable script");
     494             :     }
     495             : 
     496          44 :     if (inlineScript->needsArgsObj()) {
     497           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNeedsArgsObj);
     498           0 :         return DontInline(inlineScript, "Script that needs an arguments object");
     499             :     }
     500             : 
     501          44 :     if (inlineScript->isDebuggee()) {
     502           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineDebuggee);
     503           0 :         return DontInline(inlineScript, "Script is debuggee");
     504             :     }
     505             : 
     506          44 :     return InliningDecision_Inline;
     507             : }
     508             : 
     509             : AbortReasonOr<Ok>
     510         118 : IonBuilder::analyzeNewLoopTypes(const CFGBlock* loopEntryBlock)
     511             : {
     512         118 :     CFGLoopEntry* loopEntry = loopEntryBlock->stopIns()->toLoopEntry();
     513         118 :     CFGBlock* cfgBlock = loopEntry->successor();
     514         118 :     MBasicBlock* entry = blockWorklist[cfgBlock->id()];
     515         118 :     MOZ_ASSERT(!entry->isDead());
     516             : 
     517             :     // The phi inputs at the loop head only reflect types for variables that
     518             :     // were present at the start of the loop. If the variable changes to a new
     519             :     // type within the loop body, and that type is carried around to the loop
     520             :     // head, then we need to know about the new type up front.
     521             :     //
     522             :     // Since SSA information hasn't been constructed for the loop body yet, we
     523             :     // need a separate analysis to pick out the types that might flow around
     524             :     // the loop header. This is a best-effort analysis that may either over-
     525             :     // or under-approximate the set of such types.
     526             :     //
     527             :     // Over-approximating the types may lead to inefficient generated code, and
     528             :     // under-approximating the types will cause the loop body to be analyzed
     529             :     // multiple times as the correct types are deduced (see finishLoop).
     530             : 
     531             :     // If we restarted processing of an outer loop then get loop header types
     532             :     // directly from the last time we have previously processed this loop. This
     533             :     // both avoids repeated work from the bytecode traverse below, and will
     534             :     // also pick up types discovered while previously building the loop body.
     535         149 :     for (size_t i = 0; i < loopHeaders_.length(); i++) {
     536          31 :         if (loopHeaders_[i].pc == cfgBlock->startPc()) {
     537           0 :             MBasicBlock* oldEntry = loopHeaders_[i].header;
     538             : 
     539             :             // If this block has been discarded, its resume points will have
     540             :             // already discarded their operands.
     541           0 :             if (!oldEntry->isDead()) {
     542           0 :                 MResumePoint* oldEntryRp = oldEntry->entryResumePoint();
     543           0 :                 size_t stackDepth = oldEntryRp->stackDepth();
     544           0 :                 for (size_t slot = 0; slot < stackDepth; slot++) {
     545           0 :                     MDefinition* oldDef = oldEntryRp->getOperand(slot);
     546           0 :                     if (!oldDef->isPhi()) {
     547           0 :                         MOZ_ASSERT(oldDef->block()->id() < oldEntry->id());
     548           0 :                         MOZ_ASSERT(oldDef == entry->getSlot(slot));
     549           0 :                         continue;
     550             :                     }
     551           0 :                     MPhi* oldPhi = oldDef->toPhi();
     552           0 :                     MPhi* newPhi = entry->getSlot(slot)->toPhi();
     553           0 :                     if (!newPhi->addBackedgeType(alloc(), oldPhi->type(), oldPhi->resultTypeSet()))
     554           0 :                         return abort(AbortReason::Alloc);
     555             :                 }
     556             :             }
     557             : 
     558             :             // Update the most recent header for this loop encountered, in case
     559             :             // new types flow to the phis and the loop is processed at least
     560             :             // three times.
     561           0 :             loopHeaders_[i].header = entry;
     562           0 :             return Ok();
     563             :         }
     564             :     }
     565         118 :     if (!loopHeaders_.append(LoopHeader(cfgBlock->startPc(), entry)))
     566           0 :         return abort(AbortReason::Alloc);
     567             : 
     568             :     // Get the start and end pc of this loop.
     569         118 :     jsbytecode* start = loopEntryBlock->stopPc();
     570         118 :     start += GetBytecodeLength(start);
     571         118 :     jsbytecode* end = loopEntry->loopStopPc();
     572             : 
     573             :     // Iterate the bytecode quickly to seed possible types in the loopheader.
     574         118 :     jsbytecode* last = nullptr;
     575         118 :     jsbytecode* earlier = nullptr;
     576        6541 :     for (jsbytecode* pc = start; pc != end; earlier = last, last = pc, pc += GetBytecodeLength(pc)) {
     577             :         uint32_t slot;
     578        6423 :         if (*pc == JSOP_SETLOCAL)
     579         393 :             slot = info().localSlot(GET_LOCALNO(pc));
     580        6030 :         else if (*pc == JSOP_SETARG)
     581           0 :             slot = info().argSlotUnchecked(GET_ARGNO(pc));
     582             :         else
     583        6030 :             continue;
     584         393 :         if (slot >= info().firstStackSlot())
     585           0 :             continue;
     586         393 :         if (!analysis().maybeInfo(pc))
     587           0 :             continue;
     588         393 :         if (!last)
     589           0 :             continue;
     590             : 
     591         393 :         MPhi* phi = entry->getSlot(slot)->toPhi();
     592             : 
     593         393 :         if (*last == JSOP_POS)
     594           0 :             last = earlier;
     595             : 
     596         393 :         if (CodeSpec[*last].format & JOF_TYPESET) {
     597          80 :             TemporaryTypeSet* typeSet = bytecodeTypes(last);
     598          80 :             if (!typeSet->empty()) {
     599           8 :                 MIRType type = typeSet->getKnownMIRType();
     600           8 :                 if (!phi->addBackedgeType(alloc(), type, typeSet))
     601           0 :                     return abort(AbortReason::Alloc);
     602             :             }
     603         313 :         } else if (*last == JSOP_GETLOCAL || *last == JSOP_GETARG) {
     604           0 :             uint32_t slot = (*last == JSOP_GETLOCAL)
     605           0 :                             ? info().localSlot(GET_LOCALNO(last))
     606           0 :                             : info().argSlotUnchecked(GET_ARGNO(last));
     607           0 :             if (slot < info().firstStackSlot()) {
     608           0 :                 MPhi* otherPhi = entry->getSlot(slot)->toPhi();
     609           0 :                 if (otherPhi->hasBackedgeType()) {
     610           0 :                     if (!phi->addBackedgeType(alloc(), otherPhi->type(), otherPhi->resultTypeSet()))
     611           0 :                         return abort(AbortReason::Alloc);
     612             :                 }
     613           0 :             }
     614             :         } else {
     615         313 :             MIRType type = MIRType::None;
     616         313 :             switch (*last) {
     617             :               case JSOP_VOID:
     618             :               case JSOP_UNDEFINED:
     619           0 :                 type = MIRType::Undefined;
     620           0 :                 break;
     621             :               case JSOP_GIMPLICITTHIS:
     622           0 :                 if (!script()->hasNonSyntacticScope())
     623           0 :                     type = MIRType::Undefined;
     624           0 :                 break;
     625             :               case JSOP_NULL:
     626           0 :                 type = MIRType::Null;
     627           0 :                 break;
     628             :               case JSOP_ZERO:
     629             :               case JSOP_ONE:
     630             :               case JSOP_INT8:
     631             :               case JSOP_INT32:
     632             :               case JSOP_UINT16:
     633             :               case JSOP_UINT24:
     634             :               case JSOP_BITAND:
     635             :               case JSOP_BITOR:
     636             :               case JSOP_BITXOR:
     637             :               case JSOP_BITNOT:
     638             :               case JSOP_RSH:
     639             :               case JSOP_LSH:
     640             :               case JSOP_URSH:
     641          18 :                 type = MIRType::Int32;
     642          18 :                 break;
     643             :               case JSOP_FALSE:
     644             :               case JSOP_TRUE:
     645             :               case JSOP_EQ:
     646             :               case JSOP_NE:
     647             :               case JSOP_LT:
     648             :               case JSOP_LE:
     649             :               case JSOP_GT:
     650             :               case JSOP_GE:
     651             :               case JSOP_NOT:
     652             :               case JSOP_STRICTEQ:
     653             :               case JSOP_STRICTNE:
     654             :               case JSOP_IN:
     655             :               case JSOP_INSTANCEOF:
     656             :               case JSOP_HASOWN:
     657          88 :                 type = MIRType::Boolean;
     658          88 :                 break;
     659             :               case JSOP_DOUBLE:
     660           0 :                 type = MIRType::Double;
     661           0 :                 break;
     662             :               case JSOP_STRING:
     663             :               case JSOP_TOSTRING:
     664             :               case JSOP_TYPEOF:
     665             :               case JSOP_TYPEOFEXPR:
     666           3 :                 type = MIRType::String;
     667           3 :                 break;
     668             :               case JSOP_SYMBOL:
     669           0 :                 type = MIRType::Symbol;
     670           0 :                 break;
     671             :               case JSOP_ADD:
     672             :               case JSOP_SUB:
     673             :               case JSOP_MUL:
     674             :               case JSOP_DIV:
     675             :               case JSOP_MOD:
     676             :               case JSOP_NEG:
     677         204 :                 type = inspector->expectedResultType(last);
     678         204 :                 break;
     679             :               default:
     680           0 :                 break;
     681             :             }
     682         313 :             if (type != MIRType::None) {
     683         115 :                 if (!phi->addBackedgeType(alloc(), type, nullptr))
     684           0 :                     return abort(AbortReason::Alloc);
     685             :             }
     686             :         }
     687             :     }
     688         118 :     return Ok();
     689             : }
     690             : 
     691             : AbortReasonOr<Ok>
     692         179 : IonBuilder::init()
     693             : {
     694             :     {
     695         358 :         LifoAlloc::AutoFallibleScope fallibleAllocator(alloc().lifoAlloc());
     696         179 :         if (!TypeScript::FreezeTypeSets(constraints(), script(), &thisTypes, &argTypes, &typeArray))
     697           0 :             return abort(AbortReason::Alloc);
     698             :     }
     699             : 
     700         179 :     if (!alloc().ensureBallast())
     701           0 :         return abort(AbortReason::Alloc);
     702             : 
     703         179 :     if (inlineCallInfo_) {
     704             :         // If we're inlining, the actual this/argument types are not necessarily
     705             :         // a subset of the script's observed types. |argTypes| is never accessed
     706             :         // for inlined scripts, so we just null it.
     707          33 :         thisTypes = inlineCallInfo_->thisArg()->resultTypeSet();
     708          33 :         argTypes = nullptr;
     709             :     }
     710             : 
     711         179 :     if (!analysis().init(alloc(), gsn))
     712           0 :         return abort(AbortReason::Alloc);
     713             : 
     714             :     // The baseline script normally has the bytecode type map, but compute
     715             :     // it ourselves if we do not have a baseline script.
     716         179 :     if (script()->hasBaselineScript()) {
     717          44 :         bytecodeTypeMap = script()->baselineScript()->bytecodeTypeMap();
     718             :     } else {
     719         135 :         bytecodeTypeMap = alloc_->lifoAlloc()->newArrayUninitialized<uint32_t>(script()->nTypeSets());
     720         135 :         if (!bytecodeTypeMap)
     721           0 :             return abort(AbortReason::Alloc);
     722         135 :         FillBytecodeTypeMap(script(), bytecodeTypeMap);
     723             :     }
     724             : 
     725         179 :     return Ok();
     726             : }
     727             : 
     728             : AbortReasonOr<Ok>
     729         146 : IonBuilder::build()
     730             : {
     731         146 :     MOZ_TRY(init());
     732             : 
     733         146 :     if (script()->hasBaselineScript())
     734          11 :         script()->baselineScript()->resetMaxInliningDepth();
     735             : 
     736             :     MBasicBlock* entry;
     737         146 :     MOZ_TRY_VAR(entry, newBlock(pc));
     738         146 :     MOZ_TRY(setCurrentAndSpecializePhis(entry));
     739             : 
     740             : #ifdef JS_JITSPEW
     741         146 :     if (info().isAnalysis()) {
     742         272 :         JitSpew(JitSpew_IonScripts, "Analyzing script %s:%" PRIuSIZE " (%p) %s",
     743         136 :                 script()->filename(), script()->lineno(), (void*)script(),
     744         272 :                 AnalysisModeString(info().analysisMode()));
     745             :     } else {
     746          30 :         JitSpew(JitSpew_IonScripts, "%sompiling script %s:%" PRIuSIZE " (%p) (warmup-counter=%" PRIu32 ", level=%s)",
     747          10 :                 (script()->hasIonScript() ? "Rec" : "C"),
     748          10 :                 script()->filename(), script()->lineno(), (void*)script(),
     749          20 :                 script()->getWarmUpCount(), OptimizationLevelString(optimizationInfo().level()));
     750             :     }
     751             : #endif
     752             : 
     753         146 :     MOZ_TRY(initParameters());
     754         146 :     initLocals();
     755             : 
     756             :     // Initialize something for the env chain. We can bail out before the
     757             :     // start instruction, but the snapshot is encoded *at* the start
     758             :     // instruction, which means generating any code that could load into
     759             :     // registers is illegal.
     760         146 :     MInstruction* env = MConstant::New(alloc(), UndefinedValue());
     761         146 :     current->add(env);
     762         146 :     current->initSlot(info().environmentChainSlot(), env);
     763             : 
     764             :     // Initialize the return value.
     765         146 :     MInstruction* returnValue = MConstant::New(alloc(), UndefinedValue());
     766         146 :     current->add(returnValue);
     767         146 :     current->initSlot(info().returnValueSlot(), returnValue);
     768             : 
     769             :     // Initialize the arguments object slot to undefined if necessary.
     770         146 :     if (info().hasArguments()) {
     771         136 :         MInstruction* argsObj = MConstant::New(alloc(), UndefinedValue());
     772         136 :         current->add(argsObj);
     773         136 :         current->initSlot(info().argsObjSlot(), argsObj);
     774             :     }
     775             : 
     776             :     // Emit the start instruction, so we can begin real instructions.
     777         146 :     current->add(MStart::New(alloc()));
     778             : 
     779             :     // Guard against over-recursion. Do this before we start unboxing, since
     780             :     // this will create an OSI point that will read the incoming argument
     781             :     // values, which is nice to do before their last real use, to minimize
     782             :     // register/stack pressure.
     783         146 :     MCheckOverRecursed* check = MCheckOverRecursed::New(alloc());
     784         146 :     current->add(check);
     785         146 :     MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
     786         146 :     if (!entryRpCopy)
     787           0 :         return abort(AbortReason::Alloc);
     788         146 :     check->setResumePoint(entryRpCopy);
     789             : 
     790             :     // Parameters have been checked to correspond to the typeset, now we unbox
     791             :     // what we can in an infallible manner.
     792         146 :     MOZ_TRY(rewriteParameters());
     793             : 
     794             :     // Check for redeclaration errors for global scripts.
     795         292 :     if (!info().funMaybeLazy() && !info().module() &&
     796         146 :         script()->bodyScope()->is<GlobalScope>() &&
     797           0 :         script()->bodyScope()->as<GlobalScope>().hasBindings())
     798             :     {
     799           0 :         MGlobalNameConflictsCheck* redeclCheck = MGlobalNameConflictsCheck::New(alloc());
     800           0 :         current->add(redeclCheck);
     801           0 :         MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
     802           0 :         if (!entryRpCopy)
     803           0 :             return abort(AbortReason::Alloc);
     804           0 :         redeclCheck->setResumePoint(entryRpCopy);
     805             :     }
     806             : 
     807             :     // It's safe to start emitting actual IR, so now build the env chain.
     808         146 :     MOZ_TRY(initEnvironmentChain());
     809         146 :     if (info().needsArgsObj())
     810         135 :         initArgumentsObject();
     811             : 
     812             :     // The type analysis phase attempts to insert unbox operations near
     813             :     // definitions of values. It also attempts to replace uses in resume points
     814             :     // with the narrower, unboxed variants. However, we must prevent this
     815             :     // replacement from happening on values in the entry snapshot. Otherwise we
     816             :     // could get this:
     817             :     //
     818             :     //       v0 = MParameter(0)
     819             :     //       v1 = MParameter(1)
     820             :     //       --   ResumePoint(v2, v3)
     821             :     //       v2 = Unbox(v0, INT32)
     822             :     //       v3 = Unbox(v1, INT32)
     823             :     //
     824             :     // So we attach the initial resume point to each parameter, which the type
     825             :     // analysis explicitly checks (this is the same mechanism used for
     826             :     // effectful operations).
     827         879 :     for (uint32_t i = 0; i < info().endArgSlot(); i++) {
     828         733 :         MInstruction* ins = current->getEntrySlot(i)->toInstruction();
     829         733 :         if (ins->type() != MIRType::Value)
     830         428 :             continue;
     831             : 
     832         305 :         MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
     833         305 :         if (!entryRpCopy)
     834           0 :             return abort(AbortReason::Alloc);
     835         305 :         ins->setResumePoint(entryRpCopy);
     836             :     }
     837             : 
     838             : #ifdef DEBUG
     839             :     // lazyArguments should never be accessed in |argsObjAliasesFormals| scripts.
     840         146 :     if (info().hasArguments() && !info().argsObjAliasesFormals())
     841         129 :         hasLazyArguments_ = true;
     842             : #endif
     843             : 
     844         146 :     insertRecompileCheck();
     845             : 
     846         146 :     MOZ_TRY(traverseBytecode());
     847             : 
     848             :     // Discard unreferenced & pre-allocated resume points.
     849         142 :     replaceMaybeFallbackFunctionGetter(nullptr);
     850             : 
     851         152 :     if (script_->hasBaselineScript() &&
     852          10 :         inlinedBytecodeLength_ > script_->baselineScript()->inlinedBytecodeLength())
     853             :     {
     854           4 :         script_->baselineScript()->setInlinedBytecodeLength(inlinedBytecodeLength_);
     855             :     }
     856             : 
     857         142 :     MOZ_TRY(maybeAddOsrTypeBarriers());
     858         142 :     MOZ_TRY(processIterators());
     859             : 
     860         142 :     if (!info().isAnalysis() && !abortedPreliminaryGroups().empty())
     861           1 :         return abort(AbortReason::PreliminaryObjects);
     862             : 
     863         141 :     MOZ_ASSERT(loopDepth_ == 0);
     864         141 :     return Ok();
     865             : }
     866             : 
     867             : AbortReasonOr<Ok>
     868         142 : IonBuilder::processIterators()
     869             : {
     870             :     // Find and mark phis that must transitively hold an iterator live.
     871             : 
     872         284 :     Vector<MDefinition*, 8, SystemAllocPolicy> worklist;
     873             : 
     874         145 :     for (size_t i = 0; i < iterators_.length(); i++) {
     875           3 :         MDefinition* iter = iterators_[i];
     876           3 :         if (!iter->isInWorklist()) {
     877           3 :             if (!worklist.append(iter))
     878           0 :                 return abort(AbortReason::Alloc);
     879           3 :             iter->setInWorklist();
     880             :         }
     881             :     }
     882             : 
     883         596 :     while (!worklist.empty()) {
     884         227 :         MDefinition* def = worklist.popCopy();
     885         227 :         def->setNotInWorklist();
     886             : 
     887         227 :         if (def->isPhi()) {
     888          21 :             MPhi* phi = def->toPhi();
     889          21 :             phi->setIterator();
     890          21 :             phi->setImplicitlyUsedUnchecked();
     891             :         }
     892             : 
     893         459 :         for (MUseDefIterator iter(def); iter; iter++) {
     894         232 :             MDefinition* use = iter.def();
     895         232 :             if (!use->isInWorklist() && (!use->isPhi() || !use->toPhi()->isIterator())) {
     896         224 :                 if (!worklist.append(use))
     897           0 :                     return abort(AbortReason::Alloc);
     898         224 :                 use->setInWorklist();
     899             :             }
     900             :         }
     901             :     }
     902             : 
     903         142 :     return Ok();
     904             : }
     905             : 
     906             : AbortReasonOr<Ok>
     907          33 : IonBuilder::buildInline(IonBuilder* callerBuilder, MResumePoint* callerResumePoint,
     908             :                         CallInfo& callInfo)
     909             : {
     910          33 :     inlineCallInfo_ = &callInfo;
     911             : 
     912          33 :     MOZ_TRY(init());
     913             : 
     914          33 :     JitSpew(JitSpew_IonScripts, "Inlining script %s:%" PRIuSIZE " (%p)",
     915          66 :             script()->filename(), script()->lineno(), (void*)script());
     916             : 
     917          33 :     callerBuilder_ = callerBuilder;
     918          33 :     callerResumePoint_ = callerResumePoint;
     919             : 
     920          33 :     if (callerBuilder->failedBoundsCheck_)
     921           0 :         failedBoundsCheck_ = true;
     922             : 
     923          33 :     if (callerBuilder->failedShapeGuard_)
     924           0 :         failedShapeGuard_ = true;
     925             : 
     926          33 :     if (callerBuilder->failedLexicalCheck_)
     927           0 :         failedLexicalCheck_ = true;
     928             : 
     929          33 :     safeForMinorGC_ = callerBuilder->safeForMinorGC_;
     930             : 
     931             :     // Generate single entrance block.
     932             :     MBasicBlock* entry;
     933          33 :     MOZ_TRY_VAR(entry, newBlock(pc));
     934          33 :     MOZ_TRY(setCurrentAndSpecializePhis(entry));
     935             : 
     936          33 :     current->setCallerResumePoint(callerResumePoint);
     937             : 
     938             :     // Connect the entrance block to the last block in the caller's graph.
     939          33 :     MBasicBlock* predecessor = callerBuilder->current;
     940          33 :     MOZ_ASSERT(predecessor == callerResumePoint->block());
     941             : 
     942          33 :     predecessor->end(MGoto::New(alloc(), current));
     943          33 :     if (!current->addPredecessorWithoutPhis(predecessor))
     944           0 :         return abort(AbortReason::Alloc);
     945             : 
     946             :     // Initialize env chain slot to Undefined.  It's set later by
     947             :     // |initEnvironmentChain|.
     948          33 :     MInstruction* env = MConstant::New(alloc(), UndefinedValue());
     949          33 :     current->add(env);
     950          33 :     current->initSlot(info().environmentChainSlot(), env);
     951             : 
     952             :     // Initialize |return value| slot.
     953          33 :     MInstruction* returnValue = MConstant::New(alloc(), UndefinedValue());
     954          33 :     current->add(returnValue);
     955          33 :     current->initSlot(info().returnValueSlot(), returnValue);
     956             : 
     957             :     // Initialize |arguments| slot.
     958          33 :     if (info().hasArguments()) {
     959           0 :         MInstruction* argsObj = MConstant::New(alloc(), UndefinedValue());
     960           0 :         current->add(argsObj);
     961           0 :         current->initSlot(info().argsObjSlot(), argsObj);
     962             :     }
     963             : 
     964             :     // Initialize |this| slot.
     965          33 :     current->initSlot(info().thisSlot(), callInfo.thisArg());
     966             : 
     967          33 :     JitSpew(JitSpew_Inlining, "Initializing %u arg slots", info().nargs());
     968             : 
     969             :     // NB: Ion does not inline functions which |needsArgsObj|.  So using argSlot()
     970             :     // instead of argSlotUnchecked() below is OK
     971          33 :     MOZ_ASSERT(!info().needsArgsObj());
     972             : 
     973             :     // Initialize actually set arguments.
     974          33 :     uint32_t existing_args = Min<uint32_t>(callInfo.argc(), info().nargs());
     975          72 :     for (size_t i = 0; i < existing_args; ++i) {
     976          39 :         MDefinition* arg = callInfo.getArg(i);
     977          39 :         current->initSlot(info().argSlot(i), arg);
     978             :     }
     979             : 
     980             :     // Pass Undefined for missing arguments
     981          33 :     for (size_t i = callInfo.argc(); i < info().nargs(); ++i) {
     982           0 :         MConstant* arg = MConstant::New(alloc(), UndefinedValue());
     983           0 :         current->add(arg);
     984           0 :         current->initSlot(info().argSlot(i), arg);
     985             :     }
     986             : 
     987          33 :     JitSpew(JitSpew_Inlining, "Initializing %u locals", info().nlocals());
     988             : 
     989          33 :     initLocals();
     990             : 
     991          33 :     JitSpew(JitSpew_Inlining, "Inline entry block MResumePoint %p, %u stack slots",
     992          66 :             (void*) current->entryResumePoint(), current->entryResumePoint()->stackDepth());
     993             : 
     994             :     // +2 for the env chain and |this|, maybe another +1 for arguments object slot.
     995          33 :     MOZ_ASSERT(current->entryResumePoint()->stackDepth() == info().totalSlots());
     996             : 
     997             : #ifdef DEBUG
     998          33 :     if (script_->argumentsHasVarBinding())
     999           0 :         hasLazyArguments_ = true;
    1000             : #endif
    1001             : 
    1002          33 :     insertRecompileCheck();
    1003             : 
    1004             :     // Initialize the env chain now that all resume points operands are
    1005             :     // initialized.
    1006          33 :     MOZ_TRY(initEnvironmentChain(callInfo.fun()));
    1007             : 
    1008          33 :     MOZ_TRY(traverseBytecode());
    1009             : 
    1010             :     // Discard unreferenced & pre-allocated resume points.
    1011          31 :     replaceMaybeFallbackFunctionGetter(nullptr);
    1012             : 
    1013          31 :     MOZ_ASSERT(iterators_.empty(), "Iterators should be added to outer builder");
    1014             : 
    1015          31 :     if (!info().isAnalysis() && !abortedPreliminaryGroups().empty())
    1016           1 :         return abort(AbortReason::PreliminaryObjects);
    1017             : 
    1018          30 :     return Ok();
    1019             : }
    1020             : 
    1021             : void
    1022         305 : IonBuilder::rewriteParameter(uint32_t slotIdx, MDefinition* param, int32_t argIndex)
    1023             : {
    1024         305 :     MOZ_ASSERT(param->isParameter() || param->isGetArgumentsObjectArg());
    1025             : 
    1026         305 :     TemporaryTypeSet* types = param->resultTypeSet();
    1027         305 :     MDefinition* actual = ensureDefiniteType(param, types->getKnownMIRType());
    1028         305 :     if (actual == param)
    1029         272 :         return;
    1030             : 
    1031             :     // Careful! We leave the original MParameter in the entry resume point. The
    1032             :     // arguments still need to be checked unless proven otherwise at the call
    1033             :     // site, and these checks can bailout. We can end up:
    1034             :     //   v0 = Parameter(0)
    1035             :     //   v1 = Unbox(v0, INT32)
    1036             :     //   --   ResumePoint(v0)
    1037             :     //
    1038             :     // As usual, it would be invalid for v1 to be captured in the initial
    1039             :     // resume point, rather than v0.
    1040          33 :     current->rewriteSlot(slotIdx, actual);
    1041             : }
    1042             : 
    1043             : // Apply Type Inference information to parameters early on, unboxing them if
    1044             : // they have a definitive type. The actual guards will be emitted by the code
    1045             : // generator, explicitly, as part of the function prologue.
    1046             : AbortReasonOr<Ok>
    1047         146 : IonBuilder::rewriteParameters()
    1048             : {
    1049         146 :     MOZ_ASSERT(info().environmentChainSlot() == 0);
    1050             : 
    1051             :     // If this JSScript is not the code of a function, then skip the
    1052             :     // initialization of function parameters.
    1053         146 :     if (!info().funMaybeLazy())
    1054           0 :         return Ok();
    1055             : 
    1056         451 :     for (uint32_t i = info().startArgSlot(); i < info().endArgSlot(); i++) {
    1057         305 :         if (!alloc().ensureBallast())
    1058           0 :             return abort(AbortReason::Alloc);
    1059         305 :         MDefinition* param = current->getSlot(i);
    1060         305 :         rewriteParameter(i, param, param->toParameter()->index());
    1061             :     }
    1062             : 
    1063         146 :     return Ok();
    1064             : }
    1065             : 
    1066             : AbortReasonOr<Ok>
    1067         146 : IonBuilder::initParameters()
    1068             : {
    1069             :     // If this JSScript is not the code of a function, then skip the
    1070             :     // initialization of function parameters.
    1071         146 :     if (!info().funMaybeLazy())
    1072           0 :         return Ok();
    1073             : 
    1074             :     // If we are doing OSR on a frame which initially executed in the
    1075             :     // interpreter and didn't accumulate type information, try to use that OSR
    1076             :     // frame to determine possible initial types for 'this' and parameters.
    1077             : 
    1078         146 :     if (thisTypes->empty() && baselineFrame_) {
    1079           2 :         TypeSet::Type type = baselineFrame_->thisType;
    1080           2 :         if (type.isSingletonUnchecked())
    1081           0 :             checkNurseryObject(type.singleton());
    1082           2 :         thisTypes->addType(type, alloc_->lifoAlloc());
    1083             :     }
    1084             : 
    1085         146 :     MParameter* param = MParameter::New(alloc(), MParameter::THIS_SLOT, thisTypes);
    1086         146 :     current->add(param);
    1087         146 :     current->initSlot(info().thisSlot(), param);
    1088             : 
    1089         305 :     for (uint32_t i = 0; i < info().nargs(); i++) {
    1090         159 :         TemporaryTypeSet* types = &argTypes[i];
    1091         167 :         if (types->empty() && baselineFrame_ &&
    1092           8 :             !script_->baselineScript()->modifiesArguments())
    1093             :         {
    1094           8 :             TypeSet::Type type = baselineFrame_->argTypes[i];
    1095           8 :             if (type.isSingletonUnchecked())
    1096           0 :                 checkNurseryObject(type.singleton());
    1097           8 :             types->addType(type, alloc_->lifoAlloc());
    1098             :         }
    1099             : 
    1100         159 :         param = MParameter::New(alloc().fallible(), i, types);
    1101         159 :         if (!param)
    1102           0 :             return abort(AbortReason::Alloc);
    1103         159 :         current->add(param);
    1104         159 :         current->initSlot(info().argSlotUnchecked(i), param);
    1105             :     }
    1106             : 
    1107         146 :     return Ok();
    1108             : }
    1109             : 
    1110             : void
    1111         179 : IonBuilder::initLocals()
    1112             : {
    1113             :     // Initialize all frame slots to undefined. Lexical bindings are temporal
    1114             :     // dead zoned in bytecode.
    1115             : 
    1116         179 :     if (info().nlocals() == 0)
    1117          21 :         return;
    1118             : 
    1119         158 :     MConstant* undef = MConstant::New(alloc(), UndefinedValue());
    1120         158 :     current->add(undef);
    1121             : 
    1122        1075 :     for (uint32_t i = 0; i < info().nlocals(); i++)
    1123         917 :         current->initSlot(info().localSlot(i), undef);
    1124             : }
    1125             : 
    1126             : AbortReasonOr<Ok>
    1127         179 : IonBuilder::initEnvironmentChain(MDefinition* callee)
    1128             : {
    1129         179 :     MInstruction* env = nullptr;
    1130             : 
    1131             :     // If the script doesn't use the envchain, then it's already initialized
    1132             :     // from earlier.  However, always make a env chain when |needsArgsObj| is true
    1133             :     // for the script, since arguments object construction requires the env chain
    1134             :     // to be passed in.
    1135         179 :     if (!info().needsArgsObj() && !analysis().usesEnvironmentChain())
    1136          36 :         return Ok();
    1137             : 
    1138             :     // The env chain is only tracked in scripts that have NAME opcodes which
    1139             :     // will try to access the env. For other scripts, the env instructions
    1140             :     // will be held live by resume points and code will still be generated for
    1141             :     // them, so just use a constant undefined value.
    1142             : 
    1143         143 :     if (JSFunction* fun = info().funMaybeLazy()) {
    1144         143 :         if (!callee) {
    1145         140 :             MCallee* calleeIns = MCallee::New(alloc());
    1146         140 :             current->add(calleeIns);
    1147         140 :             callee = calleeIns;
    1148             :         }
    1149         143 :         env = MFunctionEnvironment::New(alloc(), callee);
    1150         143 :         current->add(env);
    1151             : 
    1152             :         // This reproduce what is done in CallObject::createForFunction. Skip
    1153             :         // this for the arguments analysis, as the script might not have a
    1154             :         // baseline script with template objects yet.
    1155         151 :         if (fun->needsSomeEnvironmentObject() &&
    1156           8 :             info().analysisMode() != Analysis_ArgumentsUsage)
    1157             :         {
    1158           4 :             if (fun->needsNamedLambdaEnvironment())
    1159           0 :                 env = createNamedLambdaObject(callee, env);
    1160             : 
    1161             :             // TODO: Parameter expression-induced extra var environment not
    1162             :             // yet handled.
    1163           4 :             if (fun->needsExtraBodyVarEnvironment())
    1164           0 :                 return abort(AbortReason::Disable, "Extra var environment unsupported");
    1165             : 
    1166           4 :             if (fun->needsCallObject())
    1167           4 :                 MOZ_TRY_VAR(env, createCallObject(callee, env));
    1168             :         }
    1169           0 :     } else if (ModuleObject* module = info().module()) {
    1170             :         // Modules use a pre-created env object.
    1171           0 :         env = constant(ObjectValue(module->initialEnvironment()));
    1172             :     } else {
    1173             :         // For global scripts without a non-syntactic global scope, the env
    1174             :         // chain is the global lexical env.
    1175           0 :         MOZ_ASSERT(!script()->isForEval());
    1176           0 :         MOZ_ASSERT(!script()->hasNonSyntacticScope());
    1177           0 :         env = constant(ObjectValue(script()->global().lexicalEnvironment()));
    1178             :     }
    1179             : 
    1180             :     // Update the environment slot from UndefinedValue only after initial
    1181             :     // environment is created so that bailout doesn't see a partial env.
    1182             :     // See: |InitFromBailout|
    1183         143 :     current->setEnvironmentChain(env);
    1184         143 :     return Ok();
    1185             : }
    1186             : 
    1187             : void
    1188         135 : IonBuilder::initArgumentsObject()
    1189             : {
    1190         135 :     JitSpew(JitSpew_IonMIR, "%s:%" PRIuSIZE " - Emitting code to initialize arguments object! block=%p",
    1191         135 :             script()->filename(), script()->lineno(), current);
    1192         135 :     MOZ_ASSERT(info().needsArgsObj());
    1193             : 
    1194         135 :     bool mapped = script()->hasMappedArgsObj();
    1195         135 :     ArgumentsObject* templateObj = script()->compartment()->maybeArgumentsTemplateObject(mapped);
    1196             : 
    1197             :     MCreateArgumentsObject* argsObj =
    1198         135 :         MCreateArgumentsObject::New(alloc(), current->environmentChain(), templateObj);
    1199         135 :     current->add(argsObj);
    1200         135 :     current->setArgumentsObject(argsObj);
    1201         135 : }
    1202             : 
    1203             : AbortReasonOr<Ok>
    1204          72 : IonBuilder::addOsrValueTypeBarrier(uint32_t slot, MInstruction** def_,
    1205             :                                    MIRType type, TemporaryTypeSet* typeSet)
    1206             : {
    1207          72 :     MInstruction*& def = *def_;
    1208          72 :     MBasicBlock* osrBlock = def->block();
    1209             : 
    1210             :     // Clear bogus type information added in newOsrPreheader().
    1211          72 :     def->setResultType(MIRType::Value);
    1212          72 :     def->setResultTypeSet(nullptr);
    1213             : 
    1214          72 :     if (typeSet && !typeSet->unknown()) {
    1215          72 :         MInstruction* barrier = MTypeBarrier::New(alloc(), def, typeSet);
    1216          72 :         osrBlock->insertBefore(osrBlock->lastIns(), barrier);
    1217          72 :         osrBlock->rewriteSlot(slot, barrier);
    1218          72 :         def = barrier;
    1219             : 
    1220             :         // If the TypeSet is more precise than |type|, adjust |type| for the
    1221             :         // code below.
    1222          72 :         if (type == MIRType::Value)
    1223          26 :             type = barrier->type();
    1224           0 :     } else if (type == MIRType::Null ||
    1225           0 :                type == MIRType::Undefined ||
    1226             :                type == MIRType::MagicOptimizedArguments)
    1227             :     {
    1228             :         // No unbox instruction will be added below, so check the type by
    1229             :         // adding a type barrier for a singleton type set.
    1230           0 :         TypeSet::Type ntype = TypeSet::PrimitiveType(ValueTypeFromMIRType(type));
    1231           0 :         LifoAlloc* lifoAlloc = alloc().lifoAlloc();
    1232           0 :         typeSet = lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc, ntype);
    1233           0 :         if (!typeSet)
    1234           0 :             return abort(AbortReason::Alloc);
    1235           0 :         MInstruction* barrier = MTypeBarrier::New(alloc(), def, typeSet);
    1236           0 :         osrBlock->insertBefore(osrBlock->lastIns(), barrier);
    1237           0 :         osrBlock->rewriteSlot(slot, barrier);
    1238           0 :         def = barrier;
    1239             :     }
    1240             : 
    1241          72 :     switch (type) {
    1242             :       case MIRType::Boolean:
    1243             :       case MIRType::Int32:
    1244             :       case MIRType::Double:
    1245             :       case MIRType::String:
    1246             :       case MIRType::Symbol:
    1247             :       case MIRType::Object:
    1248          44 :         if (type != def->type()) {
    1249           0 :             MUnbox* unbox = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
    1250           0 :             osrBlock->insertBefore(osrBlock->lastIns(), unbox);
    1251           0 :             osrBlock->rewriteSlot(slot, unbox);
    1252           0 :             def = unbox;
    1253             :         }
    1254          44 :         break;
    1255             : 
    1256             :       case MIRType::Null:
    1257             :       {
    1258           0 :         MConstant* c = MConstant::New(alloc(), NullValue());
    1259           0 :         osrBlock->insertBefore(osrBlock->lastIns(), c);
    1260           0 :         osrBlock->rewriteSlot(slot, c);
    1261           0 :         def = c;
    1262           0 :         break;
    1263             :       }
    1264             : 
    1265             :       case MIRType::Undefined:
    1266             :       {
    1267           1 :         MConstant* c = MConstant::New(alloc(), UndefinedValue());
    1268           1 :         osrBlock->insertBefore(osrBlock->lastIns(), c);
    1269           1 :         osrBlock->rewriteSlot(slot, c);
    1270           1 :         def = c;
    1271           1 :         break;
    1272             :       }
    1273             : 
    1274             :       case MIRType::MagicOptimizedArguments:
    1275             :       {
    1276           1 :         MOZ_ASSERT(hasLazyArguments_);
    1277           1 :         MConstant* lazyArg = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
    1278           1 :         osrBlock->insertBefore(osrBlock->lastIns(), lazyArg);
    1279           1 :         osrBlock->rewriteSlot(slot, lazyArg);
    1280           1 :         def = lazyArg;
    1281           1 :         break;
    1282             :       }
    1283             : 
    1284             :       default:
    1285          26 :         break;
    1286             :     }
    1287             : 
    1288          72 :     MOZ_ASSERT(def == osrBlock->getSlot(slot));
    1289          72 :     return Ok();
    1290             : }
    1291             : 
    1292             : AbortReasonOr<Ok>
    1293         142 : IonBuilder::maybeAddOsrTypeBarriers()
    1294             : {
    1295         142 :     if (!info().osrPc())
    1296         138 :         return Ok();
    1297             : 
    1298             :     // The loop has successfully been processed, and the loop header phis
    1299             :     // have their final type. Add unboxes and type barriers in the OSR
    1300             :     // block to check that the values have the appropriate type, and update
    1301             :     // the types in the preheader.
    1302             : 
    1303           4 :     MBasicBlock* osrBlock = graph().osrBlock();
    1304           4 :     if (!osrBlock) {
    1305             :         // Because IonBuilder does not compile catch blocks, it's possible to
    1306             :         // end up without an OSR block if the OSR pc is only reachable via a
    1307             :         // break-statement inside the catch block. For instance:
    1308             :         //
    1309             :         //   for (;;) {
    1310             :         //       try {
    1311             :         //           throw 3;
    1312             :         //       } catch(e) {
    1313             :         //           break;
    1314             :         //       }
    1315             :         //   }
    1316             :         //   while (..) { } // <= OSR here, only reachable via catch block.
    1317             :         //
    1318             :         // For now we just abort in this case.
    1319           0 :         MOZ_ASSERT(graph().hasTryBlock());
    1320           0 :         return abort(AbortReason::Disable, "OSR block only reachable through catch block");
    1321             :     }
    1322             : 
    1323           4 :     MBasicBlock* preheader = osrBlock->getSuccessor(0);
    1324           4 :     MBasicBlock* header = preheader->getSuccessor(0);
    1325             :     static const size_t OSR_PHI_POSITION = 1;
    1326           4 :     MOZ_ASSERT(preheader->getPredecessor(OSR_PHI_POSITION) == osrBlock);
    1327             : 
    1328           4 :     MResumePoint* headerRp = header->entryResumePoint();
    1329           4 :     size_t stackDepth = headerRp->stackDepth();
    1330           4 :     MOZ_ASSERT(stackDepth == osrBlock->stackDepth());
    1331          79 :     for (uint32_t slot = info().startArgSlot(); slot < stackDepth; slot++) {
    1332             :         // Aliased slots are never accessed, since they need to go through
    1333             :         // the callobject. The typebarriers are added there and can be
    1334             :         // discarded here.
    1335          75 :         if (info().isSlotAliased(slot))
    1336           3 :             continue;
    1337             : 
    1338          72 :         if (!alloc().ensureBallast())
    1339           0 :             return abort(AbortReason::Alloc);
    1340             : 
    1341          72 :         MInstruction* def = osrBlock->getSlot(slot)->toInstruction();
    1342          72 :         MPhi* preheaderPhi = preheader->getSlot(slot)->toPhi();
    1343          72 :         MPhi* headerPhi = headerRp->getOperand(slot)->toPhi();
    1344             : 
    1345          72 :         MIRType type = headerPhi->type();
    1346          72 :         TemporaryTypeSet* typeSet = headerPhi->resultTypeSet();
    1347             : 
    1348          72 :         MOZ_TRY(addOsrValueTypeBarrier(slot, &def, type, typeSet));
    1349             : 
    1350          72 :         preheaderPhi->replaceOperand(OSR_PHI_POSITION, def);
    1351          72 :         preheaderPhi->setResultType(type);
    1352          72 :         preheaderPhi->setResultTypeSet(typeSet);
    1353             :     }
    1354             : 
    1355           4 :     return Ok();
    1356             : }
    1357             : 
    1358             : enum class CFGState : uint32_t {
    1359             :     Alloc = 0,
    1360             :     Abort = 1,
    1361             :     Success = 2
    1362             : };
    1363             : 
    1364             : static CFGState
    1365         179 : GetOrCreateControlFlowGraph(TempAllocator& tempAlloc, JSScript* script,
    1366             :                             const ControlFlowGraph** cfgOut)
    1367             : {
    1368         179 :     if (script->hasBaselineScript() && script->baselineScript()->controlFlowGraph()) {
    1369          29 :         *cfgOut = script->baselineScript()->controlFlowGraph();
    1370          29 :         return CFGState::Success;
    1371             :     }
    1372             : 
    1373         300 :     ControlFlowGenerator cfgenerator(tempAlloc, script);
    1374         150 :     if (!cfgenerator.init())
    1375           0 :         return CFGState::Alloc;
    1376             : 
    1377         150 :     if (!cfgenerator.traverseBytecode()) {
    1378           0 :         if (cfgenerator.aborted())
    1379           0 :             return CFGState::Abort;
    1380           0 :         return CFGState::Alloc;
    1381             :     }
    1382             : 
    1383             :     // If possible cache the control flow graph on the baseline script.
    1384         150 :     TempAllocator* graphAlloc = nullptr;
    1385         150 :     if (script->hasBaselineScript()) {
    1386          15 :         LifoAlloc& lifoAlloc = script->zone()->jitZone()->cfgSpace()->lifoAlloc();
    1387          30 :         LifoAlloc::AutoFallibleScope fallibleAllocator(&lifoAlloc);
    1388          15 :         graphAlloc = lifoAlloc.new_<TempAllocator>(&lifoAlloc);
    1389          15 :         if (!graphAlloc)
    1390           0 :             return CFGState::Alloc;
    1391             :     } else {
    1392         135 :         graphAlloc = &tempAlloc;
    1393             :     }
    1394             : 
    1395         150 :     ControlFlowGraph* cfg = cfgenerator.getGraph(*graphAlloc);
    1396         150 :     if (!cfg)
    1397           0 :         return CFGState::Alloc;
    1398             : 
    1399         150 :     if (script->hasBaselineScript()) {
    1400          15 :         MOZ_ASSERT(!script->baselineScript()->controlFlowGraph());
    1401          15 :         script->baselineScript()->setControlFlowGraph(cfg);
    1402             :     }
    1403             : 
    1404         150 :     if (JitSpewEnabled(JitSpew_CFG)) {
    1405           0 :         JitSpew(JitSpew_CFG, "Generating graph for %s:%" PRIuSIZE,
    1406           0 :                              script->filename(), script->lineno());
    1407           0 :         Fprinter& print = JitSpewPrinter();
    1408           0 :         cfg->dump(print, script);
    1409             :     }
    1410             : 
    1411         150 :     *cfgOut = cfg;
    1412         150 :     return CFGState::Success;
    1413             : }
    1414             : 
    1415             : // We traverse the bytecode using the control flow graph. This structure contains
    1416             : // a graph of CFGBlocks in RPO order.
    1417             : //
    1418             : // Per CFGBlock we take the corresponding MBasicBlock and start iterating the
    1419             : // bytecode of that CFGBlock. Each basic block has a mapping of local slots to
    1420             : // instructions, as well as a stack depth. As we encounter instructions we
    1421             : // mutate this mapping in the current block.
    1422             : //
    1423             : // Afterwards we visit the control flow instruction. There we add the ending ins
    1424             : // of the MBasicBlock and create new MBasicBlocks for the successors. That means
    1425             : // adding phi nodes for diamond join points, making sure to propagate types
    1426             : // around loops ...
    1427             : //
    1428             : // We keep a link between a CFGBlock and the entry MBasicBlock (in blockWorklist).
    1429             : // That vector only contains the MBasicBlocks that correspond with a CFGBlock.
    1430             : // We can create new MBasicBlocks that don't correspond to a CFGBlock.
    1431             : AbortReasonOr<Ok>
    1432         179 : IonBuilder::traverseBytecode()
    1433             : {
    1434         179 :     CFGState state = GetOrCreateControlFlowGraph(alloc(), info().script(), &cfg);
    1435         179 :     MOZ_ASSERT_IF(cfg && info().script()->hasBaselineScript(),
    1436             :                   info().script()->baselineScript()->controlFlowGraph() == cfg);
    1437         179 :     if (state == CFGState::Alloc)
    1438           0 :         return abort(AbortReason::Alloc);
    1439         179 :     if (state == CFGState::Abort)
    1440           0 :         return abort(AbortReason::Disable, "Couldn't create the CFG of script");
    1441             : 
    1442         179 :     if (!blockWorklist.growBy(cfg->numBlocks()))
    1443           0 :         return abort(AbortReason::Alloc);
    1444         179 :     blockWorklist[0] = current;
    1445             : 
    1446         179 :     size_t i = 0;
    1447        5593 :     while (i < cfg->numBlocks()) {
    1448        2713 :         if (!alloc().ensureBallast())
    1449           0 :             return abort(AbortReason::Alloc);
    1450             : 
    1451        2713 :         bool restarted = false;
    1452        2713 :         const CFGBlock* cfgblock = cfg->block(i);
    1453        2713 :         MBasicBlock* mblock = blockWorklist[i];
    1454        2713 :         MOZ_ASSERT(mblock && !mblock->isDead());
    1455             : 
    1456        2713 :         MOZ_TRY(visitBlock(cfgblock, mblock));
    1457        2710 :         MOZ_TRY(visitControlInstruction(cfgblock->stopIns(), &restarted));
    1458             : 
    1459        2707 :         if (restarted) {
    1460             :             // Move back to the start of the loop.
    1461          45 :             while (!blockWorklist[i] || blockWorklist[i]->isDead()) {
    1462          22 :                 MOZ_ASSERT(i > 0);
    1463          22 :                 i--;
    1464             :             }
    1465           1 :             MOZ_ASSERT(cfgblock->stopIns()->isBackEdge());
    1466           1 :             MOZ_ASSERT(loopHeaderStack_.back() == blockWorklist[i]);
    1467             :         } else {
    1468        2706 :             i++;
    1469             :         }
    1470             :     }
    1471             : 
    1472             : #ifdef DEBUG
    1473         173 :     MOZ_ASSERT(graph().numBlocks() >= blockWorklist.length());
    1474        2818 :     for (i = 0; i < cfg->numBlocks(); i++) {
    1475        2645 :         MOZ_ASSERT(blockWorklist[i]);
    1476        2645 :         MOZ_ASSERT(!blockWorklist[i]->isDead());
    1477        2645 :         MOZ_ASSERT_IF(i != 0, blockWorklist[i]->id() != 0);
    1478             :     }
    1479             : #endif
    1480             : 
    1481         173 :     cfg = nullptr;
    1482             : 
    1483         173 :     blockWorklist.clear();
    1484         173 :     return Ok();
    1485             : }
    1486             : 
    1487             : AbortReasonOr<Ok>
    1488        2713 : IonBuilder::visitBlock(const CFGBlock* cfgblock, MBasicBlock* mblock)
    1489             : {
    1490        2713 :     mblock->setLoopDepth(loopDepth_);
    1491             : 
    1492        2713 :     cfgCurrent = cfgblock;
    1493        2713 :     pc = cfgblock->startPc();
    1494             : 
    1495        2713 :     if (mblock->pc() && script()->hasScriptCounts())
    1496         666 :         mblock->setHitCount(script()->getHitCount(mblock->pc()));
    1497             : 
    1498             :     // Optimization to move a predecessor that only has this block as successor
    1499             :     // just before this block.  Skip this optimization if the previous block is
    1500             :     // not part of the same function, as we might have to backtrack on inlining
    1501             :     // failures.
    1502        3690 :     if (mblock->numPredecessors() == 1 && mblock->getPredecessor(0)->numSuccessors() == 1 &&
    1503         977 :         !mblock->getPredecessor(0)->outerResumePoint())
    1504             :     {
    1505         944 :         graph().removeBlockFromList(mblock->getPredecessor(0));
    1506         944 :         graph().addBlock(mblock->getPredecessor(0));
    1507             :     }
    1508             : 
    1509        2713 :     MOZ_TRY(setCurrentAndSpecializePhis(mblock));
    1510        2713 :     graph().addBlock(mblock);
    1511             : 
    1512       40951 :     while (pc < cfgblock->stopPc()) {
    1513       19122 :         if (!alloc().ensureBallast())
    1514           0 :             return abort(AbortReason::Alloc);
    1515             : 
    1516             : #ifdef DEBUG
    1517             :         // In debug builds, after compiling this op, check that all values
    1518             :         // popped by this opcode either:
    1519             :         //
    1520             :         //   (1) Have the ImplicitlyUsed flag set on them.
    1521             :         //   (2) Have more uses than before compiling this op (the value is
    1522             :         //       used as operand of a new MIR instruction).
    1523             :         //
    1524             :         // This is used to catch problems where IonBuilder pops a value without
    1525             :         // adding any SSA uses and doesn't call setImplicitlyUsedUnchecked on it.
    1526       38241 :         Vector<MDefinition*, 4, JitAllocPolicy> popped(alloc());
    1527       38241 :         Vector<size_t, 4, JitAllocPolicy> poppedUses(alloc());
    1528       19122 :         unsigned nuses = GetUseCount(script_, script_->pcToOffset(pc));
    1529             : 
    1530       32816 :         for (unsigned i = 0; i < nuses; i++) {
    1531       13694 :             MDefinition* def = current->peek(-int32_t(i + 1));
    1532       13694 :             if (!popped.append(def) || !poppedUses.append(def->defUseCount()))
    1533           0 :                 return abort(AbortReason::Alloc);
    1534             :         }
    1535             : #endif
    1536             : 
    1537             :         // Nothing in inspectOpcode() is allowed to advance the pc.
    1538       19122 :         JSOp op = JSOp(*pc);
    1539       19122 :         MOZ_TRY(inspectOpcode(op));
    1540             : 
    1541             : #ifdef DEBUG
    1542       32802 :         for (size_t i = 0; i < popped.length(); i++) {
    1543       13683 :             switch (op) {
    1544             :               case JSOP_POP:
    1545             :               case JSOP_POPN:
    1546             :               case JSOP_DUPAT:
    1547             :               case JSOP_DUP:
    1548             :               case JSOP_DUP2:
    1549             :               case JSOP_PICK:
    1550             :               case JSOP_UNPICK:
    1551             :               case JSOP_SWAP:
    1552             :               case JSOP_SETARG:
    1553             :               case JSOP_SETLOCAL:
    1554             :               case JSOP_INITLEXICAL:
    1555             :               case JSOP_SETRVAL:
    1556             :               case JSOP_VOID:
    1557             :                 // Don't require SSA uses for values popped by these ops.
    1558        3854 :                 break;
    1559             : 
    1560             :               case JSOP_POS:
    1561             :               case JSOP_TOID:
    1562             :               case JSOP_TOSTRING:
    1563             :                 // These ops may leave their input on the stack without setting
    1564             :                 // the ImplicitlyUsed flag. If this value will be popped immediately,
    1565             :                 // we may replace it with |undefined|, but the difference is
    1566             :                 // not observable.
    1567         168 :                 MOZ_ASSERT(i == 0);
    1568         168 :                 if (current->peek(-1) == popped[0])
    1569         165 :                     break;
    1570             :                 MOZ_FALLTHROUGH;
    1571             : 
    1572             :               default:
    1573        9664 :                 MOZ_ASSERT(popped[i]->isImplicitlyUsed() ||
    1574             : 
    1575             :                            // MNewDerivedTypedObject instances are
    1576             :                            // often dead unless they escape from the
    1577             :                            // fn. See IonBuilder::loadTypedObjectData()
    1578             :                            // for more details.
    1579             :                            popped[i]->isNewDerivedTypedObject() ||
    1580             : 
    1581             :                            popped[i]->defUseCount() > poppedUses[i]);
    1582        9664 :                 break;
    1583             :             }
    1584             :         }
    1585             : #endif
    1586             : 
    1587       19119 :         pc += CodeSpec[op].length;
    1588       19119 :         current->updateTrackedSite(bytecodeSite(pc));
    1589             :     }
    1590        2710 :     return Ok();
    1591             : }
    1592             : 
    1593             : bool
    1594        1070 : IonBuilder::blockIsOSREntry(const CFGBlock* block, const CFGBlock* predecessor)
    1595             : {
    1596        1070 :     jsbytecode* entryPc = block->startPc();
    1597             : 
    1598        1070 :     if (!info().osrPc())
    1599         865 :         return false;
    1600             : 
    1601         205 :     if (entryPc == predecessor->startPc()) {
    1602             :         // The predecessor is the actual osr entry block. Since it is empty
    1603             :         // the current block also starts a the osr pc. But it isn't the osr entry.
    1604          70 :         MOZ_ASSERT(predecessor->stopPc() == predecessor->startPc());
    1605          70 :         return false;
    1606             :     }
    1607             : 
    1608         135 :     if (block->stopPc() == block->startPc() && block->stopIns()->isBackEdge()) {
    1609             :         // An empty block with only a backedge can never be a loop entry.
    1610           0 :         return false;
    1611             :     }
    1612             : 
    1613         135 :     MOZ_ASSERT(*info().osrPc() == JSOP_LOOPENTRY);
    1614             :     // Skip over the LOOPENTRY to match.
    1615         135 :     return GetNextPc(info().osrPc()) == entryPc;
    1616             : }
    1617             : 
    1618             : AbortReasonOr<Ok>
    1619         952 : IonBuilder::visitGoto(CFGGoto* ins)
    1620             : {
    1621             :     // Test if this potentially was a fake loop and create OSR entry if that is
    1622             :     // the case.
    1623         952 :     const CFGBlock* successor = ins->getSuccessor(0);
    1624         952 :     if (blockIsOSREntry(successor, cfgCurrent)) {
    1625             :         MBasicBlock* preheader;
    1626           0 :         MOZ_TRY_VAR(preheader, newOsrPreheader(current, successor->startPc(), pc));
    1627           0 :         current->end(MGoto::New(alloc(), preheader));
    1628           0 :         MOZ_TRY(setCurrentAndSpecializePhis(preheader));
    1629             :     }
    1630             : 
    1631         952 :     size_t id = successor->id();
    1632         952 :     bool create = !blockWorklist[id] || blockWorklist[id]->isDead();
    1633             : 
    1634         952 :     current->popn(ins->popAmount());
    1635             : 
    1636         952 :     if (create)
    1637         287 :         MOZ_TRY_VAR(blockWorklist[id], newBlock(current, successor->startPc()));
    1638             : 
    1639         952 :     MBasicBlock* succ = blockWorklist[id];
    1640         952 :     current->end(MGoto::New(alloc(), succ));
    1641             : 
    1642         952 :     if (!create) {
    1643         665 :         if (!succ->addPredecessor(alloc(), current))
    1644           0 :             return abort(AbortReason::Alloc);
    1645             :     }
    1646             : 
    1647         952 :     return Ok();
    1648             : }
    1649             : 
    1650             : AbortReasonOr<Ok>
    1651         117 : IonBuilder::visitBackEdge(CFGBackEdge* ins, bool* restarted)
    1652             : {
    1653         117 :     loopDepth_--;
    1654             : 
    1655         117 :     MBasicBlock* loopEntry = blockWorklist[ins->getSuccessor(0)->id()];
    1656         117 :     current->end(MGoto::New(alloc(), loopEntry));
    1657             : 
    1658         117 :     MOZ_ASSERT(ins->getSuccessor(0) == cfgLoopHeaderStack_.back());
    1659             : 
    1660             :     // Compute phis in the loop header and propagate them throughout the loop,
    1661             :     // including the successor.
    1662         117 :     AbortReason r = loopEntry->setBackedge(alloc(), current);
    1663         117 :     switch (r) {
    1664             :       case AbortReason::NoAbort:
    1665         116 :         loopHeaderStack_.popBack();
    1666             : #ifdef DEBUG
    1667         116 :         cfgLoopHeaderStack_.popBack();
    1668             : #endif
    1669         116 :         return Ok();
    1670             : 
    1671             :       case AbortReason::Disable:
    1672             :         // If there are types for variables on the backedge that were not
    1673             :         // present at the original loop header, then uses of the variables'
    1674             :         // phis may have generated incorrect nodes. The new types have been
    1675             :         // incorporated into the header phis, so remove all blocks for the
    1676             :         // loop body and restart with the new types.
    1677           1 :         *restarted = true;
    1678           1 :         MOZ_TRY(restartLoop(ins->getSuccessor(0)));
    1679           1 :         return Ok();
    1680             : 
    1681             :       default:
    1682           0 :         return abort(r);
    1683             :     }
    1684             : }
    1685             : 
    1686             : AbortReasonOr<Ok>
    1687         118 : IonBuilder::visitLoopEntry(CFGLoopEntry* loopEntry)
    1688             : {
    1689         118 :     unsigned stackPhiCount = loopEntry->stackPhiCount();
    1690         118 :     const CFGBlock* successor = loopEntry->getSuccessor(0);
    1691         118 :     bool osr = blockIsOSREntry(successor, cfgCurrent);
    1692         118 :     if (osr) {
    1693           4 :         MOZ_ASSERT(loopEntry->canOsr());
    1694             :         MBasicBlock* preheader;
    1695           4 :         MOZ_TRY_VAR(preheader, newOsrPreheader(current, successor->startPc(), pc));
    1696           4 :         current->end(MGoto::New(alloc(), preheader));
    1697           4 :         MOZ_TRY(setCurrentAndSpecializePhis(preheader));
    1698             :     }
    1699             : 
    1700         118 :     loopDepth_++;
    1701             :     MBasicBlock* header;
    1702         118 :     MOZ_TRY_VAR(header, newPendingLoopHeader(current, successor->startPc(), osr,
    1703             :                                              loopEntry->canOsr(), stackPhiCount));
    1704         118 :     blockWorklist[successor->id()] = header;
    1705             : 
    1706         118 :     current->end(MGoto::New(alloc(), header));
    1707             : 
    1708         118 :     if (!loopHeaderStack_.append(header))
    1709           0 :         return abort(AbortReason::Alloc);
    1710             : #ifdef DEBUG
    1711         118 :     if (!cfgLoopHeaderStack_.append(successor))
    1712           0 :         return abort(AbortReason::Alloc);
    1713             : #endif
    1714             : 
    1715         118 :     MOZ_TRY(analyzeNewLoopTypes(cfgCurrent));
    1716             : 
    1717         118 :     setCurrent(header);
    1718         118 :     pc = header->pc();
    1719             : 
    1720         118 :     initLoopEntry();
    1721         118 :     return Ok();
    1722             : }
    1723             : 
    1724             : bool
    1725         119 : IonBuilder::initLoopEntry()
    1726             : {
    1727         119 :     current->add(MInterruptCheck::New(alloc()));
    1728         119 :     insertRecompileCheck();
    1729             : 
    1730         119 :     return true;
    1731             : }
    1732             : 
    1733             : AbortReasonOr<Ok>
    1734        2710 : IonBuilder::visitControlInstruction(CFGControlInstruction* ins, bool* restarted)
    1735             : {
    1736        2710 :     switch (ins->type()) {
    1737             :       case CFGControlInstruction::Type_Test:
    1738         912 :         return visitTest(ins->toTest());
    1739             :       case CFGControlInstruction::Type_Compare:
    1740           0 :         return visitCompare(ins->toCompare());
    1741             :       case CFGControlInstruction::Type_Goto:
    1742         952 :         return visitGoto(ins->toGoto());
    1743             :       case CFGControlInstruction::Type_BackEdge:
    1744         117 :         return visitBackEdge(ins->toBackEdge(), restarted);
    1745             :       case CFGControlInstruction::Type_LoopEntry:
    1746         118 :         return visitLoopEntry(ins->toLoopEntry());
    1747             :       case CFGControlInstruction::Type_Return:
    1748             :       case CFGControlInstruction::Type_RetRVal:
    1749         561 :         return visitReturn(ins);
    1750             :       case CFGControlInstruction::Type_Try:
    1751           5 :         return visitTry(ins->toTry());
    1752             :       case CFGControlInstruction::Type_Throw:
    1753           1 :         return visitThrow(ins->toThrow());
    1754             :       case CFGControlInstruction::Type_TableSwitch:
    1755          44 :         return visitTableSwitch(ins->toTableSwitch());
    1756             :     }
    1757           0 :     MOZ_CRASH("Unknown Control Instruction");
    1758             : }
    1759             : 
    1760             : AbortReasonOr<Ok>
    1761       19122 : IonBuilder::inspectOpcode(JSOp op)
    1762             : {
    1763       19122 :     MOZ_ASSERT(analysis_.maybeInfo(pc), "Compiling unreachable op");
    1764             : 
    1765             :     // Add not yet implemented opcodes at the bottom of the switch!
    1766       19122 :     switch (op) {
    1767             :       case JSOP_NOP:
    1768             :       case JSOP_NOP_DESTRUCTURING:
    1769             :       case JSOP_TRY_DESTRUCTURING_ITERCLOSE:
    1770             :       case JSOP_LINENO:
    1771             :       case JSOP_JUMPTARGET:
    1772             :       case JSOP_LABEL:
    1773        2232 :         return Ok();
    1774             : 
    1775             :       case JSOP_UNDEFINED:
    1776             :         // If this ever changes, change what JSOP_GIMPLICITTHIS does too.
    1777         838 :         pushConstant(UndefinedValue());
    1778         838 :         return Ok();
    1779             : 
    1780             :       case JSOP_IFNE:
    1781             :       case JSOP_IFEQ:
    1782             :       case JSOP_RETURN:
    1783             :       case JSOP_RETRVAL:
    1784             :       case JSOP_AND:
    1785             :       case JSOP_OR:
    1786             :       case JSOP_TRY:
    1787             :       case JSOP_THROW:
    1788             :       case JSOP_GOTO:
    1789             :       case JSOP_CONDSWITCH:
    1790             :       case JSOP_LOOPENTRY:
    1791             :       case JSOP_TABLESWITCH:
    1792             :       case JSOP_CASE:
    1793             :       case JSOP_DEFAULT:
    1794             :         // Control flow opcodes should be handled in the ControlFlowGenerator.
    1795           0 :         MOZ_CRASH("Shouldn't encounter this opcode.");
    1796             : 
    1797             :       case JSOP_BITNOT:
    1798           6 :         return jsop_bitnot();
    1799             : 
    1800             :       case JSOP_BITAND:
    1801             :       case JSOP_BITOR:
    1802             :       case JSOP_BITXOR:
    1803             :       case JSOP_LSH:
    1804             :       case JSOP_RSH:
    1805             :       case JSOP_URSH:
    1806           2 :         return jsop_bitop(op);
    1807             : 
    1808             :       case JSOP_ADD:
    1809             :       case JSOP_SUB:
    1810             :       case JSOP_MUL:
    1811             :       case JSOP_DIV:
    1812             :       case JSOP_MOD:
    1813         246 :         return jsop_binary_arith(op);
    1814             : 
    1815             :       case JSOP_POW:
    1816           0 :         return jsop_pow();
    1817             : 
    1818             :       case JSOP_POS:
    1819         168 :         return jsop_pos();
    1820             : 
    1821             :       case JSOP_NEG:
    1822           0 :         return jsop_neg();
    1823             : 
    1824             :       case JSOP_TOSTRING:
    1825           0 :         return jsop_tostring();
    1826             : 
    1827             :       case JSOP_DEFVAR:
    1828           0 :         return jsop_defvar(GET_UINT32_INDEX(pc));
    1829             : 
    1830             :       case JSOP_DEFLET:
    1831             :       case JSOP_DEFCONST:
    1832           0 :         return jsop_deflexical(GET_UINT32_INDEX(pc));
    1833             : 
    1834             :       case JSOP_DEFFUN:
    1835           0 :         return jsop_deffun(GET_UINT32_INDEX(pc));
    1836             : 
    1837             :       case JSOP_EQ:
    1838             :       case JSOP_NE:
    1839             :       case JSOP_STRICTEQ:
    1840             :       case JSOP_STRICTNE:
    1841             :       case JSOP_LT:
    1842             :       case JSOP_LE:
    1843             :       case JSOP_GT:
    1844             :       case JSOP_GE:
    1845         486 :         return jsop_compare(op);
    1846             : 
    1847             :       case JSOP_DOUBLE:
    1848          26 :         pushConstant(info().getConst(pc));
    1849          26 :         return Ok();
    1850             : 
    1851             :       case JSOP_STRING:
    1852         137 :         pushConstant(StringValue(info().getAtom(pc)));
    1853         137 :         return Ok();
    1854             : 
    1855             :       case JSOP_SYMBOL: {
    1856           5 :         unsigned which = GET_UINT8(pc);
    1857           5 :         JS::Symbol* sym = compartment->runtime()->wellKnownSymbols().get(which);
    1858           5 :         pushConstant(SymbolValue(sym));
    1859           5 :         return Ok();
    1860             :       }
    1861             : 
    1862             :       case JSOP_ZERO:
    1863         787 :         pushConstant(Int32Value(0));
    1864         787 :         return Ok();
    1865             : 
    1866             :       case JSOP_ONE:
    1867         558 :         pushConstant(Int32Value(1));
    1868         558 :         return Ok();
    1869             : 
    1870             :       case JSOP_NULL:
    1871          44 :         pushConstant(NullValue());
    1872          44 :         return Ok();
    1873             : 
    1874             :       case JSOP_VOID:
    1875          90 :         current->pop();
    1876          90 :         pushConstant(UndefinedValue());
    1877          90 :         return Ok();
    1878             : 
    1879             :       case JSOP_HOLE:
    1880           0 :         pushConstant(MagicValue(JS_ELEMENTS_HOLE));
    1881           0 :         return Ok();
    1882             : 
    1883             :       case JSOP_FALSE:
    1884          75 :         pushConstant(BooleanValue(false));
    1885          75 :         return Ok();
    1886             : 
    1887             :       case JSOP_TRUE:
    1888          99 :         pushConstant(BooleanValue(true));
    1889          99 :         return Ok();
    1890             : 
    1891             :       case JSOP_ARGUMENTS:
    1892         136 :         return jsop_arguments();
    1893             : 
    1894             :       case JSOP_RUNONCE:
    1895           0 :         return jsop_runonce();
    1896             : 
    1897             :       case JSOP_REST:
    1898           2 :         return jsop_rest();
    1899             : 
    1900             :       case JSOP_GETARG:
    1901         390 :         if (info().argsObjAliasesFormals()) {
    1902          10 :             MGetArgumentsObjectArg* getArg = MGetArgumentsObjectArg::New(alloc(),
    1903          20 :                                                                          current->argumentsObject(),
    1904          30 :                                                                          GET_ARGNO(pc));
    1905          10 :             current->add(getArg);
    1906          10 :             current->push(getArg);
    1907             :         } else {
    1908         380 :             current->pushArg(GET_ARGNO(pc));
    1909             :         }
    1910         390 :         return Ok();
    1911             : 
    1912             :       case JSOP_SETARG:
    1913          20 :         return jsop_setarg(GET_ARGNO(pc));
    1914             : 
    1915             :       case JSOP_GETLOCAL:
    1916        3418 :         current->pushLocal(GET_LOCALNO(pc));
    1917        3418 :         return Ok();
    1918             : 
    1919             :       case JSOP_SETLOCAL:
    1920        1232 :         current->setLocal(GET_LOCALNO(pc));
    1921        1232 :         return Ok();
    1922             : 
    1923             :       case JSOP_THROWSETCONST:
    1924             :       case JSOP_THROWSETALIASEDCONST:
    1925             :       case JSOP_THROWSETCALLEE:
    1926           0 :         return jsop_throwsetconst();
    1927             : 
    1928             :       case JSOP_CHECKLEXICAL:
    1929           0 :         return jsop_checklexical();
    1930             : 
    1931             :       case JSOP_INITLEXICAL:
    1932          65 :         current->setLocal(GET_LOCALNO(pc));
    1933          65 :         return Ok();
    1934             : 
    1935             :       case JSOP_INITGLEXICAL: {
    1936           0 :         MOZ_ASSERT(!script()->hasNonSyntacticScope());
    1937           0 :         MDefinition* value = current->pop();
    1938           0 :         current->push(constant(ObjectValue(script()->global().lexicalEnvironment())));
    1939           0 :         current->push(value);
    1940           0 :         return jsop_setprop(info().getAtom(pc)->asPropertyName());
    1941             :       }
    1942             : 
    1943             :       case JSOP_CHECKALIASEDLEXICAL:
    1944           2 :         return jsop_checkaliasedlexical(EnvironmentCoordinate(pc));
    1945             : 
    1946             :       case JSOP_INITALIASEDLEXICAL:
    1947           5 :         return jsop_setaliasedvar(EnvironmentCoordinate(pc));
    1948             : 
    1949             :       case JSOP_UNINITIALIZED:
    1950          19 :         pushConstant(MagicValue(JS_UNINITIALIZED_LEXICAL));
    1951          19 :         return Ok();
    1952             : 
    1953             :       case JSOP_POP: {
    1954        2011 :         MDefinition* def = current->pop();
    1955             : 
    1956             :         // POP opcodes frequently appear where values are killed, e.g. after
    1957             :         // SET* opcodes. Place a resume point afterwards to avoid capturing
    1958             :         // the dead value in later snapshots, except in places where that
    1959             :         // resume point is obviously unnecessary.
    1960        2011 :         if (pc[JSOP_POP_LENGTH] == JSOP_POP)
    1961         150 :             return Ok();
    1962        1861 :         if (def->isConstant())
    1963         311 :             return Ok();
    1964        1550 :         return maybeInsertResume();
    1965             :       }
    1966             : 
    1967             :       case JSOP_POPN:
    1968           0 :         for (uint32_t i = 0, n = GET_UINT16(pc); i < n; i++)
    1969           0 :             current->pop();
    1970           0 :         return Ok();
    1971             : 
    1972             :       case JSOP_DUPAT:
    1973           8 :         current->pushSlot(current->stackDepth() - 1 - GET_UINT24(pc));
    1974           8 :         return Ok();
    1975             : 
    1976             :       case JSOP_NEWINIT:
    1977           0 :         if (GET_UINT8(pc) == JSProto_Array)
    1978           0 :             return jsop_newarray(0);
    1979           0 :         return jsop_newobject();
    1980             : 
    1981             :       case JSOP_NEWARRAY:
    1982             :       case JSOP_SPREADCALLARRAY:
    1983          11 :         return jsop_newarray(GET_UINT32(pc));
    1984             : 
    1985             :       case JSOP_NEWARRAY_COPYONWRITE:
    1986           0 :         return jsop_newarray_copyonwrite();
    1987             : 
    1988             :       case JSOP_NEWOBJECT:
    1989           8 :         return jsop_newobject();
    1990             : 
    1991             :       case JSOP_INITELEM:
    1992             :       case JSOP_INITHIDDENELEM:
    1993          54 :         return jsop_initelem();
    1994             : 
    1995             :       case JSOP_INITELEM_INC:
    1996           0 :         return jsop_initelem_inc();
    1997             : 
    1998             :       case JSOP_INITELEM_ARRAY:
    1999          14 :         return jsop_initelem_array();
    2000             : 
    2001             :       case JSOP_INITPROP:
    2002             :       case JSOP_INITLOCKEDPROP:
    2003             :       case JSOP_INITHIDDENPROP:
    2004             :       {
    2005          18 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2006          18 :         return jsop_initprop(name);
    2007             :       }
    2008             : 
    2009             :       case JSOP_MUTATEPROTO:
    2010             :       {
    2011           0 :         return jsop_mutateproto();
    2012             :       }
    2013             : 
    2014             :       case JSOP_INITPROP_GETTER:
    2015             :       case JSOP_INITHIDDENPROP_GETTER:
    2016             :       case JSOP_INITPROP_SETTER:
    2017             :       case JSOP_INITHIDDENPROP_SETTER: {
    2018           0 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2019           0 :         return jsop_initprop_getter_setter(name);
    2020             :       }
    2021             : 
    2022             :       case JSOP_INITELEM_GETTER:
    2023             :       case JSOP_INITHIDDENELEM_GETTER:
    2024             :       case JSOP_INITELEM_SETTER:
    2025             :       case JSOP_INITHIDDENELEM_SETTER:
    2026           0 :         return jsop_initelem_getter_setter();
    2027             : 
    2028             :       case JSOP_FUNCALL:
    2029           2 :         return jsop_funcall(GET_ARGC(pc));
    2030             : 
    2031             :       case JSOP_FUNAPPLY:
    2032          44 :         return jsop_funapply(GET_ARGC(pc));
    2033             : 
    2034             :       case JSOP_SPREADCALL:
    2035           0 :         return jsop_spreadcall();
    2036             : 
    2037             :       case JSOP_CALL:
    2038             :       case JSOP_CALL_IGNORES_RV:
    2039             :       case JSOP_CALLITER:
    2040             :       case JSOP_NEW:
    2041        1228 :         MOZ_TRY(jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL,
    2042             :                           (JSOp)*pc == JSOP_CALL_IGNORES_RV));
    2043        1225 :         if (op == JSOP_CALLITER) {
    2044           5 :             if (!outermostBuilder()->iterators_.append(current->peek(-1)))
    2045           0 :                 return abort(AbortReason::Alloc);
    2046             :         }
    2047        1225 :         return Ok();
    2048             : 
    2049             :       case JSOP_EVAL:
    2050             :       case JSOP_STRICTEVAL:
    2051           0 :         return jsop_eval(GET_ARGC(pc));
    2052             : 
    2053             :       case JSOP_INT8:
    2054         551 :         pushConstant(Int32Value(GET_INT8(pc)));
    2055         551 :         return Ok();
    2056             : 
    2057             :       case JSOP_UINT16:
    2058          25 :         pushConstant(Int32Value(GET_UINT16(pc)));
    2059          25 :         return Ok();
    2060             : 
    2061             :       case JSOP_GETGNAME:
    2062             :       {
    2063          45 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2064          45 :         if (!script()->hasNonSyntacticScope())
    2065          45 :             return jsop_getgname(name);
    2066           0 :         return jsop_getname(name);
    2067             :       }
    2068             : 
    2069             :       case JSOP_SETGNAME:
    2070             :       case JSOP_STRICTSETGNAME:
    2071             :       {
    2072           3 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2073           3 :         JSObject* obj = nullptr;
    2074           3 :         if (!script()->hasNonSyntacticScope())
    2075           3 :             obj = testGlobalLexicalBinding(name);
    2076           3 :         if (obj)
    2077           3 :             return setStaticName(obj, name);
    2078           0 :         return jsop_setprop(name);
    2079             :       }
    2080             : 
    2081             :       case JSOP_GETNAME:
    2082             :       {
    2083           6 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2084           6 :         return jsop_getname(name);
    2085             :       }
    2086             : 
    2087             :       case JSOP_GETINTRINSIC:
    2088             :       {
    2089         867 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2090         867 :         return jsop_intrinsic(name);
    2091             :       }
    2092             : 
    2093             :       case JSOP_GETIMPORT:
    2094             :       {
    2095           0 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2096           0 :         return jsop_getimport(name);
    2097             :       }
    2098             : 
    2099             :       case JSOP_BINDGNAME:
    2100           3 :         if (!script()->hasNonSyntacticScope()) {
    2101           3 :             if (JSObject* env = testGlobalLexicalBinding(info().getName(pc))) {
    2102           3 :                 pushConstant(ObjectValue(*env));
    2103           3 :                 return Ok();
    2104             :             }
    2105             :         }
    2106             :         // Fall through to JSOP_BINDNAME
    2107             :         MOZ_FALLTHROUGH;
    2108             :       case JSOP_BINDNAME:
    2109           0 :         return jsop_bindname(info().getName(pc));
    2110             : 
    2111             :       case JSOP_BINDVAR:
    2112           0 :         return jsop_bindvar();
    2113             : 
    2114             :       case JSOP_DUP:
    2115         260 :         current->pushSlot(current->stackDepth() - 1);
    2116         260 :         return Ok();
    2117             : 
    2118             :       case JSOP_DUP2:
    2119           0 :         return jsop_dup2();
    2120             : 
    2121             :       case JSOP_SWAP:
    2122          71 :         current->swapAt(-1);
    2123          71 :         return Ok();
    2124             : 
    2125             :       case JSOP_PICK:
    2126           3 :         current->pick(-GET_INT8(pc));
    2127           3 :         return Ok();
    2128             : 
    2129             :       case JSOP_UNPICK:
    2130           6 :         current->unpick(-GET_INT8(pc));
    2131           6 :         return Ok();
    2132             : 
    2133             :       case JSOP_GETALIASEDVAR:
    2134         633 :         return jsop_getaliasedvar(EnvironmentCoordinate(pc));
    2135             : 
    2136             :       case JSOP_SETALIASEDVAR:
    2137          53 :         return jsop_setaliasedvar(EnvironmentCoordinate(pc));
    2138             : 
    2139             :       case JSOP_UINT24:
    2140           0 :         pushConstant(Int32Value(GET_UINT24(pc)));
    2141           0 :         return Ok();
    2142             : 
    2143             :       case JSOP_INT32:
    2144           0 :         pushConstant(Int32Value(GET_INT32(pc)));
    2145           0 :         return Ok();
    2146             : 
    2147             :       case JSOP_LOOPHEAD:
    2148             :         // JSOP_LOOPHEAD is handled when processing the loop header.
    2149           0 :         MOZ_CRASH("JSOP_LOOPHEAD outside loop");
    2150             : 
    2151             :       case JSOP_GETELEM:
    2152             :       case JSOP_CALLELEM:
    2153         898 :         MOZ_TRY(jsop_getelem());
    2154         898 :         if (op == JSOP_CALLELEM)
    2155           5 :             MOZ_TRY(improveThisTypesForCall());
    2156         898 :         return Ok();
    2157             : 
    2158             :       case JSOP_SETELEM:
    2159             :       case JSOP_STRICTSETELEM:
    2160           0 :         return jsop_setelem();
    2161             : 
    2162             :       case JSOP_LENGTH:
    2163         324 :         return jsop_length();
    2164             : 
    2165             :       case JSOP_NOT:
    2166         127 :         return jsop_not();
    2167             : 
    2168             :       case JSOP_FUNCTIONTHIS:
    2169         111 :         return jsop_functionthis();
    2170             : 
    2171             :       case JSOP_GLOBALTHIS:
    2172           0 :         return jsop_globalthis();
    2173             : 
    2174             :       case JSOP_CALLEE: {
    2175          22 :          MDefinition* callee = getCallee();
    2176          22 :          current->push(callee);
    2177          22 :          return Ok();
    2178             :       }
    2179             : 
    2180             :       case JSOP_GETPROP:
    2181             :       case JSOP_CALLPROP:
    2182             :       {
    2183         164 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2184         164 :         MOZ_TRY(jsop_getprop(name));
    2185         164 :         if (op == JSOP_CALLPROP)
    2186          63 :             MOZ_TRY(improveThisTypesForCall());
    2187         164 :         return Ok();
    2188             :       }
    2189             : 
    2190             :       case JSOP_SETPROP:
    2191             :       case JSOP_STRICTSETPROP:
    2192             :       case JSOP_SETNAME:
    2193             :       case JSOP_STRICTSETNAME:
    2194             :       {
    2195          65 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2196          65 :         return jsop_setprop(name);
    2197             :       }
    2198             : 
    2199             :       case JSOP_DELPROP:
    2200             :       case JSOP_STRICTDELPROP:
    2201             :       {
    2202           0 :         PropertyName* name = info().getAtom(pc)->asPropertyName();
    2203           0 :         return jsop_delprop(name);
    2204             :       }
    2205             : 
    2206             :       case JSOP_DELELEM:
    2207             :       case JSOP_STRICTDELELEM:
    2208           0 :         return jsop_delelem();
    2209             : 
    2210             :       case JSOP_REGEXP:
    2211           0 :         return jsop_regexp(info().getRegExp(pc));
    2212             : 
    2213             :       case JSOP_CALLSITEOBJ:
    2214           0 :         if (info().analysisMode() == Analysis_ArgumentsUsage) {
    2215             :             // When analyzing arguments usage, it is possible that the
    2216             :             // template object is not yet canonicalized. Push an incorrect
    2217             :             // object; it does not matter for arguments analysis.
    2218           0 :             pushConstant(ObjectValue(*info().getObject(pc)));
    2219             :         } else {
    2220           0 :             JSObject* raw = script()->getObject(GET_UINT32_INDEX(pc) + 1);
    2221           0 :             JSObject* obj = script()->compartment()->getExistingTemplateLiteralObject(raw);
    2222           0 :             pushConstant(ObjectValue(*obj));
    2223             :         }
    2224           0 :         return Ok();
    2225             : 
    2226             :       case JSOP_OBJECT:
    2227           0 :         return jsop_object(info().getObject(pc));
    2228             : 
    2229             :       case JSOP_TYPEOF:
    2230             :       case JSOP_TYPEOFEXPR:
    2231           3 :         return jsop_typeof();
    2232             : 
    2233             :       case JSOP_TOASYNC:
    2234           0 :         return jsop_toasync();
    2235             : 
    2236             :       case JSOP_TOASYNCGEN:
    2237           0 :         return jsop_toasyncgen();
    2238             : 
    2239             :       case JSOP_TOASYNCITER:
    2240           0 :         return jsop_toasynciter();
    2241             : 
    2242             :       case JSOP_TOID:
    2243           0 :         return jsop_toid();
    2244             : 
    2245             :       case JSOP_LAMBDA:
    2246          28 :         return jsop_lambda(info().getFunction(pc));
    2247             : 
    2248             :       case JSOP_LAMBDA_ARROW:
    2249           1 :         return jsop_lambda_arrow(info().getFunction(pc));
    2250             : 
    2251             :       case JSOP_SETFUNNAME:
    2252           0 :         return jsop_setfunname(GET_UINT8(pc));
    2253             : 
    2254             :       case JSOP_PUSHLEXICALENV:
    2255           4 :         return jsop_pushlexicalenv(GET_UINT32_INDEX(pc));
    2256             : 
    2257             :       case JSOP_POPLEXICALENV:
    2258           3 :         current->setEnvironmentChain(walkEnvironmentChain(1));
    2259           3 :         return Ok();
    2260             : 
    2261             :       case JSOP_FRESHENLEXICALENV:
    2262           0 :         return jsop_copylexicalenv(true);
    2263             : 
    2264             :       case JSOP_RECREATELEXICALENV:
    2265           1 :         return jsop_copylexicalenv(false);
    2266             : 
    2267             :       case JSOP_ITER:
    2268           0 :         return jsop_iter(GET_INT8(pc));
    2269             : 
    2270             :       case JSOP_MOREITER:
    2271           0 :         return jsop_itermore();
    2272             : 
    2273             :       case JSOP_ISNOITER:
    2274           0 :         return jsop_isnoiter();
    2275             : 
    2276             :       case JSOP_ENDITER:
    2277           0 :         return jsop_iterend();
    2278             : 
    2279             :       case JSOP_IN:
    2280          91 :         return jsop_in();
    2281             : 
    2282             :       case JSOP_HASOWN:
    2283           0 :         return jsop_hasown();
    2284             : 
    2285             :       case JSOP_SETRVAL:
    2286           9 :         MOZ_ASSERT(!script()->noScriptRval());
    2287           9 :         current->setSlot(info().returnValueSlot(), current->pop());
    2288           9 :         return Ok();
    2289             : 
    2290             :       case JSOP_INSTANCEOF:
    2291           0 :         return jsop_instanceof();
    2292             : 
    2293             :       case JSOP_DEBUGLEAVELEXICALENV:
    2294          11 :         return Ok();
    2295             : 
    2296             :       case JSOP_DEBUGGER:
    2297           0 :         return jsop_debugger();
    2298             : 
    2299             :       case JSOP_GIMPLICITTHIS:
    2300          11 :         if (!script()->hasNonSyntacticScope()) {
    2301          11 :             pushConstant(UndefinedValue());
    2302          11 :             return Ok();
    2303             :         }
    2304             : 
    2305             :         // Just fall through to the unsupported bytecode case.
    2306           0 :         break;
    2307             : 
    2308             :       case JSOP_NEWTARGET:
    2309          23 :         return jsop_newtarget();
    2310             : 
    2311             :       case JSOP_CHECKISOBJ:
    2312          15 :         return jsop_checkisobj(GET_UINT8(pc));
    2313             : 
    2314             :       case JSOP_CHECKISCALLABLE:
    2315           0 :         return jsop_checkiscallable(GET_UINT8(pc));
    2316             : 
    2317             :       case JSOP_CHECKOBJCOERCIBLE:
    2318           0 :         return jsop_checkobjcoercible();
    2319             : 
    2320             :       case JSOP_DEBUGCHECKSELFHOSTED:
    2321             :       {
    2322             : #ifdef DEBUG
    2323          59 :         MDebugCheckSelfHosted* check = MDebugCheckSelfHosted::New(alloc(), current->pop());
    2324          59 :         current->add(check);
    2325          59 :         current->push(check);
    2326          59 :         MOZ_TRY(resumeAfter(check));
    2327             : #endif
    2328          59 :         return Ok();
    2329             :       }
    2330             : 
    2331             :       case JSOP_IS_CONSTRUCTING:
    2332         140 :         pushConstant(MagicValue(JS_IS_CONSTRUCTING));
    2333         140 :         return Ok();
    2334             : 
    2335             :       case JSOP_OPTIMIZE_SPREADCALL:
    2336             :       {
    2337             :         // Assuming optimization isn't available doesn't affect correctness.
    2338             :         // TODO: Investigate dynamic checks.
    2339           0 :         MDefinition* arr = current->peek(-1);
    2340           0 :         arr->setImplicitlyUsedUnchecked();
    2341           0 :         pushConstant(BooleanValue(false));
    2342           0 :         return Ok();
    2343             :       }
    2344             : 
    2345             :       // ===== NOT Yet Implemented =====
    2346             :       // Read below!
    2347             : 
    2348             :       // With
    2349             :       case JSOP_ENTERWITH:
    2350             :       case JSOP_LEAVEWITH:
    2351             : 
    2352             :       // Spread
    2353             :       case JSOP_SPREADNEW:
    2354             :       case JSOP_SPREADEVAL:
    2355             :       case JSOP_STRICTSPREADEVAL:
    2356             : 
    2357             :       // Classes
    2358             :       case JSOP_CHECKCLASSHERITAGE:
    2359             :       case JSOP_FUNWITHPROTO:
    2360             :       case JSOP_OBJWITHPROTO:
    2361             :       case JSOP_BUILTINPROTO:
    2362             :       case JSOP_INITHOMEOBJECT:
    2363             :       case JSOP_CLASSCONSTRUCTOR:
    2364             :       case JSOP_DERIVEDCONSTRUCTOR:
    2365             :       case JSOP_CHECKTHIS:
    2366             :       case JSOP_CHECKRETURN:
    2367             :       case JSOP_CHECKTHISREINIT:
    2368             : 
    2369             :       // Super
    2370             :       case JSOP_SUPERBASE:
    2371             :       case JSOP_SETPROP_SUPER:
    2372             :       case JSOP_GETPROP_SUPER:
    2373             :       case JSOP_GETELEM_SUPER:
    2374             :       case JSOP_SETELEM_SUPER:
    2375             :       case JSOP_STRICTSETPROP_SUPER:
    2376             :       case JSOP_STRICTSETELEM_SUPER:
    2377             :       case JSOP_SUPERFUN:
    2378             :       // Most of the infrastructure for these exists in Ion, but needs review
    2379             :       // and testing before these are enabled. Once other opcodes that are used
    2380             :       // in derived classes are supported in Ion, this can be better validated
    2381             :       // with testcases. Pay special attention to bailout and other areas where
    2382             :       // JSOP_NEW has special handling.
    2383             :       case JSOP_SPREADSUPERCALL:
    2384             :       case JSOP_SUPERCALL:
    2385             : 
    2386             :       // Environments (bug 1366470)
    2387             :       case JSOP_PUSHVARENV:
    2388             :       case JSOP_POPVARENV:
    2389             : 
    2390             :       // Compound assignment
    2391             :       case JSOP_GETBOUNDNAME:
    2392             : 
    2393             :       // Generators / Async (bug 1317690)
    2394             :       case JSOP_EXCEPTION:
    2395             :       case JSOP_THROWING:
    2396             :       case JSOP_ISGENCLOSING:
    2397             :       case JSOP_INITIALYIELD:
    2398             :       case JSOP_YIELD:
    2399             :       case JSOP_FINALYIELDRVAL:
    2400             :       case JSOP_RESUME:
    2401             :       case JSOP_DEBUGAFTERYIELD:
    2402             :       case JSOP_AWAIT:
    2403             :       case JSOP_GENERATOR:
    2404             : 
    2405             :       // Misc
    2406             :       case JSOP_ARRAYPUSH:
    2407             :       case JSOP_DELNAME:
    2408             :       case JSOP_FINALLY:
    2409             :       case JSOP_GETRVAL:
    2410             :       case JSOP_GOSUB:
    2411             :       case JSOP_IMPLICITTHIS:
    2412             :       case JSOP_RETSUB:
    2413             :       case JSOP_SETINTRINSIC:
    2414             :       case JSOP_THROWMSG:
    2415             :         // === !! WARNING WARNING WARNING !! ===
    2416             :         // Do you really want to sacrifice performance by not implementing this
    2417             :         // operation in the optimizing compiler?
    2418           0 :         break;
    2419             : 
    2420             :       case JSOP_FORCEINTERPRETER:
    2421             :         // Intentionally not implemented.
    2422           0 :         break;
    2423             : 
    2424             :       case JSOP_UNUSED222:
    2425             :       case JSOP_UNUSED223:
    2426             :       case JSOP_LIMIT:
    2427           0 :         break;
    2428             :     }
    2429             : 
    2430             :     // Track a simpler message, since the actionable abort message is a
    2431             :     // static string, and the internal opcode name isn't an actionable
    2432             :     // thing anyways.
    2433           0 :     trackActionableAbort("Unsupported bytecode");
    2434             : #ifdef DEBUG
    2435           0 :     return abort(AbortReason::Disable, "Unsupported opcode: %s", CodeName[op]);
    2436             : #else
    2437             :     return abort(AbortReason::Disable, "Unsupported opcode: %d", op);
    2438             : #endif
    2439             : }
    2440             : 
    2441             : AbortReasonOr<Ok>
    2442           1 : IonBuilder::restartLoop(const CFGBlock* cfgHeader)
    2443             : {
    2444           2 :     AutoTraceLog logCompile(traceLogger(), TraceLogger_IonBuilderRestartLoop);
    2445             : 
    2446           1 :     spew("New types at loop header, restarting loop body");
    2447             : 
    2448           1 :     if (JitOptions.limitScriptSize) {
    2449           1 :         if (++numLoopRestarts_ >= MAX_LOOP_RESTARTS)
    2450           0 :             return abort(AbortReason::Disable, "Aborted while processing control flow");
    2451             :     }
    2452             : 
    2453           1 :     MBasicBlock* header = blockWorklist[cfgHeader->id()];
    2454             : 
    2455             :     // Discard unreferenced & pre-allocated resume points.
    2456           1 :     replaceMaybeFallbackFunctionGetter(nullptr);
    2457             : 
    2458             :     // Remove all blocks in the loop body other than the header, which has phis
    2459             :     // of the appropriate type and incoming edges to preserve.
    2460           1 :     if (!graph().removeSuccessorBlocks(header))
    2461           0 :         return abort(AbortReason::Alloc);
    2462           1 :     graph().removeBlockFromList(header);
    2463             : 
    2464             :     // Remove all instructions from the header itself, and all resume points
    2465             :     // except the entry resume point.
    2466           1 :     header->discardAllInstructions();
    2467           1 :     header->discardAllResumePoints(/* discardEntry = */ false);
    2468           1 :     header->setStackDepth(header->getPredecessor(0)->stackDepth());
    2469             : 
    2470           1 :     loopDepth_ = header->loopDepth();
    2471             : 
    2472             :     // Don't specializePhis(), as the header has been visited before and the
    2473             :     // phis have already had their type set.
    2474           1 :     setCurrent(header);
    2475           1 :     pc = header->pc();
    2476             : 
    2477           1 :     initLoopEntry();
    2478           1 :     return Ok();
    2479             : }
    2480             : 
    2481             : AbortReasonOr<Ok>
    2482        1368 : IonBuilder::replaceTypeSet(MDefinition* subject, TemporaryTypeSet* type, MTest* test)
    2483             : {
    2484        1368 :     if (type->unknown())
    2485           0 :         return Ok();
    2486             : 
    2487             :     // Don't emit MFilterTypeSet if it doesn't improve the typeset.
    2488        1368 :     if (subject->resultTypeSet()) {
    2489         596 :         if (subject->resultTypeSet()->equals(type))
    2490         534 :             return Ok();
    2491             :     } else {
    2492         772 :         TemporaryTypeSet oldTypes(alloc_->lifoAlloc(), subject->type());
    2493         772 :         if (oldTypes.equals(type))
    2494         750 :             return Ok();
    2495             :     }
    2496             : 
    2497          84 :     MInstruction* replace = nullptr;
    2498             :     MDefinition* ins;
    2499             : 
    2500        1858 :     for (uint32_t i = 0; i < current->stackDepth(); i++) {
    2501        1774 :         ins = current->getSlot(i);
    2502             : 
    2503             :         // Instead of creating a new MFilterTypeSet, try to update the old one.
    2504        1774 :         if (ins->isFilterTypeSet() && ins->getOperand(0) == subject &&
    2505           0 :             ins->dependency() == test)
    2506             :         {
    2507             :             TemporaryTypeSet* intersect =
    2508           0 :                 TypeSet::intersectSets(ins->resultTypeSet(), type, alloc_->lifoAlloc());
    2509           0 :             if (!intersect)
    2510           0 :                 return abort(AbortReason::Alloc);
    2511             : 
    2512           0 :             ins->toFilterTypeSet()->setResultType(intersect->getKnownMIRType());
    2513           0 :             ins->toFilterTypeSet()->setResultTypeSet(intersect);
    2514             : 
    2515           0 :             if (ins->type() == MIRType::Undefined)
    2516           0 :                 current->setSlot(i, constant(UndefinedValue()));
    2517           0 :             else if (ins->type() == MIRType::Null)
    2518           0 :                 current->setSlot(i, constant(NullValue()));
    2519           0 :             else if (ins->type() == MIRType::MagicOptimizedArguments)
    2520           0 :                 current->setSlot(i, constant(MagicValue(JS_OPTIMIZED_ARGUMENTS)));
    2521             :             else
    2522           0 :                 MOZ_ASSERT(!IsMagicType(ins->type()));
    2523           0 :             continue;
    2524             :         }
    2525             : 
    2526        1774 :         if (ins == subject) {
    2527          53 :             if (!replace) {
    2528          47 :                 replace = MFilterTypeSet::New(alloc(), subject, type);
    2529          47 :                 current->add(replace);
    2530             : 
    2531             :                 // Make sure we don't hoist it above the MTest, we can use the
    2532             :                 // 'dependency' of an MInstruction. This is normally used by
    2533             :                 // Alias Analysis, but won't get overwritten, since this
    2534             :                 // instruction doesn't have an AliasSet.
    2535          47 :                 replace->setDependency(test);
    2536             : 
    2537          47 :                 if (replace->type() == MIRType::Undefined)
    2538           0 :                     replace = constant(UndefinedValue());
    2539          47 :                 else if (replace->type() == MIRType::Null)
    2540           0 :                     replace = constant(NullValue());
    2541          47 :                 else if (replace->type() == MIRType::MagicOptimizedArguments)
    2542           0 :                     replace = constant(MagicValue(JS_OPTIMIZED_ARGUMENTS));
    2543             :                 else
    2544          47 :                     MOZ_ASSERT(!IsMagicType(ins->type()));
    2545             :             }
    2546          53 :             current->setSlot(i, replace);
    2547             :         }
    2548             :     }
    2549          84 :     return Ok();
    2550             : }
    2551             : 
    2552             : bool
    2553         232 : IonBuilder::detectAndOrStructure(MPhi* ins, bool* branchIsAnd)
    2554             : {
    2555             :     // Look for a triangle pattern:
    2556             :     //
    2557             :     //       initialBlock
    2558             :     //         /     |
    2559             :     // branchBlock   |
    2560             :     //         \     |
    2561             :     //        testBlock
    2562             :     //
    2563             :     // Where ins is a phi from testBlock which combines two values
    2564             :     // pushed onto the stack by initialBlock and branchBlock.
    2565             : 
    2566         232 :     if (ins->numOperands() != 2)
    2567          54 :         return false;
    2568             : 
    2569         178 :     MBasicBlock* testBlock = ins->block();
    2570         178 :     MOZ_ASSERT(testBlock->numPredecessors() == 2);
    2571             : 
    2572             :     MBasicBlock* initialBlock;
    2573             :     MBasicBlock* branchBlock;
    2574         178 :     if (testBlock->getPredecessor(0)->lastIns()->isTest()) {
    2575           0 :         initialBlock = testBlock->getPredecessor(0);
    2576           0 :         branchBlock = testBlock->getPredecessor(1);
    2577         178 :     } else if (testBlock->getPredecessor(1)->lastIns()->isTest()) {
    2578           0 :         initialBlock = testBlock->getPredecessor(1);
    2579           0 :         branchBlock = testBlock->getPredecessor(0);
    2580             :     } else {
    2581         178 :         return false;
    2582             :     }
    2583             : 
    2584           0 :     if (branchBlock->numSuccessors() != 1)
    2585           0 :         return false;
    2586             : 
    2587           0 :     if (branchBlock->numPredecessors() != 1 || branchBlock->getPredecessor(0) != initialBlock)
    2588           0 :         return false;
    2589             : 
    2590           0 :     if (initialBlock->numSuccessors() != 2)
    2591           0 :         return false;
    2592             : 
    2593           0 :     MDefinition* branchResult = ins->getOperand(testBlock->indexForPredecessor(branchBlock));
    2594           0 :     MDefinition* initialResult = ins->getOperand(testBlock->indexForPredecessor(initialBlock));
    2595             : 
    2596           0 :     if (branchBlock->stackDepth() != initialBlock->stackDepth())
    2597           0 :         return false;
    2598           0 :     if (branchBlock->stackDepth() != testBlock->stackDepth() + 1)
    2599           0 :         return false;
    2600           0 :     if (branchResult != branchBlock->peek(-1) || initialResult != initialBlock->peek(-1))
    2601           0 :         return false;
    2602             : 
    2603           0 :     MTest* initialTest = initialBlock->lastIns()->toTest();
    2604           0 :     bool branchIsTrue = branchBlock == initialTest->ifTrue();
    2605           0 :     if (initialTest->input() == ins->getOperand(0))
    2606           0 :         *branchIsAnd = branchIsTrue != (testBlock->getPredecessor(0) == branchBlock);
    2607           0 :     else if (initialTest->input() == ins->getOperand(1))
    2608           0 :         *branchIsAnd = branchIsTrue != (testBlock->getPredecessor(1) == branchBlock);
    2609             :     else
    2610           0 :         return false;
    2611             : 
    2612           0 :     return true;
    2613             : }
    2614             : 
    2615             : AbortReasonOr<Ok>
    2616         482 : IonBuilder::improveTypesAtCompare(MCompare* ins, bool trueBranch, MTest* test)
    2617             : {
    2618         952 :     if (ins->compareType() == MCompare::Compare_Undefined ||
    2619         470 :         ins->compareType() == MCompare::Compare_Null)
    2620             :     {
    2621          34 :         return improveTypesAtNullOrUndefinedCompare(ins, trueBranch, test);
    2622             :     }
    2623             : 
    2624         456 :     if ((ins->lhs()->isTypeOf() || ins->rhs()->isTypeOf()) &&
    2625           8 :         (ins->lhs()->isConstant() || ins->rhs()->isConstant()))
    2626             :     {
    2627           4 :         return improveTypesAtTypeOfCompare(ins, trueBranch, test);
    2628             :     }
    2629             : 
    2630         444 :     return Ok();
    2631             : }
    2632             : 
    2633             : AbortReasonOr<Ok>
    2634           4 : IonBuilder::improveTypesAtTypeOfCompare(MCompare* ins, bool trueBranch, MTest* test)
    2635             : {
    2636           4 :     MTypeOf* typeOf = ins->lhs()->isTypeOf() ? ins->lhs()->toTypeOf() : ins->rhs()->toTypeOf();
    2637           4 :     MConstant* constant = ins->lhs()->isConstant() ? ins->lhs()->toConstant() : ins->rhs()->toConstant();
    2638             : 
    2639           4 :     if (constant->type() != MIRType::String)
    2640           0 :         return Ok();
    2641             : 
    2642           4 :     bool equal = ins->jsop() == JSOP_EQ || ins->jsop() == JSOP_STRICTEQ;
    2643           4 :     bool notEqual = ins->jsop() == JSOP_NE || ins->jsop() == JSOP_STRICTNE;
    2644             : 
    2645           4 :     if (notEqual)
    2646           0 :         trueBranch = !trueBranch;
    2647             : 
    2648             :     // Relational compares not supported.
    2649           4 :     if (!equal && !notEqual)
    2650           0 :         return Ok();
    2651             : 
    2652           4 :     MDefinition* subject = typeOf->input();
    2653           4 :     TemporaryTypeSet* inputTypes = subject->resultTypeSet();
    2654             : 
    2655             :     // Create temporary typeset equal to the type if there is no resultTypeSet.
    2656           4 :     TemporaryTypeSet tmp;
    2657           4 :     if (!inputTypes) {
    2658           0 :         if (subject->type() == MIRType::Value)
    2659           0 :             return Ok();
    2660           0 :         inputTypes = &tmp;
    2661           0 :         tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(subject->type())), alloc_->lifoAlloc());
    2662             :     }
    2663             : 
    2664           4 :     if (inputTypes->unknown())
    2665           0 :         return Ok();
    2666             : 
    2667             :     // Note: we cannot remove the AnyObject type in the false branch,
    2668             :     // since there are multiple ways to get an object. That is the reason
    2669             :     // for the 'trueBranch' test.
    2670           4 :     TemporaryTypeSet filter;
    2671           4 :     const JSAtomState& names = GetJitContext()->runtime->names();
    2672           4 :     if (constant->toString() == TypeName(JSTYPE_UNDEFINED, names)) {
    2673           0 :         filter.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
    2674           0 :         if (typeOf->inputMaybeCallableOrEmulatesUndefined() && trueBranch)
    2675           0 :             filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
    2676           4 :     } else if (constant->toString() == TypeName(JSTYPE_BOOLEAN, names)) {
    2677           0 :         filter.addType(TypeSet::BooleanType(), alloc_->lifoAlloc());
    2678           4 :     } else if (constant->toString() == TypeName(JSTYPE_NUMBER, names)) {
    2679           4 :         filter.addType(TypeSet::Int32Type(), alloc_->lifoAlloc());
    2680           4 :         filter.addType(TypeSet::DoubleType(), alloc_->lifoAlloc());
    2681           0 :     } else if (constant->toString() == TypeName(JSTYPE_STRING, names)) {
    2682           0 :         filter.addType(TypeSet::StringType(), alloc_->lifoAlloc());
    2683           0 :     } else if (constant->toString() == TypeName(JSTYPE_SYMBOL, names)) {
    2684           0 :         filter.addType(TypeSet::SymbolType(), alloc_->lifoAlloc());
    2685           0 :     } else if (constant->toString() == TypeName(JSTYPE_OBJECT, names)) {
    2686           0 :         filter.addType(TypeSet::NullType(), alloc_->lifoAlloc());
    2687           0 :         if (trueBranch)
    2688           0 :             filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
    2689           0 :     } else if (constant->toString() == TypeName(JSTYPE_FUNCTION, names)) {
    2690           0 :         if (typeOf->inputMaybeCallableOrEmulatesUndefined() && trueBranch)
    2691           0 :             filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
    2692             :     } else {
    2693           0 :         return Ok();
    2694             :     }
    2695             : 
    2696             :     TemporaryTypeSet* type;
    2697           4 :     if (trueBranch)
    2698           2 :         type = TypeSet::intersectSets(&filter, inputTypes, alloc_->lifoAlloc());
    2699             :     else
    2700           2 :         type = TypeSet::removeSet(inputTypes, &filter, alloc_->lifoAlloc());
    2701             : 
    2702           4 :     if (!type)
    2703           0 :         return abort(AbortReason::Alloc);
    2704             : 
    2705           4 :     return replaceTypeSet(subject, type, test);
    2706             : }
    2707             : 
    2708             : AbortReasonOr<Ok>
    2709          34 : IonBuilder::improveTypesAtNullOrUndefinedCompare(MCompare* ins, bool trueBranch, MTest* test)
    2710             : {
    2711          34 :     MOZ_ASSERT(ins->compareType() == MCompare::Compare_Undefined ||
    2712             :                ins->compareType() == MCompare::Compare_Null);
    2713             : 
    2714             :     // altersUndefined/Null represents if we can filter/set Undefined/Null.
    2715             :     bool altersUndefined, altersNull;
    2716          34 :     JSOp op = ins->jsop();
    2717             : 
    2718          34 :     switch(op) {
    2719             :       case JSOP_STRICTNE:
    2720             :       case JSOP_STRICTEQ:
    2721          30 :         altersUndefined = ins->compareType() == MCompare::Compare_Undefined;
    2722          30 :         altersNull = ins->compareType() == MCompare::Compare_Null;
    2723          30 :         break;
    2724             :       case JSOP_NE:
    2725             :       case JSOP_EQ:
    2726           4 :         altersUndefined = altersNull = true;
    2727           4 :         break;
    2728             :       default:
    2729           0 :         MOZ_CRASH("Relational compares not supported");
    2730             :     }
    2731             : 
    2732          34 :     MDefinition* subject = ins->lhs();
    2733          34 :     TemporaryTypeSet* inputTypes = subject->resultTypeSet();
    2734             : 
    2735          34 :     MOZ_ASSERT(IsNullOrUndefined(ins->rhs()->type()));
    2736             : 
    2737             :     // Create temporary typeset equal to the type if there is no resultTypeSet.
    2738          34 :     TemporaryTypeSet tmp;
    2739          34 :     if (!inputTypes) {
    2740          12 :         if (subject->type() == MIRType::Value)
    2741           8 :             return Ok();
    2742           4 :         inputTypes = &tmp;
    2743           4 :         tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(subject->type())), alloc_->lifoAlloc());
    2744             :     }
    2745             : 
    2746          26 :     if (inputTypes->unknown())
    2747           0 :         return Ok();
    2748             : 
    2749             :     TemporaryTypeSet* type;
    2750             : 
    2751             :     // Decide if we need to filter the type or set it.
    2752          26 :     if ((op == JSOP_STRICTEQ || op == JSOP_EQ) ^ trueBranch) {
    2753             :         // Remove undefined/null
    2754          13 :         TemporaryTypeSet remove;
    2755          13 :         if (altersUndefined)
    2756           2 :             remove.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
    2757          13 :         if (altersNull)
    2758          13 :             remove.addType(TypeSet::NullType(), alloc_->lifoAlloc());
    2759             : 
    2760          13 :         type = TypeSet::removeSet(inputTypes, &remove, alloc_->lifoAlloc());
    2761             :     } else {
    2762             :         // Set undefined/null.
    2763          13 :         TemporaryTypeSet base;
    2764          13 :         if (altersUndefined) {
    2765           2 :             base.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
    2766             :             // If TypeSet emulates undefined, then we cannot filter the objects.
    2767           2 :             if (inputTypes->maybeEmulatesUndefined(constraints()))
    2768           0 :                 base.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
    2769             :         }
    2770             : 
    2771          13 :         if (altersNull)
    2772          13 :             base.addType(TypeSet::NullType(), alloc_->lifoAlloc());
    2773             : 
    2774          13 :         type = TypeSet::intersectSets(&base, inputTypes, alloc_->lifoAlloc());
    2775             :     }
    2776             : 
    2777          26 :     if (!type)
    2778           0 :         return abort(AbortReason::Alloc);
    2779             : 
    2780          26 :     return replaceTypeSet(subject, type, test);
    2781             : }
    2782             : 
    2783             : AbortReasonOr<Ok>
    2784        2050 : IonBuilder::improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test)
    2785             : {
    2786             :     // We explore the test condition to try and deduce as much type information
    2787             :     // as possible.
    2788             : 
    2789             :     // All branches of this switch that don't want to fall through to the
    2790             :     // default behavior must return.  The default behavior assumes that a true
    2791             :     // test means the incoming ins is not null or undefined and that a false
    2792             :     // tests means it's one of null, undefined, false, 0, "", and objects
    2793             :     // emulating undefined
    2794        2050 :     switch (ins->op()) {
    2795             :       case MDefinition::Op_Not:
    2796         226 :         return improveTypesAtTest(ins->toNot()->getOperand(0), !trueBranch, test);
    2797             :       case MDefinition::Op_IsObject: {
    2798           0 :         MDefinition* subject = ins->getOperand(0);
    2799           0 :         TemporaryTypeSet* oldType = subject->resultTypeSet();
    2800             : 
    2801             :         // Create temporary typeset equal to the type if there is no resultTypeSet.
    2802           0 :         TemporaryTypeSet tmp;
    2803           0 :         if (!oldType) {
    2804           0 :             if (subject->type() == MIRType::Value)
    2805           0 :                 return Ok();
    2806           0 :             oldType = &tmp;
    2807           0 :             tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(subject->type())), alloc_->lifoAlloc());
    2808             :         }
    2809             : 
    2810           0 :         if (oldType->unknown())
    2811           0 :             return Ok();
    2812             : 
    2813           0 :         TemporaryTypeSet* type = nullptr;
    2814           0 :         if (trueBranch)
    2815           0 :             type = oldType->cloneObjectsOnly(alloc_->lifoAlloc());
    2816             :         else
    2817           0 :             type = oldType->cloneWithoutObjects(alloc_->lifoAlloc());
    2818             : 
    2819           0 :         if (!type)
    2820           0 :             return abort(AbortReason::Alloc);
    2821             : 
    2822           0 :         return replaceTypeSet(subject, type, test);
    2823             :       }
    2824             :       case MDefinition::Op_Phi: {
    2825         232 :         bool branchIsAnd = true;
    2826         232 :         if (!detectAndOrStructure(ins->toPhi(), &branchIsAnd)) {
    2827             :             // Just fall through to the default behavior.
    2828         232 :             break;
    2829             :         }
    2830             : 
    2831             :         // Now we have detected the triangular structure and determined if it
    2832             :         // was an AND or an OR.
    2833           0 :         if (branchIsAnd) {
    2834           0 :             if (trueBranch) {
    2835           0 :                 MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(0), true, test));
    2836           0 :                 MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(1), true, test));
    2837             :             }
    2838             :         } else {
    2839             :             /*
    2840             :              * if (a || b) {
    2841             :              *    ...
    2842             :              * } else {
    2843             :              *    ...
    2844             :              * }
    2845             :              *
    2846             :              * If we have a statements like the one described above,
    2847             :              * And we are in the else branch of it. It amounts to:
    2848             :              * if (!(a || b)) and being in the true branch.
    2849             :              *
    2850             :              * Simplifying, we have (!a && !b)
    2851             :              * In this case we can use the same logic we use for branchIsAnd
    2852             :              *
    2853             :              */
    2854           0 :             if (!trueBranch) {
    2855           0 :                 MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(0), false, test));
    2856           0 :                 MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(1), false, test));
    2857             :             }
    2858             :         }
    2859           0 :         return Ok();
    2860             :       }
    2861             : 
    2862             :       case MDefinition::Op_Compare:
    2863         482 :         return improveTypesAtCompare(ins->toCompare(), trueBranch, test);
    2864             : 
    2865             :       default:
    2866        1110 :         break;
    2867             :     }
    2868             : 
    2869             :     // By default MTest tests ToBoolean(input). As a result in the true branch we can filter
    2870             :     // undefined and null. In false branch we can only encounter undefined, null, false, 0, ""
    2871             :     // and objects that emulate undefined.
    2872             : 
    2873        1342 :     TemporaryTypeSet* oldType = ins->resultTypeSet();
    2874             :     TemporaryTypeSet* type;
    2875             : 
    2876             :     // Create temporary typeset equal to the type if there is no resultTypeSet.
    2877        1342 :     TemporaryTypeSet tmp;
    2878        1342 :     if (!oldType) {
    2879         772 :         if (ins->type() == MIRType::Value)
    2880           4 :             return Ok();
    2881         768 :         oldType = &tmp;
    2882         768 :         tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(ins->type())), alloc_->lifoAlloc());
    2883             :     }
    2884             : 
    2885             :     // If ins does not have a typeset we return as we cannot optimize.
    2886        1338 :     if (oldType->unknown())
    2887           0 :         return Ok();
    2888             : 
    2889             :     // Decide either to set or remove.
    2890        1338 :     if (trueBranch) {
    2891         669 :         TemporaryTypeSet remove;
    2892         669 :         remove.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
    2893         669 :         remove.addType(TypeSet::NullType(), alloc_->lifoAlloc());
    2894         669 :         type = TypeSet::removeSet(oldType, &remove, alloc_->lifoAlloc());
    2895             :     } else {
    2896         669 :         TemporaryTypeSet base;
    2897         669 :         base.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc()); // ToBoolean(undefined) == false
    2898         669 :         base.addType(TypeSet::NullType(), alloc_->lifoAlloc()); // ToBoolean(null) == false
    2899         669 :         base.addType(TypeSet::BooleanType(), alloc_->lifoAlloc()); // ToBoolean(false) == false
    2900         669 :         base.addType(TypeSet::Int32Type(), alloc_->lifoAlloc()); // ToBoolean(0) == false
    2901         669 :         base.addType(TypeSet::DoubleType(), alloc_->lifoAlloc()); // ToBoolean(0.0) == false
    2902         669 :         base.addType(TypeSet::StringType(), alloc_->lifoAlloc()); // ToBoolean("") == false
    2903             : 
    2904             :         // If the typeset does emulate undefined, then we cannot filter out
    2905             :         // objects.
    2906         669 :         if (oldType->maybeEmulatesUndefined(constraints()))
    2907           0 :             base.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
    2908             : 
    2909         669 :         type = TypeSet::intersectSets(&base, oldType, alloc_->lifoAlloc());
    2910             :     }
    2911             : 
    2912        1338 :     if (!type)
    2913           0 :         return abort(AbortReason::Alloc);
    2914             : 
    2915        1338 :     return replaceTypeSet(ins, type, test);
    2916             : }
    2917             : 
    2918             : AbortReasonOr<Ok>
    2919           0 : IonBuilder::jsop_dup2()
    2920             : {
    2921           0 :     uint32_t lhsSlot = current->stackDepth() - 2;
    2922           0 :     uint32_t rhsSlot = current->stackDepth() - 1;
    2923           0 :     current->pushSlot(lhsSlot);
    2924           0 :     current->pushSlot(rhsSlot);
    2925           0 :     return Ok();
    2926             : }
    2927             : 
    2928             : AbortReasonOr<Ok>
    2929         912 : IonBuilder::visitTest(CFGTest* test)
    2930             : {
    2931         912 :     MDefinition* ins = test->mustKeepCondition() ? current->peek(-1) : current->pop();
    2932             : 
    2933             :     // Create true and false branches.
    2934             :     MBasicBlock* ifTrue;
    2935         912 :     MOZ_TRY_VAR(ifTrue, newBlock(current, test->trueBranch()->startPc()));
    2936             :     MBasicBlock* ifFalse;
    2937         912 :     MOZ_TRY_VAR(ifFalse, newBlock(current, test->falseBranch()->startPc()));
    2938             : 
    2939         912 :     MTest* mir = newTest(ins, ifTrue, ifFalse);
    2940         912 :     current->end(mir);
    2941             : 
    2942             :     // Filter the types in the true branch.
    2943         912 :     MOZ_TRY(setCurrentAndSpecializePhis(ifTrue));
    2944         912 :     MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ true, mir));
    2945             : 
    2946         912 :     blockWorklist[test->trueBranch()->id()] = ifTrue;
    2947             : 
    2948             :     // Filter the types in the false branch.
    2949             :     // Note: sometimes the false branch is used as merge point. As a result
    2950             :     // reuse the ifFalse block as a type improvement block and create a new
    2951             :     // ifFalse which we can use for the merge.
    2952         912 :     MBasicBlock* filterBlock = ifFalse;
    2953         912 :     ifFalse = nullptr;
    2954         912 :     graph().addBlock(filterBlock);
    2955             : 
    2956         912 :     MOZ_TRY(setCurrentAndSpecializePhis(filterBlock));
    2957         912 :     MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ false, mir));
    2958             : 
    2959         912 :     MOZ_TRY_VAR(ifFalse, newBlock(filterBlock, test->falseBranch()->startPc()));
    2960         912 :     filterBlock->end(MGoto::New(alloc(), ifFalse));
    2961             : 
    2962         912 :     if (filterBlock->pc() && script()->hasScriptCounts())
    2963         250 :         filterBlock->setHitCount(script()->getHitCount(filterBlock->pc()));
    2964             : 
    2965         912 :     blockWorklist[test->falseBranch()->id()] = ifFalse;
    2966             : 
    2967         912 :     current = nullptr;
    2968             : 
    2969         912 :     return Ok();
    2970             : }
    2971             : 
    2972             : AbortReasonOr<Ok>
    2973           0 : IonBuilder::visitCompare(CFGCompare* compare)
    2974             : {
    2975           0 :     MDefinition* rhs = current->peek(-1);
    2976           0 :     MDefinition* lhs = current->peek(-2);
    2977             : 
    2978             :     // Execute the compare operation.
    2979           0 :     MOZ_TRY(jsop_compare(JSOP_STRICTEQ));
    2980           0 :     MInstruction* cmpResult = current->pop()->toInstruction();
    2981           0 :     MOZ_ASSERT(!cmpResult->isEffectful());
    2982             : 
    2983             :     // Put the rhs/lhs again on the stack.
    2984           0 :     current->push(lhs);
    2985           0 :     current->push(rhs);
    2986             : 
    2987             :     // Create true and false branches.
    2988             :     MBasicBlock* ifTrue;
    2989           0 :     MOZ_TRY_VAR(ifTrue, newBlockPopN(current, compare->trueBranch()->startPc(),
    2990             :                                      compare->truePopAmount()));
    2991             :     MBasicBlock* ifFalse;
    2992           0 :     MOZ_TRY_VAR(ifFalse, newBlockPopN(current, compare->falseBranch()->startPc(),
    2993             :                                       compare->falsePopAmount()));
    2994             : 
    2995           0 :     blockWorklist[compare->trueBranch()->id()] = ifTrue;
    2996           0 :     blockWorklist[compare->falseBranch()->id()] = ifFalse;
    2997             : 
    2998           0 :     MTest* mir = newTest(cmpResult, ifTrue, ifFalse);
    2999           0 :     current->end(mir);
    3000             : 
    3001             :     // Filter the types in the true branch.
    3002           0 :     MOZ_TRY(setCurrentAndSpecializePhis(ifTrue));
    3003           0 :     MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ true, mir));
    3004             : 
    3005             :     // Filter the types in the false branch.
    3006           0 :     MOZ_TRY(setCurrentAndSpecializePhis(ifFalse));
    3007           0 :     MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ false, mir));
    3008             : 
    3009           0 :     current = nullptr;
    3010             : 
    3011           0 :     return Ok();
    3012             : }
    3013             : 
    3014             : AbortReasonOr<Ok>
    3015           5 : IonBuilder::visitTry(CFGTry* try_)
    3016             : {
    3017             :     // Try-finally is not yet supported.
    3018           5 :     if (analysis().hasTryFinally())
    3019           0 :         return abort(AbortReason::Disable, "Has try-finally");
    3020             : 
    3021             :     // Try-catch within inline frames is not yet supported.
    3022           5 :     MOZ_ASSERT(!isInlineBuilder());
    3023             : 
    3024             :     // Try-catch during the arguments usage analysis is not yet supported. Code
    3025             :     // accessing the arguments within the 'catch' block is not accounted for.
    3026           5 :     if (info().analysisMode() == Analysis_ArgumentsUsage)
    3027           3 :         return abort(AbortReason::Disable, "Try-catch during arguments usage analysis");
    3028             : 
    3029           2 :     graph().setHasTryBlock();
    3030             : 
    3031             :     MBasicBlock* tryBlock;
    3032           2 :     MOZ_TRY_VAR(tryBlock, newBlock(current, try_->tryBlock()->startPc()));
    3033             : 
    3034           2 :     blockWorklist[try_->tryBlock()->id()] = tryBlock;
    3035             : 
    3036             :     // If the code after the try catch is reachable we connected it to the
    3037             :     // graph with an MGotoWithFake instruction that always jumps to the try
    3038             :     // block. This ensures the successor block always has a predecessor.
    3039           2 :     if (try_->codeAfterTryCatchReachable()) {
    3040             :         MBasicBlock* successor;
    3041           2 :         MOZ_TRY_VAR(successor, newBlock(current, try_->getSuccessor(1)->startPc()));
    3042             : 
    3043           2 :         blockWorklist[try_->afterTryCatchBlock()->id()] = successor;
    3044             : 
    3045           2 :         current->end(MGotoWithFake::New(alloc(), tryBlock, successor));
    3046             : 
    3047             :         // The baseline compiler should not attempt to enter the catch block
    3048             :         // via OSR.
    3049           2 :         MOZ_ASSERT(info().osrPc() < try_->catchStartPc() ||
    3050             :                    info().osrPc() >= try_->afterTryCatchBlock()->startPc());
    3051             : 
    3052             :     } else {
    3053           0 :         current->end(MGoto::New(alloc(), tryBlock));
    3054             : 
    3055             :         // The baseline compiler should not attempt to enter the catch block
    3056             :         // via OSR or the code after the catch block.
    3057             :         // TODO: pre-existing bug. OSR after the catch block. Shouldn't happen.
    3058             :         //MOZ_ASSERT(info().osrPc() < try_->catchStartPc());
    3059             :     }
    3060             : 
    3061           2 :     return Ok();
    3062             : }
    3063             : 
    3064             : AbortReasonOr<Ok>
    3065         561 : IonBuilder::visitReturn(CFGControlInstruction* control)
    3066             : {
    3067             :     MDefinition* def;
    3068         561 :     switch (control->type()) {
    3069             :       case CFGControlInstruction::Type_Return:
    3070             :         // Return the last instruction.
    3071         538 :         def = current->pop();
    3072         538 :         break;
    3073             : 
    3074             :       case CFGControlInstruction::Type_RetRVal:
    3075             :         // Return undefined eagerly if script doesn't use return value.
    3076          23 :         if (script()->noScriptRval()) {
    3077           0 :             MInstruction* ins = MConstant::New(alloc(), UndefinedValue());
    3078           0 :             current->add(ins);
    3079           0 :             def = ins;
    3080           0 :             break;
    3081             :         }
    3082             : 
    3083          23 :         def = current->getSlot(info().returnValueSlot());
    3084          23 :         break;
    3085             : 
    3086             :       default:
    3087           0 :         def = nullptr;
    3088           0 :         MOZ_CRASH("unknown return op");
    3089             :     }
    3090             : 
    3091         561 :     MReturn* ret = MReturn::New(alloc(), def);
    3092         561 :     current->end(ret);
    3093             : 
    3094         561 :     if (!graph().addReturn(current))
    3095           0 :         return abort(AbortReason::Alloc);
    3096             : 
    3097             :     // Make sure no one tries to use this block now.
    3098         561 :     setCurrent(nullptr);
    3099         561 :     return Ok();
    3100             : }
    3101             : 
    3102             : AbortReasonOr<Ok>
    3103           1 : IonBuilder::visitThrow(CFGThrow *cfgIns)
    3104             : {
    3105           1 :     MDefinition* def = current->pop();
    3106             : 
    3107             :     // MThrow is not marked as effectful. This means when it throws and we
    3108             :     // are inside a try block, we could use an earlier resume point and this
    3109             :     // resume point may not be up-to-date, for example:
    3110             :     //
    3111             :     // (function() {
    3112             :     //     try {
    3113             :     //         var x = 1;
    3114             :     //         foo(); // resume point
    3115             :     //         x = 2;
    3116             :     //         throw foo;
    3117             :     //     } catch(e) {
    3118             :     //         print(x);
    3119             :     //     }
    3120             :     // ])();
    3121             :     //
    3122             :     // If we use the resume point after the call, this will print 1 instead
    3123             :     // of 2. To fix this, we create a resume point right before the MThrow.
    3124             :     //
    3125             :     // Note that this is not a problem for instructions other than MThrow
    3126             :     // because they are either marked as effectful (have their own resume
    3127             :     // point) or cannot throw a catchable exception.
    3128             :     //
    3129             :     // We always install this resume point (instead of only when the function
    3130             :     // has a try block) in order to handle the Debugger onExceptionUnwind
    3131             :     // hook. When we need to handle the hook, we bail out to baseline right
    3132             :     // after the throw and propagate the exception when debug mode is on. This
    3133             :     // is opposed to the normal behavior of resuming directly in the
    3134             :     // associated catch block.
    3135           1 :     MNop* nop = MNop::New(alloc());
    3136           1 :     current->add(nop);
    3137             : 
    3138           1 :     MOZ_TRY(resumeAfter(nop));
    3139             : 
    3140           1 :     MThrow* ins = MThrow::New(alloc(), def);
    3141           1 :     current->end(ins);
    3142             : 
    3143           1 :     return Ok();
    3144             : }
    3145             : 
    3146             : AbortReasonOr<Ok>
    3147          44 : IonBuilder::visitTableSwitch(CFGTableSwitch* cfgIns)
    3148             : {
    3149             :     // Pop input.
    3150          44 :     MDefinition* ins = current->pop();
    3151             : 
    3152             :     // Create MIR instruction
    3153          44 :     MTableSwitch* tableswitch = MTableSwitch::New(alloc(), ins, cfgIns->low(), cfgIns->high());
    3154             : 
    3155             : #ifdef DEBUG
    3156          44 :     MOZ_ASSERT(cfgIns->defaultCase() == cfgIns->getSuccessor(0));
    3157         308 :     for (size_t i = 1; i < cfgIns->numSuccessors(); i++) {
    3158         264 :         MOZ_ASSERT(cfgIns->getCase(i-1) == cfgIns->getSuccessor(i));
    3159             :     }
    3160             : #endif
    3161             : 
    3162             :     // Create the cases
    3163         352 :     for (size_t i = 0; i < cfgIns->numSuccessors(); i++) {
    3164         308 :         const CFGBlock* cfgblock = cfgIns->getSuccessor(i);
    3165             : 
    3166             :         MBasicBlock* caseBlock;
    3167         308 :         MOZ_TRY_VAR(caseBlock, newBlock(current, cfgblock->startPc()));
    3168             : 
    3169         308 :         blockWorklist[cfgblock->id()] = caseBlock;
    3170             : 
    3171             :         size_t index;
    3172         308 :         if (i == 0) {
    3173          44 :             if (!tableswitch->addDefault(caseBlock, &index))
    3174           0 :                 return abort(AbortReason::Alloc);
    3175             : 
    3176             :         } else {
    3177         264 :             if (!tableswitch->addSuccessor(caseBlock, &index))
    3178           0 :                 return abort(AbortReason::Alloc);
    3179             : 
    3180         264 :             if (!tableswitch->addCase(index))
    3181           0 :                 return abort(AbortReason::Alloc);
    3182             : 
    3183             :             // If this is an actual case statement, optimize by replacing the
    3184             :             // input to the switch case with the actual number of the case.
    3185         264 :             MConstant* constant = MConstant::New(alloc(), Int32Value(i - 1 + tableswitch->low()));
    3186         264 :             caseBlock->add(constant);
    3187        2112 :             for (uint32_t j = 0; j < caseBlock->stackDepth(); j++) {
    3188        1848 :                 if (ins != caseBlock->getSlot(j))
    3189        1848 :                     continue;
    3190             : 
    3191           0 :                 constant->setDependency(ins);
    3192           0 :                 caseBlock->setSlot(j, constant);
    3193             :             }
    3194         264 :             graph().addBlock(caseBlock);
    3195             : 
    3196         264 :             if (caseBlock->pc() && script()->hasScriptCounts())
    3197           0 :                 caseBlock->setHitCount(script()->getHitCount(caseBlock->pc()));
    3198             : 
    3199             :             MBasicBlock* merge;
    3200         264 :             MOZ_TRY_VAR(merge, newBlock(caseBlock, cfgblock->startPc()));
    3201         264 :             if (!merge)
    3202           0 :                 return abort(AbortReason::Alloc);
    3203             : 
    3204         264 :             caseBlock->end(MGoto::New(alloc(), merge));
    3205         264 :             blockWorklist[cfgblock->id()] = merge;
    3206             :         }
    3207             : 
    3208         308 :         MOZ_ASSERT(index == i);
    3209             :     }
    3210             : 
    3211             :     // Save the MIR instruction as last instruction of this block.
    3212          44 :     current->end(tableswitch);
    3213          44 :     return Ok();
    3214             : }
    3215             : 
    3216             : void
    3217        3818 : IonBuilder::pushConstant(const Value& v)
    3218             : {
    3219        3818 :     current->push(constant(v));
    3220        3818 : }
    3221             : 
    3222             : AbortReasonOr<Ok>
    3223           6 : IonBuilder::bitnotTrySpecialized(bool* emitted, MDefinition* input)
    3224             : {
    3225           6 :     MOZ_ASSERT(*emitted == false);
    3226             : 
    3227             :     // Try to emit a specialized bitnot instruction based on the input type
    3228             :     // of the operand.
    3229             : 
    3230           6 :     if (input->mightBeType(MIRType::Object) || input->mightBeType(MIRType::Symbol))
    3231           0 :         return Ok();
    3232             : 
    3233           6 :     MBitNot* ins = MBitNot::New(alloc(), input);
    3234           6 :     ins->setSpecialization(MIRType::Int32);
    3235             : 
    3236           6 :     current->add(ins);
    3237           6 :     current->push(ins);
    3238             : 
    3239           6 :     *emitted = true;
    3240           6 :     return Ok();
    3241             : }
    3242             : 
    3243             : AbortReasonOr<Ok>
    3244           6 : IonBuilder::jsop_bitnot()
    3245             : {
    3246           6 :     bool emitted = false;
    3247             : 
    3248           6 :     MDefinition* input = current->pop();
    3249             : 
    3250           6 :     if (!forceInlineCaches()) {
    3251           6 :         MOZ_TRY(bitnotTrySpecialized(&emitted, input));
    3252           6 :         if(emitted)
    3253           6 :             return Ok();
    3254             :     }
    3255             : 
    3256           0 :     MOZ_TRY(arithTrySharedStub(&emitted, JSOP_BITNOT, nullptr, input));
    3257           0 :     if (emitted)
    3258           0 :         return Ok();
    3259             : 
    3260             :     // Not possible to optimize. Do a slow vm call.
    3261           0 :     MBitNot* ins = MBitNot::New(alloc(), input);
    3262             : 
    3263           0 :     current->add(ins);
    3264           0 :     current->push(ins);
    3265           0 :     MOZ_ASSERT(ins->isEffectful());
    3266           0 :     return resumeAfter(ins);
    3267             : }
    3268             : 
    3269             : AbortReasonOr<Ok>
    3270           2 : IonBuilder::jsop_bitop(JSOp op)
    3271             : {
    3272             :     // Pop inputs.
    3273           2 :     MDefinition* right = current->pop();
    3274           2 :     MDefinition* left = current->pop();
    3275             : 
    3276             :     MBinaryBitwiseInstruction* ins;
    3277           2 :     switch (op) {
    3278             :       case JSOP_BITAND:
    3279           0 :         ins = MBitAnd::New(alloc(), left, right);
    3280           0 :         break;
    3281             : 
    3282             :       case JSOP_BITOR:
    3283           2 :         ins = MBitOr::New(alloc(), left, right);
    3284           2 :         break;
    3285             : 
    3286             :       case JSOP_BITXOR:
    3287           0 :         ins = MBitXor::New(alloc(), left, right);
    3288           0 :         break;
    3289             : 
    3290             :       case JSOP_LSH:
    3291           0 :         ins = MLsh::New(alloc(), left, right);
    3292           0 :         break;
    3293             : 
    3294             :       case JSOP_RSH:
    3295           0 :         ins = MRsh::New(alloc(), left, right);
    3296           0 :         break;
    3297             : 
    3298             :       case JSOP_URSH:
    3299           0 :         ins = MUrsh::New(alloc(), left, right);
    3300           0 :         break;
    3301             : 
    3302             :       default:
    3303           0 :         MOZ_CRASH("unexpected bitop");
    3304             :     }
    3305             : 
    3306           2 :     current->add(ins);
    3307           2 :     ins->infer(inspector, pc);
    3308             : 
    3309           2 :     current->push(ins);
    3310           2 :     if (ins->isEffectful())
    3311           0 :         MOZ_TRY(resumeAfter(ins));
    3312             : 
    3313           2 :     return Ok();
    3314             : }
    3315             : 
    3316             : MDefinition::Opcode
    3317         211 : JSOpToMDefinition(JSOp op)
    3318             : {
    3319         211 :     switch (op) {
    3320             :       case JSOP_ADD:
    3321         208 :         return MDefinition::Op_Add;
    3322             :       case JSOP_SUB:
    3323           0 :         return MDefinition::Op_Sub;
    3324             :       case JSOP_MUL:
    3325           3 :         return MDefinition::Op_Mul;
    3326             :       case JSOP_DIV:
    3327           0 :         return MDefinition::Op_Div;
    3328             :       case JSOP_MOD:
    3329           0 :         return MDefinition::Op_Mod;
    3330             :       default:
    3331           0 :         MOZ_CRASH("unexpected binary opcode");
    3332             :     }
    3333             : }
    3334             : 
    3335             : AbortReasonOr<Ok>
    3336         249 : IonBuilder::binaryArithTryConcat(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
    3337             : {
    3338         249 :     MOZ_ASSERT(*emitted == false);
    3339             : 
    3340             :     // Try to convert an addition into a concat operation if the inputs
    3341             :     // indicate this might be a concatenation.
    3342             : 
    3343             :     // Only try to replace this with concat when we have an addition.
    3344         249 :     if (op != JSOP_ADD)
    3345           3 :         return Ok();
    3346             : 
    3347         246 :     trackOptimizationAttempt(TrackedStrategy::BinaryArith_Concat);
    3348             : 
    3349             :     // Make sure one of the inputs is a string.
    3350         246 :     if (left->type() != MIRType::String && right->type() != MIRType::String) {
    3351         208 :         trackOptimizationOutcome(TrackedOutcome::OperandNotString);
    3352         208 :         return Ok();
    3353             :     }
    3354             : 
    3355             :     // The non-string input (if present) should be atleast easily coercible to string.
    3356          52 :     if (right->type() != MIRType::String &&
    3357          42 :         (right->mightBeType(MIRType::Symbol) || right->mightBeType(MIRType::Object) ||
    3358          14 :          right->mightBeMagicType()))
    3359             :     {
    3360           0 :         trackOptimizationOutcome(TrackedOutcome::OperandNotEasilyCoercibleToString);
    3361           0 :         return Ok();
    3362             :     }
    3363          50 :     if (left->type() != MIRType::String &&
    3364          36 :         (left->mightBeType(MIRType::Symbol) || left->mightBeType(MIRType::Object) ||
    3365          12 :          left->mightBeMagicType()))
    3366             :     {
    3367           0 :         trackOptimizationOutcome(TrackedOutcome::OperandNotEasilyCoercibleToString);
    3368           0 :         return Ok();
    3369             :     }
    3370             : 
    3371          38 :     MConcat* ins = MConcat::New(alloc(), left, right);
    3372          38 :     current->add(ins);
    3373          38 :     current->push(ins);
    3374             : 
    3375          38 :     MOZ_TRY(maybeInsertResume());
    3376             : 
    3377          38 :     trackOptimizationSuccess();
    3378          38 :     *emitted = true;
    3379          38 :     return Ok();
    3380             : }
    3381             : 
    3382             : AbortReasonOr<Ok>
    3383           0 : IonBuilder::powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* power,
    3384             :                               MIRType outputType)
    3385             : {
    3386             :     // Typechecking.
    3387           0 :     MDefinition* output = nullptr;
    3388           0 :     MIRType baseType = base->type();
    3389           0 :     MIRType powerType = power->type();
    3390             : 
    3391           0 :     if (outputType != MIRType::Int32 && outputType != MIRType::Double)
    3392           0 :         return Ok();
    3393           0 :     if (!IsNumberType(baseType))
    3394           0 :         return Ok();
    3395           0 :     if (!IsNumberType(powerType))
    3396           0 :         return Ok();
    3397             : 
    3398           0 :     if (powerType == MIRType::Float32)
    3399           0 :         powerType = MIRType::Double;
    3400             : 
    3401           0 :     MPow* pow = MPow::New(alloc(), base, power, powerType);
    3402           0 :     current->add(pow);
    3403           0 :     output = pow;
    3404             : 
    3405             :     // Cast to the right type
    3406           0 :     if (outputType == MIRType::Int32 && output->type() != MIRType::Int32) {
    3407           0 :         MToInt32* toInt = MToInt32::New(alloc(), output);
    3408           0 :         current->add(toInt);
    3409           0 :         output = toInt;
    3410             :     }
    3411           0 :     if (outputType == MIRType::Double && output->type() != MIRType::Double) {
    3412           0 :         MToDouble* toDouble = MToDouble::New(alloc(), output);
    3413           0 :         current->add(toDouble);
    3414           0 :         output = toDouble;
    3415             :     }
    3416             : 
    3417           0 :     current->push(output);
    3418           0 :     *emitted = true;
    3419           0 :     return Ok();
    3420             : }
    3421             : 
    3422             : static inline bool
    3423         422 : SimpleArithOperand(MDefinition* op)
    3424             : {
    3425         422 :     return !op->mightBeType(MIRType::Object)
    3426         422 :         && !op->mightBeType(MIRType::String)
    3427         422 :         && !op->mightBeType(MIRType::Symbol)
    3428         422 :         && !op->mightBeType(MIRType::MagicOptimizedArguments)
    3429         422 :         && !op->mightBeType(MIRType::MagicHole)
    3430         844 :         && !op->mightBeType(MIRType::MagicIsConstructing);
    3431             : }
    3432             : 
    3433             : AbortReasonOr<Ok>
    3434         211 : IonBuilder::binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
    3435             : {
    3436         211 :     MOZ_ASSERT(*emitted == false);
    3437             : 
    3438             :     // Try to emit a specialized binary instruction based on the input types
    3439             :     // of the operands.
    3440             : 
    3441         211 :     trackOptimizationAttempt(TrackedStrategy::BinaryArith_SpecializedTypes);
    3442             : 
    3443             :     // Anything complex - strings, symbols, and objects - are not specialized
    3444         211 :     if (!SimpleArithOperand(left) || !SimpleArithOperand(right)) {
    3445           0 :         trackOptimizationOutcome(TrackedOutcome::OperandNotSimpleArith);
    3446           0 :         return Ok();
    3447             :     }
    3448             : 
    3449             :     // One of the inputs need to be a number.
    3450         211 :     if (!IsNumberType(left->type()) && !IsNumberType(right->type())) {
    3451           0 :         trackOptimizationOutcome(TrackedOutcome::OperandNotNumber);
    3452           0 :         return Ok();
    3453             :     }
    3454             : 
    3455         211 :     MDefinition::Opcode defOp = JSOpToMDefinition(op);
    3456         211 :     MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
    3457         211 :     ins->setNumberSpecialization(alloc(), inspector, pc);
    3458             : 
    3459         211 :     if (op == JSOP_ADD || op == JSOP_MUL)
    3460         211 :         ins->setCommutative();
    3461             : 
    3462         211 :     current->add(ins);
    3463         211 :     current->push(ins);
    3464             : 
    3465         211 :     MOZ_ASSERT(!ins->isEffectful());
    3466         211 :     MOZ_TRY(maybeInsertResume());
    3467             : 
    3468         211 :     trackOptimizationSuccess();
    3469         211 :     *emitted = true;
    3470         211 :     return Ok();
    3471             : }
    3472             : 
    3473             : AbortReasonOr<Ok>
    3474           0 : IonBuilder::binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
    3475             :                                                          MDefinition* left, MDefinition* right)
    3476             : {
    3477           0 :     MOZ_ASSERT(*emitted == false);
    3478             : 
    3479             :     // Try to emit a specialized binary instruction speculating the
    3480             :     // type using the baseline caches.
    3481             : 
    3482           0 :     trackOptimizationAttempt(TrackedStrategy::BinaryArith_SpecializedOnBaselineTypes);
    3483             : 
    3484           0 :     MIRType specialization = inspector->expectedBinaryArithSpecialization(pc);
    3485           0 :     if (specialization == MIRType::None) {
    3486           0 :         trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
    3487           0 :         return Ok();
    3488             :     }
    3489             : 
    3490           0 :     MDefinition::Opcode def_op = JSOpToMDefinition(op);
    3491           0 :     MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
    3492           0 :     ins->setSpecialization(specialization);
    3493             : 
    3494           0 :     current->add(ins);
    3495           0 :     current->push(ins);
    3496             : 
    3497           0 :     MOZ_ASSERT(!ins->isEffectful());
    3498           0 :     MOZ_TRY(maybeInsertResume());
    3499             : 
    3500           0 :     trackOptimizationSuccess();
    3501           0 :     *emitted = true;
    3502           0 :     return Ok();
    3503             : }
    3504             : 
    3505             : AbortReasonOr<Ok>
    3506           0 : IonBuilder::arithTrySharedStub(bool* emitted, JSOp op,
    3507             :                                MDefinition* left, MDefinition* right)
    3508             : {
    3509           0 :     MOZ_ASSERT(*emitted == false);
    3510           0 :     JSOp actualOp = JSOp(*pc);
    3511             : 
    3512             :     // Try to emit a shared stub cache.
    3513             : 
    3514           0 :     if (JitOptions.disableSharedStubs)
    3515           0 :         return Ok();
    3516             : 
    3517             :     // The actual jsop 'jsop_pos' is not supported yet.
    3518           0 :     if (actualOp == JSOP_POS)
    3519           0 :         return Ok();
    3520             : 
    3521             :     // FIXME: The JSOP_BITNOT path doesn't track optimizations yet.
    3522           0 :     if (actualOp != JSOP_BITNOT) {
    3523           0 :         trackOptimizationAttempt(TrackedStrategy::BinaryArith_SharedCache);
    3524           0 :         trackOptimizationSuccess();
    3525             :     }
    3526             : 
    3527           0 :     MInstruction* stub = nullptr;
    3528           0 :     switch (actualOp) {
    3529             :       case JSOP_NEG:
    3530             :       case JSOP_BITNOT:
    3531           0 :         MOZ_ASSERT_IF(op == JSOP_MUL,
    3532             :                       left->maybeConstantValue() && left->maybeConstantValue()->toInt32() == -1);
    3533           0 :         MOZ_ASSERT_IF(op != JSOP_MUL, !left);
    3534             : 
    3535           0 :         stub = MUnarySharedStub::New(alloc(), right);
    3536           0 :         break;
    3537             :       case JSOP_ADD:
    3538             :       case JSOP_SUB:
    3539             :       case JSOP_MUL:
    3540             :       case JSOP_DIV:
    3541             :       case JSOP_MOD:
    3542             :       case JSOP_POW:
    3543           0 :         stub = MBinarySharedStub::New(alloc(), left, right);
    3544           0 :         break;
    3545             :       default:
    3546           0 :         MOZ_CRASH("unsupported arith");
    3547             :     }
    3548             : 
    3549           0 :     current->add(stub);
    3550           0 :     current->push(stub);
    3551             : 
    3552             :     // Decrease type from 'any type' to 'empty type' when one of the operands
    3553             :     // is 'empty typed'.
    3554           0 :     maybeMarkEmpty(stub);
    3555             : 
    3556           0 :     MOZ_TRY(resumeAfter(stub));
    3557             : 
    3558           0 :     *emitted = true;
    3559           0 :     return Ok();
    3560             : }
    3561             : 
    3562             : AbortReasonOr<Ok>
    3563         249 : IonBuilder::jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right)
    3564             : {
    3565         249 :     bool emitted = false;
    3566             : 
    3567         249 :     startTrackingOptimizations();
    3568             : 
    3569         249 :     trackTypeInfo(TrackedTypeSite::Operand, left->type(), left->resultTypeSet());
    3570         249 :     trackTypeInfo(TrackedTypeSite::Operand, right->type(), right->resultTypeSet());
    3571             : 
    3572         249 :     if (!forceInlineCaches()) {
    3573         249 :         MOZ_TRY(binaryArithTryConcat(&emitted, op, left, right));
    3574         249 :         if (emitted)
    3575          38 :             return Ok();
    3576             : 
    3577         211 :         MOZ_TRY(binaryArithTrySpecialized(&emitted, op, left, right));
    3578         211 :         if (emitted)
    3579         211 :             return Ok();
    3580             : 
    3581           0 :         MOZ_TRY(binaryArithTrySpecializedOnBaselineInspector(&emitted, op, left, right));
    3582           0 :         if (emitted)
    3583           0 :             return Ok();
    3584             :     }
    3585             : 
    3586           0 :     MOZ_TRY(arithTrySharedStub(&emitted, op, left, right));
    3587           0 :     if (emitted)
    3588           0 :         return Ok();
    3589             : 
    3590             :     // Not possible to optimize. Do a slow vm call.
    3591           0 :     trackOptimizationAttempt(TrackedStrategy::BinaryArith_Call);
    3592           0 :     trackOptimizationSuccess();
    3593             : 
    3594           0 :     MDefinition::Opcode def_op = JSOpToMDefinition(op);
    3595           0 :     MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
    3596             : 
    3597             :     // Decrease type from 'any type' to 'empty type' when one of the operands
    3598             :     // is 'empty typed'.
    3599           0 :     maybeMarkEmpty(ins);
    3600             : 
    3601           0 :     current->add(ins);
    3602           0 :     current->push(ins);
    3603           0 :     MOZ_ASSERT(ins->isEffectful());
    3604           0 :     return resumeAfter(ins);
    3605             : }
    3606             : 
    3607             : AbortReasonOr<Ok>
    3608         246 : IonBuilder::jsop_binary_arith(JSOp op)
    3609             : {
    3610         246 :     MDefinition* right = current->pop();
    3611         246 :     MDefinition* left = current->pop();
    3612             : 
    3613         246 :     return jsop_binary_arith(op, left, right);
    3614             : }
    3615             : 
    3616             : 
    3617             : AbortReasonOr<Ok>
    3618           0 : IonBuilder::jsop_pow()
    3619             : {
    3620           0 :     MDefinition* exponent = current->pop();
    3621           0 :     MDefinition* base = current->pop();
    3622             : 
    3623           0 :     bool emitted = false;
    3624             : 
    3625           0 :     if (!forceInlineCaches()) {
    3626           0 :         MOZ_TRY(powTrySpecialized(&emitted, base, exponent, MIRType::Double));
    3627           0 :         if (emitted)
    3628           0 :             return Ok();
    3629             :     }
    3630             : 
    3631           0 :     MOZ_TRY(arithTrySharedStub(&emitted, JSOP_POW, base, exponent));
    3632           0 :     if (emitted)
    3633           0 :         return Ok();
    3634             : 
    3635             :     // For now, use MIRType::None as a safe cover-all. See bug 1188079.
    3636           0 :     MPow* pow = MPow::New(alloc(), base, exponent, MIRType::None);
    3637           0 :     current->add(pow);
    3638           0 :     current->push(pow);
    3639           0 :     MOZ_ASSERT(pow->isEffectful());
    3640           0 :     return resumeAfter(pow);
    3641             : }
    3642             : 
    3643             : AbortReasonOr<Ok>
    3644         168 : IonBuilder::jsop_pos()
    3645             : {
    3646         168 :     if (IsNumberType(current->peek(-1)->type())) {
    3647             :         // Already int32 or double. Set the operand as implicitly used so it
    3648             :         // doesn't get optimized out if it has no other uses, as we could bail
    3649             :         // out.
    3650         165 :         current->peek(-1)->setImplicitlyUsedUnchecked();
    3651         165 :         return Ok();
    3652             :     }
    3653             : 
    3654             :     // Compile +x as x * 1.
    3655           3 :     MDefinition* value = current->pop();
    3656           3 :     MConstant* one = MConstant::New(alloc(), Int32Value(1));
    3657           3 :     current->add(one);
    3658             : 
    3659           3 :     return jsop_binary_arith(JSOP_MUL, value, one);
    3660             : }
    3661             : 
    3662             : AbortReasonOr<Ok>
    3663           0 : IonBuilder::jsop_neg()
    3664             : {
    3665             :     // Since JSOP_NEG does not use a slot, we cannot push the MConstant.
    3666             :     // The MConstant is therefore passed to JSOP_MUL without slot traffic.
    3667           0 :     MConstant* negator = MConstant::New(alloc(), Int32Value(-1));
    3668           0 :     current->add(negator);
    3669             : 
    3670           0 :     MDefinition* right = current->pop();
    3671             : 
    3672           0 :     return jsop_binary_arith(JSOP_MUL, negator, right);
    3673             : }
    3674             : 
    3675             : AbortReasonOr<Ok>
    3676           0 : IonBuilder::jsop_tostring()
    3677             : {
    3678           0 :     if (current->peek(-1)->type() == MIRType::String)
    3679           0 :         return Ok();
    3680             : 
    3681           0 :     MDefinition* value = current->pop();
    3682           0 :     MToString* ins = MToString::New(alloc(), value);
    3683           0 :     current->add(ins);
    3684           0 :     current->push(ins);
    3685           0 :     MOZ_ASSERT(!ins->isEffectful());
    3686           0 :     return Ok();
    3687             : }
    3688             : 
    3689             : class AutoAccumulateReturns
    3690             : {
    3691             :     MIRGraph& graph_;
    3692             :     MIRGraphReturns* prev_;
    3693             : 
    3694             :   public:
    3695          33 :     AutoAccumulateReturns(MIRGraph& graph, MIRGraphReturns& returns)
    3696          33 :       : graph_(graph)
    3697             :     {
    3698          33 :         prev_ = graph_.returnAccumulator();
    3699          33 :         graph_.setReturnAccumulator(&returns);
    3700          33 :     }
    3701          66 :     ~AutoAccumulateReturns() {
    3702          33 :         graph_.setReturnAccumulator(prev_);
    3703          33 :     }
    3704             : };
    3705             : 
    3706             : IonBuilder::InliningResult
    3707          33 : IonBuilder::inlineScriptedCall(CallInfo& callInfo, JSFunction* target)
    3708             : {
    3709          33 :     MOZ_ASSERT(target->hasScript());
    3710          33 :     MOZ_ASSERT(IsIonInlinablePC(pc));
    3711             : 
    3712          33 :     MBasicBlock::BackupPoint backup(current);
    3713          33 :     if (!backup.init(alloc()))
    3714           0 :         return abort(AbortReason::Alloc);
    3715             : 
    3716          33 :     callInfo.setImplicitlyUsedUnchecked();
    3717             : 
    3718             :     // Ensure sufficient space in the slots: needed for inlining from FUNAPPLY.
    3719          33 :     uint32_t depth = current->stackDepth() + callInfo.numFormals();
    3720          33 :     if (depth > current->nslots()) {
    3721           0 :         if (!current->increaseSlots(depth - current->nslots()))
    3722           0 :             return abort(AbortReason::Alloc);
    3723             :     }
    3724             : 
    3725             :     // Create new |this| on the caller-side for inlined constructors.
    3726          33 :     if (callInfo.constructing()) {
    3727           0 :         MDefinition* thisDefn = createThis(target, callInfo.fun(), callInfo.getNewTarget());
    3728           0 :         if (!thisDefn)
    3729           0 :             return abort(AbortReason::Alloc);
    3730           0 :         callInfo.setThis(thisDefn);
    3731             :     }
    3732             : 
    3733             :     // Capture formals in the outer resume point.
    3734          33 :     callInfo.pushFormals(current);
    3735             : 
    3736             :     MResumePoint* outerResumePoint =
    3737          33 :         MResumePoint::New(alloc(), current, pc, MResumePoint::Outer);
    3738          33 :     if (!outerResumePoint)
    3739           0 :         return abort(AbortReason::Alloc);
    3740          33 :     current->setOuterResumePoint(outerResumePoint);
    3741             : 
    3742             :     // Pop formals again, except leave |fun| on stack for duration of call.
    3743          33 :     callInfo.popFormals(current);
    3744          33 :     current->push(callInfo.fun());
    3745             : 
    3746          33 :     JSScript* calleeScript = target->nonLazyScript();
    3747          33 :     BaselineInspector inspector(calleeScript);
    3748             : 
    3749             :     // Improve type information of |this| when not set.
    3750          33 :     if (callInfo.constructing() &&
    3751           0 :         !callInfo.thisArg()->resultTypeSet())
    3752             :     {
    3753           0 :         StackTypeSet* types = TypeScript::ThisTypes(calleeScript);
    3754           0 :         if (types && !types->unknown()) {
    3755           0 :             TemporaryTypeSet* clonedTypes = types->clone(alloc_->lifoAlloc());
    3756           0 :             if (!clonedTypes)
    3757           0 :                 return abort(AbortReason::Alloc);
    3758           0 :             MTypeBarrier* barrier = MTypeBarrier::New(alloc(), callInfo.thisArg(), clonedTypes);
    3759           0 :             current->add(barrier);
    3760           0 :             if (barrier->type() == MIRType::Undefined)
    3761           0 :                 callInfo.setThis(constant(UndefinedValue()));
    3762           0 :             else if (barrier->type() == MIRType::Null)
    3763           0 :                 callInfo.setThis(constant(NullValue()));
    3764             :             else
    3765           0 :                 callInfo.setThis(barrier);
    3766             :         }
    3767             :     }
    3768             : 
    3769             :     // Start inlining.
    3770          33 :     LifoAlloc* lifoAlloc = alloc_->lifoAlloc();
    3771             :     InlineScriptTree* inlineScriptTree =
    3772          33 :         info().inlineScriptTree()->addCallee(alloc_, pc, calleeScript);
    3773          33 :     if (!inlineScriptTree)
    3774           0 :         return abort(AbortReason::Alloc);
    3775         132 :     CompileInfo* info = lifoAlloc->new_<CompileInfo>(calleeScript, target,
    3776             :                                                      (jsbytecode*)nullptr,
    3777          66 :                                                      this->info().analysisMode(),
    3778             :                                                      /* needsArgsObj = */ false,
    3779          33 :                                                      inlineScriptTree);
    3780          33 :     if (!info)
    3781           0 :         return abort(AbortReason::Alloc);
    3782             : 
    3783          66 :     MIRGraphReturns returns(alloc());
    3784          66 :     AutoAccumulateReturns aar(graph(), returns);
    3785             : 
    3786             :     // Build the graph.
    3787          33 :     IonBuilder inlineBuilder(analysisContext, compartment, options, &alloc(), &graph(), constraints(),
    3788          66 :                              &inspector, info, &optimizationInfo(), nullptr, inliningDepth_ + 1,
    3789         165 :                              loopDepth_);
    3790          33 :     AbortReasonOr<Ok> result = inlineBuilder.buildInline(this, outerResumePoint, callInfo);
    3791          33 :     if (result.isErr()) {
    3792           3 :         if (analysisContext && analysisContext->isExceptionPending()) {
    3793           0 :             JitSpew(JitSpew_IonAbort, "Inline builder raised exception.");
    3794           0 :             MOZ_ASSERT(result.unwrapErr() == AbortReason::Error);
    3795           0 :             return Err(result.unwrapErr());
    3796             :         }
    3797             : 
    3798             :         // Inlining the callee failed. Mark the callee as uninlineable only if
    3799             :         // the inlining was aborted for a non-exception reason.
    3800           3 :         switch (result.unwrapErr()) {
    3801             :           case AbortReason::Disable:
    3802           0 :             calleeScript->setUninlineable();
    3803           0 :             if (!JitOptions.disableInlineBacktracking) {
    3804           0 :                 current = backup.restore();
    3805           0 :                 if (!current)
    3806           0 :                     return abort(AbortReason::Alloc);
    3807           0 :                 return InliningStatus_NotInlined;
    3808             :             }
    3809           0 :             return abort(AbortReason::Inlining);
    3810             : 
    3811             :           case AbortReason::PreliminaryObjects: {
    3812           3 :             const ObjectGroupVector& groups = inlineBuilder.abortedPreliminaryGroups();
    3813           3 :             MOZ_ASSERT(!groups.empty());
    3814           6 :             for (size_t i = 0; i < groups.length(); i++)
    3815           3 :                 addAbortedPreliminaryGroup(groups[i]);
    3816           3 :             return Err(result.unwrapErr());
    3817             :           }
    3818             : 
    3819             :           case AbortReason::Alloc:
    3820             :           case AbortReason::Inlining:
    3821             :           case AbortReason::Error:
    3822           0 :             return Err(result.unwrapErr());
    3823             : 
    3824             :           case AbortReason::NoAbort:
    3825           0 :             MOZ_CRASH("Abort with AbortReason::NoAbort");
    3826             :             return abort(AbortReason::Error);
    3827             :         }
    3828             :     }
    3829             : 
    3830          30 :     if (returns.empty()) {
    3831             :         // Inlining of functions that have no exit is not supported.
    3832           0 :         calleeScript->setUninlineable();
    3833           0 :         if (!JitOptions.disableInlineBacktracking) {
    3834           0 :             current = backup.restore();
    3835           0 :             if (!current)
    3836           0 :                 return abort(AbortReason::Alloc);
    3837           0 :             return InliningStatus_NotInlined;
    3838             :         }
    3839           0 :         return abort(AbortReason::Inlining);
    3840             :     }
    3841             : 
    3842             :     // Create return block.
    3843          30 :     jsbytecode* postCall = GetNextPc(pc);
    3844             :     MBasicBlock* returnBlock;
    3845          30 :     MOZ_TRY_VAR(returnBlock, newBlock(nullptr, postCall));
    3846          30 :     graph().addBlock(returnBlock);
    3847          30 :     returnBlock->setCallerResumePoint(callerResumePoint_);
    3848             : 
    3849             :     // Inherit the slots from current and pop |fun|.
    3850          30 :     returnBlock->inheritSlots(current);
    3851          30 :     returnBlock->pop();
    3852             : 
    3853             :     // Accumulate return values.
    3854          30 :     MDefinition* retvalDefn = patchInlinedReturns(callInfo, returns, returnBlock);
    3855          30 :     if (!retvalDefn)
    3856           0 :         return abort(AbortReason::Alloc);
    3857          30 :     returnBlock->push(retvalDefn);
    3858             : 
    3859             :     // Initialize entry slots now that the stack has been fixed up.
    3860          30 :     if (!returnBlock->initEntrySlots(alloc()))
    3861           0 :         return abort(AbortReason::Alloc);
    3862             : 
    3863          30 :     MOZ_TRY(setCurrentAndSpecializePhis(returnBlock));
    3864             : 
    3865          30 :     return InliningStatus_Inlined;
    3866             : }
    3867             : 
    3868             : MDefinition*
    3869          60 : IonBuilder::patchInlinedReturn(CallInfo& callInfo, MBasicBlock* exit, MBasicBlock* bottom)
    3870             : {
    3871             :     // Replaces the MReturn in the exit block with an MGoto.
    3872          60 :     MDefinition* rdef = exit->lastIns()->toReturn()->input();
    3873          60 :     exit->discardLastIns();
    3874             : 
    3875             :     // Constructors must be patched by the caller to always return an object.
    3876          60 :     if (callInfo.constructing()) {
    3877           0 :         if (rdef->type() == MIRType::Value) {
    3878             :             // Unknown return: dynamically detect objects.
    3879           0 :             MReturnFromCtor* filter = MReturnFromCtor::New(alloc(), rdef, callInfo.thisArg());
    3880           0 :             exit->add(filter);
    3881           0 :             rdef = filter;
    3882           0 :         } else if (rdef->type() != MIRType::Object) {
    3883             :             // Known non-object return: force |this|.
    3884           0 :             rdef = callInfo.thisArg();
    3885             :         }
    3886          60 :     } else if (callInfo.isSetter()) {
    3887             :         // Setters return their argument, not whatever value is returned.
    3888           0 :         rdef = callInfo.getArg(0);
    3889             :     }
    3890             : 
    3891          60 :     if (!callInfo.isSetter())
    3892          60 :         rdef = specializeInlinedReturn(rdef, exit);
    3893             : 
    3894          60 :     MGoto* replacement = MGoto::New(alloc(), bottom);
    3895          60 :     exit->end(replacement);
    3896          60 :     if (!bottom->addPredecessorWithoutPhis(exit))
    3897           0 :         return nullptr;
    3898             : 
    3899          60 :     return rdef;
    3900             : }
    3901             : 
    3902             : MDefinition*
    3903          60 : IonBuilder::specializeInlinedReturn(MDefinition* rdef, MBasicBlock* exit)
    3904             : {
    3905             :     // Remove types from the return definition that weren't observed.
    3906          60 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    3907             : 
    3908             :     // The observed typeset doesn't contain extra information.
    3909          60 :     if (types->empty() || types->unknown())
    3910           0 :         return rdef;
    3911             : 
    3912             :     // Decide if specializing is needed using the result typeset if available,
    3913             :     // else use the result type.
    3914             : 
    3915          60 :     if (rdef->resultTypeSet()) {
    3916             :         // Don't specialize if return typeset is a subset of the
    3917             :         // observed typeset. The return typeset is already more specific.
    3918          50 :         if (rdef->resultTypeSet()->isSubset(types))
    3919          50 :             return rdef;
    3920             :     } else {
    3921          10 :         MIRType observedType = types->getKnownMIRType();
    3922             : 
    3923             :         // Don't specialize if type is MIRType::Float32 and TI reports
    3924             :         // MIRType::Double. Float is more specific than double.
    3925          10 :         if (observedType == MIRType::Double && rdef->type() == MIRType::Float32)
    3926           0 :             return rdef;
    3927             : 
    3928             :         // Don't specialize if types are inaccordance, except for MIRType::Value
    3929             :         // and MIRType::Object (when not unknown object), since the typeset
    3930             :         // contains more specific information.
    3931          30 :         if (observedType == rdef->type() &&
    3932          30 :             observedType != MIRType::Value &&
    3933           0 :             (observedType != MIRType::Object || types->unknownObject()))
    3934             :         {
    3935          10 :             return rdef;
    3936             :         }
    3937             :     }
    3938             : 
    3939           0 :     setCurrent(exit);
    3940             : 
    3941           0 :     MTypeBarrier* barrier = nullptr;
    3942           0 :     rdef = addTypeBarrier(rdef, types, BarrierKind::TypeSet, &barrier);
    3943           0 :     if (barrier)
    3944           0 :         barrier->setNotMovable();
    3945             : 
    3946           0 :     return rdef;
    3947             : }
    3948             : 
    3949             : MDefinition*
    3950          30 : IonBuilder::patchInlinedReturns(CallInfo& callInfo, MIRGraphReturns& returns, MBasicBlock* bottom)
    3951             : {
    3952             :     // Replaces MReturns with MGotos, returning the MDefinition
    3953             :     // representing the return value, or nullptr.
    3954          30 :     MOZ_ASSERT(returns.length() > 0);
    3955             : 
    3956          30 :     if (returns.length() == 1)
    3957          24 :         return patchInlinedReturn(callInfo, returns[0], bottom);
    3958             : 
    3959             :     // Accumulate multiple returns with a phi.
    3960           6 :     MPhi* phi = MPhi::New(alloc());
    3961           6 :     if (!phi->reserveLength(returns.length()))
    3962           0 :         return nullptr;
    3963             : 
    3964          42 :     for (size_t i = 0; i < returns.length(); i++) {
    3965          36 :         MDefinition* rdef = patchInlinedReturn(callInfo, returns[i], bottom);
    3966          36 :         if (!rdef)
    3967           0 :             return nullptr;
    3968          36 :         phi->addInput(rdef);
    3969             :     }
    3970             : 
    3971           6 :     bottom->addPhi(phi);
    3972           6 :     return phi;
    3973             : }
    3974             : 
    3975             : IonBuilder::InliningDecision
    3976         363 : IonBuilder::makeInliningDecision(JSObject* targetArg, CallInfo& callInfo)
    3977             : {
    3978             :     // When there is no target, inlining is impossible.
    3979         363 :     if (targetArg == nullptr) {
    3980           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNoTarget);
    3981           0 :         return InliningDecision_DontInline;
    3982             :     }
    3983             : 
    3984             :     // Inlining non-function targets is handled by inlineNonFunctionCall().
    3985         363 :     if (!targetArg->is<JSFunction>())
    3986           0 :         return InliningDecision_Inline;
    3987             : 
    3988         363 :     JSFunction* target = &targetArg->as<JSFunction>();
    3989             : 
    3990             :     // Never inline during the arguments usage analysis.
    3991         363 :     if (info().analysisMode() == Analysis_ArgumentsUsage)
    3992         185 :         return InliningDecision_DontInline;
    3993             : 
    3994             :     // Native functions provide their own detection in inlineNativeCall().
    3995         178 :     if (target->isNative())
    3996         116 :         return InliningDecision_Inline;
    3997             : 
    3998             :     // Determine whether inlining is possible at callee site
    3999          62 :     InliningDecision decision = canInlineTarget(target, callInfo);
    4000          62 :     if (decision != InliningDecision_Inline)
    4001          18 :         return decision;
    4002             : 
    4003             :     // Heuristics!
    4004          44 :     JSScript* targetScript = target->nonLazyScript();
    4005             : 
    4006             :     // Callee must not be excessively large.
    4007             :     // This heuristic also applies to the callsite as a whole.
    4008          44 :     bool offThread = options.offThreadCompilationAvailable();
    4009          44 :     if (targetScript->length() > optimizationInfo().inlineMaxBytecodePerCallSite(offThread)) {
    4010           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineBigCallee);
    4011           0 :         return DontInline(targetScript, "Vetoed: callee excessively large");
    4012             :     }
    4013             : 
    4014             :     // Callee must have been called a few times to have somewhat stable
    4015             :     // type information, except for definite properties analysis,
    4016             :     // as the caller has not run yet.
    4017          99 :     if (targetScript->getWarmUpCount() < optimizationInfo().inliningWarmUpThreshold() &&
    4018          55 :         !targetScript->baselineScript()->ionCompiledOrInlined() &&
    4019          11 :         info().analysisMode() != Analysis_DefiniteProperties)
    4020             :     {
    4021          11 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNotHot);
    4022          11 :         JitSpew(JitSpew_Inlining, "Cannot inline %s:%" PRIuSIZE ": callee is insufficiently hot.",
    4023          11 :                 targetScript->filename(), targetScript->lineno());
    4024          11 :         return InliningDecision_WarmUpCountTooLow;
    4025             :     }
    4026             : 
    4027             :     // Don't inline if the callee is known to inline a lot of code, to avoid
    4028             :     // huge MIR graphs.
    4029          33 :     uint32_t inlinedBytecodeLength = targetScript->baselineScript()->inlinedBytecodeLength();
    4030          33 :     if (inlinedBytecodeLength > optimizationInfo().inlineMaxCalleeInlinedBytecodeLength()) {
    4031           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineBigCalleeInlinedBytecodeLength);
    4032           0 :         return DontInline(targetScript, "Vetoed: callee inlinedBytecodeLength is too big");
    4033             :     }
    4034             : 
    4035          33 :     IonBuilder* outerBuilder = outermostBuilder();
    4036             : 
    4037             :     // Cap the total bytecode length we inline under a single script, to avoid
    4038             :     // excessive inlining in pathological cases.
    4039          33 :     size_t totalBytecodeLength = outerBuilder->inlinedBytecodeLength_ + targetScript->length();
    4040          33 :     if (totalBytecodeLength > optimizationInfo().inlineMaxTotalBytecodeLength()) {
    4041           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineExceededTotalBytecodeLength);
    4042           0 :         return DontInline(targetScript, "Vetoed: exceeding max total bytecode length");
    4043             :     }
    4044             : 
    4045             :     // Cap the inlining depth.
    4046             : 
    4047             :     uint32_t maxInlineDepth;
    4048          33 :     if (JitOptions.isSmallFunction(targetScript)) {
    4049          27 :         maxInlineDepth = optimizationInfo().smallFunctionMaxInlineDepth();
    4050             :     } else {
    4051           6 :         maxInlineDepth = optimizationInfo().maxInlineDepth();
    4052             : 
    4053             :         // Caller must not be excessively large.
    4054           6 :         if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength()) {
    4055           0 :             trackOptimizationOutcome(TrackedOutcome::CantInlineBigCaller);
    4056           0 :             return DontInline(targetScript, "Vetoed: caller excessively large");
    4057             :         }
    4058             :     }
    4059             : 
    4060          33 :     BaselineScript* outerBaseline = outermostBuilder()->script()->baselineScript();
    4061          33 :     if (inliningDepth_ >= maxInlineDepth) {
    4062             :         // We hit the depth limit and won't inline this function. Give the
    4063             :         // outermost script a max inlining depth of 0, so that it won't be
    4064             :         // inlined in other scripts. This heuristic is currently only used
    4065             :         // when we're inlining scripts with loops, see the comment below.
    4066           0 :         outerBaseline->setMaxInliningDepth(0);
    4067             : 
    4068           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth);
    4069           0 :         return DontInline(targetScript, "Vetoed: exceeding allowed inline depth");
    4070             :     }
    4071             : 
    4072             :     // Inlining functions with loops can be complicated. For instance, if we're
    4073             :     // close to the inlining depth limit and we inline the function f below, we
    4074             :     // can no longer inline the call to g:
    4075             :     //
    4076             :     //   function f() {
    4077             :     //      while (cond) {
    4078             :     //          g();
    4079             :     //      }
    4080             :     //   }
    4081             :     //
    4082             :     // If the loop has many iterations, it's more efficient to call f and inline
    4083             :     // g in f.
    4084             :     //
    4085             :     // To avoid this problem, we record a separate max inlining depth for each
    4086             :     // script, indicating at which depth we won't be able to inline all functions
    4087             :     // we inlined this time. This solves the issue above, because we will only
    4088             :     // inline f if it means we can also inline g.
    4089          33 :     if (targetScript->hasLoops() &&
    4090           0 :         inliningDepth_ >= targetScript->baselineScript()->maxInliningDepth())
    4091             :     {
    4092           0 :         trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth);
    4093           0 :         return DontInline(targetScript, "Vetoed: exceeding allowed script inline depth");
    4094             :     }
    4095             : 
    4096             :     // Update the max depth at which we can inline the outer script.
    4097          33 :     MOZ_ASSERT(maxInlineDepth > inliningDepth_);
    4098          33 :     uint32_t scriptInlineDepth = maxInlineDepth - inliningDepth_ - 1;
    4099          33 :     if (scriptInlineDepth < outerBaseline->maxInliningDepth())
    4100          11 :         outerBaseline->setMaxInliningDepth(scriptInlineDepth);
    4101             : 
    4102             :     // End of heuristics, we will inline this function.
    4103             : 
    4104          33 :     outerBuilder->inlinedBytecodeLength_ += targetScript->length();
    4105             : 
    4106          33 :     return InliningDecision_Inline;
    4107             : }
    4108             : 
    4109             : AbortReasonOr<Ok>
    4110           6 : IonBuilder::selectInliningTargets(const InliningTargets& targets, CallInfo& callInfo,
    4111             :                                   BoolVector& choiceSet, uint32_t* numInlineable)
    4112             : {
    4113           6 :     *numInlineable = 0;
    4114           6 :     uint32_t totalSize = 0;
    4115             : 
    4116             :     // For each target, ask whether it may be inlined.
    4117           6 :     if (!choiceSet.reserve(targets.length()))
    4118           0 :         return abort(AbortReason::Alloc);
    4119             : 
    4120             :     // Don't inline polymorphic sites during the definite properties analysis.
    4121             :     // AddClearDefiniteFunctionUsesInScript depends on this for correctness.
    4122           6 :     if (info().analysisMode() == Analysis_DefiniteProperties && targets.length() > 1)
    4123           0 :         return Ok();
    4124             : 
    4125          18 :     for (size_t i = 0; i < targets.length(); i++) {
    4126          12 :         JSObject* target = targets[i].target;
    4127             : 
    4128          12 :         trackOptimizationAttempt(TrackedStrategy::Call_Inline);
    4129          12 :         trackTypeInfo(TrackedTypeSite::Call_Target, target);
    4130             : 
    4131             :         bool inlineable;
    4132          12 :         InliningDecision decision = makeInliningDecision(target, callInfo);
    4133          12 :         switch (decision) {
    4134             :           case InliningDecision_Error:
    4135           0 :             return abort(AbortReason::Alloc);
    4136             :           case InliningDecision_DontInline:
    4137             :           case InliningDecision_WarmUpCountTooLow:
    4138           8 :             inlineable = false;
    4139           8 :             break;
    4140             :           case InliningDecision_Inline:
    4141           4 :             inlineable = true;
    4142           4 :             break;
    4143             :           default:
    4144           0 :             MOZ_CRASH("Unhandled InliningDecision value!");
    4145             :         }
    4146             : 
    4147          12 :         if (target->is<JSFunction>()) {
    4148             :             // Enforce a maximum inlined bytecode limit at the callsite.
    4149          12 :             if (inlineable && target->as<JSFunction>().isInterpreted()) {
    4150           4 :                 totalSize += target->as<JSFunction>().nonLazyScript()->length();
    4151           4 :                 bool offThread = options.offThreadCompilationAvailable();
    4152           4 :                 if (totalSize > optimizationInfo().inlineMaxBytecodePerCallSite(offThread))
    4153           0 :                     inlineable = false;
    4154             :             }
    4155             :         } else {
    4156             :             // Non-function targets are not supported by polymorphic inlining.
    4157           0 :             inlineable = false;
    4158             :         }
    4159             : 
    4160          12 :         choiceSet.infallibleAppend(inlineable);
    4161          12 :         if (inlineable)
    4162           4 :             *numInlineable += 1;
    4163             :     }
    4164             : 
    4165             :     // If optimization tracking is turned on and one of the inlineable targets
    4166             :     // is a native, track the type info of the call. Most native inlinings
    4167             :     // depend on the types of the arguments and the return value.
    4168           6 :     if (isOptimizationTrackingEnabled()) {
    4169           0 :         for (size_t i = 0; i < targets.length(); i++) {
    4170           0 :             if (choiceSet[i] && targets[i].target->as<JSFunction>().isNative()) {
    4171           0 :                 trackTypeInfo(callInfo);
    4172           0 :                 break;
    4173             :             }
    4174             :         }
    4175             :     }
    4176             : 
    4177           6 :     MOZ_ASSERT(choiceSet.length() == targets.length());
    4178           6 :     return Ok();
    4179             : }
    4180             : 
    4181             : static bool
    4182           1 : CanInlineGetPropertyCache(MGetPropertyCache* cache, MDefinition* thisDef)
    4183             : {
    4184           1 :     if (cache->value()->type() != MIRType::Object)
    4185           0 :         return false;
    4186             : 
    4187           1 :     if (cache->value() != thisDef)
    4188           0 :         return false;
    4189             : 
    4190           1 :     InlinePropertyTable* table = cache->propTable();
    4191           1 :     if (!table)
    4192           1 :         return false;
    4193           0 :     if (table->numEntries() == 0)
    4194           0 :         return false;
    4195           0 :     return true;
    4196             : }
    4197             : 
    4198             : class WrapMGetPropertyCache
    4199             : {
    4200             :     MGetPropertyCache* cache_;
    4201             : 
    4202             :   private:
    4203        1629 :     void discardPriorResumePoint() {
    4204        1629 :         if (!cache_)
    4205        1626 :             return;
    4206             : 
    4207           3 :         InlinePropertyTable* propTable = cache_->propTable();
    4208           3 :         if (!propTable)
    4209           3 :             return;
    4210           0 :         MResumePoint* rp = propTable->takePriorResumePoint();
    4211           0 :         if (!rp)
    4212           0 :             return;
    4213           0 :         cache_->block()->discardPreAllocatedResumePoint(rp);
    4214             :     }
    4215             : 
    4216             :   public:
    4217        1629 :     explicit WrapMGetPropertyCache(MGetPropertyCache* cache)
    4218        1629 :       : cache_(cache)
    4219        1629 :     { }
    4220             : 
    4221        3258 :     ~WrapMGetPropertyCache() {
    4222        1629 :         discardPriorResumePoint();
    4223        1629 :     }
    4224             : 
    4225         718 :     MGetPropertyCache* get() {
    4226         718 :         return cache_;
    4227             :     }
    4228             :     MGetPropertyCache* operator->() {
    4229             :         return get();
    4230             :     }
    4231             : 
    4232             :     // This function returns the cache given to the constructor if the
    4233             :     // GetPropertyCache can be moved into the ObjectGroup fallback path.
    4234           3 :     MGetPropertyCache* moveableCache(bool hasTypeBarrier, MDefinition* thisDef) {
    4235             :         // If we have unhandled uses of the MGetPropertyCache, then we cannot
    4236             :         // move it to the ObjectGroup fallback path.
    4237           3 :         if (!hasTypeBarrier) {
    4238           3 :             if (cache_->hasUses())
    4239           2 :                 return nullptr;
    4240             :         } else {
    4241             :             // There is the TypeBarrier consumer, so we check that this is the
    4242             :             // only consumer.
    4243           0 :             MOZ_ASSERT(cache_->hasUses());
    4244           0 :             if (!cache_->hasOneUse())
    4245           0 :                 return nullptr;
    4246             :         }
    4247             : 
    4248             :         // If the this-object is not identical to the object of the
    4249             :         // MGetPropertyCache, then we cannot use the InlinePropertyTable, or if
    4250             :         // we do not yet have enough information from the ObjectGroup.
    4251           1 :         if (!CanInlineGetPropertyCache(cache_, thisDef))
    4252           1 :             return nullptr;
    4253             : 
    4254           0 :         MGetPropertyCache* ret = cache_;
    4255           0 :         cache_ = nullptr;
    4256           0 :         return ret;
    4257             :     }
    4258             : };
    4259             : 
    4260             : MGetPropertyCache*
    4261         357 : IonBuilder::getInlineableGetPropertyCache(CallInfo& callInfo)
    4262             : {
    4263         357 :     if (callInfo.constructing())
    4264           1 :         return nullptr;
    4265             : 
    4266         356 :     MDefinition* thisDef = callInfo.thisArg();
    4267         356 :     if (thisDef->type() != MIRType::Object)
    4268         334 :         return nullptr;
    4269             : 
    4270          22 :     MDefinition* funcDef = callInfo.fun();
    4271          22 :     if (funcDef->type() != MIRType::Object)
    4272           0 :         return nullptr;
    4273             : 
    4274             :     // MGetPropertyCache with no uses may be optimized away.
    4275          22 :     if (funcDef->isGetPropertyCache()) {
    4276           6 :         WrapMGetPropertyCache cache(funcDef->toGetPropertyCache());
    4277           3 :         return cache.moveableCache(/* hasTypeBarrier = */ false, thisDef);
    4278             :     }
    4279             : 
    4280             :     // Optimize away the following common pattern:
    4281             :     // MTypeBarrier[MIRType::Object] <- MGetPropertyCache
    4282          19 :     if (funcDef->isTypeBarrier()) {
    4283           1 :         MTypeBarrier* barrier = funcDef->toTypeBarrier();
    4284           1 :         if (barrier->hasUses())
    4285           0 :             return nullptr;
    4286           1 :         if (barrier->type() != MIRType::Object)
    4287           0 :             return nullptr;
    4288           1 :         if (!barrier->input()->isGetPropertyCache())
    4289           1 :             return nullptr;
    4290             : 
    4291           0 :         WrapMGetPropertyCache cache(barrier->input()->toGetPropertyCache());
    4292           0 :         return cache.moveableCache(/* hasTypeBarrier = */ true, thisDef);
    4293             :     }
    4294             : 
    4295          18 :     return nullptr;
    4296             : }
    4297             : 
    4298             : IonBuilder::InliningResult
    4299         149 : IonBuilder::inlineSingleCall(CallInfo& callInfo, JSObject* targetArg)
    4300             : {
    4301             :     InliningStatus status;
    4302         149 :     if (!targetArg->is<JSFunction>()) {
    4303           0 :         MOZ_TRY_VAR(status, inlineNonFunctionCall(callInfo, targetArg));
    4304           0 :         trackInlineSuccess(status);
    4305           0 :         return status;
    4306             :     }
    4307             : 
    4308         149 :     JSFunction* target = &targetArg->as<JSFunction>();
    4309         149 :     if (target->isNative()) {
    4310         116 :         MOZ_TRY_VAR(status, inlineNativeCall(callInfo, target));
    4311         116 :         trackInlineSuccess(status);
    4312         116 :         return status;
    4313             :     }
    4314             : 
    4315             :     // Track success now, as inlining a scripted call makes a new return block
    4316             :     // which has a different pc than the current call pc.
    4317          33 :     trackInlineSuccess();
    4318          33 :     return inlineScriptedCall(callInfo, target);
    4319             : }
    4320             : 
    4321             : IonBuilder::InliningResult
    4322        1228 : IonBuilder::inlineCallsite(const InliningTargets& targets, CallInfo& callInfo)
    4323             : {
    4324        1228 :     if (targets.empty()) {
    4325         871 :         trackOptimizationAttempt(TrackedStrategy::Call_Inline);
    4326         871 :         trackOptimizationOutcome(TrackedOutcome::CantInlineNoTarget);
    4327         871 :         return InliningStatus_NotInlined;
    4328             :     }
    4329             : 
    4330             :     // Is the function provided by an MGetPropertyCache?
    4331             :     // If so, the cache may be movable to a fallback path, with a dispatch
    4332             :     // instruction guarding on the incoming ObjectGroup.
    4333         714 :     WrapMGetPropertyCache propCache(getInlineableGetPropertyCache(callInfo));
    4334         357 :     keepFallbackFunctionGetter(propCache.get());
    4335             : 
    4336             :     // Inline single targets -- unless they derive from a cache, in which case
    4337             :     // avoiding the cache and guarding is still faster.
    4338         357 :     if (!propCache.get() && targets.length() == 1) {
    4339         351 :         JSObject* target = targets[0].target;
    4340             : 
    4341         351 :         trackOptimizationAttempt(TrackedStrategy::Call_Inline);
    4342         351 :         trackTypeInfo(TrackedTypeSite::Call_Target, target);
    4343             : 
    4344         351 :         InliningDecision decision = makeInliningDecision(target, callInfo);
    4345         351 :         switch (decision) {
    4346             :           case InliningDecision_Error:
    4347           0 :             return abort(AbortReason::Alloc);
    4348             :           case InliningDecision_DontInline:
    4349         197 :             return InliningStatus_NotInlined;
    4350             :           case InliningDecision_WarmUpCountTooLow:
    4351           9 :             return InliningStatus_WarmUpCountTooLow;
    4352             :           case InliningDecision_Inline:
    4353         145 :             break;
    4354             :         }
    4355             : 
    4356             :         // Inlining will elminate uses of the original callee, but it needs to
    4357             :         // be preserved in phis if we bail out.  Mark the old callee definition as
    4358             :         // implicitly used to ensure this happens.
    4359         145 :         callInfo.fun()->setImplicitlyUsedUnchecked();
    4360             : 
    4361             :         // If the callee is not going to be a lambda (which may vary across
    4362             :         // different invocations), then the callee definition can be replaced by a
    4363             :         // constant.
    4364         145 :         if (target->isSingleton()) {
    4365             :             // Replace the function with an MConstant.
    4366         142 :             MConstant* constFun = constant(ObjectValue(*target));
    4367         142 :             if (callInfo.constructing() && callInfo.getNewTarget() == callInfo.fun())
    4368           1 :                 callInfo.setNewTarget(constFun);
    4369         142 :             callInfo.setFun(constFun);
    4370             :         }
    4371             : 
    4372         145 :         return inlineSingleCall(callInfo, target);
    4373             :     }
    4374             : 
    4375             :     // Choose a subset of the targets for polymorphic inlining.
    4376          12 :     BoolVector choiceSet(alloc());
    4377             :     uint32_t numInlined;
    4378           6 :     MOZ_TRY(selectInliningTargets(targets, callInfo, choiceSet, &numInlined));
    4379           6 :     if (numInlined == 0)
    4380           2 :         return InliningStatus_NotInlined;
    4381             : 
    4382             :     // Perform a polymorphic dispatch.
    4383           4 :     MOZ_TRY(inlineCalls(callInfo, targets, choiceSet, propCache.get()));
    4384             : 
    4385           4 :     return InliningStatus_Inlined;
    4386             : }
    4387             : 
    4388             : AbortReasonOr<Ok>
    4389           4 : IonBuilder::inlineGenericFallback(JSFunction* target, CallInfo& callInfo, MBasicBlock* dispatchBlock)
    4390             : {
    4391             :     // Generate a new block with all arguments on-stack.
    4392             :     MBasicBlock* fallbackBlock;
    4393           4 :     MOZ_TRY_VAR(fallbackBlock, newBlock(dispatchBlock, pc));
    4394           4 :     graph().addBlock(fallbackBlock);
    4395             : 
    4396             :     // Create a new CallInfo to track modified state within this block.
    4397           8 :     CallInfo fallbackInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue());
    4398           4 :     if (!fallbackInfo.init(callInfo))
    4399           0 :         return abort(AbortReason::Alloc);
    4400           4 :     fallbackInfo.popFormals(fallbackBlock);
    4401             : 
    4402             :     // Generate an MCall, which uses stateful |current|.
    4403           4 :     MOZ_TRY(setCurrentAndSpecializePhis(fallbackBlock));
    4404           4 :     MOZ_TRY(makeCall(target, fallbackInfo));
    4405             : 
    4406             :     // Pass return block to caller as |current|.
    4407           4 :     return Ok();
    4408             : }
    4409             : 
    4410             : AbortReasonOr<Ok>
    4411           0 : IonBuilder::inlineObjectGroupFallback(CallInfo& callInfo, MBasicBlock* dispatchBlock,
    4412             :                                      MObjectGroupDispatch* dispatch, MGetPropertyCache* cache,
    4413             :                                      MBasicBlock** fallbackTarget)
    4414             : {
    4415             :     // Getting here implies the following:
    4416             :     // 1. The call function is an MGetPropertyCache, or an MGetPropertyCache
    4417             :     //    followed by an MTypeBarrier.
    4418           0 :     MOZ_ASSERT(callInfo.fun()->isGetPropertyCache() || callInfo.fun()->isTypeBarrier());
    4419             : 
    4420             :     // 2. The MGetPropertyCache has inlineable cases by guarding on the ObjectGroup.
    4421           0 :     MOZ_ASSERT(dispatch->numCases() > 0);
    4422             : 
    4423             :     // 3. The MGetPropertyCache (and, if applicable, MTypeBarrier) only
    4424             :     //    have at most a single use.
    4425           0 :     MOZ_ASSERT_IF(callInfo.fun()->isGetPropertyCache(), !cache->hasUses());
    4426           0 :     MOZ_ASSERT_IF(callInfo.fun()->isTypeBarrier(), cache->hasOneUse());
    4427             : 
    4428             :     // This means that no resume points yet capture the MGetPropertyCache,
    4429             :     // so everything from the MGetPropertyCache up until the call is movable.
    4430             :     // We now move the MGetPropertyCache and friends into a fallback path.
    4431           0 :     MOZ_ASSERT(cache->idempotent());
    4432             : 
    4433             :     // Create a new CallInfo to track modified state within the fallback path.
    4434           0 :     CallInfo fallbackInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue());
    4435           0 :     if (!fallbackInfo.init(callInfo))
    4436           0 :         return abort(AbortReason::Alloc);
    4437             : 
    4438             :     // Capture stack prior to the call operation. This captures the function.
    4439             :     MResumePoint* preCallResumePoint =
    4440           0 :         MResumePoint::New(alloc(), dispatchBlock, pc, MResumePoint::ResumeAt);
    4441           0 :     if (!preCallResumePoint)
    4442           0 :         return abort(AbortReason::Alloc);
    4443             : 
    4444           0 :     DebugOnly<size_t> preCallFuncIndex = preCallResumePoint->stackDepth() - callInfo.numFormals();
    4445           0 :     MOZ_ASSERT(preCallResumePoint->getOperand(preCallFuncIndex) == fallbackInfo.fun());
    4446             : 
    4447             :     // In the dispatch block, replace the function's slot entry with Undefined.
    4448           0 :     MConstant* undefined = MConstant::New(alloc(), UndefinedValue());
    4449           0 :     dispatchBlock->add(undefined);
    4450           0 :     dispatchBlock->rewriteAtDepth(-int(callInfo.numFormals()), undefined);
    4451             : 
    4452             :     // Construct a block that does nothing but remove formals from the stack.
    4453             :     // This is effectively changing the entry resume point of the later fallback block.
    4454             :     MBasicBlock* prepBlock;
    4455           0 :     MOZ_TRY_VAR(prepBlock, newBlock(dispatchBlock, pc));
    4456           0 :     graph().addBlock(prepBlock);
    4457           0 :     fallbackInfo.popFormals(prepBlock);
    4458             : 
    4459             :     // Construct a block into which the MGetPropertyCache can be moved.
    4460             :     // This is subtle: the pc and resume point are those of the MGetPropertyCache!
    4461           0 :     InlinePropertyTable* propTable = cache->propTable();
    4462           0 :     MResumePoint* priorResumePoint = propTable->takePriorResumePoint();
    4463           0 :     MOZ_ASSERT(propTable->pc() != nullptr);
    4464           0 :     MOZ_ASSERT(priorResumePoint != nullptr);
    4465             :     MBasicBlock* getPropBlock;
    4466           0 :     MOZ_TRY_VAR(getPropBlock, newBlock(prepBlock, propTable->pc(), priorResumePoint));
    4467           0 :     graph().addBlock(getPropBlock);
    4468             : 
    4469           0 :     prepBlock->end(MGoto::New(alloc(), getPropBlock));
    4470             : 
    4471             :     // Since the getPropBlock inherited the stack from right before the MGetPropertyCache,
    4472             :     // the target of the MGetPropertyCache is still on the stack.
    4473           0 :     DebugOnly<MDefinition*> checkObject = getPropBlock->pop();
    4474           0 :     MOZ_ASSERT(checkObject == cache->value());
    4475             : 
    4476             :     // Move the MGetPropertyCache and friends into the getPropBlock.
    4477           0 :     if (fallbackInfo.fun()->isGetPropertyCache()) {
    4478           0 :         MOZ_ASSERT(fallbackInfo.fun()->toGetPropertyCache() == cache);
    4479           0 :         getPropBlock->addFromElsewhere(cache);
    4480           0 :         getPropBlock->push(cache);
    4481             :     } else {
    4482           0 :         MTypeBarrier* barrier = callInfo.fun()->toTypeBarrier();
    4483           0 :         MOZ_ASSERT(barrier->type() == MIRType::Object);
    4484           0 :         MOZ_ASSERT(barrier->input()->isGetPropertyCache());
    4485           0 :         MOZ_ASSERT(barrier->input()->toGetPropertyCache() == cache);
    4486             : 
    4487           0 :         getPropBlock->addFromElsewhere(cache);
    4488           0 :         getPropBlock->addFromElsewhere(barrier);
    4489           0 :         getPropBlock->push(barrier);
    4490             :     }
    4491             : 
    4492             :     // Construct an end block with the correct resume point.
    4493             :     MBasicBlock* preCallBlock;
    4494           0 :     MOZ_TRY_VAR(preCallBlock, newBlock(getPropBlock, pc, preCallResumePoint));
    4495           0 :     graph().addBlock(preCallBlock);
    4496           0 :     getPropBlock->end(MGoto::New(alloc(), preCallBlock));
    4497             : 
    4498             :     // Now inline the MCallGeneric, using preCallBlock as the dispatch point.
    4499           0 :     MOZ_TRY(inlineGenericFallback(nullptr, fallbackInfo, preCallBlock));
    4500             : 
    4501             :     // inlineGenericFallback() set the return block as |current|.
    4502           0 :     preCallBlock->end(MGoto::New(alloc(), current));
    4503           0 :     *fallbackTarget = prepBlock;
    4504           0 :     return Ok();
    4505             : }
    4506             : 
    4507             : AbortReasonOr<Ok>
    4508           4 : IonBuilder::inlineCalls(CallInfo& callInfo, const InliningTargets& targets, BoolVector& choiceSet,
    4509             :                         MGetPropertyCache* maybeCache)
    4510             : {
    4511             :     // Only handle polymorphic inlining.
    4512           4 :     MOZ_ASSERT(IsIonInlinablePC(pc));
    4513           4 :     MOZ_ASSERT(choiceSet.length() == targets.length());
    4514           4 :     MOZ_ASSERT_IF(!maybeCache, targets.length() >= 2);
    4515           4 :     MOZ_ASSERT_IF(maybeCache, targets.length() >= 1);
    4516           4 :     MOZ_ASSERT_IF(maybeCache, maybeCache->value()->type() == MIRType::Object);
    4517             : 
    4518           4 :     MBasicBlock* dispatchBlock = current;
    4519           4 :     callInfo.setImplicitlyUsedUnchecked();
    4520           4 :     callInfo.pushFormals(dispatchBlock);
    4521             : 
    4522             :     // Patch any InlinePropertyTable to only contain functions that are
    4523             :     // inlineable. The InlinePropertyTable will also be patched at the end to
    4524             :     // exclude native functions that vetoed inlining.
    4525           4 :     if (maybeCache) {
    4526           0 :         InlinePropertyTable* propTable = maybeCache->propTable();
    4527           0 :         propTable->trimToTargets(targets);
    4528           0 :         if (propTable->numEntries() == 0)
    4529           0 :             maybeCache = nullptr;
    4530             :     }
    4531             : 
    4532             :     // Generate a dispatch based on guard kind.
    4533             :     MDispatchInstruction* dispatch;
    4534           4 :     if (maybeCache) {
    4535           0 :         dispatch = MObjectGroupDispatch::New(alloc(), maybeCache->value(), maybeCache->propTable());
    4536           0 :         callInfo.fun()->setImplicitlyUsedUnchecked();
    4537             :     } else {
    4538           4 :         dispatch = MFunctionDispatch::New(alloc(), callInfo.fun());
    4539             :     }
    4540             : 
    4541             :     // Generate a return block to host the rval-collecting MPhi.
    4542           4 :     jsbytecode* postCall = GetNextPc(pc);
    4543             :     MBasicBlock* returnBlock;
    4544           4 :     MOZ_TRY_VAR(returnBlock, newBlock(nullptr, postCall));
    4545           4 :     graph().addBlock(returnBlock);
    4546           4 :     returnBlock->setCallerResumePoint(callerResumePoint_);
    4547             : 
    4548             :     // Set up stack, used to manually create a post-call resume point.
    4549           4 :     returnBlock->inheritSlots(dispatchBlock);
    4550           4 :     callInfo.popFormals(returnBlock);
    4551             : 
    4552           4 :     MPhi* retPhi = MPhi::New(alloc());
    4553           4 :     returnBlock->addPhi(retPhi);
    4554           4 :     returnBlock->push(retPhi);
    4555             : 
    4556             :     // Create a resume point from current stack state.
    4557           4 :     if (!returnBlock->initEntrySlots(alloc()))
    4558           0 :         return abort(AbortReason::Alloc);
    4559             : 
    4560             :     // Reserve the capacity for the phi.
    4561             :     // Note: this is an upperbound. Unreachable targets and uninlineable natives are also counted.
    4562           4 :     uint32_t count = 1; // Possible fallback block.
    4563          12 :     for (uint32_t i = 0; i < targets.length(); i++) {
    4564           8 :         if (choiceSet[i])
    4565           4 :             count++;
    4566             :     }
    4567           4 :     if (!retPhi->reserveLength(count))
    4568           0 :         return abort(AbortReason::Alloc);
    4569             : 
    4570             :     // Inline each of the inlineable targets.
    4571          12 :     for (uint32_t i = 0; i < targets.length(); i++) {
    4572             :         // Target must be inlineable.
    4573           8 :         if (!choiceSet[i])
    4574           8 :             continue;
    4575             : 
    4576             :         // Even though we made one round of inline decisions already, we may
    4577             :         // be amending them below.
    4578           4 :         amendOptimizationAttempt(i);
    4579             : 
    4580             :         // Target must be reachable by the MDispatchInstruction.
    4581           4 :         JSFunction* target = &targets[i].target->as<JSFunction>();
    4582           4 :         if (maybeCache && !maybeCache->propTable()->hasFunction(target)) {
    4583           0 :             choiceSet[i] = false;
    4584           0 :             trackOptimizationOutcome(TrackedOutcome::CantInlineNotInDispatch);
    4585           0 :             continue;
    4586             :         }
    4587             : 
    4588             :         MBasicBlock* inlineBlock;
    4589           4 :         MOZ_TRY_VAR(inlineBlock, newBlock(dispatchBlock, pc));
    4590           4 :         graph().addBlock(inlineBlock);
    4591             : 
    4592             :         // Create a function MConstant to use in the entry ResumePoint. If we
    4593             :         // can't use a constant, add a no-op MPolyInlineGuard, to prevent
    4594             :         // hoisting env chain gets above the dispatch instruction.
    4595             :         MInstruction* funcDef;
    4596           4 :         if (target->isSingleton())
    4597           0 :             funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints());
    4598             :         else
    4599           4 :             funcDef = MPolyInlineGuard::New(alloc(), callInfo.fun());
    4600             : 
    4601           4 :         funcDef->setImplicitlyUsedUnchecked();
    4602           4 :         dispatchBlock->add(funcDef);
    4603             : 
    4604             :         // Use the inlined callee in the inline resume point and on stack.
    4605           4 :         int funIndex = inlineBlock->entryResumePoint()->stackDepth() - callInfo.numFormals();
    4606           4 :         inlineBlock->entryResumePoint()->replaceOperand(funIndex, funcDef);
    4607           4 :         inlineBlock->rewriteSlot(funIndex, funcDef);
    4608             : 
    4609             :         // Create a new CallInfo to track modified state within the inline block.
    4610           8 :         CallInfo inlineInfo(alloc(), callInfo.constructing(), callInfo.ignoresReturnValue());
    4611           4 :         if (!inlineInfo.init(callInfo))
    4612           0 :             return abort(AbortReason::Alloc);
    4613           4 :         inlineInfo.popFormals(inlineBlock);
    4614           4 :         inlineInfo.setFun(funcDef);
    4615             : 
    4616           4 :         if (maybeCache) {
    4617             :             // Assign the 'this' value a TypeSet specialized to the groups that
    4618             :             // can generate this inlining target.
    4619           0 :             MOZ_ASSERT(callInfo.thisArg() == maybeCache->value());
    4620           0 :             TemporaryTypeSet* thisTypes = maybeCache->propTable()->buildTypeSetForFunction(target);
    4621           0 :             if (!thisTypes)
    4622           0 :                 return abort(AbortReason::Alloc);
    4623             : 
    4624           0 :             MFilterTypeSet* filter = MFilterTypeSet::New(alloc(), inlineInfo.thisArg(), thisTypes);
    4625           0 :             inlineBlock->add(filter);
    4626           0 :             inlineInfo.setThis(filter);
    4627             :         }
    4628             : 
    4629             :         // Inline the call into the inlineBlock.
    4630           4 :         MOZ_TRY(setCurrentAndSpecializePhis(inlineBlock));
    4631             :         InliningStatus status;
    4632           4 :         MOZ_TRY_VAR(status, inlineSingleCall(inlineInfo, target));
    4633             : 
    4634             :         // Natives may veto inlining.
    4635           4 :         if (status == InliningStatus_NotInlined) {
    4636           0 :             MOZ_ASSERT(current == inlineBlock);
    4637           0 :             graph().removeBlock(inlineBlock);
    4638           0 :             choiceSet[i] = false;
    4639           0 :             continue;
    4640             :         }
    4641             : 
    4642             :         // inlineSingleCall() changed |current| to the inline return block.
    4643           4 :         MBasicBlock* inlineReturnBlock = current;
    4644           4 :         setCurrent(dispatchBlock);
    4645             : 
    4646             :         // Connect the inline path to the returnBlock.
    4647           4 :         if (!dispatch->addCase(target, targets[i].group, inlineBlock))
    4648           0 :             return abort(AbortReason::Alloc);
    4649             : 
    4650           4 :         MDefinition* retVal = inlineReturnBlock->peek(-1);
    4651           4 :         retPhi->addInput(retVal);
    4652           4 :         inlineReturnBlock->end(MGoto::New(alloc(), returnBlock));
    4653           4 :         if (!returnBlock->addPredecessorWithoutPhis(inlineReturnBlock))
    4654           0 :             return abort(AbortReason::Alloc);
    4655             :     }
    4656             : 
    4657             :     // Patch the InlinePropertyTable to not dispatch to vetoed paths.
    4658             :     bool useFallback;
    4659           4 :     if (maybeCache) {
    4660           0 :         InlinePropertyTable* propTable = maybeCache->propTable();
    4661           0 :         propTable->trimTo(targets, choiceSet);
    4662             : 
    4663           0 :         if (propTable->numEntries() == 0 || !propTable->hasPriorResumePoint()) {
    4664             :             // Output a generic fallback path.
    4665           0 :             MOZ_ASSERT_IF(propTable->numEntries() == 0, dispatch->numCases() == 0);
    4666           0 :             maybeCache = nullptr;
    4667           0 :             useFallback = true;
    4668             :         } else {
    4669             :             // We need a fallback path if the ObjectGroup dispatch does not
    4670             :             // handle all incoming objects.
    4671           0 :             useFallback = false;
    4672           0 :             TemporaryTypeSet* objectTypes = maybeCache->value()->resultTypeSet();
    4673           0 :             for (uint32_t i = 0; i < objectTypes->getObjectCount(); i++) {
    4674           0 :                 TypeSet::ObjectKey* obj = objectTypes->getObject(i);
    4675           0 :                 if (!obj)
    4676           0 :                     continue;
    4677             : 
    4678           0 :                 if (!obj->isGroup()) {
    4679           0 :                     useFallback = true;
    4680           0 :                     break;
    4681             :                 }
    4682             : 
    4683           0 :                 if (!propTable->hasObjectGroup(obj->group())) {
    4684           0 :                     useFallback = true;
    4685           0 :                     break;
    4686             :                 }
    4687             :             }
    4688             : 
    4689           0 :             if (!useFallback) {
    4690             :                 // The object group dispatch handles all possible incoming
    4691             :                 // objects, so the cache and barrier will not be reached and
    4692             :                 // can be eliminated.
    4693           0 :                 if (callInfo.fun()->isGetPropertyCache()) {
    4694           0 :                     MOZ_ASSERT(callInfo.fun() == maybeCache);
    4695             :                 } else {
    4696           0 :                     MTypeBarrier* barrier = callInfo.fun()->toTypeBarrier();
    4697           0 :                     MOZ_ASSERT(!barrier->hasUses());
    4698           0 :                     MOZ_ASSERT(barrier->type() == MIRType::Object);
    4699           0 :                     MOZ_ASSERT(barrier->input()->isGetPropertyCache());
    4700           0 :                     MOZ_ASSERT(barrier->input()->toGetPropertyCache() == maybeCache);
    4701           0 :                     barrier->block()->discard(barrier);
    4702             :                 }
    4703             : 
    4704           0 :                 MOZ_ASSERT(!maybeCache->hasUses());
    4705           0 :                 maybeCache->block()->discard(maybeCache);
    4706             :             }
    4707             :         }
    4708             :     } else {
    4709           4 :         useFallback = dispatch->numCases() < targets.length();
    4710             :     }
    4711             : 
    4712             :     // If necessary, generate a fallback path.
    4713           4 :     if (useFallback) {
    4714             :         // Generate fallback blocks, and set |current| to the fallback return block.
    4715           4 :         if (maybeCache) {
    4716             :             MBasicBlock* fallbackTarget;
    4717           0 :             MOZ_TRY(inlineObjectGroupFallback(callInfo, dispatchBlock,
    4718             :                                               dispatch->toObjectGroupDispatch(),
    4719             :                                               maybeCache, &fallbackTarget));
    4720           0 :             dispatch->addFallback(fallbackTarget);
    4721             :         } else {
    4722           4 :             JSFunction* remaining = nullptr;
    4723             : 
    4724             :             // If there is only 1 remaining case, we can annotate the fallback call
    4725             :             // with the target information.
    4726           4 :             if (dispatch->numCases() + 1 == targets.length()) {
    4727           8 :                 for (uint32_t i = 0; i < targets.length(); i++) {
    4728           8 :                     if (choiceSet[i])
    4729           4 :                         continue;
    4730             : 
    4731           4 :                     MOZ_ASSERT(!remaining);
    4732           4 :                     JSObject* target = targets[i].target;
    4733           4 :                     if (target->is<JSFunction>() && target->isSingleton())
    4734           0 :                         remaining = &target->as<JSFunction>();
    4735           4 :                     break;
    4736             :                 }
    4737             :             }
    4738             : 
    4739           4 :             MOZ_TRY(inlineGenericFallback(remaining, callInfo, dispatchBlock));
    4740           4 :             dispatch->addFallback(current);
    4741             :         }
    4742             : 
    4743           4 :         MBasicBlock* fallbackReturnBlock = current;
    4744             : 
    4745             :         // Connect fallback case to return infrastructure.
    4746           4 :         MDefinition* retVal = fallbackReturnBlock->peek(-1);
    4747           4 :         retPhi->addInput(retVal);
    4748           4 :         fallbackReturnBlock->end(MGoto::New(alloc(), returnBlock));
    4749           4 :         if (!returnBlock->addPredecessorWithoutPhis(fallbackReturnBlock))
    4750           0 :             return abort(AbortReason::Alloc);
    4751             :     }
    4752             : 
    4753             :     // Finally add the dispatch instruction.
    4754             :     // This must be done at the end so that add() may be called above.
    4755           4 :     dispatchBlock->end(dispatch);
    4756             : 
    4757             :     // Check the depth change: +1 for retval
    4758           4 :     MOZ_ASSERT(returnBlock->stackDepth() == dispatchBlock->stackDepth() - callInfo.numFormals() + 1);
    4759             : 
    4760           4 :     graph().moveBlockToEnd(returnBlock);
    4761           4 :     return setCurrentAndSpecializePhis(returnBlock);
    4762             : }
    4763             : 
    4764             : MInstruction*
    4765           0 : IonBuilder::createNamedLambdaObject(MDefinition* callee, MDefinition* env)
    4766             : {
    4767             :     // Get a template CallObject that we'll use to generate inline object
    4768             :     // creation.
    4769           0 :     LexicalEnvironmentObject* templateObj = inspector->templateNamedLambdaObject();
    4770             : 
    4771             :     // One field is added to the function to handle its name.  This cannot be a
    4772             :     // dynamic slot because there is still plenty of room on the NamedLambda object.
    4773           0 :     MOZ_ASSERT(!templateObj->hasDynamicSlots());
    4774             : 
    4775             :     // Allocate the actual object. It is important that no intervening
    4776             :     // instructions could potentially bailout, thus leaking the dynamic slots
    4777             :     // pointer.
    4778           0 :     MInstruction* declEnvObj = MNewNamedLambdaObject::New(alloc(), templateObj);
    4779           0 :     current->add(declEnvObj);
    4780             : 
    4781             :     // Initialize the object's reserved slots. No post barrier is needed here:
    4782             :     // the object will be allocated in the nursery if possible, and if the
    4783             :     // tenured heap is used instead, a minor collection will have been performed
    4784             :     // that moved env/callee to the tenured heap.
    4785           0 :     current->add(MStoreFixedSlot::New(alloc(), declEnvObj,
    4786           0 :                                       NamedLambdaObject::enclosingEnvironmentSlot(), env));
    4787           0 :     current->add(MStoreFixedSlot::New(alloc(), declEnvObj,
    4788           0 :                                       NamedLambdaObject::lambdaSlot(), callee));
    4789             : 
    4790           0 :     return declEnvObj;
    4791             : }
    4792             : 
    4793             : AbortReasonOr<MInstruction*>
    4794           4 : IonBuilder::createCallObject(MDefinition* callee, MDefinition* env)
    4795             : {
    4796             :     // Get a template CallObject that we'll use to generate inline object
    4797             :     // creation.
    4798           4 :     CallObject* templateObj = inspector->templateCallObject();
    4799             : 
    4800             :     // Allocate the object. Run-once scripts need a singleton type, so always do
    4801             :     // a VM call in such cases.
    4802             :     MNewCallObjectBase* callObj;
    4803           4 :     if (script()->treatAsRunOnce() || templateObj->isSingleton())
    4804           0 :         callObj = MNewSingletonCallObject::New(alloc(), templateObj);
    4805             :     else
    4806           4 :         callObj = MNewCallObject::New(alloc(), templateObj);
    4807           4 :     current->add(callObj);
    4808             : 
    4809             :     // Initialize the object's reserved slots. No post barrier is needed here,
    4810             :     // for the same reason as in createNamedLambdaObject.
    4811           4 :     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::enclosingEnvironmentSlot(), env));
    4812           4 :     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::calleeSlot(), callee));
    4813             : 
    4814             :     //if (!script()->functionHasParameterExprs()) {
    4815             : 
    4816             :     // Copy closed-over argument slots if there aren't parameter expressions.
    4817           4 :     MSlots* slots = nullptr;
    4818          16 :     for (PositionalFormalParameterIter fi(script()); fi; fi++) {
    4819          12 :         if (!fi.closedOver())
    4820           9 :             continue;
    4821             : 
    4822           3 :         if (!alloc().ensureBallast())
    4823           0 :             return abort(AbortReason::Alloc);
    4824             : 
    4825           3 :         unsigned slot = fi.location().slot();
    4826           3 :         unsigned formal = fi.argumentSlot();
    4827           3 :         unsigned numFixedSlots = templateObj->numFixedSlots();
    4828             :         MDefinition* param;
    4829           3 :         if (script()->functionHasParameterExprs())
    4830           0 :             param = constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
    4831             :         else
    4832           3 :             param = current->getSlot(info().argSlotUnchecked(formal));
    4833           3 :         if (slot >= numFixedSlots) {
    4834           0 :             if (!slots) {
    4835           0 :                 slots = MSlots::New(alloc(), callObj);
    4836           0 :                 current->add(slots);
    4837             :             }
    4838           0 :             current->add(MStoreSlot::New(alloc(), slots, slot - numFixedSlots, param));
    4839             :         } else {
    4840           3 :             current->add(MStoreFixedSlot::New(alloc(), callObj, slot, param));
    4841             :         }
    4842             :     }
    4843             : 
    4844           4 :     return AbortReasonOr<MInstruction*>(callObj);
    4845             : }
    4846             : 
    4847             : MDefinition*
    4848           0 : IonBuilder::createThisScripted(MDefinition* callee, MDefinition* newTarget)
    4849             : {
    4850             :     // Get callee.prototype.
    4851             :     //
    4852             :     // This instruction MUST be idempotent: since it does not correspond to an
    4853             :     // explicit operation in the bytecode, we cannot use resumeAfter().
    4854             :     // Getters may not override |prototype| fetching, so this operation is indeed idempotent.
    4855             :     // - First try an idempotent property cache.
    4856             :     // - Upon failing idempotent property cache, we can't use a non-idempotent cache,
    4857             :     //   therefore we fallback to CallGetProperty
    4858             :     //
    4859             :     // Note: both CallGetProperty and GetPropertyCache can trigger a GC,
    4860             :     //       and thus invalidation.
    4861             :     MInstruction* getProto;
    4862           0 :     if (!invalidatedIdempotentCache()) {
    4863           0 :         MConstant* id = constant(StringValue(names().prototype));
    4864           0 :         MGetPropertyCache* getPropCache = MGetPropertyCache::New(alloc(), newTarget, id,
    4865           0 :                                                                  /* monitored = */ false);
    4866           0 :         getPropCache->setIdempotent();
    4867           0 :         getProto = getPropCache;
    4868             :     } else {
    4869           0 :         MCallGetProperty* callGetProp = MCallGetProperty::New(alloc(), newTarget, names().prototype);
    4870           0 :         callGetProp->setIdempotent();
    4871           0 :         getProto = callGetProp;
    4872             :     }
    4873           0 :     current->add(getProto);
    4874             : 
    4875             :     // Create this from prototype
    4876           0 :     MCreateThisWithProto* createThis = MCreateThisWithProto::New(alloc(), callee, newTarget, getProto);
    4877           0 :     current->add(createThis);
    4878             : 
    4879           0 :     return createThis;
    4880             : }
    4881             : 
    4882             : JSObject*
    4883           0 : IonBuilder::getSingletonPrototype(JSFunction* target)
    4884             : {
    4885           0 :     TypeSet::ObjectKey* targetKey = TypeSet::ObjectKey::get(target);
    4886           0 :     if (targetKey->unknownProperties())
    4887           0 :         return nullptr;
    4888             : 
    4889           0 :     jsid protoid = NameToId(names().prototype);
    4890           0 :     HeapTypeSetKey protoProperty = targetKey->property(protoid);
    4891             : 
    4892           0 :     return protoProperty.singleton(constraints());
    4893             : }
    4894             : 
    4895             : MDefinition*
    4896           0 : IonBuilder::createThisScriptedSingleton(JSFunction* target, MDefinition* callee)
    4897             : {
    4898           0 :     if (!target->hasScript())
    4899           0 :         return nullptr;
    4900             : 
    4901             :     // Get the singleton prototype (if exists)
    4902           0 :     JSObject* proto = getSingletonPrototype(target);
    4903           0 :     if (!proto)
    4904           0 :         return nullptr;
    4905             : 
    4906           0 :     JSObject* templateObject = inspector->getTemplateObject(pc);
    4907           0 :     if (!templateObject)
    4908           0 :         return nullptr;
    4909           0 :     if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
    4910           0 :         return nullptr;
    4911           0 :     if (templateObject->staticPrototype() != proto)
    4912           0 :         return nullptr;
    4913             : 
    4914           0 :     TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group());
    4915           0 :     if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED))
    4916           0 :         return nullptr;
    4917             : 
    4918           0 :     StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript());
    4919           0 :     if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject)))
    4920           0 :         return nullptr;
    4921             : 
    4922             :     // Generate an inline path to create a new |this| object with
    4923             :     // the given singleton prototype.
    4924           0 :     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    4925             :     MCreateThisWithTemplate* createThis =
    4926           0 :         MCreateThisWithTemplate::New(alloc(), constraints(), templateConst,
    4927           0 :                                      templateObject->group()->initialHeap(constraints()));
    4928           0 :     current->add(templateConst);
    4929           0 :     current->add(createThis);
    4930             : 
    4931           0 :     return createThis;
    4932             : }
    4933             : 
    4934             : MDefinition*
    4935         139 : IonBuilder::createThisScriptedBaseline(MDefinition* callee)
    4936             : {
    4937             :     // Try to inline |this| creation based on Baseline feedback.
    4938             : 
    4939         139 :     JSFunction* target = inspector->getSingleCallee(pc);
    4940         139 :     if (!target || !target->hasScript())
    4941         139 :         return nullptr;
    4942             : 
    4943           0 :     if (target->isBoundFunction() || target->isDerivedClassConstructor())
    4944           0 :         return nullptr;
    4945             : 
    4946           0 :     JSObject* templateObject = inspector->getTemplateObject(pc);
    4947           0 :     if (!templateObject)
    4948           0 :         return nullptr;
    4949           0 :     if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
    4950           0 :         return nullptr;
    4951             : 
    4952           0 :     Shape* shape = target->lookupPure(compartment->runtime()->names().prototype);
    4953           0 :     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
    4954           0 :         return nullptr;
    4955             : 
    4956           0 :     Value protov = target->getSlot(shape->slot());
    4957           0 :     if (!protov.isObject())
    4958           0 :         return nullptr;
    4959             : 
    4960           0 :     JSObject* proto = checkNurseryObject(&protov.toObject());
    4961           0 :     if (proto != templateObject->staticPrototype())
    4962           0 :         return nullptr;
    4963             : 
    4964           0 :     TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group());
    4965           0 :     if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED))
    4966           0 :         return nullptr;
    4967             : 
    4968           0 :     StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript());
    4969           0 :     if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject)))
    4970           0 :         return nullptr;
    4971             : 
    4972             :     // Shape guard.
    4973           0 :     callee = addShapeGuard(callee, target->lastProperty(), Bailout_ShapeGuard);
    4974             : 
    4975             :     // Guard callee.prototype == proto.
    4976           0 :     MOZ_ASSERT(shape->numFixedSlots() == 0, "Must be a dynamic slot");
    4977           0 :     MSlots* slots = MSlots::New(alloc(), callee);
    4978           0 :     current->add(slots);
    4979           0 :     MLoadSlot* prototype = MLoadSlot::New(alloc(), slots, shape->slot());
    4980           0 :     current->add(prototype);
    4981           0 :     MDefinition* protoConst = constant(ObjectValue(*proto));
    4982           0 :     MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst,
    4983           0 :                                                             /* bailOnEquality = */ false);
    4984           0 :     current->add(guard);
    4985             : 
    4986             :     // Generate an inline path to create a new |this| object with
    4987             :     // the given prototype.
    4988           0 :     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    4989             :     MCreateThisWithTemplate* createThis =
    4990           0 :         MCreateThisWithTemplate::New(alloc(), constraints(), templateConst,
    4991           0 :                                      templateObject->group()->initialHeap(constraints()));
    4992           0 :     current->add(templateConst);
    4993           0 :     current->add(createThis);
    4994             : 
    4995           0 :     return createThis;
    4996             : }
    4997             : 
    4998             : MDefinition*
    4999         140 : IonBuilder::createThis(JSFunction* target, MDefinition* callee, MDefinition* newTarget)
    5000             : {
    5001             :     // Create |this| for unknown target.
    5002         140 :     if (!target) {
    5003         139 :         if (MDefinition* createThis = createThisScriptedBaseline(callee))
    5004           0 :             return createThis;
    5005             : 
    5006         139 :         MCreateThis* createThis = MCreateThis::New(alloc(), callee, newTarget);
    5007         139 :         current->add(createThis);
    5008         139 :         return createThis;
    5009             :     }
    5010             : 
    5011             :     // Native constructors build the new Object themselves.
    5012           1 :     if (target->isNative()) {
    5013           1 :         if (!target->isConstructor())
    5014           0 :             return nullptr;
    5015             : 
    5016           1 :         MConstant* magic = MConstant::New(alloc(), MagicValue(JS_IS_CONSTRUCTING));
    5017           1 :         current->add(magic);
    5018           1 :         return magic;
    5019             :     }
    5020             : 
    5021           0 :     if (target->isBoundFunction())
    5022           0 :         return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
    5023             : 
    5024           0 :     if (target->isDerivedClassConstructor()) {
    5025           0 :         MOZ_ASSERT(target->isClassConstructor());
    5026           0 :         return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
    5027             :     }
    5028             : 
    5029             :     // Try baking in the prototype.
    5030           0 :     if (MDefinition* createThis = createThisScriptedSingleton(target, callee))
    5031           0 :         return createThis;
    5032             : 
    5033           0 :     if (MDefinition* createThis = createThisScriptedBaseline(callee))
    5034           0 :         return createThis;
    5035             : 
    5036           0 :     return createThisScripted(callee, newTarget);
    5037             : }
    5038             : 
    5039             : AbortReasonOr<Ok>
    5040           2 : IonBuilder::jsop_funcall(uint32_t argc)
    5041             : {
    5042             :     // Stack for JSOP_FUNCALL:
    5043             :     // 1:      arg0
    5044             :     // ...
    5045             :     // argc:   argN
    5046             :     // argc+1: JSFunction*, the 'f' in |f.call()|, in |this| position.
    5047             :     // argc+2: The native 'call' function.
    5048             : 
    5049           2 :     int calleeDepth = -((int)argc + 2);
    5050           2 :     int funcDepth = -((int)argc + 1);
    5051             : 
    5052             :     // If |Function.prototype.call| may be overridden, don't optimize callsite.
    5053           2 :     TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
    5054           2 :     JSFunction* native = getSingleCallTarget(calleeTypes);
    5055           2 :     if (!native || !native->isNative() || native->native() != &fun_call) {
    5056             :         CallInfo callInfo(alloc(), /* constructing = */ false,
    5057           4 :                           /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5058           2 :         if (!callInfo.init(current, argc))
    5059           0 :             return abort(AbortReason::Alloc);
    5060           2 :         return makeCall(native, callInfo);
    5061             :     }
    5062           0 :     current->peek(calleeDepth)->setImplicitlyUsedUnchecked();
    5063             : 
    5064             :     // Extract call target.
    5065           0 :     TemporaryTypeSet* funTypes = current->peek(funcDepth)->resultTypeSet();
    5066           0 :     JSFunction* target = getSingleCallTarget(funTypes);
    5067             : 
    5068             :     // Shimmy the slots down to remove the native 'call' function.
    5069           0 :     current->shimmySlots(funcDepth - 1);
    5070             : 
    5071           0 :     bool zeroArguments = (argc == 0);
    5072             : 
    5073             :     // If no |this| argument was provided, explicitly pass Undefined.
    5074             :     // Pushing is safe here, since one stack slot has been removed.
    5075           0 :     if (zeroArguments) {
    5076           0 :         pushConstant(UndefinedValue());
    5077             :     } else {
    5078             :         // |this| becomes implicit in the call.
    5079           0 :         argc -= 1;
    5080             :     }
    5081             : 
    5082             :     CallInfo callInfo(alloc(), /* constructing = */ false,
    5083           0 :                       /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5084           0 :     if (!callInfo.init(current, argc))
    5085           0 :         return abort(AbortReason::Alloc);
    5086             : 
    5087             :     // Try to inline the call.
    5088           0 :     if (!zeroArguments) {
    5089           0 :         InliningDecision decision = makeInliningDecision(target, callInfo);
    5090           0 :         switch (decision) {
    5091             :           case InliningDecision_Error:
    5092           0 :             return abort(AbortReason::Alloc);
    5093             :           case InliningDecision_DontInline:
    5094             :           case InliningDecision_WarmUpCountTooLow:
    5095           0 :             break;
    5096             :           case InliningDecision_Inline:
    5097           0 :             if (target->isInterpreted()) {
    5098             :                 InliningStatus status;
    5099           0 :                 MOZ_TRY_VAR(status, inlineScriptedCall(callInfo, target));
    5100           0 :                 if (status == InliningStatus_Inlined)
    5101           0 :                     return Ok();
    5102             :             }
    5103           0 :             break;
    5104             :         }
    5105             :     }
    5106             : 
    5107             :     // Call without inlining.
    5108           0 :     return makeCall(target, callInfo);
    5109             : }
    5110             : 
    5111             : AbortReasonOr<Ok>
    5112          44 : IonBuilder::jsop_funapply(uint32_t argc)
    5113             : {
    5114          44 :     int calleeDepth = -((int)argc + 2);
    5115             : 
    5116          44 :     TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
    5117          44 :     JSFunction* native = getSingleCallTarget(calleeTypes);
    5118          44 :     if (argc != 2 || info().analysisMode() == Analysis_ArgumentsUsage) {
    5119             :         CallInfo callInfo(alloc(), /* constructing = */ false,
    5120          88 :                           /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5121          44 :         if (!callInfo.init(current, argc))
    5122           0 :             return abort(AbortReason::Alloc);
    5123          44 :         return makeCall(native, callInfo);
    5124             :     }
    5125             : 
    5126             :     // Disable compilation if the second argument to |apply| cannot be guaranteed
    5127             :     // to be either definitely |arguments| or definitely not |arguments|.
    5128           0 :     MDefinition* argument = current->peek(-1);
    5129           0 :     if (script()->argumentsHasVarBinding() &&
    5130           0 :         argument->mightBeType(MIRType::MagicOptimizedArguments) &&
    5131           0 :         argument->type() != MIRType::MagicOptimizedArguments)
    5132             :     {
    5133           0 :         return abort(AbortReason::Disable, "fun.apply with MaybeArguments");
    5134             :     }
    5135             : 
    5136             :     // Fallback to regular call if arg 2 is not definitely |arguments|.
    5137           0 :     if (argument->type() != MIRType::MagicOptimizedArguments) {
    5138             :         // Optimize fun.apply(self, array) if the length is sane and there are no holes.
    5139           0 :         TemporaryTypeSet* objTypes = argument->resultTypeSet();
    5140           0 :         if (native && native->isNative() && native->native() == fun_apply &&
    5141           0 :             objTypes &&
    5142           0 :             objTypes->getKnownClass(constraints()) == &ArrayObject::class_ &&
    5143           0 :             !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW) &&
    5144           0 :             ElementAccessIsPacked(constraints(), argument))
    5145             :         {
    5146           0 :             return jsop_funapplyarray(argc);
    5147             :         }
    5148             : 
    5149             :         CallInfo callInfo(alloc(), /* constructing = */ false,
    5150           0 :                           /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5151           0 :         if (!callInfo.init(current, argc))
    5152           0 :             return abort(AbortReason::Alloc);
    5153           0 :         return makeCall(native, callInfo);
    5154             :     }
    5155             : 
    5156           0 :     if ((!native || !native->isNative() ||
    5157           0 :         native->native() != fun_apply) &&
    5158           0 :         info().analysisMode() != Analysis_DefiniteProperties)
    5159             :     {
    5160           0 :         return abort(AbortReason::Disable, "fun.apply speculation failed");
    5161             :     }
    5162             : 
    5163             :     // Use funapply that definitely uses |arguments|
    5164           0 :     return jsop_funapplyarguments(argc);
    5165             : }
    5166             : 
    5167             : AbortReasonOr<Ok>
    5168           0 : IonBuilder::jsop_spreadcall()
    5169             : {
    5170             :     // The arguments array is constructed by a JSOP_SPREADCALLARRAY and not
    5171             :     // leaked to user. The complications of spread call iterator behaviour are
    5172             :     // handled when the user objects are expanded and copied into this hidden
    5173             :     // array.
    5174             : 
    5175             : #ifdef DEBUG
    5176             :     // If we know class, ensure it is what we expected
    5177           0 :     MDefinition* argument = current->peek(-1);
    5178           0 :     if (TemporaryTypeSet* objTypes = argument->resultTypeSet())
    5179           0 :         if (const Class* clasp = objTypes->getKnownClass(constraints()))
    5180           0 :             MOZ_ASSERT(clasp == &ArrayObject::class_);
    5181             : #endif
    5182             : 
    5183           0 :     MDefinition* argArr = current->pop();
    5184           0 :     MDefinition* argThis = current->pop();
    5185           0 :     MDefinition* argFunc = current->pop();
    5186             : 
    5187             :     // Extract call target.
    5188           0 :     TemporaryTypeSet* funTypes = argFunc->resultTypeSet();
    5189           0 :     JSFunction* target = getSingleCallTarget(funTypes);
    5190           0 :     WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
    5191             : 
    5192             :     // Dense elements of argument array
    5193           0 :     MElements* elements = MElements::New(alloc(), argArr);
    5194           0 :     current->add(elements);
    5195             : 
    5196           0 :     MApplyArray* apply = MApplyArray::New(alloc(), wrappedTarget, argFunc, elements, argThis);
    5197           0 :     current->add(apply);
    5198           0 :     current->push(apply);
    5199           0 :     MOZ_TRY(resumeAfter(apply));
    5200             : 
    5201             :     // TypeBarrier the call result
    5202           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    5203           0 :     return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
    5204             : }
    5205             : 
    5206             : AbortReasonOr<Ok>
    5207           0 : IonBuilder::jsop_funapplyarray(uint32_t argc)
    5208             : {
    5209           0 :     MOZ_ASSERT(argc == 2);
    5210             : 
    5211           0 :     int funcDepth = -((int)argc + 1);
    5212             : 
    5213             :     // Extract call target.
    5214           0 :     TemporaryTypeSet* funTypes = current->peek(funcDepth)->resultTypeSet();
    5215           0 :     JSFunction* target = getSingleCallTarget(funTypes);
    5216             : 
    5217             :     // Pop the array agument
    5218           0 :     MDefinition* argObj = current->pop();
    5219             : 
    5220           0 :     MElements* elements = MElements::New(alloc(), argObj);
    5221           0 :     current->add(elements);
    5222             : 
    5223             :     // Pop the |this| argument.
    5224           0 :     MDefinition* argThis = current->pop();
    5225             : 
    5226             :     // Unwrap the (JSFunction *) parameter.
    5227           0 :     MDefinition* argFunc = current->pop();
    5228             : 
    5229             :     // Pop apply function.
    5230           0 :     MDefinition* nativeFunc = current->pop();
    5231           0 :     nativeFunc->setImplicitlyUsedUnchecked();
    5232             : 
    5233           0 :     WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
    5234           0 :     MApplyArray* apply = MApplyArray::New(alloc(), wrappedTarget, argFunc, elements, argThis);
    5235           0 :     current->add(apply);
    5236           0 :     current->push(apply);
    5237           0 :     MOZ_TRY(resumeAfter(apply));
    5238             : 
    5239           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    5240           0 :     return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
    5241             : }
    5242             : 
    5243             : AbortReasonOr<Ok>
    5244           0 : IonBuilder::jsop_funapplyarguments(uint32_t argc)
    5245             : {
    5246             :     // Stack for JSOP_FUNAPPLY:
    5247             :     // 1:      Vp
    5248             :     // 2:      This
    5249             :     // argc+1: JSFunction*, the 'f' in |f.call()|, in |this| position.
    5250             :     // argc+2: The native 'apply' function.
    5251             : 
    5252           0 :     int funcDepth = -((int)argc + 1);
    5253             : 
    5254             :     // Extract call target.
    5255           0 :     TemporaryTypeSet* funTypes = current->peek(funcDepth)->resultTypeSet();
    5256           0 :     JSFunction* target = getSingleCallTarget(funTypes);
    5257             : 
    5258             :     // When this script isn't inlined, use MApplyArgs,
    5259             :     // to copy the arguments from the stack and call the function
    5260           0 :     if (inliningDepth_ == 0 && info().analysisMode() != Analysis_DefiniteProperties) {
    5261             :         // The array argument corresponds to the arguments object. As the JIT
    5262             :         // is implicitly reading the arguments object in the next instruction,
    5263             :         // we need to prevent the deletion of the arguments object from resume
    5264             :         // points, so that Baseline will behave correctly after a bailout.
    5265           0 :         MDefinition* vp = current->pop();
    5266           0 :         vp->setImplicitlyUsedUnchecked();
    5267             : 
    5268           0 :         MDefinition* argThis = current->pop();
    5269             : 
    5270             :         // Unwrap the (JSFunction*) parameter.
    5271           0 :         MDefinition* argFunc = current->pop();
    5272             : 
    5273             :         // Pop apply function.
    5274           0 :         MDefinition* nativeFunc = current->pop();
    5275           0 :         nativeFunc->setImplicitlyUsedUnchecked();
    5276             : 
    5277           0 :         MArgumentsLength* numArgs = MArgumentsLength::New(alloc());
    5278           0 :         current->add(numArgs);
    5279             : 
    5280           0 :         WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
    5281           0 :         MApplyArgs* apply = MApplyArgs::New(alloc(), wrappedTarget, argFunc, numArgs, argThis);
    5282           0 :         current->add(apply);
    5283           0 :         current->push(apply);
    5284           0 :         MOZ_TRY(resumeAfter(apply));
    5285             : 
    5286           0 :         TemporaryTypeSet* types = bytecodeTypes(pc);
    5287           0 :         return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
    5288             :     }
    5289             : 
    5290             :     // When inlining we have the arguments the function gets called with
    5291             :     // and can optimize even more, by just calling the functions with the args.
    5292             :     // We also try this path when doing the definite properties analysis, as we
    5293             :     // can inline the apply() target and don't care about the actual arguments
    5294             :     // that were passed in.
    5295             : 
    5296             :     CallInfo callInfo(alloc(), /* constructing = */ false,
    5297           0 :                       /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5298             : 
    5299             :     // Vp
    5300           0 :     MDefinition* vp = current->pop();
    5301           0 :     vp->setImplicitlyUsedUnchecked();
    5302             : 
    5303             :     // Arguments
    5304           0 :     if (inliningDepth_) {
    5305           0 :         if (!callInfo.setArgs(inlineCallInfo_->argv()))
    5306           0 :             return abort(AbortReason::Alloc);
    5307             :     }
    5308             : 
    5309             :     // This
    5310           0 :     MDefinition* argThis = current->pop();
    5311           0 :     callInfo.setThis(argThis);
    5312             : 
    5313             :     // Pop function parameter.
    5314           0 :     MDefinition* argFunc = current->pop();
    5315           0 :     callInfo.setFun(argFunc);
    5316             : 
    5317             :     // Pop apply function.
    5318           0 :     MDefinition* nativeFunc = current->pop();
    5319           0 :     nativeFunc->setImplicitlyUsedUnchecked();
    5320             : 
    5321             :     // Try to inline the call.
    5322           0 :     InliningDecision decision = makeInliningDecision(target, callInfo);
    5323           0 :     switch (decision) {
    5324             :       case InliningDecision_Error:
    5325           0 :         return abort(AbortReason::Alloc);
    5326             :       case InliningDecision_DontInline:
    5327             :       case InliningDecision_WarmUpCountTooLow:
    5328           0 :         break;
    5329             :       case InliningDecision_Inline:
    5330           0 :         if (target->isInterpreted()) {
    5331             :             InliningStatus status;
    5332           0 :             MOZ_TRY_VAR(status, inlineScriptedCall(callInfo, target));
    5333           0 :             if (status == InliningStatus_Inlined)
    5334           0 :                 return Ok();
    5335             :         }
    5336             :     }
    5337             : 
    5338           0 :     return makeCall(target, callInfo);
    5339             : }
    5340             : 
    5341             : AbortReasonOr<Ok>
    5342        1228 : IonBuilder::jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue)
    5343             : {
    5344        1228 :     startTrackingOptimizations();
    5345             : 
    5346             :     // If this call has never executed, try to seed the observed type set
    5347             :     // based on how the call result is used.
    5348        1228 :     TemporaryTypeSet* observed = bytecodeTypes(pc);
    5349        1228 :     if (observed->empty()) {
    5350        1067 :         if (BytecodeFlowsToBitop(pc)) {
    5351           0 :             observed->addType(TypeSet::Int32Type(), alloc_->lifoAlloc());
    5352        1067 :         } else if (*GetNextPc(pc) == JSOP_POS) {
    5353             :             // Note: this is lame, overspecialized on the code patterns used
    5354             :             // by asm.js and should be replaced by a more general mechanism.
    5355             :             // See bug 870847.
    5356           0 :             observed->addType(TypeSet::DoubleType(), alloc_->lifoAlloc());
    5357             :         }
    5358             :     }
    5359             : 
    5360        1228 :     int calleeDepth = -((int)argc + 2 + constructing);
    5361             : 
    5362             :     // Acquire known call target if existent.
    5363        2456 :     InliningTargets targets(alloc());
    5364        1228 :     TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
    5365        1228 :     if (calleeTypes)
    5366        1228 :         MOZ_TRY(getPolyCallTargets(calleeTypes, constructing, targets, 4));
    5367             : 
    5368        2456 :     CallInfo callInfo(alloc(), constructing, ignoresReturnValue);
    5369        1228 :     if (!callInfo.init(current, argc))
    5370           0 :         return abort(AbortReason::Alloc);
    5371             : 
    5372             :     // Try inlining
    5373             :     InliningStatus status;
    5374        1228 :     MOZ_TRY_VAR(status, inlineCallsite(targets, callInfo));
    5375        1225 :     if (status == InliningStatus_Inlined)
    5376         130 :         return Ok();
    5377             : 
    5378             :     // Discard unreferenced & pre-allocated resume points.
    5379        1095 :     replaceMaybeFallbackFunctionGetter(nullptr);
    5380             : 
    5381             :     // No inline, just make the call.
    5382        1095 :     JSFunction* target = nullptr;
    5383        1095 :     if (targets.length() == 1 && targets[0].target->is<JSFunction>())
    5384         222 :         target = &targets[0].target->as<JSFunction>();
    5385             : 
    5386        1095 :     if (target && status == InliningStatus_WarmUpCountTooLow) {
    5387             :         MRecompileCheck* check =
    5388          36 :             MRecompileCheck::New(alloc(), target->nonLazyScript(),
    5389          18 :                                  optimizationInfo().inliningRecompileThreshold(),
    5390           9 :                                  MRecompileCheck::RecompileCheck_Inlining);
    5391           9 :         current->add(check);
    5392             :     }
    5393             : 
    5394        1095 :     return makeCall(target, callInfo);
    5395             : }
    5396             : 
    5397             : AbortReasonOr<bool>
    5398           0 : IonBuilder::testShouldDOMCall(TypeSet* inTypes, JSFunction* func, JSJitInfo::OpType opType)
    5399             : {
    5400           0 :     if (!func->isNative() || !func->jitInfo())
    5401           0 :         return false;
    5402             : 
    5403             :     // If all the DOM objects flowing through are legal with this
    5404             :     // property, we can bake in a call to the bottom half of the DOM
    5405             :     // accessor
    5406             :     DOMInstanceClassHasProtoAtDepth instanceChecker =
    5407           0 :         compartment->runtime()->DOMcallbacks()->instanceClassMatchesProto;
    5408             : 
    5409           0 :     const JSJitInfo* jinfo = func->jitInfo();
    5410           0 :     if (jinfo->type() != opType)
    5411           0 :         return false;
    5412             : 
    5413           0 :     for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
    5414           0 :         TypeSet::ObjectKey* key = inTypes->getObject(i);
    5415           0 :         if (!key)
    5416           0 :             continue;
    5417             : 
    5418           0 :         if (!alloc().ensureBallast())
    5419           0 :             return abort(AbortReason::Alloc);
    5420             : 
    5421           0 :         if (!key->hasStableClassAndProto(constraints()))
    5422           0 :             return false;
    5423             : 
    5424           0 :         if (!instanceChecker(key->clasp(), jinfo->protoID, jinfo->depth))
    5425           0 :             return false;
    5426             :     }
    5427             : 
    5428           0 :     return true;
    5429             : }
    5430             : 
    5431             : static bool
    5432         137 : ArgumentTypesMatch(MDefinition* def, StackTypeSet* calleeTypes)
    5433             : {
    5434         137 :     if (!calleeTypes)
    5435          52 :         return false;
    5436             : 
    5437          85 :     if (def->resultTypeSet()) {
    5438          47 :         MOZ_ASSERT(def->type() == MIRType::Value || def->mightBeType(def->type()));
    5439          47 :         return def->resultTypeSet()->isSubset(calleeTypes);
    5440             :     }
    5441             : 
    5442          38 :     if (def->type() == MIRType::Value)
    5443           0 :         return false;
    5444             : 
    5445          38 :     if (def->type() == MIRType::Object)
    5446           0 :         return calleeTypes->unknownObject();
    5447             : 
    5448          38 :     return calleeTypes->mightBeMIRType(def->type());
    5449             : }
    5450             : 
    5451             : bool
    5452         263 : IonBuilder::testNeedsArgumentCheck(JSFunction* target, CallInfo& callInfo)
    5453             : {
    5454             :     // If we have a known target, check if the caller arg types are a subset of callee.
    5455             :     // Since typeset accumulates and can't decrease that means we don't need to check
    5456             :     // the arguments anymore.
    5457         263 :     if (!target->hasScript())
    5458         168 :         return true;
    5459             : 
    5460          95 :     JSScript* targetScript = target->nonLazyScript();
    5461             : 
    5462          95 :     if (!ArgumentTypesMatch(callInfo.thisArg(), TypeScript::ThisTypes(targetScript)))
    5463          52 :         return true;
    5464          43 :     uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->nargs());
    5465          85 :     for (size_t i = 0; i < expected_args; i++) {
    5466          42 :         if (!ArgumentTypesMatch(callInfo.getArg(i), TypeScript::ArgTypes(targetScript, i)))
    5467           0 :             return true;
    5468             :     }
    5469          43 :     for (size_t i = callInfo.argc(); i < target->nargs(); i++) {
    5470           0 :         if (!TypeScript::ArgTypes(targetScript, i)->mightBeMIRType(MIRType::Undefined))
    5471           0 :             return true;
    5472             :     }
    5473             : 
    5474          43 :     return false;
    5475             : }
    5476             : 
    5477             : AbortReasonOr<MCall*>
    5478        1145 : IonBuilder::makeCallHelper(JSFunction* target, CallInfo& callInfo)
    5479             : {
    5480             :     // This function may be called with mutated stack.
    5481             :     // Querying TI for popped types is invalid.
    5482             : 
    5483        1145 :     uint32_t targetArgs = callInfo.argc();
    5484             : 
    5485             :     // Collect number of missing arguments provided that the target is
    5486             :     // scripted. Native functions are passed an explicit 'argc' parameter.
    5487        1145 :     if (target && !target->isNative())
    5488          98 :         targetArgs = Max<uint32_t>(target->nargs(), callInfo.argc());
    5489             : 
    5490        1145 :     bool isDOMCall = false;
    5491        1145 :     if (target && !callInfo.constructing()) {
    5492             :         // We know we have a single call target.  Check whether the "this" types
    5493             :         // are DOM types and our function a DOM function, and if so flag the
    5494             :         // MCall accordingly.
    5495         262 :         TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
    5496         317 :         if (thisTypes &&
    5497         270 :             thisTypes->getKnownMIRType() == MIRType::Object &&
    5498           8 :             thisTypes->isDOMClass(constraints()))
    5499             :         {
    5500           0 :             MOZ_TRY_VAR(isDOMCall, testShouldDOMCall(thisTypes, target, JSJitInfo::Method));
    5501             :         }
    5502             :     }
    5503             : 
    5504        4580 :     MCall* call = MCall::New(alloc(), target, targetArgs + 1 + callInfo.constructing(),
    5505        2290 :                              callInfo.argc(), callInfo.constructing(),
    5506        2290 :                              callInfo.ignoresReturnValue(), isDOMCall);
    5507        1145 :     if (!call)
    5508           0 :         return abort(AbortReason::Alloc);
    5509             : 
    5510        1145 :     if (callInfo.constructing())
    5511         140 :         call->addArg(targetArgs + 1, callInfo.getNewTarget());
    5512             : 
    5513             :     // Explicitly pad any missing arguments with |undefined|.
    5514             :     // This permits skipping the argumentsRectifier.
    5515        1145 :     for (int i = targetArgs; i > (int)callInfo.argc(); i--) {
    5516           0 :         MOZ_ASSERT_IF(target, !target->isNative());
    5517           0 :         MConstant* undef = constant(UndefinedValue());
    5518           0 :         if (!alloc().ensureBallast())
    5519           0 :             return abort(AbortReason::Alloc);
    5520           0 :         call->addArg(i, undef);
    5521             :     }
    5522             : 
    5523             :     // Add explicit arguments.
    5524             :     // Skip addArg(0) because it is reserved for this
    5525        3352 :     for (int32_t i = callInfo.argc() - 1; i >= 0; i--)
    5526        2207 :         call->addArg(i + 1, callInfo.getArg(i));
    5527             : 
    5528             :     // Now that we've told it about all the args, compute whether it's movable
    5529        1145 :     call->computeMovable();
    5530             : 
    5531             :     // Inline the constructor on the caller-side.
    5532        1145 :     if (callInfo.constructing()) {
    5533         140 :         MDefinition* create = createThis(target, callInfo.fun(), callInfo.getNewTarget());
    5534         140 :         if (!create)
    5535           0 :             return abort(AbortReason::Disable, "Failure inlining constructor for call.");
    5536             : 
    5537         140 :         callInfo.thisArg()->setImplicitlyUsedUnchecked();
    5538         140 :         callInfo.setThis(create);
    5539             :     }
    5540             : 
    5541             :     // Pass |this| and function.
    5542        1145 :     MDefinition* thisArg = callInfo.thisArg();
    5543        1145 :     call->addArg(0, thisArg);
    5544             : 
    5545        1145 :     if (target && !testNeedsArgumentCheck(target, callInfo))
    5546          43 :         call->disableArgCheck();
    5547             : 
    5548        1145 :     call->initFunction(callInfo.fun());
    5549             : 
    5550        1145 :     current->add(call);
    5551        1145 :     return call;
    5552             : }
    5553             : 
    5554             : static bool
    5555           0 : DOMCallNeedsBarrier(const JSJitInfo* jitinfo, TemporaryTypeSet* types)
    5556             : {
    5557           0 :     MOZ_ASSERT(jitinfo->type() != JSJitInfo::InlinableNative);
    5558             : 
    5559             :     // If the return type of our DOM native is in "types" already, we don't
    5560             :     // actually need a barrier.
    5561           0 :     if (jitinfo->returnType() == JSVAL_TYPE_UNKNOWN)
    5562           0 :         return true;
    5563             : 
    5564             :     // JSVAL_TYPE_OBJECT doesn't tell us much; we still have to barrier on the
    5565             :     // actual type of the object.
    5566           0 :     if (jitinfo->returnType() == JSVAL_TYPE_OBJECT)
    5567           0 :         return true;
    5568             : 
    5569             :     // No need for a barrier if we're already expecting the type we'll produce.
    5570           0 :     return MIRTypeFromValueType(jitinfo->returnType()) != types->getKnownMIRType();
    5571             : }
    5572             : 
    5573             : AbortReasonOr<Ok>
    5574        1145 : IonBuilder::makeCall(JSFunction* target, CallInfo& callInfo)
    5575             : {
    5576             :     // Constructor calls to non-constructors should throw. We don't want to use
    5577             :     // CallKnown in this case.
    5578        1145 :     MOZ_ASSERT_IF(callInfo.constructing() && target, target->isConstructor());
    5579             : 
    5580             :     MCall* call;
    5581        1145 :     MOZ_TRY_VAR(call, makeCallHelper(target, callInfo));
    5582             : 
    5583        1145 :     current->push(call);
    5584        1145 :     if (call->isEffectful())
    5585        1145 :         MOZ_TRY(resumeAfter(call));
    5586             : 
    5587        1145 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    5588             : 
    5589        1145 :     if (call->isCallDOMNative())
    5590           0 :         return pushDOMTypeBarrier(call, types, call->getSingleTarget()->rawJSFunction());
    5591             : 
    5592        1145 :     return pushTypeBarrier(call, types, BarrierKind::TypeSet);
    5593             : }
    5594             : 
    5595             : AbortReasonOr<Ok>
    5596           0 : IonBuilder::jsop_eval(uint32_t argc)
    5597             : {
    5598           0 :     int calleeDepth = -((int)argc + 2);
    5599           0 :     TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
    5600             : 
    5601             :     // Emit a normal call if the eval has never executed. This keeps us from
    5602             :     // disabling compilation for the script when testing with --ion-eager.
    5603           0 :     if (calleeTypes && calleeTypes->empty())
    5604           0 :         return jsop_call(argc, /* constructing = */ false, false);
    5605             : 
    5606           0 :     JSFunction* target = getSingleCallTarget(calleeTypes);
    5607           0 :     if (!target)
    5608           0 :         return abort(AbortReason::Disable, "No single callee for eval()");
    5609             : 
    5610           0 :     if (script()->global().valueIsEval(ObjectValue(*target))) {
    5611           0 :         if (argc != 1)
    5612           0 :             return abort(AbortReason::Disable, "Direct eval with more than one argument");
    5613             : 
    5614           0 :         if (!info().funMaybeLazy())
    5615           0 :             return abort(AbortReason::Disable, "Direct eval in global code");
    5616             : 
    5617           0 :         if (info().funMaybeLazy()->isArrow())
    5618           0 :             return abort(AbortReason::Disable, "Direct eval from arrow function");
    5619             : 
    5620             :         CallInfo callInfo(alloc(), /* constructing = */ false,
    5621           0 :                           /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5622           0 :         if (!callInfo.init(current, argc))
    5623           0 :             return abort(AbortReason::Alloc);
    5624           0 :         callInfo.setImplicitlyUsedUnchecked();
    5625             : 
    5626           0 :         callInfo.fun()->setImplicitlyUsedUnchecked();
    5627             : 
    5628           0 :         MDefinition* envChain = current->environmentChain();
    5629           0 :         MDefinition* string = callInfo.getArg(0);
    5630             : 
    5631             :         // Direct eval acts as identity on non-string types according to
    5632             :         // ES5 15.1.2.1 step 1.
    5633           0 :         if (!string->mightBeType(MIRType::String)) {
    5634           0 :             current->push(string);
    5635           0 :             TemporaryTypeSet* types = bytecodeTypes(pc);
    5636           0 :             return pushTypeBarrier(string, types, BarrierKind::TypeSet);
    5637             :         }
    5638             : 
    5639           0 :         MOZ_TRY(jsop_newtarget());
    5640           0 :         MDefinition* newTargetValue = current->pop();
    5641             : 
    5642             :         // Try to pattern match 'eval(v + "()")'. In this case v is likely a
    5643             :         // name on the env chain and the eval is performing a call on that
    5644             :         // value. Use an env chain lookup rather than a full eval.
    5645           0 :         if (string->isConcat() &&
    5646           0 :             string->getOperand(1)->type() == MIRType::String &&
    5647           0 :             string->getOperand(1)->maybeConstantValue())
    5648             :         {
    5649           0 :             JSAtom* atom = &string->getOperand(1)->maybeConstantValue()->toString()->asAtom();
    5650             : 
    5651           0 :             if (StringEqualsAscii(atom, "()")) {
    5652           0 :                 MDefinition* name = string->getOperand(0);
    5653           0 :                 MInstruction* dynamicName = MGetDynamicName::New(alloc(), envChain, name);
    5654           0 :                 current->add(dynamicName);
    5655             : 
    5656           0 :                 current->push(dynamicName);
    5657           0 :                 current->push(constant(UndefinedValue())); // thisv
    5658             : 
    5659             :                 CallInfo evalCallInfo(alloc(), /* constructing = */ false,
    5660           0 :                                       /* ignoresReturnValue = */ BytecodeIsPopped(pc));
    5661           0 :                 if (!evalCallInfo.init(current, /* argc = */ 0))
    5662           0 :                     return abort(AbortReason::Alloc);
    5663             : 
    5664           0 :                 return makeCall(nullptr, evalCallInfo);
    5665             :             }
    5666             :         }
    5667             : 
    5668           0 :         MInstruction* ins = MCallDirectEval::New(alloc(), envChain, string,
    5669           0 :                                                  newTargetValue, pc);
    5670           0 :         current->add(ins);
    5671           0 :         current->push(ins);
    5672             : 
    5673           0 :         TemporaryTypeSet* types = bytecodeTypes(pc);
    5674           0 :         MOZ_TRY(resumeAfter(ins));
    5675           0 :         return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
    5676             :     }
    5677             : 
    5678           0 :     return jsop_call(argc, /* constructing = */ false, false);
    5679             : }
    5680             : 
    5681             : AbortReasonOr<Ok>
    5682         486 : IonBuilder::jsop_compare(JSOp op)
    5683             : {
    5684         486 :     MDefinition* right = current->pop();
    5685         486 :     MDefinition* left = current->pop();
    5686             : 
    5687         486 :     return jsop_compare(op, left, right);
    5688             : }
    5689             : 
    5690             : AbortReasonOr<Ok>
    5691         486 : IonBuilder::jsop_compare(JSOp op, MDefinition* left, MDefinition* right)
    5692             : {
    5693         486 :     bool emitted = false;
    5694         486 :     startTrackingOptimizations();
    5695             : 
    5696         486 :     if (!forceInlineCaches()) {
    5697         486 :         MOZ_TRY(compareTrySpecialized(&emitted, op, left, right));
    5698         486 :         if (emitted)
    5699         170 :             return Ok();
    5700         316 :         MOZ_TRY(compareTryBitwise(&emitted, op, left, right));
    5701         316 :         if (emitted)
    5702          97 :             return Ok();
    5703         219 :         MOZ_TRY(compareTrySpecializedOnBaselineInspector(&emitted, op, left, right));
    5704         219 :         if (emitted)
    5705           2 :             return Ok();
    5706             :     }
    5707             : 
    5708         217 :     MOZ_TRY(compareTrySharedStub(&emitted, op, left, right));
    5709         217 :     if (emitted)
    5710         217 :         return Ok();
    5711             : 
    5712           0 :     trackOptimizationAttempt(TrackedStrategy::Compare_Call);
    5713             : 
    5714             :     // Not possible to optimize. Do a slow vm call.
    5715           0 :     MCompare* ins = MCompare::New(alloc(), left, right, op);
    5716           0 :     ins->cacheOperandMightEmulateUndefined(constraints());
    5717             : 
    5718           0 :     current->add(ins);
    5719           0 :     current->push(ins);
    5720           0 :     if (ins->isEffectful())
    5721           0 :         MOZ_TRY(resumeAfter(ins));
    5722             : 
    5723           0 :     trackOptimizationSuccess();
    5724           0 :     return Ok();
    5725             : }
    5726             : 
    5727             : static bool
    5728         216 : ObjectOrSimplePrimitive(MDefinition* op)
    5729             : {
    5730             :     // Return true if op is either undefined/null/boolean/int32/symbol or an object.
    5731         216 :     return !op->mightBeType(MIRType::String)
    5732         194 :         && !op->mightBeType(MIRType::Double)
    5733         194 :         && !op->mightBeType(MIRType::Float32)
    5734         194 :         && !op->mightBeType(MIRType::MagicOptimizedArguments)
    5735         194 :         && !op->mightBeType(MIRType::MagicHole)
    5736         410 :         && !op->mightBeType(MIRType::MagicIsConstructing);
    5737             : }
    5738             : 
    5739             : AbortReasonOr<Ok>
    5740         486 : IonBuilder::compareTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
    5741             : {
    5742         486 :     MOZ_ASSERT(*emitted == false);
    5743         486 :     trackOptimizationAttempt(TrackedStrategy::Compare_SpecializedTypes);
    5744             : 
    5745             :     // Try to emit an compare based on the input types.
    5746             : 
    5747         486 :     MCompare::CompareType type = MCompare::determineCompareType(op, left, right);
    5748         486 :     if (type == MCompare::Compare_Unknown) {
    5749         316 :         trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
    5750         316 :         return Ok();
    5751             :     }
    5752             : 
    5753         170 :     MCompare* ins = MCompare::New(alloc(), left, right, op);
    5754         170 :     ins->setCompareType(type);
    5755         170 :     ins->cacheOperandMightEmulateUndefined(constraints());
    5756             : 
    5757             :     // Some compare types need to have the specific type in the rhs.
    5758             :     // Swap operands if that is not the case.
    5759         170 :     if (type == MCompare::Compare_StrictString && right->type() != MIRType::String)
    5760           0 :         ins->swapOperands();
    5761         170 :     else if (type == MCompare::Compare_Null && right->type() != MIRType::Null)
    5762           0 :         ins->swapOperands();
    5763         170 :     else if (type == MCompare::Compare_Undefined && right->type() != MIRType::Undefined)
    5764           0 :         ins->swapOperands();
    5765         170 :     else if (type == MCompare::Compare_Boolean && right->type() != MIRType::Boolean)
    5766           0 :         ins->swapOperands();
    5767             : 
    5768             :     // Replace inputs with unsigned variants if needed.
    5769         170 :     if (type == MCompare::Compare_UInt32)
    5770           0 :         ins->replaceWithUnsignedOperands();
    5771             : 
    5772         170 :     current->add(ins);
    5773         170 :     current->push(ins);
    5774             : 
    5775         170 :     MOZ_ASSERT(!ins->isEffectful());
    5776         170 :     trackOptimizationSuccess();
    5777         170 :     *emitted = true;
    5778         170 :     return Ok();
    5779             : }
    5780             : 
    5781             : AbortReasonOr<Ok>
    5782         316 : IonBuilder::compareTryBitwise(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
    5783             : {
    5784         316 :     MOZ_ASSERT(*emitted == false);
    5785         316 :     trackOptimizationAttempt(TrackedStrategy::Compare_Bitwise);
    5786             : 
    5787             :     // Try to emit a bitwise compare. Check if a bitwise compare equals the wanted
    5788             :     // result for all observed operand types.
    5789             : 
    5790             :     // Only allow loose and strict equality.
    5791         316 :     if (op != JSOP_EQ && op != JSOP_NE && op != JSOP_STRICTEQ && op != JSOP_STRICTNE) {
    5792         197 :         trackOptimizationOutcome(TrackedOutcome::RelationalCompare);
    5793         197 :         return Ok();
    5794             :     }
    5795             : 
    5796             :     // Only primitive (not double/string) or objects are supported.
    5797             :     // I.e. Undefined/Null/Boolean/Int32/Symbol and Object
    5798         119 :     if (!ObjectOrSimplePrimitive(left) || !ObjectOrSimplePrimitive(right)) {
    5799          22 :         trackOptimizationOutcome(TrackedOutcome::OperandTypeNotBitwiseComparable);
    5800          22 :         return Ok();
    5801             :     }
    5802             : 
    5803             :     // Objects that emulate undefined are not supported.
    5804         194 :     if (left->maybeEmulatesUndefined(constraints()) ||
    5805          97 :         right->maybeEmulatesUndefined(constraints()))
    5806             :     {
    5807           0 :         trackOptimizationOutcome(TrackedOutcome::OperandMaybeEmulatesUndefined);
    5808           0 :         return Ok();
    5809             :     }
    5810             : 
    5811             :     // In the loose comparison more values could be the same,
    5812             :     // but value comparison reporting otherwise.
    5813          97 :     if (op == JSOP_EQ || op == JSOP_NE) {
    5814             : 
    5815             :         // Undefined compared loosy to Null is not supported,
    5816             :         // because tag is different, but value can be the same (undefined == null).
    5817           4 :         if ((left->mightBeType(MIRType::Undefined) && right->mightBeType(MIRType::Null)) ||
    5818           2 :             (left->mightBeType(MIRType::Null) && right->mightBeType(MIRType::Undefined)))
    5819             :         {
    5820           0 :             trackOptimizationOutcome(TrackedOutcome::LoosyUndefinedNullCompare);
    5821           0 :             return Ok();
    5822             :         }
    5823             : 
    5824             :         // Int32 compared loosy to Boolean is not supported,
    5825             :         // because tag is different, but value can be the same (1 == true).
    5826           4 :         if ((left->mightBeType(MIRType::Int32) && right->mightBeType(MIRType::Boolean)) ||
    5827           2 :             (left->mightBeType(MIRType::Boolean) && right->mightBeType(MIRType::Int32)))
    5828             :         {
    5829           0 :             trackOptimizationOutcome(TrackedOutcome::LoosyInt32BooleanCompare);
    5830           0 :             return Ok();
    5831             :         }
    5832             : 
    5833             :         // For loosy comparison of an object with a Boolean/Number/String/Symbol
    5834             :         // the valueOf the object is taken. Therefore not supported.
    5835           4 :         bool simpleLHS = left->mightBeType(MIRType::Boolean) ||
    5836           4 :                          left->mightBeType(MIRType::Int32) ||
    5837           4 :                          left->mightBeType(MIRType::Symbol);
    5838           4 :         bool simpleRHS = right->mightBeType(MIRType::Boolean) ||
    5839           2 :                          right->mightBeType(MIRType::Int32) ||
    5840           2 :                          right->mightBeType(MIRType::Symbol);
    5841           4 :         if ((left->mightBeType(MIRType::Object) && simpleRHS) ||
    5842           2 :             (right->mightBeType(MIRType::Object) && simpleLHS))
    5843             :         {
    5844           0 :             trackOptimizationOutcome(TrackedOutcome::CallsValueOf);
    5845           0 :             return Ok();
    5846             :         }
    5847             :     }
    5848             : 
    5849          97 :     MCompare* ins = MCompare::New(alloc(), left, right, op);
    5850          97 :     ins->setCompareType(MCompare::Compare_Bitwise);
    5851          97 :     ins->cacheOperandMightEmulateUndefined(constraints());
    5852             : 
    5853          97 :     current->add(ins);
    5854          97 :     current->push(ins);
    5855             : 
    5856          97 :     MOZ_ASSERT(!ins->isEffectful());
    5857          97 :     trackOptimizationSuccess();
    5858          97 :     *emitted = true;
    5859          97 :     return Ok();
    5860             : }
    5861             : 
    5862             : AbortReasonOr<Ok>
    5863         219 : IonBuilder::compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
    5864             :                                                      MDefinition* right)
    5865             : {
    5866         219 :     MOZ_ASSERT(*emitted == false);
    5867         219 :     trackOptimizationAttempt(TrackedStrategy::Compare_SpecializedOnBaselineTypes);
    5868             : 
    5869             :     // Try to specialize based on any baseline caches that have been generated
    5870             :     // for the opcode. These will cause the instruction's type policy to insert
    5871             :     // fallible unboxes to the appropriate input types.
    5872             : 
    5873             :     // Strict equality isn't supported.
    5874         219 :     if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE) {
    5875          22 :         trackOptimizationOutcome(TrackedOutcome::StrictCompare);
    5876          22 :         return Ok();
    5877             :     }
    5878             : 
    5879         197 :     MCompare::CompareType type = inspector->expectedCompareType(pc);
    5880         197 :     if (type == MCompare::Compare_Unknown) {
    5881         195 :         trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
    5882         195 :         return Ok();
    5883             :     }
    5884             : 
    5885           2 :     MCompare* ins = MCompare::New(alloc(), left, right, op);
    5886           2 :     ins->setCompareType(type);
    5887           2 :     ins->cacheOperandMightEmulateUndefined(constraints());
    5888             : 
    5889           2 :     current->add(ins);
    5890           2 :     current->push(ins);
    5891             : 
    5892           2 :     MOZ_ASSERT(!ins->isEffectful());
    5893           2 :     trackOptimizationSuccess();
    5894           2 :     *emitted = true;
    5895           2 :     return Ok();
    5896             : }
    5897             : 
    5898             : AbortReasonOr<Ok>
    5899         217 : IonBuilder::compareTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
    5900             : {
    5901         217 :     MOZ_ASSERT(*emitted == false);
    5902             : 
    5903             :     // Try to emit a shared stub cache.
    5904             : 
    5905         217 :     if (JitOptions.disableSharedStubs)
    5906           0 :         return Ok();
    5907             : 
    5908         217 :     if (JSOp(*pc) == JSOP_CASE)
    5909           0 :         return Ok();
    5910             : 
    5911         217 :     trackOptimizationAttempt(TrackedStrategy::Compare_SharedCache);
    5912             : 
    5913         217 :     MBinarySharedStub* stub = MBinarySharedStub::New(alloc(), left, right);
    5914         217 :     current->add(stub);
    5915         217 :     current->push(stub);
    5916         217 :     MOZ_TRY(resumeAfter(stub));
    5917             : 
    5918         217 :     MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Boolean, MUnbox::Infallible);
    5919         217 :     current->add(unbox);
    5920         217 :     current->push(unbox);
    5921             : 
    5922         217 :     trackOptimizationSuccess();
    5923         217 :     *emitted = true;
    5924         217 :     return Ok();
    5925             : }
    5926             : 
    5927             : AbortReasonOr<Ok>
    5928          11 : IonBuilder::newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, uint32_t length)
    5929             : {
    5930          11 :     MOZ_ASSERT(*emitted == false);
    5931             : 
    5932             :     // TODO: Support tracking optimizations for inlining a call and regular
    5933             :     // optimization tracking at the same time. Currently just drop optimization
    5934             :     // tracking when that happens.
    5935          11 :     bool canTrackOptimization = !IsCallPC(pc);
    5936             : 
    5937          11 :     if (canTrackOptimization)
    5938          11 :         trackOptimizationAttempt(TrackedStrategy::NewArray_TemplateObject);
    5939             : 
    5940          11 :     if (!templateObject) {
    5941           2 :         if (canTrackOptimization)
    5942           2 :             trackOptimizationOutcome(TrackedOutcome::NoTemplateObject);
    5943           2 :         return Ok();
    5944             :     }
    5945             : 
    5946           9 :     if (templateObject->is<UnboxedArrayObject>()) {
    5947           0 :         MOZ_ASSERT(templateObject->as<UnboxedArrayObject>().capacity() >= length);
    5948           0 :         if (!templateObject->as<UnboxedArrayObject>().hasInlineElements()) {
    5949           0 :             if (canTrackOptimization)
    5950           0 :                 trackOptimizationOutcome(TrackedOutcome::TemplateObjectIsUnboxedWithoutInlineElements);
    5951           0 :             return Ok();
    5952             :         }
    5953             :     }
    5954             : 
    5955           9 :     MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT);
    5956             : 
    5957             :     size_t arraySlots =
    5958           9 :         gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER;
    5959             : 
    5960           9 :     if (length > arraySlots) {
    5961           0 :         if (canTrackOptimization)
    5962           0 :             trackOptimizationOutcome(TrackedOutcome::LengthTooBig);
    5963           0 :         return Ok();
    5964             :     }
    5965             : 
    5966             :     // Emit fastpath.
    5967             : 
    5968           9 :     gc::InitialHeap heap = templateObject->group()->initialHeap(constraints());
    5969           9 :     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    5970           9 :     current->add(templateConst);
    5971             : 
    5972           9 :     MNewArray* ins = MNewArray::New(alloc(), constraints(), length, templateConst, heap, pc);
    5973           9 :     current->add(ins);
    5974           9 :     current->push(ins);
    5975             : 
    5976           9 :     if (canTrackOptimization)
    5977           9 :         trackOptimizationSuccess();
    5978           9 :     *emitted = true;
    5979           9 :     return Ok();
    5980             : }
    5981             : 
    5982             : AbortReasonOr<Ok>
    5983           2 : IonBuilder::newArrayTrySharedStub(bool* emitted)
    5984             : {
    5985           2 :     MOZ_ASSERT(*emitted == false);
    5986             : 
    5987             :     // TODO: Support tracking optimizations for inlining a call and regular
    5988             :     // optimization tracking at the same time. Currently just drop optimization
    5989             :     // tracking when that happens.
    5990           2 :     bool canTrackOptimization = !IsCallPC(pc);
    5991             : 
    5992             :     // Try to emit a shared stub cache.
    5993             : 
    5994           2 :     if (JitOptions.disableSharedStubs)
    5995           0 :         return Ok();
    5996             : 
    5997           2 :     if (*pc != JSOP_NEWINIT && *pc != JSOP_NEWARRAY)
    5998           0 :         return Ok();
    5999             : 
    6000           2 :     if (canTrackOptimization)
    6001           2 :         trackOptimizationAttempt(TrackedStrategy::NewArray_SharedCache);
    6002             : 
    6003           2 :     MInstruction* stub = MNullarySharedStub::New(alloc());
    6004           2 :     current->add(stub);
    6005           2 :     current->push(stub);
    6006             : 
    6007           2 :     MOZ_TRY(resumeAfter(stub));
    6008             : 
    6009           2 :     MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Object, MUnbox::Infallible);
    6010           2 :     current->add(unbox);
    6011           2 :     current->push(unbox);
    6012             : 
    6013           2 :     if (canTrackOptimization)
    6014           2 :         trackOptimizationSuccess();
    6015             : 
    6016           2 :     *emitted = true;
    6017           2 :     return Ok();
    6018             : }
    6019             : 
    6020             : AbortReasonOr<Ok>
    6021           0 : IonBuilder::newArrayTryVM(bool* emitted, JSObject* templateObject, uint32_t length)
    6022             : {
    6023           0 :     MOZ_ASSERT(*emitted == false);
    6024             : 
    6025             :     // TODO: Support tracking optimizations for inlining a call and regular
    6026             :     // optimization tracking at the same time. Currently just drop optimization
    6027             :     // tracking when that happens.
    6028           0 :     bool canTrackOptimization = !IsCallPC(pc);
    6029             : 
    6030             :     // Emit a VM call.
    6031           0 :     if (canTrackOptimization)
    6032           0 :         trackOptimizationAttempt(TrackedStrategy::NewArray_Call);
    6033             : 
    6034           0 :     gc::InitialHeap heap = gc::DefaultHeap;
    6035           0 :     MConstant* templateConst = MConstant::New(alloc(), NullValue());
    6036             : 
    6037           0 :     if (templateObject) {
    6038           0 :         heap = templateObject->group()->initialHeap(constraints());
    6039           0 :         templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    6040             :     }
    6041             : 
    6042           0 :     current->add(templateConst);
    6043             : 
    6044           0 :     MNewArray* ins = MNewArray::NewVM(alloc(), constraints(), length, templateConst, heap, pc);
    6045           0 :     current->add(ins);
    6046           0 :     current->push(ins);
    6047             : 
    6048           0 :     if (canTrackOptimization)
    6049           0 :         trackOptimizationSuccess();
    6050           0 :     *emitted = true;
    6051           0 :     return Ok();
    6052             : }
    6053             : 
    6054             : AbortReasonOr<Ok>
    6055          11 : IonBuilder::jsop_newarray(uint32_t length)
    6056             : {
    6057          11 :     JSObject* templateObject = inspector->getTemplateObject(pc);
    6058          11 :     MOZ_TRY(jsop_newarray(templateObject, length));
    6059             : 
    6060             :     // Improve resulting typeset.
    6061          11 :     ObjectGroup* templateGroup = inspector->getTemplateObjectGroup(pc);
    6062          11 :     if (templateGroup) {
    6063           9 :         TemporaryTypeSet* types = MakeSingletonTypeSet(constraints(), templateGroup);
    6064           9 :         current->peek(-1)->setResultTypeSet(types);
    6065             :     }
    6066             : 
    6067          11 :     return Ok();
    6068             : }
    6069             : 
    6070             : AbortReasonOr<Ok>
    6071          11 : IonBuilder::jsop_newarray(JSObject* templateObject, uint32_t length)
    6072             : {
    6073             :     // TODO: Support tracking optimizations for inlining a call and regular
    6074             :     // optimization tracking at the same time. Currently just drop optimization
    6075             :     // tracking when that happens.
    6076          11 :     bool canTrackOptimization = !IsCallPC(pc);
    6077             : 
    6078          11 :     bool emitted = false;
    6079          11 :     if (canTrackOptimization)
    6080          11 :         startTrackingOptimizations();
    6081             : 
    6082          11 :     if (!forceInlineCaches()) {
    6083          11 :         MOZ_TRY(newArrayTryTemplateObject(&emitted, templateObject, length));
    6084          11 :         if (emitted)
    6085           9 :             return Ok();
    6086             :     }
    6087             : 
    6088           2 :     MOZ_TRY(newArrayTrySharedStub(&emitted));
    6089           2 :     if (emitted)
    6090           2 :         return Ok();
    6091             : 
    6092           0 :     MOZ_TRY(newArrayTryVM(&emitted, templateObject, length));
    6093           0 :     if (emitted)
    6094           0 :         return Ok();
    6095             : 
    6096           0 :     MOZ_CRASH("newarray should have been emited");
    6097             : }
    6098             : 
    6099             : AbortReasonOr<Ok>
    6100           0 : IonBuilder::jsop_newarray_copyonwrite()
    6101             : {
    6102           0 :     ArrayObject* templateObject = ObjectGroup::getCopyOnWriteObject(script(), pc);
    6103             : 
    6104             :     // The baseline compiler should have ensured the template object has a type
    6105             :     // with the copy on write flag set already. During the arguments usage
    6106             :     // analysis the baseline compiler hasn't run yet, however, though in this
    6107             :     // case the template object's type doesn't matter.
    6108           0 :     MOZ_ASSERT_IF(info().analysisMode() != Analysis_ArgumentsUsage,
    6109             :                   templateObject->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE));
    6110             : 
    6111             :     MNewArrayCopyOnWrite* ins =
    6112           0 :         MNewArrayCopyOnWrite::New(alloc(), constraints(), templateObject,
    6113           0 :                                   templateObject->group()->initialHeap(constraints()));
    6114             : 
    6115           0 :     current->add(ins);
    6116           0 :     current->push(ins);
    6117             : 
    6118           0 :     return Ok();
    6119             : }
    6120             : 
    6121             : AbortReasonOr<Ok>
    6122           8 : IonBuilder::newObjectTryTemplateObject(bool* emitted, JSObject* templateObject)
    6123             : {
    6124           8 :     MOZ_ASSERT(*emitted == false);
    6125             : 
    6126             :     // TODO: Support tracking optimizations for inlining a call and regular
    6127             :     // optimization tracking at the same time. Currently just drop optimization
    6128             :     // tracking when that happens.
    6129           8 :     bool canTrackOptimization = !IsCallPC(pc);
    6130             : 
    6131           8 :     if (canTrackOptimization)
    6132           8 :         trackOptimizationAttempt(TrackedStrategy::NewObject_TemplateObject);
    6133           8 :     if (!templateObject) {
    6134           1 :         if (canTrackOptimization)
    6135           1 :             trackOptimizationOutcome(TrackedOutcome::NoTemplateObject);
    6136           1 :         return Ok();
    6137             :     }
    6138             : 
    6139           7 :     if (templateObject->is<PlainObject>() && templateObject->as<PlainObject>().hasDynamicSlots()) {
    6140           0 :         if (canTrackOptimization)
    6141           0 :             trackOptimizationOutcome(TrackedOutcome::TemplateObjectIsPlainObjectWithDynamicSlots);
    6142           0 :         return Ok();
    6143             :     }
    6144             : 
    6145             :     // Emit fastpath.
    6146             : 
    6147             :     MNewObject::Mode mode;
    6148           7 :     if (JSOp(*pc) == JSOP_NEWOBJECT || JSOp(*pc) == JSOP_NEWINIT)
    6149           7 :         mode = MNewObject::ObjectLiteral;
    6150             :     else
    6151           0 :         mode = MNewObject::ObjectCreate;
    6152             : 
    6153           7 :     gc::InitialHeap heap = templateObject->group()->initialHeap(constraints());
    6154           7 :     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    6155           7 :     current->add(templateConst);
    6156             : 
    6157           7 :     MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, heap, mode);
    6158           7 :     current->add(ins);
    6159           7 :     current->push(ins);
    6160             : 
    6161           7 :     MOZ_TRY(resumeAfter(ins));
    6162             : 
    6163           7 :     if (canTrackOptimization)
    6164           7 :         trackOptimizationSuccess();
    6165           7 :     *emitted = true;
    6166           7 :     return Ok();
    6167             : }
    6168             : 
    6169             : AbortReasonOr<Ok>
    6170           1 : IonBuilder::newObjectTrySharedStub(bool* emitted)
    6171             : {
    6172           1 :     MOZ_ASSERT(*emitted == false);
    6173             : 
    6174             :     // TODO: Support tracking optimizations for inlining a call and regular
    6175             :     // optimization tracking at the same time. Currently just drop optimization
    6176             :     // tracking when that happens.
    6177           1 :     bool canTrackOptimization = !IsCallPC(pc);
    6178             : 
    6179             :     // Try to emit a shared stub cache.
    6180             : 
    6181           1 :     if (JitOptions.disableSharedStubs)
    6182           0 :         return Ok();
    6183             : 
    6184           1 :     if (canTrackOptimization)
    6185           1 :         trackOptimizationAttempt(TrackedStrategy::NewObject_SharedCache);
    6186             : 
    6187           1 :     MInstruction* stub = MNullarySharedStub::New(alloc());
    6188           1 :     current->add(stub);
    6189           1 :     current->push(stub);
    6190             : 
    6191           1 :     MOZ_TRY(resumeAfter(stub));
    6192             : 
    6193           1 :     MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Object, MUnbox::Infallible);
    6194           1 :     current->add(unbox);
    6195           1 :     current->push(unbox);
    6196             : 
    6197           1 :     if (canTrackOptimization)
    6198           1 :         trackOptimizationSuccess();
    6199           1 :     *emitted = true;
    6200           1 :     return Ok();
    6201             : }
    6202             : 
    6203             : AbortReasonOr<Ok>
    6204           0 : IonBuilder::newObjectTryVM(bool* emitted, JSObject* templateObject)
    6205             : {
    6206             :     // Emit a VM call.
    6207           0 :     MOZ_ASSERT(JSOp(*pc) == JSOP_NEWOBJECT || JSOp(*pc) == JSOP_NEWINIT);
    6208             : 
    6209           0 :     trackOptimizationAttempt(TrackedStrategy::NewObject_Call);
    6210             : 
    6211           0 :     gc::InitialHeap heap = gc::DefaultHeap;
    6212           0 :     MConstant* templateConst = MConstant::New(alloc(), NullValue());
    6213             : 
    6214           0 :     if (templateObject) {
    6215           0 :         heap = templateObject->group()->initialHeap(constraints());
    6216           0 :         templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
    6217             :     }
    6218             : 
    6219           0 :     current->add(templateConst);
    6220             : 
    6221           0 :     MNewObject* ins = MNewObject::NewVM(alloc(), constraints(), templateConst, heap,
    6222           0 :                                         MNewObject::ObjectLiteral);
    6223           0 :     current->add(ins);
    6224           0 :     current->push(ins);
    6225             : 
    6226           0 :     MOZ_TRY(resumeAfter(ins));
    6227             : 
    6228           0 :     trackOptimizationSuccess();
    6229           0 :     *emitted = true;
    6230           0 :     return Ok();
    6231             : }
    6232             : 
    6233             : AbortReasonOr<Ok>
    6234           8 : IonBuilder::jsop_newobject()
    6235             : {
    6236           8 :     bool emitted = false;
    6237           8 :     startTrackingOptimizations();
    6238             : 
    6239           8 :     JSObject* templateObject = inspector->getTemplateObject(pc);
    6240             : 
    6241           8 :     if (!forceInlineCaches()) {
    6242           8 :         MOZ_TRY(newObjectTryTemplateObject(&emitted, templateObject));
    6243           8 :         if (emitted)
    6244           7 :             return Ok();
    6245             :     }
    6246           1 :     MOZ_TRY(newObjectTrySharedStub(&emitted));
    6247           1 :     if (emitted)
    6248           1 :         return Ok();
    6249             : 
    6250           0 :     MOZ_TRY(newObjectTryVM(&emitted, templateObject));
    6251           0 :     if (emitted)
    6252           0 :         return Ok();
    6253             : 
    6254           0 :     MOZ_CRASH("newobject should have been emited");
    6255             : }
    6256             : 
    6257             : AbortReasonOr<Ok>
    6258          54 : IonBuilder::jsop_initelem()
    6259             : {
    6260          54 :     MOZ_ASSERT(*pc == JSOP_INITELEM || *pc == JSOP_INITHIDDENELEM);
    6261             : 
    6262          54 :     MDefinition* value = current->pop();
    6263          54 :     MDefinition* id = current->pop();
    6264          54 :     MDefinition* obj = current->peek(-1);
    6265             : 
    6266          54 :     bool emitted = false;
    6267             : 
    6268          54 :     if (!forceInlineCaches() && *pc == JSOP_INITELEM) {
    6269          54 :         MOZ_TRY(initOrSetElemTryDense(&emitted, obj, id, value, /* writeHole = */ true));
    6270          54 :         if (emitted)
    6271           1 :             return Ok();
    6272             :     }
    6273             : 
    6274          53 :     MOZ_TRY(initOrSetElemTryCache(&emitted, obj, id, value));
    6275          53 :     if (emitted)
    6276           0 :         return Ok();
    6277             : 
    6278          53 :     MInitElem* initElem = MInitElem::New(alloc(), obj, id, value);
    6279          53 :     current->add(initElem);
    6280             : 
    6281          53 :     return resumeAfter(initElem);
    6282             : }
    6283             : 
    6284             : AbortReasonOr<Ok>
    6285           0 : IonBuilder::jsop_initelem_inc()
    6286             : {
    6287           0 :     MDefinition* value = current->pop();
    6288           0 :     MDefinition* id = current->pop();
    6289           0 :     MDefinition* obj = current->peek(-1);
    6290             : 
    6291           0 :     bool emitted = false;
    6292             : 
    6293           0 :     MAdd* nextId = MAdd::New(alloc(), id, constantInt(1), MIRType::Int32);
    6294           0 :     current->add(nextId);
    6295           0 :     current->push(nextId);
    6296             : 
    6297           0 :     if (!forceInlineCaches()) {
    6298           0 :         MOZ_TRY(initOrSetElemTryDense(&emitted, obj, id, value, /* writeHole = */ true));
    6299           0 :         if (emitted)
    6300           0 :             return Ok();
    6301             :     }
    6302             : 
    6303           0 :     MOZ_TRY(initOrSetElemTryCache(&emitted, obj, id, value));
    6304           0 :     if (emitted)
    6305           0 :         return Ok();
    6306             : 
    6307           0 :     MCallInitElementArray* initElem = MCallInitElementArray::New(alloc(), obj, id, value);
    6308           0 :     current->add(initElem);
    6309             : 
    6310           0 :     return resumeAfter(initElem);
    6311             : }
    6312             : 
    6313             : AbortReasonOr<Ok>
    6314          14 : IonBuilder::jsop_initelem_array()
    6315             : {
    6316          14 :     MDefinition* value = current->pop();
    6317          14 :     MDefinition* obj = current->peek(-1);
    6318             : 
    6319             :     // Make sure that arrays have the type being written to them by the
    6320             :     // intializer, and that arrays are marked as non-packed when writing holes
    6321             :     // to them during initialization.
    6322          14 :     bool needStub = false;
    6323          14 :     JSValueType unboxedType = JSVAL_TYPE_MAGIC;
    6324          14 :     if (shouldAbortOnPreliminaryGroups(obj)) {
    6325           0 :         needStub = true;
    6326          42 :     } else if (!obj->resultTypeSet() ||
    6327          28 :                obj->resultTypeSet()->unknownObject() ||
    6328          14 :                obj->resultTypeSet()->getObjectCount() != 1)
    6329             :     {
    6330           0 :         needStub = true;
    6331             :     } else {
    6332          14 :         MOZ_ASSERT(obj->resultTypeSet()->getObjectCount() == 1);
    6333          14 :         TypeSet::ObjectKey* initializer = obj->resultTypeSet()->getObject(0);
    6334          14 :         if (initializer->clasp() == &UnboxedArrayObject::class_) {
    6335           0 :             if (initializer->group()->unboxedLayout().nativeGroup())
    6336           0 :                 needStub = true;
    6337             :             else
    6338           0 :                 unboxedType = initializer->group()->unboxedLayout().elementType();
    6339             :         }
    6340          14 :         if (value->type() == MIRType::MagicHole) {
    6341           0 :             if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED))
    6342           0 :                 needStub = true;
    6343          14 :         } else if (!initializer->unknownProperties()) {
    6344          14 :             HeapTypeSetKey elemTypes = initializer->property(JSID_VOID);
    6345          14 :             if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
    6346           0 :                 elemTypes.freeze(constraints());
    6347           0 :                 needStub = true;
    6348             :             }
    6349             :         }
    6350             :     }
    6351             : 
    6352          14 :     uint32_t index = GET_UINT32(pc);
    6353          14 :     if (needStub) {
    6354           0 :         MOZ_ASSERT(index <= INT32_MAX,
    6355             :                    "the bytecode emitter must fail to compile code that would "
    6356             :                    "produce JSOP_INITELEM_ARRAY with an index exceeding "
    6357             :                    "int32_t range");
    6358           0 :         MCallInitElementArray* store = MCallInitElementArray::New(alloc(), obj,
    6359           0 :                                                                   constantInt(index), value);
    6360           0 :         current->add(store);
    6361           0 :         return resumeAfter(store);
    6362             :     }
    6363             : 
    6364          14 :     return initializeArrayElement(obj, index, value, unboxedType, /* addResumePoint = */ true);
    6365             : }
    6366             : 
    6367             : AbortReasonOr<Ok>
    6368          14 : IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
    6369             :                                    JSValueType unboxedType,
    6370             :                                    bool addResumePointAndIncrementInitializedLength)
    6371             : {
    6372          14 :     MConstant* id = MConstant::New(alloc(), Int32Value(index));
    6373          14 :     current->add(id);
    6374             : 
    6375             :     // Get the elements vector.
    6376          14 :     MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
    6377          14 :     current->add(elements);
    6378             : 
    6379          14 :     if (unboxedType != JSVAL_TYPE_MAGIC) {
    6380             :         // Note: storeUnboxedValue takes care of any post barriers on the value.
    6381           0 :         storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false);
    6382             : 
    6383           0 :         if (addResumePointAndIncrementInitializedLength) {
    6384           0 :             MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj);
    6385           0 :             current->add(increment);
    6386             : 
    6387           0 :             MOZ_TRY(resumeAfter(increment));
    6388             :         }
    6389             :     } else {
    6390          14 :         if (NeedsPostBarrier(value))
    6391           0 :             current->add(MPostWriteBarrier::New(alloc(), obj, value));
    6392             : 
    6393          28 :         if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) ||
    6394          14 :             (obj->isNullarySharedStub() &&
    6395           0 :             obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles))
    6396             :         {
    6397           0 :             MInstruction* valueDouble = MToDouble::New(alloc(), value);
    6398           0 :             current->add(valueDouble);
    6399           0 :             value = valueDouble;
    6400             :         }
    6401             : 
    6402             :         // Store the value.
    6403          28 :         MStoreElement* store = MStoreElement::New(alloc(), elements, id, value,
    6404          14 :                                                   /* needsHoleCheck = */ false);
    6405          14 :         current->add(store);
    6406             : 
    6407          14 :         if (addResumePointAndIncrementInitializedLength) {
    6408             :             // Update the initialized length. (The template object for this
    6409             :             // array has the array's ultimate length, so the length field is
    6410             :             // already correct: no updating needed.)
    6411          14 :             MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id);
    6412          14 :             current->add(initLength);
    6413             : 
    6414          14 :             MOZ_TRY(resumeAfter(initLength));
    6415             :         }
    6416             :     }
    6417             : 
    6418          14 :     return Ok();
    6419             : }
    6420             : 
    6421             : AbortReasonOr<Ok>
    6422           0 : IonBuilder::jsop_mutateproto()
    6423             : {
    6424           0 :     MDefinition* value = current->pop();
    6425           0 :     MDefinition* obj = current->peek(-1);
    6426             : 
    6427           0 :     MMutateProto* mutate = MMutateProto::New(alloc(), obj, value);
    6428           0 :     current->add(mutate);
    6429           0 :     return resumeAfter(mutate);
    6430             : }
    6431             : 
    6432             : AbortReasonOr<Ok>
    6433          18 : IonBuilder::jsop_initprop(PropertyName* name)
    6434             : {
    6435          18 :     bool useSlowPath = false;
    6436             : 
    6437          18 :     MDefinition* value = current->peek(-1);
    6438          18 :     MDefinition* obj = current->peek(-2);
    6439          18 :     if (obj->isLambda()) {
    6440           0 :         useSlowPath = true;
    6441          18 :     } else if (obj->isNewObject()) {
    6442          14 :         if (JSObject* templateObject = obj->toNewObject()->templateObject()) {
    6443          14 :             if (templateObject->is<PlainObject>()) {
    6444          14 :                 if (!templateObject->as<PlainObject>().containsPure(name))
    6445           0 :                     useSlowPath = true;
    6446             :             } else {
    6447           0 :                 MOZ_ASSERT(templateObject->as<UnboxedPlainObject>().layout().lookup(name));
    6448             :             }
    6449             :         } else {
    6450           0 :             useSlowPath = true;
    6451             :         }
    6452             :     } else {
    6453           4 :         MOZ_ASSERT(obj->isUnbox() && obj->getOperand(0)->isNullarySharedStub());
    6454           4 :         useSlowPath = true;
    6455             :     }
    6456             : 
    6457          18 :     if (useSlowPath) {
    6458           4 :         current->pop();
    6459           4 :         MInitProp* init = MInitProp::New(alloc(), obj, name, value);
    6460           4 :         current->add(init);
    6461           4 :         return resumeAfter(init);
    6462             :     }
    6463             : 
    6464          14 :     MInstruction* last = *current->rbegin();
    6465             : 
    6466             :     // This is definitely initializing an 'own' property of the object, treat
    6467             :     // it as an assignment.
    6468          14 :     MOZ_TRY(jsop_setprop(name));
    6469             : 
    6470             :     // SETPROP pushed the value, instead of the object. Fix this on the stack,
    6471             :     // and check the most recent resume point to see if it needs updating too.
    6472          14 :     current->pop();
    6473          14 :     current->push(obj);
    6474          14 :     for (MInstructionReverseIterator riter = current->rbegin(); *riter != last; riter++) {
    6475          14 :         if (MResumePoint* resumePoint = riter->resumePoint()) {
    6476          14 :             MOZ_ASSERT(resumePoint->pc() == pc);
    6477          14 :             if (resumePoint->mode() == MResumePoint::ResumeAfter) {
    6478          14 :                 size_t index = resumePoint->numOperands() - 1;
    6479          14 :                 resumePoint->replaceOperand(index, obj);
    6480             :             }
    6481          14 :             break;
    6482             :         }
    6483             :     }
    6484             : 
    6485          14 :     return Ok();
    6486             : }
    6487             : 
    6488             : AbortReasonOr<Ok>
    6489           0 : IonBuilder::jsop_initprop_getter_setter(PropertyName* name)
    6490             : {
    6491           0 :     MDefinition* value = current->pop();
    6492           0 :     MDefinition* obj = current->peek(-1);
    6493             : 
    6494           0 :     MInitPropGetterSetter* init = MInitPropGetterSetter::New(alloc(), obj, name, value);
    6495           0 :     current->add(init);
    6496           0 :     return resumeAfter(init);
    6497             : }
    6498             : 
    6499             : AbortReasonOr<Ok>
    6500           0 : IonBuilder::jsop_initelem_getter_setter()
    6501             : {
    6502           0 :     MDefinition* value = current->pop();
    6503           0 :     MDefinition* id = current->pop();
    6504           0 :     MDefinition* obj = current->peek(-1);
    6505             : 
    6506           0 :     MInitElemGetterSetter* init = MInitElemGetterSetter::New(alloc(), obj, id, value);
    6507           0 :     current->add(init);
    6508           0 :     return resumeAfter(init);
    6509             : }
    6510             : 
    6511             : AbortReasonOr<MBasicBlock*>
    6512        3824 : IonBuilder::newBlock(MBasicBlock* predecessor, jsbytecode* pc)
    6513             : {
    6514        3824 :     MBasicBlock* block = MBasicBlock::New(graph(), &analysis(), info(), predecessor,
    6515        3824 :                                           bytecodeSite(pc), MBasicBlock::NORMAL);
    6516        3824 :     if (!block)
    6517           0 :         return abort(AbortReason::Alloc);
    6518             : 
    6519        3824 :     block->setLoopDepth(loopDepth_);
    6520        3824 :     return block;
    6521             : }
    6522             : 
    6523             : AbortReasonOr<MBasicBlock*>
    6524           0 : IonBuilder::newBlock(MBasicBlock* predecessor, jsbytecode* pc, MResumePoint* priorResumePoint)
    6525             : {
    6526           0 :     MBasicBlock* block = MBasicBlock::NewWithResumePoint(graph(), info(), predecessor,
    6527           0 :                                                          bytecodeSite(pc), priorResumePoint);
    6528           0 :     if (!block)
    6529           0 :         return abort(AbortReason::Alloc);
    6530             : 
    6531           0 :     block->setLoopDepth(loopDepth_);
    6532           0 :     return block;
    6533             : }
    6534             : 
    6535             : AbortReasonOr<MBasicBlock*>
    6536           0 : IonBuilder::newBlockPopN(MBasicBlock* predecessor, jsbytecode* pc, uint32_t popped)
    6537             : {
    6538           0 :     MBasicBlock* block = MBasicBlock::NewPopN(graph(), info(), predecessor, bytecodeSite(pc),
    6539           0 :                                               MBasicBlock::NORMAL, popped);
    6540           0 :     if (!block)
    6541           0 :         return abort(AbortReason::Alloc);
    6542             : 
    6543           0 :     block->setLoopDepth(loopDepth_);
    6544           0 :     return block;
    6545             : }
    6546             : 
    6547             : AbortReasonOr<MBasicBlock*>
    6548           4 : IonBuilder::newBlockAfter(MBasicBlock* at, MBasicBlock* predecessor, jsbytecode* pc)
    6549             : {
    6550           4 :     MBasicBlock* block = MBasicBlock::New(graph(), &analysis(), info(), predecessor,
    6551           4 :                                           bytecodeSite(pc), MBasicBlock::NORMAL);
    6552           4 :     if (!block)
    6553           0 :         return abort(AbortReason::Alloc);
    6554             : 
    6555           4 :     block->setLoopDepth(loopDepth_);
    6556           4 :     block->setHitCount(0); // osr block
    6557           4 :     graph().insertBlockAfter(at, block);
    6558           4 :     return block;
    6559             : }
    6560             : 
    6561             : AbortReasonOr<MBasicBlock*>
    6562           4 : IonBuilder::newOsrPreheader(MBasicBlock* predecessor, jsbytecode* loopEntry,
    6563             :                             jsbytecode* beforeLoopEntry)
    6564             : {
    6565           4 :     MOZ_ASSERT(loopEntry == GetNextPc(info().osrPc()));
    6566             : 
    6567             :     // Create two blocks: one for the OSR entry with no predecessors, one for
    6568             :     // the preheader, which has the OSR entry block as a predecessor. The
    6569             :     // OSR block is always the second block (with id 1).
    6570             :     MBasicBlock* osrBlock;
    6571           4 :     MOZ_TRY_VAR(osrBlock, newBlockAfter(*graph().begin(), loopEntry));
    6572             :     MBasicBlock* preheader;
    6573           4 :     MOZ_TRY_VAR(preheader, newBlock(predecessor, loopEntry));
    6574             : 
    6575           4 :     graph().addBlock(preheader);
    6576             : 
    6577             :     // Give the pre-header the same hit count as the code before the loop.
    6578           4 :     if (script()->hasScriptCounts())
    6579           4 :         preheader->setHitCount(script()->getHitCount(beforeLoopEntry));
    6580             : 
    6581           4 :     MOsrEntry* entry = MOsrEntry::New(alloc());
    6582           4 :     osrBlock->add(entry);
    6583             : 
    6584             :     // Initialize |envChain|.
    6585             :     {
    6586           4 :         uint32_t slot = info().environmentChainSlot();
    6587             : 
    6588             :         MInstruction* envv;
    6589           4 :         if (analysis().usesEnvironmentChain()) {
    6590           3 :             envv = MOsrEnvironmentChain::New(alloc(), entry);
    6591             :         } else {
    6592             :             // Use an undefined value if the script does not need its env
    6593             :             // chain, to match the type that is already being tracked for the
    6594             :             // slot.
    6595           1 :             envv = MConstant::New(alloc(), UndefinedValue());
    6596             :         }
    6597             : 
    6598           4 :         osrBlock->add(envv);
    6599           4 :         osrBlock->initSlot(slot, envv);
    6600             :     }
    6601             :     // Initialize |return value|
    6602             :     {
    6603             :         MInstruction* returnValue;
    6604           4 :         if (!script()->noScriptRval())
    6605           4 :             returnValue = MOsrReturnValue::New(alloc(), entry);
    6606             :         else
    6607           0 :             returnValue = MConstant::New(alloc(), UndefinedValue());
    6608           4 :         osrBlock->add(returnValue);
    6609           4 :         osrBlock->initSlot(info().returnValueSlot(), returnValue);
    6610             :     }
    6611             : 
    6612             :     // Initialize arguments object.
    6613           4 :     bool needsArgsObj = info().needsArgsObj();
    6614           4 :     MInstruction* argsObj = nullptr;
    6615           4 :     if (info().hasArguments()) {
    6616           1 :         if (needsArgsObj)
    6617           0 :             argsObj = MOsrArgumentsObject::New(alloc(), entry);
    6618             :         else
    6619           1 :             argsObj = MConstant::New(alloc(), UndefinedValue());
    6620           1 :         osrBlock->add(argsObj);
    6621           1 :         osrBlock->initSlot(info().argsObjSlot(), argsObj);
    6622             :     }
    6623             : 
    6624           4 :     if (info().funMaybeLazy()) {
    6625             :         // Initialize |this| parameter.
    6626           4 :         MParameter* thisv = MParameter::New(alloc(), MParameter::THIS_SLOT, nullptr);
    6627           4 :         osrBlock->add(thisv);
    6628           4 :         osrBlock->initSlot(info().thisSlot(), thisv);
    6629             : 
    6630             :         // Initialize arguments.
    6631          17 :         for (uint32_t i = 0; i < info().nargs(); i++) {
    6632          13 :             uint32_t slot = needsArgsObj ? info().argSlotUnchecked(i) : info().argSlot(i);
    6633             : 
    6634             :             // Only grab arguments from the arguments object if the arguments object
    6635             :             // aliases formals.  If the argsobj does not alias formals, then the
    6636             :             // formals may have been assigned to during interpretation, and that change
    6637             :             // will not be reflected in the argsobj.
    6638          13 :             if (needsArgsObj && info().argsObjAliasesFormals()) {
    6639           0 :                 MOZ_ASSERT(argsObj && argsObj->isOsrArgumentsObject());
    6640             :                 // If this is an aliased formal, then the arguments object
    6641             :                 // contains a hole at this index.  Any references to this
    6642             :                 // variable in the jitcode will come from JSOP_*ALIASEDVAR
    6643             :                 // opcodes, so the slot itself can be set to undefined.  If
    6644             :                 // it's not aliased, it must be retrieved from the arguments
    6645             :                 // object.
    6646             :                 MInstruction* osrv;
    6647           0 :                 if (script()->formalIsAliased(i))
    6648           0 :                     osrv = MConstant::New(alloc(), UndefinedValue());
    6649             :                 else
    6650           0 :                     osrv = MGetArgumentsObjectArg::New(alloc(), argsObj, i);
    6651             : 
    6652           0 :                 osrBlock->add(osrv);
    6653           0 :                 osrBlock->initSlot(slot, osrv);
    6654             :             } else {
    6655          13 :                 MParameter* arg = MParameter::New(alloc(), i, nullptr);
    6656          13 :                 osrBlock->add(arg);
    6657          13 :                 osrBlock->initSlot(slot, arg);
    6658             :             }
    6659             :         }
    6660             :     }
    6661             : 
    6662             :     // Initialize locals.
    6663          62 :     for (uint32_t i = 0; i < info().nlocals(); i++) {
    6664          58 :         uint32_t slot = info().localSlot(i);
    6665          58 :         ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(i);
    6666             : 
    6667          58 :         MOsrValue* osrv = MOsrValue::New(alloc().fallible(), entry, offset);
    6668          58 :         if (!osrv)
    6669           0 :             return abort(AbortReason::Alloc);
    6670          58 :         osrBlock->add(osrv);
    6671          58 :         osrBlock->initSlot(slot, osrv);
    6672             :     }
    6673             : 
    6674             :     // Initialize stack.
    6675           4 :     uint32_t numStackSlots = preheader->stackDepth() - info().firstStackSlot();
    6676           4 :     for (uint32_t i = 0; i < numStackSlots; i++) {
    6677           0 :         uint32_t slot = info().stackSlot(i);
    6678           0 :         ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(info().nlocals() + i);
    6679             : 
    6680           0 :         MOsrValue* osrv = MOsrValue::New(alloc().fallible(), entry, offset);
    6681           0 :         if (!osrv)
    6682           0 :             return abort(AbortReason::Alloc);
    6683           0 :         osrBlock->add(osrv);
    6684           0 :         osrBlock->initSlot(slot, osrv);
    6685             :     }
    6686             : 
    6687             :     // Create an MStart to hold the first valid MResumePoint.
    6688           4 :     MStart* start = MStart::New(alloc());
    6689           4 :     osrBlock->add(start);
    6690             : 
    6691             :     // MOsrValue instructions are infallible, so the first MResumePoint must
    6692             :     // occur after they execute, at the point of the MStart.
    6693           4 :     MOZ_TRY(resumeAt(start, loopEntry));
    6694             : 
    6695             :     // Link the same MResumePoint from the MStart to each MOsrValue.
    6696             :     // This causes logic in ShouldSpecializeInput() to not replace Uses with
    6697             :     // Unboxes in the MResumePiont, so that the MStart always sees Values.
    6698           4 :     if (!osrBlock->linkOsrValues(start))
    6699           0 :         return abort(AbortReason::Alloc);
    6700             : 
    6701             :     // Clone types of the other predecessor of the pre-header to the osr block,
    6702             :     // such as pre-header phi's won't discard specialized type of the
    6703             :     // predecessor.
    6704           4 :     MOZ_ASSERT(predecessor->stackDepth() == osrBlock->stackDepth());
    6705           4 :     MOZ_ASSERT(info().environmentChainSlot() == 0);
    6706             : 
    6707             :     // Treat the OSR values as having the same type as the existing values
    6708             :     // coming in to the loop. These will be fixed up with appropriate
    6709             :     // unboxing and type barriers in finishLoop, once the possible types
    6710             :     // at the loop header are known.
    6711          79 :     for (uint32_t i = info().startArgSlot(); i < osrBlock->stackDepth(); i++) {
    6712          75 :         MDefinition* existing = current->getSlot(i);
    6713          75 :         MDefinition* def = osrBlock->getSlot(i);
    6714          75 :         MOZ_ASSERT_IF(!needsArgsObj || !info().isSlotAliased(i), def->type() == MIRType::Value);
    6715             : 
    6716             :         // Aliased slots are never accessed, since they need to go through
    6717             :         // the callobject. No need to type them here.
    6718          75 :         if (info().isSlotAliased(i))
    6719           3 :             continue;
    6720             : 
    6721          72 :         def->setResultType(existing->type());
    6722          72 :         def->setResultTypeSet(existing->resultTypeSet());
    6723             :     }
    6724             : 
    6725             :     // Finish the osrBlock.
    6726           4 :     osrBlock->end(MGoto::New(alloc(), preheader));
    6727           4 :     if (!preheader->addPredecessor(alloc(), osrBlock))
    6728           0 :         return abort(AbortReason::Alloc);
    6729           4 :     graph().setOsrBlock(osrBlock);
    6730             : 
    6731           4 :     return preheader;
    6732             : }
    6733             : 
    6734             : AbortReasonOr<MBasicBlock*>
    6735         118 : IonBuilder::newPendingLoopHeader(MBasicBlock* predecessor, jsbytecode* pc, bool osr, bool canOsr,
    6736             :                                  unsigned stackPhiCount)
    6737             : {
    6738             :     // If this site can OSR, all values on the expression stack are part of the loop.
    6739         118 :     if (canOsr)
    6740         118 :         stackPhiCount = predecessor->stackDepth() - info().firstStackSlot();
    6741             : 
    6742         118 :     MBasicBlock* block = MBasicBlock::NewPendingLoopHeader(graph(), info(), predecessor,
    6743         118 :                                                            bytecodeSite(pc), stackPhiCount);
    6744         118 :     if (!block)
    6745           0 :         return abort(AbortReason::Alloc);
    6746             : 
    6747         118 :     if (osr) {
    6748             :         // Incorporate type information from the OSR frame into the loop
    6749             :         // header. The OSR frame may have unexpected types due to type changes
    6750             :         // within the loop body or due to incomplete profiling information,
    6751             :         // in which case this may avoid restarts of loop analysis or bailouts
    6752             :         // during the OSR itself.
    6753             : 
    6754           4 :         MOZ_ASSERT(info().firstLocalSlot() - info().firstArgSlot() ==
    6755             :                    baselineFrame_->argTypes.length());
    6756           4 :         MOZ_ASSERT(block->stackDepth() - info().firstLocalSlot() ==
    6757             :                    baselineFrame_->varTypes.length());
    6758             : 
    6759             :         // Unbox the MOsrValue if it is known to be unboxable.
    6760          79 :         for (uint32_t i = info().startArgSlot(); i < block->stackDepth(); i++) {
    6761             : 
    6762             :             // The value of aliased args and slots are in the callobject. So we can't
    6763             :             // the value from the baseline frame.
    6764          75 :             if (info().isSlotAliased(i))
    6765           3 :                 continue;
    6766             : 
    6767          72 :             MPhi* phi = block->getSlot(i)->toPhi();
    6768             : 
    6769             :             // Get the type from the baseline frame.
    6770          72 :             TypeSet::Type existingType = TypeSet::UndefinedType();
    6771          72 :             uint32_t arg = i - info().firstArgSlot();
    6772          72 :             uint32_t var = i - info().firstLocalSlot();
    6773          72 :             if (info().funMaybeLazy() && i == info().thisSlot())
    6774           4 :                 existingType = baselineFrame_->thisType;
    6775          68 :             else if (arg < info().nargs())
    6776          10 :                 existingType = baselineFrame_->argTypes[arg];
    6777             :             else
    6778          58 :                 existingType = baselineFrame_->varTypes[var];
    6779             : 
    6780          72 :             if (existingType.isSingletonUnchecked())
    6781           0 :                 checkNurseryObject(existingType.singleton());
    6782             : 
    6783             :             // Extract typeset from value.
    6784          72 :             LifoAlloc* lifoAlloc = alloc().lifoAlloc();
    6785             :             TemporaryTypeSet* typeSet =
    6786          72 :                 lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc, existingType);
    6787          72 :             if (!typeSet)
    6788           0 :                 return abort(AbortReason::Alloc);
    6789          72 :             MIRType type = typeSet->getKnownMIRType();
    6790          72 :             if (!phi->addBackedgeType(alloc(), type, typeSet))
    6791           0 :                 return abort(AbortReason::Alloc);
    6792             :         }
    6793             :     }
    6794             : 
    6795         118 :     return block;
    6796             : }
    6797             : 
    6798             : MTest*
    6799         912 : IonBuilder::newTest(MDefinition* ins, MBasicBlock* ifTrue, MBasicBlock* ifFalse)
    6800             : {
    6801         912 :     MTest* test = MTest::New(alloc(), ins, ifTrue, ifFalse);
    6802         912 :     test->cacheOperandMightEmulateUndefined(constraints());
    6803         912 :     return test;
    6804             : }
    6805             : 
    6806             : // A resume point is a mapping of stack slots to MDefinitions. It is used to
    6807             : // capture the environment such that if a guard fails, and IonMonkey needs
    6808             : // to exit back to the interpreter, the interpreter state can be
    6809             : // reconstructed.
    6810             : //
    6811             : // We capture stack state at critical points:
    6812             : //   * (1) At the beginning of every basic block.
    6813             : //   * (2) After every effectful operation.
    6814             : //
    6815             : // As long as these two properties are maintained, instructions can
    6816             : // be moved, hoisted, or, eliminated without problems, and ops without side
    6817             : // effects do not need to worry about capturing state at precisely the
    6818             : // right point in time.
    6819             : //
    6820             : // Effectful instructions, of course, need to capture state after completion,
    6821             : // where the interpreter will not attempt to repeat the operation. For this,
    6822             : // ResumeAfter must be used. The state is attached directly to the effectful
    6823             : // instruction to ensure that no intermediate instructions could be injected
    6824             : // in between by a future analysis pass.
    6825             : //
    6826             : // During LIR construction, if an instruction can bail back to the interpreter,
    6827             : // we create an LSnapshot, which uses the last known resume point to request
    6828             : // register/stack assignments for every live value.
    6829             : AbortReasonOr<Ok>
    6830        4436 : IonBuilder::resume(MInstruction* ins, jsbytecode* pc, MResumePoint::Mode mode)
    6831             : {
    6832        4436 :     MOZ_ASSERT(ins->isEffectful() || !ins->isMovable());
    6833             : 
    6834        4436 :     MResumePoint* resumePoint = MResumePoint::New(alloc(), ins->block(), pc,
    6835        4436 :                                                   mode);
    6836        4436 :     if (!resumePoint)
    6837           0 :         return abort(AbortReason::Alloc);
    6838        4436 :     ins->setResumePoint(resumePoint);
    6839        4436 :     return Ok();
    6840             : }
    6841             : 
    6842             : AbortReasonOr<Ok>
    6843           4 : IonBuilder::resumeAt(MInstruction* ins, jsbytecode* pc)
    6844             : {
    6845           4 :     return resume(ins, pc, MResumePoint::ResumeAt);
    6846             : }
    6847             : 
    6848             : AbortReasonOr<Ok>
    6849        4432 : IonBuilder::resumeAfter(MInstruction* ins)
    6850             : {
    6851        4432 :     return resume(ins, pc, MResumePoint::ResumeAfter);
    6852             : }
    6853             : 
    6854             : AbortReasonOr<Ok>
    6855        1799 : IonBuilder::maybeInsertResume()
    6856             : {
    6857             :     // Create a resume point at the current position, without an existing
    6858             :     // effectful instruction. This resume point is not necessary for correct
    6859             :     // behavior (see above), but is added to avoid holding any values from the
    6860             :     // previous resume point which are now dead. This shortens the live ranges
    6861             :     // of such values and improves register allocation.
    6862             :     //
    6863             :     // This optimization is not performed outside of loop bodies, where good
    6864             :     // register allocation is not as critical, in order to avoid creating
    6865             :     // excessive resume points.
    6866             : 
    6867        1799 :     if (loopDepth_ == 0)
    6868        1005 :         return Ok();
    6869             : 
    6870         794 :     MNop* ins = MNop::New(alloc());
    6871         794 :     current->add(ins);
    6872             : 
    6873         794 :     return resumeAfter(ins);
    6874             : }
    6875             : 
    6876             : void
    6877           0 : IonBuilder::maybeMarkEmpty(MDefinition* ins)
    6878             : {
    6879           0 :     MOZ_ASSERT(ins->type() == MIRType::Value);
    6880             : 
    6881             :     // When one of the operands has no type information, mark the output
    6882             :     // as having no possible types too. This is to avoid degrading
    6883             :     // subsequent analysis.
    6884           0 :     for (size_t i = 0; i < ins->numOperands(); i++) {
    6885           0 :         if (!ins->getOperand(i)->emptyResultTypeSet())
    6886           0 :             continue;
    6887             : 
    6888           0 :         TemporaryTypeSet* types = alloc().lifoAlloc()->new_<TemporaryTypeSet>();
    6889           0 :         if (types) {
    6890           0 :             ins->setResultTypeSet(types);
    6891           0 :             return;
    6892             :         }
    6893             :     }
    6894             : }
    6895             : 
    6896             : // Return whether property lookups can be performed effectlessly on clasp.
    6897             : static bool
    6898          76 : ClassHasEffectlessLookup(const Class* clasp)
    6899             : {
    6900          76 :     return (clasp == &UnboxedPlainObject::class_) ||
    6901          76 :            (clasp == &UnboxedArrayObject::class_) ||
    6902         304 :            IsTypedObjectClass(clasp) ||
    6903         228 :            (clasp->isNative() && !clasp->getOpsLookupProperty());
    6904             : }
    6905             : 
    6906             : // Return whether an object might have a property for name which is not
    6907             : // accounted for by type information.
    6908             : static bool
    6909          55 : ObjectHasExtraOwnProperty(CompileCompartment* comp, TypeSet::ObjectKey* object, jsid id)
    6910             : {
    6911             :     // Some typed object properties are not reflected in type information.
    6912          55 :     if (object->isGroup() && object->group()->maybeTypeDescr())
    6913           0 :         return object->group()->typeDescr().hasProperty(comp->runtime()->names(), id);
    6914             : 
    6915          55 :     const Class* clasp = object->clasp();
    6916             : 
    6917             :     // Array |length| properties are not reflected in type information.
    6918          55 :     if (clasp == &ArrayObject::class_)
    6919           8 :         return JSID_IS_ATOM(id, comp->runtime()->names().length);
    6920             : 
    6921             :     // Resolve hooks can install new properties on objects on demand.
    6922          47 :     JSObject* singleton = object->isSingleton() ? object->singleton() : nullptr;
    6923          47 :     return ClassMayResolveId(comp->runtime()->names(), clasp, id, singleton);
    6924             : }
    6925             : 
    6926             : void
    6927         298 : IonBuilder::insertRecompileCheck()
    6928             : {
    6929             :     // No need for recompile checks if this is the highest optimization level.
    6930         298 :     OptimizationLevel curLevel = optimizationInfo().level();
    6931         298 :     if (IonOptimizations.isLastLevel(curLevel))
    6932         298 :         return;
    6933             : 
    6934             :     // Add recompile check.
    6935             : 
    6936             :     // Get the topmost builder. The topmost script will get recompiled when
    6937             :     // warm-up counter is high enough to justify a higher optimization level.
    6938           0 :     IonBuilder* topBuilder = outermostBuilder();
    6939             : 
    6940             :     // Add recompile check to recompile when the warm-up count reaches the
    6941             :     // threshold of the next optimization level.
    6942           0 :     OptimizationLevel nextLevel = IonOptimizations.nextLevel(curLevel);
    6943           0 :     const OptimizationInfo* info = IonOptimizations.get(nextLevel);
    6944           0 :     uint32_t warmUpThreshold = info->compilerWarmUpThreshold(topBuilder->script());
    6945           0 :     MRecompileCheck* check = MRecompileCheck::New(alloc(), topBuilder->script(), warmUpThreshold,
    6946           0 :                                 MRecompileCheck::RecompileCheck_OptimizationLevel);
    6947           0 :     current->add(check);
    6948             : }
    6949             : 
    6950             : JSObject*
    6951          22 : IonBuilder::testSingletonProperty(JSObject* obj, jsid id)
    6952             : {
    6953             :     // We would like to completely no-op property/global accesses which can
    6954             :     // produce only a particular JSObject. When indicating the access result is
    6955             :     // definitely an object, type inference does not account for the
    6956             :     // possibility that the property is entirely missing from the input object
    6957             :     // and its prototypes (if this happens, a semantic trigger would be hit and
    6958             :     // the pushed types updated, even if there is no type barrier).
    6959             :     //
    6960             :     // If the access definitely goes through obj, either directly or on the
    6961             :     // prototype chain, and the object has singleton type, then the type
    6962             :     // information for that property reflects the value that will definitely be
    6963             :     // read on accesses to the object. If the property is later deleted or
    6964             :     // reconfigured as a getter/setter then the type information for the
    6965             :     // property will change and trigger invalidation.
    6966             : 
    6967          23 :     while (obj) {
    6968          22 :         if (!ClassHasEffectlessLookup(obj->getClass()))
    6969          21 :             return nullptr;
    6970             : 
    6971          22 :         TypeSet::ObjectKey* objKey = TypeSet::ObjectKey::get(obj);
    6972          22 :         if (analysisContext)
    6973           1 :             objKey->ensureTrackedProperty(analysisContext, id);
    6974             : 
    6975          22 :         if (objKey->unknownProperties())
    6976           0 :             return nullptr;
    6977             : 
    6978          22 :         HeapTypeSetKey property = objKey->property(id);
    6979          22 :         if (property.isOwnProperty(constraints())) {
    6980          21 :             if (obj->isSingleton())
    6981          21 :                 return property.singleton(constraints());
    6982           0 :             return nullptr;
    6983             :         }
    6984             : 
    6985           1 :         if (ObjectHasExtraOwnProperty(compartment, objKey, id))
    6986           0 :             return nullptr;
    6987             : 
    6988           1 :         obj = checkNurseryObject(obj->staticPrototype());
    6989             :     }
    6990             : 
    6991           0 :     return nullptr;
    6992             : }
    6993             : 
    6994             : JSObject*
    6995          33 : IonBuilder::testSingletonPropertyTypes(MDefinition* obj, jsid id)
    6996             : {
    6997             :     // As for TestSingletonProperty, but the input is any value in a type set
    6998             :     // rather than a specific object.
    6999             : 
    7000          33 :     TemporaryTypeSet* types = obj->resultTypeSet();
    7001          33 :     if (types && types->unknownObject())
    7002           0 :         return nullptr;
    7003             : 
    7004          33 :     JSObject* objectSingleton = types ? types->maybeSingleton() : nullptr;
    7005          33 :     if (objectSingleton)
    7006           0 :         return testSingletonProperty(objectSingleton, id);
    7007             : 
    7008          33 :     MIRType objType = obj->type();
    7009          33 :     if (objType == MIRType::Value && types)
    7010           0 :         objType = types->getKnownMIRType();
    7011             : 
    7012             :     JSProtoKey key;
    7013          33 :     switch (objType) {
    7014             :       case MIRType::String:
    7015           3 :         key = JSProto_String;
    7016           3 :         break;
    7017             : 
    7018             :       case MIRType::Symbol:
    7019           0 :         key = JSProto_Symbol;
    7020           0 :         break;
    7021             : 
    7022             :       case MIRType::Int32:
    7023             :       case MIRType::Double:
    7024           0 :         key = JSProto_Number;
    7025           0 :         break;
    7026             : 
    7027             :       case MIRType::Boolean:
    7028           0 :         key = JSProto_Boolean;
    7029           0 :         break;
    7030             : 
    7031             :       case MIRType::Object: {
    7032          30 :         if (!types)
    7033           0 :             return nullptr;
    7034             : 
    7035             :         // For property accesses which may be on many objects, we just need to
    7036             :         // find a prototype common to all the objects; if that prototype
    7037             :         // has the singleton property, the access will not be on a missing property.
    7038          30 :         JSObject* singleton = nullptr;
    7039          45 :         for (unsigned i = 0; i < types->getObjectCount(); i++) {
    7040          30 :             TypeSet::ObjectKey* key = types->getObject(i);
    7041          30 :             if (!key)
    7042           0 :                 continue;
    7043          30 :             if (analysisContext)
    7044           0 :                 key->ensureTrackedProperty(analysisContext, id);
    7045             : 
    7046          30 :             const Class* clasp = key->clasp();
    7047          30 :             if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key, id))
    7048          20 :                 return nullptr;
    7049          25 :             if (key->unknownProperties())
    7050           0 :                 return nullptr;
    7051          25 :             HeapTypeSetKey property = key->property(id);
    7052          25 :             if (property.isOwnProperty(constraints()))
    7053          10 :                 return nullptr;
    7054             : 
    7055          15 :             if (JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull())) {
    7056             :                 // Test this type.
    7057          15 :                 JSObject* thisSingleton = testSingletonProperty(proto, id);
    7058          15 :                 if (!thisSingleton)
    7059           0 :                     return nullptr;
    7060          15 :                 if (singleton) {
    7061           0 :                     if (thisSingleton != singleton)
    7062           0 :                         return nullptr;
    7063             :                 } else {
    7064          15 :                     singleton = thisSingleton;
    7065             :                 }
    7066             :             } else {
    7067             :                 // Can't be on the prototype chain with no prototypes...
    7068           0 :                 return nullptr;
    7069             :             }
    7070             :         }
    7071          15 :         return singleton;
    7072             :       }
    7073             :       default:
    7074           0 :         return nullptr;
    7075             :     }
    7076             : 
    7077           3 :     JSObject* proto = GetBuiltinPrototypePure(&script()->global(), key);
    7078           3 :     if (proto)
    7079           3 :         return testSingletonProperty(proto, id);
    7080             : 
    7081           0 :     return nullptr;
    7082             : }
    7083             : 
    7084             : AbortReasonOr<bool>
    7085          10 : IonBuilder::testNotDefinedProperty(MDefinition* obj, jsid id, bool ownProperty /* = false */)
    7086             : {
    7087          10 :     TemporaryTypeSet* types = obj->resultTypeSet();
    7088          10 :     if (!types || types->unknownObject() || types->getKnownMIRType() != MIRType::Object)
    7089           1 :         return false;
    7090             : 
    7091          17 :     for (unsigned i = 0, count = types->getObjectCount(); i < count; i++) {
    7092           9 :         TypeSet::ObjectKey* key = types->getObject(i);
    7093           9 :         if (!key)
    7094           0 :             continue;
    7095             : 
    7096             :         while (true) {
    7097          21 :             if (!alloc().ensureBallast())
    7098           0 :                 return abort(AbortReason::Alloc);
    7099             : 
    7100          21 :             if (!key->hasStableClassAndProto(constraints()) || key->unknownProperties())
    7101           0 :                 return false;
    7102             : 
    7103          21 :             const Class* clasp = key->clasp();
    7104          21 :             if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key, id))
    7105           0 :                 return false;
    7106             : 
    7107             :             // If the object is a singleton, we can do a lookup now to avoid
    7108             :             // unnecessary invalidations later on, in case the property types
    7109             :             // have not yet been instantiated.
    7110          54 :             if (key->isSingleton() &&
    7111          33 :                 key->singleton()->is<NativeObject>() &&
    7112          12 :                 key->singleton()->as<NativeObject>().lookupPure(id))
    7113             :             {
    7114           0 :                 return false;
    7115             :             }
    7116             : 
    7117          21 :             HeapTypeSetKey property = key->property(id);
    7118          21 :             if (property.isOwnProperty(constraints()))
    7119           1 :                 return false;
    7120             : 
    7121             :             // If we only care about own properties don't check the proto.
    7122          20 :             if (ownProperty)
    7123           8 :                 break;
    7124             : 
    7125          20 :             JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
    7126          20 :             if (!proto)
    7127           8 :                 break;
    7128          12 :             key = TypeSet::ObjectKey::get(proto);
    7129          12 :         }
    7130             :     }
    7131             : 
    7132           8 :     return true;
    7133             : }
    7134             : 
    7135             : AbortReasonOr<Ok>
    7136        3696 : IonBuilder::pushTypeBarrier(MDefinition* def, TemporaryTypeSet* observed, BarrierKind kind)
    7137             : {
    7138        3696 :     MOZ_ASSERT(def == current->peek(-1));
    7139             : 
    7140        3696 :     MDefinition* replace = addTypeBarrier(current->pop(), observed, kind);
    7141        3696 :     if (!replace)
    7142           0 :         return abort(AbortReason::Alloc);
    7143             : 
    7144        3696 :     current->push(replace);
    7145        3696 :     return Ok();
    7146             : }
    7147             : 
    7148             : // Given an observed type set, annotates the IR as much as possible:
    7149             : // (1) If no type information is provided, the given value is returned.
    7150             : // (2) If a single type definitely exists, and no type barrier is needed,
    7151             : //     then an infallible unbox instruction is returned.
    7152             : // (3) If a type barrier is needed, but has an unknown type set, the given
    7153             : //     value is returned.
    7154             : // (4) Lastly, a type barrier instruction is added and returned.
    7155             : MDefinition*
    7156        3696 : IonBuilder::addTypeBarrier(MDefinition* def, TemporaryTypeSet* observed, BarrierKind kind,
    7157             :                            MTypeBarrier** pbarrier)
    7158             : {
    7159             :     // Barriers are never needed for instructions whose result will not be used.
    7160        3696 :     if (BytecodeIsPopped(pc))
    7161         248 :         return def;
    7162             : 
    7163             :     // If the instruction has no side effects, we'll resume the entire operation.
    7164             :     // The actual type barrier will occur in the interpreter. If the
    7165             :     // instruction is effectful, even if it has a singleton type, there
    7166             :     // must be a resume point capturing the original def, and resuming
    7167             :     // to that point will explicitly monitor the new type.
    7168        3448 :     if (kind == BarrierKind::NoBarrier) {
    7169          26 :         MDefinition* replace = ensureDefiniteType(def, observed->getKnownMIRType());
    7170          26 :         replace->setResultTypeSet(observed);
    7171          26 :         return replace;
    7172             :     }
    7173             : 
    7174        3422 :     if (observed->unknown())
    7175           0 :         return def;
    7176             : 
    7177        3422 :     MTypeBarrier* barrier = MTypeBarrier::New(alloc(), def, observed, kind);
    7178        3422 :     current->add(barrier);
    7179             : 
    7180        3422 :     if (pbarrier)
    7181           0 :         *pbarrier = barrier;
    7182             : 
    7183        3422 :     if (barrier->type() == MIRType::Undefined)
    7184          18 :         return constant(UndefinedValue());
    7185        3404 :     if (barrier->type() == MIRType::Null)
    7186           0 :         return constant(NullValue());
    7187             : 
    7188        3404 :     return barrier;
    7189             : }
    7190             : 
    7191             : AbortReasonOr<Ok>
    7192           0 : IonBuilder::pushDOMTypeBarrier(MInstruction* ins, TemporaryTypeSet* observed, JSFunction* func)
    7193             : {
    7194           0 :     MOZ_ASSERT(func && func->isNative() && func->jitInfo());
    7195             : 
    7196           0 :     const JSJitInfo* jitinfo = func->jitInfo();
    7197           0 :     bool barrier = DOMCallNeedsBarrier(jitinfo, observed);
    7198             :     // Need to be a bit careful: if jitinfo->returnType is JSVAL_TYPE_DOUBLE but
    7199             :     // types->getKnownMIRType() is MIRType::Int32, then don't unconditionally
    7200             :     // unbox as a double.  Instead, go ahead and barrier on having an int type,
    7201             :     // since we know we need a barrier anyway due to the type mismatch.  This is
    7202             :     // the only situation in which TI actually has more information about the
    7203             :     // JSValueType than codegen can, short of jitinfo->returnType just being
    7204             :     // JSVAL_TYPE_UNKNOWN.
    7205           0 :     MDefinition* replace = ins;
    7206           0 :     if (jitinfo->returnType() != JSVAL_TYPE_DOUBLE ||
    7207           0 :         observed->getKnownMIRType() != MIRType::Int32) {
    7208           0 :         replace = ensureDefiniteType(ins, MIRTypeFromValueType(jitinfo->returnType()));
    7209           0 :         if (replace != ins) {
    7210           0 :             current->pop();
    7211           0 :             current->push(replace);
    7212             :         }
    7213             :     } else {
    7214           0 :         MOZ_ASSERT(barrier);
    7215             :     }
    7216             : 
    7217             :     return pushTypeBarrier(replace, observed,
    7218           0 :                            barrier ? BarrierKind::TypeSet : BarrierKind::NoBarrier);
    7219             : }
    7220             : 
    7221             : MDefinition*
    7222         331 : IonBuilder::ensureDefiniteType(MDefinition* def, MIRType definiteType)
    7223             : {
    7224             :     MInstruction* replace;
    7225         331 :     switch (definiteType) {
    7226             :       case MIRType::Undefined:
    7227           7 :         def->setImplicitlyUsedUnchecked();
    7228           7 :         replace = MConstant::New(alloc(), UndefinedValue());
    7229           7 :         break;
    7230             : 
    7231             :       case MIRType::Null:
    7232           0 :         def->setImplicitlyUsedUnchecked();
    7233           0 :         replace = MConstant::New(alloc(), NullValue());
    7234           0 :         break;
    7235             : 
    7236             :       case MIRType::Value:
    7237         272 :         return def;
    7238             : 
    7239             :       default: {
    7240          52 :         if (def->type() != MIRType::Value) {
    7241          25 :             if (def->type() == MIRType::Int32 && definiteType == MIRType::Double) {
    7242           0 :                 replace = MToDouble::New(alloc(), def);
    7243           0 :                 break;
    7244             :             }
    7245          25 :             MOZ_ASSERT(def->type() == definiteType);
    7246          25 :             return def;
    7247             :         }
    7248          27 :         replace = MUnbox::New(alloc(), def, definiteType, MUnbox::Infallible);
    7249          27 :         break;
    7250             :       }
    7251             :     }
    7252             : 
    7253          34 :     current->add(replace);
    7254          34 :     return replace;
    7255             : }
    7256             : 
    7257             : MDefinition*
    7258           0 : IonBuilder::ensureDefiniteTypeSet(MDefinition* def, TemporaryTypeSet* types)
    7259             : {
    7260             :     // We cannot arbitrarily add a typeset to a definition. It can be shared
    7261             :     // in another path. So we always need to create a new MIR.
    7262             : 
    7263             :     // Use ensureDefiniteType to do unboxing. If that happened the type can
    7264             :     // be added on the newly created unbox operation.
    7265           0 :     MDefinition* replace = ensureDefiniteType(def, types->getKnownMIRType());
    7266           0 :     if (replace != def) {
    7267           0 :         replace->setResultTypeSet(types);
    7268           0 :         return replace;
    7269             :     }
    7270             : 
    7271             :     // Don't replace if input type is more accurate than given typeset.
    7272           0 :     if (def->type() != types->getKnownMIRType()) {
    7273           0 :         MOZ_ASSERT(types->getKnownMIRType() == MIRType::Value);
    7274           0 :         return def;
    7275             :     }
    7276             : 
    7277             :     // Create a NOP mir instruction to filter the typeset.
    7278           0 :     MFilterTypeSet* filter = MFilterTypeSet::New(alloc(), def, types);
    7279           0 :     current->add(filter);
    7280           0 :     return filter;
    7281             : }
    7282             : 
    7283             : static size_t
    7284           0 : NumFixedSlots(JSObject* object)
    7285             : {
    7286             :     // Note: we can't use object->numFixedSlots() here, as this will read the
    7287             :     // shape and can race with the active thread if we are building off thread.
    7288             :     // The allocation kind and object class (which goes through the type) can
    7289             :     // be read freely, however.
    7290           0 :     gc::AllocKind kind = object->asTenured().getAllocKind();
    7291           0 :     return gc::GetGCKindSlots(kind, object->getClass());
    7292             : }
    7293             : 
    7294             : static bool
    7295           4 : IsUninitializedGlobalLexicalSlot(JSObject* obj, PropertyName* name)
    7296             : {
    7297           4 :     LexicalEnvironmentObject &globalLexical = obj->as<LexicalEnvironmentObject>();
    7298           4 :     MOZ_ASSERT(globalLexical.isGlobal());
    7299           4 :     Shape* shape = globalLexical.lookupPure(name);
    7300           4 :     if (!shape)
    7301           0 :         return false;
    7302           4 :     return globalLexical.getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL);
    7303             : }
    7304             : 
    7305             : AbortReasonOr<Ok>
    7306          45 : IonBuilder::getStaticName(bool* emitted, JSObject* staticObject, PropertyName* name,
    7307             :                           MDefinition* lexicalCheck)
    7308             : {
    7309          45 :     MOZ_ASSERT(*emitted == false);
    7310             : 
    7311          45 :     jsid id = NameToId(name);
    7312             : 
    7313          56 :     bool isGlobalLexical = staticObject->is<LexicalEnvironmentObject>() &&
    7314          56 :                            staticObject->as<LexicalEnvironmentObject>().isGlobal();
    7315          45 :     MOZ_ASSERT(isGlobalLexical ||
    7316             :                staticObject->is<GlobalObject>() ||
    7317             :                staticObject->is<CallObject>() ||
    7318             :                staticObject->is<ModuleEnvironmentObject>());
    7319          45 :     MOZ_ASSERT(staticObject->isSingleton());
    7320             : 
    7321             :     // Always emit the lexical check. This could be optimized, but is
    7322             :     // currently not for simplicity's sake.
    7323          45 :     if (lexicalCheck)
    7324           0 :         return Ok();
    7325             : 
    7326          45 :     TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(staticObject);
    7327          45 :     if (analysisContext)
    7328           1 :         staticKey->ensureTrackedProperty(analysisContext, NameToId(name));
    7329             : 
    7330          45 :     if (staticKey->unknownProperties())
    7331           0 :         return Ok();
    7332             : 
    7333          45 :     HeapTypeSetKey property = staticKey->property(id);
    7334          99 :     if (!property.maybeTypes() ||
    7335          54 :         !property.maybeTypes()->definiteProperty() ||
    7336           9 :         property.nonData(constraints()))
    7337             :     {
    7338             :         // The property has been reconfigured as non-configurable, non-enumerable
    7339             :         // or non-writable.
    7340          36 :         return Ok();
    7341             :     }
    7342             : 
    7343             :     // Don't optimize global lexical bindings if they aren't initialized at
    7344             :     // compile time.
    7345           9 :     if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name))
    7346           0 :         return Ok();
    7347             : 
    7348           9 :     *emitted = true;
    7349             : 
    7350           9 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    7351           9 :     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey,
    7352           9 :                                                        name, types, /* updateObserved = */ true);
    7353             : 
    7354           9 :     if (barrier == BarrierKind::NoBarrier) {
    7355             :         // Try to inline properties holding a known constant object.
    7356           9 :         JSObject* singleton = types->maybeSingleton();
    7357           9 :         if (singleton) {
    7358           3 :             if (testSingletonProperty(staticObject, id) == singleton) {
    7359           3 :                 pushConstant(ObjectValue(*singleton));
    7360           3 :                 return Ok();
    7361             :             }
    7362             :         }
    7363             : 
    7364             :         // Try to inline properties that have never been overwritten.
    7365           6 :         Value constantValue;
    7366           6 :         if (property.constant(constraints(), &constantValue)) {
    7367           6 :             pushConstant(constantValue);
    7368           6 :             return Ok();
    7369             :         }
    7370             :     }
    7371             : 
    7372           0 :     MOZ_TRY(loadStaticSlot(staticObject, barrier, types, property.maybeTypes()->definiteSlot()));
    7373             : 
    7374           0 :     return Ok();
    7375             : }
    7376             : 
    7377             : AbortReasonOr<Ok>
    7378           0 : IonBuilder::loadStaticSlot(JSObject* staticObject, BarrierKind barrier, TemporaryTypeSet* types,
    7379             :                            uint32_t slot)
    7380             : {
    7381           0 :     if (barrier == BarrierKind::NoBarrier) {
    7382             :         // Try to inline properties that can only have one value.
    7383           0 :         MIRType knownType = types->getKnownMIRType();
    7384           0 :         if (knownType == MIRType::Undefined) {
    7385           0 :             pushConstant(UndefinedValue());
    7386           0 :             return Ok();
    7387             :         }
    7388           0 :         if (knownType == MIRType::Null) {
    7389           0 :             pushConstant(NullValue());
    7390           0 :             return Ok();
    7391             :         }
    7392             :     }
    7393             : 
    7394           0 :     MInstruction* obj = constant(ObjectValue(*staticObject));
    7395             : 
    7396           0 :     MIRType rvalType = types->getKnownMIRType();
    7397           0 :     if (barrier != BarrierKind::NoBarrier)
    7398           0 :         rvalType = MIRType::Value;
    7399             : 
    7400           0 :     return loadSlot(obj, slot, NumFixedSlots(staticObject), rvalType, barrier, types);
    7401             : }
    7402             : 
    7403             : // Whether a write of the given value may need a post-write barrier for GC purposes.
    7404             : bool
    7405         162 : jit::NeedsPostBarrier(MDefinition* value)
    7406             : {
    7407         162 :     if (!GetJitContext()->compartment->zone()->nurseryExists())
    7408           0 :         return false;
    7409         162 :     return value->mightBeType(MIRType::Object);
    7410             : }
    7411             : 
    7412             : AbortReasonOr<Ok>
    7413           3 : IonBuilder::setStaticName(JSObject* staticObject, PropertyName* name)
    7414             : {
    7415           3 :     jsid id = NameToId(name);
    7416             : 
    7417           3 :     bool isGlobalLexical = staticObject->is<LexicalEnvironmentObject>() &&
    7418           3 :                            staticObject->as<LexicalEnvironmentObject>().isGlobal();
    7419           3 :     MOZ_ASSERT(isGlobalLexical ||
    7420             :                staticObject->is<GlobalObject>() ||
    7421             :                staticObject->is<CallObject>());
    7422             : 
    7423           3 :     MDefinition* value = current->peek(-1);
    7424             : 
    7425           3 :     TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(staticObject);
    7426           3 :     if (staticKey->unknownProperties())
    7427           0 :         return jsop_setprop(name);
    7428             : 
    7429           3 :     HeapTypeSetKey property = staticKey->property(id);
    7430           6 :     if (!property.maybeTypes() ||
    7431           0 :         !property.maybeTypes()->definiteProperty() ||
    7432           3 :         property.nonData(constraints()) ||
    7433           0 :         property.nonWritable(constraints()))
    7434             :     {
    7435             :         // The property has been reconfigured as non-configurable, non-enumerable
    7436             :         // or non-writable.
    7437           3 :         return jsop_setprop(name);
    7438             :     }
    7439             : 
    7440           0 :     if (!CanWriteProperty(alloc(), constraints(), property, value))
    7441           0 :         return jsop_setprop(name);
    7442             : 
    7443             :     // Don't optimize global lexical bindings if they aren't initialized at
    7444             :     // compile time.
    7445           0 :     if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name))
    7446           0 :         return jsop_setprop(name);
    7447             : 
    7448           0 :     current->pop();
    7449             : 
    7450             :     // Pop the bound object on the stack.
    7451           0 :     MDefinition* obj = current->pop();
    7452           0 :     MOZ_ASSERT(&obj->toConstant()->toObject() == staticObject);
    7453             : 
    7454           0 :     if (NeedsPostBarrier(value))
    7455           0 :         current->add(MPostWriteBarrier::New(alloc(), obj, value));
    7456             : 
    7457             :     // If the property has a known type, we may be able to optimize typed stores by not
    7458             :     // storing the type tag.
    7459           0 :     MIRType slotType = MIRType::None;
    7460           0 :     MIRType knownType = property.knownMIRType(constraints());
    7461           0 :     if (knownType != MIRType::Value)
    7462           0 :         slotType = knownType;
    7463             : 
    7464           0 :     bool needsPreBarrier = property.needsBarrier(constraints());
    7465           0 :     return storeSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
    7466           0 :                      value, needsPreBarrier, slotType);
    7467             : }
    7468             : 
    7469             : JSObject*
    7470          51 : IonBuilder::testGlobalLexicalBinding(PropertyName* name)
    7471             : {
    7472          51 :     MOZ_ASSERT(JSOp(*pc) == JSOP_BINDGNAME ||
    7473             :                JSOp(*pc) == JSOP_GETGNAME ||
    7474             :                JSOp(*pc) == JSOP_SETGNAME ||
    7475             :                JSOp(*pc) == JSOP_STRICTSETGNAME);
    7476             : 
    7477             :     // The global isn't the global lexical env's prototype, but its enclosing
    7478             :     // env. Test for the existence of |name| manually on the global lexical
    7479             :     // env. If it is not found, look for it on the global itself.
    7480             : 
    7481          51 :     NativeObject* obj = &script()->global().lexicalEnvironment();
    7482          51 :     TypeSet::ObjectKey* lexicalKey = TypeSet::ObjectKey::get(obj);
    7483          51 :     jsid id = NameToId(name);
    7484          51 :     if (analysisContext)
    7485           1 :         lexicalKey->ensureTrackedProperty(analysisContext, id);
    7486             : 
    7487             :     // If the property is not found on the global lexical env but it is found
    7488             :     // on the global and is configurable, try to freeze the typeset for its
    7489             :     // non-existence.  If we don't have type information then fail.
    7490             :     //
    7491             :     // In the case that it is found on the global but is non-configurable,
    7492             :     // the binding cannot be shadowed by a global lexical binding.
    7493         102 :     Maybe<HeapTypeSetKey> lexicalProperty;
    7494          51 :     if (!lexicalKey->unknownProperties())
    7495          51 :         lexicalProperty.emplace(lexicalKey->property(id));
    7496          51 :     Shape* shape = obj->lookupPure(name);
    7497          51 :     if (shape) {
    7498          22 :         if ((JSOp(*pc) != JSOP_GETGNAME && !shape->writable()) ||
    7499          11 :             obj->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
    7500             :         {
    7501           0 :             return nullptr;
    7502             :         }
    7503             :     } else {
    7504          40 :         shape = script()->global().lookupPure(name);
    7505          40 :         if (!shape || shape->configurable()) {
    7506          13 :             if (lexicalProperty.isSome())
    7507          13 :                 MOZ_ALWAYS_FALSE(lexicalProperty->isOwnProperty(constraints()));
    7508             :             else
    7509           0 :                 return nullptr;
    7510             :         }
    7511          40 :         obj = &script()->global();
    7512             :     }
    7513             : 
    7514          51 :     return obj;
    7515             : }
    7516             : 
    7517             : AbortReasonOr<Ok>
    7518          45 : IonBuilder::jsop_getgname(PropertyName* name)
    7519             : {
    7520             :     // Optimize undefined/NaN/Infinity first. We must ensure we handle these
    7521             :     // cases *exactly* like Baseline, because it's invalid to add an Ion IC or
    7522             :     // VM call (that might trigger invalidation) if there's no Baseline IC for
    7523             :     // this op.
    7524          45 :     if (name == names().undefined) {
    7525           0 :         pushConstant(UndefinedValue());
    7526           0 :         return Ok();
    7527             :     }
    7528          45 :     if (name == names().NaN) {
    7529           0 :         pushConstant(compartment->runtime()->NaNValue());
    7530           0 :         return Ok();
    7531             :     }
    7532          45 :     if (name == names().Infinity) {
    7533           0 :         pushConstant(compartment->runtime()->positiveInfinityValue());
    7534           0 :         return Ok();
    7535             :     }
    7536             : 
    7537          45 :     if (JSObject* obj = testGlobalLexicalBinding(name)) {
    7538          45 :         bool emitted = false;
    7539          45 :         MOZ_TRY(getStaticName(&emitted, obj, name));
    7540          45 :         if (emitted)
    7541           9 :             return Ok();
    7542             : 
    7543          36 :         if (!forceInlineCaches() && obj->is<GlobalObject>()) {
    7544          29 :             TemporaryTypeSet* types = bytecodeTypes(pc);
    7545          29 :             MDefinition* globalObj = constant(ObjectValue(*obj));
    7546          29 :             MOZ_TRY(getPropTryCommonGetter(&emitted, globalObj, name, types));
    7547          29 :             if (emitted)
    7548           0 :                 return Ok();
    7549             :         }
    7550             :     }
    7551             : 
    7552          36 :     return jsop_getname(name);
    7553             : }
    7554             : 
    7555             : AbortReasonOr<Ok>
    7556          42 : IonBuilder::jsop_getname(PropertyName* name)
    7557             : {
    7558             :     MDefinition* object;
    7559          42 :     if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope())
    7560          36 :         object = constant(ObjectValue(script()->global().lexicalEnvironment()));
    7561             :     else
    7562           6 :         object = current->environmentChain();
    7563             : 
    7564          42 :     MGetNameCache* ins = MGetNameCache::New(alloc(), object);
    7565          42 :     current->add(ins);
    7566          42 :     current->push(ins);
    7567             : 
    7568          42 :     MOZ_TRY(resumeAfter(ins));
    7569             : 
    7570          42 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    7571          42 :     return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
    7572             : }
    7573             : 
    7574             : AbortReasonOr<Ok>
    7575         867 : IonBuilder::jsop_intrinsic(PropertyName* name)
    7576             : {
    7577         867 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    7578             : 
    7579         867 :     Value vp = UndefinedValue();
    7580             :     // If the intrinsic value doesn't yet exist, we haven't executed this
    7581             :     // opcode yet, so we need to get it and monitor the result.
    7582         867 :     if (!script()->global().maybeExistingIntrinsicValue(name, &vp)) {
    7583         506 :         MCallGetIntrinsicValue* ins = MCallGetIntrinsicValue::New(alloc(), name);
    7584             : 
    7585         506 :         current->add(ins);
    7586         506 :         current->push(ins);
    7587             : 
    7588         506 :         MOZ_TRY(resumeAfter(ins));
    7589             : 
    7590         506 :         return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
    7591             :     }
    7592             : 
    7593         361 :     if (types->empty())
    7594         235 :         types->addType(TypeSet::GetValueType(vp), alloc().lifoAlloc());
    7595             : 
    7596             :     // Bake in the intrinsic, guaranteed to exist because a non-empty typeset
    7597             :     // means the intrinsic was successfully gotten in the VM call above.
    7598             :     // Assert that TI agrees with us on the type.
    7599         361 :     MOZ_ASSERT(types->hasType(TypeSet::GetValueType(vp)));
    7600             : 
    7601         361 :     pushConstant(vp);
    7602         361 :     return Ok();
    7603             : }
    7604             : 
    7605             : AbortReasonOr<Ok>
    7606           0 : IonBuilder::jsop_getimport(PropertyName* name)
    7607             : {
    7608           0 :     ModuleEnvironmentObject* env = GetModuleEnvironmentForScript(script());
    7609           0 :     MOZ_ASSERT(env);
    7610             : 
    7611             :     Shape* shape;
    7612             :     ModuleEnvironmentObject* targetEnv;
    7613           0 :     MOZ_ALWAYS_TRUE(env->lookupImport(NameToId(name), &targetEnv, &shape));
    7614             : 
    7615           0 :     PropertyName* localName = JSID_TO_STRING(shape->propid())->asAtom().asPropertyName();
    7616           0 :     bool emitted = false;
    7617           0 :     MOZ_TRY(getStaticName(&emitted, targetEnv, localName));
    7618             : 
    7619           0 :     if (!emitted) {
    7620             :         // This can happen if we don't have type information.
    7621           0 :         TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(targetEnv);
    7622           0 :         TemporaryTypeSet* types = bytecodeTypes(pc);
    7623           0 :         BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey,
    7624           0 :                                                            name, types, /* updateObserved = */ true);
    7625             : 
    7626           0 :         MOZ_TRY(loadStaticSlot(targetEnv, barrier, types, shape->slot()));
    7627             :     }
    7628             : 
    7629             :     // In the rare case where this import hasn't been initialized already (we
    7630             :     // have an import cycle where modules reference each other's imports), emit
    7631             :     // a check.
    7632           0 :     if (targetEnv->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) {
    7633             :         MDefinition* checked;
    7634           0 :         MOZ_TRY_VAR(checked, addLexicalCheck(current->pop()));
    7635           0 :         current->push(checked);
    7636             :     }
    7637             : 
    7638           0 :     return Ok();
    7639             : }
    7640             : 
    7641             : AbortReasonOr<Ok>
    7642           0 : IonBuilder::jsop_bindname(PropertyName* name)
    7643             : {
    7644             :     MDefinition* envChain;
    7645           0 :     if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope())
    7646           0 :         envChain = constant(ObjectValue(script()->global().lexicalEnvironment()));
    7647             :     else
    7648           0 :         envChain = current->environmentChain();
    7649             : 
    7650           0 :     MBindNameCache* ins = MBindNameCache::New(alloc(), envChain, name, script(), pc);
    7651           0 :     current->add(ins);
    7652           0 :     current->push(ins);
    7653             : 
    7654           0 :     return resumeAfter(ins);
    7655             : }
    7656             : 
    7657             : AbortReasonOr<Ok>
    7658           0 : IonBuilder::jsop_bindvar()
    7659             : {
    7660           0 :     MOZ_ASSERT(analysis().usesEnvironmentChain());
    7661           0 :     MCallBindVar* ins = MCallBindVar::New(alloc(), current->environmentChain());
    7662           0 :     current->add(ins);
    7663           0 :     current->push(ins);
    7664           0 :     return Ok();
    7665             : }
    7666             : 
    7667             : static MIRType
    7668           1 : GetElemKnownType(bool needsHoleCheck, TemporaryTypeSet* types)
    7669             : {
    7670           1 :     MIRType knownType = types->getKnownMIRType();
    7671             : 
    7672             :     // Null and undefined have no payload so they can't be specialized.
    7673             :     // Since folding null/undefined while building SSA is not safe (see the
    7674             :     // comment in IsPhiObservable), we just add an untyped load instruction
    7675             :     // and rely on pushTypeBarrier and DCE to replace it with a null/undefined
    7676             :     // constant.
    7677           1 :     if (knownType == MIRType::Undefined || knownType == MIRType::Null)
    7678           0 :         knownType = MIRType::Value;
    7679             : 
    7680             :     // Different architectures may want typed element reads which require
    7681             :     // hole checks to be done as either value or typed reads.
    7682           1 :     if (needsHoleCheck && !LIRGenerator::allowTypedElementHoleCheck())
    7683           0 :         knownType = MIRType::Value;
    7684             : 
    7685           1 :     return knownType;
    7686             : }
    7687             : 
    7688             : AbortReasonOr<Ok>
    7689         898 : IonBuilder::jsop_getelem()
    7690             : {
    7691         898 :     startTrackingOptimizations();
    7692             : 
    7693         898 :     MDefinition* index = current->pop();
    7694         898 :     MDefinition* obj = current->pop();
    7695             : 
    7696         898 :     trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet());
    7697         898 :     trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet());
    7698             : 
    7699             :     // Always use a call if we are performing analysis and not actually
    7700             :     // emitting code, to simplify later analysis.
    7701         898 :     if (info().isAnalysis() || shouldAbortOnPreliminaryGroups(obj)) {
    7702         867 :         MInstruction* ins = MCallGetElement::New(alloc(), obj, index);
    7703             : 
    7704         867 :         current->add(ins);
    7705         867 :         current->push(ins);
    7706             : 
    7707         867 :         MOZ_TRY(resumeAfter(ins));
    7708             : 
    7709         867 :         TemporaryTypeSet* types = bytecodeTypes(pc);
    7710         867 :         return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
    7711             :     }
    7712             : 
    7713          31 :     bool emitted = false;
    7714             : 
    7715             :     // Handle lazy-arguments first. We have to do this even if forceInlineCaches
    7716             :     // is true (lazy arguments cannot escape to the IC). Like the code in
    7717             :     // IonBuilder::jsop_getprop, we only do this if we're not in analysis mode,
    7718             :     // to avoid unnecessary analysis aborts.
    7719          31 :     if (obj->mightBeType(MIRType::MagicOptimizedArguments) && !info().isAnalysis()) {
    7720           1 :         trackOptimizationAttempt(TrackedStrategy::GetElem_Arguments);
    7721           1 :         MOZ_TRY(getElemTryArguments(&emitted, obj, index));
    7722           1 :         if (emitted)
    7723           1 :             return Ok();
    7724             : 
    7725           0 :         trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlined);
    7726           0 :         MOZ_TRY(getElemTryArgumentsInlined(&emitted, obj, index));
    7727           0 :         if (emitted)
    7728           0 :             return Ok();
    7729             : 
    7730           0 :         if (script()->argumentsHasVarBinding())
    7731           0 :             return abort(AbortReason::Disable, "Type is not definitely lazy arguments.");
    7732             :     }
    7733             : 
    7734          30 :     obj = maybeUnboxForPropertyAccess(obj);
    7735          30 :     if (obj->type() == MIRType::Object)
    7736          27 :         obj = convertUnboxedObjects(obj);
    7737             : 
    7738          30 :     if (!forceInlineCaches()) {
    7739             :         // Note: no trackOptimizationAttempt call is needed, getElemTryGetProp
    7740             :         // will call it.
    7741          30 :         MOZ_TRY(getElemTryGetProp(&emitted, obj, index));
    7742          30 :         if (emitted)
    7743           3 :             return Ok();
    7744             : 
    7745          27 :         trackOptimizationAttempt(TrackedStrategy::GetElem_Dense);
    7746          27 :         MOZ_TRY(getElemTryDense(&emitted, obj, index));
    7747          27 :         if (emitted)
    7748           1 :             return Ok();
    7749             : 
    7750          26 :         trackOptimizationAttempt(TrackedStrategy::GetElem_TypedStatic);
    7751          26 :         MOZ_TRY(getElemTryTypedStatic(&emitted, obj, index));
    7752          26 :         if (emitted)
    7753           0 :             return Ok();
    7754             : 
    7755          26 :         trackOptimizationAttempt(TrackedStrategy::GetElem_TypedArray);
    7756          26 :         MOZ_TRY(getElemTryTypedArray(&emitted, obj, index));
    7757          26 :         if (emitted)
    7758           0 :             return Ok();
    7759             : 
    7760          26 :         trackOptimizationAttempt(TrackedStrategy::GetElem_String);
    7761          26 :         MOZ_TRY(getElemTryString(&emitted, obj, index));
    7762          26 :         if (emitted)
    7763           3 :             return Ok();
    7764             : 
    7765          23 :         trackOptimizationAttempt(TrackedStrategy::GetElem_TypedObject);
    7766          23 :         MOZ_TRY(getElemTryTypedObject(&emitted, obj, index));
    7767          23 :         if (emitted)
    7768           0 :             return Ok();
    7769             :     }
    7770             : 
    7771          23 :     trackOptimizationAttempt(TrackedStrategy::GetElem_InlineCache);
    7772          23 :     return getElemAddCache(obj, index);
    7773             : }
    7774             : 
    7775             : AbortReasonOr<Ok>
    7776          23 : IonBuilder::getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index)
    7777             : {
    7778          23 :     MOZ_ASSERT(*emitted == false);
    7779             : 
    7780             :     // The next several failures are all due to types not predicting that we
    7781             :     // are definitely doing a getelem access on a typed object.
    7782          23 :     trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject);
    7783             : 
    7784          23 :     TypedObjectPrediction objPrediction = typedObjectPrediction(obj);
    7785          23 :     if (objPrediction.isUseless())
    7786          23 :         return Ok();
    7787             : 
    7788           0 :     if (!objPrediction.ofArrayKind())
    7789           0 :         return Ok();
    7790             : 
    7791           0 :     TypedObjectPrediction elemPrediction = objPrediction.arrayElementType();
    7792           0 :     if (elemPrediction.isUseless())
    7793           0 :         return Ok();
    7794             : 
    7795             :     uint32_t elemSize;
    7796           0 :     if (!elemPrediction.hasKnownSize(&elemSize))
    7797           0 :         return Ok();
    7798             : 
    7799           0 :     switch (elemPrediction.kind()) {
    7800             :       case type::Simd:
    7801             :         // FIXME (bug 894105): load into a MIRType::float32x4 etc
    7802           0 :         trackOptimizationOutcome(TrackedOutcome::GenericFailure);
    7803           0 :         return Ok();
    7804             : 
    7805             :       case type::Struct:
    7806             :       case type::Array:
    7807             :         return getElemTryComplexElemOfTypedObject(emitted,
    7808             :                                                   obj,
    7809             :                                                   index,
    7810             :                                                   objPrediction,
    7811             :                                                   elemPrediction,
    7812           0 :                                                   elemSize);
    7813             :       case type::Scalar:
    7814             :         return getElemTryScalarElemOfTypedObject(emitted,
    7815             :                                                  obj,
    7816             :                                                  index,
    7817             :                                                  objPrediction,
    7818             :                                                  elemPrediction,
    7819           0 :                                                  elemSize);
    7820             : 
    7821             :       case type::Reference:
    7822             :         return getElemTryReferenceElemOfTypedObject(emitted,
    7823             :                                                     obj,
    7824             :                                                     index,
    7825             :                                                     objPrediction,
    7826           0 :                                                     elemPrediction);
    7827             :     }
    7828             : 
    7829           0 :     MOZ_CRASH("Bad kind");
    7830             : }
    7831             : 
    7832             : static MIRType
    7833             : MIRTypeForTypedArrayRead(Scalar::Type arrayType, bool observedDouble);
    7834             : 
    7835             : bool
    7836           0 : IonBuilder::checkTypedObjectIndexInBounds(uint32_t elemSize,
    7837             :                                           MDefinition* obj,
    7838             :                                           MDefinition* index,
    7839             :                                           TypedObjectPrediction objPrediction,
    7840             :                                           LinearSum* indexAsByteOffset)
    7841             : {
    7842             :     // Ensure index is an integer.
    7843           0 :     MInstruction* idInt32 = MToInt32::New(alloc(), index);
    7844           0 :     current->add(idInt32);
    7845             : 
    7846             :     // If we know the length statically from the type, just embed it.
    7847             :     // Otherwise, load it from the appropriate reserved slot on the
    7848             :     // typed object.  We know it's an int32, so we can convert from
    7849             :     // Value to int32 using truncation.
    7850             :     int32_t lenOfAll;
    7851             :     MDefinition* length;
    7852           0 :     if (objPrediction.hasKnownArrayLength(&lenOfAll)) {
    7853           0 :         length = constantInt(lenOfAll);
    7854             : 
    7855             :         // If we are not loading the length from the object itself, only
    7856             :         // optimize if the array buffer can never be a detached array buffer.
    7857           0 :         TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
    7858           0 :         if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER)) {
    7859           0 :             trackOptimizationOutcome(TrackedOutcome::TypedObjectHasDetachedBuffer);
    7860           0 :             return false;
    7861             :         }
    7862             :     } else {
    7863           0 :         trackOptimizationOutcome(TrackedOutcome::TypedObjectArrayRange);
    7864           0 :         return false;
    7865             :     }
    7866             : 
    7867           0 :     index = addBoundsCheck(idInt32, length);
    7868             : 
    7869           0 :     return indexAsByteOffset->add(index, AssertedCast<int32_t>(elemSize));
    7870             : }
    7871             : 
    7872             : AbortReasonOr<Ok>
    7873           0 : IonBuilder::getElemTryScalarElemOfTypedObject(bool* emitted,
    7874             :                                               MDefinition* obj,
    7875             :                                               MDefinition* index,
    7876             :                                               TypedObjectPrediction objPrediction,
    7877             :                                               TypedObjectPrediction elemPrediction,
    7878             :                                               uint32_t elemSize)
    7879             : {
    7880           0 :     MOZ_ASSERT(objPrediction.ofArrayKind());
    7881             : 
    7882             :     // Must always be loading the same scalar type
    7883           0 :     ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
    7884           0 :     MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
    7885             : 
    7886           0 :     LinearSum indexAsByteOffset(alloc());
    7887           0 :     if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
    7888           0 :         return Ok();
    7889             : 
    7890           0 :     trackOptimizationSuccess();
    7891           0 :     *emitted = true;
    7892             : 
    7893           0 :     return pushScalarLoadFromTypedObject(obj, indexAsByteOffset, elemType);
    7894             : }
    7895             : 
    7896             : AbortReasonOr<Ok>
    7897           0 : IonBuilder::getElemTryReferenceElemOfTypedObject(bool* emitted,
    7898             :                                                  MDefinition* obj,
    7899             :                                                  MDefinition* index,
    7900             :                                                  TypedObjectPrediction objPrediction,
    7901             :                                                  TypedObjectPrediction elemPrediction)
    7902             : {
    7903           0 :     MOZ_ASSERT(objPrediction.ofArrayKind());
    7904             : 
    7905           0 :     ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
    7906           0 :     uint32_t elemSize = ReferenceTypeDescr::size(elemType);
    7907             : 
    7908           0 :     LinearSum indexAsByteOffset(alloc());
    7909           0 :     if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
    7910           0 :         return Ok();
    7911             : 
    7912           0 :     trackOptimizationSuccess();
    7913           0 :     *emitted = true;
    7914             : 
    7915           0 :     return pushReferenceLoadFromTypedObject(obj, indexAsByteOffset, elemType, nullptr);
    7916             : }
    7917             : 
    7918             : AbortReasonOr<Ok>
    7919           0 : IonBuilder::pushScalarLoadFromTypedObject(MDefinition* obj,
    7920             :                                           const LinearSum& byteOffset,
    7921             :                                           ScalarTypeDescr::Type elemType)
    7922             : {
    7923           0 :     uint32_t size = ScalarTypeDescr::size(elemType);
    7924           0 :     MOZ_ASSERT(size == ScalarTypeDescr::alignment(elemType));
    7925             : 
    7926             :     // Find location within the owner object.
    7927             :     MDefinition* elements;
    7928             :     MDefinition* scaledOffset;
    7929             :     int32_t adjustment;
    7930           0 :     MOZ_TRY(loadTypedObjectElements(obj, byteOffset, size, &elements, &scaledOffset, &adjustment));
    7931             : 
    7932             :     // Load the element.
    7933           0 :     MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset,
    7934             :                                                        elemType,
    7935             :                                                        DoesNotRequireMemoryBarrier,
    7936           0 :                                                        adjustment);
    7937           0 :     current->add(load);
    7938           0 :     current->push(load);
    7939             : 
    7940             :     // If we are reading in-bounds elements, we can use knowledge about
    7941             :     // the array type to determine the result type, even if the opcode has
    7942             :     // never executed. The known pushed type is only used to distinguish
    7943             :     // uint32 reads that may produce either doubles or integers.
    7944           0 :     TemporaryTypeSet* resultTypes = bytecodeTypes(pc);
    7945           0 :     bool allowDouble = resultTypes->hasType(TypeSet::DoubleType());
    7946             : 
    7947             :     // Note: knownType is not necessarily in resultTypes; e.g. if we
    7948             :     // have only observed integers coming out of float array.
    7949           0 :     MIRType knownType = MIRTypeForTypedArrayRead(elemType, allowDouble);
    7950             : 
    7951             :     // Note: we can ignore the type barrier here, we know the type must
    7952             :     // be valid and unbarriered. Also, need not set resultTypeSet,
    7953             :     // because knownType is scalar and a resultTypeSet would provide
    7954             :     // no useful additional info.
    7955           0 :     load->setResultType(knownType);
    7956             : 
    7957           0 :     return Ok();
    7958             : }
    7959             : 
    7960             : AbortReasonOr<Ok>
    7961           0 : IonBuilder::pushReferenceLoadFromTypedObject(MDefinition* typedObj,
    7962             :                                              const LinearSum& byteOffset,
    7963             :                                              ReferenceTypeDescr::Type type,
    7964             :                                              PropertyName* name)
    7965             : {
    7966             :     // Find location within the owner object.
    7967             :     MDefinition* elements;
    7968             :     MDefinition* scaledOffset;
    7969             :     int32_t adjustment;
    7970           0 :     uint32_t alignment = ReferenceTypeDescr::alignment(type);
    7971           0 :     MOZ_TRY(loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset, &adjustment));
    7972             : 
    7973           0 :     TemporaryTypeSet* observedTypes = bytecodeTypes(pc);
    7974             : 
    7975           0 :     MInstruction* load = nullptr;  // initialize to silence GCC warning
    7976           0 :     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
    7977           0 :                                                        typedObj, name, observedTypes);
    7978             : 
    7979           0 :     switch (type) {
    7980             :       case ReferenceTypeDescr::TYPE_ANY: {
    7981             :         // Make sure the barrier reflects the possibility of reading undefined.
    7982           0 :         bool bailOnUndefined = barrier == BarrierKind::NoBarrier &&
    7983           0 :                                !observedTypes->hasType(TypeSet::UndefinedType());
    7984           0 :         if (bailOnUndefined)
    7985           0 :             barrier = BarrierKind::TypeTagOnly;
    7986           0 :         load = MLoadElement::New(alloc(), elements, scaledOffset, false, false, adjustment);
    7987           0 :         break;
    7988             :       }
    7989             :       case ReferenceTypeDescr::TYPE_OBJECT: {
    7990             :         // Make sure the barrier reflects the possibility of reading null. When
    7991             :         // there is no other barrier needed we include the null bailout with
    7992             :         // MLoadUnboxedObjectOrNull, which avoids the need to box the result
    7993             :         // for a type barrier instruction.
    7994             :         MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
    7995           0 :         if (barrier == BarrierKind::NoBarrier && !observedTypes->hasType(TypeSet::NullType()))
    7996           0 :             nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull;
    7997             :         else
    7998           0 :             nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
    7999           0 :         load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, nullBehavior,
    8000           0 :                                              adjustment);
    8001           0 :         break;
    8002             :       }
    8003             :       case ReferenceTypeDescr::TYPE_STRING: {
    8004           0 :         load = MLoadUnboxedString::New(alloc(), elements, scaledOffset, adjustment);
    8005           0 :         observedTypes->addType(TypeSet::StringType(), alloc().lifoAlloc());
    8006           0 :         break;
    8007             :       }
    8008             :     }
    8009             : 
    8010           0 :     current->add(load);
    8011           0 :     current->push(load);
    8012             : 
    8013           0 :     return pushTypeBarrier(load, observedTypes, barrier);
    8014             : }
    8015             : 
    8016             : AbortReasonOr<Ok>
    8017           0 : IonBuilder::getElemTryComplexElemOfTypedObject(bool* emitted,
    8018             :                                                MDefinition* obj,
    8019             :                                                MDefinition* index,
    8020             :                                                TypedObjectPrediction objPrediction,
    8021             :                                                TypedObjectPrediction elemPrediction,
    8022             :                                                uint32_t elemSize)
    8023             : {
    8024           0 :     MOZ_ASSERT(objPrediction.ofArrayKind());
    8025             : 
    8026           0 :     MDefinition* type = loadTypedObjectType(obj);
    8027           0 :     MDefinition* elemTypeObj = typeObjectForElementFromArrayStructType(type);
    8028             : 
    8029           0 :     LinearSum indexAsByteOffset(alloc());
    8030           0 :     if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
    8031           0 :         return Ok();
    8032             : 
    8033             :     return pushDerivedTypedObject(emitted, obj, indexAsByteOffset,
    8034           0 :                                   elemPrediction, elemTypeObj);
    8035             : }
    8036             : 
    8037             : AbortReasonOr<Ok>
    8038           0 : IonBuilder::pushDerivedTypedObject(bool* emitted,
    8039             :                                    MDefinition* obj,
    8040             :                                    const LinearSum& baseByteOffset,
    8041             :                                    TypedObjectPrediction derivedPrediction,
    8042             :                                    MDefinition* derivedTypeObj)
    8043             : {
    8044             :     // Find location within the owner object.
    8045             :     MDefinition* owner;
    8046           0 :     LinearSum ownerByteOffset(alloc());
    8047           0 :     MOZ_TRY(loadTypedObjectData(obj, &owner, &ownerByteOffset));
    8048           0 :     if (!ownerByteOffset.add(baseByteOffset, 1))
    8049           0 :         return abort(AbortReason::Disable, "Overflow/underflow on type object offset.");
    8050             : 
    8051           0 :     MDefinition* offset = ConvertLinearSum(alloc(), current, ownerByteOffset,
    8052           0 :                                            /* convertConstant = */ true);
    8053             : 
    8054             :     // Create the derived typed object.
    8055           0 :     MInstruction* derivedTypedObj = MNewDerivedTypedObject::New(alloc(),
    8056             :                                                                 derivedPrediction,
    8057             :                                                                 derivedTypeObj,
    8058             :                                                                 owner,
    8059           0 :                                                                 offset);
    8060           0 :     current->add(derivedTypedObj);
    8061           0 :     current->push(derivedTypedObj);
    8062             : 
    8063             :     // Determine (if possible) the class/proto that `derivedTypedObj` will
    8064             :     // have. For derived typed objects, the opacity will be the same as the
    8065             :     // incoming object from which the derived typed object is, well, derived.
    8066             :     // The prototype will be determined based on the type descriptor (and is
    8067             :     // immutable).
    8068           0 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
    8069           0 :     const Class* expectedClass = nullptr;
    8070           0 :     if (const Class* objClass = objTypes ? objTypes->getKnownClass(constraints()) : nullptr) {
    8071           0 :         MOZ_ASSERT(IsTypedObjectClass(objClass));
    8072           0 :         expectedClass = GetOutlineTypedObjectClass(IsOpaqueTypedObjectClass(objClass));
    8073             :     }
    8074           0 :     const TypedProto* expectedProto = derivedPrediction.getKnownPrototype();
    8075           0 :     MOZ_ASSERT_IF(expectedClass, IsTypedObjectClass(expectedClass));
    8076             : 
    8077             :     // Determine (if possible) the class/proto that the observed type set
    8078             :     // describes.
    8079           0 :     TemporaryTypeSet* observedTypes = bytecodeTypes(pc);
    8080           0 :     const Class* observedClass = observedTypes->getKnownClass(constraints());
    8081             : 
    8082             :     // If expectedClass/expectedProto are both non-null (and hence known), we
    8083             :     // can predict precisely what object group derivedTypedObj will have.
    8084             :     // Therefore, if we observe that this group is already contained in the set
    8085             :     // of observedTypes, we can skip the barrier.
    8086             :     //
    8087             :     // Barriers still wind up being needed in some relatively
    8088             :     // rare cases:
    8089             :     //
    8090             :     // - if multiple kinds of typed objects flow into this point,
    8091             :     //   in which case we will not be able to predict expectedClass
    8092             :     //   nor expectedProto.
    8093             :     //
    8094             :     // - if the code has never executed, in which case the set of
    8095             :     //   observed types will be incomplete.
    8096             :     //
    8097             :     // Barriers are particularly expensive here because they prevent
    8098             :     // us from optimizing the MNewDerivedTypedObject away.
    8099             :     JSObject* observedProto;
    8100           0 :     if (observedTypes->getCommonPrototype(constraints(), &observedProto) &&
    8101           0 :         observedClass && observedProto && observedClass == expectedClass &&
    8102           0 :         observedProto == expectedProto)
    8103             :     {
    8104           0 :         derivedTypedObj->setResultTypeSet(observedTypes);
    8105             :     } else {
    8106           0 :         MOZ_TRY(pushTypeBarrier(derivedTypedObj, observedTypes, BarrierKind::TypeSet));
    8107             :     }
    8108             : 
    8109           0 :     trackOptimizationSuccess();
    8110           0 :     *emitted = true;
    8111           0 :     return Ok();
    8112             : }
    8113             : 
    8114             : AbortReasonOr<Ok>
    8115          30 : IonBuilder::getElemTryGetProp(bool* emitted, MDefinition* obj, MDefinition* index)
    8116             : {
    8117             :     // If index is a constant string or symbol, try to optimize this GETELEM
    8118             :     // as a GETPROP.
    8119             : 
    8120          30 :     MOZ_ASSERT(*emitted == false);
    8121             : 
    8122          30 :     MConstant* indexConst = index->maybeConstantValue();
    8123             :     jsid id;
    8124          30 :     if (!indexConst || !ValueToIdPure(indexConst->toJSValue(), &id))
    8125          27 :         return Ok();
    8126             : 
    8127           3 :     if (id != IdToTypeId(id))
    8128           0 :         return Ok();
    8129             : 
    8130           3 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    8131             : 
    8132           3 :     trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
    8133           3 :     MOZ_TRY(getPropTryConstant(emitted, obj, id, types) );
    8134           3 :     if (*emitted) {
    8135           3 :         index->setImplicitlyUsedUnchecked();
    8136           3 :         return Ok();
    8137             :     }
    8138             : 
    8139           0 :     trackOptimizationAttempt(TrackedStrategy::GetProp_NotDefined);
    8140           0 :     MOZ_TRY(getPropTryNotDefined(emitted, obj, id, types) );
    8141           0 :     if (*emitted) {
    8142           0 :         index->setImplicitlyUsedUnchecked();
    8143           0 :         return Ok();
    8144             :     }
    8145             : 
    8146           0 :     return Ok();
    8147             : }
    8148             : 
    8149             : AbortReasonOr<Ok>
    8150          27 : IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
    8151             : {
    8152          27 :     MOZ_ASSERT(*emitted == false);
    8153             : 
    8154          27 :     JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, index);
    8155          27 :     if (unboxedType == JSVAL_TYPE_MAGIC) {
    8156          27 :         if (!ElementAccessIsDenseNative(constraints(), obj, index)) {
    8157          26 :             trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
    8158          26 :             return Ok();
    8159             :         }
    8160             :     }
    8161             : 
    8162             :     // Don't generate a fast path if there have been bounds check failures
    8163             :     // and this access might be on a sparse property.
    8164             :     bool hasExtraIndexedProperty;
    8165           1 :     MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
    8166           1 :     if (hasExtraIndexedProperty && failedBoundsCheck_) {
    8167           0 :         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
    8168           0 :         return Ok();
    8169             :     }
    8170             : 
    8171             :     // Don't generate a fast path if this pc has seen negative indexes accessed,
    8172             :     // which will not appear to be extra indexed properties.
    8173           1 :     if (inspector->hasSeenNegativeIndexGetElement(pc)) {
    8174           0 :         trackOptimizationOutcome(TrackedOutcome::ArraySeenNegativeIndex);
    8175           0 :         return Ok();
    8176             :     }
    8177             : 
    8178           1 :     MOZ_TRY(jsop_getelem_dense(obj, index, unboxedType));
    8179             : 
    8180           1 :     trackOptimizationSuccess();
    8181           1 :     *emitted = true;
    8182           1 :     return Ok();
    8183             : }
    8184             : 
    8185             : AbortReasonOr<JSObject*>
    8186          26 : IonBuilder::getStaticTypedArrayObject(MDefinition* obj, MDefinition* index)
    8187             : {
    8188             :     Scalar::Type arrayType;
    8189          26 :     if (!ElementAccessIsTypedArray(constraints(), obj, index, &arrayType)) {
    8190          26 :         trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray);
    8191          26 :         return nullptr;
    8192             :     }
    8193             : 
    8194           0 :     if (!LIRGenerator::allowStaticTypedArrayAccesses()) {
    8195           0 :         trackOptimizationOutcome(TrackedOutcome::Disabled);
    8196           0 :         return nullptr;
    8197             :     }
    8198             : 
    8199             :     bool hasExtraIndexedProperty;
    8200           0 :     MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
    8201           0 :     if (hasExtraIndexedProperty) {
    8202           0 :         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
    8203           0 :         return nullptr;
    8204             :     }
    8205             : 
    8206           0 :     if (!obj->resultTypeSet()) {
    8207           0 :         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
    8208           0 :         return nullptr;
    8209             :     }
    8210             : 
    8211           0 :     JSObject* tarrObj = obj->resultTypeSet()->maybeSingleton();
    8212           0 :     if (!tarrObj) {
    8213           0 :         trackOptimizationOutcome(TrackedOutcome::NotSingleton);
    8214           0 :         return nullptr;
    8215             :     }
    8216             : 
    8217           0 :     TypeSet::ObjectKey* tarrKey = TypeSet::ObjectKey::get(tarrObj);
    8218           0 :     if (tarrKey->unknownProperties()) {
    8219           0 :         trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
    8220           0 :         return nullptr;
    8221             :     }
    8222             : 
    8223           0 :     return tarrObj;
    8224             : }
    8225             : 
    8226             : AbortReasonOr<Ok>
    8227          26 : IonBuilder::getElemTryTypedStatic(bool* emitted, MDefinition* obj, MDefinition* index)
    8228             : {
    8229          26 :     MOZ_ASSERT(*emitted == false);
    8230             : 
    8231             :     JSObject* tarrObj;
    8232          26 :     MOZ_TRY_VAR(tarrObj, getStaticTypedArrayObject(obj, index));
    8233          26 :     if (!tarrObj)
    8234          26 :         return Ok();
    8235             : 
    8236             :     // LoadTypedArrayElementStatic currently treats uint32 arrays as int32.
    8237           0 :     Scalar::Type viewType = tarrObj->as<TypedArrayObject>().type();
    8238           0 :     if (viewType == Scalar::Uint32) {
    8239           0 :         trackOptimizationOutcome(TrackedOutcome::StaticTypedArrayUint32);
    8240           0 :         return Ok();
    8241             :     }
    8242             : 
    8243           0 :     MDefinition* ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
    8244           0 :     if (!ptr)
    8245           0 :         return Ok();
    8246             : 
    8247             :     // Emit LoadTypedArrayElementStatic.
    8248             : 
    8249           0 :     if (tarrObj->is<TypedArrayObject>()) {
    8250           0 :         TypeSet::ObjectKey* tarrKey = TypeSet::ObjectKey::get(tarrObj);
    8251           0 :         tarrKey->watchStateChangeForTypedArrayData(constraints());
    8252             :     }
    8253             : 
    8254           0 :     obj->setImplicitlyUsedUnchecked();
    8255           0 :     index->setImplicitlyUsedUnchecked();
    8256             : 
    8257           0 :     MLoadTypedArrayElementStatic* load = MLoadTypedArrayElementStatic::New(alloc(), tarrObj, ptr);
    8258           0 :     current->add(load);
    8259           0 :     current->push(load);
    8260             : 
    8261             :     // The load is infallible if an undefined result will be coerced to the
    8262             :     // appropriate numeric type if the read is out of bounds. The truncation
    8263             :     // analysis picks up some of these cases, but is incomplete with respect
    8264             :     // to others. For now, sniff the bytecode for simple patterns following
    8265             :     // the load which guarantee a truncation or numeric conversion.
    8266           0 :     if (viewType == Scalar::Float32 || viewType == Scalar::Float64) {
    8267           0 :         jsbytecode* next = pc + JSOP_GETELEM_LENGTH;
    8268           0 :         if (*next == JSOP_POS)
    8269           0 :             load->setInfallible();
    8270             :     } else {
    8271           0 :         jsbytecode* next = pc + JSOP_GETELEM_LENGTH;
    8272           0 :         if (*next == JSOP_ZERO && *(next + JSOP_ZERO_LENGTH) == JSOP_BITOR)
    8273           0 :             load->setInfallible();
    8274             :     }
    8275             : 
    8276           0 :     trackOptimizationSuccess();
    8277           0 :     *emitted = true;
    8278           0 :     return Ok();
    8279             : }
    8280             : 
    8281             : AbortReasonOr<Ok>
    8282          26 : IonBuilder::getElemTryTypedArray(bool* emitted, MDefinition* obj, MDefinition* index)
    8283             : {
    8284          26 :     MOZ_ASSERT(*emitted == false);
    8285             : 
    8286             :     Scalar::Type arrayType;
    8287          26 :     if (!ElementAccessIsTypedArray(constraints(), obj, index, &arrayType)) {
    8288          26 :         trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray);
    8289          26 :         return Ok();
    8290             :     }
    8291             : 
    8292             :     // Emit typed getelem variant.
    8293           0 :     MOZ_TRY(jsop_getelem_typed(obj, index, arrayType));
    8294             : 
    8295           0 :     trackOptimizationSuccess();
    8296           0 :     *emitted = true;
    8297           0 :     return Ok();
    8298             : }
    8299             : 
    8300             : AbortReasonOr<Ok>
    8301          26 : IonBuilder::getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index)
    8302             : {
    8303          26 :     MOZ_ASSERT(*emitted == false);
    8304             : 
    8305          26 :     if (obj->type() != MIRType::String || !IsNumberType(index->type())) {
    8306          23 :         trackOptimizationOutcome(TrackedOutcome::AccessNotString);
    8307          23 :         return Ok();
    8308             :     }
    8309             : 
    8310             :     // If the index is expected to be out-of-bounds, don't optimize to avoid
    8311             :     // frequent bailouts.
    8312           3 :     if (bytecodeTypes(pc)->hasType(TypeSet::UndefinedType())) {
    8313           0 :         trackOptimizationOutcome(TrackedOutcome::OutOfBounds);
    8314           0 :         return Ok();
    8315             :     }
    8316             : 
    8317             :     // Emit fast path for string[index].
    8318           3 :     MInstruction* idInt32 = MToInt32::New(alloc(), index);
    8319           3 :     current->add(idInt32);
    8320           3 :     index = idInt32;
    8321             : 
    8322           3 :     MStringLength* length = MStringLength::New(alloc(), obj);
    8323           3 :     current->add(length);
    8324             : 
    8325           3 :     index = addBoundsCheck(index, length);
    8326             : 
    8327           3 :     MCharCodeAt* charCode = MCharCodeAt::New(alloc(), obj, index);
    8328           3 :     current->add(charCode);
    8329             : 
    8330           3 :     MFromCharCode* result = MFromCharCode::New(alloc(), charCode);
    8331           3 :     current->add(result);
    8332           3 :     current->push(result);
    8333             : 
    8334           3 :     trackOptimizationSuccess();
    8335           3 :     *emitted = true;
    8336           3 :     return Ok();
    8337             : }
    8338             : 
    8339             : AbortReasonOr<Ok>
    8340           1 : IonBuilder::getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* index)
    8341             : {
    8342           1 :     MOZ_ASSERT(*emitted == false);
    8343             : 
    8344           1 :     if (inliningDepth_ > 0)
    8345           0 :         return Ok();
    8346             : 
    8347           1 :     if (obj->type() != MIRType::MagicOptimizedArguments)
    8348           0 :         return Ok();
    8349             : 
    8350             :     // Emit GetFrameArgument.
    8351             : 
    8352           1 :     MOZ_ASSERT(!info().argsObjAliasesFormals());
    8353             : 
    8354             :     // Type Inference has guaranteed this is an optimized arguments object.
    8355           1 :     obj->setImplicitlyUsedUnchecked();
    8356             : 
    8357             :     // To ensure that we are not looking above the number of actual arguments.
    8358           1 :     MArgumentsLength* length = MArgumentsLength::New(alloc());
    8359           1 :     current->add(length);
    8360             : 
    8361             :     // Ensure index is an integer.
    8362           1 :     MInstruction* idInt32 = MToInt32::New(alloc(), index);
    8363           1 :     current->add(idInt32);
    8364           1 :     index = idInt32;
    8365             : 
    8366             :     // Bailouts if we read more than the number of actual arguments.
    8367           1 :     index = addBoundsCheck(index, length);
    8368             : 
    8369             :     // Load the argument from the actual arguments.
    8370           1 :     MGetFrameArgument* load = MGetFrameArgument::New(alloc(), index, analysis_.hasSetArg());
    8371           1 :     current->add(load);
    8372           1 :     current->push(load);
    8373             : 
    8374           1 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    8375           1 :     MOZ_TRY(pushTypeBarrier(load, types, BarrierKind::TypeSet));
    8376             : 
    8377           1 :     trackOptimizationSuccess();
    8378           1 :     *emitted = true;
    8379           1 :     return Ok();
    8380             : }
    8381             : 
    8382             : AbortReasonOr<Ok>
    8383           0 : IonBuilder::getElemTryArgumentsInlined(bool* emitted, MDefinition* obj, MDefinition* index)
    8384             : {
    8385           0 :     MOZ_ASSERT(*emitted == false);
    8386             : 
    8387           0 :     if (inliningDepth_ == 0)
    8388           0 :         return Ok();
    8389             : 
    8390           0 :     if (obj->type() != MIRType::MagicOptimizedArguments)
    8391           0 :         return Ok();
    8392             : 
    8393             :     // Emit inlined arguments.
    8394           0 :     obj->setImplicitlyUsedUnchecked();
    8395             : 
    8396           0 :     MOZ_ASSERT(!info().argsObjAliasesFormals());
    8397             : 
    8398             :     // When the id is constant, we can just return the corresponding inlined argument
    8399           0 :     MConstant* indexConst = index->maybeConstantValue();
    8400           0 :     if (indexConst && indexConst->type() == MIRType::Int32) {
    8401           0 :         MOZ_ASSERT(inliningDepth_ > 0);
    8402             : 
    8403           0 :         int32_t id = indexConst->toInt32();
    8404           0 :         index->setImplicitlyUsedUnchecked();
    8405             : 
    8406           0 :         if (id < (int32_t)inlineCallInfo_->argc() && id >= 0)
    8407           0 :             current->push(inlineCallInfo_->getArg(id));
    8408             :         else
    8409           0 :             pushConstant(UndefinedValue());
    8410             : 
    8411           0 :         trackOptimizationSuccess();
    8412           0 :         *emitted = true;
    8413           0 :         return Ok();
    8414             :     }
    8415             : 
    8416             :     // inlined not constant not supported, yet.
    8417           0 :     return abort(AbortReason::Disable, "NYI inlined not constant get argument element");
    8418             : }
    8419             : 
    8420             : AbortReasonOr<Ok>
    8421          23 : IonBuilder::getElemAddCache(MDefinition* obj, MDefinition* index)
    8422             : {
    8423             :     // Emit GetPropertyCache.
    8424             : 
    8425          23 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    8426             : 
    8427             :     BarrierKind barrier;
    8428          23 :     if (obj->type() == MIRType::Object) {
    8429             :         // Always add a barrier if the index is not an int32 value, so we can
    8430             :         // attach stubs for particular properties.
    8431          23 :         if (index->type() == MIRType::Int32) {
    8432          15 :             barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj,
    8433          15 :                                                    nullptr, types);
    8434             :         } else {
    8435           8 :             barrier = BarrierKind::TypeSet;
    8436             :         }
    8437             :     } else {
    8438             :         // PropertyReadNeedsTypeBarrier only accounts for object types, so for
    8439             :         // now always insert a barrier if the input is not known to be an
    8440             :         // object.
    8441           0 :         barrier = BarrierKind::TypeSet;
    8442             :     }
    8443             : 
    8444             :     // Ensure we insert a type barrier for reads from typed objects, as type
    8445             :     // information does not account for the initial undefined/null types.
    8446          23 :     if (barrier != BarrierKind::TypeSet && !types->unknown()) {
    8447           0 :         MOZ_ASSERT(obj->resultTypeSet());
    8448           0 :         switch (obj->resultTypeSet()->forAllClasses(constraints(), IsTypedObjectClass)) {
    8449             :           case TemporaryTypeSet::ForAllResult::ALL_FALSE:
    8450             :           case TemporaryTypeSet::ForAllResult::EMPTY:
    8451           0 :             break;
    8452             :           case TemporaryTypeSet::ForAllResult::ALL_TRUE:
    8453             :           case TemporaryTypeSet::ForAllResult::MIXED:
    8454           0 :             barrier = BarrierKind::TypeSet;
    8455           0 :             break;
    8456             :         }
    8457             :     }
    8458             : 
    8459          23 :     MGetPropertyCache* ins = MGetPropertyCache::New(alloc(), obj, index,
    8460          46 :                                                     barrier == BarrierKind::TypeSet);
    8461          23 :     current->add(ins);
    8462          23 :     current->push(ins);
    8463             : 
    8464          23 :     MOZ_TRY(resumeAfter(ins));
    8465             : 
    8466             :     // Spice up type information.
    8467          23 :     if (index->type() == MIRType::Int32 && barrier == BarrierKind::NoBarrier) {
    8468           0 :         bool needHoleCheck = !ElementAccessIsPacked(constraints(), obj);
    8469           0 :         MIRType knownType = GetElemKnownType(needHoleCheck, types);
    8470             : 
    8471           0 :         if (knownType != MIRType::Value && knownType != MIRType::Double)
    8472           0 :             ins->setResultType(knownType);
    8473             :     }
    8474             : 
    8475          23 :     MOZ_TRY(pushTypeBarrier(ins, types, barrier));
    8476             : 
    8477          23 :     trackOptimizationSuccess();
    8478          23 :     return Ok();
    8479             : }
    8480             : 
    8481             : TemporaryTypeSet*
    8482           1 : IonBuilder::computeHeapType(const TemporaryTypeSet* objTypes, const jsid id)
    8483             : {
    8484           1 :     if (objTypes->unknownObject() || objTypes->getObjectCount() == 0)
    8485           0 :         return nullptr;
    8486             : 
    8487           1 :     TemporaryTypeSet empty;
    8488           1 :     TemporaryTypeSet* acc = &empty;
    8489           1 :     LifoAlloc* lifoAlloc = alloc().lifoAlloc();
    8490             : 
    8491           2 :     Vector<HeapTypeSetKey, 4, SystemAllocPolicy> properties;
    8492           1 :     if (!properties.reserve(objTypes->getObjectCount()))
    8493           0 :         return nullptr;
    8494             : 
    8495           2 :     for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
    8496           1 :         TypeSet::ObjectKey* key = objTypes->getObject(i);
    8497             : 
    8498           1 :         if (key->unknownProperties())
    8499           0 :             return nullptr;
    8500             : 
    8501           1 :         HeapTypeSetKey property = key->property(id);
    8502           1 :         HeapTypeSet* currentSet = property.maybeTypes();
    8503             : 
    8504           1 :         if (!currentSet || currentSet->unknown())
    8505           0 :             return nullptr;
    8506             : 
    8507           1 :         properties.infallibleAppend(property);
    8508           1 :         acc = TypeSet::unionSets(acc, currentSet, lifoAlloc);
    8509           1 :         if (!acc)
    8510           0 :             return nullptr;
    8511             :     }
    8512             : 
    8513             :     // Freeze all the properties associated with the refined type set.
    8514           2 :     for (HeapTypeSetKey* i = properties.begin(); i != properties.end(); i++)
    8515           1 :         i->freeze(constraints());
    8516             : 
    8517           1 :     return acc;
    8518             : }
    8519             : 
    8520             : AbortReasonOr<Ok>
    8521           1 : IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType)
    8522             : {
    8523           1 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    8524             : 
    8525           1 :     MOZ_ASSERT(index->type() == MIRType::Int32 || index->type() == MIRType::Double);
    8526           1 :     if (JSOp(*pc) == JSOP_CALLELEM) {
    8527             :         // Indexed call on an element of an array. Populate the observed types
    8528             :         // with any objects that could be in the array, to avoid extraneous
    8529             :         // type barriers.
    8530           0 :         AddObjectsForPropertyRead(obj, nullptr, types);
    8531             :     }
    8532             : 
    8533           1 :     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj,
    8534           1 :                                                        nullptr, types);
    8535           1 :     bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
    8536             : 
    8537             :     // Reads which are on holes in the object do not have to bail out if
    8538             :     // undefined values have been observed at this access site and the access
    8539             :     // cannot hit another indexed property on the object or its prototypes.
    8540           1 :     bool readOutOfBounds = false;
    8541           1 :     if (types->hasType(TypeSet::UndefinedType())) {
    8542             :         bool hasExtraIndexedProperty;
    8543           0 :         MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
    8544           0 :         readOutOfBounds = !hasExtraIndexedProperty;
    8545             :     }
    8546             : 
    8547           1 :     MIRType knownType = MIRType::Value;
    8548           1 :     if (unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier)
    8549           1 :         knownType = GetElemKnownType(needsHoleCheck, types);
    8550             : 
    8551             :     // Ensure index is an integer.
    8552           1 :     MInstruction* idInt32 = MToInt32::New(alloc(), index);
    8553           1 :     current->add(idInt32);
    8554           1 :     index = idInt32;
    8555             : 
    8556             :     // Get the elements vector.
    8557           1 :     MInstruction* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
    8558           1 :     current->add(elements);
    8559             : 
    8560             :     // Note: to help GVN, use the original MElements instruction and not
    8561             :     // MConvertElementsToDoubles as operand. This is fine because converting
    8562             :     // elements to double does not change the initialized length.
    8563           1 :     MInstruction* initLength = initializedLength(obj, elements, unboxedType);
    8564             : 
    8565             :     // If we can load the element as a definite double, make sure to check that
    8566             :     // the array has been converted to homogenous doubles first.
    8567           1 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
    8568           1 :     bool inBounds = !readOutOfBounds && !needsHoleCheck;
    8569             : 
    8570           1 :     if (inBounds) {
    8571           1 :         TemporaryTypeSet* heapTypes = computeHeapType(objTypes, JSID_VOID);
    8572           1 :         if (heapTypes && heapTypes->isSubset(types)) {
    8573           1 :             knownType = heapTypes->getKnownMIRType();
    8574           1 :             types = heapTypes;
    8575             :         }
    8576             :     }
    8577             : 
    8578             :     bool loadDouble =
    8579           2 :         unboxedType == JSVAL_TYPE_MAGIC &&
    8580           1 :         barrier == BarrierKind::NoBarrier &&
    8581           2 :         loopDepth_ &&
    8582           1 :         inBounds &&
    8583           0 :         knownType == MIRType::Double &&
    8584           1 :         objTypes &&
    8585           1 :         objTypes->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles;
    8586           1 :     if (loadDouble)
    8587           0 :         elements = addConvertElementsToDoubles(elements);
    8588             : 
    8589             :     MInstruction* load;
    8590             : 
    8591           1 :     if (!readOutOfBounds) {
    8592             :         // This load should not return undefined, so likely we're reading
    8593             :         // in-bounds elements, and the array is packed or its holes are not
    8594             :         // read. This is the best case: we can separate the bounds check for
    8595             :         // hoisting.
    8596           1 :         index = addBoundsCheck(index, initLength);
    8597             : 
    8598           1 :         if (unboxedType != JSVAL_TYPE_MAGIC) {
    8599           0 :             load = loadUnboxedValue(elements, 0, index, unboxedType, barrier, types);
    8600             :         } else {
    8601           1 :             load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble);
    8602           1 :             current->add(load);
    8603             :         }
    8604             :     } else {
    8605             :         // This load may return undefined, so assume that we *can* read holes,
    8606             :         // or that we can read out-of-bounds accesses. In this case, the bounds
    8607             :         // check is part of the opcode.
    8608           0 :         load = MLoadElementHole::New(alloc(), elements, index, initLength,
    8609           0 :                                      unboxedType, needsHoleCheck);
    8610           0 :         current->add(load);
    8611             : 
    8612             :         // If maybeUndefined was true, the typeset must have undefined, and
    8613             :         // then either additional types or a barrier. This means we should
    8614             :         // never have a typed version of LoadElementHole.
    8615           0 :         MOZ_ASSERT(knownType == MIRType::Value);
    8616             :     }
    8617             : 
    8618           1 :     if (knownType != MIRType::Value) {
    8619           1 :         if (unboxedType == JSVAL_TYPE_MAGIC)
    8620           1 :             load->setResultType(knownType);
    8621           1 :         load->setResultTypeSet(types);
    8622             :     }
    8623             : 
    8624           1 :     current->push(load);
    8625           1 :     return pushTypeBarrier(load, types, barrier);
    8626             : }
    8627             : 
    8628             : MInstruction*
    8629           0 : IonBuilder::addArrayBufferByteLength(MDefinition* obj)
    8630             : {
    8631           0 :     MLoadFixedSlot* ins = MLoadFixedSlot::New(alloc(), obj, size_t(ArrayBufferObject::BYTE_LENGTH_SLOT));
    8632           0 :     current->add(ins);
    8633           0 :     ins->setResultType(MIRType::Int32);
    8634           0 :     return ins;
    8635             : }
    8636             : 
    8637             : void
    8638           0 : IonBuilder::addTypedArrayLengthAndData(MDefinition* obj,
    8639             :                                        BoundsChecking checking,
    8640             :                                        MDefinition** index,
    8641             :                                        MInstruction** length, MInstruction** elements)
    8642             : {
    8643           0 :     MOZ_ASSERT((index != nullptr) == (elements != nullptr));
    8644             : 
    8645           0 :     JSObject* tarr = nullptr;
    8646             : 
    8647           0 :     if (MConstant* objConst = obj->maybeConstantValue()) {
    8648           0 :         if (objConst->type() == MIRType::Object)
    8649           0 :             tarr = &objConst->toObject();
    8650           0 :     } else if (TemporaryTypeSet* types = obj->resultTypeSet()) {
    8651           0 :         tarr = types->maybeSingleton();
    8652             :     }
    8653             : 
    8654           0 :     if (tarr) {
    8655           0 :         SharedMem<void*> data = tarr->as<TypedArrayObject>().viewDataEither();
    8656             :         // Bug 979449 - Optimistically embed the elements and use TI to
    8657             :         //              invalidate if we move them.
    8658           0 :         bool isTenured = !tarr->zone()->group()->nursery().isInside(data);
    8659           0 :         if (isTenured && tarr->isSingleton()) {
    8660             :             // The 'data' pointer of TypedArrayObject can change in rare circumstances
    8661             :             // (ArrayBufferObject::changeContents).
    8662           0 :             TypeSet::ObjectKey* tarrKey = TypeSet::ObjectKey::get(tarr);
    8663           0 :             if (!tarrKey->unknownProperties()) {
    8664           0 :                 if (tarr->is<TypedArrayObject>())
    8665           0 :                     tarrKey->watchStateChangeForTypedArrayData(constraints());
    8666             : 
    8667           0 :                 obj->setImplicitlyUsedUnchecked();
    8668             : 
    8669           0 :                 int32_t len = AssertedCast<int32_t>(tarr->as<TypedArrayObject>().length());
    8670           0 :                 *length = MConstant::New(alloc(), Int32Value(len));
    8671           0 :                 current->add(*length);
    8672             : 
    8673           0 :                 if (index) {
    8674           0 :                     if (checking == DoBoundsCheck)
    8675           0 :                         *index = addBoundsCheck(*index, *length);
    8676             : 
    8677           0 :                     *elements = MConstantElements::New(alloc(), data);
    8678           0 :                     current->add(*elements);
    8679             :                 }
    8680           0 :                 return;
    8681             :             }
    8682             :         }
    8683             :     }
    8684             : 
    8685           0 :     *length = MTypedArrayLength::New(alloc(), obj);
    8686           0 :     current->add(*length);
    8687             : 
    8688           0 :     if (index) {
    8689           0 :         if (checking == DoBoundsCheck)
    8690           0 :             *index = addBoundsCheck(*index, *length);
    8691             : 
    8692           0 :         *elements = MTypedArrayElements::New(alloc(), obj);
    8693           0 :         current->add(*elements);
    8694             :     }
    8695             : }
    8696             : 
    8697             : MDefinition*
    8698           0 : IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition* id,
    8699             :                                                   Scalar::Type viewType)
    8700             : {
    8701           0 :     trackOptimizationOutcome(TrackedOutcome::StaticTypedArrayCantComputeMask);
    8702             : 
    8703             :     // No shifting is necessary if the typed array has single byte elements.
    8704           0 :     if (TypedArrayShift(viewType) == 0)
    8705           0 :         return id;
    8706             : 
    8707             :     // If the index is an already shifted constant, undo the shift to get the
    8708             :     // absolute offset being accessed.
    8709           0 :     if (MConstant* idConst = id->maybeConstantValue()) {
    8710           0 :         if (idConst->type() == MIRType::Int32) {
    8711           0 :             int32_t index = idConst->toInt32();
    8712           0 :             MConstant* offset = MConstant::New(alloc(), Int32Value(index << TypedArrayShift(viewType)));
    8713           0 :             current->add(offset);
    8714           0 :             return offset;
    8715             :         }
    8716             :     }
    8717             : 
    8718           0 :     if (!id->isRsh() || id->isEffectful())
    8719           0 :         return nullptr;
    8720             : 
    8721           0 :     MConstant* shiftAmount = id->toRsh()->rhs()->maybeConstantValue();
    8722           0 :     if (!shiftAmount || shiftAmount->type() != MIRType::Int32)
    8723           0 :         return nullptr;
    8724           0 :     if (uint32_t(shiftAmount->toInt32()) != TypedArrayShift(viewType))
    8725           0 :         return nullptr;
    8726             : 
    8727             :     // Instead of shifting, mask off the low bits of the index so that
    8728             :     // a non-scaled access on the typed array can be performed.
    8729           0 :     MConstant* mask = MConstant::New(alloc(), Int32Value(~((1 << shiftAmount->toInt32()) - 1)));
    8730           0 :     MBitAnd* ptr = MBitAnd::New(alloc(), id->getOperand(0), mask);
    8731             : 
    8732           0 :     ptr->infer(nullptr, nullptr);
    8733           0 :     MOZ_ASSERT(!ptr->isEffectful());
    8734             : 
    8735           0 :     current->add(mask);
    8736           0 :     current->add(ptr);
    8737             : 
    8738           0 :     return ptr;
    8739             : }
    8740             : 
    8741             : static MIRType
    8742           0 : MIRTypeForTypedArrayRead(Scalar::Type arrayType, bool observedDouble)
    8743             : {
    8744           0 :     switch (arrayType) {
    8745             :       case Scalar::Int8:
    8746             :       case Scalar::Uint8:
    8747             :       case Scalar::Uint8Clamped:
    8748             :       case Scalar::Int16:
    8749             :       case Scalar::Uint16:
    8750             :       case Scalar::Int32:
    8751           0 :         return MIRType::Int32;
    8752             :       case Scalar::Uint32:
    8753           0 :         return observedDouble ? MIRType::Double : MIRType::Int32;
    8754             :       case Scalar::Float32:
    8755           0 :         return MIRType::Float32;
    8756             :       case Scalar::Float64:
    8757           0 :         return MIRType::Double;
    8758             :       default:
    8759           0 :         break;
    8760             :     }
    8761           0 :     MOZ_CRASH("Unknown typed array type");
    8762             : }
    8763             : 
    8764             : AbortReasonOr<Ok>
    8765           0 : IonBuilder::jsop_getelem_typed(MDefinition* obj, MDefinition* index,
    8766             :                                Scalar::Type arrayType)
    8767             : {
    8768           0 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    8769             : 
    8770           0 :     bool maybeUndefined = types->hasType(TypeSet::UndefinedType());
    8771             : 
    8772             :     // Reading from an Uint32Array will result in a double for values
    8773             :     // that don't fit in an int32. We have to bailout if this happens
    8774             :     // and the instruction is not known to return a double.
    8775           0 :     bool allowDouble = types->hasType(TypeSet::DoubleType());
    8776             : 
    8777             :     // Ensure id is an integer.
    8778           0 :     MInstruction* idInt32 = MToInt32::New(alloc(), index);
    8779           0 :     current->add(idInt32);
    8780           0 :     index = idInt32;
    8781             : 
    8782           0 :     if (!maybeUndefined) {
    8783             :         // Assume the index is in range, so that we can hoist the length,
    8784             :         // elements vector and bounds check.
    8785             : 
    8786             :         // If we are reading in-bounds elements, we can use knowledge about
    8787             :         // the array type to determine the result type, even if the opcode has
    8788             :         // never executed. The known pushed type is only used to distinguish
    8789             :         // uint32 reads that may produce either doubles or integers.
    8790           0 :         MIRType knownType = MIRTypeForTypedArrayRead(arrayType, allowDouble);
    8791             : 
    8792             :         // Get length, bounds-check, then get elements, and add all instructions.
    8793             :         MInstruction* length;
    8794             :         MInstruction* elements;
    8795           0 :         addTypedArrayLengthAndData(obj, DoBoundsCheck, &index, &length, &elements);
    8796             : 
    8797             :         // Load the element.
    8798           0 :         MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
    8799           0 :         current->add(load);
    8800           0 :         current->push(load);
    8801             : 
    8802             :         // Note: we can ignore the type barrier here, we know the type must
    8803             :         // be valid and unbarriered.
    8804           0 :         load->setResultType(knownType);
    8805           0 :         return Ok();
    8806             :     } else {
    8807             :         // We need a type barrier if the array's element type has never been
    8808             :         // observed (we've only read out-of-bounds values). Note that for
    8809             :         // Uint32Array, we only check for int32: if allowDouble is false we
    8810             :         // will bailout when we read a double.
    8811           0 :         BarrierKind barrier = BarrierKind::TypeSet;
    8812           0 :         switch (arrayType) {
    8813             :           case Scalar::Int8:
    8814             :           case Scalar::Uint8:
    8815             :           case Scalar::Uint8Clamped:
    8816             :           case Scalar::Int16:
    8817             :           case Scalar::Uint16:
    8818             :           case Scalar::Int32:
    8819             :           case Scalar::Uint32:
    8820           0 :             if (types->hasType(TypeSet::Int32Type()))
    8821           0 :                 barrier = BarrierKind::NoBarrier;
    8822           0 :             break;
    8823             :           case Scalar::Float32:
    8824             :           case Scalar::Float64:
    8825           0 :             if (allowDouble)
    8826           0 :                 barrier = BarrierKind::NoBarrier;
    8827           0 :             break;
    8828             :           default:
    8829           0 :             MOZ_CRASH("Unknown typed array type");
    8830             :         }
    8831             : 
    8832             :         // Assume we will read out-of-bound values. In this case the
    8833             :         // bounds check will be part of the instruction, and the instruction
    8834             :         // will always return a Value.
    8835             :         MLoadTypedArrayElementHole* load =
    8836           0 :             MLoadTypedArrayElementHole::New(alloc(), obj, index, arrayType, allowDouble);
    8837           0 :         current->add(load);
    8838           0 :         current->push(load);
    8839             : 
    8840           0 :         return pushTypeBarrier(load, types, barrier);
    8841             :     }
    8842             : }
    8843             : 
    8844             : AbortReasonOr<Ok>
    8845           0 : IonBuilder::jsop_setelem()
    8846             : {
    8847           0 :     bool emitted = false;
    8848           0 :     startTrackingOptimizations();
    8849             : 
    8850           0 :     MDefinition* value = current->pop();
    8851           0 :     MDefinition* index = current->pop();
    8852           0 :     MDefinition* object = convertUnboxedObjects(current->pop());
    8853             : 
    8854           0 :     trackTypeInfo(TrackedTypeSite::Receiver, object->type(), object->resultTypeSet());
    8855           0 :     trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet());
    8856           0 :     trackTypeInfo(TrackedTypeSite::Value, value->type(), value->resultTypeSet());
    8857             : 
    8858           0 :     if (shouldAbortOnPreliminaryGroups(object)) {
    8859           0 :         MInstruction* ins = MCallSetElement::New(alloc(), object, index, value, IsStrictSetPC(pc));
    8860           0 :         current->add(ins);
    8861           0 :         current->push(value);
    8862           0 :         return resumeAfter(ins);
    8863             :     }
    8864             : 
    8865           0 :     if (!forceInlineCaches()) {
    8866           0 :         trackOptimizationAttempt(TrackedStrategy::SetElem_TypedStatic);
    8867           0 :         MOZ_TRY(setElemTryTypedStatic(&emitted, object, index, value));
    8868           0 :         if (emitted)
    8869           0 :             return Ok();
    8870             : 
    8871           0 :         trackOptimizationAttempt(TrackedStrategy::SetElem_TypedArray);
    8872           0 :         MOZ_TRY(setElemTryTypedArray(&emitted, object, index, value));
    8873           0 :         if (emitted)
    8874           0 :             return Ok();
    8875             : 
    8876           0 :         trackOptimizationAttempt(TrackedStrategy::SetElem_Dense);
    8877           0 :         SetElemICInspector icInspect(inspector->setElemICInspector(pc));
    8878           0 :         bool writeHole = icInspect.sawOOBDenseWrite();
    8879           0 :         MOZ_TRY(initOrSetElemTryDense(&emitted, object, index, value, writeHole));
    8880           0 :         if (emitted)
    8881           0 :             return Ok();
    8882             : 
    8883           0 :         trackOptimizationAttempt(TrackedStrategy::SetElem_Arguments);
    8884           0 :         MOZ_TRY(setElemTryArguments(&emitted, object, index, value));
    8885           0 :         if (emitted)
    8886           0 :             return Ok();
    8887             : 
    8888           0 :         trackOptimizationAttempt(TrackedStrategy::SetElem_TypedObject);
    8889           0 :         MOZ_TRY(setElemTryTypedObject(&emitted, object, index, value));
    8890           0 :         if (emitted)
    8891           0 :             return Ok();
    8892             :     }
    8893             : 
    8894           0 :     if (script()->argumentsHasVarBinding() &&
    8895           0 :         object->mightBeType(MIRType::MagicOptimizedArguments) &&
    8896           0 :         info().analysisMode() != Analysis_ArgumentsUsage)
    8897             :     {
    8898           0 :         return abort(AbortReason::Disable, "Type is not definitely lazy arguments.");
    8899             :     }
    8900             : 
    8901           0 :     trackOptimizationAttempt(TrackedStrategy::SetElem_InlineCache);
    8902           0 :     MOZ_TRY(initOrSetElemTryCache(&emitted, object, index, value));
    8903           0 :     if (emitted)
    8904           0 :         return Ok();
    8905             : 
    8906             :     // Emit call.
    8907           0 :     MInstruction* ins = MCallSetElement::New(alloc(), object, index, value, IsStrictSetPC(pc));
    8908           0 :     current->add(ins);
    8909           0 :     current->push(value);
    8910             : 
    8911           0 :     return resumeAfter(ins);
    8912             : }
    8913             : 
    8914             : AbortReasonOr<Ok>
    8915           0 : IonBuilder::setElemTryTypedObject(bool* emitted, MDefinition* obj,
    8916             :                                   MDefinition* index, MDefinition* value)
    8917             : {
    8918           0 :     MOZ_ASSERT(*emitted == false);
    8919             : 
    8920             :     // The next several failures are all due to types not predicting that we
    8921             :     // are definitely doing a getelem access on a typed object.
    8922           0 :     trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject);
    8923             : 
    8924           0 :     TypedObjectPrediction objPrediction = typedObjectPrediction(obj);
    8925           0 :     if (objPrediction.isUseless())
    8926           0 :         return Ok();
    8927             : 
    8928           0 :     if (!objPrediction.ofArrayKind())
    8929           0 :         return Ok();
    8930             : 
    8931           0 :     TypedObjectPrediction elemPrediction = objPrediction.arrayElementType();
    8932           0 :     if (elemPrediction.isUseless())
    8933           0 :         return Ok();
    8934             : 
    8935             :     uint32_t elemSize;
    8936           0 :     if (!elemPrediction.hasKnownSize(&elemSize))
    8937           0 :         return Ok();
    8938             : 
    8939           0 :     switch (elemPrediction.kind()) {
    8940             :       case type::Simd:
    8941             :         // FIXME (bug 894105): store a MIRType::float32x4 etc
    8942           0 :         trackOptimizationOutcome(TrackedOutcome::GenericFailure);
    8943           0 :         return Ok();
    8944             : 
    8945             :       case type::Reference:
    8946             :         return setElemTryReferenceElemOfTypedObject(emitted, obj, index,
    8947           0 :                                                     objPrediction, value, elemPrediction);
    8948             : 
    8949             :       case type::Scalar:
    8950             :         return setElemTryScalarElemOfTypedObject(emitted,
    8951             :                                                  obj,
    8952             :                                                  index,
    8953             :                                                  objPrediction,
    8954             :                                                  value,
    8955             :                                                  elemPrediction,
    8956           0 :                                                  elemSize);
    8957             : 
    8958             :       case type::Struct:
    8959             :       case type::Array:
    8960             :         // Not yet optimized.
    8961           0 :         trackOptimizationOutcome(TrackedOutcome::GenericFailure);
    8962           0 :         return Ok();
    8963             :     }
    8964             : 
    8965           0 :     MOZ_CRASH("Bad kind");
    8966             : }
    8967             : 
    8968             : AbortReasonOr<Ok>
    8969           0 : IonBuilder::setElemTryReferenceElemOfTypedObject(bool* emitted,
    8970             :                                                  MDefinition* obj,
    8971             :                                                  MDefinition* index,
    8972             :                                                  TypedObjectPrediction objPrediction,
    8973             :                                                  MDefinition* value,
    8974             :                                                  TypedObjectPrediction elemPrediction)
    8975             : {
    8976           0 :     ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
    8977           0 :     uint32_t elemSize = ReferenceTypeDescr::size(elemType);
    8978             : 
    8979           0 :     LinearSum indexAsByteOffset(alloc());
    8980           0 :     if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
    8981           0 :         return Ok();
    8982             : 
    8983           0 :     MOZ_TRY_VAR(*emitted, storeReferenceTypedObjectValue(obj, indexAsByteOffset, elemType, value, nullptr));
    8984           0 :     if (!*emitted)
    8985           0 :         return Ok();
    8986             : 
    8987           0 :     current->push(value);
    8988             : 
    8989           0 :     trackOptimizationSuccess();
    8990           0 :     *emitted = true;
    8991           0 :     return Ok();
    8992             : }
    8993             : 
    8994             : AbortReasonOr<Ok>
    8995           0 : IonBuilder::setElemTryScalarElemOfTypedObject(bool* emitted,
    8996             :                                               MDefinition* obj,
    8997             :                                               MDefinition* index,
    8998             :                                               TypedObjectPrediction objPrediction,
    8999             :                                               MDefinition* value,
    9000             :                                               TypedObjectPrediction elemPrediction,
    9001             :                                               uint32_t elemSize)
    9002             : {
    9003             :     // Must always be loading the same scalar type
    9004           0 :     ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
    9005           0 :     MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
    9006             : 
    9007           0 :     LinearSum indexAsByteOffset(alloc());
    9008           0 :     if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
    9009           0 :         return Ok();
    9010             : 
    9011             :     // Store the element
    9012           0 :     MOZ_TRY(storeScalarTypedObjectValue(obj, indexAsByteOffset, elemType, value));
    9013             : 
    9014           0 :     current->push(value);
    9015             : 
    9016           0 :     trackOptimizationSuccess();
    9017           0 :     *emitted = true;
    9018           0 :     return Ok();
    9019             : }
    9020             : 
    9021             : AbortReasonOr<Ok>
    9022           0 : IonBuilder::setElemTryTypedStatic(bool* emitted, MDefinition* object,
    9023             :                                   MDefinition* index, MDefinition* value)
    9024             : {
    9025           0 :     MOZ_ASSERT(*emitted == false);
    9026             : 
    9027             :     JSObject* tarrObj;
    9028           0 :     MOZ_TRY_VAR(tarrObj, getStaticTypedArrayObject(object, index));
    9029           0 :     if (!tarrObj)
    9030           0 :         return Ok();
    9031             : 
    9032           0 :     SharedMem<void*> viewData = tarrObj->as<TypedArrayObject>().viewDataEither();
    9033           0 :     if (tarrObj->zone()->group()->nursery().isInside(viewData))
    9034           0 :         return Ok();
    9035             : 
    9036           0 :     Scalar::Type viewType = tarrObj->as<TypedArrayObject>().type();
    9037           0 :     MDefinition* ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
    9038           0 :     if (!ptr)
    9039           0 :         return Ok();
    9040             : 
    9041             :     // Emit StoreTypedArrayElementStatic.
    9042             : 
    9043           0 :     if (tarrObj->is<TypedArrayObject>()) {
    9044           0 :         TypeSet::ObjectKey* tarrKey = TypeSet::ObjectKey::get(tarrObj);
    9045           0 :         tarrKey->watchStateChangeForTypedArrayData(constraints());
    9046             :     }
    9047             : 
    9048           0 :     object->setImplicitlyUsedUnchecked();
    9049           0 :     index->setImplicitlyUsedUnchecked();
    9050             : 
    9051             :     // Clamp value to [0, 255] for Uint8ClampedArray.
    9052           0 :     MDefinition* toWrite = value;
    9053           0 :     if (viewType == Scalar::Uint8Clamped) {
    9054           0 :         toWrite = MClampToUint8::New(alloc(), value);
    9055           0 :         current->add(toWrite->toInstruction());
    9056             :     }
    9057             : 
    9058           0 :     MInstruction* store = MStoreTypedArrayElementStatic::New(alloc(), tarrObj, ptr, toWrite);
    9059           0 :     current->add(store);
    9060           0 :     current->push(value);
    9061             : 
    9062           0 :     MOZ_TRY(resumeAfter(store));
    9063             : 
    9064           0 :     trackOptimizationSuccess();
    9065           0 :     *emitted = true;
    9066           0 :     return Ok();
    9067             : }
    9068             : 
    9069             : AbortReasonOr<Ok>
    9070           0 : IonBuilder::setElemTryTypedArray(bool* emitted, MDefinition* object,
    9071             :                                  MDefinition* index, MDefinition* value)
    9072             : {
    9073           0 :     MOZ_ASSERT(*emitted == false);
    9074             : 
    9075             :     Scalar::Type arrayType;
    9076           0 :     if (!ElementAccessIsTypedArray(constraints(), object, index, &arrayType)) {
    9077           0 :         trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray);
    9078           0 :         return Ok();
    9079             :     }
    9080             : 
    9081             :     // Emit typed setelem variant.
    9082           0 :     MOZ_TRY(jsop_setelem_typed(arrayType, object, index, value));
    9083             : 
    9084           0 :     trackOptimizationSuccess();
    9085           0 :     *emitted = true;
    9086           0 :     return Ok();
    9087             : }
    9088             : 
    9089             : AbortReasonOr<Ok>
    9090          54 : IonBuilder::initOrSetElemTryDense(bool* emitted, MDefinition* object,
    9091             :                                   MDefinition* index, MDefinition* value, bool writeHole)
    9092             : {
    9093          54 :     MOZ_ASSERT(*emitted == false);
    9094             : 
    9095          54 :     if (value->type() == MIRType::MagicHole)
    9096             :     {
    9097           0 :         trackOptimizationOutcome(TrackedOutcome::InitHole);
    9098           0 :         return Ok();
    9099             :     }
    9100             : 
    9101          54 :     JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index);
    9102          54 :     if (unboxedType == JSVAL_TYPE_MAGIC) {
    9103          54 :         if (!ElementAccessIsDenseNative(constraints(), object, index)) {
    9104          53 :             trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
    9105          53 :             return Ok();
    9106             :         }
    9107             :     }
    9108             : 
    9109           1 :     if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
    9110             :                                       &object, nullptr, &value, /* canModify = */ true))
    9111             :     {
    9112           0 :         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
    9113           0 :         return Ok();
    9114             :     }
    9115             : 
    9116           1 :     if (!object->resultTypeSet()) {
    9117           0 :         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
    9118           0 :         return Ok();
    9119             :     }
    9120             : 
    9121             :     TemporaryTypeSet::DoubleConversion conversion =
    9122           1 :         object->resultTypeSet()->convertDoubleElements(constraints());
    9123             : 
    9124             :     // If AmbiguousDoubleConversion, only handle int32 values for now.
    9125           1 :     if (conversion == TemporaryTypeSet::AmbiguousDoubleConversion &&
    9126           0 :         value->type() != MIRType::Int32)
    9127             :     {
    9128           0 :         trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion);
    9129           0 :         return Ok();
    9130             :     }
    9131             : 
    9132             :     // Don't generate a fast path if there have been bounds check failures
    9133             :     // and this access might be on a sparse property.
    9134             :     bool hasExtraIndexedProperty;
    9135           1 :     MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, object));
    9136           1 :     if (hasExtraIndexedProperty && failedBoundsCheck_) {
    9137           0 :         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
    9138           0 :         return Ok();
    9139             :     }
    9140             : 
    9141             :     // Emit dense setelem variant.
    9142           1 :     MOZ_TRY(initOrSetElemDense(conversion, object, index, value, unboxedType, writeHole, emitted));
    9143             : 
    9144           1 :     if (!*emitted) {
    9145           0 :         trackOptimizationOutcome(TrackedOutcome::NonWritableProperty);
    9146           0 :         return Ok();
    9147             :     }
    9148             : 
    9149           1 :     trackOptimizationSuccess();
    9150           1 :     return Ok();
    9151             : }
    9152             : 
    9153             : AbortReasonOr<Ok>
    9154           0 : IonBuilder::setElemTryArguments(bool* emitted, MDefinition* object,
    9155             :                                 MDefinition* index, MDefinition* value)
    9156             : {
    9157           0 :     MOZ_ASSERT(*emitted == false);
    9158             : 
    9159           0 :     if (object->type() != MIRType::MagicOptimizedArguments)
    9160           0 :         return Ok();
    9161             : 
    9162             :     // Arguments are not supported yet.
    9163           0 :     return abort(AbortReason::Disable, "NYI arguments[]=");
    9164             : }
    9165             : 
    9166             : AbortReasonOr<Ok>
    9167          53 : IonBuilder::initOrSetElemTryCache(bool* emitted, MDefinition* object,
    9168             :                                   MDefinition* index, MDefinition* value)
    9169             : {
    9170          53 :     MOZ_ASSERT(*emitted == false);
    9171             : 
    9172          53 :     if (!object->mightBeType(MIRType::Object)) {
    9173          53 :         trackOptimizationOutcome(TrackedOutcome::NotObject);
    9174          53 :         return Ok();
    9175             :     }
    9176             : 
    9177           0 :     if (value->type() == MIRType::MagicHole)
    9178             :     {
    9179           0 :         trackOptimizationOutcome(TrackedOutcome::InitHole);
    9180           0 :         return Ok();
    9181             :     }
    9182             : 
    9183           0 :     bool barrier = true;
    9184           0 :     if (index->type() == MIRType::Int32 &&
    9185           0 :         !PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
    9186             :                                        &object, nullptr, &value, /* canModify = */ true))
    9187             :     {
    9188           0 :         barrier = false;
    9189             :     }
    9190             : 
    9191             :     // We can avoid worrying about holes in the IC if we know a priori we are safe
    9192             :     // from them. If TI can guard that there are no indexed properties on the prototype
    9193             :     // chain, we know that we anen't missing any setters by overwriting the hole with
    9194             :     // another value.
    9195             :     bool guardHoles;
    9196           0 :     MOZ_TRY_VAR(guardHoles, ElementAccessHasExtraIndexedProperty(this, object));
    9197             : 
    9198             :     // Make sure the object being written to doesn't have copy on write elements.
    9199           0 :     const Class* clasp = object->resultTypeSet() ? object->resultTypeSet()->getKnownClass(constraints()) : nullptr;
    9200           0 :     bool checkNative = !clasp || !clasp->isNative();
    9201           0 :     object = addMaybeCopyElementsForWrite(object, checkNative);
    9202             : 
    9203             :     // Emit SetPropertyCache.
    9204           0 :     bool strict = JSOp(*pc) == JSOP_STRICTSETELEM;
    9205             :     MSetPropertyCache* ins =
    9206           0 :         MSetPropertyCache::New(alloc(), object, index, value, strict, NeedsPostBarrier(value),
    9207           0 :                                barrier, guardHoles);
    9208           0 :     current->add(ins);
    9209             : 
    9210             :     // Push value back onto stack. Init ops keep their object on stack.
    9211           0 :     if (!IsPropertyInitOp(JSOp(*pc)))
    9212           0 :         current->push(value);
    9213             : 
    9214           0 :     MOZ_TRY(resumeAfter(ins));
    9215             : 
    9216           0 :     trackOptimizationSuccess();
    9217           0 :     *emitted = true;
    9218           0 :     return Ok();
    9219             : }
    9220             : 
    9221             : AbortReasonOr<Ok>
    9222           1 : IonBuilder::initOrSetElemDense(TemporaryTypeSet::DoubleConversion conversion,
    9223             :                                MDefinition* obj, MDefinition* id, MDefinition* value,
    9224             :                                JSValueType unboxedType, bool writeHole, bool* emitted)
    9225             : {
    9226           1 :     MOZ_ASSERT(*emitted == false);
    9227             : 
    9228           1 :     MIRType elementType = MIRType::None;
    9229           1 :     if (unboxedType == JSVAL_TYPE_MAGIC)
    9230           1 :         elementType = DenseNativeElementType(constraints(), obj);
    9231           1 :     bool packed = ElementAccessIsPacked(constraints(), obj);
    9232             : 
    9233             :     // Writes which are on holes in the object do not have to bail out if they
    9234             :     // cannot hit another indexed property on the object or its prototypes.
    9235             :     bool hasExtraIndexedProperty;
    9236           1 :     MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
    9237             : 
    9238           1 :     bool mayBeFrozen = ElementAccessMightBeFrozen(constraints(), obj);
    9239             : 
    9240           1 :     if (mayBeFrozen && hasExtraIndexedProperty) {
    9241             :         // FallibleStoreElement does not know how to deal with extra indexed
    9242             :         // properties on the prototype. This case should be rare so we fall back
    9243             :         // to an IC.
    9244           0 :         return Ok();
    9245             :     }
    9246             : 
    9247           1 :     *emitted = true;
    9248             : 
    9249             :     // Ensure id is an integer.
    9250           1 :     MInstruction* idInt32 = MToInt32::New(alloc(), id);
    9251           1 :     current->add(idInt32);
    9252           1 :     id = idInt32;
    9253             : 
    9254           1 :     if (NeedsPostBarrier(value))
    9255           0 :         current->add(MPostWriteElementBarrier::New(alloc(), obj, value, id));
    9256             : 
    9257             :     // Copy the elements vector if necessary.
    9258           1 :     obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
    9259             : 
    9260             :     // Get the elements vector.
    9261           1 :     MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
    9262           1 :     current->add(elements);
    9263             : 
    9264             :     // Ensure the value is a double, if double conversion might be needed.
    9265           1 :     MDefinition* newValue = value;
    9266           1 :     switch (conversion) {
    9267             :       case TemporaryTypeSet::AlwaysConvertToDoubles:
    9268             :       case TemporaryTypeSet::MaybeConvertToDoubles: {
    9269           0 :         MInstruction* valueDouble = MToDouble::New(alloc(), value);
    9270           0 :         current->add(valueDouble);
    9271           0 :         newValue = valueDouble;
    9272           0 :         break;
    9273             :       }
    9274             : 
    9275             :       case TemporaryTypeSet::AmbiguousDoubleConversion: {
    9276           0 :         MOZ_ASSERT(value->type() == MIRType::Int32);
    9277           0 :         MInstruction* maybeDouble = MMaybeToDoubleElement::New(alloc(), elements, value);
    9278           0 :         current->add(maybeDouble);
    9279           0 :         newValue = maybeDouble;
    9280           0 :         break;
    9281             :       }
    9282             : 
    9283             :       case TemporaryTypeSet::DontConvertToDoubles:
    9284           1 :         break;
    9285             : 
    9286             :       default:
    9287           0 :         MOZ_CRASH("Unknown double conversion");
    9288             :     }
    9289             : 
    9290             :     // Use MStoreElementHole if this SETELEM has written to out-of-bounds
    9291             :     // indexes in the past. Otherwise, use MStoreElement so that we can hoist
    9292             :     // the initialized length and bounds check.
    9293             :     // If an object may have been frozen, no previous expectation hold and we
    9294             :     // fallback to MFallibleStoreElement.
    9295             :     MInstruction* store;
    9296           1 :     MStoreElementCommon* common = nullptr;
    9297           1 :     if (writeHole && !hasExtraIndexedProperty && !mayBeFrozen) {
    9298           1 :         MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType);
    9299           1 :         store = ins;
    9300           1 :         common = ins;
    9301             : 
    9302           1 :         current->add(ins);
    9303           0 :     } else if (mayBeFrozen) {
    9304           0 :         MOZ_ASSERT(!hasExtraIndexedProperty,
    9305             :                    "FallibleStoreElement codegen assumes no extra indexed properties");
    9306             : 
    9307           0 :         bool strict = IsStrictSetPC(pc);
    9308           0 :         MFallibleStoreElement* ins = MFallibleStoreElement::New(alloc(), obj, elements, id,
    9309           0 :                                                                 newValue, unboxedType, strict);
    9310           0 :         store = ins;
    9311           0 :         common = ins;
    9312             : 
    9313           0 :         current->add(ins);
    9314             :     } else {
    9315           0 :         MInstruction* initLength = initializedLength(obj, elements, unboxedType);
    9316             : 
    9317           0 :         id = addBoundsCheck(id, initLength);
    9318           0 :         bool needsHoleCheck = !packed && hasExtraIndexedProperty;
    9319             : 
    9320           0 :         if (unboxedType != JSVAL_TYPE_MAGIC) {
    9321           0 :             store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue);
    9322             :         } else {
    9323           0 :             MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
    9324           0 :             store = ins;
    9325           0 :             common = ins;
    9326             : 
    9327           0 :             current->add(store);
    9328             :         }
    9329             :     }
    9330             : 
    9331             :     // Push value back onto stack. Init ops keep their object on stack.
    9332           1 :     if (!IsPropertyInitOp(JSOp(*pc)))
    9333           0 :         current->push(value);
    9334             : 
    9335           1 :     MOZ_TRY(resumeAfter(store));
    9336             : 
    9337           1 :     if (common) {
    9338             :         // Determine whether a write barrier is required.
    9339           1 :         if (obj->resultTypeSet()->propertyNeedsBarrier(constraints(), JSID_VOID))
    9340           1 :             common->setNeedsBarrier();
    9341             : 
    9342           1 :         if (elementType != MIRType::None && packed)
    9343           1 :             common->setElementType(elementType);
    9344             :     }
    9345             : 
    9346           1 :     return Ok();
    9347             : }
    9348             : 
    9349             : 
    9350             : AbortReasonOr<Ok>
    9351           0 : IonBuilder::jsop_setelem_typed(Scalar::Type arrayType,
    9352             :                                MDefinition* obj, MDefinition* id, MDefinition* value)
    9353             : {
    9354           0 :     SetElemICInspector icInspect(inspector->setElemICInspector(pc));
    9355           0 :     bool expectOOB = icInspect.sawOOBTypedArrayWrite();
    9356             : 
    9357           0 :     if (expectOOB)
    9358           0 :         spew("Emitting OOB TypedArray SetElem");
    9359             : 
    9360             :     // Ensure id is an integer.
    9361           0 :     MInstruction* idInt32 = MToInt32::New(alloc(), id);
    9362           0 :     current->add(idInt32);
    9363           0 :     id = idInt32;
    9364             : 
    9365             :     // Get length, bounds-check, then get elements, and add all instructions.
    9366             :     MInstruction* length;
    9367             :     MInstruction* elements;
    9368           0 :     BoundsChecking checking = expectOOB ? SkipBoundsCheck : DoBoundsCheck;
    9369           0 :     addTypedArrayLengthAndData(obj, checking, &id, &length, &elements);
    9370             : 
    9371             :     // Clamp value to [0, 255] for Uint8ClampedArray.
    9372           0 :     MDefinition* toWrite = value;
    9373           0 :     if (arrayType == Scalar::Uint8Clamped) {
    9374           0 :         toWrite = MClampToUint8::New(alloc(), value);
    9375           0 :         current->add(toWrite->toInstruction());
    9376             :     }
    9377             : 
    9378             :     // Store the value.
    9379             :     MInstruction* ins;
    9380           0 :     if (expectOOB) {
    9381           0 :         ins = MStoreTypedArrayElementHole::New(alloc(), elements, length, id, toWrite, arrayType);
    9382             :     } else {
    9383             :         MStoreUnboxedScalar* store =
    9384           0 :             MStoreUnboxedScalar::New(alloc(), elements, id, toWrite, arrayType,
    9385           0 :                                      MStoreUnboxedScalar::TruncateInput);
    9386           0 :         ins = store;
    9387             :     }
    9388             : 
    9389           0 :     current->add(ins);
    9390           0 :     current->push(value);
    9391             : 
    9392           0 :     return resumeAfter(ins);
    9393             : }
    9394             : 
    9395             : AbortReasonOr<Ok>
    9396         324 : IonBuilder::jsop_length()
    9397             : {
    9398         324 :     if (jsop_length_fastPath())
    9399           6 :         return Ok();
    9400             : 
    9401         318 :     PropertyName* name = info().getAtom(pc)->asPropertyName();
    9402         318 :     return jsop_getprop(name);
    9403             : }
    9404             : 
    9405             : bool
    9406         324 : IonBuilder::jsop_length_fastPath()
    9407             : {
    9408         324 :     TemporaryTypeSet* types = bytecodeTypes(pc);
    9409             : 
    9410         324 :     if (types->getKnownMIRType() != MIRType::Int32)
    9411         308 :         return false;
    9412             : 
    9413          16 :     MDefinition* obj = current->peek(-1);
    9414             : 
    9415          16 :     if (shouldAbortOnPreliminaryGroups(obj))
    9416           0 :         return false;
    9417             : 
    9418          16 :     if (obj->mightBeType(MIRType::String)) {
    9419           3 :         if (obj->mightBeType(MIRType::Object))
    9420           0 :             return false;
    9421           3 :         current->pop();
    9422           3 :         MStringLength* ins = MStringLength::New(alloc(), obj);
    9423           3 :         current->add(ins);
    9424           3 :         current->push(ins);
    9425           3 :         return true;
    9426             :     }
    9427             : 
    9428          13 :     if (obj->mightBeType(MIRType::Object)) {
    9429          11 :         TemporaryTypeSet* objTypes = obj->resultTypeSet();
    9430             : 
    9431             :         // Compute the length for array objects.
    9432          22 :         if (objTypes &&
    9433          14 :             objTypes->getKnownClass(constraints()) == &ArrayObject::class_ &&
    9434           3 :             !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW))
    9435             :         {
    9436           3 :             current->pop();
    9437           3 :             MElements* elements = MElements::New(alloc(), obj);
    9438           3 :             current->add(elements);
    9439             : 
    9440             :             // Read length.
    9441           3 :             MArrayLength* length = MArrayLength::New(alloc(), elements);
    9442           3 :             current->add(length);
    9443           3 :             current->push(length);
    9444           3 :             return true;
    9445             :         }
    9446             : 
    9447             :         // Compute the length for unboxed array objects.
    9448           8 :         if (UnboxedArrayElementType(constraints(), obj, nullptr) != JSVAL_TYPE_MAGIC &&
    9449           0 :             !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW))
    9450             :         {
    9451           0 :             current->pop();
    9452             : 
    9453           0 :             MUnboxedArrayLength* length = MUnboxedArrayLength::New(alloc(), obj);
    9454           0 :             current->add(length);
    9455           0 :             current->push(length);
    9456           0 :             return true;
    9457             :         }
    9458             : 
    9459             :         // Compute the length for array typed objects.
    9460           8 :         TypedObjectPrediction prediction = typedObjectPrediction(obj);
    9461           8 :         if (!prediction.isUseless()) {
    9462           0 :             TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
    9463           0 :             if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
    9464           0 :                 return false;
    9465             : 
    9466             :             MInstruction* length;
    9467             :             int32_t sizedLength;
    9468           0 :             if (prediction.hasKnownArrayLength(&sizedLength)) {
    9469           0 :                 obj->setImplicitlyUsedUnchecked();
    9470           0 :                 length = MConstant::New(alloc(), Int32Value(sizedLength));
    9471             :             } else {
    9472           0 :                 return false;
    9473             :             }
    9474             : 
    9475           0 :             current->pop();
    9476           0 :             current->add(length);
    9477           0 :             current->push(length);
    9478           0 :             return true;
    9479             :         }
    9480             :     }
    9481             : 
    9482          10 :     return false;
    9483             : }
    9484             : 
    9485             : AbortReasonOr<Ok>
    9486         136 : IonBuilder::jsop_arguments()
    9487             : {
    9488         136 :     if (info().needsArgsObj()) {
    9489         135 :         current->push(current->argumentsObject());
    9490         135 :         return Ok();
    9491             :     }
    9492             : 
    9493           1 :     MOZ_ASSERT(hasLazyArguments_);
    9494           1 :     MConstant* lazyArg = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
    9495           1 :     current->add(lazyArg);
    9496           1 :     current->push(lazyArg);
    9497           1 :     return Ok();
    9498             : }
    9499             : 
    9500             : AbortReasonOr<Ok>
    9501          23 : IonBuilder::jsop_newtarget()
    9502             : {
    9503          23 :     if (!info().funMaybeLazy()) {
    9504           0 :         MOZ_ASSERT(!info().script()->isForEval());
    9505           0 :         pushConstant(NullValue());
    9506           0 :         return Ok();
    9507             :     }
    9508             : 
    9509          23 :     MOZ_ASSERT(info().funMaybeLazy());
    9510             : 
    9511          23 :     if (info().funMaybeLazy()->isArrow()) {
    9512           0 :         MArrowNewTarget* arrowNewTarget = MArrowNewTarget::New(alloc(), getCallee());
    9513           0 :         current->add(arrowNewTarget);
    9514           0 :         current->push(arrowNewTarget);
    9515           0 :         return Ok();
    9516             :     }
    9517             : 
    9518          23 :     if (inliningDepth_ == 0) {
    9519          23 :         MNewTarget* newTarget = MNewTarget::New(alloc());
    9520          23 :         current->add(newTarget);
    9521          23 :         current->push(newTarget);
    9522          23 :         return Ok();
    9523             :     }
    9524             : 
    9525           0 :     if (!inlineCallInfo_->constructing()) {
    9526           0 :         pushConstant(UndefinedValue());
    9527           0 :         return Ok();
    9528             :     }
    9529             : 
    9530           0 :     current->push(inlineCallInfo_->getNewTarget());
    9531           0 :     return Ok();
    9532             : }
    9533             : 
    9534             : AbortReasonOr<Ok>
    9535           2 : IonBuilder::jsop_rest()
    9536             : {
    9537           2 :     if (info().analysisMode() == Analysis_ArgumentsUsage) {
    9538             :         // There's no BaselineScript with the template object. Just push a
    9539             :         // dummy value, it does not affect the arguments analysis.
    9540           0 :         MUnknownValue* unknown = MUnknownValue::New(alloc());
    9541           0 :         current->add(unknown);
    9542           0 :         current->push(unknown);
    9543           0 :         return Ok();
    9544             :     }
    9545             : 
    9546           2 :     ArrayObject* templateObject = &inspector->getTemplateObject(pc)->as<ArrayObject>();
    9547             : 
    9548           2 :     if (inliningDepth_ == 0) {
    9549             :         // We don't know anything about the callee.
    9550           2 :         MArgumentsLength* numActuals = MArgumentsLength::New(alloc());
    9551           2 :         current->add(numActuals);
    9552             : 
    9553             :         // Pass in the number of actual arguments, the number of formals (not
    9554             :         // including the rest parameter slot itself), and the template object.
    9555           4 :         MRest* rest = MRest::New(alloc(), constraints(), numActuals, info().nargs() - 1,
    9556           2 :                                  templateObject);
    9557           2 :         current->add(rest);
    9558           2 :         current->push(rest);
    9559           2 :         return Ok();
    9560             :     }
    9561             : 
    9562             :     // We know the exact number of arguments the callee pushed.
    9563           0 :     unsigned numActuals = inlineCallInfo_->argc();
    9564           0 :     unsigned numFormals = info().nargs() - 1;
    9565           0 :     unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
    9566             : 
    9567           0 :     MOZ_TRY(jsop_newarray(numRest));
    9568             : 
    9569           0 :     if (numRest == 0) {
    9570             :         // No more updating to do. (Note that in this one case the length from
    9571             :         // the template object is already correct.)
    9572           0 :         return Ok();
    9573             :     }
    9574             : 
    9575           0 :     MDefinition *array = current->peek(-1);
    9576           0 :     MElements* elements = MElements::New(alloc(), array);
    9577           0 :     current->add(elements);
    9578             : 
    9579             :     // Unroll the argument copy loop. We don't need to do any bounds or hole
    9580             :     // checking here.
    9581           0 :     MConstant* index = nullptr;
    9582           0 :     for (unsigned i = numFormals; i < numActuals; i++) {
    9583           0 :         if (!alloc().ensureBallast())
    9584           0 :             return abort(AbortReason::Alloc);
    9585             : 
    9586           0 :         index = MConstant::New(alloc(), Int32Value(i - numFormals));
    9587           0 :         current->add(index);
    9588             : 
    9589           0 :         MDefinition* arg = inlineCallInfo_->argv()[i];
    9590           0 :         MStoreElement* store = MStoreElement::New(alloc(), elements, index, arg,
    9591           0 :                                                   /* needsHoleCheck = */ false);
    9592           0 :         current->add(store);
    9593             : 
    9594           0 :         if (NeedsPostBarrier(arg))
    9595           0 :             current->add(MPostWriteBarrier::New(alloc(), array, arg));
    9596             :     }
    9597             : 
    9598             :     // The array's length is incorrectly 0 now, from the template object
    9599             :     // created by BaselineCompiler::emit_JSOP_REST() before the actual argument
    9600             :     // count was known. Set the correct length now that we know that count.
    9601           0 :     MSetArrayLength* length = MSetArrayLength::New(alloc(), elements, index);
    9602           0 :     current->add(length);
    9603             : 
    9604             :     // Update the initialized length for all the (necessarily non-hole)
    9605             :     // elements added.
    9606           0 :     MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, index);
    9607           0 :     current->add(initLength);
    9608             : 
    9609           0 :     return Ok();
    9610             : }
    9611             : 
    9612             : AbortReasonOr<Ok>
    9613          15 : IonBuilder::jsop_checkisobj(uint8_t kind)
    9614             : {
    9615          15 :     MDefinition* toCheck = current->peek(-1);
    9616             : 
    9617          15 :     if (toCheck->type() == MIRType::Object) {
    9618           9 :         toCheck->setImplicitlyUsedUnchecked();
    9619           9 :         return Ok();
    9620             :     }
    9621             : 
    9622           6 :     MCheckIsObj* check = MCheckIsObj::New(alloc(), current->pop(), kind);
    9623           6 :     current->add(check);
    9624           6 :     current->push(check);
    9625           6 :     return Ok();
    9626             : }
    9627             : 
    9628             : AbortReasonOr<Ok>
    9629           0 : IonBuilder::jsop_checkiscallable(uint8_t kind)
    9630             : {
    9631           0 :     MCheckIsCallable* check = MCheckIsCallable::New(alloc(), current->pop(), kind);
    9632           0 :     current->add(check);
    9633           0 :     current->push(check);
    9634           0 :     return Ok();
    9635             : }
    9636             : 
    9637             : AbortReasonOr<Ok>
    9638           0 : IonBuilder::jsop_checkobjcoercible()
    9639             : {
    9640           0 :     MDefinition* toCheck = current->peek(-1);
    9641             : 
    9642           0 :     if (!toCheck->mightBeType(MIRType::Undefined) &&
    9643           0 :         !toCheck->mightBeType(MIRType::Null))
    9644             :     {
    9645           0 :         toCheck->setImplicitlyUsedUnchecked();
    9646           0 :         return Ok();
    9647             :     }
    9648             : 
    9649           0 :     MOZ_ASSERT(toCheck->type() == MIRType::Value ||
    9650             :                toCheck->type() == MIRType::Null  ||
    9651             :                toCheck->type() == MIRType::Undefined);
    9652             : 
    9653             :     // If we want to squeeze more perf here, we can throw without checking,
    9654             :     // if IsNullOrUndefined(toCheck->type()). Since this is a failure case,
    9655             :     // it should be OK.
    9656           0 :     MCheckObjCoercible* check = MCheckObjCoercible::New(alloc(), current->pop());
    9657           0 :     current->add(check);
    9658           0 :     current->push(check);
    9659           0 :     return resumeAfter(check);
    9660             : }
    9661             : 
    9662             : uint32_t
    9663          95 : IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed)
    9664             : {
    9665          95 :     if (!types || types->unknownObject() || !types->objectOrSentinel()) {
    9666           8 :         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
    9667           8 :         return UINT32_MAX;
    9668             :     }
    9669             : 
    9670          87 :     uint32_t slot = UINT32_MAX;
    9671             : 
    9672         154 :     for (size_t i = 0; i < types->getObjectCount(); i++) {
    9673          87 :         TypeSet::ObjectKey* key = types->getObject(i);
    9674          87 :         if (!key)
    9675           0 :             continue;
    9676             : 
    9677          87 :         if (key->unknownProperties()) {
    9678           0 :             trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
    9679          20 :             return UINT32_MAX;
    9680             :         }
    9681             : 
    9682          87 :         if (key->isSingleton()) {
    9683           0 :             trackOptimizationOutcome(TrackedOutcome::Singleton);
    9684           0 :             return UINT32_MAX;
    9685             :         }
    9686             : 
    9687          87 :         HeapTypeSetKey property = key->property(NameToId(name));
    9688         254 :         if (!property.maybeTypes() ||
    9689         154 :             !property.maybeTypes()->definiteProperty() ||
    9690          67 :             property.nonData(constraints()))
    9691             :         {
    9692          20 :             trackOptimizationOutcome(TrackedOutcome::NotFixedSlot);
    9693          20 :             return UINT32_MAX;
    9694             :         }
    9695             : 
    9696             :         // Definite slots will always be fixed slots when they are in the
    9697             :         // allowable range for fixed slots, except for objects which were
    9698             :         // converted from unboxed objects and have a smaller allocation size.
    9699          67 :         size_t nfixed = NativeObject::MAX_FIXED_SLOTS;
    9700          67 :         if (ObjectGroup* group = key->group()->maybeOriginalUnboxedGroup())
    9701           0 :             nfixed = gc::GetGCKindSlots(group->unboxedLayout().getAllocKind());
    9702             : 
    9703          67 :         uint32_t propertySlot = property.maybeTypes()->definiteSlot();
    9704          67 :         if (slot == UINT32_MAX) {
    9705          67 :             slot = propertySlot;
    9706          67 :             *pnfixed = nfixed;
    9707           0 :         } else if (slot != propertySlot || nfixed != *pnfixed) {
    9708           0 :             trackOptimizationOutcome(TrackedOutcome::InconsistentFixedSlot);
    9709           0 :             return UINT32_MAX;
    9710             :         }
    9711             :     }
    9712             : 
    9713          67 :     return slot;
    9714             : }
    9715             : 
    9716             : uint32_t
    9717          79 : IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name, JSValueType* punboxedType)
    9718             : {
    9719          79 :     if (!types || types->unknownObject() || !types->objectOrSentinel()) {
    9720           8 :         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
    9721           8 :         return UINT32_MAX;
    9722             :     }
    9723             : 
    9724          71 :     uint32_t offset = UINT32_MAX;
    9725             : 
    9726          71 :     for (size_t i = 0; i < types->getObjectCount(); i++) {
    9727          71 :         TypeSet::ObjectKey* key = types->getObject(i);
    9728          71 :         if (!key)
    9729           0 :             continue;
    9730             : 
    9731          71 :         if (key->unknownProperties()) {
    9732           0 :             trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
    9733           0 :             return UINT32_MAX;
    9734             :         }
    9735             : 
    9736          71 :         if (key->isSingleton()) {
    9737           0 :             trackOptimizationOutcome(TrackedOutcome::Singleton);
    9738           0 :             return UINT32_MAX;
    9739             :         }
    9740             : 
    9741          71 :         UnboxedLayout* layout = key->group()->maybeUnboxedLayout();
    9742          71 :         if (!layout) {
    9743          71 :             trackOptimizationOutcome(TrackedOutcome::NotUnboxed);
    9744          71 :             return UINT32_MAX;
    9745             :         }
    9746             : 
    9747           0 :         const UnboxedLayout::Property* property = layout->lookup(name);
    9748           0 :         if (!property) {
    9749           0 :             trackOptimizationOutcome(TrackedOutcome::StructNoField);
    9750           0 :             return UINT32_MAX;
    9751             :         }
    9752             : 
    9753           0 :         if (layout->nativeGroup()) {
    9754           0 :             trackOptimizationOutcome(TrackedOutcome::UnboxedConvertedToNative);
    9755           0 :             return UINT32_MAX;
    9756             :         }
    9757             : 
    9758           0 :         key->watchStateChangeForUnboxedConvertedToNative(constraints());
    9759             : 
    9760           0 :         if (offset == UINT32_MAX) {
    9761           0 :             offset = property->offset;
    9762           0 :             *punboxedType = property->type;
    9763           0 :         } else if (offset != property->offset) {
    9764           0 :             trackOptimizationOutcome(TrackedOutcome::InconsistentFieldOffset);
    9765           0 :             return UINT32_MAX;
    9766           0 :         } else if (*punboxedType != property->type) {
    9767           0 :             trackOptimizationOutcome(TrackedOutcome::InconsistentFieldType);
    9768           0 :             return UINT32_MAX;
    9769             :         }
    9770             :     }
    9771             : 
    9772           0 :     return offset;
    9773             : }
    9774             : 
    9775             : AbortReasonOr<Ok>
    9776           0 : IonBuilder::jsop_runonce()
    9777             : {
    9778           0 :     MRunOncePrologue* ins = MRunOncePrologue::New(alloc());
    9779           0 :     current->add(ins);
    9780           0 :     return resumeAfter(ins);
    9781             : }
    9782             : 
    9783             : AbortReasonOr<Ok>
    9784         127 : IonBuilder::jsop_not()
    9785             : {
    9786         127 :     MDefinition* value = current->pop();
    9787             : 
    9788         127 :     MNot* ins = MNot::New(alloc(), value, constraints());
    9789         127 :     current->add(ins);
    9790         127 :     current->push(ins);
    9791         127 :     return Ok();
    9792             : }
    9793             : 
    9794             : NativeObject*
    9795           0 : IonBuilder::commonPrototypeWithGetterSetter(TemporaryTypeSet* types, PropertyName* name,
    9796             :                                             bool isGetter, JSFunction* getterOrSetter,
    9797             :                                             bool* guardGlobal)
    9798             : {
    9799             :     // If there's a single object on the proto chain of all objects in |types|
    9800             :     // that contains a property |name| with |getterOrSetter| getter or setter
    9801             :     // function, return that object.
    9802             : 
    9803             :     // No sense looking if we don't know what's going on.
    9804           0 :     if (!types || types->unknownObject())
    9805           0 :         return nullptr;
    9806           0 :     *guardGlobal = false;
    9807             : 
    9808           0 :     NativeObject* foundProto = nullptr;
    9809           0 :     for (unsigned i = 0; i < types->getObjectCount(); i++) {
    9810           0 :         TypeSet::ObjectKey* key = types->getObject(i);
    9811           0 :         if (!key)
    9812           0 :             continue;
    9813             : 
    9814           0 :         while (key) {
    9815           0 :             if (key->unknownProperties())
    9816           0 :                 return nullptr;
    9817             : 
    9818           0 :             const Class* clasp = key->clasp();
    9819           0 :             if (!ClassHasEffectlessLookup(clasp))
    9820           0 :                 return nullptr;
    9821           0 :             JSObject* singleton = key->isSingleton() ? key->singleton() : nullptr;
    9822           0 :             if (ObjectHasExtraOwnProperty(compartment, key, NameToId(name))) {
    9823           0 :                 if (!singleton || !singleton->is<GlobalObject>())
    9824           0 :                     return nullptr;
    9825           0 :                 *guardGlobal = true;
    9826             :             }
    9827             : 
    9828             :             // Look for a getter/setter on the class itself which may need
    9829             :             // to be called.
    9830           0 :             if (isGetter && clasp->getOpsGetProperty())
    9831           0 :                 return nullptr;
    9832           0 :             if (!isGetter && clasp->getOpsSetProperty())
    9833           0 :                 return nullptr;
    9834             : 
    9835             :             // If we have a singleton, check if it contains the getter or
    9836             :             // setter we're looking for. Note that we don't need to add any
    9837             :             // type constraints as the caller will add a Shape guard for the
    9838             :             // holder and type constraints for other objects on the proto
    9839             :             // chain.
    9840             :             //
    9841             :             // If the object is not a singleton, we fall back to the code below
    9842             :             // and check whether the property is missing. That's fine because
    9843             :             // we're looking for a getter or setter on the proto chain and
    9844             :             // these objects are singletons.
    9845           0 :             if (singleton) {
    9846           0 :                 if (!singleton->is<NativeObject>())
    9847           0 :                     return nullptr;
    9848             : 
    9849           0 :                 NativeObject* singletonNative = &singleton->as<NativeObject>();
    9850           0 :                 if (Shape* propShape = singletonNative->lookupPure(name)) {
    9851             :                     // We found a property. Check if it's the getter or setter
    9852             :                     // we're looking for.
    9853           0 :                     Value getterSetterVal = ObjectValue(*getterOrSetter);
    9854           0 :                     if (isGetter) {
    9855           0 :                         if (propShape->getterOrUndefined() != getterSetterVal)
    9856           0 :                             return nullptr;
    9857             :                     } else {
    9858           0 :                         if (propShape->setterOrUndefined() != getterSetterVal)
    9859           0 :                             return nullptr;
    9860             :                     }
    9861             : 
    9862           0 :                     if (!foundProto)
    9863           0 :                         foundProto = singletonNative;
    9864           0 :                     else if (foundProto != singletonNative)
    9865           0 :                         return nullptr;
    9866           0 :                     break;
    9867             :                 }
    9868             :             }
    9869             : 
    9870             :             // Test for isOwnProperty() without freezing. If we end up
    9871             :             // optimizing, freezePropertiesForCommonPropFunc will freeze the
    9872             :             // property type sets later on.
    9873           0 :             HeapTypeSetKey property = key->property(NameToId(name));
    9874           0 :             if (TypeSet* types = property.maybeTypes()) {
    9875           0 :                 if (!types->empty() || types->nonDataProperty())
    9876           0 :                     return nullptr;
    9877             :             }
    9878           0 :             if (singleton) {
    9879           0 :                 if (CanHaveEmptyPropertyTypesForOwnProperty(singleton)) {
    9880           0 :                     MOZ_ASSERT(singleton->is<GlobalObject>());
    9881           0 :                     *guardGlobal = true;
    9882             :                 }
    9883             :             }
    9884             : 
    9885           0 :             JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
    9886           0 :             if (foundProto && proto == foundProto) {
    9887             :                 // We found an object on the proto chain that's known to have
    9888             :                 // the getter or setter property, so we can stop looking.
    9889           0 :                 break;
    9890             :             }
    9891             : 
    9892           0 :             if (!proto) {
    9893             :                 // The getter or setter being searched for did not show up on
    9894             :                 // the object's prototype chain.
    9895           0 :                 return nullptr;
    9896             :             }
    9897           0 :             key = TypeSet::ObjectKey::get(proto);
    9898             :         }
    9899             :     }
    9900             : 
    9901           0 :     return foundProto;
    9902             : }
    9903             : 
    9904             : void
    9905           0 : IonBuilder::freezePropertiesForCommonPrototype(TemporaryTypeSet* types, PropertyName* name,
    9906             :                                                JSObject* foundProto,
    9907             :                                                bool allowEmptyTypesforGlobal/* = false*/)
    9908             : {
    9909           0 :     for (unsigned i = 0; i < types->getObjectCount(); i++) {
    9910             :         // If we found a Singleton object's own-property, there's nothing to
    9911             :         // freeze.
    9912           0 :         if (types->getSingleton(i) == foundProto)
    9913           0 :             continue;
    9914             : 
    9915           0 :         TypeSet::ObjectKey* key = types->getObject(i);
    9916           0 :         if (!key)
    9917           0 :             continue;
    9918             : 
    9919             :         while (true) {
    9920           0 :             HeapTypeSetKey property = key->property(NameToId(name));
    9921           0 :             JS_ALWAYS_TRUE(!property.isOwnProperty(constraints(), allowEmptyTypesforGlobal));
    9922             : 
    9923             :             // Don't mark the proto. It will be held down by the shape
    9924             :             // guard. This allows us to use properties found on prototypes
    9925             :             // with properties unknown to TI.
    9926           0 :             if (key->proto() == TaggedProto(foundProto))
    9927           0 :                 break;
    9928           0 :             key = TypeSet::ObjectKey::get(key->proto().toObjectOrNull());
    9929           0 :         }
    9930             :     }
    9931           0 : }
    9932             : 
    9933             : bool
    9934           0 : IonBuilder::testCommonGetterSetter(TemporaryTypeSet* types, PropertyName* name,
    9935             :                                    bool isGetter, JSFunction* getterOrSetter,
    9936             :                                    MDefinition** guard,
    9937             :                                    Shape* globalShape/* = nullptr*/,
    9938             :                                    MDefinition** globalGuard/* = nullptr */)
    9939             : {
    9940           0 :     MOZ_ASSERT(getterOrSetter);
    9941           0 :     MOZ_ASSERT_IF(globalShape, globalGuard);
    9942             :     bool guardGlobal;
    9943             : 
    9944             :     // Check if all objects being accessed will lookup the name through foundProto.
    9945             :     NativeObject* foundProto =
    9946           0 :         commonPrototypeWithGetterSetter(types, name, isGetter, getterOrSetter, &guardGlobal);
    9947           0 :     if (!foundProto || (guardGlobal && !globalShape)) {
    9948           0 :         trackOptimizationOutcome(TrackedOutcome::MultiProtoPaths);
    9949           0 :         return false;
    9950             :     }
    9951             : 
    9952             :     // We can optimize the getter/setter, so freeze all involved properties to
    9953             :     // ensure there isn't a lower shadowing getter or setter installed in the
    9954             :     // future.
    9955           0 :     freezePropertiesForCommonPrototype(types, name, foundProto, guardGlobal);
    9956             : 
    9957             :     // Add a shape guard on the prototype we found the property on. The rest of
    9958             :     // the prototype chain is guarded by TI freezes, except when name is a global
    9959             :     // name. In this case, we also have to guard on the globals shape to be able
    9960             :     // to optimize, because the way global property sets are handled means
    9961             :     // freezing doesn't work for what we want here. Note that a shape guard is
    9962             :     // good enough here, even in the proxy case, because we have ensured there
    9963             :     // are no lookup hooks for this property.
    9964           0 :     if (guardGlobal) {
    9965           0 :         JSObject* obj = &script()->global();
    9966           0 :         MDefinition* globalObj = constant(ObjectValue(*obj));
    9967           0 :         *globalGuard = addShapeGuard(globalObj, globalShape, Bailout_ShapeGuard);
    9968             :     }
    9969             : 
    9970             :     // If the getter/setter is not configurable we don't have to guard on the
    9971             :     // proto's shape.
    9972           0 :     Shape* propShape = foundProto->lookupPure(name);
    9973           0 :     MOZ_ASSERT_IF(isGetter, propShape->getterObject() == getterOrSetter);
    9974           0 :     MOZ_ASSERT_IF(!isGetter, propShape->setterObject() == getterOrSetter);
    9975           0 :     if (propShape && !propShape->configurable())
    9976           0 :         return true;
    9977             : 
    9978           0 :     MInstruction* wrapper = constant(ObjectValue(*foundProto));
    9979           0 :     *guard = addShapeGuard(wrapper, foundProto->lastProperty(), Bailout_ShapeGuard);
    9980           0 :     return true;
    9981             : }
    9982             : 
    9983             : void
    9984        1269 : IonBuilder::replaceMaybeFallbackFunctionGetter(MGetPropertyCache* cache)
    9985             : {
    9986             :     // Discard the last prior resume point of the previous MGetPropertyCache.
    9987        2538 :     WrapMGetPropertyCache rai(maybeFallbackFunctionGetter_);
    9988        1269 :     maybeFallbackFunctionGetter_ = cache;
    9989        1269 : }
    9990             : 
    9991             : AbortReasonOr<Ok>
    9992           3 : IonBuilder::annotateGetPropertyCache(MDefinition* obj, PropertyName* name,
    9993             :                                      MGetPropertyCache* getPropCache, TemporaryTypeSet* objTypes,
    9994             :                                      TemporaryTypeSet* pushedTypes)
    9995             : {
    9996             :     // Ensure every pushed value is a singleton.
    9997           3 :     if (pushedTypes->unknownObject() || pushedTypes->baseFlags() != 0)
    9998           0 :         return Ok();
    9999             : 
   10000           6 :     for (unsigned i = 0; i < pushedTypes->getObjectCount(); i++) {
   10001           3 :         if (pushedTypes->getGroup(i) != nullptr)
   10002           0 :             return Ok();
   10003             :     }
   10004             : 
   10005             :     // Object's typeset should be a proper object
   10006           3 :     if (!objTypes || objTypes->baseFlags() || objTypes->unknownObject())
   10007           0 :         return Ok();
   10008             : 
   10009           3 :     unsigned int objCount = objTypes->getObjectCount();
   10010           3 :     if (objCount == 0)
   10011           0 :         return Ok();
   10012             : 
   10013           3 :     InlinePropertyTable* inlinePropTable = getPropCache->initInlinePropertyTable(alloc(), pc);
   10014           3 :     if (!inlinePropTable)
   10015           0 :         return abort(AbortReason::Alloc);
   10016             : 
   10017             :     // Ensure that the relevant property typeset for each group is
   10018             :     // is a single-object typeset containing a JSFunction
   10019           6 :     for (unsigned int i = 0; i < objCount; i++) {
   10020           3 :         ObjectGroup* group = objTypes->getGroup(i);
   10021           3 :         if (!group)
   10022           3 :             continue;
   10023           3 :         TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(group);
   10024           3 :         if (key->unknownProperties() || !key->proto().isObject())
   10025           0 :             continue;
   10026           3 :         JSObject* proto = checkNurseryObject(key->proto().toObject());
   10027             : 
   10028           3 :         const Class* clasp = key->clasp();
   10029           3 :         if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key, NameToId(name)))
   10030           3 :             continue;
   10031             : 
   10032           0 :         HeapTypeSetKey ownTypes = key->property(NameToId(name));
   10033           0 :         if (ownTypes.isOwnProperty(constraints()))
   10034           0 :             continue;
   10035             : 
   10036           0 :         JSObject* singleton = testSingletonProperty(proto, NameToId(name));
   10037           0 :         if (!singleton || !singleton->is<JSFunction>())
   10038           0 :             continue;
   10039             : 
   10040             :         // Don't add cases corresponding to non-observed pushes
   10041           0 :         if (!pushedTypes->hasType(TypeSet::ObjectType(singleton)))
   10042           0 :             continue;
   10043             : 
   10044           0 :         if (!inlinePropTable->addEntry(alloc(), group, &singleton->as<JSFunction>()))
   10045           0 :             return abort(AbortReason::Alloc);
   10046             :     }
   10047             : 
   10048           3 :     if (inlinePropTable->numEntries() == 0) {
   10049           3 :         getPropCache->clearInlinePropertyTable();
   10050           3 :         return Ok();
   10051             :     }
   10052             : 
   10053             : #ifdef JS_JITSPEW
   10054           0 :     if (inlinePropTable->numEntries() > 0)
   10055           0 :         JitSpew(JitSpew_Inlining, "Annotated GetPropertyCache with %d/%d inline cases",
   10056           0 :                                     (int) inlinePropTable->numEntries(), (int) objCount);
   10057             : #endif
   10058             : 
   10059             :     // If we successfully annotated the GetPropertyCache and there are inline cases,
   10060             :     // then keep a resume point of the state right before this instruction for use
   10061             :     // later when we have to bail out to this point in the fallback case of a
   10062             :     // PolyInlineDispatch.
   10063           0 :     if (inlinePropTable->numEntries() > 0) {
   10064             :         // Push the object back onto the stack temporarily to capture the resume point.
   10065           0 :         current->push(obj);
   10066           0 :         MResumePoint* resumePoint = MResumePoint::New(alloc(), current, pc,
   10067           0 :                                                       MResumePoint::ResumeAt);
   10068           0 :         if (!resumePoint)
   10069           0 :             return abort(AbortReason::Alloc);
   10070           0 :         inlinePropTable->setPriorResumePoint(resumePoint);
   10071           0 :         replaceMaybeFallbackFunctionGetter(getPropCache);
   10072           0 :         current->pop();
   10073             :     }
   10074           0 :     return Ok();
   10075             : }
   10076             : 
   10077             : // Returns true if an idempotent cache has ever invalidated this script
   10078             : // or an outer script.
   10079             : bool
   10080          14 : IonBuilder::invalidatedIdempotentCache()
   10081             : {
   10082          14 :     IonBuilder* builder = this;
   10083           6 :     do {
   10084          20 :         if (builder->script()->invalidatedIdempotentCache())
   10085           0 :             return true;
   10086          20 :         builder = builder->callerBuilder_;
   10087          20 :     } while (builder);
   10088             : 
   10089          14 :     return false;
   10090             : }
   10091             : 
   10092             : AbortReasonOr<Ok>
   10093           9 : IonBuilder::loadSlot(MDefinition* obj, size_t slot, size_t nfixed, MIRType rvalType,
   10094             :                      BarrierKind barrier, TemporaryTypeSet* types)
   10095             : {
   10096           9 :     if (slot < nfixed) {
   10097           0 :         MLoadFixedSlot* load = MLoadFixedSlot::New(alloc(), obj, slot);
   10098           0 :         current->add(load);
   10099           0 :         current->push(load);
   10100             : 
   10101           0 :         load->setResultType(rvalType);
   10102           0 :         return pushTypeBarrier(load, types, barrier);
   10103             :     }
   10104             : 
   10105           9 :     MSlots* slots = MSlots::New(alloc(), obj);
   10106           9 :     current->add(slots);
   10107             : 
   10108           9 :     MLoadSlot* load = MLoadSlot::New(alloc(), slots, slot - nfixed);
   10109           9 :     current->add(load);
   10110           9 :     current->push(load);
   10111             : 
   10112           9 :     load->setResultType(rvalType);
   10113           9 :     return pushTypeBarrier(load, types, barrier);
   10114             : }
   10115             : 
   10116             : AbortReasonOr<Ok>
   10117           9 : IonBuilder::loadSlot(MDefinition* obj, Shape* shape, MIRType rvalType,
   10118             :                      BarrierKind barrier, TemporaryTypeSet* types)
   10119             : {
   10120           9 :     return loadSlot(obj, shape->slot(), shape->numFixedSlots(), rvalType, barrier, types);
   10121             : }
   10122             : 
   10123             : AbortReasonOr<Ok>
   10124           0 : IonBuilder::storeSlot(MDefinition* obj, size_t slot, size_t nfixed,
   10125             :                       MDefinition* value, bool needsBarrier,
   10126             :                       MIRType slotType /* = MIRType::None */)
   10127             : {
   10128           0 :     if (slot < nfixed) {
   10129           0 :         MStoreFixedSlot* store = MStoreFixedSlot::New(alloc(), obj, slot, value);
   10130           0 :         current->add(store);
   10131           0 :         current->push(value);
   10132           0 :         if (needsBarrier)
   10133           0 :             store->setNeedsBarrier();
   10134           0 :         return resumeAfter(store);
   10135             :     }
   10136             : 
   10137           0 :     MSlots* slots = MSlots::New(alloc(), obj);
   10138           0 :     current->add(slots);
   10139             : 
   10140           0 :     MStoreSlot* store = MStoreSlot::New(alloc(), slots, slot - nfixed, value);
   10141           0 :     current->add(store);
   10142           0 :     current->push(value);
   10143           0 :     if (needsBarrier)
   10144           0 :         store->setNeedsBarrier();
   10145           0 :     if (slotType != MIRType::None)
   10146           0 :         store->setSlotType(slotType);
   10147           0 :     return resumeAfter(store);
   10148             : }
   10149             : 
   10150             : AbortReasonOr<Ok>
   10151           0 : IonBuilder::storeSlot(MDefinition* obj, Shape* shape, MDefinition* value, bool needsBarrier,
   10152             :                       MIRType slotType /* = MIRType::None */)
   10153             : {
   10154           0 :     MOZ_ASSERT(shape->writable());
   10155           0 :     return storeSlot(obj, shape->slot(), shape->numFixedSlots(), value, needsBarrier, slotType);
   10156             : }
   10157             : 
   10158             : bool
   10159         587 : IonBuilder::shouldAbortOnPreliminaryGroups(MDefinition *obj)
   10160             : {
   10161             :     // Watch for groups which still have preliminary object information and
   10162             :     // have not had the new script properties or unboxed layout analyses
   10163             :     // performed. Normally this is done after a small number of the objects
   10164             :     // have been created, but if only a few have been created we can still
   10165             :     // perform the analysis with a smaller object population. The analysis can
   10166             :     // have side effects so we will end up aborting compilation after building
   10167             :     // finishes and retrying later.
   10168         587 :     TemporaryTypeSet *types = obj->resultTypeSet();
   10169         587 :     if (!types || types->unknownObject())
   10170         249 :         return false;
   10171             : 
   10172         338 :     bool preliminary = false;
   10173         560 :     for (size_t i = 0; i < types->getObjectCount(); i++) {
   10174         222 :         TypeSet::ObjectKey* key = types->getObject(i);
   10175         222 :         if (!key)
   10176           0 :             continue;
   10177             : 
   10178         222 :         if (ObjectGroup* group = key->maybeGroup()) {
   10179         222 :             if (group->hasUnanalyzedPreliminaryObjects()) {
   10180          10 :                 addAbortedPreliminaryGroup(group);
   10181          10 :                 preliminary = true;
   10182             :             }
   10183             :         }
   10184             :     }
   10185             : 
   10186         338 :     return preliminary;
   10187             : }
   10188             : 
   10189             : MDefinition*
   10190         510 : IonBuilder::maybeUnboxForPropertyAccess(MDefinition* def)
   10191             : {
   10192         510 :     if (def->type() != MIRType::Value)
   10193         320 :         return def;
   10194             : 
   10195         190 :     MIRType type = inspector->expectedPropertyAccessInputType(pc);
   10196         190 :     if (type == MIRType::Value || !def->mightBeType(type))
   10197         190 :         return def;
   10198             : 
   10199           0 :     MUnbox* unbox = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
   10200           0 :     current->add(unbox);
   10201             : 
   10202             :     // Fixup type information for a common case where a property call
   10203             :     // is converted to the following bytecodes
   10204             :     //
   10205             :     //      a.foo()
   10206             :     // ================= Compiles to ================
   10207             :     //      LOAD "a"
   10208             :     //      DUP
   10209             :     //      CALLPROP "foo"
   10210             :     //      SWAP
   10211             :     //      CALL 0
   10212             :     //
   10213             :     // If we have better type information to unbox the first copy going into
   10214             :     // the CALLPROP operation, we can replace the duplicated copy on the
   10215             :     // stack as well.
   10216           0 :     if (*pc == JSOP_CALLPROP || *pc == JSOP_CALLELEM) {
   10217           0 :         uint32_t idx = current->stackDepth() - 1;
   10218           0 :         MOZ_ASSERT(current->getSlot(idx) == def);
   10219           0 :         current->setSlot(idx, unbox);
   10220             :     }
   10221             : 
   10222           0 :     return unbox;
   10223             : }
   10224             : 
   10225             : AbortReasonOr<Ok>
   10226         482 : IonBuilder::jsop_getprop(PropertyName* name)
   10227             : {
   10228         482 :     bool emitted = false;
   10229         482 :     startTrackingOptimizations();
   10230             : 
   10231         482 :     MDefinition* obj = current->pop();
   10232         482 :     TemporaryTypeSet* types = bytecodeTypes(pc);
   10233             : 
   10234         482 :     trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet());
   10235             : 
   10236         482 :     if (!info().isAnalysis()) {
   10237             :         // The calls below can abort compilation, so we only try this if we're
   10238             :         // not analyzing.
   10239             :         // Try to optimize arguments.length.
   10240          76 :         trackOptimizationAttempt(TrackedStrategy::GetProp_ArgumentsLength);
   10241          76 :         MOZ_TRY(getPropTryArgumentsLength(&emitted, obj));
   10242          76 :         if (emitted)
   10243           2 :             return Ok();
   10244             : 
   10245             :         // Try to optimize arguments.callee.
   10246          74 :         trackOptimizationAttempt(TrackedStrategy::GetProp_ArgumentsCallee);
   10247          74 :         MOZ_TRY(getPropTryArgumentsCallee(&emitted, obj, name));
   10248          74 :         if (emitted)
   10249           0 :             return Ok();
   10250             :     }
   10251             : 
   10252         480 :     obj = maybeUnboxForPropertyAccess(obj);
   10253         480 :     if (obj->type() == MIRType::Object)
   10254         287 :         obj = convertUnboxedObjects(obj);
   10255             : 
   10256         480 :     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
   10257         480 :                                                        obj, name, types);
   10258             : 
   10259             :     // Try to optimize to a specific constant.
   10260         480 :     trackOptimizationAttempt(TrackedStrategy::GetProp_InferredConstant);
   10261         480 :     if (barrier == BarrierKind::NoBarrier) {
   10262         240 :         MOZ_TRY(getPropTryInferredConstant(&emitted, obj, name, types));
   10263         240 :         if (emitted)
   10264           0 :             return Ok();
   10265             :     } else {
   10266         240 :         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
   10267             :     }
   10268             : 
   10269             :     // Always use a call if we are performing analysis and
   10270             :     // not actually emitting code, to simplify later analysis. Also skip deeper
   10271             :     // analysis if there are no known types for this operation, as it will
   10272             :     // always invalidate when executing.
   10273         480 :     if (info().isAnalysis() || types->empty() || shouldAbortOnPreliminaryGroups(obj)) {
   10274         418 :         if (types->empty()) {
   10275             :             // Since no further optimizations will be tried, use the IC
   10276             :             // strategy, which would have been the last one to be tried, as a
   10277             :             // sentinel value for why everything failed.
   10278         414 :             trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
   10279         414 :             trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
   10280             :         }
   10281             : 
   10282         418 :         MCallGetProperty* call = MCallGetProperty::New(alloc(), obj, name);
   10283         418 :         current->add(call);
   10284             : 
   10285             :         // During the definite properties analysis we can still try to bake in
   10286             :         // constants read off the prototype chain, to allow inlining later on.
   10287             :         // In this case we still need the getprop call so that the later
   10288             :         // analysis knows when the |this| value has been read from.
   10289         418 :         if (info().isAnalysis()) {
   10290         406 :             MOZ_TRY(getPropTryConstant(&emitted, obj, NameToId(name), types));
   10291         406 :             if (emitted)
   10292           0 :                 return Ok();
   10293             :         }
   10294             : 
   10295         418 :         current->push(call);
   10296         418 :         MOZ_TRY(resumeAfter(call));
   10297         418 :         return pushTypeBarrier(call, types, BarrierKind::TypeSet);
   10298             :     }
   10299             : 
   10300             :     // Try to optimize accesses on outer window proxies, for example window.foo.
   10301             :     // This needs to come before the various strategies getPropTryInnerize tries
   10302             :     // internally, since some of those strategies will "succeed" in silly ways
   10303             :     // even for an outer object.
   10304          62 :     trackOptimizationAttempt(TrackedStrategy::GetProp_Innerize);
   10305          62 :     MOZ_TRY(getPropTryInnerize(&emitted, obj, name, types));
   10306          62 :     if (emitted)
   10307           0 :         return Ok();
   10308             : 
   10309          62 :     if (!forceInlineCaches()) {
   10310             :         // Try to hardcode known constants.
   10311          62 :         trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
   10312          62 :         MOZ_TRY(getPropTryConstant(&emitted, obj, NameToId(name), types));
   10313          62 :         if (emitted)
   10314          15 :             return Ok();
   10315             : 
   10316             :         // Try to hardcode known not-defined
   10317          47 :         trackOptimizationAttempt(TrackedStrategy::GetProp_NotDefined);
   10318          47 :         MOZ_TRY(getPropTryNotDefined(&emitted, obj, NameToId(name), types));
   10319          47 :         if (emitted)
   10320           8 :             return Ok();
   10321             : 
   10322             :         // Try to emit loads from definite slots.
   10323          39 :         trackOptimizationAttempt(TrackedStrategy::GetProp_DefiniteSlot);
   10324          39 :         MOZ_TRY(getPropTryDefiniteSlot(&emitted, obj, name, barrier, types));
   10325          39 :         if (emitted)
   10326          16 :             return Ok();
   10327             : 
   10328             :         // Try to emit loads from unboxed objects.
   10329          23 :         trackOptimizationAttempt(TrackedStrategy::GetProp_Unboxed);
   10330          23 :         MOZ_TRY(getPropTryUnboxed(&emitted, obj, name, barrier, types));
   10331          23 :         if (emitted)
   10332           0 :             return Ok();
   10333             : 
   10334             :         // Try to inline a common property getter, or make a call.
   10335          23 :         trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
   10336          23 :         MOZ_TRY(getPropTryCommonGetter(&emitted, obj, name, types));
   10337          23 :         if (emitted)
   10338           0 :             return Ok();
   10339             : 
   10340             :         // Try to emit a monomorphic/polymorphic access based on baseline caches.
   10341          23 :         trackOptimizationAttempt(TrackedStrategy::GetProp_InlineAccess);
   10342          23 :         MOZ_TRY(getPropTryInlineAccess(&emitted, obj, name, barrier, types));
   10343          23 :         if (emitted)
   10344           9 :             return Ok();
   10345             : 
   10346             :         // Try to emit loads from a module namespace.
   10347          14 :         trackOptimizationAttempt(TrackedStrategy::GetProp_ModuleNamespace);
   10348          14 :         MOZ_TRY(getPropTryModuleNamespace(&emitted, obj, name, barrier, types));
   10349          14 :         if (emitted)
   10350           0 :             return Ok();
   10351             : 
   10352             :         // Try to emit loads from known binary data blocks
   10353          14 :         trackOptimizationAttempt(TrackedStrategy::GetProp_TypedObject);
   10354          14 :         MOZ_TRY(getPropTryTypedObject(&emitted, obj, name));
   10355          14 :         if (emitted)
   10356           0 :             return Ok();
   10357             :     }
   10358             : 
   10359             :     // Emit a polymorphic cache.
   10360          14 :     trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
   10361          14 :     return getPropAddCache(obj, name, barrier, types);
   10362             : }
   10363             : 
   10364             : AbortReasonOr<Ok>
   10365          68 : IonBuilder::improveThisTypesForCall()
   10366             : {
   10367             :     // After a CALLPROP (or CALLELEM) for obj.prop(), the this-value and callee
   10368             :     // for the call are on top of the stack:
   10369             :     //
   10370             :     // ... [this: obj], [callee: obj.prop]
   10371             :     //
   10372             :     // If obj is null or undefined, obj.prop would have thrown an exception so
   10373             :     // at this point we can remove null and undefined from obj's TypeSet, to
   10374             :     // improve type information for the call that will follow.
   10375             : 
   10376          68 :     MOZ_ASSERT(*pc == JSOP_CALLPROP || *pc == JSOP_CALLELEM);
   10377             : 
   10378             :     // Ensure |this| has types {object, null/undefined}.
   10379          68 :     MDefinition* thisDef = current->peek(-2);
   10380         177 :     if (thisDef->type() != MIRType::Value ||
   10381          41 :         !thisDef->mightBeType(MIRType::Object) ||
   10382          68 :         !thisDef->resultTypeSet() ||
   10383           0 :         !thisDef->resultTypeSet()->objectOrSentinel())
   10384             :     {
   10385          68 :         return Ok();
   10386             :     }
   10387             : 
   10388             :     // Remove null/undefined from the TypeSet.
   10389           0 :     TemporaryTypeSet* types = thisDef->resultTypeSet()->cloneObjectsOnly(alloc_->lifoAlloc());
   10390           0 :     if (!types)
   10391           0 :         return abort(AbortReason::Alloc);
   10392             : 
   10393           0 :     MFilterTypeSet* filter = MFilterTypeSet::New(alloc(), thisDef, types);
   10394           0 :     current->add(filter);
   10395           0 :     current->rewriteAtDepth(-2, filter);
   10396             : 
   10397             :     // FilterTypeSetPolicy::adjustInputs will insert an infallible Unbox(Object)
   10398             :     // for the input. Don't hoist this unbox above the getprop or getelem
   10399             :     // operation.
   10400           0 :     filter->setDependency(current->peek(-1)->toInstruction());
   10401           0 :     return Ok();
   10402             : }
   10403             : 
   10404             : AbortReasonOr<Ok>
   10405          10 : IonBuilder::checkIsDefinitelyOptimizedArguments(MDefinition* obj, bool* isOptimizedArgs)
   10406             : {
   10407          10 :     if (obj->type() != MIRType::MagicOptimizedArguments) {
   10408           9 :         if (script()->argumentsHasVarBinding() &&
   10409           1 :             obj->mightBeType(MIRType::MagicOptimizedArguments))
   10410             :         {
   10411           0 :             return abort(AbortReason::Disable, "Type is not definitely lazy arguments.");
   10412             :         }
   10413             : 
   10414           8 :         *isOptimizedArgs = false;
   10415           8 :         return Ok();
   10416             :     }
   10417             : 
   10418           2 :     *isOptimizedArgs = true;
   10419           2 :     return Ok();
   10420             : }
   10421             : 
   10422             : AbortReasonOr<Ok>
   10423         240 : IonBuilder::getPropTryInferredConstant(bool* emitted, MDefinition* obj, PropertyName* name,
   10424             :                                        TemporaryTypeSet* types)
   10425             : {
   10426         240 :     MOZ_ASSERT(*emitted == false);
   10427             : 
   10428             :     // Need a result typeset to optimize.
   10429         240 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
   10430         240 :     if (!objTypes) {
   10431           0 :         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
   10432           0 :         return Ok();
   10433             :     }
   10434             : 
   10435         240 :     JSObject* singleton = objTypes->maybeSingleton();
   10436         240 :     if (!singleton) {
   10437         240 :         trackOptimizationOutcome(TrackedOutcome::NotSingleton);
   10438         240 :         return Ok();
   10439             :     }
   10440             : 
   10441           0 :     TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(singleton);
   10442           0 :     if (key->unknownProperties()) {
   10443           0 :         trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
   10444           0 :         return Ok();
   10445             :     }
   10446             : 
   10447           0 :     HeapTypeSetKey property = key->property(NameToId(name));
   10448             : 
   10449           0 :     Value constantValue = UndefinedValue();
   10450           0 :     if (property.constant(constraints(), &constantValue)) {
   10451           0 :         spew("Optimized constant property");
   10452           0 :         obj->setImplicitlyUsedUnchecked();
   10453           0 :         pushConstant(constantValue);
   10454           0 :         types->addType(TypeSet::GetValueType(constantValue), alloc_->lifoAlloc());
   10455           0 :         trackOptimizationSuccess();
   10456           0 :         *emitted = true;
   10457             :     }
   10458             : 
   10459           0 :     return Ok();
   10460             : }
   10461             : 
   10462             : AbortReasonOr<Ok>
   10463          76 : IonBuilder::getPropTryArgumentsLength(bool* emitted, MDefinition* obj)
   10464             : {
   10465          76 :     MOZ_ASSERT(*emitted == false);
   10466             : 
   10467          76 :     if (JSOp(*pc) != JSOP_LENGTH)
   10468          66 :         return Ok();
   10469             : 
   10470          10 :     bool isOptimizedArgs = false;
   10471          10 :     MOZ_TRY(checkIsDefinitelyOptimizedArguments(obj, &isOptimizedArgs));
   10472          10 :     if (!isOptimizedArgs)
   10473           8 :         return Ok();
   10474             : 
   10475           2 :     trackOptimizationSuccess();
   10476           2 :     *emitted = true;
   10477             : 
   10478           2 :     obj->setImplicitlyUsedUnchecked();
   10479             : 
   10480             :     // We don't know anything from the callee
   10481           2 :     if (inliningDepth_ == 0) {
   10482           2 :         MInstruction* ins = MArgumentsLength::New(alloc());
   10483           2 :         current->add(ins);
   10484           2 :         current->push(ins);
   10485           2 :         return Ok();
   10486             :     }
   10487             : 
   10488             :     // We are inlining and know the number of arguments the callee pushed
   10489           0 :     pushConstant(Int32Value(inlineCallInfo_->argv().length()));
   10490           0 :     return Ok();
   10491             : }
   10492             : 
   10493             : AbortReasonOr<Ok>
   10494          74 : IonBuilder::getPropTryArgumentsCallee(bool* emitted, MDefinition* obj, PropertyName* name)
   10495             : {
   10496          74 :     MOZ_ASSERT(*emitted == false);
   10497             : 
   10498          74 :     if (name != names().callee)
   10499          74 :         return Ok();
   10500             : 
   10501           0 :     bool isOptimizedArgs = false;
   10502           0 :     MOZ_TRY(checkIsDefinitelyOptimizedArguments(obj, &isOptimizedArgs));
   10503           0 :     if (!isOptimizedArgs)
   10504           0 :         return Ok();
   10505             : 
   10506           0 :     MOZ_ASSERT(script()->hasMappedArgsObj());
   10507             : 
   10508           0 :     obj->setImplicitlyUsedUnchecked();
   10509           0 :     current->push(getCallee());
   10510             : 
   10511           0 :     trackOptimizationSuccess();
   10512           0 :     *emitted = true;
   10513           0 :     return Ok();
   10514             : }
   10515             : 
   10516             : AbortReasonOr<Ok>
   10517         471 : IonBuilder::getPropTryConstant(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types)
   10518             : {
   10519         471 :     MOZ_ASSERT(*emitted == false);
   10520             : 
   10521         471 :     if (!types->mightBeMIRType(MIRType::Object)) {
   10522             :         // If we have not observed an object result here, don't look for a
   10523             :         // singleton constant.
   10524         438 :         trackOptimizationOutcome(TrackedOutcome::NotObject);
   10525         438 :         return Ok();
   10526             :     }
   10527             : 
   10528          33 :     JSObject* singleton = testSingletonPropertyTypes(obj, id);
   10529          33 :     if (!singleton) {
   10530          15 :         trackOptimizationOutcome(TrackedOutcome::NotSingleton);
   10531          15 :         return Ok();
   10532             :     }
   10533             : 
   10534             :     // Property access is a known constant -- safe to emit.
   10535          18 :     obj->setImplicitlyUsedUnchecked();
   10536             : 
   10537          18 :     pushConstant(ObjectValue(*singleton));
   10538             : 
   10539          18 :     trackOptimizationSuccess();
   10540          18 :     *emitted = true;
   10541          18 :     return Ok();
   10542             : }
   10543             : 
   10544             : AbortReasonOr<Ok>
   10545          47 : IonBuilder::getPropTryNotDefined(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types)
   10546             : {
   10547          47 :     MOZ_ASSERT(*emitted == false);
   10548             : 
   10549          47 :     if (!types->mightBeMIRType(MIRType::Undefined)) {
   10550             :         // Only optimize if we expect this property access to return undefined.
   10551          38 :         trackOptimizationOutcome(TrackedOutcome::NotUndefined);
   10552          38 :         return Ok();
   10553             :     }
   10554             : 
   10555             :     bool res;
   10556           9 :     MOZ_TRY_VAR(res, testNotDefinedProperty(obj, id));
   10557           9 :     if (!res) {
   10558           1 :         trackOptimizationOutcome(TrackedOutcome::GenericFailure);
   10559           1 :         return Ok();
   10560             :     }
   10561             : 
   10562           8 :     obj->setImplicitlyUsedUnchecked();
   10563           8 :     pushConstant(UndefinedValue());
   10564             : 
   10565           8 :     trackOptimizationSuccess();
   10566           8 :     *emitted = true;
   10567           8 :     return Ok();
   10568             : }
   10569             : 
   10570             : AbortReasonOr<Ok>
   10571          14 : IonBuilder::getPropTryTypedObject(bool* emitted,
   10572             :                                   MDefinition* obj,
   10573             :                                   PropertyName* name)
   10574             : {
   10575          14 :     TypedObjectPrediction fieldPrediction;
   10576             :     size_t fieldOffset;
   10577             :     size_t fieldIndex;
   10578          14 :     if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
   10579          14 :         return Ok();
   10580             : 
   10581           0 :     switch (fieldPrediction.kind()) {
   10582             :       case type::Simd:
   10583             :         // FIXME (bug 894104): load into a MIRType::float32x4 etc
   10584           0 :         return Ok();
   10585             : 
   10586             :       case type::Struct:
   10587             :       case type::Array:
   10588             :         return getPropTryComplexPropOfTypedObject(emitted,
   10589             :                                                   obj,
   10590             :                                                   fieldOffset,
   10591             :                                                   fieldPrediction,
   10592           0 :                                                   fieldIndex);
   10593             : 
   10594             :       case type::Reference:
   10595             :         return getPropTryReferencePropOfTypedObject(emitted,
   10596             :                                                     obj,
   10597             :                                                     fieldOffset,
   10598             :                                                     fieldPrediction,
   10599           0 :                                                     name);
   10600             : 
   10601             :       case type::Scalar:
   10602             :         return getPropTryScalarPropOfTypedObject(emitted,
   10603             :                                                  obj,
   10604             :                                                  fieldOffset,
   10605           0 :                                                  fieldPrediction);
   10606             :     }
   10607             : 
   10608           0 :     MOZ_CRASH("Bad kind");
   10609             : }
   10610             : 
   10611             : AbortReasonOr<Ok>
   10612           0 : IonBuilder::getPropTryScalarPropOfTypedObject(bool* emitted, MDefinition* typedObj,
   10613             :                                               int32_t fieldOffset,
   10614             :                                               TypedObjectPrediction fieldPrediction)
   10615             : {
   10616             :     // Must always be loading the same scalar type
   10617           0 :     Scalar::Type fieldType = fieldPrediction.scalarType();
   10618             : 
   10619             :     // Don't optimize if the typed object's underlying buffer may be detached.
   10620           0 :     TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
   10621           0 :     if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
   10622           0 :         return Ok();
   10623             : 
   10624           0 :     trackOptimizationSuccess();
   10625           0 :     *emitted = true;
   10626             : 
   10627           0 :     LinearSum byteOffset(alloc());
   10628           0 :     if (!byteOffset.add(fieldOffset))
   10629           0 :         return abort(AbortReason::Disable, "Overflow of field offsets.");
   10630             : 
   10631           0 :     return pushScalarLoadFromTypedObject(typedObj, byteOffset, fieldType);
   10632             : }
   10633             : 
   10634             : AbortReasonOr<Ok>
   10635           0 : IonBuilder::getPropTryReferencePropOfTypedObject(bool* emitted, MDefinition* typedObj,
   10636             :                                                  int32_t fieldOffset,
   10637             :                                                  TypedObjectPrediction fieldPrediction,
   10638             :                                                  PropertyName* name)
   10639             : {
   10640           0 :     ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
   10641             : 
   10642           0 :     TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
   10643           0 :     if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
   10644           0 :         return Ok();
   10645             : 
   10646           0 :     trackOptimizationSuccess();
   10647           0 :     *emitted = true;
   10648             : 
   10649           0 :     LinearSum byteOffset(alloc());
   10650           0 :     if (!byteOffset.add(fieldOffset))
   10651           0 :         return abort(AbortReason::Disable, "Overflow of field offsets.");
   10652             : 
   10653           0 :     return pushReferenceLoadFromTypedObject(typedObj, byteOffset, fieldType, name);
   10654             : }
   10655             : 
   10656             : AbortReasonOr<Ok>
   10657           0 : IonBuilder::getPropTryComplexPropOfTypedObject(bool* emitted,
   10658             :                                                MDefinition* typedObj,
   10659             :                                                int32_t fieldOffset,
   10660             :                                                TypedObjectPrediction fieldPrediction,
   10661             :                                                size_t fieldIndex)
   10662             : {
   10663             :     // Don't optimize if the typed object's underlying buffer may be detached.
   10664           0 :     TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
   10665           0 :     if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
   10666           0 :         return Ok();
   10667             : 
   10668             :     // OK, perform the optimization
   10669             : 
   10670             :     // Identify the type object for the field.
   10671           0 :     MDefinition* type = loadTypedObjectType(typedObj);
   10672           0 :     MDefinition* fieldTypeObj = typeObjectForFieldFromStructType(type, fieldIndex);
   10673             : 
   10674           0 :     LinearSum byteOffset(alloc());
   10675           0 :     if (!byteOffset.add(fieldOffset))
   10676           0 :         return abort(AbortReason::Disable, "Overflow of field offsets.");
   10677             : 
   10678             :     return pushDerivedTypedObject(emitted, typedObj, byteOffset,
   10679           0 :                                   fieldPrediction, fieldTypeObj);
   10680             : }
   10681             : 
   10682             : MDefinition*
   10683         491 : IonBuilder::convertUnboxedObjects(MDefinition* obj)
   10684             : {
   10685             :     // If obj might be in any particular unboxed group which should be
   10686             :     // converted to a native representation, perform that conversion. This does
   10687             :     // not guarantee the object will not have such a group afterwards, if the
   10688             :     // object's possible groups are not precisely known.
   10689         491 :     TemporaryTypeSet* types = obj->resultTypeSet();
   10690         491 :     if (!types || types->unknownObject() || !types->objectOrSentinel())
   10691         342 :         return obj;
   10692             : 
   10693         298 :     BaselineInspector::ObjectGroupVector list(alloc());
   10694         298 :     for (size_t i = 0; i < types->getObjectCount(); i++) {
   10695         149 :         TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i);
   10696         149 :         if (!key || !key->isGroup())
   10697           3 :             continue;
   10698             : 
   10699         146 :         if (UnboxedLayout* layout = key->group()->maybeUnboxedLayout()) {
   10700           0 :             AutoEnterOOMUnsafeRegion oomUnsafe;
   10701           0 :             if (layout->nativeGroup() && !list.append(key->group()))
   10702           0 :                 oomUnsafe.crash("IonBuilder::convertUnboxedObjects");
   10703             :         }
   10704             :     }
   10705             : 
   10706         149 :     return convertUnboxedObjects(obj, list);
   10707             : }
   10708             : 
   10709             : MDefinition*
   10710         158 : IonBuilder::convertUnboxedObjects(MDefinition* obj,
   10711             :                                   const BaselineInspector::ObjectGroupVector& list)
   10712             : {
   10713         158 :     for (size_t i = 0; i < list.length(); i++) {
   10714           0 :         ObjectGroup* group = list[i];
   10715           0 :         if (TemporaryTypeSet* types = obj->resultTypeSet()) {
   10716           0 :             if (!types->hasType(TypeSet::ObjectType(group)))
   10717           0 :                 continue;
   10718             :         }
   10719           0 :         obj = MConvertUnboxedObjectToNative::New(alloc(), obj, group);
   10720           0 :         current->add(obj->toInstruction());
   10721             :     }
   10722         158 :     return obj;
   10723             : }
   10724             : 
   10725             : AbortReasonOr<Ok>
   10726          39 : IonBuilder::getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name,
   10727             :                                    BarrierKind barrier, TemporaryTypeSet* types)
   10728             : {
   10729          39 :     MOZ_ASSERT(*emitted == false);
   10730             : 
   10731             :     uint32_t nfixed;
   10732          39 :     uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), name, &nfixed);
   10733          39 :     if (slot == UINT32_MAX)
   10734          23 :         return Ok();
   10735             : 
   10736          16 :     if (obj->type() != MIRType::Object) {
   10737           0 :         MGuardObject* guard = MGuardObject::New(alloc(), obj);
   10738           0 :         current->add(guard);
   10739           0 :         obj = guard;
   10740             :     }
   10741             : 
   10742             :     MInstruction* load;
   10743          16 :     if (slot < nfixed) {
   10744          16 :         load = MLoadFixedSlot::New(alloc(), obj, slot);
   10745             :     } else {
   10746           0 :         MInstruction* slots = MSlots::New(alloc(), obj);
   10747           0 :         current->add(slots);
   10748             : 
   10749           0 :         load = MLoadSlot::New(alloc(), slots, slot - nfixed);
   10750             :     }
   10751             : 
   10752          16 :     if (barrier == BarrierKind::NoBarrier)
   10753          10 :         load->setResultType(types->getKnownMIRType());
   10754             : 
   10755          16 :     current->add(load);
   10756          16 :     current->push(load);
   10757             : 
   10758          16 :     MOZ_TRY(pushTypeBarrier(load, types, barrier));
   10759             : 
   10760          16 :     trackOptimizationSuccess();
   10761          16 :     *emitted = true;
   10762          16 :     return Ok();
   10763             : }
   10764             : 
   10765             : AbortReasonOr<Ok>
   10766          14 : IonBuilder::getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name,
   10767             :                                       BarrierKind barrier, TemporaryTypeSet* types)
   10768             : {
   10769          14 :     MOZ_ASSERT(*emitted == false);
   10770             : 
   10771          14 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
   10772          14 :     if (!objTypes) {
   10773           0 :         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
   10774           0 :         return Ok();
   10775             :     }
   10776             : 
   10777          14 :     JSObject* singleton = objTypes->maybeSingleton();
   10778          14 :     if (!singleton) {
   10779          14 :         trackOptimizationOutcome(TrackedOutcome::NotSingleton);
   10780          14 :         return Ok();
   10781             :     }
   10782             : 
   10783           0 :     if (!singleton->is<ModuleNamespaceObject>()) {
   10784           0 :         trackOptimizationOutcome(TrackedOutcome::NotModuleNamespace);
   10785           0 :         return Ok();
   10786             :     }
   10787             : 
   10788           0 :     ModuleNamespaceObject* ns = &singleton->as<ModuleNamespaceObject>();
   10789             :     ModuleEnvironmentObject* env;
   10790             :     Shape* shape;
   10791           0 :     if (!ns->bindings().lookup(NameToId(name), &env, &shape)) {
   10792           0 :         trackOptimizationOutcome(TrackedOutcome::UnknownProperty);
   10793           0 :         return Ok();
   10794             :     }
   10795             : 
   10796           0 :     obj->setImplicitlyUsedUnchecked();
   10797           0 :     MConstant* envConst = constant(ObjectValue(*env));
   10798           0 :     uint32_t slot = shape->slot();
   10799           0 :     uint32_t nfixed = env->numFixedSlots();
   10800           0 :     MOZ_TRY(loadSlot(envConst, slot, nfixed, types->getKnownMIRType(), barrier, types));
   10801             : 
   10802           0 :     trackOptimizationSuccess();
   10803           0 :     *emitted = true;
   10804           0 :     return Ok();
   10805             : }
   10806             : 
   10807             : MInstruction*
   10808           0 : IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
   10809             :                                 BarrierKind barrier, TemporaryTypeSet* types)
   10810             : {
   10811             :     // loadUnboxedValue is designed to load any value as if it were contained in
   10812             :     // an array. Thus a property offset is converted to an index, when the
   10813             :     // object is reinterpreted as an array of properties of the same size.
   10814           0 :     size_t index = offset / UnboxedTypeSize(unboxedType);
   10815           0 :     MInstruction* indexConstant = MConstant::New(alloc(), Int32Value(index));
   10816           0 :     current->add(indexConstant);
   10817             : 
   10818           0 :     return loadUnboxedValue(obj, UnboxedPlainObject::offsetOfData(),
   10819           0 :                             indexConstant, unboxedType, barrier, types);
   10820             : }
   10821             : 
   10822             : MInstruction*
   10823           0 : IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset,
   10824             :                              MDefinition* index, JSValueType unboxedType,
   10825             :                              BarrierKind barrier, TemporaryTypeSet* types)
   10826             : {
   10827             :     MInstruction* load;
   10828           0 :     switch (unboxedType) {
   10829             :       case JSVAL_TYPE_BOOLEAN:
   10830           0 :         load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Uint8,
   10831           0 :                                        DoesNotRequireMemoryBarrier, elementsOffset);
   10832           0 :         load->setResultType(MIRType::Boolean);
   10833           0 :         break;
   10834             : 
   10835             :       case JSVAL_TYPE_INT32:
   10836           0 :         load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Int32,
   10837           0 :                                        DoesNotRequireMemoryBarrier, elementsOffset);
   10838           0 :         load->setResultType(MIRType::Int32);
   10839           0 :         break;
   10840             : 
   10841             :       case JSVAL_TYPE_DOUBLE:
   10842           0 :         load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Float64,
   10843             :                                        DoesNotRequireMemoryBarrier, elementsOffset,
   10844           0 :                                        /* canonicalizeDoubles = */ false);
   10845           0 :         load->setResultType(MIRType::Double);
   10846           0 :         break;
   10847             : 
   10848             :       case JSVAL_TYPE_STRING:
   10849           0 :         load = MLoadUnboxedString::New(alloc(), elements, index, elementsOffset);
   10850           0 :         break;
   10851             : 
   10852             :       case JSVAL_TYPE_OBJECT: {
   10853             :         MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
   10854           0 :         if (types->hasType(TypeSet::NullType()))
   10855           0 :             nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
   10856           0 :         else if (barrier != BarrierKind::NoBarrier)
   10857           0 :             nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull;
   10858             :         else
   10859           0 :             nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible;
   10860           0 :         load = MLoadUnboxedObjectOrNull::New(alloc(), elements, index, nullBehavior,
   10861           0 :                                              elementsOffset);
   10862           0 :         break;
   10863             :       }
   10864             : 
   10865             :       default:
   10866           0 :         MOZ_CRASH();
   10867             :     }
   10868             : 
   10869           0 :     current->add(load);
   10870           0 :     return load;
   10871             : }
   10872             : 
   10873             : AbortReasonOr<Ok>
   10874          23 : IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name,
   10875             :                               BarrierKind barrier, TemporaryTypeSet* types)
   10876             : {
   10877          23 :     MOZ_ASSERT(*emitted == false);
   10878             : 
   10879             :     JSValueType unboxedType;
   10880          23 :     uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType);
   10881          23 :     if (offset == UINT32_MAX)
   10882          23 :         return Ok();
   10883             : 
   10884           0 :     if (obj->type() != MIRType::Object) {
   10885           0 :         MGuardObject* guard = MGuardObject::New(alloc(), obj);
   10886           0 :         current->add(guard);
   10887           0 :         obj = guard;
   10888             :     }
   10889             : 
   10890           0 :     MInstruction* load = loadUnboxedProperty(obj, offset, unboxedType, barrier, types);
   10891           0 :     current->push(load);
   10892             : 
   10893           0 :     MOZ_TRY(pushTypeBarrier(load, types, barrier));
   10894             : 
   10895           0 :     trackOptimizationSuccess();
   10896           0 :     *emitted = true;
   10897           0 :     return Ok();
   10898             : }
   10899             : 
   10900             : MDefinition*
   10901           0 : IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
   10902             :                 const BaselineInspector::ReceiverVector& receivers,
   10903             :                 const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
   10904             :                 bool isOwnProperty)
   10905             : {
   10906           0 :     MOZ_ASSERT(isOwnProperty == !holder);
   10907           0 :     MOZ_ASSERT(holderShape);
   10908             : 
   10909           0 :     obj = convertUnboxedObjects(obj, convertUnboxedGroups);
   10910             : 
   10911           0 :     if (isOwnProperty) {
   10912           0 :         MOZ_ASSERT(receivers.empty());
   10913           0 :         return addShapeGuard(obj, holderShape, Bailout_ShapeGuard);
   10914             :     }
   10915             : 
   10916           0 :     MDefinition* holderDef = constant(ObjectValue(*holder));
   10917           0 :     addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard);
   10918             : 
   10919           0 :     return addGuardReceiverPolymorphic(obj, receivers);
   10920             : }
   10921             : 
   10922             : AbortReasonOr<Ok>
   10923          52 : IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name,
   10924             :                                    TemporaryTypeSet* types, bool innerized)
   10925             : {
   10926          52 :     MOZ_ASSERT(*emitted == false);
   10927             : 
   10928          52 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
   10929             : 
   10930          52 :     JSFunction* commonGetter = nullptr;
   10931          52 :     MDefinition* guard = nullptr;
   10932          52 :     MDefinition* globalGuard = nullptr;
   10933             : 
   10934             :     {
   10935          52 :         Shape* lastProperty = nullptr;
   10936          52 :         Shape* globalShape = nullptr;
   10937          52 :         JSObject* foundProto = nullptr;
   10938          52 :         bool isOwnProperty = false;
   10939          52 :         BaselineInspector::ReceiverVector receivers(alloc());
   10940          52 :         BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
   10941          52 :         if (inspector->commonGetPropFunction(pc, innerized, &foundProto, &lastProperty, &commonGetter,
   10942             :                                               &globalShape, &isOwnProperty,
   10943             :                                               receivers, convertUnboxedGroups))
   10944             :         {
   10945           0 :             bool canUseTIForGetter = false;
   10946           0 :             if (!isOwnProperty) {
   10947             :                 // If it's not an own property, try to use TI to avoid shape guards.
   10948             :                 // For own properties we use the path below.
   10949           0 :                 canUseTIForGetter = testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
   10950             :                                                            commonGetter, &guard,
   10951           0 :                                                            globalShape, &globalGuard);
   10952             :             }
   10953           0 :             if (!canUseTIForGetter) {
   10954             :                 // If it's an own property or type information is bad, we can still
   10955             :                 // optimize the getter if we shape guard.
   10956           0 :                 obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
   10957             :                                                     receivers, convertUnboxedGroups,
   10958             :                                                     isOwnProperty);
   10959           0 :                 if (!obj)
   10960           0 :                     return abort(AbortReason::Alloc);
   10961             :             }
   10962          52 :         } else if (inspector->megamorphicGetterSetterFunction(pc, /* isGetter = */ true,
   10963             :                                                               &commonGetter))
   10964             :         {
   10965             :             // Try to use TI to guard on this getter.
   10966           0 :             if (!testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
   10967             :                                         commonGetter, &guard))
   10968             :             {
   10969           0 :                 return Ok();
   10970             :             }
   10971             :         } else {
   10972             :             // The Baseline IC didn't have any information we can use.
   10973          52 :             return Ok();
   10974             :         }
   10975             :     }
   10976             : 
   10977           0 :     bool isDOM = objTypes && objTypes->isDOMClass(constraints());
   10978           0 :     if (isDOM)
   10979           0 :         MOZ_TRY_VAR(isDOM, testShouldDOMCall(objTypes, commonGetter, JSJitInfo::Getter));
   10980             : 
   10981           0 :     if (isDOM) {
   10982           0 :         const JSJitInfo* jitinfo = commonGetter->jitInfo();
   10983             :         // We don't support MGetDOMProperty/MGetDOMMember on things that might
   10984             :         // be proxies when the value might be in a slot, because the
   10985             :         // CodeGenerator code for LGetDOMProperty/LGetDOMMember doesn't handle
   10986             :         // that case correctly.
   10987           0 :         if ((!jitinfo->isAlwaysInSlot && !jitinfo->isLazilyCachedInSlot) ||
   10988           0 :             !objTypes->maybeProxy(constraints())) {
   10989             :             MInstruction* get;
   10990           0 :             if (jitinfo->isAlwaysInSlot) {
   10991             :                 // If our object is a singleton and we know the property is
   10992             :                 // constant (which is true if and only if the get doesn't alias
   10993             :                 // anything), we can just read the slot here and use that
   10994             :                 // constant.
   10995           0 :                 JSObject* singleton = objTypes->maybeSingleton();
   10996           0 :                 if (singleton && jitinfo->aliasSet() == JSJitInfo::AliasNone) {
   10997           0 :                     size_t slot = jitinfo->slotIndex;
   10998           0 :                     *emitted = true;
   10999           0 :                     pushConstant(GetReservedSlot(singleton, slot));
   11000           0 :                     return Ok();
   11001             :                 }
   11002             : 
   11003             :                 // We can't use MLoadFixedSlot here because it might not have
   11004             :                 // the right aliasing behavior; we want to alias DOM setters as
   11005             :                 // needed.
   11006           0 :                 get = MGetDOMMember::New(alloc(), jitinfo, obj, guard, globalGuard);
   11007             :             } else {
   11008           0 :                 get = MGetDOMProperty::New(alloc(), jitinfo, obj, guard, globalGuard);
   11009             :             }
   11010           0 :             if (!get)
   11011           0 :                 return abort(AbortReason::Alloc);
   11012           0 :             current->add(get);
   11013           0 :             current->push(get);
   11014             : 
   11015           0 :             if (get->isEffectful())
   11016           0 :                 MOZ_TRY(resumeAfter(get));
   11017             : 
   11018           0 :             MOZ_TRY(pushDOMTypeBarrier(get, types, commonGetter));
   11019             : 
   11020           0 :             trackOptimizationOutcome(TrackedOutcome::DOM);
   11021           0 :             *emitted = true;
   11022           0 :             return Ok();
   11023             :         }
   11024             :     }
   11025             : 
   11026             :     // Don't call the getter with a primitive value.
   11027           0 :     if (obj->type() != MIRType::Object) {
   11028           0 :         MGuardObject* guardObj = MGuardObject::New(alloc(), obj);
   11029           0 :         current->add(guardObj);
   11030           0 :         obj = guardObj;
   11031             :     }
   11032             : 
   11033             :     // Spoof stack to expected state for call.
   11034             : 
   11035             :     // Make sure there's enough room
   11036           0 :     if (!current->ensureHasSlots(2))
   11037           0 :         return abort(AbortReason::Alloc);
   11038           0 :     current->push(constant(ObjectValue(*commonGetter)));
   11039             : 
   11040           0 :     current->push(obj);
   11041             : 
   11042             :     CallInfo callInfo(alloc(), /* constructing = */ false,
   11043           0 :                       /* ignoresReturnValue = */ BytecodeIsPopped(pc));
   11044           0 :     if (!callInfo.init(current, 0))
   11045           0 :         return abort(AbortReason::Alloc);
   11046             : 
   11047           0 :     if (commonGetter->isNative()) {
   11048             :         InliningStatus status;
   11049           0 :         MOZ_TRY_VAR(status, inlineNativeGetter(callInfo, commonGetter));
   11050           0 :         switch (status) {
   11051             :           case InliningStatus_WarmUpCountTooLow:
   11052             :           case InliningStatus_NotInlined:
   11053           0 :             break;
   11054             :           case InliningStatus_Inlined:
   11055           0 :             trackOptimizationOutcome(TrackedOutcome::Inlined);
   11056           0 :             *emitted = true;
   11057           0 :             return Ok();
   11058             :         }
   11059             :     }
   11060             : 
   11061             :     // Inline if we can, otherwise, forget it and just generate a call.
   11062           0 :     if (commonGetter->isInterpreted()) {
   11063           0 :         InliningDecision decision = makeInliningDecision(commonGetter, callInfo);
   11064           0 :         switch (decision) {
   11065             :           case InliningDecision_Error:
   11066           0 :             return abort(AbortReason::Alloc);
   11067             :           case InliningDecision_DontInline:
   11068             :           case InliningDecision_WarmUpCountTooLow:
   11069           0 :             break;
   11070             :           case InliningDecision_Inline: {
   11071             :             InliningStatus status;
   11072           0 :             MOZ_TRY_VAR(status, inlineScriptedCall(callInfo, commonGetter));
   11073           0 :             if (status == InliningStatus_Inlined) {
   11074           0 :                 *emitted = true;
   11075           0 :                 return Ok();
   11076             :             }
   11077           0 :             break;
   11078             :           }
   11079             :         }
   11080             :     }
   11081             : 
   11082           0 :     MOZ_TRY(makeCall(commonGetter, callInfo));
   11083             : 
   11084             :     // If the getter could have been inlined, don't track success. The call to
   11085             :     // makeInliningDecision above would have tracked a specific reason why we
   11086             :     // couldn't inline.
   11087           0 :     if (!commonGetter->isInterpreted())
   11088           0 :         trackOptimizationSuccess();
   11089             : 
   11090           0 :     *emitted = true;
   11091           0 :     return Ok();
   11092             : }
   11093             : 
   11094             : bool
   11095          28 : IonBuilder::canInlinePropertyOpShapes(const BaselineInspector::ReceiverVector& receivers)
   11096             : {
   11097          28 :     if (receivers.empty()) {
   11098          19 :         trackOptimizationOutcome(TrackedOutcome::NoShapeInfo);
   11099          19 :         return false;
   11100             :     }
   11101             : 
   11102          18 :     for (size_t i = 0; i < receivers.length(); i++) {
   11103             :         // We inline the property access as long as the shape is not in
   11104             :         // dictionary mode. We cannot be sure that the shape is still a
   11105             :         // lastProperty, and calling Shape::search() on dictionary mode
   11106             :         // shapes that aren't lastProperty is invalid.
   11107           9 :         if (receivers[i].shape && receivers[i].shape->inDictionary()) {
   11108           0 :             trackOptimizationOutcome(TrackedOutcome::InDictionaryMode);
   11109           0 :             return false;
   11110             :         }
   11111             :     }
   11112             : 
   11113           9 :     return true;
   11114             : }
   11115             : 
   11116             : static Shape*
   11117           0 : PropertyShapesHaveSameSlot(const BaselineInspector::ReceiverVector& receivers, jsid id)
   11118             : {
   11119           0 :     Shape* firstShape = nullptr;
   11120           0 :     for (size_t i = 0; i < receivers.length(); i++) {
   11121           0 :         if (receivers[i].group)
   11122           0 :             return nullptr;
   11123             : 
   11124           0 :         Shape* shape = receivers[i].shape->searchLinear(id);
   11125           0 :         MOZ_ASSERT(shape);
   11126             : 
   11127           0 :         if (i == 0) {
   11128           0 :             firstShape = shape;
   11129           0 :         } else if (shape->slot() != firstShape->slot() ||
   11130           0 :                    shape->numFixedSlots() != firstShape->numFixedSlots())
   11131             :         {
   11132           0 :             return nullptr;
   11133             :         }
   11134             :     }
   11135             : 
   11136           0 :     return firstShape;
   11137             : }
   11138             : 
   11139             : AbortReasonOr<Ok>
   11140          23 : IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name,
   11141             :                                    BarrierKind barrier, TemporaryTypeSet* types)
   11142             : {
   11143          23 :     MOZ_ASSERT(*emitted == false);
   11144             : 
   11145          46 :     BaselineInspector::ReceiverVector receivers(alloc());
   11146          46 :     BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
   11147          23 :     if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
   11148           0 :         return abort(AbortReason::Alloc);
   11149             : 
   11150          23 :     if (!canInlinePropertyOpShapes(receivers))
   11151          14 :         return Ok();
   11152             : 
   11153           9 :     obj = convertUnboxedObjects(obj, convertUnboxedGroups);
   11154             : 
   11155           9 :     MIRType rvalType = types->getKnownMIRType();
   11156           9 :     if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
   11157           1 :         rvalType = MIRType::Value;
   11158             : 
   11159           9 :     if (receivers.length() == 1) {
   11160           9 :         if (!receivers[0].group) {
   11161             :             // Monomorphic load from a native object.
   11162           9 :             spew("Inlining monomorphic native GETPROP");
   11163             : 
   11164           9 :             obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
   11165             : 
   11166           9 :             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
   11167           9 :             MOZ_ASSERT(shape);
   11168             : 
   11169           9 :             MOZ_TRY(loadSlot(obj, shape, rvalType, barrier, types));
   11170             : 
   11171           9 :             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11172           9 :             *emitted = true;
   11173           9 :             return Ok();
   11174             :         }
   11175             : 
   11176           0 :         if (receivers[0].shape) {
   11177             :             // Monomorphic load from an unboxed object expando.
   11178           0 :             spew("Inlining monomorphic unboxed expando GETPROP");
   11179             : 
   11180           0 :             obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
   11181           0 :             obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard);
   11182             : 
   11183           0 :             MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
   11184           0 :             current->add(expando);
   11185             : 
   11186           0 :             expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
   11187             : 
   11188           0 :             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
   11189           0 :             MOZ_ASSERT(shape);
   11190             : 
   11191           0 :             MOZ_TRY(loadSlot(expando, shape, rvalType, barrier, types));
   11192             : 
   11193           0 :             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11194           0 :             *emitted = true;
   11195           0 :             return Ok();
   11196             :         }
   11197             : 
   11198             :         // Monomorphic load from an unboxed object.
   11199           0 :         ObjectGroup* group = receivers[0].group;
   11200           0 :         if (obj->resultTypeSet() && !obj->resultTypeSet()->hasType(TypeSet::ObjectType(group)))
   11201           0 :             return Ok();
   11202             : 
   11203           0 :         obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
   11204             : 
   11205           0 :         const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name);
   11206           0 :         MInstruction* load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types);
   11207           0 :         current->push(load);
   11208             : 
   11209           0 :         MOZ_TRY(pushTypeBarrier(load, types, barrier));
   11210             : 
   11211           0 :         trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11212           0 :         *emitted = true;
   11213           0 :         return Ok();
   11214             :     }
   11215             : 
   11216           0 :     MOZ_ASSERT(receivers.length() > 1);
   11217           0 :     spew("Inlining polymorphic GETPROP");
   11218             : 
   11219           0 :     if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) {
   11220           0 :         obj = addGuardReceiverPolymorphic(obj, receivers);
   11221           0 :         if (!obj)
   11222           0 :             return abort(AbortReason::Alloc);
   11223             : 
   11224           0 :         MOZ_TRY(loadSlot(obj, propShape, rvalType, barrier, types));
   11225             : 
   11226           0 :         trackOptimizationOutcome(TrackedOutcome::Polymorphic);
   11227           0 :         *emitted = true;
   11228           0 :         return Ok();
   11229             :     }
   11230             : 
   11231           0 :     MGetPropertyPolymorphic* load = MGetPropertyPolymorphic::New(alloc(), obj, name);
   11232           0 :     current->add(load);
   11233           0 :     current->push(load);
   11234             : 
   11235           0 :     for (size_t i = 0; i < receivers.length(); i++) {
   11236           0 :         Shape* propShape = nullptr;
   11237           0 :         if (receivers[i].shape) {
   11238           0 :             propShape = receivers[i].shape->searchLinear(NameToId(name));
   11239           0 :             MOZ_ASSERT(propShape);
   11240             :         }
   11241           0 :         if (!load->addReceiver(receivers[i], propShape))
   11242           0 :             return abort(AbortReason::Alloc);
   11243             :     }
   11244             : 
   11245           0 :     if (failedShapeGuard_)
   11246           0 :         load->setNotMovable();
   11247             : 
   11248           0 :     load->setResultType(rvalType);
   11249           0 :     MOZ_TRY(pushTypeBarrier(load, types, barrier));
   11250             : 
   11251           0 :     trackOptimizationOutcome(TrackedOutcome::Polymorphic);
   11252           0 :     *emitted = true;
   11253           0 :     return Ok();
   11254             : }
   11255             : 
   11256             : AbortReasonOr<Ok>
   11257          14 : IonBuilder::getPropAddCache(MDefinition* obj, PropertyName* name,
   11258             :                             BarrierKind barrier, TemporaryTypeSet* types)
   11259             : {
   11260             :     // PropertyReadNeedsTypeBarrier only accounts for object types, so for now
   11261             :     // always insert a barrier if the input is not known to be an object.
   11262          14 :     if (obj->type() != MIRType::Object)
   11263           0 :         barrier = BarrierKind::TypeSet;
   11264             : 
   11265             :     // Since getters have no guaranteed return values, we must barrier in order to be
   11266             :     // able to attach stubs for them.
   11267          14 :     if (inspector->hasSeenAccessedGetter(pc))
   11268           0 :         barrier = BarrierKind::TypeSet;
   11269             : 
   11270             :     // Caches can read values from prototypes, so update the barrier to
   11271             :     // reflect such possible values.
   11272          14 :     if (barrier != BarrierKind::TypeSet) {
   11273             :         BarrierKind protoBarrier;
   11274           6 :         MOZ_TRY_VAR(protoBarrier, PropertyReadOnPrototypeNeedsTypeBarrier(this, obj, name, types));
   11275           6 :         if (protoBarrier != BarrierKind::NoBarrier) {
   11276           0 :             MOZ_ASSERT(barrier <= protoBarrier);
   11277           0 :             barrier = protoBarrier;
   11278             :         }
   11279             :     }
   11280             : 
   11281             :     // Ensure we insert a type barrier for reads from typed objects, as type
   11282             :     // information does not account for the initial undefined/null types.
   11283          14 :     if (barrier != BarrierKind::TypeSet && !types->unknown()) {
   11284           6 :         MOZ_ASSERT(obj->resultTypeSet());
   11285           6 :         switch (obj->resultTypeSet()->forAllClasses(constraints(), IsTypedObjectClass)) {
   11286             :           case TemporaryTypeSet::ForAllResult::ALL_FALSE:
   11287             :           case TemporaryTypeSet::ForAllResult::EMPTY:
   11288           6 :             break;
   11289             :           case TemporaryTypeSet::ForAllResult::ALL_TRUE:
   11290             :           case TemporaryTypeSet::ForAllResult::MIXED:
   11291           0 :             barrier = BarrierKind::TypeSet;
   11292           0 :             break;
   11293             :         }
   11294             :     }
   11295             : 
   11296          14 :     MConstant* id = constant(StringValue(name));
   11297          14 :     MGetPropertyCache* load = MGetPropertyCache::New(alloc(), obj, id,
   11298          28 :                                                      barrier == BarrierKind::TypeSet);
   11299             : 
   11300             :     // Try to mark the cache as idempotent.
   11301          14 :     if (obj->type() == MIRType::Object && !invalidatedIdempotentCache()) {
   11302          14 :         if (PropertyReadIsIdempotent(constraints(), obj, name))
   11303           3 :             load->setIdempotent();
   11304             :     }
   11305             : 
   11306             :     // When we are in the context of making a call from the value returned from
   11307             :     // a property, we query the typeObject for the given property name to fill
   11308             :     // the InlinePropertyTable of the GetPropertyCache.  This information is
   11309             :     // then used in inlineCallsite and inlineCalls, if the "this" definition is
   11310             :     // matching the "object" definition of the GetPropertyCache (see
   11311             :     // CanInlineGetPropertyCache).
   11312             :     //
   11313             :     // If this GetPropertyCache is idempotent, then we can dispatch to the right
   11314             :     // function only by checking the typed object, instead of querying the value
   11315             :     // of the property.  Thus this GetPropertyCache can be moved into the
   11316             :     // fallback path (see inlineObjectGroupFallback).  Otherwise, we always have
   11317             :     // to do the GetPropertyCache, and we can dispatch based on the JSFunction
   11318             :     // value.
   11319          14 :     if (JSOp(*pc) == JSOP_CALLPROP && load->idempotent())
   11320           3 :         MOZ_TRY(annotateGetPropertyCache(obj, name, load, obj->resultTypeSet(), types));
   11321             : 
   11322          14 :     current->add(load);
   11323          14 :     current->push(load);
   11324             : 
   11325          14 :     if (load->isEffectful())
   11326          11 :         MOZ_TRY(resumeAfter(load));
   11327             : 
   11328          14 :     MIRType rvalType = types->getKnownMIRType();
   11329          14 :     if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
   11330           8 :         rvalType = MIRType::Value;
   11331          14 :     load->setResultType(rvalType);
   11332             : 
   11333          14 :     if (*pc != JSOP_CALLPROP || !IsNullOrUndefined(obj->type())) {
   11334             :         // Due to inlining, it's possible the observed TypeSet is non-empty,
   11335             :         // even though we know |obj| is null/undefined and the MCallGetProperty
   11336             :         // will throw. Don't push a TypeBarrier in this case, to avoid
   11337             :         // inlining the following (unreachable) JSOP_CALL.
   11338          14 :         MOZ_TRY(pushTypeBarrier(load, types, barrier));
   11339             :     }
   11340             : 
   11341          14 :     trackOptimizationSuccess();
   11342          14 :     return Ok();
   11343             : }
   11344             : 
   11345             : MDefinition*
   11346          62 : IonBuilder::tryInnerizeWindow(MDefinition* obj)
   11347             : {
   11348             :     // Try to optimize accesses on outer window proxies (window.foo, for
   11349             :     // example) to go directly to the inner window, the global.
   11350             :     //
   11351             :     // Callers should be careful not to pass the inner object to getters or
   11352             :     // setters that require outerization.
   11353             : 
   11354          62 :     if (obj->type() != MIRType::Object)
   11355           3 :         return obj;
   11356             : 
   11357          59 :     TemporaryTypeSet* types = obj->resultTypeSet();
   11358          59 :     if (!types)
   11359           0 :         return obj;
   11360             : 
   11361          59 :     JSObject* singleton = types->maybeSingleton();
   11362          59 :     if (!singleton)
   11363          59 :         return obj;
   11364             : 
   11365           0 :     if (!IsWindowProxy(singleton))
   11366           0 :         return obj;
   11367             : 
   11368             :     // This must be a WindowProxy for the current Window/global. Else it'd be
   11369             :     // a cross-compartment wrapper and IsWindowProxy returns false for those.
   11370           0 :     MOZ_ASSERT(ToWindowIfWindowProxy(singleton) == &script()->global());
   11371             : 
   11372             :     // When we navigate, the WindowProxy is brain transplanted and we'll mark
   11373             :     // its ObjectGroup as having unknown properties. The type constraint we add
   11374             :     // here will invalidate JIT code when this happens.
   11375           0 :     TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(singleton);
   11376           0 :     if (key->hasFlags(constraints(), OBJECT_FLAG_UNKNOWN_PROPERTIES))
   11377           0 :         return obj;
   11378             : 
   11379           0 :     obj->setImplicitlyUsedUnchecked();
   11380           0 :     return constant(ObjectValue(script()->global()));
   11381             : }
   11382             : 
   11383             : AbortReasonOr<Ok>
   11384          62 : IonBuilder::getPropTryInnerize(bool* emitted, MDefinition* obj, PropertyName* name,
   11385             :                                TemporaryTypeSet* types)
   11386             : {
   11387             :     // See the comment in tryInnerizeWindow for how this works.
   11388             : 
   11389             :     // Note that it's important that we do this _before_ we'd try to
   11390             :     // do the optimizations below on obj normally, since some of those
   11391             :     // optimizations have fallback paths that are slower than the path
   11392             :     // we'd produce here.
   11393             : 
   11394          62 :     MOZ_ASSERT(*emitted == false);
   11395             : 
   11396          62 :     MDefinition* inner = tryInnerizeWindow(obj);
   11397          62 :     if (inner == obj)
   11398          62 :         return Ok();
   11399             : 
   11400           0 :     if (!forceInlineCaches()) {
   11401           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
   11402           0 :         MOZ_TRY(getPropTryConstant(emitted, inner, NameToId(name), types));
   11403           0 :         if (*emitted)
   11404           0 :             return Ok();
   11405             : 
   11406           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_StaticName);
   11407           0 :         MOZ_TRY(getStaticName(emitted, &script()->global(), name));
   11408           0 :         if (*emitted)
   11409           0 :             return Ok();
   11410             : 
   11411           0 :         trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
   11412           0 :         MOZ_TRY(getPropTryCommonGetter(emitted, inner, name, types, /* innerized = */true));
   11413           0 :         if (*emitted)
   11414           0 :             return Ok();
   11415             :     }
   11416             : 
   11417             :     // Passing the inner object to GetProperty IC is safe, see the
   11418             :     // needsOuterizedThisObject check in IsCacheableGetPropCallNative.
   11419           0 :     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
   11420           0 :                                                        inner, name, types);
   11421           0 :     trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
   11422           0 :     MOZ_TRY(getPropAddCache(inner, name, barrier, types));
   11423             : 
   11424           0 :     *emitted = true;
   11425           0 :     return Ok();
   11426             : }
   11427             : 
   11428             : AbortReasonOr<Ok>
   11429          82 : IonBuilder::jsop_setprop(PropertyName* name)
   11430             : {
   11431          82 :     MDefinition* value = current->pop();
   11432          82 :     MDefinition* obj = convertUnboxedObjects(current->pop());
   11433             : 
   11434          82 :     bool emitted = false;
   11435          82 :     startTrackingOptimizations();
   11436          82 :     trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet());
   11437          82 :     trackTypeInfo(TrackedTypeSite::Value, value->type(), value->resultTypeSet());
   11438             : 
   11439             :     // Always use a call if we are doing the definite properties analysis and
   11440             :     // not actually emitting code, to simplify later analysis.
   11441          82 :     if (info().isAnalysis() || shouldAbortOnPreliminaryGroups(obj)) {
   11442          23 :         bool strict = IsStrictSetPC(pc);
   11443          23 :         MInstruction* ins = MCallSetProperty::New(alloc(), obj, value, name, strict);
   11444          23 :         current->add(ins);
   11445          23 :         current->push(value);
   11446          23 :         return resumeAfter(ins);
   11447             :     }
   11448             : 
   11449          59 :     if (!forceInlineCaches()) {
   11450             :         // Try to inline a common property setter, or make a call.
   11451          59 :         trackOptimizationAttempt(TrackedStrategy::SetProp_CommonSetter);
   11452          59 :         MOZ_TRY(setPropTryCommonSetter(&emitted, obj, name, value));
   11453          59 :         if (emitted)
   11454           0 :             return Ok();
   11455             : 
   11456             :         // Try to emit stores to known binary data blocks
   11457          59 :         trackOptimizationAttempt(TrackedStrategy::SetProp_TypedObject);
   11458          59 :         MOZ_TRY(setPropTryTypedObject(&emitted, obj, name, value));
   11459          59 :         if (emitted)
   11460           0 :             return Ok();
   11461             :     }
   11462             : 
   11463          59 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
   11464          59 :     bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
   11465          59 :                                                  /* canModify = */ true);
   11466             : 
   11467          59 :     if (!forceInlineCaches()) {
   11468             :         // Try to emit stores to unboxed objects.
   11469          59 :         trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed);
   11470          59 :         MOZ_TRY(setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes));
   11471          59 :         if (emitted)
   11472           0 :             return Ok();
   11473             :     }
   11474             : 
   11475          59 :     if (!forceInlineCaches()) {
   11476             :         // Try to emit store from definite slots.
   11477          59 :         trackOptimizationAttempt(TrackedStrategy::SetProp_DefiniteSlot);
   11478          59 :         MOZ_TRY(setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes));
   11479          59 :         if (emitted)
   11480          51 :             return Ok();
   11481             : 
   11482             :         // Try to emit a monomorphic/polymorphic store based on baseline caches.
   11483           8 :         trackOptimizationAttempt(TrackedStrategy::SetProp_InlineAccess);
   11484           8 :         MOZ_TRY(setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes));
   11485           8 :         if (emitted)
   11486           0 :             return Ok();
   11487             :     }
   11488             : 
   11489             :     // Emit a polymorphic cache.
   11490           8 :     trackOptimizationAttempt(TrackedStrategy::SetProp_InlineCache);
   11491           8 :     MOZ_TRY(setPropTryCache(&emitted, obj, name, value, barrier, objTypes));
   11492           8 :     MOZ_ASSERT(emitted == true);
   11493           8 :     return Ok();
   11494             : }
   11495             : 
   11496             : AbortReasonOr<Ok>
   11497          59 : IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj,
   11498             :                                    PropertyName* name, MDefinition* value)
   11499             : {
   11500          59 :     MOZ_ASSERT(*emitted == false);
   11501             : 
   11502          59 :     TemporaryTypeSet* objTypes = obj->resultTypeSet();
   11503          59 :     JSFunction* commonSetter = nullptr;
   11504          59 :     MDefinition* guard = nullptr;
   11505             : 
   11506             :     {
   11507          59 :         Shape* lastProperty = nullptr;
   11508          59 :         JSObject* foundProto = nullptr;
   11509             :         bool isOwnProperty;
   11510          59 :         BaselineInspector::ReceiverVector receivers(alloc());
   11511          59 :         BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
   11512          59 :         if (inspector->commonSetPropFunction(pc, &foundProto, &lastProperty, &commonSetter,
   11513             :                                               &isOwnProperty,
   11514             :                                               receivers, convertUnboxedGroups))
   11515             :         {
   11516           0 :             bool canUseTIForSetter = false;
   11517           0 :             if (!isOwnProperty) {
   11518             :                 // If it's not an own property, try to use TI to avoid shape guards.
   11519             :                 // For own properties we use the path below.
   11520           0 :                 canUseTIForSetter = testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
   11521           0 :                                                            commonSetter, &guard);
   11522             :             }
   11523           0 :             if (!canUseTIForSetter) {
   11524             :                 // If it's an own property or type information is bad, we can still
   11525             :                 // optimize the setter if we shape guard.
   11526           0 :                 obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
   11527             :                                                     receivers, convertUnboxedGroups,
   11528             :                                                     isOwnProperty);
   11529           0 :                 if (!obj)
   11530           0 :                     return abort(AbortReason::Alloc);
   11531             :             }
   11532          59 :         } else if (inspector->megamorphicGetterSetterFunction(pc, /* isGetter = */ false,
   11533             :                                                               &commonSetter))
   11534             :         {
   11535             :             // Try to use TI to guard on this setter.
   11536           0 :             if (!testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
   11537             :                                         commonSetter, &guard))
   11538             :             {
   11539           0 :                 return Ok();
   11540             :             }
   11541             :         } else {
   11542             :             // The Baseline IC didn't have any information we can use.
   11543          59 :             return Ok();
   11544             :         }
   11545             :     }
   11546             : 
   11547             :     // Emit common setter.
   11548             : 
   11549             :     // Setters can be called even if the property write needs a type
   11550             :     // barrier, as calling the setter does not actually write any data
   11551             :     // properties.
   11552             : 
   11553             :     // Try emitting dom call.
   11554           0 :     MOZ_TRY(setPropTryCommonDOMSetter(emitted, obj, value, commonSetter, objTypes));
   11555           0 :     if (*emitted) {
   11556           0 :         trackOptimizationOutcome(TrackedOutcome::DOM);
   11557           0 :         return Ok();
   11558             :     }
   11559             : 
   11560             :     // Don't call the setter with a primitive value.
   11561           0 :     if (obj->type() != MIRType::Object) {
   11562           0 :         MGuardObject* guardObj = MGuardObject::New(alloc(), obj);
   11563           0 :         current->add(guardObj);
   11564           0 :         obj = guardObj;
   11565             :     }
   11566             : 
   11567             :     // Dummy up the stack, as in getprop. We are pushing an extra value, so
   11568             :     // ensure there is enough space.
   11569           0 :     if (!current->ensureHasSlots(3))
   11570           0 :         return abort(AbortReason::Alloc);
   11571             : 
   11572           0 :     current->push(constant(ObjectValue(*commonSetter)));
   11573           0 :     current->push(obj);
   11574           0 :     current->push(value);
   11575             : 
   11576             :     // Call the setter. Note that we have to push the original value, not
   11577             :     // the setter's return value.
   11578             :     CallInfo callInfo(alloc(), /* constructing = */ false,
   11579           0 :                       /* ignoresReturnValue = */ BytecodeIsPopped(pc));
   11580           0 :     if (!callInfo.init(current, 1))
   11581           0 :         return abort(AbortReason::Alloc);
   11582             : 
   11583             :     // Ensure that we know we are calling a setter in case we inline it.
   11584           0 :     callInfo.markAsSetter();
   11585             : 
   11586             :     // Inline the setter if we can.
   11587           0 :     if (commonSetter->isInterpreted()) {
   11588           0 :         InliningDecision decision = makeInliningDecision(commonSetter, callInfo);
   11589           0 :         switch (decision) {
   11590             :           case InliningDecision_Error:
   11591           0 :             return abort(AbortReason::Alloc);
   11592             :           case InliningDecision_DontInline:
   11593             :           case InliningDecision_WarmUpCountTooLow:
   11594           0 :             break;
   11595             :           case InliningDecision_Inline: {
   11596             :             InliningStatus status;
   11597           0 :             MOZ_TRY_VAR(status, inlineScriptedCall(callInfo, commonSetter));
   11598           0 :             if (status == InliningStatus_Inlined) {
   11599           0 :                 *emitted = true;
   11600           0 :                 return Ok();
   11601             :             }
   11602             :           }
   11603             :         }
   11604             :     }
   11605             : 
   11606             :     MCall* call;
   11607           0 :     MOZ_TRY_VAR(call, makeCallHelper(commonSetter, callInfo));
   11608             : 
   11609           0 :     current->push(value);
   11610           0 :     MOZ_TRY(resumeAfter(call));
   11611             : 
   11612             :     // If the setter could have been inlined, don't track success. The call to
   11613             :     // makeInliningDecision above would have tracked a specific reason why we
   11614             :     // couldn't inline.
   11615           0 :     if (!commonSetter->isInterpreted())
   11616           0 :         trackOptimizationSuccess();
   11617             : 
   11618           0 :     *emitted = true;
   11619           0 :     return Ok();
   11620             : }
   11621             : 
   11622             : AbortReasonOr<Ok>
   11623           0 : IonBuilder::setPropTryCommonDOMSetter(bool* emitted, MDefinition* obj,
   11624             :                                       MDefinition* value, JSFunction* setter,
   11625             :                                       TemporaryTypeSet* objTypes)
   11626             : {
   11627           0 :     MOZ_ASSERT(*emitted == false);
   11628             : 
   11629           0 :     if (!objTypes || !objTypes->isDOMClass(constraints()))
   11630           0 :         return Ok();
   11631             : 
   11632           0 :     bool isDOM = false;
   11633           0 :     MOZ_TRY_VAR(isDOM, testShouldDOMCall(objTypes, setter, JSJitInfo::Setter));
   11634           0 :     if (!isDOM)
   11635           0 :         return Ok();
   11636             : 
   11637             :     // Emit SetDOMProperty.
   11638           0 :     MOZ_ASSERT(setter->jitInfo()->type() == JSJitInfo::Setter);
   11639           0 :     MSetDOMProperty* set = MSetDOMProperty::New(alloc(), setter->jitInfo()->setter, obj, value);
   11640             : 
   11641           0 :     current->add(set);
   11642           0 :     current->push(value);
   11643             : 
   11644           0 :     MOZ_TRY(resumeAfter(set));
   11645             : 
   11646           0 :     *emitted = true;
   11647           0 :     return Ok();
   11648             : }
   11649             : 
   11650             : AbortReasonOr<Ok>
   11651          59 : IonBuilder::setPropTryTypedObject(bool* emitted, MDefinition* obj,
   11652             :                                   PropertyName* name, MDefinition* value)
   11653             : {
   11654          59 :     TypedObjectPrediction fieldPrediction;
   11655             :     size_t fieldOffset;
   11656             :     size_t fieldIndex;
   11657          59 :     if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
   11658          59 :         return Ok();
   11659             : 
   11660           0 :     switch (fieldPrediction.kind()) {
   11661             :       case type::Simd:
   11662             :         // FIXME (bug 894104): store into a MIRType::float32x4 etc
   11663           0 :         return Ok();
   11664             : 
   11665             :       case type::Reference:
   11666             :         return setPropTryReferencePropOfTypedObject(emitted, obj, fieldOffset,
   11667           0 :                                                     value, fieldPrediction, name);
   11668             : 
   11669             :       case type::Scalar:
   11670             :         return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset,
   11671           0 :                                                  value, fieldPrediction);
   11672             : 
   11673             :       case type::Struct:
   11674             :       case type::Array:
   11675           0 :         return Ok();
   11676             :     }
   11677             : 
   11678           0 :     MOZ_CRASH("Unknown kind");
   11679             : }
   11680             : 
   11681             : AbortReasonOr<Ok>
   11682           0 : IonBuilder::setPropTryReferencePropOfTypedObject(bool* emitted,
   11683             :                                                  MDefinition* obj,
   11684             :                                                  int32_t fieldOffset,
   11685             :                                                  MDefinition* value,
   11686             :                                                  TypedObjectPrediction fieldPrediction,
   11687             :                                                  PropertyName* name)
   11688             : {
   11689           0 :     ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
   11690             : 
   11691           0 :     TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
   11692           0 :     if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
   11693           0 :         return Ok();
   11694             : 
   11695           0 :     LinearSum byteOffset(alloc());
   11696           0 :     if (!byteOffset.add(fieldOffset))
   11697           0 :         return abort(AbortReason::Disable, "Overflow of field offset.");
   11698             : 
   11699           0 :     MOZ_TRY_VAR(*emitted, storeReferenceTypedObjectValue(obj, byteOffset, fieldType, value, name));
   11700           0 :     if (!*emitted)
   11701           0 :         return Ok();
   11702             : 
   11703           0 :     current->push(value);
   11704             : 
   11705           0 :     trackOptimizationSuccess();
   11706           0 :     *emitted = true;
   11707           0 :     return Ok();
   11708             : }
   11709             : 
   11710             : AbortReasonOr<Ok>
   11711           0 : IonBuilder::setPropTryScalarPropOfTypedObject(bool* emitted,
   11712             :                                               MDefinition* obj,
   11713             :                                               int32_t fieldOffset,
   11714             :                                               MDefinition* value,
   11715             :                                               TypedObjectPrediction fieldPrediction)
   11716             : {
   11717             :     // Must always be loading the same scalar type
   11718           0 :     Scalar::Type fieldType = fieldPrediction.scalarType();
   11719             : 
   11720             :     // Don't optimize if the typed object's underlying buffer may be detached.
   11721           0 :     TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
   11722           0 :     if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
   11723           0 :         return Ok();
   11724             : 
   11725           0 :     LinearSum byteOffset(alloc());
   11726           0 :     if (!byteOffset.add(fieldOffset))
   11727           0 :         return abort(AbortReason::Disable, "Overflow of field offet.");
   11728             : 
   11729           0 :     MOZ_TRY(storeScalarTypedObjectValue(obj, byteOffset, fieldType, value));
   11730             : 
   11731           0 :     current->push(value);
   11732             : 
   11733           0 :     trackOptimizationSuccess();
   11734           0 :     *emitted = true;
   11735           0 :     return Ok();
   11736             : }
   11737             : 
   11738             : AbortReasonOr<Ok>
   11739          59 : IonBuilder::setPropTryDefiniteSlot(bool* emitted, MDefinition* obj,
   11740             :                                    PropertyName* name, MDefinition* value,
   11741             :                                    bool barrier, TemporaryTypeSet* objTypes)
   11742             : {
   11743          59 :     MOZ_ASSERT(*emitted == false);
   11744             : 
   11745          59 :     if (barrier) {
   11746           3 :         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
   11747           3 :         return Ok();
   11748             :     }
   11749             : 
   11750             :     uint32_t nfixed;
   11751          56 :     uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), name, &nfixed);
   11752          56 :     if (slot == UINT32_MAX)
   11753           5 :         return Ok();
   11754             : 
   11755          51 :     bool writeBarrier = false;
   11756         102 :     for (size_t i = 0; i < obj->resultTypeSet()->getObjectCount(); i++) {
   11757          51 :         TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i);
   11758          51 :         if (!key)
   11759           0 :             continue;
   11760             : 
   11761          51 :         HeapTypeSetKey property = key->property(NameToId(name));
   11762          51 :         if (property.nonWritable(constraints())) {
   11763           0 :             trackOptimizationOutcome(TrackedOutcome::NonWritableProperty);
   11764           0 :             return Ok();
   11765             :         }
   11766          51 :         writeBarrier |= property.needsBarrier(constraints());
   11767             :     }
   11768             : 
   11769          51 :     if (NeedsPostBarrier(value))
   11770          14 :         current->add(MPostWriteBarrier::New(alloc(), obj, value));
   11771             : 
   11772             :     MInstruction* store;
   11773          51 :     if (slot < nfixed) {
   11774          51 :         store = MStoreFixedSlot::New(alloc(), obj, slot, value);
   11775          51 :         if (writeBarrier)
   11776          28 :             store->toStoreFixedSlot()->setNeedsBarrier();
   11777             :     } else {
   11778           0 :         MInstruction* slots = MSlots::New(alloc(), obj);
   11779           0 :         current->add(slots);
   11780             : 
   11781           0 :         store = MStoreSlot::New(alloc(), slots, slot - nfixed, value);
   11782           0 :         if (writeBarrier)
   11783           0 :             store->toStoreSlot()->setNeedsBarrier();
   11784             :     }
   11785             : 
   11786          51 :     current->add(store);
   11787          51 :     current->push(value);
   11788             : 
   11789          51 :     MOZ_TRY(resumeAfter(store));
   11790             : 
   11791          51 :     trackOptimizationSuccess();
   11792          51 :     *emitted = true;
   11793          51 :     return Ok();
   11794             : }
   11795             : 
   11796             : MInstruction*
   11797           0 : IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
   11798             :                                  MDefinition* value)
   11799             : {
   11800           0 :     size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType);
   11801           0 :     MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant));
   11802           0 :     current->add(scaledOffset);
   11803             : 
   11804           0 :     return storeUnboxedValue(obj, obj, UnboxedPlainObject::offsetOfData(),
   11805           0 :                              scaledOffset, unboxedType, value);
   11806             : }
   11807             : 
   11808             : MInstruction*
   11809           0 : IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset,
   11810             :                               MDefinition* scaledOffset, JSValueType unboxedType,
   11811             :                               MDefinition* value, bool preBarrier /* = true */)
   11812             : {
   11813             :     MInstruction* store;
   11814           0 :     switch (unboxedType) {
   11815             :       case JSVAL_TYPE_BOOLEAN:
   11816           0 :         store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8,
   11817             :                                          MStoreUnboxedScalar::DontTruncateInput,
   11818           0 :                                          DoesNotRequireMemoryBarrier, elementsOffset);
   11819           0 :         break;
   11820             : 
   11821             :       case JSVAL_TYPE_INT32:
   11822           0 :         store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32,
   11823             :                                          MStoreUnboxedScalar::DontTruncateInput,
   11824           0 :                                          DoesNotRequireMemoryBarrier, elementsOffset);
   11825           0 :         break;
   11826             : 
   11827             :       case JSVAL_TYPE_DOUBLE:
   11828           0 :         store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64,
   11829             :                                          MStoreUnboxedScalar::DontTruncateInput,
   11830           0 :                                          DoesNotRequireMemoryBarrier, elementsOffset);
   11831           0 :         break;
   11832             : 
   11833             :       case JSVAL_TYPE_STRING:
   11834           0 :         store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value,
   11835           0 :                                          elementsOffset, preBarrier);
   11836           0 :         break;
   11837             : 
   11838             :       case JSVAL_TYPE_OBJECT:
   11839           0 :         MOZ_ASSERT(value->type() == MIRType::Object ||
   11840             :                    value->type() == MIRType::Null ||
   11841             :                    value->type() == MIRType::Value);
   11842           0 :         MOZ_ASSERT(!value->mightBeType(MIRType::Undefined),
   11843             :                    "MToObjectOrNull slow path is invalid for unboxed objects");
   11844           0 :         store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj,
   11845           0 :                                                elementsOffset, preBarrier);
   11846           0 :         break;
   11847             : 
   11848             :       default:
   11849           0 :         MOZ_CRASH();
   11850             :     }
   11851             : 
   11852           0 :     current->add(store);
   11853           0 :     return store;
   11854             : }
   11855             : 
   11856             : AbortReasonOr<Ok>
   11857          59 : IonBuilder::setPropTryUnboxed(bool* emitted, MDefinition* obj,
   11858             :                               PropertyName* name, MDefinition* value,
   11859             :                               bool barrier, TemporaryTypeSet* objTypes)
   11860             : {
   11861          59 :     MOZ_ASSERT(*emitted == false);
   11862             : 
   11863          59 :     if (barrier) {
   11864           3 :         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
   11865           3 :         return Ok();
   11866             :     }
   11867             : 
   11868             :     JSValueType unboxedType;
   11869          56 :     uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType);
   11870          56 :     if (offset == UINT32_MAX)
   11871          56 :         return Ok();
   11872             : 
   11873           0 :     if (obj->type() != MIRType::Object) {
   11874           0 :         MGuardObject* guard = MGuardObject::New(alloc(), obj);
   11875           0 :         current->add(guard);
   11876           0 :         obj = guard;
   11877             :     }
   11878             : 
   11879           0 :     MInstruction* store = storeUnboxedProperty(obj, offset, unboxedType, value);
   11880             : 
   11881           0 :     current->push(value);
   11882             : 
   11883           0 :     MOZ_TRY(resumeAfter(store));
   11884             : 
   11885           0 :     *emitted = true;
   11886           0 :     return Ok();
   11887             : }
   11888             : 
   11889             : AbortReasonOr<Ok>
   11890           8 : IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj,
   11891             :                                    PropertyName* name, MDefinition* value,
   11892             :                                    bool barrier, TemporaryTypeSet* objTypes)
   11893             : {
   11894           8 :     MOZ_ASSERT(*emitted == false);
   11895             : 
   11896           8 :     if (barrier) {
   11897           3 :         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
   11898           3 :         return Ok();
   11899             :     }
   11900             : 
   11901          10 :     BaselineInspector::ReceiverVector receivers(alloc());
   11902          10 :     BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
   11903           5 :     if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
   11904           0 :         return abort(AbortReason::Alloc);
   11905             : 
   11906           5 :     if (!canInlinePropertyOpShapes(receivers))
   11907           5 :         return Ok();
   11908             : 
   11909           0 :     obj = convertUnboxedObjects(obj, convertUnboxedGroups);
   11910             : 
   11911           0 :     if (receivers.length() == 1) {
   11912           0 :         if (!receivers[0].group) {
   11913             :             // Monomorphic store to a native object.
   11914           0 :             spew("Inlining monomorphic native SETPROP");
   11915             : 
   11916           0 :             obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
   11917             : 
   11918           0 :             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
   11919           0 :             MOZ_ASSERT(shape);
   11920             : 
   11921           0 :             if (NeedsPostBarrier(value))
   11922           0 :                 current->add(MPostWriteBarrier::New(alloc(), obj, value));
   11923             : 
   11924           0 :             bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
   11925           0 :             MOZ_TRY(storeSlot(obj, shape, value, needsPreBarrier));
   11926             : 
   11927           0 :             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11928           0 :             *emitted = true;
   11929           0 :             return Ok();
   11930             :         }
   11931             : 
   11932           0 :         if (receivers[0].shape) {
   11933             :             // Monomorphic store to an unboxed object expando.
   11934           0 :             spew("Inlining monomorphic unboxed expando SETPROP");
   11935             : 
   11936           0 :             obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
   11937           0 :             obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard);
   11938             : 
   11939           0 :             MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
   11940           0 :             current->add(expando);
   11941             : 
   11942           0 :             expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
   11943             : 
   11944           0 :             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
   11945           0 :             MOZ_ASSERT(shape);
   11946             : 
   11947           0 :             if (NeedsPostBarrier(value))
   11948           0 :                 current->add(MPostWriteBarrier::New(alloc(), obj, value));
   11949             : 
   11950           0 :             bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
   11951           0 :             MOZ_TRY(storeSlot(expando, shape, value, needsPreBarrier));
   11952             : 
   11953           0 :             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11954           0 :             *emitted = true;
   11955           0 :             return Ok();
   11956             :         }
   11957             : 
   11958             :         // Monomorphic store to an unboxed object.
   11959           0 :         spew("Inlining monomorphic unboxed SETPROP");
   11960             : 
   11961           0 :         ObjectGroup* group = receivers[0].group;
   11962           0 :         if (!objTypes->hasType(TypeSet::ObjectType(group)))
   11963           0 :             return Ok();
   11964             : 
   11965           0 :         obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
   11966             : 
   11967           0 :         if (NeedsPostBarrier(value))
   11968           0 :             current->add(MPostWriteBarrier::New(alloc(), obj, value));
   11969             : 
   11970           0 :         const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name);
   11971           0 :         storeUnboxedProperty(obj, property->offset, property->type, value);
   11972             : 
   11973           0 :         current->push(value);
   11974             : 
   11975           0 :         trackOptimizationOutcome(TrackedOutcome::Monomorphic);
   11976           0 :         *emitted = true;
   11977           0 :         return Ok();
   11978             :     }
   11979             : 
   11980           0 :     MOZ_ASSERT(receivers.length() > 1);
   11981           0 :     spew("Inlining polymorphic SETPROP");
   11982             : 
   11983           0 :     if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) {
   11984           0 :         obj = addGuardReceiverPolymorphic(obj, receivers);
   11985           0 :         if (!obj)
   11986           0 :             return abort(AbortReason::Alloc);
   11987             : 
   11988           0 :         if (NeedsPostBarrier(value))
   11989           0 :             current->add(MPostWriteBarrier::New(alloc(), obj, value));
   11990             : 
   11991           0 :         bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
   11992           0 :         MOZ_TRY(storeSlot(obj, propShape, value, needsPreBarrier));
   11993             : 
   11994           0 :         trackOptimizationOutcome(TrackedOutcome::Polymorphic);
   11995           0 :         *emitted = true;
   11996           0 :         return Ok();
   11997             :     }
   11998             : 
   11999           0 :     if (NeedsPostBarrier(value))
   12000           0 :         current->add(MPostWriteBarrier::New(alloc(), obj, value));
   12001             : 
   12002           0 :     MSetPropertyPolymorphic* ins = MSetPropertyPolymorphic::New(alloc(), obj, value, name);
   12003           0 :     current->add(ins);
   12004           0 :     current->push(value);
   12005             : 
   12006           0 :     for (size_t i = 0; i < receivers.length(); i++) {
   12007           0 :         Shape* propShape = nullptr;
   12008           0 :         if (receivers[i].shape) {
   12009           0 :             propShape = receivers[i].shape->searchLinear(NameToId(name));
   12010           0 :             MOZ_ASSERT(propShape);
   12011             :         }
   12012           0 :         if (!ins->addReceiver(receivers[i], propShape))
   12013           0 :             return abort(AbortReason::Alloc);
   12014             :     }
   12015             : 
   12016           0 :     if (objTypes->propertyNeedsBarrier(constraints(), NameToId(name)))
   12017           0 :         ins->setNeedsBarrier();
   12018             : 
   12019           0 :     MOZ_TRY(resumeAfter(ins));
   12020             : 
   12021           0 :     trackOptimizationOutcome(TrackedOutcome::Polymorphic);
   12022           0 :     *emitted = true;
   12023           0 :     return Ok();
   12024             : }
   12025             : 
   12026             : AbortReasonOr<Ok>
   12027           8 : IonBuilder::setPropTryCache(bool* emitted, MDefinition* obj,
   12028             :                             PropertyName* name, MDefinition* value,
   12029             :                             bool barrier, TemporaryTypeSet* objTypes)
   12030             : {
   12031           8 :     MOZ_ASSERT(*emitted == false);
   12032             : 
   12033           8 :     bool strict = IsStrictSetPC(pc);
   12034             : 
   12035           8 :     MConstant* id = constant(StringValue(name));
   12036          24 :     MSetPropertyCache* ins = MSetPropertyCache::New(alloc(), obj, id, value, strict,
   12037          16 :                                                     NeedsPostBarrier(value), barrier,
   12038           8 :                                                     /* guardHoles = */ false);
   12039           8 :     current->add(ins);
   12040           8 :     current->push(value);
   12041             : 
   12042           8 :     MOZ_TRY(resumeAfter(ins));
   12043             : 
   12044           8 :     trackOptimizationSuccess();
   12045           8 :     *emitted = true;
   12046           8 :     return Ok();
   12047             : }
   12048             : 
   12049             : AbortReasonOr<Ok>
   12050           0 : IonBuilder::jsop_delprop(PropertyName* name)
   12051             : {
   12052           0 :     MDefinition* obj = current->pop();
   12053             : 
   12054           0 :     bool strict = JSOp(*pc) == JSOP_STRICTDELPROP;
   12055           0 :     MInstruction* ins = MDeleteProperty::New(alloc(), obj, name, strict);
   12056             : 
   12057           0 :     current->add(ins);
   12058           0 :     current->push(ins);
   12059             : 
   12060           0 :     return resumeAfter(ins);
   12061             : }
   12062             : 
   12063             : AbortReasonOr<Ok>
   12064           0 : IonBuilder::jsop_delelem()
   12065             : {
   12066           0 :     MDefinition* index = current->pop();
   12067           0 :     MDefinition* obj = current->pop();
   12068             : 
   12069           0 :     bool strict = JSOp(*pc) == JSOP_STRICTDELELEM;
   12070           0 :     MDeleteElement* ins = MDeleteElement::New(alloc(), obj, index, strict);
   12071           0 :     current->add(ins);
   12072           0 :     current->push(ins);
   12073             : 
   12074           0 :     return resumeAfter(ins);
   12075             : }
   12076             : 
   12077             : AbortReasonOr<Ok>
   12078           0 : IonBuilder::jsop_regexp(RegExpObject* reobj)
   12079             : {
   12080           0 :     MOZ_ASSERT(!IsInsideNursery(reobj));
   12081             : 
   12082             :     // Determine this while we're still on the main thread to avoid races.
   12083           0 :     bool hasShared = reobj->hasShared();
   12084             : 
   12085           0 :     MRegExp* regexp = MRegExp::New(alloc(), constraints(), reobj, hasShared);
   12086           0 :     current->add(regexp);
   12087           0 :     current->push(regexp);
   12088             : 
   12089           0 :     return Ok();
   12090             : }
   12091             : 
   12092             : AbortReasonOr<Ok>
   12093           0 : IonBuilder::jsop_object(JSObject* obj)
   12094             : {
   12095           0 :     if (options.cloneSingletons()) {
   12096           0 :         MCloneLiteral* clone = MCloneLiteral::New(alloc(), constant(ObjectValue(*obj)));
   12097           0 :         current->add(clone);
   12098           0 :         current->push(clone);
   12099           0 :         return resumeAfter(clone);
   12100             :     }
   12101             : 
   12102           0 :     compartment->setSingletonsAsValues();
   12103           0 :     pushConstant(ObjectValue(*obj));
   12104           0 :     return Ok();
   12105             : }
   12106             : 
   12107             : AbortReasonOr<Ok>
   12108          28 : IonBuilder::jsop_lambda(JSFunction* fun)
   12109             : {
   12110          28 :     MOZ_ASSERT(analysis().usesEnvironmentChain());
   12111          28 :     MOZ_ASSERT(!fun->isArrow());
   12112             : 
   12113          28 :     if (IsAsmJSModule(fun))
   12114           0 :         return abort(AbortReason::Disable, "Lambda is an asm.js module function");
   12115             : 
   12116          28 :     MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
   12117          28 :     current->add(cst);
   12118          28 :     MLambda* ins = MLambda::New(alloc(), constraints(), current->environmentChain(), cst);
   12119          28 :     current->add(ins);
   12120          28 :     current->push(ins);
   12121             : 
   12122          28 :     return resumeAfter(ins);
   12123             : }
   12124             : 
   12125             : AbortReasonOr<Ok>
   12126           1 : IonBuilder::jsop_lambda_arrow(JSFunction* fun)
   12127             : {
   12128           1 :     MOZ_ASSERT(analysis().usesEnvironmentChain());
   12129           1 :     MOZ_ASSERT(fun->isArrow());
   12130           1 :     MOZ_ASSERT(!fun->isNative());
   12131             : 
   12132           1 :     MDefinition* newTargetDef = current->pop();
   12133           1 :     MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
   12134           1 :     current->add(cst);
   12135           2 :     MLambdaArrow* ins = MLambdaArrow::New(alloc(), constraints(), current->environmentChain(),
   12136           1 :                                           newTargetDef, cst);
   12137           1 :     current->add(ins);
   12138           1 :     current->push(ins);
   12139             : 
   12140           1 :     return resumeAfter(ins);
   12141             : }
   12142             : 
   12143             : AbortReasonOr<Ok>
   12144           0 : IonBuilder::jsop_setfunname(uint8_t prefixKind)
   12145             : {
   12146           0 :     MDefinition* name = current->pop();
   12147           0 :     MDefinition* fun = current->pop();
   12148           0 :     MOZ_ASSERT(fun->type() == MIRType::Object);
   12149             : 
   12150           0 :     MSetFunName* ins = MSetFunName::New(alloc(), fun, name, prefixKind);
   12151             : 
   12152           0 :     current->add(ins);
   12153           0 :     current->push(fun);
   12154             : 
   12155           0 :     return resumeAfter(ins);
   12156             : }
   12157             : 
   12158             : AbortReasonOr<Ok>
   12159           4 : IonBuilder::jsop_pushlexicalenv(uint32_t index)
   12160             : {
   12161           4 :     MOZ_ASSERT(analysis().usesEnvironmentChain());
   12162             : 
   12163           4 :     LexicalScope* scope = &script()->getScope(index)->as<LexicalScope>();
   12164             :     MNewLexicalEnvironmentObject* ins =
   12165           4 :         MNewLexicalEnvironmentObject::New(alloc(), current->environmentChain(), scope);
   12166             : 
   12167           4 :     current->add(ins);
   12168           4 :     current->setEnvironmentChain(ins);
   12169             : 
   12170           4 :     return Ok();
   12171             : }
   12172             : 
   12173             : AbortReasonOr<Ok>
   12174           1 : IonBuilder::jsop_copylexicalenv(bool copySlots)
   12175             : {
   12176           1 :     MOZ_ASSERT(analysis().usesEnvironmentChain());
   12177             : 
   12178             :     MCopyLexicalEnvironmentObject* ins =
   12179           1 :         MCopyLexicalEnvironmentObject::New(alloc(), current->environmentChain(), copySlots);
   12180             : 
   12181           1 :     current->add(ins);
   12182           1 :     current->setEnvironmentChain(ins);
   12183             : 
   12184           1 :     return Ok();
   12185             : }
   12186             : 
   12187             : AbortReasonOr<Ok>
   12188          20 : IonBuilder::jsop_setarg(uint32_t arg)
   12189             : {
   12190             :     // To handle this case, we should spill the arguments to the space where
   12191             :     // actual arguments are stored. The tricky part is that if we add a MIR
   12192             :     // to wrap the spilling action, we don't want the spilling to be
   12193             :     // captured by the GETARG and by the resume point, only by
   12194             :     // MGetFrameArgument.
   12195          20 :     MOZ_ASSERT(analysis_.hasSetArg());
   12196          20 :     MDefinition* val = current->peek(-1);
   12197             : 
   12198             :     // If an arguments object is in use, and it aliases formals, then all SETARGs
   12199             :     // must go through the arguments object.
   12200          20 :     if (info().argsObjAliasesFormals()) {
   12201           2 :         if (NeedsPostBarrier(val))
   12202           0 :             current->add(MPostWriteBarrier::New(alloc(), current->argumentsObject(), val));
   12203           4 :         current->add(MSetArgumentsObjectArg::New(alloc(), current->argumentsObject(),
   12204           6 :                                                  GET_ARGNO(pc), val));
   12205           2 :         return Ok();
   12206             :     }
   12207             : 
   12208             :     // :TODO: if hasArguments() is true, and the script has a JSOP_SETARG, then
   12209             :     // convert all arg accesses to go through the arguments object. (see Bug 957475)
   12210          18 :     if (info().hasArguments())
   12211           0 :         return abort(AbortReason::Disable, "NYI: arguments & setarg.");
   12212             : 
   12213             :     // Otherwise, if a magic arguments is in use, and it aliases formals, and there exist
   12214             :     // arguments[...] GETELEM expressions in the script, then SetFrameArgument must be used.
   12215             :     // If no arguments[...] GETELEM expressions are in the script, and an argsobj is not
   12216             :     // required, then it means that any aliased argument set can never be observed, and
   12217             :     // the frame does not actually need to be updated with the new arg value.
   12218          18 :     if (info().argumentsAliasesFormals()) {
   12219             :         // JSOP_SETARG with magic arguments within inline frames is not yet supported.
   12220           0 :         MOZ_ASSERT(script()->uninlineable() && !isInlineBuilder());
   12221             : 
   12222           0 :         MSetFrameArgument* store = MSetFrameArgument::New(alloc(), arg, val);
   12223           0 :         modifiesFrameArguments_ = true;
   12224           0 :         current->add(store);
   12225           0 :         current->setArg(arg);
   12226           0 :         return Ok();
   12227             :     }
   12228             : 
   12229             :     // If this assignment is at the start of the function and is coercing
   12230             :     // the original value for the argument which was passed in, loosen
   12231             :     // the type information for that original argument if it is currently
   12232             :     // empty due to originally executing in the interpreter.
   12233          22 :     if (graph().numBlocks() == 1 &&
   12234           8 :         (val->isBitOr() || val->isBitAnd() || val->isMul() /* for JSOP_POS */))
   12235             :      {
   12236           0 :          for (size_t i = 0; i < val->numOperands(); i++) {
   12237           0 :             MDefinition* op = val->getOperand(i);
   12238           0 :             if (op->isParameter() &&
   12239           0 :                 op->toParameter()->index() == (int32_t)arg &&
   12240           0 :                 op->resultTypeSet() &&
   12241           0 :                 op->resultTypeSet()->empty())
   12242             :             {
   12243           0 :                 bool otherUses = false;
   12244           0 :                 for (MUseDefIterator iter(op); iter; iter++) {
   12245           0 :                     MDefinition* def = iter.def();
   12246           0 :                     if (def == val)
   12247           0 :                         continue;
   12248           0 :                     otherUses = true;
   12249             :                 }
   12250           0 :                 if (!otherUses) {
   12251           0 :                     MOZ_ASSERT(op->resultTypeSet() == &argTypes[arg]);
   12252           0 :                     argTypes[arg].addType(TypeSet::UnknownType(), alloc_->lifoAlloc());
   12253           0 :                     if (val->isMul()) {
   12254           0 :                         val->setResultType(MIRType::Double);
   12255           0 :                         val->toMul()->setSpecialization(MIRType::Double);
   12256             :                     } else {
   12257           0 :                         MOZ_ASSERT(val->type() == MIRType::Int32);
   12258             :                     }
   12259           0 :                     val->setResultTypeSet(nullptr);
   12260             :                 }
   12261             :             }
   12262             :         }
   12263             :     }
   12264             : 
   12265          18 :     current->setArg(arg);
   12266          18 :     return Ok();
   12267             : }
   12268             : 
   12269             : AbortReasonOr<Ok>
   12270           0 : IonBuilder::jsop_defvar(uint32_t index)
   12271             : {
   12272           0 :     MOZ_ASSERT(JSOp(*pc) == JSOP_DEFVAR);
   12273             : 
   12274           0 :     PropertyName* name = script()->getName(index);
   12275             : 
   12276             :     // Bake in attrs.
   12277           0 :     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
   12278           0 :     MOZ_ASSERT(!script()->isForEval());
   12279             : 
   12280             :     // Pass the EnvironmentChain.
   12281           0 :     MOZ_ASSERT(analysis().usesEnvironmentChain());
   12282             : 
   12283             :     // Bake the name pointer into the MDefVar.
   12284           0 :     MDefVar* defvar = MDefVar::New(alloc(), name, attrs, current->environmentChain());
   12285           0 :     current->add(defvar);
   12286             : 
   12287           0 :     return resumeAfter(defvar);
   12288             : }
   12289             : 
   12290             : AbortReasonOr<Ok>
   12291           0 : IonBuilder::jsop_deflexical(uint32_t index)
   12292             : {
   12293           0 :     MOZ_ASSERT(!script()->hasNonSyntacticScope());
   12294           0 :     MOZ_ASSERT(JSOp(*pc) == JSOP_DEFLET || JSOp(*pc) == JSOP_DEFCONST);
   12295             : 
   12296           0 :     PropertyName* name = script()->getName(index);
   12297           0 :     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
   12298           0 :     if (JSOp(*pc) == JSOP_DEFCONST)
   12299           0 :         attrs |= JSPROP_READONLY;
   12300             : 
   12301           0 :     MDefLexical* deflex = MDefLexical::New(alloc(), name, attrs);
   12302           0 :     current->add(deflex);
   12303             : 
   12304           0 :     return resumeAfter(deflex);
   12305             : }
   12306             : 
   12307             : AbortReasonOr<Ok>
   12308           0 : IonBuilder::jsop_deffun(uint32_t index)
   12309             : {
   12310           0 :     MOZ_ASSERT(analysis().usesEnvironmentChain());
   12311             : 
   12312           0 :     MDefFun* deffun = MDefFun::New(alloc(), current->pop(), current->environmentChain());
   12313           0 :     current->add(deffun);
   12314             : 
   12315           0 :     return resumeAfter(deffun);
   12316             : }
   12317             : 
   12318             : AbortReasonOr<Ok>
   12319           0 : IonBuilder::jsop_throwsetconst()
   12320             : {
   12321           0 :     current->peek(-1)->setImplicitlyUsedUnchecked();
   12322           0 :     MInstruction* lexicalError = MThrowRuntimeLexicalError::New(alloc(), JSMSG_BAD_CONST_ASSIGN);
   12323           0 :     current->add(lexicalError);
   12324           0 :     return resumeAfter(lexicalError);
   12325             : }
   12326             : 
   12327             : AbortReasonOr<Ok>
   12328           0 : IonBuilder::jsop_checklexical()
   12329             : {
   12330           0 :     uint32_t slot = info().localSlot(GET_LOCALNO(pc));
   12331             :     MDefinition* lexical;
   12332           0 :     MOZ_TRY_VAR(lexical, addLexicalCheck(current->getSlot(slot)));
   12333           0 :     current->setSlot(slot, lexical);
   12334           0 :     return Ok();
   12335             : }
   12336             : 
   12337             : AbortReasonOr<Ok>
   12338           2 : IonBuilder::jsop_checkaliasedlexical(EnvironmentCoordinate ec)
   12339             : {
   12340             :     MDefinition* let;
   12341           2 :     MOZ_TRY_VAR(let, addLexicalCheck(getAliasedVar(ec)));
   12342             : 
   12343           2 :     jsbytecode* nextPc = pc + JSOP_CHECKALIASEDLEXICAL_LENGTH;
   12344           2 :     MOZ_ASSERT(JSOp(*nextPc) == JSOP_GETALIASEDVAR ||
   12345             :                JSOp(*nextPc) == JSOP_SETALIASEDVAR ||
   12346             :                JSOp(*nextPc) == JSOP_THROWSETALIASEDCONST);
   12347           2 :     MOZ_ASSERT(ec == EnvironmentCoordinate(nextPc));
   12348             : 
   12349             :     // If we are checking for a load, push the checked let so that the load
   12350             :     // can use it.
   12351           2 :     if (JSOp(*nextPc) == JSOP_GETALIASEDVAR)
   12352           0 :         setLexicalCheck(let);
   12353             : 
   12354           2 :     return Ok();
   12355             : }
   12356             : 
   12357             : AbortReasonOr<Ok>
   12358         111 : IonBuilder::jsop_functionthis()
   12359             : {
   12360         111 :     MOZ_ASSERT(info().funMaybeLazy());
   12361         111 :     MOZ_ASSERT(!info().funMaybeLazy()->isArrow());
   12362             : 
   12363         111 :     if (script()->strict() || info().funMaybeLazy()->isSelfHostedBuiltin()) {
   12364             :         // No need to wrap primitive |this| in strict mode or self-hosted code.
   12365         105 :         current->pushSlot(info().thisSlot());
   12366         105 :         return Ok();
   12367             :     }
   12368             : 
   12369          12 :     if (thisTypes && (thisTypes->getKnownMIRType() == MIRType::Object ||
   12370          10 :         (thisTypes->empty() && baselineFrame_ && baselineFrame_->thisType.isSomeObject())))
   12371             :     {
   12372             :         // This is safe, because if the entry type of |this| is an object, it
   12373             :         // will necessarily be an object throughout the entire function. OSR
   12374             :         // can introduce a phi, but this phi will be specialized.
   12375           1 :         current->pushSlot(info().thisSlot());
   12376           1 :         return Ok();
   12377             :     }
   12378             : 
   12379             :     // If we are doing an analysis, we might not yet know the type of |this|.
   12380             :     // Instead of bailing out just push the |this| slot, as this code won't
   12381             :     // actually execute and it does not matter whether |this| is primitive.
   12382           5 :     if (info().isAnalysis()) {
   12383           5 :         current->pushSlot(info().thisSlot());
   12384           5 :         return Ok();
   12385             :     }
   12386             : 
   12387             :     // Hard case: |this| may be a primitive we have to wrap.
   12388           0 :     MDefinition* def = current->getSlot(info().thisSlot());
   12389             : 
   12390           0 :     if (def->type() == MIRType::Object) {
   12391           0 :         current->push(def);
   12392           0 :         return Ok();
   12393             :     }
   12394             : 
   12395           0 :     if (IsNullOrUndefined(def->type())) {
   12396           0 :         pushConstant(GetThisValue(&script()->global()));
   12397           0 :         return Ok();
   12398             :     }
   12399             : 
   12400           0 :     MComputeThis* thisObj = MComputeThis::New(alloc(), def);
   12401           0 :     current->add(thisObj);
   12402           0 :     current->push(thisObj);
   12403             : 
   12404           0 :     return resumeAfter(thisObj);
   12405             : }
   12406             : 
   12407             : AbortReasonOr<Ok>
   12408           0 : IonBuilder::jsop_globalthis()
   12409             : {
   12410           0 :     if (script()->hasNonSyntacticScope()) {
   12411             :         // Ion does not compile global scripts with a non-syntactic scope, but
   12412             :         // we can end up here when we're compiling an arrow function.
   12413           0 :         return abort(AbortReason::Disable, "JSOP_GLOBALTHIS in script with non-syntactic scope");
   12414             :     }
   12415             : 
   12416           0 :     LexicalEnvironmentObject* globalLexical = &script()->global().lexicalEnvironment();
   12417           0 :     pushConstant(globalLexical->thisValue());
   12418           0 :     return Ok();
   12419             : }
   12420             : 
   12421             : AbortReasonOr<Ok>
   12422           3 : IonBuilder::jsop_typeof()
   12423             : {
   12424           3 :     MDefinition* input = current->pop();
   12425           3 :     MTypeOf* ins = MTypeOf::New(alloc(), input, input->type());
   12426             : 
   12427           3 :     ins->cacheInputMaybeCallableOrEmulatesUndefined(constraints());
   12428             : 
   12429           3 :     current->add(ins);
   12430           3 :     current->push(ins);
   12431             : 
   12432           3 :     return Ok();
   12433             : }
   12434             : 
   12435             : AbortReasonOr<Ok>
   12436           0 : IonBuilder::jsop_toasync()
   12437             : {
   12438           0 :     MDefinition* unwrapped = current->pop();
   12439           0 :     MOZ_ASSERT(unwrapped->type() == MIRType::Object);
   12440             : 
   12441           0 :     MToAsync* ins = MToAsync::New(alloc(), unwrapped);
   12442             : 
   12443           0 :     current->add(ins);
   12444           0 :     current->push(ins);
   12445             : 
   12446           0 :     return resumeAfter(ins);
   12447             : }
   12448             : 
   12449             : AbortReasonOr<Ok>
   12450           0 : IonBuilder::jsop_toasyncgen()
   12451             : {
   12452           0 :     MDefinition* unwrapped = current->pop();
   12453           0 :     MOZ_ASSERT(unwrapped->type() == MIRType::Object);
   12454             : 
   12455           0 :     MToAsyncGen* ins = MToAsyncGen::New(alloc(), unwrapped);
   12456             : 
   12457           0 :     current->add(ins);
   12458           0 :     current->push(ins);
   12459             : 
   12460           0 :     return resumeAfter(ins);
   12461             : }
   12462             : 
   12463             : AbortReasonOr<Ok>
   12464           0 : IonBuilder::jsop_toasynciter()
   12465             : {
   12466           0 :     MDefinition* unwrapped = current->pop();
   12467           0 :     MOZ_ASSERT(unwrapped->type() == MIRType::Object);
   12468             : 
   12469           0 :     MToAsyncIter* ins = MToAsyncIter::New(alloc(), unwrapped);
   12470             : 
   12471           0 :     current->add(ins);
   12472           0 :     current->push(ins);
   12473             : 
   12474           0 :     return resumeAfter(ins);
   12475             : }
   12476             : 
   12477             : AbortReasonOr<Ok>
   12478           0 : IonBuilder::jsop_toid()
   12479             : {
   12480             :     // No-op if the index is an integer.
   12481           0 :     if (current->peek(-1)->type() == MIRType::Int32)
   12482           0 :         return Ok();
   12483             : 
   12484           0 :     MDefinition* index = current->pop();
   12485           0 :     MToId* ins = MToId::New(alloc(), index);
   12486             : 
   12487           0 :     current->add(ins);
   12488           0 :     current->push(ins);
   12489             : 
   12490           0 :     return resumeAfter(ins);
   12491             : }
   12492             : 
   12493             : AbortReasonOr<Ok>
   12494           0 : IonBuilder::jsop_iter(uint8_t flags)
   12495             : {
   12496           0 :     if (flags != JSITER_ENUMERATE)
   12497           0 :         nonStringIteration_ = true;
   12498             : 
   12499           0 :     MDefinition* obj = current->pop();
   12500           0 :     MInstruction* ins = MIteratorStart::New(alloc(), obj, flags);
   12501             : 
   12502           0 :     if (!outermostBuilder()->iterators_.append(ins))
   12503           0 :         return abort(AbortReason::Alloc);
   12504             : 
   12505           0 :     current->add(ins);
   12506           0 :     current->push(ins);
   12507             : 
   12508           0 :     return resumeAfter(ins);
   12509             : }
   12510             : 
   12511             : AbortReasonOr<Ok>
   12512           0 : IonBuilder::jsop_itermore()
   12513             : {
   12514           0 :     MDefinition* iter = current->peek(-1);
   12515           0 :     MInstruction* ins = MIteratorMore::New(alloc(), iter);
   12516             : 
   12517           0 :     current->add(ins);
   12518           0 :     current->push(ins);
   12519             : 
   12520           0 :     return resumeAfter(ins);
   12521             : }
   12522             : 
   12523             : AbortReasonOr<Ok>
   12524           0 : IonBuilder::jsop_isnoiter()
   12525             : {
   12526           0 :     MDefinition* def = current->peek(-1);
   12527           0 :     MOZ_ASSERT(def->isIteratorMore());
   12528             : 
   12529           0 :     MInstruction* ins = MIsNoIter::New(alloc(), def);
   12530           0 :     current->add(ins);
   12531           0 :     current->push(ins);
   12532             : 
   12533           0 :     return Ok();
   12534             : }
   12535             : 
   12536             : AbortReasonOr<Ok>
   12537           0 : IonBuilder::jsop_iterend()
   12538             : {
   12539           0 :     MDefinition* iter = current->pop();
   12540           0 :     MInstruction* ins = MIteratorEnd::New(alloc(), iter);
   12541             : 
   12542           0 :     current->add(ins);
   12543             : 
   12544           0 :     return resumeAfter(ins);
   12545             : }
   12546             : 
   12547             : MDefinition*
   12548         696 : IonBuilder::walkEnvironmentChain(unsigned hops)
   12549             : {
   12550         696 :     MDefinition* env = current->getSlot(info().environmentChainSlot());
   12551             : 
   12552         699 :     for (unsigned i = 0; i < hops; i++) {
   12553           3 :         MInstruction* ins = MEnclosingEnvironment::New(alloc(), env);
   12554           3 :         current->add(ins);
   12555           3 :         env = ins;
   12556             :     }
   12557             : 
   12558         696 :     return env;
   12559             : }
   12560             : 
   12561             : bool
   12562         691 : IonBuilder::hasStaticEnvironmentObject(EnvironmentCoordinate ec, JSObject** pcall)
   12563             : {
   12564         691 :     JSScript* outerScript = EnvironmentCoordinateFunctionScript(script(), pc);
   12565         691 :     if (!outerScript || !outerScript->treatAsRunOnce())
   12566         691 :         return false;
   12567             : 
   12568             :     TypeSet::ObjectKey* funKey =
   12569           0 :         TypeSet::ObjectKey::get(outerScript->functionNonDelazifying());
   12570           0 :     if (funKey->hasFlags(constraints(), OBJECT_FLAG_RUNONCE_INVALIDATED))
   12571           0 :         return false;
   12572             : 
   12573             :     // The script this aliased var operation is accessing will run only once,
   12574             :     // so there will be only one call object and the aliased var access can be
   12575             :     // compiled in the same manner as a global access. We still need to find
   12576             :     // the call object though.
   12577             : 
   12578             :     // Look for the call object on the current script's function's env chain.
   12579             :     // If the current script is inner to the outer script and the function has
   12580             :     // singleton type then it should show up here.
   12581             : 
   12582           0 :     MDefinition* envDef = current->getSlot(info().environmentChainSlot());
   12583           0 :     envDef->setImplicitlyUsedUnchecked();
   12584             : 
   12585           0 :     JSObject* environment = script()->functionNonDelazifying()->environment();
   12586           0 :     while (environment && !environment->is<GlobalObject>()) {
   12587           0 :         if (environment->is<CallObject>() &&
   12588           0 :             environment->as<CallObject>().callee().nonLazyScript() == outerScript)
   12589             :         {
   12590           0 :             MOZ_ASSERT(environment->isSingleton());
   12591           0 :             *pcall = environment;
   12592           0 :             return true;
   12593             :         }
   12594           0 :         environment = environment->enclosingEnvironment();
   12595             :     }
   12596             : 
   12597             :     // Look for the call object on the current frame, if we are compiling the
   12598             :     // outer script itself. Don't do this if we are at entry to the outer
   12599             :     // script, as the call object we see will not be the real one --- after
   12600             :     // entering the Ion code a different call object will be created.
   12601             : 
   12602           0 :     if (script() == outerScript && baselineFrame_ && info().osrPc()) {
   12603           0 :         JSObject* singletonScope = baselineFrame_->singletonEnvChain;
   12604           0 :         if (singletonScope &&
   12605           0 :             singletonScope->is<CallObject>() &&
   12606           0 :             singletonScope->as<CallObject>().callee().nonLazyScript() == outerScript)
   12607             :         {
   12608           0 :             MOZ_ASSERT(singletonScope->isSingleton());
   12609           0 :             *pcall = singletonScope;
   12610           0 :             return true;
   12611             :         }
   12612             :     }
   12613             : 
   12614           0 :     return true;
   12615             : }
   12616             : 
   12617             : MDefinition*
   12618         635 : IonBuilder::getAliasedVar(EnvironmentCoordinate ec)
   12619             : {
   12620         635 :     MDefinition* obj = walkEnvironmentChain(ec.hops());
   12621             : 
   12622         635 :     Shape* shape = EnvironmentCoordinateToEnvironmentShape(script(), pc);
   12623             : 
   12624             :     MInstruction* load;
   12625         635 :     if (shape->numFixedSlots() <= ec.slot()) {
   12626           0 :         MInstruction* slots = MSlots::New(alloc(), obj);
   12627           0 :         current->add(slots);
   12628             : 
   12629           0 :         load = MLoadSlot::New(alloc(), slots, ec.slot() - shape->numFixedSlots());
   12630             :     } else {
   12631         635 :         load = MLoadFixedSlot::New(alloc(), obj, ec.slot());
   12632             :     }
   12633             : 
   12634         635 :     current->add(load);
   12635         635 :     return load;
   12636             : }
   12637             : 
   12638             : AbortReasonOr<Ok>
   12639         633 : IonBuilder::jsop_getaliasedvar(EnvironmentCoordinate ec)
   12640             : {
   12641         633 :     JSObject* call = nullptr;
   12642         633 :     if (hasStaticEnvironmentObject(ec, &call) && call) {
   12643           0 :         PropertyName* name = EnvironmentCoordinateName(envCoordinateNameCache, script(), pc);
   12644           0 :         bool emitted = false;
   12645           0 :         MOZ_TRY(getStaticName(&emitted, call, name, takeLexicalCheck()));
   12646           0 :         if (emitted)
   12647           0 :             return Ok();
   12648             :     }
   12649             : 
   12650             :     // See jsop_checkaliasedlexical.
   12651         633 :     MDefinition* load = takeLexicalCheck();
   12652         633 :     if (!load)
   12653         633 :         load = getAliasedVar(ec);
   12654         633 :     current->push(load);
   12655             : 
   12656         633 :     TemporaryTypeSet* types = bytecodeTypes(pc);
   12657         633 :     return pushTypeBarrier(load, types, BarrierKind::TypeSet);
   12658             : }
   12659             : 
   12660             : AbortReasonOr<Ok>
   12661          58 : IonBuilder::jsop_setaliasedvar(EnvironmentCoordinate ec)
   12662             : {
   12663          58 :     JSObject* call = nullptr;
   12664          58 :     if (hasStaticEnvironmentObject(ec, &call)) {
   12665           0 :         uint32_t depth = current->stackDepth() + 1;
   12666           0 :         if (depth > current->nslots()) {
   12667           0 :             if (!current->increaseSlots(depth - current->nslots()))
   12668           0 :                 return abort(AbortReason::Alloc);
   12669             :         }
   12670           0 :         MDefinition* value = current->pop();
   12671           0 :         PropertyName* name = EnvironmentCoordinateName(envCoordinateNameCache, script(), pc);
   12672             : 
   12673           0 :         if (call) {
   12674             :             // Push the object on the stack to match the bound object expected in
   12675             :             // the global and property set cases.
   12676           0 :             pushConstant(ObjectValue(*call));
   12677           0 :             current->push(value);
   12678           0 :             return setStaticName(call, name);
   12679             :         }
   12680             : 
   12681             :         // The call object has type information we need to respect but we
   12682             :         // couldn't find it. Just do a normal property assign.
   12683           0 :         MDefinition* obj = walkEnvironmentChain(ec.hops());
   12684           0 :         current->push(obj);
   12685           0 :         current->push(value);
   12686           0 :         return jsop_setprop(name);
   12687             :     }
   12688             : 
   12689          58 :     MDefinition* rval = current->peek(-1);
   12690          58 :     MDefinition* obj = walkEnvironmentChain(ec.hops());
   12691             : 
   12692          58 :     Shape* shape = EnvironmentCoordinateToEnvironmentShape(script(), pc);
   12693             : 
   12694          58 :     if (NeedsPostBarrier(rval))
   12695           6 :         current->add(MPostWriteBarrier::New(alloc(), obj, rval));
   12696             : 
   12697             :     MInstruction* store;
   12698          58 :     if (shape->numFixedSlots() <= ec.slot()) {
   12699           0 :         MInstruction* slots = MSlots::New(alloc(), obj);
   12700           0 :         current->add(slots);
   12701             : 
   12702           0 :         store = MStoreSlot::NewBarriered(alloc(), slots, ec.slot() - shape->numFixedSlots(), rval);
   12703             :     } else {
   12704          58 :         store = MStoreFixedSlot::NewBarriered(alloc(), obj, ec.slot(), rval);
   12705             :     }
   12706             : 
   12707          58 :     current->add(store);
   12708          58 :     return resumeAfter(store);
   12709             : }
   12710             : 
   12711             : AbortReasonOr<Ok>
   12712          91 : IonBuilder::jsop_in()
   12713             : {
   12714          91 :     MDefinition* obj = convertUnboxedObjects(current->pop());
   12715          91 :     MDefinition* id = current->pop();
   12716             : 
   12717          91 :     if (!forceInlineCaches()) {
   12718          91 :         bool emitted = false;
   12719             : 
   12720          91 :         MOZ_TRY(inTryDense(&emitted, obj, id));
   12721          91 :         if (emitted)
   12722           0 :             return Ok();
   12723             : 
   12724          91 :         MOZ_TRY(hasTryNotDefined(&emitted, obj, id, /* ownProperty = */ false));
   12725          91 :         if (emitted)
   12726           0 :             return Ok();
   12727             :     }
   12728             : 
   12729          91 :     MInCache* ins = MInCache::New(alloc(), id, obj);
   12730             : 
   12731          91 :     current->add(ins);
   12732          91 :     current->push(ins);
   12733             : 
   12734          91 :     return resumeAfter(ins);
   12735             : }
   12736             : 
   12737             : AbortReasonOr<Ok>
   12738          91 : IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
   12739             : {
   12740          91 :     MOZ_ASSERT(!*emitted);
   12741             : 
   12742          91 :     if (shouldAbortOnPreliminaryGroups(obj))
   12743           0 :         return Ok();
   12744             : 
   12745          91 :     JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id);
   12746          91 :     if (unboxedType == JSVAL_TYPE_MAGIC) {
   12747          91 :         if (!ElementAccessIsDenseNative(constraints(), obj, id))
   12748          91 :             return Ok();
   12749             :     }
   12750             : 
   12751             :     bool hasExtraIndexedProperty;
   12752           0 :     MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
   12753           0 :     if (hasExtraIndexedProperty)
   12754           0 :         return Ok();
   12755             : 
   12756           0 :     *emitted = true;
   12757             : 
   12758           0 :     bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
   12759             : 
   12760             :     // Ensure id is an integer.
   12761           0 :     MInstruction* idInt32 = MToInt32::New(alloc(), id);
   12762           0 :     current->add(idInt32);
   12763           0 :     id = idInt32;
   12764             : 
   12765             :     // Get the elements vector.
   12766           0 :     MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
   12767           0 :     current->add(elements);
   12768             : 
   12769           0 :     MInstruction* initLength = initializedLength(obj, elements, unboxedType);
   12770             : 
   12771             :     // If there are no holes, speculate the InArray check will not fail.
   12772           0 :     if (!needsHoleCheck && !failedBoundsCheck_) {
   12773           0 :         addBoundsCheck(idInt32, initLength);
   12774           0 :         pushConstant(BooleanValue(true));
   12775           0 :         return Ok();
   12776             :     }
   12777             : 
   12778             :     // Check if id < initLength and elem[id] not a hole.
   12779           0 :     MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck,
   12780           0 :                                   unboxedType);
   12781             : 
   12782           0 :     current->add(ins);
   12783           0 :     current->push(ins);
   12784             : 
   12785           0 :     return Ok();
   12786             : }
   12787             : 
   12788             : AbortReasonOr<Ok>
   12789          91 : IonBuilder::hasTryNotDefined(bool* emitted, MDefinition* obj, MDefinition* id, bool ownProperty)
   12790             : {
   12791             :     // Fold |id in obj| to |false|, if we know the object (or an object on its
   12792             :     // prototype chain) does not have this property.
   12793             : 
   12794          91 :     MOZ_ASSERT(!*emitted);
   12795             : 
   12796          91 :     MConstant* idConst = id->maybeConstantValue();
   12797             :     jsid propId;
   12798          91 :     if (!idConst || !ValueToIdPure(idConst->toJSValue(), &propId))
   12799          90 :         return Ok();
   12800             : 
   12801           1 :     if (propId != IdToTypeId(propId))
   12802           0 :         return Ok();
   12803             : 
   12804             :     bool res;
   12805           1 :     MOZ_TRY_VAR(res, testNotDefinedProperty(obj, propId, ownProperty));
   12806           1 :     if (!res)
   12807           1 :         return Ok();
   12808             : 
   12809           0 :     *emitted = true;
   12810             : 
   12811           0 :     pushConstant(BooleanValue(false));
   12812           0 :     obj->setImplicitlyUsedUnchecked();
   12813           0 :     id->setImplicitlyUsedUnchecked();
   12814           0 :     return Ok();
   12815             : }
   12816             : 
   12817             : AbortReasonOr<Ok>
   12818           0 : IonBuilder::jsop_hasown()
   12819             : {
   12820           0 :     MDefinition* obj = convertUnboxedObjects(current->pop());
   12821           0 :     MDefinition* id = current->pop();
   12822             : 
   12823           0 :     if (!forceInlineCaches()) {
   12824           0 :         bool emitted = false;
   12825           0 :         MOZ_TRY(hasTryNotDefined(&emitted, obj, id, /* ownProperty = */ true));
   12826           0 :         if (emitted)
   12827           0 :             return Ok();
   12828             :     }
   12829             : 
   12830           0 :     MHasOwnCache* ins = MHasOwnCache::New(alloc(), obj, id);
   12831           0 :     current->add(ins);
   12832           0 :     current->push(ins);
   12833             : 
   12834           0 :     MOZ_TRY(resumeAfter(ins));
   12835           0 :     return Ok();
   12836             : }
   12837             : 
   12838             : AbortReasonOr<bool>
   12839           0 : IonBuilder::hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* onProto)
   12840             : {
   12841           0 :     MOZ_ASSERT(protoObject);
   12842             : 
   12843             :     while (true) {
   12844           0 :         if (!alloc().ensureBallast())
   12845           0 :             return abort(AbortReason::Alloc);
   12846             : 
   12847           0 :         if (!key->hasStableClassAndProto(constraints()) || !key->clasp()->isNative())
   12848           0 :             return false;
   12849             : 
   12850           0 :         JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
   12851           0 :         if (!proto) {
   12852           0 :             *onProto = false;
   12853           0 :             return true;
   12854             :         }
   12855             : 
   12856           0 :         if (proto == protoObject) {
   12857           0 :             *onProto = true;
   12858           0 :             return true;
   12859             :         }
   12860             : 
   12861           0 :         key = TypeSet::ObjectKey::get(proto);
   12862           0 :     }
   12863             : 
   12864             :     MOZ_CRASH("Unreachable");
   12865             : }
   12866             : 
   12867             : AbortReasonOr<Ok>
   12868           0 : IonBuilder::tryFoldInstanceOf(bool* emitted, MDefinition* lhs, JSObject* protoObject)
   12869             : {
   12870             :     // Try to fold the js::IsDelegate part of the instanceof operation.
   12871           0 :     MOZ_ASSERT(*emitted == false);
   12872             : 
   12873           0 :     if (!lhs->mightBeType(MIRType::Object)) {
   12874             :         // If the lhs is a primitive, the result is false.
   12875           0 :         lhs->setImplicitlyUsedUnchecked();
   12876           0 :         pushConstant(BooleanValue(false));
   12877           0 :         *emitted = true;
   12878           0 :         return Ok();
   12879             :     }
   12880             : 
   12881           0 :     TemporaryTypeSet* lhsTypes = lhs->resultTypeSet();
   12882           0 :     if (!lhsTypes || lhsTypes->unknownObject())
   12883           0 :         return Ok();
   12884             : 
   12885             :     // We can fold if either all objects have protoObject on their proto chain
   12886             :     // or none have.
   12887           0 :     bool isFirst = true;
   12888           0 :     bool knownIsInstance = false;
   12889             : 
   12890           0 :     for (unsigned i = 0; i < lhsTypes->getObjectCount(); i++) {
   12891           0 :         TypeSet::ObjectKey* key = lhsTypes->getObject(i);
   12892           0 :         if (!key)
   12893           0 :             continue;
   12894             : 
   12895             :         bool checkSucceeded;
   12896             :         bool isInstance;
   12897           0 :         MOZ_TRY_VAR(checkSucceeded, hasOnProtoChain(key, protoObject, &isInstance));
   12898           0 :         if (!checkSucceeded)
   12899           0 :             return Ok();
   12900             : 
   12901           0 :         if (isFirst) {
   12902           0 :             knownIsInstance = isInstance;
   12903           0 :             isFirst = false;
   12904           0 :         } else if (knownIsInstance != isInstance) {
   12905             :             // Some of the objects have protoObject on their proto chain and
   12906             :             // others don't, so we can't optimize this.
   12907           0 :             return Ok();
   12908             :         }
   12909             :     }
   12910             : 
   12911           0 :     if (knownIsInstance && lhsTypes->getKnownMIRType() != MIRType::Object) {
   12912             :         // The result is true for all objects, but the lhs might be a primitive.
   12913             :         // We can't fold this completely but we can use a much faster IsObject
   12914             :         // test.
   12915           0 :         MIsObject* isObject = MIsObject::New(alloc(), lhs);
   12916           0 :         current->add(isObject);
   12917           0 :         current->push(isObject);
   12918           0 :         *emitted = true;
   12919           0 :         return Ok();
   12920             :     }
   12921             : 
   12922           0 :     lhs->setImplicitlyUsedUnchecked();
   12923           0 :     pushConstant(BooleanValue(knownIsInstance));
   12924           0 :     *emitted = true;
   12925           0 :     return Ok();
   12926             : }
   12927             : 
   12928             : AbortReasonOr<Ok>
   12929           0 : IonBuilder::jsop_instanceof()
   12930             : {
   12931           0 :     MDefinition* rhs = current->pop();
   12932           0 :     MDefinition* obj = current->pop();
   12933           0 :     bool emitted = false;
   12934             : 
   12935             :     // If this is an 'x instanceof function' operation and we can determine the
   12936             :     // exact function and prototype object being tested for, use a typed path.
   12937             :     do {
   12938           0 :         TemporaryTypeSet* rhsTypes = rhs->resultTypeSet();
   12939           0 :         JSObject* rhsObject = rhsTypes ? rhsTypes->maybeSingleton() : nullptr;
   12940           0 :         if (!rhsObject || !rhsObject->is<JSFunction>() || rhsObject->isBoundFunction())
   12941           0 :             break;
   12942             : 
   12943             :         // Refuse to optimize anything whose [[Prototype]] isn't Function.prototype
   12944             :         // since we can't guarantee that it uses the default @@hasInstance method.
   12945           0 :         if (rhsObject->hasUncacheableProto() || !rhsObject->hasStaticPrototype())
   12946           0 :             break;
   12947             : 
   12948           0 :         Value funProto = script()->global().getPrototype(JSProto_Function);
   12949           0 :         if (!funProto.isObject() || rhsObject->staticPrototype() != &funProto.toObject())
   12950           0 :             break;
   12951             : 
   12952             :         // If the user has supplied their own @@hasInstance method we shouldn't
   12953             :         // clobber it.
   12954           0 :         JSFunction* fun = &rhsObject->as<JSFunction>();
   12955           0 :         const WellKnownSymbols* symbols = &compartment->runtime()->wellKnownSymbols();
   12956           0 :         if (!js::FunctionHasDefaultHasInstance(fun, *symbols))
   12957           0 :             break;
   12958             : 
   12959             :         // Ensure that we will bail if the @@hasInstance property or [[Prototype]]
   12960             :         // change.
   12961           0 :         TypeSet::ObjectKey* rhsKey = TypeSet::ObjectKey::get(rhsObject);
   12962           0 :         if (!rhsKey->hasStableClassAndProto(constraints()))
   12963           0 :             break;
   12964             : 
   12965           0 :         if (rhsKey->unknownProperties())
   12966           0 :             break;
   12967             : 
   12968             :         HeapTypeSetKey hasInstanceObject =
   12969           0 :             rhsKey->property(SYMBOL_TO_JSID(symbols->hasInstance));
   12970           0 :         if (hasInstanceObject.isOwnProperty(constraints()))
   12971           0 :             break;
   12972             : 
   12973             :         HeapTypeSetKey protoProperty =
   12974           0 :             rhsKey->property(NameToId(names().prototype));
   12975           0 :         JSObject* protoObject = protoProperty.singleton(constraints());
   12976           0 :         if (!protoObject)
   12977           0 :             break;
   12978             : 
   12979           0 :         rhs->setImplicitlyUsedUnchecked();
   12980             : 
   12981           0 :         MOZ_TRY(tryFoldInstanceOf(&emitted, obj, protoObject));
   12982           0 :         if (emitted)
   12983           0 :             return Ok();
   12984             : 
   12985           0 :         MInstanceOf* ins = MInstanceOf::New(alloc(), obj, protoObject);
   12986             : 
   12987           0 :         current->add(ins);
   12988           0 :         current->push(ins);
   12989             : 
   12990           0 :         return resumeAfter(ins);
   12991             :     } while (false);
   12992             : 
   12993             :     // Try to inline a fast path based on Baseline ICs.
   12994             :     do {
   12995             :         Shape* shape;
   12996             :         uint32_t slot;
   12997             :         JSObject* protoObject;
   12998           0 :         if (!inspector->instanceOfData(pc, &shape, &slot, &protoObject))
   12999           0 :             break;
   13000             : 
   13001             :         // Shape guard.
   13002           0 :         rhs = addShapeGuard(rhs, shape, Bailout_ShapeGuard);
   13003             : 
   13004             :         // Guard .prototype == protoObject.
   13005           0 :         MOZ_ASSERT(shape->numFixedSlots() == 0, "Must be a dynamic slot");
   13006           0 :         MSlots* slots = MSlots::New(alloc(), rhs);
   13007           0 :         current->add(slots);
   13008           0 :         MLoadSlot* prototype = MLoadSlot::New(alloc(), slots, slot);
   13009           0 :         current->add(prototype);
   13010           0 :         MConstant* protoConst = MConstant::NewConstraintlessObject(alloc(), protoObject);
   13011           0 :         current->add(protoConst);
   13012           0 :         MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst,
   13013           0 :                                                                 /* bailOnEquality = */ false);
   13014           0 :         current->add(guard);
   13015             : 
   13016           0 :         MOZ_TRY(tryFoldInstanceOf(&emitted, obj, protoObject));
   13017           0 :         if (emitted)
   13018           0 :             return Ok();
   13019             : 
   13020           0 :         MInstanceOf* ins = MInstanceOf::New(alloc(), obj, protoObject);
   13021           0 :         current->add(ins);
   13022           0 :         current->push(ins);
   13023           0 :         return resumeAfter(ins);
   13024             :     } while (false);
   13025             : 
   13026           0 :     MCallInstanceOf* ins = MCallInstanceOf::New(alloc(), obj, rhs);
   13027             : 
   13028           0 :     current->add(ins);
   13029           0 :     current->push(ins);
   13030             : 
   13031           0 :     return resumeAfter(ins);
   13032             : }
   13033             : 
   13034             : AbortReasonOr<Ok>
   13035           0 : IonBuilder::jsop_debugger()
   13036             : {
   13037           0 :     MDebugger* debugger = MDebugger::New(alloc());
   13038           0 :     current->add(debugger);
   13039             : 
   13040             :     // The |debugger;| statement will always bail out to baseline if
   13041             :     // cx->compartment()->isDebuggee(). Resume in-place and have baseline
   13042             :     // handle the details.
   13043           0 :     return resumeAt(debugger, pc);
   13044             : }
   13045             : 
   13046             : MInstruction*
   13047           0 : IonBuilder::addConvertElementsToDoubles(MDefinition* elements)
   13048             : {
   13049           0 :     MInstruction* convert = MConvertElementsToDoubles::New(alloc(), elements);
   13050           0 :     current->add(convert);
   13051           0 :     return convert;
   13052             : }
   13053             : 
   13054             : MDefinition*
   13055           3 : IonBuilder::addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
   13056             : {
   13057           3 :     if (!ElementAccessMightBeCopyOnWrite(constraints(), object))
   13058           3 :         return object;
   13059           0 :     MInstruction* copy = MMaybeCopyElementsForWrite::New(alloc(), object, checkNative);
   13060           0 :     current->add(copy);
   13061           0 :     return copy;
   13062             : }
   13063             : 
   13064             : MInstruction*
   13065           8 : IonBuilder::addBoundsCheck(MDefinition* index, MDefinition* length)
   13066             : {
   13067           8 :     MInstruction* check = MBoundsCheck::New(alloc(), index, length);
   13068           8 :     current->add(check);
   13069             : 
   13070             :     // If a bounds check failed in the past, don't optimize bounds checks.
   13071           8 :     if (failedBoundsCheck_)
   13072           0 :         check->setNotMovable();
   13073             : 
   13074           8 :     return check;
   13075             : }
   13076             : 
   13077             : MInstruction*
   13078           9 : IonBuilder::addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind)
   13079             : {
   13080           9 :     MGuardShape* guard = MGuardShape::New(alloc(), obj, shape, bailoutKind);
   13081           9 :     current->add(guard);
   13082             : 
   13083             :     // If a shape guard failed in the past, don't optimize shape guard.
   13084           9 :     if (failedShapeGuard_)
   13085           0 :         guard->setNotMovable();
   13086             : 
   13087           9 :     return guard;
   13088             : }
   13089             : 
   13090             : MInstruction*
   13091           0 : IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind)
   13092             : {
   13093           0 :     MGuardObjectGroup* guard = MGuardObjectGroup::New(alloc(), obj, group,
   13094           0 :                                                       /* bailOnEquality = */ false, bailoutKind);
   13095           0 :     current->add(guard);
   13096             : 
   13097             :     // If a shape guard failed in the past, don't optimize group guards.
   13098           0 :     if (failedShapeGuard_)
   13099           0 :         guard->setNotMovable();
   13100             : 
   13101           0 :     LifoAlloc* lifoAlloc = alloc().lifoAlloc();
   13102           0 :     guard->setResultTypeSet(lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc,
   13103           0 :                                                             TypeSet::ObjectType(group)));
   13104             : 
   13105           0 :     return guard;
   13106             : }
   13107             : 
   13108             : MInstruction*
   13109           0 : IonBuilder::addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind)
   13110             : {
   13111           0 :     MGuardUnboxedExpando* guard = MGuardUnboxedExpando::New(alloc(), obj, hasExpando, bailoutKind);
   13112           0 :     current->add(guard);
   13113             : 
   13114             :     // If a shape guard failed in the past, don't optimize group guards.
   13115           0 :     if (failedShapeGuard_)
   13116           0 :         guard->setNotMovable();
   13117             : 
   13118           0 :     return guard;
   13119             : }
   13120             : 
   13121             : MInstruction*
   13122           0 : IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj,
   13123             :                                         const BaselineInspector::ReceiverVector& receivers)
   13124             : {
   13125           0 :     if (receivers.length() == 1) {
   13126           0 :         if (!receivers[0].group) {
   13127             :             // Monomorphic guard on a native object.
   13128           0 :             return addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
   13129             :         }
   13130             : 
   13131           0 :         if (!receivers[0].shape) {
   13132             :             // Guard on an unboxed object that does not have an expando.
   13133           0 :             obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
   13134           0 :             return addUnboxedExpandoGuard(obj, /* hasExpando = */ false, Bailout_ShapeGuard);
   13135             :         }
   13136             : 
   13137             :         // Monomorphic receiver guards are not yet supported when the receiver
   13138             :         // is an unboxed object with an expando.
   13139             :     }
   13140             : 
   13141           0 :     MGuardReceiverPolymorphic* guard = MGuardReceiverPolymorphic::New(alloc(), obj);
   13142           0 :     current->add(guard);
   13143             : 
   13144           0 :     if (failedShapeGuard_)
   13145           0 :         guard->setNotMovable();
   13146             : 
   13147           0 :     for (size_t i = 0; i < receivers.length(); i++) {
   13148           0 :         if (!guard->addReceiver(receivers[i]))
   13149           0 :             return nullptr;
   13150             :     }
   13151             : 
   13152           0 :     return guard;
   13153             : }
   13154             : 
   13155             : MInstruction*
   13156           0 : IonBuilder::addSharedTypedArrayGuard(MDefinition* obj)
   13157             : {
   13158           0 :     MGuardSharedTypedArray* guard = MGuardSharedTypedArray::New(alloc(), obj);
   13159           0 :     current->add(guard);
   13160           0 :     return guard;
   13161             : }
   13162             : 
   13163             : TemporaryTypeSet*
   13164        5914 : IonBuilder::bytecodeTypes(jsbytecode* pc)
   13165             : {
   13166        5914 :     return TypeScript::BytecodeTypes(script(), pc, bytecodeTypeMap, &typeArrayHint, typeArray);
   13167             : }
   13168             : 
   13169             : TypedObjectPrediction
   13170         104 : IonBuilder::typedObjectPrediction(MDefinition* typedObj)
   13171             : {
   13172             :     // Extract TypedObjectPrediction directly if we can
   13173         104 :     if (typedObj->isNewDerivedTypedObject()) {
   13174           0 :         return typedObj->toNewDerivedTypedObject()->prediction();
   13175             :     }
   13176             : 
   13177         104 :     TemporaryTypeSet* types = typedObj->resultTypeSet();
   13178         104 :     return typedObjectPrediction(types);
   13179             : }
   13180             : 
   13181             : TypedObjectPrediction
   13182         104 : IonBuilder::typedObjectPrediction(TemporaryTypeSet* types)
   13183             : {
   13184             :     // Type set must be known to be an object.
   13185         104 :     if (!types || types->getKnownMIRType() != MIRType::Object)
   13186           0 :         return TypedObjectPrediction();
   13187             : 
   13188             :     // And only known objects.
   13189         104 :     if (types->unknownObject())
   13190          31 :         return TypedObjectPrediction();
   13191             : 
   13192          73 :     TypedObjectPrediction out;
   13193          73 :     for (uint32_t i = 0; i < types->getObjectCount(); i++) {
   13194          73 :         ObjectGroup* group = types->getGroup(i);
   13195          73 :         if (!group || !IsTypedObjectClass(group->clasp()))
   13196          73 :             return TypedObjectPrediction();
   13197             : 
   13198           0 :         if (!TypeSet::ObjectKey::get(group)->hasStableClassAndProto(constraints()))
   13199           0 :             return TypedObjectPrediction();
   13200             : 
   13201           0 :         out.addDescr(group->typeDescr());
   13202             :     }
   13203             : 
   13204           0 :     return out;
   13205             : }
   13206             : 
   13207             : MDefinition*
   13208           0 : IonBuilder::loadTypedObjectType(MDefinition* typedObj)
   13209             : {
   13210             :     // Shortcircuit derived type objects, meaning the intermediate
   13211             :     // objects created to represent `a.b` in an expression like
   13212             :     // `a.b.c`. In that case, the type object can be simply pulled
   13213             :     // from the operands of that instruction.
   13214           0 :     if (typedObj->isNewDerivedTypedObject())
   13215           0 :         return typedObj->toNewDerivedTypedObject()->type();
   13216             : 
   13217           0 :     MInstruction* descr = MTypedObjectDescr::New(alloc(), typedObj);
   13218           0 :     current->add(descr);
   13219             : 
   13220           0 :     return descr;
   13221             : }
   13222             : 
   13223             : // Given a typed object `typedObj` and an offset `offset` into that
   13224             : // object's data, returns another typed object and adusted offset
   13225             : // where the data can be found. Often, these returned values are the
   13226             : // same as the inputs, but in cases where intermediate derived type
   13227             : // objects have been created, the return values will remove
   13228             : // intermediate layers (often rendering those derived type objects
   13229             : // into dead code).
   13230             : AbortReasonOr<Ok>
   13231           0 : IonBuilder::loadTypedObjectData(MDefinition* typedObj,
   13232             :                                 MDefinition** owner,
   13233             :                                 LinearSum* ownerOffset)
   13234             : {
   13235           0 :     MOZ_ASSERT(typedObj->type() == MIRType::Object);
   13236             : 
   13237             :     // Shortcircuit derived type objects, meaning the intermediate
   13238             :     // objects created to represent `a.b` in an expression like
   13239             :     // `a.b.c`. In that case, the owned and a base offset can be
   13240             :     // pulled from the operands of the instruction and combined with
   13241             :     // `offset`.
   13242           0 :     if (typedObj->isNewDerivedTypedObject()) {
   13243           0 :         MNewDerivedTypedObject* ins = typedObj->toNewDerivedTypedObject();
   13244             : 
   13245           0 :         SimpleLinearSum base = ExtractLinearSum(ins->offset());
   13246           0 :         if (!ownerOffset->add(base))
   13247           0 :             return abort(AbortReason::Disable, "Overflow/underflow on type object offset.");
   13248             : 
   13249           0 :         *owner = ins->owner();
   13250           0 :         return Ok();
   13251             :     }
   13252             : 
   13253           0 :     *owner = typedObj;
   13254           0 :     return Ok();
   13255             : }
   13256             : 
   13257             : // Takes as input a typed object, an offset into that typed object's
   13258             : // memory, and the type repr of the data found at that offset. Returns
   13259             : // the elements pointer and a scaled offset. The scaled offset is
   13260             : // expressed in units of `unit`; when working with typed array MIR,
   13261             : // this is typically the alignment.
   13262             : AbortReasonOr<Ok>
   13263           0 : IonBuilder::loadTypedObjectElements(MDefinition* typedObj,
   13264             :                                     const LinearSum& baseByteOffset,
   13265             :                                     uint32_t scale,
   13266             :                                     MDefinition** ownerElements,
   13267             :                                     MDefinition** ownerScaledOffset,
   13268             :                                     int32_t* ownerByteAdjustment)
   13269             : {
   13270             :     MDefinition* owner;
   13271           0 :     LinearSum ownerByteOffset(alloc());
   13272           0 :     MOZ_TRY(loadTypedObjectData(typedObj, &owner, &ownerByteOffset));
   13273             : 
   13274           0 :     if (!ownerByteOffset.add(baseByteOffset))
   13275           0 :         return abort(AbortReason::Disable, "Overflow after adding the base offset.");
   13276             : 
   13277           0 :     TemporaryTypeSet* ownerTypes = owner->resultTypeSet();
   13278           0 :     const Class* clasp = ownerTypes ? ownerTypes->getKnownClass(constraints()) : nullptr;
   13279           0 :     if (clasp && IsInlineTypedObjectClass(clasp)) {
   13280             :         // Perform the load directly from the owner pointer.
   13281           0 :         if (!ownerByteOffset.add(InlineTypedObject::offsetOfDataStart()))
   13282           0 :             return abort(AbortReason::Disable, "Overflow after adding the data start.");
   13283           0 :         *ownerElements = owner;
   13284             :     } else {
   13285           0 :         bool definitelyOutline = clasp && IsOutlineTypedObjectClass(clasp);
   13286           0 :         *ownerElements = MTypedObjectElements::New(alloc(), owner, definitelyOutline);
   13287           0 :         current->add((*ownerElements)->toInstruction());
   13288             :     }
   13289             : 
   13290             :     // Extract the constant adjustment from the byte offset.
   13291           0 :     *ownerByteAdjustment = ownerByteOffset.constant();
   13292             :     int32_t negativeAdjustment;
   13293           0 :     if (!SafeSub(0, *ownerByteAdjustment, &negativeAdjustment))
   13294           0 :         return abort(AbortReason::Disable);
   13295           0 :     if (!ownerByteOffset.add(negativeAdjustment))
   13296           0 :         return abort(AbortReason::Disable);
   13297             : 
   13298             :     // Scale the byte offset if required by the MIR node which will access the
   13299             :     // typed object. In principle we should always be able to cleanly divide
   13300             :     // the terms in this lienar sum due to alignment restrictions, but due to
   13301             :     // limitations of ExtractLinearSum when applied to the terms in derived
   13302             :     // typed objects this isn't always be possible. In these cases, fall back
   13303             :     // on an explicit division operation.
   13304           0 :     if (ownerByteOffset.divide(scale)) {
   13305           0 :         *ownerScaledOffset = ConvertLinearSum(alloc(), current, ownerByteOffset);
   13306             :     } else {
   13307           0 :         MDefinition* unscaledOffset = ConvertLinearSum(alloc(), current, ownerByteOffset);
   13308           0 :         *ownerScaledOffset = MDiv::New(alloc(), unscaledOffset, constantInt(scale),
   13309             :                                        MIRType::Int32, /* unsigned = */ false);
   13310           0 :         current->add((*ownerScaledOffset)->toInstruction());
   13311             :     }
   13312           0 :     return Ok();
   13313             : }
   13314             : 
   13315             : // Looks up the offset/type-repr-set of the field `id`, given the type
   13316             : // set `objTypes` of the field owner. If a field is found, returns true
   13317             : // and sets *fieldOffset, *fieldPrediction, and *fieldIndex. Returns false
   13318             : // otherwise. Infallible.
   13319             : bool
   13320          73 : IonBuilder::typedObjectHasField(MDefinition* typedObj,
   13321             :                                 PropertyName* name,
   13322             :                                 size_t* fieldOffset,
   13323             :                                 TypedObjectPrediction* fieldPrediction,
   13324             :                                 size_t* fieldIndex)
   13325             : {
   13326          73 :     TypedObjectPrediction objPrediction = typedObjectPrediction(typedObj);
   13327          73 :     if (objPrediction.isUseless()) {
   13328          73 :         trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject);
   13329          73 :         return false;
   13330             :     }
   13331             : 
   13332             :     // Must be accessing a struct.
   13333           0 :     if (objPrediction.kind() != type::Struct) {
   13334           0 :         trackOptimizationOutcome(TrackedOutcome::NotStruct);
   13335           0 :         return false;
   13336             :     }
   13337             : 
   13338             :     // Determine the type/offset of the field `name`, if any.
   13339           0 :     if (!objPrediction.hasFieldNamed(NameToId(name), fieldOffset,
   13340             :                                      fieldPrediction, fieldIndex))
   13341             :     {
   13342           0 :         trackOptimizationOutcome(TrackedOutcome::StructNoField);
   13343           0 :         return false;
   13344             :     }
   13345             : 
   13346           0 :     return true;
   13347             : }
   13348             : 
   13349             : MDefinition*
   13350           0 : IonBuilder::typeObjectForElementFromArrayStructType(MDefinition* typeObj)
   13351             : {
   13352           0 :     MInstruction* elemType = MLoadFixedSlot::New(alloc(), typeObj, JS_DESCR_SLOT_ARRAY_ELEM_TYPE);
   13353           0 :     current->add(elemType);
   13354             : 
   13355           0 :     MInstruction* unboxElemType = MUnbox::New(alloc(), elemType, MIRType::Object, MUnbox::Infallible);
   13356           0 :     current->add(unboxElemType);
   13357             : 
   13358           0 :     return unboxElemType;
   13359             : }
   13360             : 
   13361             : MDefinition*
   13362           0 : IonBuilder::typeObjectForFieldFromStructType(MDefinition* typeObj,
   13363             :                                              size_t fieldIndex)
   13364             : {
   13365             :     // Load list of field type objects.
   13366             : 
   13367           0 :     MInstruction* fieldTypes = MLoadFixedSlot::New(alloc(), typeObj, JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
   13368           0 :     current->add(fieldTypes);
   13369             : 
   13370           0 :     MInstruction* unboxFieldTypes = MUnbox::New(alloc(), fieldTypes, MIRType::Object, MUnbox::Infallible);
   13371           0 :     current->add(unboxFieldTypes);
   13372             : 
   13373             :     // Index into list with index of field.
   13374             : 
   13375           0 :     MInstruction* fieldTypesElements = MElements::New(alloc(), unboxFieldTypes);
   13376           0 :     current->add(fieldTypesElements);
   13377             : 
   13378           0 :     MConstant* fieldIndexDef = constantInt(fieldIndex);
   13379             : 
   13380           0 :     MInstruction* fieldType = MLoadElement::New(alloc(), fieldTypesElements, fieldIndexDef, false, false);
   13381           0 :     current->add(fieldType);
   13382             : 
   13383           0 :     MInstruction* unboxFieldType = MUnbox::New(alloc(), fieldType, MIRType::Object, MUnbox::Infallible);
   13384           0 :     current->add(unboxFieldType);
   13385             : 
   13386           0 :     return unboxFieldType;
   13387             : }
   13388             : 
   13389             : AbortReasonOr<Ok>
   13390           0 : IonBuilder::storeScalarTypedObjectValue(MDefinition* typedObj,
   13391             :                                         const LinearSum& byteOffset,
   13392             :                                         ScalarTypeDescr::Type type,
   13393             :                                         MDefinition* value)
   13394             : {
   13395             :     // Find location within the owner object.
   13396             :     MDefinition* elements;
   13397             :     MDefinition* scaledOffset;
   13398             :     int32_t adjustment;
   13399           0 :     uint32_t alignment = ScalarTypeDescr::alignment(type);
   13400           0 :     MOZ_TRY(loadTypedObjectElements(typedObj, byteOffset, alignment,
   13401             :                                     &elements, &scaledOffset, &adjustment));
   13402             : 
   13403             :     // Clamp value to [0, 255] when type is Uint8Clamped
   13404           0 :     MDefinition* toWrite = value;
   13405           0 :     if (type == Scalar::Uint8Clamped) {
   13406           0 :         toWrite = MClampToUint8::New(alloc(), value);
   13407           0 :         current->add(toWrite->toInstruction());
   13408             :     }
   13409             : 
   13410             :     MStoreUnboxedScalar* store =
   13411           0 :         MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, toWrite,
   13412             :                                  type, MStoreUnboxedScalar::TruncateInput,
   13413           0 :                                  DoesNotRequireMemoryBarrier, adjustment);
   13414           0 :     current->add(store);
   13415             : 
   13416           0 :     return Ok();
   13417             : }
   13418             : 
   13419             : AbortReasonOr<bool>
   13420           0 : IonBuilder::storeReferenceTypedObjectValue(MDefinition* typedObj,
   13421             :                                            const LinearSum& byteOffset,
   13422             :                                            ReferenceTypeDescr::Type type,
   13423             :                                            MDefinition* value,
   13424             :                                            PropertyName* name)
   13425             : {
   13426             :     // Make sure we aren't adding new type information for writes of object and value
   13427             :     // references.
   13428           0 :     if (type != ReferenceTypeDescr::TYPE_STRING) {
   13429           0 :         MOZ_ASSERT(type == ReferenceTypeDescr::TYPE_ANY ||
   13430             :                    type == ReferenceTypeDescr::TYPE_OBJECT);
   13431             :         MIRType implicitType =
   13432           0 :             (type == ReferenceTypeDescr::TYPE_ANY) ? MIRType::Undefined : MIRType::Null;
   13433             : 
   13434           0 :         if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &typedObj, name, &value,
   13435             :                                           /* canModify = */ true, implicitType))
   13436             :         {
   13437           0 :             trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
   13438           0 :             return false;
   13439             :         }
   13440             :     }
   13441             : 
   13442             :     // Find location within the owner object.
   13443             :     MDefinition* elements;
   13444             :     MDefinition* scaledOffset;
   13445             :     int32_t adjustment;
   13446           0 :     uint32_t alignment = ReferenceTypeDescr::alignment(type);
   13447           0 :     MOZ_TRY(loadTypedObjectElements(typedObj, byteOffset, alignment,
   13448             :                                     &elements, &scaledOffset, &adjustment));
   13449             : 
   13450           0 :     MInstruction* store = nullptr;  // initialize to silence GCC warning
   13451           0 :     switch (type) {
   13452             :       case ReferenceTypeDescr::TYPE_ANY:
   13453           0 :         if (NeedsPostBarrier(value))
   13454           0 :             current->add(MPostWriteBarrier::New(alloc(), typedObj, value));
   13455           0 :         store = MStoreElement::New(alloc(), elements, scaledOffset, value, false, adjustment);
   13456           0 :         store->toStoreElement()->setNeedsBarrier();
   13457           0 :         break;
   13458             :       case ReferenceTypeDescr::TYPE_OBJECT:
   13459             :         // Note: We cannot necessarily tell at this point whether a post
   13460             :         // barrier is needed, because the type policy may insert ToObjectOrNull
   13461             :         // instructions later, and those may require a post barrier. Therefore,
   13462             :         // defer the insertion of post barriers to the type policy.
   13463           0 :         store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, typedObj, adjustment);
   13464           0 :         break;
   13465             :       case ReferenceTypeDescr::TYPE_STRING:
   13466             :         // Strings are not nursery allocated, so these writes do not need post
   13467             :         // barriers.
   13468           0 :         store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, adjustment);
   13469           0 :         break;
   13470             :     }
   13471             : 
   13472           0 :     current->add(store);
   13473           0 :     return true;
   13474             : }
   13475             : 
   13476             : JSObject*
   13477         642 : IonBuilder::checkNurseryObject(JSObject* obj)
   13478             : {
   13479             :     // If we try to use any nursery pointers during compilation, make sure that
   13480             :     // the active thread will cancel this compilation before performing a minor
   13481             :     // GC. All constants used during compilation should either go through this
   13482             :     // function or should come from a type set (which has a similar barrier).
   13483         642 :     if (obj && IsInsideNursery(obj)) {
   13484           0 :         compartment->zone()->setMinorGCShouldCancelIonCompilations();
   13485           0 :         IonBuilder* builder = this;
   13486           0 :         while (builder) {
   13487           0 :             builder->setNotSafeForMinorGC();
   13488           0 :             builder = builder->callerBuilder_;
   13489             :         }
   13490             :     }
   13491             : 
   13492         642 :     return obj;
   13493             : }
   13494             : 
   13495             : MConstant*
   13496        4065 : IonBuilder::constant(const Value& v)
   13497             : {
   13498        4065 :     MOZ_ASSERT(!v.isString() || v.toString()->isAtom(),
   13499             :                "Handle non-atomized strings outside IonBuilder.");
   13500             : 
   13501        4065 :     if (v.isObject())
   13502         581 :         checkNurseryObject(&v.toObject());
   13503             : 
   13504        4065 :     MConstant* c = MConstant::New(alloc(), v, constraints());
   13505        4065 :     current->add(c);
   13506        4065 :     return c;
   13507             : }
   13508             : 
   13509             : MConstant*
   13510           0 : IonBuilder::constantInt(int32_t i)
   13511             : {
   13512           0 :     return constant(Int32Value(i));
   13513             : }
   13514             : 
   13515             : MInstruction*
   13516           1 : IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements, JSValueType unboxedType)
   13517             : {
   13518             :     MInstruction* res;
   13519           1 :     if (unboxedType != JSVAL_TYPE_MAGIC)
   13520           0 :         res = MUnboxedArrayInitializedLength::New(alloc(), obj);
   13521             :     else
   13522           1 :         res = MInitializedLength::New(alloc(), elements);
   13523           1 :     current->add(res);
   13524           1 :     return res;
   13525             : }
   13526             : 
   13527             : MInstruction*
   13528           0 : IonBuilder::setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count)
   13529             : {
   13530           0 :     MOZ_ASSERT(count);
   13531             : 
   13532             :     MInstruction* res;
   13533           0 :     if (unboxedType != JSVAL_TYPE_MAGIC) {
   13534           0 :         res = MSetUnboxedArrayInitializedLength::New(alloc(), obj, constant(Int32Value(count)));
   13535             :     } else {
   13536             :         // MSetInitializedLength takes the index of the last element, rather
   13537             :         // than the count itself.
   13538           0 :         MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false);
   13539           0 :         current->add(elements);
   13540           0 :         res = MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1)));
   13541             :     }
   13542           0 :     current->add(res);
   13543           0 :     return res;
   13544             : }
   13545             : 
   13546             : MDefinition*
   13547          22 : IonBuilder::getCallee()
   13548             : {
   13549          22 :     if (inliningDepth_ == 0) {
   13550          22 :         MInstruction* callee = MCallee::New(alloc());
   13551          22 :         current->add(callee);
   13552          22 :         return callee;
   13553             :     }
   13554             : 
   13555           0 :     return inlineCallInfo_->fun();
   13556             : }
   13557             : 
   13558             : AbortReasonOr<MDefinition*>
   13559           2 : IonBuilder::addLexicalCheck(MDefinition* input)
   13560             : {
   13561           2 :     MOZ_ASSERT(JSOp(*pc) == JSOP_CHECKLEXICAL ||
   13562             :                JSOp(*pc) == JSOP_CHECKALIASEDLEXICAL ||
   13563             :                JSOp(*pc) == JSOP_GETIMPORT);
   13564             : 
   13565             :     MInstruction* lexicalCheck;
   13566             : 
   13567             :     // If we're guaranteed to not be JS_UNINITIALIZED_LEXICAL, no need to check.
   13568           2 :     if (input->type() == MIRType::MagicUninitializedLexical) {
   13569             :         // Mark the input as implicitly used so the JS_UNINITIALIZED_LEXICAL
   13570             :         // magic value will be preserved on bailout.
   13571           0 :         input->setImplicitlyUsedUnchecked();
   13572           0 :         lexicalCheck = MThrowRuntimeLexicalError::New(alloc(), JSMSG_UNINITIALIZED_LEXICAL);
   13573           0 :         current->add(lexicalCheck);
   13574           0 :         MOZ_TRY(resumeAfter(lexicalCheck));
   13575           0 :         return constant(UndefinedValue());
   13576             :     }
   13577             : 
   13578           2 :     if (input->type() == MIRType::Value) {
   13579           2 :         lexicalCheck = MLexicalCheck::New(alloc(), input);
   13580           2 :         current->add(lexicalCheck);
   13581           2 :         if (failedLexicalCheck_)
   13582           0 :             lexicalCheck->setNotMovableUnchecked();
   13583           2 :         return lexicalCheck;
   13584             :     }
   13585             : 
   13586           0 :     return input;
   13587             : }
   13588             : 
   13589             : MDefinition*
   13590           0 : IonBuilder::convertToBoolean(MDefinition* input)
   13591             : {
   13592             :     // Convert to bool with the '!!' idiom
   13593           0 :     MNot* resultInverted = MNot::New(alloc(), input, constraints());
   13594           0 :     current->add(resultInverted);
   13595           0 :     MNot* result = MNot::New(alloc(), resultInverted, constraints());
   13596           0 :     current->add(result);
   13597             : 
   13598           0 :     return result;
   13599             : }
   13600             : 
   13601             : void
   13602           2 : IonBuilder::trace(JSTracer* trc)
   13603             : {
   13604           2 :     if (!compartment->runtime()->runtimeMatches(trc->runtime()))
   13605           0 :         return;
   13606             : 
   13607           2 :     MOZ_ASSERT(rootList_);
   13608           2 :     rootList_->trace(trc);
   13609             : }

Generated by: LCOV version 1.13