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