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 "jsmath.h"
8 
9 #include "builtin/AtomicsObject.h"
10 #include "builtin/SIMD.h"
11 #include "builtin/TestingFunctions.h"
12 #include "builtin/TypedObject.h"
13 #include "jit/BaselineInspector.h"
14 #include "jit/InlinableNatives.h"
15 #include "jit/IonBuilder.h"
16 #include "jit/Lowering.h"
17 #include "jit/MIR.h"
18 #include "jit/MIRGraph.h"
19 #include "vm/ArgumentsObject.h"
20 
21 #include "jsscriptinlines.h"
22 
23 #include "jit/shared/Lowering-shared-inl.h"
24 #include "vm/NativeObject-inl.h"
25 #include "vm/StringObject-inl.h"
26 #include "vm/UnboxedObject-inl.h"
27 
28 using mozilla::ArrayLength;
29 
30 using JS::DoubleNaNValue;
31 using JS::TrackedOutcome;
32 using JS::TrackedStrategy;
33 using JS::TrackedTypeSite;
34 
35 namespace js {
36 namespace jit {
37 
38 IonBuilder::InliningStatus
inlineNativeCall(CallInfo & callInfo,JSFunction * target)39 IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
40 {
41     MOZ_ASSERT(target->isNative());
42 
43     if (!optimizationInfo().inlineNative()) {
44         trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
45         return InliningStatus_NotInlined;
46     }
47 
48     if (!target->jitInfo() || target->jitInfo()->type() != JSJitInfo::InlinableNative) {
49         // Reaching here means we tried to inline a native for which there is no
50         // Ion specialization.
51         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoSpecialization);
52         return InliningStatus_NotInlined;
53     }
54 
55     // Default failure reason is observing an unsupported type.
56     trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType);
57 
58     if (shouldAbortOnPreliminaryGroups(callInfo.thisArg()))
59         return InliningStatus_NotInlined;
60     for (size_t i = 0; i < callInfo.argc(); i++) {
61         if (shouldAbortOnPreliminaryGroups(callInfo.getArg(i)))
62             return InliningStatus_NotInlined;
63     }
64 
65     switch (InlinableNative inlNative = target->jitInfo()->inlinableNative) {
66       // Array natives.
67       case InlinableNative::Array:
68         return inlineArray(callInfo);
69       case InlinableNative::ArrayIsArray:
70         return inlineArrayIsArray(callInfo);
71       case InlinableNative::ArrayPop:
72         return inlineArrayPopShift(callInfo, MArrayPopShift::Pop);
73       case InlinableNative::ArrayShift:
74         return inlineArrayPopShift(callInfo, MArrayPopShift::Shift);
75       case InlinableNative::ArrayPush:
76         return inlineArrayPush(callInfo);
77       case InlinableNative::ArrayConcat:
78         return inlineArrayConcat(callInfo);
79       case InlinableNative::ArraySlice:
80         return inlineArraySlice(callInfo);
81       case InlinableNative::ArraySplice:
82         return inlineArraySplice(callInfo);
83 
84       // Atomic natives.
85       case InlinableNative::AtomicsCompareExchange:
86         return inlineAtomicsCompareExchange(callInfo);
87       case InlinableNative::AtomicsExchange:
88         return inlineAtomicsExchange(callInfo);
89       case InlinableNative::AtomicsLoad:
90         return inlineAtomicsLoad(callInfo);
91       case InlinableNative::AtomicsStore:
92         return inlineAtomicsStore(callInfo);
93       case InlinableNative::AtomicsFence:
94         return inlineAtomicsFence(callInfo);
95       case InlinableNative::AtomicsAdd:
96       case InlinableNative::AtomicsSub:
97       case InlinableNative::AtomicsAnd:
98       case InlinableNative::AtomicsOr:
99       case InlinableNative::AtomicsXor:
100         return inlineAtomicsBinop(callInfo, inlNative);
101       case InlinableNative::AtomicsIsLockFree:
102         return inlineAtomicsIsLockFree(callInfo);
103 
104       // Math natives.
105       case InlinableNative::MathAbs:
106         return inlineMathAbs(callInfo);
107       case InlinableNative::MathFloor:
108         return inlineMathFloor(callInfo);
109       case InlinableNative::MathCeil:
110         return inlineMathCeil(callInfo);
111       case InlinableNative::MathRound:
112         return inlineMathRound(callInfo);
113       case InlinableNative::MathClz32:
114         return inlineMathClz32(callInfo);
115       case InlinableNative::MathSqrt:
116         return inlineMathSqrt(callInfo);
117       case InlinableNative::MathATan2:
118         return inlineMathAtan2(callInfo);
119       case InlinableNative::MathHypot:
120         return inlineMathHypot(callInfo);
121       case InlinableNative::MathMax:
122         return inlineMathMinMax(callInfo, true /* max */);
123       case InlinableNative::MathMin:
124         return inlineMathMinMax(callInfo, false /* max */);
125       case InlinableNative::MathPow:
126         return inlineMathPow(callInfo);
127       case InlinableNative::MathRandom:
128         return inlineMathRandom(callInfo);
129       case InlinableNative::MathImul:
130         return inlineMathImul(callInfo);
131       case InlinableNative::MathFRound:
132         return inlineMathFRound(callInfo);
133       case InlinableNative::MathSin:
134         return inlineMathFunction(callInfo, MMathFunction::Sin);
135       case InlinableNative::MathTan:
136         return inlineMathFunction(callInfo, MMathFunction::Tan);
137       case InlinableNative::MathCos:
138         return inlineMathFunction(callInfo, MMathFunction::Cos);
139       case InlinableNative::MathExp:
140         return inlineMathFunction(callInfo, MMathFunction::Exp);
141       case InlinableNative::MathLog:
142         return inlineMathFunction(callInfo, MMathFunction::Log);
143       case InlinableNative::MathASin:
144         return inlineMathFunction(callInfo, MMathFunction::ASin);
145       case InlinableNative::MathATan:
146         return inlineMathFunction(callInfo, MMathFunction::ATan);
147       case InlinableNative::MathACos:
148         return inlineMathFunction(callInfo, MMathFunction::ACos);
149       case InlinableNative::MathLog10:
150         return inlineMathFunction(callInfo, MMathFunction::Log10);
151       case InlinableNative::MathLog2:
152         return inlineMathFunction(callInfo, MMathFunction::Log2);
153       case InlinableNative::MathLog1P:
154         return inlineMathFunction(callInfo, MMathFunction::Log1P);
155       case InlinableNative::MathExpM1:
156         return inlineMathFunction(callInfo, MMathFunction::ExpM1);
157       case InlinableNative::MathCosH:
158         return inlineMathFunction(callInfo, MMathFunction::CosH);
159       case InlinableNative::MathSinH:
160         return inlineMathFunction(callInfo, MMathFunction::SinH);
161       case InlinableNative::MathTanH:
162         return inlineMathFunction(callInfo, MMathFunction::TanH);
163       case InlinableNative::MathACosH:
164         return inlineMathFunction(callInfo, MMathFunction::ACosH);
165       case InlinableNative::MathASinH:
166         return inlineMathFunction(callInfo, MMathFunction::ASinH);
167       case InlinableNative::MathATanH:
168         return inlineMathFunction(callInfo, MMathFunction::ATanH);
169       case InlinableNative::MathSign:
170         return inlineMathFunction(callInfo, MMathFunction::Sign);
171       case InlinableNative::MathTrunc:
172         return inlineMathFunction(callInfo, MMathFunction::Trunc);
173       case InlinableNative::MathCbrt:
174         return inlineMathFunction(callInfo, MMathFunction::Cbrt);
175 
176       // RegExp natives.
177       case InlinableNative::RegExpExec:
178         return CallResultEscapes(pc) ? inlineRegExpExec(callInfo) : inlineRegExpTest(callInfo);
179       case InlinableNative::RegExpTest:
180         return inlineRegExpTest(callInfo);
181 
182       // String natives.
183       case InlinableNative::String:
184         return inlineStringObject(callInfo);
185       case InlinableNative::StringSplit:
186         return inlineStringSplit(callInfo);
187       case InlinableNative::StringCharCodeAt:
188         return inlineStrCharCodeAt(callInfo);
189       case InlinableNative::StringFromCharCode:
190         return inlineStrFromCharCode(callInfo);
191       case InlinableNative::StringCharAt:
192         return inlineStrCharAt(callInfo);
193       case InlinableNative::StringReplace:
194         return inlineStrReplace(callInfo);
195 
196       // Object natives.
197       case InlinableNative::ObjectCreate:
198         return inlineObjectCreate(callInfo);
199 
200       // Bound function.
201       case InlinableNative::CallBoundFunction:
202         return inlineBoundFunction(callInfo, target);
203 
204       // SIMD natives.
205       case InlinableNative::SimdInt32x4:
206         return inlineSimdInt32x4(callInfo, target->native());
207       case InlinableNative::SimdFloat32x4:
208         return inlineSimdFloat32x4(callInfo, target->native());
209 
210       // Testing functions.
211       case InlinableNative::TestBailout:
212         return inlineBailout(callInfo);
213       case InlinableNative::TestAssertFloat32:
214         return inlineAssertFloat32(callInfo);
215       case InlinableNative::TestAssertRecoveredOnBailout:
216         return inlineAssertRecoveredOnBailout(callInfo);
217 
218       // Slot intrinsics.
219       case InlinableNative::IntrinsicUnsafeSetReservedSlot:
220         return inlineUnsafeSetReservedSlot(callInfo);
221       case InlinableNative::IntrinsicUnsafeGetReservedSlot:
222         return inlineUnsafeGetReservedSlot(callInfo, MIRType_Value);
223       case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot:
224         return inlineUnsafeGetReservedSlot(callInfo, MIRType_Object);
225       case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot:
226         return inlineUnsafeGetReservedSlot(callInfo, MIRType_Int32);
227       case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot:
228         return inlineUnsafeGetReservedSlot(callInfo, MIRType_String);
229       case InlinableNative::IntrinsicUnsafeGetBooleanFromReservedSlot:
230         return inlineUnsafeGetReservedSlot(callInfo, MIRType_Boolean);
231 
232       // Utility intrinsics.
233       case InlinableNative::IntrinsicIsCallable:
234         return inlineIsCallable(callInfo);
235       case InlinableNative::IntrinsicToObject:
236         return inlineToObject(callInfo);
237       case InlinableNative::IntrinsicIsObject:
238         return inlineIsObject(callInfo);
239       case InlinableNative::IntrinsicToInteger:
240         return inlineToInteger(callInfo);
241       case InlinableNative::IntrinsicToString:
242         return inlineToString(callInfo);
243       case InlinableNative::IntrinsicIsConstructing:
244         return inlineIsConstructing(callInfo);
245       case InlinableNative::IntrinsicSubstringKernel:
246         return inlineSubstringKernel(callInfo);
247       case InlinableNative::IntrinsicIsArrayIterator:
248         return inlineHasClass(callInfo, &ArrayIteratorObject::class_);
249       case InlinableNative::IntrinsicIsMapIterator:
250         return inlineHasClass(callInfo, &MapIteratorObject::class_);
251       case InlinableNative::IntrinsicIsStringIterator:
252         return inlineHasClass(callInfo, &StringIteratorObject::class_);
253       case InlinableNative::IntrinsicIsListIterator:
254         return inlineHasClass(callInfo, &ListIteratorObject::class_);
255       case InlinableNative::IntrinsicDefineDataProperty:
256         return inlineDefineDataProperty(callInfo);
257 
258       // TypedArray intrinsics.
259       case InlinableNative::IntrinsicIsTypedArray:
260         return inlineIsTypedArray(callInfo);
261       case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray:
262         return inlineIsPossiblyWrappedTypedArray(callInfo);
263       case InlinableNative::IntrinsicTypedArrayLength:
264         return inlineTypedArrayLength(callInfo);
265       case InlinableNative::IntrinsicSetDisjointTypedElements:
266         return inlineSetDisjointTypedElements(callInfo);
267 
268       // TypedObject intrinsics.
269       case InlinableNative::IntrinsicObjectIsTypedObject:
270         return inlineHasClass(callInfo,
271                               &OutlineTransparentTypedObject::class_,
272                               &OutlineOpaqueTypedObject::class_,
273                               &InlineTransparentTypedObject::class_,
274                               &InlineOpaqueTypedObject::class_);
275       case InlinableNative::IntrinsicObjectIsTransparentTypedObject:
276         return inlineHasClass(callInfo,
277                               &OutlineTransparentTypedObject::class_,
278                               &InlineTransparentTypedObject::class_);
279       case InlinableNative::IntrinsicObjectIsOpaqueTypedObject:
280         return inlineHasClass(callInfo,
281                               &OutlineOpaqueTypedObject::class_,
282                               &InlineOpaqueTypedObject::class_);
283       case InlinableNative::IntrinsicObjectIsTypeDescr:
284         return inlineObjectIsTypeDescr(callInfo);
285       case InlinableNative::IntrinsicTypeDescrIsSimpleType:
286         return inlineHasClass(callInfo,
287                               &ScalarTypeDescr::class_, &ReferenceTypeDescr::class_);
288       case InlinableNative::IntrinsicTypeDescrIsArrayType:
289         return inlineHasClass(callInfo, &ArrayTypeDescr::class_);
290       case InlinableNative::IntrinsicSetTypedObjectOffset:
291         return inlineSetTypedObjectOffset(callInfo);
292     }
293 
294     MOZ_CRASH("Shouldn't get here");
295 }
296 
297 IonBuilder::InliningStatus
inlineNativeGetter(CallInfo & callInfo,JSFunction * target)298 IonBuilder::inlineNativeGetter(CallInfo& callInfo, JSFunction* target)
299 {
300     MOZ_ASSERT(target->isNative());
301     JSNative native = target->native();
302 
303     if (!optimizationInfo().inlineNative())
304         return InliningStatus_NotInlined;
305 
306     TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
307     MOZ_ASSERT(callInfo.argc() == 0);
308 
309     // Try to optimize typed array lengths.
310     if (thisTypes) {
311         Scalar::Type type;
312 
313         type = thisTypes->getTypedArrayType(constraints());
314         if (type != Scalar::MaxTypedArrayViewType &&
315             TypedArrayObject::isOriginalLengthGetter(native))
316         {
317             MInstruction* length = addTypedArrayLength(callInfo.thisArg());
318             current->push(length);
319             return InliningStatus_Inlined;
320         }
321     }
322 
323     return InliningStatus_NotInlined;
324 }
325 
326 IonBuilder::InliningStatus
inlineNonFunctionCall(CallInfo & callInfo,JSObject * target)327 IonBuilder::inlineNonFunctionCall(CallInfo& callInfo, JSObject* target)
328 {
329     // Inline a call to a non-function object, invoking the object's call or
330     // construct hook.
331 
332     if (callInfo.constructing() && target->constructHook() == TypedObject::construct)
333         return inlineConstructTypedObject(callInfo, &target->as<TypeDescr>());
334 
335     if (!callInfo.constructing() && target->callHook() == SimdTypeDescr::call)
336         return inlineConstructSimdObject(callInfo, &target->as<SimdTypeDescr>());
337 
338     return InliningStatus_NotInlined;
339 }
340 
341 TemporaryTypeSet*
getInlineReturnTypeSet()342 IonBuilder::getInlineReturnTypeSet()
343 {
344     return bytecodeTypes(pc);
345 }
346 
347 MIRType
getInlineReturnType()348 IonBuilder::getInlineReturnType()
349 {
350     TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
351     return returnTypes->getKnownMIRType();
352 }
353 
354 IonBuilder::InliningStatus
inlineMathFunction(CallInfo & callInfo,MMathFunction::Function function)355 IonBuilder::inlineMathFunction(CallInfo& callInfo, MMathFunction::Function function)
356 {
357     if (callInfo.constructing())
358         return InliningStatus_NotInlined;
359 
360     if (callInfo.argc() != 1)
361         return InliningStatus_NotInlined;
362 
363     if (getInlineReturnType() != MIRType_Double)
364         return InliningStatus_NotInlined;
365     if (!IsNumberType(callInfo.getArg(0)->type()))
366         return InliningStatus_NotInlined;
367 
368     const MathCache* cache = compartment->runtime()->maybeGetMathCache();
369 
370     callInfo.fun()->setImplicitlyUsedUnchecked();
371     callInfo.thisArg()->setImplicitlyUsedUnchecked();
372 
373     MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), function, cache);
374     current->add(ins);
375     current->push(ins);
376     return InliningStatus_Inlined;
377 }
378 
379 IonBuilder::InliningStatus
inlineArray(CallInfo & callInfo)380 IonBuilder::inlineArray(CallInfo& callInfo)
381 {
382     uint32_t initLength = 0;
383 
384     JSObject* templateObject = inspector->getTemplateObjectForNative(pc, ArrayConstructor);
385     if (!templateObject) {
386         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoTemplateObj);
387         return InliningStatus_NotInlined;
388     }
389 
390     if (templateObject->is<UnboxedArrayObject>()) {
391         if (templateObject->group()->unboxedLayout().nativeGroup())
392             return InliningStatus_NotInlined;
393     }
394 
395     // Multiple arguments imply array initialization, not just construction.
396     if (callInfo.argc() >= 2) {
397         initLength = callInfo.argc();
398 
399         TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(templateObject);
400         if (!key->unknownProperties()) {
401             HeapTypeSetKey elemTypes = key->property(JSID_VOID);
402 
403             for (uint32_t i = 0; i < initLength; i++) {
404                 MDefinition* value = callInfo.getArg(i);
405                 if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
406                     elemTypes.freeze(constraints());
407                     return InliningStatus_NotInlined;
408                 }
409             }
410         }
411     }
412 
413     // A single integer argument denotes initial length.
414     if (callInfo.argc() == 1) {
415         if (callInfo.getArg(0)->type() != MIRType_Int32)
416             return InliningStatus_NotInlined;
417 
418         MDefinition* arg = callInfo.getArg(0);
419         if (!arg->isConstantValue()) {
420             callInfo.setImplicitlyUsedUnchecked();
421             MNewArrayDynamicLength* ins =
422                 MNewArrayDynamicLength::New(alloc(), constraints(), templateObject,
423                                             templateObject->group()->initialHeap(constraints()),
424                                             arg);
425             current->add(ins);
426             current->push(ins);
427             return InliningStatus_Inlined;
428         }
429 
430         // The next several checks all may fail due to range conditions.
431         trackOptimizationOutcome(TrackedOutcome::ArrayRange);
432 
433         // Negative lengths generate a RangeError, unhandled by the inline path.
434         initLength = arg->constantValue().toInt32();
435         if (initLength > NativeObject::MAX_DENSE_ELEMENTS_COUNT)
436             return InliningStatus_NotInlined;
437         MOZ_ASSERT(initLength <= INT32_MAX);
438 
439         // Make sure initLength matches the template object's length. This is
440         // not guaranteed to be the case, for instance if we're inlining the
441         // MConstant may come from an outer script.
442         if (initLength != GetAnyBoxedOrUnboxedArrayLength(templateObject))
443             return InliningStatus_NotInlined;
444 
445         // Don't inline large allocations.
446         if (initLength > ArrayObject::EagerAllocationMaxLength)
447             return InliningStatus_NotInlined;
448     }
449 
450     callInfo.setImplicitlyUsedUnchecked();
451 
452     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
453     current->add(templateConst);
454 
455     MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
456                                     templateObject->group()->initialHeap(constraints()), pc);
457     current->add(ins);
458     current->push(ins);
459 
460     if (callInfo.argc() >= 2) {
461         JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
462         for (uint32_t i = 0; i < initLength; i++) {
463             MDefinition* value = callInfo.getArg(i);
464             if (!initializeArrayElement(ins, i, value, unboxedType, /* addResumePoint = */ false))
465                 return InliningStatus_Error;
466         }
467 
468         MInstruction* setLength = setInitializedLength(ins, unboxedType, initLength);
469         if (!resumeAfter(setLength))
470             return InliningStatus_Error;
471     }
472 
473     return InliningStatus_Inlined;
474 }
475 
476 IonBuilder::InliningStatus
inlineArrayIsArray(CallInfo & callInfo)477 IonBuilder::inlineArrayIsArray(CallInfo& callInfo)
478 {
479     if (callInfo.constructing() || callInfo.argc() != 1) {
480         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
481         return InliningStatus_NotInlined;
482     }
483 
484     if (getInlineReturnType() != MIRType_Boolean)
485         return InliningStatus_NotInlined;
486 
487     MDefinition* arg = callInfo.getArg(0);
488 
489     bool isArray;
490     if (!arg->mightBeType(MIRType_Object)) {
491         isArray = false;
492     } else {
493         if (arg->type() != MIRType_Object)
494             return InliningStatus_NotInlined;
495 
496         TemporaryTypeSet* types = arg->resultTypeSet();
497         const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
498         if (!clasp || clasp->isProxy())
499             return InliningStatus_NotInlined;
500 
501         isArray = (clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_);
502     }
503 
504     pushConstant(BooleanValue(isArray));
505 
506     callInfo.setImplicitlyUsedUnchecked();
507     return InliningStatus_Inlined;
508 }
509 
510 IonBuilder::InliningStatus
inlineArrayPopShift(CallInfo & callInfo,MArrayPopShift::Mode mode)511 IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
512 {
513     if (callInfo.constructing()) {
514         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
515         return InliningStatus_NotInlined;
516     }
517 
518     MIRType returnType = getInlineReturnType();
519     if (returnType == MIRType_Undefined || returnType == MIRType_Null)
520         return InliningStatus_NotInlined;
521     if (callInfo.thisArg()->type() != MIRType_Object)
522         return InliningStatus_NotInlined;
523 
524     // Pop and shift are only handled for dense arrays that have never been
525     // used in an iterator: popping elements does not account for suppressing
526     // deleted properties in active iterators.
527     ObjectGroupFlags unhandledFlags =
528         OBJECT_FLAG_SPARSE_INDEXES |
529         OBJECT_FLAG_LENGTH_OVERFLOW |
530         OBJECT_FLAG_ITERATED;
531 
532     MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
533     TemporaryTypeSet* thisTypes = obj->resultTypeSet();
534     if (!thisTypes)
535         return InliningStatus_NotInlined;
536     const Class* clasp = thisTypes->getKnownClass(constraints());
537     if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
538         return InliningStatus_NotInlined;
539     if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
540         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
541         return InliningStatus_NotInlined;
542     }
543 
544     if (ArrayPrototypeHasIndexedProperty(this, script())) {
545         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
546         return InliningStatus_NotInlined;
547     }
548 
549     JSValueType unboxedType = JSVAL_TYPE_MAGIC;
550     if (clasp == &UnboxedArrayObject::class_) {
551         unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
552         if (unboxedType == JSVAL_TYPE_MAGIC)
553             return InliningStatus_NotInlined;
554     }
555 
556     callInfo.setImplicitlyUsedUnchecked();
557 
558     if (clasp == &ArrayObject::class_)
559         obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
560 
561     TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
562     bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED);
563     bool maybeUndefined = returnTypes->hasType(TypeSet::UndefinedType());
564 
565     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
566                                                        obj, nullptr, returnTypes);
567     if (barrier != BarrierKind::NoBarrier)
568         returnType = MIRType_Value;
569 
570     MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode,
571                                               unboxedType, needsHoleCheck, maybeUndefined);
572     current->add(ins);
573     current->push(ins);
574     ins->setResultType(returnType);
575 
576     if (!resumeAfter(ins))
577         return InliningStatus_Error;
578 
579     if (!pushTypeBarrier(ins, returnTypes, barrier))
580         return InliningStatus_Error;
581 
582     return InliningStatus_Inlined;
583 }
584 
585 IonBuilder::InliningStatus
inlineArraySplice(CallInfo & callInfo)586 IonBuilder::inlineArraySplice(CallInfo& callInfo)
587 {
588     if (callInfo.argc() != 2 || callInfo.constructing()) {
589         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
590         return InliningStatus_NotInlined;
591     }
592 
593     // Ensure |this|, argument and result are objects.
594     if (getInlineReturnType() != MIRType_Object)
595         return InliningStatus_NotInlined;
596     if (callInfo.thisArg()->type() != MIRType_Object)
597         return InliningStatus_NotInlined;
598     if (callInfo.getArg(0)->type() != MIRType_Int32)
599         return InliningStatus_NotInlined;
600     if (callInfo.getArg(1)->type() != MIRType_Int32)
601         return InliningStatus_NotInlined;
602 
603     callInfo.setImplicitlyUsedUnchecked();
604 
605     // Specialize arr.splice(start, deleteCount) with unused return value and
606     // avoid creating the result array in this case.
607     if (!BytecodeIsPopped(pc)) {
608         trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
609         return InliningStatus_NotInlined;
610     }
611 
612     MArraySplice* ins = MArraySplice::New(alloc(),
613                                           callInfo.thisArg(),
614                                           callInfo.getArg(0),
615                                           callInfo.getArg(1));
616 
617     current->add(ins);
618     pushConstant(UndefinedValue());
619 
620     if (!resumeAfter(ins))
621         return InliningStatus_Error;
622     return InliningStatus_Inlined;
623 }
624 
625 IonBuilder::InliningStatus
inlineArrayJoin(CallInfo & callInfo)626 IonBuilder::inlineArrayJoin(CallInfo& callInfo)
627 {
628     if (callInfo.argc() != 1 || callInfo.constructing()) {
629         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
630         return InliningStatus_NotInlined;
631     }
632 
633     if (getInlineReturnType() != MIRType_String)
634         return InliningStatus_NotInlined;
635     if (callInfo.thisArg()->type() != MIRType_Object)
636         return InliningStatus_NotInlined;
637     if (callInfo.getArg(0)->type() != MIRType_String)
638         return InliningStatus_NotInlined;
639 
640     callInfo.setImplicitlyUsedUnchecked();
641 
642     MArrayJoin* ins = MArrayJoin::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
643 
644     current->add(ins);
645     current->push(ins);
646 
647     return InliningStatus_Inlined;
648 }
649 
650 IonBuilder::InliningStatus
inlineArrayPush(CallInfo & callInfo)651 IonBuilder::inlineArrayPush(CallInfo& callInfo)
652 {
653     if (callInfo.argc() != 1 || callInfo.constructing()) {
654         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
655         return InliningStatus_NotInlined;
656     }
657 
658     MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
659     MDefinition* value = callInfo.getArg(0);
660     if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
661                                       &obj, nullptr, &value, /* canModify = */ false))
662     {
663         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
664         return InliningStatus_NotInlined;
665     }
666 
667     if (getInlineReturnType() != MIRType_Int32)
668         return InliningStatus_NotInlined;
669     if (obj->type() != MIRType_Object)
670         return InliningStatus_NotInlined;
671 
672     TemporaryTypeSet* thisTypes = obj->resultTypeSet();
673     if (!thisTypes)
674         return InliningStatus_NotInlined;
675     const Class* clasp = thisTypes->getKnownClass(constraints());
676     if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
677         return InliningStatus_NotInlined;
678     if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
679                                   OBJECT_FLAG_LENGTH_OVERFLOW))
680     {
681         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
682         return InliningStatus_NotInlined;
683     }
684 
685     if (ArrayPrototypeHasIndexedProperty(this, script())) {
686         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
687         return InliningStatus_NotInlined;
688     }
689 
690     TemporaryTypeSet::DoubleConversion conversion =
691         thisTypes->convertDoubleElements(constraints());
692     if (conversion == TemporaryTypeSet::AmbiguousDoubleConversion) {
693         trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion);
694         return InliningStatus_NotInlined;
695     }
696 
697     JSValueType unboxedType = JSVAL_TYPE_MAGIC;
698     if (clasp == &UnboxedArrayObject::class_) {
699         unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
700         if (unboxedType == JSVAL_TYPE_MAGIC)
701             return InliningStatus_NotInlined;
702     }
703 
704     callInfo.setImplicitlyUsedUnchecked();
705 
706     if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles ||
707         conversion == TemporaryTypeSet::MaybeConvertToDoubles)
708     {
709         MInstruction* valueDouble = MToDouble::New(alloc(), value);
710         current->add(valueDouble);
711         value = valueDouble;
712     }
713 
714     if (unboxedType == JSVAL_TYPE_MAGIC)
715         obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
716 
717     if (NeedsPostBarrier(value))
718         current->add(MPostWriteBarrier::New(alloc(), obj, value));
719 
720     MArrayPush* ins = MArrayPush::New(alloc(), obj, value, unboxedType);
721     current->add(ins);
722     current->push(ins);
723 
724     if (!resumeAfter(ins))
725         return InliningStatus_Error;
726     return InliningStatus_Inlined;
727 }
728 
729 IonBuilder::InliningStatus
inlineArrayConcat(CallInfo & callInfo)730 IonBuilder::inlineArrayConcat(CallInfo& callInfo)
731 {
732     if (callInfo.argc() != 1 || callInfo.constructing()) {
733         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
734         return InliningStatus_NotInlined;
735     }
736 
737     MDefinition* thisArg = convertUnboxedObjects(callInfo.thisArg());
738     MDefinition* objArg = convertUnboxedObjects(callInfo.getArg(0));
739 
740     // Ensure |this|, argument and result are objects.
741     if (getInlineReturnType() != MIRType_Object)
742         return InliningStatus_NotInlined;
743     if (thisArg->type() != MIRType_Object)
744         return InliningStatus_NotInlined;
745     if (objArg->type() != MIRType_Object)
746         return InliningStatus_NotInlined;
747 
748     // |this| and the argument must be dense arrays.
749     TemporaryTypeSet* thisTypes = thisArg->resultTypeSet();
750     TemporaryTypeSet* argTypes = objArg->resultTypeSet();
751     if (!thisTypes || !argTypes)
752         return InliningStatus_NotInlined;
753 
754     const Class* thisClasp = thisTypes->getKnownClass(constraints());
755     if (thisClasp != &ArrayObject::class_ && thisClasp != &UnboxedArrayObject::class_)
756         return InliningStatus_NotInlined;
757     bool unboxedThis = (thisClasp == &UnboxedArrayObject::class_);
758     if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
759                                   OBJECT_FLAG_LENGTH_OVERFLOW))
760     {
761         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
762         return InliningStatus_NotInlined;
763     }
764 
765     const Class* argClasp = argTypes->getKnownClass(constraints());
766     if (argClasp != &ArrayObject::class_ && argClasp != &UnboxedArrayObject::class_)
767         return InliningStatus_NotInlined;
768     bool unboxedArg = (argClasp == &UnboxedArrayObject::class_);
769     if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
770                                  OBJECT_FLAG_LENGTH_OVERFLOW))
771     {
772         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
773         return InliningStatus_NotInlined;
774     }
775 
776     // Watch out for indexed properties on the prototype.
777     if (ArrayPrototypeHasIndexedProperty(this, script())) {
778         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
779         return InliningStatus_NotInlined;
780     }
781 
782     // Require the 'this' types to have a specific type matching the current
783     // global, so we can create the result object inline.
784     if (thisTypes->getObjectCount() != 1)
785         return InliningStatus_NotInlined;
786 
787     ObjectGroup* thisGroup = thisTypes->getGroup(0);
788     if (!thisGroup)
789         return InliningStatus_NotInlined;
790     TypeSet::ObjectKey* thisKey = TypeSet::ObjectKey::get(thisGroup);
791     if (thisKey->unknownProperties())
792         return InliningStatus_NotInlined;
793 
794     // Don't inline if 'this' is packed and the argument may not be packed
795     // (the result array will reuse the 'this' type).
796     if (!thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED) &&
797         argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED))
798     {
799         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
800         return InliningStatus_NotInlined;
801     }
802 
803     // Constraints modeling this concat have not been generated by inference,
804     // so check that type information already reflects possible side effects of
805     // this call.
806     HeapTypeSetKey thisElemTypes = thisKey->property(JSID_VOID);
807 
808     TemporaryTypeSet* resTypes = getInlineReturnTypeSet();
809     if (!resTypes->hasType(TypeSet::ObjectType(thisKey)))
810         return InliningStatus_NotInlined;
811 
812     for (unsigned i = 0; i < argTypes->getObjectCount(); i++) {
813         TypeSet::ObjectKey* key = argTypes->getObject(i);
814         if (!key)
815             continue;
816 
817         if (key->unknownProperties())
818             return InliningStatus_NotInlined;
819 
820         HeapTypeSetKey elemTypes = key->property(JSID_VOID);
821         if (!elemTypes.knownSubset(constraints(), thisElemTypes))
822             return InliningStatus_NotInlined;
823 
824         if (thisGroup->clasp() == &UnboxedArrayObject::class_ &&
825             !CanStoreUnboxedType(alloc(), thisGroup->unboxedLayout().elementType(),
826                                  MIRType_Value, elemTypes.maybeTypes()))
827         {
828             return InliningStatus_NotInlined;
829         }
830     }
831 
832     // Inline the call.
833     JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
834     if (!templateObj || templateObj->group() != thisGroup)
835         return InliningStatus_NotInlined;
836 
837     callInfo.setImplicitlyUsedUnchecked();
838 
839     MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), thisArg, objArg,
840                                           templateObj,
841                                           templateObj->group()->initialHeap(constraints()),
842                                           unboxedThis, unboxedArg);
843     current->add(ins);
844     current->push(ins);
845 
846     if (!resumeAfter(ins))
847         return InliningStatus_Error;
848     return InliningStatus_Inlined;
849 }
850 
851 IonBuilder::InliningStatus
inlineArraySlice(CallInfo & callInfo)852 IonBuilder::inlineArraySlice(CallInfo& callInfo)
853 {
854     if (callInfo.constructing()) {
855         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
856         return InliningStatus_NotInlined;
857     }
858 
859     MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
860 
861     // Ensure |this| and result are objects.
862     if (getInlineReturnType() != MIRType_Object)
863         return InliningStatus_NotInlined;
864     if (obj->type() != MIRType_Object)
865         return InliningStatus_NotInlined;
866 
867     // Arguments for the sliced region must be integers.
868     if (callInfo.argc() > 0) {
869         if (callInfo.getArg(0)->type() != MIRType_Int32)
870             return InliningStatus_NotInlined;
871         if (callInfo.argc() > 1) {
872             if (callInfo.getArg(1)->type() != MIRType_Int32)
873                 return InliningStatus_NotInlined;
874         }
875     }
876 
877     // |this| must be a dense array.
878     TemporaryTypeSet* thisTypes = obj->resultTypeSet();
879     if (!thisTypes)
880         return InliningStatus_NotInlined;
881 
882     const Class* clasp = thisTypes->getKnownClass(constraints());
883     if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
884         return InliningStatus_NotInlined;
885     if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
886                                   OBJECT_FLAG_LENGTH_OVERFLOW))
887     {
888         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
889         return InliningStatus_NotInlined;
890     }
891 
892     JSValueType unboxedType = JSVAL_TYPE_MAGIC;
893     if (clasp == &UnboxedArrayObject::class_) {
894         unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
895         if (unboxedType == JSVAL_TYPE_MAGIC)
896             return InliningStatus_NotInlined;
897     }
898 
899     // Watch out for indexed properties on the prototype.
900     if (ArrayPrototypeHasIndexedProperty(this, script())) {
901         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
902         return InliningStatus_NotInlined;
903     }
904 
905     // The group of the result will be dynamically fixed up to match the input
906     // object, allowing us to handle 'this' objects that might have more than
907     // one group. Make sure that no singletons can be sliced here.
908     for (unsigned i = 0; i < thisTypes->getObjectCount(); i++) {
909         TypeSet::ObjectKey* key = thisTypes->getObject(i);
910         if (key && key->isSingleton())
911             return InliningStatus_NotInlined;
912     }
913 
914     // Inline the call.
915     JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_slice);
916     if (!templateObj)
917         return InliningStatus_NotInlined;
918 
919     if (unboxedType == JSVAL_TYPE_MAGIC) {
920         if (!templateObj->is<ArrayObject>())
921             return InliningStatus_NotInlined;
922     } else {
923         if (!templateObj->is<UnboxedArrayObject>())
924             return InliningStatus_NotInlined;
925         if (templateObj->as<UnboxedArrayObject>().elementType() != unboxedType)
926             return InliningStatus_NotInlined;
927     }
928 
929     callInfo.setImplicitlyUsedUnchecked();
930 
931     MDefinition* begin;
932     if (callInfo.argc() > 0)
933         begin = callInfo.getArg(0);
934     else
935         begin = constant(Int32Value(0));
936 
937     MDefinition* end;
938     if (callInfo.argc() > 1) {
939         end = callInfo.getArg(1);
940     } else if (clasp == &ArrayObject::class_) {
941         MElements* elements = MElements::New(alloc(), obj);
942         current->add(elements);
943 
944         end = MArrayLength::New(alloc(), elements);
945         current->add(end->toInstruction());
946     } else {
947         end = MUnboxedArrayLength::New(alloc(), obj);
948         current->add(end->toInstruction());
949     }
950 
951     MArraySlice* ins = MArraySlice::New(alloc(), constraints(),
952                                         obj, begin, end,
953                                         templateObj,
954                                         templateObj->group()->initialHeap(constraints()),
955                                         unboxedType);
956     current->add(ins);
957     current->push(ins);
958 
959     if (!resumeAfter(ins))
960         return InliningStatus_Error;
961     return InliningStatus_Inlined;
962 }
963 
964 IonBuilder::InliningStatus
inlineMathAbs(CallInfo & callInfo)965 IonBuilder::inlineMathAbs(CallInfo& callInfo)
966 {
967     if (callInfo.argc() != 1 || callInfo.constructing()) {
968         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
969         return InliningStatus_NotInlined;
970     }
971 
972     MIRType returnType = getInlineReturnType();
973     MIRType argType = callInfo.getArg(0)->type();
974     if (!IsNumberType(argType))
975         return InliningStatus_NotInlined;
976 
977     // Either argType == returnType, or
978     //        argType == Double or Float32, returnType == Int, or
979     //        argType == Float32, returnType == Double
980     if (argType != returnType && !(IsFloatingPointType(argType) && returnType == MIRType_Int32)
981         && !(argType == MIRType_Float32 && returnType == MIRType_Double))
982     {
983         return InliningStatus_NotInlined;
984     }
985 
986     callInfo.setImplicitlyUsedUnchecked();
987 
988     // If the arg is a Float32, we specialize the op as double, it will be specialized
989     // as float32 if necessary later.
990     MIRType absType = (argType == MIRType_Float32) ? MIRType_Double : argType;
991     MInstruction* ins = MAbs::New(alloc(), callInfo.getArg(0), absType);
992     current->add(ins);
993 
994     current->push(ins);
995     return InliningStatus_Inlined;
996 }
997 
998 IonBuilder::InliningStatus
inlineMathFloor(CallInfo & callInfo)999 IonBuilder::inlineMathFloor(CallInfo& callInfo)
1000 {
1001     if (callInfo.argc() != 1 || callInfo.constructing()) {
1002         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1003         return InliningStatus_NotInlined;
1004     }
1005 
1006     MIRType argType = callInfo.getArg(0)->type();
1007     MIRType returnType = getInlineReturnType();
1008 
1009     // Math.floor(int(x)) == int(x)
1010     if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
1011         callInfo.setImplicitlyUsedUnchecked();
1012         // The int operand may be something which bails out if the actual value
1013         // is not in the range of the result type of the MIR. We need to tell
1014         // the optimizer to preserve this bailout even if the final result is
1015         // fully truncated.
1016         MLimitedTruncate* ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
1017                                                       MDefinition::IndirectTruncate);
1018         current->add(ins);
1019         current->push(ins);
1020         return InliningStatus_Inlined;
1021     }
1022 
1023     if (IsFloatingPointType(argType) && returnType == MIRType_Int32) {
1024         callInfo.setImplicitlyUsedUnchecked();
1025         MFloor* ins = MFloor::New(alloc(), callInfo.getArg(0));
1026         current->add(ins);
1027         current->push(ins);
1028         return InliningStatus_Inlined;
1029     }
1030 
1031     if (IsFloatingPointType(argType) && returnType == MIRType_Double) {
1032         callInfo.setImplicitlyUsedUnchecked();
1033         MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor, nullptr);
1034         current->add(ins);
1035         current->push(ins);
1036         return InliningStatus_Inlined;
1037     }
1038 
1039     return InliningStatus_NotInlined;
1040 }
1041 
1042 IonBuilder::InliningStatus
inlineMathCeil(CallInfo & callInfo)1043 IonBuilder::inlineMathCeil(CallInfo& callInfo)
1044 {
1045     if (callInfo.argc() != 1 || callInfo.constructing()) {
1046         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1047         return InliningStatus_NotInlined;
1048     }
1049 
1050     MIRType argType = callInfo.getArg(0)->type();
1051     MIRType returnType = getInlineReturnType();
1052 
1053     // Math.ceil(int(x)) == int(x)
1054     if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
1055         callInfo.setImplicitlyUsedUnchecked();
1056         // The int operand may be something which bails out if the actual value
1057         // is not in the range of the result type of the MIR. We need to tell
1058         // the optimizer to preserve this bailout even if the final result is
1059         // fully truncated.
1060         MLimitedTruncate* ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
1061                                                       MDefinition::IndirectTruncate);
1062         current->add(ins);
1063         current->push(ins);
1064         return InliningStatus_Inlined;
1065     }
1066 
1067     if (IsFloatingPointType(argType) && returnType == MIRType_Int32) {
1068         callInfo.setImplicitlyUsedUnchecked();
1069         MCeil* ins = MCeil::New(alloc(), callInfo.getArg(0));
1070         current->add(ins);
1071         current->push(ins);
1072         return InliningStatus_Inlined;
1073     }
1074 
1075     if (IsFloatingPointType(argType) && returnType == MIRType_Double) {
1076         callInfo.setImplicitlyUsedUnchecked();
1077         MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil, nullptr);
1078         current->add(ins);
1079         current->push(ins);
1080         return InliningStatus_Inlined;
1081     }
1082 
1083     return InliningStatus_NotInlined;
1084 }
1085 
1086 IonBuilder::InliningStatus
inlineMathClz32(CallInfo & callInfo)1087 IonBuilder::inlineMathClz32(CallInfo& callInfo)
1088 {
1089     if (callInfo.argc() != 1 || callInfo.constructing()) {
1090         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1091         return InliningStatus_NotInlined;
1092     }
1093 
1094     MIRType returnType = getInlineReturnType();
1095     if (returnType != MIRType_Int32)
1096         return InliningStatus_NotInlined;
1097 
1098     if (!IsNumberType(callInfo.getArg(0)->type()))
1099         return InliningStatus_NotInlined;
1100 
1101     callInfo.setImplicitlyUsedUnchecked();
1102 
1103     MClz* ins = MClz::New(alloc(), callInfo.getArg(0));
1104     current->add(ins);
1105     current->push(ins);
1106     return InliningStatus_Inlined;
1107 
1108 }
1109 
1110 IonBuilder::InliningStatus
inlineMathRound(CallInfo & callInfo)1111 IonBuilder::inlineMathRound(CallInfo& callInfo)
1112 {
1113     if (callInfo.argc() != 1 || callInfo.constructing()) {
1114         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1115         return InliningStatus_NotInlined;
1116     }
1117 
1118     MIRType returnType = getInlineReturnType();
1119     MIRType argType = callInfo.getArg(0)->type();
1120 
1121     // Math.round(int(x)) == int(x)
1122     if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
1123         callInfo.setImplicitlyUsedUnchecked();
1124         // The int operand may be something which bails out if the actual value
1125         // is not in the range of the result type of the MIR. We need to tell
1126         // the optimizer to preserve this bailout even if the final result is
1127         // fully truncated.
1128         MLimitedTruncate* ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
1129                                                       MDefinition::IndirectTruncate);
1130         current->add(ins);
1131         current->push(ins);
1132         return InliningStatus_Inlined;
1133     }
1134 
1135     if (IsFloatingPointType(argType) && returnType == MIRType_Int32) {
1136         callInfo.setImplicitlyUsedUnchecked();
1137         MRound* ins = MRound::New(alloc(), callInfo.getArg(0));
1138         current->add(ins);
1139         current->push(ins);
1140         return InliningStatus_Inlined;
1141     }
1142 
1143     if (IsFloatingPointType(argType) && returnType == MIRType_Double) {
1144         callInfo.setImplicitlyUsedUnchecked();
1145         MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round, nullptr);
1146         current->add(ins);
1147         current->push(ins);
1148         return InliningStatus_Inlined;
1149     }
1150 
1151     return InliningStatus_NotInlined;
1152 }
1153 
1154 IonBuilder::InliningStatus
inlineMathSqrt(CallInfo & callInfo)1155 IonBuilder::inlineMathSqrt(CallInfo& callInfo)
1156 {
1157     if (callInfo.argc() != 1 || callInfo.constructing()) {
1158         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1159         return InliningStatus_NotInlined;
1160     }
1161 
1162     MIRType argType = callInfo.getArg(0)->type();
1163     if (getInlineReturnType() != MIRType_Double)
1164         return InliningStatus_NotInlined;
1165     if (!IsNumberType(argType))
1166         return InliningStatus_NotInlined;
1167 
1168     callInfo.setImplicitlyUsedUnchecked();
1169 
1170     MSqrt* sqrt = MSqrt::New(alloc(), callInfo.getArg(0));
1171     current->add(sqrt);
1172     current->push(sqrt);
1173     return InliningStatus_Inlined;
1174 }
1175 
1176 IonBuilder::InliningStatus
inlineMathAtan2(CallInfo & callInfo)1177 IonBuilder::inlineMathAtan2(CallInfo& callInfo)
1178 {
1179     if (callInfo.argc() != 2 || callInfo.constructing()) {
1180         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1181         return InliningStatus_NotInlined;
1182     }
1183 
1184     if (getInlineReturnType() != MIRType_Double)
1185         return InliningStatus_NotInlined;
1186 
1187     MIRType argType0 = callInfo.getArg(0)->type();
1188     MIRType argType1 = callInfo.getArg(1)->type();
1189 
1190     if (!IsNumberType(argType0) || !IsNumberType(argType1))
1191         return InliningStatus_NotInlined;
1192 
1193     callInfo.setImplicitlyUsedUnchecked();
1194 
1195     MAtan2* atan2 = MAtan2::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
1196     current->add(atan2);
1197     current->push(atan2);
1198     return InliningStatus_Inlined;
1199 }
1200 
1201 IonBuilder::InliningStatus
inlineMathHypot(CallInfo & callInfo)1202 IonBuilder::inlineMathHypot(CallInfo& callInfo)
1203 {
1204     if (callInfo.constructing()) {
1205         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1206         return InliningStatus_NotInlined;
1207     }
1208 
1209     uint32_t argc = callInfo.argc();
1210     if (argc < 2 || argc > 4) {
1211         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1212         return InliningStatus_NotInlined;
1213     }
1214 
1215     if (getInlineReturnType() != MIRType_Double)
1216         return InliningStatus_NotInlined;
1217 
1218     MDefinitionVector vector(alloc());
1219     if (!vector.reserve(argc))
1220         return InliningStatus_NotInlined;
1221 
1222     for (uint32_t i = 0; i < argc; ++i) {
1223         MDefinition * arg = callInfo.getArg(i);
1224         if (!IsNumberType(arg->type()))
1225             return InliningStatus_NotInlined;
1226         vector.infallibleAppend(arg);
1227     }
1228 
1229     callInfo.setImplicitlyUsedUnchecked();
1230     MHypot* hypot = MHypot::New(alloc(), vector);
1231 
1232     if (!hypot)
1233         return InliningStatus_NotInlined;
1234 
1235     current->add(hypot);
1236     current->push(hypot);
1237     return InliningStatus_Inlined;
1238 }
1239 
1240 IonBuilder::InliningStatus
inlineMathPowHelper(MDefinition * lhs,MDefinition * rhs,MIRType outputType)1241 IonBuilder::inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outputType)
1242 {
1243     // Typechecking.
1244     MIRType baseType = lhs->type();
1245     MIRType powerType = rhs->type();
1246 
1247     if (outputType != MIRType_Int32 && outputType != MIRType_Double)
1248         return InliningStatus_NotInlined;
1249     if (!IsNumberType(baseType))
1250         return InliningStatus_NotInlined;
1251     if (!IsNumberType(powerType))
1252         return InliningStatus_NotInlined;
1253 
1254     MDefinition* base = lhs;
1255     MDefinition* power = rhs;
1256     MDefinition* output = nullptr;
1257 
1258     // Optimize some constant powers.
1259     if (rhs->isConstantValue() && rhs->constantValue().isNumber()) {
1260         double pow = rhs->constantValue().toNumber();
1261 
1262         // Math.pow(x, 0.5) is a sqrt with edge-case detection.
1263         if (pow == 0.5) {
1264             MPowHalf* half = MPowHalf::New(alloc(), base);
1265             current->add(half);
1266             output = half;
1267         }
1268 
1269         // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases.
1270         if (pow == -0.5) {
1271             MPowHalf* half = MPowHalf::New(alloc(), base);
1272             current->add(half);
1273             MConstant* one = MConstant::New(alloc(), DoubleValue(1.0));
1274             current->add(one);
1275             MDiv* div = MDiv::New(alloc(), one, half, MIRType_Double);
1276             current->add(div);
1277             output = div;
1278         }
1279 
1280         // Math.pow(x, 1) == x.
1281         if (pow == 1.0)
1282             output = base;
1283 
1284         // Math.pow(x, 2) == x*x.
1285         if (pow == 2.0) {
1286             MMul* mul = MMul::New(alloc(), base, base, outputType);
1287             current->add(mul);
1288             output = mul;
1289         }
1290 
1291         // Math.pow(x, 3) == x*x*x.
1292         if (pow == 3.0) {
1293             MMul* mul1 = MMul::New(alloc(), base, base, outputType);
1294             current->add(mul1);
1295             MMul* mul2 = MMul::New(alloc(), base, mul1, outputType);
1296             current->add(mul2);
1297             output = mul2;
1298         }
1299 
1300         // Math.pow(x, 4) == y*y, where y = x*x.
1301         if (pow == 4.0) {
1302             MMul* y = MMul::New(alloc(), base, base, outputType);
1303             current->add(y);
1304             MMul* mul = MMul::New(alloc(), y, y, outputType);
1305             current->add(mul);
1306             output = mul;
1307         }
1308     }
1309 
1310     // Use MPow for other powers
1311     if (!output) {
1312         if (powerType == MIRType_Float32)
1313             powerType = MIRType_Double;
1314         MPow* pow = MPow::New(alloc(), base, power, powerType);
1315         current->add(pow);
1316         output = pow;
1317     }
1318 
1319     // Cast to the right type
1320     if (outputType == MIRType_Int32 && output->type() != MIRType_Int32) {
1321         MToInt32* toInt = MToInt32::New(alloc(), output);
1322         current->add(toInt);
1323         output = toInt;
1324     }
1325     if (outputType == MIRType_Double && output->type() != MIRType_Double) {
1326         MToDouble* toDouble = MToDouble::New(alloc(), output);
1327         current->add(toDouble);
1328         output = toDouble;
1329     }
1330 
1331     current->push(output);
1332     return InliningStatus_Inlined;
1333 }
1334 
1335 IonBuilder::InliningStatus
inlineMathPow(CallInfo & callInfo)1336 IonBuilder::inlineMathPow(CallInfo& callInfo)
1337 {
1338     if (callInfo.argc() != 2 || callInfo.constructing()) {
1339         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1340         return InliningStatus_NotInlined;
1341     }
1342 
1343     IonBuilder::InliningStatus status =
1344         inlineMathPowHelper(callInfo.getArg(0), callInfo.getArg(1), getInlineReturnType());
1345 
1346     if (status == IonBuilder::InliningStatus_Inlined)
1347         callInfo.setImplicitlyUsedUnchecked();
1348 
1349     return status;
1350 }
1351 
1352 IonBuilder::InliningStatus
inlineMathRandom(CallInfo & callInfo)1353 IonBuilder::inlineMathRandom(CallInfo& callInfo)
1354 {
1355     if (callInfo.constructing()) {
1356         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1357         return InliningStatus_NotInlined;
1358     }
1359 
1360     if (getInlineReturnType() != MIRType_Double)
1361         return InliningStatus_NotInlined;
1362 
1363     // MRandom JIT code directly accesses the RNG. It's (barely) possible to
1364     // inline Math.random without it having been called yet, so ensure RNG
1365     // state that isn't guaranteed to be initialized already.
1366     script()->compartment()->ensureRandomNumberGenerator();
1367 
1368     callInfo.setImplicitlyUsedUnchecked();
1369 
1370     MRandom* rand = MRandom::New(alloc());
1371     current->add(rand);
1372     current->push(rand);
1373     return InliningStatus_Inlined;
1374 }
1375 
1376 IonBuilder::InliningStatus
inlineMathImul(CallInfo & callInfo)1377 IonBuilder::inlineMathImul(CallInfo& callInfo)
1378 {
1379     if (callInfo.argc() != 2 || callInfo.constructing()) {
1380         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1381         return InliningStatus_NotInlined;
1382     }
1383 
1384     MIRType returnType = getInlineReturnType();
1385     if (returnType != MIRType_Int32)
1386         return InliningStatus_NotInlined;
1387 
1388     if (!IsNumberType(callInfo.getArg(0)->type()))
1389         return InliningStatus_NotInlined;
1390     if (!IsNumberType(callInfo.getArg(1)->type()))
1391         return InliningStatus_NotInlined;
1392 
1393     callInfo.setImplicitlyUsedUnchecked();
1394 
1395     MInstruction* first = MTruncateToInt32::New(alloc(), callInfo.getArg(0));
1396     current->add(first);
1397 
1398     MInstruction* second = MTruncateToInt32::New(alloc(), callInfo.getArg(1));
1399     current->add(second);
1400 
1401     MMul* ins = MMul::New(alloc(), first, second, MIRType_Int32, MMul::Integer);
1402     current->add(ins);
1403     current->push(ins);
1404     return InliningStatus_Inlined;
1405 }
1406 
1407 IonBuilder::InliningStatus
inlineMathFRound(CallInfo & callInfo)1408 IonBuilder::inlineMathFRound(CallInfo& callInfo)
1409 {
1410     if (callInfo.argc() != 1 || callInfo.constructing()) {
1411         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1412         return InliningStatus_NotInlined;
1413     }
1414 
1415     // MIRType can't be Float32, as this point, as getInlineReturnType uses JSVal types
1416     // to infer the returned MIR type.
1417     TemporaryTypeSet* returned = getInlineReturnTypeSet();
1418     if (returned->empty()) {
1419         // As there's only one possible returned type, just add it to the observed
1420         // returned typeset
1421         returned->addType(TypeSet::DoubleType(), alloc_->lifoAlloc());
1422     } else {
1423         MIRType returnType = getInlineReturnType();
1424         if (!IsNumberType(returnType))
1425             return InliningStatus_NotInlined;
1426     }
1427 
1428     MIRType arg = callInfo.getArg(0)->type();
1429     if (!IsNumberType(arg))
1430         return InliningStatus_NotInlined;
1431 
1432     callInfo.setImplicitlyUsedUnchecked();
1433 
1434     MToFloat32* ins = MToFloat32::New(alloc(), callInfo.getArg(0));
1435     current->add(ins);
1436     current->push(ins);
1437     return InliningStatus_Inlined;
1438 }
1439 
1440 IonBuilder::InliningStatus
inlineMathMinMax(CallInfo & callInfo,bool max)1441 IonBuilder::inlineMathMinMax(CallInfo& callInfo, bool max)
1442 {
1443     if (callInfo.argc() < 1 || callInfo.constructing()) {
1444         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1445         return InliningStatus_NotInlined;
1446     }
1447 
1448     MIRType returnType = getInlineReturnType();
1449     if (!IsNumberType(returnType))
1450         return InliningStatus_NotInlined;
1451 
1452     MDefinitionVector int32_cases(alloc());
1453     for (unsigned i = 0; i < callInfo.argc(); i++) {
1454         MDefinition* arg = callInfo.getArg(i);
1455 
1456         switch (arg->type()) {
1457           case MIRType_Int32:
1458             if (!int32_cases.append(arg))
1459                 return InliningStatus_Error;
1460             break;
1461           case MIRType_Double:
1462           case MIRType_Float32:
1463             // Don't force a double MMinMax for arguments that would be a NOP
1464             // when doing an integer MMinMax.
1465             if (arg->isConstantValue()) {
1466                 double cte = arg->constantValue().toDouble();
1467                 // min(int32, cte >= INT32_MAX) = int32
1468                 if (cte >= INT32_MAX && !max)
1469                     break;
1470                 // max(int32, cte <= INT32_MIN) = int32
1471                 if (cte <= INT32_MIN && max)
1472                     break;
1473             }
1474 
1475             // Force double MMinMax if argument is a "effectfull" double.
1476             returnType = MIRType_Double;
1477             break;
1478           default:
1479             return InliningStatus_NotInlined;
1480         }
1481     }
1482 
1483     if (int32_cases.length() == 0)
1484         returnType = MIRType_Double;
1485 
1486     callInfo.setImplicitlyUsedUnchecked();
1487 
1488     MDefinitionVector& cases = (returnType == MIRType_Int32) ? int32_cases : callInfo.argv();
1489 
1490     if (cases.length() == 1) {
1491         MLimitedTruncate* limit = MLimitedTruncate::New(alloc(), cases[0], MDefinition::NoTruncate);
1492         current->add(limit);
1493         current->push(limit);
1494         return InliningStatus_Inlined;
1495     }
1496 
1497     // Chain N-1 MMinMax instructions to compute the MinMax.
1498     MMinMax* last = MMinMax::New(alloc(), cases[0], cases[1], returnType, max);
1499     current->add(last);
1500 
1501     for (unsigned i = 2; i < cases.length(); i++) {
1502         MMinMax* ins = MMinMax::New(alloc(), last, cases[i], returnType, max);
1503         current->add(ins);
1504         last = ins;
1505     }
1506 
1507     current->push(last);
1508     return InliningStatus_Inlined;
1509 }
1510 
1511 IonBuilder::InliningStatus
inlineStringObject(CallInfo & callInfo)1512 IonBuilder::inlineStringObject(CallInfo& callInfo)
1513 {
1514     if (callInfo.argc() != 1 || !callInfo.constructing()) {
1515         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1516         return InliningStatus_NotInlined;
1517     }
1518 
1519     // ConvertToString doesn't support objects.
1520     if (callInfo.getArg(0)->mightBeType(MIRType_Object))
1521         return InliningStatus_NotInlined;
1522 
1523     JSObject* templateObj = inspector->getTemplateObjectForNative(pc, StringConstructor);
1524     if (!templateObj)
1525         return InliningStatus_NotInlined;
1526     MOZ_ASSERT(templateObj->is<StringObject>());
1527 
1528     callInfo.setImplicitlyUsedUnchecked();
1529 
1530     MNewStringObject* ins = MNewStringObject::New(alloc(), callInfo.getArg(0), templateObj);
1531     current->add(ins);
1532     current->push(ins);
1533 
1534     if (!resumeAfter(ins))
1535         return InliningStatus_Error;
1536 
1537     return InliningStatus_Inlined;
1538 }
1539 
1540 IonBuilder::InliningStatus
inlineConstantStringSplit(CallInfo & callInfo)1541 IonBuilder::inlineConstantStringSplit(CallInfo& callInfo)
1542 {
1543     if (!callInfo.thisArg()->isConstant())
1544         return InliningStatus_NotInlined;
1545 
1546     if (!callInfo.getArg(0)->isConstant())
1547         return InliningStatus_NotInlined;
1548 
1549     const js::Value* argval = callInfo.getArg(0)->toConstant()->vp();
1550     if (!argval->isString())
1551         return InliningStatus_NotInlined;
1552 
1553     const js::Value* strval = callInfo.thisArg()->toConstant()->vp();
1554     if (!strval->isString())
1555         return InliningStatus_NotInlined;
1556 
1557     MOZ_ASSERT(callInfo.getArg(0)->type() == MIRType_String);
1558     MOZ_ASSERT(callInfo.thisArg()->type() == MIRType_String);
1559 
1560     // Check if exist a template object in stub.
1561     JSString* stringThis = nullptr;
1562     JSString* stringArg = nullptr;
1563     JSObject* templateObject = nullptr;
1564     if (!inspector->isOptimizableCallStringSplit(pc, &stringThis, &stringArg, &templateObject))
1565         return InliningStatus_NotInlined;
1566 
1567     MOZ_ASSERT(stringThis);
1568     MOZ_ASSERT(stringArg);
1569     MOZ_ASSERT(templateObject);
1570 
1571     if (strval->toString() != stringThis)
1572         return InliningStatus_NotInlined;
1573 
1574     if (argval->toString() != stringArg)
1575         return InliningStatus_NotInlined;
1576 
1577     // Check if |templateObject| is valid.
1578     TypeSet::ObjectKey* retType = TypeSet::ObjectKey::get(templateObject);
1579     if (retType->unknownProperties())
1580         return InliningStatus_NotInlined;
1581 
1582     HeapTypeSetKey key = retType->property(JSID_VOID);
1583     if (!key.maybeTypes())
1584         return InliningStatus_NotInlined;
1585 
1586     if (!key.maybeTypes()->hasType(TypeSet::StringType()))
1587         return InliningStatus_NotInlined;
1588 
1589     uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(templateObject);
1590     if (GetAnyBoxedOrUnboxedInitializedLength(templateObject) != initLength)
1591         return InliningStatus_NotInlined;
1592 
1593     Vector<MConstant*, 0, SystemAllocPolicy> arrayValues;
1594     for (uint32_t i = 0; i < initLength; i++) {
1595         Value str = GetAnyBoxedOrUnboxedDenseElement(templateObject, i);
1596         MOZ_ASSERT(str.toString()->isAtom());
1597         MConstant* value = MConstant::New(alloc(), str, constraints());
1598         if (!TypeSetIncludes(key.maybeTypes(), value->type(), value->resultTypeSet()))
1599             return InliningStatus_NotInlined;
1600 
1601         if (!arrayValues.append(value))
1602             return InliningStatus_Error;
1603     }
1604     callInfo.setImplicitlyUsedUnchecked();
1605 
1606     TemporaryTypeSet::DoubleConversion conversion =
1607             getInlineReturnTypeSet()->convertDoubleElements(constraints());
1608     if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles)
1609         return InliningStatus_NotInlined;
1610 
1611     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
1612     current->add(templateConst);
1613 
1614     MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
1615                                     templateObject->group()->initialHeap(constraints()), pc);
1616 
1617     current->add(ins);
1618     current->push(ins);
1619 
1620     if (!initLength) {
1621         if (!resumeAfter(ins))
1622             return InliningStatus_Error;
1623         return InliningStatus_Inlined;
1624     }
1625 
1626     JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
1627 
1628     // Store all values, no need to initialize the length after each as
1629     // jsop_initelem_array is doing because we do not expect to bailout
1630     // because the memory is supposed to be allocated by now.
1631     for (uint32_t i = 0; i < initLength; i++) {
1632        MConstant* value = arrayValues[i];
1633        current->add(value);
1634 
1635        if (!initializeArrayElement(ins, i, value, unboxedType, /* addResumePoint = */ false))
1636            return InliningStatus_Error;
1637     }
1638 
1639     MInstruction* setLength = setInitializedLength(ins, unboxedType, initLength);
1640     if (!resumeAfter(setLength))
1641         return InliningStatus_Error;
1642 
1643     return InliningStatus_Inlined;
1644 }
1645 
1646 IonBuilder::InliningStatus
inlineStringSplit(CallInfo & callInfo)1647 IonBuilder::inlineStringSplit(CallInfo& callInfo)
1648 {
1649     if (callInfo.argc() != 1 || callInfo.constructing()) {
1650         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1651         return InliningStatus_NotInlined;
1652     }
1653 
1654     if (callInfo.thisArg()->type() != MIRType_String)
1655         return InliningStatus_NotInlined;
1656     if (callInfo.getArg(0)->type() != MIRType_String)
1657         return InliningStatus_NotInlined;
1658 
1659     IonBuilder::InliningStatus resultConstStringSplit = inlineConstantStringSplit(callInfo);
1660     if (resultConstStringSplit != InliningStatus_NotInlined)
1661         return resultConstStringSplit;
1662 
1663     JSObject* templateObject = inspector->getTemplateObjectForNative(pc, js::str_split);
1664     if (!templateObject)
1665         return InliningStatus_NotInlined;
1666 
1667     TypeSet::ObjectKey* retKey = TypeSet::ObjectKey::get(templateObject);
1668     if (retKey->unknownProperties())
1669         return InliningStatus_NotInlined;
1670 
1671     HeapTypeSetKey key = retKey->property(JSID_VOID);
1672     if (!key.maybeTypes())
1673         return InliningStatus_NotInlined;
1674 
1675     if (!key.maybeTypes()->hasType(TypeSet::StringType())) {
1676         key.freeze(constraints());
1677         return InliningStatus_NotInlined;
1678     }
1679 
1680     callInfo.setImplicitlyUsedUnchecked();
1681     MConstant* templateObjectDef = MConstant::New(alloc(), ObjectValue(*templateObject),
1682                                                   constraints());
1683     current->add(templateObjectDef);
1684 
1685     MStringSplit* ins = MStringSplit::New(alloc(), constraints(), callInfo.thisArg(),
1686                                           callInfo.getArg(0), templateObjectDef);
1687     current->add(ins);
1688     current->push(ins);
1689 
1690     return InliningStatus_Inlined;
1691 }
1692 
1693 IonBuilder::InliningStatus
inlineStrCharCodeAt(CallInfo & callInfo)1694 IonBuilder::inlineStrCharCodeAt(CallInfo& callInfo)
1695 {
1696     if (callInfo.argc() != 1 || callInfo.constructing()) {
1697         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1698         return InliningStatus_NotInlined;
1699     }
1700 
1701     if (getInlineReturnType() != MIRType_Int32)
1702         return InliningStatus_NotInlined;
1703     if (callInfo.thisArg()->type() != MIRType_String && callInfo.thisArg()->type() != MIRType_Value)
1704         return InliningStatus_NotInlined;
1705     MIRType argType = callInfo.getArg(0)->type();
1706     if (argType != MIRType_Int32 && argType != MIRType_Double)
1707         return InliningStatus_NotInlined;
1708 
1709     // Check for STR.charCodeAt(IDX) where STR is a constant string and IDX is a
1710     // constant integer.
1711     InliningStatus constInlineStatus = inlineConstantCharCodeAt(callInfo);
1712     if (constInlineStatus != InliningStatus_NotInlined)
1713         return constInlineStatus;
1714 
1715     callInfo.setImplicitlyUsedUnchecked();
1716 
1717     MInstruction* index = MToInt32::New(alloc(), callInfo.getArg(0));
1718     current->add(index);
1719 
1720     MStringLength* length = MStringLength::New(alloc(), callInfo.thisArg());
1721     current->add(length);
1722 
1723     index = addBoundsCheck(index, length);
1724 
1725     MCharCodeAt* charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
1726     current->add(charCode);
1727     current->push(charCode);
1728     return InliningStatus_Inlined;
1729 }
1730 
1731 IonBuilder::InliningStatus
inlineConstantCharCodeAt(CallInfo & callInfo)1732 IonBuilder::inlineConstantCharCodeAt(CallInfo& callInfo)
1733 {
1734     if (!callInfo.thisArg()->isConstantValue() || !callInfo.getArg(0)->isConstantValue()) {
1735         trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
1736         return InliningStatus_NotInlined;
1737     }
1738 
1739     const js::Value* strval = callInfo.thisArg()->constantVp();
1740     const js::Value* idxval  = callInfo.getArg(0)->constantVp();
1741 
1742     if (!strval->isString() || !idxval->isInt32())
1743         return InliningStatus_NotInlined;
1744 
1745     JSString* str = strval->toString();
1746     if (!str->isLinear()) {
1747         trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
1748         return InliningStatus_NotInlined;
1749     }
1750 
1751     int32_t idx = idxval->toInt32();
1752     if (idx < 0 || (uint32_t(idx) >= str->length())) {
1753         trackOptimizationOutcome(TrackedOutcome::OutOfBounds);
1754         return InliningStatus_NotInlined;
1755     }
1756 
1757     callInfo.setImplicitlyUsedUnchecked();
1758 
1759     JSLinearString& linstr = str->asLinear();
1760     char16_t ch = linstr.latin1OrTwoByteChar(idx);
1761     MConstant* result = MConstant::New(alloc(), Int32Value(ch));
1762     current->add(result);
1763     current->push(result);
1764     return InliningStatus_Inlined;
1765 }
1766 
1767 IonBuilder::InliningStatus
inlineStrFromCharCode(CallInfo & callInfo)1768 IonBuilder::inlineStrFromCharCode(CallInfo& callInfo)
1769 {
1770     if (callInfo.argc() != 1 || callInfo.constructing()) {
1771         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1772         return InliningStatus_NotInlined;
1773     }
1774 
1775     if (getInlineReturnType() != MIRType_String)
1776         return InliningStatus_NotInlined;
1777     if (callInfo.getArg(0)->type() != MIRType_Int32)
1778         return InliningStatus_NotInlined;
1779 
1780     callInfo.setImplicitlyUsedUnchecked();
1781 
1782     MToInt32* charCode = MToInt32::New(alloc(), callInfo.getArg(0));
1783     current->add(charCode);
1784 
1785     MFromCharCode* string = MFromCharCode::New(alloc(), charCode);
1786     current->add(string);
1787     current->push(string);
1788     return InliningStatus_Inlined;
1789 }
1790 
1791 IonBuilder::InliningStatus
inlineStrCharAt(CallInfo & callInfo)1792 IonBuilder::inlineStrCharAt(CallInfo& callInfo)
1793 {
1794     if (callInfo.argc() != 1 || callInfo.constructing()) {
1795         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1796         return InliningStatus_NotInlined;
1797     }
1798 
1799     if (getInlineReturnType() != MIRType_String)
1800         return InliningStatus_NotInlined;
1801     if (callInfo.thisArg()->type() != MIRType_String)
1802         return InliningStatus_NotInlined;
1803     MIRType argType = callInfo.getArg(0)->type();
1804     if (argType != MIRType_Int32 && argType != MIRType_Double)
1805         return InliningStatus_NotInlined;
1806 
1807     callInfo.setImplicitlyUsedUnchecked();
1808 
1809     MInstruction* index = MToInt32::New(alloc(), callInfo.getArg(0));
1810     current->add(index);
1811 
1812     MStringLength* length = MStringLength::New(alloc(), callInfo.thisArg());
1813     current->add(length);
1814 
1815     index = addBoundsCheck(index, length);
1816 
1817     // String.charAt(x) = String.fromCharCode(String.charCodeAt(x))
1818     MCharCodeAt* charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
1819     current->add(charCode);
1820 
1821     MFromCharCode* string = MFromCharCode::New(alloc(), charCode);
1822     current->add(string);
1823     current->push(string);
1824     return InliningStatus_Inlined;
1825 }
1826 
1827 IonBuilder::InliningStatus
inlineRegExpExec(CallInfo & callInfo)1828 IonBuilder::inlineRegExpExec(CallInfo& callInfo)
1829 {
1830     if (callInfo.argc() != 1 || callInfo.constructing()) {
1831         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1832         return InliningStatus_NotInlined;
1833     }
1834 
1835     if (callInfo.thisArg()->type() != MIRType_Object)
1836         return InliningStatus_NotInlined;
1837 
1838     TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
1839     const Class* clasp = thisTypes ? thisTypes->getKnownClass(constraints()) : nullptr;
1840     if (clasp != &RegExpObject::class_)
1841         return InliningStatus_NotInlined;
1842 
1843     if (callInfo.getArg(0)->mightBeType(MIRType_Object))
1844         return InliningStatus_NotInlined;
1845 
1846     JSContext* cx = GetJitContext()->cx;
1847     if (!cx->compartment()->jitCompartment()->ensureRegExpExecStubExists(cx))
1848         return InliningStatus_Error;
1849 
1850     callInfo.setImplicitlyUsedUnchecked();
1851 
1852     MInstruction* exec = MRegExpExec::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
1853     current->add(exec);
1854     current->push(exec);
1855 
1856     if (!resumeAfter(exec))
1857         return InliningStatus_Error;
1858 
1859     if (!pushTypeBarrier(exec, getInlineReturnTypeSet(), BarrierKind::TypeSet))
1860         return InliningStatus_Error;
1861 
1862     return InliningStatus_Inlined;
1863 }
1864 
1865 IonBuilder::InliningStatus
inlineRegExpTest(CallInfo & callInfo)1866 IonBuilder::inlineRegExpTest(CallInfo& callInfo)
1867 {
1868     if (callInfo.argc() != 1 || callInfo.constructing()) {
1869         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1870         return InliningStatus_NotInlined;
1871     }
1872 
1873     // TI can infer a nullptr return type of regexp_test with eager compilation.
1874     if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean)
1875         return InliningStatus_NotInlined;
1876 
1877     if (callInfo.thisArg()->type() != MIRType_Object)
1878         return InliningStatus_NotInlined;
1879     TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
1880     const Class* clasp = thisTypes ? thisTypes->getKnownClass(constraints()) : nullptr;
1881     if (clasp != &RegExpObject::class_)
1882         return InliningStatus_NotInlined;
1883     if (callInfo.getArg(0)->mightBeType(MIRType_Object))
1884         return InliningStatus_NotInlined;
1885 
1886     JSContext* cx = GetJitContext()->cx;
1887     if (!cx->compartment()->jitCompartment()->ensureRegExpTestStubExists(cx))
1888         return InliningStatus_Error;
1889 
1890     callInfo.setImplicitlyUsedUnchecked();
1891 
1892     MInstruction* match = MRegExpTest::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
1893     current->add(match);
1894     current->push(match);
1895     if (!resumeAfter(match))
1896         return InliningStatus_Error;
1897 
1898     return InliningStatus_Inlined;
1899 }
1900 
1901 IonBuilder::InliningStatus
inlineStrReplace(CallInfo & callInfo)1902 IonBuilder::inlineStrReplace(CallInfo& callInfo)
1903 {
1904     if (callInfo.argc() != 2 || callInfo.constructing()) {
1905         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1906         return InliningStatus_NotInlined;
1907     }
1908 
1909     // Return: String.
1910     if (getInlineReturnType() != MIRType_String)
1911         return InliningStatus_NotInlined;
1912 
1913     // This: String.
1914     if (callInfo.thisArg()->type() != MIRType_String)
1915         return InliningStatus_NotInlined;
1916 
1917     // Arg 0: RegExp.
1918     TemporaryTypeSet* arg0Type = callInfo.getArg(0)->resultTypeSet();
1919     const Class* clasp = arg0Type ? arg0Type->getKnownClass(constraints()) : nullptr;
1920     if (clasp != &RegExpObject::class_ && callInfo.getArg(0)->type() != MIRType_String)
1921         return InliningStatus_NotInlined;
1922 
1923     // Arg 1: String.
1924     if (callInfo.getArg(1)->type() != MIRType_String)
1925         return InliningStatus_NotInlined;
1926 
1927     callInfo.setImplicitlyUsedUnchecked();
1928 
1929     MInstruction* cte;
1930     if (callInfo.getArg(0)->type() == MIRType_String) {
1931         cte = MStringReplace::New(alloc(), callInfo.thisArg(), callInfo.getArg(0),
1932                                   callInfo.getArg(1));
1933     } else {
1934         cte = MRegExpReplace::New(alloc(), callInfo.thisArg(), callInfo.getArg(0),
1935                                   callInfo.getArg(1));
1936     }
1937     current->add(cte);
1938     current->push(cte);
1939     if (cte->isEffectful() && !resumeAfter(cte))
1940         return InliningStatus_Error;
1941     return InliningStatus_Inlined;
1942 }
1943 
1944 IonBuilder::InliningStatus
inlineSubstringKernel(CallInfo & callInfo)1945 IonBuilder::inlineSubstringKernel(CallInfo& callInfo)
1946 {
1947     MOZ_ASSERT(callInfo.argc() == 3);
1948     MOZ_ASSERT(!callInfo.constructing());
1949 
1950     // Return: String.
1951     if (getInlineReturnType() != MIRType_String)
1952         return InliningStatus_NotInlined;
1953 
1954     // Arg 0: String.
1955     if (callInfo.getArg(0)->type() != MIRType_String)
1956         return InliningStatus_NotInlined;
1957 
1958     // Arg 1: Int.
1959     if (callInfo.getArg(1)->type() != MIRType_Int32)
1960         return InliningStatus_NotInlined;
1961 
1962     // Arg 2: Int.
1963     if (callInfo.getArg(2)->type() != MIRType_Int32)
1964         return InliningStatus_NotInlined;
1965 
1966     callInfo.setImplicitlyUsedUnchecked();
1967 
1968     MSubstr* substr = MSubstr::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
1969                                             callInfo.getArg(2));
1970     current->add(substr);
1971     current->push(substr);
1972 
1973     return InliningStatus_Inlined;
1974 }
1975 
1976 IonBuilder::InliningStatus
inlineObjectCreate(CallInfo & callInfo)1977 IonBuilder::inlineObjectCreate(CallInfo& callInfo)
1978 {
1979     if (callInfo.argc() != 1 || callInfo.constructing())
1980         return InliningStatus_NotInlined;
1981 
1982     JSObject* templateObject = inspector->getTemplateObjectForNative(pc, obj_create);
1983     if (!templateObject)
1984         return InliningStatus_NotInlined;
1985 
1986     MOZ_ASSERT(templateObject->is<PlainObject>());
1987     MOZ_ASSERT(!templateObject->isSingleton());
1988 
1989     // Ensure the argument matches the template object's prototype.
1990     MDefinition* arg = callInfo.getArg(0);
1991     if (JSObject* proto = templateObject->getProto()) {
1992         if (IsInsideNursery(proto))
1993             return InliningStatus_NotInlined;
1994 
1995         TemporaryTypeSet* types = arg->resultTypeSet();
1996         if (!types || types->maybeSingleton() != proto)
1997             return InliningStatus_NotInlined;
1998 
1999         MOZ_ASSERT(types->getKnownMIRType() == MIRType_Object);
2000     } else {
2001         if (arg->type() != MIRType_Null)
2002             return InliningStatus_NotInlined;
2003     }
2004 
2005     callInfo.setImplicitlyUsedUnchecked();
2006 
2007     MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
2008     current->add(templateConst);
2009     MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst,
2010                                       templateObject->group()->initialHeap(constraints()),
2011                                       MNewObject::ObjectCreate);
2012     current->add(ins);
2013     current->push(ins);
2014     if (!resumeAfter(ins))
2015         return InliningStatus_Error;
2016 
2017     return InliningStatus_Inlined;
2018 }
2019 
2020 IonBuilder::InliningStatus
inlineDefineDataProperty(CallInfo & callInfo)2021 IonBuilder::inlineDefineDataProperty(CallInfo& callInfo)
2022 {
2023     MOZ_ASSERT(!callInfo.constructing());
2024 
2025     // Only handle definitions of plain data properties.
2026     if (callInfo.argc() != 3)
2027         return InliningStatus_NotInlined;
2028 
2029     MDefinition* obj = convertUnboxedObjects(callInfo.getArg(0));
2030     MDefinition* id = callInfo.getArg(1);
2031     MDefinition* value = callInfo.getArg(2);
2032 
2033     if (ElementAccessHasExtraIndexedProperty(this, obj))
2034         return InliningStatus_NotInlined;
2035 
2036     // setElemTryDense will push the value as the result of the define instead
2037     // of |undefined|, but this is fine if the rval is ignored (as it should be
2038     // in self hosted code.)
2039     MOZ_ASSERT(*GetNextPc(pc) == JSOP_POP);
2040 
2041     bool emitted = false;
2042     if (!setElemTryDense(&emitted, obj, id, value, /* writeHole = */ true))
2043         return InliningStatus_Error;
2044     if (!emitted)
2045         return InliningStatus_NotInlined;
2046 
2047     callInfo.setImplicitlyUsedUnchecked();
2048     return InliningStatus_Inlined;
2049 }
2050 
2051 IonBuilder::InliningStatus
inlineHasClass(CallInfo & callInfo,const Class * clasp1,const Class * clasp2,const Class * clasp3,const Class * clasp4)2052 IonBuilder::inlineHasClass(CallInfo& callInfo,
2053                            const Class* clasp1, const Class* clasp2,
2054                            const Class* clasp3, const Class* clasp4)
2055 {
2056     if (callInfo.constructing() || callInfo.argc() != 1) {
2057         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2058         return InliningStatus_NotInlined;
2059     }
2060 
2061     if (callInfo.getArg(0)->type() != MIRType_Object)
2062         return InliningStatus_NotInlined;
2063     if (getInlineReturnType() != MIRType_Boolean)
2064         return InliningStatus_NotInlined;
2065 
2066     TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
2067     const Class* knownClass = types ? types->getKnownClass(constraints()) : nullptr;
2068     if (knownClass) {
2069         pushConstant(BooleanValue(knownClass == clasp1 ||
2070                                   knownClass == clasp2 ||
2071                                   knownClass == clasp3 ||
2072                                   knownClass == clasp4));
2073     } else {
2074         MHasClass* hasClass1 = MHasClass::New(alloc(), callInfo.getArg(0), clasp1);
2075         current->add(hasClass1);
2076 
2077         if (!clasp2 && !clasp3 && !clasp4) {
2078             current->push(hasClass1);
2079         } else {
2080             const Class* remaining[] = { clasp2, clasp3, clasp4 };
2081             MDefinition* last = hasClass1;
2082             for (size_t i = 0; i < ArrayLength(remaining); i++) {
2083                 MHasClass* hasClass = MHasClass::New(alloc(), callInfo.getArg(0), remaining[i]);
2084                 current->add(hasClass);
2085                 MBitOr* either = MBitOr::New(alloc(), last, hasClass);
2086                 either->infer(inspector, pc);
2087                 current->add(either);
2088                 last = either;
2089             }
2090 
2091             // Convert to bool with the '!!' idiom
2092             MNot* resultInverted = MNot::New(alloc(), last, constraints());
2093             current->add(resultInverted);
2094             MNot* result = MNot::New(alloc(), resultInverted, constraints());
2095             current->add(result);
2096             current->push(result);
2097         }
2098     }
2099 
2100     callInfo.setImplicitlyUsedUnchecked();
2101     return InliningStatus_Inlined;
2102 }
2103 
2104 IonBuilder::InliningStatus
inlineIsTypedArrayHelper(CallInfo & callInfo,WrappingBehavior wrappingBehavior)2105 IonBuilder::inlineIsTypedArrayHelper(CallInfo& callInfo, WrappingBehavior wrappingBehavior)
2106 {
2107     MOZ_ASSERT(!callInfo.constructing());
2108     MOZ_ASSERT(callInfo.argc() == 1);
2109 
2110     if (callInfo.getArg(0)->type() != MIRType_Object)
2111         return InliningStatus_NotInlined;
2112     if (getInlineReturnType() != MIRType_Boolean)
2113         return InliningStatus_NotInlined;
2114 
2115     // The test is elaborate: in-line only if there is exact
2116     // information.
2117 
2118     TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
2119     if (!types)
2120         return InliningStatus_NotInlined;
2121 
2122     bool result = false;
2123     switch (types->forAllClasses(constraints(), IsTypedArrayClass)) {
2124       case TemporaryTypeSet::ForAllResult::ALL_FALSE:
2125       case TemporaryTypeSet::ForAllResult::EMPTY: {
2126         // Wrapped typed arrays won't appear to be typed arrays per a
2127         // |forAllClasses| query.  If wrapped typed arrays are to be considered
2128         // typed arrays, a negative answer is not conclusive.  Don't inline in
2129         // that case.
2130         if (wrappingBehavior == AllowWrappedTypedArrays)
2131             return InliningStatus_NotInlined;
2132 
2133         result = false;
2134         break;
2135       }
2136 
2137       case TemporaryTypeSet::ForAllResult::ALL_TRUE:
2138         result = true;
2139         break;
2140 
2141       case TemporaryTypeSet::ForAllResult::MIXED:
2142         return InliningStatus_NotInlined;
2143     }
2144 
2145     pushConstant(BooleanValue(result));
2146 
2147     callInfo.setImplicitlyUsedUnchecked();
2148     return InliningStatus_Inlined;
2149 }
2150 
2151 IonBuilder::InliningStatus
inlineIsTypedArray(CallInfo & callInfo)2152 IonBuilder::inlineIsTypedArray(CallInfo& callInfo)
2153 {
2154     return inlineIsTypedArrayHelper(callInfo, RejectWrappedTypedArrays);
2155 }
2156 
2157 IonBuilder::InliningStatus
inlineIsPossiblyWrappedTypedArray(CallInfo & callInfo)2158 IonBuilder::inlineIsPossiblyWrappedTypedArray(CallInfo& callInfo)
2159 {
2160     return inlineIsTypedArrayHelper(callInfo, AllowWrappedTypedArrays);
2161 }
2162 
2163 static bool
IsTypedArrayObject(CompilerConstraintList * constraints,MDefinition * def)2164 IsTypedArrayObject(CompilerConstraintList* constraints, MDefinition* def)
2165 {
2166     MOZ_ASSERT(def->type() == MIRType_Object);
2167 
2168     TemporaryTypeSet* types = def->resultTypeSet();
2169     if (!types)
2170         return false;
2171 
2172     return types->forAllClasses(constraints, IsTypedArrayClass) ==
2173            TemporaryTypeSet::ForAllResult::ALL_TRUE;
2174 }
2175 
2176 IonBuilder::InliningStatus
inlineTypedArrayLength(CallInfo & callInfo)2177 IonBuilder::inlineTypedArrayLength(CallInfo& callInfo)
2178 {
2179     MOZ_ASSERT(!callInfo.constructing());
2180     MOZ_ASSERT(callInfo.argc() == 1);
2181     if (callInfo.getArg(0)->type() != MIRType_Object)
2182         return InliningStatus_NotInlined;
2183     if (getInlineReturnType() != MIRType_Int32)
2184         return InliningStatus_NotInlined;
2185 
2186     // Note that the argument we see here is not necessarily a typed array.
2187     // If it's not, this call should be unreachable though.
2188     if (!IsTypedArrayObject(constraints(), callInfo.getArg(0)))
2189         return InliningStatus_NotInlined;
2190 
2191     MInstruction* length = addTypedArrayLength(callInfo.getArg(0));
2192     current->push(length);
2193 
2194     callInfo.setImplicitlyUsedUnchecked();
2195     return InliningStatus_Inlined;
2196 }
2197 
2198 IonBuilder::InliningStatus
inlineSetDisjointTypedElements(CallInfo & callInfo)2199 IonBuilder::inlineSetDisjointTypedElements(CallInfo& callInfo)
2200 {
2201     MOZ_ASSERT(!callInfo.constructing());
2202     MOZ_ASSERT(callInfo.argc() == 3);
2203 
2204     // Initial argument requirements.
2205 
2206     MDefinition* target = callInfo.getArg(0);
2207     if (target->type() != MIRType_Object)
2208         return InliningStatus_NotInlined;
2209 
2210     if (getInlineReturnType() != MIRType_Undefined)
2211         return InliningStatus_NotInlined;
2212 
2213     MDefinition* targetOffset = callInfo.getArg(1);
2214     MOZ_ASSERT(targetOffset->type() == MIRType_Int32);
2215 
2216     MDefinition* sourceTypedArray = callInfo.getArg(2);
2217     if (sourceTypedArray->type() != MIRType_Object)
2218         return InliningStatus_NotInlined;
2219 
2220     // Only attempt to optimize if |target| and |sourceTypedArray| are both
2221     // definitely typed arrays.  (The former always is.  The latter is not,
2222     // necessarily, because of wrappers.)
2223     if (!IsTypedArrayObject(constraints(), target) ||
2224         !IsTypedArrayObject(constraints(), sourceTypedArray))
2225     {
2226         return InliningStatus_NotInlined;
2227     }
2228 
2229     auto sets = MSetDisjointTypedElements::New(alloc(), target, targetOffset, sourceTypedArray);
2230     current->add(sets);
2231 
2232     pushConstant(UndefinedValue());
2233 
2234     if (!resumeAfter(sets))
2235         return InliningStatus_Error;
2236 
2237     callInfo.setImplicitlyUsedUnchecked();
2238     return InliningStatus_Inlined;
2239 }
2240 
2241 IonBuilder::InliningStatus
inlineObjectIsTypeDescr(CallInfo & callInfo)2242 IonBuilder::inlineObjectIsTypeDescr(CallInfo& callInfo)
2243 {
2244     if (callInfo.constructing() || callInfo.argc() != 1) {
2245         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2246         return InliningStatus_NotInlined;
2247     }
2248 
2249     if (callInfo.getArg(0)->type() != MIRType_Object)
2250         return InliningStatus_NotInlined;
2251     if (getInlineReturnType() != MIRType_Boolean)
2252         return InliningStatus_NotInlined;
2253 
2254     // The test is elaborate: in-line only if there is exact
2255     // information.
2256 
2257     TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
2258     if (!types)
2259         return InliningStatus_NotInlined;
2260 
2261     bool result = false;
2262     switch (types->forAllClasses(constraints(), IsTypeDescrClass)) {
2263     case TemporaryTypeSet::ForAllResult::ALL_FALSE:
2264     case TemporaryTypeSet::ForAllResult::EMPTY:
2265         result = false;
2266         break;
2267     case TemporaryTypeSet::ForAllResult::ALL_TRUE:
2268         result = true;
2269         break;
2270     case TemporaryTypeSet::ForAllResult::MIXED:
2271         return InliningStatus_NotInlined;
2272     }
2273 
2274     pushConstant(BooleanValue(result));
2275 
2276     callInfo.setImplicitlyUsedUnchecked();
2277     return InliningStatus_Inlined;
2278 }
2279 
2280 IonBuilder::InliningStatus
inlineSetTypedObjectOffset(CallInfo & callInfo)2281 IonBuilder::inlineSetTypedObjectOffset(CallInfo& callInfo)
2282 {
2283     if (callInfo.argc() != 2 || callInfo.constructing()) {
2284         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2285         return InliningStatus_NotInlined;
2286     }
2287 
2288     MDefinition* typedObj = callInfo.getArg(0);
2289     MDefinition* offset = callInfo.getArg(1);
2290 
2291     // Return type should be undefined or something wacky is going on.
2292     if (getInlineReturnType() != MIRType_Undefined)
2293         return InliningStatus_NotInlined;
2294 
2295     // Check typedObj is a, well, typed object. Go ahead and use TI
2296     // data. If this check should fail, that is almost certainly a bug
2297     // in self-hosted code -- either because it's not being careful
2298     // with TI or because of something else -- but we'll just let it
2299     // fall through to the SetTypedObjectOffset intrinsic in such
2300     // cases.
2301     TemporaryTypeSet* types = typedObj->resultTypeSet();
2302     if (typedObj->type() != MIRType_Object || !types)
2303         return InliningStatus_NotInlined;
2304     switch (types->forAllClasses(constraints(), IsTypedObjectClass)) {
2305       case TemporaryTypeSet::ForAllResult::ALL_FALSE:
2306       case TemporaryTypeSet::ForAllResult::EMPTY:
2307       case TemporaryTypeSet::ForAllResult::MIXED:
2308         return InliningStatus_NotInlined;
2309       case TemporaryTypeSet::ForAllResult::ALL_TRUE:
2310         break;
2311     }
2312 
2313     // Check type of offset argument is an integer.
2314     if (offset->type() != MIRType_Int32)
2315         return InliningStatus_NotInlined;
2316 
2317     callInfo.setImplicitlyUsedUnchecked();
2318     MInstruction* ins = MSetTypedObjectOffset::New(alloc(), typedObj, offset);
2319     current->add(ins);
2320     current->push(ins);
2321     return InliningStatus_Inlined;
2322 }
2323 
2324 IonBuilder::InliningStatus
inlineUnsafeSetReservedSlot(CallInfo & callInfo)2325 IonBuilder::inlineUnsafeSetReservedSlot(CallInfo& callInfo)
2326 {
2327     if (callInfo.argc() != 3 || callInfo.constructing()) {
2328         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2329         return InliningStatus_NotInlined;
2330     }
2331     if (getInlineReturnType() != MIRType_Undefined)
2332         return InliningStatus_NotInlined;
2333     if (callInfo.getArg(0)->type() != MIRType_Object)
2334         return InliningStatus_NotInlined;
2335     if (callInfo.getArg(1)->type() != MIRType_Int32)
2336         return InliningStatus_NotInlined;
2337 
2338     // Don't inline if we don't have a constant slot.
2339     MDefinition* arg = callInfo.getArg(1);
2340     if (!arg->isConstantValue())
2341         return InliningStatus_NotInlined;
2342     uint32_t slot = arg->constantValue().toPrivateUint32();
2343 
2344     callInfo.setImplicitlyUsedUnchecked();
2345 
2346     MStoreFixedSlot* store =
2347         MStoreFixedSlot::NewBarriered(alloc(), callInfo.getArg(0), slot, callInfo.getArg(2));
2348     current->add(store);
2349     current->push(store);
2350 
2351     if (NeedsPostBarrier(callInfo.getArg(2)))
2352         current->add(MPostWriteBarrier::New(alloc(), callInfo.getArg(0), callInfo.getArg(2)));
2353 
2354     return InliningStatus_Inlined;
2355 }
2356 
2357 IonBuilder::InliningStatus
inlineUnsafeGetReservedSlot(CallInfo & callInfo,MIRType knownValueType)2358 IonBuilder::inlineUnsafeGetReservedSlot(CallInfo& callInfo, MIRType knownValueType)
2359 {
2360     if (callInfo.argc() != 2 || callInfo.constructing()) {
2361         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2362         return InliningStatus_NotInlined;
2363     }
2364     if (callInfo.getArg(0)->type() != MIRType_Object)
2365         return InliningStatus_NotInlined;
2366     if (callInfo.getArg(1)->type() != MIRType_Int32)
2367         return InliningStatus_NotInlined;
2368 
2369     // Don't inline if we don't have a constant slot.
2370     MDefinition* arg = callInfo.getArg(1);
2371     if (!arg->isConstantValue())
2372         return InliningStatus_NotInlined;
2373     uint32_t slot = arg->constantValue().toPrivateUint32();
2374 
2375     callInfo.setImplicitlyUsedUnchecked();
2376 
2377     MLoadFixedSlot* load = MLoadFixedSlot::New(alloc(), callInfo.getArg(0), slot);
2378     current->add(load);
2379     current->push(load);
2380     if (knownValueType != MIRType_Value) {
2381         // We know what type we have in this slot.  Assert that this is in fact
2382         // what we've seen coming from this slot in the past, then tell the
2383         // MLoadFixedSlot about its result type.  That will make us do an
2384         // infallible unbox as part of the slot load and then we'll barrier on
2385         // the unbox result.  That way the type barrier code won't end up doing
2386         // MIRType checks and conditional unboxing.
2387         MOZ_ASSERT_IF(!getInlineReturnTypeSet()->empty(),
2388                       getInlineReturnType() == knownValueType);
2389         load->setResultType(knownValueType);
2390     }
2391 
2392     // We don't track reserved slot types, so always emit a barrier.
2393     if (!pushTypeBarrier(load, getInlineReturnTypeSet(), BarrierKind::TypeSet))
2394         return InliningStatus_Error;
2395 
2396     return InliningStatus_Inlined;
2397 }
2398 
2399 IonBuilder::InliningStatus
inlineIsCallable(CallInfo & callInfo)2400 IonBuilder::inlineIsCallable(CallInfo& callInfo)
2401 {
2402     if (callInfo.argc() != 1 || callInfo.constructing()) {
2403         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2404         return InliningStatus_NotInlined;
2405     }
2406 
2407     if (getInlineReturnType() != MIRType_Boolean)
2408         return InliningStatus_NotInlined;
2409     if (callInfo.getArg(0)->type() != MIRType_Object)
2410         return InliningStatus_NotInlined;
2411 
2412     // Try inlining with constant true/false: only objects may be callable at
2413     // all, and if we know the class check if it is callable.
2414     bool isCallableKnown = false;
2415     bool isCallableConstant;
2416     if (callInfo.getArg(0)->type() != MIRType_Object) {
2417         isCallableKnown = true;
2418         isCallableConstant = false;
2419     } else {
2420         TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
2421         const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
2422         if (clasp && !clasp->isProxy()) {
2423             isCallableKnown = true;
2424             isCallableConstant = clasp->nonProxyCallable();
2425         }
2426     }
2427 
2428     callInfo.setImplicitlyUsedUnchecked();
2429 
2430     if (isCallableKnown) {
2431         MConstant* constant = MConstant::New(alloc(), BooleanValue(isCallableConstant));
2432         current->add(constant);
2433         current->push(constant);
2434         return InliningStatus_Inlined;
2435     }
2436 
2437     MIsCallable* isCallable = MIsCallable::New(alloc(), callInfo.getArg(0));
2438     current->add(isCallable);
2439     current->push(isCallable);
2440 
2441     return InliningStatus_Inlined;
2442 }
2443 
2444 IonBuilder::InliningStatus
inlineIsObject(CallInfo & callInfo)2445 IonBuilder::inlineIsObject(CallInfo& callInfo)
2446 {
2447     if (callInfo.argc() != 1 || callInfo.constructing()) {
2448         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2449         return InliningStatus_NotInlined;
2450     }
2451     if (getInlineReturnType() != MIRType_Boolean)
2452         return InliningStatus_NotInlined;
2453 
2454     callInfo.setImplicitlyUsedUnchecked();
2455     if (callInfo.getArg(0)->type() == MIRType_Object) {
2456         pushConstant(BooleanValue(true));
2457     } else {
2458         MIsObject* isObject = MIsObject::New(alloc(), callInfo.getArg(0));
2459         current->add(isObject);
2460         current->push(isObject);
2461     }
2462     return InliningStatus_Inlined;
2463 }
2464 
2465 IonBuilder::InliningStatus
inlineToObject(CallInfo & callInfo)2466 IonBuilder::inlineToObject(CallInfo& callInfo)
2467 {
2468     if (callInfo.argc() != 1 || callInfo.constructing()) {
2469         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2470         return InliningStatus_NotInlined;
2471     }
2472 
2473     // If we know the input type is an object, nop ToObject.
2474     if (getInlineReturnType() != MIRType_Object)
2475         return InliningStatus_NotInlined;
2476     if (callInfo.getArg(0)->type() != MIRType_Object)
2477         return InliningStatus_NotInlined;
2478 
2479     callInfo.setImplicitlyUsedUnchecked();
2480     MDefinition* object = callInfo.getArg(0);
2481 
2482     current->push(object);
2483     return InliningStatus_Inlined;
2484 }
2485 
2486 IonBuilder::InliningStatus
inlineToInteger(CallInfo & callInfo)2487 IonBuilder::inlineToInteger(CallInfo& callInfo)
2488 {
2489     if (callInfo.argc() != 1 || callInfo.constructing()) {
2490         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2491         return InliningStatus_NotInlined;
2492     }
2493 
2494     MDefinition* input = callInfo.getArg(0);
2495 
2496     // Only optimize cases where input contains only number, null or boolean
2497     if (input->mightBeType(MIRType_Object) ||
2498         input->mightBeType(MIRType_String) ||
2499         input->mightBeType(MIRType_Symbol) ||
2500         input->mightBeType(MIRType_Undefined) ||
2501         input->mightBeMagicType())
2502     {
2503         return InliningStatus_NotInlined;
2504     }
2505 
2506     MOZ_ASSERT(input->type() == MIRType_Value || input->type() == MIRType_Null ||
2507                input->type() == MIRType_Boolean || IsNumberType(input->type()));
2508 
2509     // Only optimize cases where output is int32
2510     if (getInlineReturnType() != MIRType_Int32)
2511         return InliningStatus_NotInlined;
2512 
2513     callInfo.setImplicitlyUsedUnchecked();
2514 
2515     MToInt32* toInt32 = MToInt32::New(alloc(), callInfo.getArg(0));
2516     current->add(toInt32);
2517     current->push(toInt32);
2518     return InliningStatus_Inlined;
2519 }
2520 
2521 IonBuilder::InliningStatus
inlineToString(CallInfo & callInfo)2522 IonBuilder::inlineToString(CallInfo& callInfo)
2523 {
2524     if (callInfo.argc() != 1 || callInfo.constructing())
2525         return InliningStatus_NotInlined;
2526 
2527     if (getInlineReturnType() != MIRType_String)
2528         return InliningStatus_NotInlined;
2529 
2530     callInfo.setImplicitlyUsedUnchecked();
2531     MToString* toString = MToString::New(alloc(), callInfo.getArg(0));
2532     current->add(toString);
2533     current->push(toString);
2534     return InliningStatus_Inlined;
2535 }
2536 
2537 IonBuilder::InliningStatus
inlineBailout(CallInfo & callInfo)2538 IonBuilder::inlineBailout(CallInfo& callInfo)
2539 {
2540     callInfo.setImplicitlyUsedUnchecked();
2541 
2542     current->add(MBail::New(alloc()));
2543 
2544     MConstant* undefined = MConstant::New(alloc(), UndefinedValue());
2545     current->add(undefined);
2546     current->push(undefined);
2547     return InliningStatus_Inlined;
2548 }
2549 
2550 IonBuilder::InliningStatus
inlineAssertFloat32(CallInfo & callInfo)2551 IonBuilder::inlineAssertFloat32(CallInfo& callInfo)
2552 {
2553     if (callInfo.argc() != 2)
2554         return InliningStatus_NotInlined;
2555 
2556     MDefinition* secondArg = callInfo.getArg(1);
2557 
2558     MOZ_ASSERT(secondArg->type() == MIRType_Boolean);
2559     MOZ_ASSERT(secondArg->isConstantValue());
2560 
2561     bool mustBeFloat32 = secondArg->constantValue().toBoolean();
2562     current->add(MAssertFloat32::New(alloc(), callInfo.getArg(0), mustBeFloat32));
2563 
2564     MConstant* undefined = MConstant::New(alloc(), UndefinedValue());
2565     current->add(undefined);
2566     current->push(undefined);
2567     callInfo.setImplicitlyUsedUnchecked();
2568     return InliningStatus_Inlined;
2569 }
2570 
2571 IonBuilder::InliningStatus
inlineAssertRecoveredOnBailout(CallInfo & callInfo)2572 IonBuilder::inlineAssertRecoveredOnBailout(CallInfo& callInfo)
2573 {
2574     if (callInfo.argc() != 2)
2575         return InliningStatus_NotInlined;
2576 
2577     if (JitOptions.checkRangeAnalysis) {
2578         // If we are checking the range of all instructions, then the guards
2579         // inserted by Range Analysis prevent the use of recover
2580         // instruction. Thus, we just disable these checks.
2581         current->push(constant(UndefinedValue()));
2582         callInfo.setImplicitlyUsedUnchecked();
2583         return InliningStatus_Inlined;
2584     }
2585 
2586     MDefinition* secondArg = callInfo.getArg(1);
2587 
2588     MOZ_ASSERT(secondArg->type() == MIRType_Boolean);
2589     MOZ_ASSERT(secondArg->isConstantValue());
2590 
2591     bool mustBeRecovered = secondArg->constantValue().toBoolean();
2592     MAssertRecoveredOnBailout* assert =
2593         MAssertRecoveredOnBailout::New(alloc(), callInfo.getArg(0), mustBeRecovered);
2594     current->add(assert);
2595     current->push(assert);
2596 
2597     // Create an instruction sequence which implies that the argument of the
2598     // assertRecoveredOnBailout function would be encoded at least in one
2599     // Snapshot.
2600     MNop* nop = MNop::New(alloc());
2601     current->add(nop);
2602     if (!resumeAfter(nop))
2603         return InliningStatus_Error;
2604     current->add(MEncodeSnapshot::New(alloc()));
2605 
2606     current->pop();
2607     current->push(constant(UndefinedValue()));
2608     callInfo.setImplicitlyUsedUnchecked();
2609     return InliningStatus_Inlined;
2610 }
2611 
2612 IonBuilder::InliningStatus
inlineBoundFunction(CallInfo & nativeCallInfo,JSFunction * target)2613 IonBuilder::inlineBoundFunction(CallInfo& nativeCallInfo, JSFunction* target)
2614 {
2615     trackOptimizationOutcome(TrackedOutcome::CantInlineBound);
2616 
2617     if (!target->getBoundFunctionTarget()->is<JSFunction>())
2618         return InliningStatus_NotInlined;
2619 
2620     JSFunction* scriptedTarget = &(target->getBoundFunctionTarget()->as<JSFunction>());
2621 
2622     // Don't optimize if we're constructing and the callee is not a
2623     // constructor, so that CallKnown does not have to handle this case
2624     // (it should always throw).
2625     if (nativeCallInfo.constructing() && !scriptedTarget->isConstructor())
2626         return InliningStatus_NotInlined;
2627 
2628     if (nativeCallInfo.constructing() && nativeCallInfo.getNewTarget() != nativeCallInfo.fun())
2629         return InliningStatus_NotInlined;
2630 
2631     if (gc::IsInsideNursery(scriptedTarget))
2632         return InliningStatus_NotInlined;
2633 
2634     for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) {
2635         const Value val = target->getBoundFunctionArgument(i);
2636         if (val.isObject() && gc::IsInsideNursery(&val.toObject()))
2637             return InliningStatus_NotInlined;
2638         if (val.isString() && !val.toString()->isAtom())
2639             return InliningStatus_NotInlined;
2640     }
2641 
2642     const Value thisVal = target->getBoundFunctionThis();
2643     if (thisVal.isObject() && gc::IsInsideNursery(&thisVal.toObject()))
2644         return InliningStatus_NotInlined;
2645     if (thisVal.isString() && !thisVal.toString()->isAtom())
2646         return InliningStatus_NotInlined;
2647 
2648     size_t argc = target->getBoundFunctionArgumentCount() + nativeCallInfo.argc();
2649     if (argc > ARGS_LENGTH_MAX)
2650         return InliningStatus_NotInlined;
2651 
2652     nativeCallInfo.thisArg()->setImplicitlyUsedUnchecked();
2653 
2654     CallInfo callInfo(alloc(), nativeCallInfo.constructing());
2655     callInfo.setFun(constant(ObjectValue(*scriptedTarget)));
2656     callInfo.setThis(constant(thisVal));
2657 
2658     if (!callInfo.argv().reserve(argc))
2659         return InliningStatus_Error;
2660 
2661     for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) {
2662         MConstant* argConst = constant(target->getBoundFunctionArgument(i));
2663         callInfo.argv().infallibleAppend(argConst);
2664     }
2665     for (size_t i = 0; i < nativeCallInfo.argc(); i++)
2666         callInfo.argv().infallibleAppend(nativeCallInfo.getArg(i));
2667 
2668     // We only inline when it was not a super-call, so just set the newTarget
2669     // to be the target function, per spec.
2670     if (nativeCallInfo.constructing())
2671         callInfo.setNewTarget(callInfo.fun());
2672 
2673     if (!makeCall(scriptedTarget, callInfo))
2674         return InliningStatus_Error;
2675 
2676     return InliningStatus_Inlined;
2677 }
2678 
2679 IonBuilder::InliningStatus
inlineAtomicsCompareExchange(CallInfo & callInfo)2680 IonBuilder::inlineAtomicsCompareExchange(CallInfo& callInfo)
2681 {
2682     if (callInfo.argc() != 4 || callInfo.constructing()) {
2683         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2684         return InliningStatus_NotInlined;
2685     }
2686 
2687     // These guards are desirable here and in subsequent atomics to
2688     // avoid bad bailouts with MTruncateToInt32, see https://bugzilla.mozilla.org/show_bug.cgi?id=1141986#c20.
2689     MDefinition* oldval = callInfo.getArg(2);
2690     if (oldval->mightBeType(MIRType_Object) || oldval->mightBeType(MIRType_Symbol))
2691         return InliningStatus_NotInlined;
2692 
2693     MDefinition* newval = callInfo.getArg(3);
2694     if (newval->mightBeType(MIRType_Object) || newval->mightBeType(MIRType_Symbol))
2695         return InliningStatus_NotInlined;
2696 
2697     Scalar::Type arrayType;
2698     bool requiresCheck = false;
2699     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
2700         return InliningStatus_NotInlined;
2701 
2702     callInfo.setImplicitlyUsedUnchecked();
2703 
2704     MInstruction* elements;
2705     MDefinition* index;
2706     atomicsCheckBounds(callInfo, &elements, &index);
2707 
2708     if (requiresCheck)
2709         addSharedTypedArrayGuard(callInfo.getArg(0));
2710 
2711     MCompareExchangeTypedArrayElement* cas =
2712         MCompareExchangeTypedArrayElement::New(alloc(), elements, index, arrayType, oldval, newval);
2713     cas->setResultType(getInlineReturnType());
2714     current->add(cas);
2715     current->push(cas);
2716 
2717     if (!resumeAfter(cas))
2718         return InliningStatus_Error;
2719 
2720     return InliningStatus_Inlined;
2721 }
2722 
2723 IonBuilder::InliningStatus
inlineAtomicsExchange(CallInfo & callInfo)2724 IonBuilder::inlineAtomicsExchange(CallInfo& callInfo)
2725 {
2726     if (callInfo.argc() != 3 || callInfo.constructing()) {
2727         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2728         return InliningStatus_NotInlined;
2729     }
2730 
2731     MDefinition* value = callInfo.getArg(2);
2732     if (value->mightBeType(MIRType_Object) || value->mightBeType(MIRType_Symbol))
2733         return InliningStatus_NotInlined;
2734 
2735     Scalar::Type arrayType;
2736     bool requiresCheck = false;
2737     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
2738         return InliningStatus_NotInlined;
2739 
2740     callInfo.setImplicitlyUsedUnchecked();
2741 
2742     MInstruction* elements;
2743     MDefinition* index;
2744     atomicsCheckBounds(callInfo, &elements, &index);
2745 
2746     if (requiresCheck)
2747         addSharedTypedArrayGuard(callInfo.getArg(0));
2748 
2749     MInstruction* exchange =
2750         MAtomicExchangeTypedArrayElement::New(alloc(), elements, index, value, arrayType);
2751     exchange->setResultType(getInlineReturnType());
2752     current->add(exchange);
2753     current->push(exchange);
2754 
2755     if (!resumeAfter(exchange))
2756         return InliningStatus_Error;
2757 
2758     return InliningStatus_Inlined;
2759 }
2760 
2761 IonBuilder::InliningStatus
inlineAtomicsLoad(CallInfo & callInfo)2762 IonBuilder::inlineAtomicsLoad(CallInfo& callInfo)
2763 {
2764     if (callInfo.argc() != 2 || callInfo.constructing()) {
2765         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2766         return InliningStatus_NotInlined;
2767     }
2768 
2769     Scalar::Type arrayType;
2770     bool requiresCheck = false;
2771     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
2772         return InliningStatus_NotInlined;
2773 
2774     callInfo.setImplicitlyUsedUnchecked();
2775 
2776     MInstruction* elements;
2777     MDefinition* index;
2778     atomicsCheckBounds(callInfo, &elements, &index);
2779 
2780     if (requiresCheck)
2781         addSharedTypedArrayGuard(callInfo.getArg(0));
2782 
2783     MLoadUnboxedScalar* load =
2784         MLoadUnboxedScalar::New(alloc(), elements, index, arrayType,
2785                                 DoesRequireMemoryBarrier);
2786     load->setResultType(getInlineReturnType());
2787     current->add(load);
2788     current->push(load);
2789 
2790     // Loads are considered effectful (they execute a memory barrier).
2791     if (!resumeAfter(load))
2792         return InliningStatus_Error;
2793 
2794     return InliningStatus_Inlined;
2795 }
2796 
2797 IonBuilder::InliningStatus
inlineAtomicsStore(CallInfo & callInfo)2798 IonBuilder::inlineAtomicsStore(CallInfo& callInfo)
2799 {
2800     if (callInfo.argc() != 3 || callInfo.constructing()) {
2801         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2802         return InliningStatus_NotInlined;
2803     }
2804 
2805     MDefinition* value = callInfo.getArg(2);
2806     if (value->mightBeType(MIRType_Object) || value->mightBeType(MIRType_Symbol))
2807         return InliningStatus_NotInlined;
2808 
2809     Scalar::Type arrayType;
2810     bool requiresCheck = false;
2811     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck, DontCheckAtomicResult))
2812         return InliningStatus_NotInlined;
2813 
2814     callInfo.setImplicitlyUsedUnchecked();
2815 
2816     MInstruction* elements;
2817     MDefinition* index;
2818     atomicsCheckBounds(callInfo, &elements, &index);
2819 
2820     if (requiresCheck)
2821         addSharedTypedArrayGuard(callInfo.getArg(0));
2822 
2823     MDefinition* toWrite = value;
2824     if (value->type() != MIRType_Int32) {
2825         toWrite = MTruncateToInt32::New(alloc(), value);
2826         current->add(toWrite->toInstruction());
2827     }
2828     MStoreUnboxedScalar* store =
2829         MStoreUnboxedScalar::New(alloc(), elements, index, toWrite, arrayType,
2830                                  MStoreUnboxedScalar::TruncateInput, DoesRequireMemoryBarrier);
2831     current->add(store);
2832     current->push(value);
2833 
2834     if (!resumeAfter(store))
2835         return InliningStatus_Error;
2836 
2837     return InliningStatus_Inlined;
2838 }
2839 
2840 IonBuilder::InliningStatus
inlineAtomicsFence(CallInfo & callInfo)2841 IonBuilder::inlineAtomicsFence(CallInfo& callInfo)
2842 {
2843     if (callInfo.argc() != 0 || callInfo.constructing()) {
2844         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2845         return InliningStatus_NotInlined;
2846     }
2847 
2848     if (!JitSupportsAtomics())
2849         return InliningStatus_NotInlined;
2850 
2851     callInfo.setImplicitlyUsedUnchecked();
2852 
2853     MMemoryBarrier* fence = MMemoryBarrier::New(alloc());
2854     current->add(fence);
2855     pushConstant(UndefinedValue());
2856 
2857     // Fences are considered effectful (they execute a memory barrier).
2858     if (!resumeAfter(fence))
2859         return InliningStatus_Error;
2860 
2861     return InliningStatus_Inlined;
2862 }
2863 
2864 IonBuilder::InliningStatus
inlineAtomicsBinop(CallInfo & callInfo,InlinableNative target)2865 IonBuilder::inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target)
2866 {
2867     if (callInfo.argc() != 3 || callInfo.constructing()) {
2868         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2869         return InliningStatus_NotInlined;
2870     }
2871 
2872     MDefinition* value = callInfo.getArg(2);
2873     if (value->mightBeType(MIRType_Object) || value->mightBeType(MIRType_Symbol))
2874         return InliningStatus_NotInlined;
2875 
2876     Scalar::Type arrayType;
2877     bool requiresCheck = false;
2878     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
2879         return InliningStatus_NotInlined;
2880 
2881     callInfo.setImplicitlyUsedUnchecked();
2882 
2883     if (requiresCheck)
2884         addSharedTypedArrayGuard(callInfo.getArg(0));
2885 
2886     MInstruction* elements;
2887     MDefinition* index;
2888     atomicsCheckBounds(callInfo, &elements, &index);
2889 
2890     AtomicOp k = AtomicFetchAddOp;
2891     switch (target) {
2892       case InlinableNative::AtomicsAdd:
2893         k = AtomicFetchAddOp;
2894         break;
2895       case InlinableNative::AtomicsSub:
2896         k = AtomicFetchSubOp;
2897         break;
2898       case InlinableNative::AtomicsAnd:
2899         k = AtomicFetchAndOp;
2900         break;
2901       case InlinableNative::AtomicsOr:
2902         k = AtomicFetchOrOp;
2903         break;
2904       case InlinableNative::AtomicsXor:
2905         k = AtomicFetchXorOp;
2906         break;
2907       default:
2908         MOZ_CRASH("Bad atomic operation");
2909     }
2910 
2911     MAtomicTypedArrayElementBinop* binop =
2912         MAtomicTypedArrayElementBinop::New(alloc(), k, elements, index, arrayType, value);
2913     binop->setResultType(getInlineReturnType());
2914     current->add(binop);
2915     current->push(binop);
2916 
2917     if (!resumeAfter(binop))
2918         return InliningStatus_Error;
2919 
2920     return InliningStatus_Inlined;
2921 }
2922 
2923 IonBuilder::InliningStatus
inlineAtomicsIsLockFree(CallInfo & callInfo)2924 IonBuilder::inlineAtomicsIsLockFree(CallInfo& callInfo)
2925 {
2926     if (callInfo.argc() != 1 || callInfo.constructing()) {
2927         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2928         return InliningStatus_NotInlined;
2929     }
2930 
2931     callInfo.setImplicitlyUsedUnchecked();
2932 
2933     MAtomicIsLockFree* ilf =
2934         MAtomicIsLockFree::New(alloc(), callInfo.getArg(0));
2935     current->add(ilf);
2936     current->push(ilf);
2937 
2938     return InliningStatus_Inlined;
2939 }
2940 
2941 bool
atomicsMeetsPreconditions(CallInfo & callInfo,Scalar::Type * arrayType,bool * requiresTagCheck,AtomicCheckResult checkResult)2942 IonBuilder::atomicsMeetsPreconditions(CallInfo& callInfo, Scalar::Type* arrayType,
2943                                       bool* requiresTagCheck, AtomicCheckResult checkResult)
2944 {
2945     if (!JitSupportsAtomics())
2946         return false;
2947 
2948     if (callInfo.getArg(0)->type() != MIRType_Object)
2949         return false;
2950 
2951     if (callInfo.getArg(1)->type() != MIRType_Int32)
2952         return false;
2953 
2954     // Ensure that the first argument is a TypedArray that maps shared
2955     // memory.
2956     //
2957     // Then check both that the element type is something we can
2958     // optimize and that the return type is suitable for that element
2959     // type.
2960 
2961     TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
2962     if (!arg0Types)
2963         return false;
2964 
2965     TemporaryTypeSet::TypedArraySharedness sharedness;
2966     *arrayType = arg0Types->getTypedArrayType(constraints(), &sharedness);
2967     *requiresTagCheck = sharedness != TemporaryTypeSet::KnownShared;
2968     switch (*arrayType) {
2969       case Scalar::Int8:
2970       case Scalar::Uint8:
2971       case Scalar::Int16:
2972       case Scalar::Uint16:
2973       case Scalar::Int32:
2974         return checkResult == DontCheckAtomicResult || getInlineReturnType() == MIRType_Int32;
2975       case Scalar::Uint32:
2976         // Bug 1077305: it would be attractive to allow inlining even
2977         // if the inline return type is Int32, which it will frequently
2978         // be.
2979         return checkResult == DontCheckAtomicResult || getInlineReturnType() == MIRType_Double;
2980       default:
2981         // Excludes floating types and Uint8Clamped.
2982         return false;
2983     }
2984 }
2985 
2986 void
atomicsCheckBounds(CallInfo & callInfo,MInstruction ** elements,MDefinition ** index)2987 IonBuilder::atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index)
2988 {
2989     // Perform bounds checking and extract the elements vector.
2990     MDefinition* obj = callInfo.getArg(0);
2991     MInstruction* length = nullptr;
2992     *index = callInfo.getArg(1);
2993     *elements = nullptr;
2994     addTypedArrayLengthAndData(obj, DoBoundsCheck, index, &length, elements);
2995 }
2996 
2997 IonBuilder::InliningStatus
inlineIsConstructing(CallInfo & callInfo)2998 IonBuilder::inlineIsConstructing(CallInfo& callInfo)
2999 {
3000     MOZ_ASSERT(!callInfo.constructing());
3001     MOZ_ASSERT(callInfo.argc() == 0);
3002     MOZ_ASSERT(script()->functionNonDelazifying(),
3003                "isConstructing() should only be called in function scripts");
3004 
3005     if (getInlineReturnType() != MIRType_Boolean)
3006         return InliningStatus_NotInlined;
3007 
3008     callInfo.setImplicitlyUsedUnchecked();
3009 
3010     if (inliningDepth_ == 0) {
3011         MInstruction* ins = MIsConstructing::New(alloc());
3012         current->add(ins);
3013         current->push(ins);
3014         return InliningStatus_Inlined;
3015     }
3016 
3017     bool constructing = inlineCallInfo_->constructing();
3018     pushConstant(BooleanValue(constructing));
3019     return InliningStatus_Inlined;
3020 }
3021 
3022 IonBuilder::InliningStatus
inlineConstructTypedObject(CallInfo & callInfo,TypeDescr * descr)3023 IonBuilder::inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* descr)
3024 {
3025     // Only inline default constructors for now.
3026     if (callInfo.argc() != 0) {
3027         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
3028         return InliningStatus_NotInlined;
3029     }
3030 
3031     if (size_t(descr->size()) > InlineTypedObject::MaximumSize)
3032         return InliningStatus_NotInlined;
3033 
3034     JSObject* obj = inspector->getTemplateObjectForClassHook(pc, descr->getClass());
3035     if (!obj || !obj->is<InlineTypedObject>())
3036         return InliningStatus_NotInlined;
3037 
3038     InlineTypedObject* templateObject = &obj->as<InlineTypedObject>();
3039     if (&templateObject->typeDescr() != descr)
3040         return InliningStatus_NotInlined;
3041 
3042     callInfo.setImplicitlyUsedUnchecked();
3043 
3044     MNewTypedObject* ins = MNewTypedObject::New(alloc(), constraints(), templateObject,
3045                                                 templateObject->group()->initialHeap(constraints()));
3046     current->add(ins);
3047     current->push(ins);
3048 
3049     return InliningStatus_Inlined;
3050 }
3051 
3052 IonBuilder::InliningStatus
inlineSimdInt32x4(CallInfo & callInfo,JSNative native)3053 IonBuilder::inlineSimdInt32x4(CallInfo& callInfo, JSNative native)
3054 {
3055 #define INLINE_INT32X4_SIMD_ARITH_(OP)                                                           \
3056     if (native == js::simd_int32x4_##OP)                                                         \
3057         return inlineBinarySimd<MSimdBinaryArith>(callInfo, native, MSimdBinaryArith::Op_##OP,   \
3058                                                   SimdTypeDescr::Int32x4);
3059 
3060     ARITH_COMMONX4_SIMD_OP(INLINE_INT32X4_SIMD_ARITH_)
3061 #undef INLINE_INT32X4_SIMD_ARITH_
3062 
3063 #define INLINE_SIMD_BITWISE_(OP)                                                                 \
3064     if (native == js::simd_int32x4_##OP)                                                         \
3065         return inlineBinarySimd<MSimdBinaryBitwise>(callInfo, native, MSimdBinaryBitwise::OP##_, \
3066                                                     SimdTypeDescr::Int32x4);
3067 
3068     BITWISE_COMMONX4_SIMD_OP(INLINE_SIMD_BITWISE_)
3069 #undef INLINE_SIMD_BITWISE_
3070 
3071     if (native == js::simd_int32x4_shiftLeftByScalar)
3072         return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::lsh, SimdTypeDescr::Int32x4);
3073     if (native == js::simd_int32x4_shiftRightArithmeticByScalar)
3074         return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::rsh, SimdTypeDescr::Int32x4);
3075     if (native == js::simd_int32x4_shiftRightLogicalByScalar)
3076         return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::ursh, SimdTypeDescr::Int32x4);
3077 
3078 #define INLINE_SIMD_COMPARISON_(OP)                                                                \
3079     if (native == js::simd_int32x4_##OP)                                                           \
3080         return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Int32x4);
3081 
3082     COMP_COMMONX4_TO_INT32X4_SIMD_OP(INLINE_SIMD_COMPARISON_)
3083 #undef INLINE_SIMD_COMPARISON_
3084 
3085     if (native == js::simd_int32x4_extractLane)
3086         return inlineSimdExtractLane(callInfo, native, SimdTypeDescr::Int32x4);
3087     if (native == js::simd_int32x4_replaceLane)
3088         return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Int32x4);
3089 
3090     if (native == js::simd_int32x4_not)
3091         return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Int32x4);
3092     if (native == js::simd_int32x4_neg)
3093         return inlineUnarySimd(callInfo, native, MSimdUnaryArith::neg, SimdTypeDescr::Int32x4);
3094 
3095     typedef bool IsCast;
3096     if (native == js::simd_int32x4_fromFloat32x4)
3097         return inlineSimdConvert(callInfo, native, IsCast(false), SimdTypeDescr::Float32x4, SimdTypeDescr::Int32x4);
3098     if (native == js::simd_int32x4_fromFloat32x4Bits)
3099         return inlineSimdConvert(callInfo, native, IsCast(true), SimdTypeDescr::Float32x4, SimdTypeDescr::Int32x4);
3100 
3101     if (native == js::simd_int32x4_splat)
3102         return inlineSimdSplat(callInfo, native, SimdTypeDescr::Int32x4);
3103 
3104     if (native == js::simd_int32x4_check)
3105         return inlineSimdCheck(callInfo, native, SimdTypeDescr::Int32x4);
3106 
3107     typedef bool IsElementWise;
3108     if (native == js::simd_int32x4_select)
3109         return inlineSimdSelect(callInfo, native, IsElementWise(true), SimdTypeDescr::Int32x4);
3110     if (native == js::simd_int32x4_selectBits)
3111         return inlineSimdSelect(callInfo, native, IsElementWise(false), SimdTypeDescr::Int32x4);
3112 
3113     if (native == js::simd_int32x4_swizzle)
3114         return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Int32x4, 1, 4);
3115     if (native == js::simd_int32x4_shuffle)
3116         return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Int32x4, 2, 4);
3117 
3118     if (native == js::simd_int32x4_load)
3119         return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 4);
3120     if (native == js::simd_int32x4_load1)
3121         return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 1);
3122     if (native == js::simd_int32x4_load2)
3123         return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 2);
3124     if (native == js::simd_int32x4_load3)
3125         return inlineSimdLoad(callInfo, native, SimdTypeDescr::Int32x4, 3);
3126 
3127     if (native == js::simd_int32x4_store)
3128         return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 4);
3129     if (native == js::simd_int32x4_store1)
3130         return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 1);
3131     if (native == js::simd_int32x4_store2)
3132         return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 2);
3133     if (native == js::simd_int32x4_store3)
3134         return inlineSimdStore(callInfo, native, SimdTypeDescr::Int32x4, 3);
3135 
3136     return InliningStatus_NotInlined;
3137 }
3138 
3139 IonBuilder::InliningStatus
inlineSimdFloat32x4(CallInfo & callInfo,JSNative native)3140 IonBuilder::inlineSimdFloat32x4(CallInfo& callInfo, JSNative native)
3141 {
3142     // Simd functions
3143 #define INLINE_FLOAT32X4_SIMD_ARITH_(OP)                                                         \
3144     if (native == js::simd_float32x4_##OP)                                                       \
3145         return inlineBinarySimd<MSimdBinaryArith>(callInfo, native, MSimdBinaryArith::Op_##OP,   \
3146                                                   SimdTypeDescr::Float32x4);
3147 
3148     ARITH_COMMONX4_SIMD_OP(INLINE_FLOAT32X4_SIMD_ARITH_)
3149     BINARY_ARITH_FLOAT32X4_SIMD_OP(INLINE_FLOAT32X4_SIMD_ARITH_)
3150 #undef INLINE_FLOAT32X4_SIMD_ARITH_
3151 
3152 #define INLINE_SIMD_BITWISE_(OP)                                                                 \
3153     if (native == js::simd_float32x4_##OP)                                                       \
3154         return inlineBinarySimd<MSimdBinaryBitwise>(callInfo, native, MSimdBinaryBitwise::OP##_, \
3155                                                     SimdTypeDescr::Float32x4);
3156 
3157     BITWISE_COMMONX4_SIMD_OP(INLINE_SIMD_BITWISE_)
3158 #undef INLINE_SIMD_BITWISE_
3159 
3160 #define INLINE_SIMD_COMPARISON_(OP)                                                                \
3161     if (native == js::simd_float32x4_##OP)                                                         \
3162         return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Float32x4);
3163 
3164     COMP_COMMONX4_TO_INT32X4_SIMD_OP(INLINE_SIMD_COMPARISON_)
3165 #undef INLINE_SIMD_COMPARISON_
3166 
3167     if (native == js::simd_float32x4_extractLane)
3168         return inlineSimdExtractLane(callInfo, native, SimdTypeDescr::Float32x4);
3169     if (native == js::simd_float32x4_replaceLane)
3170         return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Float32x4);
3171 
3172 #define INLINE_SIMD_FLOAT32X4_UNARY_(OP)                                                           \
3173     if (native == js::simd_float32x4_##OP)                                                         \
3174         return inlineUnarySimd(callInfo, native, MSimdUnaryArith::OP, SimdTypeDescr::Float32x4);
3175 
3176     UNARY_ARITH_FLOAT32X4_SIMD_OP(INLINE_SIMD_FLOAT32X4_UNARY_)
3177     INLINE_SIMD_FLOAT32X4_UNARY_(neg)
3178 #undef INLINE_SIMD_FLOAT32X4_UNARY_
3179 
3180     if (native == js::simd_float32x4_not)
3181         return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Float32x4);
3182 
3183     typedef bool IsCast;
3184     if (native == js::simd_float32x4_fromInt32x4)
3185         return inlineSimdConvert(callInfo, native, IsCast(false), SimdTypeDescr::Int32x4, SimdTypeDescr::Float32x4);
3186     if (native == js::simd_float32x4_fromInt32x4Bits)
3187         return inlineSimdConvert(callInfo, native, IsCast(true), SimdTypeDescr::Int32x4, SimdTypeDescr::Float32x4);
3188 
3189     if (native == js::simd_float32x4_splat)
3190         return inlineSimdSplat(callInfo, native, SimdTypeDescr::Float32x4);
3191 
3192     if (native == js::simd_float32x4_check)
3193         return inlineSimdCheck(callInfo, native, SimdTypeDescr::Float32x4);
3194 
3195     typedef bool IsElementWise;
3196     if (native == js::simd_float32x4_select)
3197         return inlineSimdSelect(callInfo, native, IsElementWise(true), SimdTypeDescr::Float32x4);
3198 
3199     if (native == js::simd_float32x4_swizzle)
3200         return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Float32x4, 1, 4);
3201     if (native == js::simd_float32x4_shuffle)
3202         return inlineSimdShuffle(callInfo, native, SimdTypeDescr::Float32x4, 2, 4);
3203 
3204     if (native == js::simd_float32x4_load)
3205         return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 4);
3206     if (native == js::simd_float32x4_load1)
3207         return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 1);
3208     if (native == js::simd_float32x4_load2)
3209         return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 2);
3210     if (native == js::simd_float32x4_load3)
3211         return inlineSimdLoad(callInfo, native, SimdTypeDescr::Float32x4, 3);
3212 
3213     if (native == js::simd_float32x4_store)
3214         return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 4);
3215     if (native == js::simd_float32x4_store1)
3216         return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 1);
3217     if (native == js::simd_float32x4_store2)
3218         return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 2);
3219     if (native == js::simd_float32x4_store3)
3220         return inlineSimdStore(callInfo, native, SimdTypeDescr::Float32x4, 3);
3221 
3222     return InliningStatus_NotInlined;
3223 }
3224 
3225 IonBuilder::InliningStatus
inlineConstructSimdObject(CallInfo & callInfo,SimdTypeDescr * descr)3226 IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr)
3227 {
3228     // Generic constructor of SIMD valuesX4.
3229     MIRType simdType = SimdTypeDescrToMIRType(descr->type());
3230 
3231     // TODO Happens for Float64x2 (Bug 1124205) and Int8x16/Int16x8 (Bug 1136226)
3232     if (simdType == MIRType_Undefined)
3233         return InliningStatus_NotInlined;
3234 
3235     // Take the templateObject out of Baseline ICs, such that we can box
3236     // SIMD value type in the same kind of objects.
3237     MOZ_ASSERT(size_t(descr->size(descr->type())) < InlineTypedObject::MaximumSize);
3238     JSObject* templateObject = inspector->getTemplateObjectForClassHook(pc, descr->getClass());
3239     if (!templateObject)
3240         return InliningStatus_NotInlined;
3241 
3242     // The previous assertion ensures this will never fail if we were able to
3243     // allocate a templateObject in Baseline.
3244     InlineTypedObject* inlineTypedObject = &templateObject->as<InlineTypedObject>();
3245     MOZ_ASSERT(&inlineTypedObject->typeDescr() == descr);
3246 
3247     // When there are missing arguments, provide a default value
3248     // containing the coercion of 'undefined' to the right type.
3249     MConstant* defVal = nullptr;
3250     if (callInfo.argc() < SimdTypeToLength(simdType)) {
3251         MIRType laneType = SimdTypeToLaneType(simdType);
3252         if (laneType == MIRType_Int32) {
3253             defVal = constant(Int32Value(0));
3254         } else {
3255             MOZ_ASSERT(IsFloatingPointType(laneType));
3256             defVal = constant(DoubleNaNValue());
3257             defVal->setResultType(laneType);
3258         }
3259     }
3260 
3261     MSimdValueX4* values =
3262         MSimdValueX4::New(alloc(), simdType,
3263                           callInfo.getArgWithDefault(0, defVal), callInfo.getArgWithDefault(1, defVal),
3264                           callInfo.getArgWithDefault(2, defVal), callInfo.getArgWithDefault(3, defVal));
3265     current->add(values);
3266 
3267     MSimdBox* obj = MSimdBox::New(alloc(), constraints(), values, inlineTypedObject,
3268                                   inlineTypedObject->group()->initialHeap(constraints()));
3269     current->add(obj);
3270     current->push(obj);
3271 
3272     callInfo.setImplicitlyUsedUnchecked();
3273     return InliningStatus_Inlined;
3274 }
3275 
3276 bool
checkInlineSimd(CallInfo & callInfo,JSNative native,SimdTypeDescr::Type type,unsigned numArgs,InlineTypedObject ** templateObj)3277 IonBuilder::checkInlineSimd(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type,
3278                             unsigned numArgs, InlineTypedObject** templateObj)
3279 {
3280     if (callInfo.argc() != numArgs)
3281         return false;
3282 
3283     JSObject* templateObject = inspector->getTemplateObjectForNative(pc, native);
3284     if (!templateObject)
3285         return false;;
3286 
3287     InlineTypedObject* inlineTypedObject = &templateObject->as<InlineTypedObject>();
3288     MOZ_ASSERT(inlineTypedObject->typeDescr().as<SimdTypeDescr>().type() == type);
3289     *templateObj = inlineTypedObject;
3290     return true;
3291 }
3292 
3293 IonBuilder::InliningStatus
boxSimd(CallInfo & callInfo,MInstruction * ins,InlineTypedObject * templateObj)3294 IonBuilder::boxSimd(CallInfo& callInfo, MInstruction* ins, InlineTypedObject* templateObj)
3295 {
3296     MSimdBox* obj = MSimdBox::New(alloc(), constraints(), ins, templateObj,
3297                                   templateObj->group()->initialHeap(constraints()));
3298     current->add(ins);
3299     current->add(obj);
3300     current->push(obj);
3301 
3302     callInfo.setImplicitlyUsedUnchecked();
3303     return InliningStatus_Inlined;
3304 }
3305 
3306 template<typename T>
3307 IonBuilder::InliningStatus
inlineBinarySimd(CallInfo & callInfo,JSNative native,typename T::Operation op,SimdTypeDescr::Type type)3308 IonBuilder::inlineBinarySimd(CallInfo& callInfo, JSNative native, typename T::Operation op,
3309                              SimdTypeDescr::Type type)
3310 {
3311     InlineTypedObject* templateObj = nullptr;
3312     if (!checkInlineSimd(callInfo, native, type, 2, &templateObj))
3313         return InliningStatus_NotInlined;
3314 
3315     // If the type of any of the arguments is neither a SIMD type, an Object
3316     // type, or a Value, then the applyTypes phase will add a fallible box &
3317     // unbox sequence.  This does not matter much as all binary SIMD
3318     // instructions are supposed to produce a TypeError when they're called
3319     // with non SIMD-arguments.
3320     MIRType mirType = SimdTypeDescrToMIRType(type);
3321     T* ins = T::New(alloc(), callInfo.getArg(0), callInfo.getArg(1), op, mirType);
3322     return boxSimd(callInfo, ins, templateObj);
3323 }
3324 
3325 IonBuilder::InliningStatus
inlineCompSimd(CallInfo & callInfo,JSNative native,MSimdBinaryComp::Operation op,SimdTypeDescr::Type compType)3326 IonBuilder::inlineCompSimd(CallInfo& callInfo, JSNative native, MSimdBinaryComp::Operation op,
3327                            SimdTypeDescr::Type compType)
3328 {
3329     InlineTypedObject* templateObj = nullptr;
3330     if (!checkInlineSimd(callInfo, native, SimdTypeDescr::Int32x4, 2, &templateObj))
3331         return InliningStatus_NotInlined;
3332 
3333     // If the type of any of the arguments is neither a SIMD type, an Object
3334     // type, or a Value, then the applyTypes phase will add a fallible box &
3335     // unbox sequence.  This does not matter much as all binary SIMD
3336     // instructions are supposed to produce a TypeError when they're called
3337     // with non SIMD-arguments.
3338     MIRType mirType = SimdTypeDescrToMIRType(compType);
3339     MSimdBinaryComp* ins = MSimdBinaryComp::New(alloc(), callInfo.getArg(0), callInfo.getArg(1), op, mirType);
3340     return boxSimd(callInfo, ins, templateObj);
3341 }
3342 
3343 IonBuilder::InliningStatus
inlineUnarySimd(CallInfo & callInfo,JSNative native,MSimdUnaryArith::Operation op,SimdTypeDescr::Type type)3344 IonBuilder::inlineUnarySimd(CallInfo& callInfo, JSNative native, MSimdUnaryArith::Operation op,
3345                             SimdTypeDescr::Type type)
3346 {
3347     InlineTypedObject* templateObj = nullptr;
3348     if (!checkInlineSimd(callInfo, native, type, 1, &templateObj))
3349         return InliningStatus_NotInlined;
3350 
3351     // See comment in inlineBinarySimd
3352     MIRType mirType = SimdTypeDescrToMIRType(type);
3353     MSimdUnaryArith* ins = MSimdUnaryArith::New(alloc(), callInfo.getArg(0), op, mirType);
3354     return boxSimd(callInfo, ins, templateObj);
3355 }
3356 
3357 IonBuilder::InliningStatus
inlineSimdSplat(CallInfo & callInfo,JSNative native,SimdTypeDescr::Type type)3358 IonBuilder::inlineSimdSplat(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type)
3359 {
3360     InlineTypedObject* templateObj = nullptr;
3361     if (!checkInlineSimd(callInfo, native, type, 1, &templateObj))
3362         return InliningStatus_NotInlined;
3363 
3364     // See comment in inlineBinarySimd
3365     MIRType mirType = SimdTypeDescrToMIRType(type);
3366     MSimdSplatX4* ins = MSimdSplatX4::New(alloc(), callInfo.getArg(0), mirType);
3367     return boxSimd(callInfo, ins, templateObj);
3368 }
3369 
3370 IonBuilder::InliningStatus
inlineSimdExtractLane(CallInfo & callInfo,JSNative native,SimdTypeDescr::Type type)3371 IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type)
3372 {
3373     InlineTypedObject* templateObj = nullptr;
3374     if (!checkInlineSimd(callInfo, native, type, 2, &templateObj))
3375         return InliningStatus_NotInlined;
3376 
3377     MDefinition* arg = callInfo.getArg(1);
3378     if (!arg->isConstantValue() || arg->type() != MIRType_Int32)
3379         return InliningStatus_NotInlined;
3380     int32_t lane = callInfo.getArg(1)->constantValue().toInt32();
3381     if (lane < 0 || lane >= 4)
3382         return InliningStatus_NotInlined;
3383 
3384     // See comment in inlineBinarySimd
3385     MIRType vecType = SimdTypeDescrToMIRType(type);
3386     MIRType laneType = SimdTypeToLaneType(vecType);
3387     MSimdExtractElement* ins = MSimdExtractElement::New(alloc(), callInfo.getArg(0),
3388                                                         vecType, laneType, SimdLane(lane));
3389     current->add(ins);
3390     current->push(ins);
3391     callInfo.setImplicitlyUsedUnchecked();
3392     return InliningStatus_Inlined;
3393 }
3394 
3395 IonBuilder::InliningStatus
inlineSimdReplaceLane(CallInfo & callInfo,JSNative native,SimdTypeDescr::Type type)3396 IonBuilder::inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type)
3397 {
3398     InlineTypedObject* templateObj = nullptr;
3399     if (!checkInlineSimd(callInfo, native, type, 3, &templateObj))
3400         return InliningStatus_NotInlined;
3401 
3402     MDefinition* arg = callInfo.getArg(1);
3403     if (!arg->isConstantValue() || arg->type() != MIRType_Int32)
3404         return InliningStatus_NotInlined;
3405 
3406     int32_t lane = arg->constantValue().toInt32();
3407     if (lane < 0 || lane >= 4)
3408         return InliningStatus_NotInlined;
3409 
3410     // See comment in inlineBinarySimd
3411     MIRType mirType = SimdTypeDescrToMIRType(type);
3412     MSimdInsertElement* ins = MSimdInsertElement::New(alloc(), callInfo.getArg(0),
3413                                                       callInfo.getArg(2), mirType, SimdLane(lane));
3414     return boxSimd(callInfo, ins, templateObj);
3415 }
3416 
3417 IonBuilder::InliningStatus
inlineSimdConvert(CallInfo & callInfo,JSNative native,bool isCast,SimdTypeDescr::Type from,SimdTypeDescr::Type to)3418 IonBuilder::inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast,
3419                               SimdTypeDescr::Type from, SimdTypeDescr::Type to)
3420 {
3421     InlineTypedObject* templateObj = nullptr;
3422     if (!checkInlineSimd(callInfo, native, to, 1, &templateObj))
3423         return InliningStatus_NotInlined;
3424 
3425     // See comment in inlineBinarySimd
3426     MInstruction* ins;
3427     MIRType fromType = SimdTypeDescrToMIRType(from);
3428     MIRType toType = SimdTypeDescrToMIRType(to);
3429     if (isCast)
3430         ins = MSimdReinterpretCast::New(alloc(), callInfo.getArg(0), fromType, toType);
3431     else
3432         ins = MSimdConvert::New(alloc(), callInfo.getArg(0), fromType, toType);
3433 
3434     return boxSimd(callInfo, ins, templateObj);
3435 }
3436 
3437 IonBuilder::InliningStatus
inlineSimdSelect(CallInfo & callInfo,JSNative native,bool isElementWise,SimdTypeDescr::Type type)3438 IonBuilder::inlineSimdSelect(CallInfo& callInfo, JSNative native, bool isElementWise,
3439                              SimdTypeDescr::Type type)
3440 {
3441     InlineTypedObject* templateObj = nullptr;
3442     if (!checkInlineSimd(callInfo, native, type, 3, &templateObj))
3443         return InliningStatus_NotInlined;
3444 
3445     // See comment in inlineBinarySimd
3446     MIRType mirType = SimdTypeDescrToMIRType(type);
3447     MSimdSelect* ins = MSimdSelect::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
3448                                         callInfo.getArg(2), mirType, isElementWise);
3449     return boxSimd(callInfo, ins, templateObj);
3450 }
3451 
3452 IonBuilder::InliningStatus
inlineSimdCheck(CallInfo & callInfo,JSNative native,SimdTypeDescr::Type type)3453 IonBuilder::inlineSimdCheck(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type)
3454 {
3455     InlineTypedObject* templateObj = nullptr;
3456     if (!checkInlineSimd(callInfo, native, type, 1, &templateObj))
3457         return InliningStatus_NotInlined;
3458 
3459     MIRType mirType = SimdTypeDescrToMIRType(type);
3460     MSimdUnbox* unbox = MSimdUnbox::New(alloc(), callInfo.getArg(0), mirType);
3461     current->add(unbox);
3462     current->push(callInfo.getArg(0));
3463 
3464     callInfo.setImplicitlyUsedUnchecked();
3465     return InliningStatus_Inlined;
3466 }
3467 
3468 IonBuilder::InliningStatus
inlineSimdShuffle(CallInfo & callInfo,JSNative native,SimdTypeDescr::Type type,unsigned numVectors,unsigned numLanes)3469 IonBuilder::inlineSimdShuffle(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type,
3470                               unsigned numVectors, unsigned numLanes)
3471 {
3472     InlineTypedObject* templateObj = nullptr;
3473     if (!checkInlineSimd(callInfo, native, type, numVectors + numLanes, &templateObj))
3474         return InliningStatus_NotInlined;
3475 
3476     MIRType mirType = SimdTypeDescrToMIRType(type);
3477     MSimdGeneralShuffle* ins = MSimdGeneralShuffle::New(alloc(), numVectors, numLanes, mirType);
3478 
3479     if (!ins->init(alloc()))
3480         return InliningStatus_Error;
3481 
3482     for (unsigned i = 0; i < numVectors; i++)
3483         ins->setVector(i, callInfo.getArg(i));
3484     for (size_t i = 0; i < numLanes; i++)
3485         ins->setLane(i, callInfo.getArg(numVectors + i));
3486 
3487     return boxSimd(callInfo, ins, templateObj);
3488 }
3489 
3490 // Get the typed array element type corresponding to the lanes in a SIMD vector type.
3491 // This only applies to SIMD types that can be loaded and stored to a typed array.
3492 static Scalar::Type
SimdTypeToArrayElementType(SimdTypeDescr::Type type)3493 SimdTypeToArrayElementType(SimdTypeDescr::Type type)
3494 {
3495     switch (type) {
3496       case SimdTypeDescr::Float32x4: return Scalar::Float32x4;
3497       case SimdTypeDescr::Int32x4:   return Scalar::Int32x4;
3498       case SimdTypeDescr::Int8x16:
3499       case SimdTypeDescr::Int16x8:
3500       case SimdTypeDescr::Float64x2: break;
3501     }
3502     MOZ_CRASH("unexpected simd type");
3503 }
3504 
3505 bool
prepareForSimdLoadStore(CallInfo & callInfo,Scalar::Type simdType,MInstruction ** elements,MDefinition ** index,Scalar::Type * arrayType)3506 IonBuilder::prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType, MInstruction** elements,
3507                                     MDefinition** index, Scalar::Type* arrayType)
3508 {
3509     MDefinition* array = callInfo.getArg(0);
3510     *index = callInfo.getArg(1);
3511 
3512     if (!ElementAccessIsAnyTypedArray(constraints(), array, *index, arrayType))
3513         return false;
3514 
3515     MInstruction* indexAsInt32 = MToInt32::New(alloc(), *index);
3516     current->add(indexAsInt32);
3517     *index = indexAsInt32;
3518 
3519     MDefinition* indexForBoundsCheck = *index;
3520 
3521     // Artificially make sure the index is in bounds by adding the difference
3522     // number of slots needed (e.g. reading from Float32Array we need to make
3523     // sure to be in bounds for 4 slots, so add 3, etc.).
3524     MOZ_ASSERT(Scalar::byteSize(simdType) % Scalar::byteSize(*arrayType) == 0);
3525     int32_t suppSlotsNeeded = Scalar::byteSize(simdType) / Scalar::byteSize(*arrayType) - 1;
3526     if (suppSlotsNeeded) {
3527         MConstant* suppSlots = constant(Int32Value(suppSlotsNeeded));
3528         MAdd* addedIndex = MAdd::New(alloc(), *index, suppSlots);
3529         // We're fine even with the add overflows, as long as the generated code
3530         // for the bounds check uses an unsigned comparison.
3531         addedIndex->setInt32Specialization();
3532         current->add(addedIndex);
3533         indexForBoundsCheck = addedIndex;
3534     }
3535 
3536     MInstruction* length;
3537     addTypedArrayLengthAndData(array, SkipBoundsCheck, index, &length, elements);
3538 
3539     // It can be that the index is out of bounds, while the added index for the
3540     // bounds check is in bounds, so we actually need two bounds checks here.
3541     MInstruction* positiveCheck = MBoundsCheck::New(alloc(), *index, length);
3542     current->add(positiveCheck);
3543 
3544     MInstruction* fullCheck = MBoundsCheck::New(alloc(), indexForBoundsCheck, length);
3545     current->add(fullCheck);
3546     return true;
3547 }
3548 
3549 IonBuilder::InliningStatus
inlineSimdLoad(CallInfo & callInfo,JSNative native,SimdTypeDescr::Type type,unsigned numElems)3550 IonBuilder::inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type,
3551                            unsigned numElems)
3552 {
3553     InlineTypedObject* templateObj = nullptr;
3554     if (!checkInlineSimd(callInfo, native, type, 2, &templateObj))
3555         return InliningStatus_NotInlined;
3556 
3557     Scalar::Type simdType = SimdTypeToArrayElementType(type);
3558 
3559     MDefinition* index = nullptr;
3560     MInstruction* elements = nullptr;
3561     Scalar::Type arrayType;
3562     if (!prepareForSimdLoadStore(callInfo, simdType, &elements, &index, &arrayType))
3563         return InliningStatus_NotInlined;
3564 
3565     MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
3566     load->setResultType(SimdTypeDescrToMIRType(type));
3567     load->setSimdRead(simdType, numElems);
3568 
3569     return boxSimd(callInfo, load, templateObj);
3570 }
3571 
3572 IonBuilder::InliningStatus
inlineSimdStore(CallInfo & callInfo,JSNative native,SimdTypeDescr::Type type,unsigned numElems)3573 IonBuilder::inlineSimdStore(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type,
3574                             unsigned numElems)
3575 {
3576     InlineTypedObject* templateObj = nullptr;
3577     if (!checkInlineSimd(callInfo, native, type, 3, &templateObj))
3578         return InliningStatus_NotInlined;
3579 
3580     Scalar::Type simdType = SimdTypeToArrayElementType(type);
3581 
3582     MDefinition* index = nullptr;
3583     MInstruction* elements = nullptr;
3584     Scalar::Type arrayType;
3585     if (!prepareForSimdLoadStore(callInfo, simdType, &elements, &index, &arrayType))
3586         return InliningStatus_NotInlined;
3587 
3588     MDefinition* valueToWrite = callInfo.getArg(2);
3589     MStoreUnboxedScalar* store = MStoreUnboxedScalar::New(alloc(), elements, index,
3590                                                           valueToWrite, arrayType,
3591                                                           MStoreUnboxedScalar::TruncateInput);
3592     store->setSimdWrite(simdType, numElems);
3593 
3594     current->add(store);
3595     current->push(valueToWrite);
3596 
3597     callInfo.setImplicitlyUsedUnchecked();
3598 
3599     if (!resumeAfter(store))
3600         return InliningStatus_Error;
3601 
3602     return InliningStatus_Inlined;
3603 }
3604 
3605 #define ADD_NATIVE(native) const JSJitInfo JitInfo_##native { \
3606     { nullptr }, { uint16_t(InlinableNative::native) }, 0, JSJitInfo::InlinableNative };
3607     INLINABLE_NATIVE_LIST(ADD_NATIVE)
3608 #undef ADD_NATIVE
3609 
3610 } // namespace jit
3611 } // namespace js
3612