Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "jit/IonBuilder.h"
8 :
9 : #include "mozilla/DebugOnly.h"
10 : #include "mozilla/ScopeExit.h"
11 :
12 : #include "builtin/Eval.h"
13 : #include "builtin/TypedObject.h"
14 : #include "frontend/SourceNotes.h"
15 : #include "jit/BaselineFrame.h"
16 : #include "jit/BaselineInspector.h"
17 : #include "jit/Ion.h"
18 : #include "jit/IonControlFlow.h"
19 : #include "jit/IonOptimizationLevels.h"
20 : #include "jit/JitSpewer.h"
21 : #include "jit/Lowering.h"
22 : #include "jit/MIRGraph.h"
23 : #include "vm/ArgumentsObject.h"
24 : #include "vm/Opcodes.h"
25 : #include "vm/RegExpStatics.h"
26 : #include "vm/TraceLogging.h"
27 :
28 : #include "gc/Nursery-inl.h"
29 : #include "jit/CompileInfo-inl.h"
30 : #include "jit/shared/Lowering-shared-inl.h"
31 : #include "vm/BytecodeUtil-inl.h"
32 : #include "vm/EnvironmentObject-inl.h"
33 : #include "vm/JSScript-inl.h"
34 : #include "vm/NativeObject-inl.h"
35 : #include "vm/ObjectGroup-inl.h"
36 : #include "vm/UnboxedObject-inl.h"
37 :
38 : using namespace js;
39 : using namespace js::jit;
40 :
41 : using mozilla::AssertedCast;
42 : using mozilla::DebugOnly;
43 : using mozilla::Maybe;
44 : using mozilla::Nothing;
45 :
46 : using JS::TrackedStrategy;
47 : using JS::TrackedOutcome;
48 : using JS::TrackedTypeSite;
49 :
50 : class jit::BaselineFrameInspector
51 : {
52 : public:
53 : TypeSet::Type thisType;
54 : JSObject* singletonEnvChain;
55 :
56 : Vector<TypeSet::Type, 4, JitAllocPolicy> argTypes;
57 : Vector<TypeSet::Type, 4, JitAllocPolicy> varTypes;
58 :
59 : explicit BaselineFrameInspector(TempAllocator* temp)
60 0 : : thisType(TypeSet::UndefinedType()),
61 : singletonEnvChain(nullptr),
62 : argTypes(*temp),
63 0 : varTypes(*temp)
64 : {}
65 : };
66 :
67 : BaselineFrameInspector*
68 0 : jit::NewBaselineFrameInspector(TempAllocator* temp, BaselineFrame* frame)
69 : {
70 0 : MOZ_ASSERT(frame);
71 :
72 0 : BaselineFrameInspector* inspector = temp->lifoAlloc()->new_<BaselineFrameInspector>(temp);
73 0 : if (!inspector)
74 : return nullptr;
75 :
76 : // Note: copying the actual values into a temporary structure for use
77 : // during compilation could capture nursery pointers, so the values' types
78 : // are recorded instead.
79 :
80 0 : if (frame->isFunctionFrame())
81 0 : inspector->thisType = TypeSet::GetMaybeUntrackedValueType(frame->thisArgument());
82 :
83 0 : if (frame->environmentChain()->isSingleton())
84 0 : inspector->singletonEnvChain = frame->environmentChain();
85 :
86 0 : JSScript* script = frame->script();
87 :
88 0 : if (script->functionNonDelazifying()) {
89 0 : if (!inspector->argTypes.reserve(frame->numFormalArgs()))
90 : return nullptr;
91 0 : for (size_t i = 0; i < frame->numFormalArgs(); i++) {
92 0 : if (script->formalIsAliased(i)) {
93 0 : inspector->argTypes.infallibleAppend(TypeSet::UndefinedType());
94 0 : } else if (!script->argsObjAliasesFormals()) {
95 : TypeSet::Type type =
96 0 : TypeSet::GetMaybeUntrackedValueType(frame->unaliasedFormal(i));
97 0 : inspector->argTypes.infallibleAppend(type);
98 0 : } else if (frame->hasArgsObj()) {
99 : TypeSet::Type type =
100 0 : TypeSet::GetMaybeUntrackedValueType(frame->argsObj().arg(i));
101 0 : inspector->argTypes.infallibleAppend(type);
102 : } else {
103 0 : inspector->argTypes.infallibleAppend(TypeSet::UndefinedType());
104 : }
105 : }
106 : }
107 :
108 0 : if (!inspector->varTypes.reserve(frame->numValueSlots()))
109 : return nullptr;
110 0 : for (size_t i = 0; i < frame->numValueSlots(); i++) {
111 0 : TypeSet::Type type = TypeSet::GetMaybeUntrackedValueType(*frame->valueSlot(i));
112 0 : inspector->varTypes.infallibleAppend(type);
113 : }
114 :
115 : return inspector;
116 : }
117 :
118 0 : IonBuilder::IonBuilder(JSContext* analysisContext, CompileRealm* realm,
119 : const JitCompileOptions& options, TempAllocator* temp,
120 : MIRGraph* graph, CompilerConstraintList* constraints,
121 : BaselineInspector* inspector, CompileInfo* info,
122 : const OptimizationInfo* optimizationInfo,
123 : BaselineFrameInspector* baselineFrame, size_t inliningDepth,
124 0 : uint32_t loopDepth)
125 : : MIRGenerator(realm, options, temp, graph, info, optimizationInfo),
126 : backgroundCodegen_(nullptr),
127 : actionableAbortScript_(nullptr),
128 : actionableAbortPc_(nullptr),
129 : actionableAbortMessage_(nullptr),
130 : rootList_(nullptr),
131 : analysisContext(analysisContext),
132 : baselineFrame_(baselineFrame),
133 : constraints_(constraints),
134 : thisTypes(nullptr),
135 : argTypes(nullptr),
136 : typeArray(nullptr),
137 : typeArrayHint(0),
138 : bytecodeTypeMap(nullptr),
139 : loopDepth_(loopDepth),
140 : blockWorklist(*temp),
141 : cfgCurrent(nullptr),
142 : cfg(nullptr),
143 : trackedOptimizationSites_(*temp),
144 : lexicalCheck_(nullptr),
145 : callerResumePoint_(nullptr),
146 : callerBuilder_(nullptr),
147 : iterators_(*temp),
148 : loopHeaders_(*temp),
149 : loopHeaderStack_(*temp),
150 : #ifdef DEBUG
151 : cfgLoopHeaderStack_(*temp),
152 : #endif
153 : inspector(inspector),
154 : inliningDepth_(inliningDepth),
155 : inlinedBytecodeLength_(0),
156 : numLoopRestarts_(0),
157 0 : failedBoundsCheck_(info->script()->failedBoundsCheck()),
158 0 : failedShapeGuard_(info->script()->failedShapeGuard()),
159 0 : failedLexicalCheck_(info->script()->failedLexicalCheck()),
160 : #ifdef DEBUG
161 : hasLazyArguments_(false),
162 : #endif
163 : inlineCallInfo_(nullptr),
164 0 : maybeFallbackFunctionGetter_(nullptr)
165 : {
166 0 : script_ = info->script();
167 0 : scriptHasIonScript_ = script_->hasIonScript();
168 0 : pc = info->startPC();
169 :
170 0 : MOZ_ASSERT(script()->hasBaselineScript() == (info->analysisMode() != Analysis_ArgumentsUsage));
171 0 : MOZ_ASSERT(!!analysisContext == (info->analysisMode() == Analysis_DefiniteProperties));
172 0 : MOZ_ASSERT(script_->nTypeSets() < UINT16_MAX);
173 :
174 0 : if (!info->isAnalysis())
175 0 : script()->baselineScript()->setIonCompiledOrInlined();
176 0 : }
177 :
178 : void
179 0 : IonBuilder::clearForBackEnd()
180 : {
181 0 : MOZ_ASSERT(!analysisContext);
182 0 : baselineFrame_ = nullptr;
183 :
184 : // The caches below allocate data from the malloc heap. Release this before
185 : // later phases of compilation to avoid leaks, as the top level IonBuilder
186 : // is not explicitly destroyed. Note that builders for inner scripts are
187 : // constructed on the stack and will release this memory on destruction.
188 0 : envCoordinateNameCache.purge();
189 0 : }
190 :
191 : mozilla::GenericErrorResult<AbortReason>
192 0 : IonBuilder::abort(AbortReason r)
193 : {
194 0 : auto res = this->MIRGenerator::abort(r);
195 : # ifdef DEBUG
196 0 : JitSpew(JitSpew_IonAbort, "aborted @ %s:%d", script()->filename(), PCToLineNumber(script(), pc));
197 : # else
198 : JitSpew(JitSpew_IonAbort, "aborted @ %s", script()->filename());
199 : # endif
200 0 : return res;
201 : }
202 :
203 : mozilla::GenericErrorResult<AbortReason>
204 0 : IonBuilder::abort(AbortReason r, const char* message, ...)
205 : {
206 : // Don't call PCToLineNumber in release builds.
207 : va_list ap;
208 0 : va_start(ap, message);
209 0 : auto res = this->MIRGenerator::abortFmt(r, message, ap);
210 0 : va_end(ap);
211 : # ifdef DEBUG
212 0 : JitSpew(JitSpew_IonAbort, "aborted @ %s:%d", script()->filename(), PCToLineNumber(script(), pc));
213 : # else
214 : JitSpew(JitSpew_IonAbort, "aborted @ %s", script()->filename());
215 : # endif
216 0 : trackActionableAbort(message);
217 0 : return res;
218 : }
219 :
220 : IonBuilder*
221 0 : IonBuilder::outermostBuilder()
222 : {
223 0 : IonBuilder* builder = this;
224 0 : while (builder->callerBuilder_)
225 : builder = builder->callerBuilder_;
226 0 : return builder;
227 : }
228 :
229 : void
230 0 : IonBuilder::trackActionableAbort(const char* message)
231 : {
232 0 : if (!isOptimizationTrackingEnabled())
233 : return;
234 :
235 0 : IonBuilder* topBuilder = outermostBuilder();
236 0 : if (topBuilder->hadActionableAbort())
237 : return;
238 :
239 0 : topBuilder->actionableAbortScript_ = script();
240 0 : topBuilder->actionableAbortPc_ = pc;
241 0 : topBuilder->actionableAbortMessage_ = message;
242 : }
243 :
244 : void
245 0 : IonBuilder::spew(const char* message)
246 : {
247 : // Don't call PCToLineNumber in release builds.
248 : #ifdef DEBUG
249 0 : JitSpew(JitSpew_IonMIR, "%s @ %s:%d", message, script()->filename(), PCToLineNumber(script(), pc));
250 : #endif
251 0 : }
252 :
253 : JSFunction*
254 0 : IonBuilder::getSingleCallTarget(TemporaryTypeSet* calleeTypes)
255 : {
256 0 : if (!calleeTypes)
257 : return nullptr;
258 :
259 0 : TemporaryTypeSet::ObjectKey* key = calleeTypes->maybeSingleObject();
260 0 : if (!key || key->clasp() != &JSFunction::class_)
261 : return nullptr;
262 :
263 : if (key->isSingleton())
264 0 : return &key->singleton()->as<JSFunction>();
265 :
266 0 : if (JSFunction* fun = key->group()->maybeInterpretedFunction())
267 : return fun;
268 :
269 0 : return nullptr;
270 : }
271 :
272 : AbortReasonOr<Ok>
273 0 : IonBuilder::getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
274 : InliningTargets& targets, uint32_t maxTargets)
275 : {
276 0 : MOZ_ASSERT(targets.empty());
277 :
278 0 : if (!calleeTypes)
279 0 : return Ok();
280 :
281 0 : if (calleeTypes->baseFlags() != 0)
282 0 : return Ok();
283 :
284 0 : unsigned objCount = calleeTypes->getObjectCount();
285 :
286 0 : if (objCount == 0 || objCount > maxTargets)
287 0 : return Ok();
288 :
289 0 : if (!targets.reserve(objCount))
290 0 : return abort(AbortReason::Alloc);
291 0 : for (unsigned i = 0; i < objCount; i++) {
292 0 : JSObject* obj = calleeTypes->getSingleton(i);
293 0 : ObjectGroup* group = nullptr;
294 0 : if (obj) {
295 0 : MOZ_ASSERT(obj->isSingleton());
296 : } else {
297 0 : group = calleeTypes->getGroup(i);
298 0 : if (!group)
299 : continue;
300 :
301 0 : obj = group->maybeInterpretedFunction();
302 0 : if (!obj) {
303 0 : targets.clear();
304 0 : return Ok();
305 : }
306 :
307 0 : MOZ_ASSERT(!obj->isSingleton());
308 : }
309 :
310 : // Don't optimize if the callee is not callable or constructable per
311 : // the manner it is being invoked, so that CallKnown does not have to
312 : // handle these cases (they will always throw).
313 0 : if (constructing ? !obj->isConstructor() : !obj->isCallable()) {
314 0 : targets.clear();
315 0 : return Ok();
316 : }
317 :
318 0 : targets.infallibleAppend(InliningTarget(obj, group));
319 : }
320 :
321 0 : return Ok();
322 : }
323 :
324 : IonBuilder::InliningDecision
325 0 : IonBuilder::DontInline(JSScript* targetScript, const char* reason)
326 : {
327 0 : if (targetScript) {
328 0 : JitSpew(JitSpew_Inlining, "Cannot inline %s:%u: %s",
329 0 : targetScript->filename(), targetScript->lineno(), reason);
330 : } else {
331 0 : JitSpew(JitSpew_Inlining, "Cannot inline: %s", reason);
332 : }
333 :
334 0 : return InliningDecision_DontInline;
335 : }
336 :
337 : /*
338 : * |hasCommonInliningPath| determines whether the current inlining path has been
339 : * seen before based on the sequence of scripts in the chain of |IonBuilder|s.
340 : *
341 : * An inlining path for a function |f| is the sequence of functions whose
342 : * inlinings precede |f| up to any previous occurrences of |f|.
343 : * So, if we have the chain of inlinings
344 : *
345 : * f1 -> f2 -> f -> f3 -> f4 -> f5 -> f
346 : * -------- --------------
347 : *
348 : * the inlining paths for |f| are [f2, f1] and [f5, f4, f3].
349 : * When attempting to inline |f|, we find all existing inlining paths for |f|
350 : * and check whether they share a common prefix with the path created were |f|
351 : * inlined.
352 : *
353 : * For example, given mutually recursive functions |f| and |g|, a possible
354 : * inlining is
355 : *
356 : * +---- Inlining stopped here...
357 : * |
358 : * v
359 : * a -> f -> g -> f \ -> g -> f -> g -> ...
360 : *
361 : * where the vertical bar denotes the termination of inlining.
362 : * Inlining is terminated because we have already observed the inlining path
363 : * [f] when inlining function |g|. Note that this will inline recursive
364 : * functions such as |fib| only one level, as |fib| has a zero length inlining
365 : * path which trivially prefixes all inlining paths.
366 : *
367 : */
368 : bool
369 0 : IonBuilder::hasCommonInliningPath(const JSScript* scriptToInline)
370 : {
371 : // Find all previous inlinings of the |scriptToInline| and check for common
372 : // inlining paths with the top of the inlining stack.
373 0 : for (IonBuilder* it = this->callerBuilder_; it; it = it->callerBuilder_) {
374 0 : if (it->script() != scriptToInline)
375 : continue;
376 :
377 : // This only needs to check the top of each stack for a match,
378 : // as a match of length one ensures a common prefix.
379 0 : IonBuilder* path = it->callerBuilder_;
380 0 : if (!path || this->script() == path->script())
381 : return true;
382 : }
383 :
384 : return false;
385 : }
386 :
387 : IonBuilder::InliningDecision
388 0 : IonBuilder::canInlineTarget(JSFunction* target, CallInfo& callInfo)
389 : {
390 0 : if (!optimizationInfo().inlineInterpreted()) {
391 : trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
392 : return InliningDecision_DontInline;
393 : }
394 :
395 0 : if (TraceLogTextIdEnabled(TraceLogger_InlinedScripts)) {
396 : return DontInline(nullptr, "Tracelogging of inlined scripts is enabled"
397 0 : "but Tracelogger cannot do that yet.");
398 : }
399 :
400 0 : if (!target->isInterpreted()) {
401 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNotInterpreted);
402 0 : return DontInline(nullptr, "Non-interpreted target");
403 : }
404 :
405 0 : if (info().analysisMode() != Analysis_DefiniteProperties) {
406 : // If |this| or an argument has an empty resultTypeSet, don't bother
407 : // inlining, as the call is currently unreachable due to incomplete type
408 : // information. This does not apply to the definite properties analysis,
409 : // in that case we want to inline anyway.
410 :
411 0 : if (callInfo.thisArg()->emptyResultTypeSet()) {
412 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineUnreachable);
413 0 : return DontInline(nullptr, "Empty TypeSet for |this|");
414 : }
415 :
416 0 : for (size_t i = 0; i < callInfo.argc(); i++) {
417 0 : if (callInfo.getArg(i)->emptyResultTypeSet()) {
418 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineUnreachable);
419 0 : return DontInline(nullptr, "Empty TypeSet for argument");
420 : }
421 : }
422 : }
423 :
424 : // Allow constructing lazy scripts when performing the definite properties
425 : // analysis, as baseline has not been used to warm the caller up yet.
426 0 : if (target->isInterpreted() && info().analysisMode() == Analysis_DefiniteProperties) {
427 0 : RootedFunction fun(analysisContext, target);
428 0 : RootedScript script(analysisContext, JSFunction::getOrCreateScript(analysisContext, fun));
429 0 : if (!script)
430 0 : return InliningDecision_Error;
431 :
432 0 : if (!script->hasBaselineScript() && script->canBaselineCompile()) {
433 0 : MethodStatus status = BaselineCompile(analysisContext, script);
434 0 : if (status == Method_Error)
435 : return InliningDecision_Error;
436 0 : if (status != Method_Compiled) {
437 : trackOptimizationOutcome(TrackedOutcome::CantInlineNoBaseline);
438 : return InliningDecision_DontInline;
439 : }
440 : }
441 : }
442 :
443 0 : if (!target->hasScript()) {
444 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineLazy);
445 0 : return DontInline(nullptr, "Lazy script");
446 : }
447 :
448 0 : JSScript* inlineScript = target->nonLazyScript();
449 0 : if (callInfo.constructing() && !target->isConstructor()) {
450 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNotConstructor);
451 0 : return DontInline(inlineScript, "Callee is not a constructor");
452 : }
453 :
454 0 : if (!callInfo.constructing() && target->isClassConstructor()) {
455 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineClassConstructor);
456 0 : return DontInline(inlineScript, "Not constructing class constructor");
457 : }
458 :
459 0 : if (!CanIonInlineScript(inlineScript)) {
460 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
461 0 : return DontInline(inlineScript, "Disabled Ion compilation");
462 : }
463 :
464 : // Don't inline functions which don't have baseline scripts.
465 0 : if (!inlineScript->hasBaselineScript()) {
466 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNoBaseline);
467 0 : return DontInline(inlineScript, "No baseline jitcode");
468 : }
469 :
470 0 : if (TooManyFormalArguments(target->nargs())) {
471 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineTooManyArgs);
472 0 : return DontInline(inlineScript, "Too many args");
473 : }
474 :
475 : // We check the number of actual arguments against the maximum number of
476 : // formal arguments as we do not want to encode all actual arguments in the
477 : // callerResumePoint.
478 0 : if (TooManyFormalArguments(callInfo.argc())) {
479 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineTooManyArgs);
480 0 : return DontInline(inlineScript, "Too many actual args");
481 : }
482 :
483 0 : if (hasCommonInliningPath(inlineScript)) {
484 0 : trackOptimizationOutcome(TrackedOutcome::HasCommonInliningPath);
485 0 : return DontInline(inlineScript, "Common inlining path");
486 : }
487 :
488 0 : if (inlineScript->uninlineable()) {
489 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
490 0 : return DontInline(inlineScript, "Uninlineable script");
491 : }
492 :
493 0 : if (inlineScript->needsArgsObj()) {
494 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNeedsArgsObj);
495 0 : return DontInline(inlineScript, "Script that needs an arguments object");
496 : }
497 :
498 0 : if (inlineScript->isDebuggee()) {
499 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineDebuggee);
500 0 : return DontInline(inlineScript, "Script is debuggee");
501 : }
502 :
503 : return InliningDecision_Inline;
504 : }
505 :
506 : AbortReasonOr<Ok>
507 0 : IonBuilder::analyzeNewLoopTypes(const CFGBlock* loopEntryBlock)
508 : {
509 0 : CFGLoopEntry* loopEntry = loopEntryBlock->stopIns()->toLoopEntry();
510 0 : CFGBlock* cfgBlock = loopEntry->successor();
511 0 : MBasicBlock* entry = blockWorklist[cfgBlock->id()];
512 0 : MOZ_ASSERT(!entry->isDead());
513 :
514 : // The phi inputs at the loop head only reflect types for variables that
515 : // were present at the start of the loop. If the variable changes to a new
516 : // type within the loop body, and that type is carried around to the loop
517 : // head, then we need to know about the new type up front.
518 : //
519 : // Since SSA information hasn't been constructed for the loop body yet, we
520 : // need a separate analysis to pick out the types that might flow around
521 : // the loop header. This is a best-effort analysis that may either over-
522 : // or under-approximate the set of such types.
523 : //
524 : // Over-approximating the types may lead to inefficient generated code, and
525 : // under-approximating the types will cause the loop body to be analyzed
526 : // multiple times as the correct types are deduced (see finishLoop).
527 :
528 : // If we restarted processing of an outer loop then get loop header types
529 : // directly from the last time we have previously processed this loop. This
530 : // both avoids repeated work from the bytecode traverse below, and will
531 : // also pick up types discovered while previously building the loop body.
532 : bool foundEntry = false;
533 0 : for (size_t i = 0; i < loopHeaders_.length(); i++) {
534 0 : if (loopHeaders_[i].pc == cfgBlock->startPc()) {
535 0 : MBasicBlock* oldEntry = loopHeaders_[i].header;
536 :
537 : // If this block has been discarded, its resume points will have
538 : // already discarded their operands.
539 0 : if (oldEntry->isDead()) {
540 0 : loopHeaders_[i].header = entry;
541 0 : foundEntry = true;
542 0 : break;
543 : }
544 :
545 0 : MResumePoint* oldEntryRp = oldEntry->entryResumePoint();
546 0 : size_t stackDepth = oldEntryRp->stackDepth();
547 0 : for (size_t slot = 0; slot < stackDepth; slot++) {
548 0 : MDefinition* oldDef = oldEntryRp->getOperand(slot);
549 0 : if (!oldDef->isPhi()) {
550 0 : MOZ_ASSERT(oldDef->block()->id() < oldEntry->id());
551 0 : MOZ_ASSERT(oldDef == entry->getSlot(slot));
552 : continue;
553 : }
554 0 : MPhi* oldPhi = oldDef->toPhi();
555 0 : MPhi* newPhi = entry->getSlot(slot)->toPhi();
556 0 : if (!newPhi->addBackedgeType(alloc(), oldPhi->type(), oldPhi->resultTypeSet()))
557 0 : return abort(AbortReason::Alloc);
558 : }
559 :
560 : // Update the most recent header for this loop encountered, in case
561 : // new types flow to the phis and the loop is processed at least
562 : // three times.
563 0 : loopHeaders_[i].header = entry;
564 0 : return Ok();
565 : }
566 : }
567 0 : if (!foundEntry) {
568 0 : if (!loopHeaders_.append(LoopHeader(cfgBlock->startPc(), entry)))
569 0 : return abort(AbortReason::Alloc);
570 : }
571 :
572 0 : if (loopEntry->isForIn()) {
573 : // The backedge will have MIteratorMore with MIRType::Value. This slot
574 : // is initialized to MIRType::Undefined before the loop. Add
575 : // MIRType::Value to avoid unnecessary loop restarts.
576 :
577 0 : MPhi* phi = entry->getSlot(entry->stackDepth() - 1)->toPhi();
578 0 : MOZ_ASSERT(phi->getOperand(0)->type() == MIRType::Undefined);
579 :
580 0 : if (!phi->addBackedgeType(alloc(), MIRType::Value, nullptr))
581 0 : return abort(AbortReason::Alloc);
582 : }
583 :
584 : // Get the start and end pc of this loop.
585 0 : jsbytecode* start = loopEntryBlock->stopPc();
586 0 : start += GetBytecodeLength(start);
587 0 : jsbytecode* end = loopEntry->loopStopPc();
588 :
589 : // Iterate the bytecode quickly to seed possible types in the loopheader.
590 0 : jsbytecode* last = nullptr;
591 0 : jsbytecode* earlier = nullptr;
592 0 : for (jsbytecode* pc = start; pc != end; earlier = last, last = pc, pc += GetBytecodeLength(pc)) {
593 : uint32_t slot;
594 0 : if (*pc == JSOP_SETLOCAL)
595 0 : slot = info().localSlot(GET_LOCALNO(pc));
596 0 : else if (*pc == JSOP_SETARG)
597 0 : slot = info().argSlotUnchecked(GET_ARGNO(pc));
598 : else
599 : continue;
600 0 : if (slot >= info().firstStackSlot())
601 : continue;
602 0 : if (!last)
603 : continue;
604 :
605 0 : MPhi* phi = entry->getSlot(slot)->toPhi();
606 :
607 0 : if (*last == JSOP_POS)
608 0 : last = earlier;
609 :
610 0 : if (CodeSpec[*last].format & JOF_TYPESET) {
611 0 : TemporaryTypeSet* typeSet = bytecodeTypes(last);
612 0 : if (!typeSet->empty()) {
613 0 : MIRType type = typeSet->getKnownMIRType();
614 0 : if (!phi->addBackedgeType(alloc(), type, typeSet))
615 0 : return abort(AbortReason::Alloc);
616 : }
617 0 : } else if (*last == JSOP_GETLOCAL || *last == JSOP_GETARG) {
618 : uint32_t slot = (*last == JSOP_GETLOCAL)
619 0 : ? info().localSlot(GET_LOCALNO(last))
620 0 : : info().argSlotUnchecked(GET_ARGNO(last));
621 0 : if (slot < info().firstStackSlot()) {
622 0 : MPhi* otherPhi = entry->getSlot(slot)->toPhi();
623 0 : if (otherPhi->hasBackedgeType()) {
624 0 : if (!phi->addBackedgeType(alloc(), otherPhi->type(), otherPhi->resultTypeSet()))
625 0 : return abort(AbortReason::Alloc);
626 : }
627 : }
628 : } else {
629 0 : MIRType type = MIRType::None;
630 0 : switch (*last) {
631 : case JSOP_VOID:
632 : case JSOP_UNDEFINED:
633 0 : type = MIRType::Undefined;
634 0 : break;
635 : case JSOP_GIMPLICITTHIS:
636 0 : if (!script()->hasNonSyntacticScope())
637 0 : type = MIRType::Undefined;
638 : break;
639 : case JSOP_NULL:
640 0 : type = MIRType::Null;
641 0 : break;
642 : case JSOP_ZERO:
643 : case JSOP_ONE:
644 : case JSOP_INT8:
645 : case JSOP_INT32:
646 : case JSOP_UINT16:
647 : case JSOP_UINT24:
648 : case JSOP_BITAND:
649 : case JSOP_BITOR:
650 : case JSOP_BITXOR:
651 : case JSOP_BITNOT:
652 : case JSOP_RSH:
653 : case JSOP_LSH:
654 : case JSOP_URSH:
655 0 : type = MIRType::Int32;
656 0 : break;
657 : case JSOP_FALSE:
658 : case JSOP_TRUE:
659 : case JSOP_EQ:
660 : case JSOP_NE:
661 : case JSOP_LT:
662 : case JSOP_LE:
663 : case JSOP_GT:
664 : case JSOP_GE:
665 : case JSOP_NOT:
666 : case JSOP_STRICTEQ:
667 : case JSOP_STRICTNE:
668 : case JSOP_IN:
669 : case JSOP_INSTANCEOF:
670 : case JSOP_HASOWN:
671 0 : type = MIRType::Boolean;
672 0 : break;
673 : case JSOP_DOUBLE:
674 0 : type = MIRType::Double;
675 0 : break;
676 : case JSOP_ITERNEXT:
677 : case JSOP_STRING:
678 : case JSOP_TOSTRING:
679 : case JSOP_TYPEOF:
680 : case JSOP_TYPEOFEXPR:
681 0 : type = MIRType::String;
682 0 : break;
683 : case JSOP_SYMBOL:
684 0 : type = MIRType::Symbol;
685 0 : break;
686 : case JSOP_ADD:
687 : case JSOP_SUB:
688 : case JSOP_MUL:
689 : case JSOP_DIV:
690 : case JSOP_MOD:
691 : case JSOP_NEG:
692 0 : type = inspector->expectedResultType(last);
693 0 : break;
694 : default:
695 : break;
696 : }
697 0 : if (type != MIRType::None) {
698 0 : if (!phi->addBackedgeType(alloc(), type, nullptr))
699 0 : return abort(AbortReason::Alloc);
700 : }
701 : }
702 : }
703 0 : return Ok();
704 : }
705 :
706 : AbortReasonOr<Ok>
707 0 : IonBuilder::init()
708 : {
709 : {
710 0 : LifoAlloc::AutoFallibleScope fallibleAllocator(alloc().lifoAlloc());
711 0 : if (!TypeScript::FreezeTypeSets(constraints(), script(), &thisTypes, &argTypes, &typeArray))
712 0 : return abort(AbortReason::Alloc);
713 : }
714 :
715 0 : if (!alloc().ensureBallast())
716 0 : return abort(AbortReason::Alloc);
717 :
718 0 : if (inlineCallInfo_) {
719 : // If we're inlining, the actual this/argument types are not necessarily
720 : // a subset of the script's observed types. |argTypes| is never accessed
721 : // for inlined scripts, so we just null it.
722 0 : thisTypes = inlineCallInfo_->thisArg()->resultTypeSet();
723 0 : argTypes = nullptr;
724 : }
725 :
726 : // The baseline script normally has the bytecode type map, but compute
727 : // it ourselves if we do not have a baseline script.
728 0 : if (script()->hasBaselineScript()) {
729 0 : bytecodeTypeMap = script()->baselineScript()->bytecodeTypeMap();
730 : } else {
731 0 : bytecodeTypeMap = alloc_->lifoAlloc()->newArrayUninitialized<uint32_t>(script()->nTypeSets());
732 0 : if (!bytecodeTypeMap)
733 0 : return abort(AbortReason::Alloc);
734 0 : FillBytecodeTypeMap(script(), bytecodeTypeMap);
735 : }
736 :
737 0 : return Ok();
738 : }
739 :
740 : AbortReasonOr<Ok>
741 0 : IonBuilder::build()
742 : {
743 0 : MOZ_TRY(init());
744 :
745 0 : if (script()->hasBaselineScript())
746 0 : script()->baselineScript()->resetMaxInliningDepth();
747 :
748 : MBasicBlock* entry;
749 0 : MOZ_TRY_VAR(entry, newBlock(info().firstStackSlot(), pc));
750 0 : MOZ_TRY(setCurrentAndSpecializePhis(entry));
751 :
752 : #ifdef JS_JITSPEW
753 0 : if (info().isAnalysis()) {
754 0 : JitSpew(JitSpew_IonScripts, "Analyzing script %s:%u (%p) %s",
755 0 : script()->filename(), script()->lineno(), (void*)script(),
756 0 : AnalysisModeString(info().analysisMode()));
757 : } else {
758 0 : JitSpew(JitSpew_IonScripts, "%sompiling script %s:%u (%p) (warmup-counter=%" PRIu32 ", level=%s)",
759 0 : (script()->hasIonScript() ? "Rec" : "C"),
760 0 : script()->filename(), script()->lineno(), (void*)script(),
761 0 : script()->getWarmUpCount(), OptimizationLevelString(optimizationInfo().level()));
762 : }
763 : #endif
764 :
765 0 : MOZ_TRY(initParameters());
766 0 : initLocals();
767 :
768 : // Initialize something for the env chain. We can bail out before the
769 : // start instruction, but the snapshot is encoded *at* the start
770 : // instruction, which means generating any code that could load into
771 : // registers is illegal.
772 0 : MInstruction* env = MConstant::New(alloc(), UndefinedValue());
773 0 : current->add(env);
774 0 : current->initSlot(info().environmentChainSlot(), env);
775 :
776 : // Initialize the return value.
777 0 : MInstruction* returnValue = MConstant::New(alloc(), UndefinedValue());
778 0 : current->add(returnValue);
779 0 : current->initSlot(info().returnValueSlot(), returnValue);
780 :
781 : // Initialize the arguments object slot to undefined if necessary.
782 0 : if (info().hasArguments()) {
783 0 : MInstruction* argsObj = MConstant::New(alloc(), UndefinedValue());
784 0 : current->add(argsObj);
785 0 : current->initSlot(info().argsObjSlot(), argsObj);
786 : }
787 :
788 : // Emit the start instruction, so we can begin real instructions.
789 0 : current->add(MStart::New(alloc()));
790 :
791 : // Guard against over-recursion. Do this before we start unboxing, since
792 : // this will create an OSI point that will read the incoming argument
793 : // values, which is nice to do before their last real use, to minimize
794 : // register/stack pressure.
795 0 : MCheckOverRecursed* check = MCheckOverRecursed::New(alloc());
796 0 : current->add(check);
797 0 : MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
798 0 : if (!entryRpCopy)
799 0 : return abort(AbortReason::Alloc);
800 0 : check->setResumePoint(entryRpCopy);
801 :
802 : // Parameters have been checked to correspond to the typeset, now we unbox
803 : // what we can in an infallible manner.
804 0 : MOZ_TRY(rewriteParameters());
805 :
806 : // Check for redeclaration errors for global scripts.
807 0 : if (!info().funMaybeLazy() && !info().module() &&
808 0 : script()->bodyScope()->is<GlobalScope>() &&
809 0 : script()->bodyScope()->as<GlobalScope>().hasBindings())
810 : {
811 0 : MGlobalNameConflictsCheck* redeclCheck = MGlobalNameConflictsCheck::New(alloc());
812 0 : current->add(redeclCheck);
813 0 : MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
814 0 : if (!entryRpCopy)
815 0 : return abort(AbortReason::Alloc);
816 0 : redeclCheck->setResumePoint(entryRpCopy);
817 : }
818 :
819 : // It's safe to start emitting actual IR, so now build the env chain.
820 0 : MOZ_TRY(initEnvironmentChain());
821 0 : if (info().needsArgsObj())
822 0 : initArgumentsObject();
823 :
824 : // The type analysis phase attempts to insert unbox operations near
825 : // definitions of values. It also attempts to replace uses in resume points
826 : // with the narrower, unboxed variants. However, we must prevent this
827 : // replacement from happening on values in the entry snapshot. Otherwise we
828 : // could get this:
829 : //
830 : // v0 = MParameter(0)
831 : // v1 = MParameter(1)
832 : // -- ResumePoint(v2, v3)
833 : // v2 = Unbox(v0, INT32)
834 : // v3 = Unbox(v1, INT32)
835 : //
836 : // So we attach the initial resume point to each parameter, which the type
837 : // analysis explicitly checks (this is the same mechanism used for
838 : // effectful operations).
839 0 : for (uint32_t i = 0; i < info().endArgSlot(); i++) {
840 0 : MInstruction* ins = current->getEntrySlot(i)->toInstruction();
841 0 : if (ins->type() != MIRType::Value)
842 : continue;
843 :
844 0 : MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
845 0 : if (!entryRpCopy)
846 0 : return abort(AbortReason::Alloc);
847 0 : ins->setResumePoint(entryRpCopy);
848 : }
849 :
850 : #ifdef DEBUG
851 : // lazyArguments should never be accessed in |argsObjAliasesFormals| scripts.
852 0 : if (info().hasArguments() && !info().argsObjAliasesFormals())
853 0 : hasLazyArguments_ = true;
854 : #endif
855 :
856 0 : insertRecompileCheck();
857 :
858 : auto clearLastPriorResumePoint = mozilla::MakeScopeExit([&] {
859 : // Discard unreferenced & pre-allocated resume points.
860 0 : replaceMaybeFallbackFunctionGetter(nullptr);
861 0 : });
862 :
863 0 : MOZ_TRY(traverseBytecode());
864 :
865 0 : if (script_->hasBaselineScript() &&
866 0 : inlinedBytecodeLength_ > script_->baselineScript()->inlinedBytecodeLength())
867 : {
868 0 : script_->baselineScript()->setInlinedBytecodeLength(inlinedBytecodeLength_);
869 : }
870 :
871 0 : MOZ_TRY(maybeAddOsrTypeBarriers());
872 0 : MOZ_TRY(processIterators());
873 :
874 0 : if (!info().isAnalysis() && !abortedPreliminaryGroups().empty())
875 0 : return abort(AbortReason::PreliminaryObjects);
876 :
877 0 : MOZ_ASSERT(loopDepth_ == 0);
878 0 : return Ok();
879 : }
880 :
881 : AbortReasonOr<Ok>
882 0 : IonBuilder::processIterators()
883 : {
884 : // Find and mark phis that must transitively hold an iterator live.
885 :
886 0 : Vector<MDefinition*, 8, SystemAllocPolicy> worklist;
887 :
888 0 : for (size_t i = 0; i < iterators_.length(); i++) {
889 0 : MDefinition* iter = iterators_[i];
890 0 : if (!iter->isInWorklist()) {
891 0 : if (!worklist.append(iter))
892 0 : return abort(AbortReason::Alloc);
893 0 : iter->setInWorklist();
894 : }
895 : }
896 :
897 0 : while (!worklist.empty()) {
898 0 : MDefinition* def = worklist.popCopy();
899 0 : def->setNotInWorklist();
900 :
901 0 : if (def->isPhi()) {
902 0 : MPhi* phi = def->toPhi();
903 : phi->setIterator();
904 0 : phi->setImplicitlyUsedUnchecked();
905 : }
906 :
907 0 : for (MUseDefIterator iter(def); iter; iter++) {
908 0 : MDefinition* use = iter.def();
909 0 : if (!use->isInWorklist() && (!use->isPhi() || !use->toPhi()->isIterator())) {
910 0 : if (!worklist.append(use))
911 0 : return abort(AbortReason::Alloc);
912 0 : use->setInWorklist();
913 : }
914 : }
915 : }
916 :
917 0 : return Ok();
918 : }
919 :
920 : AbortReasonOr<Ok>
921 0 : IonBuilder::buildInline(IonBuilder* callerBuilder, MResumePoint* callerResumePoint,
922 : CallInfo& callInfo)
923 : {
924 0 : inlineCallInfo_ = &callInfo;
925 :
926 0 : MOZ_TRY(init());
927 :
928 0 : JitSpew(JitSpew_IonScripts, "Inlining script %s:%u (%p)",
929 0 : script()->filename(), script()->lineno(), (void*)script());
930 :
931 0 : callerBuilder_ = callerBuilder;
932 0 : callerResumePoint_ = callerResumePoint;
933 :
934 0 : if (callerBuilder->failedBoundsCheck_)
935 0 : failedBoundsCheck_ = true;
936 :
937 0 : if (callerBuilder->failedShapeGuard_)
938 0 : failedShapeGuard_ = true;
939 :
940 0 : if (callerBuilder->failedLexicalCheck_)
941 0 : failedLexicalCheck_ = true;
942 :
943 0 : safeForMinorGC_ = callerBuilder->safeForMinorGC_;
944 :
945 : // Generate single entrance block.
946 : MBasicBlock* entry;
947 0 : MOZ_TRY_VAR(entry, newBlock(info().firstStackSlot(), pc));
948 0 : MOZ_TRY(setCurrentAndSpecializePhis(entry));
949 :
950 0 : current->setCallerResumePoint(callerResumePoint);
951 :
952 : // Connect the entrance block to the last block in the caller's graph.
953 0 : MBasicBlock* predecessor = callerBuilder->current;
954 0 : MOZ_ASSERT(predecessor == callerResumePoint->block());
955 :
956 0 : predecessor->end(MGoto::New(alloc(), current));
957 0 : if (!current->addPredecessorWithoutPhis(predecessor))
958 0 : return abort(AbortReason::Alloc);
959 :
960 : // Initialize env chain slot to Undefined. It's set later by
961 : // |initEnvironmentChain|.
962 0 : MInstruction* env = MConstant::New(alloc(), UndefinedValue());
963 0 : current->add(env);
964 0 : current->initSlot(info().environmentChainSlot(), env);
965 :
966 : // Initialize |return value| slot.
967 0 : MInstruction* returnValue = MConstant::New(alloc(), UndefinedValue());
968 0 : current->add(returnValue);
969 0 : current->initSlot(info().returnValueSlot(), returnValue);
970 :
971 : // Initialize |arguments| slot.
972 0 : if (info().hasArguments()) {
973 0 : MInstruction* argsObj = MConstant::New(alloc(), UndefinedValue());
974 0 : current->add(argsObj);
975 0 : current->initSlot(info().argsObjSlot(), argsObj);
976 : }
977 :
978 : // Initialize |this| slot.
979 0 : current->initSlot(info().thisSlot(), callInfo.thisArg());
980 :
981 0 : JitSpew(JitSpew_Inlining, "Initializing %u arg slots", info().nargs());
982 :
983 : // NB: Ion does not inline functions which |needsArgsObj|. So using argSlot()
984 : // instead of argSlotUnchecked() below is OK
985 0 : MOZ_ASSERT(!info().needsArgsObj());
986 :
987 : // Initialize actually set arguments.
988 0 : uint32_t existing_args = Min<uint32_t>(callInfo.argc(), info().nargs());
989 0 : for (size_t i = 0; i < existing_args; ++i) {
990 0 : MDefinition* arg = callInfo.getArg(i);
991 0 : current->initSlot(info().argSlot(i), arg);
992 : }
993 :
994 : // Pass Undefined for missing arguments
995 0 : for (size_t i = callInfo.argc(); i < info().nargs(); ++i) {
996 0 : MConstant* arg = MConstant::New(alloc(), UndefinedValue());
997 0 : current->add(arg);
998 0 : current->initSlot(info().argSlot(i), arg);
999 : }
1000 :
1001 0 : JitSpew(JitSpew_Inlining, "Initializing %u locals", info().nlocals());
1002 :
1003 0 : initLocals();
1004 :
1005 0 : JitSpew(JitSpew_Inlining, "Inline entry block MResumePoint %p, %u stack slots",
1006 0 : (void*) current->entryResumePoint(), current->entryResumePoint()->stackDepth());
1007 :
1008 : // +2 for the env chain and |this|, maybe another +1 for arguments object slot.
1009 0 : MOZ_ASSERT(current->entryResumePoint()->stackDepth() == info().totalSlots());
1010 :
1011 : #ifdef DEBUG
1012 0 : if (script_->argumentsHasVarBinding())
1013 0 : hasLazyArguments_ = true;
1014 : #endif
1015 :
1016 0 : insertRecompileCheck();
1017 :
1018 : // Initialize the env chain now that all resume points operands are
1019 : // initialized.
1020 0 : MOZ_TRY(initEnvironmentChain(callInfo.fun()));
1021 :
1022 : auto clearLastPriorResumePoint = mozilla::MakeScopeExit([&] {
1023 : // Discard unreferenced & pre-allocated resume points.
1024 0 : replaceMaybeFallbackFunctionGetter(nullptr);
1025 0 : });
1026 :
1027 0 : MOZ_TRY(traverseBytecode());
1028 :
1029 :
1030 0 : MOZ_ASSERT(iterators_.empty(), "Iterators should be added to outer builder");
1031 :
1032 0 : if (!info().isAnalysis() && !abortedPreliminaryGroups().empty())
1033 0 : return abort(AbortReason::PreliminaryObjects);
1034 :
1035 0 : return Ok();
1036 : }
1037 :
1038 : void
1039 0 : IonBuilder::rewriteParameter(uint32_t slotIdx, MDefinition* param)
1040 : {
1041 0 : MOZ_ASSERT(param->isParameter() || param->isGetArgumentsObjectArg());
1042 :
1043 0 : TemporaryTypeSet* types = param->resultTypeSet();
1044 0 : MDefinition* actual = ensureDefiniteType(param, types->getKnownMIRType());
1045 0 : if (actual == param)
1046 : return;
1047 :
1048 : // Careful! We leave the original MParameter in the entry resume point. The
1049 : // arguments still need to be checked unless proven otherwise at the call
1050 : // site, and these checks can bailout. We can end up:
1051 : // v0 = Parameter(0)
1052 : // v1 = Unbox(v0, INT32)
1053 : // -- ResumePoint(v0)
1054 : //
1055 : // As usual, it would be invalid for v1 to be captured in the initial
1056 : // resume point, rather than v0.
1057 0 : current->rewriteSlot(slotIdx, actual);
1058 : }
1059 :
1060 : // Apply Type Inference information to parameters early on, unboxing them if
1061 : // they have a definitive type. The actual guards will be emitted by the code
1062 : // generator, explicitly, as part of the function prologue.
1063 : AbortReasonOr<Ok>
1064 0 : IonBuilder::rewriteParameters()
1065 : {
1066 0 : MOZ_ASSERT(info().environmentChainSlot() == 0);
1067 :
1068 : // If this JSScript is not the code of a function, then skip the
1069 : // initialization of function parameters.
1070 0 : if (!info().funMaybeLazy())
1071 0 : return Ok();
1072 :
1073 0 : for (uint32_t i = info().startArgSlot(); i < info().endArgSlot(); i++) {
1074 0 : if (!alloc().ensureBallast())
1075 0 : return abort(AbortReason::Alloc);
1076 0 : MDefinition* param = current->getSlot(i);
1077 0 : rewriteParameter(i, param);
1078 : }
1079 :
1080 0 : return Ok();
1081 : }
1082 :
1083 : AbortReasonOr<Ok>
1084 0 : IonBuilder::initParameters()
1085 : {
1086 : // If this JSScript is not the code of a function, then skip the
1087 : // initialization of function parameters.
1088 0 : if (!info().funMaybeLazy())
1089 0 : return Ok();
1090 :
1091 : // If we are doing OSR on a frame which initially executed in the
1092 : // interpreter and didn't accumulate type information, try to use that OSR
1093 : // frame to determine possible initial types for 'this' and parameters.
1094 :
1095 0 : if (thisTypes->empty() && baselineFrame_) {
1096 0 : TypeSet::Type type = baselineFrame_->thisType;
1097 0 : if (type.isSingletonUnchecked())
1098 0 : checkNurseryObject(type.singleton());
1099 0 : thisTypes->addType(type, alloc_->lifoAlloc());
1100 : }
1101 :
1102 0 : MParameter* param = MParameter::New(alloc(), MParameter::THIS_SLOT, thisTypes);
1103 0 : current->add(param);
1104 0 : current->initSlot(info().thisSlot(), param);
1105 :
1106 0 : for (uint32_t i = 0; i < info().nargs(); i++) {
1107 0 : TemporaryTypeSet* types = &argTypes[i];
1108 0 : if (types->empty() && baselineFrame_ &&
1109 0 : !script_->baselineScript()->modifiesArguments())
1110 : {
1111 0 : TypeSet::Type type = baselineFrame_->argTypes[i];
1112 0 : if (type.isSingletonUnchecked())
1113 0 : checkNurseryObject(type.singleton());
1114 0 : types->addType(type, alloc_->lifoAlloc());
1115 : }
1116 :
1117 0 : param = MParameter::New(alloc().fallible(), i, types);
1118 0 : if (!param)
1119 0 : return abort(AbortReason::Alloc);
1120 0 : current->add(param);
1121 0 : current->initSlot(info().argSlotUnchecked(i), param);
1122 : }
1123 :
1124 0 : return Ok();
1125 : }
1126 :
1127 : void
1128 0 : IonBuilder::initLocals()
1129 : {
1130 : // Initialize all frame slots to undefined. Lexical bindings are temporal
1131 : // dead zoned in bytecode.
1132 :
1133 0 : if (info().nlocals() == 0)
1134 : return;
1135 :
1136 0 : MConstant* undef = MConstant::New(alloc(), UndefinedValue());
1137 0 : current->add(undef);
1138 :
1139 0 : for (uint32_t i = 0; i < info().nlocals(); i++)
1140 0 : current->initSlot(info().localSlot(i), undef);
1141 : }
1142 :
1143 : bool
1144 0 : IonBuilder::usesEnvironmentChain()
1145 : {
1146 : // We don't have a BaselineScript if we're running the arguments analysis,
1147 : // but it's fine to assume we always use the environment chain in this case.
1148 0 : if (info().analysisMode() == Analysis_ArgumentsUsage)
1149 : return true;
1150 0 : return script()->baselineScript()->usesEnvironmentChain();
1151 : }
1152 :
1153 : AbortReasonOr<Ok>
1154 0 : IonBuilder::initEnvironmentChain(MDefinition* callee)
1155 : {
1156 0 : MInstruction* env = nullptr;
1157 :
1158 : // If the script doesn't use the envchain, then it's already initialized
1159 : // from earlier. However, always make a env chain when |needsArgsObj| is true
1160 : // for the script, since arguments object construction requires the env chain
1161 : // to be passed in.
1162 0 : if (!info().needsArgsObj() && !usesEnvironmentChain())
1163 0 : return Ok();
1164 :
1165 : // The env chain is only tracked in scripts that have NAME opcodes which
1166 : // will try to access the env. For other scripts, the env instructions
1167 : // will be held live by resume points and code will still be generated for
1168 : // them, so just use a constant undefined value.
1169 :
1170 0 : if (JSFunction* fun = info().funMaybeLazy()) {
1171 0 : if (!callee) {
1172 0 : MCallee* calleeIns = MCallee::New(alloc());
1173 0 : current->add(calleeIns);
1174 0 : callee = calleeIns;
1175 : }
1176 0 : env = MFunctionEnvironment::New(alloc(), callee);
1177 0 : current->add(env);
1178 :
1179 : // This reproduce what is done in CallObject::createForFunction. Skip
1180 : // this for the arguments analysis, as the script might not have a
1181 : // baseline script with template objects yet.
1182 0 : if (fun->needsSomeEnvironmentObject() &&
1183 0 : info().analysisMode() != Analysis_ArgumentsUsage)
1184 : {
1185 0 : if (fun->needsNamedLambdaEnvironment())
1186 0 : env = createNamedLambdaObject(callee, env);
1187 :
1188 : // TODO: Parameter expression-induced extra var environment not
1189 : // yet handled.
1190 0 : if (fun->needsExtraBodyVarEnvironment())
1191 0 : return abort(AbortReason::Disable, "Extra var environment unsupported");
1192 :
1193 0 : if (fun->needsCallObject())
1194 0 : MOZ_TRY_VAR(env, createCallObject(callee, env));
1195 : }
1196 0 : } else if (ModuleObject* module = info().module()) {
1197 : // Modules use a pre-created env object.
1198 0 : env = constant(ObjectValue(module->initialEnvironment()));
1199 : } else {
1200 : // For global scripts without a non-syntactic global scope, the env
1201 : // chain is the global lexical env.
1202 0 : MOZ_ASSERT(!script()->isForEval());
1203 0 : MOZ_ASSERT(!script()->hasNonSyntacticScope());
1204 0 : env = constant(ObjectValue(script()->global().lexicalEnvironment()));
1205 : }
1206 :
1207 : // Update the environment slot from UndefinedValue only after initial
1208 : // environment is created so that bailout doesn't see a partial env.
1209 : // See: |InitFromBailout|
1210 0 : current->setEnvironmentChain(env);
1211 0 : return Ok();
1212 : }
1213 :
1214 : void
1215 0 : IonBuilder::initArgumentsObject()
1216 : {
1217 0 : JitSpew(JitSpew_IonMIR, "%s:%u - Emitting code to initialize arguments object! block=%p",
1218 0 : script()->filename(), script()->lineno(), current);
1219 0 : MOZ_ASSERT(info().needsArgsObj());
1220 :
1221 0 : bool mapped = script()->hasMappedArgsObj();
1222 0 : ArgumentsObject* templateObj = script()->realm()->maybeArgumentsTemplateObject(mapped);
1223 :
1224 : MCreateArgumentsObject* argsObj =
1225 0 : MCreateArgumentsObject::New(alloc(), current->environmentChain(), templateObj);
1226 0 : current->add(argsObj);
1227 0 : current->setArgumentsObject(argsObj);
1228 0 : }
1229 :
1230 : AbortReasonOr<Ok>
1231 0 : IonBuilder::addOsrValueTypeBarrier(uint32_t slot, MInstruction** def_,
1232 : MIRType type, TemporaryTypeSet* typeSet)
1233 : {
1234 0 : MInstruction*& def = *def_;
1235 0 : MBasicBlock* osrBlock = def->block();
1236 :
1237 : // Clear bogus type information added in newOsrPreheader().
1238 0 : def->setResultType(MIRType::Value);
1239 0 : def->setResultTypeSet(nullptr);
1240 :
1241 0 : if (typeSet && !typeSet->unknown()) {
1242 0 : MInstruction* barrier = MTypeBarrier::New(alloc(), def, typeSet);
1243 0 : osrBlock->insertBefore(osrBlock->lastIns(), barrier);
1244 0 : osrBlock->rewriteSlot(slot, barrier);
1245 0 : def = barrier;
1246 :
1247 : // If the TypeSet is more precise than |type|, adjust |type| for the
1248 : // code below.
1249 0 : if (type == MIRType::Value)
1250 0 : type = barrier->type();
1251 0 : } else if (type == MIRType::Null ||
1252 0 : type == MIRType::Undefined ||
1253 0 : type == MIRType::MagicOptimizedArguments)
1254 : {
1255 : // No unbox instruction will be added below, so check the type by
1256 : // adding a type barrier for a singleton type set.
1257 0 : TypeSet::Type ntype = TypeSet::PrimitiveType(ValueTypeFromMIRType(type));
1258 0 : LifoAlloc* lifoAlloc = alloc().lifoAlloc();
1259 0 : typeSet = lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc, ntype);
1260 0 : if (!typeSet)
1261 0 : return abort(AbortReason::Alloc);
1262 0 : MInstruction* barrier = MTypeBarrier::New(alloc(), def, typeSet);
1263 0 : osrBlock->insertBefore(osrBlock->lastIns(), barrier);
1264 0 : osrBlock->rewriteSlot(slot, barrier);
1265 0 : def = barrier;
1266 : }
1267 :
1268 0 : switch (type) {
1269 : case MIRType::Boolean:
1270 : case MIRType::Int32:
1271 : case MIRType::Double:
1272 : case MIRType::String:
1273 : case MIRType::Symbol:
1274 : case MIRType::Object:
1275 0 : if (type != def->type()) {
1276 0 : MUnbox* unbox = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
1277 0 : osrBlock->insertBefore(osrBlock->lastIns(), unbox);
1278 0 : osrBlock->rewriteSlot(slot, unbox);
1279 0 : def = unbox;
1280 : }
1281 : break;
1282 :
1283 : case MIRType::Null:
1284 : {
1285 0 : MConstant* c = MConstant::New(alloc(), NullValue());
1286 0 : osrBlock->insertBefore(osrBlock->lastIns(), c);
1287 0 : osrBlock->rewriteSlot(slot, c);
1288 0 : def = c;
1289 0 : break;
1290 : }
1291 :
1292 : case MIRType::Undefined:
1293 : {
1294 0 : MConstant* c = MConstant::New(alloc(), UndefinedValue());
1295 0 : osrBlock->insertBefore(osrBlock->lastIns(), c);
1296 0 : osrBlock->rewriteSlot(slot, c);
1297 0 : def = c;
1298 0 : break;
1299 : }
1300 :
1301 : case MIRType::MagicOptimizedArguments:
1302 : {
1303 0 : MOZ_ASSERT(hasLazyArguments_);
1304 0 : MConstant* lazyArg = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
1305 0 : osrBlock->insertBefore(osrBlock->lastIns(), lazyArg);
1306 0 : osrBlock->rewriteSlot(slot, lazyArg);
1307 0 : def = lazyArg;
1308 0 : break;
1309 : }
1310 :
1311 : default:
1312 : break;
1313 : }
1314 :
1315 0 : MOZ_ASSERT(def == osrBlock->getSlot(slot));
1316 0 : return Ok();
1317 : }
1318 :
1319 : AbortReasonOr<Ok>
1320 0 : IonBuilder::maybeAddOsrTypeBarriers()
1321 : {
1322 0 : if (!info().osrPc())
1323 0 : return Ok();
1324 :
1325 : // The loop has successfully been processed, and the loop header phis
1326 : // have their final type. Add unboxes and type barriers in the OSR
1327 : // block to check that the values have the appropriate type, and update
1328 : // the types in the preheader.
1329 :
1330 0 : MBasicBlock* osrBlock = graph().osrBlock();
1331 0 : if (!osrBlock) {
1332 : // Because IonBuilder does not compile catch blocks, it's possible to
1333 : // end up without an OSR block if the OSR pc is only reachable via a
1334 : // break-statement inside the catch block. For instance:
1335 : //
1336 : // for (;;) {
1337 : // try {
1338 : // throw 3;
1339 : // } catch(e) {
1340 : // break;
1341 : // }
1342 : // }
1343 : // while (..) { } // <= OSR here, only reachable via catch block.
1344 : //
1345 : // For now we just abort in this case.
1346 0 : MOZ_ASSERT(graph().hasTryBlock());
1347 0 : return abort(AbortReason::Disable, "OSR block only reachable through catch block");
1348 : }
1349 :
1350 0 : MBasicBlock* preheader = osrBlock->getSuccessor(0);
1351 0 : MBasicBlock* header = preheader->getSuccessor(0);
1352 : static const size_t OSR_PHI_POSITION = 1;
1353 0 : MOZ_ASSERT(preheader->getPredecessor(OSR_PHI_POSITION) == osrBlock);
1354 :
1355 0 : MResumePoint* headerRp = header->entryResumePoint();
1356 0 : size_t stackDepth = headerRp->stackDepth();
1357 0 : MOZ_ASSERT(stackDepth == osrBlock->stackDepth());
1358 0 : for (uint32_t slot = info().startArgSlot(); slot < stackDepth; slot++) {
1359 : // Aliased slots are never accessed, since they need to go through
1360 : // the callobject. The typebarriers are added there and can be
1361 : // discarded here.
1362 0 : if (info().isSlotAliased(slot))
1363 0 : continue;
1364 :
1365 0 : if (!alloc().ensureBallast())
1366 0 : return abort(AbortReason::Alloc);
1367 :
1368 0 : MInstruction* def = osrBlock->getSlot(slot)->toInstruction();
1369 0 : MPhi* preheaderPhi = preheader->getSlot(slot)->toPhi();
1370 0 : MPhi* headerPhi = headerRp->getOperand(slot)->toPhi();
1371 :
1372 0 : MIRType type = headerPhi->type();
1373 0 : TemporaryTypeSet* typeSet = headerPhi->resultTypeSet();
1374 :
1375 0 : MOZ_TRY(addOsrValueTypeBarrier(slot, &def, type, typeSet));
1376 :
1377 0 : preheaderPhi->replaceOperand(OSR_PHI_POSITION, def);
1378 0 : preheaderPhi->setResultType(type);
1379 0 : preheaderPhi->setResultTypeSet(typeSet);
1380 : }
1381 :
1382 0 : return Ok();
1383 : }
1384 :
1385 : enum class CFGState : uint32_t {
1386 : Alloc = 0,
1387 : Abort = 1,
1388 : Success = 2
1389 : };
1390 :
1391 : static CFGState
1392 0 : GetOrCreateControlFlowGraph(TempAllocator& tempAlloc, JSScript* script,
1393 : const ControlFlowGraph** cfgOut)
1394 : {
1395 0 : if (script->hasBaselineScript() && script->baselineScript()->controlFlowGraph()) {
1396 0 : *cfgOut = script->baselineScript()->controlFlowGraph();
1397 0 : return CFGState::Success;
1398 : }
1399 :
1400 0 : ControlFlowGenerator cfgenerator(tempAlloc, script);
1401 0 : if (!cfgenerator.traverseBytecode()) {
1402 0 : if (cfgenerator.aborted())
1403 : return CFGState::Abort;
1404 0 : return CFGState::Alloc;
1405 : }
1406 :
1407 : // If possible cache the control flow graph on the baseline script.
1408 0 : TempAllocator* graphAlloc = nullptr;
1409 0 : if (script->hasBaselineScript()) {
1410 0 : LifoAlloc& lifoAlloc = script->zone()->jitZone()->cfgSpace()->lifoAlloc();
1411 0 : LifoAlloc::AutoFallibleScope fallibleAllocator(&lifoAlloc);
1412 0 : graphAlloc = lifoAlloc.new_<TempAllocator>(&lifoAlloc);
1413 0 : if (!graphAlloc)
1414 0 : return CFGState::Alloc;
1415 : } else {
1416 : graphAlloc = &tempAlloc;
1417 : }
1418 :
1419 0 : ControlFlowGraph* cfg = cfgenerator.getGraph(*graphAlloc);
1420 0 : if (!cfg)
1421 : return CFGState::Alloc;
1422 :
1423 0 : if (script->hasBaselineScript()) {
1424 0 : MOZ_ASSERT(!script->baselineScript()->controlFlowGraph());
1425 0 : script->baselineScript()->setControlFlowGraph(cfg);
1426 : }
1427 :
1428 0 : if (JitSpewEnabled(JitSpew_CFG)) {
1429 0 : JitSpew(JitSpew_CFG, "Generating graph for %s:%u",
1430 0 : script->filename(), script->lineno());
1431 0 : Fprinter& print = JitSpewPrinter();
1432 0 : cfg->dump(print, script);
1433 : }
1434 :
1435 0 : *cfgOut = cfg;
1436 0 : return CFGState::Success;
1437 : }
1438 :
1439 : // We traverse the bytecode using the control flow graph. This structure contains
1440 : // a graph of CFGBlocks in RPO order.
1441 : //
1442 : // Per CFGBlock we take the corresponding MBasicBlock and start iterating the
1443 : // bytecode of that CFGBlock. Each basic block has a mapping of local slots to
1444 : // instructions, as well as a stack depth. As we encounter instructions we
1445 : // mutate this mapping in the current block.
1446 : //
1447 : // Afterwards we visit the control flow instruction. There we add the ending ins
1448 : // of the MBasicBlock and create new MBasicBlocks for the successors. That means
1449 : // adding phi nodes for diamond join points, making sure to propagate types
1450 : // around loops ...
1451 : //
1452 : // We keep a link between a CFGBlock and the entry MBasicBlock (in blockWorklist).
1453 : // That vector only contains the MBasicBlocks that correspond with a CFGBlock.
1454 : // We can create new MBasicBlocks that don't correspond to a CFGBlock.
1455 : AbortReasonOr<Ok>
1456 0 : IonBuilder::traverseBytecode()
1457 : {
1458 0 : CFGState state = GetOrCreateControlFlowGraph(alloc(), info().script(), &cfg);
1459 0 : MOZ_ASSERT_IF(cfg && info().script()->hasBaselineScript(),
1460 : info().script()->baselineScript()->controlFlowGraph() == cfg);
1461 0 : if (state == CFGState::Alloc)
1462 0 : return abort(AbortReason::Alloc);
1463 0 : if (state == CFGState::Abort)
1464 0 : return abort(AbortReason::Disable, "Couldn't create the CFG of script");
1465 :
1466 0 : if (!blockWorklist.growBy(cfg->numBlocks()))
1467 0 : return abort(AbortReason::Alloc);
1468 0 : blockWorklist[0] = current;
1469 :
1470 0 : size_t i = 0;
1471 0 : while (i < cfg->numBlocks()) {
1472 0 : if (!alloc().ensureBallast())
1473 0 : return abort(AbortReason::Alloc);
1474 :
1475 0 : bool restarted = false;
1476 0 : const CFGBlock* cfgblock = cfg->block(i);
1477 0 : MBasicBlock* mblock = blockWorklist[i];
1478 0 : MOZ_ASSERT(mblock && !mblock->isDead());
1479 :
1480 0 : MOZ_TRY(visitBlock(cfgblock, mblock));
1481 0 : MOZ_TRY(visitControlInstruction(cfgblock->stopIns(), &restarted));
1482 :
1483 0 : if (restarted) {
1484 : // Move back to the start of the loop.
1485 0 : while (!blockWorklist[i] || blockWorklist[i]->isDead()) {
1486 0 : MOZ_ASSERT(i > 0);
1487 0 : i--;
1488 : }
1489 0 : MOZ_ASSERT(cfgblock->stopIns()->isBackEdge());
1490 0 : MOZ_ASSERT(loopHeaderStack_.back() == blockWorklist[i]);
1491 : } else {
1492 0 : i++;
1493 : }
1494 : }
1495 :
1496 : #ifdef DEBUG
1497 0 : MOZ_ASSERT(graph().numBlocks() >= blockWorklist.length());
1498 0 : for (i = 0; i < cfg->numBlocks(); i++) {
1499 0 : MOZ_ASSERT(blockWorklist[i]);
1500 0 : MOZ_ASSERT(!blockWorklist[i]->isDead());
1501 0 : MOZ_ASSERT_IF(i != 0, blockWorklist[i]->id() != 0);
1502 : }
1503 : #endif
1504 :
1505 0 : cfg = nullptr;
1506 :
1507 0 : blockWorklist.clear();
1508 0 : return Ok();
1509 : }
1510 :
1511 : AbortReasonOr<Ok>
1512 0 : IonBuilder::visitBlock(const CFGBlock* cfgblock, MBasicBlock* mblock)
1513 : {
1514 0 : mblock->setLoopDepth(loopDepth_);
1515 :
1516 0 : cfgCurrent = cfgblock;
1517 0 : pc = cfgblock->startPc();
1518 :
1519 0 : if (mblock->pc() && script()->hasScriptCounts())
1520 0 : mblock->setHitCount(script()->getHitCount(mblock->pc()));
1521 :
1522 : // Optimization to move a predecessor that only has this block as successor
1523 : // just before this block. Skip this optimization if the previous block is
1524 : // not part of the same function, as we might have to backtrack on inlining
1525 : // failures.
1526 0 : if (mblock->numPredecessors() == 1 && mblock->getPredecessor(0)->numSuccessors() == 1 &&
1527 0 : !mblock->getPredecessor(0)->outerResumePoint())
1528 : {
1529 0 : graph().removeBlockFromList(mblock->getPredecessor(0));
1530 0 : graph().addBlock(mblock->getPredecessor(0));
1531 : }
1532 :
1533 0 : MOZ_TRY(setCurrentAndSpecializePhis(mblock));
1534 0 : graph().addBlock(mblock);
1535 :
1536 0 : while (pc < cfgblock->stopPc()) {
1537 0 : if (!alloc().ensureBallast())
1538 0 : return abort(AbortReason::Alloc);
1539 :
1540 : #ifdef DEBUG
1541 : // In debug builds, after compiling this op, check that all values
1542 : // popped by this opcode either:
1543 : //
1544 : // (1) Have the ImplicitlyUsed flag set on them.
1545 : // (2) Have more uses than before compiling this op (the value is
1546 : // used as operand of a new MIR instruction).
1547 : //
1548 : // This is used to catch problems where IonBuilder pops a value without
1549 : // adding any SSA uses and doesn't call setImplicitlyUsedUnchecked on it.
1550 0 : Vector<MDefinition*, 4, JitAllocPolicy> popped(alloc());
1551 0 : Vector<size_t, 4, JitAllocPolicy> poppedUses(alloc());
1552 0 : unsigned nuses = GetUseCount(pc);
1553 :
1554 0 : for (unsigned i = 0; i < nuses; i++) {
1555 0 : MDefinition* def = current->peek(-int32_t(i + 1));
1556 0 : if (!popped.append(def) || !poppedUses.append(def->defUseCount()))
1557 0 : return abort(AbortReason::Alloc);
1558 : }
1559 : #endif
1560 :
1561 : // Nothing in inspectOpcode() is allowed to advance the pc.
1562 0 : JSOp op = JSOp(*pc);
1563 0 : MOZ_TRY(inspectOpcode(op));
1564 :
1565 : #ifdef DEBUG
1566 0 : for (size_t i = 0; i < popped.length(); i++) {
1567 0 : switch (op) {
1568 : case JSOP_POP:
1569 : case JSOP_POPN:
1570 : case JSOP_DUPAT:
1571 : case JSOP_DUP:
1572 : case JSOP_DUP2:
1573 : case JSOP_PICK:
1574 : case JSOP_UNPICK:
1575 : case JSOP_SWAP:
1576 : case JSOP_SETARG:
1577 : case JSOP_SETLOCAL:
1578 : case JSOP_INITLEXICAL:
1579 : case JSOP_SETRVAL:
1580 : case JSOP_VOID:
1581 : // Don't require SSA uses for values popped by these ops.
1582 : break;
1583 :
1584 : case JSOP_POS:
1585 : case JSOP_TOID:
1586 : case JSOP_TOSTRING:
1587 : // These ops may leave their input on the stack without setting
1588 : // the ImplicitlyUsed flag. If this value will be popped immediately,
1589 : // we may replace it with |undefined|, but the difference is
1590 : // not observable.
1591 0 : MOZ_ASSERT(i == 0);
1592 0 : if (current->peek(-1) == popped[0])
1593 : break;
1594 : MOZ_FALLTHROUGH;
1595 :
1596 : default:
1597 0 : MOZ_ASSERT(popped[i]->isImplicitlyUsed() ||
1598 :
1599 : // MNewDerivedTypedObject instances are
1600 : // often dead unless they escape from the
1601 : // fn. See IonBuilder::loadTypedObjectData()
1602 : // for more details.
1603 : popped[i]->isNewDerivedTypedObject() ||
1604 :
1605 : popped[i]->defUseCount() > poppedUses[i]);
1606 : break;
1607 : }
1608 : }
1609 : #endif
1610 :
1611 0 : pc += CodeSpec[op].length;
1612 0 : current->updateTrackedSite(bytecodeSite(pc));
1613 : }
1614 0 : return Ok();
1615 : }
1616 :
1617 : bool
1618 0 : IonBuilder::blockIsOSREntry(const CFGBlock* block, const CFGBlock* predecessor)
1619 : {
1620 0 : jsbytecode* entryPc = block->startPc();
1621 :
1622 0 : if (!info().osrPc())
1623 : return false;
1624 :
1625 0 : if (entryPc == predecessor->startPc()) {
1626 : // The predecessor is the actual osr entry block. Since it is empty
1627 : // the current block also starts a the osr pc. But it isn't the osr entry.
1628 0 : MOZ_ASSERT(predecessor->stopPc() == predecessor->startPc());
1629 : return false;
1630 : }
1631 :
1632 0 : if (block->stopPc() == block->startPc() && block->stopIns()->isBackEdge()) {
1633 : // An empty block with only a backedge can never be a loop entry.
1634 : return false;
1635 : }
1636 :
1637 0 : MOZ_ASSERT(*info().osrPc() == JSOP_LOOPENTRY);
1638 17 : return info().osrPc() == entryPc;
1639 : }
1640 :
1641 : AbortReasonOr<Ok>
1642 1980 : IonBuilder::visitGoto(CFGGoto* ins)
1643 : {
1644 : // Test if this potentially was a fake loop and create OSR entry if that is
1645 : // the case.
1646 3960 : const CFGBlock* successor = ins->getSuccessor(0);
1647 0 : if (blockIsOSREntry(successor, cfgCurrent)) {
1648 : MBasicBlock* preheader;
1649 0 : MOZ_TRY_VAR(preheader, newOsrPreheader(current, successor->startPc(), pc));
1650 0 : current->end(MGoto::New(alloc(), preheader));
1651 0 : MOZ_TRY(setCurrentAndSpecializePhis(preheader));
1652 : }
1653 :
1654 1980 : size_t id = successor->id();
1655 0 : bool create = !blockWorklist[id] || blockWorklist[id]->isDead();
1656 :
1657 1980 : current->popn(ins->popAmount());
1658 :
1659 1980 : if (create)
1660 0 : MOZ_TRY_VAR(blockWorklist[id], newBlock(current, successor->startPc()));
1661 :
1662 1980 : MBasicBlock* succ = blockWorklist[id];
1663 0 : current->end(MGoto::New(alloc(), succ));
1664 :
1665 1980 : if (!create) {
1666 0 : if (!succ->addPredecessor(alloc(), current))
1667 0 : return abort(AbortReason::Alloc);
1668 : }
1669 :
1670 1980 : return Ok();
1671 : }
1672 :
1673 : AbortReasonOr<Ok>
1674 230 : IonBuilder::visitBackEdge(CFGBackEdge* ins, bool* restarted)
1675 : {
1676 230 : loopDepth_--;
1677 :
1678 460 : MBasicBlock* loopEntry = blockWorklist[ins->getSuccessor(0)->id()];
1679 0 : current->end(MGoto::New(alloc(), loopEntry));
1680 :
1681 460 : MOZ_ASSERT(ins->getSuccessor(0) == cfgLoopHeaderStack_.back());
1682 :
1683 : // Compute phis in the loop header and propagate them throughout the loop,
1684 : // including the successor.
1685 230 : AbortReason r = loopEntry->setBackedge(alloc(), current);
1686 0 : switch (r) {
1687 : case AbortReason::NoAbort:
1688 202 : loopHeaderStack_.popBack();
1689 : #ifdef DEBUG
1690 202 : cfgLoopHeaderStack_.popBack();
1691 : #endif
1692 202 : return Ok();
1693 :
1694 : case AbortReason::Disable:
1695 : // If there are types for variables on the backedge that were not
1696 : // present at the original loop header, then uses of the variables'
1697 : // phis may have generated incorrect nodes. The new types have been
1698 : // incorporated into the header phis, so remove all blocks for the
1699 : // loop body and restart with the new types.
1700 28 : *restarted = true;
1701 0 : MOZ_TRY(restartLoop(ins->getSuccessor(0)));
1702 0 : return Ok();
1703 :
1704 : default:
1705 0 : return abort(r);
1706 : }
1707 : }
1708 :
1709 : AbortReasonOr<Ok>
1710 202 : IonBuilder::visitLoopEntry(CFGLoopEntry* loopEntry)
1711 : {
1712 202 : unsigned stackPhiCount = loopEntry->stackPhiCount();
1713 0 : const CFGBlock* successor = loopEntry->getSuccessor(0);
1714 0 : bool osr = blockIsOSREntry(successor, cfgCurrent);
1715 0 : if (osr) {
1716 0 : MOZ_ASSERT(loopEntry->canOsr());
1717 : MBasicBlock* preheader;
1718 4 : MOZ_TRY_VAR(preheader, newOsrPreheader(current, successor->startPc(), pc));
1719 0 : current->end(MGoto::New(alloc(), preheader));
1720 0 : MOZ_TRY(setCurrentAndSpecializePhis(preheader));
1721 : }
1722 :
1723 202 : loopDepth_++;
1724 : MBasicBlock* header;
1725 808 : MOZ_TRY_VAR(header, newPendingLoopHeader(current, successor->startPc(), osr,
1726 : loopEntry->canOsr(), stackPhiCount));
1727 202 : blockWorklist[successor->id()] = header;
1728 :
1729 202 : current->end(MGoto::New(alloc(), header));
1730 :
1731 202 : if (!loopHeaderStack_.append(header))
1732 0 : return abort(AbortReason::Alloc);
1733 : #ifdef DEBUG
1734 202 : if (!cfgLoopHeaderStack_.append(successor))
1735 0 : return abort(AbortReason::Alloc);
1736 : #endif
1737 :
1738 606 : MOZ_TRY(analyzeNewLoopTypes(cfgCurrent));
1739 :
1740 404 : setCurrent(header);
1741 0 : pc = header->pc();
1742 :
1743 202 : return Ok();
1744 : }
1745 :
1746 : AbortReasonOr<Ok>
1747 230 : IonBuilder::jsop_loopentry()
1748 : {
1749 0 : MOZ_ASSERT(*pc == JSOP_LOOPENTRY);
1750 :
1751 0 : current->add(MInterruptCheck::New(alloc()));
1752 0 : insertRecompileCheck();
1753 :
1754 0 : return Ok();
1755 : }
1756 :
1757 : AbortReasonOr<Ok>
1758 0 : IonBuilder::visitControlInstruction(CFGControlInstruction* ins, bool* restarted)
1759 : {
1760 0 : switch (ins->type()) {
1761 : case CFGControlInstruction::Type_Test:
1762 0 : return visitTest(ins->toTest());
1763 : case CFGControlInstruction::Type_Compare:
1764 0 : return visitCompare(ins->toCompare());
1765 : case CFGControlInstruction::Type_Goto:
1766 0 : return visitGoto(ins->toGoto());
1767 : case CFGControlInstruction::Type_BackEdge:
1768 0 : return visitBackEdge(ins->toBackEdge(), restarted);
1769 : case CFGControlInstruction::Type_LoopEntry:
1770 0 : return visitLoopEntry(ins->toLoopEntry());
1771 : case CFGControlInstruction::Type_Return:
1772 : case CFGControlInstruction::Type_RetRVal:
1773 0 : return visitReturn(ins);
1774 : case CFGControlInstruction::Type_Try:
1775 0 : return visitTry(ins->toTry());
1776 : case CFGControlInstruction::Type_Throw:
1777 0 : return visitThrow(ins->toThrow());
1778 : case CFGControlInstruction::Type_TableSwitch:
1779 0 : return visitTableSwitch(ins->toTableSwitch());
1780 : }
1781 0 : MOZ_CRASH("Unknown Control Instruction");
1782 : }
1783 :
1784 : AbortReasonOr<Ok>
1785 0 : IonBuilder::inspectOpcode(JSOp op)
1786 : {
1787 : // Add not yet implemented opcodes at the bottom of the switch!
1788 0 : switch (op) {
1789 : case JSOP_NOP:
1790 : case JSOP_NOP_DESTRUCTURING:
1791 : case JSOP_TRY_DESTRUCTURING_ITERCLOSE:
1792 : case JSOP_LINENO:
1793 : case JSOP_JUMPTARGET:
1794 : case JSOP_LABEL:
1795 0 : return Ok();
1796 :
1797 : case JSOP_UNDEFINED:
1798 : // If this ever changes, change what JSOP_GIMPLICITTHIS does too.
1799 0 : pushConstant(UndefinedValue());
1800 0 : return Ok();
1801 :
1802 : case JSOP_IFNE:
1803 : case JSOP_IFEQ:
1804 : case JSOP_RETURN:
1805 : case JSOP_RETRVAL:
1806 : case JSOP_AND:
1807 : case JSOP_OR:
1808 : case JSOP_TRY:
1809 : case JSOP_THROW:
1810 : case JSOP_GOTO:
1811 : case JSOP_CONDSWITCH:
1812 : case JSOP_TABLESWITCH:
1813 : case JSOP_CASE:
1814 : case JSOP_DEFAULT:
1815 : // Control flow opcodes should be handled in the ControlFlowGenerator.
1816 0 : MOZ_CRASH("Shouldn't encounter this opcode.");
1817 :
1818 : case JSOP_BITNOT:
1819 0 : return jsop_bitnot();
1820 :
1821 : case JSOP_BITAND:
1822 : case JSOP_BITOR:
1823 : case JSOP_BITXOR:
1824 : case JSOP_LSH:
1825 : case JSOP_RSH:
1826 : case JSOP_URSH:
1827 60 : return jsop_bitop(op);
1828 :
1829 : case JSOP_ADD:
1830 : case JSOP_SUB:
1831 : case JSOP_MUL:
1832 : case JSOP_DIV:
1833 : case JSOP_MOD:
1834 337 : return jsop_binary_arith(op);
1835 :
1836 : case JSOP_POW:
1837 0 : return jsop_pow();
1838 :
1839 : case JSOP_POS:
1840 86 : return jsop_pos();
1841 :
1842 : case JSOP_NEG:
1843 0 : return jsop_neg();
1844 :
1845 : case JSOP_TOSTRING:
1846 32 : return jsop_tostring();
1847 :
1848 : case JSOP_DEFVAR:
1849 0 : return jsop_defvar(GET_UINT32_INDEX(pc));
1850 :
1851 : case JSOP_DEFLET:
1852 : case JSOP_DEFCONST:
1853 0 : return jsop_deflexical(GET_UINT32_INDEX(pc));
1854 :
1855 : case JSOP_DEFFUN:
1856 0 : return jsop_deffun();
1857 :
1858 : case JSOP_EQ:
1859 : case JSOP_NE:
1860 : case JSOP_STRICTEQ:
1861 : case JSOP_STRICTNE:
1862 : case JSOP_LT:
1863 : case JSOP_LE:
1864 : case JSOP_GT:
1865 : case JSOP_GE:
1866 1016 : return jsop_compare(op);
1867 :
1868 : case JSOP_DOUBLE:
1869 162 : pushConstant(info().getConst(pc));
1870 0 : return Ok();
1871 :
1872 : case JSOP_STRING:
1873 1749 : pushConstant(StringValue(info().getAtom(pc)));
1874 0 : return Ok();
1875 :
1876 : case JSOP_SYMBOL: {
1877 80 : unsigned which = GET_UINT8(pc);
1878 0 : JS::Symbol* sym = realm->runtime()->wellKnownSymbols().get(which);
1879 0 : pushConstant(SymbolValue(sym));
1880 0 : return Ok();
1881 : }
1882 :
1883 : case JSOP_ZERO:
1884 843 : pushConstant(Int32Value(0));
1885 0 : return Ok();
1886 :
1887 : case JSOP_ONE:
1888 588 : pushConstant(Int32Value(1));
1889 0 : return Ok();
1890 :
1891 : case JSOP_NULL:
1892 416 : pushConstant(NullValue());
1893 0 : return Ok();
1894 :
1895 : case JSOP_VOID:
1896 40 : current->pop();
1897 0 : pushConstant(UndefinedValue());
1898 0 : return Ok();
1899 :
1900 : case JSOP_HOLE:
1901 0 : pushConstant(MagicValue(JS_ELEMENTS_HOLE));
1902 0 : return Ok();
1903 :
1904 : case JSOP_FALSE:
1905 290 : pushConstant(BooleanValue(false));
1906 0 : return Ok();
1907 :
1908 : case JSOP_TRUE:
1909 199 : pushConstant(BooleanValue(true));
1910 0 : return Ok();
1911 :
1912 : case JSOP_ARGUMENTS:
1913 56 : return jsop_arguments();
1914 :
1915 : case JSOP_RUNONCE:
1916 0 : return jsop_runonce();
1917 :
1918 : case JSOP_REST:
1919 1 : return jsop_rest();
1920 :
1921 : case JSOP_GETARG:
1922 2264 : if (info().argsObjAliasesFormals()) {
1923 0 : MGetArgumentsObjectArg* getArg = MGetArgumentsObjectArg::New(alloc(),
1924 0 : current->argumentsObject(),
1925 0 : GET_ARGNO(pc));
1926 0 : current->add(getArg);
1927 0 : current->push(getArg);
1928 : } else {
1929 2262 : current->pushArg(GET_ARGNO(pc));
1930 : }
1931 1132 : return Ok();
1932 :
1933 : case JSOP_SETARG:
1934 318 : return jsop_setarg(GET_ARGNO(pc));
1935 :
1936 : case JSOP_GETLOCAL:
1937 15888 : current->pushLocal(GET_LOCALNO(pc));
1938 0 : return Ok();
1939 :
1940 : case JSOP_SETLOCAL:
1941 4959 : current->setLocal(GET_LOCALNO(pc));
1942 0 : return Ok();
1943 :
1944 : case JSOP_THROWSETCONST:
1945 : case JSOP_THROWSETALIASEDCONST:
1946 : case JSOP_THROWSETCALLEE:
1947 0 : return jsop_throwsetconst();
1948 :
1949 : case JSOP_CHECKLEXICAL:
1950 0 : return jsop_checklexical();
1951 :
1952 : case JSOP_INITLEXICAL:
1953 855 : current->setLocal(GET_LOCALNO(pc));
1954 0 : return Ok();
1955 :
1956 : case JSOP_INITGLEXICAL: {
1957 0 : MOZ_ASSERT(!script()->hasNonSyntacticScope());
1958 0 : MDefinition* value = current->pop();
1959 0 : current->push(constant(ObjectValue(script()->global().lexicalEnvironment())));
1960 0 : current->push(value);
1961 0 : return jsop_setprop(info().getAtom(pc)->asPropertyName());
1962 : }
1963 :
1964 : case JSOP_CHECKALIASEDLEXICAL:
1965 3 : return jsop_checkaliasedlexical(EnvironmentCoordinate(pc));
1966 :
1967 : case JSOP_INITALIASEDLEXICAL:
1968 1 : return jsop_setaliasedvar(EnvironmentCoordinate(pc));
1969 :
1970 : case JSOP_UNINITIALIZED:
1971 106 : pushConstant(MagicValue(JS_UNINITIALIZED_LEXICAL));
1972 0 : return Ok();
1973 :
1974 : case JSOP_POP: {
1975 3700 : MDefinition* def = current->pop();
1976 :
1977 : // POP opcodes frequently appear where values are killed, e.g. after
1978 : // SET* opcodes. Place a resume point afterwards to avoid capturing
1979 : // the dead value in later snapshots, except in places where that
1980 : // resume point is obviously unnecessary.
1981 3700 : if (pc[JSOP_POP_LENGTH] == JSOP_POP)
1982 0 : return Ok();
1983 0 : if (def->isConstant())
1984 0 : return Ok();
1985 0 : return maybeInsertResume();
1986 : }
1987 :
1988 : case JSOP_POPN:
1989 155 : for (uint32_t i = 0, n = GET_UINT16(pc); i < n; i++)
1990 0 : current->pop();
1991 0 : return Ok();
1992 :
1993 : case JSOP_DUPAT:
1994 354 : current->pushSlot(current->stackDepth() - 1 - GET_UINT24(pc));
1995 0 : return Ok();
1996 :
1997 : case JSOP_NEWINIT:
1998 2 : if (GET_UINT8(pc) == JSProto_Array)
1999 0 : return jsop_newarray(0);
2000 1 : return jsop_newobject();
2001 :
2002 : case JSOP_NEWARRAY:
2003 220 : return jsop_newarray(GET_UINT32(pc));
2004 :
2005 : case JSOP_NEWARRAY_COPYONWRITE:
2006 0 : return jsop_newarray_copyonwrite();
2007 :
2008 : case JSOP_NEWOBJECT:
2009 146 : return jsop_newobject();
2010 :
2011 : case JSOP_INITELEM:
2012 : case JSOP_INITHIDDENELEM:
2013 16 : return jsop_initelem();
2014 :
2015 : case JSOP_INITELEM_INC:
2016 12 : return jsop_initelem_inc();
2017 :
2018 : case JSOP_INITELEM_ARRAY:
2019 170 : return jsop_initelem_array();
2020 :
2021 : case JSOP_INITPROP:
2022 : case JSOP_INITLOCKEDPROP:
2023 : case JSOP_INITHIDDENPROP:
2024 : {
2025 536 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2026 0 : return jsop_initprop(name);
2027 : }
2028 :
2029 : case JSOP_MUTATEPROTO:
2030 : {
2031 0 : return jsop_mutateproto();
2032 : }
2033 :
2034 : case JSOP_INITPROP_GETTER:
2035 : case JSOP_INITHIDDENPROP_GETTER:
2036 : case JSOP_INITPROP_SETTER:
2037 : case JSOP_INITHIDDENPROP_SETTER: {
2038 0 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2039 0 : return jsop_initprop_getter_setter(name);
2040 : }
2041 :
2042 : case JSOP_INITELEM_GETTER:
2043 : case JSOP_INITHIDDENELEM_GETTER:
2044 : case JSOP_INITELEM_SETTER:
2045 : case JSOP_INITHIDDENELEM_SETTER:
2046 0 : return jsop_initelem_getter_setter();
2047 :
2048 : case JSOP_FUNCALL:
2049 4 : return jsop_funcall(GET_ARGC(pc));
2050 :
2051 : case JSOP_FUNAPPLY:
2052 30 : return jsop_funapply(GET_ARGC(pc));
2053 :
2054 : case JSOP_SPREADCALL:
2055 1 : return jsop_spreadcall();
2056 :
2057 : case JSOP_CALL:
2058 : case JSOP_CALL_IGNORES_RV:
2059 : case JSOP_CALLITER:
2060 : case JSOP_NEW:
2061 10352 : MOZ_TRY(jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL,
2062 : (JSOp)*pc == JSOP_CALL_IGNORES_RV));
2063 2588 : if (op == JSOP_CALLITER) {
2064 0 : if (!outermostBuilder()->iterators_.append(current->peek(-1)))
2065 0 : return abort(AbortReason::Alloc);
2066 : }
2067 2588 : return Ok();
2068 :
2069 : case JSOP_EVAL:
2070 : case JSOP_STRICTEVAL:
2071 0 : return jsop_eval(GET_ARGC(pc));
2072 :
2073 : case JSOP_INT8:
2074 1290 : pushConstant(Int32Value(GET_INT8(pc)));
2075 0 : return Ok();
2076 :
2077 : case JSOP_UINT16:
2078 261 : pushConstant(Int32Value(GET_UINT16(pc)));
2079 0 : return Ok();
2080 :
2081 : case JSOP_GETGNAME:
2082 : {
2083 122 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2084 0 : if (!script()->hasNonSyntacticScope())
2085 0 : return jsop_getgname(name);
2086 0 : return jsop_getname(name);
2087 : }
2088 :
2089 : case JSOP_SETGNAME:
2090 : case JSOP_STRICTSETGNAME:
2091 : {
2092 0 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2093 0 : JSObject* obj = nullptr;
2094 0 : if (!script()->hasNonSyntacticScope())
2095 0 : obj = testGlobalLexicalBinding(name);
2096 0 : if (obj)
2097 0 : return setStaticName(obj, name);
2098 0 : return jsop_setprop(name);
2099 : }
2100 :
2101 : case JSOP_GETNAME:
2102 : {
2103 244 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2104 0 : return jsop_getname(name);
2105 : }
2106 :
2107 : case JSOP_GETINTRINSIC:
2108 : {
2109 4216 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2110 0 : return jsop_intrinsic(name);
2111 : }
2112 :
2113 : case JSOP_GETIMPORT:
2114 : {
2115 0 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2116 0 : return jsop_getimport(name);
2117 : }
2118 :
2119 : case JSOP_BINDGNAME:
2120 0 : if (!script()->hasNonSyntacticScope()) {
2121 0 : if (JSObject* env = testGlobalLexicalBinding(info().getName(pc))) {
2122 0 : pushConstant(ObjectValue(*env));
2123 0 : return Ok();
2124 : }
2125 : }
2126 : // Fall through to JSOP_BINDNAME
2127 : MOZ_FALLTHROUGH;
2128 : case JSOP_BINDNAME:
2129 0 : return jsop_bindname(info().getName(pc));
2130 :
2131 : case JSOP_BINDVAR:
2132 0 : return jsop_bindvar();
2133 :
2134 : case JSOP_DUP:
2135 1412 : current->pushSlot(current->stackDepth() - 1);
2136 0 : return Ok();
2137 :
2138 : case JSOP_DUP2:
2139 38 : return jsop_dup2();
2140 :
2141 : case JSOP_SWAP:
2142 497 : current->swapAt(-1);
2143 0 : return Ok();
2144 :
2145 : case JSOP_PICK:
2146 108 : current->pick(-GET_INT8(pc));
2147 0 : return Ok();
2148 :
2149 : case JSOP_UNPICK:
2150 66 : current->unpick(-GET_INT8(pc));
2151 0 : return Ok();
2152 :
2153 : case JSOP_GETALIASEDVAR:
2154 200 : return jsop_getaliasedvar(EnvironmentCoordinate(pc));
2155 :
2156 : case JSOP_SETALIASEDVAR:
2157 8 : return jsop_setaliasedvar(EnvironmentCoordinate(pc));
2158 :
2159 : case JSOP_UINT24:
2160 0 : pushConstant(Int32Value(GET_UINT24(pc)));
2161 0 : return Ok();
2162 :
2163 : case JSOP_INT32:
2164 0 : pushConstant(Int32Value(GET_INT32(pc)));
2165 0 : return Ok();
2166 :
2167 : case JSOP_LOOPHEAD:
2168 : // JSOP_LOOPHEAD is handled when processing the loop header.
2169 0 : MOZ_CRASH("JSOP_LOOPHEAD outside loop");
2170 :
2171 : case JSOP_GETELEM:
2172 : case JSOP_CALLELEM:
2173 1719 : MOZ_TRY(jsop_getelem());
2174 0 : if (op == JSOP_CALLELEM)
2175 0 : MOZ_TRY(improveThisTypesForCall());
2176 0 : return Ok();
2177 :
2178 : case JSOP_SETELEM:
2179 : case JSOP_STRICTSETELEM:
2180 73 : return jsop_setelem();
2181 :
2182 : case JSOP_LENGTH:
2183 253 : return jsop_length();
2184 :
2185 : case JSOP_NOT:
2186 396 : return jsop_not();
2187 :
2188 : case JSOP_FUNCTIONTHIS:
2189 210 : return jsop_functionthis();
2190 :
2191 : case JSOP_GLOBALTHIS:
2192 0 : return jsop_globalthis();
2193 :
2194 : case JSOP_CALLEE: {
2195 6 : MDefinition* callee = getCallee();
2196 0 : current->push(callee);
2197 0 : return Ok();
2198 : }
2199 :
2200 : case JSOP_SUPERBASE:
2201 2 : return jsop_superbase();
2202 :
2203 : case JSOP_GETPROP_SUPER:
2204 : {
2205 4 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2206 0 : return jsop_getprop_super(name);
2207 : }
2208 :
2209 : case JSOP_GETELEM_SUPER:
2210 0 : return jsop_getelem_super();
2211 :
2212 : case JSOP_GETPROP:
2213 : case JSOP_CALLPROP:
2214 : {
2215 1872 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2216 0 : MOZ_TRY(jsop_getprop(name));
2217 0 : if (op == JSOP_CALLPROP)
2218 0 : MOZ_TRY(improveThisTypesForCall());
2219 0 : return Ok();
2220 : }
2221 :
2222 : case JSOP_SETPROP:
2223 : case JSOP_STRICTSETPROP:
2224 : case JSOP_SETNAME:
2225 : case JSOP_STRICTSETNAME:
2226 : {
2227 970 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2228 0 : return jsop_setprop(name);
2229 : }
2230 :
2231 : case JSOP_DELPROP:
2232 : case JSOP_STRICTDELPROP:
2233 : {
2234 0 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2235 0 : return jsop_delprop(name);
2236 : }
2237 :
2238 : case JSOP_DELELEM:
2239 : case JSOP_STRICTDELELEM:
2240 0 : return jsop_delelem();
2241 :
2242 : case JSOP_REGEXP:
2243 2 : return jsop_regexp(info().getRegExp(pc));
2244 :
2245 : case JSOP_CALLSITEOBJ:
2246 0 : pushConstant(ObjectValue(*info().getObject(pc)));
2247 0 : return Ok();
2248 :
2249 : case JSOP_OBJECT:
2250 0 : return jsop_object(info().getObject(pc));
2251 :
2252 : case JSOP_CLASSCONSTRUCTOR:
2253 0 : return jsop_classconstructor();
2254 :
2255 : case JSOP_TYPEOF:
2256 : case JSOP_TYPEOFEXPR:
2257 48 : return jsop_typeof();
2258 :
2259 : case JSOP_TOASYNC:
2260 0 : return jsop_toasync();
2261 :
2262 : case JSOP_TOASYNCGEN:
2263 0 : return jsop_toasyncgen();
2264 :
2265 : case JSOP_TOASYNCITER:
2266 0 : return jsop_toasynciter();
2267 :
2268 : case JSOP_TOID:
2269 1 : return jsop_toid();
2270 :
2271 : case JSOP_ITERNEXT:
2272 0 : return jsop_iternext();
2273 :
2274 : case JSOP_LAMBDA:
2275 34 : return jsop_lambda(info().getFunction(pc));
2276 :
2277 : case JSOP_LAMBDA_ARROW:
2278 2 : return jsop_lambda_arrow(info().getFunction(pc));
2279 :
2280 : case JSOP_SETFUNNAME:
2281 0 : return jsop_setfunname(GET_UINT8(pc));
2282 :
2283 : case JSOP_PUSHLEXICALENV:
2284 2 : return jsop_pushlexicalenv(GET_UINT32_INDEX(pc));
2285 :
2286 : case JSOP_POPLEXICALENV:
2287 2 : current->setEnvironmentChain(walkEnvironmentChain(1));
2288 0 : return Ok();
2289 :
2290 : case JSOP_FRESHENLEXICALENV:
2291 0 : return jsop_copylexicalenv(true);
2292 :
2293 : case JSOP_RECREATELEXICALENV:
2294 0 : return jsop_copylexicalenv(false);
2295 :
2296 : case JSOP_ITER:
2297 0 : return jsop_iter();
2298 :
2299 : case JSOP_MOREITER:
2300 0 : return jsop_itermore();
2301 :
2302 : case JSOP_ISNOITER:
2303 0 : return jsop_isnoiter();
2304 :
2305 : case JSOP_ENDITER:
2306 0 : return jsop_iterend();
2307 :
2308 : case JSOP_IN:
2309 49 : return jsop_in();
2310 :
2311 : case JSOP_HASOWN:
2312 1 : return jsop_hasown();
2313 :
2314 : case JSOP_SETRVAL:
2315 96 : MOZ_ASSERT(!script()->noScriptRval());
2316 0 : current->setSlot(info().returnValueSlot(), current->pop());
2317 0 : return Ok();
2318 :
2319 : case JSOP_INSTANCEOF:
2320 11 : return jsop_instanceof();
2321 :
2322 : case JSOP_DEBUGLEAVELEXICALENV:
2323 109 : return Ok();
2324 :
2325 : case JSOP_DEBUGGER:
2326 0 : return jsop_debugger();
2327 :
2328 : case JSOP_GIMPLICITTHIS:
2329 44 : if (!script()->hasNonSyntacticScope()) {
2330 0 : pushConstant(UndefinedValue());
2331 0 : return Ok();
2332 : }
2333 : // Fallthrough to IMPLICITTHIS in non-syntactic scope case
2334 : MOZ_FALLTHROUGH;
2335 : case JSOP_IMPLICITTHIS:
2336 : {
2337 44 : PropertyName* name = info().getAtom(pc)->asPropertyName();
2338 0 : return jsop_implicitthis(name);
2339 : }
2340 :
2341 : case JSOP_NEWTARGET:
2342 7 : return jsop_newtarget();
2343 :
2344 : case JSOP_CHECKISOBJ:
2345 288 : return jsop_checkisobj(GET_UINT8(pc));
2346 :
2347 : case JSOP_CHECKISCALLABLE:
2348 0 : return jsop_checkiscallable(GET_UINT8(pc));
2349 :
2350 : case JSOP_CHECKOBJCOERCIBLE:
2351 3 : return jsop_checkobjcoercible();
2352 :
2353 : case JSOP_DEBUGCHECKSELFHOSTED:
2354 : {
2355 : #ifdef DEBUG
2356 208 : MDebugCheckSelfHosted* check = MDebugCheckSelfHosted::New(alloc(), current->pop());
2357 0 : current->add(check);
2358 0 : current->push(check);
2359 0 : MOZ_TRY(resumeAfter(check));
2360 : #endif
2361 104 : return Ok();
2362 : }
2363 :
2364 : case JSOP_IS_CONSTRUCTING:
2365 88 : pushConstant(MagicValue(JS_IS_CONSTRUCTING));
2366 0 : return Ok();
2367 :
2368 : case JSOP_OPTIMIZE_SPREADCALL:
2369 : {
2370 : // Assuming optimization isn't available doesn't affect correctness.
2371 : // TODO: Investigate dynamic checks.
2372 0 : MDefinition* arr = current->peek(-1);
2373 0 : arr->setImplicitlyUsedUnchecked();
2374 0 : pushConstant(BooleanValue(false));
2375 0 : return Ok();
2376 : }
2377 :
2378 : case JSOP_IMPORTMETA:
2379 0 : return jsop_importmeta();
2380 :
2381 : case JSOP_LOOPENTRY:
2382 230 : return jsop_loopentry();
2383 :
2384 : // ===== NOT Yet Implemented =====
2385 : // Read below!
2386 :
2387 : // With
2388 : case JSOP_ENTERWITH:
2389 : case JSOP_LEAVEWITH:
2390 :
2391 : // Spread
2392 : case JSOP_SPREADNEW:
2393 : case JSOP_SPREADEVAL:
2394 : case JSOP_STRICTSPREADEVAL:
2395 :
2396 : // Classes
2397 : case JSOP_CHECKCLASSHERITAGE:
2398 : case JSOP_FUNWITHPROTO:
2399 : case JSOP_OBJWITHPROTO:
2400 : case JSOP_BUILTINPROTO:
2401 : case JSOP_INITHOMEOBJECT:
2402 : case JSOP_DERIVEDCONSTRUCTOR:
2403 : case JSOP_CHECKTHIS:
2404 : case JSOP_CHECKRETURN:
2405 : case JSOP_CHECKTHISREINIT:
2406 :
2407 : // Super
2408 : case JSOP_SETPROP_SUPER:
2409 : case JSOP_SETELEM_SUPER:
2410 : case JSOP_STRICTSETPROP_SUPER:
2411 : case JSOP_STRICTSETELEM_SUPER:
2412 : case JSOP_SUPERFUN:
2413 : // Most of the infrastructure for these exists in Ion, but needs review
2414 : // and testing before these are enabled. Once other opcodes that are used
2415 : // in derived classes are supported in Ion, this can be better validated
2416 : // with testcases. Pay special attention to bailout and other areas where
2417 : // JSOP_NEW has special handling.
2418 : case JSOP_SPREADSUPERCALL:
2419 : case JSOP_SUPERCALL:
2420 :
2421 : // Environments (bug 1366470)
2422 : case JSOP_PUSHVARENV:
2423 : case JSOP_POPVARENV:
2424 :
2425 : // Compound assignment
2426 : case JSOP_GETBOUNDNAME:
2427 :
2428 : // Generators / Async (bug 1317690)
2429 : case JSOP_EXCEPTION:
2430 : case JSOP_THROWING:
2431 : case JSOP_ISGENCLOSING:
2432 : case JSOP_INITIALYIELD:
2433 : case JSOP_YIELD:
2434 : case JSOP_FINALYIELDRVAL:
2435 : case JSOP_RESUME:
2436 : case JSOP_DEBUGAFTERYIELD:
2437 : case JSOP_AWAIT:
2438 : case JSOP_GENERATOR:
2439 :
2440 : // Misc
2441 : case JSOP_DELNAME:
2442 : case JSOP_FINALLY:
2443 : case JSOP_GETRVAL:
2444 : case JSOP_GOSUB:
2445 : case JSOP_RETSUB:
2446 : case JSOP_SETINTRINSIC:
2447 : case JSOP_THROWMSG:
2448 : // === !! WARNING WARNING WARNING !! ===
2449 : // Do you really want to sacrifice performance by not implementing this
2450 : // operation in the optimizing compiler?
2451 : break;
2452 :
2453 : case JSOP_FORCEINTERPRETER:
2454 : // Intentionally not implemented.
2455 : break;
2456 :
2457 : case JSOP_UNUSED126:
2458 : case JSOP_UNUSED206:
2459 : case JSOP_UNUSED223:
2460 : case JSOP_LIMIT:
2461 : break;
2462 : }
2463 :
2464 : // Track a simpler message, since the actionable abort message is a
2465 : // static string, and the internal opcode name isn't an actionable
2466 : // thing anyways.
2467 0 : trackActionableAbort("Unsupported bytecode");
2468 : #ifdef DEBUG
2469 18 : return abort(AbortReason::Disable, "Unsupported opcode: %s", CodeName[op]);
2470 : #else
2471 : return abort(AbortReason::Disable, "Unsupported opcode: %d", op);
2472 : #endif
2473 : }
2474 :
2475 : AbortReasonOr<Ok>
2476 0 : IonBuilder::restartLoop(const CFGBlock* cfgHeader)
2477 : {
2478 0 : AutoTraceLog logCompile(traceLogger(), TraceLogger_IonBuilderRestartLoop);
2479 :
2480 0 : spew("New types at loop header, restarting loop body");
2481 :
2482 1 : if (JitOptions.limitScriptSize) {
2483 28 : if (++numLoopRestarts_ >= MAX_LOOP_RESTARTS)
2484 0 : return abort(AbortReason::Disable, "Aborted while processing control flow");
2485 : }
2486 :
2487 28 : MBasicBlock* header = blockWorklist[cfgHeader->id()];
2488 :
2489 : // Discard unreferenced & pre-allocated resume points.
2490 28 : replaceMaybeFallbackFunctionGetter(nullptr);
2491 :
2492 : // Remove all blocks in the loop body other than the header, which has phis
2493 : // of the appropriate type and incoming edges to preserve.
2494 0 : if (!graph().removeSuccessorBlocks(header))
2495 0 : return abort(AbortReason::Alloc);
2496 56 : graph().removeBlockFromList(header);
2497 :
2498 : // Remove all instructions from the header itself, and all resume points
2499 : // except the entry resume point.
2500 0 : header->discardAllInstructions();
2501 28 : header->discardAllResumePoints(/* discardEntry = */ false);
2502 0 : header->setStackDepth(header->getPredecessor(0)->stackDepth());
2503 :
2504 28 : loopDepth_ = header->loopDepth();
2505 :
2506 : // Don't specializePhis(), as the header has been visited before and the
2507 : // phis have already had their type set.
2508 56 : setCurrent(header);
2509 0 : pc = header->pc();
2510 :
2511 28 : return Ok();
2512 : }
2513 :
2514 : AbortReasonOr<Ok>
2515 2850 : IonBuilder::replaceTypeSet(MDefinition* subject, TemporaryTypeSet* type, MTest* test)
2516 : {
2517 1 : if (type->unknown())
2518 0 : return Ok();
2519 :
2520 : // Don't emit MFilterTypeSet if it doesn't improve the typeset.
2521 0 : if (subject->resultTypeSet()) {
2522 0 : if (subject->resultTypeSet()->equals(type))
2523 1122 : return Ok();
2524 : } else {
2525 0 : TemporaryTypeSet oldTypes(alloc_->lifoAlloc(), subject->type());
2526 0 : if (oldTypes.equals(type))
2527 1500 : return Ok();
2528 : }
2529 :
2530 : MInstruction* replace = nullptr;
2531 : MDefinition* ins;
2532 :
2533 0 : for (uint32_t i = 0; i < current->stackDepth(); i++) {
2534 2294 : ins = current->getSlot(i);
2535 :
2536 : // Instead of creating a new MFilterTypeSet, try to update the old one.
2537 0 : if (ins->isFilterTypeSet() && ins->getOperand(0) == subject &&
2538 0 : ins->dependency() == test)
2539 : {
2540 : TemporaryTypeSet* intersect =
2541 0 : TypeSet::intersectSets(ins->resultTypeSet(), type, alloc_->lifoAlloc());
2542 0 : if (!intersect)
2543 0 : return abort(AbortReason::Alloc);
2544 :
2545 0 : ins->toFilterTypeSet()->setResultType(intersect->getKnownMIRType());
2546 0 : ins->toFilterTypeSet()->setResultTypeSet(intersect);
2547 :
2548 0 : if (ins->type() == MIRType::Undefined)
2549 0 : current->setSlot(i, constant(UndefinedValue()));
2550 0 : else if (ins->type() == MIRType::Null)
2551 0 : current->setSlot(i, constant(NullValue()));
2552 0 : else if (ins->type() == MIRType::MagicOptimizedArguments)
2553 0 : current->setSlot(i, constant(MagicValue(JS_OPTIMIZED_ARGUMENTS)));
2554 : else
2555 0 : MOZ_ASSERT(!IsMagicType(ins->type()));
2556 : continue;
2557 : }
2558 :
2559 0 : if (ins == subject) {
2560 0 : if (!replace) {
2561 0 : replace = MFilterTypeSet::New(alloc(), subject, type);
2562 216 : current->add(replace);
2563 :
2564 : // Make sure we don't hoist it above the MTest, we can use the
2565 : // 'dependency' of an MInstruction. This is normally used by
2566 : // Alias Analysis, but won't get overwritten, since this
2567 : // instruction doesn't have an AliasSet.
2568 216 : replace->setDependency(test);
2569 :
2570 0 : if (replace->type() == MIRType::Undefined)
2571 0 : replace = constant(UndefinedValue());
2572 0 : else if (replace->type() == MIRType::Null)
2573 0 : replace = constant(NullValue());
2574 1 : else if (replace->type() == MIRType::MagicOptimizedArguments)
2575 0 : replace = constant(MagicValue(JS_OPTIMIZED_ARGUMENTS));
2576 : else
2577 412 : MOZ_ASSERT(!IsMagicType(ins->type()));
2578 : }
2579 245 : current->setSlot(i, replace);
2580 : }
2581 : }
2582 228 : return Ok();
2583 : }
2584 :
2585 : bool
2586 496 : IonBuilder::detectAndOrStructure(MPhi* ins, bool* branchIsAnd)
2587 : {
2588 : // Look for a triangle pattern:
2589 : //
2590 : // initialBlock
2591 : // / |
2592 : // branchBlock |
2593 : // \ |
2594 : // testBlock
2595 : //
2596 : // Where ins is a phi from testBlock which combines two values
2597 : // pushed onto the stack by initialBlock and branchBlock.
2598 :
2599 496 : if (ins->numOperands() != 2)
2600 : return false;
2601 :
2602 0 : MBasicBlock* testBlock = ins->block();
2603 464 : MOZ_ASSERT(testBlock->numPredecessors() == 2);
2604 :
2605 : MBasicBlock* initialBlock;
2606 : MBasicBlock* branchBlock;
2607 1 : if (testBlock->getPredecessor(0)->lastIns()->isTest()) {
2608 0 : initialBlock = testBlock->getPredecessor(0);
2609 0 : branchBlock = testBlock->getPredecessor(1);
2610 1 : } else if (testBlock->getPredecessor(1)->lastIns()->isTest()) {
2611 0 : initialBlock = testBlock->getPredecessor(1);
2612 0 : branchBlock = testBlock->getPredecessor(0);
2613 : } else {
2614 : return false;
2615 : }
2616 :
2617 0 : if (branchBlock->numSuccessors() != 1)
2618 : return false;
2619 :
2620 0 : if (branchBlock->numPredecessors() != 1 || branchBlock->getPredecessor(0) != initialBlock)
2621 : return false;
2622 :
2623 0 : if (initialBlock->numSuccessors() != 2)
2624 : return false;
2625 :
2626 0 : MDefinition* branchResult = ins->getOperand(testBlock->indexForPredecessor(branchBlock));
2627 0 : MDefinition* initialResult = ins->getOperand(testBlock->indexForPredecessor(initialBlock));
2628 :
2629 0 : if (branchBlock->stackDepth() != initialBlock->stackDepth())
2630 : return false;
2631 0 : if (branchBlock->stackDepth() != testBlock->stackDepth() + 1)
2632 : return false;
2633 0 : if (branchResult != branchBlock->peek(-1) || initialResult != initialBlock->peek(-1))
2634 : return false;
2635 :
2636 0 : MTest* initialTest = initialBlock->lastIns()->toTest();
2637 0 : bool branchIsTrue = branchBlock == initialTest->ifTrue();
2638 0 : if (initialTest->input() == ins->getOperand(0))
2639 0 : *branchIsAnd = branchIsTrue != (testBlock->getPredecessor(0) == branchBlock);
2640 0 : else if (initialTest->input() == ins->getOperand(1))
2641 0 : *branchIsAnd = branchIsTrue != (testBlock->getPredecessor(1) == branchBlock);
2642 : else
2643 : return false;
2644 :
2645 : return true;
2646 : }
2647 :
2648 : AbortReasonOr<Ok>
2649 1462 : IonBuilder::improveTypesAtCompare(MCompare* ins, bool trueBranch, MTest* test)
2650 : {
2651 0 : if (ins->compareType() == MCompare::Compare_Undefined ||
2652 1340 : ins->compareType() == MCompare::Compare_Null)
2653 : {
2654 300 : return improveTypesAtNullOrUndefinedCompare(ins, trueBranch, test);
2655 : }
2656 :
2657 0 : if ((ins->lhs()->isTypeOf() || ins->rhs()->isTypeOf()) &&
2658 396 : (ins->lhs()->isConstant() || ins->rhs()->isConstant()))
2659 : {
2660 66 : return improveTypesAtTypeOfCompare(ins, trueBranch, test);
2661 : }
2662 :
2663 1096 : return Ok();
2664 : }
2665 :
2666 : AbortReasonOr<Ok>
2667 66 : IonBuilder::improveTypesAtTypeOfCompare(MCompare* ins, bool trueBranch, MTest* test)
2668 : {
2669 0 : MTypeOf* typeOf = ins->lhs()->isTypeOf() ? ins->lhs()->toTypeOf() : ins->rhs()->toTypeOf();
2670 330 : MConstant* constant = ins->lhs()->isConstant() ? ins->lhs()->toConstant() : ins->rhs()->toConstant();
2671 :
2672 0 : if (constant->type() != MIRType::String)
2673 0 : return Ok();
2674 :
2675 0 : bool equal = ins->jsop() == JSOP_EQ || ins->jsop() == JSOP_STRICTEQ;
2676 66 : bool notEqual = ins->jsop() == JSOP_NE || ins->jsop() == JSOP_STRICTNE;
2677 :
2678 0 : if (notEqual)
2679 28 : trueBranch = !trueBranch;
2680 :
2681 : // Relational compares not supported.
2682 1 : if (!equal && !notEqual)
2683 0 : return Ok();
2684 :
2685 0 : MDefinition* subject = typeOf->input();
2686 66 : TemporaryTypeSet* inputTypes = subject->resultTypeSet();
2687 :
2688 : // Create temporary typeset equal to the type if there is no resultTypeSet.
2689 0 : TemporaryTypeSet tmp;
2690 0 : if (!inputTypes) {
2691 0 : if (subject->type() == MIRType::Value)
2692 0 : return Ok();
2693 0 : inputTypes = &tmp;
2694 8 : tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(subject->type())), alloc_->lifoAlloc());
2695 : }
2696 :
2697 1 : if (inputTypes->unknown())
2698 0 : return Ok();
2699 :
2700 : // Note: we cannot remove the AnyObject type in the false branch,
2701 : // since there are multiple ways to get an object. That is the reason
2702 : // for the 'trueBranch' test.
2703 0 : TemporaryTypeSet filter;
2704 0 : const JSAtomState& names = runtime->names();
2705 0 : if (constant->toString() == TypeName(JSTYPE_UNDEFINED, names)) {
2706 0 : filter.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
2707 0 : if (typeOf->inputMaybeCallableOrEmulatesUndefined() && trueBranch)
2708 0 : filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
2709 0 : } else if (constant->toString() == TypeName(JSTYPE_BOOLEAN, names)) {
2710 0 : filter.addType(TypeSet::BooleanType(), alloc_->lifoAlloc());
2711 0 : } else if (constant->toString() == TypeName(JSTYPE_NUMBER, names)) {
2712 0 : filter.addType(TypeSet::Int32Type(), alloc_->lifoAlloc());
2713 0 : filter.addType(TypeSet::DoubleType(), alloc_->lifoAlloc());
2714 0 : } else if (constant->toString() == TypeName(JSTYPE_STRING, names)) {
2715 0 : filter.addType(TypeSet::StringType(), alloc_->lifoAlloc());
2716 0 : } else if (constant->toString() == TypeName(JSTYPE_SYMBOL, names)) {
2717 0 : filter.addType(TypeSet::SymbolType(), alloc_->lifoAlloc());
2718 0 : } else if (constant->toString() == TypeName(JSTYPE_OBJECT, names)) {
2719 0 : filter.addType(TypeSet::NullType(), alloc_->lifoAlloc());
2720 0 : if (trueBranch)
2721 0 : filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
2722 0 : } else if (constant->toString() == TypeName(JSTYPE_FUNCTION, names)) {
2723 0 : if (typeOf->inputMaybeCallableOrEmulatesUndefined() && trueBranch)
2724 0 : filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
2725 : } else {
2726 0 : return Ok();
2727 : }
2728 :
2729 : TemporaryTypeSet* type;
2730 0 : if (trueBranch)
2731 66 : type = TypeSet::intersectSets(&filter, inputTypes, alloc_->lifoAlloc());
2732 : else
2733 66 : type = TypeSet::removeSet(inputTypes, &filter, alloc_->lifoAlloc());
2734 :
2735 1 : if (!type)
2736 0 : return abort(AbortReason::Alloc);
2737 :
2738 66 : return replaceTypeSet(subject, type, test);
2739 : }
2740 :
2741 : AbortReasonOr<Ok>
2742 300 : IonBuilder::improveTypesAtNullOrUndefinedCompare(MCompare* ins, bool trueBranch, MTest* test)
2743 : {
2744 300 : MOZ_ASSERT(ins->compareType() == MCompare::Compare_Undefined ||
2745 : ins->compareType() == MCompare::Compare_Null);
2746 :
2747 : // altersUndefined/Null represents if we can filter/set Undefined/Null.
2748 : bool altersUndefined, altersNull;
2749 300 : JSOp op = ins->jsop();
2750 :
2751 300 : switch(op) {
2752 : case JSOP_STRICTNE:
2753 : case JSOP_STRICTEQ:
2754 0 : altersUndefined = ins->compareType() == MCompare::Compare_Undefined;
2755 0 : altersNull = ins->compareType() == MCompare::Compare_Null;
2756 300 : break;
2757 : case JSOP_NE:
2758 : case JSOP_EQ:
2759 : altersUndefined = altersNull = true;
2760 : break;
2761 : default:
2762 0 : MOZ_CRASH("Relational compares not supported");
2763 : }
2764 :
2765 0 : MDefinition* subject = ins->lhs();
2766 300 : TemporaryTypeSet* inputTypes = subject->resultTypeSet();
2767 :
2768 900 : MOZ_ASSERT(IsNullOrUndefined(ins->rhs()->type()));
2769 :
2770 : // Create temporary typeset equal to the type if there is no resultTypeSet.
2771 0 : TemporaryTypeSet tmp;
2772 0 : if (!inputTypes) {
2773 0 : if (subject->type() == MIRType::Value)
2774 0 : return Ok();
2775 0 : inputTypes = &tmp;
2776 100 : tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(subject->type())), alloc_->lifoAlloc());
2777 : }
2778 :
2779 0 : if (inputTypes->unknown())
2780 0 : return Ok();
2781 :
2782 : TemporaryTypeSet* type;
2783 :
2784 : // Decide if we need to filter the type or set it.
2785 296 : if ((op == JSOP_STRICTEQ || op == JSOP_EQ) ^ trueBranch) {
2786 : // Remove undefined/null
2787 0 : TemporaryTypeSet remove;
2788 0 : if (altersUndefined)
2789 0 : remove.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
2790 0 : if (altersNull)
2791 267 : remove.addType(TypeSet::NullType(), alloc_->lifoAlloc());
2792 :
2793 296 : type = TypeSet::removeSet(inputTypes, &remove, alloc_->lifoAlloc());
2794 : } else {
2795 : // Set undefined/null.
2796 0 : TemporaryTypeSet base;
2797 0 : if (altersUndefined) {
2798 177 : base.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
2799 : // If TypeSet emulates undefined, then we cannot filter the objects.
2800 0 : if (inputTypes->maybeEmulatesUndefined(constraints()))
2801 9 : base.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
2802 : }
2803 :
2804 0 : if (altersNull)
2805 267 : base.addType(TypeSet::NullType(), alloc_->lifoAlloc());
2806 :
2807 296 : type = TypeSet::intersectSets(&base, inputTypes, alloc_->lifoAlloc());
2808 : }
2809 :
2810 1 : if (!type)
2811 0 : return abort(AbortReason::Alloc);
2812 :
2813 296 : return replaceTypeSet(subject, type, test);
2814 : }
2815 :
2816 : AbortReasonOr<Ok>
2817 4868 : IonBuilder::improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test)
2818 : {
2819 : // We explore the test condition to try and deduce as much type information
2820 : // as possible.
2821 :
2822 : // All branches of this switch that don't want to fall through to the
2823 : // default behavior must return. The default behavior assumes that a true
2824 : // test means the incoming ins is not null or undefined and that a false
2825 : // tests means it's one of null, undefined, false, 0, "", and objects
2826 : // emulating undefined
2827 4868 : switch (ins->op()) {
2828 : case MDefinition::Opcode::Not:
2829 1836 : return improveTypesAtTest(ins->toNot()->getOperand(0), !trueBranch, test);
2830 : case MDefinition::Opcode::IsObject: {
2831 0 : MDefinition* subject = ins->getOperand(0);
2832 12 : TemporaryTypeSet* oldType = subject->resultTypeSet();
2833 :
2834 : // Create temporary typeset equal to the type if there is no resultTypeSet.
2835 0 : TemporaryTypeSet tmp;
2836 0 : if (!oldType) {
2837 0 : if (subject->type() == MIRType::Value)
2838 0 : return Ok();
2839 0 : oldType = &tmp;
2840 0 : tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(subject->type())), alloc_->lifoAlloc());
2841 : }
2842 :
2843 1 : if (oldType->unknown())
2844 0 : return Ok();
2845 :
2846 0 : TemporaryTypeSet* type = nullptr;
2847 0 : if (trueBranch)
2848 12 : type = oldType->cloneObjectsOnly(alloc_->lifoAlloc());
2849 : else
2850 12 : type = oldType->cloneWithoutObjects(alloc_->lifoAlloc());
2851 :
2852 1 : if (!type)
2853 0 : return abort(AbortReason::Alloc);
2854 :
2855 12 : return replaceTypeSet(subject, type, test);
2856 : }
2857 : case MDefinition::Opcode::Phi: {
2858 0 : bool branchIsAnd = true;
2859 496 : if (!detectAndOrStructure(ins->toPhi(), &branchIsAnd)) {
2860 : // Just fall through to the default behavior.
2861 : break;
2862 : }
2863 :
2864 : // Now we have detected the triangular structure and determined if it
2865 : // was an AND or an OR.
2866 0 : if (branchIsAnd) {
2867 0 : if (trueBranch) {
2868 0 : MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(0), true, test));
2869 0 : MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(1), true, test));
2870 : }
2871 : } else {
2872 : /*
2873 : * if (a || b) {
2874 : * ...
2875 : * } else {
2876 : * ...
2877 : * }
2878 : *
2879 : * If we have a statements like the one described above,
2880 : * And we are in the else branch of it. It amounts to:
2881 : * if (!(a || b)) and being in the true branch.
2882 : *
2883 : * Simplifying, we have (!a && !b)
2884 : * In this case we can use the same logic we use for branchIsAnd
2885 : *
2886 : */
2887 0 : if (!trueBranch) {
2888 0 : MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(0), false, test));
2889 0 : MOZ_TRY(improveTypesAtTest(ins->toPhi()->getOperand(1), false, test));
2890 : }
2891 : }
2892 0 : return Ok();
2893 : }
2894 :
2895 : case MDefinition::Opcode::Compare:
2896 2924 : return improveTypesAtCompare(ins->toCompare(), trueBranch, test);
2897 :
2898 : default:
2899 : break;
2900 : }
2901 :
2902 : // By default MTest tests ToBoolean(input). As a result in the true branch we can filter
2903 : // undefined and null. In false branch we can only encounter undefined, null, false, 0, ""
2904 : // and objects that emulate undefined.
2905 :
2906 2476 : TemporaryTypeSet* oldType = ins->resultTypeSet();
2907 : TemporaryTypeSet* type;
2908 :
2909 : // Create temporary typeset equal to the type if there is no resultTypeSet.
2910 0 : TemporaryTypeSet tmp;
2911 0 : if (!oldType) {
2912 0 : if (ins->type() == MIRType::Value)
2913 0 : return Ok();
2914 0 : oldType = &tmp;
2915 2972 : tmp.addType(TypeSet::PrimitiveType(ValueTypeFromMIRType(ins->type())), alloc_->lifoAlloc());
2916 : }
2917 :
2918 : // If ins does not have a typeset we return as we cannot optimize.
2919 0 : if (oldType->unknown())
2920 0 : return Ok();
2921 :
2922 : // Decide either to set or remove.
2923 0 : if (trueBranch) {
2924 0 : TemporaryTypeSet remove;
2925 0 : remove.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
2926 0 : remove.addType(TypeSet::NullType(), alloc_->lifoAlloc());
2927 2476 : type = TypeSet::removeSet(oldType, &remove, alloc_->lifoAlloc());
2928 : } else {
2929 0 : TemporaryTypeSet base;
2930 0 : base.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc()); // ToBoolean(undefined) == false
2931 0 : base.addType(TypeSet::NullType(), alloc_->lifoAlloc()); // ToBoolean(null) == false
2932 0 : base.addType(TypeSet::BooleanType(), alloc_->lifoAlloc()); // ToBoolean(false) == false
2933 0 : base.addType(TypeSet::Int32Type(), alloc_->lifoAlloc()); // ToBoolean(0) == false
2934 0 : base.addType(TypeSet::DoubleType(), alloc_->lifoAlloc()); // ToBoolean(0.0) == false
2935 3714 : base.addType(TypeSet::StringType(), alloc_->lifoAlloc()); // ToBoolean("") == false
2936 :
2937 : // If the typeset does emulate undefined, then we cannot filter out
2938 : // objects.
2939 0 : if (oldType->maybeEmulatesUndefined(constraints()))
2940 138 : base.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
2941 :
2942 2476 : type = TypeSet::intersectSets(&base, oldType, alloc_->lifoAlloc());
2943 : }
2944 :
2945 1 : if (!type)
2946 0 : return abort(AbortReason::Alloc);
2947 :
2948 2476 : return replaceTypeSet(ins, type, test);
2949 : }
2950 :
2951 : AbortReasonOr<Ok>
2952 38 : IonBuilder::jsop_dup2()
2953 : {
2954 0 : uint32_t lhsSlot = current->stackDepth() - 2;
2955 0 : uint32_t rhsSlot = current->stackDepth() - 1;
2956 0 : current->pushSlot(lhsSlot);
2957 0 : current->pushSlot(rhsSlot);
2958 38 : return Ok();
2959 : }
2960 :
2961 : AbortReasonOr<Ok>
2962 1975 : IonBuilder::visitTest(CFGTest* test)
2963 : {
2964 1975 : MDefinition* ins = test->mustKeepCondition() ? current->peek(-1) : current->pop();
2965 :
2966 : // Create true and false branches.
2967 : MBasicBlock* ifTrue;
2968 9875 : MOZ_TRY_VAR(ifTrue, newBlock(current, test->trueBranch()->startPc()));
2969 : MBasicBlock* ifFalse;
2970 9875 : MOZ_TRY_VAR(ifFalse, newBlock(current, test->falseBranch()->startPc()));
2971 :
2972 0 : MTest* mir = newTest(ins, ifTrue, ifFalse);
2973 1975 : current->end(mir);
2974 :
2975 : // Filter the types in the true branch.
2976 0 : MOZ_TRY(setCurrentAndSpecializePhis(ifTrue));
2977 5925 : MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ true, mir));
2978 :
2979 1975 : blockWorklist[test->trueBranch()->id()] = ifTrue;
2980 :
2981 : // Filter the types in the false branch.
2982 : // Note: sometimes the false branch is used as merge point. As a result
2983 : // reuse the ifFalse block as a type improvement block and create a new
2984 : // ifFalse which we can use for the merge.
2985 0 : MBasicBlock* filterBlock = ifFalse;
2986 0 : ifFalse = nullptr;
2987 1975 : graph().addBlock(filterBlock);
2988 :
2989 0 : MOZ_TRY(setCurrentAndSpecializePhis(filterBlock));
2990 5925 : MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ false, mir));
2991 :
2992 0 : MOZ_TRY_VAR(ifFalse, newBlock(filterBlock, test->falseBranch()->startPc()));
2993 1975 : filterBlock->end(MGoto::New(alloc(), ifFalse));
2994 :
2995 0 : if (filterBlock->pc() && script()->hasScriptCounts())
2996 1975 : filterBlock->setHitCount(script()->getHitCount(filterBlock->pc()));
2997 :
2998 1975 : blockWorklist[test->falseBranch()->id()] = ifFalse;
2999 :
3000 1975 : current = nullptr;
3001 :
3002 1975 : return Ok();
3003 : }
3004 :
3005 : AbortReasonOr<Ok>
3006 0 : IonBuilder::visitCompare(CFGCompare* compare)
3007 : {
3008 0 : MDefinition* rhs = current->peek(-1);
3009 0 : MDefinition* lhs = current->peek(-2);
3010 :
3011 : // Execute the compare operation.
3012 0 : MOZ_TRY(jsop_compare(JSOP_STRICTEQ));
3013 0 : MInstruction* cmpResult = current->pop()->toInstruction();
3014 0 : MOZ_ASSERT(!cmpResult->isEffectful());
3015 :
3016 : // Put the rhs/lhs again on the stack.
3017 0 : current->push(lhs);
3018 0 : current->push(rhs);
3019 :
3020 : // Create true and false branches.
3021 : MBasicBlock* ifTrue;
3022 0 : MOZ_TRY_VAR(ifTrue, newBlockPopN(current, compare->trueBranch()->startPc(),
3023 : compare->truePopAmount()));
3024 : MBasicBlock* ifFalse;
3025 0 : MOZ_TRY_VAR(ifFalse, newBlockPopN(current, compare->falseBranch()->startPc(),
3026 : compare->falsePopAmount()));
3027 :
3028 0 : blockWorklist[compare->trueBranch()->id()] = ifTrue;
3029 0 : blockWorklist[compare->falseBranch()->id()] = ifFalse;
3030 :
3031 0 : MTest* mir = newTest(cmpResult, ifTrue, ifFalse);
3032 0 : current->end(mir);
3033 :
3034 : // Filter the types in the true branch.
3035 0 : MOZ_TRY(setCurrentAndSpecializePhis(ifTrue));
3036 0 : MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ true, mir));
3037 :
3038 : // Filter the types in the false branch.
3039 0 : MOZ_TRY(setCurrentAndSpecializePhis(ifFalse));
3040 0 : MOZ_TRY(improveTypesAtTest(mir->getOperand(0), /* trueBranch = */ false, mir));
3041 :
3042 0 : current = nullptr;
3043 :
3044 0 : return Ok();
3045 : }
3046 :
3047 : AbortReasonOr<Ok>
3048 38 : IonBuilder::visitTry(CFGTry* try_)
3049 : {
3050 : // We don't support try-finally. The ControlFlowGenerator should have
3051 : // aborted compilation in this case.
3052 :
3053 : // Try-catch within inline frames is not yet supported.
3054 76 : MOZ_ASSERT(!isInlineBuilder());
3055 :
3056 : // Try-catch during the arguments usage analysis is not yet supported. Code
3057 : // accessing the arguments within the 'catch' block is not accounted for.
3058 0 : if (info().analysisMode() == Analysis_ArgumentsUsage)
3059 0 : return abort(AbortReason::Disable, "Try-catch during arguments usage analysis");
3060 :
3061 38 : graph().setHasTryBlock();
3062 :
3063 : MBasicBlock* tryBlock;
3064 228 : MOZ_TRY_VAR(tryBlock, newBlock(current, try_->tryBlock()->startPc()));
3065 :
3066 76 : blockWorklist[try_->tryBlock()->id()] = tryBlock;
3067 :
3068 : // Connect the code after the try-catch to the graph with an MGotoWithFake
3069 : // instruction that always jumps to the try block. This ensures the
3070 : // successor block always has a predecessor.
3071 : MBasicBlock* successor;
3072 190 : MOZ_TRY_VAR(successor, newBlock(current, try_->getSuccessor(1)->startPc()));
3073 :
3074 76 : blockWorklist[try_->afterTryCatchBlock()->id()] = successor;
3075 :
3076 38 : current->end(MGotoWithFake::New(alloc(), tryBlock, successor));
3077 :
3078 : // The baseline compiler should not attempt to enter the catch block
3079 : // via OSR.
3080 42 : MOZ_ASSERT(info().osrPc() < try_->catchStartPc() ||
3081 : info().osrPc() >= try_->afterTryCatchBlock()->startPc());
3082 :
3083 38 : return Ok();
3084 : }
3085 :
3086 : AbortReasonOr<Ok>
3087 972 : IonBuilder::visitReturn(CFGControlInstruction* control)
3088 : {
3089 : MDefinition* def;
3090 972 : switch (control->type()) {
3091 : case CFGControlInstruction::Type_Return:
3092 : // Return the last instruction.
3093 0 : def = current->pop();
3094 901 : break;
3095 :
3096 : case CFGControlInstruction::Type_RetRVal:
3097 : // Return undefined eagerly if script doesn't use return value.
3098 0 : if (script()->noScriptRval()) {
3099 0 : MInstruction* ins = MConstant::New(alloc(), UndefinedValue());
3100 0 : current->add(ins);
3101 0 : def = ins;
3102 0 : break;
3103 : }
3104 :
3105 0 : def = current->getSlot(info().returnValueSlot());
3106 71 : break;
3107 :
3108 : default:
3109 0 : def = nullptr;
3110 0 : MOZ_CRASH("unknown return op");
3111 : }
3112 :
3113 0 : MReturn* ret = MReturn::New(alloc(), def);
3114 972 : current->end(ret);
3115 :
3116 0 : if (!graph().addReturn(current))
3117 0 : return abort(AbortReason::Alloc);
3118 :
3119 : // Make sure no one tries to use this block now.
3120 0 : setCurrent(nullptr);
3121 972 : return Ok();
3122 : }
3123 :
3124 : AbortReasonOr<Ok>
3125 27 : IonBuilder::visitThrow(CFGThrow *cfgIns)
3126 : {
3127 27 : MDefinition* def = current->pop();
3128 :
3129 : // MThrow is not marked as effectful. This means when it throws and we
3130 : // are inside a try block, we could use an earlier resume point and this
3131 : // resume point may not be up-to-date, for example:
3132 : //
3133 : // (function() {
3134 : // try {
3135 : // var x = 1;
3136 : // foo(); // resume point
3137 : // x = 2;
3138 : // throw foo;
3139 : // } catch(e) {
3140 : // print(x);
3141 : // }
3142 : // ])();
3143 : //
3144 : // If we use the resume point after the call, this will print 1 instead
3145 : // of 2. To fix this, we create a resume point right before the MThrow.
3146 : //
3147 : // Note that this is not a problem for instructions other than MThrow
3148 : // because they are either marked as effectful (have their own resume
3149 : // point) or cannot throw a catchable exception.
3150 : //
3151 : // We always install this resume point (instead of only when the function
3152 : // has a try block) in order to handle the Debugger onExceptionUnwind
3153 : // hook. When we need to handle the hook, we bail out to baseline right
3154 : // after the throw and propagate the exception when debug mode is on. This
3155 : // is opposed to the normal behavior of resuming directly in the
3156 : // associated catch block.
3157 0 : MNop* nop = MNop::New(alloc());
3158 27 : current->add(nop);
3159 :
3160 81 : MOZ_TRY(resumeAfter(nop));
3161 :
3162 0 : MThrow* ins = MThrow::New(alloc(), def);
3163 27 : current->end(ins);
3164 :
3165 27 : return Ok();
3166 : }
3167 :
3168 : AbortReasonOr<Ok>
3169 12 : IonBuilder::visitTableSwitch(CFGTableSwitch* cfgIns)
3170 : {
3171 : // Pop input.
3172 12 : MDefinition* ins = current->pop();
3173 :
3174 : // Create MIR instruction
3175 12 : MTableSwitch* tableswitch = MTableSwitch::New(alloc(), ins, cfgIns->low(), cfgIns->high());
3176 :
3177 : #ifdef DEBUG
3178 0 : MOZ_ASSERT(cfgIns->defaultCase() == cfgIns->getSuccessor(0));
3179 0 : for (size_t i = 1; i < cfgIns->numSuccessors(); i++) {
3180 144 : MOZ_ASSERT(cfgIns->getCase(i-1) == cfgIns->getSuccessor(i));
3181 : }
3182 : #endif
3183 :
3184 : // Create the cases
3185 0 : for (size_t i = 0; i < cfgIns->numSuccessors(); i++) {
3186 84 : const CFGBlock* cfgblock = cfgIns->getSuccessor(i);
3187 :
3188 : MBasicBlock* caseBlock;
3189 420 : MOZ_TRY_VAR(caseBlock, newBlock(current, cfgblock->startPc()));
3190 :
3191 84 : blockWorklist[cfgblock->id()] = caseBlock;
3192 :
3193 : size_t index;
3194 0 : if (i == 0) {
3195 1 : if (!tableswitch->addDefault(caseBlock, &index))
3196 0 : return abort(AbortReason::Alloc);
3197 :
3198 : } else {
3199 1 : if (!tableswitch->addSuccessor(caseBlock, &index))
3200 0 : return abort(AbortReason::Alloc);
3201 :
3202 1 : if (!tableswitch->addCase(index))
3203 0 : return abort(AbortReason::Alloc);
3204 :
3205 : // If this is an actual case statement, optimize by replacing the
3206 : // input to the switch case with the actual number of the case.
3207 0 : MConstant* constant = MConstant::New(alloc(), Int32Value(i - 1 + tableswitch->low()));
3208 0 : caseBlock->add(constant);
3209 0 : for (uint32_t j = 0; j < caseBlock->stackDepth(); j++) {
3210 504 : if (ins != caseBlock->getSlot(j))
3211 : continue;
3212 :
3213 0 : constant->setDependency(ins);
3214 0 : caseBlock->setSlot(j, constant);
3215 : }
3216 72 : graph().addBlock(caseBlock);
3217 :
3218 0 : if (caseBlock->pc() && script()->hasScriptCounts())
3219 72 : caseBlock->setHitCount(script()->getHitCount(caseBlock->pc()));
3220 :
3221 : MBasicBlock* merge;
3222 0 : MOZ_TRY_VAR(merge, newBlock(caseBlock, cfgblock->startPc()));
3223 1 : if (!merge)
3224 0 : return abort(AbortReason::Alloc);
3225 :
3226 0 : caseBlock->end(MGoto::New(alloc(), merge));
3227 72 : blockWorklist[cfgblock->id()] = merge;
3228 : }
3229 :
3230 84 : MOZ_ASSERT(index == i);
3231 : }
3232 :
3233 : // Save the MIR instruction as last instruction of this block.
3234 0 : current->end(tableswitch);
3235 12 : return Ok();
3236 : }
3237 :
3238 : void
3239 7647 : IonBuilder::pushConstant(const Value& v)
3240 : {
3241 0 : current->push(constant(v));
3242 7647 : }
3243 :
3244 : AbortReasonOr<Ok>
3245 0 : IonBuilder::bitnotTrySpecialized(bool* emitted, MDefinition* input)
3246 : {
3247 0 : MOZ_ASSERT(*emitted == false);
3248 :
3249 : // Try to emit a specialized bitnot instruction based on the input type
3250 : // of the operand.
3251 :
3252 0 : if (input->mightBeType(MIRType::Object) || input->mightBeType(MIRType::Symbol))
3253 0 : return Ok();
3254 :
3255 0 : MBitNot* ins = MBitNot::New(alloc(), input);
3256 0 : ins->setSpecialization(MIRType::Int32);
3257 :
3258 0 : current->add(ins);
3259 0 : current->push(ins);
3260 :
3261 0 : *emitted = true;
3262 0 : return Ok();
3263 : }
3264 :
3265 : AbortReasonOr<Ok>
3266 0 : IonBuilder::jsop_bitnot()
3267 : {
3268 0 : bool emitted = false;
3269 :
3270 0 : MDefinition* input = current->pop();
3271 :
3272 0 : if (!forceInlineCaches()) {
3273 0 : MOZ_TRY(bitnotTrySpecialized(&emitted, input));
3274 0 : if(emitted)
3275 0 : return Ok();
3276 : }
3277 :
3278 0 : MOZ_TRY(arithTrySharedStub(&emitted, JSOP_BITNOT, nullptr, input));
3279 0 : if (emitted)
3280 0 : return Ok();
3281 :
3282 : // Not possible to optimize. Do a slow vm call.
3283 0 : MBitNot* ins = MBitNot::New(alloc(), input);
3284 :
3285 0 : current->add(ins);
3286 0 : current->push(ins);
3287 0 : MOZ_ASSERT(ins->isEffectful());
3288 : return resumeAfter(ins);
3289 : }
3290 :
3291 : AbortReasonOr<Ok>
3292 60 : IonBuilder::jsop_bitop(JSOp op)
3293 : {
3294 : // Pop inputs.
3295 0 : MDefinition* right = current->pop();
3296 60 : MDefinition* left = current->pop();
3297 :
3298 : MBinaryBitwiseInstruction* ins;
3299 60 : switch (op) {
3300 : case JSOP_BITAND:
3301 0 : ins = MBitAnd::New(alloc(), left, right);
3302 17 : break;
3303 :
3304 : case JSOP_BITOR:
3305 0 : ins = MBitOr::New(alloc(), left, right);
3306 42 : break;
3307 :
3308 : case JSOP_BITXOR:
3309 0 : ins = MBitXor::New(alloc(), left, right);
3310 0 : break;
3311 :
3312 : case JSOP_LSH:
3313 0 : ins = MLsh::New(alloc(), left, right);
3314 0 : break;
3315 :
3316 : case JSOP_RSH:
3317 0 : ins = MRsh::New(alloc(), left, right);
3318 1 : break;
3319 :
3320 : case JSOP_URSH:
3321 0 : ins = MUrsh::New(alloc(), left, right);
3322 0 : break;
3323 :
3324 : default:
3325 0 : MOZ_CRASH("unexpected bitop");
3326 : }
3327 :
3328 0 : current->add(ins);
3329 60 : ins->infer(inspector, pc);
3330 :
3331 0 : current->push(ins);
3332 0 : if (ins->isEffectful())
3333 0 : MOZ_TRY(resumeAfter(ins));
3334 :
3335 60 : return Ok();
3336 : }
3337 :
3338 : MDefinition::Opcode
3339 152 : JSOpToMDefinition(JSOp op)
3340 : {
3341 152 : switch (op) {
3342 : case JSOP_ADD:
3343 : return MDefinition::Opcode::Add;
3344 : case JSOP_SUB:
3345 2 : return MDefinition::Opcode::Sub;
3346 : case JSOP_MUL:
3347 4 : return MDefinition::Opcode::Mul;
3348 : case JSOP_DIV:
3349 0 : return MDefinition::Opcode::Div;
3350 : case JSOP_MOD:
3351 0 : return MDefinition::Opcode::Mod;
3352 : default:
3353 0 : MOZ_CRASH("unexpected binary opcode");
3354 : }
3355 : }
3356 :
3357 : AbortReasonOr<Ok>
3358 341 : IonBuilder::binaryArithTryConcat(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
3359 : {
3360 341 : MOZ_ASSERT(*emitted == false);
3361 :
3362 : // Try to convert an addition into a concat operation if the inputs
3363 : // indicate this might be a concatenation.
3364 :
3365 : // Only try to replace this with concat when we have an addition.
3366 0 : if (op != JSOP_ADD)
3367 12 : return Ok();
3368 :
3369 329 : trackOptimizationAttempt(TrackedStrategy::BinaryArith_Concat);
3370 :
3371 : // Make sure one of the inputs is a string.
3372 0 : if (left->type() != MIRType::String && right->type() != MIRType::String) {
3373 0 : trackOptimizationOutcome(TrackedOutcome::OperandNotString);
3374 161 : return Ok();
3375 : }
3376 :
3377 : // The non-string input (if present) should be atleast easily coercible to string.
3378 0 : if (right->type() != MIRType::String &&
3379 0 : (right->mightBeType(MIRType::Symbol) || right->mightBeType(MIRType::Object) ||
3380 81 : right->mightBeMagicType()))
3381 : {
3382 0 : trackOptimizationOutcome(TrackedOutcome::OperandNotEasilyCoercibleToString);
3383 0 : return Ok();
3384 : }
3385 0 : if (left->type() != MIRType::String &&
3386 0 : (left->mightBeType(MIRType::Symbol) || left->mightBeType(MIRType::Object) ||
3387 0 : left->mightBeMagicType()))
3388 : {
3389 0 : trackOptimizationOutcome(TrackedOutcome::OperandNotEasilyCoercibleToString);
3390 0 : return Ok();
3391 : }
3392 :
3393 0 : MConcat* ins = MConcat::New(alloc(), left, right);
3394 0 : current->add(ins);
3395 168 : current->push(ins);
3396 :
3397 504 : MOZ_TRY(maybeInsertResume());
3398 :
3399 0 : trackOptimizationSuccess();
3400 0 : *emitted = true;
3401 168 : return Ok();
3402 : }
3403 :
3404 : AbortReasonOr<Ok>
3405 0 : IonBuilder::powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* power,
3406 : MIRType outputType)
3407 : {
3408 : // Typechecking.
3409 0 : MDefinition* output = nullptr;
3410 0 : MIRType baseType = base->type();
3411 0 : MIRType powerType = power->type();
3412 :
3413 0 : if (outputType != MIRType::Int32 && outputType != MIRType::Double)
3414 0 : return Ok();
3415 0 : if (!IsNumberType(baseType))
3416 0 : return Ok();
3417 0 : if (!IsNumberType(powerType))
3418 0 : return Ok();
3419 :
3420 0 : if (powerType == MIRType::Float32)
3421 0 : powerType = MIRType::Double;
3422 :
3423 0 : MPow* pow = MPow::New(alloc(), base, power, powerType);
3424 0 : current->add(pow);
3425 0 : output = pow;
3426 :
3427 : // Cast to the right type
3428 0 : if (outputType == MIRType::Int32 && output->type() != MIRType::Int32) {
3429 0 : auto* toInt = MToNumberInt32::New(alloc(), output);
3430 0 : current->add(toInt);
3431 0 : output = toInt;
3432 : }
3433 0 : if (outputType == MIRType::Double && output->type() != MIRType::Double) {
3434 0 : MToDouble* toDouble = MToDouble::New(alloc(), output);
3435 0 : current->add(toDouble);
3436 0 : output = toDouble;
3437 : }
3438 :
3439 0 : current->push(output);
3440 0 : *emitted = true;
3441 0 : return Ok();
3442 : }
3443 :
3444 : static inline bool
3445 326 : SimpleArithOperand(MDefinition* op)
3446 : {
3447 0 : return !op->emptyResultTypeSet()
3448 0 : && !op->mightBeType(MIRType::Object)
3449 0 : && !op->mightBeType(MIRType::String)
3450 0 : && !op->mightBeType(MIRType::Symbol)
3451 0 : && !op->mightBeType(MIRType::MagicOptimizedArguments)
3452 0 : && !op->mightBeType(MIRType::MagicHole)
3453 627 : && !op->mightBeType(MIRType::MagicIsConstructing);
3454 : }
3455 :
3456 : AbortReasonOr<Ok>
3457 173 : IonBuilder::binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
3458 : {
3459 173 : MOZ_ASSERT(*emitted == false);
3460 :
3461 : // Try to emit a specialized binary instruction based on the input types
3462 : // of the operands.
3463 :
3464 173 : trackOptimizationAttempt(TrackedStrategy::BinaryArith_SpecializedTypes);
3465 :
3466 : // Anything complex - strings, symbols, and objects - are not specialized
3467 0 : if (!SimpleArithOperand(left) || !SimpleArithOperand(right)) {
3468 0 : trackOptimizationOutcome(TrackedOutcome::OperandNotSimpleArith);
3469 25 : return Ok();
3470 : }
3471 :
3472 : // One of the inputs need to be a number.
3473 0 : if (!IsNumberType(left->type()) && !IsNumberType(right->type())) {
3474 0 : trackOptimizationOutcome(TrackedOutcome::OperandNotNumber);
3475 0 : return Ok();
3476 : }
3477 :
3478 0 : MDefinition::Opcode defOp = JSOpToMDefinition(op);
3479 0 : MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
3480 148 : ins->setNumberSpecialization(alloc(), inspector, pc);
3481 :
3482 0 : if (op == JSOP_ADD || op == JSOP_MUL)
3483 146 : ins->setCommutative();
3484 :
3485 0 : current->add(ins);
3486 148 : current->push(ins);
3487 :
3488 0 : MOZ_ASSERT(!ins->isEffectful());
3489 444 : MOZ_TRY(maybeInsertResume());
3490 :
3491 0 : trackOptimizationSuccess();
3492 0 : *emitted = true;
3493 148 : return Ok();
3494 : }
3495 :
3496 : AbortReasonOr<Ok>
3497 25 : IonBuilder::binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
3498 : MDefinition* left, MDefinition* right)
3499 : {
3500 25 : MOZ_ASSERT(*emitted == false);
3501 :
3502 : // Try to emit a specialized binary instruction speculating the
3503 : // type using the baseline caches.
3504 :
3505 25 : trackOptimizationAttempt(TrackedStrategy::BinaryArith_SpecializedOnBaselineTypes);
3506 :
3507 0 : MIRType specialization = inspector->expectedBinaryArithSpecialization(pc);
3508 0 : if (specialization == MIRType::None) {
3509 0 : trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
3510 25 : return Ok();
3511 : }
3512 :
3513 0 : MDefinition::Opcode def_op = JSOpToMDefinition(op);
3514 0 : MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
3515 0 : ins->setSpecialization(specialization);
3516 :
3517 0 : current->add(ins);
3518 0 : current->push(ins);
3519 :
3520 0 : MOZ_ASSERT(!ins->isEffectful());
3521 0 : MOZ_TRY(maybeInsertResume());
3522 :
3523 0 : trackOptimizationSuccess();
3524 0 : *emitted = true;
3525 0 : return Ok();
3526 : }
3527 :
3528 : AbortReasonOr<Ok>
3529 25 : IonBuilder::arithTrySharedStub(bool* emitted, JSOp op,
3530 : MDefinition* left, MDefinition* right)
3531 : {
3532 0 : MOZ_ASSERT(*emitted == false);
3533 25 : JSOp actualOp = JSOp(*pc);
3534 :
3535 : // Try to emit a shared stub cache.
3536 :
3537 1 : if (JitOptions.disableSharedStubs)
3538 0 : return Ok();
3539 :
3540 : // The actual jsop 'jsop_pos' is not supported yet.
3541 0 : if (actualOp == JSOP_POS)
3542 4 : return Ok();
3543 :
3544 :
3545 0 : MInstruction* stub = nullptr;
3546 21 : switch (actualOp) {
3547 : case JSOP_NEG:
3548 : case JSOP_BITNOT:
3549 0 : MOZ_ASSERT_IF(op == JSOP_MUL,
3550 : left->maybeConstantValue() && left->maybeConstantValue()->toInt32() == -1);
3551 0 : MOZ_ASSERT_IF(op != JSOP_MUL, !left);
3552 0 : stub = MUnaryCache::New(alloc(), right);
3553 0 : break;
3554 : case JSOP_ADD:
3555 : case JSOP_SUB:
3556 : case JSOP_MUL:
3557 : case JSOP_DIV:
3558 : case JSOP_MOD:
3559 : case JSOP_POW:
3560 0 : stub = MBinarySharedStub::New(alloc(), left, right);
3561 21 : break;
3562 : default:
3563 0 : MOZ_CRASH("unsupported arith");
3564 : }
3565 :
3566 0 : current->add(stub);
3567 21 : current->push(stub);
3568 :
3569 : // Decrease type from 'any type' to 'empty type' when one of the operands
3570 : // is 'empty typed'.
3571 21 : maybeMarkEmpty(stub);
3572 :
3573 63 : MOZ_TRY(resumeAfter(stub));
3574 :
3575 0 : *emitted = true;
3576 21 : return Ok();
3577 : }
3578 :
3579 : AbortReasonOr<Ok>
3580 341 : IonBuilder::jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right)
3581 : {
3582 341 : bool emitted = false;
3583 :
3584 341 : startTrackingOptimizations();
3585 :
3586 0 : trackTypeInfo(TrackedTypeSite::Operand, left->type(), left->resultTypeSet());
3587 682 : trackTypeInfo(TrackedTypeSite::Operand, right->type(), right->resultTypeSet());
3588 :
3589 0 : if (!forceInlineCaches()) {
3590 0 : MOZ_TRY(binaryArithTryConcat(&emitted, op, left, right));
3591 0 : if (emitted)
3592 168 : return Ok();
3593 :
3594 0 : MOZ_TRY(binaryArithTrySpecialized(&emitted, op, left, right));
3595 0 : if (emitted)
3596 148 : return Ok();
3597 :
3598 0 : MOZ_TRY(binaryArithTrySpecializedOnBaselineInspector(&emitted, op, left, right));
3599 0 : if (emitted)
3600 0 : return Ok();
3601 : }
3602 :
3603 0 : MOZ_TRY(arithTrySharedStub(&emitted, op, left, right));
3604 0 : if (emitted)
3605 21 : return Ok();
3606 :
3607 : // Not possible to optimize. Do a slow vm call.
3608 0 : trackOptimizationAttempt(TrackedStrategy::BinaryArith_Call);
3609 4 : trackOptimizationSuccess();
3610 :
3611 0 : MDefinition::Opcode def_op = JSOpToMDefinition(op);
3612 4 : MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
3613 :
3614 : // Decrease type from 'any type' to 'empty type' when one of the operands
3615 : // is 'empty typed'.
3616 4 : maybeMarkEmpty(ins);
3617 :
3618 0 : current->add(ins);
3619 0 : current->push(ins);
3620 8 : MOZ_ASSERT(ins->isEffectful());
3621 : return resumeAfter(ins);
3622 : }
3623 :
3624 : AbortReasonOr<Ok>
3625 337 : IonBuilder::jsop_binary_arith(JSOp op)
3626 : {
3627 0 : MDefinition* right = current->pop();
3628 337 : MDefinition* left = current->pop();
3629 :
3630 337 : return jsop_binary_arith(op, left, right);
3631 : }
3632 :
3633 :
3634 : AbortReasonOr<Ok>
3635 0 : IonBuilder::jsop_pow()
3636 : {
3637 0 : MDefinition* exponent = current->pop();
3638 0 : MDefinition* base = current->pop();
3639 :
3640 0 : bool emitted = false;
3641 :
3642 0 : if (!forceInlineCaches()) {
3643 0 : MOZ_TRY(powTrySpecialized(&emitted, base, exponent, MIRType::Double));
3644 0 : if (emitted)
3645 0 : return Ok();
3646 : }
3647 :
3648 0 : MOZ_TRY(arithTrySharedStub(&emitted, JSOP_POW, base, exponent));
3649 0 : if (emitted)
3650 0 : return Ok();
3651 :
3652 : // For now, use MIRType::None as a safe cover-all. See bug 1188079.
3653 0 : MPow* pow = MPow::New(alloc(), base, exponent, MIRType::None);
3654 0 : current->add(pow);
3655 0 : current->push(pow);
3656 0 : MOZ_ASSERT(pow->isEffectful());
3657 : return resumeAfter(pow);
3658 : }
3659 :
3660 : AbortReasonOr<Ok>
3661 86 : IonBuilder::jsop_pos()
3662 : {
3663 172 : if (IsNumberType(current->peek(-1)->type())) {
3664 : // Already int32 or double. Set the operand as implicitly used so it
3665 : // doesn't get optimized out if it has no other uses, as we could bail
3666 : // out.
3667 0 : current->peek(-1)->setImplicitlyUsedUnchecked();
3668 82 : return Ok();
3669 : }
3670 :
3671 : // Compile +x as x * 1.
3672 0 : MDefinition* value = current->pop();
3673 0 : MConstant* one = MConstant::New(alloc(), Int32Value(1));
3674 4 : current->add(one);
3675 :
3676 4 : return jsop_binary_arith(JSOP_MUL, value, one);
3677 : }
3678 :
3679 : AbortReasonOr<Ok>
3680 0 : IonBuilder::jsop_neg()
3681 : {
3682 : // Since JSOP_NEG does not use a slot, we cannot push the MConstant.
3683 : // The MConstant is therefore passed to JSOP_MUL without slot traffic.
3684 0 : MConstant* negator = MConstant::New(alloc(), Int32Value(-1));
3685 0 : current->add(negator);
3686 :
3687 0 : MDefinition* right = current->pop();
3688 :
3689 0 : return jsop_binary_arith(JSOP_MUL, negator, right);
3690 : }
3691 :
3692 : AbortReasonOr<Ok>
3693 32 : IonBuilder::jsop_tostring()
3694 : {
3695 0 : if (current->peek(-1)->type() == MIRType::String)
3696 14 : return Ok();
3697 :
3698 0 : MDefinition* value = current->pop();
3699 0 : MToString* ins = MToString::New(alloc(), value);
3700 0 : current->add(ins);
3701 0 : current->push(ins);
3702 0 : MOZ_ASSERT(!ins->isEffectful());
3703 18 : return Ok();
3704 : }
3705 :
3706 : class AutoAccumulateReturns
3707 : {
3708 : MIRGraph& graph_;
3709 : MIRGraphReturns* prev_;
3710 :
3711 : public:
3712 : AutoAccumulateReturns(MIRGraph& graph, MIRGraphReturns& returns)
3713 300 : : graph_(graph)
3714 : {
3715 0 : prev_ = graph_.returnAccumulator();
3716 600 : graph_.setReturnAccumulator(&returns);
3717 : }
3718 : ~AutoAccumulateReturns() {
3719 600 : graph_.setReturnAccumulator(prev_);
3720 : }
3721 : };
3722 :
3723 : IonBuilder::InliningResult
3724 300 : IonBuilder::inlineScriptedCall(CallInfo& callInfo, JSFunction* target)
3725 : {
3726 0 : MOZ_ASSERT(target->hasScript());
3727 300 : MOZ_ASSERT(IsIonInlinablePC(pc));
3728 :
3729 0 : MBasicBlock::BackupPoint backup(current);
3730 1 : if (!backup.init(alloc()))
3731 0 : return abort(AbortReason::Alloc);
3732 :
3733 300 : callInfo.setImplicitlyUsedUnchecked();
3734 :
3735 : // Create new |this| on the caller-side for inlined constructors.
3736 0 : if (callInfo.constructing()) {
3737 0 : MDefinition* thisDefn = createThis(target, callInfo.fun(), callInfo.getNewTarget());
3738 1 : if (!thisDefn)
3739 0 : return abort(AbortReason::Alloc);
3740 4 : callInfo.setThis(thisDefn);
3741 : }
3742 :
3743 : // Capture formals in the outer resume point.
3744 900 : MOZ_TRY(callInfo.pushCallStack(this, current));
3745 :
3746 : MResumePoint* outerResumePoint =
3747 0 : MResumePoint::New(alloc(), current, pc, MResumePoint::Outer);
3748 1 : if (!outerResumePoint)
3749 0 : return abort(AbortReason::Alloc);
3750 300 : current->setOuterResumePoint(outerResumePoint);
3751 :
3752 : // Pop formals again, except leave |fun| on stack for duration of call.
3753 0 : callInfo.popCallStack(current);
3754 300 : current->push(callInfo.fun());
3755 :
3756 0 : JSScript* calleeScript = target->nonLazyScript();
3757 300 : BaselineInspector inspector(calleeScript);
3758 :
3759 : // Improve type information of |this| when not set.
3760 0 : if (callInfo.constructing() &&
3761 4 : !callInfo.thisArg()->resultTypeSet())
3762 : {
3763 0 : StackTypeSet* types = TypeScript::ThisTypes(calleeScript);
3764 0 : if (types && !types->unknown()) {
3765 0 : TemporaryTypeSet* clonedTypes = types->clone(alloc_->lifoAlloc());
3766 0 : if (!clonedTypes)
3767 0 : return abort(AbortReason::Alloc);
3768 0 : MTypeBarrier* barrier = MTypeBarrier::New(alloc(), callInfo.thisArg(), clonedTypes);
3769 0 : current->add(barrier);
3770 0 : if (barrier->type() == MIRType::Undefined)
3771 0 : callInfo.setThis(constant(UndefinedValue()));
3772 0 : else if (barrier->type() == MIRType::Null)
3773 0 : callInfo.setThis(constant(NullValue()));
3774 : else
3775 0 : callInfo.setThis(barrier);
3776 : }
3777 : }
3778 :
3779 : // Start inlining.
3780 600 : LifoAlloc* lifoAlloc = alloc_->lifoAlloc();
3781 : InlineScriptTree* inlineScriptTree =
3782 0 : info().inlineScriptTree()->addCallee(alloc_, pc, calleeScript);
3783 0 : if (!inlineScriptTree)
3784 0 : return abort(AbortReason::Alloc);
3785 1200 : CompileInfo* info = lifoAlloc->new_<CompileInfo>(runtime, calleeScript, target,
3786 : (jsbytecode*)nullptr,
3787 600 : this->info().analysisMode(),
3788 : /* needsArgsObj = */ false,
3789 0 : inlineScriptTree);
3790 1 : if (!info)
3791 0 : return abort(AbortReason::Alloc);
3792 :
3793 0 : MIRGraphReturns returns(alloc());
3794 900 : AutoAccumulateReturns aar(graph(), returns);
3795 :
3796 : // Build the graph.
3797 0 : IonBuilder inlineBuilder(analysisContext, realm, options, &alloc(), &graph(), constraints(),
3798 0 : &inspector, info, &optimizationInfo(), nullptr, inliningDepth_ + 1,
3799 0 : loopDepth_);
3800 0 : AbortReasonOr<Ok> result = inlineBuilder.buildInline(this, outerResumePoint, callInfo);
3801 0 : if (result.isErr()) {
3802 0 : if (analysisContext && analysisContext->isExceptionPending()) {
3803 0 : JitSpew(JitSpew_IonAbort, "Inline builder raised exception.");
3804 0 : MOZ_ASSERT(result.unwrapErr() == AbortReason::Error);
3805 0 : return Err(result.unwrapErr());
3806 : }
3807 :
3808 : // Inlining the callee failed. Mark the callee as uninlineable only if
3809 : // the inlining was aborted for a non-exception reason.
3810 0 : switch (result.unwrapErr()) {
3811 : case AbortReason::Disable:
3812 0 : calleeScript->setUninlineable();
3813 0 : if (!JitOptions.disableInlineBacktracking) {
3814 0 : current = backup.restore();
3815 0 : if (!current)
3816 0 : return abort(AbortReason::Alloc);
3817 0 : return InliningStatus_NotInlined;
3818 : }
3819 0 : return abort(AbortReason::Inlining);
3820 :
3821 : case AbortReason::PreliminaryObjects: {
3822 0 : const ObjectGroupVector& groups = inlineBuilder.abortedPreliminaryGroups();
3823 0 : MOZ_ASSERT(!groups.empty());
3824 0 : for (size_t i = 0; i < groups.length(); i++)
3825 0 : addAbortedPreliminaryGroup(groups[i]);
3826 0 : return Err(result.unwrapErr());
3827 : }
3828 :
3829 : case AbortReason::Alloc:
3830 : case AbortReason::Inlining:
3831 : case AbortReason::Error:
3832 0 : return Err(result.unwrapErr());
3833 :
3834 : case AbortReason::NoAbort:
3835 0 : MOZ_CRASH("Abort with AbortReason::NoAbort");
3836 : return abort(AbortReason::Error);
3837 : }
3838 : }
3839 :
3840 300 : if (returns.empty()) {
3841 : // Inlining of functions that have no exit is not supported.
3842 0 : calleeScript->setUninlineable();
3843 0 : if (!JitOptions.disableInlineBacktracking) {
3844 0 : current = backup.restore();
3845 0 : if (!current)
3846 0 : return abort(AbortReason::Alloc);
3847 0 : return InliningStatus_NotInlined;
3848 : }
3849 0 : return abort(AbortReason::Inlining);
3850 : }
3851 :
3852 : // Create return block.
3853 600 : jsbytecode* postCall = GetNextPc(pc);
3854 : MBasicBlock* returnBlock;
3855 0 : MOZ_TRY_VAR(returnBlock, newBlock(current->stackDepth(), postCall));
3856 0 : graph().addBlock(returnBlock);
3857 600 : returnBlock->setCallerResumePoint(callerResumePoint_);
3858 :
3859 : // Inherit the slots from current and pop |fun|.
3860 0 : returnBlock->inheritSlots(current);
3861 300 : returnBlock->pop();
3862 :
3863 : // Accumulate return values.
3864 0 : MDefinition* retvalDefn = patchInlinedReturns(callInfo, returns, returnBlock);
3865 0 : if (!retvalDefn)
3866 0 : return abort(AbortReason::Alloc);
3867 300 : returnBlock->push(retvalDefn);
3868 :
3869 : // Initialize entry slots now that the stack has been fixed up.
3870 0 : if (!returnBlock->initEntrySlots(alloc()))
3871 0 : return abort(AbortReason::Alloc);
3872 :
3873 900 : MOZ_TRY(setCurrentAndSpecializePhis(returnBlock));
3874 :
3875 300 : return InliningStatus_Inlined;
3876 : }
3877 :
3878 : MDefinition*
3879 685 : IonBuilder::patchInlinedReturn(CallInfo& callInfo, MBasicBlock* exit, MBasicBlock* bottom)
3880 : {
3881 : // Replaces the MReturn in the exit block with an MGoto.
3882 0 : MDefinition* rdef = exit->lastIns()->toReturn()->input();
3883 685 : exit->discardLastIns();
3884 :
3885 : // Constructors must be patched by the caller to always return an object.
3886 0 : if (callInfo.constructing()) {
3887 4 : if (rdef->type() == MIRType::Value) {
3888 : // Unknown return: dynamically detect objects.
3889 0 : MReturnFromCtor* filter = MReturnFromCtor::New(alloc(), rdef, callInfo.thisArg());
3890 0 : exit->add(filter);
3891 0 : rdef = filter;
3892 4 : } else if (rdef->type() != MIRType::Object) {
3893 : // Known non-object return: force |this|.
3894 4 : rdef = callInfo.thisArg();
3895 : }
3896 681 : } else if (callInfo.isSetter()) {
3897 : // Setters return their argument, not whatever value is returned.
3898 0 : rdef = callInfo.getArg(0);
3899 : }
3900 :
3901 0 : if (!callInfo.isSetter())
3902 685 : rdef = specializeInlinedReturn(rdef, exit);
3903 :
3904 0 : MGoto* replacement = MGoto::New(alloc(), bottom);
3905 0 : exit->end(replacement);
3906 685 : if (!bottom->addPredecessorWithoutPhis(exit))
3907 : return nullptr;
3908 :
3909 685 : return rdef;
3910 : }
3911 :
3912 : MDefinition*
3913 685 : IonBuilder::specializeInlinedReturn(MDefinition* rdef, MBasicBlock* exit)
3914 : {
3915 : // Remove types from the return definition that weren't observed.
3916 1370 : TemporaryTypeSet* types = bytecodeTypes(pc);
3917 :
3918 : // The observed typeset doesn't contain extra information.
3919 2055 : if (types->empty() || types->unknown())
3920 : return rdef;
3921 :
3922 : // Decide if specializing is needed using the result typeset if available,
3923 : // else use the result type.
3924 :
3925 685 : if (rdef->resultTypeSet()) {
3926 : // Don't specialize if return typeset is a subset of the
3927 : // observed typeset. The return typeset is already more specific.
3928 573 : if (rdef->resultTypeSet()->isSubset(types))
3929 : return rdef;
3930 : } else {
3931 112 : MIRType observedType = types->getKnownMIRType();
3932 :
3933 : // Don't specialize if type is MIRType::Float32 and TI reports
3934 : // MIRType::Double. Float is more specific than double.
3935 112 : if (observedType == MIRType::Double && rdef->type() == MIRType::Float32)
3936 : return rdef;
3937 :
3938 : // Don't specialize if types are inaccordance, except for MIRType::Value
3939 : // and MIRType::Object (when not unknown object), since the typeset
3940 : // contains more specific information.
3941 0 : if (observedType == rdef->type() &&
3942 0 : observedType != MIRType::Value &&
3943 14 : (observedType != MIRType::Object || types->unknownObject()))
3944 : {
3945 : return rdef;
3946 : }
3947 : }
3948 :
3949 96 : setCurrent(exit);
3950 :
3951 0 : MTypeBarrier* barrier = nullptr;
3952 0 : rdef = addTypeBarrier(rdef, types, BarrierKind::TypeSet, &barrier);
3953 0 : if (barrier)
3954 48 : barrier->setNotMovable();
3955 :
3956 : return rdef;
3957 : }
3958 :
3959 : MDefinition*
3960 300 : IonBuilder::patchInlinedReturns(CallInfo& callInfo, MIRGraphReturns& returns, MBasicBlock* bottom)
3961 : {
3962 : // Replaces MReturns with MGotos, returning the MDefinition
3963 : // representing the return value, or nullptr.
3964 300 : MOZ_ASSERT(returns.length() > 0);
3965 :
3966 0 : if (returns.length() == 1)
3967 185 : return patchInlinedReturn(callInfo, returns[0], bottom);
3968 :
3969 : // Accumulate multiple returns with a phi.
3970 0 : MPhi* phi = MPhi::New(alloc());
3971 230 : if (!phi->reserveLength(returns.length()))
3972 : return nullptr;
3973 :
3974 0 : for (size_t i = 0; i < returns.length(); i++) {
3975 0 : MDefinition* rdef = patchInlinedReturn(callInfo, returns[i], bottom);
3976 500 : if (!rdef)
3977 : return nullptr;
3978 500 : phi->addInput(rdef);
3979 : }
3980 :
3981 0 : bottom->addPhi(phi);
3982 115 : return phi;
3983 : }
3984 :
3985 : IonBuilder::InliningDecision
3986 1645 : IonBuilder::makeInliningDecision(JSObject* targetArg, CallInfo& callInfo)
3987 : {
3988 : // When there is no target, inlining is impossible.
3989 1645 : if (targetArg == nullptr) {
3990 : trackOptimizationOutcome(TrackedOutcome::CantInlineNoTarget);
3991 : return InliningDecision_DontInline;
3992 : }
3993 :
3994 : // Inlining non-function targets is handled by inlineNonFunctionCall().
3995 1645 : if (!targetArg->is<JSFunction>())
3996 : return InliningDecision_Inline;
3997 :
3998 1645 : JSFunction* target = &targetArg->as<JSFunction>();
3999 :
4000 : // Never inline during the arguments usage analysis.
4001 1645 : if (info().analysisMode() == Analysis_ArgumentsUsage)
4002 : return InliningDecision_DontInline;
4003 :
4004 : // Native functions provide their own detection in inlineNativeCall().
4005 1571 : if (target->isNative())
4006 : return InliningDecision_Inline;
4007 :
4008 : // Determine whether inlining is possible at callee site
4009 0 : InliningDecision decision = canInlineTarget(target, callInfo);
4010 348 : if (decision != InliningDecision_Inline)
4011 : return decision;
4012 :
4013 : // Heuristics!
4014 321 : JSScript* targetScript = target->nonLazyScript();
4015 :
4016 : // Callee must not be excessively large.
4017 : // This heuristic also applies to the callsite as a whole.
4018 0 : bool offThread = options.offThreadCompilationAvailable();
4019 0 : if (targetScript->length() > optimizationInfo().inlineMaxBytecodePerCallSite(offThread)) {
4020 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineBigCallee);
4021 0 : return DontInline(targetScript, "Vetoed: callee excessively large");
4022 : }
4023 :
4024 : // Callee must have been called a few times to have somewhat stable
4025 : // type information, except for definite properties analysis,
4026 : // as the caller has not run yet.
4027 0 : if (targetScript->getWarmUpCount() < optimizationInfo().inliningWarmUpThreshold() &&
4028 0 : !targetScript->baselineScript()->ionCompiledOrInlined() &&
4029 29 : info().analysisMode() != Analysis_DefiniteProperties)
4030 : {
4031 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNotHot);
4032 0 : JitSpew(JitSpew_Inlining, "Cannot inline %s:%u: callee is insufficiently hot.",
4033 0 : targetScript->filename(), targetScript->lineno());
4034 19 : return InliningDecision_WarmUpCountTooLow;
4035 : }
4036 :
4037 : // Don't inline if the callee is known to inline a lot of code, to avoid
4038 : // huge MIR graphs.
4039 0 : uint32_t inlinedBytecodeLength = targetScript->baselineScript()->inlinedBytecodeLength();
4040 0 : if (inlinedBytecodeLength > optimizationInfo().inlineMaxCalleeInlinedBytecodeLength()) {
4041 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineBigCalleeInlinedBytecodeLength);
4042 0 : return DontInline(targetScript, "Vetoed: callee inlinedBytecodeLength is too big");
4043 : }
4044 :
4045 302 : IonBuilder* outerBuilder = outermostBuilder();
4046 :
4047 : // Cap the total bytecode length we inline under a single script, to avoid
4048 : // excessive inlining in pathological cases.
4049 0 : size_t totalBytecodeLength = outerBuilder->inlinedBytecodeLength_ + targetScript->length();
4050 0 : if (totalBytecodeLength > optimizationInfo().inlineMaxTotalBytecodeLength()) {
4051 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineExceededTotalBytecodeLength);
4052 0 : return DontInline(targetScript, "Vetoed: exceeding max total bytecode length");
4053 : }
4054 :
4055 : // Cap the inlining depth.
4056 :
4057 : uint32_t maxInlineDepth;
4058 0 : if (JitOptions.isSmallFunction(targetScript)) {
4059 168 : maxInlineDepth = optimizationInfo().smallFunctionMaxInlineDepth();
4060 : } else {
4061 134 : maxInlineDepth = optimizationInfo().maxInlineDepth();
4062 :
4063 : // Caller must not be excessively large.
4064 0 : if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength()) {
4065 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineBigCaller);
4066 0 : return DontInline(targetScript, "Vetoed: caller excessively large");
4067 : }
4068 : }
4069 :
4070 0 : BaselineScript* outerBaseline = outermostBuilder()->script()->baselineScript();
4071 302 : if (inliningDepth_ >= maxInlineDepth) {
4072 : // We hit the depth limit and won't inline this function. Give the
4073 : // outermost script a max inlining depth of 0, so that it won't be
4074 : // inlined in other scripts. This heuristic is currently only used
4075 : // when we're inlining scripts with loops, see the comment below.
4076 0 : outerBaseline->setMaxInliningDepth(0);
4077 :
4078 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth);
4079 0 : return DontInline(targetScript, "Vetoed: exceeding allowed inline depth");
4080 : }
4081 :
4082 : // Inlining functions with loops can be complicated. For instance, if we're
4083 : // close to the inlining depth limit and we inline the function f below, we
4084 : // can no longer inline the call to g:
4085 : //
4086 : // function f() {
4087 : // while (cond) {
4088 : // g();
4089 : // }
4090 : // }
4091 : //
4092 : // If the loop has many iterations, it's more efficient to call f and inline
4093 : // g in f.
4094 : //
4095 : // To avoid this problem, we record a separate max inlining depth for each
4096 : // script, indicating at which depth we won't be able to inline all functions
4097 : // we inlined this time. This solves the issue above, because we will only
4098 : // inline f if it means we can also inline g.
4099 0 : if (targetScript->hasLoops() &&
4100 96 : inliningDepth_ >= targetScript->baselineScript()->maxInliningDepth())
4101 : {
4102 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineExceededDepth);
4103 0 : return DontInline(targetScript, "Vetoed: exceeding allowed script inline depth");
4104 : }
4105 :
4106 : // Update the max depth at which we can inline the outer script.
4107 0 : MOZ_ASSERT(maxInlineDepth > inliningDepth_);
4108 0 : uint32_t scriptInlineDepth = maxInlineDepth - inliningDepth_ - 1;
4109 0 : if (scriptInlineDepth < outerBaseline->maxInliningDepth())
4110 79 : outerBaseline->setMaxInliningDepth(scriptInlineDepth);
4111 :
4112 : // End of heuristics, we will inline this function.
4113 :
4114 302 : outerBuilder->inlinedBytecodeLength_ += targetScript->length();
4115 :
4116 302 : return InliningDecision_Inline;
4117 : }
4118 :
4119 : AbortReasonOr<Ok>
4120 11 : IonBuilder::selectInliningTargets(const InliningTargets& targets, CallInfo& callInfo,
4121 : BoolVector& choiceSet, uint32_t* numInlineable)
4122 : {
4123 0 : *numInlineable = 0;
4124 11 : uint32_t totalSize = 0;
4125 :
4126 : // For each target, ask whether it may be inlined.
4127 1 : if (!choiceSet.reserve(targets.length()))
4128 0 : return abort(AbortReason::Alloc);
4129 :
4130 : // Don't inline polymorphic sites during the definite properties analysis.
4131 : // AddClearDefiniteFunctionUsesInScript depends on this for correctness.
4132 0 : if (info().analysisMode() == Analysis_DefiniteProperties && targets.length() > 1)
4133 0 : return Ok();
4134 :
4135 0 : for (size_t i = 0; i < targets.length(); i++) {
4136 27 : JSObject* target = targets[i].target;
4137 :
4138 0 : trackOptimizationAttempt(TrackedStrategy::Call_Inline);
4139 27 : trackTypeInfo(TrackedTypeSite::Call_Target, target);
4140 :
4141 : bool inlineable;
4142 0 : InliningDecision decision = makeInliningDecision(target, callInfo);
4143 27 : switch (decision) {
4144 : case InliningDecision_Error:
4145 0 : return abort(AbortReason::Error);
4146 : case InliningDecision_DontInline:
4147 : case InliningDecision_WarmUpCountTooLow:
4148 0 : inlineable = false;
4149 3 : break;
4150 : case InliningDecision_Inline:
4151 0 : inlineable = true;
4152 24 : break;
4153 : default:
4154 0 : MOZ_CRASH("Unhandled InliningDecision value!");
4155 : }
4156 :
4157 27 : if (target->is<JSFunction>()) {
4158 : // Enforce a maximum inlined bytecode limit at the callsite.
4159 0 : if (inlineable && target->as<JSFunction>().isInterpreted()) {
4160 0 : totalSize += target->as<JSFunction>().nonLazyScript()->length();
4161 0 : bool offThread = options.offThreadCompilationAvailable();
4162 0 : if (totalSize > optimizationInfo().inlineMaxBytecodePerCallSite(offThread))
4163 2 : inlineable = false;
4164 : }
4165 : } else {
4166 : // Non-function targets are not supported by polymorphic inlining.
4167 0 : inlineable = false;
4168 : }
4169 :
4170 0 : choiceSet.infallibleAppend(inlineable);
4171 0 : if (inlineable)
4172 22 : *numInlineable += 1;
4173 : }
4174 :
4175 : // If optimization tracking is turned on and one of the inlineable targets
4176 : // is a native, track the type info of the call. Most native inlinings
4177 : // depend on the types of the arguments and the return value.
4178 1 : if (isOptimizationTrackingEnabled()) {
4179 0 : for (size_t i = 0; i < targets.length(); i++) {
4180 0 : if (choiceSet[i] && targets[i].target->as<JSFunction>().isNative()) {
4181 : trackTypeInfo(callInfo);
4182 : break;
4183 : }
4184 : }
4185 : }
4186 :
4187 0 : MOZ_ASSERT(choiceSet.length() == targets.length());
4188 11 : return Ok();
4189 : }
4190 :
4191 : static bool
4192 0 : CanInlineGetPropertyCache(MGetPropertyCache* cache, MDefinition* thisDef)
4193 : {
4194 0 : if (cache->value()->type() != MIRType::Object)
4195 : return false;
4196 :
4197 0 : if (cache->value() != thisDef)
4198 : return false;
4199 :
4200 0 : InlinePropertyTable* table = cache->propTable();
4201 0 : if (!table)
4202 : return false;
4203 0 : if (table->numEntries() == 0)
4204 : return false;
4205 0 : return true;
4206 : }
4207 :
4208 : class WrapMGetPropertyCache
4209 : {
4210 : MGetPropertyCache* cache_;
4211 :
4212 : private:
4213 0 : void discardPriorResumePoint() {
4214 3357 : if (!cache_)
4215 : return;
4216 :
4217 0 : InlinePropertyTable* propTable = cache_->propTable();
4218 20 : if (!propTable)
4219 : return;
4220 0 : MResumePoint* rp = propTable->takePriorResumePoint();
4221 0 : if (!rp)
4222 : return;
4223 0 : cache_->block()->discardPreAllocatedResumePoint(rp);
4224 : }
4225 :
4226 : public:
4227 : explicit WrapMGetPropertyCache(MGetPropertyCache* cache)
4228 3357 : : cache_(cache)
4229 : { }
4230 :
4231 : ~WrapMGetPropertyCache() {
4232 3357 : discardPriorResumePoint();
4233 : }
4234 :
4235 : MGetPropertyCache* get() {
4236 11 : return cache_;
4237 : }
4238 : MGetPropertyCache* operator->() {
4239 : return get();
4240 : }
4241 :
4242 : // This function returns the cache given to the constructor if the
4243 : // GetPropertyCache can be moved into the ObjectGroup fallback path.
4244 20 : MGetPropertyCache* moveableCache(bool hasTypeBarrier, MDefinition* thisDef) {
4245 : // If we have unhandled uses of the MGetPropertyCache, then we cannot
4246 : // move it to the ObjectGroup fallback path.
4247 0 : if (!hasTypeBarrier) {
4248 8 : if (cache_->hasUses())
4249 : return nullptr;
4250 : } else {
4251 : // There is the TypeBarrier consumer, so we check that this is the
4252 : // only consumer.
4253 0 : MOZ_ASSERT(cache_->hasUses());
4254 16 : if (!cache_->hasOneUse())
4255 : return nullptr;
4256 : }
4257 :
4258 : // If the this-object is not identical to the object of the
4259 : // MGetPropertyCache, then we cannot use the InlinePropertyTable, or if
4260 : // we do not yet have enough information from the ObjectGroup.
4261 0 : if (!CanInlineGetPropertyCache(cache_, thisDef))
4262 : return nullptr;
4263 :
4264 0 : MGetPropertyCache* ret = cache_;
4265 0 : cache_ = nullptr;
4266 0 : return ret;
4267 : }
4268 : };
4269 :
4270 : MGetPropertyCache*
4271 1629 : IonBuilder::getInlineableGetPropertyCache(CallInfo& callInfo)
4272 : {
4273 1629 : if (callInfo.constructing())
4274 : return nullptr;
4275 :
4276 0 : MDefinition* thisDef = callInfo.thisArg();
4277 1609 : if (thisDef->type() != MIRType::Object)
4278 : return nullptr;
4279 :
4280 0 : MDefinition* funcDef = callInfo.fun();
4281 237 : if (funcDef->type() != MIRType::Object)
4282 : return nullptr;
4283 :
4284 : // MGetPropertyCache with no uses may be optimized away.
4285 0 : if (funcDef->isGetPropertyCache()) {
4286 0 : WrapMGetPropertyCache cache(funcDef->toGetPropertyCache());
4287 4 : return cache.moveableCache(/* hasTypeBarrier = */ false, thisDef);
4288 : }
4289 :
4290 : // Optimize away the following common pattern:
4291 : // MTypeBarrier[MIRType::Object] <- MGetPropertyCache
4292 0 : if (funcDef->isTypeBarrier()) {
4293 0 : MTypeBarrier* barrier = funcDef->toTypeBarrier();
4294 64 : if (barrier->hasUses())
4295 : return nullptr;
4296 31 : if (barrier->type() != MIRType::Object)
4297 : return nullptr;
4298 93 : if (!barrier->input()->isGetPropertyCache())
4299 : return nullptr;
4300 :
4301 0 : WrapMGetPropertyCache cache(barrier->input()->toGetPropertyCache());
4302 16 : return cache.moveableCache(/* hasTypeBarrier = */ true, thisDef);
4303 : }
4304 :
4305 : return nullptr;
4306 : }
4307 :
4308 : IonBuilder::InliningResult
4309 1523 : IonBuilder::inlineSingleCall(CallInfo& callInfo, JSObject* targetArg)
4310 : {
4311 : InliningStatus status;
4312 0 : if (!targetArg->is<JSFunction>()) {
4313 0 : MOZ_TRY_VAR(status, inlineNonFunctionCall(callInfo, targetArg));
4314 0 : trackInlineSuccess(status);
4315 0 : return status;
4316 : }
4317 :
4318 0 : JSFunction* target = &targetArg->as<JSFunction>();
4319 0 : if (target->isNative()) {
4320 0 : MOZ_TRY_VAR(status, inlineNativeCall(callInfo, target));
4321 0 : trackInlineSuccess(status);
4322 2446 : return status;
4323 : }
4324 :
4325 : // Track success now, as inlining a scripted call makes a new return block
4326 : // which has a different pc than the current call pc.
4327 0 : trackInlineSuccess();
4328 300 : return inlineScriptedCall(callInfo, target);
4329 : }
4330 :
4331 : IonBuilder::InliningResult
4332 2588 : IonBuilder::inlineCallsite(const InliningTargets& targets, CallInfo& callInfo)
4333 : {
4334 0 : if (targets.empty()) {
4335 0 : trackOptimizationAttempt(TrackedStrategy::Call_Inline);
4336 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNoTarget);
4337 959 : return InliningStatus_NotInlined;
4338 : }
4339 :
4340 : // Is the function provided by an MGetPropertyCache?
4341 : // If so, the cache may be movable to a fallback path, with a dispatch
4342 : // instruction guarding on the incoming ObjectGroup.
4343 0 : WrapMGetPropertyCache propCache(getInlineableGetPropertyCache(callInfo));
4344 3258 : keepFallbackFunctionGetter(propCache.get());
4345 :
4346 : // Inline single targets -- unless they derive from a cache, in which case
4347 : // avoiding the cache and guarding is still faster.
4348 0 : if (!propCache.get() && targets.length() == 1) {
4349 1618 : JSObject* target = targets[0].target;
4350 :
4351 0 : trackOptimizationAttempt(TrackedStrategy::Call_Inline);
4352 1618 : trackTypeInfo(TrackedTypeSite::Call_Target, target);
4353 :
4354 0 : InliningDecision decision = makeInliningDecision(target, callInfo);
4355 1618 : switch (decision) {
4356 : case InliningDecision_Error:
4357 0 : return abort(AbortReason::Error);
4358 : case InliningDecision_DontInline:
4359 99 : return InliningStatus_NotInlined;
4360 : case InliningDecision_WarmUpCountTooLow:
4361 18 : return InliningStatus_WarmUpCountTooLow;
4362 : case InliningDecision_Inline:
4363 : break;
4364 : }
4365 :
4366 : // Inlining will elminate uses of the original callee, but it needs to
4367 : // be preserved in phis if we bail out. Mark the old callee definition as
4368 : // implicitly used to ensure this happens.
4369 3002 : callInfo.fun()->setImplicitlyUsedUnchecked();
4370 :
4371 : // If the callee is not going to be a lambda (which may vary across
4372 : // different invocations), then the callee definition can be replaced by a
4373 : // constant.
4374 1501 : if (target->isSingleton()) {
4375 : // Replace the function with an MConstant.
4376 0 : MConstant* constFun = constant(ObjectValue(*target));
4377 0 : if (callInfo.constructing() && callInfo.getNewTarget() == callInfo.fun())
4378 0 : callInfo.setNewTarget(constFun);
4379 1459 : callInfo.setFun(constFun);
4380 : }
4381 :
4382 1501 : return inlineSingleCall(callInfo, target);
4383 : }
4384 :
4385 : // Choose a subset of the targets for polymorphic inlining.
4386 33 : BoolVector choiceSet(alloc());
4387 : uint32_t numInlined;
4388 0 : MOZ_TRY(selectInliningTargets(targets, callInfo, choiceSet, &numInlined));
4389 0 : if (numInlined == 0)
4390 0 : return InliningStatus_NotInlined;
4391 :
4392 : // Perform a polymorphic dispatch.
4393 33 : MOZ_TRY(inlineCalls(callInfo, targets, choiceSet, propCache.get()));
4394 :
4395 11 : return InliningStatus_Inlined;
4396 : }
4397 :
4398 : AbortReasonOr<Ok>
4399 3 : IonBuilder::inlineGenericFallback(const Maybe<CallTargets>& targets, CallInfo& callInfo,
4400 : MBasicBlock* dispatchBlock)
4401 : {
4402 : // Generate a new block with all arguments on-stack.
4403 : MBasicBlock* fallbackBlock;
4404 0 : MOZ_TRY_VAR(fallbackBlock, newBlock(dispatchBlock, pc));
4405 3 : graph().addBlock(fallbackBlock);
4406 :
4407 : // Create a new CallInfo to track modified state within this block.
4408 0 : CallInfo fallbackInfo(alloc(), pc, callInfo.constructing(), callInfo.ignoresReturnValue());
4409 1 : if (!fallbackInfo.init(callInfo))
4410 0 : return abort(AbortReason::Alloc);
4411 6 : fallbackInfo.popCallStack(fallbackBlock);
4412 :
4413 : // Generate an MCall, which uses stateful |current|.
4414 0 : MOZ_TRY(setCurrentAndSpecializePhis(fallbackBlock));
4415 9 : MOZ_TRY(makeCall(targets, fallbackInfo));
4416 :
4417 : // Pass return block to caller as |current|.
4418 3 : return Ok();
4419 : }
4420 :
4421 : AbortReasonOr<Ok>
4422 0 : IonBuilder::inlineObjectGroupFallback(const Maybe<CallTargets>& targets,
4423 : CallInfo& callInfo, MBasicBlock* dispatchBlock,
4424 : MObjectGroupDispatch* dispatch, MGetPropertyCache* cache,
4425 : MBasicBlock** fallbackTarget)
4426 : {
4427 : // Getting here implies the following:
4428 : // 1. The call function is an MGetPropertyCache, or an MGetPropertyCache
4429 : // followed by an MTypeBarrier.
4430 0 : MOZ_ASSERT(callInfo.fun()->isGetPropertyCache() || callInfo.fun()->isTypeBarrier());
4431 :
4432 : // 2. The MGetPropertyCache has inlineable cases by guarding on the ObjectGroup.
4433 0 : MOZ_ASSERT(dispatch->numCases() > 0);
4434 :
4435 : // 3. The MGetPropertyCache (and, if applicable, MTypeBarrier) only
4436 : // have at most a single use.
4437 0 : MOZ_ASSERT_IF(callInfo.fun()->isGetPropertyCache(), !cache->hasUses());
4438 0 : MOZ_ASSERT_IF(callInfo.fun()->isTypeBarrier(), cache->hasOneUse());
4439 :
4440 : // This means that no resume points yet capture the MGetPropertyCache,
4441 : // so everything from the MGetPropertyCache up until the call is movable.
4442 : // We now move the MGetPropertyCache and friends into a fallback path.
4443 0 : MOZ_ASSERT(cache->idempotent());
4444 :
4445 : // Create a new CallInfo to track modified state within the fallback path.
4446 0 : CallInfo fallbackInfo(alloc(), pc, callInfo.constructing(), callInfo.ignoresReturnValue());
4447 0 : if (!fallbackInfo.init(callInfo))
4448 0 : return abort(AbortReason::Alloc);
4449 :
4450 : // Capture stack prior to the call operation. This captures the function.
4451 : MResumePoint* preCallResumePoint =
4452 0 : MResumePoint::New(alloc(), dispatchBlock, pc, MResumePoint::ResumeAt);
4453 0 : if (!preCallResumePoint)
4454 0 : return abort(AbortReason::Alloc);
4455 :
4456 0 : DebugOnly<size_t> preCallFuncIndex = preCallResumePoint->stackDepth() - callInfo.numFormals();
4457 0 : MOZ_ASSERT(preCallResumePoint->getOperand(preCallFuncIndex) == fallbackInfo.fun());
4458 :
4459 : // In the dispatch block, replace the function's slot entry with Undefined.
4460 0 : MConstant* undefined = MConstant::New(alloc(), UndefinedValue());
4461 0 : dispatchBlock->add(undefined);
4462 0 : dispatchBlock->rewriteAtDepth(-int(callInfo.numFormals()), undefined);
4463 :
4464 : // Construct a block that does nothing but remove formals from the stack.
4465 : // This is effectively changing the entry resume point of the later fallback block.
4466 : MBasicBlock* prepBlock;
4467 0 : MOZ_TRY_VAR(prepBlock, newBlock(dispatchBlock, pc));
4468 0 : graph().addBlock(prepBlock);
4469 0 : fallbackInfo.popCallStack(prepBlock);
4470 :
4471 : // Construct a block into which the MGetPropertyCache can be moved.
4472 : // This is subtle: the pc and resume point are those of the MGetPropertyCache!
4473 0 : InlinePropertyTable* propTable = cache->propTable();
4474 0 : MResumePoint* priorResumePoint = propTable->takePriorResumePoint();
4475 0 : MOZ_ASSERT(propTable->pc() != nullptr);
4476 0 : MOZ_ASSERT(priorResumePoint != nullptr);
4477 : MBasicBlock* getPropBlock;
4478 0 : MOZ_TRY_VAR(getPropBlock, newBlock(prepBlock, propTable->pc(), priorResumePoint));
4479 0 : graph().addBlock(getPropBlock);
4480 :
4481 0 : prepBlock->end(MGoto::New(alloc(), getPropBlock));
4482 :
4483 : // Since the getPropBlock inherited the stack from right before the MGetPropertyCache,
4484 : // the target of the MGetPropertyCache is still on the stack.
4485 0 : DebugOnly<MDefinition*> checkObject = getPropBlock->pop();
4486 0 : MOZ_ASSERT(checkObject == cache->value());
4487 :
4488 : // Move the MGetPropertyCache and friends into the getPropBlock.
4489 0 : if (fallbackInfo.fun()->isGetPropertyCache()) {
4490 0 : MOZ_ASSERT(fallbackInfo.fun()->toGetPropertyCache() == cache);
4491 0 : getPropBlock->addFromElsewhere(cache);
4492 0 : getPropBlock->push(cache);
4493 : } else {
4494 0 : MTypeBarrier* barrier = callInfo.fun()->toTypeBarrier();
4495 0 : MOZ_ASSERT(barrier->type() == MIRType::Object);
4496 0 : MOZ_ASSERT(barrier->input()->isGetPropertyCache());
4497 0 : MOZ_ASSERT(barrier->input()->toGetPropertyCache() == cache);
4498 :
4499 0 : getPropBlock->addFromElsewhere(cache);
4500 0 : getPropBlock->addFromElsewhere(barrier);
4501 0 : getPropBlock->push(barrier);
4502 : }
4503 :
4504 : // Construct an end block with the correct resume point.
4505 : MBasicBlock* preCallBlock;
4506 0 : MOZ_TRY_VAR(preCallBlock, newBlock(getPropBlock, pc, preCallResumePoint));
4507 0 : graph().addBlock(preCallBlock);
4508 0 : getPropBlock->end(MGoto::New(alloc(), preCallBlock));
4509 :
4510 : // Now inline the MCallGeneric, using preCallBlock as the dispatch point.
4511 0 : MOZ_TRY(inlineGenericFallback(targets, fallbackInfo, preCallBlock));
4512 :
4513 : // inlineGenericFallback() set the return block as |current|.
4514 0 : preCallBlock->end(MGoto::New(alloc(), current));
4515 0 : *fallbackTarget = prepBlock;
4516 0 : return Ok();
4517 : }
4518 :
4519 : AbortReasonOr<Ok>
4520 11 : IonBuilder::inlineCalls(CallInfo& callInfo, const InliningTargets& targets, BoolVector& choiceSet,
4521 : MGetPropertyCache* maybeCache)
4522 : {
4523 : // Only handle polymorphic inlining.
4524 0 : MOZ_ASSERT(IsIonInlinablePC(pc));
4525 0 : MOZ_ASSERT(choiceSet.length() == targets.length());
4526 0 : MOZ_ASSERT_IF(!maybeCache, targets.length() >= 2);
4527 0 : MOZ_ASSERT_IF(maybeCache, targets.length() >= 1);
4528 11 : MOZ_ASSERT_IF(maybeCache, maybeCache->value()->type() == MIRType::Object);
4529 :
4530 0 : MBasicBlock* dispatchBlock = current;
4531 0 : callInfo.setImplicitlyUsedUnchecked();
4532 33 : MOZ_TRY(callInfo.pushCallStack(this, dispatchBlock));
4533 :
4534 : // Patch any InlinePropertyTable to only contain functions that are
4535 : // inlineable. The InlinePropertyTable will also be patched at the end to
4536 : // exclude native functions that vetoed inlining.
4537 0 : if (maybeCache) {
4538 0 : InlinePropertyTable* propTable = maybeCache->propTable();
4539 0 : propTable->trimToTargets(targets);
4540 0 : if (propTable->numEntries() == 0)
4541 0 : maybeCache = nullptr;
4542 : }
4543 :
4544 : // Generate a dispatch based on guard kind.
4545 : MDispatchInstruction* dispatch;
4546 0 : if (maybeCache) {
4547 0 : dispatch = MObjectGroupDispatch::New(alloc(), maybeCache->value(), maybeCache->propTable());
4548 0 : callInfo.fun()->setImplicitlyUsedUnchecked();
4549 : } else {
4550 11 : dispatch = MFunctionDispatch::New(alloc(), callInfo.fun());
4551 : }
4552 :
4553 0 : MOZ_ASSERT(dispatchBlock->stackDepth() >= callInfo.numFormals());
4554 22 : uint32_t stackDepth = dispatchBlock->stackDepth() - callInfo.numFormals() + 1;
4555 :
4556 : // Generate a return block to host the rval-collecting MPhi.
4557 22 : jsbytecode* postCall = GetNextPc(pc);
4558 : MBasicBlock* returnBlock;
4559 0 : MOZ_TRY_VAR(returnBlock, newBlock(stackDepth, postCall));
4560 0 : graph().addBlock(returnBlock);
4561 22 : returnBlock->setCallerResumePoint(callerResumePoint_);
4562 :
4563 : // Set up stack, used to manually create a post-call resume point.
4564 0 : returnBlock->inheritSlots(dispatchBlock);
4565 22 : callInfo.popCallStack(returnBlock);
4566 :
4567 0 : MPhi* retPhi = MPhi::New(alloc());
4568 0 : returnBlock->addPhi(retPhi);
4569 11 : returnBlock->push(retPhi);
4570 :
4571 : // Create a resume point from current stack state.
4572 1 : if (!returnBlock->initEntrySlots(alloc()))
4573 0 : return abort(AbortReason::Alloc);
4574 :
4575 : // Reserve the capacity for the phi.
4576 : // Note: this is an upperbound. Unreachable targets and uninlineable natives are also counted.
4577 : uint32_t count = 1; // Possible fallback block.
4578 0 : for (uint32_t i = 0; i < targets.length(); i++) {
4579 0 : if (choiceSet[i])
4580 22 : count++;
4581 : }
4582 1 : if (!retPhi->reserveLength(count))
4583 0 : return abort(AbortReason::Alloc);
4584 :
4585 : // Inline each of the inlineable targets.
4586 65 : for (uint32_t i = 0; i < targets.length(); i++) {
4587 : // Target must be inlineable.
4588 0 : if (!choiceSet[i])
4589 5 : continue;
4590 :
4591 : // Even though we made one round of inline decisions already, we may
4592 : // be amending them below.
4593 22 : amendOptimizationAttempt(i);
4594 :
4595 : // Target must be reachable by the MDispatchInstruction.
4596 0 : JSFunction* target = &targets[i].target->as<JSFunction>();
4597 0 : if (maybeCache && !maybeCache->propTable()->hasFunction(target)) {
4598 0 : choiceSet[i] = false;
4599 : trackOptimizationOutcome(TrackedOutcome::CantInlineNotInDispatch);
4600 : continue;
4601 : }
4602 :
4603 : MBasicBlock* inlineBlock;
4604 0 : MOZ_TRY_VAR(inlineBlock, newBlock(dispatchBlock, pc));
4605 22 : graph().addBlock(inlineBlock);
4606 :
4607 : // Create a function MConstant to use in the entry ResumePoint. If we
4608 : // can't use a constant, add a no-op MPolyInlineGuard, to prevent
4609 : // hoisting env chain gets above the dispatch instruction.
4610 : MInstruction* funcDef;
4611 0 : if (target->isSingleton())
4612 44 : funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints());
4613 : else
4614 0 : funcDef = MPolyInlineGuard::New(alloc(), callInfo.fun());
4615 :
4616 0 : funcDef->setImplicitlyUsedUnchecked();
4617 22 : dispatchBlock->add(funcDef);
4618 :
4619 : // Use the inlined callee in the inline resume point and on stack.
4620 0 : int funIndex = inlineBlock->entryResumePoint()->stackDepth() - callInfo.numFormals();
4621 0 : inlineBlock->entryResumePoint()->replaceOperand(funIndex, funcDef);
4622 44 : inlineBlock->rewriteSlot(funIndex, funcDef);
4623 :
4624 : // Create a new CallInfo to track modified state within the inline block.
4625 0 : CallInfo inlineInfo(alloc(), pc, callInfo.constructing(), callInfo.ignoresReturnValue());
4626 1 : if (!inlineInfo.init(callInfo))
4627 0 : return abort(AbortReason::Alloc);
4628 0 : inlineInfo.popCallStack(inlineBlock);
4629 44 : inlineInfo.setFun(funcDef);
4630 :
4631 22 : if (maybeCache) {
4632 : // Assign the 'this' value a TypeSet specialized to the groups that
4633 : // can generate this inlining target.
4634 0 : MOZ_ASSERT(callInfo.thisArg() == maybeCache->value());
4635 : TemporaryTypeSet* thisTypes =
4636 0 : maybeCache->propTable()->buildTypeSetForFunction(alloc(), target);
4637 0 : if (!thisTypes)
4638 0 : return abort(AbortReason::Alloc);
4639 :
4640 0 : MFilterTypeSet* filter = MFilterTypeSet::New(alloc(), inlineInfo.thisArg(), thisTypes);
4641 0 : inlineBlock->add(filter);
4642 0 : inlineInfo.setThis(filter);
4643 : }
4644 :
4645 : // Inline the call into the inlineBlock.
4646 66 : MOZ_TRY(setCurrentAndSpecializePhis(inlineBlock));
4647 : InliningStatus status;
4648 44 : MOZ_TRY_VAR(status, inlineSingleCall(inlineInfo, target));
4649 :
4650 : // Natives may veto inlining.
4651 0 : if (status == InliningStatus_NotInlined) {
4652 0 : MOZ_ASSERT(current == inlineBlock);
4653 0 : graph().removeBlock(inlineBlock);
4654 0 : choiceSet[i] = false;
4655 : continue;
4656 : }
4657 :
4658 : // inlineSingleCall() changed |current| to the inline return block.
4659 0 : MBasicBlock* inlineReturnBlock = current;
4660 44 : setCurrent(dispatchBlock);
4661 :
4662 : // Connect the inline path to the returnBlock.
4663 1 : if (!dispatch->addCase(target, targets[i].group, inlineBlock))
4664 0 : return abort(AbortReason::Alloc);
4665 :
4666 0 : MDefinition* retVal = inlineReturnBlock->peek(-1);
4667 0 : retPhi->addInput(retVal);
4668 0 : inlineReturnBlock->end(MGoto::New(alloc(), returnBlock));
4669 1 : if (!returnBlock->addPredecessorWithoutPhis(inlineReturnBlock))
4670 0 : return abort(AbortReason::Alloc);
4671 : }
4672 :
4673 : // Patch the InlinePropertyTable to not dispatch to vetoed paths.
4674 : bool useFallback;
4675 0 : if (maybeCache) {
4676 0 : InlinePropertyTable* propTable = maybeCache->propTable();
4677 0 : propTable->trimTo(targets, choiceSet);
4678 :
4679 0 : if (propTable->numEntries() == 0 || !propTable->hasPriorResumePoint()) {
4680 : // Output a generic fallback path.
4681 0 : MOZ_ASSERT_IF(propTable->numEntries() == 0, dispatch->numCases() == 0);
4682 : maybeCache = nullptr;
4683 : useFallback = true;
4684 : } else {
4685 : // We need a fallback path if the ObjectGroup dispatch does not
4686 : // handle all incoming objects.
4687 0 : useFallback = false;
4688 0 : TemporaryTypeSet* objectTypes = maybeCache->value()->resultTypeSet();
4689 0 : for (uint32_t i = 0; i < objectTypes->getObjectCount(); i++) {
4690 0 : TypeSet::ObjectKey* obj = objectTypes->getObject(i);
4691 0 : if (!obj)
4692 : continue;
4693 :
4694 0 : if (!obj->isGroup()) {
4695 : useFallback = true;
4696 : break;
4697 : }
4698 :
4699 0 : if (!propTable->hasObjectGroup(obj->group())) {
4700 : useFallback = true;
4701 : break;
4702 : }
4703 : }
4704 :
4705 0 : if (!useFallback) {
4706 : // The object group dispatch handles all possible incoming
4707 : // objects, so the cache and barrier will not be reached and
4708 : // can be eliminated.
4709 0 : if (callInfo.fun()->isGetPropertyCache()) {
4710 0 : MOZ_ASSERT(callInfo.fun() == maybeCache);
4711 : } else {
4712 0 : MTypeBarrier* barrier = callInfo.fun()->toTypeBarrier();
4713 0 : MOZ_ASSERT(!barrier->hasUses());
4714 0 : MOZ_ASSERT(barrier->type() == MIRType::Object);
4715 0 : MOZ_ASSERT(barrier->input()->isGetPropertyCache());
4716 0 : MOZ_ASSERT(barrier->input()->toGetPropertyCache() == maybeCache);
4717 0 : barrier->block()->discard(barrier);
4718 : }
4719 :
4720 0 : MOZ_ASSERT(!maybeCache->hasUses());
4721 0 : maybeCache->block()->discard(maybeCache);
4722 : }
4723 : }
4724 : } else {
4725 11 : useFallback = dispatch->numCases() < targets.length();
4726 : }
4727 :
4728 : // If necessary, generate a fallback path.
4729 11 : if (useFallback) {
4730 : // Annotate the fallback call with the target information.
4731 0 : Maybe<CallTargets> remainingTargets;
4732 0 : remainingTargets.emplace(alloc());
4733 0 : for (uint32_t i = 0; i < targets.length(); i++) {
4734 10 : if (!maybeCache && choiceSet[i])
4735 : continue;
4736 :
4737 0 : JSObject* target = targets[i].target;
4738 0 : if (!target->is<JSFunction>()) {
4739 0 : remainingTargets = Nothing();
4740 0 : break;
4741 : }
4742 1 : if (!remainingTargets->append(&target->as<JSFunction>()))
4743 0 : return abort(AbortReason::Alloc);
4744 : }
4745 :
4746 : // Generate fallback blocks, and set |current| to the fallback return block.
4747 3 : if (maybeCache) {
4748 : MBasicBlock* fallbackTarget;
4749 0 : MOZ_TRY(inlineObjectGroupFallback(remainingTargets, callInfo, dispatchBlock,
4750 : dispatch->toObjectGroupDispatch(),
4751 : maybeCache, &fallbackTarget));
4752 0 : dispatch->addFallback(fallbackTarget);
4753 : } else {
4754 0 : MOZ_TRY(inlineGenericFallback(remainingTargets, callInfo, dispatchBlock));
4755 3 : dispatch->addFallback(current);
4756 : }
4757 :
4758 3 : MBasicBlock* fallbackReturnBlock = current;
4759 :
4760 : // Connect fallback case to return infrastructure.
4761 0 : MDefinition* retVal = fallbackReturnBlock->peek(-1);
4762 0 : retPhi->addInput(retVal);
4763 0 : fallbackReturnBlock->end(MGoto::New(alloc(), returnBlock));
4764 1 : if (!returnBlock->addPredecessorWithoutPhis(fallbackReturnBlock))
4765 0 : return abort(AbortReason::Alloc);
4766 : }
4767 :
4768 : // Finally add the dispatch instruction.
4769 : // This must be done at the end so that add() may be called above.
4770 11 : dispatchBlock->end(dispatch);
4771 :
4772 : // Check the depth change: +1 for retval
4773 22 : MOZ_ASSERT(returnBlock->stackDepth() == dispatchBlock->stackDepth() - callInfo.numFormals() + 1);
4774 :
4775 0 : graph().moveBlockToEnd(returnBlock);
4776 11 : return setCurrentAndSpecializePhis(returnBlock);
4777 : }
4778 :
4779 : MInstruction*
4780 0 : IonBuilder::createNamedLambdaObject(MDefinition* callee, MDefinition* env)
4781 : {
4782 : // Get a template CallObject that we'll use to generate inline object
4783 : // creation.
4784 0 : LexicalEnvironmentObject* templateObj = inspector->templateNamedLambdaObject();
4785 :
4786 : // One field is added to the function to handle its name. This cannot be a
4787 : // dynamic slot because there is still plenty of room on the NamedLambda object.
4788 0 : MOZ_ASSERT(!templateObj->hasDynamicSlots());
4789 :
4790 : // Allocate the actual object. It is important that no intervening
4791 : // instructions could potentially bailout, thus leaking the dynamic slots
4792 : // pointer.
4793 0 : MInstruction* declEnvObj = MNewNamedLambdaObject::New(alloc(), templateObj);
4794 0 : current->add(declEnvObj);
4795 :
4796 : // Initialize the object's reserved slots. No post barrier is needed here:
4797 : // the object will be allocated in the nursery if possible, and if the
4798 : // tenured heap is used instead, a minor collection will have been performed
4799 : // that moved env/callee to the tenured heap.
4800 0 : current->add(MStoreFixedSlot::New(alloc(), declEnvObj,
4801 0 : NamedLambdaObject::enclosingEnvironmentSlot(), env));
4802 0 : current->add(MStoreFixedSlot::New(alloc(), declEnvObj,
4803 0 : NamedLambdaObject::lambdaSlot(), callee));
4804 :
4805 0 : return declEnvObj;
4806 : }
4807 :
4808 : AbortReasonOr<MInstruction*>
4809 15 : IonBuilder::createCallObject(MDefinition* callee, MDefinition* env)
4810 : {
4811 : // Get a template CallObject that we'll use to generate inline object
4812 : // creation.
4813 0 : CallObject* templateObj = inspector->templateCallObject();
4814 0 : MConstant* templateCst = MConstant::NewConstraintlessObject(alloc(), templateObj);
4815 15 : current->add(templateCst);
4816 :
4817 : // Allocate the object. Run-once scripts need a singleton type, so always do
4818 : // a VM call in such cases.
4819 : MNewCallObjectBase* callObj;
4820 0 : if (script()->treatAsRunOnce() || templateObj->isSingleton())
4821 0 : callObj = MNewSingletonCallObject::New(alloc(), templateCst);
4822 : else
4823 0 : callObj = MNewCallObject::New(alloc(), templateCst);
4824 15 : current->add(callObj);
4825 :
4826 : // Initialize the object's reserved slots. No post barrier is needed here,
4827 : // for the same reason as in createNamedLambdaObject.
4828 0 : current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::enclosingEnvironmentSlot(), env));
4829 15 : current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::calleeSlot(), callee));
4830 :
4831 : //if (!script()->functionHasParameterExprs()) {
4832 :
4833 : // Copy closed-over argument slots if there aren't parameter expressions.
4834 0 : MSlots* slots = nullptr;
4835 0 : for (PositionalFormalParameterIter fi(script()); fi; fi++) {
4836 42 : if (!fi.closedOver())
4837 : continue;
4838 :
4839 1 : if (!alloc().ensureBallast())
4840 0 : return abort(AbortReason::Alloc);
4841 :
4842 0 : unsigned slot = fi.location().slot();
4843 0 : unsigned formal = fi.argumentSlot();
4844 4 : unsigned numFixedSlots = templateObj->numFixedSlots();
4845 : MDefinition* param;
4846 0 : if (script()->functionHasParameterExprs())
4847 0 : param = constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
4848 : else
4849 0 : param = current->getSlot(info().argSlotUnchecked(formal));
4850 0 : if (slot >= numFixedSlots) {
4851 0 : if (!slots) {
4852 0 : slots = MSlots::New(alloc(), callObj);
4853 0 : current->add(slots);
4854 : }
4855 0 : current->add(MStoreSlot::New(alloc(), slots, slot - numFixedSlots, param));
4856 : } else {
4857 2 : current->add(MStoreFixedSlot::New(alloc(), callObj, slot, param));
4858 : }
4859 : }
4860 :
4861 15 : return AbortReasonOr<MInstruction*>(callObj);
4862 : }
4863 :
4864 : MDefinition*
4865 0 : IonBuilder::createThisScripted(MDefinition* callee, MDefinition* newTarget)
4866 : {
4867 : // Get callee.prototype.
4868 : //
4869 : // This instruction MUST be idempotent: since it does not correspond to an
4870 : // explicit operation in the bytecode, we cannot use resumeAfter().
4871 : // Getters may not override |prototype| fetching, so this operation is indeed idempotent.
4872 : // - First try an idempotent property cache.
4873 : // - Upon failing idempotent property cache, we can't use a non-idempotent cache,
4874 : // therefore we fallback to CallGetProperty
4875 : //
4876 : // Note: both CallGetProperty and GetPropertyCache can trigger a GC,
4877 : // and thus invalidation.
4878 : MInstruction* getProto;
4879 0 : if (!invalidatedIdempotentCache()) {
4880 0 : MConstant* id = constant(StringValue(names().prototype));
4881 0 : MGetPropertyCache* getPropCache = MGetPropertyCache::New(alloc(), newTarget, id,
4882 0 : /* monitored = */ false);
4883 0 : getPropCache->setIdempotent();
4884 0 : getProto = getPropCache;
4885 : } else {
4886 0 : MCallGetProperty* callGetProp = MCallGetProperty::New(alloc(), newTarget, names().prototype);
4887 : callGetProp->setIdempotent();
4888 0 : getProto = callGetProp;
4889 : }
4890 0 : current->add(getProto);
4891 :
4892 : // Create this from prototype
4893 0 : MCreateThisWithProto* createThis = MCreateThisWithProto::New(alloc(), callee, newTarget, getProto);
4894 0 : current->add(createThis);
4895 :
4896 0 : return createThis;
4897 : }
4898 :
4899 : JSObject*
4900 4 : IonBuilder::getSingletonPrototype(JSFunction* target)
4901 : {
4902 0 : TypeSet::ObjectKey* targetKey = TypeSet::ObjectKey::get(target);
4903 4 : if (targetKey->unknownProperties())
4904 : return nullptr;
4905 :
4906 0 : jsid protoid = NameToId(names().prototype);
4907 4 : HeapTypeSetKey protoProperty = targetKey->property(protoid);
4908 :
4909 4 : return protoProperty.singleton(constraints());
4910 : }
4911 :
4912 : MDefinition*
4913 4 : IonBuilder::createThisScriptedSingleton(JSFunction* target)
4914 : {
4915 4 : if (!target->hasScript())
4916 : return nullptr;
4917 :
4918 : // Get the singleton prototype (if exists)
4919 0 : JSObject* proto = getSingletonPrototype(target);
4920 4 : if (!proto)
4921 : return nullptr;
4922 :
4923 0 : JSObject* templateObject = inspector->getTemplateObject(pc);
4924 0 : if (!templateObject)
4925 : return nullptr;
4926 0 : if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
4927 : return nullptr;
4928 0 : if (templateObject->staticPrototype() != proto)
4929 : return nullptr;
4930 :
4931 0 : TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group());
4932 0 : if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED))
4933 : return nullptr;
4934 :
4935 0 : StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript());
4936 0 : if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject)))
4937 : return nullptr;
4938 :
4939 : // Generate an inline path to create a new |this| object with
4940 : // the given singleton prototype.
4941 0 : MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
4942 : MCreateThisWithTemplate* createThis =
4943 0 : MCreateThisWithTemplate::New(alloc(), constraints(), templateConst,
4944 0 : templateObject->group()->initialHeap(constraints()));
4945 0 : current->add(templateConst);
4946 0 : current->add(createThis);
4947 :
4948 0 : return createThis;
4949 : }
4950 :
4951 : MDefinition*
4952 72 : IonBuilder::createThisScriptedBaseline(MDefinition* callee)
4953 : {
4954 : // Try to inline |this| creation based on Baseline feedback.
4955 :
4956 0 : JSFunction* target = inspector->getSingleCallee(pc);
4957 77 : if (!target || !target->hasScript())
4958 : return nullptr;
4959 :
4960 5 : if (target->isBoundFunction() || target->isDerivedClassConstructor())
4961 : return nullptr;
4962 :
4963 0 : JSObject* templateObject = inspector->getTemplateObject(pc);
4964 4 : if (!templateObject)
4965 : return nullptr;
4966 4 : if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
4967 : return nullptr;
4968 :
4969 0 : Shape* shape = target->lookupPure(realm->runtime()->names().prototype);
4970 4 : if (!shape || !shape->isDataProperty())
4971 : return nullptr;
4972 :
4973 0 : Value protov = target->getSlot(shape->slot());
4974 4 : if (!protov.isObject())
4975 : return nullptr;
4976 :
4977 0 : JSObject* proto = checkNurseryObject(&protov.toObject());
4978 4 : if (proto != templateObject->staticPrototype())
4979 : return nullptr;
4980 :
4981 0 : TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group());
4982 4 : if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED))
4983 : return nullptr;
4984 :
4985 0 : StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript());
4986 4 : if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject)))
4987 : return nullptr;
4988 :
4989 : // Shape guard.
4990 4 : callee = addShapeGuard(callee, target->lastProperty(), Bailout_ShapeGuard);
4991 :
4992 : // Guard callee.prototype == proto.
4993 0 : MOZ_ASSERT(shape->numFixedSlots() == 0, "Must be a dynamic slot");
4994 0 : MSlots* slots = MSlots::New(alloc(), callee);
4995 0 : current->add(slots);
4996 0 : MLoadSlot* prototype = MLoadSlot::New(alloc(), slots, shape->slot());
4997 0 : current->add(prototype);
4998 0 : MDefinition* protoConst = constant(ObjectValue(*proto));
4999 0 : MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst,
5000 0 : /* bailOnEquality = */ false);
5001 4 : current->add(guard);
5002 :
5003 : // Generate an inline path to create a new |this| object with
5004 : // the given prototype.
5005 4 : MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
5006 : MCreateThisWithTemplate* createThis =
5007 0 : MCreateThisWithTemplate::New(alloc(), constraints(), templateConst,
5008 0 : templateObject->group()->initialHeap(constraints()));
5009 0 : current->add(templateConst);
5010 4 : current->add(createThis);
5011 :
5012 4 : return createThis;
5013 : }
5014 :
5015 : MDefinition*
5016 87 : IonBuilder::createThis(JSFunction* target, MDefinition* callee, MDefinition* newTarget)
5017 : {
5018 : // Create |this| for unknown target.
5019 0 : if (!target) {
5020 68 : if (MDefinition* createThis = createThisScriptedBaseline(callee))
5021 : return createThis;
5022 :
5023 0 : MCreateThis* createThis = MCreateThis::New(alloc(), callee, newTarget);
5024 0 : current->add(createThis);
5025 68 : return createThis;
5026 : }
5027 :
5028 : // Native constructors build the new Object themselves.
5029 0 : if (target->isNative()) {
5030 15 : if (!target->isConstructor())
5031 : return nullptr;
5032 :
5033 15 : if (target->isNativeWithJitEntry()) {
5034 : // Do not bother inlining constructor calls to asm.js, since it is
5035 : // not used much in practice.
5036 0 : MOZ_ASSERT(target->isWasmOptimized());
5037 : return nullptr;
5038 : }
5039 :
5040 0 : MConstant* magic = MConstant::New(alloc(), MagicValue(JS_IS_CONSTRUCTING));
5041 0 : current->add(magic);
5042 15 : return magic;
5043 : }
5044 :
5045 1 : if (target->isBoundFunction())
5046 0 : return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
5047 :
5048 0 : if (target->isDerivedClassConstructor()) {
5049 0 : MOZ_ASSERT(target->isClassConstructor());
5050 0 : return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
5051 : }
5052 :
5053 : // Try baking in the prototype.
5054 4 : if (MDefinition* createThis = createThisScriptedSingleton(target))
5055 : return createThis;
5056 :
5057 4 : if (MDefinition* createThis = createThisScriptedBaseline(callee))
5058 : return createThis;
5059 :
5060 0 : return createThisScripted(callee, newTarget);
5061 : }
5062 :
5063 : AbortReasonOr<Ok>
5064 2 : IonBuilder::jsop_funcall(uint32_t argc)
5065 : {
5066 : // Stack for JSOP_FUNCALL:
5067 : // 1: arg0
5068 : // ...
5069 : // argc: argN
5070 : // argc+1: JSFunction*, the 'f' in |f.call()|, in |this| position.
5071 : // argc+2: The native 'call' function.
5072 :
5073 0 : int calleeDepth = -((int)argc + 2);
5074 2 : int funcDepth = -((int)argc + 1);
5075 :
5076 : // If |Function.prototype.call| may be overridden, don't optimize callsite.
5077 0 : TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
5078 0 : JSFunction* native = getSingleCallTarget(calleeTypes);
5079 2 : if (!native || !native->isNative() || native->native() != &fun_call) {
5080 : CallInfo callInfo(alloc(), pc, /* constructing = */ false,
5081 0 : /* ignoresReturnValue = */ BytecodeIsPopped(pc));
5082 1 : if (!callInfo.init(current, argc))
5083 0 : return abort(AbortReason::Alloc);
5084 2 : return makeCall(native, callInfo);
5085 : }
5086 0 : current->peek(calleeDepth)->setImplicitlyUsedUnchecked();
5087 :
5088 : // Extract call target.
5089 0 : TemporaryTypeSet* funTypes = current->peek(funcDepth)->resultTypeSet();
5090 0 : JSFunction* target = getSingleCallTarget(funTypes);
5091 :
5092 : // Shimmy the slots down to remove the native 'call' function.
5093 0 : current->shimmySlots(funcDepth - 1);
5094 :
5095 0 : bool zeroArguments = (argc == 0);
5096 :
5097 : // If no |this| argument was provided, explicitly pass Undefined.
5098 : // Pushing is safe here, since one stack slot has been removed.
5099 0 : if (zeroArguments) {
5100 0 : pushConstant(UndefinedValue());
5101 : } else {
5102 : // |this| becomes implicit in the call.
5103 0 : argc -= 1;
5104 : }
5105 :
5106 : CallInfo callInfo(alloc(), pc, /* constructing = */ false,
5107 0 : /* ignoresReturnValue = */ BytecodeIsPopped(pc));
5108 0 : if (!callInfo.init(current, argc))
5109 0 : return abort(AbortReason::Alloc);
5110 :
5111 : // Try to inline the call.
5112 0 : if (!zeroArguments) {
5113 0 : InliningDecision decision = makeInliningDecision(target, callInfo);
5114 0 : switch (decision) {
5115 : case InliningDecision_Error:
5116 0 : return abort(AbortReason::Error);
5117 : case InliningDecision_DontInline:
5118 : case InliningDecision_WarmUpCountTooLow:
5119 : break;
5120 : case InliningDecision_Inline: {
5121 : InliningStatus status;
5122 0 : MOZ_TRY_VAR(status, inlineSingleCall(callInfo, target));
5123 0 : if (status == InliningStatus_Inlined)
5124 0 : return Ok();
5125 : break;
5126 : }
5127 : }
5128 : }
5129 :
5130 : // Call without inlining.
5131 0 : return makeCall(target, callInfo);
5132 : }
5133 :
5134 : AbortReasonOr<Ok>
5135 15 : IonBuilder::jsop_funapply(uint32_t argc)
5136 : {
5137 15 : int calleeDepth = -((int)argc + 2);
5138 :
5139 0 : TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
5140 0 : JSFunction* native = getSingleCallTarget(calleeTypes);
5141 15 : if (argc != 2 || info().analysisMode() == Analysis_ArgumentsUsage) {
5142 : CallInfo callInfo(alloc(), pc, /* constructing = */ false,
5143 0 : /* ignoresReturnValue = */ BytecodeIsPopped(pc));
5144 1 : if (!callInfo.init(current, argc))
5145 0 : return abort(AbortReason::Alloc);
5146 15 : return makeCall(native, callInfo);
5147 : }
5148 :
5149 : // Disable compilation if the second argument to |apply| cannot be guaranteed
5150 : // to be either definitely |arguments| or definitely not |arguments|.
5151 0 : MDefinition* argument = current->peek(-1);
5152 0 : if (script()->argumentsHasVarBinding() &&
5153 0 : argument->mightBeType(MIRType::MagicOptimizedArguments) &&
5154 0 : argument->type() != MIRType::MagicOptimizedArguments)
5155 : {
5156 0 : return abort(AbortReason::Disable, "fun.apply with MaybeArguments");
5157 : }
5158 :
5159 : // Fallback to regular call if arg 2 is not definitely |arguments|.
5160 0 : if (argument->type() != MIRType::MagicOptimizedArguments) {
5161 : // Optimize fun.apply(self, array) if the length is sane and there are no holes.
5162 0 : TemporaryTypeSet* objTypes = argument->resultTypeSet();
5163 0 : if (native && native->isNative() && native->native() == fun_apply &&
5164 0 : objTypes &&
5165 0 : objTypes->getKnownClass(constraints()) == &ArrayObject::class_ &&
5166 0 : !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW) &&
5167 0 : ElementAccessIsPacked(constraints(), argument))
5168 : {
5169 0 : return jsop_funapplyarray(argc);
5170 : }
5171 :
5172 : CallInfo callInfo(alloc(), pc, /* constructing = */ false,
5173 0 : /* ignoresReturnValue = */ BytecodeIsPopped(pc));
5174 0 : if (!callInfo.init(current, argc))
5175 0 : return abort(AbortReason::Alloc);
5176 0 : return makeCall(native, callInfo);
5177 : }
5178 :
5179 0 : if ((!native || !native->isNative() ||
5180 0 : native->native() != fun_apply) &&
5181 0 : info().analysisMode() != Analysis_DefiniteProperties)
5182 : {
5183 0 : return abort(AbortReason::Disable, "fun.apply speculation failed");
5184 : }
5185 :
5186 : // Use funapply that definitely uses |arguments|
5187 0 : return jsop_funapplyarguments(argc);
5188 : }
5189 :
5190 : AbortReasonOr<Ok>
5191 1 : IonBuilder::jsop_spreadcall()
5192 : {
5193 : // The arguments array is constructed by a JSOP_NEWARRAY and not
5194 : // leaked to user. The complications of spread call iterator behaviour are
5195 : // handled when the user objects are expanded and copied into this hidden
5196 : // array.
5197 :
5198 : #ifdef DEBUG
5199 : // If we know class, ensure it is what we expected
5200 0 : MDefinition* argument = current->peek(-1);
5201 0 : if (TemporaryTypeSet* objTypes = argument->resultTypeSet())
5202 0 : if (const Class* clasp = objTypes->getKnownClass(constraints()))
5203 0 : MOZ_ASSERT(clasp == &ArrayObject::class_);
5204 : #endif
5205 :
5206 0 : MDefinition* argArr = current->pop();
5207 0 : MDefinition* argThis = current->pop();
5208 1 : MDefinition* argFunc = current->pop();
5209 :
5210 : // Extract call target.
5211 0 : TemporaryTypeSet* funTypes = argFunc->resultTypeSet();
5212 0 : JSFunction* target = getSingleCallTarget(funTypes);
5213 1 : WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
5214 :
5215 : // Dense elements of argument array
5216 0 : MElements* elements = MElements::New(alloc(), argArr);
5217 1 : current->add(elements);
5218 :
5219 0 : MApplyArray* apply = MApplyArray::New(alloc(), wrappedTarget, argFunc, elements, argThis);
5220 0 : current->add(apply);
5221 0 : current->push(apply);
5222 3 : MOZ_TRY(resumeAfter(apply));
5223 :
5224 : // TypeBarrier the call result
5225 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
5226 1 : return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
5227 : }
5228 :
5229 : AbortReasonOr<Ok>
5230 0 : IonBuilder::jsop_funapplyarray(uint32_t argc)
5231 : {
5232 0 : MOZ_ASSERT(argc == 2);
5233 :
5234 0 : int funcDepth = -((int)argc + 1);
5235 :
5236 : // Extract call target.
5237 0 : TemporaryTypeSet* funTypes = current->peek(funcDepth)->resultTypeSet();
5238 0 : JSFunction* target = getSingleCallTarget(funTypes);
5239 :
5240 : // Pop the array agument
5241 0 : MDefinition* argObj = current->pop();
5242 :
5243 0 : MElements* elements = MElements::New(alloc(), argObj);
5244 0 : current->add(elements);
5245 :
5246 : // Pop the |this| argument.
5247 0 : MDefinition* argThis = current->pop();
5248 :
5249 : // Unwrap the (JSFunction *) parameter.
5250 0 : MDefinition* argFunc = current->pop();
5251 :
5252 : // Pop apply function.
5253 0 : MDefinition* nativeFunc = current->pop();
5254 0 : nativeFunc->setImplicitlyUsedUnchecked();
5255 :
5256 0 : WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
5257 0 : MApplyArray* apply = MApplyArray::New(alloc(), wrappedTarget, argFunc, elements, argThis);
5258 0 : current->add(apply);
5259 0 : current->push(apply);
5260 0 : MOZ_TRY(resumeAfter(apply));
5261 :
5262 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
5263 0 : return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
5264 : }
5265 :
5266 : AbortReasonOr<Ok>
5267 0 : CallInfo::savePriorCallStack(MIRGenerator* mir, MBasicBlock* current, size_t peekDepth)
5268 : {
5269 0 : MOZ_ASSERT(priorArgs_.empty());
5270 0 : if (!priorArgs_.reserve(peekDepth))
5271 0 : return mir->abort(AbortReason::Alloc);
5272 0 : while (peekDepth) {
5273 0 : priorArgs_.infallibleAppend(current->peek(0 - int32_t(peekDepth)));
5274 0 : peekDepth--;
5275 : }
5276 0 : return Ok();
5277 : }
5278 :
5279 :
5280 : AbortReasonOr<Ok>
5281 0 : IonBuilder::jsop_funapplyarguments(uint32_t argc)
5282 : {
5283 : // Stack for JSOP_FUNAPPLY:
5284 : // 1: Vp
5285 : // 2: This
5286 : // argc+1: JSFunction*, the 'f' in |f.call()|, in |this| position.
5287 : // argc+2: The native 'apply' function.
5288 :
5289 0 : int funcDepth = -((int)argc + 1);
5290 :
5291 : // Extract call target.
5292 0 : TemporaryTypeSet* funTypes = current->peek(funcDepth)->resultTypeSet();
5293 0 : JSFunction* target = getSingleCallTarget(funTypes);
5294 :
5295 : // When this script isn't inlined, use MApplyArgs,
5296 : // to copy the arguments from the stack and call the function
5297 0 : if (inliningDepth_ == 0 && info().analysisMode() != Analysis_DefiniteProperties) {
5298 : // The array argument corresponds to the arguments object. As the JIT
5299 : // is implicitly reading the arguments object in the next instruction,
5300 : // we need to prevent the deletion of the arguments object from resume
5301 : // points, so that Baseline will behave correctly after a bailout.
5302 0 : MDefinition* vp = current->pop();
5303 0 : vp->setImplicitlyUsedUnchecked();
5304 :
5305 0 : MDefinition* argThis = current->pop();
5306 :
5307 : // Unwrap the (JSFunction*) parameter.
5308 0 : MDefinition* argFunc = current->pop();
5309 :
5310 : // Pop apply function.
5311 0 : MDefinition* nativeFunc = current->pop();
5312 0 : nativeFunc->setImplicitlyUsedUnchecked();
5313 :
5314 0 : MArgumentsLength* numArgs = MArgumentsLength::New(alloc());
5315 0 : current->add(numArgs);
5316 :
5317 0 : WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
5318 0 : MApplyArgs* apply = MApplyArgs::New(alloc(), wrappedTarget, argFunc, numArgs, argThis);
5319 0 : current->add(apply);
5320 0 : current->push(apply);
5321 0 : MOZ_TRY(resumeAfter(apply));
5322 :
5323 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
5324 0 : return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
5325 : }
5326 :
5327 : // When inlining we have the arguments the function gets called with
5328 : // and can optimize even more, by just calling the functions with the args.
5329 : // We also try this path when doing the definite properties analysis, as we
5330 : // can inline the apply() target and don't care about the actual arguments
5331 : // that were passed in.
5332 :
5333 : CallInfo callInfo(alloc(), pc, /* constructing = */ false,
5334 0 : /* ignoresReturnValue = */ BytecodeIsPopped(pc));
5335 0 : MOZ_TRY(callInfo.savePriorCallStack(this, current, 4));
5336 :
5337 : // Vp
5338 0 : MDefinition* vp = current->pop();
5339 0 : vp->setImplicitlyUsedUnchecked();
5340 :
5341 : // Arguments
5342 0 : if (inliningDepth_) {
5343 0 : if (!callInfo.setArgs(inlineCallInfo_->argv()))
5344 0 : return abort(AbortReason::Alloc);
5345 : }
5346 :
5347 : // This
5348 0 : MDefinition* argThis = current->pop();
5349 0 : callInfo.setThis(argThis);
5350 :
5351 : // Pop function parameter.
5352 0 : MDefinition* argFunc = current->pop();
5353 0 : callInfo.setFun(argFunc);
5354 :
5355 : // Pop apply function.
5356 0 : MDefinition* nativeFunc = current->pop();
5357 0 : nativeFunc->setImplicitlyUsedUnchecked();
5358 :
5359 : // Try to inline the call.
5360 0 : InliningDecision decision = makeInliningDecision(target, callInfo);
5361 0 : switch (decision) {
5362 : case InliningDecision_Error:
5363 0 : return abort(AbortReason::Error);
5364 : case InliningDecision_DontInline:
5365 : case InliningDecision_WarmUpCountTooLow:
5366 : break;
5367 : case InliningDecision_Inline: {
5368 : InliningStatus status;
5369 0 : MOZ_TRY_VAR(status, inlineSingleCall(callInfo, target));
5370 0 : if (status == InliningStatus_Inlined)
5371 0 : return Ok();
5372 : }
5373 : }
5374 :
5375 0 : return makeCall(target, callInfo);
5376 : }
5377 :
5378 : AbortReasonOr<Ok>
5379 2588 : IonBuilder::jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue)
5380 : {
5381 2588 : startTrackingOptimizations();
5382 :
5383 : // If this call has never executed, try to seed the observed type set
5384 : // based on how the call result is used.
5385 0 : TemporaryTypeSet* observed = bytecodeTypes(pc);
5386 0 : if (observed->empty()) {
5387 0 : if (BytecodeFlowsToBitop(pc)) {
5388 0 : observed->addType(TypeSet::Int32Type(), alloc_->lifoAlloc());
5389 1890 : } else if (*GetNextPc(pc) == JSOP_POS) {
5390 : // Note: this is lame, overspecialized on the code patterns used
5391 : // by asm.js and should be replaced by a more general mechanism.
5392 : // See bug 870847.
5393 0 : observed->addType(TypeSet::DoubleType(), alloc_->lifoAlloc());
5394 : }
5395 : }
5396 :
5397 2588 : int calleeDepth = -((int)argc + 2 + constructing);
5398 :
5399 : // Acquire known call target if existent.
5400 0 : InliningTargets targets(alloc());
5401 0 : TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
5402 0 : if (calleeTypes)
5403 7707 : MOZ_TRY(getPolyCallTargets(calleeTypes, constructing, targets, 4));
5404 :
5405 0 : CallInfo callInfo(alloc(), pc, constructing, ignoresReturnValue);
5406 1 : if (!callInfo.init(current, argc))
5407 0 : return abort(AbortReason::Alloc);
5408 :
5409 : // Try inlining
5410 : InliningStatus status;
5411 0 : MOZ_TRY_VAR(status, inlineCallsite(targets, callInfo));
5412 0 : if (status == InliningStatus_Inlined)
5413 1325 : return Ok();
5414 :
5415 : // Discard unreferenced & pre-allocated resume points.
5416 1263 : replaceMaybeFallbackFunctionGetter(nullptr);
5417 :
5418 : // No inline, just make the call.
5419 0 : Maybe<CallTargets> callTargets;
5420 0 : if (!targets.empty()) {
5421 0 : callTargets.emplace(alloc());
5422 0 : for (const InliningTarget& target : targets) {
5423 0 : if (!target.target->is<JSFunction>()) {
5424 0 : callTargets = Nothing();
5425 0 : break;
5426 : }
5427 1 : if (!callTargets->append(&target.target->as<JSFunction>()))
5428 0 : return abort(AbortReason::Alloc);
5429 : }
5430 : }
5431 :
5432 0 : if (status == InliningStatus_WarmUpCountTooLow &&
5433 0 : callTargets &&
5434 18 : callTargets->length() == 1)
5435 : {
5436 18 : JSFunction* target = callTargets.ref()[0];
5437 : MRecompileCheck* check =
5438 0 : MRecompileCheck::New(alloc(), target->nonLazyScript(),
5439 0 : optimizationInfo().inliningRecompileThreshold(),
5440 0 : MRecompileCheck::RecompileCheck_Inlining);
5441 18 : current->add(check);
5442 : }
5443 :
5444 1263 : return makeCall(callTargets, callInfo);
5445 : }
5446 :
5447 : AbortReasonOr<bool>
5448 0 : IonBuilder::testShouldDOMCall(TypeSet* inTypes, JSFunction* func, JSJitInfo::OpType opType)
5449 : {
5450 0 : if (!func->isNative() || !func->hasJitInfo())
5451 0 : return false;
5452 :
5453 : // If all the DOM objects flowing through are legal with this
5454 : // property, we can bake in a call to the bottom half of the DOM
5455 : // accessor
5456 : DOMInstanceClassHasProtoAtDepth instanceChecker =
5457 0 : realm->runtime()->DOMcallbacks()->instanceClassMatchesProto;
5458 :
5459 0 : const JSJitInfo* jinfo = func->jitInfo();
5460 0 : if (jinfo->type() != opType)
5461 0 : return false;
5462 :
5463 0 : for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
5464 0 : TypeSet::ObjectKey* key = inTypes->getObject(i);
5465 0 : if (!key)
5466 : continue;
5467 :
5468 0 : if (!alloc().ensureBallast())
5469 0 : return abort(AbortReason::Alloc);
5470 :
5471 0 : if (!key->hasStableClassAndProto(constraints()))
5472 0 : return false;
5473 :
5474 0 : if (!instanceChecker(key->clasp(), jinfo->protoID, jinfo->depth))
5475 0 : return false;
5476 : }
5477 :
5478 0 : return true;
5479 : }
5480 :
5481 : static bool
5482 119 : ArgumentTypesMatch(MDefinition* def, StackTypeSet* calleeTypes)
5483 : {
5484 119 : if (!calleeTypes)
5485 : return false;
5486 :
5487 0 : if (def->resultTypeSet()) {
5488 0 : MOZ_ASSERT(def->type() == MIRType::Value || def->mightBeType(def->type()));
5489 68 : return def->resultTypeSet()->isSubset(calleeTypes);
5490 : }
5491 :
5492 45 : if (def->type() == MIRType::Value)
5493 : return false;
5494 :
5495 0 : if (def->type() == MIRType::Object)
5496 0 : return calleeTypes->unknownObject();
5497 :
5498 34 : return calleeTypes->mightBeMIRType(def->type());
5499 : }
5500 :
5501 : bool
5502 320 : IonBuilder::testNeedsArgumentCheck(JSFunction* target, CallInfo& callInfo)
5503 : {
5504 : // If we have a known target, check if the caller arg types are a subset of callee.
5505 : // Since typeset accumulates and can't decrease that means we don't need to check
5506 : // the arguments anymore.
5507 :
5508 320 : if (target->isNative())
5509 : return false;
5510 :
5511 71 : if (!target->hasScript())
5512 : return true;
5513 :
5514 67 : JSScript* targetScript = target->nonLazyScript();
5515 :
5516 67 : if (!ArgumentTypesMatch(callInfo.thisArg(), TypeScript::ThisTypes(targetScript)))
5517 : return true;
5518 0 : uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->nargs());
5519 0 : for (size_t i = 0; i < expected_args; i++) {
5520 52 : if (!ArgumentTypesMatch(callInfo.getArg(i), TypeScript::ArgTypes(targetScript, i)))
5521 : return true;
5522 : }
5523 0 : for (size_t i = callInfo.argc(); i < target->nargs(); i++) {
5524 1 : if (!TypeScript::ArgTypes(targetScript, i)->mightBeMIRType(MIRType::Undefined))
5525 : return true;
5526 : }
5527 :
5528 : return false;
5529 : }
5530 :
5531 : AbortReasonOr<MCall*>
5532 1285 : IonBuilder::makeCallHelper(const Maybe<CallTargets>& targets, CallInfo& callInfo)
5533 : {
5534 : // This function may be called with mutated stack.
5535 : // Querying TI for popped types is invalid.
5536 :
5537 1605 : MOZ_ASSERT_IF(targets, !targets->empty());
5538 :
5539 0 : JSFunction* target = nullptr;
5540 0 : if (targets && targets->length() == 1)
5541 318 : target = targets.ref()[0];
5542 :
5543 1285 : uint32_t targetArgs = callInfo.argc();
5544 :
5545 : // Collect number of missing arguments provided that the target is
5546 : // scripted. Native functions are passed an explicit 'argc' parameter.
5547 0 : if (target && !target->isNativeWithCppEntry())
5548 69 : targetArgs = Max<uint32_t>(target->nargs(), callInfo.argc());
5549 :
5550 0 : bool isDOMCall = false;
5551 0 : DOMObjectKind objKind = DOMObjectKind::Unknown;
5552 1603 : if (target && !callInfo.constructing()) {
5553 : // We know we have a single call target. Check whether the "this" types
5554 : // are DOM types and our function a DOM function, and if so flag the
5555 : // MCall accordingly.
5556 0 : TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
5557 0 : if (thisTypes &&
5558 0 : thisTypes->getKnownMIRType() == MIRType::Object &&
5559 64 : thisTypes->isDOMClass(constraints(), &objKind))
5560 : {
5561 0 : MOZ_TRY_VAR(isDOMCall, testShouldDOMCall(thisTypes, target, JSJitInfo::Method));
5562 : }
5563 : }
5564 :
5565 0 : MCall* call = MCall::New(alloc(), target, targetArgs + 1 + callInfo.constructing(),
5566 0 : callInfo.argc(), callInfo.constructing(),
5567 0 : callInfo.ignoresReturnValue(), isDOMCall, objKind);
5568 0 : if (!call)
5569 0 : return abort(AbortReason::Alloc);
5570 :
5571 0 : if (callInfo.constructing())
5572 83 : call->addArg(targetArgs + 1, callInfo.getNewTarget());
5573 :
5574 : // Explicitly pad any missing arguments with |undefined|.
5575 : // This permits skipping the argumentsRectifier.
5576 0 : MOZ_ASSERT_IF(target && targetArgs > callInfo.argc(), !target->isNativeWithCppEntry());
5577 0 : for (int i = targetArgs; i > (int)callInfo.argc(); i--) {
5578 0 : MConstant* undef = constant(UndefinedValue());
5579 1 : if (!alloc().ensureBallast())
5580 0 : return abort(AbortReason::Alloc);
5581 4 : call->addArg(i, undef);
5582 : }
5583 :
5584 : // Add explicit arguments.
5585 : // Skip addArg(0) because it is reserved for this
5586 0 : for (int32_t i = callInfo.argc() - 1; i >= 0; i--)
5587 1704 : call->addArg(i + 1, callInfo.getArg(i));
5588 :
5589 : // Now that we've told it about all the args, compute whether it's movable
5590 1285 : call->computeMovable();
5591 :
5592 : // Inline the constructor on the caller-side.
5593 0 : if (callInfo.constructing()) {
5594 0 : MDefinition* create = createThis(target, callInfo.fun(), callInfo.getNewTarget());
5595 1 : if (!create)
5596 0 : return abort(AbortReason::Disable, "Failure inlining constructor for call.");
5597 :
5598 0 : callInfo.thisArg()->setImplicitlyUsedUnchecked();
5599 83 : callInfo.setThis(create);
5600 : }
5601 :
5602 : // Pass |this| and function.
5603 0 : MDefinition* thisArg = callInfo.thisArg();
5604 1285 : call->addArg(0, thisArg);
5605 :
5606 1285 : if (targets) {
5607 : // The callee must be one of the target JSFunctions, so we don't need a
5608 : // Class check.
5609 320 : call->disableClassCheck();
5610 :
5611 : // Determine whether we can skip the callee's prologue type checks.
5612 0 : bool needArgCheck = false;
5613 0 : for (JSFunction* target : targets.ref()) {
5614 320 : if (testNeedsArgumentCheck(target, callInfo)) {
5615 : needArgCheck = true;
5616 : break;
5617 : }
5618 : }
5619 320 : if (!needArgCheck)
5620 : call->disableArgCheck();
5621 : }
5622 :
5623 1285 : call->initFunction(callInfo.fun());
5624 :
5625 0 : current->add(call);
5626 1285 : return call;
5627 : }
5628 :
5629 : static bool
5630 0 : DOMCallNeedsBarrier(const JSJitInfo* jitinfo, TemporaryTypeSet* types)
5631 : {
5632 0 : MOZ_ASSERT(jitinfo->type() != JSJitInfo::InlinableNative);
5633 :
5634 : // If the return type of our DOM native is in "types" already, we don't
5635 : // actually need a barrier.
5636 0 : if (jitinfo->returnType() == JSVAL_TYPE_UNKNOWN)
5637 : return true;
5638 :
5639 : // JSVAL_TYPE_OBJECT doesn't tell us much; we still have to barrier on the
5640 : // actual type of the object.
5641 0 : if (jitinfo->returnType() == JSVAL_TYPE_OBJECT)
5642 : return true;
5643 :
5644 : // No need for a barrier if we're already expecting the type we'll produce.
5645 0 : return MIRTypeFromValueType(jitinfo->returnType()) != types->getKnownMIRType();
5646 : }
5647 :
5648 : AbortReasonOr<Ok>
5649 1285 : IonBuilder::makeCall(const Maybe<CallTargets>& targets, CallInfo& callInfo)
5650 : {
5651 : #ifdef DEBUG
5652 : // Constructor calls to non-constructors should throw. We don't want to use
5653 : // CallKnown in this case.
5654 0 : if (callInfo.constructing() && targets) {
5655 0 : for (JSFunction* target : targets.ref())
5656 15 : MOZ_ASSERT(target->isConstructor());
5657 : }
5658 : #endif
5659 :
5660 : MCall* call;
5661 5140 : MOZ_TRY_VAR(call, makeCallHelper(targets, callInfo));
5662 :
5663 0 : current->push(call);
5664 0 : if (call->isEffectful())
5665 5140 : MOZ_TRY(resumeAfter(call));
5666 :
5667 2570 : TemporaryTypeSet* types = bytecodeTypes(pc);
5668 :
5669 0 : if (call->isCallDOMNative())
5670 0 : return pushDOMTypeBarrier(call, types, call->getSingleTarget()->rawJSFunction());
5671 :
5672 1285 : return pushTypeBarrier(call, types, BarrierKind::TypeSet);
5673 : }
5674 :
5675 : AbortReasonOr<Ok>
5676 19 : IonBuilder::makeCall(JSFunction* target, CallInfo& callInfo)
5677 : {
5678 0 : Maybe<CallTargets> targets;
5679 0 : if (target) {
5680 0 : targets.emplace(alloc());
5681 1 : if (!targets->append(target))
5682 0 : return abort(AbortReason::Alloc);
5683 : }
5684 19 : return makeCall(targets, callInfo);
5685 : }
5686 :
5687 : AbortReasonOr<Ok>
5688 0 : IonBuilder::jsop_eval(uint32_t argc)
5689 : {
5690 0 : int calleeDepth = -((int)argc + 2);
5691 0 : TemporaryTypeSet* calleeTypes = current->peek(calleeDepth)->resultTypeSet();
5692 :
5693 : // Emit a normal call if the eval has never executed. This keeps us from
5694 : // disabling compilation for the script when testing with --ion-eager.
5695 0 : if (calleeTypes && calleeTypes->empty())
5696 0 : return jsop_call(argc, /* constructing = */ false, false);
5697 :
5698 0 : JSFunction* target = getSingleCallTarget(calleeTypes);
5699 0 : if (!target)
5700 0 : return abort(AbortReason::Disable, "No single callee for eval()");
5701 :
5702 0 : if (script()->global().valueIsEval(ObjectValue(*target))) {
5703 0 : if (argc != 1)
5704 0 : return abort(AbortReason::Disable, "Direct eval with more than one argument");
5705 :
5706 0 : if (!info().funMaybeLazy())
5707 0 : return abort(AbortReason::Disable, "Direct eval in global code");
5708 :
5709 0 : if (info().funMaybeLazy()->isArrow())
5710 0 : return abort(AbortReason::Disable, "Direct eval from arrow function");
5711 :
5712 : CallInfo callInfo(alloc(), pc, /* constructing = */ false,
5713 0 : /* ignoresReturnValue = */ BytecodeIsPopped(pc));
5714 0 : if (!callInfo.init(current, argc))
5715 0 : return abort(AbortReason::Alloc);
5716 0 : callInfo.setImplicitlyUsedUnchecked();
5717 :
5718 0 : callInfo.fun()->setImplicitlyUsedUnchecked();
5719 :
5720 0 : MDefinition* envChain = current->environmentChain();
5721 0 : MDefinition* string = callInfo.getArg(0);
5722 :
5723 : // Direct eval acts as identity on non-string types according to
5724 : // ES5 15.1.2.1 step 1.
5725 0 : if (!string->mightBeType(MIRType::String)) {
5726 0 : current->push(string);
5727 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
5728 0 : return pushTypeBarrier(string, types, BarrierKind::TypeSet);
5729 : }
5730 :
5731 0 : MOZ_TRY(jsop_newtarget());
5732 0 : MDefinition* newTargetValue = current->pop();
5733 :
5734 : // Try to pattern match 'eval(v + "()")'. In this case v is likely a
5735 : // name on the env chain and the eval is performing a call on that
5736 : // value. Use an env chain lookup rather than a full eval.
5737 0 : if (string->isConcat() &&
5738 0 : string->getOperand(1)->type() == MIRType::String &&
5739 0 : string->getOperand(1)->maybeConstantValue())
5740 : {
5741 0 : JSAtom* atom = &string->getOperand(1)->maybeConstantValue()->toString()->asAtom();
5742 :
5743 0 : if (StringEqualsAscii(atom, "()")) {
5744 0 : MDefinition* name = string->getOperand(0);
5745 0 : MInstruction* dynamicName = MGetDynamicName::New(alloc(), envChain, name);
5746 0 : current->add(dynamicName);
5747 :
5748 0 : current->push(dynamicName);
5749 0 : current->push(constant(UndefinedValue())); // thisv
5750 :
5751 : CallInfo evalCallInfo(alloc(), pc, /* constructing = */ false,
5752 0 : /* ignoresReturnValue = */ BytecodeIsPopped(pc));
5753 0 : if (!evalCallInfo.init(current, /* argc = */ 0))
5754 0 : return abort(AbortReason::Alloc);
5755 :
5756 0 : return makeCall(nullptr, evalCallInfo);
5757 : }
5758 : }
5759 :
5760 0 : MInstruction* ins = MCallDirectEval::New(alloc(), envChain, string,
5761 0 : newTargetValue, pc);
5762 0 : current->add(ins);
5763 0 : current->push(ins);
5764 :
5765 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
5766 0 : MOZ_TRY(resumeAfter(ins));
5767 0 : return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
5768 : }
5769 :
5770 0 : return jsop_call(argc, /* constructing = */ false, false);
5771 : }
5772 :
5773 : AbortReasonOr<Ok>
5774 1016 : IonBuilder::jsop_compare(JSOp op)
5775 : {
5776 0 : MDefinition* right = current->pop();
5777 1016 : MDefinition* left = current->pop();
5778 :
5779 1016 : return jsop_compare(op, left, right);
5780 : }
5781 :
5782 : AbortReasonOr<Ok>
5783 1016 : IonBuilder::jsop_compare(JSOp op, MDefinition* left, MDefinition* right)
5784 : {
5785 0 : bool emitted = false;
5786 1016 : startTrackingOptimizations();
5787 :
5788 0 : if (!forceInlineCaches()) {
5789 0 : MOZ_TRY(compareTrySpecialized(&emitted, op, left, right, true));
5790 0 : if (emitted)
5791 0 : return Ok();
5792 0 : MOZ_TRY(compareTryBitwise(&emitted, op, left, right));
5793 0 : if (emitted)
5794 0 : return Ok();
5795 0 : MOZ_TRY(compareTrySpecializedOnBaselineInspector(&emitted, op, left, right));
5796 0 : if (emitted)
5797 0 : return Ok();
5798 : }
5799 :
5800 0 : MOZ_TRY(compareTrySharedStub(&emitted, left, right));
5801 0 : if (emitted)
5802 95 : return Ok();
5803 :
5804 0 : trackOptimizationAttempt(TrackedStrategy::Compare_Call);
5805 :
5806 : // Not possible to optimize. Do a slow vm call.
5807 0 : MCompare* ins = MCompare::New(alloc(), left, right, op);
5808 0 : ins->cacheOperandMightEmulateUndefined(constraints());
5809 :
5810 0 : current->add(ins);
5811 0 : current->push(ins);
5812 0 : if (ins->isEffectful())
5813 0 : MOZ_TRY(resumeAfter(ins));
5814 :
5815 0 : trackOptimizationSuccess();
5816 0 : return Ok();
5817 : }
5818 :
5819 : static bool
5820 217 : ObjectOrSimplePrimitive(MDefinition* op)
5821 : {
5822 : // Return true if op is either undefined/null/boolean/int32/symbol or an object.
5823 0 : return !op->mightBeType(MIRType::String)
5824 0 : && !op->mightBeType(MIRType::Double)
5825 0 : && !op->mightBeType(MIRType::Float32)
5826 0 : && !op->mightBeType(MIRType::MagicOptimizedArguments)
5827 0 : && !op->mightBeType(MIRType::MagicHole)
5828 427 : && !op->mightBeType(MIRType::MagicIsConstructing);
5829 : }
5830 :
5831 : AbortReasonOr<Ok>
5832 1016 : IonBuilder::compareTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right,
5833 : bool canTrackOptimization)
5834 : {
5835 0 : MOZ_ASSERT(*emitted == false);
5836 1016 : if (canTrackOptimization)
5837 : trackOptimizationAttempt(TrackedStrategy::Compare_SpecializedTypes);
5838 :
5839 : // Try to emit an compare based on the input types.
5840 :
5841 0 : MCompare::CompareType type = MCompare::determineCompareType(op, left, right);
5842 0 : if (type == MCompare::Compare_Unknown) {
5843 200 : if (canTrackOptimization)
5844 : trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
5845 200 : return Ok();
5846 : }
5847 :
5848 0 : MCompare* ins = MCompare::New(alloc(), left, right, op);
5849 0 : ins->setCompareType(type);
5850 816 : ins->cacheOperandMightEmulateUndefined(constraints());
5851 :
5852 : // Some compare types need to have the specific type in the rhs.
5853 : // Swap operands if that is not the case.
5854 0 : if (type == MCompare::Compare_StrictString && right->type() != MIRType::String)
5855 0 : ins->swapOperands();
5856 0 : else if (type == MCompare::Compare_Null && right->type() != MIRType::Null)
5857 0 : ins->swapOperands();
5858 0 : else if (type == MCompare::Compare_Undefined && right->type() != MIRType::Undefined)
5859 0 : ins->swapOperands();
5860 0 : else if (type == MCompare::Compare_Boolean && right->type() != MIRType::Boolean)
5861 0 : ins->swapOperands();
5862 :
5863 : // Replace inputs with unsigned variants if needed.
5864 0 : if (type == MCompare::Compare_UInt32)
5865 2 : ins->replaceWithUnsignedOperands();
5866 :
5867 0 : current->add(ins);
5868 816 : current->push(ins);
5869 :
5870 0 : MOZ_ASSERT(!ins->isEffectful());
5871 816 : if (canTrackOptimization)
5872 : trackOptimizationSuccess();
5873 0 : *emitted = true;
5874 816 : return Ok();
5875 : }
5876 :
5877 : AbortReasonOr<Ok>
5878 200 : IonBuilder::compareTryBitwise(bool* emitted, JSOp op, MDefinition* left, MDefinition* right)
5879 : {
5880 0 : MOZ_ASSERT(*emitted == false);
5881 200 : trackOptimizationAttempt(TrackedStrategy::Compare_Bitwise);
5882 :
5883 : // Try to emit a bitwise compare. Check if a bitwise compare equals the wanted
5884 : // result for all observed operand types.
5885 :
5886 : // Only allow loose and strict equality.
5887 0 : if (op != JSOP_EQ && op != JSOP_NE && op != JSOP_STRICTEQ && op != JSOP_STRICTNE) {
5888 0 : trackOptimizationOutcome(TrackedOutcome::RelationalCompare);
5889 88 : return Ok();
5890 : }
5891 :
5892 : // Only primitive (not double/string) or objects are supported.
5893 : // I.e. Undefined/Null/Boolean/Int32/Symbol and Object
5894 0 : if (!ObjectOrSimplePrimitive(left) || !ObjectOrSimplePrimitive(right)) {
5895 0 : trackOptimizationOutcome(TrackedOutcome::OperandTypeNotBitwiseComparable);
5896 7 : return Ok();
5897 : }
5898 :
5899 : // Objects that emulate undefined are not supported.
5900 0 : if (left->maybeEmulatesUndefined(constraints()) ||
5901 105 : right->maybeEmulatesUndefined(constraints()))
5902 : {
5903 0 : trackOptimizationOutcome(TrackedOutcome::OperandMaybeEmulatesUndefined);
5904 0 : return Ok();
5905 : }
5906 :
5907 : // In the loose comparison more values could be the same,
5908 : // but value comparison reporting otherwise.
5909 105 : if (op == JSOP_EQ || op == JSOP_NE) {
5910 :
5911 : // Undefined compared loosy to Null is not supported,
5912 : // because tag is different, but value can be the same (undefined == null).
5913 0 : if ((left->mightBeType(MIRType::Undefined) && right->mightBeType(MIRType::Null)) ||
5914 3 : (left->mightBeType(MIRType::Null) && right->mightBeType(MIRType::Undefined)))
5915 : {
5916 0 : trackOptimizationOutcome(TrackedOutcome::LoosyUndefinedNullCompare);
5917 0 : return Ok();
5918 : }
5919 :
5920 : // Int32 compared loosy to Boolean is not supported,
5921 : // because tag is different, but value can be the same (1 == true).
5922 0 : if ((left->mightBeType(MIRType::Int32) && right->mightBeType(MIRType::Boolean)) ||
5923 3 : (left->mightBeType(MIRType::Boolean) && right->mightBeType(MIRType::Int32)))
5924 : {
5925 0 : trackOptimizationOutcome(TrackedOutcome::LoosyInt32BooleanCompare);
5926 0 : return Ok();
5927 : }
5928 :
5929 : // For loosy comparison of an object with a Boolean/Number/String/Symbol
5930 : // the valueOf the object is taken. Therefore not supported.
5931 0 : bool simpleLHS = left->mightBeType(MIRType::Boolean) ||
5932 0 : left->mightBeType(MIRType::Int32) ||
5933 0 : left->mightBeType(MIRType::Symbol);
5934 0 : bool simpleRHS = right->mightBeType(MIRType::Boolean) ||
5935 0 : right->mightBeType(MIRType::Int32) ||
5936 0 : right->mightBeType(MIRType::Symbol);
5937 0 : if ((left->mightBeType(MIRType::Object) && simpleRHS) ||
5938 3 : (right->mightBeType(MIRType::Object) && simpleLHS))
5939 : {
5940 0 : trackOptimizationOutcome(TrackedOutcome::CallsValueOf);
5941 0 : return Ok();
5942 : }
5943 : }
5944 :
5945 0 : MCompare* ins = MCompare::New(alloc(), left, right, op);
5946 0 : ins->setCompareType(MCompare::Compare_Bitwise);
5947 105 : ins->cacheOperandMightEmulateUndefined(constraints());
5948 :
5949 0 : current->add(ins);
5950 105 : current->push(ins);
5951 :
5952 0 : MOZ_ASSERT(!ins->isEffectful());
5953 0 : trackOptimizationSuccess();
5954 0 : *emitted = true;
5955 105 : return Ok();
5956 : }
5957 :
5958 : AbortReasonOr<Ok>
5959 95 : IonBuilder::compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
5960 : MDefinition* right)
5961 : {
5962 0 : MOZ_ASSERT(*emitted == false);
5963 95 : trackOptimizationAttempt(TrackedStrategy::Compare_SpecializedOnBaselineTypes);
5964 :
5965 : // Try to specialize based on any baseline caches that have been generated
5966 : // for the opcode. These will cause the instruction's type policy to insert
5967 : // fallible unboxes to the appropriate input types.
5968 :
5969 : // Strict equality isn't supported.
5970 0 : if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE) {
5971 0 : trackOptimizationOutcome(TrackedOutcome::StrictCompare);
5972 7 : return Ok();
5973 : }
5974 :
5975 0 : MCompare::CompareType type = inspector->expectedCompareType(pc);
5976 0 : if (type == MCompare::Compare_Unknown) {
5977 0 : trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
5978 88 : return Ok();
5979 : }
5980 :
5981 0 : MCompare* ins = MCompare::New(alloc(), left, right, op);
5982 0 : ins->setCompareType(type);
5983 0 : ins->cacheOperandMightEmulateUndefined(constraints());
5984 :
5985 0 : current->add(ins);
5986 0 : current->push(ins);
5987 :
5988 0 : MOZ_ASSERT(!ins->isEffectful());
5989 0 : trackOptimizationSuccess();
5990 0 : *emitted = true;
5991 0 : return Ok();
5992 : }
5993 :
5994 : AbortReasonOr<Ok>
5995 95 : IonBuilder::compareTrySharedStub(bool* emitted, MDefinition* left, MDefinition* right)
5996 : {
5997 95 : MOZ_ASSERT(*emitted == false);
5998 :
5999 : // Try to emit a shared stub cache.
6000 :
6001 1 : if (JitOptions.disableSharedStubs)
6002 0 : return Ok();
6003 :
6004 0 : if (JSOp(*pc) == JSOP_CASE)
6005 0 : return Ok();
6006 :
6007 95 : trackOptimizationAttempt(TrackedStrategy::Compare_SharedCache);
6008 :
6009 0 : MBinarySharedStub* stub = MBinarySharedStub::New(alloc(), left, right);
6010 0 : current->add(stub);
6011 0 : current->push(stub);
6012 285 : MOZ_TRY(resumeAfter(stub));
6013 :
6014 0 : MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Boolean, MUnbox::Infallible);
6015 0 : current->add(unbox);
6016 95 : current->push(unbox);
6017 :
6018 0 : trackOptimizationSuccess();
6019 0 : *emitted = true;
6020 95 : return Ok();
6021 : }
6022 :
6023 : AbortReasonOr<Ok>
6024 111 : IonBuilder::newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, uint32_t length)
6025 : {
6026 111 : MOZ_ASSERT(*emitted == false);
6027 :
6028 : // TODO: Support tracking optimizations for inlining a call and regular
6029 : // optimization tracking at the same time. Currently just drop optimization
6030 : // tracking when that happens.
6031 222 : bool canTrackOptimization = !IsCallPC(pc);
6032 :
6033 111 : if (canTrackOptimization)
6034 : trackOptimizationAttempt(TrackedStrategy::NewArray_TemplateObject);
6035 :
6036 0 : if (!templateObject) {
6037 16 : if (canTrackOptimization)
6038 : trackOptimizationOutcome(TrackedOutcome::NoTemplateObject);
6039 16 : return Ok();
6040 : }
6041 :
6042 95 : MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT);
6043 :
6044 : size_t arraySlots =
6045 190 : gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER;
6046 :
6047 0 : if (length > arraySlots) {
6048 0 : if (canTrackOptimization)
6049 : trackOptimizationOutcome(TrackedOutcome::LengthTooBig);
6050 0 : return Ok();
6051 : }
6052 :
6053 : // Emit fastpath.
6054 :
6055 0 : gc::InitialHeap heap = templateObject->group()->initialHeap(constraints());
6056 0 : MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
6057 95 : current->add(templateConst);
6058 :
6059 0 : MNewArray* ins = MNewArray::New(alloc(), constraints(), length, templateConst, heap, pc);
6060 0 : current->add(ins);
6061 95 : current->push(ins);
6062 :
6063 95 : if (canTrackOptimization)
6064 : trackOptimizationSuccess();
6065 0 : *emitted = true;
6066 95 : return Ok();
6067 : }
6068 :
6069 : AbortReasonOr<Ok>
6070 16 : IonBuilder::newArrayTrySharedStub(bool* emitted)
6071 : {
6072 16 : MOZ_ASSERT(*emitted == false);
6073 :
6074 : // TODO: Support tracking optimizations for inlining a call and regular
6075 : // optimization tracking at the same time. Currently just drop optimization
6076 : // tracking when that happens.
6077 32 : bool canTrackOptimization = !IsCallPC(pc);
6078 :
6079 : // Try to emit a shared stub cache.
6080 :
6081 1 : if (JitOptions.disableSharedStubs)
6082 0 : return Ok();
6083 :
6084 0 : if (*pc != JSOP_NEWINIT && *pc != JSOP_NEWARRAY)
6085 0 : return Ok();
6086 :
6087 16 : if (canTrackOptimization)
6088 : trackOptimizationAttempt(TrackedStrategy::NewArray_SharedCache);
6089 :
6090 0 : MInstruction* stub = MNullarySharedStub::New(alloc());
6091 0 : current->add(stub);
6092 16 : current->push(stub);
6093 :
6094 48 : MOZ_TRY(resumeAfter(stub));
6095 :
6096 0 : MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Object, MUnbox::Infallible);
6097 0 : current->add(unbox);
6098 16 : current->push(unbox);
6099 :
6100 16 : if (canTrackOptimization)
6101 : trackOptimizationSuccess();
6102 :
6103 0 : *emitted = true;
6104 16 : return Ok();
6105 : }
6106 :
6107 : AbortReasonOr<Ok>
6108 0 : IonBuilder::newArrayTryVM(bool* emitted, JSObject* templateObject, uint32_t length)
6109 : {
6110 0 : MOZ_ASSERT(*emitted == false);
6111 :
6112 : // TODO: Support tracking optimizations for inlining a call and regular
6113 : // optimization tracking at the same time. Currently just drop optimization
6114 : // tracking when that happens.
6115 0 : bool canTrackOptimization = !IsCallPC(pc);
6116 :
6117 : // Emit a VM call.
6118 0 : if (canTrackOptimization)
6119 : trackOptimizationAttempt(TrackedStrategy::NewArray_Call);
6120 :
6121 0 : gc::InitialHeap heap = gc::DefaultHeap;
6122 0 : MConstant* templateConst = MConstant::New(alloc(), NullValue());
6123 :
6124 0 : if (templateObject) {
6125 0 : heap = templateObject->group()->initialHeap(constraints());
6126 0 : templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
6127 : }
6128 :
6129 0 : current->add(templateConst);
6130 :
6131 0 : MNewArray* ins = MNewArray::NewVM(alloc(), constraints(), length, templateConst, heap, pc);
6132 0 : current->add(ins);
6133 0 : current->push(ins);
6134 :
6135 0 : if (canTrackOptimization)
6136 : trackOptimizationSuccess();
6137 0 : *emitted = true;
6138 0 : return Ok();
6139 : }
6140 :
6141 : AbortReasonOr<Ok>
6142 110 : IonBuilder::jsop_newarray(uint32_t length)
6143 : {
6144 0 : JSObject* templateObject = inspector->getTemplateObject(pc);
6145 330 : MOZ_TRY(jsop_newarray(templateObject, length));
6146 :
6147 : // Improve resulting typeset.
6148 0 : ObjectGroup* templateGroup = inspector->getTemplateObjectGroup(pc);
6149 0 : if (templateGroup) {
6150 0 : TemporaryTypeSet* types = MakeSingletonTypeSet(alloc(), constraints(), templateGroup);
6151 109 : current->peek(-1)->setResultTypeSet(types);
6152 : }
6153 :
6154 110 : return Ok();
6155 : }
6156 :
6157 : AbortReasonOr<Ok>
6158 111 : IonBuilder::jsop_newarray(JSObject* templateObject, uint32_t length)
6159 : {
6160 : // TODO: Support tracking optimizations for inlining a call and regular
6161 : // optimization tracking at the same time. Currently just drop optimization
6162 : // tracking when that happens.
6163 222 : bool canTrackOptimization = !IsCallPC(pc);
6164 :
6165 0 : bool emitted = false;
6166 0 : if (canTrackOptimization)
6167 110 : startTrackingOptimizations();
6168 :
6169 0 : if (!forceInlineCaches()) {
6170 0 : MOZ_TRY(newArrayTryTemplateObject(&emitted, templateObject, length));
6171 0 : if (emitted)
6172 95 : return Ok();
6173 : }
6174 :
6175 0 : MOZ_TRY(newArrayTrySharedStub(&emitted));
6176 0 : if (emitted)
6177 16 : return Ok();
6178 :
6179 0 : MOZ_TRY(newArrayTryVM(&emitted, templateObject, length));
6180 0 : if (emitted)
6181 0 : return Ok();
6182 :
6183 0 : MOZ_CRASH("newarray should have been emited");
6184 : }
6185 :
6186 : AbortReasonOr<Ok>
6187 0 : IonBuilder::jsop_newarray_copyonwrite()
6188 : {
6189 0 : ArrayObject* templateObject = ObjectGroup::getCopyOnWriteObject(script(), pc);
6190 :
6191 : // The baseline compiler should have ensured the template object has a type
6192 : // with the copy on write flag set already. During the arguments usage
6193 : // analysis the baseline compiler hasn't run yet, however, though in this
6194 : // case the template object's type doesn't matter.
6195 0 : ObjectGroup* group = templateObject->group();
6196 0 : MOZ_ASSERT_IF(info().analysisMode() != Analysis_ArgumentsUsage,
6197 : group->hasAllFlagsDontCheckGeneration(OBJECT_FLAG_COPY_ON_WRITE));
6198 :
6199 :
6200 0 : MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
6201 0 : current->add(templateConst);
6202 :
6203 : MNewArrayCopyOnWrite* ins =
6204 0 : MNewArrayCopyOnWrite::New(alloc(), constraints(), templateConst,
6205 0 : group->initialHeap(constraints()));
6206 :
6207 0 : current->add(ins);
6208 0 : current->push(ins);
6209 :
6210 0 : return Ok();
6211 : }
6212 :
6213 : AbortReasonOr<Ok>
6214 147 : IonBuilder::newObjectTryTemplateObject(bool* emitted, JSObject* templateObject)
6215 : {
6216 147 : MOZ_ASSERT(*emitted == false);
6217 :
6218 : // TODO: Support tracking optimizations for inlining a call and regular
6219 : // optimization tracking at the same time. Currently just drop optimization
6220 : // tracking when that happens.
6221 294 : bool canTrackOptimization = !IsCallPC(pc);
6222 :
6223 147 : if (canTrackOptimization)
6224 : trackOptimizationAttempt(TrackedStrategy::NewObject_TemplateObject);
6225 0 : if (!templateObject) {
6226 13 : if (canTrackOptimization)
6227 : trackOptimizationOutcome(TrackedOutcome::NoTemplateObject);
6228 13 : return Ok();
6229 : }
6230 :
6231 0 : if (templateObject->is<PlainObject>() && templateObject->as<PlainObject>().hasDynamicSlots()) {
6232 0 : if (canTrackOptimization)
6233 : trackOptimizationOutcome(TrackedOutcome::TemplateObjectIsPlainObjectWithDynamicSlots);
6234 0 : return Ok();
6235 : }
6236 :
6237 : // Emit fastpath.
6238 :
6239 : MNewObject::Mode mode;
6240 134 : if (JSOp(*pc) == JSOP_NEWOBJECT || JSOp(*pc) == JSOP_NEWINIT)
6241 : mode = MNewObject::ObjectLiteral;
6242 : else
6243 0 : mode = MNewObject::ObjectCreate;
6244 :
6245 0 : gc::InitialHeap heap = templateObject->group()->initialHeap(constraints());
6246 0 : MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
6247 134 : current->add(templateConst);
6248 :
6249 0 : MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, heap, mode);
6250 0 : current->add(ins);
6251 134 : current->push(ins);
6252 :
6253 402 : MOZ_TRY(resumeAfter(ins));
6254 :
6255 134 : if (canTrackOptimization)
6256 : trackOptimizationSuccess();
6257 0 : *emitted = true;
6258 134 : return Ok();
6259 : }
6260 :
6261 : AbortReasonOr<Ok>
6262 13 : IonBuilder::newObjectTrySharedStub(bool* emitted)
6263 : {
6264 13 : MOZ_ASSERT(*emitted == false);
6265 :
6266 : // TODO: Support tracking optimizations for inlining a call and regular
6267 : // optimization tracking at the same time. Currently just drop optimization
6268 : // tracking when that happens.
6269 26 : bool canTrackOptimization = !IsCallPC(pc);
6270 :
6271 : // Try to emit a shared stub cache.
6272 :
6273 1 : if (JitOptions.disableSharedStubs)
6274 0 : return Ok();
6275 :
6276 13 : if (canTrackOptimization)
6277 : trackOptimizationAttempt(TrackedStrategy::NewObject_SharedCache);
6278 :
6279 0 : MInstruction* stub = MNullarySharedStub::New(alloc());
6280 0 : current->add(stub);
6281 13 : current->push(stub);
6282 :
6283 39 : MOZ_TRY(resumeAfter(stub));
6284 :
6285 0 : MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Object, MUnbox::Infallible);
6286 0 : current->add(unbox);
6287 13 : current->push(unbox);
6288 :
6289 13 : if (canTrackOptimization)
6290 : trackOptimizationSuccess();
6291 0 : *emitted = true;
6292 13 : return Ok();
6293 : }
6294 :
6295 : AbortReasonOr<Ok>
6296 0 : IonBuilder::newObjectTryVM(bool* emitted, JSObject* templateObject)
6297 : {
6298 : // Emit a VM call.
6299 0 : MOZ_ASSERT(JSOp(*pc) == JSOP_NEWOBJECT || JSOp(*pc) == JSOP_NEWINIT);
6300 :
6301 0 : trackOptimizationAttempt(TrackedStrategy::NewObject_Call);
6302 :
6303 0 : gc::InitialHeap heap = gc::DefaultHeap;
6304 0 : MConstant* templateConst = MConstant::New(alloc(), NullValue());
6305 :
6306 0 : if (templateObject) {
6307 0 : heap = templateObject->group()->initialHeap(constraints());
6308 0 : templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
6309 : }
6310 :
6311 0 : current->add(templateConst);
6312 :
6313 0 : MNewObject* ins = MNewObject::NewVM(alloc(), constraints(), templateConst, heap,
6314 0 : MNewObject::ObjectLiteral);
6315 0 : current->add(ins);
6316 0 : current->push(ins);
6317 :
6318 0 : MOZ_TRY(resumeAfter(ins));
6319 :
6320 0 : trackOptimizationSuccess();
6321 0 : *emitted = true;
6322 0 : return Ok();
6323 : }
6324 :
6325 : AbortReasonOr<Ok>
6326 147 : IonBuilder::jsop_newobject()
6327 : {
6328 0 : bool emitted = false;
6329 147 : startTrackingOptimizations();
6330 :
6331 147 : JSObject* templateObject = inspector->getTemplateObject(pc);
6332 :
6333 0 : if (!forceInlineCaches()) {
6334 0 : MOZ_TRY(newObjectTryTemplateObject(&emitted, templateObject));
6335 0 : if (emitted)
6336 134 : return Ok();
6337 : }
6338 0 : MOZ_TRY(newObjectTrySharedStub(&emitted));
6339 0 : if (emitted)
6340 13 : return Ok();
6341 :
6342 0 : MOZ_TRY(newObjectTryVM(&emitted, templateObject));
6343 0 : if (emitted)
6344 0 : return Ok();
6345 :
6346 0 : MOZ_CRASH("newobject should have been emited");
6347 : }
6348 :
6349 : AbortReasonOr<Ok>
6350 16 : IonBuilder::jsop_initelem()
6351 : {
6352 16 : MOZ_ASSERT(*pc == JSOP_INITELEM || *pc == JSOP_INITHIDDENELEM);
6353 :
6354 0 : MDefinition* value = current->pop();
6355 0 : MDefinition* id = current->pop();
6356 16 : MDefinition* obj = current->peek(-1);
6357 :
6358 16 : bool emitted = false;
6359 :
6360 0 : if (!forceInlineCaches() && *pc == JSOP_INITELEM) {
6361 0 : MOZ_TRY(initOrSetElemTryDense(&emitted, obj, id, value, /* writeHole = */ true));
6362 0 : if (emitted)
6363 2 : return Ok();
6364 : }
6365 :
6366 0 : MOZ_TRY(initOrSetElemTryCache(&emitted, obj, id, value));
6367 0 : if (emitted)
6368 5 : return Ok();
6369 :
6370 0 : MInitElem* initElem = MInitElem::New(alloc(), obj, id, value);
6371 9 : current->add(initElem);
6372 :
6373 : return resumeAfter(initElem);
6374 : }
6375 :
6376 : AbortReasonOr<Ok>
6377 12 : IonBuilder::jsop_initelem_inc()
6378 : {
6379 0 : MDefinition* value = current->pop();
6380 0 : MDefinition* id = current->pop();
6381 12 : MDefinition* obj = current->peek(-1);
6382 :
6383 12 : bool emitted = false;
6384 :
6385 0 : MAdd* nextId = MAdd::New(alloc(), id, constantInt(1), MIRType::Int32);
6386 0 : current->add(nextId);
6387 12 : current->push(nextId);
6388 :
6389 0 : if (!forceInlineCaches()) {
6390 0 : MOZ_TRY(initOrSetElemTryDense(&emitted, obj, id, value, /* writeHole = */ true));
6391 0 : if (emitted)
6392 11 : return Ok();
6393 : }
6394 :
6395 0 : MOZ_TRY(initOrSetElemTryCache(&emitted, obj, id, value));
6396 0 : if (emitted)
6397 1 : return Ok();
6398 :
6399 0 : MCallInitElementArray* initElem = MCallInitElementArray::New(alloc(), obj, id, value);
6400 0 : current->add(initElem);
6401 :
6402 : return resumeAfter(initElem);
6403 : }
6404 :
6405 : AbortReasonOr<Ok>
6406 170 : IonBuilder::jsop_initelem_array()
6407 : {
6408 0 : MDefinition* value = current->pop();
6409 170 : MDefinition* obj = current->peek(-1);
6410 :
6411 : // Make sure that arrays have the type being written to them by the
6412 : // intializer, and that arrays are marked as non-packed when writing holes
6413 : // to them during initialization.
6414 0 : bool needStub = false;
6415 170 : if (shouldAbortOnPreliminaryGroups(obj)) {
6416 : needStub = true;
6417 0 : } else if (!obj->resultTypeSet() ||
6418 0 : obj->resultTypeSet()->unknownObject() ||
6419 170 : obj->resultTypeSet()->getObjectCount() != 1)
6420 : {
6421 : needStub = true;
6422 : } else {
6423 0 : MOZ_ASSERT(obj->resultTypeSet()->getObjectCount() == 1);
6424 0 : TypeSet::ObjectKey* initializer = obj->resultTypeSet()->getObject(0);
6425 0 : if (value->type() == MIRType::MagicHole) {
6426 0 : if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED))
6427 0 : needStub = true;
6428 0 : } else if (!initializer->unknownProperties()) {
6429 0 : HeapTypeSetKey elemTypes = initializer->property(JSID_VOID);
6430 0 : if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
6431 0 : elemTypes.freeze(constraints());
6432 0 : needStub = true;
6433 : }
6434 : }
6435 : }
6436 :
6437 0 : uint32_t index = GET_UINT32(pc);
6438 0 : if (needStub) {
6439 0 : MOZ_ASSERT(index <= INT32_MAX,
6440 : "the bytecode emitter must fail to compile code that would "
6441 : "produce JSOP_INITELEM_ARRAY with an index exceeding "
6442 : "int32_t range");
6443 0 : MCallInitElementArray* store = MCallInitElementArray::New(alloc(), obj,
6444 0 : constantInt(index), value);
6445 0 : current->add(store);
6446 : return resumeAfter(store);
6447 : }
6448 :
6449 170 : return initializeArrayElement(obj, index, value, /* addResumePoint = */ true);
6450 : }
6451 :
6452 : AbortReasonOr<Ok>
6453 170 : IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
6454 : bool addResumePointAndIncrementInitializedLength)
6455 : {
6456 0 : MConstant* id = MConstant::New(alloc(), Int32Value(index));
6457 170 : current->add(id);
6458 :
6459 : // Get the elements vector.
6460 0 : MElements* elements = MElements::New(alloc(), obj);
6461 170 : current->add(elements);
6462 :
6463 0 : if (needsPostBarrier(value))
6464 29 : current->add(MPostWriteBarrier::New(alloc(), obj, value));
6465 :
6466 0 : if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) ||
6467 1 : (obj->isNullarySharedStub() &&
6468 0 : obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles))
6469 : {
6470 0 : MInstruction* valueDouble = MToDouble::New(alloc(), value);
6471 0 : current->add(valueDouble);
6472 0 : value = valueDouble;
6473 : }
6474 :
6475 : // Store the value.
6476 0 : MStoreElement* store = MStoreElement::New(alloc(), elements, id, value,
6477 0 : /* needsHoleCheck = */ false);
6478 170 : current->add(store);
6479 :
6480 170 : if (addResumePointAndIncrementInitializedLength) {
6481 : // Update the initialized length. (The template object for this
6482 : // array has the array's ultimate length, so the length field is
6483 : // already correct: no updating needed.)
6484 0 : MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id);
6485 170 : current->add(initLength);
6486 :
6487 510 : MOZ_TRY(resumeAfter(initLength));
6488 : }
6489 :
6490 170 : return Ok();
6491 : }
6492 :
6493 : AbortReasonOr<Ok>
6494 0 : IonBuilder::jsop_mutateproto()
6495 : {
6496 0 : MDefinition* value = current->pop();
6497 0 : MDefinition* obj = current->peek(-1);
6498 :
6499 0 : MMutateProto* mutate = MMutateProto::New(alloc(), obj, value);
6500 0 : current->add(mutate);
6501 0 : return resumeAfter(mutate);
6502 : }
6503 :
6504 : AbortReasonOr<Ok>
6505 268 : IonBuilder::jsop_initprop(PropertyName* name)
6506 : {
6507 268 : bool useFastPath = false;
6508 :
6509 0 : MDefinition* obj = current->peek(-2);
6510 0 : if (obj->isNewObject()) {
6511 0 : if (JSObject* templateObject = obj->toNewObject()->templateObject()) {
6512 0 : if (templateObject->is<PlainObject>()) {
6513 0 : if (templateObject->as<PlainObject>().containsPure(name))
6514 205 : useFastPath = true;
6515 : } else {
6516 46 : MOZ_ASSERT(templateObject->as<UnboxedPlainObject>().layout().lookup(name));
6517 : useFastPath = true;
6518 : }
6519 : }
6520 : }
6521 536 : MInstructionReverseIterator last = current->rbegin();
6522 :
6523 519 : if (useFastPath && !forceInlineCaches()) {
6524 : // This is definitely initializing an 'own' property of the object, treat
6525 : // it as an assignment.
6526 753 : MOZ_TRY(jsop_setprop(name));
6527 : } else {
6528 0 : MDefinition* value = current->pop();
6529 17 : MDefinition* obj = current->pop();
6530 :
6531 0 : bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
6532 17 : /* canModify = */ true);
6533 :
6534 0 : bool emitted = false;
6535 0 : MOZ_TRY(setPropTryCache(&emitted, obj, name, value, barrier));
6536 17 : MOZ_ASSERT(emitted == true);
6537 : }
6538 :
6539 : // SETPROP pushed the value, instead of the object. Fix this on the stack,
6540 : // and check the most recent resume point to see if it needs updating too.
6541 0 : current->pop();
6542 0 : current->push(obj);
6543 0 : for (MInstructionReverseIterator riter = current->rbegin(); riter != last; riter++) {
6544 0 : if (MResumePoint* resumePoint = riter->resumePoint()) {
6545 0 : MOZ_ASSERT(resumePoint->pc() == pc);
6546 0 : if (resumePoint->mode() == MResumePoint::ResumeAfter) {
6547 268 : size_t index = resumePoint->numOperands() - 1;
6548 : resumePoint->replaceOperand(index, obj);
6549 : }
6550 : break;
6551 : }
6552 : }
6553 :
6554 268 : return Ok();
6555 : }
6556 :
6557 : AbortReasonOr<Ok>
6558 0 : IonBuilder::jsop_initprop_getter_setter(PropertyName* name)
6559 : {
6560 0 : MDefinition* value = current->pop();
6561 0 : MDefinition* obj = current->peek(-1);
6562 :
6563 0 : MInitPropGetterSetter* init = MInitPropGetterSetter::New(alloc(), obj, name, value);
6564 0 : current->add(init);
6565 0 : return resumeAfter(init);
6566 : }
6567 :
6568 : AbortReasonOr<Ok>
6569 0 : IonBuilder::jsop_initelem_getter_setter()
6570 : {
6571 0 : MDefinition* value = current->pop();
6572 0 : MDefinition* id = current->pop();
6573 0 : MDefinition* obj = current->peek(-1);
6574 :
6575 0 : MInitElemGetterSetter* init = MInitElemGetterSetter::New(alloc(), obj, id, value);
6576 0 : current->add(init);
6577 0 : return resumeAfter(init);
6578 : }
6579 :
6580 : AbortReasonOr<MBasicBlock*>
6581 7646 : IonBuilder::newBlock(size_t stackDepth, jsbytecode* pc, MBasicBlock* maybePredecessor)
6582 : {
6583 7646 : MOZ_ASSERT_IF(maybePredecessor, maybePredecessor->stackDepth() == stackDepth);
6584 :
6585 0 : MBasicBlock* block = MBasicBlock::New(graph(), stackDepth, info(), maybePredecessor,
6586 0 : bytecodeSite(pc), MBasicBlock::NORMAL);
6587 0 : if (!block)
6588 0 : return abort(AbortReason::Alloc);
6589 :
6590 0 : block->setLoopDepth(loopDepth_);
6591 7646 : return block;
6592 : }
6593 :
6594 : AbortReasonOr<MBasicBlock*>
6595 0 : IonBuilder::newBlock(MBasicBlock* predecessor, jsbytecode* pc, MResumePoint* priorResumePoint)
6596 : {
6597 0 : MBasicBlock* block = MBasicBlock::NewWithResumePoint(graph(), info(), predecessor,
6598 0 : bytecodeSite(pc), priorResumePoint);
6599 0 : if (!block)
6600 0 : return abort(AbortReason::Alloc);
6601 :
6602 0 : block->setLoopDepth(loopDepth_);
6603 0 : return block;
6604 : }
6605 :
6606 : AbortReasonOr<MBasicBlock*>
6607 0 : IonBuilder::newBlockPopN(MBasicBlock* predecessor, jsbytecode* pc, uint32_t popped)
6608 : {
6609 0 : MBasicBlock* block = MBasicBlock::NewPopN(graph(), info(), predecessor, bytecodeSite(pc),
6610 0 : MBasicBlock::NORMAL, popped);
6611 0 : if (!block)
6612 0 : return abort(AbortReason::Alloc);
6613 :
6614 0 : block->setLoopDepth(loopDepth_);
6615 0 : return block;
6616 : }
6617 :
6618 : AbortReasonOr<MBasicBlock*>
6619 1 : IonBuilder::newBlockAfter(MBasicBlock* at, size_t stackDepth, jsbytecode* pc,
6620 : MBasicBlock* maybePredecessor)
6621 : {
6622 1 : MOZ_ASSERT_IF(maybePredecessor, maybePredecessor->stackDepth() == stackDepth);
6623 :
6624 0 : MBasicBlock* block = MBasicBlock::New(graph(), stackDepth, info(), maybePredecessor,
6625 0 : bytecodeSite(pc), MBasicBlock::NORMAL);
6626 0 : if (!block)
6627 0 : return abort(AbortReason::Alloc);
6628 :
6629 0 : block->setLoopDepth(loopDepth_);
6630 0 : block->setHitCount(0); // osr block
6631 0 : graph().insertBlockAfter(at, block);
6632 1 : return block;
6633 : }
6634 :
6635 : AbortReasonOr<MBasicBlock*>
6636 1 : IonBuilder::newOsrPreheader(MBasicBlock* predecessor, jsbytecode* loopEntry,
6637 : jsbytecode* beforeLoopEntry)
6638 : {
6639 1 : MOZ_ASSERT(JSOp(*loopEntry) == JSOP_LOOPENTRY);
6640 1 : MOZ_ASSERT(loopEntry == info().osrPc());
6641 :
6642 : // Create two blocks: one for the OSR entry with no predecessors, one for
6643 : // the preheader, which has the OSR entry block as a predecessor. The
6644 : // OSR block is always the second block (with id 1).
6645 : MBasicBlock* osrBlock;
6646 0 : MOZ_TRY_VAR(osrBlock, newBlockAfter(*graph().begin(), predecessor->stackDepth(), loopEntry));
6647 : MBasicBlock* preheader;
6648 0 : MOZ_TRY_VAR(preheader, newBlock(predecessor, loopEntry));
6649 :
6650 1 : graph().addBlock(preheader);
6651 :
6652 : // Give the pre-header the same hit count as the code before the loop.
6653 2 : if (script()->hasScriptCounts())
6654 0 : preheader->setHitCount(script()->getHitCount(beforeLoopEntry));
6655 :
6656 1 : MOsrEntry* entry = MOsrEntry::New(alloc());
6657 1 : osrBlock->add(entry);
6658 :
6659 : // Initialize |envChain|.
6660 : {
6661 1 : uint32_t slot = info().environmentChainSlot();
6662 :
6663 : MInstruction* envv;
6664 1 : if (usesEnvironmentChain()) {
6665 2 : envv = MOsrEnvironmentChain::New(alloc(), entry);
6666 : } else {
6667 : // Use an undefined value if the script does not need its env
6668 : // chain, to match the type that is already being tracked for the
6669 : // slot.
6670 0 : envv = MConstant::New(alloc(), UndefinedValue());
6671 : }
6672 :
6673 1 : osrBlock->add(envv);
6674 1 : osrBlock->initSlot(slot, envv);
6675 : }
6676 : // Initialize |return value|
6677 : {
6678 : MInstruction* returnValue;
6679 2 : if (!script()->noScriptRval())
6680 0 : returnValue = MOsrReturnValue::New(alloc(), entry);
6681 : else
6682 0 : returnValue = MConstant::New(alloc(), UndefinedValue());
6683 1 : osrBlock->add(returnValue);
6684 1 : osrBlock->initSlot(info().returnValueSlot(), returnValue);
6685 : }
6686 :
6687 : // Initialize arguments object.
6688 0 : bool needsArgsObj = info().needsArgsObj();
6689 0 : MInstruction* argsObj = nullptr;
6690 0 : if (info().hasArguments()) {
6691 0 : if (needsArgsObj)
6692 0 : argsObj = MOsrArgumentsObject::New(alloc(), entry);
6693 : else
6694 0 : argsObj = MConstant::New(alloc(), UndefinedValue());
6695 0 : osrBlock->add(argsObj);
6696 0 : osrBlock->initSlot(info().argsObjSlot(), argsObj);
6697 : }
6698 :
6699 0 : if (info().funMaybeLazy()) {
6700 : // Initialize |this| parameter.
6701 0 : MParameter* thisv = MParameter::New(alloc(), MParameter::THIS_SLOT, nullptr);
6702 1 : osrBlock->add(thisv);
6703 1 : osrBlock->initSlot(info().thisSlot(), thisv);
6704 :
6705 : // Initialize arguments.
6706 3 : for (uint32_t i = 0; i < info().nargs(); i++) {
6707 2 : uint32_t slot = needsArgsObj ? info().argSlotUnchecked(i) : info().argSlot(i);
6708 :
6709 : // Only grab arguments from the arguments object if the arguments object
6710 : // aliases formals. If the argsobj does not alias formals, then the
6711 : // formals may have been assigned to during interpretation, and that change
6712 : // will not be reflected in the argsobj.
6713 2 : if (needsArgsObj && info().argsObjAliasesFormals()) {
6714 0 : MOZ_ASSERT(argsObj && argsObj->isOsrArgumentsObject());
6715 : // If this is an aliased formal, then the arguments object
6716 : // contains a hole at this index. Any references to this
6717 : // variable in the jitcode will come from JSOP_*ALIASEDVAR
6718 : // opcodes, so the slot itself can be set to undefined. If
6719 : // it's not aliased, it must be retrieved from the arguments
6720 : // object.
6721 : MInstruction* osrv;
6722 0 : if (script()->formalIsAliased(i))
6723 0 : osrv = MConstant::New(alloc(), UndefinedValue());
6724 : else
6725 0 : osrv = MGetArgumentsObjectArg::New(alloc(), argsObj, i);
6726 :
6727 0 : osrBlock->add(osrv);
6728 0 : osrBlock->initSlot(slot, osrv);
6729 : } else {
6730 0 : MParameter* arg = MParameter::New(alloc(), i, nullptr);
6731 2 : osrBlock->add(arg);
6732 2 : osrBlock->initSlot(slot, arg);
6733 : }
6734 : }
6735 : }
6736 :
6737 : // Initialize locals.
6738 0 : for (uint32_t i = 0; i < info().nlocals(); i++) {
6739 6 : uint32_t slot = info().localSlot(i);
6740 0 : ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(i);
6741 :
6742 1 : MOsrValue* osrv = MOsrValue::New(alloc().fallible(), entry, offset);
6743 0 : if (!osrv)
6744 0 : return abort(AbortReason::Alloc);
6745 3 : osrBlock->add(osrv);
6746 3 : osrBlock->initSlot(slot, osrv);
6747 : }
6748 :
6749 : // Initialize stack.
6750 0 : uint32_t numStackSlots = preheader->stackDepth() - info().firstStackSlot();
6751 0 : for (uint32_t i = 0; i < numStackSlots; i++) {
6752 6 : uint32_t slot = info().stackSlot(i);
6753 0 : ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(info().nlocals() + i);
6754 :
6755 1 : MOsrValue* osrv = MOsrValue::New(alloc().fallible(), entry, offset);
6756 0 : if (!osrv)
6757 0 : return abort(AbortReason::Alloc);
6758 3 : osrBlock->add(osrv);
6759 3 : osrBlock->initSlot(slot, osrv);
6760 : }
6761 :
6762 : // Create an MStart to hold the first valid MResumePoint.
6763 1 : MStart* start = MStart::New(alloc());
6764 1 : osrBlock->add(start);
6765 :
6766 : // MOsrValue instructions are infallible, so the first MResumePoint must
6767 : // occur after they execute, at the point of the MStart.
6768 3 : MOZ_TRY(resumeAt(start, loopEntry));
6769 :
6770 : // Link the same MResumePoint from the MStart to each MOsrValue.
6771 : // This causes logic in ShouldSpecializeInput() to not replace Uses with
6772 : // Unboxes in the MResumePiont, so that the MStart always sees Values.
6773 1 : if (!osrBlock->linkOsrValues(start))
6774 0 : return abort(AbortReason::Alloc);
6775 :
6776 : // Clone types of the other predecessor of the pre-header to the osr block,
6777 : // such as pre-header phi's won't discard specialized type of the
6778 : // predecessor.
6779 1 : MOZ_ASSERT(predecessor->stackDepth() == osrBlock->stackDepth());
6780 1 : MOZ_ASSERT(info().environmentChainSlot() == 0);
6781 :
6782 : // Treat the OSR values as having the same type as the existing values
6783 : // coming in to the loop. These will be fixed up with appropriate
6784 : // unboxing and type barriers in finishLoop, once the possible types
6785 : // at the loop header are known.
6786 0 : for (uint32_t i = info().startArgSlot(); i < osrBlock->stackDepth(); i++) {
6787 0 : MDefinition* existing = current->getSlot(i);
6788 9 : MDefinition* def = osrBlock->getSlot(i);
6789 9 : MOZ_ASSERT_IF(!needsArgsObj || !info().isSlotAliased(i), def->type() == MIRType::Value);
6790 :
6791 : // Aliased slots are never accessed, since they need to go through
6792 : // the callobject. No need to type them here.
6793 9 : if (info().isSlotAliased(i))
6794 : continue;
6795 :
6796 18 : def->setResultType(existing->type());
6797 9 : def->setResultTypeSet(existing->resultTypeSet());
6798 : }
6799 :
6800 : // Finish the osrBlock.
6801 1 : osrBlock->end(MGoto::New(alloc(), preheader));
6802 0 : if (!preheader->addPredecessor(alloc(), osrBlock))
6803 0 : return abort(AbortReason::Alloc);
6804 0 : graph().setOsrBlock(osrBlock);
6805 :
6806 1 : return preheader;
6807 : }
6808 :
6809 : AbortReasonOr<MBasicBlock*>
6810 202 : IonBuilder::newPendingLoopHeader(MBasicBlock* predecessor, jsbytecode* pc, bool osr, bool canOsr,
6811 : unsigned stackPhiCount)
6812 : {
6813 : // If this site can OSR, all values on the expression stack are part of the loop.
6814 202 : if (canOsr)
6815 0 : stackPhiCount = predecessor->stackDepth() - info().firstStackSlot();
6816 :
6817 0 : MBasicBlock* block = MBasicBlock::NewPendingLoopHeader(graph(), info(), predecessor,
6818 0 : bytecodeSite(pc), stackPhiCount);
6819 202 : if (!block)
6820 0 : return abort(AbortReason::Alloc);
6821 :
6822 202 : if (osr) {
6823 : // Incorporate type information from the OSR frame into the loop
6824 : // header. The OSR frame may have unexpected types due to type changes
6825 : // within the loop body or due to incomplete profiling information,
6826 : // in which case this may avoid restarts of loop analysis or bailouts
6827 : // during the OSR itself.
6828 :
6829 0 : MOZ_ASSERT(info().firstLocalSlot() - info().firstArgSlot() ==
6830 : baselineFrame_->argTypes.length());
6831 1 : MOZ_ASSERT(block->stackDepth() - info().firstLocalSlot() ==
6832 : baselineFrame_->varTypes.length());
6833 :
6834 : // Unbox the MOsrValue if it is known to be unboxable.
6835 10 : for (uint32_t i = info().startArgSlot(); i < block->stackDepth(); i++) {
6836 :
6837 : // The value of aliased args and slots are in the callobject. So we can't
6838 : // the value from the baseline frame.
6839 9 : if (info().isSlotAliased(i))
6840 0 : continue;
6841 :
6842 18 : MPhi* phi = block->getSlot(i)->toPhi();
6843 :
6844 : // Get the type from the baseline frame.
6845 0 : TypeSet::Type existingType = TypeSet::UndefinedType();
6846 0 : uint32_t arg = i - info().firstArgSlot();
6847 0 : uint32_t var = i - info().firstLocalSlot();
6848 0 : if (info().funMaybeLazy() && i == info().thisSlot())
6849 0 : existingType = baselineFrame_->thisType;
6850 8 : else if (arg < info().nargs())
6851 0 : existingType = baselineFrame_->argTypes[arg];
6852 : else
6853 0 : existingType = baselineFrame_->varTypes[var];
6854 :
6855 9 : if (existingType.isSingletonUnchecked())
6856 2 : checkNurseryObject(existingType.singleton());
6857 :
6858 : // Extract typeset from value.
6859 0 : LifoAlloc* lifoAlloc = alloc().lifoAlloc();
6860 : TemporaryTypeSet* typeSet =
6861 1 : lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc, existingType);
6862 0 : if (!typeSet)
6863 0 : return abort(AbortReason::Alloc);
6864 1 : MIRType type = typeSet->getKnownMIRType();
6865 9 : if (!phi->addBackedgeType(alloc(), type, typeSet))
6866 0 : return abort(AbortReason::Alloc);
6867 : }
6868 : }
6869 :
6870 202 : return block;
6871 : }
6872 :
6873 : MTest*
6874 0 : IonBuilder::newTest(MDefinition* ins, MBasicBlock* ifTrue, MBasicBlock* ifFalse)
6875 : {
6876 1 : MTest* test = MTest::New(alloc(), ins, ifTrue, ifFalse);
6877 1975 : test->cacheOperandMightEmulateUndefined(constraints());
6878 0 : return test;
6879 : }
6880 :
6881 : // A resume point is a mapping of stack slots to MDefinitions. It is used to
6882 : // capture the environment such that if a guard fails, and IonMonkey needs
6883 : // to exit back to the interpreter, the interpreter state can be
6884 : // reconstructed.
6885 : //
6886 : // We capture stack state at critical points:
6887 : // * (1) At the beginning of every basic block.
6888 : // * (2) After every effectful operation.
6889 : //
6890 : // As long as these two properties are maintained, instructions can
6891 : // be moved, hoisted, or, eliminated without problems, and ops without side
6892 : // effects do not need to worry about capturing state at precisely the
6893 : // right point in time.
6894 : //
6895 : // Effectful instructions, of course, need to capture state after completion,
6896 : // where the interpreter will not attempt to repeat the operation. For this,
6897 : // ResumeAfter must be used. The state is attached directly to the effectful
6898 : // instruction to ensure that no intermediate instructions could be injected
6899 : // in between by a future analysis pass.
6900 : //
6901 : // During LIR construction, if an instruction can bail back to the interpreter,
6902 : // we create an LSnapshot, which uses the last known resume point to request
6903 : // register/stack assignments for every live value.
6904 : AbortReasonOr<Ok>
6905 0 : IonBuilder::resume(MInstruction* ins, jsbytecode* pc, MResumePoint::Mode mode)
6906 : {
6907 0 : MOZ_ASSERT(ins->isEffectful() || !ins->isMovable());
6908 :
6909 0 : MResumePoint* resumePoint = MResumePoint::New(alloc(), ins->block(), pc,
6910 0 : mode);
6911 0 : if (!resumePoint)
6912 0 : return abort(AbortReason::Alloc);
6913 7223 : ins->setResumePoint(resumePoint);
6914 7223 : return Ok();
6915 : }
6916 :
6917 : AbortReasonOr<Ok>
6918 0 : IonBuilder::resumeAt(MInstruction* ins, jsbytecode* pc)
6919 : {
6920 1 : return resume(ins, pc, MResumePoint::ResumeAt);
6921 : }
6922 :
6923 : AbortReasonOr<Ok>
6924 0 : IonBuilder::resumeAfter(MInstruction* ins)
6925 : {
6926 7222 : return resume(ins, pc, MResumePoint::ResumeAfter);
6927 : }
6928 :
6929 : AbortReasonOr<Ok>
6930 3358 : IonBuilder::maybeInsertResume()
6931 : {
6932 : // Create a resume point at the current position, without an existing
6933 : // effectful instruction. This resume point is not necessary for correct
6934 : // behavior (see above), but is added to avoid holding any values from the
6935 : // previous resume point which are now dead. This shortens the live ranges
6936 : // of such values and improves register allocation.
6937 : //
6938 : // This optimization is not performed outside of loop bodies, where good
6939 : // register allocation is not as critical, in order to avoid creating
6940 : // excessive resume points.
6941 :
6942 3358 : if (loopDepth_ == 0)
6943 0 : return Ok();
6944 :
6945 2268 : MNop* ins = MNop::New(alloc());
6946 2268 : current->add(ins);
6947 :
6948 : return resumeAfter(ins);
6949 : }
6950 :
6951 : void
6952 0 : IonBuilder::maybeMarkEmpty(MDefinition* ins)
6953 : {
6954 25 : MOZ_ASSERT(ins->type() == MIRType::Value);
6955 :
6956 : // When one of the operands has no type information, mark the output
6957 : // as having no possible types too. This is to avoid degrading
6958 : // subsequent analysis.
6959 35 : for (size_t i = 0; i < ins->numOperands(); i++) {
6960 30 : if (!ins->getOperand(i)->emptyResultTypeSet())
6961 : continue;
6962 :
6963 0 : TemporaryTypeSet* types = alloc().lifoAlloc()->new_<TemporaryTypeSet>();
6964 25 : if (types) {
6965 25 : ins->setResultTypeSet(types);
6966 : return;
6967 : }
6968 : }
6969 : }
6970 :
6971 : // Return whether property lookups can be performed effectlessly on clasp.
6972 : static bool
6973 0 : ClassHasEffectlessLookup(const Class* clasp)
6974 : {
6975 0 : return (clasp == &UnboxedPlainObject::class_) ||
6976 1677 : IsTypedObjectClass(clasp) ||
6977 1677 : (clasp->isNative() && !clasp->getOpsLookupProperty());
6978 : }
6979 :
6980 : // Return whether an object might have a property for name which is not
6981 : // accounted for by type information.
6982 : static bool
6983 346 : ObjectHasExtraOwnProperty(CompileRealm* realm, TypeSet::ObjectKey* object, jsid id)
6984 : {
6985 : // Some typed object properties are not reflected in type information.
6986 904 : if (object->isGroup() && object->group()->maybeTypeDescr())
6987 0 : return object->group()->typeDescr().hasProperty(realm->runtime()->names(), id);
6988 :
6989 346 : const Class* clasp = object->clasp();
6990 :
6991 : // Array |length| properties are not reflected in type information.
6992 346 : if (clasp == &ArrayObject::class_)
6993 204 : return JSID_IS_ATOM(id, realm->runtime()->names().length);
6994 :
6995 : // Resolve hooks can install new properties on objects on demand.
6996 345 : JSObject* singleton = object->isSingleton() ? object->singleton() : nullptr;
6997 278 : return ClassMayResolveId(realm->runtime()->names(), clasp, id, singleton);
6998 : }
6999 :
7000 : void
7001 647 : IonBuilder::insertRecompileCheck()
7002 : {
7003 : // No need for recompile checks if this is the highest optimization level.
7004 647 : OptimizationLevel curLevel = optimizationInfo().level();
7005 647 : if (IonOptimizations.isLastLevel(curLevel))
7006 : return;
7007 :
7008 : // Add recompile check.
7009 :
7010 : // Get the topmost builder. The topmost script will get recompiled when
7011 : // warm-up counter is high enough to justify a higher optimization level.
7012 0 : IonBuilder* topBuilder = outermostBuilder();
7013 :
7014 : // Add recompile check to recompile when the warm-up count reaches the
7015 : // threshold of the next optimization level.
7016 0 : OptimizationLevel nextLevel = IonOptimizations.nextLevel(curLevel);
7017 0 : const OptimizationInfo* info = IonOptimizations.get(nextLevel);
7018 0 : uint32_t warmUpThreshold = info->compilerWarmUpThreshold(topBuilder->script());
7019 0 : MRecompileCheck* check = MRecompileCheck::New(alloc(), topBuilder->script(), warmUpThreshold,
7020 0 : MRecompileCheck::RecompileCheck_OptimizationLevel);
7021 0 : current->add(check);
7022 : }
7023 :
7024 : JSObject*
7025 216 : IonBuilder::testSingletonProperty(JSObject* obj, jsid id)
7026 : {
7027 : // We would like to completely no-op property/global accesses which can
7028 : // produce only a particular JSObject. When indicating the access result is
7029 : // definitely an object, type inference does not account for the
7030 : // possibility that the property is entirely missing from the input object
7031 : // and its prototypes (if this happens, a semantic trigger would be hit and
7032 : // the pushed types updated, even if there is no type barrier).
7033 : //
7034 : // If the access definitely goes through obj, either directly or on the
7035 : // prototype chain, and the object has singleton type, then the type
7036 : // information for that property reflects the value that will definitely be
7037 : // read on accesses to the object. If the property is later deleted or
7038 : // reconfigured as a getter/setter then the type information for the
7039 : // property will change and trigger invalidation.
7040 :
7041 0 : while (obj) {
7042 222 : if (!ClassHasEffectlessLookup(obj->getClass()))
7043 0 : return nullptr;
7044 :
7045 0 : TypeSet::ObjectKey* objKey = TypeSet::ObjectKey::get(obj);
7046 222 : if (analysisContext)
7047 0 : objKey->ensureTrackedProperty(analysisContext, id);
7048 :
7049 222 : if (objKey->unknownProperties())
7050 : return nullptr;
7051 :
7052 0 : HeapTypeSetKey property = objKey->property(id);
7053 0 : if (property.isOwnProperty(constraints())) {
7054 213 : if (obj->isSingleton())
7055 212 : return property.singleton(constraints());
7056 : return nullptr;
7057 : }
7058 :
7059 9 : if (ObjectHasExtraOwnProperty(realm, objKey, id))
7060 : return nullptr;
7061 :
7062 9 : obj = checkNurseryObject(obj->staticPrototype());
7063 : }
7064 :
7065 : return nullptr;
7066 : }
7067 :
7068 : JSObject*
7069 513 : IonBuilder::testSingletonPropertyTypes(MDefinition* obj, jsid id)
7070 : {
7071 : // As for TestSingletonProperty, but the input is any value in a type set
7072 : // rather than a specific object.
7073 :
7074 513 : TemporaryTypeSet* types = obj->resultTypeSet();
7075 1026 : if (types && types->unknownObject())
7076 : return nullptr;
7077 :
7078 0 : JSObject* objectSingleton = types ? types->maybeSingleton() : nullptr;
7079 314 : if (objectSingleton)
7080 0 : return testSingletonProperty(objectSingleton, id);
7081 :
7082 0 : MIRType objType = obj->type();
7083 288 : if (objType == MIRType::Value && types)
7084 42 : objType = types->getKnownMIRType();
7085 :
7086 : JSProtoKey key;
7087 288 : switch (objType) {
7088 : case MIRType::String:
7089 : key = JSProto_String;
7090 : break;
7091 :
7092 : case MIRType::Symbol:
7093 0 : key = JSProto_Symbol;
7094 0 : break;
7095 :
7096 : case MIRType::Int32:
7097 : case MIRType::Double:
7098 0 : key = JSProto_Number;
7099 0 : break;
7100 :
7101 : case MIRType::Boolean:
7102 0 : key = JSProto_Boolean;
7103 0 : break;
7104 :
7105 : case MIRType::Object: {
7106 242 : if (!types)
7107 : return nullptr;
7108 :
7109 : // For property accesses which may be on many objects, we just need to
7110 : // find a prototype common to all the objects; if that prototype
7111 : // has the singleton property, the access will not be on a missing property.
7112 : JSObject* singleton = nullptr;
7113 0 : for (unsigned i = 0; i < types->getObjectCount(); i++) {
7114 0 : TypeSet::ObjectKey* key = types->getObject(i);
7115 0 : if (!key)
7116 0 : continue;
7117 255 : if (analysisContext)
7118 0 : key->ensureTrackedProperty(analysisContext, id);
7119 :
7120 0 : const Class* clasp = key->clasp();
7121 0 : if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(realm, key, id))
7122 119 : return nullptr;
7123 0 : if (key->unknownProperties())
7124 : return nullptr;
7125 254 : HeapTypeSetKey property = key->property(id);
7126 254 : if (property.isOwnProperty(constraints()))
7127 : return nullptr;
7128 :
7129 0 : if (JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull())) {
7130 : // Test this type.
7131 186 : JSObject* thisSingleton = testSingletonProperty(proto, id);
7132 0 : if (!thisSingleton)
7133 : return nullptr;
7134 145 : if (singleton) {
7135 13 : if (thisSingleton != singleton)
7136 : return nullptr;
7137 : } else {
7138 : singleton = thisSingleton;
7139 : }
7140 : } else {
7141 : // Can't be on the prototype chain with no prototypes...
7142 : return nullptr;
7143 : }
7144 : }
7145 : return singleton;
7146 : }
7147 : default:
7148 : return nullptr;
7149 : }
7150 :
7151 8 : if (JSObject* proto = script()->global().maybeGetPrototype(key))
7152 4 : return testSingletonProperty(proto, id);
7153 :
7154 : return nullptr;
7155 : }
7156 :
7157 : AbortReasonOr<bool>
7158 0 : IonBuilder::testNotDefinedProperty(MDefinition* obj, jsid id, bool ownProperty /* = false */)
7159 : {
7160 0 : TemporaryTypeSet* types = obj->resultTypeSet();
7161 98 : if (!types || types->unknownObject() || types->getKnownMIRType() != MIRType::Object)
7162 0 : return false;
7163 :
7164 0 : for (unsigned i = 0, count = types->getObjectCount(); i < count; i++) {
7165 25 : TypeSet::ObjectKey* key = types->getObject(i);
7166 25 : if (!key)
7167 : continue;
7168 :
7169 : while (true) {
7170 82 : if (!alloc().ensureBallast())
7171 0 : return abort(AbortReason::Alloc);
7172 :
7173 82 : if (!key->hasStableClassAndProto(constraints()) || key->unknownProperties())
7174 0 : return false;
7175 :
7176 0 : const Class* clasp = key->clasp();
7177 82 : if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(realm, key, id))
7178 0 : return false;
7179 :
7180 : // If the object is a singleton, we can do a lookup now to avoid
7181 : // unnecessary invalidations later on, in case the property types
7182 : // have not yet been instantiated.
7183 0 : if (key->isSingleton() &&
7184 253 : key->singleton()->is<NativeObject>() &&
7185 0 : key->singleton()->as<NativeObject>().lookupPure(id))
7186 : {
7187 0 : return false;
7188 : }
7189 :
7190 0 : HeapTypeSetKey property = key->property(id);
7191 82 : if (property.isOwnProperty(constraints()))
7192 6 : return false;
7193 :
7194 : // If we only care about own properties don't check the proto.
7195 76 : if (ownProperty)
7196 : break;
7197 :
7198 76 : JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
7199 0 : if (!proto)
7200 : break;
7201 57 : key = TypeSet::ObjectKey::get(proto);
7202 57 : }
7203 : }
7204 :
7205 19 : return true;
7206 : }
7207 :
7208 : AbortReasonOr<Ok>
7209 0 : IonBuilder::pushTypeBarrier(MDefinition* def, TemporaryTypeSet* observed, BarrierKind kind)
7210 : {
7211 0 : MOZ_ASSERT(def == current->peek(-1));
7212 :
7213 1 : MDefinition* replace = addTypeBarrier(current->pop(), observed, kind);
7214 4046 : if (!replace)
7215 0 : return abort(AbortReason::Alloc);
7216 :
7217 4046 : current->push(replace);
7218 4046 : return Ok();
7219 : }
7220 :
7221 : // Given an observed type set, annotates the IR as much as possible:
7222 : // (1) If no type information is provided, the given value is returned.
7223 : // (2) If a single type definitely exists, and no type barrier is needed,
7224 : // then an infallible unbox instruction is returned.
7225 : // (3) If a type barrier is needed, but has an unknown type set, the given
7226 : // value is returned.
7227 : // (4) Lastly, a type barrier instruction is added and returned.
7228 : MDefinition*
7229 4094 : IonBuilder::addTypeBarrier(MDefinition* def, TemporaryTypeSet* observed, BarrierKind kind,
7230 : MTypeBarrier** pbarrier)
7231 : {
7232 : // Barriers are never needed for instructions whose result will not be used.
7233 8188 : if (BytecodeIsPopped(pc))
7234 : return def;
7235 :
7236 : // If the instruction has no side effects, we'll resume the entire operation.
7237 : // The actual type barrier will occur in the interpreter. If the
7238 : // instruction is effectful, even if it has a singleton type, there
7239 : // must be a resume point capturing the original def, and resuming
7240 : // to that point will explicitly monitor the new type.
7241 0 : if (kind == BarrierKind::NoBarrier) {
7242 0 : MDefinition* replace = ensureDefiniteType(def, observed->getKnownMIRType());
7243 356 : replace->setResultTypeSet(observed);
7244 178 : return replace;
7245 : }
7246 :
7247 7214 : if (observed->unknown())
7248 : return def;
7249 :
7250 7214 : MTypeBarrier* barrier = MTypeBarrier::New(alloc(), def, observed, kind);
7251 0 : current->add(barrier);
7252 :
7253 3607 : if (pbarrier)
7254 0 : *pbarrier = barrier;
7255 :
7256 0 : if (barrier->type() == MIRType::Undefined)
7257 0 : return constant(UndefinedValue());
7258 3598 : if (barrier->type() == MIRType::Null)
7259 3 : return constant(NullValue());
7260 :
7261 : return barrier;
7262 : }
7263 :
7264 : AbortReasonOr<Ok>
7265 0 : IonBuilder::pushDOMTypeBarrier(MInstruction* ins, TemporaryTypeSet* observed, JSFunction* func)
7266 : {
7267 0 : MOZ_ASSERT(func && func->isNative() && func->hasJitInfo());
7268 :
7269 0 : const JSJitInfo* jitinfo = func->jitInfo();
7270 0 : bool barrier = DOMCallNeedsBarrier(jitinfo, observed);
7271 : // Need to be a bit careful: if jitinfo->returnType is JSVAL_TYPE_DOUBLE but
7272 : // types->getKnownMIRType() is MIRType::Int32, then don't unconditionally
7273 : // unbox as a double. Instead, go ahead and barrier on having an int type,
7274 : // since we know we need a barrier anyway due to the type mismatch. This is
7275 : // the only situation in which TI actually has more information about the
7276 : // JSValueType than codegen can, short of jitinfo->returnType just being
7277 : // JSVAL_TYPE_UNKNOWN.
7278 0 : MDefinition* replace = ins;
7279 0 : if (jitinfo->returnType() != JSVAL_TYPE_DOUBLE ||
7280 0 : observed->getKnownMIRType() != MIRType::Int32) {
7281 0 : replace = ensureDefiniteType(ins, MIRTypeFromValueType(jitinfo->returnType()));
7282 0 : if (replace != ins) {
7283 0 : current->pop();
7284 0 : current->push(replace);
7285 : }
7286 : } else {
7287 0 : MOZ_ASSERT(barrier);
7288 : }
7289 :
7290 : return pushTypeBarrier(replace, observed,
7291 0 : barrier ? BarrierKind::TypeSet : BarrierKind::NoBarrier);
7292 : }
7293 :
7294 : MDefinition*
7295 462 : IonBuilder::ensureDefiniteType(MDefinition* def, MIRType definiteType)
7296 : {
7297 : MInstruction* replace;
7298 0 : switch (definiteType) {
7299 : case MIRType::Undefined:
7300 0 : def->setImplicitlyUsedUnchecked();
7301 32 : replace = MConstant::New(alloc(), UndefinedValue());
7302 32 : break;
7303 :
7304 : case MIRType::Null:
7305 0 : def->setImplicitlyUsedUnchecked();
7306 3 : replace = MConstant::New(alloc(), NullValue());
7307 3 : break;
7308 :
7309 : case MIRType::Value:
7310 : return def;
7311 :
7312 : default: {
7313 0 : if (def->type() != MIRType::Value) {
7314 0 : if (def->type() == MIRType::Int32 && definiteType == MIRType::Double) {
7315 0 : replace = MToDouble::New(alloc(), def);
7316 0 : break;
7317 : }
7318 : return def;
7319 : }
7320 135 : replace = MUnbox::New(alloc(), def, definiteType, MUnbox::Infallible);
7321 135 : break;
7322 : }
7323 : }
7324 :
7325 170 : current->add(replace);
7326 170 : return replace;
7327 : }
7328 :
7329 : static size_t
7330 0 : NumFixedSlots(JSObject* object)
7331 : {
7332 : // Note: we can't use object->numFixedSlots() here, as this will read the
7333 : // shape and can race with the main thread if we are building off thread.
7334 : // The allocation kind and object class (which goes through the type) can
7335 : // be read freely, however.
7336 0 : gc::AllocKind kind = object->asTenured().getAllocKind();
7337 0 : return gc::GetGCKindSlots(kind, object->getClass());
7338 : }
7339 :
7340 : static bool
7341 0 : IsUninitializedGlobalLexicalSlot(JSObject* obj, PropertyName* name)
7342 : {
7343 0 : LexicalEnvironmentObject &globalLexical = obj->as<LexicalEnvironmentObject>();
7344 0 : MOZ_ASSERT(globalLexical.isGlobal());
7345 0 : Shape* shape = globalLexical.lookupPure(name);
7346 0 : if (!shape)
7347 : return false;
7348 0 : return globalLexical.getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL);
7349 : }
7350 :
7351 : AbortReasonOr<Ok>
7352 5 : IonBuilder::getStaticName(bool* emitted, JSObject* staticObject, PropertyName* name,
7353 : MDefinition* lexicalCheck)
7354 : {
7355 0 : MOZ_ASSERT(*emitted == false);
7356 :
7357 0 : jsid id = NameToId(name);
7358 :
7359 0 : bool isGlobalLexical = staticObject->is<LexicalEnvironmentObject>() &&
7360 5 : staticObject->as<LexicalEnvironmentObject>().isGlobal();
7361 10 : MOZ_ASSERT(isGlobalLexical ||
7362 : staticObject->is<GlobalObject>() ||
7363 : staticObject->is<CallObject>() ||
7364 : staticObject->is<ModuleEnvironmentObject>());
7365 5 : MOZ_ASSERT(staticObject->isSingleton());
7366 :
7367 : // Always emit the lexical check. This could be optimized, but is
7368 : // currently not for simplicity's sake.
7369 5 : if (lexicalCheck)
7370 0 : return Ok();
7371 :
7372 0 : TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(staticObject);
7373 5 : if (analysisContext)
7374 0 : staticKey->ensureTrackedProperty(analysisContext, NameToId(name));
7375 :
7376 5 : if (staticKey->unknownProperties())
7377 0 : return Ok();
7378 :
7379 0 : HeapTypeSetKey property = staticKey->property(id);
7380 0 : if (!property.maybeTypes() ||
7381 5 : !property.maybeTypes()->definiteProperty() ||
7382 0 : property.nonData(constraints()))
7383 : {
7384 : // The property has been reconfigured as non-configurable, non-enumerable
7385 : // or non-writable.
7386 5 : return Ok();
7387 : }
7388 :
7389 : // Don't optimize global lexical bindings if they aren't initialized at
7390 : // compile time.
7391 0 : if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name))
7392 0 : return Ok();
7393 :
7394 0 : *emitted = true;
7395 :
7396 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
7397 0 : BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
7398 : staticKey, name, types,
7399 0 : /* updateObserved = */ true);
7400 :
7401 0 : if (barrier == BarrierKind::NoBarrier) {
7402 : // Try to inline properties holding a known constant object.
7403 0 : JSObject* singleton = types->maybeSingleton();
7404 0 : if (singleton) {
7405 0 : if (testSingletonProperty(staticObject, id) == singleton) {
7406 0 : pushConstant(ObjectValue(*singleton));
7407 0 : return Ok();
7408 : }
7409 : }
7410 :
7411 : // Try to inline properties that have never been overwritten.
7412 0 : Value constantValue;
7413 0 : if (property.constant(constraints(), &constantValue)) {
7414 0 : pushConstant(constantValue);
7415 0 : return Ok();
7416 : }
7417 : }
7418 :
7419 0 : MOZ_TRY(loadStaticSlot(staticObject, barrier, types, property.maybeTypes()->definiteSlot()));
7420 :
7421 0 : return Ok();
7422 : }
7423 :
7424 : AbortReasonOr<Ok>
7425 0 : IonBuilder::loadStaticSlot(JSObject* staticObject, BarrierKind barrier, TemporaryTypeSet* types,
7426 : uint32_t slot)
7427 : {
7428 0 : if (barrier == BarrierKind::NoBarrier) {
7429 : // Try to inline properties that can only have one value.
7430 0 : MIRType knownType = types->getKnownMIRType();
7431 0 : if (knownType == MIRType::Undefined) {
7432 0 : pushConstant(UndefinedValue());
7433 0 : return Ok();
7434 : }
7435 0 : if (knownType == MIRType::Null) {
7436 0 : pushConstant(NullValue());
7437 0 : return Ok();
7438 : }
7439 : }
7440 :
7441 0 : MInstruction* obj = constant(ObjectValue(*staticObject));
7442 :
7443 0 : MIRType rvalType = types->getKnownMIRType();
7444 0 : if (barrier != BarrierKind::NoBarrier)
7445 0 : rvalType = MIRType::Value;
7446 :
7447 0 : return loadSlot(obj, slot, NumFixedSlots(staticObject), rvalType, barrier, types);
7448 : }
7449 :
7450 : // Whether a write of the given value may need a post-write barrier for GC purposes.
7451 : bool
7452 0 : IonBuilder::needsPostBarrier(MDefinition* value)
7453 : {
7454 1189 : CompileZone* zone = realm->zone();
7455 0 : if (!zone->nurseryExists())
7456 : return false;
7457 0 : if (value->mightBeType(MIRType::Object))
7458 : return true;
7459 0 : if (value->mightBeType(MIRType::String) && zone->canNurseryAllocateStrings())
7460 : return true;
7461 907 : return false;
7462 : }
7463 :
7464 : AbortReasonOr<Ok>
7465 0 : IonBuilder::setStaticName(JSObject* staticObject, PropertyName* name)
7466 : {
7467 0 : jsid id = NameToId(name);
7468 :
7469 0 : bool isGlobalLexical = staticObject->is<LexicalEnvironmentObject>() &&
7470 0 : staticObject->as<LexicalEnvironmentObject>().isGlobal();
7471 0 : MOZ_ASSERT(isGlobalLexical ||
7472 : staticObject->is<GlobalObject>() ||
7473 : staticObject->is<CallObject>());
7474 :
7475 0 : MDefinition* value = current->peek(-1);
7476 :
7477 0 : TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(staticObject);
7478 0 : if (staticKey->unknownProperties())
7479 0 : return jsop_setprop(name);
7480 :
7481 0 : HeapTypeSetKey property = staticKey->property(id);
7482 0 : if (!property.maybeTypes() ||
7483 0 : !property.maybeTypes()->definiteProperty() ||
7484 0 : property.nonData(constraints()) ||
7485 0 : property.nonWritable(constraints()))
7486 : {
7487 : // The property has been reconfigured as non-configurable, non-enumerable
7488 : // or non-writable.
7489 0 : return jsop_setprop(name);
7490 : }
7491 :
7492 0 : if (!CanWriteProperty(alloc(), constraints(), property, value))
7493 0 : return jsop_setprop(name);
7494 :
7495 : // Don't optimize global lexical bindings if they aren't initialized at
7496 : // compile time.
7497 0 : if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name))
7498 0 : return jsop_setprop(name);
7499 :
7500 0 : current->pop();
7501 :
7502 : // Pop the bound object on the stack.
7503 0 : MDefinition* obj = current->pop();
7504 0 : MOZ_ASSERT(&obj->toConstant()->toObject() == staticObject);
7505 :
7506 0 : if (needsPostBarrier(value))
7507 0 : current->add(MPostWriteBarrier::New(alloc(), obj, value));
7508 :
7509 : // If the property has a known type, we may be able to optimize typed stores by not
7510 : // storing the type tag.
7511 0 : MIRType slotType = MIRType::None;
7512 0 : MIRType knownType = property.knownMIRType(constraints());
7513 0 : if (knownType != MIRType::Value)
7514 0 : slotType = knownType;
7515 :
7516 0 : bool needsPreBarrier = property.needsBarrier(constraints());
7517 0 : return storeSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
7518 0 : value, needsPreBarrier, slotType);
7519 : }
7520 :
7521 : JSObject*
7522 0 : IonBuilder::testGlobalLexicalBinding(PropertyName* name)
7523 : {
7524 5 : MOZ_ASSERT(JSOp(*pc) == JSOP_BINDGNAME ||
7525 : JSOp(*pc) == JSOP_GETGNAME ||
7526 : JSOp(*pc) == JSOP_SETGNAME ||
7527 : JSOp(*pc) == JSOP_STRICTSETGNAME);
7528 :
7529 : // The global isn't the global lexical env's prototype, but its enclosing
7530 : // env. Test for the existence of |name| manually on the global lexical
7531 : // env. If it is not found, look for it on the global itself.
7532 :
7533 0 : NativeObject* obj = &script()->global().lexicalEnvironment();
7534 0 : TypeSet::ObjectKey* lexicalKey = TypeSet::ObjectKey::get(obj);
7535 0 : jsid id = NameToId(name);
7536 5 : if (analysisContext)
7537 0 : lexicalKey->ensureTrackedProperty(analysisContext, id);
7538 :
7539 : // If the property is not found on the global lexical env but it is found
7540 : // on the global and is configurable, try to freeze the typeset for its
7541 : // non-existence. If we don't have type information then fail.
7542 : //
7543 : // In the case that it is found on the global but is non-configurable,
7544 : // the binding cannot be shadowed by a global lexical binding.
7545 0 : Maybe<HeapTypeSetKey> lexicalProperty;
7546 0 : if (!lexicalKey->unknownProperties())
7547 0 : lexicalProperty.emplace(lexicalKey->property(id));
7548 0 : Shape* shape = obj->lookupPure(name);
7549 0 : if (shape) {
7550 0 : if ((JSOp(*pc) != JSOP_GETGNAME && !shape->writable()) ||
7551 0 : obj->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
7552 : {
7553 : return nullptr;
7554 : }
7555 : } else {
7556 0 : shape = script()->global().lookupPure(name);
7557 0 : if (!shape || shape->configurable()) {
7558 3 : if (lexicalProperty.isSome())
7559 3 : MOZ_ALWAYS_FALSE(lexicalProperty->isOwnProperty(constraints()));
7560 : else
7561 : return nullptr;
7562 : }
7563 10 : obj = &script()->global();
7564 : }
7565 :
7566 : return obj;
7567 : }
7568 :
7569 : AbortReasonOr<Ok>
7570 5 : IonBuilder::jsop_getgname(PropertyName* name)
7571 : {
7572 : // Optimize undefined/NaN/Infinity first. We must ensure we handle these
7573 : // cases *exactly* like Baseline, because it's invalid to add an Ion IC or
7574 : // VM call (that might trigger invalidation) if there's no Baseline IC for
7575 : // this op.
7576 0 : if (name == names().undefined) {
7577 0 : pushConstant(UndefinedValue());
7578 0 : return Ok();
7579 : }
7580 0 : if (name == names().NaN) {
7581 0 : pushConstant(realm->runtime()->NaNValue());
7582 0 : return Ok();
7583 : }
7584 0 : if (name == names().Infinity) {
7585 0 : pushConstant(realm->runtime()->positiveInfinityValue());
7586 0 : return Ok();
7587 : }
7588 :
7589 0 : if (JSObject* obj = testGlobalLexicalBinding(name)) {
7590 0 : bool emitted = false;
7591 0 : MOZ_TRY(getStaticName(&emitted, obj, name));
7592 5 : if (emitted)
7593 0 : return Ok();
7594 :
7595 0 : if (!forceInlineCaches() && obj->is<GlobalObject>()) {
7596 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
7597 0 : MDefinition* globalObj = constant(ObjectValue(*obj));
7598 0 : MOZ_TRY(getPropTryCommonGetter(&emitted, globalObj, name, types));
7599 5 : if (emitted)
7600 0 : return Ok();
7601 : }
7602 : }
7603 :
7604 5 : return jsop_getname(name);
7605 : }
7606 :
7607 : AbortReasonOr<Ok>
7608 183 : IonBuilder::jsop_getname(PropertyName* name)
7609 : {
7610 : MDefinition* object;
7611 427 : if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope())
7612 0 : object = constant(ObjectValue(script()->global().lexicalEnvironment()));
7613 : else
7614 0 : object = current->environmentChain();
7615 :
7616 0 : MGetNameCache* ins = MGetNameCache::New(alloc(), object);
7617 183 : current->add(ins);
7618 0 : current->push(ins);
7619 :
7620 0 : MOZ_TRY(resumeAfter(ins));
7621 :
7622 366 : TemporaryTypeSet* types = bytecodeTypes(pc);
7623 183 : return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
7624 : }
7625 :
7626 : AbortReasonOr<Ok>
7627 0 : IonBuilder::jsop_intrinsic(PropertyName* name)
7628 : {
7629 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
7630 :
7631 2108 : Value vp = UndefinedValue();
7632 : // If the intrinsic value doesn't yet exist, we haven't executed this
7633 : // opcode yet, so we need to get it and monitor the result.
7634 4216 : if (!script()->global().maybeExistingIntrinsicValue(name, &vp)) {
7635 0 : MCallGetIntrinsicValue* ins = MCallGetIntrinsicValue::New(alloc(), name);
7636 :
7637 574 : current->add(ins);
7638 0 : current->push(ins);
7639 :
7640 0 : MOZ_TRY(resumeAfter(ins));
7641 :
7642 574 : return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
7643 : }
7644 :
7645 3068 : if (types->empty())
7646 252 : types->addType(TypeSet::GetValueType(vp), alloc().lifoAlloc());
7647 :
7648 : // Bake in the intrinsic, guaranteed to exist because a non-empty typeset
7649 : // means the intrinsic was successfully gotten in the VM call above.
7650 : // Assert that TI agrees with us on the type.
7651 0 : MOZ_ASSERT(types->hasType(TypeSet::GetValueType(vp)));
7652 :
7653 1534 : pushConstant(vp);
7654 1534 : return Ok();
7655 : }
7656 :
7657 : AbortReasonOr<Ok>
7658 0 : IonBuilder::jsop_getimport(PropertyName* name)
7659 : {
7660 0 : ModuleEnvironmentObject* env = GetModuleEnvironmentForScript(script());
7661 0 : MOZ_ASSERT(env);
7662 :
7663 : Shape* shape;
7664 : ModuleEnvironmentObject* targetEnv;
7665 0 : MOZ_ALWAYS_TRUE(env->lookupImport(NameToId(name), &targetEnv, &shape));
7666 :
7667 0 : PropertyName* localName = JSID_TO_STRING(shape->propid())->asAtom().asPropertyName();
7668 0 : bool emitted = false;
7669 0 : MOZ_TRY(getStaticName(&emitted, targetEnv, localName));
7670 :
7671 0 : if (!emitted) {
7672 : // This can happen if we don't have type information.
7673 0 : TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(targetEnv);
7674 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
7675 0 : BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
7676 : staticKey, name, types,
7677 0 : /* updateObserved = */ true);
7678 :
7679 0 : MOZ_TRY(loadStaticSlot(targetEnv, barrier, types, shape->slot()));
7680 : }
7681 :
7682 : // In the rare case where this import hasn't been initialized already (we
7683 : // have an import cycle where modules reference each other's imports), emit
7684 : // a check.
7685 0 : if (targetEnv->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) {
7686 : MDefinition* checked;
7687 0 : MOZ_TRY_VAR(checked, addLexicalCheck(current->pop()));
7688 0 : current->push(checked);
7689 : }
7690 :
7691 0 : return Ok();
7692 : }
7693 :
7694 : AbortReasonOr<Ok>
7695 0 : IonBuilder::jsop_bindname(PropertyName* name)
7696 : {
7697 : MDefinition* envChain;
7698 0 : if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope())
7699 0 : envChain = constant(ObjectValue(script()->global().lexicalEnvironment()));
7700 : else
7701 0 : envChain = current->environmentChain();
7702 :
7703 0 : MBindNameCache* ins = MBindNameCache::New(alloc(), envChain, name, script(), pc);
7704 0 : current->add(ins);
7705 0 : current->push(ins);
7706 :
7707 0 : return resumeAfter(ins);
7708 : }
7709 :
7710 : AbortReasonOr<Ok>
7711 0 : IonBuilder::jsop_bindvar()
7712 : {
7713 0 : MOZ_ASSERT(usesEnvironmentChain());
7714 0 : MCallBindVar* ins = MCallBindVar::New(alloc(), current->environmentChain());
7715 0 : current->add(ins);
7716 0 : current->push(ins);
7717 0 : return Ok();
7718 : }
7719 :
7720 : static MIRType
7721 : GetElemKnownType(bool needsHoleCheck, TemporaryTypeSet* types)
7722 : {
7723 0 : MIRType knownType = types->getKnownMIRType();
7724 :
7725 : // Null and undefined have no payload so they can't be specialized.
7726 : // Since folding null/undefined while building SSA is not safe (see the
7727 : // comment in IsPhiObservable), we just add an untyped load instruction
7728 : // and rely on pushTypeBarrier and DCE to replace it with a null/undefined
7729 : // constant.
7730 0 : if (knownType == MIRType::Undefined || knownType == MIRType::Null)
7731 0 : knownType = MIRType::Value;
7732 :
7733 : // Different architectures may want typed element reads which require
7734 : // hole checks to be done as either value or typed reads.
7735 0 : if (needsHoleCheck && !LIRGenerator::allowTypedElementHoleCheck())
7736 0 : knownType = MIRType::Value;
7737 :
7738 : return knownType;
7739 : }
7740 :
7741 : AbortReasonOr<Ok>
7742 0 : IonBuilder::jsop_getelem()
7743 : {
7744 0 : startTrackingOptimizations();
7745 :
7746 573 : MDefinition* index = current->pop();
7747 0 : MDefinition* obj = current->pop();
7748 :
7749 1146 : trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet());
7750 1146 : trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet());
7751 :
7752 : // Always use a call if we are performing analysis and not actually
7753 : // emitting code, to simplify later analysis.
7754 573 : if (info().isAnalysis() || shouldAbortOnPreliminaryGroups(obj)) {
7755 0 : MInstruction* ins = MCallGetElement::New(alloc(), obj, index);
7756 :
7757 267 : current->add(ins);
7758 0 : current->push(ins);
7759 :
7760 0 : MOZ_TRY(resumeAfter(ins));
7761 :
7762 534 : TemporaryTypeSet* types = bytecodeTypes(pc);
7763 267 : return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
7764 : }
7765 :
7766 306 : bool emitted = false;
7767 :
7768 : // Handle lazy-arguments first. We have to do this even if forceInlineCaches
7769 : // is true (lazy arguments cannot escape to the IC). Like the code in
7770 : // IonBuilder::jsop_getprop, we only do this if we're not in analysis mode,
7771 : // to avoid unnecessary analysis aborts.
7772 0 : if (obj->mightBeType(MIRType::MagicOptimizedArguments) && !info().isAnalysis()) {
7773 0 : trackOptimizationAttempt(TrackedStrategy::GetElem_Arguments);
7774 0 : MOZ_TRY(getElemTryArguments(&emitted, obj, index));
7775 3 : if (emitted)
7776 0 : return Ok();
7777 :
7778 0 : trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlinedConstant);
7779 0 : MOZ_TRY(getElemTryArgumentsInlinedConstant(&emitted, obj, index));
7780 1 : if (emitted)
7781 0 : return Ok();
7782 :
7783 0 : trackOptimizationAttempt(TrackedStrategy::GetElem_ArgumentsInlinedSwitch);
7784 0 : MOZ_TRY(getElemTryArgumentsInlinedIndex(&emitted, obj, index));
7785 0 : if (emitted)
7786 0 : return Ok();
7787 :
7788 0 : if (script()->argumentsHasVarBinding()) {
7789 0 : trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
7790 0 : return abort(AbortReason::Disable, "Type is not definitely lazy arguments.");
7791 : }
7792 : }
7793 :
7794 0 : obj = maybeUnboxForPropertyAccess(obj);
7795 303 : if (obj->type() == MIRType::Object)
7796 0 : obj = convertUnboxedObjects(obj);
7797 :
7798 606 : if (!forceInlineCaches()) {
7799 : // Note: no trackOptimizationAttempt call is needed, getElemTryGetProp
7800 : // will call it.
7801 0 : MOZ_TRY(getElemTryGetProp(&emitted, obj, index));
7802 303 : if (emitted)
7803 0 : return Ok();
7804 :
7805 0 : trackOptimizationAttempt(TrackedStrategy::GetElem_CallSiteObject);
7806 0 : MOZ_TRY(getElemTryCallSiteObject(&emitted, obj, index));
7807 278 : if (emitted)
7808 0 : return Ok();
7809 :
7810 0 : trackOptimizationAttempt(TrackedStrategy::GetElem_Dense);
7811 0 : MOZ_TRY(getElemTryDense(&emitted, obj, index));
7812 278 : if (emitted)
7813 0 : return Ok();
7814 :
7815 0 : trackOptimizationAttempt(TrackedStrategy::GetElem_TypedArray);
7816 0 : MOZ_TRY(getElemTryTypedArray(&emitted, obj, index));
7817 210 : if (emitted)
7818 0 : return Ok();
7819 :
7820 0 : trackOptimizationAttempt(TrackedStrategy::GetElem_String);
7821 0 : MOZ_TRY(getElemTryString(&emitted, obj, index));
7822 210 : if (emitted)
7823 0 : return Ok();
7824 :
7825 0 : trackOptimizationAttempt(TrackedStrategy::GetElem_TypedObject);
7826 0 : MOZ_TRY(getElemTryTypedObject(&emitted, obj, index));
7827 210 : if (emitted)
7828 0 : return Ok();
7829 : }
7830 :
7831 210 : trackOptimizationAttempt(TrackedStrategy::GetElem_InlineCache);
7832 210 : return getElemAddCache(obj, index);
7833 : }
7834 :
7835 : AbortReasonOr<Ok>
7836 0 : IonBuilder::getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index)
7837 : {
7838 210 : MOZ_ASSERT(*emitted == false);
7839 :
7840 : // The next several failures are all due to types not predicting that we
7841 : // are definitely doing a getelem access on a typed object.
7842 0 : trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject);
7843 :
7844 0 : TypedObjectPrediction objPrediction = typedObjectPrediction(obj);
7845 210 : if (objPrediction.isUseless())
7846 0 : return Ok();
7847 :
7848 0 : if (!objPrediction.ofArrayKind())
7849 0 : return Ok();
7850 :
7851 0 : TypedObjectPrediction elemPrediction = objPrediction.arrayElementType();
7852 0 : if (elemPrediction.isUseless())
7853 0 : return Ok();
7854 :
7855 : uint32_t elemSize;
7856 0 : if (!elemPrediction.hasKnownSize(&elemSize))
7857 0 : return Ok();
7858 :
7859 0 : switch (elemPrediction.kind()) {
7860 : case type::Simd:
7861 : // FIXME (bug 894105): load into a MIRType::float32x4 etc
7862 0 : trackOptimizationOutcome(TrackedOutcome::GenericFailure);
7863 0 : return Ok();
7864 :
7865 : case type::Struct:
7866 : case type::Array:
7867 : return getElemTryComplexElemOfTypedObject(emitted,
7868 : obj,
7869 : index,
7870 : objPrediction,
7871 : elemPrediction,
7872 0 : elemSize);
7873 : case type::Scalar:
7874 : return getElemTryScalarElemOfTypedObject(emitted,
7875 : obj,
7876 : index,
7877 : objPrediction,
7878 : elemPrediction,
7879 0 : elemSize);
7880 :
7881 : case type::Reference:
7882 : return getElemTryReferenceElemOfTypedObject(emitted,
7883 : obj,
7884 : index,
7885 : objPrediction,
7886 0 : elemPrediction);
7887 : }
7888 :
7889 0 : MOZ_CRASH("Bad kind");
7890 : }
7891 :
7892 : bool
7893 0 : IonBuilder::checkTypedObjectIndexInBounds(uint32_t elemSize,
7894 : MDefinition* index,
7895 : TypedObjectPrediction objPrediction,
7896 : LinearSum* indexAsByteOffset)
7897 : {
7898 : // Ensure index is an integer.
7899 0 : MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
7900 0 : current->add(idInt32);
7901 :
7902 : // If we know the length statically from the type, just embed it.
7903 : // Otherwise, load it from the appropriate reserved slot on the
7904 : // typed object. We know it's an int32, so we can convert from
7905 : // Value to int32 using truncation.
7906 : int32_t lenOfAll;
7907 : MDefinition* length;
7908 0 : if (objPrediction.hasKnownArrayLength(&lenOfAll)) {
7909 0 : length = constantInt(lenOfAll);
7910 :
7911 : // If we are not loading the length from the object itself, only
7912 : // optimize if the array buffer can never be a detached array buffer.
7913 0 : TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
7914 0 : if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER)) {
7915 : trackOptimizationOutcome(TrackedOutcome::TypedObjectHasDetachedBuffer);
7916 : return false;
7917 : }
7918 : } else {
7919 : trackOptimizationOutcome(TrackedOutcome::TypedObjectArrayRange);
7920 : return false;
7921 : }
7922 :
7923 0 : index = addBoundsCheck(idInt32, length);
7924 :
7925 0 : return indexAsByteOffset->add(index, AssertedCast<int32_t>(elemSize));
7926 : }
7927 :
7928 : AbortReasonOr<Ok>
7929 0 : IonBuilder::getElemTryScalarElemOfTypedObject(bool* emitted,
7930 : MDefinition* obj,
7931 : MDefinition* index,
7932 : TypedObjectPrediction objPrediction,
7933 : TypedObjectPrediction elemPrediction,
7934 : uint32_t elemSize)
7935 : {
7936 0 : MOZ_ASSERT(objPrediction.ofArrayKind());
7937 :
7938 : // Must always be loading the same scalar type
7939 0 : ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
7940 0 : MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
7941 :
7942 0 : LinearSum indexAsByteOffset(alloc());
7943 0 : if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
7944 0 : return Ok();
7945 :
7946 0 : trackOptimizationSuccess();
7947 0 : *emitted = true;
7948 :
7949 0 : return pushScalarLoadFromTypedObject(obj, indexAsByteOffset, elemType);
7950 : }
7951 :
7952 : AbortReasonOr<Ok>
7953 0 : IonBuilder::getElemTryReferenceElemOfTypedObject(bool* emitted,
7954 : MDefinition* obj,
7955 : MDefinition* index,
7956 : TypedObjectPrediction objPrediction,
7957 : TypedObjectPrediction elemPrediction)
7958 : {
7959 0 : MOZ_ASSERT(objPrediction.ofArrayKind());
7960 :
7961 0 : ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
7962 0 : uint32_t elemSize = ReferenceTypeDescr::size(elemType);
7963 :
7964 0 : LinearSum indexAsByteOffset(alloc());
7965 0 : if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
7966 0 : return Ok();
7967 :
7968 0 : trackOptimizationSuccess();
7969 0 : *emitted = true;
7970 :
7971 0 : return pushReferenceLoadFromTypedObject(obj, indexAsByteOffset, elemType, nullptr);
7972 : }
7973 :
7974 : AbortReasonOr<Ok>
7975 0 : IonBuilder::pushScalarLoadFromTypedObject(MDefinition* obj,
7976 : const LinearSum& byteOffset,
7977 : ScalarTypeDescr::Type elemType)
7978 : {
7979 0 : uint32_t size = ScalarTypeDescr::size(elemType);
7980 0 : MOZ_ASSERT(size == ScalarTypeDescr::alignment(elemType));
7981 :
7982 : // Find location within the owner object.
7983 : MDefinition* elements;
7984 : MDefinition* scaledOffset;
7985 : int32_t adjustment;
7986 0 : MOZ_TRY(loadTypedObjectElements(obj, byteOffset, size, &elements, &scaledOffset, &adjustment));
7987 :
7988 : // Load the element.
7989 0 : MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset,
7990 : elemType,
7991 : DoesNotRequireMemoryBarrier,
7992 0 : adjustment);
7993 0 : current->add(load);
7994 0 : current->push(load);
7995 :
7996 : // If we are reading in-bounds elements, we can use knowledge about
7997 : // the array type to determine the result type, even if the opcode has
7998 : // never executed. The known pushed type is only used to distinguish
7999 : // uint32 reads that may produce either doubles or integers.
8000 0 : TemporaryTypeSet* resultTypes = bytecodeTypes(pc);
8001 0 : bool allowDouble = resultTypes->hasType(TypeSet::DoubleType());
8002 :
8003 : // Note: knownType is not necessarily in resultTypes; e.g. if we
8004 : // have only observed integers coming out of float array.
8005 0 : MIRType knownType = MIRTypeForTypedArrayRead(elemType, allowDouble);
8006 :
8007 : // Note: we can ignore the type barrier here, we know the type must
8008 : // be valid and unbarriered. Also, need not set resultTypeSet,
8009 : // because knownType is scalar and a resultTypeSet would provide
8010 : // no useful additional info.
8011 0 : load->setResultType(knownType);
8012 :
8013 0 : return Ok();
8014 : }
8015 :
8016 : AbortReasonOr<Ok>
8017 0 : IonBuilder::pushReferenceLoadFromTypedObject(MDefinition* typedObj,
8018 : const LinearSum& byteOffset,
8019 : ReferenceTypeDescr::Type type,
8020 : PropertyName* name)
8021 : {
8022 : // Find location within the owner object.
8023 : MDefinition* elements;
8024 : MDefinition* scaledOffset;
8025 : int32_t adjustment;
8026 0 : uint32_t alignment = ReferenceTypeDescr::alignment(type);
8027 0 : MOZ_TRY(loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset, &adjustment));
8028 :
8029 0 : TemporaryTypeSet* observedTypes = bytecodeTypes(pc);
8030 :
8031 0 : MInstruction* load = nullptr; // initialize to silence GCC warning
8032 0 : BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
8033 0 : typedObj, name, observedTypes);
8034 :
8035 0 : switch (type) {
8036 : case ReferenceTypeDescr::TYPE_ANY: {
8037 : // Make sure the barrier reflects the possibility of reading undefined.
8038 0 : bool bailOnUndefined = barrier == BarrierKind::NoBarrier &&
8039 0 : !observedTypes->hasType(TypeSet::UndefinedType());
8040 0 : if (bailOnUndefined)
8041 0 : barrier = BarrierKind::TypeTagOnly;
8042 0 : load = MLoadElement::New(alloc(), elements, scaledOffset, false, false, adjustment);
8043 0 : break;
8044 : }
8045 : case ReferenceTypeDescr::TYPE_OBJECT: {
8046 : // Make sure the barrier reflects the possibility of reading null. When
8047 : // there is no other barrier needed we include the null bailout with
8048 : // MLoadUnboxedObjectOrNull, which avoids the need to box the result
8049 : // for a type barrier instruction.
8050 : MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
8051 0 : if (barrier == BarrierKind::NoBarrier && !observedTypes->hasType(TypeSet::NullType()))
8052 : nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull;
8053 : else
8054 0 : nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
8055 0 : load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, nullBehavior,
8056 0 : adjustment);
8057 : break;
8058 : }
8059 : case ReferenceTypeDescr::TYPE_STRING: {
8060 0 : load = MLoadUnboxedString::New(alloc(), elements, scaledOffset, adjustment);
8061 0 : observedTypes->addType(TypeSet::StringType(), alloc().lifoAlloc());
8062 0 : break;
8063 : }
8064 : }
8065 :
8066 0 : current->add(load);
8067 0 : current->push(load);
8068 :
8069 0 : return pushTypeBarrier(load, observedTypes, barrier);
8070 : }
8071 :
8072 : AbortReasonOr<Ok>
8073 0 : IonBuilder::getElemTryComplexElemOfTypedObject(bool* emitted,
8074 : MDefinition* obj,
8075 : MDefinition* index,
8076 : TypedObjectPrediction objPrediction,
8077 : TypedObjectPrediction elemPrediction,
8078 : uint32_t elemSize)
8079 : {
8080 0 : MOZ_ASSERT(objPrediction.ofArrayKind());
8081 :
8082 0 : MDefinition* type = loadTypedObjectType(obj);
8083 0 : MDefinition* elemTypeObj = typeObjectForElementFromArrayStructType(type);
8084 :
8085 0 : LinearSum indexAsByteOffset(alloc());
8086 0 : if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
8087 0 : return Ok();
8088 :
8089 : return pushDerivedTypedObject(emitted, obj, indexAsByteOffset,
8090 0 : elemPrediction, elemTypeObj);
8091 : }
8092 :
8093 : AbortReasonOr<Ok>
8094 0 : IonBuilder::pushDerivedTypedObject(bool* emitted,
8095 : MDefinition* obj,
8096 : const LinearSum& baseByteOffset,
8097 : TypedObjectPrediction derivedPrediction,
8098 : MDefinition* derivedTypeObj)
8099 : {
8100 : // Find location within the owner object.
8101 : MDefinition* owner;
8102 0 : LinearSum ownerByteOffset(alloc());
8103 0 : MOZ_TRY(loadTypedObjectData(obj, &owner, &ownerByteOffset));
8104 0 : if (!ownerByteOffset.add(baseByteOffset, 1))
8105 0 : return abort(AbortReason::Disable, "Overflow/underflow on type object offset.");
8106 :
8107 0 : MDefinition* offset = ConvertLinearSum(alloc(), current, ownerByteOffset,
8108 0 : /* convertConstant = */ true);
8109 :
8110 : // Create the derived typed object.
8111 0 : MInstruction* derivedTypedObj = MNewDerivedTypedObject::New(alloc(),
8112 : derivedPrediction,
8113 : derivedTypeObj,
8114 : owner,
8115 0 : offset);
8116 0 : current->add(derivedTypedObj);
8117 0 : current->push(derivedTypedObj);
8118 :
8119 : // Determine (if possible) the class/proto that `derivedTypedObj` will
8120 : // have. For derived typed objects, the opacity will be the same as the
8121 : // incoming object from which the derived typed object is, well, derived.
8122 : // The prototype will be determined based on the type descriptor (and is
8123 : // immutable).
8124 0 : TemporaryTypeSet* objTypes = obj->resultTypeSet();
8125 0 : const Class* expectedClass = nullptr;
8126 0 : if (const Class* objClass = objTypes ? objTypes->getKnownClass(constraints()) : nullptr) {
8127 0 : MOZ_ASSERT(IsTypedObjectClass(objClass));
8128 0 : expectedClass = GetOutlineTypedObjectClass(IsOpaqueTypedObjectClass(objClass));
8129 : }
8130 0 : const TypedProto* expectedProto = derivedPrediction.getKnownPrototype();
8131 0 : MOZ_ASSERT_IF(expectedClass, IsTypedObjectClass(expectedClass));
8132 :
8133 : // Determine (if possible) the class/proto that the observed type set
8134 : // describes.
8135 0 : TemporaryTypeSet* observedTypes = bytecodeTypes(pc);
8136 0 : const Class* observedClass = observedTypes->getKnownClass(constraints());
8137 :
8138 : // If expectedClass/expectedProto are both non-null (and hence known), we
8139 : // can predict precisely what object group derivedTypedObj will have.
8140 : // Therefore, if we observe that this group is already contained in the set
8141 : // of observedTypes, we can skip the barrier.
8142 : //
8143 : // Barriers still wind up being needed in some relatively
8144 : // rare cases:
8145 : //
8146 : // - if multiple kinds of typed objects flow into this point,
8147 : // in which case we will not be able to predict expectedClass
8148 : // nor expectedProto.
8149 : //
8150 : // - if the code has never executed, in which case the set of
8151 : // observed types will be incomplete.
8152 : //
8153 : // Barriers are particularly expensive here because they prevent
8154 : // us from optimizing the MNewDerivedTypedObject away.
8155 : JSObject* observedProto;
8156 0 : if (observedTypes->getCommonPrototype(constraints(), &observedProto) &&
8157 0 : observedClass && observedProto && observedClass == expectedClass &&
8158 : observedProto == expectedProto)
8159 : {
8160 0 : derivedTypedObj->setResultTypeSet(observedTypes);
8161 : } else {
8162 0 : MOZ_TRY(pushTypeBarrier(derivedTypedObj, observedTypes, BarrierKind::TypeSet));
8163 : }
8164 :
8165 0 : trackOptimizationSuccess();
8166 0 : *emitted = true;
8167 0 : return Ok();
8168 : }
8169 :
8170 : AbortReasonOr<Ok>
8171 303 : IonBuilder::getElemTryGetProp(bool* emitted, MDefinition* obj, MDefinition* index)
8172 : {
8173 : // If index is a constant string or symbol, try to optimize this GETELEM
8174 : // as a GETPROP.
8175 :
8176 0 : MOZ_ASSERT(*emitted == false);
8177 :
8178 0 : MConstant* indexConst = index->maybeConstantValue();
8179 0 : jsid id;
8180 303 : if (!indexConst || !ValueToIdPure(indexConst->toJSValue(), &id))
8181 0 : return Ok();
8182 :
8183 236 : if (id != IdToTypeId(id))
8184 0 : return Ok();
8185 :
8186 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
8187 :
8188 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
8189 0 : MOZ_TRY(getPropTryConstant(emitted, obj, id, types) );
8190 0 : if (*emitted) {
8191 25 : index->setImplicitlyUsedUnchecked();
8192 25 : return Ok();
8193 : }
8194 :
8195 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_NotDefined);
8196 0 : MOZ_TRY(getPropTryNotDefined(emitted, obj, id, types) );
8197 0 : if (*emitted) {
8198 0 : index->setImplicitlyUsedUnchecked();
8199 0 : return Ok();
8200 : }
8201 :
8202 21 : return Ok();
8203 : }
8204 :
8205 : AbortReasonOr<Ok>
8206 0 : IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
8207 : {
8208 0 : MOZ_ASSERT(*emitted == false);
8209 :
8210 0 : if (!ElementAccessIsDenseNative(constraints(), obj, index)) {
8211 210 : trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
8212 210 : return Ok();
8213 : }
8214 :
8215 : // Don't generate a fast path if there have been bounds check failures
8216 : // and this access might be on a sparse property.
8217 : bool hasExtraIndexedProperty;
8218 0 : MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
8219 0 : if (hasExtraIndexedProperty && failedBoundsCheck_) {
8220 0 : trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
8221 0 : return Ok();
8222 : }
8223 :
8224 : // Don't generate a fast path if this pc has seen negative indexes accessed,
8225 : // which will not appear to be extra indexed properties.
8226 0 : if (inspector->hasSeenNegativeIndexGetElement(pc)) {
8227 0 : trackOptimizationOutcome(TrackedOutcome::ArraySeenNegativeIndex);
8228 0 : return Ok();
8229 : }
8230 :
8231 0 : MOZ_TRY(jsop_getelem_dense(obj, index));
8232 :
8233 0 : trackOptimizationSuccess();
8234 68 : *emitted = true;
8235 68 : return Ok();
8236 : }
8237 :
8238 : AbortReasonOr<Ok>
8239 0 : IonBuilder::getElemTryTypedArray(bool* emitted, MDefinition* obj, MDefinition* index)
8240 : {
8241 210 : MOZ_ASSERT(*emitted == false);
8242 :
8243 : Scalar::Type arrayType;
8244 0 : if (!ElementAccessIsTypedArray(constraints(), obj, index, &arrayType)) {
8245 210 : trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray);
8246 210 : return Ok();
8247 : }
8248 :
8249 : // Emit typed getelem variant.
8250 0 : MOZ_TRY(jsop_getelem_typed(obj, index, arrayType));
8251 :
8252 0 : trackOptimizationSuccess();
8253 0 : *emitted = true;
8254 0 : return Ok();
8255 : }
8256 :
8257 : AbortReasonOr<Ok>
8258 0 : IonBuilder::getElemTryCallSiteObject(bool* emitted, MDefinition* obj, MDefinition* index)
8259 : {
8260 0 : MOZ_ASSERT(*emitted == false);
8261 :
8262 0 : if (!obj->isConstant() || obj->type() != MIRType::Object) {
8263 275 : trackOptimizationOutcome(TrackedOutcome::NotObject);
8264 275 : return Ok();
8265 : }
8266 :
8267 0 : if (!index->isConstant() || index->type() != MIRType::Int32) {
8268 3 : trackOptimizationOutcome(TrackedOutcome::IndexType);
8269 3 : return Ok();
8270 : }
8271 :
8272 0 : JSObject* cst = &obj->toConstant()->toObject();
8273 0 : if (!cst->is<ArrayObject>()) {
8274 0 : trackOptimizationOutcome(TrackedOutcome::GenericFailure);
8275 0 : return Ok();
8276 : }
8277 :
8278 : // Technically this code would work with any kind of frozen array,
8279 : // in pratice it is usually a CallSiteObject.
8280 :
8281 0 : ArrayObject* array = &cst->as<ArrayObject>();
8282 0 : if (array->lengthIsWritable() || array->hasEmptyElements() || !array->denseElementsAreFrozen()) {
8283 0 : trackOptimizationOutcome(TrackedOutcome::GenericFailure);
8284 0 : return Ok();
8285 : }
8286 :
8287 0 : int32_t idx = index->toConstant()->toInt32();
8288 0 : if (idx < 0 || !array->containsDenseElement(uint32_t(idx))) {
8289 0 : trackOptimizationOutcome(TrackedOutcome::OutOfBounds);
8290 0 : return Ok();
8291 : }
8292 :
8293 0 : const Value& v = array->getDenseElement(uint32_t(idx));
8294 : // Strings should have been atomized by the parser.
8295 0 : if (!v.isString() || !v.toString()->isAtom())
8296 0 : return Ok();
8297 :
8298 0 : obj->setImplicitlyUsedUnchecked();
8299 0 : index->setImplicitlyUsedUnchecked();
8300 :
8301 0 : pushConstant(v);
8302 :
8303 0 : trackOptimizationSuccess();
8304 0 : *emitted = true;
8305 0 : return Ok();
8306 : }
8307 :
8308 : AbortReasonOr<Ok>
8309 0 : IonBuilder::getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index)
8310 : {
8311 0 : MOZ_ASSERT(*emitted == false);
8312 :
8313 0 : if (obj->type() != MIRType::String || !IsNumberType(index->type())) {
8314 210 : trackOptimizationOutcome(TrackedOutcome::AccessNotString);
8315 210 : return Ok();
8316 : }
8317 :
8318 : // If the index is expected to be out-of-bounds, don't optimize to avoid
8319 : // frequent bailouts.
8320 0 : if (bytecodeTypes(pc)->hasType(TypeSet::UndefinedType())) {
8321 0 : trackOptimizationOutcome(TrackedOutcome::OutOfBounds);
8322 0 : return Ok();
8323 : }
8324 :
8325 : // Emit fast path for string[index].
8326 0 : MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
8327 0 : current->add(idInt32);
8328 0 : index = idInt32;
8329 :
8330 0 : MStringLength* length = MStringLength::New(alloc(), obj);
8331 0 : current->add(length);
8332 :
8333 0 : index = addBoundsCheck(index, length);
8334 :
8335 0 : MCharCodeAt* charCode = MCharCodeAt::New(alloc(), obj, index);
8336 0 : current->add(charCode);
8337 :
8338 0 : MFromCharCode* result = MFromCharCode::New(alloc(), charCode);
8339 0 : current->add(result);
8340 0 : current->push(result);
8341 :
8342 0 : trackOptimizationSuccess();
8343 0 : *emitted = true;
8344 0 : return Ok();
8345 : }
8346 :
8347 : AbortReasonOr<Ok>
8348 0 : IonBuilder::getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* index)
8349 : {
8350 0 : MOZ_ASSERT(*emitted == false);
8351 :
8352 3 : if (inliningDepth_ > 0)
8353 0 : return Ok();
8354 :
8355 2 : if (obj->type() != MIRType::MagicOptimizedArguments)
8356 0 : return Ok();
8357 :
8358 : // Emit GetFrameArgument.
8359 :
8360 4 : MOZ_ASSERT(!info().argsObjAliasesFormals());
8361 :
8362 : // Type Inference has guaranteed this is an optimized arguments object.
8363 2 : obj->setImplicitlyUsedUnchecked();
8364 :
8365 : // To ensure that we are not looking above the number of actual arguments.
8366 2 : MArgumentsLength* length = MArgumentsLength::New(alloc());
8367 2 : current->add(length);
8368 :
8369 : // Ensure index is an integer.
8370 0 : MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
8371 2 : current->add(idInt32);
8372 2 : index = idInt32;
8373 :
8374 : // Bailouts if we read more than the number of actual arguments.
8375 2 : index = addBoundsCheck(index, length);
8376 :
8377 : // Load the argument from the actual arguments.
8378 0 : bool modifiesArgs = script()->baselineScript()->modifiesArguments();
8379 0 : MGetFrameArgument* load = MGetFrameArgument::New(alloc(), index, modifiesArgs);
8380 2 : current->add(load);
8381 0 : current->push(load);
8382 :
8383 4 : TemporaryTypeSet* types = bytecodeTypes(pc);
8384 0 : MOZ_TRY(pushTypeBarrier(load, types, BarrierKind::TypeSet));
8385 :
8386 0 : trackOptimizationSuccess();
8387 2 : *emitted = true;
8388 2 : return Ok();
8389 : }
8390 :
8391 : AbortReasonOr<Ok>
8392 0 : IonBuilder::getElemTryArgumentsInlinedConstant(bool* emitted, MDefinition* obj, MDefinition* index)
8393 : {
8394 0 : MOZ_ASSERT(*emitted == false);
8395 :
8396 1 : if (inliningDepth_ == 0)
8397 0 : return Ok();
8398 :
8399 1 : if (obj->type() != MIRType::MagicOptimizedArguments)
8400 0 : return Ok();
8401 :
8402 0 : MConstant* indexConst = index->maybeConstantValue();
8403 1 : if (!indexConst || indexConst->type() != MIRType::Int32)
8404 0 : return Ok();
8405 :
8406 : // Emit inlined arguments.
8407 0 : obj->setImplicitlyUsedUnchecked();
8408 :
8409 2 : MOZ_ASSERT(!info().argsObjAliasesFormals());
8410 :
8411 : // When the id is constant, we can just return the corresponding inlined argument
8412 0 : MOZ_ASSERT(inliningDepth_ > 0);
8413 :
8414 1 : int32_t id = indexConst->toInt32();
8415 0 : index->setImplicitlyUsedUnchecked();
8416 :
8417 2 : if (id < (int32_t)inlineCallInfo_->argc() && id >= 0)
8418 0 : current->push(inlineCallInfo_->getArg(id));
8419 : else
8420 0 : pushConstant(UndefinedValue());
8421 :
8422 0 : trackOptimizationSuccess();
8423 1 : *emitted = true;
8424 1 : return Ok();
8425 : }
8426 :
8427 : AbortReasonOr<Ok>
8428 0 : IonBuilder::getElemTryArgumentsInlinedIndex(bool* emitted, MDefinition* obj, MDefinition* index)
8429 : {
8430 0 : MOZ_ASSERT(*emitted == false);
8431 :
8432 0 : if (inliningDepth_ == 0)
8433 0 : return Ok();
8434 :
8435 0 : if (obj->type() != MIRType::MagicOptimizedArguments)
8436 0 : return Ok();
8437 :
8438 0 : if (!IsNumberType(index->type()))
8439 0 : return Ok();
8440 :
8441 : // Currently, we do not support any arguments vector larger than 10, as this
8442 : // is being translated into code at the call site, and it would be better to
8443 : // store the arguments contiguously on the stack.
8444 0 : if (inlineCallInfo_->argc() > 10) {
8445 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineBound);
8446 0 : return abort(AbortReason::Disable, "NYI get argument element with too many arguments");
8447 : }
8448 :
8449 : // Emit inlined arguments.
8450 0 : obj->setImplicitlyUsedUnchecked();
8451 :
8452 0 : MOZ_ASSERT(!info().argsObjAliasesFormals());
8453 :
8454 : // Ensure index is an integer.
8455 0 : MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
8456 0 : current->add(idInt32);
8457 0 : index = idInt32;
8458 :
8459 : // Bailout if we read more than the number of actual arguments. This bailout
8460 : // cannot re-enter because reading out of bounds arguments will disable the
8461 : // lazy arguments optimization for this script, when this code would be
8462 : // executed in Baseline. (see GetElemOptimizedArguments)
8463 0 : index = addBoundsCheck(index, constantInt(inlineCallInfo_->argc()));
8464 :
8465 : // Get an instruction to represent the state of the argument vector.
8466 0 : MInstruction* args = MArgumentState::New(alloc().fallible(), inlineCallInfo_->argv());
8467 0 : if (!args)
8468 0 : return abort(AbortReason::Alloc);
8469 0 : current->add(args);
8470 :
8471 : // Select a value to pick from a vector.
8472 0 : MInstruction* load = MLoadElementFromState::New(alloc(), args, index);
8473 0 : current->add(load);
8474 0 : current->push(load);
8475 :
8476 0 : trackOptimizationSuccess();
8477 0 : *emitted = true;
8478 0 : return Ok();
8479 : }
8480 :
8481 : AbortReasonOr<Ok>
8482 210 : IonBuilder::getElemAddCache(MDefinition* obj, MDefinition* index)
8483 : {
8484 : // Emit GetPropertyCache.
8485 :
8486 420 : TemporaryTypeSet* types = bytecodeTypes(pc);
8487 :
8488 : BarrierKind barrier;
8489 210 : if (obj->type() == MIRType::Object) {
8490 : // Always add a barrier if the index is not an int32 value, so we can
8491 : // attach stubs for particular properties.
8492 0 : if (index->type() == MIRType::Int32) {
8493 135 : barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(), obj,
8494 135 : nullptr, types);
8495 : } else {
8496 : barrier = BarrierKind::TypeSet;
8497 : }
8498 : } else {
8499 : // PropertyReadNeedsTypeBarrier only accounts for object types, so for
8500 : // now always insert a barrier if the input is not known to be an
8501 : // object.
8502 : barrier = BarrierKind::TypeSet;
8503 : }
8504 :
8505 : // Ensure we insert a type barrier for reads from typed objects, as type
8506 : // information does not account for the initial undefined/null types.
8507 0 : if (barrier != BarrierKind::TypeSet && !types->unknown()) {
8508 0 : MOZ_ASSERT(obj->resultTypeSet());
8509 0 : switch (obj->resultTypeSet()->forAllClasses(constraints(), IsTypedObjectClass)) {
8510 : case TemporaryTypeSet::ForAllResult::ALL_FALSE:
8511 : case TemporaryTypeSet::ForAllResult::EMPTY:
8512 : break;
8513 : case TemporaryTypeSet::ForAllResult::ALL_TRUE:
8514 : case TemporaryTypeSet::ForAllResult::MIXED:
8515 0 : barrier = BarrierKind::TypeSet;
8516 0 : break;
8517 : }
8518 : }
8519 :
8520 0 : MGetPropertyCache* ins = MGetPropertyCache::New(alloc(), obj, index,
8521 0 : barrier == BarrierKind::TypeSet);
8522 210 : current->add(ins);
8523 0 : current->push(ins);
8524 :
8525 630 : MOZ_TRY(resumeAfter(ins));
8526 :
8527 : // Spice up type information.
8528 0 : if (index->type() == MIRType::Int32 && barrier == BarrierKind::NoBarrier) {
8529 0 : bool needHoleCheck = !ElementAccessIsPacked(constraints(), obj);
8530 0 : MIRType knownType = GetElemKnownType(needHoleCheck, types);
8531 :
8532 0 : if (knownType != MIRType::Value && knownType != MIRType::Double)
8533 0 : ins->setResultType(knownType);
8534 : }
8535 :
8536 0 : MOZ_TRY(pushTypeBarrier(ins, types, barrier));
8537 :
8538 210 : trackOptimizationSuccess();
8539 210 : return Ok();
8540 : }
8541 :
8542 : TemporaryTypeSet*
8543 0 : IonBuilder::computeHeapType(const TemporaryTypeSet* objTypes, const jsid id)
8544 : {
8545 136 : if (objTypes->unknownObject() || objTypes->getObjectCount() == 0)
8546 : return nullptr;
8547 :
8548 68 : TemporaryTypeSet* acc = nullptr;
8549 0 : LifoAlloc* lifoAlloc = alloc().lifoAlloc();
8550 :
8551 136 : Vector<HeapTypeSetKey, 4, SystemAllocPolicy> properties;
8552 68 : if (!properties.reserve(objTypes->getObjectCount()))
8553 : return nullptr;
8554 :
8555 76 : for (unsigned i = 0; i < objTypes->getObjectCount(); i++) {
8556 0 : TypeSet::ObjectKey* key = objTypes->getObject(i);
8557 :
8558 68 : if (key->unknownProperties())
8559 0 : return nullptr;
8560 :
8561 68 : HeapTypeSetKey property = key->property(id);
8562 0 : HeapTypeSet* currentSet = property.maybeTypes();
8563 :
8564 136 : if (!currentSet || currentSet->unknown())
8565 : return nullptr;
8566 :
8567 0 : properties.infallibleAppend(property);
8568 :
8569 4 : if (acc) {
8570 0 : acc = TypeSet::unionSets(acc, currentSet, lifoAlloc);
8571 : } else {
8572 4 : TemporaryTypeSet empty;
8573 4 : acc = TypeSet::unionSets(&empty, currentSet, lifoAlloc);
8574 : }
8575 :
8576 4 : if (!acc)
8577 : return nullptr;
8578 : }
8579 :
8580 : // Freeze all the properties associated with the refined type set.
8581 16 : for (HeapTypeSetKey* i = properties.begin(); i != properties.end(); i++)
8582 4 : i->freeze(constraints());
8583 :
8584 : return acc;
8585 : }
8586 :
8587 : AbortReasonOr<Ok>
8588 0 : IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index)
8589 : {
8590 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
8591 :
8592 0 : MOZ_ASSERT(index->type() == MIRType::Int32 || index->type() == MIRType::Double);
8593 :
8594 0 : BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
8595 68 : obj, nullptr, types);
8596 68 : bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
8597 :
8598 : // Reads which are on holes in the object do not have to bail out if
8599 : // undefined values have been observed at this access site and the access
8600 : // cannot hit another indexed property on the object or its prototypes.
8601 68 : bool readOutOfBounds = false;
8602 0 : if (types->hasType(TypeSet::UndefinedType())) {
8603 : bool hasExtraIndexedProperty;
8604 0 : MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
8605 0 : readOutOfBounds = !hasExtraIndexedProperty;
8606 : }
8607 :
8608 68 : MIRType knownType = MIRType::Value;
8609 68 : if (barrier == BarrierKind::NoBarrier)
8610 : knownType = GetElemKnownType(needsHoleCheck, types);
8611 :
8612 : // Ensure index is an integer.
8613 0 : MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
8614 68 : current->add(idInt32);
8615 68 : index = idInt32;
8616 :
8617 : // Get the elements vector.
8618 68 : MInstruction* elements = MElements::New(alloc(), obj);
8619 68 : current->add(elements);
8620 :
8621 : // Note: to help GVN, use the original MElements instruction and not
8622 : // MConvertElementsToDoubles as operand. This is fine because converting
8623 : // elements to double does not change the initialized length.
8624 68 : MInstruction* initLength = initializedLength(elements);
8625 :
8626 : // If we can load the element as a definite double, make sure to check that
8627 : // the array has been converted to homogenous doubles first.
8628 68 : TemporaryTypeSet* objTypes = obj->resultTypeSet();
8629 0 : bool inBounds = !readOutOfBounds && !needsHoleCheck;
8630 :
8631 0 : if (inBounds) {
8632 0 : TemporaryTypeSet* heapTypes = computeHeapType(objTypes, JSID_VOID);
8633 0 : if (heapTypes && heapTypes->isSubset(types)) {
8634 0 : knownType = heapTypes->getKnownMIRType();
8635 0 : types = heapTypes;
8636 : }
8637 : }
8638 :
8639 : bool loadDouble =
8640 0 : barrier == BarrierKind::NoBarrier &&
8641 0 : loopDepth_ &&
8642 0 : inBounds &&
8643 0 : knownType == MIRType::Double &&
8644 0 : objTypes &&
8645 0 : objTypes->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles;
8646 68 : if (loadDouble)
8647 0 : elements = addConvertElementsToDoubles(elements);
8648 :
8649 : MInstruction* load;
8650 :
8651 68 : if (!readOutOfBounds) {
8652 : // This load should not return undefined, so likely we're reading
8653 : // in-bounds elements, and the array is packed or its holes are not
8654 : // read. This is the best case: we can separate the bounds check for
8655 : // hoisting.
8656 0 : index = addBoundsCheck(index, initLength);
8657 :
8658 68 : load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble);
8659 68 : current->add(load);
8660 : } else {
8661 : // This load may return undefined, so assume that we *can* read holes,
8662 : // or that we can read out-of-bounds accesses. In this case, the bounds
8663 : // check is part of the opcode.
8664 0 : load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck);
8665 0 : current->add(load);
8666 :
8667 : // If maybeUndefined was true, the typeset must have undefined, and
8668 : // then either additional types or a barrier. This means we should
8669 : // never have a typed version of LoadElementHole.
8670 0 : MOZ_ASSERT(knownType == MIRType::Value);
8671 : }
8672 :
8673 0 : if (knownType != MIRType::Value) {
8674 0 : load->setResultType(knownType);
8675 0 : load->setResultTypeSet(types);
8676 : }
8677 :
8678 68 : current->push(load);
8679 68 : return pushTypeBarrier(load, types, barrier);
8680 : }
8681 :
8682 : MInstruction*
8683 0 : IonBuilder::addArrayBufferByteLength(MDefinition* obj)
8684 : {
8685 0 : MLoadFixedSlot* ins = MLoadFixedSlot::New(alloc(), obj, size_t(ArrayBufferObject::BYTE_LENGTH_SLOT));
8686 0 : current->add(ins);
8687 0 : ins->setResultType(MIRType::Int32);
8688 0 : return ins;
8689 : }
8690 :
8691 : void
8692 0 : IonBuilder::addTypedArrayLengthAndData(MDefinition* obj,
8693 : BoundsChecking checking,
8694 : MDefinition** index,
8695 : MInstruction** length, MInstruction** elements)
8696 : {
8697 0 : MOZ_ASSERT((index != nullptr) == (elements != nullptr));
8698 :
8699 0 : JSObject* tarr = nullptr;
8700 :
8701 0 : if (MConstant* objConst = obj->maybeConstantValue()) {
8702 0 : if (objConst->type() == MIRType::Object)
8703 0 : tarr = &objConst->toObject();
8704 0 : } else if (TemporaryTypeSet* types = obj->resultTypeSet()) {
8705 0 : tarr = types->maybeSingleton();
8706 : }
8707 :
8708 0 : if (tarr) {
8709 0 : SharedMem<void*> data = tarr->as<TypedArrayObject>().viewDataEither();
8710 : // Bug 979449 - Optimistically embed the elements and use TI to
8711 : // invalidate if we move them.
8712 0 : bool isTenured = !tarr->runtimeFromMainThread()->gc.nursery().isInside(data);
8713 0 : if (isTenured && tarr->isSingleton()) {
8714 : // The 'data' pointer of TypedArrayObject can change in rare circumstances
8715 : // (ArrayBufferObject::changeContents).
8716 0 : TypeSet::ObjectKey* tarrKey = TypeSet::ObjectKey::get(tarr);
8717 0 : if (!tarrKey->unknownProperties()) {
8718 0 : if (tarr->is<TypedArrayObject>())
8719 0 : tarrKey->watchStateChangeForTypedArrayData(constraints());
8720 :
8721 0 : obj->setImplicitlyUsedUnchecked();
8722 :
8723 0 : int32_t len = AssertedCast<int32_t>(tarr->as<TypedArrayObject>().length());
8724 0 : *length = MConstant::New(alloc(), Int32Value(len));
8725 0 : current->add(*length);
8726 :
8727 0 : if (index) {
8728 0 : if (checking == DoBoundsCheck)
8729 0 : *index = addBoundsCheck(*index, *length);
8730 :
8731 0 : *elements = MConstantElements::New(alloc(), data);
8732 0 : current->add(*elements);
8733 : }
8734 0 : return;
8735 : }
8736 : }
8737 : }
8738 :
8739 0 : *length = MTypedArrayLength::New(alloc(), obj);
8740 0 : current->add(*length);
8741 :
8742 0 : if (index) {
8743 0 : if (checking == DoBoundsCheck)
8744 0 : *index = addBoundsCheck(*index, *length);
8745 :
8746 0 : *elements = MTypedArrayElements::New(alloc(), obj);
8747 0 : current->add(*elements);
8748 : }
8749 : }
8750 :
8751 : AbortReasonOr<Ok>
8752 0 : IonBuilder::jsop_getelem_typed(MDefinition* obj, MDefinition* index,
8753 : Scalar::Type arrayType)
8754 : {
8755 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
8756 :
8757 0 : bool maybeUndefined = types->hasType(TypeSet::UndefinedType());
8758 :
8759 : // Reading from an Uint32Array will result in a double for values
8760 : // that don't fit in an int32. We have to bailout if this happens
8761 : // and the instruction is not known to return a double.
8762 0 : bool allowDouble = types->hasType(TypeSet::DoubleType());
8763 :
8764 : // Ensure id is an integer.
8765 0 : MInstruction* idInt32 = MToNumberInt32::New(alloc(), index);
8766 0 : current->add(idInt32);
8767 0 : index = idInt32;
8768 :
8769 0 : if (!maybeUndefined) {
8770 : // Assume the index is in range, so that we can hoist the length,
8771 : // elements vector and bounds check.
8772 :
8773 : // If we are reading in-bounds elements, we can use knowledge about
8774 : // the array type to determine the result type, even if the opcode has
8775 : // never executed. The known pushed type is only used to distinguish
8776 : // uint32 reads that may produce either doubles or integers.
8777 0 : MIRType knownType = MIRTypeForTypedArrayRead(arrayType, allowDouble);
8778 :
8779 : // Get length, bounds-check, then get elements, and add all instructions.
8780 : MInstruction* length;
8781 : MInstruction* elements;
8782 0 : addTypedArrayLengthAndData(obj, DoBoundsCheck, &index, &length, &elements);
8783 :
8784 : // Load the element.
8785 0 : MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
8786 0 : current->add(load);
8787 0 : current->push(load);
8788 :
8789 : // Note: we can ignore the type barrier here, we know the type must
8790 : // be valid and unbarriered.
8791 0 : load->setResultType(knownType);
8792 0 : return Ok();
8793 : } else {
8794 : // We need a type barrier if the array's element type has never been
8795 : // observed (we've only read out-of-bounds values). Note that for
8796 : // Uint32Array, we only check for int32: if allowDouble is false we
8797 : // will bailout when we read a double.
8798 0 : BarrierKind barrier = BarrierKind::TypeSet;
8799 : switch (arrayType) {
8800 : case Scalar::Int8:
8801 : case Scalar::Uint8:
8802 : case Scalar::Uint8Clamped:
8803 : case Scalar::Int16:
8804 : case Scalar::Uint16:
8805 : case Scalar::Int32:
8806 : case Scalar::Uint32:
8807 0 : if (types->hasType(TypeSet::Int32Type()))
8808 0 : barrier = BarrierKind::NoBarrier;
8809 : break;
8810 : case Scalar::Float32:
8811 : case Scalar::Float64:
8812 0 : if (allowDouble)
8813 0 : barrier = BarrierKind::NoBarrier;
8814 : break;
8815 : default:
8816 0 : MOZ_CRASH("Unknown typed array type");
8817 : }
8818 :
8819 : // Assume we will read out-of-bound values. In this case the
8820 : // bounds check will be part of the instruction, and the instruction
8821 : // will always return a Value.
8822 : MLoadTypedArrayElementHole* load =
8823 0 : MLoadTypedArrayElementHole::New(alloc(), obj, index, arrayType, allowDouble);
8824 0 : current->add(load);
8825 0 : current->push(load);
8826 :
8827 0 : return pushTypeBarrier(load, types, barrier);
8828 : }
8829 : }
8830 :
8831 : AbortReasonOr<Ok>
8832 0 : IonBuilder::jsop_setelem()
8833 : {
8834 73 : bool emitted = false;
8835 0 : startTrackingOptimizations();
8836 :
8837 0 : MDefinition* value = current->pop();
8838 73 : MDefinition* index = current->pop();
8839 0 : MDefinition* object = convertUnboxedObjects(current->pop());
8840 :
8841 0 : trackTypeInfo(TrackedTypeSite::Receiver, object->type(), object->resultTypeSet());
8842 146 : trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet());
8843 0 : trackTypeInfo(TrackedTypeSite::Value, value->type(), value->resultTypeSet());
8844 :
8845 0 : if (shouldAbortOnPreliminaryGroups(object)) {
8846 0 : MInstruction* ins = MCallSetElement::New(alloc(), object, index, value, IsStrictSetPC(pc));
8847 0 : current->add(ins);
8848 0 : current->push(value);
8849 : return resumeAfter(ins);
8850 : }
8851 :
8852 0 : if (!forceInlineCaches()) {
8853 0 : trackOptimizationAttempt(TrackedStrategy::SetElem_TypedArray);
8854 0 : MOZ_TRY(setElemTryTypedArray(&emitted, object, index, value));
8855 73 : if (emitted)
8856 0 : return Ok();
8857 :
8858 0 : trackOptimizationAttempt(TrackedStrategy::SetElem_Dense);
8859 0 : SetElemICInspector icInspect(inspector->setElemICInspector(pc));
8860 0 : bool writeHole = icInspect.sawOOBDenseWrite();
8861 0 : MOZ_TRY(initOrSetElemTryDense(&emitted, object, index, value, writeHole));
8862 73 : if (emitted)
8863 0 : return Ok();
8864 :
8865 0 : trackOptimizationAttempt(TrackedStrategy::SetElem_Arguments);
8866 1 : MOZ_TRY(setElemTryArguments(&emitted, object));
8867 45 : if (emitted)
8868 0 : return Ok();
8869 :
8870 0 : trackOptimizationAttempt(TrackedStrategy::SetElem_TypedObject);
8871 0 : MOZ_TRY(setElemTryTypedObject(&emitted, object, index, value));
8872 45 : if (emitted)
8873 0 : return Ok();
8874 : }
8875 :
8876 0 : if (script()->argumentsHasVarBinding() &&
8877 45 : object->mightBeType(MIRType::MagicOptimizedArguments) &&
8878 0 : info().analysisMode() != Analysis_ArgumentsUsage)
8879 : {
8880 0 : return abort(AbortReason::Disable, "Type is not definitely lazy arguments.");
8881 : }
8882 :
8883 0 : trackOptimizationAttempt(TrackedStrategy::SetElem_InlineCache);
8884 0 : MOZ_TRY(initOrSetElemTryCache(&emitted, object, index, value));
8885 45 : if (emitted)
8886 42 : return Ok();
8887 :
8888 : // Emit call.
8889 0 : MInstruction* ins = MCallSetElement::New(alloc(), object, index, value, IsStrictSetPC(pc));
8890 3 : current->add(ins);
8891 3 : current->push(value);
8892 :
8893 : return resumeAfter(ins);
8894 : }
8895 :
8896 : AbortReasonOr<Ok>
8897 45 : IonBuilder::setElemTryTypedObject(bool* emitted, MDefinition* obj,
8898 : MDefinition* index, MDefinition* value)
8899 : {
8900 45 : MOZ_ASSERT(*emitted == false);
8901 :
8902 : // The next several failures are all due to types not predicting that we
8903 : // are definitely doing a getelem access on a typed object.
8904 0 : trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject);
8905 :
8906 0 : TypedObjectPrediction objPrediction = typedObjectPrediction(obj);
8907 45 : if (objPrediction.isUseless())
8908 0 : return Ok();
8909 :
8910 0 : if (!objPrediction.ofArrayKind())
8911 0 : return Ok();
8912 :
8913 0 : TypedObjectPrediction elemPrediction = objPrediction.arrayElementType();
8914 0 : if (elemPrediction.isUseless())
8915 0 : return Ok();
8916 :
8917 : uint32_t elemSize;
8918 0 : if (!elemPrediction.hasKnownSize(&elemSize))
8919 0 : return Ok();
8920 :
8921 0 : switch (elemPrediction.kind()) {
8922 : case type::Simd:
8923 : // FIXME (bug 894105): store a MIRType::float32x4 etc
8924 0 : trackOptimizationOutcome(TrackedOutcome::GenericFailure);
8925 0 : return Ok();
8926 :
8927 : case type::Reference:
8928 : return setElemTryReferenceElemOfTypedObject(emitted, obj, index,
8929 0 : objPrediction, value, elemPrediction);
8930 :
8931 : case type::Scalar:
8932 : return setElemTryScalarElemOfTypedObject(emitted,
8933 : obj,
8934 : index,
8935 : objPrediction,
8936 : value,
8937 : elemPrediction,
8938 0 : elemSize);
8939 :
8940 : case type::Struct:
8941 : case type::Array:
8942 : // Not yet optimized.
8943 0 : trackOptimizationOutcome(TrackedOutcome::GenericFailure);
8944 0 : return Ok();
8945 : }
8946 :
8947 0 : MOZ_CRASH("Bad kind");
8948 : }
8949 :
8950 : AbortReasonOr<Ok>
8951 0 : IonBuilder::setElemTryReferenceElemOfTypedObject(bool* emitted,
8952 : MDefinition* obj,
8953 : MDefinition* index,
8954 : TypedObjectPrediction objPrediction,
8955 : MDefinition* value,
8956 : TypedObjectPrediction elemPrediction)
8957 : {
8958 0 : ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
8959 0 : uint32_t elemSize = ReferenceTypeDescr::size(elemType);
8960 :
8961 0 : LinearSum indexAsByteOffset(alloc());
8962 0 : if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
8963 0 : return Ok();
8964 :
8965 : return setPropTryReferenceTypedObjectValue(emitted, obj, indexAsByteOffset,
8966 0 : elemType, value, nullptr);
8967 : }
8968 :
8969 : AbortReasonOr<Ok>
8970 0 : IonBuilder::setElemTryScalarElemOfTypedObject(bool* emitted,
8971 : MDefinition* obj,
8972 : MDefinition* index,
8973 : TypedObjectPrediction objPrediction,
8974 : MDefinition* value,
8975 : TypedObjectPrediction elemPrediction,
8976 : uint32_t elemSize)
8977 : {
8978 : // Must always be loading the same scalar type
8979 0 : ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
8980 0 : MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
8981 :
8982 0 : LinearSum indexAsByteOffset(alloc());
8983 0 : if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset))
8984 0 : return Ok();
8985 :
8986 0 : return setPropTryScalarTypedObjectValue(emitted, obj, indexAsByteOffset, elemType, value);
8987 : }
8988 :
8989 : AbortReasonOr<Ok>
8990 73 : IonBuilder::setElemTryTypedArray(bool* emitted, MDefinition* object,
8991 : MDefinition* index, MDefinition* value)
8992 : {
8993 73 : MOZ_ASSERT(*emitted == false);
8994 :
8995 : Scalar::Type arrayType;
8996 0 : if (!ElementAccessIsTypedArray(constraints(), object, index, &arrayType)) {
8997 73 : trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray);
8998 73 : return Ok();
8999 : }
9000 :
9001 : // Emit typed setelem variant.
9002 0 : MOZ_TRY(jsop_setelem_typed(arrayType, object, index, value));
9003 :
9004 0 : trackOptimizationSuccess();
9005 0 : *emitted = true;
9006 0 : return Ok();
9007 : }
9008 :
9009 : AbortReasonOr<Ok>
9010 101 : IonBuilder::initOrSetElemTryDense(bool* emitted, MDefinition* object,
9011 : MDefinition* index, MDefinition* value, bool writeHole)
9012 : {
9013 0 : MOZ_ASSERT(*emitted == false);
9014 :
9015 0 : if (value->type() == MIRType::MagicHole)
9016 : {
9017 0 : trackOptimizationOutcome(TrackedOutcome::InitHole);
9018 0 : return Ok();
9019 : }
9020 :
9021 0 : if (!ElementAccessIsDenseNative(constraints(), object, index)) {
9022 58 : trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
9023 58 : return Ok();
9024 : }
9025 :
9026 43 : if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
9027 : &object, nullptr, &value, /* canModify = */ true))
9028 : {
9029 0 : trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
9030 0 : return Ok();
9031 : }
9032 :
9033 0 : if (!object->resultTypeSet()) {
9034 2 : trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
9035 2 : return Ok();
9036 : }
9037 :
9038 : TemporaryTypeSet::DoubleConversion conversion =
9039 41 : object->resultTypeSet()->convertDoubleElements(constraints());
9040 :
9041 : // If AmbiguousDoubleConversion, only handle int32 values for now.
9042 41 : if (conversion == TemporaryTypeSet::AmbiguousDoubleConversion &&
9043 0 : value->type() != MIRType::Int32)
9044 : {
9045 0 : trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion);
9046 0 : return Ok();
9047 : }
9048 :
9049 : // Don't generate a fast path if there have been bounds check failures
9050 : // and this access might be on a sparse property.
9051 : bool hasExtraIndexedProperty;
9052 0 : MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, object));
9053 0 : if (hasExtraIndexedProperty && failedBoundsCheck_) {
9054 0 : trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
9055 0 : return Ok();
9056 : }
9057 :
9058 : // Emit dense setelem variant.
9059 0 : MOZ_TRY(initOrSetElemDense(conversion, object, index, value, writeHole, emitted));
9060 :
9061 0 : if (!*emitted) {
9062 0 : trackOptimizationOutcome(TrackedOutcome::NonWritableProperty);
9063 0 : return Ok();
9064 : }
9065 :
9066 41 : trackOptimizationSuccess();
9067 41 : return Ok();
9068 : }
9069 :
9070 : AbortReasonOr<Ok>
9071 0 : IonBuilder::setElemTryArguments(bool* emitted, MDefinition* object)
9072 : {
9073 0 : MOZ_ASSERT(*emitted == false);
9074 :
9075 45 : if (object->type() != MIRType::MagicOptimizedArguments)
9076 45 : return Ok();
9077 :
9078 : // Arguments are not supported yet.
9079 0 : return abort(AbortReason::Disable, "NYI arguments[]=");
9080 : }
9081 :
9082 : AbortReasonOr<Ok>
9083 60 : IonBuilder::initOrSetElemTryCache(bool* emitted, MDefinition* object,
9084 : MDefinition* index, MDefinition* value)
9085 : {
9086 0 : MOZ_ASSERT(*emitted == false);
9087 :
9088 0 : if (!object->mightBeType(MIRType::Object)) {
9089 12 : trackOptimizationOutcome(TrackedOutcome::NotObject);
9090 12 : return Ok();
9091 : }
9092 :
9093 0 : if (value->type() == MIRType::MagicHole)
9094 : {
9095 0 : trackOptimizationOutcome(TrackedOutcome::InitHole);
9096 0 : return Ok();
9097 : }
9098 :
9099 0 : bool barrier = true;
9100 53 : if (index->type() == MIRType::Int32 &&
9101 5 : !PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
9102 : &object, nullptr, &value, /* canModify = */ true))
9103 : {
9104 2 : barrier = false;
9105 : }
9106 :
9107 : // We can avoid worrying about holes in the IC if we know a priori we are safe
9108 : // from them. If TI can guard that there are no indexed properties on the prototype
9109 : // chain, we know that we anen't missing any setters by overwriting the hole with
9110 : // another value.
9111 : bool guardHoles;
9112 96 : MOZ_TRY_VAR(guardHoles, ElementAccessHasExtraIndexedProperty(this, object));
9113 :
9114 : // Make sure the object being written to doesn't have copy on write elements.
9115 0 : const Class* clasp = object->resultTypeSet() ? object->resultTypeSet()->getKnownClass(constraints()) : nullptr;
9116 84 : bool checkNative = !clasp || !clasp->isNative();
9117 48 : object = addMaybeCopyElementsForWrite(object, checkNative);
9118 :
9119 : // Emit SetPropertyCache.
9120 0 : bool strict = JSOp(*pc) == JSOP_STRICTSETELEM;
9121 : MSetPropertyCache* ins =
9122 0 : MSetPropertyCache::New(alloc(), object, index, value, strict, needsPostBarrier(value),
9123 48 : barrier, guardHoles);
9124 48 : current->add(ins);
9125 :
9126 : // Push value back onto stack. Init ops keep their object on stack.
9127 96 : if (!IsPropertyInitOp(JSOp(*pc)))
9128 0 : current->push(value);
9129 :
9130 0 : MOZ_TRY(resumeAfter(ins));
9131 :
9132 0 : trackOptimizationSuccess();
9133 48 : *emitted = true;
9134 48 : return Ok();
9135 : }
9136 :
9137 : AbortReasonOr<Ok>
9138 41 : IonBuilder::initOrSetElemDense(TemporaryTypeSet::DoubleConversion conversion,
9139 : MDefinition* obj, MDefinition* id, MDefinition* value,
9140 : bool writeHole, bool* emitted)
9141 : {
9142 0 : MOZ_ASSERT(*emitted == false);
9143 :
9144 41 : MIRType elementType = DenseNativeElementType(constraints(), obj);
9145 41 : bool packed = ElementAccessIsPacked(constraints(), obj);
9146 :
9147 : // Writes which are on holes in the object do not have to bail out if they
9148 : // cannot hit another indexed property on the object or its prototypes.
9149 : bool hasExtraIndexedProperty;
9150 0 : MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
9151 :
9152 0 : bool mayBeNonExtensible = ElementAccessMightBeNonExtensible(constraints(), obj);
9153 :
9154 41 : if (mayBeNonExtensible && hasExtraIndexedProperty) {
9155 : // FallibleStoreElement does not know how to deal with extra indexed
9156 : // properties on the prototype. This case should be rare so we fall back
9157 : // to an IC.
9158 0 : return Ok();
9159 : }
9160 :
9161 41 : *emitted = true;
9162 :
9163 : // Ensure id is an integer.
9164 0 : MInstruction* idInt32 = MToNumberInt32::New(alloc(), id);
9165 41 : current->add(idInt32);
9166 0 : id = idInt32;
9167 :
9168 41 : if (needsPostBarrier(value))
9169 2 : current->add(MPostWriteElementBarrier::New(alloc(), obj, value, id));
9170 :
9171 : // Copy the elements vector if necessary.
9172 41 : obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
9173 :
9174 : // Get the elements vector.
9175 41 : MElements* elements = MElements::New(alloc(), obj);
9176 41 : current->add(elements);
9177 :
9178 : // Ensure the value is a double, if double conversion might be needed.
9179 41 : MDefinition* newValue = value;
9180 41 : switch (conversion) {
9181 : case TemporaryTypeSet::AlwaysConvertToDoubles:
9182 : case TemporaryTypeSet::MaybeConvertToDoubles: {
9183 0 : MInstruction* valueDouble = MToDouble::New(alloc(), value);
9184 0 : current->add(valueDouble);
9185 0 : newValue = valueDouble;
9186 0 : break;
9187 : }
9188 :
9189 : case TemporaryTypeSet::AmbiguousDoubleConversion: {
9190 0 : MOZ_ASSERT(value->type() == MIRType::Int32);
9191 0 : MInstruction* maybeDouble = MMaybeToDoubleElement::New(alloc(), elements, value);
9192 0 : current->add(maybeDouble);
9193 0 : newValue = maybeDouble;
9194 0 : break;
9195 : }
9196 :
9197 : case TemporaryTypeSet::DontConvertToDoubles:
9198 : break;
9199 :
9200 : default:
9201 0 : MOZ_CRASH("Unknown double conversion");
9202 : }
9203 :
9204 : // Use MStoreElementHole if this SETELEM has written to out-of-bounds
9205 : // indexes in the past. Otherwise, use MStoreElement so that we can hoist
9206 : // the initialized length and bounds check.
9207 : // If an object may have been made non-extensible, no previous expectations
9208 : // hold and we fallback to MFallibleStoreElement.
9209 : MInstruction* store;
9210 0 : MStoreElementCommon* common = nullptr;
9211 0 : if (writeHole && !hasExtraIndexedProperty && !mayBeNonExtensible) {
9212 0 : MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue);
9213 13 : store = ins;
9214 0 : common = ins;
9215 :
9216 0 : current->add(ins);
9217 28 : } else if (mayBeNonExtensible) {
9218 0 : MOZ_ASSERT(!hasExtraIndexedProperty,
9219 : "FallibleStoreElement codegen assumes no extra indexed properties");
9220 :
9221 0 : bool needsHoleCheck = !packed;
9222 0 : MFallibleStoreElement* ins = MFallibleStoreElement::New(alloc(), obj, elements, id,
9223 0 : newValue, needsHoleCheck);
9224 0 : store = ins;
9225 0 : common = ins;
9226 :
9227 0 : current->add(ins);
9228 : } else {
9229 0 : MInstruction* initLength = initializedLength(elements);
9230 :
9231 28 : id = addBoundsCheck(id, initLength);
9232 0 : bool needsHoleCheck = !packed && hasExtraIndexedProperty;
9233 :
9234 0 : MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
9235 28 : store = ins;
9236 0 : common = ins;
9237 :
9238 28 : current->add(store);
9239 : }
9240 :
9241 : // Push value back onto stack. Init ops keep their object on stack.
9242 82 : if (!IsPropertyInitOp(JSOp(*pc)))
9243 0 : current->push(value);
9244 :
9245 0 : MOZ_TRY(resumeAfter(store));
9246 :
9247 0 : if (common) {
9248 : // Determine whether a write barrier is required.
9249 41 : if (obj->resultTypeSet()->propertyNeedsBarrier(constraints(), JSID_VOID))
9250 : common->setNeedsBarrier();
9251 :
9252 41 : if (elementType != MIRType::None && packed)
9253 41 : common->setElementType(elementType);
9254 : }
9255 :
9256 41 : return Ok();
9257 : }
9258 :
9259 :
9260 : AbortReasonOr<Ok>
9261 0 : IonBuilder::jsop_setelem_typed(Scalar::Type arrayType,
9262 : MDefinition* obj, MDefinition* id, MDefinition* value)
9263 : {
9264 0 : SetElemICInspector icInspect(inspector->setElemICInspector(pc));
9265 0 : bool expectOOB = icInspect.sawOOBTypedArrayWrite();
9266 :
9267 0 : if (expectOOB)
9268 0 : spew("Emitting OOB TypedArray SetElem");
9269 :
9270 : // Ensure id is an integer.
9271 0 : MInstruction* idInt32 = MToNumberInt32::New(alloc(), id);
9272 0 : current->add(idInt32);
9273 0 : id = idInt32;
9274 :
9275 : // Get length, bounds-check, then get elements, and add all instructions.
9276 : MInstruction* length;
9277 : MInstruction* elements;
9278 0 : BoundsChecking checking = expectOOB ? SkipBoundsCheck : DoBoundsCheck;
9279 0 : addTypedArrayLengthAndData(obj, checking, &id, &length, &elements);
9280 :
9281 : // Clamp value to [0, 255] for Uint8ClampedArray.
9282 0 : MDefinition* toWrite = value;
9283 0 : if (arrayType == Scalar::Uint8Clamped) {
9284 0 : toWrite = MClampToUint8::New(alloc(), value);
9285 0 : current->add(toWrite->toInstruction());
9286 : }
9287 :
9288 : // Store the value.
9289 : MInstruction* ins;
9290 0 : if (expectOOB) {
9291 0 : ins = MStoreTypedArrayElementHole::New(alloc(), elements, length, id, toWrite, arrayType);
9292 : } else {
9293 : MStoreUnboxedScalar* store =
9294 0 : MStoreUnboxedScalar::New(alloc(), elements, id, toWrite, arrayType,
9295 0 : MStoreUnboxedScalar::TruncateInput);
9296 0 : ins = store;
9297 : }
9298 :
9299 0 : current->add(ins);
9300 0 : current->push(value);
9301 :
9302 0 : return resumeAfter(ins);
9303 : }
9304 :
9305 : AbortReasonOr<Ok>
9306 0 : IonBuilder::jsop_length()
9307 : {
9308 253 : if (jsop_length_fastPath())
9309 0 : return Ok();
9310 :
9311 480 : PropertyName* name = info().getAtom(pc)->asPropertyName();
9312 240 : return jsop_getprop(name);
9313 : }
9314 :
9315 : bool
9316 0 : IonBuilder::jsop_length_fastPath()
9317 : {
9318 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
9319 :
9320 253 : if (types->getKnownMIRType() != MIRType::Int32)
9321 : return false;
9322 :
9323 0 : MDefinition* obj = current->peek(-1);
9324 :
9325 119 : if (shouldAbortOnPreliminaryGroups(obj))
9326 : return false;
9327 :
9328 119 : if (obj->mightBeType(MIRType::String)) {
9329 0 : if (obj->mightBeType(MIRType::Object))
9330 : return false;
9331 0 : current->pop();
9332 0 : MStringLength* ins = MStringLength::New(alloc(), obj);
9333 0 : current->add(ins);
9334 1 : current->push(ins);
9335 1 : return true;
9336 : }
9337 :
9338 118 : if (obj->mightBeType(MIRType::Object)) {
9339 112 : TemporaryTypeSet* objTypes = obj->resultTypeSet();
9340 :
9341 : // Compute the length for array objects.
9342 0 : if (objTypes &&
9343 124 : objTypes->getKnownClass(constraints()) == &ArrayObject::class_ &&
9344 0 : !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW))
9345 : {
9346 0 : current->pop();
9347 12 : MElements* elements = MElements::New(alloc(), obj);
9348 12 : current->add(elements);
9349 :
9350 : // Read length.
9351 0 : MArrayLength* length = MArrayLength::New(alloc(), elements);
9352 12 : current->add(length);
9353 12 : current->push(length);
9354 : return true;
9355 : }
9356 :
9357 : // Compute the length for array typed objects.
9358 0 : TypedObjectPrediction prediction = typedObjectPrediction(obj);
9359 0 : if (!prediction.isUseless()) {
9360 0 : TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
9361 0 : if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
9362 : return false;
9363 :
9364 : MInstruction* length;
9365 : int32_t sizedLength;
9366 0 : if (prediction.hasKnownArrayLength(&sizedLength)) {
9367 0 : obj->setImplicitlyUsedUnchecked();
9368 0 : length = MConstant::New(alloc(), Int32Value(sizedLength));
9369 : } else {
9370 : return false;
9371 : }
9372 :
9373 0 : current->pop();
9374 0 : current->add(length);
9375 0 : current->push(length);
9376 0 : return true;
9377 : }
9378 : }
9379 :
9380 : return false;
9381 : }
9382 :
9383 : AbortReasonOr<Ok>
9384 0 : IonBuilder::jsop_arguments()
9385 : {
9386 0 : if (info().needsArgsObj()) {
9387 53 : current->push(current->argumentsObject());
9388 53 : return Ok();
9389 : }
9390 :
9391 0 : MOZ_ASSERT(hasLazyArguments_);
9392 0 : MConstant* lazyArg = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
9393 0 : current->add(lazyArg);
9394 3 : current->push(lazyArg);
9395 3 : return Ok();
9396 : }
9397 :
9398 : AbortReasonOr<Ok>
9399 0 : IonBuilder::jsop_newtarget()
9400 : {
9401 1 : if (!info().funMaybeLazy()) {
9402 0 : MOZ_ASSERT(!info().script()->isForEval());
9403 0 : pushConstant(NullValue());
9404 0 : return Ok();
9405 : }
9406 :
9407 0 : MOZ_ASSERT(info().funMaybeLazy());
9408 :
9409 0 : if (info().funMaybeLazy()->isArrow()) {
9410 0 : MArrowNewTarget* arrowNewTarget = MArrowNewTarget::New(alloc(), getCallee());
9411 0 : current->add(arrowNewTarget);
9412 0 : current->push(arrowNewTarget);
9413 0 : return Ok();
9414 : }
9415 :
9416 0 : if (inliningDepth_ == 0) {
9417 0 : MNewTarget* newTarget = MNewTarget::New(alloc());
9418 0 : current->add(newTarget);
9419 7 : current->push(newTarget);
9420 7 : return Ok();
9421 : }
9422 :
9423 0 : if (!inlineCallInfo_->constructing()) {
9424 0 : pushConstant(UndefinedValue());
9425 0 : return Ok();
9426 : }
9427 :
9428 0 : current->push(inlineCallInfo_->getNewTarget());
9429 0 : return Ok();
9430 : }
9431 :
9432 : AbortReasonOr<Ok>
9433 0 : IonBuilder::jsop_rest()
9434 : {
9435 1 : if (info().analysisMode() == Analysis_ArgumentsUsage) {
9436 : // There's no BaselineScript with the template object. Just push a
9437 : // dummy value, it does not affect the arguments analysis.
9438 0 : MUnknownValue* unknown = MUnknownValue::New(alloc());
9439 0 : current->add(unknown);
9440 0 : current->push(unknown);
9441 0 : return Ok();
9442 : }
9443 :
9444 0 : ArrayObject* templateObject = &inspector->getTemplateObject(pc)->as<ArrayObject>();
9445 :
9446 0 : if (inliningDepth_ == 0) {
9447 : // We don't know anything about the callee.
9448 1 : MArgumentsLength* numActuals = MArgumentsLength::New(alloc());
9449 1 : current->add(numActuals);
9450 :
9451 : // Pass in the number of actual arguments, the number of formals (not
9452 : // including the rest parameter slot itself), and the template object.
9453 0 : MRest* rest = MRest::New(alloc(), constraints(), numActuals, info().nargs() - 1,
9454 0 : templateObject);
9455 0 : current->add(rest);
9456 1 : current->push(rest);
9457 1 : return Ok();
9458 : }
9459 :
9460 : // We know the exact number of arguments the callee pushed.
9461 0 : unsigned numActuals = inlineCallInfo_->argc();
9462 0 : unsigned numFormals = info().nargs() - 1;
9463 0 : unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
9464 :
9465 0 : MOZ_TRY(jsop_newarray(numRest));
9466 :
9467 0 : if (numRest == 0) {
9468 : // No more updating to do. (Note that in this one case the length from
9469 : // the template object is already correct.)
9470 0 : return Ok();
9471 : }
9472 :
9473 0 : MDefinition *array = current->peek(-1);
9474 0 : MElements* elements = MElements::New(alloc(), array);
9475 0 : current->add(elements);
9476 :
9477 : // Unroll the argument copy loop. We don't need to do any bounds or hole
9478 : // checking here.
9479 0 : MConstant* index = nullptr;
9480 0 : for (unsigned i = numFormals; i < numActuals; i++) {
9481 0 : if (!alloc().ensureBallast())
9482 0 : return abort(AbortReason::Alloc);
9483 :
9484 0 : index = MConstant::New(alloc(), Int32Value(i - numFormals));
9485 0 : current->add(index);
9486 :
9487 0 : MDefinition* arg = inlineCallInfo_->argv()[i];
9488 0 : MStoreElement* store = MStoreElement::New(alloc(), elements, index, arg,
9489 0 : /* needsHoleCheck = */ false);
9490 0 : current->add(store);
9491 :
9492 0 : if (needsPostBarrier(arg))
9493 0 : current->add(MPostWriteBarrier::New(alloc(), array, arg));
9494 : }
9495 :
9496 : // The array's length is incorrectly 0 now, from the template object
9497 : // created by BaselineCompiler::emit_JSOP_REST() before the actual argument
9498 : // count was known. Set the correct length now that we know that count.
9499 0 : MSetArrayLength* length = MSetArrayLength::New(alloc(), elements, index);
9500 0 : current->add(length);
9501 :
9502 : // Update the initialized length for all the (necessarily non-hole)
9503 : // elements added.
9504 0 : MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, index);
9505 0 : current->add(initLength);
9506 :
9507 0 : return Ok();
9508 : }
9509 :
9510 : AbortReasonOr<Ok>
9511 0 : IonBuilder::jsop_checkisobj(uint8_t kind)
9512 : {
9513 0 : MDefinition* toCheck = current->peek(-1);
9514 :
9515 0 : if (toCheck->type() == MIRType::Object) {
9516 101 : toCheck->setImplicitlyUsedUnchecked();
9517 101 : return Ok();
9518 : }
9519 :
9520 0 : MCheckIsObj* check = MCheckIsObj::New(alloc(), current->pop(), kind);
9521 0 : current->add(check);
9522 43 : current->push(check);
9523 43 : return Ok();
9524 : }
9525 :
9526 : AbortReasonOr<Ok>
9527 0 : IonBuilder::jsop_checkiscallable(uint8_t kind)
9528 : {
9529 0 : MCheckIsCallable* check = MCheckIsCallable::New(alloc(), current->pop(), kind);
9530 0 : current->add(check);
9531 0 : current->push(check);
9532 0 : return Ok();
9533 : }
9534 :
9535 : AbortReasonOr<Ok>
9536 0 : IonBuilder::jsop_checkobjcoercible()
9537 : {
9538 0 : MDefinition* toCheck = current->peek(-1);
9539 :
9540 6 : if (!toCheck->mightBeType(MIRType::Undefined) &&
9541 0 : !toCheck->mightBeType(MIRType::Null))
9542 : {
9543 3 : toCheck->setImplicitlyUsedUnchecked();
9544 3 : return Ok();
9545 : }
9546 :
9547 0 : MOZ_ASSERT(toCheck->type() == MIRType::Value ||
9548 : toCheck->type() == MIRType::Null ||
9549 : toCheck->type() == MIRType::Undefined);
9550 :
9551 : // If we want to squeeze more perf here, we can throw without checking,
9552 : // if IsNullOrUndefined(toCheck->type()). Since this is a failure case,
9553 : // it should be OK.
9554 0 : MCheckObjCoercible* check = MCheckObjCoercible::New(alloc(), current->pop());
9555 0 : current->add(check);
9556 0 : current->push(check);
9557 : return resumeAfter(check);
9558 : }
9559 :
9560 : uint32_t
9561 0 : IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, jsid id, uint32_t* pnfixed)
9562 : {
9563 2562 : if (!types || types->unknownObject() || !types->objectOrSentinel()) {
9564 : trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
9565 : return UINT32_MAX;
9566 : }
9567 :
9568 : uint32_t slot = UINT32_MAX;
9569 :
9570 0 : for (size_t i = 0; i < types->getObjectCount(); i++) {
9571 0 : TypeSet::ObjectKey* key = types->getObject(i);
9572 912 : if (!key)
9573 0 : continue;
9574 :
9575 0 : if (key->unknownProperties()) {
9576 : trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
9577 187 : return UINT32_MAX;
9578 : }
9579 :
9580 912 : if (key->isSingleton()) {
9581 : trackOptimizationOutcome(TrackedOutcome::Singleton);
9582 : return UINT32_MAX;
9583 : }
9584 :
9585 0 : HeapTypeSetKey property = key->property(id);
9586 0 : if (!property.maybeTypes() ||
9587 1606 : !property.maybeTypes()->definiteProperty() ||
9588 731 : property.nonData(constraints()))
9589 : {
9590 : trackOptimizationOutcome(TrackedOutcome::NotFixedSlot);
9591 : return UINT32_MAX;
9592 : }
9593 :
9594 : // Definite slots will always be fixed slots when they are in the
9595 : // allowable range for fixed slots, except for objects which were
9596 : // converted from unboxed objects and have a smaller allocation size.
9597 0 : size_t nfixed = NativeObject::MAX_FIXED_SLOTS;
9598 0 : if (ObjectGroup* group = key->group()->maybeOriginalUnboxedGroup()) {
9599 12 : AutoSweepObjectGroup sweepGroup(group);
9600 6 : nfixed = gc::GetGCKindSlots(group->unboxedLayout(sweepGroup).getAllocKind());
9601 : }
9602 :
9603 0 : uint32_t propertySlot = property.maybeTypes()->definiteSlot();
9604 0 : if (slot == UINT32_MAX) {
9605 0 : slot = propertySlot;
9606 709 : *pnfixed = nfixed;
9607 22 : } else if (slot != propertySlot || nfixed != *pnfixed) {
9608 : trackOptimizationOutcome(TrackedOutcome::InconsistentFixedSlot);
9609 : return UINT32_MAX;
9610 : }
9611 : }
9612 :
9613 : return slot;
9614 : }
9615 :
9616 : uint32_t
9617 0 : IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, jsid id, JSValueType* punboxedType)
9618 : {
9619 2342 : if (!types || types->unknownObject() || !types->objectOrSentinel()) {
9620 : trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
9621 : return UINT32_MAX;
9622 : }
9623 :
9624 : uint32_t offset = UINT32_MAX;
9625 :
9626 0 : for (size_t i = 0; i < types->getObjectCount(); i++) {
9627 0 : TypeSet::ObjectKey* key = types->getObject(i);
9628 780 : if (!key)
9629 0 : continue;
9630 :
9631 0 : if (key->unknownProperties()) {
9632 : trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
9633 740 : return UINT32_MAX;
9634 : }
9635 :
9636 780 : if (key->isSingleton()) {
9637 : trackOptimizationOutcome(TrackedOutcome::Singleton);
9638 : return UINT32_MAX;
9639 : }
9640 :
9641 0 : AutoSweepObjectGroup sweep(key->group());
9642 743 : UnboxedLayout* layout = key->group()->maybeUnboxedLayout(sweep);
9643 0 : if (!layout) {
9644 : trackOptimizationOutcome(TrackedOutcome::NotUnboxed);
9645 703 : return UINT32_MAX;
9646 : }
9647 :
9648 40 : const UnboxedLayout::Property* property = layout->lookup(id);
9649 40 : if (!property) {
9650 : trackOptimizationOutcome(TrackedOutcome::StructNoField);
9651 : return UINT32_MAX;
9652 : }
9653 :
9654 40 : if (layout->nativeGroup()) {
9655 : trackOptimizationOutcome(TrackedOutcome::UnboxedConvertedToNative);
9656 : return UINT32_MAX;
9657 : }
9658 :
9659 0 : key->watchStateChangeForUnboxedConvertedToNative(constraints());
9660 :
9661 0 : if (offset == UINT32_MAX) {
9662 0 : offset = property->offset;
9663 40 : *punboxedType = property->type;
9664 0 : } else if (offset != property->offset) {
9665 : trackOptimizationOutcome(TrackedOutcome::InconsistentFieldOffset);
9666 : return UINT32_MAX;
9667 0 : } else if (*punboxedType != property->type) {
9668 : trackOptimizationOutcome(TrackedOutcome::InconsistentFieldType);
9669 : return UINT32_MAX;
9670 : }
9671 : }
9672 :
9673 : return offset;
9674 : }
9675 :
9676 : AbortReasonOr<Ok>
9677 0 : IonBuilder::jsop_runonce()
9678 : {
9679 0 : MRunOncePrologue* ins = MRunOncePrologue::New(alloc());
9680 0 : current->add(ins);
9681 0 : return resumeAfter(ins);
9682 : }
9683 :
9684 : AbortReasonOr<Ok>
9685 0 : IonBuilder::jsop_not()
9686 : {
9687 0 : MDefinition* value = current->pop();
9688 :
9689 0 : MNot* ins = MNot::New(alloc(), value, constraints());
9690 0 : current->add(ins);
9691 396 : current->push(ins);
9692 396 : return Ok();
9693 : }
9694 :
9695 : AbortReasonOr<Ok>
9696 0 : IonBuilder::jsop_superbase()
9697 : {
9698 1 : JSFunction* fun = info().funMaybeLazy();
9699 4 : if (!fun || !fun->allowSuperProperty())
9700 0 : return abort(AbortReason::Disable, "super only supported directly in methods");
9701 :
9702 4 : auto* homeObject = MHomeObject::New(alloc(), getCallee());
9703 0 : current->add(homeObject);
9704 :
9705 0 : auto* superBase = MHomeObjectSuperBase::New(alloc(), homeObject);
9706 2 : current->add(superBase);
9707 0 : current->push(superBase);
9708 :
9709 6 : MOZ_TRY(resumeAfter(superBase));
9710 2 : return Ok();
9711 : }
9712 :
9713 :
9714 : AbortReasonOr<Ok>
9715 0 : IonBuilder::jsop_getprop_super(PropertyName* name)
9716 : {
9717 2 : MDefinition* obj = current->pop();
9718 0 : MDefinition* receiver = current->pop();
9719 :
9720 0 : MConstant* id = constant(StringValue(name));
9721 0 : auto* ins = MGetPropSuperCache::New(alloc(), obj, receiver, id);
9722 2 : current->add(ins);
9723 0 : current->push(ins);
9724 :
9725 0 : MOZ_TRY(resumeAfter(ins));
9726 :
9727 4 : TemporaryTypeSet* types = bytecodeTypes(pc);
9728 2 : return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
9729 : }
9730 :
9731 : AbortReasonOr<Ok>
9732 0 : IonBuilder::jsop_getelem_super()
9733 : {
9734 0 : MDefinition* obj = current->pop();
9735 0 : MDefinition* receiver = current->pop();
9736 0 : MDefinition* id = current->pop();
9737 :
9738 : #if defined(JS_CODEGEN_X86)
9739 : if (instrumentedProfiling())
9740 : return abort(AbortReason::Disable, "profiling functions with GETELEM_SUPER is disabled on x86");
9741 : #endif
9742 :
9743 0 : auto* ins = MGetPropSuperCache::New(alloc(), obj, receiver, id);
9744 0 : current->add(ins);
9745 0 : current->push(ins);
9746 :
9747 0 : MOZ_TRY(resumeAfter(ins));
9748 :
9749 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
9750 0 : return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
9751 : }
9752 :
9753 : NativeObject*
9754 0 : IonBuilder::commonPrototypeWithGetterSetter(TemporaryTypeSet* types, PropertyName* name,
9755 : bool isGetter, JSFunction* getterOrSetter,
9756 : bool* guardGlobal)
9757 : {
9758 : // If there's a single object on the proto chain of all objects in |types|
9759 : // that contains a property |name| with |getterOrSetter| getter or setter
9760 : // function, return that object.
9761 :
9762 : // No sense looking if we don't know what's going on.
9763 0 : if (!types || types->unknownObject())
9764 : return nullptr;
9765 0 : *guardGlobal = false;
9766 :
9767 0 : NativeObject* foundProto = nullptr;
9768 0 : for (unsigned i = 0; i < types->getObjectCount(); i++) {
9769 0 : TypeSet::ObjectKey* key = types->getObject(i);
9770 0 : if (!key)
9771 : continue;
9772 :
9773 0 : while (key) {
9774 0 : if (key->unknownProperties())
9775 0 : return nullptr;
9776 :
9777 0 : const Class* clasp = key->clasp();
9778 0 : if (!ClassHasEffectlessLookup(clasp))
9779 : return nullptr;
9780 0 : JSObject* singleton = key->isSingleton() ? key->singleton() : nullptr;
9781 0 : if (ObjectHasExtraOwnProperty(realm, key, NameToId(name))) {
9782 0 : if (!singleton || !singleton->is<GlobalObject>())
9783 : return nullptr;
9784 0 : *guardGlobal = true;
9785 : }
9786 :
9787 : // Look for a getter/setter on the class itself which may need
9788 : // to be called.
9789 0 : if (isGetter && clasp->getOpsGetProperty())
9790 : return nullptr;
9791 0 : if (!isGetter && clasp->getOpsSetProperty())
9792 : return nullptr;
9793 :
9794 : // If we have a singleton, check if it contains the getter or
9795 : // setter we're looking for. Note that we don't need to add any
9796 : // type constraints as the caller will add a Shape guard for the
9797 : // holder and type constraints for other objects on the proto
9798 : // chain.
9799 : //
9800 : // If the object is not a singleton, we fall back to the code below
9801 : // and check whether the property is missing. That's fine because
9802 : // we're looking for a getter or setter on the proto chain and
9803 : // these objects are singletons.
9804 0 : if (singleton) {
9805 0 : if (!singleton->is<NativeObject>())
9806 : return nullptr;
9807 :
9808 0 : NativeObject* singletonNative = &singleton->as<NativeObject>();
9809 0 : if (Shape* propShape = singletonNative->lookupPure(name)) {
9810 : // We found a property. Check if it's the getter or setter
9811 : // we're looking for.
9812 0 : Value getterSetterVal = ObjectValue(*getterOrSetter);
9813 0 : if (isGetter) {
9814 0 : if (propShape->getterOrUndefined() != getterSetterVal)
9815 : return nullptr;
9816 : } else {
9817 0 : if (propShape->setterOrUndefined() != getterSetterVal)
9818 : return nullptr;
9819 : }
9820 :
9821 0 : if (!foundProto)
9822 : foundProto = singletonNative;
9823 0 : else if (foundProto != singletonNative)
9824 : return nullptr;
9825 : break;
9826 : }
9827 : }
9828 :
9829 : // Test for isOwnProperty() without freezing. If we end up
9830 : // optimizing, freezePropertiesForCommonPropFunc will freeze the
9831 : // property type sets later on.
9832 0 : HeapTypeSetKey property = key->property(NameToId(name));
9833 0 : if (TypeSet* types = property.maybeTypes()) {
9834 0 : if (!types->empty() || types->nonDataProperty())
9835 : return nullptr;
9836 : }
9837 0 : if (singleton) {
9838 0 : if (CanHaveEmptyPropertyTypesForOwnProperty(singleton)) {
9839 0 : MOZ_ASSERT(singleton->is<GlobalObject>());
9840 0 : *guardGlobal = true;
9841 : }
9842 : }
9843 :
9844 0 : JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
9845 0 : if (foundProto && proto == foundProto) {
9846 : // We found an object on the proto chain that's known to have
9847 : // the getter or setter property, so we can stop looking.
9848 : break;
9849 : }
9850 :
9851 0 : if (!proto) {
9852 : // The getter or setter being searched for did not show up on
9853 : // the object's prototype chain.
9854 : return nullptr;
9855 : }
9856 0 : key = TypeSet::ObjectKey::get(proto);
9857 : }
9858 : }
9859 :
9860 : return foundProto;
9861 : }
9862 :
9863 : void
9864 0 : IonBuilder::freezePropertiesForCommonPrototype(TemporaryTypeSet* types, PropertyName* name,
9865 : JSObject* foundProto,
9866 : bool allowEmptyTypesforGlobal/* = false*/)
9867 : {
9868 0 : for (unsigned i = 0; i < types->getObjectCount(); i++) {
9869 : // If we found a Singleton object's own-property, there's nothing to
9870 : // freeze.
9871 0 : if (types->getSingleton(i) == foundProto)
9872 : continue;
9873 :
9874 0 : TypeSet::ObjectKey* key = types->getObject(i);
9875 0 : if (!key)
9876 : continue;
9877 :
9878 : while (true) {
9879 0 : HeapTypeSetKey property = key->property(NameToId(name));
9880 0 : MOZ_ALWAYS_TRUE(!property.isOwnProperty(constraints(), allowEmptyTypesforGlobal));
9881 :
9882 : // Don't mark the proto. It will be held down by the shape
9883 : // guard. This allows us to use properties found on prototypes
9884 : // with properties unknown to TI.
9885 0 : if (key->proto() == TaggedProto(foundProto))
9886 : break;
9887 0 : key = TypeSet::ObjectKey::get(key->proto().toObjectOrNull());
9888 0 : }
9889 : }
9890 0 : }
9891 :
9892 : bool
9893 0 : IonBuilder::testCommonGetterSetter(TemporaryTypeSet* types, PropertyName* name,
9894 : bool isGetter, JSFunction* getterOrSetter,
9895 : MDefinition** guard,
9896 : Shape* globalShape/* = nullptr*/,
9897 : MDefinition** globalGuard/* = nullptr */)
9898 : {
9899 0 : MOZ_ASSERT(getterOrSetter);
9900 0 : MOZ_ASSERT_IF(globalShape, globalGuard);
9901 : bool guardGlobal;
9902 :
9903 : // Check if all objects being accessed will lookup the name through foundProto.
9904 : NativeObject* foundProto =
9905 0 : commonPrototypeWithGetterSetter(types, name, isGetter, getterOrSetter, &guardGlobal);
9906 0 : if (!foundProto || (guardGlobal && !globalShape)) {
9907 : trackOptimizationOutcome(TrackedOutcome::MultiProtoPaths);
9908 : return false;
9909 : }
9910 :
9911 : // We can optimize the getter/setter, so freeze all involved properties to
9912 : // ensure there isn't a lower shadowing getter or setter installed in the
9913 : // future.
9914 0 : freezePropertiesForCommonPrototype(types, name, foundProto, guardGlobal);
9915 :
9916 : // Add a shape guard on the prototype we found the property on. The rest of
9917 : // the prototype chain is guarded by TI freezes, except when name is a global
9918 : // name. In this case, we also have to guard on the globals shape to be able
9919 : // to optimize, because the way global property sets are handled means
9920 : // freezing doesn't work for what we want here. Note that a shape guard is
9921 : // good enough here, even in the proxy case, because we have ensured there
9922 : // are no lookup hooks for this property.
9923 0 : if (guardGlobal) {
9924 0 : JSObject* obj = &script()->global();
9925 0 : MDefinition* globalObj = constant(ObjectValue(*obj));
9926 0 : *globalGuard = addShapeGuard(globalObj, globalShape, Bailout_ShapeGuard);
9927 : }
9928 :
9929 : // If the getter/setter is not configurable we don't have to guard on the
9930 : // proto's shape.
9931 0 : Shape* propShape = foundProto->lookupPure(name);
9932 0 : MOZ_ASSERT_IF(isGetter, propShape->getterObject() == getterOrSetter);
9933 0 : MOZ_ASSERT_IF(!isGetter, propShape->setterObject() == getterOrSetter);
9934 0 : if (propShape && !propShape->configurable())
9935 : return true;
9936 :
9937 0 : MInstruction* wrapper = constant(ObjectValue(*foundProto));
9938 0 : *guard = addShapeGuard(wrapper, foundProto->lastProperty(), Bailout_ShapeGuard);
9939 0 : return true;
9940 : }
9941 :
9942 : void
9943 0 : IonBuilder::replaceMaybeFallbackFunctionGetter(MGetPropertyCache* cache)
9944 : {
9945 : // Discard the last prior resume point of the previous MGetPropertyCache.
9946 1 : WrapMGetPropertyCache rai(maybeFallbackFunctionGetter_);
9947 1708 : maybeFallbackFunctionGetter_ = cache;
9948 0 : }
9949 :
9950 : AbortReasonOr<Ok>
9951 1 : IonBuilder::annotateGetPropertyCache(MDefinition* obj, PropertyName* name,
9952 : MGetPropertyCache* getPropCache, TemporaryTypeSet* objTypes,
9953 : TemporaryTypeSet* pushedTypes)
9954 : {
9955 : // Ensure every pushed value is a singleton.
9956 3 : if (pushedTypes->unknownObject() || pushedTypes->baseFlags() != 0)
9957 0 : return Ok();
9958 :
9959 0 : for (unsigned i = 0; i < pushedTypes->getObjectCount(); i++) {
9960 1 : if (pushedTypes->getGroup(i) != nullptr)
9961 1 : return Ok();
9962 : }
9963 :
9964 : // Object's typeset should be a proper object
9965 0 : if (!objTypes || objTypes->baseFlags() || objTypes->unknownObject())
9966 0 : return Ok();
9967 :
9968 0 : unsigned int objCount = objTypes->getObjectCount();
9969 0 : if (objCount == 0)
9970 0 : return Ok();
9971 :
9972 0 : InlinePropertyTable* inlinePropTable = getPropCache->initInlinePropertyTable(alloc(), pc);
9973 0 : if (!inlinePropTable)
9974 0 : return abort(AbortReason::Alloc);
9975 :
9976 : // Ensure that the relevant property typeset for each group is
9977 : // is a single-object typeset containing a JSFunction
9978 0 : for (unsigned int i = 0; i < objCount; i++) {
9979 0 : ObjectGroup* group = objTypes->getGroup(i);
9980 0 : if (!group)
9981 0 : continue;
9982 0 : TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(group);
9983 0 : if (key->unknownProperties() || !key->proto().isObject())
9984 : continue;
9985 0 : JSObject* proto = checkNurseryObject(key->proto().toObject());
9986 :
9987 0 : const Class* clasp = key->clasp();
9988 0 : if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(realm, key, NameToId(name)))
9989 : continue;
9990 :
9991 0 : HeapTypeSetKey ownTypes = key->property(NameToId(name));
9992 0 : if (ownTypes.isOwnProperty(constraints()))
9993 : continue;
9994 :
9995 0 : JSObject* singleton = testSingletonProperty(proto, NameToId(name));
9996 0 : if (!singleton || !singleton->is<JSFunction>())
9997 : continue;
9998 :
9999 : // Don't add cases corresponding to non-observed pushes
10000 0 : if (!pushedTypes->hasType(TypeSet::ObjectType(singleton)))
10001 : continue;
10002 :
10003 0 : if (!inlinePropTable->addEntry(alloc(), group, &singleton->as<JSFunction>()))
10004 0 : return abort(AbortReason::Alloc);
10005 : }
10006 :
10007 0 : if (inlinePropTable->numEntries() == 0) {
10008 0 : getPropCache->clearInlinePropertyTable();
10009 0 : return Ok();
10010 : }
10011 :
10012 : #ifdef JS_JITSPEW
10013 0 : if (inlinePropTable->numEntries() > 0)
10014 0 : JitSpew(JitSpew_Inlining, "Annotated GetPropertyCache with %d/%d inline cases",
10015 0 : (int) inlinePropTable->numEntries(), (int) objCount);
10016 : #endif
10017 :
10018 : // If we successfully annotated the GetPropertyCache and there are inline cases,
10019 : // then keep a resume point of the state right before this instruction for use
10020 : // later when we have to bail out to this point in the fallback case of a
10021 : // PolyInlineDispatch.
10022 0 : if (inlinePropTable->numEntries() > 0) {
10023 : // Push the object back onto the stack temporarily to capture the resume point.
10024 0 : current->push(obj);
10025 0 : MResumePoint* resumePoint = MResumePoint::New(alloc(), current, pc,
10026 0 : MResumePoint::ResumeAt);
10027 0 : if (!resumePoint)
10028 0 : return abort(AbortReason::Alloc);
10029 0 : inlinePropTable->setPriorResumePoint(resumePoint);
10030 0 : replaceMaybeFallbackFunctionGetter(getPropCache);
10031 0 : current->pop();
10032 : }
10033 0 : return Ok();
10034 : }
10035 :
10036 : // Returns true if an idempotent cache has ever invalidated this script
10037 : // or an outer script.
10038 : bool
10039 0 : IonBuilder::invalidatedIdempotentCache()
10040 : {
10041 0 : IonBuilder* builder = this;
10042 : do {
10043 0 : if (builder->script()->invalidatedIdempotentCache())
10044 : return true;
10045 703 : builder = builder->callerBuilder_;
10046 703 : } while (builder);
10047 :
10048 : return false;
10049 : }
10050 :
10051 : AbortReasonOr<Ok>
10052 80 : IonBuilder::loadSlot(MDefinition* obj, size_t slot, size_t nfixed, MIRType rvalType,
10053 : BarrierKind barrier, TemporaryTypeSet* types)
10054 : {
10055 0 : if (slot < nfixed) {
10056 0 : MLoadFixedSlot* load = MLoadFixedSlot::New(alloc(), obj, slot);
10057 66 : current->add(load);
10058 0 : current->push(load);
10059 :
10060 132 : load->setResultType(rvalType);
10061 66 : return pushTypeBarrier(load, types, barrier);
10062 : }
10063 :
10064 28 : MSlots* slots = MSlots::New(alloc(), obj);
10065 0 : current->add(slots);
10066 :
10067 0 : MLoadSlot* load = MLoadSlot::New(alloc(), slots, slot - nfixed);
10068 14 : current->add(load);
10069 0 : current->push(load);
10070 :
10071 28 : load->setResultType(rvalType);
10072 14 : return pushTypeBarrier(load, types, barrier);
10073 : }
10074 :
10075 : AbortReasonOr<Ok>
10076 80 : IonBuilder::loadSlot(MDefinition* obj, Shape* shape, MIRType rvalType,
10077 : BarrierKind barrier, TemporaryTypeSet* types)
10078 : {
10079 160 : return loadSlot(obj, shape->slot(), shape->numFixedSlots(), rvalType, barrier, types);
10080 : }
10081 :
10082 : AbortReasonOr<Ok>
10083 1 : IonBuilder::storeSlot(MDefinition* obj, size_t slot, size_t nfixed,
10084 : MDefinition* value, bool needsBarrier,
10085 : MIRType slotType /* = MIRType::None */)
10086 : {
10087 0 : if (slot < nfixed) {
10088 0 : MStoreFixedSlot* store = MStoreFixedSlot::New(alloc(), obj, slot, value);
10089 0 : current->add(store);
10090 1 : current->push(value);
10091 1 : if (needsBarrier)
10092 : store->setNeedsBarrier();
10093 : return resumeAfter(store);
10094 : }
10095 :
10096 0 : MSlots* slots = MSlots::New(alloc(), obj);
10097 0 : current->add(slots);
10098 :
10099 0 : MStoreSlot* store = MStoreSlot::New(alloc(), slots, slot - nfixed, value);
10100 0 : current->add(store);
10101 0 : current->push(value);
10102 0 : if (needsBarrier)
10103 0 : store->setNeedsBarrier();
10104 0 : if (slotType != MIRType::None)
10105 0 : store->setSlotType(slotType);
10106 : return resumeAfter(store);
10107 : }
10108 :
10109 : AbortReasonOr<Ok>
10110 1 : IonBuilder::storeSlot(MDefinition* obj, Shape* shape, MDefinition* value, bool needsBarrier,
10111 : MIRType slotType /* = MIRType::None */)
10112 : {
10113 1 : MOZ_ASSERT(shape->writable());
10114 2 : return storeSlot(obj, shape->slot(), shape->numFixedSlots(), value, needsBarrier, slotType);
10115 : }
10116 :
10117 : bool
10118 5284 : IonBuilder::shouldAbortOnPreliminaryGroups(MDefinition *obj)
10119 : {
10120 : // Watch for groups which still have preliminary object information and
10121 : // have not had the new script properties or unboxed layout analyses
10122 : // performed. Normally this is done after a small number of the objects
10123 : // have been created, but if only a few have been created we can still
10124 : // perform the analysis with a smaller object population. The analysis can
10125 : // have side effects so we will end up aborting compilation after building
10126 : // finishes and retrying later.
10127 5284 : TemporaryTypeSet *types = obj->resultTypeSet();
10128 8454 : if (!types || types->unknownObject())
10129 : return false;
10130 :
10131 : bool preliminary = false;
10132 0 : for (size_t i = 0; i < types->getObjectCount(); i++) {
10133 2370 : TypeSet::ObjectKey* key = types->getObject(i);
10134 2370 : if (!key)
10135 : continue;
10136 :
10137 0 : if (ObjectGroup* group = key->maybeGroup()) {
10138 0 : if (group->hasUnanalyzedPreliminaryObjects()) {
10139 7 : addAbortedPreliminaryGroup(group);
10140 7 : preliminary = true;
10141 : }
10142 : }
10143 : }
10144 :
10145 : return preliminary;
10146 : }
10147 :
10148 : MDefinition*
10149 0 : IonBuilder::maybeUnboxForPropertyAccess(MDefinition* def)
10150 : {
10151 1473 : if (def->type() != MIRType::Value)
10152 : return def;
10153 :
10154 236 : MIRType type = inspector->expectedPropertyAccessInputType(pc);
10155 236 : if (type == MIRType::Value || !def->mightBeType(type))
10156 : return def;
10157 :
10158 1 : MUnbox* unbox = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
10159 1 : current->add(unbox);
10160 :
10161 : // Fixup type information for a common case where a property call
10162 : // is converted to the following bytecodes
10163 : //
10164 : // a.foo()
10165 : // ================= Compiles to ================
10166 : // LOAD "a"
10167 : // DUP
10168 : // CALLPROP "foo"
10169 : // SWAP
10170 : // CALL 0
10171 : //
10172 : // If we have better type information to unbox the first copy going into
10173 : // the CALLPROP operation, we can replace the duplicated copy on the
10174 : // stack as well.
10175 0 : if (*pc == JSOP_CALLPROP || *pc == JSOP_CALLELEM) {
10176 0 : uint32_t idx = current->stackDepth() - 1;
10177 1 : MOZ_ASSERT(current->getSlot(idx) == def);
10178 1 : current->setSlot(idx, unbox);
10179 : }
10180 :
10181 : return unbox;
10182 : }
10183 :
10184 : AbortReasonOr<Ok>
10185 0 : IonBuilder::jsop_getprop(PropertyName* name)
10186 : {
10187 1176 : bool emitted = false;
10188 0 : startTrackingOptimizations();
10189 :
10190 1176 : MDefinition* obj = current->pop();
10191 0 : TemporaryTypeSet* types = bytecodeTypes(pc);
10192 :
10193 0 : trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet());
10194 :
10195 1176 : if (!info().isAnalysis()) {
10196 : // The calls below can abort compilation, so we only try this if we're
10197 : // not analyzing.
10198 : // Try to optimize arguments.length.
10199 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_ArgumentsLength);
10200 0 : MOZ_TRY(getPropTryArgumentsLength(&emitted, obj));
10201 832 : if (emitted)
10202 6 : return Ok();
10203 :
10204 : // Try to optimize arguments.callee.
10205 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_ArgumentsCallee);
10206 0 : MOZ_TRY(getPropTryArgumentsCallee(&emitted, obj, name));
10207 826 : if (emitted)
10208 0 : return Ok();
10209 : }
10210 :
10211 0 : obj = maybeUnboxForPropertyAccess(obj);
10212 1170 : if (obj->type() == MIRType::Object)
10213 0 : obj = convertUnboxedObjects(obj);
10214 :
10215 1170 : BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
10216 1170 : obj, name, types);
10217 :
10218 : // Try to optimize to a specific constant.
10219 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_InferredConstant);
10220 0 : if (barrier == BarrierKind::NoBarrier) {
10221 0 : MOZ_TRY(getPropTryInferredConstant(&emitted, obj, name, types));
10222 575 : if (emitted)
10223 10 : return Ok();
10224 : } else {
10225 : trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
10226 : }
10227 :
10228 : // Always use a call if we are performing analysis and
10229 : // not actually emitting code, to simplify later analysis. Also skip deeper
10230 : // analysis if there are no known types for this operation, as it will
10231 : // always invalidate when executing.
10232 1976 : if (info().isAnalysis() || types->empty() || shouldAbortOnPreliminaryGroups(obj)) {
10233 840 : if (types->empty()) {
10234 : // Since no further optimizations will be tried, use the IC
10235 : // strategy, which would have been the last one to be tried, as a
10236 : // sentinel value for why everything failed.
10237 315 : trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
10238 : trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
10239 : }
10240 :
10241 840 : MCallGetProperty* call = MCallGetProperty::New(alloc(), obj, name);
10242 420 : current->add(call);
10243 :
10244 : // During the definite properties analysis we can still try to bake in
10245 : // constants read off the prototype chain, to allow inlining later on.
10246 : // In this case we still need the getprop call so that the later
10247 : // analysis knows when the |this| value has been read from.
10248 0 : if (info().isAnalysis()) {
10249 0 : MOZ_TRY(getPropTryConstant(&emitted, obj, NameToId(name), types));
10250 344 : if (emitted)
10251 6 : return Ok();
10252 : }
10253 :
10254 0 : current->push(call);
10255 1242 : MOZ_TRY(resumeAfter(call));
10256 414 : return pushTypeBarrier(call, types, BarrierKind::TypeSet);
10257 : }
10258 :
10259 : // Try to optimize accesses on outer window proxies, for example window.foo.
10260 : // This needs to come before the various strategies getPropTryInnerize tries
10261 : // internally, since some of those strategies will "succeed" in silly ways
10262 : // even for an outer object.
10263 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_Innerize);
10264 0 : MOZ_TRY(getPropTryInnerize(&emitted, obj, name, types));
10265 740 : if (emitted)
10266 0 : return Ok();
10267 :
10268 0 : if (!forceInlineCaches()) {
10269 : // Try to hardcode known constants.
10270 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
10271 0 : MOZ_TRY(getPropTryConstant(&emitted, obj, NameToId(name), types));
10272 740 : if (emitted)
10273 100 : return Ok();
10274 :
10275 : // Try to hardcode known not-defined
10276 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_NotDefined);
10277 0 : MOZ_TRY(getPropTryNotDefined(&emitted, obj, NameToId(name), types));
10278 640 : if (emitted)
10279 19 : return Ok();
10280 :
10281 : // Try to emit loads from definite slots.
10282 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_DefiniteSlot);
10283 0 : MOZ_TRY(getPropTryDefiniteSlot(&emitted, obj, name, barrier, types));
10284 621 : if (emitted)
10285 142 : return Ok();
10286 :
10287 : // Try to emit loads from unboxed objects.
10288 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_Unboxed);
10289 0 : MOZ_TRY(getPropTryUnboxed(&emitted, obj, name, barrier, types));
10290 479 : if (emitted)
10291 8 : return Ok();
10292 :
10293 : // Try to inline a common property getter, or make a call.
10294 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
10295 0 : MOZ_TRY(getPropTryCommonGetter(&emitted, obj, name, types));
10296 471 : if (emitted)
10297 2 : return Ok();
10298 :
10299 : // Try to emit a monomorphic/polymorphic access based on baseline caches.
10300 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_InlineAccess);
10301 0 : MOZ_TRY(getPropTryInlineAccess(&emitted, obj, name, barrier, types));
10302 469 : if (emitted)
10303 55 : return Ok();
10304 :
10305 : // Try to emit a property access on the prototype based on baseline
10306 : // caches.
10307 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_InlineProtoAccess);
10308 0 : MOZ_TRY(getPropTryInlineProtoAccess(&emitted, obj, name, types));
10309 414 : if (emitted)
10310 37 : return Ok();
10311 :
10312 : // Try to emit loads from a module namespace.
10313 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_ModuleNamespace);
10314 0 : MOZ_TRY(getPropTryModuleNamespace(&emitted, obj, name, barrier, types));
10315 377 : if (emitted)
10316 0 : return Ok();
10317 :
10318 : // Try to emit loads from known binary data blocks
10319 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_TypedObject);
10320 0 : MOZ_TRY(getPropTryTypedObject(&emitted, obj, name));
10321 377 : if (emitted)
10322 0 : return Ok();
10323 : }
10324 :
10325 : // Emit a polymorphic cache.
10326 377 : trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
10327 377 : return getPropAddCache(obj, name, barrier, types);
10328 : }
10329 :
10330 : AbortReasonOr<Ok>
10331 400 : IonBuilder::improveThisTypesForCall()
10332 : {
10333 : // After a CALLPROP (or CALLELEM) for obj.prop(), the this-value and callee
10334 : // for the call are on top of the stack:
10335 : //
10336 : // ... [this: obj], [callee: obj.prop]
10337 : //
10338 : // If obj is null or undefined, obj.prop would have thrown an exception so
10339 : // at this point we can remove null and undefined from obj's TypeSet, to
10340 : // improve type information for the call that will follow.
10341 :
10342 400 : MOZ_ASSERT(*pc == JSOP_CALLPROP || *pc == JSOP_CALLELEM);
10343 :
10344 : // Ensure |this| has types {object, null/undefined}.
10345 0 : MDefinition* thisDef = current->peek(-2);
10346 0 : if (thisDef->type() != MIRType::Value ||
10347 0 : !thisDef->mightBeType(MIRType::Object) ||
10348 400 : !thisDef->resultTypeSet() ||
10349 0 : !thisDef->resultTypeSet()->objectOrSentinel())
10350 : {
10351 400 : return Ok();
10352 : }
10353 :
10354 : // Remove null/undefined from the TypeSet.
10355 0 : TemporaryTypeSet* types = thisDef->resultTypeSet()->cloneObjectsOnly(alloc_->lifoAlloc());
10356 0 : if (!types)
10357 0 : return abort(AbortReason::Alloc);
10358 :
10359 0 : MFilterTypeSet* filter = MFilterTypeSet::New(alloc(), thisDef, types);
10360 0 : current->add(filter);
10361 0 : current->rewriteAtDepth(-2, filter);
10362 :
10363 : // FilterTypeSetPolicy::adjustInputs will insert an infallible Unbox(Object)
10364 : // for the input. Don't hoist this unbox above the getprop or getelem
10365 : // operation.
10366 0 : filter->setDependency(current->peek(-1)->toInstruction());
10367 0 : return Ok();
10368 : }
10369 :
10370 : AbortReasonOr<Ok>
10371 0 : IonBuilder::checkIsDefinitelyOptimizedArguments(MDefinition* obj, bool* isOptimizedArgs)
10372 : {
10373 0 : if (obj->type() != MIRType::MagicOptimizedArguments) {
10374 235 : if (script()->argumentsHasVarBinding() &&
10375 0 : obj->mightBeType(MIRType::MagicOptimizedArguments))
10376 : {
10377 0 : return abort(AbortReason::Disable, "Type is not definitely lazy arguments.");
10378 : }
10379 :
10380 116 : *isOptimizedArgs = false;
10381 116 : return Ok();
10382 : }
10383 :
10384 6 : *isOptimizedArgs = true;
10385 6 : return Ok();
10386 : }
10387 :
10388 : AbortReasonOr<Ok>
10389 575 : IonBuilder::getPropTryInferredConstant(bool* emitted, MDefinition* obj, PropertyName* name,
10390 : TemporaryTypeSet* types)
10391 : {
10392 575 : MOZ_ASSERT(*emitted == false);
10393 :
10394 : // Need a result typeset to optimize.
10395 0 : TemporaryTypeSet* objTypes = obj->resultTypeSet();
10396 0 : if (!objTypes) {
10397 0 : trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
10398 0 : return Ok();
10399 : }
10400 :
10401 0 : JSObject* singleton = objTypes->maybeSingleton();
10402 0 : if (!singleton) {
10403 547 : trackOptimizationOutcome(TrackedOutcome::NotSingleton);
10404 547 : return Ok();
10405 : }
10406 :
10407 1 : TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(singleton);
10408 1 : if (key->unknownProperties()) {
10409 0 : trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
10410 0 : return Ok();
10411 : }
10412 :
10413 0 : HeapTypeSetKey property = key->property(NameToId(name));
10414 :
10415 0 : Value constantValue = UndefinedValue();
10416 0 : if (property.constant(constraints(), &constantValue)) {
10417 0 : spew("Optimized constant property");
10418 0 : obj->setImplicitlyUsedUnchecked();
10419 0 : pushConstant(constantValue);
10420 0 : types->addType(TypeSet::GetValueType(constantValue), alloc_->lifoAlloc());
10421 10 : trackOptimizationSuccess();
10422 10 : *emitted = true;
10423 : }
10424 :
10425 28 : return Ok();
10426 : }
10427 :
10428 : AbortReasonOr<Ok>
10429 0 : IonBuilder::getPropTryArgumentsLength(bool* emitted, MDefinition* obj)
10430 : {
10431 0 : MOZ_ASSERT(*emitted == false);
10432 :
10433 832 : if (JSOp(*pc) != JSOP_LENGTH)
10434 0 : return Ok();
10435 :
10436 0 : bool isOptimizedArgs = false;
10437 0 : MOZ_TRY(checkIsDefinitelyOptimizedArguments(obj, &isOptimizedArgs));
10438 122 : if (!isOptimizedArgs)
10439 0 : return Ok();
10440 :
10441 6 : trackOptimizationSuccess();
10442 0 : *emitted = true;
10443 :
10444 6 : obj->setImplicitlyUsedUnchecked();
10445 :
10446 : // We don't know anything from the callee
10447 0 : if (inliningDepth_ == 0) {
10448 0 : MInstruction* ins = MArgumentsLength::New(alloc());
10449 0 : current->add(ins);
10450 4 : current->push(ins);
10451 4 : return Ok();
10452 : }
10453 :
10454 : // We are inlining and know the number of arguments the callee pushed
10455 4 : pushConstant(Int32Value(inlineCallInfo_->argv().length()));
10456 2 : return Ok();
10457 : }
10458 :
10459 : AbortReasonOr<Ok>
10460 0 : IonBuilder::getPropTryArgumentsCallee(bool* emitted, MDefinition* obj, PropertyName* name)
10461 : {
10462 0 : MOZ_ASSERT(*emitted == false);
10463 :
10464 2478 : if (name != names().callee)
10465 0 : return Ok();
10466 :
10467 0 : bool isOptimizedArgs = false;
10468 0 : MOZ_TRY(checkIsDefinitelyOptimizedArguments(obj, &isOptimizedArgs));
10469 0 : if (!isOptimizedArgs)
10470 0 : return Ok();
10471 :
10472 0 : MOZ_ASSERT(script()->hasMappedArgsObj());
10473 :
10474 0 : obj->setImplicitlyUsedUnchecked();
10475 0 : current->push(getCallee());
10476 :
10477 0 : trackOptimizationSuccess();
10478 0 : *emitted = true;
10479 0 : return Ok();
10480 : }
10481 :
10482 : AbortReasonOr<Ok>
10483 0 : IonBuilder::getPropTryConstant(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types)
10484 : {
10485 0 : MOZ_ASSERT(*emitted == false);
10486 :
10487 1130 : if (!types->mightBeMIRType(MIRType::Object)) {
10488 : // If we have not observed an object result here, don't look for a
10489 : // singleton constant.
10490 617 : trackOptimizationOutcome(TrackedOutcome::NotObject);
10491 617 : return Ok();
10492 : }
10493 :
10494 0 : JSObject* singleton = testSingletonPropertyTypes(obj, id);
10495 0 : if (!singleton) {
10496 382 : trackOptimizationOutcome(TrackedOutcome::NotSingleton);
10497 382 : return Ok();
10498 : }
10499 :
10500 : // Property access is a known constant -- safe to emit.
10501 0 : obj->setImplicitlyUsedUnchecked();
10502 :
10503 0 : pushConstant(ObjectValue(*singleton));
10504 :
10505 0 : trackOptimizationSuccess();
10506 131 : *emitted = true;
10507 131 : return Ok();
10508 : }
10509 :
10510 : AbortReasonOr<Ok>
10511 0 : IonBuilder::getPropTryNotDefined(bool* emitted, MDefinition* obj, jsid id, TemporaryTypeSet* types)
10512 : {
10513 0 : MOZ_ASSERT(*emitted == false);
10514 :
10515 0 : if (!types->mightBeMIRType(MIRType::Undefined)) {
10516 : // Only optimize if we expect this property access to return undefined.
10517 628 : trackOptimizationOutcome(TrackedOutcome::NotUndefined);
10518 628 : return Ok();
10519 : }
10520 :
10521 : bool res;
10522 0 : MOZ_TRY_VAR(res, testNotDefinedProperty(obj, id));
10523 0 : if (!res) {
10524 14 : trackOptimizationOutcome(TrackedOutcome::GenericFailure);
10525 14 : return Ok();
10526 : }
10527 :
10528 19 : obj->setImplicitlyUsedUnchecked();
10529 0 : pushConstant(UndefinedValue());
10530 :
10531 0 : trackOptimizationSuccess();
10532 19 : *emitted = true;
10533 19 : return Ok();
10534 : }
10535 :
10536 : AbortReasonOr<Ok>
10537 377 : IonBuilder::getPropTryTypedObject(bool* emitted,
10538 : MDefinition* obj,
10539 : PropertyName* name)
10540 : {
10541 377 : TypedObjectPrediction fieldPrediction;
10542 : size_t fieldOffset;
10543 : size_t fieldIndex;
10544 377 : if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
10545 0 : return Ok();
10546 :
10547 0 : switch (fieldPrediction.kind()) {
10548 : case type::Simd:
10549 : // FIXME (bug 894104): load into a MIRType::float32x4 etc
10550 0 : return Ok();
10551 :
10552 : case type::Struct:
10553 : case type::Array:
10554 : return getPropTryComplexPropOfTypedObject(emitted,
10555 : obj,
10556 : fieldOffset,
10557 : fieldPrediction,
10558 0 : fieldIndex);
10559 :
10560 : case type::Reference:
10561 : return getPropTryReferencePropOfTypedObject(emitted,
10562 : obj,
10563 : fieldOffset,
10564 : fieldPrediction,
10565 0 : name);
10566 :
10567 : case type::Scalar:
10568 : return getPropTryScalarPropOfTypedObject(emitted,
10569 : obj,
10570 : fieldOffset,
10571 0 : fieldPrediction);
10572 : }
10573 :
10574 0 : MOZ_CRASH("Bad kind");
10575 : }
10576 :
10577 : AbortReasonOr<Ok>
10578 0 : IonBuilder::getPropTryScalarPropOfTypedObject(bool* emitted, MDefinition* typedObj,
10579 : int32_t fieldOffset,
10580 : TypedObjectPrediction fieldPrediction)
10581 : {
10582 : // Must always be loading the same scalar type
10583 0 : Scalar::Type fieldType = fieldPrediction.scalarType();
10584 :
10585 : // Don't optimize if the typed object's underlying buffer may be detached.
10586 0 : TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
10587 0 : if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
10588 0 : return Ok();
10589 :
10590 0 : trackOptimizationSuccess();
10591 0 : *emitted = true;
10592 :
10593 0 : LinearSum byteOffset(alloc());
10594 0 : if (!byteOffset.add(fieldOffset))
10595 0 : return abort(AbortReason::Disable, "Overflow of field offsets.");
10596 :
10597 0 : return pushScalarLoadFromTypedObject(typedObj, byteOffset, fieldType);
10598 : }
10599 :
10600 : AbortReasonOr<Ok>
10601 0 : IonBuilder::getPropTryReferencePropOfTypedObject(bool* emitted, MDefinition* typedObj,
10602 : int32_t fieldOffset,
10603 : TypedObjectPrediction fieldPrediction,
10604 : PropertyName* name)
10605 : {
10606 0 : ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
10607 :
10608 0 : TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
10609 0 : if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
10610 0 : return Ok();
10611 :
10612 0 : trackOptimizationSuccess();
10613 0 : *emitted = true;
10614 :
10615 0 : LinearSum byteOffset(alloc());
10616 0 : if (!byteOffset.add(fieldOffset))
10617 0 : return abort(AbortReason::Disable, "Overflow of field offsets.");
10618 :
10619 0 : return pushReferenceLoadFromTypedObject(typedObj, byteOffset, fieldType, name);
10620 : }
10621 :
10622 : AbortReasonOr<Ok>
10623 0 : IonBuilder::getPropTryComplexPropOfTypedObject(bool* emitted,
10624 : MDefinition* typedObj,
10625 : int32_t fieldOffset,
10626 : TypedObjectPrediction fieldPrediction,
10627 : size_t fieldIndex)
10628 : {
10629 : // Don't optimize if the typed object's underlying buffer may be detached.
10630 0 : TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
10631 0 : if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
10632 0 : return Ok();
10633 :
10634 : // OK, perform the optimization
10635 :
10636 : // Identify the type object for the field.
10637 0 : MDefinition* type = loadTypedObjectType(typedObj);
10638 0 : MDefinition* fieldTypeObj = typeObjectForFieldFromStructType(type, fieldIndex);
10639 :
10640 0 : LinearSum byteOffset(alloc());
10641 0 : if (!byteOffset.add(fieldOffset))
10642 0 : return abort(AbortReason::Disable, "Overflow of field offsets.");
10643 :
10644 : return pushDerivedTypedObject(emitted, typedObj, byteOffset,
10645 0 : fieldPrediction, fieldTypeObj);
10646 : }
10647 :
10648 : MDefinition*
10649 2108 : IonBuilder::convertUnboxedObjects(MDefinition* obj)
10650 : {
10651 : // If obj might be in any particular unboxed group which should be
10652 : // converted to a native representation, perform that conversion. This does
10653 : // not guarantee the object will not have such a group afterwards, if the
10654 : // object's possible groups are not precisely known.
10655 2108 : TemporaryTypeSet* types = obj->resultTypeSet();
10656 4135 : if (!types || types->unknownObject() || !types->objectOrSentinel())
10657 : return obj;
10658 :
10659 0 : BaselineInspector::ObjectGroupVector list(alloc());
10660 0 : for (size_t i = 0; i < types->getObjectCount(); i++) {
10661 0 : TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i);
10662 2948 : if (!key || !key->isGroup())
10663 0 : continue;
10664 :
10665 0 : AutoSweepObjectGroup sweep(key->group());
10666 0 : if (UnboxedLayout* layout = key->group()->maybeUnboxedLayout(sweep)) {
10667 1 : AutoEnterOOMUnsafeRegion oomUnsafe;
10668 130 : if (layout->nativeGroup() && !list.append(key->group()))
10669 0 : oomUnsafe.crash("IonBuilder::convertUnboxedObjects");
10670 : }
10671 : }
10672 :
10673 1358 : return convertUnboxedObjects(obj, list);
10674 : }
10675 :
10676 : MDefinition*
10677 1453 : IonBuilder::convertUnboxedObjects(MDefinition* obj,
10678 : const BaselineInspector::ObjectGroupVector& list)
10679 : {
10680 0 : for (size_t i = 0; i < list.length(); i++) {
10681 0 : ObjectGroup* group = list[i];
10682 12 : if (TemporaryTypeSet* types = obj->resultTypeSet()) {
10683 12 : if (!types->hasType(TypeSet::ObjectType(group)))
10684 : continue;
10685 : }
10686 6 : obj = MConvertUnboxedObjectToNative::New(alloc(), obj, group);
10687 0 : current->add(obj->toInstruction());
10688 : }
10689 1453 : return obj;
10690 : }
10691 :
10692 : AbortReasonOr<Ok>
10693 621 : IonBuilder::getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name,
10694 : BarrierKind barrier, TemporaryTypeSet* types)
10695 : {
10696 621 : MOZ_ASSERT(*emitted == false);
10697 :
10698 : uint32_t nfixed;
10699 0 : uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), NameToId(name), &nfixed);
10700 621 : if (slot == UINT32_MAX)
10701 0 : return Ok();
10702 :
10703 0 : if (obj->type() != MIRType::Object) {
10704 0 : MGuardObject* guard = MGuardObject::New(alloc(), obj);
10705 0 : current->add(guard);
10706 0 : obj = guard;
10707 : }
10708 :
10709 : MInstruction* load;
10710 142 : if (slot < nfixed) {
10711 0 : load = MLoadFixedSlot::New(alloc(), obj, slot);
10712 : } else {
10713 0 : MInstruction* slots = MSlots::New(alloc(), obj);
10714 0 : current->add(slots);
10715 :
10716 0 : load = MLoadSlot::New(alloc(), slots, slot - nfixed);
10717 : }
10718 :
10719 142 : if (barrier == BarrierKind::NoBarrier)
10720 0 : load->setResultType(types->getKnownMIRType());
10721 :
10722 142 : current->add(load);
10723 0 : current->push(load);
10724 :
10725 0 : MOZ_TRY(pushTypeBarrier(load, types, barrier));
10726 :
10727 0 : trackOptimizationSuccess();
10728 142 : *emitted = true;
10729 142 : return Ok();
10730 : }
10731 :
10732 : AbortReasonOr<Ok>
10733 377 : IonBuilder::getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name,
10734 : BarrierKind barrier, TemporaryTypeSet* types)
10735 : {
10736 0 : MOZ_ASSERT(*emitted == false);
10737 :
10738 0 : TemporaryTypeSet* objTypes = obj->resultTypeSet();
10739 0 : if (!objTypes) {
10740 0 : trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
10741 0 : return Ok();
10742 : }
10743 :
10744 0 : JSObject* singleton = objTypes->maybeSingleton();
10745 0 : if (!singleton) {
10746 377 : trackOptimizationOutcome(TrackedOutcome::NotSingleton);
10747 377 : return Ok();
10748 : }
10749 :
10750 0 : if (!singleton->is<ModuleNamespaceObject>()) {
10751 0 : trackOptimizationOutcome(TrackedOutcome::NotModuleNamespace);
10752 0 : return Ok();
10753 : }
10754 :
10755 0 : ModuleNamespaceObject* ns = &singleton->as<ModuleNamespaceObject>();
10756 : ModuleEnvironmentObject* env;
10757 : Shape* shape;
10758 0 : if (!ns->bindings().lookup(NameToId(name), &env, &shape)) {
10759 0 : trackOptimizationOutcome(TrackedOutcome::UnknownProperty);
10760 0 : return Ok();
10761 : }
10762 :
10763 0 : obj->setImplicitlyUsedUnchecked();
10764 0 : MConstant* envConst = constant(ObjectValue(*env));
10765 0 : uint32_t slot = shape->slot();
10766 0 : uint32_t nfixed = env->numFixedSlots();
10767 :
10768 0 : MIRType rvalType = types->getKnownMIRType();
10769 0 : if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
10770 0 : rvalType = MIRType::Value;
10771 :
10772 0 : MOZ_TRY(loadSlot(envConst, slot, nfixed, rvalType, barrier, types));
10773 :
10774 0 : trackOptimizationSuccess();
10775 0 : *emitted = true;
10776 0 : return Ok();
10777 : }
10778 :
10779 : MInstruction*
10780 8 : IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
10781 : BarrierKind barrier, TemporaryTypeSet* types)
10782 : {
10783 : // loadUnboxedValue is designed to load any value as if it were contained in
10784 : // an array. Thus a property offset is converted to an index, when the
10785 : // object is reinterpreted as an array of properties of the same size.
10786 0 : size_t index = offset / UnboxedTypeSize(unboxedType);
10787 16 : MInstruction* indexConstant = MConstant::New(alloc(), Int32Value(index));
10788 8 : current->add(indexConstant);
10789 :
10790 : return loadUnboxedValue(obj, UnboxedPlainObject::offsetOfData(),
10791 8 : indexConstant, unboxedType, barrier, types);
10792 : }
10793 :
10794 : MInstruction*
10795 8 : IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset,
10796 : MDefinition* index, JSValueType unboxedType,
10797 : BarrierKind barrier, TemporaryTypeSet* types)
10798 : {
10799 : MInstruction* load;
10800 0 : switch (unboxedType) {
10801 : case JSVAL_TYPE_BOOLEAN:
10802 0 : load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Uint8,
10803 0 : DoesNotRequireMemoryBarrier, elementsOffset);
10804 0 : load->setResultType(MIRType::Boolean);
10805 : break;
10806 :
10807 : case JSVAL_TYPE_INT32:
10808 0 : load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Int32,
10809 0 : DoesNotRequireMemoryBarrier, elementsOffset);
10810 0 : load->setResultType(MIRType::Int32);
10811 : break;
10812 :
10813 : case JSVAL_TYPE_DOUBLE:
10814 0 : load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Float64,
10815 : DoesNotRequireMemoryBarrier, elementsOffset,
10816 0 : /* canonicalizeDoubles = */ false);
10817 0 : load->setResultType(MIRType::Double);
10818 : break;
10819 :
10820 : case JSVAL_TYPE_STRING:
10821 16 : load = MLoadUnboxedString::New(alloc(), elements, index, elementsOffset);
10822 8 : break;
10823 :
10824 : case JSVAL_TYPE_OBJECT: {
10825 : MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
10826 0 : if (types->hasType(TypeSet::NullType()))
10827 : nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
10828 0 : else if (barrier != BarrierKind::NoBarrier)
10829 : nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull;
10830 : else
10831 0 : nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible;
10832 0 : load = MLoadUnboxedObjectOrNull::New(alloc(), elements, index, nullBehavior,
10833 0 : elementsOffset);
10834 : break;
10835 : }
10836 :
10837 : default:
10838 0 : MOZ_CRASH();
10839 : }
10840 :
10841 8 : current->add(load);
10842 8 : return load;
10843 : }
10844 :
10845 : AbortReasonOr<Ok>
10846 479 : IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name,
10847 : BarrierKind barrier, TemporaryTypeSet* types)
10848 : {
10849 479 : MOZ_ASSERT(*emitted == false);
10850 :
10851 : JSValueType unboxedType;
10852 0 : uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), NameToId(name), &unboxedType);
10853 479 : if (offset == UINT32_MAX)
10854 0 : return Ok();
10855 :
10856 0 : if (obj->type() != MIRType::Object) {
10857 0 : MGuardObject* guard = MGuardObject::New(alloc(), obj);
10858 0 : current->add(guard);
10859 0 : obj = guard;
10860 : }
10861 :
10862 8 : MInstruction* load = loadUnboxedProperty(obj, offset, unboxedType, barrier, types);
10863 0 : current->push(load);
10864 :
10865 0 : MOZ_TRY(pushTypeBarrier(load, types, barrier));
10866 :
10867 0 : trackOptimizationSuccess();
10868 8 : *emitted = true;
10869 8 : return Ok();
10870 : }
10871 :
10872 : MDefinition*
10873 2 : IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
10874 : const BaselineInspector::ReceiverVector& receivers,
10875 : const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
10876 : bool isOwnProperty)
10877 : {
10878 2 : MOZ_ASSERT(isOwnProperty == !holder);
10879 0 : MOZ_ASSERT(holderShape);
10880 :
10881 0 : obj = convertUnboxedObjects(obj, convertUnboxedGroups);
10882 :
10883 0 : if (isOwnProperty) {
10884 2 : MOZ_ASSERT(receivers.empty());
10885 2 : return addShapeGuard(obj, holderShape, Bailout_ShapeGuard);
10886 : }
10887 :
10888 0 : MDefinition* holderDef = constant(ObjectValue(*holder));
10889 0 : addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard);
10890 :
10891 0 : return addGuardReceiverPolymorphic(obj, receivers);
10892 : }
10893 :
10894 : AbortReasonOr<Ok>
10895 476 : IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name,
10896 : TemporaryTypeSet* types, bool innerized)
10897 : {
10898 0 : MOZ_ASSERT(*emitted == false);
10899 :
10900 0 : TemporaryTypeSet* objTypes = obj->resultTypeSet();
10901 :
10902 0 : JSFunction* commonGetter = nullptr;
10903 476 : MDefinition* guard = nullptr;
10904 476 : MDefinition* globalGuard = nullptr;
10905 :
10906 : {
10907 0 : Shape* lastProperty = nullptr;
10908 0 : Shape* globalShape = nullptr;
10909 0 : JSObject* foundProto = nullptr;
10910 0 : bool isOwnProperty = false;
10911 0 : BaselineInspector::ReceiverVector receivers(alloc());
10912 954 : BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
10913 476 : if (inspector->commonGetPropFunction(pc, innerized, &foundProto, &lastProperty, &commonGetter,
10914 : &globalShape, &isOwnProperty,
10915 : receivers, convertUnboxedGroups))
10916 : {
10917 2 : bool canUseTIForGetter = false;
10918 2 : if (!isOwnProperty) {
10919 : // If it's not an own property, try to use TI to avoid shape guards.
10920 : // For own properties we use the path below.
10921 0 : canUseTIForGetter = testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
10922 : commonGetter, &guard,
10923 0 : globalShape, &globalGuard);
10924 : }
10925 2 : if (!canUseTIForGetter) {
10926 : // If it's an own property or type information is bad, we can still
10927 : // optimize the getter if we shape guard.
10928 2 : obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
10929 : receivers, convertUnboxedGroups,
10930 : isOwnProperty);
10931 2 : if (!obj)
10932 0 : return abort(AbortReason::Alloc);
10933 : }
10934 474 : } else if (inspector->megamorphicGetterSetterFunction(pc, /* isGetter = */ true,
10935 : &commonGetter))
10936 : {
10937 : // Try to use TI to guard on this getter.
10938 0 : if (!testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
10939 : commonGetter, &guard))
10940 : {
10941 0 : return Ok();
10942 : }
10943 : } else {
10944 : // The Baseline IC didn't have any information we can use.
10945 474 : return Ok();
10946 : }
10947 : }
10948 :
10949 0 : DOMObjectKind objKind = DOMObjectKind::Unknown;
10950 0 : bool isDOM = objTypes && objTypes->isDOMClass(constraints(), &objKind);
10951 2 : if (isDOM)
10952 0 : MOZ_TRY_VAR(isDOM, testShouldDOMCall(objTypes, commonGetter, JSJitInfo::Getter));
10953 :
10954 2 : if (isDOM) {
10955 0 : const JSJitInfo* jitinfo = commonGetter->jitInfo();
10956 : // We don't support MGetDOMProperty/MGetDOMMember on things that might
10957 : // be proxies when the value might be in a slot, because the
10958 : // CodeGenerator code for LGetDOMProperty/LGetDOMMember doesn't handle
10959 : // that case correctly.
10960 0 : if (objKind == DOMObjectKind::Native ||
10961 0 : (!jitinfo->isAlwaysInSlot && !jitinfo->isLazilyCachedInSlot))
10962 : {
10963 : MInstruction* get;
10964 0 : if (jitinfo->isAlwaysInSlot) {
10965 : // If our object is a singleton and we know the property is
10966 : // constant (which is true if and only if the get doesn't alias
10967 : // anything), we can just read the slot here and use that
10968 : // constant.
10969 0 : JSObject* singleton = objTypes->maybeSingleton();
10970 0 : if (singleton && jitinfo->aliasSet() == JSJitInfo::AliasNone) {
10971 0 : size_t slot = jitinfo->slotIndex;
10972 0 : *emitted = true;
10973 0 : pushConstant(GetReservedSlot(singleton, slot));
10974 0 : return Ok();
10975 : }
10976 :
10977 : // We can't use MLoadFixedSlot here because it might not have
10978 : // the right aliasing behavior; we want to alias DOM setters as
10979 : // needed.
10980 0 : get = MGetDOMMember::New(alloc(), jitinfo, obj, guard, globalGuard);
10981 : } else {
10982 0 : get = MGetDOMProperty::New(alloc(), jitinfo, objKind, obj, guard, globalGuard);
10983 : }
10984 0 : if (!get)
10985 0 : return abort(AbortReason::Alloc);
10986 0 : current->add(get);
10987 0 : current->push(get);
10988 :
10989 0 : if (get->isEffectful())
10990 0 : MOZ_TRY(resumeAfter(get));
10991 :
10992 0 : MOZ_TRY(pushDOMTypeBarrier(get, types, commonGetter));
10993 :
10994 0 : trackOptimizationOutcome(TrackedOutcome::DOM);
10995 0 : *emitted = true;
10996 0 : return Ok();
10997 : }
10998 : }
10999 :
11000 : // Don't call the getter with a primitive value.
11001 1 : if (obj->type() != MIRType::Object) {
11002 0 : MGuardObject* guardObj = MGuardObject::New(alloc(), obj);
11003 0 : current->add(guardObj);
11004 0 : obj = guardObj;
11005 : }
11006 :
11007 : // Spoof stack to expected state for call.
11008 :
11009 : // Make sure there's enough room
11010 0 : if (!current->ensureHasSlots(2))
11011 0 : return abort(AbortReason::Alloc);
11012 0 : current->push(constant(ObjectValue(*commonGetter)));
11013 :
11014 2 : current->push(obj);
11015 :
11016 : CallInfo callInfo(alloc(), pc, /* constructing = */ false,
11017 1 : /* ignoresReturnValue = */ BytecodeIsPopped(pc));
11018 2 : if (!callInfo.init(current, 0))
11019 0 : return abort(AbortReason::Alloc);
11020 :
11021 0 : if (commonGetter->isNative()) {
11022 : InliningStatus status;
11023 4 : MOZ_TRY_VAR(status, inlineNativeGetter(callInfo, commonGetter));
11024 2 : switch (status) {
11025 : case InliningStatus_WarmUpCountTooLow:
11026 : case InliningStatus_NotInlined:
11027 : break;
11028 : case InliningStatus_Inlined:
11029 0 : trackOptimizationOutcome(TrackedOutcome::Inlined);
11030 0 : *emitted = true;
11031 0 : return Ok();
11032 : }
11033 : }
11034 :
11035 : // Inline if we can, otherwise, forget it and just generate a call.
11036 0 : if (commonGetter->isInterpreted()) {
11037 0 : InliningDecision decision = makeInliningDecision(commonGetter, callInfo);
11038 0 : switch (decision) {
11039 : case InliningDecision_Error:
11040 0 : return abort(AbortReason::Error);
11041 : case InliningDecision_DontInline:
11042 : case InliningDecision_WarmUpCountTooLow:
11043 : break;
11044 : case InliningDecision_Inline: {
11045 : InliningStatus status;
11046 0 : MOZ_TRY_VAR(status, inlineScriptedCall(callInfo, commonGetter));
11047 0 : if (status == InliningStatus_Inlined) {
11048 0 : *emitted = true;
11049 0 : return Ok();
11050 : }
11051 : break;
11052 : }
11053 : }
11054 : }
11055 :
11056 6 : MOZ_TRY(makeCall(commonGetter, callInfo));
11057 :
11058 : // If the getter could have been inlined, don't track success. The call to
11059 : // makeInliningDecision above would have tracked a specific reason why we
11060 : // couldn't inline.
11061 4 : if (!commonGetter->isInterpreted())
11062 : trackOptimizationSuccess();
11063 :
11064 2 : *emitted = true;
11065 2 : return Ok();
11066 : }
11067 :
11068 : bool
11069 0 : IonBuilder::canInlinePropertyOpShapes(const BaselineInspector::ReceiverVector& receivers)
11070 : {
11071 966 : if (receivers.empty()) {
11072 : trackOptimizationOutcome(TrackedOutcome::NoShapeInfo);
11073 : return false;
11074 : }
11075 :
11076 317 : for (size_t i = 0; i < receivers.length(); i++) {
11077 : // We inline the property access as long as the shape is not in
11078 : // dictionary mode. We cannot be sure that the shape is still a
11079 : // lastProperty, and calling Shape::search() on dictionary mode
11080 : // shapes that aren't lastProperty is invalid.
11081 220 : if (receivers[i].shape && receivers[i].shape->inDictionary()) {
11082 : trackOptimizationOutcome(TrackedOutcome::InDictionaryMode);
11083 : return false;
11084 : }
11085 : }
11086 :
11087 : return true;
11088 : }
11089 :
11090 : static Shape*
11091 0 : PropertyShapesHaveSameSlot(const BaselineInspector::ReceiverVector& receivers, jsid id)
11092 : {
11093 0 : Shape* firstShape = nullptr;
11094 25 : for (size_t i = 0; i < receivers.length(); i++) {
11095 25 : if (receivers[i].group)
11096 : return nullptr;
11097 :
11098 42 : Shape* shape = receivers[i].shape->searchLinear(id);
11099 0 : MOZ_ASSERT(shape);
11100 :
11101 0 : if (i == 0) {
11102 : firstShape = shape;
11103 16 : } else if (shape->slot() != firstShape->slot() ||
11104 14 : shape->numFixedSlots() != firstShape->numFixedSlots())
11105 : {
11106 : return nullptr;
11107 : }
11108 : }
11109 :
11110 : return firstShape;
11111 : }
11112 :
11113 : AbortReasonOr<Ok>
11114 469 : IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name,
11115 : BarrierKind barrier, TemporaryTypeSet* types)
11116 : {
11117 0 : MOZ_ASSERT(*emitted == false);
11118 :
11119 0 : BaselineInspector::ReceiverVector receivers(alloc());
11120 1 : BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
11121 469 : if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
11122 0 : return abort(AbortReason::Alloc);
11123 :
11124 469 : if (!canInlinePropertyOpShapes(receivers))
11125 0 : return Ok();
11126 :
11127 0 : obj = convertUnboxedObjects(obj, convertUnboxedGroups);
11128 :
11129 0 : MIRType rvalType = types->getKnownMIRType();
11130 103 : if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
11131 0 : rvalType = MIRType::Value;
11132 :
11133 55 : if (receivers.length() == 1) {
11134 0 : if (!receivers[0].group) {
11135 : // Monomorphic load from a native object.
11136 0 : spew("Inlining monomorphic native GETPROP");
11137 :
11138 0 : obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
11139 :
11140 86 : Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
11141 0 : MOZ_ASSERT(shape);
11142 :
11143 0 : MOZ_TRY(loadSlot(obj, shape, rvalType, barrier, types));
11144 :
11145 0 : trackOptimizationOutcome(TrackedOutcome::Monomorphic);
11146 43 : *emitted = true;
11147 43 : return Ok();
11148 : }
11149 :
11150 0 : if (receivers[0].shape) {
11151 : // Monomorphic load from an unboxed object expando.
11152 0 : spew("Inlining monomorphic unboxed expando GETPROP");
11153 :
11154 0 : obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
11155 0 : obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard);
11156 :
11157 0 : MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
11158 0 : current->add(expando);
11159 :
11160 0 : expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
11161 :
11162 0 : Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
11163 0 : MOZ_ASSERT(shape);
11164 :
11165 0 : MOZ_TRY(loadSlot(expando, shape, rvalType, barrier, types));
11166 :
11167 0 : trackOptimizationOutcome(TrackedOutcome::Monomorphic);
11168 0 : *emitted = true;
11169 0 : return Ok();
11170 : }
11171 :
11172 : // Monomorphic load from an unboxed object.
11173 0 : ObjectGroup* group = receivers[0].group;
11174 0 : if (obj->resultTypeSet() && !obj->resultTypeSet()->hasType(TypeSet::ObjectType(group)))
11175 0 : return Ok();
11176 :
11177 0 : obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
11178 :
11179 0 : AutoSweepObjectGroup sweep(group);
11180 0 : const UnboxedLayout::Property* property = group->unboxedLayout(sweep).lookup(name);
11181 0 : MInstruction* load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types);
11182 0 : current->push(load);
11183 :
11184 0 : MOZ_TRY(pushTypeBarrier(load, types, barrier));
11185 :
11186 0 : trackOptimizationOutcome(TrackedOutcome::Monomorphic);
11187 0 : *emitted = true;
11188 0 : return Ok();
11189 : }
11190 :
11191 12 : MOZ_ASSERT(receivers.length() > 1);
11192 0 : spew("Inlining polymorphic GETPROP");
11193 :
11194 0 : if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) {
11195 0 : obj = addGuardReceiverPolymorphic(obj, receivers);
11196 0 : if (!obj)
11197 0 : return abort(AbortReason::Alloc);
11198 :
11199 0 : MOZ_TRY(loadSlot(obj, propShape, rvalType, barrier, types));
11200 :
11201 0 : trackOptimizationOutcome(TrackedOutcome::Polymorphic);
11202 0 : *emitted = true;
11203 0 : return Ok();
11204 : }
11205 :
11206 0 : MGetPropertyPolymorphic* load = MGetPropertyPolymorphic::New(alloc(), obj, name);
11207 12 : current->add(load);
11208 0 : current->push(load);
11209 :
11210 0 : for (size_t i = 0; i < receivers.length(); i++) {
11211 0 : Shape* propShape = nullptr;
11212 0 : if (receivers[i].shape) {
11213 48 : propShape = receivers[i].shape->searchLinear(NameToId(name));
11214 0 : MOZ_ASSERT(propShape);
11215 : }
11216 56 : if (!load->addReceiver(receivers[i], propShape))
11217 0 : return abort(AbortReason::Alloc);
11218 : }
11219 :
11220 12 : if (failedShapeGuard_)
11221 0 : load->setNotMovable();
11222 :
11223 24 : load->setResultType(rvalType);
11224 0 : MOZ_TRY(pushTypeBarrier(load, types, barrier));
11225 :
11226 0 : trackOptimizationOutcome(TrackedOutcome::Polymorphic);
11227 12 : *emitted = true;
11228 12 : return Ok();
11229 : }
11230 :
11231 : AbortReasonOr<Ok>
11232 414 : IonBuilder::getPropTryInlineProtoAccess(bool* emitted, MDefinition* obj, PropertyName* name,
11233 : TemporaryTypeSet* types)
11234 : {
11235 0 : MOZ_ASSERT(*emitted == false);
11236 :
11237 0 : BaselineInspector::ReceiverVector receivers(alloc());
11238 0 : BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
11239 1 : JSObject* holder = nullptr;
11240 414 : if (!inspector->maybeInfoForProtoReadSlot(pc, receivers, convertUnboxedGroups, &holder))
11241 0 : return abort(AbortReason::Alloc);
11242 :
11243 414 : if (!canInlinePropertyOpShapes(receivers))
11244 0 : return Ok();
11245 :
11246 37 : MOZ_ASSERT(holder);
11247 37 : holder = checkNurseryObject(holder);
11248 :
11249 : BarrierKind barrier;
11250 0 : MOZ_TRY_VAR(barrier, PropertyReadOnPrototypeNeedsTypeBarrier(this, obj, name, types));
11251 :
11252 0 : MIRType rvalType = types->getKnownMIRType();
11253 67 : if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
11254 7 : rvalType = MIRType::Value;
11255 :
11256 : // Guard on the receiver shapes/groups.
11257 0 : obj = convertUnboxedObjects(obj, convertUnboxedGroups);
11258 1 : obj = addGuardReceiverPolymorphic(obj, receivers);
11259 37 : if (!obj)
11260 0 : return abort(AbortReason::Alloc);
11261 :
11262 : // Guard on the holder's shape.
11263 0 : MInstruction* holderDef = constant(ObjectValue(*holder));
11264 74 : Shape* holderShape = holder->as<NativeObject>().shape();
11265 0 : holderDef = addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard);
11266 :
11267 74 : Shape* propShape = holderShape->searchLinear(NameToId(name));
11268 0 : MOZ_ASSERT(propShape);
11269 :
11270 0 : MOZ_TRY(loadSlot(holderDef, propShape, rvalType, barrier, types));
11271 :
11272 0 : trackOptimizationSuccess();
11273 37 : *emitted = true;
11274 37 : return Ok();
11275 : }
11276 :
11277 : AbortReasonOr<Ok>
11278 377 : IonBuilder::getPropAddCache(MDefinition* obj, PropertyName* name,
11279 : BarrierKind barrier, TemporaryTypeSet* types)
11280 : {
11281 : // PropertyReadNeedsTypeBarrier only accounts for object types, so for now
11282 : // always insert a barrier if the input is not known to be an object.
11283 377 : if (obj->type() != MIRType::Object)
11284 28 : barrier = BarrierKind::TypeSet;
11285 :
11286 : // Since getters have no guaranteed return values, we must barrier in order to be
11287 : // able to attach stubs for them.
11288 377 : if (inspector->hasSeenAccessedGetter(pc))
11289 7 : barrier = BarrierKind::TypeSet;
11290 :
11291 : // Caches can read values from prototypes, so update the barrier to
11292 : // reflect such possible values.
11293 0 : if (barrier != BarrierKind::TypeSet) {
11294 : BarrierKind protoBarrier;
11295 0 : MOZ_TRY_VAR(protoBarrier, PropertyReadOnPrototypeNeedsTypeBarrier(this, obj, name, types));
11296 12 : if (protoBarrier != BarrierKind::NoBarrier) {
11297 0 : MOZ_ASSERT(barrier <= protoBarrier);
11298 : barrier = protoBarrier;
11299 : }
11300 : }
11301 :
11302 : // Ensure we insert a type barrier for reads from typed objects, as type
11303 : // information does not account for the initial undefined/null types.
11304 0 : if (barrier != BarrierKind::TypeSet && !types->unknown()) {
11305 12 : MOZ_ASSERT(obj->resultTypeSet());
11306 12 : switch (obj->resultTypeSet()->forAllClasses(constraints(), IsTypedObjectClass)) {
11307 : case TemporaryTypeSet::ForAllResult::ALL_FALSE:
11308 : case TemporaryTypeSet::ForAllResult::EMPTY:
11309 : break;
11310 : case TemporaryTypeSet::ForAllResult::ALL_TRUE:
11311 : case TemporaryTypeSet::ForAllResult::MIXED:
11312 0 : barrier = BarrierKind::TypeSet;
11313 0 : break;
11314 : }
11315 : }
11316 :
11317 0 : MConstant* id = constant(StringValue(name));
11318 377 : MGetPropertyCache* load = MGetPropertyCache::New(alloc(), obj, id,
11319 1131 : barrier == BarrierKind::TypeSet);
11320 :
11321 : // Try to mark the cache as idempotent.
11322 726 : if (obj->type() == MIRType::Object && !invalidatedIdempotentCache()) {
11323 349 : if (PropertyReadIsIdempotent(constraints(), obj, name))
11324 : load->setIdempotent();
11325 : }
11326 :
11327 : // When we are in the context of making a call from the value returned from
11328 : // a property, we query the typeObject for the given property name to fill
11329 : // the InlinePropertyTable of the GetPropertyCache. This information is
11330 : // then used in inlineCallsite and inlineCalls, if the "this" definition is
11331 : // matching the "object" definition of the GetPropertyCache (see
11332 : // CanInlineGetPropertyCache).
11333 : //
11334 : // If this GetPropertyCache is idempotent, then we can dispatch to the right
11335 : // function only by checking the typed object, instead of querying the value
11336 : // of the property. Thus this GetPropertyCache can be moved into the
11337 : // fallback path (see inlineObjectGroupFallback). Otherwise, we always have
11338 : // to do the GetPropertyCache, and we can dispatch based on the JSFunction
11339 : // value.
11340 504 : if (JSOp(*pc) == JSOP_CALLPROP && load->idempotent())
11341 0 : MOZ_TRY(annotateGetPropertyCache(obj, name, load, obj->resultTypeSet(), types));
11342 :
11343 377 : current->add(load);
11344 0 : current->push(load);
11345 :
11346 754 : if (load->isEffectful())
11347 0 : MOZ_TRY(resumeAfter(load));
11348 :
11349 377 : MIRType rvalType = types->getKnownMIRType();
11350 377 : if (barrier != BarrierKind::NoBarrier) {
11351 : rvalType = MIRType::Value;
11352 : } else {
11353 0 : load->setResultTypeSet(types);
11354 12 : if (IsNullOrUndefined(rvalType))
11355 0 : rvalType = MIRType::Value;
11356 : }
11357 0 : load->setResultType(rvalType);
11358 :
11359 504 : if (*pc != JSOP_CALLPROP || !IsNullOrUndefined(obj->type())) {
11360 : // Due to inlining, it's possible the observed TypeSet is non-empty,
11361 : // even though we know |obj| is null/undefined and the MCallGetProperty
11362 : // will throw. Don't push a TypeBarrier in this case, to avoid
11363 : // inlining the following (unreachable) JSOP_CALL.
11364 1131 : MOZ_TRY(pushTypeBarrier(load, types, barrier));
11365 : }
11366 :
11367 377 : trackOptimizationSuccess();
11368 377 : return Ok();
11369 : }
11370 :
11371 : MDefinition*
11372 740 : IonBuilder::tryInnerizeWindow(MDefinition* obj)
11373 : {
11374 : // Try to optimize accesses on outer window proxies (window.foo, for
11375 : // example) to go directly to the inner window, the global.
11376 : //
11377 : // Callers should be careful not to pass the inner object to getters or
11378 : // setters that require outerization.
11379 :
11380 740 : if (obj->type() != MIRType::Object)
11381 : return obj;
11382 :
11383 708 : TemporaryTypeSet* types = obj->resultTypeSet();
11384 708 : if (!types)
11385 : return obj;
11386 :
11387 708 : JSObject* singleton = types->maybeSingleton();
11388 708 : if (!singleton)
11389 : return obj;
11390 :
11391 18 : if (!IsWindowProxy(singleton))
11392 : return obj;
11393 :
11394 : // This must be a WindowProxy for the current Window/global. Else it'd be
11395 : // a cross-compartment wrapper and IsWindowProxy returns false for those.
11396 0 : MOZ_ASSERT(ToWindowIfWindowProxy(singleton) == &script()->global());
11397 :
11398 : // When we navigate, the WindowProxy is brain transplanted and we'll mark
11399 : // its ObjectGroup as having unknown properties. The type constraint we add
11400 : // here will invalidate JIT code when this happens.
11401 0 : TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(singleton);
11402 0 : if (key->hasFlags(constraints(), OBJECT_FLAG_UNKNOWN_PROPERTIES))
11403 : return obj;
11404 :
11405 0 : obj->setImplicitlyUsedUnchecked();
11406 0 : return constant(ObjectValue(script()->global()));
11407 : }
11408 :
11409 : AbortReasonOr<Ok>
11410 740 : IonBuilder::getPropTryInnerize(bool* emitted, MDefinition* obj, PropertyName* name,
11411 : TemporaryTypeSet* types)
11412 : {
11413 : // See the comment in tryInnerizeWindow for how this works.
11414 :
11415 : // Note that it's important that we do this _before_ we'd try to
11416 : // do the optimizations below on obj normally, since some of those
11417 : // optimizations have fallback paths that are slower than the path
11418 : // we'd produce here.
11419 :
11420 0 : MOZ_ASSERT(*emitted == false);
11421 :
11422 0 : MDefinition* inner = tryInnerizeWindow(obj);
11423 740 : if (inner == obj)
11424 0 : return Ok();
11425 :
11426 0 : if (!forceInlineCaches()) {
11427 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_Constant);
11428 0 : MOZ_TRY(getPropTryConstant(emitted, inner, NameToId(name), types));
11429 0 : if (*emitted)
11430 0 : return Ok();
11431 :
11432 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_StaticName);
11433 0 : MOZ_TRY(getStaticName(emitted, &script()->global(), name));
11434 0 : if (*emitted)
11435 0 : return Ok();
11436 :
11437 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
11438 0 : MOZ_TRY(getPropTryCommonGetter(emitted, inner, name, types, /* innerized = */true));
11439 0 : if (*emitted)
11440 0 : return Ok();
11441 : }
11442 :
11443 : // Passing the inner object to GetProperty IC is safe, see the
11444 : // needsOuterizedThisObject check in IsCacheableGetPropCallNative.
11445 0 : BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
11446 0 : inner, name, types);
11447 0 : trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
11448 0 : MOZ_TRY(getPropAddCache(inner, name, barrier, types));
11449 :
11450 0 : *emitted = true;
11451 0 : return Ok();
11452 : }
11453 :
11454 : AbortReasonOr<Ok>
11455 0 : IonBuilder::jsop_setprop(PropertyName* name)
11456 : {
11457 736 : MDefinition* value = current->pop();
11458 0 : MDefinition* obj = convertUnboxedObjects(current->pop());
11459 :
11460 0 : bool emitted = false;
11461 0 : startTrackingOptimizations();
11462 1472 : trackTypeInfo(TrackedTypeSite::Receiver, obj->type(), obj->resultTypeSet());
11463 1472 : trackTypeInfo(TrackedTypeSite::Value, value->type(), value->resultTypeSet());
11464 :
11465 : // Always use a call if we are doing the definite properties analysis and
11466 : // not actually emitting code, to simplify later analysis.
11467 0 : if (info().isAnalysis() || shouldAbortOnPreliminaryGroups(obj)) {
11468 0 : bool strict = IsStrictSetPC(pc);
11469 0 : MInstruction* ins = MCallSetProperty::New(alloc(), obj, value, name, strict);
11470 0 : current->add(ins);
11471 59 : current->push(value);
11472 59 : return resumeAfter(ins);
11473 : }
11474 :
11475 0 : if (!forceInlineCaches()) {
11476 : // Try to inline a common property setter, or make a call.
11477 0 : trackOptimizationAttempt(TrackedStrategy::SetProp_CommonSetter);
11478 0 : MOZ_TRY(setPropTryCommonSetter(&emitted, obj, name, value));
11479 677 : if (emitted)
11480 0 : return Ok();
11481 :
11482 : // Try to emit stores to known binary data blocks
11483 0 : trackOptimizationAttempt(TrackedStrategy::SetProp_TypedObject);
11484 0 : MOZ_TRY(setPropTryTypedObject(&emitted, obj, name, value));
11485 677 : if (emitted)
11486 0 : return Ok();
11487 : }
11488 :
11489 0 : TemporaryTypeSet* objTypes = obj->resultTypeSet();
11490 677 : bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
11491 0 : /* canModify = */ true);
11492 :
11493 0 : if (!forceInlineCaches()) {
11494 : // Try to emit stores to unboxed objects.
11495 0 : trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed);
11496 0 : MOZ_TRY(setPropTryUnboxed(&emitted, obj, name, value, barrier));
11497 677 : if (emitted)
11498 32 : return Ok();
11499 : }
11500 :
11501 0 : if (!forceInlineCaches()) {
11502 : // Try to emit store from definite slots.
11503 0 : trackOptimizationAttempt(TrackedStrategy::SetProp_DefiniteSlot);
11504 0 : MOZ_TRY(setPropTryDefiniteSlot(&emitted, obj, name, value, barrier));
11505 645 : if (emitted)
11506 561 : return Ok();
11507 :
11508 : // Try to emit a monomorphic/polymorphic store based on baseline caches.
11509 0 : trackOptimizationAttempt(TrackedStrategy::SetProp_InlineAccess);
11510 0 : MOZ_TRY(setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes));
11511 84 : if (emitted)
11512 1 : return Ok();
11513 : }
11514 :
11515 : // Emit a polymorphic cache.
11516 0 : trackOptimizationAttempt(TrackedStrategy::SetProp_InlineCache);
11517 0 : MOZ_TRY(setPropTryCache(&emitted, obj, name, value, barrier));
11518 83 : MOZ_ASSERT(emitted == true);
11519 83 : return Ok();
11520 : }
11521 :
11522 : AbortReasonOr<Ok>
11523 677 : IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj,
11524 : PropertyName* name, MDefinition* value)
11525 : {
11526 0 : MOZ_ASSERT(*emitted == false);
11527 :
11528 0 : TemporaryTypeSet* objTypes = obj->resultTypeSet();
11529 677 : JSFunction* commonSetter = nullptr;
11530 677 : MDefinition* guard = nullptr;
11531 :
11532 : {
11533 677 : Shape* lastProperty = nullptr;
11534 0 : JSObject* foundProto = nullptr;
11535 : bool isOwnProperty;
11536 0 : BaselineInspector::ReceiverVector receivers(alloc());
11537 1354 : BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
11538 677 : if (inspector->commonSetPropFunction(pc, &foundProto, &lastProperty, &commonSetter,
11539 : &isOwnProperty,
11540 : receivers, convertUnboxedGroups))
11541 : {
11542 0 : bool canUseTIForSetter = false;
11543 0 : if (!isOwnProperty) {
11544 : // If it's not an own property, try to use TI to avoid shape guards.
11545 : // For own properties we use the path below.
11546 0 : canUseTIForSetter = testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
11547 0 : commonSetter, &guard);
11548 : }
11549 0 : if (!canUseTIForSetter) {
11550 : // If it's an own property or type information is bad, we can still
11551 : // optimize the setter if we shape guard.
11552 0 : obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
11553 : receivers, convertUnboxedGroups,
11554 : isOwnProperty);
11555 0 : if (!obj)
11556 0 : return abort(AbortReason::Alloc);
11557 : }
11558 677 : } else if (inspector->megamorphicGetterSetterFunction(pc, /* isGetter = */ false,
11559 : &commonSetter))
11560 : {
11561 : // Try to use TI to guard on this setter.
11562 0 : if (!testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
11563 : commonSetter, &guard))
11564 : {
11565 0 : return Ok();
11566 : }
11567 : } else {
11568 : // The Baseline IC didn't have any information we can use.
11569 677 : return Ok();
11570 : }
11571 : }
11572 :
11573 : // Emit common setter.
11574 :
11575 : // Setters can be called even if the property write needs a type
11576 : // barrier, as calling the setter does not actually write any data
11577 : // properties.
11578 :
11579 : // Try emitting dom call.
11580 0 : MOZ_TRY(setPropTryCommonDOMSetter(emitted, obj, value, commonSetter, objTypes));
11581 0 : if (*emitted) {
11582 0 : trackOptimizationOutcome(TrackedOutcome::DOM);
11583 0 : return Ok();
11584 : }
11585 :
11586 : // Don't call the setter with a primitive value.
11587 0 : if (obj->type() != MIRType::Object) {
11588 0 : MGuardObject* guardObj = MGuardObject::New(alloc(), obj);
11589 0 : current->add(guardObj);
11590 0 : obj = guardObj;
11591 : }
11592 :
11593 : // Dummy up the stack, as in getprop. We are pushing an extra value, so
11594 : // ensure there is enough space.
11595 0 : if (!current->ensureHasSlots(3))
11596 0 : return abort(AbortReason::Alloc);
11597 :
11598 0 : current->push(constant(ObjectValue(*commonSetter)));
11599 0 : current->push(obj);
11600 0 : current->push(value);
11601 :
11602 : // Call the setter. Note that we have to push the original value, not
11603 : // the setter's return value.
11604 : CallInfo callInfo(alloc(), pc, /* constructing = */ false,
11605 0 : /* ignoresReturnValue = */ BytecodeIsPopped(pc));
11606 0 : if (!callInfo.init(current, 1))
11607 0 : return abort(AbortReason::Alloc);
11608 :
11609 : // Ensure that we know we are calling a setter in case we inline it.
11610 0 : callInfo.markAsSetter();
11611 :
11612 : // Inline the setter if we can.
11613 0 : if (commonSetter->isInterpreted()) {
11614 0 : InliningDecision decision = makeInliningDecision(commonSetter, callInfo);
11615 0 : switch (decision) {
11616 : case InliningDecision_Error:
11617 0 : return abort(AbortReason::Error);
11618 : case InliningDecision_DontInline:
11619 : case InliningDecision_WarmUpCountTooLow:
11620 : break;
11621 : case InliningDecision_Inline: {
11622 : InliningStatus status;
11623 0 : MOZ_TRY_VAR(status, inlineScriptedCall(callInfo, commonSetter));
11624 0 : if (status == InliningStatus_Inlined) {
11625 0 : *emitted = true;
11626 0 : return Ok();
11627 : }
11628 : }
11629 : }
11630 : }
11631 :
11632 0 : Maybe<CallTargets> targets;
11633 0 : targets.emplace(alloc());
11634 0 : if (!targets->append(commonSetter))
11635 0 : return abort(AbortReason::Alloc);
11636 : MCall* call;
11637 0 : MOZ_TRY_VAR(call, makeCallHelper(targets, callInfo));
11638 :
11639 0 : current->push(value);
11640 0 : MOZ_TRY(resumeAfter(call));
11641 :
11642 : // If the setter could have been inlined, don't track success. The call to
11643 : // makeInliningDecision above would have tracked a specific reason why we
11644 : // couldn't inline.
11645 0 : if (!commonSetter->isInterpreted())
11646 : trackOptimizationSuccess();
11647 :
11648 0 : *emitted = true;
11649 0 : return Ok();
11650 : }
11651 :
11652 : AbortReasonOr<Ok>
11653 0 : IonBuilder::setPropTryCommonDOMSetter(bool* emitted, MDefinition* obj,
11654 : MDefinition* value, JSFunction* setter,
11655 : TemporaryTypeSet* objTypes)
11656 : {
11657 0 : MOZ_ASSERT(*emitted == false);
11658 :
11659 0 : DOMObjectKind objKind = DOMObjectKind::Unknown;
11660 0 : if (!objTypes || !objTypes->isDOMClass(constraints(), &objKind))
11661 0 : return Ok();
11662 :
11663 0 : bool isDOM = false;
11664 0 : MOZ_TRY_VAR(isDOM, testShouldDOMCall(objTypes, setter, JSJitInfo::Setter));
11665 0 : if (!isDOM)
11666 0 : return Ok();
11667 :
11668 : // Emit SetDOMProperty.
11669 0 : MOZ_ASSERT(setter->jitInfo()->type() == JSJitInfo::Setter);
11670 0 : MSetDOMProperty* set = MSetDOMProperty::New(alloc(), setter->jitInfo()->setter, objKind,
11671 0 : obj, value);
11672 :
11673 0 : current->add(set);
11674 0 : current->push(value);
11675 :
11676 0 : MOZ_TRY(resumeAfter(set));
11677 :
11678 0 : *emitted = true;
11679 0 : return Ok();
11680 : }
11681 :
11682 : AbortReasonOr<Ok>
11683 677 : IonBuilder::setPropTryTypedObject(bool* emitted, MDefinition* obj,
11684 : PropertyName* name, MDefinition* value)
11685 : {
11686 677 : TypedObjectPrediction fieldPrediction;
11687 : size_t fieldOffset;
11688 : size_t fieldIndex;
11689 677 : if (!typedObjectHasField(obj, name, &fieldOffset, &fieldPrediction, &fieldIndex))
11690 0 : return Ok();
11691 :
11692 0 : switch (fieldPrediction.kind()) {
11693 : case type::Simd:
11694 : // FIXME (bug 894104): store into a MIRType::float32x4 etc
11695 0 : return Ok();
11696 :
11697 : case type::Reference:
11698 : return setPropTryReferencePropOfTypedObject(emitted, obj, fieldOffset,
11699 0 : value, fieldPrediction, name);
11700 :
11701 : case type::Scalar:
11702 : return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset,
11703 0 : value, fieldPrediction);
11704 :
11705 : case type::Struct:
11706 : case type::Array:
11707 0 : return Ok();
11708 : }
11709 :
11710 0 : MOZ_CRASH("Unknown kind");
11711 : }
11712 :
11713 : AbortReasonOr<Ok>
11714 0 : IonBuilder::setPropTryReferencePropOfTypedObject(bool* emitted,
11715 : MDefinition* obj,
11716 : int32_t fieldOffset,
11717 : MDefinition* value,
11718 : TypedObjectPrediction fieldPrediction,
11719 : PropertyName* name)
11720 : {
11721 0 : ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
11722 :
11723 0 : TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
11724 0 : if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
11725 0 : return Ok();
11726 :
11727 0 : LinearSum byteOffset(alloc());
11728 0 : if (!byteOffset.add(fieldOffset))
11729 0 : return abort(AbortReason::Disable, "Overflow of field offset.");
11730 :
11731 0 : return setPropTryReferenceTypedObjectValue(emitted, obj, byteOffset, fieldType, value, name);
11732 : }
11733 :
11734 : AbortReasonOr<Ok>
11735 0 : IonBuilder::setPropTryScalarPropOfTypedObject(bool* emitted,
11736 : MDefinition* obj,
11737 : int32_t fieldOffset,
11738 : MDefinition* value,
11739 : TypedObjectPrediction fieldPrediction)
11740 : {
11741 : // Must always be loading the same scalar type
11742 0 : Scalar::Type fieldType = fieldPrediction.scalarType();
11743 :
11744 : // Don't optimize if the typed object's underlying buffer may be detached.
11745 0 : TypeSet::ObjectKey* globalKey = TypeSet::ObjectKey::get(&script()->global());
11746 0 : if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER))
11747 0 : return Ok();
11748 :
11749 0 : LinearSum byteOffset(alloc());
11750 0 : if (!byteOffset.add(fieldOffset))
11751 0 : return abort(AbortReason::Disable, "Overflow of field offet.");
11752 :
11753 0 : return setPropTryScalarTypedObjectValue(emitted, obj, byteOffset, fieldType, value);
11754 : }
11755 :
11756 : AbortReasonOr<Ok>
11757 645 : IonBuilder::setPropTryDefiniteSlot(bool* emitted, MDefinition* obj,
11758 : PropertyName* name, MDefinition* value,
11759 : bool barrier)
11760 : {
11761 0 : MOZ_ASSERT(*emitted == false);
11762 :
11763 0 : if (barrier) {
11764 1 : trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
11765 1 : return Ok();
11766 : }
11767 :
11768 : uint32_t nfixed;
11769 0 : uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), NameToId(name), &nfixed);
11770 644 : if (slot == UINT32_MAX)
11771 83 : return Ok();
11772 :
11773 : bool writeBarrier = false;
11774 0 : for (size_t i = 0; i < obj->resultTypeSet()->getObjectCount(); i++) {
11775 1 : TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i);
11776 569 : if (!key)
11777 0 : continue;
11778 :
11779 0 : HeapTypeSetKey property = key->property(NameToId(name));
11780 0 : if (property.nonWritable(constraints())) {
11781 0 : trackOptimizationOutcome(TrackedOutcome::NonWritableProperty);
11782 0 : return Ok();
11783 : }
11784 569 : writeBarrier |= property.needsBarrier(constraints());
11785 : }
11786 :
11787 561 : if (needsPostBarrier(value))
11788 150 : current->add(MPostWriteBarrier::New(alloc(), obj, value));
11789 :
11790 : MInstruction* store;
11791 0 : if (slot < nfixed) {
11792 0 : store = MStoreFixedSlot::New(alloc(), obj, slot, value);
11793 561 : if (writeBarrier)
11794 0 : store->toStoreFixedSlot()->setNeedsBarrier();
11795 : } else {
11796 0 : MInstruction* slots = MSlots::New(alloc(), obj);
11797 0 : current->add(slots);
11798 :
11799 0 : store = MStoreSlot::New(alloc(), slots, slot - nfixed, value);
11800 0 : if (writeBarrier)
11801 0 : store->toStoreSlot()->setNeedsBarrier();
11802 : }
11803 :
11804 561 : current->add(store);
11805 0 : current->push(value);
11806 :
11807 0 : MOZ_TRY(resumeAfter(store));
11808 :
11809 0 : trackOptimizationSuccess();
11810 561 : *emitted = true;
11811 561 : return Ok();
11812 : }
11813 :
11814 : MInstruction*
11815 32 : IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
11816 : MDefinition* value)
11817 : {
11818 0 : size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType);
11819 64 : MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant));
11820 32 : current->add(scaledOffset);
11821 :
11822 : return storeUnboxedValue(obj, obj, UnboxedPlainObject::offsetOfData(),
11823 32 : scaledOffset, unboxedType, value);
11824 : }
11825 :
11826 : MInstruction*
11827 32 : IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset,
11828 : MDefinition* scaledOffset, JSValueType unboxedType,
11829 : MDefinition* value, bool preBarrier /* = true */)
11830 : {
11831 : MInstruction* store;
11832 0 : switch (unboxedType) {
11833 : case JSVAL_TYPE_BOOLEAN:
11834 0 : store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8,
11835 : MStoreUnboxedScalar::DontTruncateInput,
11836 0 : DoesNotRequireMemoryBarrier, elementsOffset);
11837 0 : break;
11838 :
11839 : case JSVAL_TYPE_INT32:
11840 0 : store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32,
11841 : MStoreUnboxedScalar::DontTruncateInput,
11842 0 : DoesNotRequireMemoryBarrier, elementsOffset);
11843 0 : break;
11844 :
11845 : case JSVAL_TYPE_DOUBLE:
11846 0 : store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64,
11847 : MStoreUnboxedScalar::DontTruncateInput,
11848 0 : DoesNotRequireMemoryBarrier, elementsOffset);
11849 0 : break;
11850 :
11851 : case JSVAL_TYPE_STRING:
11852 0 : store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, obj,
11853 64 : elementsOffset, preBarrier);
11854 32 : break;
11855 :
11856 : case JSVAL_TYPE_OBJECT:
11857 0 : MOZ_ASSERT(value->type() == MIRType::Object ||
11858 : value->type() == MIRType::Null ||
11859 : value->type() == MIRType::Value);
11860 0 : MOZ_ASSERT(!value->mightBeType(MIRType::Undefined),
11861 : "MToObjectOrNull slow path is invalid for unboxed objects");
11862 0 : store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj,
11863 0 : elementsOffset, preBarrier);
11864 0 : break;
11865 :
11866 : default:
11867 0 : MOZ_CRASH();
11868 : }
11869 :
11870 32 : current->add(store);
11871 32 : return store;
11872 : }
11873 :
11874 : AbortReasonOr<Ok>
11875 677 : IonBuilder::setPropTryUnboxed(bool* emitted, MDefinition* obj,
11876 : PropertyName* name, MDefinition* value,
11877 : bool barrier)
11878 : {
11879 0 : MOZ_ASSERT(*emitted == false);
11880 :
11881 0 : if (barrier) {
11882 1 : trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
11883 1 : return Ok();
11884 : }
11885 :
11886 : JSValueType unboxedType;
11887 0 : uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), NameToId(name), &unboxedType);
11888 676 : if (offset == UINT32_MAX)
11889 0 : return Ok();
11890 :
11891 1 : if (obj->type() != MIRType::Object) {
11892 0 : MGuardObject* guard = MGuardObject::New(alloc(), obj);
11893 0 : current->add(guard);
11894 0 : obj = guard;
11895 : }
11896 :
11897 0 : MInstruction* store = storeUnboxedProperty(obj, offset, unboxedType, value);
11898 :
11899 0 : current->push(value);
11900 :
11901 0 : MOZ_TRY(resumeAfter(store));
11902 :
11903 32 : *emitted = true;
11904 32 : return Ok();
11905 : }
11906 :
11907 : AbortReasonOr<Ok>
11908 84 : IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj,
11909 : PropertyName* name, MDefinition* value,
11910 : bool barrier, TemporaryTypeSet* objTypes)
11911 : {
11912 0 : MOZ_ASSERT(*emitted == false);
11913 :
11914 0 : if (barrier) {
11915 1 : trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
11916 1 : return Ok();
11917 : }
11918 :
11919 0 : BaselineInspector::ReceiverVector receivers(alloc());
11920 1 : BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
11921 83 : if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
11922 0 : return abort(AbortReason::Alloc);
11923 :
11924 83 : if (!canInlinePropertyOpShapes(receivers))
11925 0 : return Ok();
11926 :
11927 0 : obj = convertUnboxedObjects(obj, convertUnboxedGroups);
11928 :
11929 1 : if (receivers.length() == 1) {
11930 0 : if (!receivers[0].group) {
11931 : // Monomorphic store to a native object.
11932 0 : spew("Inlining monomorphic native SETPROP");
11933 :
11934 0 : obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
11935 :
11936 2 : Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
11937 0 : MOZ_ASSERT(shape);
11938 :
11939 1 : if (needsPostBarrier(value))
11940 0 : current->add(MPostWriteBarrier::New(alloc(), obj, value));
11941 :
11942 1 : bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
11943 0 : MOZ_TRY(storeSlot(obj, shape, value, needsPreBarrier));
11944 :
11945 0 : trackOptimizationOutcome(TrackedOutcome::Monomorphic);
11946 1 : *emitted = true;
11947 1 : return Ok();
11948 : }
11949 :
11950 0 : if (receivers[0].shape) {
11951 : // Monomorphic store to an unboxed object expando.
11952 0 : spew("Inlining monomorphic unboxed expando SETPROP");
11953 :
11954 0 : obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
11955 0 : obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard);
11956 :
11957 0 : MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
11958 0 : current->add(expando);
11959 :
11960 0 : expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
11961 :
11962 0 : Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
11963 0 : MOZ_ASSERT(shape);
11964 :
11965 0 : if (needsPostBarrier(value))
11966 0 : current->add(MPostWriteBarrier::New(alloc(), obj, value));
11967 :
11968 0 : bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
11969 0 : MOZ_TRY(storeSlot(expando, shape, value, needsPreBarrier));
11970 :
11971 0 : trackOptimizationOutcome(TrackedOutcome::Monomorphic);
11972 0 : *emitted = true;
11973 0 : return Ok();
11974 : }
11975 :
11976 : // Monomorphic store to an unboxed object.
11977 0 : spew("Inlining monomorphic unboxed SETPROP");
11978 :
11979 0 : ObjectGroup* group = receivers[0].group;
11980 0 : if (!objTypes->hasType(TypeSet::ObjectType(group)))
11981 0 : return Ok();
11982 :
11983 0 : obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
11984 :
11985 0 : if (needsPostBarrier(value))
11986 0 : current->add(MPostWriteBarrier::New(alloc(), obj, value));
11987 :
11988 0 : AutoSweepObjectGroup sweep(group);
11989 0 : const UnboxedLayout::Property* property = group->unboxedLayout(sweep).lookup(name);
11990 0 : MInstruction* store = storeUnboxedProperty(obj, property->offset, property->type, value);
11991 :
11992 0 : current->push(value);
11993 :
11994 0 : MOZ_TRY(resumeAfter(store));
11995 :
11996 0 : trackOptimizationOutcome(TrackedOutcome::Monomorphic);
11997 0 : *emitted = true;
11998 0 : return Ok();
11999 : }
12000 :
12001 0 : MOZ_ASSERT(receivers.length() > 1);
12002 0 : spew("Inlining polymorphic SETPROP");
12003 :
12004 0 : if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) {
12005 0 : obj = addGuardReceiverPolymorphic(obj, receivers);
12006 0 : if (!obj)
12007 0 : return abort(AbortReason::Alloc);
12008 :
12009 0 : if (needsPostBarrier(value))
12010 0 : current->add(MPostWriteBarrier::New(alloc(), obj, value));
12011 :
12012 0 : bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
12013 0 : MOZ_TRY(storeSlot(obj, propShape, value, needsPreBarrier));
12014 :
12015 0 : trackOptimizationOutcome(TrackedOutcome::Polymorphic);
12016 0 : *emitted = true;
12017 0 : return Ok();
12018 : }
12019 :
12020 0 : if (needsPostBarrier(value))
12021 0 : current->add(MPostWriteBarrier::New(alloc(), obj, value));
12022 :
12023 0 : MSetPropertyPolymorphic* ins = MSetPropertyPolymorphic::New(alloc(), obj, value, name);
12024 0 : current->add(ins);
12025 0 : current->push(value);
12026 :
12027 0 : for (size_t i = 0; i < receivers.length(); i++) {
12028 0 : Shape* propShape = nullptr;
12029 0 : if (receivers[i].shape) {
12030 0 : propShape = receivers[i].shape->searchLinear(NameToId(name));
12031 0 : MOZ_ASSERT(propShape);
12032 : }
12033 0 : if (!ins->addReceiver(receivers[i], propShape))
12034 0 : return abort(AbortReason::Alloc);
12035 : }
12036 :
12037 0 : if (objTypes->propertyNeedsBarrier(constraints(), NameToId(name)))
12038 0 : ins->setNeedsBarrier();
12039 :
12040 0 : MOZ_TRY(resumeAfter(ins));
12041 :
12042 0 : trackOptimizationOutcome(TrackedOutcome::Polymorphic);
12043 0 : *emitted = true;
12044 0 : return Ok();
12045 : }
12046 :
12047 : AbortReasonOr<Ok>
12048 100 : IonBuilder::setPropTryCache(bool* emitted, MDefinition* obj,
12049 : PropertyName* name, MDefinition* value,
12050 : bool barrier)
12051 : {
12052 0 : MOZ_ASSERT(*emitted == false);
12053 :
12054 0 : bool strict = IsStrictSetPC(pc);
12055 :
12056 0 : MConstant* id = constant(StringValue(name));
12057 0 : MSetPropertyCache* ins = MSetPropertyCache::New(alloc(), obj, id, value, strict,
12058 0 : needsPostBarrier(value), barrier,
12059 0 : /* guardHoles = */ false);
12060 100 : current->add(ins);
12061 0 : current->push(value);
12062 :
12063 0 : MOZ_TRY(resumeAfter(ins));
12064 :
12065 0 : trackOptimizationSuccess();
12066 100 : *emitted = true;
12067 100 : return Ok();
12068 : }
12069 :
12070 : AbortReasonOr<Ok>
12071 0 : IonBuilder::jsop_delprop(PropertyName* name)
12072 : {
12073 0 : MDefinition* obj = current->pop();
12074 :
12075 0 : bool strict = JSOp(*pc) == JSOP_STRICTDELPROP;
12076 0 : MInstruction* ins = MDeleteProperty::New(alloc(), obj, name, strict);
12077 :
12078 0 : current->add(ins);
12079 0 : current->push(ins);
12080 :
12081 0 : return resumeAfter(ins);
12082 : }
12083 :
12084 : AbortReasonOr<Ok>
12085 0 : IonBuilder::jsop_delelem()
12086 : {
12087 0 : MDefinition* index = current->pop();
12088 0 : MDefinition* obj = current->pop();
12089 :
12090 0 : bool strict = JSOp(*pc) == JSOP_STRICTDELELEM;
12091 0 : MDeleteElement* ins = MDeleteElement::New(alloc(), obj, index, strict);
12092 0 : current->add(ins);
12093 0 : current->push(ins);
12094 :
12095 0 : return resumeAfter(ins);
12096 : }
12097 :
12098 : AbortReasonOr<Ok>
12099 0 : IonBuilder::jsop_regexp(RegExpObject* reobj)
12100 : {
12101 1 : MOZ_ASSERT(!IsInsideNursery(reobj));
12102 :
12103 : // Determine this while we're still on the main thread to avoid races.
12104 0 : bool hasShared = reobj->hasShared();
12105 :
12106 0 : MRegExp* regexp = MRegExp::New(alloc(), constraints(), reobj, hasShared);
12107 1 : current->add(regexp);
12108 0 : current->push(regexp);
12109 :
12110 1 : return Ok();
12111 : }
12112 :
12113 : AbortReasonOr<Ok>
12114 0 : IonBuilder::jsop_object(JSObject* obj)
12115 : {
12116 0 : if (options.cloneSingletons()) {
12117 0 : MCloneLiteral* clone = MCloneLiteral::New(alloc(), constant(ObjectValue(*obj)));
12118 0 : current->add(clone);
12119 0 : current->push(clone);
12120 : return resumeAfter(clone);
12121 : }
12122 :
12123 0 : realm->setSingletonsAsValues();
12124 0 : pushConstant(ObjectValue(*obj));
12125 0 : return Ok();
12126 : }
12127 :
12128 : AbortReasonOr<Ok>
12129 0 : IonBuilder::jsop_classconstructor()
12130 : {
12131 0 : MClassConstructor* constructor = MClassConstructor::New(alloc(), pc);
12132 0 : current->add(constructor);
12133 0 : current->push(constructor);
12134 0 : return resumeAfter(constructor);
12135 : }
12136 :
12137 : AbortReasonOr<Ok>
12138 0 : IonBuilder::jsop_lambda(JSFunction* fun)
12139 : {
12140 17 : MOZ_ASSERT(usesEnvironmentChain());
12141 0 : MOZ_ASSERT(!fun->isArrow());
12142 :
12143 17 : if (IsAsmJSModule(fun))
12144 0 : return abort(AbortReason::Disable, "Lambda is an asm.js module function");
12145 :
12146 0 : MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
12147 0 : current->add(cst);
12148 0 : MLambda* ins = MLambda::New(alloc(), constraints(), current->environmentChain(), cst);
12149 17 : current->add(ins);
12150 17 : current->push(ins);
12151 :
12152 : return resumeAfter(ins);
12153 : }
12154 :
12155 : AbortReasonOr<Ok>
12156 0 : IonBuilder::jsop_lambda_arrow(JSFunction* fun)
12157 : {
12158 0 : MOZ_ASSERT(usesEnvironmentChain());
12159 1 : MOZ_ASSERT(fun->isArrow());
12160 0 : MOZ_ASSERT(!fun->isNative());
12161 :
12162 0 : MDefinition* newTargetDef = current->pop();
12163 0 : MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
12164 0 : current->add(cst);
12165 0 : MLambdaArrow* ins = MLambdaArrow::New(alloc(), constraints(), current->environmentChain(),
12166 0 : newTargetDef, cst);
12167 1 : current->add(ins);
12168 0 : current->push(ins);
12169 :
12170 1 : return resumeAfter(ins);
12171 : }
12172 :
12173 : AbortReasonOr<Ok>
12174 0 : IonBuilder::jsop_setfunname(uint8_t prefixKind)
12175 : {
12176 0 : MDefinition* name = current->pop();
12177 0 : MDefinition* fun = current->pop();
12178 0 : MOZ_ASSERT(fun->type() == MIRType::Object);
12179 :
12180 0 : MSetFunName* ins = MSetFunName::New(alloc(), fun, name, prefixKind);
12181 :
12182 0 : current->add(ins);
12183 0 : current->push(fun);
12184 :
12185 0 : return resumeAfter(ins);
12186 : }
12187 :
12188 : AbortReasonOr<Ok>
12189 0 : IonBuilder::jsop_pushlexicalenv(uint32_t index)
12190 : {
12191 0 : MOZ_ASSERT(usesEnvironmentChain());
12192 :
12193 0 : LexicalScope* scope = &script()->getScope(index)->as<LexicalScope>();
12194 : MNewLexicalEnvironmentObject* ins =
12195 0 : MNewLexicalEnvironmentObject::New(alloc(), current->environmentChain(), scope);
12196 :
12197 1 : current->add(ins);
12198 0 : current->setEnvironmentChain(ins);
12199 :
12200 1 : return Ok();
12201 : }
12202 :
12203 : AbortReasonOr<Ok>
12204 0 : IonBuilder::jsop_copylexicalenv(bool copySlots)
12205 : {
12206 0 : MOZ_ASSERT(usesEnvironmentChain());
12207 :
12208 : MCopyLexicalEnvironmentObject* ins =
12209 0 : MCopyLexicalEnvironmentObject::New(alloc(), current->environmentChain(), copySlots);
12210 :
12211 0 : current->add(ins);
12212 0 : current->setEnvironmentChain(ins);
12213 :
12214 0 : return Ok();
12215 : }
12216 :
12217 : AbortReasonOr<Ok>
12218 159 : IonBuilder::jsop_setarg(uint32_t arg)
12219 : {
12220 : // To handle this case, we should spill the arguments to the space where
12221 : // actual arguments are stored. The tricky part is that if we add a MIR
12222 : // to wrap the spilling action, we don't want the spilling to be
12223 : // captured by the GETARG and by the resume point, only by
12224 : // MGetFrameArgument.
12225 0 : MOZ_ASSERT_IF(script()->hasBaselineScript(),
12226 : script()->baselineScript()->modifiesArguments());
12227 159 : MDefinition* val = current->peek(-1);
12228 :
12229 : // If an arguments object is in use, and it aliases formals, then all SETARGs
12230 : // must go through the arguments object.
12231 0 : if (info().argsObjAliasesFormals()) {
12232 0 : if (needsPostBarrier(val))
12233 0 : current->add(MPostWriteBarrier::New(alloc(), current->argumentsObject(), val));
12234 0 : current->add(MSetArgumentsObjectArg::New(alloc(), current->argumentsObject(),
12235 0 : GET_ARGNO(pc), val));
12236 0 : return Ok();
12237 : }
12238 :
12239 : // :TODO: if hasArguments() is true, and the script has a JSOP_SETARG, then
12240 : // convert all arg accesses to go through the arguments object. (see Bug 957475)
12241 318 : if (info().hasArguments())
12242 0 : return abort(AbortReason::Disable, "NYI: arguments & setarg.");
12243 :
12244 : // Otherwise, if a magic arguments is in use, and it aliases formals, and there exist
12245 : // arguments[...] GETELEM expressions in the script, then SetFrameArgument must be used.
12246 : // If no arguments[...] GETELEM expressions are in the script, and an argsobj is not
12247 : // required, then it means that any aliased argument set can never be observed, and
12248 : // the frame does not actually need to be updated with the new arg value.
12249 318 : if (info().argumentsAliasesFormals()) {
12250 : // JSOP_SETARG with magic arguments within inline frames is not yet supported.
12251 : MOZ_ASSERT(script()->uninlineable() && !isInlineBuilder());
12252 :
12253 : MSetFrameArgument* store = MSetFrameArgument::New(alloc(), arg, val);
12254 : modifiesFrameArguments_ = true;
12255 : current->add(store);
12256 : current->setArg(arg);
12257 : return Ok();
12258 : }
12259 :
12260 : // If this assignment is at the start of the function and is coercing
12261 : // the original value for the argument which was passed in, loosen
12262 : // the type information for that original argument if it is currently
12263 : // empty due to originally executing in the interpreter.
12264 321 : if (graph().numBlocks() == 1 &&
12265 0 : (val->isBitOr() || val->isBitAnd() || val->isMul() /* for JSOP_POS */))
12266 : {
12267 0 : for (size_t i = 0; i < val->numOperands(); i++) {
12268 0 : MDefinition* op = val->getOperand(i);
12269 0 : if (op->isParameter() &&
12270 0 : op->toParameter()->index() == (int32_t)arg &&
12271 0 : op->resultTypeSet() &&
12272 0 : op->resultTypeSet()->empty())
12273 : {
12274 0 : bool otherUses = false;
12275 0 : for (MUseDefIterator iter(op); iter; iter++) {
12276 0 : MDefinition* def = iter.def();
12277 0 : if (def == val)
12278 : continue;
12279 0 : otherUses = true;
12280 : }
12281 0 : if (!otherUses) {
12282 0 : MOZ_ASSERT(op->resultTypeSet() == &argTypes[arg]);
12283 0 : argTypes[arg].addType(TypeSet::UnknownType(), alloc_->lifoAlloc());
12284 0 : if (val->isMul()) {
12285 0 : val->setResultType(MIRType::Double);
12286 0 : val->toMul()->setSpecialization(MIRType::Double);
12287 : } else {
12288 0 : MOZ_ASSERT(val->type() == MIRType::Int32);
12289 : }
12290 0 : val->setResultTypeSet(nullptr);
12291 : }
12292 : }
12293 : }
12294 : }
12295 :
12296 159 : current->setArg(arg);
12297 159 : return Ok();
12298 : }
12299 :
12300 : AbortReasonOr<Ok>
12301 0 : IonBuilder::jsop_defvar(uint32_t index)
12302 : {
12303 0 : MOZ_ASSERT(JSOp(*pc) == JSOP_DEFVAR);
12304 :
12305 0 : PropertyName* name = script()->getName(index);
12306 :
12307 : // Bake in attrs.
12308 0 : unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
12309 0 : MOZ_ASSERT(!script()->isForEval());
12310 :
12311 : // Pass the EnvironmentChain.
12312 0 : MOZ_ASSERT(usesEnvironmentChain());
12313 :
12314 : // Bake the name pointer into the MDefVar.
12315 0 : MDefVar* defvar = MDefVar::New(alloc(), name, attrs, current->environmentChain());
12316 0 : current->add(defvar);
12317 :
12318 0 : return resumeAfter(defvar);
12319 : }
12320 :
12321 : AbortReasonOr<Ok>
12322 0 : IonBuilder::jsop_deflexical(uint32_t index)
12323 : {
12324 0 : MOZ_ASSERT(!script()->hasNonSyntacticScope());
12325 0 : MOZ_ASSERT(JSOp(*pc) == JSOP_DEFLET || JSOp(*pc) == JSOP_DEFCONST);
12326 :
12327 0 : PropertyName* name = script()->getName(index);
12328 0 : unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
12329 0 : if (JSOp(*pc) == JSOP_DEFCONST)
12330 0 : attrs |= JSPROP_READONLY;
12331 :
12332 0 : MDefLexical* deflex = MDefLexical::New(alloc(), name, attrs);
12333 0 : current->add(deflex);
12334 :
12335 0 : return resumeAfter(deflex);
12336 : }
12337 :
12338 : AbortReasonOr<Ok>
12339 0 : IonBuilder::jsop_deffun()
12340 : {
12341 0 : MOZ_ASSERT(usesEnvironmentChain());
12342 :
12343 0 : MDefFun* deffun = MDefFun::New(alloc(), current->pop(), current->environmentChain());
12344 0 : current->add(deffun);
12345 :
12346 0 : return resumeAfter(deffun);
12347 : }
12348 :
12349 : AbortReasonOr<Ok>
12350 0 : IonBuilder::jsop_throwsetconst()
12351 : {
12352 0 : current->peek(-1)->setImplicitlyUsedUnchecked();
12353 0 : MInstruction* lexicalError = MThrowRuntimeLexicalError::New(alloc(), JSMSG_BAD_CONST_ASSIGN);
12354 0 : current->add(lexicalError);
12355 0 : return resumeAfter(lexicalError);
12356 : }
12357 :
12358 : AbortReasonOr<Ok>
12359 0 : IonBuilder::jsop_checklexical()
12360 : {
12361 0 : uint32_t slot = info().localSlot(GET_LOCALNO(pc));
12362 : MDefinition* lexical;
12363 0 : MOZ_TRY_VAR(lexical, addLexicalCheck(current->getSlot(slot)));
12364 0 : current->setSlot(slot, lexical);
12365 0 : return Ok();
12366 : }
12367 :
12368 : AbortReasonOr<Ok>
12369 3 : IonBuilder::jsop_checkaliasedlexical(EnvironmentCoordinate ec)
12370 : {
12371 : MDefinition* let;
12372 0 : MOZ_TRY_VAR(let, addLexicalCheck(getAliasedVar(ec)));
12373 :
12374 3 : jsbytecode* nextPc = pc + JSOP_CHECKALIASEDLEXICAL_LENGTH;
12375 3 : MOZ_ASSERT(JSOp(*nextPc) == JSOP_GETALIASEDVAR ||
12376 : JSOp(*nextPc) == JSOP_SETALIASEDVAR ||
12377 : JSOp(*nextPc) == JSOP_THROWSETALIASEDCONST);
12378 3 : MOZ_ASSERT(ec == EnvironmentCoordinate(nextPc));
12379 :
12380 : // If we are checking for a load, push the checked let so that the load
12381 : // can use it.
12382 3 : if (JSOp(*nextPc) == JSOP_GETALIASEDVAR)
12383 0 : setLexicalCheck(let);
12384 :
12385 3 : return Ok();
12386 : }
12387 :
12388 : AbortReasonOr<Ok>
12389 0 : IonBuilder::jsop_functionthis()
12390 : {
12391 210 : MOZ_ASSERT(info().funMaybeLazy());
12392 0 : MOZ_ASSERT(!info().funMaybeLazy()->isArrow());
12393 :
12394 0 : if (script()->strict() || info().funMaybeLazy()->isSelfHostedBuiltin()) {
12395 : // No need to wrap primitive |this| in strict mode or self-hosted code.
12396 418 : current->pushSlot(info().thisSlot());
12397 209 : return Ok();
12398 : }
12399 :
12400 3 : if (thisTypes && (thisTypes->getKnownMIRType() == MIRType::Object ||
12401 3 : (thisTypes->empty() && baselineFrame_ && baselineFrame_->thisType.isSomeObject())))
12402 : {
12403 : // This is safe, because if the entry type of |this| is an object, it
12404 : // will necessarily be an object throughout the entire function. OSR
12405 : // can introduce a phi, but this phi will be specialized.
12406 0 : current->pushSlot(info().thisSlot());
12407 0 : return Ok();
12408 : }
12409 :
12410 : // If we are doing an analysis, we might not yet know the type of |this|.
12411 : // Instead of bailing out just push the |this| slot, as this code won't
12412 : // actually execute and it does not matter whether |this| is primitive.
12413 0 : if (info().isAnalysis()) {
12414 2 : current->pushSlot(info().thisSlot());
12415 1 : return Ok();
12416 : }
12417 :
12418 : // Hard case: |this| may be a primitive we have to wrap.
12419 0 : MDefinition* def = current->getSlot(info().thisSlot());
12420 :
12421 0 : if (def->type() == MIRType::Object) {
12422 0 : current->push(def);
12423 0 : return Ok();
12424 : }
12425 :
12426 : // Beyond this point we may need to access non-syntactic global. Ion doesn't
12427 : // currently support this so just abort.
12428 0 : if (script()->hasNonSyntacticScope())
12429 0 : return abort(AbortReason::Disable, "JSOP_FUNCTIONTHIS would need non-syntactic global");
12430 :
12431 0 : if (IsNullOrUndefined(def->type())) {
12432 0 : LexicalEnvironmentObject* globalLexical = &script()->global().lexicalEnvironment();
12433 0 : pushConstant(globalLexical->thisValue());
12434 0 : return Ok();
12435 : }
12436 :
12437 0 : MComputeThis* thisObj = MComputeThis::New(alloc(), def);
12438 0 : current->add(thisObj);
12439 0 : current->push(thisObj);
12440 :
12441 : return resumeAfter(thisObj);
12442 : }
12443 :
12444 : AbortReasonOr<Ok>
12445 0 : IonBuilder::jsop_globalthis()
12446 : {
12447 0 : if (script()->hasNonSyntacticScope()) {
12448 : // Ion does not compile global scripts with a non-syntactic scope, but
12449 : // we can end up here when we're compiling an arrow function.
12450 0 : return abort(AbortReason::Disable, "JSOP_GLOBALTHIS in script with non-syntactic scope");
12451 : }
12452 :
12453 0 : LexicalEnvironmentObject* globalLexical = &script()->global().lexicalEnvironment();
12454 0 : pushConstant(globalLexical->thisValue());
12455 0 : return Ok();
12456 : }
12457 :
12458 : AbortReasonOr<Ok>
12459 0 : IonBuilder::jsop_typeof()
12460 : {
12461 48 : MDefinition* input = current->pop();
12462 0 : MTypeOf* ins = MTypeOf::New(alloc(), input, input->type());
12463 :
12464 0 : ins->cacheInputMaybeCallableOrEmulatesUndefined(constraints());
12465 :
12466 48 : current->add(ins);
12467 0 : current->push(ins);
12468 :
12469 48 : return Ok();
12470 : }
12471 :
12472 : AbortReasonOr<Ok>
12473 0 : IonBuilder::jsop_toasync()
12474 : {
12475 0 : MDefinition* unwrapped = current->pop();
12476 0 : MOZ_ASSERT(unwrapped->type() == MIRType::Object);
12477 :
12478 0 : MToAsync* ins = MToAsync::New(alloc(), unwrapped);
12479 :
12480 0 : current->add(ins);
12481 0 : current->push(ins);
12482 :
12483 0 : return resumeAfter(ins);
12484 : }
12485 :
12486 : AbortReasonOr<Ok>
12487 0 : IonBuilder::jsop_toasyncgen()
12488 : {
12489 0 : MDefinition* unwrapped = current->pop();
12490 0 : MOZ_ASSERT(unwrapped->type() == MIRType::Object);
12491 :
12492 0 : MToAsyncGen* ins = MToAsyncGen::New(alloc(), unwrapped);
12493 :
12494 0 : current->add(ins);
12495 0 : current->push(ins);
12496 :
12497 0 : return resumeAfter(ins);
12498 : }
12499 :
12500 : AbortReasonOr<Ok>
12501 0 : IonBuilder::jsop_toasynciter()
12502 : {
12503 0 : MDefinition* nextMethod = current->pop();
12504 0 : MDefinition* iterator = current->pop();
12505 0 : MOZ_ASSERT(iterator->type() == MIRType::Object);
12506 :
12507 0 : MToAsyncIter* ins = MToAsyncIter::New(alloc(), iterator, nextMethod);
12508 :
12509 0 : current->add(ins);
12510 0 : current->push(ins);
12511 :
12512 0 : return resumeAfter(ins);
12513 : }
12514 :
12515 : AbortReasonOr<Ok>
12516 1 : IonBuilder::jsop_toid()
12517 : {
12518 : // No-op if the index is trivally convertable to an id.
12519 0 : MIRType type = current->peek(-1)->type();
12520 1 : if (type == MIRType::Int32 || type == MIRType::String || type == MIRType::Symbol)
12521 0 : return Ok();
12522 :
12523 0 : MDefinition* index = current->pop();
12524 0 : MToId* ins = MToId::New(alloc(), index);
12525 :
12526 0 : current->add(ins);
12527 0 : current->push(ins);
12528 :
12529 : return resumeAfter(ins);
12530 : }
12531 :
12532 : AbortReasonOr<Ok>
12533 0 : IonBuilder::jsop_iter()
12534 : {
12535 0 : MDefinition* obj = current->pop();
12536 0 : MInstruction* ins = MGetIteratorCache::New(alloc(), obj);
12537 :
12538 0 : if (!outermostBuilder()->iterators_.append(ins))
12539 0 : return abort(AbortReason::Alloc);
12540 :
12541 0 : current->add(ins);
12542 0 : current->push(ins);
12543 :
12544 0 : return resumeAfter(ins);
12545 : }
12546 :
12547 : AbortReasonOr<Ok>
12548 0 : IonBuilder::jsop_itermore()
12549 : {
12550 0 : MDefinition* iter = current->peek(-1);
12551 0 : MInstruction* ins = MIteratorMore::New(alloc(), iter);
12552 :
12553 0 : current->add(ins);
12554 0 : current->push(ins);
12555 :
12556 0 : return resumeAfter(ins);
12557 : }
12558 :
12559 : AbortReasonOr<Ok>
12560 0 : IonBuilder::jsop_isnoiter()
12561 : {
12562 0 : MDefinition* def = current->peek(-1);
12563 0 : MOZ_ASSERT(def->isIteratorMore());
12564 :
12565 0 : MInstruction* ins = MIsNoIter::New(alloc(), def);
12566 0 : current->add(ins);
12567 0 : current->push(ins);
12568 :
12569 0 : return Ok();
12570 : }
12571 :
12572 : AbortReasonOr<Ok>
12573 0 : IonBuilder::jsop_iterend()
12574 : {
12575 0 : MDefinition* iter = current->pop();
12576 0 : MInstruction* ins = MIteratorEnd::New(alloc(), iter);
12577 :
12578 0 : current->add(ins);
12579 :
12580 0 : return resumeAfter(ins);
12581 : }
12582 :
12583 : AbortReasonOr<Ok>
12584 0 : IonBuilder::jsop_iternext()
12585 : {
12586 0 : MDefinition* def = current->pop();
12587 0 : MOZ_ASSERT(def->type() == MIRType::Value);
12588 :
12589 : // The value must be a string.
12590 0 : MInstruction* unbox = MUnbox::New(alloc(), def, MIRType::String, MUnbox::Infallible);
12591 0 : current->add(unbox);
12592 0 : current->push(unbox);
12593 :
12594 0 : return Ok();
12595 : }
12596 :
12597 : MDefinition*
12598 0 : IonBuilder::walkEnvironmentChain(unsigned hops)
12599 : {
12600 0 : MDefinition* env = current->getSlot(info().environmentChainSlot());
12601 :
12602 0 : for (unsigned i = 0; i < hops; i++) {
12603 0 : MInstruction* ins = MEnclosingEnvironment::New(alloc(), env);
12604 11 : current->add(ins);
12605 11 : env = ins;
12606 : }
12607 :
12608 211 : return env;
12609 : }
12610 :
12611 : bool
12612 0 : IonBuilder::hasStaticEnvironmentObject(JSObject** pcall)
12613 : {
12614 209 : JSScript* outerScript = EnvironmentCoordinateFunctionScript(script(), pc);
12615 415 : if (!outerScript || !outerScript->treatAsRunOnce())
12616 : return false;
12617 :
12618 : TypeSet::ObjectKey* funKey =
12619 0 : TypeSet::ObjectKey::get(outerScript->functionNonDelazifying());
12620 0 : if (funKey->hasFlags(constraints(), OBJECT_FLAG_RUNONCE_INVALIDATED))
12621 : return false;
12622 :
12623 : // The script this aliased var operation is accessing will run only once,
12624 : // so there will be only one call object and the aliased var access can be
12625 : // compiled in the same manner as a global access. We still need to find
12626 : // the call object though.
12627 :
12628 : // Look for the call object on the current script's function's env chain.
12629 : // If the current script is inner to the outer script and the function has
12630 : // singleton type then it should show up here.
12631 :
12632 0 : MDefinition* envDef = current->getSlot(info().environmentChainSlot());
12633 0 : envDef->setImplicitlyUsedUnchecked();
12634 :
12635 0 : JSObject* environment = script()->functionNonDelazifying()->environment();
12636 0 : while (environment && !environment->is<GlobalObject>()) {
12637 0 : if (environment->is<CallObject>() &&
12638 0 : environment->as<CallObject>().callee().nonLazyScript() == outerScript)
12639 : {
12640 0 : MOZ_ASSERT(environment->isSingleton());
12641 0 : *pcall = environment;
12642 0 : return true;
12643 : }
12644 0 : environment = environment->enclosingEnvironment();
12645 : }
12646 :
12647 : // Look for the call object on the current frame, if we are compiling the
12648 : // outer script itself. Don't do this if we are at entry to the outer
12649 : // script, as the call object we see will not be the real one --- after
12650 : // entering the Ion code a different call object will be created.
12651 :
12652 0 : if (script() == outerScript && baselineFrame_ && info().osrPc()) {
12653 0 : JSObject* singletonScope = baselineFrame_->singletonEnvChain;
12654 0 : if (singletonScope &&
12655 0 : singletonScope->is<CallObject>() &&
12656 0 : singletonScope->as<CallObject>().callee().nonLazyScript() == outerScript)
12657 : {
12658 0 : MOZ_ASSERT(singletonScope->isSingleton());
12659 0 : *pcall = singletonScope;
12660 0 : return true;
12661 : }
12662 : }
12663 :
12664 : return true;
12665 : }
12666 :
12667 : MDefinition*
12668 0 : IonBuilder::getAliasedVar(EnvironmentCoordinate ec)
12669 : {
12670 0 : MDefinition* obj = walkEnvironmentChain(ec.hops());
12671 :
12672 200 : Shape* shape = EnvironmentCoordinateToEnvironmentShape(script(), pc);
12673 :
12674 : MInstruction* load;
12675 0 : if (shape->numFixedSlots() <= ec.slot()) {
12676 0 : MInstruction* slots = MSlots::New(alloc(), obj);
12677 0 : current->add(slots);
12678 :
12679 0 : load = MLoadSlot::New(alloc(), slots, ec.slot() - shape->numFixedSlots());
12680 : } else {
12681 400 : load = MLoadFixedSlot::New(alloc(), obj, ec.slot());
12682 : }
12683 :
12684 200 : current->add(load);
12685 200 : return load;
12686 : }
12687 :
12688 : AbortReasonOr<Ok>
12689 0 : IonBuilder::jsop_getaliasedvar(EnvironmentCoordinate ec)
12690 : {
12691 0 : JSObject* call = nullptr;
12692 0 : if (hasStaticEnvironmentObject(&call) && call) {
12693 0 : PropertyName* name = EnvironmentCoordinateName(envCoordinateNameCache, script(), pc);
12694 0 : bool emitted = false;
12695 0 : MOZ_TRY(getStaticName(&emitted, call, name, takeLexicalCheck()));
12696 0 : if (emitted)
12697 0 : return Ok();
12698 : }
12699 :
12700 : // See jsop_checkaliasedlexical.
12701 0 : MDefinition* load = takeLexicalCheck();
12702 0 : if (!load)
12703 197 : load = getAliasedVar(ec);
12704 0 : current->push(load);
12705 :
12706 400 : TemporaryTypeSet* types = bytecodeTypes(pc);
12707 200 : return pushTypeBarrier(load, types, BarrierKind::TypeSet);
12708 : }
12709 :
12710 : AbortReasonOr<Ok>
12711 0 : IonBuilder::jsop_setaliasedvar(EnvironmentCoordinate ec)
12712 : {
12713 0 : JSObject* call = nullptr;
12714 0 : if (hasStaticEnvironmentObject(&call)) {
12715 0 : uint32_t depth = current->stackDepth() + 1;
12716 0 : if (depth > current->nslots()) {
12717 0 : if (!current->increaseSlots(depth - current->nslots()))
12718 0 : return abort(AbortReason::Alloc);
12719 : }
12720 0 : MDefinition* value = current->pop();
12721 0 : PropertyName* name = EnvironmentCoordinateName(envCoordinateNameCache, script(), pc);
12722 :
12723 0 : if (call) {
12724 : // Push the object on the stack to match the bound object expected in
12725 : // the global and property set cases.
12726 0 : pushConstant(ObjectValue(*call));
12727 0 : current->push(value);
12728 0 : return setStaticName(call, name);
12729 : }
12730 :
12731 : // The call object has type information we need to respect but we
12732 : // couldn't find it. Just do a normal property assign.
12733 0 : MDefinition* obj = walkEnvironmentChain(ec.hops());
12734 0 : current->push(obj);
12735 0 : current->push(value);
12736 0 : return jsop_setprop(name);
12737 : }
12738 :
12739 9 : MDefinition* rval = current->peek(-1);
12740 0 : MDefinition* obj = walkEnvironmentChain(ec.hops());
12741 :
12742 0 : Shape* shape = EnvironmentCoordinateToEnvironmentShape(script(), pc);
12743 :
12744 9 : if (needsPostBarrier(rval))
12745 4 : current->add(MPostWriteBarrier::New(alloc(), obj, rval));
12746 :
12747 : MInstruction* store;
12748 0 : if (shape->numFixedSlots() <= ec.slot()) {
12749 0 : MInstruction* slots = MSlots::New(alloc(), obj);
12750 0 : current->add(slots);
12751 :
12752 0 : store = MStoreSlot::NewBarriered(alloc(), slots, ec.slot() - shape->numFixedSlots(), rval);
12753 : } else {
12754 9 : store = MStoreFixedSlot::NewBarriered(alloc(), obj, ec.slot(), rval);
12755 : }
12756 :
12757 9 : current->add(store);
12758 : return resumeAfter(store);
12759 : }
12760 :
12761 : AbortReasonOr<Ok>
12762 0 : IonBuilder::jsop_in()
12763 : {
12764 49 : MDefinition* obj = convertUnboxedObjects(current->pop());
12765 0 : MDefinition* id = current->pop();
12766 :
12767 98 : if (!forceInlineCaches()) {
12768 0 : bool emitted = false;
12769 :
12770 0 : MOZ_TRY(inTryDense(&emitted, obj, id));
12771 49 : if (emitted)
12772 0 : return Ok();
12773 :
12774 0 : MOZ_TRY(hasTryNotDefined(&emitted, obj, id, /* ownProperty = */ false));
12775 49 : if (emitted)
12776 0 : return Ok();
12777 :
12778 0 : MOZ_TRY(hasTryDefiniteSlotOrUnboxed(&emitted, obj, id));
12779 49 : if (emitted)
12780 0 : return Ok();
12781 : }
12782 :
12783 0 : MInCache* ins = MInCache::New(alloc(), id, obj);
12784 :
12785 49 : current->add(ins);
12786 49 : current->push(ins);
12787 :
12788 : return resumeAfter(ins);
12789 : }
12790 :
12791 : AbortReasonOr<Ok>
12792 0 : IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
12793 : {
12794 0 : MOZ_ASSERT(!*emitted);
12795 :
12796 49 : if (shouldAbortOnPreliminaryGroups(obj))
12797 0 : return Ok();
12798 :
12799 48 : if (!ElementAccessIsDenseNative(constraints(), obj, id))
12800 48 : return Ok();
12801 :
12802 : bool hasExtraIndexedProperty;
12803 0 : MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
12804 0 : if (hasExtraIndexedProperty)
12805 0 : return Ok();
12806 :
12807 0 : *emitted = true;
12808 :
12809 0 : bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
12810 :
12811 : // Ensure id is an integer.
12812 0 : MInstruction* idInt32 = MToNumberInt32::New(alloc(), id);
12813 0 : current->add(idInt32);
12814 0 : id = idInt32;
12815 :
12816 : // Get the elements vector.
12817 0 : MElements* elements = MElements::New(alloc(), obj);
12818 0 : current->add(elements);
12819 :
12820 0 : MInstruction* initLength = initializedLength(elements);
12821 :
12822 : // If there are no holes, speculate the InArray check will not fail.
12823 0 : if (!needsHoleCheck && !failedBoundsCheck_) {
12824 0 : addBoundsCheck(idInt32, initLength);
12825 0 : pushConstant(BooleanValue(true));
12826 0 : return Ok();
12827 : }
12828 :
12829 : // Check if id < initLength and elem[id] not a hole.
12830 0 : MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck);
12831 :
12832 0 : current->add(ins);
12833 0 : current->push(ins);
12834 :
12835 0 : return Ok();
12836 : }
12837 :
12838 : AbortReasonOr<Ok>
12839 50 : IonBuilder::hasTryNotDefined(bool* emitted, MDefinition* obj, MDefinition* id, bool ownProperty)
12840 : {
12841 : // Fold |id in obj| to |false|, if we know the object (or an object on its
12842 : // prototype chain) does not have this property.
12843 :
12844 0 : MOZ_ASSERT(!*emitted);
12845 :
12846 0 : MConstant* idConst = id->maybeConstantValue();
12847 0 : jsid propId;
12848 50 : if (!idConst || !ValueToIdPure(idConst->toJSValue(), &propId))
12849 0 : return Ok();
12850 :
12851 32 : if (propId != IdToTypeId(propId))
12852 0 : return Ok();
12853 :
12854 : bool res;
12855 0 : MOZ_TRY_VAR(res, testNotDefinedProperty(obj, propId, ownProperty));
12856 16 : if (!res)
12857 0 : return Ok();
12858 :
12859 0 : *emitted = true;
12860 :
12861 0 : pushConstant(BooleanValue(false));
12862 0 : obj->setImplicitlyUsedUnchecked();
12863 0 : id->setImplicitlyUsedUnchecked();
12864 0 : return Ok();
12865 : }
12866 :
12867 : AbortReasonOr<Ok>
12868 50 : IonBuilder::hasTryDefiniteSlotOrUnboxed(bool* emitted, MDefinition* obj, MDefinition* id)
12869 : {
12870 : // Fold |id in obj| to |true|, when obj definitely contains a property with
12871 : // that name.
12872 0 : MOZ_ASSERT(!*emitted);
12873 :
12874 50 : if (obj->type() != MIRType::Object)
12875 0 : return Ok();
12876 :
12877 0 : MConstant* idConst = id->maybeConstantValue();
12878 0 : jsid propId;
12879 21 : if (!idConst || !ValueToIdPure(idConst->toJSValue(), &propId))
12880 0 : return Ok();
12881 :
12882 32 : if (propId != IdToTypeId(propId))
12883 0 : return Ok();
12884 :
12885 : // Try finding a native definite slot first.
12886 : uint32_t nfixed;
12887 16 : uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), propId, &nfixed);
12888 16 : if (slot == UINT32_MAX) {
12889 : // Check for unboxed object properties next.
12890 : JSValueType unboxedType;
12891 0 : uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), propId, &unboxedType);
12892 16 : if (offset == UINT32_MAX)
12893 16 : return Ok();
12894 : }
12895 :
12896 0 : *emitted = true;
12897 :
12898 0 : pushConstant(BooleanValue(true));
12899 0 : obj->setImplicitlyUsedUnchecked();
12900 0 : id->setImplicitlyUsedUnchecked();
12901 0 : return Ok();
12902 : }
12903 :
12904 : AbortReasonOr<Ok>
12905 0 : IonBuilder::jsop_hasown()
12906 : {
12907 1 : MDefinition* obj = convertUnboxedObjects(current->pop());
12908 0 : MDefinition* id = current->pop();
12909 :
12910 2 : if (!forceInlineCaches()) {
12911 0 : bool emitted = false;
12912 :
12913 0 : MOZ_TRY(hasTryNotDefined(&emitted, obj, id, /* ownProperty = */ true));
12914 1 : if (emitted)
12915 0 : return Ok();
12916 :
12917 0 : MOZ_TRY(hasTryDefiniteSlotOrUnboxed(&emitted, obj, id));
12918 1 : if (emitted)
12919 0 : return Ok();
12920 : }
12921 :
12922 0 : MHasOwnCache* ins = MHasOwnCache::New(alloc(), obj, id);
12923 1 : current->add(ins);
12924 0 : current->push(ins);
12925 :
12926 3 : MOZ_TRY(resumeAfter(ins));
12927 1 : return Ok();
12928 : }
12929 :
12930 : AbortReasonOr<bool>
12931 0 : IonBuilder::hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* onProto)
12932 : {
12933 6 : MOZ_ASSERT(protoObject);
12934 :
12935 : while (true) {
12936 6 : if (!alloc().ensureBallast())
12937 0 : return abort(AbortReason::Alloc);
12938 :
12939 12 : if (!key->hasStableClassAndProto(constraints()) || !key->clasp()->isNative())
12940 0 : return false;
12941 :
12942 0 : JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
12943 0 : if (!proto) {
12944 0 : *onProto = false;
12945 0 : return true;
12946 : }
12947 :
12948 0 : if (proto == protoObject) {
12949 6 : *onProto = true;
12950 6 : return true;
12951 : }
12952 :
12953 0 : key = TypeSet::ObjectKey::get(proto);
12954 0 : }
12955 :
12956 : MOZ_CRASH("Unreachable");
12957 : }
12958 :
12959 : AbortReasonOr<Ok>
12960 6 : IonBuilder::tryFoldInstanceOf(bool* emitted, MDefinition* lhs, JSObject* protoObject)
12961 : {
12962 : // Try to fold the js::IsDelegate part of the instanceof operation.
12963 0 : MOZ_ASSERT(*emitted == false);
12964 :
12965 0 : if (!lhs->mightBeType(MIRType::Object)) {
12966 : // If the lhs is a primitive, the result is false.
12967 0 : lhs->setImplicitlyUsedUnchecked();
12968 0 : pushConstant(BooleanValue(false));
12969 0 : *emitted = true;
12970 0 : return Ok();
12971 : }
12972 :
12973 0 : TemporaryTypeSet* lhsTypes = lhs->resultTypeSet();
12974 12 : if (!lhsTypes || lhsTypes->unknownObject())
12975 0 : return Ok();
12976 :
12977 : // We can fold if either all objects have protoObject on their proto chain
12978 : // or none have.
12979 : bool isFirst = true;
12980 : bool knownIsInstance = false;
12981 :
12982 0 : for (unsigned i = 0; i < lhsTypes->getObjectCount(); i++) {
12983 1 : TypeSet::ObjectKey* key = lhsTypes->getObject(i);
12984 6 : if (!key)
12985 0 : continue;
12986 :
12987 : bool checkSucceeded;
12988 : bool isInstance;
12989 0 : MOZ_TRY_VAR(checkSucceeded, hasOnProtoChain(key, protoObject, &isInstance));
12990 6 : if (!checkSucceeded)
12991 0 : return Ok();
12992 :
12993 0 : if (isFirst) {
12994 0 : knownIsInstance = isInstance;
12995 6 : isFirst = false;
12996 0 : } else if (knownIsInstance != isInstance) {
12997 : // Some of the objects have protoObject on their proto chain and
12998 : // others don't, so we can't optimize this.
12999 0 : return Ok();
13000 : }
13001 : }
13002 :
13003 6 : if (knownIsInstance && lhsTypes->getKnownMIRType() != MIRType::Object) {
13004 : // The result is true for all objects, but the lhs might be a primitive.
13005 : // We can't fold this completely but we can use a much faster IsObject
13006 : // test.
13007 0 : MIsObject* isObject = MIsObject::New(alloc(), lhs);
13008 0 : current->add(isObject);
13009 0 : current->push(isObject);
13010 6 : *emitted = true;
13011 6 : return Ok();
13012 : }
13013 :
13014 0 : lhs->setImplicitlyUsedUnchecked();
13015 0 : pushConstant(BooleanValue(knownIsInstance));
13016 0 : *emitted = true;
13017 0 : return Ok();
13018 : }
13019 :
13020 : AbortReasonOr<Ok>
13021 0 : IonBuilder::jsop_instanceof()
13022 : {
13023 0 : MDefinition* rhs = current->pop();
13024 11 : MDefinition* obj = current->pop();
13025 11 : bool emitted = false;
13026 :
13027 : // If this is an 'x instanceof function' operation and we can determine the
13028 : // exact function and prototype object being tested for, use a typed path.
13029 : do {
13030 0 : TemporaryTypeSet* rhsTypes = rhs->resultTypeSet();
13031 11 : JSObject* rhsObject = rhsTypes ? rhsTypes->maybeSingleton() : nullptr;
13032 17 : if (!rhsObject || !rhsObject->is<JSFunction>() || rhsObject->isBoundFunction())
13033 : break;
13034 :
13035 : // Refuse to optimize anything whose [[Prototype]] isn't Function.prototype
13036 : // since we can't guarantee that it uses the default @@hasInstance method.
13037 12 : if (rhsObject->hasUncacheableProto() || !rhsObject->hasStaticPrototype())
13038 : break;
13039 :
13040 12 : Value funProto = script()->global().getPrototype(JSProto_Function);
13041 6 : if (!funProto.isObject() || rhsObject->staticPrototype() != &funProto.toObject())
13042 : break;
13043 :
13044 : // If the user has supplied their own @@hasInstance method we shouldn't
13045 : // clobber it.
13046 0 : JSFunction* fun = &rhsObject->as<JSFunction>();
13047 6 : const WellKnownSymbols* symbols = &realm->runtime()->wellKnownSymbols();
13048 6 : if (!js::FunctionHasDefaultHasInstance(fun, *symbols))
13049 : break;
13050 :
13051 : // Ensure that we will bail if the @@hasInstance property or [[Prototype]]
13052 : // change.
13053 6 : TypeSet::ObjectKey* rhsKey = TypeSet::ObjectKey::get(rhsObject);
13054 6 : if (!rhsKey->hasStableClassAndProto(constraints()))
13055 : break;
13056 :
13057 6 : if (rhsKey->unknownProperties())
13058 : break;
13059 :
13060 : HeapTypeSetKey hasInstanceObject =
13061 12 : rhsKey->property(SYMBOL_TO_JSID(symbols->hasInstance));
13062 6 : if (hasInstanceObject.isOwnProperty(constraints()))
13063 : break;
13064 :
13065 : HeapTypeSetKey protoProperty =
13066 0 : rhsKey->property(NameToId(names().prototype));
13067 6 : JSObject* protoObject = protoProperty.singleton(constraints());
13068 6 : if (!protoObject)
13069 : break;
13070 :
13071 0 : rhs->setImplicitlyUsedUnchecked();
13072 :
13073 0 : MOZ_TRY(tryFoldInstanceOf(&emitted, obj, protoObject));
13074 6 : if (emitted)
13075 0 : return Ok();
13076 :
13077 0 : MInstanceOf* ins = MInstanceOf::New(alloc(), obj, protoObject);
13078 :
13079 0 : current->add(ins);
13080 0 : current->push(ins);
13081 :
13082 : return resumeAfter(ins);
13083 : } while (false);
13084 :
13085 : // Try to inline a fast path based on Baseline ICs.
13086 : do {
13087 : Shape* shape;
13088 : uint32_t slot;
13089 : JSObject* protoObject;
13090 5 : if (!inspector->instanceOfData(pc, &shape, &slot, &protoObject))
13091 : break;
13092 :
13093 : // Shape guard.
13094 0 : rhs = addShapeGuard(rhs, shape, Bailout_ShapeGuard);
13095 :
13096 : // Guard .prototype == protoObject.
13097 0 : MOZ_ASSERT(shape->numFixedSlots() == 0, "Must be a dynamic slot");
13098 0 : MSlots* slots = MSlots::New(alloc(), rhs);
13099 0 : current->add(slots);
13100 0 : MLoadSlot* prototype = MLoadSlot::New(alloc(), slots, slot);
13101 0 : current->add(prototype);
13102 0 : MConstant* protoConst = MConstant::NewConstraintlessObject(alloc(), protoObject);
13103 0 : current->add(protoConst);
13104 0 : MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst,
13105 0 : /* bailOnEquality = */ false);
13106 0 : current->add(guard);
13107 :
13108 0 : MOZ_TRY(tryFoldInstanceOf(&emitted, obj, protoObject));
13109 0 : if (emitted)
13110 0 : return Ok();
13111 :
13112 0 : MInstanceOf* ins = MInstanceOf::New(alloc(), obj, protoObject);
13113 0 : current->add(ins);
13114 0 : current->push(ins);
13115 : return resumeAfter(ins);
13116 : } while (false);
13117 :
13118 0 : MInstanceOfCache* ins = MInstanceOfCache::New(alloc(), obj, rhs);
13119 :
13120 5 : current->add(ins);
13121 5 : current->push(ins);
13122 :
13123 : return resumeAfter(ins);
13124 : }
13125 :
13126 : AbortReasonOr<Ok>
13127 0 : IonBuilder::jsop_debugger()
13128 : {
13129 0 : MDebugger* debugger = MDebugger::New(alloc());
13130 0 : current->add(debugger);
13131 :
13132 : // The |debugger;| statement will always bail out to baseline if
13133 : // cx->compartment()->isDebuggee(). Resume in-place and have baseline
13134 : // handle the details.
13135 0 : return resumeAt(debugger, pc);
13136 : }
13137 :
13138 : AbortReasonOr<Ok>
13139 0 : IonBuilder::jsop_implicitthis(PropertyName* name)
13140 : {
13141 0 : MOZ_ASSERT(usesEnvironmentChain());
13142 :
13143 0 : MImplicitThis* implicitThis = MImplicitThis::New(alloc(), current->environmentChain(), name);
13144 22 : current->add(implicitThis);
13145 0 : current->push(implicitThis);
13146 :
13147 22 : return resumeAfter(implicitThis);
13148 : }
13149 :
13150 : AbortReasonOr<Ok>
13151 0 : IonBuilder::jsop_importmeta()
13152 : {
13153 0 : ModuleObject* module = GetModuleObjectForScript(script());
13154 0 : MOZ_ASSERT(module);
13155 :
13156 : // The object must have been created already when we compiled for baseline.
13157 0 : JSObject* metaObject = module->metaObject();
13158 0 : MOZ_ASSERT(metaObject);
13159 :
13160 0 : pushConstant(ObjectValue(*metaObject));
13161 :
13162 0 : return Ok();
13163 : }
13164 :
13165 : MInstruction*
13166 0 : IonBuilder::addConvertElementsToDoubles(MDefinition* elements)
13167 : {
13168 0 : MInstruction* convert = MConvertElementsToDoubles::New(alloc(), elements);
13169 0 : current->add(convert);
13170 0 : return convert;
13171 : }
13172 :
13173 : MDefinition*
13174 0 : IonBuilder::addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
13175 : {
13176 0 : if (!ElementAccessMightBeCopyOnWrite(constraints(), object))
13177 : return object;
13178 0 : MInstruction* copy = MMaybeCopyElementsForWrite::New(alloc(), object, checkNative);
13179 12 : current->add(copy);
13180 12 : return copy;
13181 : }
13182 :
13183 : MInstruction*
13184 0 : IonBuilder::addBoundsCheck(MDefinition* index, MDefinition* length)
13185 : {
13186 196 : MInstruction* check = MBoundsCheck::New(alloc(), index, length);
13187 98 : current->add(check);
13188 :
13189 : // If a bounds check failed in the past, don't optimize bounds checks.
13190 98 : if (failedBoundsCheck_)
13191 0 : check->setNotMovable();
13192 :
13193 98 : if (JitOptions.spectreIndexMasking) {
13194 : // Use a separate MIR instruction for the index masking. Doing this as
13195 : // part of MBoundsCheck would be unsound because bounds checks can be
13196 : // optimized or eliminated completely. Consider this:
13197 : //
13198 : // for (var i = 0; i < x; i++)
13199 : // res = arr[i];
13200 : //
13201 : // If we can prove |x < arr.length|, we are able to eliminate the bounds
13202 : // check, but we should not get rid of the index masking because the
13203 : // |i < x| branch could still be mispredicted.
13204 : //
13205 : // Using a separate instruction lets us eliminate the bounds check
13206 : // without affecting the index masking.
13207 196 : check = MSpectreMaskIndex::New(alloc(), check, length);
13208 98 : current->add(check);
13209 : }
13210 :
13211 98 : return check;
13212 : }
13213 :
13214 : MInstruction*
13215 0 : IonBuilder::addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind)
13216 : {
13217 242 : MGuardShape* guard = MGuardShape::New(alloc(), obj, shape, bailoutKind);
13218 121 : current->add(guard);
13219 :
13220 : // If a shape guard failed in the past, don't optimize shape guard.
13221 121 : if (failedShapeGuard_)
13222 0 : guard->setNotMovable();
13223 :
13224 121 : return guard;
13225 : }
13226 :
13227 : MInstruction*
13228 0 : IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind)
13229 : {
13230 0 : MGuardObjectGroup* guard = MGuardObjectGroup::New(alloc(), obj, group,
13231 0 : /* bailOnEquality = */ false, bailoutKind);
13232 0 : current->add(guard);
13233 :
13234 : // If a shape guard failed in the past, don't optimize group guards.
13235 0 : if (failedShapeGuard_)
13236 0 : guard->setNotMovable();
13237 :
13238 0 : LifoAlloc* lifoAlloc = alloc().lifoAlloc();
13239 0 : guard->setResultTypeSet(lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc,
13240 0 : TypeSet::ObjectType(group)));
13241 :
13242 0 : return guard;
13243 : }
13244 :
13245 : MInstruction*
13246 0 : IonBuilder::addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind)
13247 : {
13248 0 : MGuardUnboxedExpando* guard = MGuardUnboxedExpando::New(alloc(), obj, hasExpando, bailoutKind);
13249 0 : current->add(guard);
13250 :
13251 : // If a shape guard failed in the past, don't optimize group guards.
13252 0 : if (failedShapeGuard_)
13253 0 : guard->setNotMovable();
13254 :
13255 0 : return guard;
13256 : }
13257 :
13258 : MInstruction*
13259 37 : IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj,
13260 : const BaselineInspector::ReceiverVector& receivers)
13261 : {
13262 37 : if (receivers.length() == 1) {
13263 0 : if (!receivers[0].group) {
13264 : // Monomorphic guard on a native object.
13265 34 : return addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
13266 : }
13267 :
13268 0 : if (!receivers[0].shape) {
13269 : // Guard on an unboxed object that does not have an expando.
13270 0 : obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard);
13271 0 : return addUnboxedExpandoGuard(obj, /* hasExpando = */ false, Bailout_ShapeGuard);
13272 : }
13273 :
13274 : // Monomorphic receiver guards are not yet supported when the receiver
13275 : // is an unboxed object with an expando.
13276 : }
13277 :
13278 3 : MGuardReceiverPolymorphic* guard = MGuardReceiverPolymorphic::New(alloc(), obj);
13279 0 : current->add(guard);
13280 :
13281 3 : if (failedShapeGuard_)
13282 0 : guard->setNotMovable();
13283 :
13284 15 : for (size_t i = 0; i < receivers.length(); i++) {
13285 12 : if (!guard->addReceiver(receivers[i]))
13286 : return nullptr;
13287 : }
13288 :
13289 : return guard;
13290 : }
13291 :
13292 : MInstruction*
13293 0 : IonBuilder::addSharedTypedArrayGuard(MDefinition* obj)
13294 : {
13295 0 : MGuardSharedTypedArray* guard = MGuardSharedTypedArray::New(alloc(), obj);
13296 0 : current->add(guard);
13297 0 : return guard;
13298 : }
13299 :
13300 : TemporaryTypeSet*
13301 0 : IonBuilder::bytecodeTypes(jsbytecode* pc)
13302 : {
13303 10446 : return TypeScript::BytecodeTypes(script(), pc, bytecodeTypeMap, &typeArrayHint, typeArray);
13304 : }
13305 :
13306 : TypedObjectPrediction
13307 1409 : IonBuilder::typedObjectPrediction(MDefinition* typedObj)
13308 : {
13309 : // Extract TypedObjectPrediction directly if we can
13310 1409 : if (typedObj->isNewDerivedTypedObject()) {
13311 0 : return typedObj->toNewDerivedTypedObject()->prediction();
13312 : }
13313 :
13314 1409 : TemporaryTypeSet* types = typedObj->resultTypeSet();
13315 1409 : return typedObjectPrediction(types);
13316 : }
13317 :
13318 : TypedObjectPrediction
13319 1409 : IonBuilder::typedObjectPrediction(TemporaryTypeSet* types)
13320 : {
13321 : // Type set must be known to be an object.
13322 1409 : if (!types || types->getKnownMIRType() != MIRType::Object)
13323 : return TypedObjectPrediction();
13324 :
13325 : // And only known objects.
13326 2742 : if (types->unknownObject())
13327 : return TypedObjectPrediction();
13328 :
13329 0 : TypedObjectPrediction out;
13330 0 : for (uint32_t i = 0; i < types->getObjectCount(); i++) {
13331 765 : ObjectGroup* group = types->getGroup(i);
13332 1499 : if (!group || !IsTypedObjectClass(group->clasp()))
13333 : return TypedObjectPrediction();
13334 :
13335 0 : if (!TypeSet::ObjectKey::get(group)->hasStableClassAndProto(constraints()))
13336 : return TypedObjectPrediction();
13337 :
13338 0 : out.addDescr(group->typeDescr());
13339 : }
13340 :
13341 0 : return out;
13342 : }
13343 :
13344 : MDefinition*
13345 0 : IonBuilder::loadTypedObjectType(MDefinition* typedObj)
13346 : {
13347 : // Shortcircuit derived type objects, meaning the intermediate
13348 : // objects created to represent `a.b` in an expression like
13349 : // `a.b.c`. In that case, the type object can be simply pulled
13350 : // from the operands of that instruction.
13351 0 : if (typedObj->isNewDerivedTypedObject())
13352 0 : return typedObj->toNewDerivedTypedObject()->type();
13353 :
13354 0 : MInstruction* descr = MTypedObjectDescr::New(alloc(), typedObj);
13355 0 : current->add(descr);
13356 :
13357 0 : return descr;
13358 : }
13359 :
13360 : // Given a typed object `typedObj` and an offset `offset` into that
13361 : // object's data, returns another typed object and adusted offset
13362 : // where the data can be found. Often, these returned values are the
13363 : // same as the inputs, but in cases where intermediate derived type
13364 : // objects have been created, the return values will remove
13365 : // intermediate layers (often rendering those derived type objects
13366 : // into dead code).
13367 : AbortReasonOr<Ok>
13368 0 : IonBuilder::loadTypedObjectData(MDefinition* typedObj,
13369 : MDefinition** owner,
13370 : LinearSum* ownerOffset)
13371 : {
13372 0 : MOZ_ASSERT(typedObj->type() == MIRType::Object);
13373 :
13374 : // Shortcircuit derived type objects, meaning the intermediate
13375 : // objects created to represent `a.b` in an expression like
13376 : // `a.b.c`. In that case, the owned and a base offset can be
13377 : // pulled from the operands of the instruction and combined with
13378 : // `offset`.
13379 0 : if (typedObj->isNewDerivedTypedObject()) {
13380 0 : MNewDerivedTypedObject* ins = typedObj->toNewDerivedTypedObject();
13381 :
13382 0 : SimpleLinearSum base = ExtractLinearSum(ins->offset());
13383 0 : if (!ownerOffset->add(base))
13384 0 : return abort(AbortReason::Disable, "Overflow/underflow on type object offset.");
13385 :
13386 0 : *owner = ins->owner();
13387 0 : return Ok();
13388 : }
13389 :
13390 0 : *owner = typedObj;
13391 0 : return Ok();
13392 : }
13393 :
13394 : // Takes as input a typed object, an offset into that typed object's
13395 : // memory, and the type repr of the data found at that offset. Returns
13396 : // the elements pointer and a scaled offset. The scaled offset is
13397 : // expressed in units of `unit`; when working with typed array MIR,
13398 : // this is typically the alignment.
13399 : AbortReasonOr<Ok>
13400 0 : IonBuilder::loadTypedObjectElements(MDefinition* typedObj,
13401 : const LinearSum& baseByteOffset,
13402 : uint32_t scale,
13403 : MDefinition** ownerElements,
13404 : MDefinition** ownerScaledOffset,
13405 : int32_t* ownerByteAdjustment)
13406 : {
13407 : MDefinition* owner;
13408 0 : LinearSum ownerByteOffset(alloc());
13409 0 : MOZ_TRY(loadTypedObjectData(typedObj, &owner, &ownerByteOffset));
13410 :
13411 0 : if (!ownerByteOffset.add(baseByteOffset))
13412 0 : return abort(AbortReason::Disable, "Overflow after adding the base offset.");
13413 :
13414 0 : TemporaryTypeSet* ownerTypes = owner->resultTypeSet();
13415 0 : const Class* clasp = ownerTypes ? ownerTypes->getKnownClass(constraints()) : nullptr;
13416 0 : if (clasp && IsInlineTypedObjectClass(clasp)) {
13417 : // Perform the load directly from the owner pointer.
13418 0 : if (!ownerByteOffset.add(InlineTypedObject::offsetOfDataStart()))
13419 0 : return abort(AbortReason::Disable, "Overflow after adding the data start.");
13420 0 : *ownerElements = owner;
13421 : } else {
13422 0 : bool definitelyOutline = clasp && IsOutlineTypedObjectClass(clasp);
13423 0 : *ownerElements = MTypedObjectElements::New(alloc(), owner, definitelyOutline);
13424 0 : current->add((*ownerElements)->toInstruction());
13425 : }
13426 :
13427 : // Extract the constant adjustment from the byte offset.
13428 0 : *ownerByteAdjustment = ownerByteOffset.constant();
13429 : int32_t negativeAdjustment;
13430 0 : if (!SafeSub(0, *ownerByteAdjustment, &negativeAdjustment))
13431 0 : return abort(AbortReason::Disable);
13432 0 : if (!ownerByteOffset.add(negativeAdjustment))
13433 0 : return abort(AbortReason::Disable);
13434 :
13435 : // Scale the byte offset if required by the MIR node which will access the
13436 : // typed object. In principle we should always be able to cleanly divide
13437 : // the terms in this lienar sum due to alignment restrictions, but due to
13438 : // limitations of ExtractLinearSum when applied to the terms in derived
13439 : // typed objects this isn't always be possible. In these cases, fall back
13440 : // on an explicit division operation.
13441 0 : if (ownerByteOffset.divide(scale)) {
13442 0 : *ownerScaledOffset = ConvertLinearSum(alloc(), current, ownerByteOffset);
13443 : } else {
13444 0 : MDefinition* unscaledOffset = ConvertLinearSum(alloc(), current, ownerByteOffset);
13445 0 : *ownerScaledOffset = MDiv::New(alloc(), unscaledOffset, constantInt(scale),
13446 : MIRType::Int32, /* unsigned = */ false);
13447 0 : current->add((*ownerScaledOffset)->toInstruction());
13448 : }
13449 0 : return Ok();
13450 : }
13451 :
13452 : // Looks up the offset/type-repr-set of the field `id`, given the type
13453 : // set `objTypes` of the field owner. If a field is found, returns true
13454 : // and sets *fieldOffset, *fieldPrediction, and *fieldIndex. Returns false
13455 : // otherwise. Infallible.
13456 : bool
13457 1054 : IonBuilder::typedObjectHasField(MDefinition* typedObj,
13458 : PropertyName* name,
13459 : size_t* fieldOffset,
13460 : TypedObjectPrediction* fieldPrediction,
13461 : size_t* fieldIndex)
13462 : {
13463 1054 : TypedObjectPrediction objPrediction = typedObjectPrediction(typedObj);
13464 1054 : if (objPrediction.isUseless()) {
13465 : trackOptimizationOutcome(TrackedOutcome::AccessNotTypedObject);
13466 : return false;
13467 : }
13468 :
13469 : // Must be accessing a struct.
13470 0 : if (objPrediction.kind() != type::Struct) {
13471 : trackOptimizationOutcome(TrackedOutcome::NotStruct);
13472 : return false;
13473 : }
13474 :
13475 : // Determine the type/offset of the field `name`, if any.
13476 0 : if (!objPrediction.hasFieldNamed(NameToId(name), fieldOffset,
13477 : fieldPrediction, fieldIndex))
13478 : {
13479 : trackOptimizationOutcome(TrackedOutcome::StructNoField);
13480 : return false;
13481 : }
13482 :
13483 : return true;
13484 : }
13485 :
13486 : MDefinition*
13487 0 : IonBuilder::typeObjectForElementFromArrayStructType(MDefinition* typeObj)
13488 : {
13489 0 : MInstruction* elemType = MLoadFixedSlot::New(alloc(), typeObj, JS_DESCR_SLOT_ARRAY_ELEM_TYPE);
13490 0 : current->add(elemType);
13491 :
13492 0 : MInstruction* unboxElemType = MUnbox::New(alloc(), elemType, MIRType::Object, MUnbox::Infallible);
13493 0 : current->add(unboxElemType);
13494 :
13495 0 : return unboxElemType;
13496 : }
13497 :
13498 : MDefinition*
13499 0 : IonBuilder::typeObjectForFieldFromStructType(MDefinition* typeObj,
13500 : size_t fieldIndex)
13501 : {
13502 : // Load list of field type objects.
13503 :
13504 0 : MInstruction* fieldTypes = MLoadFixedSlot::New(alloc(), typeObj, JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
13505 0 : current->add(fieldTypes);
13506 :
13507 0 : MInstruction* unboxFieldTypes = MUnbox::New(alloc(), fieldTypes, MIRType::Object, MUnbox::Infallible);
13508 0 : current->add(unboxFieldTypes);
13509 :
13510 : // Index into list with index of field.
13511 :
13512 0 : MInstruction* fieldTypesElements = MElements::New(alloc(), unboxFieldTypes);
13513 0 : current->add(fieldTypesElements);
13514 :
13515 0 : MConstant* fieldIndexDef = constantInt(fieldIndex);
13516 :
13517 0 : MInstruction* fieldType = MLoadElement::New(alloc(), fieldTypesElements, fieldIndexDef, false, false);
13518 0 : current->add(fieldType);
13519 :
13520 0 : MInstruction* unboxFieldType = MUnbox::New(alloc(), fieldType, MIRType::Object, MUnbox::Infallible);
13521 0 : current->add(unboxFieldType);
13522 :
13523 0 : return unboxFieldType;
13524 : }
13525 :
13526 : AbortReasonOr<Ok>
13527 0 : IonBuilder::setPropTryScalarTypedObjectValue(bool* emitted,
13528 : MDefinition* typedObj,
13529 : const LinearSum& byteOffset,
13530 : ScalarTypeDescr::Type type,
13531 : MDefinition* value)
13532 : {
13533 0 : MOZ_ASSERT(!*emitted);
13534 :
13535 : // Find location within the owner object.
13536 : MDefinition* elements;
13537 : MDefinition* scaledOffset;
13538 : int32_t adjustment;
13539 0 : uint32_t alignment = ScalarTypeDescr::alignment(type);
13540 0 : MOZ_TRY(loadTypedObjectElements(typedObj, byteOffset, alignment,
13541 : &elements, &scaledOffset, &adjustment));
13542 :
13543 : // Clamp value to [0, 255] when type is Uint8Clamped
13544 0 : MDefinition* toWrite = value;
13545 0 : if (type == Scalar::Uint8Clamped) {
13546 0 : toWrite = MClampToUint8::New(alloc(), value);
13547 0 : current->add(toWrite->toInstruction());
13548 : }
13549 :
13550 : MStoreUnboxedScalar* store =
13551 0 : MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, toWrite,
13552 : type, MStoreUnboxedScalar::TruncateInput,
13553 0 : DoesNotRequireMemoryBarrier, adjustment);
13554 0 : current->add(store);
13555 0 : current->push(value);
13556 :
13557 0 : trackOptimizationSuccess();
13558 0 : *emitted = true;
13559 : return resumeAfter(store);
13560 : }
13561 :
13562 : AbortReasonOr<Ok>
13563 0 : IonBuilder::setPropTryReferenceTypedObjectValue(bool* emitted,
13564 : MDefinition* typedObj,
13565 : const LinearSum& byteOffset,
13566 : ReferenceTypeDescr::Type type,
13567 : MDefinition* value,
13568 : PropertyName* name)
13569 : {
13570 0 : MOZ_ASSERT(!*emitted);
13571 :
13572 : // Make sure we aren't adding new type information for writes of object and value
13573 : // references.
13574 0 : if (type != ReferenceTypeDescr::TYPE_STRING) {
13575 0 : MOZ_ASSERT(type == ReferenceTypeDescr::TYPE_ANY ||
13576 : type == ReferenceTypeDescr::TYPE_OBJECT);
13577 : MIRType implicitType =
13578 0 : (type == ReferenceTypeDescr::TYPE_ANY) ? MIRType::Undefined : MIRType::Null;
13579 :
13580 0 : if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &typedObj, name, &value,
13581 : /* canModify = */ true, implicitType))
13582 : {
13583 0 : trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
13584 0 : return Ok();
13585 : }
13586 : }
13587 :
13588 : // Find location within the owner object.
13589 : MDefinition* elements;
13590 : MDefinition* scaledOffset;
13591 : int32_t adjustment;
13592 0 : uint32_t alignment = ReferenceTypeDescr::alignment(type);
13593 0 : MOZ_TRY(loadTypedObjectElements(typedObj, byteOffset, alignment,
13594 : &elements, &scaledOffset, &adjustment));
13595 :
13596 0 : MInstruction* store = nullptr; // initialize to silence GCC warning
13597 0 : switch (type) {
13598 : case ReferenceTypeDescr::TYPE_ANY:
13599 0 : if (needsPostBarrier(value))
13600 0 : current->add(MPostWriteBarrier::New(alloc(), typedObj, value));
13601 0 : store = MStoreElement::New(alloc(), elements, scaledOffset, value, false, adjustment);
13602 0 : store->toStoreElement()->setNeedsBarrier();
13603 : break;
13604 : case ReferenceTypeDescr::TYPE_OBJECT:
13605 : // Note: We cannot necessarily tell at this point whether a post
13606 : // barrier is needed, because the type policy may insert ToObjectOrNull
13607 : // instructions later, and those may require a post barrier. Therefore,
13608 : // defer the insertion of post barriers to the type policy.
13609 0 : store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, typedObj, adjustment);
13610 0 : break;
13611 : case ReferenceTypeDescr::TYPE_STRING:
13612 : // See previous comment. The StoreUnboxedString type policy may insert
13613 : // ToString instructions that require a post barrier.
13614 0 : store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, typedObj, adjustment);
13615 0 : break;
13616 : }
13617 :
13618 0 : current->add(store);
13619 0 : current->push(value);
13620 :
13621 0 : trackOptimizationSuccess();
13622 0 : *emitted = true;
13623 : return resumeAfter(store);
13624 : }
13625 :
13626 : JSObject*
13627 3951 : IonBuilder::checkNurseryObject(JSObject* obj)
13628 : {
13629 : // If we try to use any nursery pointers during compilation, make sure that
13630 : // the main thread will cancel this compilation before performing a minor
13631 : // GC. All constants used during compilation should either go through this
13632 : // function or should come from a type set (which has a similar barrier).
13633 0 : if (obj && IsInsideNursery(obj)) {
13634 0 : realm->zone()->setMinorGCShouldCancelIonCompilations();
13635 0 : IonBuilder* builder = this;
13636 0 : while (builder) {
13637 0 : builder->setNotSafeForMinorGC();
13638 0 : builder = builder->callerBuilder_;
13639 : }
13640 : }
13641 :
13642 3951 : return obj;
13643 : }
13644 :
13645 : MConstant*
13646 0 : IonBuilder::constant(const Value& v)
13647 : {
13648 10738 : MOZ_ASSERT(!v.isString() || v.toString()->isAtom(),
13649 : "Handle non-atomized strings outside IonBuilder.");
13650 :
13651 9676 : if (v.isObject())
13652 0 : checkNurseryObject(&v.toObject());
13653 :
13654 0 : MConstant* c = MConstant::New(alloc(), v, constraints());
13655 9676 : current->add(c);
13656 9676 : return c;
13657 : }
13658 :
13659 : MConstant*
13660 0 : IonBuilder::constantInt(int32_t i)
13661 : {
13662 12 : return constant(Int32Value(i));
13663 : }
13664 :
13665 : MInstruction*
13666 0 : IonBuilder::initializedLength(MDefinition* elements)
13667 : {
13668 0 : MInstruction* res = MInitializedLength::New(alloc(), elements);
13669 96 : current->add(res);
13670 96 : return res;
13671 : }
13672 :
13673 : MInstruction*
13674 0 : IonBuilder::setInitializedLength(MDefinition* obj, size_t count)
13675 : {
13676 0 : MOZ_ASSERT(count);
13677 :
13678 : // MSetInitializedLength takes the index of the last element, rather
13679 : // than the count itself.
13680 0 : MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false);
13681 0 : current->add(elements);
13682 : MInstruction* res =
13683 0 : MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1)));
13684 0 : current->add(res);
13685 0 : return res;
13686 : }
13687 :
13688 : MDefinition*
13689 0 : IonBuilder::getCallee()
13690 : {
13691 0 : if (inliningDepth_ == 0) {
13692 0 : MInstruction* callee = MCallee::New(alloc());
13693 8 : current->add(callee);
13694 8 : return callee;
13695 : }
13696 :
13697 0 : return inlineCallInfo_->fun();
13698 : }
13699 :
13700 : AbortReasonOr<MDefinition*>
13701 0 : IonBuilder::addLexicalCheck(MDefinition* input)
13702 : {
13703 3 : MOZ_ASSERT(JSOp(*pc) == JSOP_CHECKLEXICAL ||
13704 : JSOp(*pc) == JSOP_CHECKALIASEDLEXICAL ||
13705 : JSOp(*pc) == JSOP_GETIMPORT);
13706 :
13707 : MInstruction* lexicalCheck;
13708 :
13709 : // If we're guaranteed to not be JS_UNINITIALIZED_LEXICAL, no need to check.
13710 3 : if (input->type() == MIRType::MagicUninitializedLexical) {
13711 : // Mark the input as implicitly used so the JS_UNINITIALIZED_LEXICAL
13712 : // magic value will be preserved on bailout.
13713 0 : input->setImplicitlyUsedUnchecked();
13714 0 : lexicalCheck = MThrowRuntimeLexicalError::New(alloc(), JSMSG_UNINITIALIZED_LEXICAL);
13715 0 : current->add(lexicalCheck);
13716 0 : MOZ_TRY(resumeAfter(lexicalCheck));
13717 0 : return constant(UndefinedValue());
13718 : }
13719 :
13720 0 : if (input->type() == MIRType::Value) {
13721 0 : lexicalCheck = MLexicalCheck::New(alloc(), input);
13722 0 : current->add(lexicalCheck);
13723 0 : if (failedLexicalCheck_)
13724 0 : lexicalCheck->setNotMovableUnchecked();
13725 3 : return lexicalCheck;
13726 : }
13727 :
13728 0 : return input;
13729 : }
13730 :
13731 : MDefinition*
13732 0 : IonBuilder::convertToBoolean(MDefinition* input)
13733 : {
13734 : // Convert to bool with the '!!' idiom
13735 0 : MNot* resultInverted = MNot::New(alloc(), input, constraints());
13736 0 : current->add(resultInverted);
13737 0 : MNot* result = MNot::New(alloc(), resultInverted, constraints());
13738 0 : current->add(result);
13739 :
13740 0 : return result;
13741 : }
13742 :
13743 : void
13744 0 : IonBuilder::trace(JSTracer* trc)
13745 : {
13746 0 : if (!realm->runtime()->runtimeMatches(trc->runtime()))
13747 : return;
13748 :
13749 0 : MOZ_ASSERT(rootList_);
13750 0 : rootList_->trace(trc);
13751 : }
13752 :
13753 : size_t
13754 0 : IonBuilder::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
13755 : {
13756 : // See js::jit::FreeIonBuilder.
13757 : // The IonBuilder and most of its contents live in the LifoAlloc we point
13758 : // to. Note that this is only true for background IonBuilders.
13759 :
13760 0 : size_t result = alloc_->lifoAlloc()->sizeOfIncludingThis(mallocSizeOf);
13761 :
13762 0 : if (backgroundCodegen_)
13763 0 : result += mallocSizeOf(backgroundCodegen_);
13764 :
13765 : return result;
13766 : }
|