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 = ∅
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 : }
|