Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/Casting.h"
8 :
9 : #include "jsmath.h"
10 : #include "jsobj.h"
11 : #include "jsstr.h"
12 :
13 : #include "builtin/AtomicsObject.h"
14 : #include "builtin/Intl.h"
15 : #include "builtin/SIMD.h"
16 : #include "builtin/TestingFunctions.h"
17 : #include "builtin/TypedObject.h"
18 : #include "jit/BaselineInspector.h"
19 : #include "jit/InlinableNatives.h"
20 : #include "jit/IonBuilder.h"
21 : #include "jit/Lowering.h"
22 : #include "jit/MIR.h"
23 : #include "jit/MIRGraph.h"
24 : #include "vm/ArgumentsObject.h"
25 : #include "vm/ProxyObject.h"
26 : #include "vm/SelfHosting.h"
27 : #include "vm/TypedArrayObject.h"
28 :
29 : #include "jsscriptinlines.h"
30 :
31 : #include "jit/shared/Lowering-shared-inl.h"
32 : #include "vm/NativeObject-inl.h"
33 : #include "vm/StringObject-inl.h"
34 : #include "vm/UnboxedObject-inl.h"
35 :
36 : using mozilla::ArrayLength;
37 : using mozilla::AssertedCast;
38 :
39 : using JS::DoubleNaNValue;
40 : using JS::TrackedOutcome;
41 : using JS::TrackedStrategy;
42 : using JS::TrackedTypeSite;
43 :
44 : namespace js {
45 : namespace jit {
46 :
47 : IonBuilder::InliningResult
48 116 : IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
49 : {
50 116 : MOZ_ASSERT(target->isNative());
51 :
52 116 : if (!optimizationInfo().inlineNative()) {
53 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineDisabledIon);
54 0 : return InliningStatus_NotInlined;
55 : }
56 :
57 116 : if (!target->jitInfo() || target->jitInfo()->type() != JSJitInfo::InlinableNative) {
58 : // Reaching here means we tried to inline a native for which there is no
59 : // Ion specialization.
60 6 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoSpecialization);
61 6 : return InliningStatus_NotInlined;
62 : }
63 :
64 : // Default failure reason is observing an unsupported type.
65 110 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType);
66 :
67 110 : if (shouldAbortOnPreliminaryGroups(callInfo.thisArg()))
68 0 : return InliningStatus_NotInlined;
69 304 : for (size_t i = 0; i < callInfo.argc(); i++) {
70 195 : if (shouldAbortOnPreliminaryGroups(callInfo.getArg(i)))
71 1 : return InliningStatus_NotInlined;
72 : }
73 :
74 109 : switch (InlinableNative inlNative = target->jitInfo()->inlinableNative) {
75 : // Array natives.
76 : case InlinableNative::Array:
77 0 : return inlineArray(callInfo);
78 : case InlinableNative::ArrayIsArray:
79 0 : return inlineArrayIsArray(callInfo);
80 : case InlinableNative::ArrayJoin:
81 1 : return inlineArrayJoin(callInfo);
82 : case InlinableNative::ArrayPop:
83 0 : return inlineArrayPopShift(callInfo, MArrayPopShift::Pop);
84 : case InlinableNative::ArrayShift:
85 0 : return inlineArrayPopShift(callInfo, MArrayPopShift::Shift);
86 : case InlinableNative::ArrayPush:
87 4 : return inlineArrayPush(callInfo);
88 : case InlinableNative::ArraySlice:
89 0 : return inlineArraySlice(callInfo);
90 :
91 : // Array intrinsics.
92 : case InlinableNative::IntrinsicNewArrayIterator:
93 4 : return inlineNewIterator(callInfo, MNewIterator::ArrayIterator);
94 :
95 : // Atomic natives.
96 : case InlinableNative::AtomicsCompareExchange:
97 0 : return inlineAtomicsCompareExchange(callInfo);
98 : case InlinableNative::AtomicsExchange:
99 0 : return inlineAtomicsExchange(callInfo);
100 : case InlinableNative::AtomicsLoad:
101 0 : return inlineAtomicsLoad(callInfo);
102 : case InlinableNative::AtomicsStore:
103 0 : return inlineAtomicsStore(callInfo);
104 : case InlinableNative::AtomicsAdd:
105 : case InlinableNative::AtomicsSub:
106 : case InlinableNative::AtomicsAnd:
107 : case InlinableNative::AtomicsOr:
108 : case InlinableNative::AtomicsXor:
109 0 : return inlineAtomicsBinop(callInfo, inlNative);
110 : case InlinableNative::AtomicsIsLockFree:
111 0 : return inlineAtomicsIsLockFree(callInfo);
112 :
113 : // Boolean natives.
114 : case InlinableNative::Boolean:
115 0 : return inlineBoolean(callInfo);
116 :
117 : // Intl natives.
118 : case InlinableNative::IntlIsCollator:
119 0 : return inlineHasClass(callInfo, &CollatorObject::class_);
120 : case InlinableNative::IntlIsDateTimeFormat:
121 0 : return inlineHasClass(callInfo, &DateTimeFormatObject::class_);
122 : case InlinableNative::IntlIsNumberFormat:
123 0 : return inlineHasClass(callInfo, &NumberFormatObject::class_);
124 : case InlinableNative::IntlIsPluralRules:
125 0 : return inlineHasClass(callInfo, &PluralRulesObject::class_);
126 :
127 : // Math natives.
128 : case InlinableNative::MathAbs:
129 0 : return inlineMathAbs(callInfo);
130 : case InlinableNative::MathFloor:
131 0 : return inlineMathFloor(callInfo);
132 : case InlinableNative::MathCeil:
133 0 : return inlineMathCeil(callInfo);
134 : case InlinableNative::MathRound:
135 0 : return inlineMathRound(callInfo);
136 : case InlinableNative::MathClz32:
137 0 : return inlineMathClz32(callInfo);
138 : case InlinableNative::MathSqrt:
139 0 : return inlineMathSqrt(callInfo);
140 : case InlinableNative::MathATan2:
141 0 : return inlineMathAtan2(callInfo);
142 : case InlinableNative::MathHypot:
143 0 : return inlineMathHypot(callInfo);
144 : case InlinableNative::MathMax:
145 8 : return inlineMathMinMax(callInfo, true /* max */);
146 : case InlinableNative::MathMin:
147 8 : return inlineMathMinMax(callInfo, false /* max */);
148 : case InlinableNative::MathPow:
149 0 : return inlineMathPow(callInfo);
150 : case InlinableNative::MathRandom:
151 0 : return inlineMathRandom(callInfo);
152 : case InlinableNative::MathImul:
153 0 : return inlineMathImul(callInfo);
154 : case InlinableNative::MathFRound:
155 0 : return inlineMathFRound(callInfo);
156 : case InlinableNative::MathSin:
157 0 : return inlineMathFunction(callInfo, MMathFunction::Sin);
158 : case InlinableNative::MathTan:
159 0 : return inlineMathFunction(callInfo, MMathFunction::Tan);
160 : case InlinableNative::MathCos:
161 0 : return inlineMathFunction(callInfo, MMathFunction::Cos);
162 : case InlinableNative::MathExp:
163 0 : return inlineMathFunction(callInfo, MMathFunction::Exp);
164 : case InlinableNative::MathLog:
165 0 : return inlineMathFunction(callInfo, MMathFunction::Log);
166 : case InlinableNative::MathASin:
167 0 : return inlineMathFunction(callInfo, MMathFunction::ASin);
168 : case InlinableNative::MathATan:
169 0 : return inlineMathFunction(callInfo, MMathFunction::ATan);
170 : case InlinableNative::MathACos:
171 0 : return inlineMathFunction(callInfo, MMathFunction::ACos);
172 : case InlinableNative::MathLog10:
173 0 : return inlineMathFunction(callInfo, MMathFunction::Log10);
174 : case InlinableNative::MathLog2:
175 0 : return inlineMathFunction(callInfo, MMathFunction::Log2);
176 : case InlinableNative::MathLog1P:
177 0 : return inlineMathFunction(callInfo, MMathFunction::Log1P);
178 : case InlinableNative::MathExpM1:
179 0 : return inlineMathFunction(callInfo, MMathFunction::ExpM1);
180 : case InlinableNative::MathCosH:
181 0 : return inlineMathFunction(callInfo, MMathFunction::CosH);
182 : case InlinableNative::MathSinH:
183 0 : return inlineMathFunction(callInfo, MMathFunction::SinH);
184 : case InlinableNative::MathTanH:
185 0 : return inlineMathFunction(callInfo, MMathFunction::TanH);
186 : case InlinableNative::MathACosH:
187 0 : return inlineMathFunction(callInfo, MMathFunction::ACosH);
188 : case InlinableNative::MathASinH:
189 0 : return inlineMathFunction(callInfo, MMathFunction::ASinH);
190 : case InlinableNative::MathATanH:
191 0 : return inlineMathFunction(callInfo, MMathFunction::ATanH);
192 : case InlinableNative::MathSign:
193 0 : return inlineMathFunction(callInfo, MMathFunction::Sign);
194 : case InlinableNative::MathTrunc:
195 0 : return inlineMathFunction(callInfo, MMathFunction::Trunc);
196 : case InlinableNative::MathCbrt:
197 0 : return inlineMathFunction(callInfo, MMathFunction::Cbrt);
198 :
199 : // RegExp natives.
200 : case InlinableNative::RegExpMatcher:
201 0 : return inlineRegExpMatcher(callInfo);
202 : case InlinableNative::RegExpSearcher:
203 0 : return inlineRegExpSearcher(callInfo);
204 : case InlinableNative::RegExpTester:
205 0 : return inlineRegExpTester(callInfo);
206 : case InlinableNative::IsRegExpObject:
207 0 : return inlineIsRegExpObject(callInfo);
208 : case InlinableNative::RegExpPrototypeOptimizable:
209 0 : return inlineRegExpPrototypeOptimizable(callInfo);
210 : case InlinableNative::RegExpInstanceOptimizable:
211 0 : return inlineRegExpInstanceOptimizable(callInfo);
212 : case InlinableNative::GetFirstDollarIndex:
213 0 : return inlineGetFirstDollarIndex(callInfo);
214 :
215 : // String natives.
216 : case InlinableNative::String:
217 0 : return inlineStringObject(callInfo);
218 : case InlinableNative::StringCharCodeAt:
219 3 : return inlineStrCharCodeAt(callInfo);
220 : case InlinableNative::StringFromCharCode:
221 0 : return inlineStrFromCharCode(callInfo);
222 : case InlinableNative::StringFromCodePoint:
223 0 : return inlineStrFromCodePoint(callInfo);
224 : case InlinableNative::StringCharAt:
225 0 : return inlineStrCharAt(callInfo);
226 :
227 : // String intrinsics.
228 : case InlinableNative::IntrinsicStringReplaceString:
229 0 : return inlineStringReplaceString(callInfo);
230 : case InlinableNative::IntrinsicStringSplitString:
231 0 : return inlineStringSplitString(callInfo);
232 : case InlinableNative::IntrinsicNewStringIterator:
233 0 : return inlineNewIterator(callInfo, MNewIterator::StringIterator);
234 :
235 : // Object natives.
236 : case InlinableNative::ObjectCreate:
237 0 : return inlineObjectCreate(callInfo);
238 :
239 : // SIMD natives.
240 : case InlinableNative::SimdInt32x4:
241 0 : return inlineSimd(callInfo, target, SimdType::Int32x4);
242 : case InlinableNative::SimdUint32x4:
243 0 : return inlineSimd(callInfo, target, SimdType::Uint32x4);
244 : case InlinableNative::SimdInt16x8:
245 0 : return inlineSimd(callInfo, target, SimdType::Int16x8);
246 : case InlinableNative::SimdUint16x8:
247 0 : return inlineSimd(callInfo, target, SimdType::Uint16x8);
248 : case InlinableNative::SimdInt8x16:
249 0 : return inlineSimd(callInfo, target, SimdType::Int8x16);
250 : case InlinableNative::SimdUint8x16:
251 0 : return inlineSimd(callInfo, target, SimdType::Uint8x16);
252 : case InlinableNative::SimdFloat32x4:
253 0 : return inlineSimd(callInfo, target, SimdType::Float32x4);
254 : case InlinableNative::SimdBool32x4:
255 0 : return inlineSimd(callInfo, target, SimdType::Bool32x4);
256 : case InlinableNative::SimdBool16x8:
257 0 : return inlineSimd(callInfo, target, SimdType::Bool16x8);
258 : case InlinableNative::SimdBool8x16:
259 0 : return inlineSimd(callInfo, target, SimdType::Bool8x16);
260 :
261 : // Testing functions.
262 : case InlinableNative::TestBailout:
263 0 : return inlineBailout(callInfo);
264 : case InlinableNative::TestAssertFloat32:
265 0 : return inlineAssertFloat32(callInfo);
266 : case InlinableNative::TestAssertRecoveredOnBailout:
267 0 : return inlineAssertRecoveredOnBailout(callInfo);
268 :
269 : // Slot intrinsics.
270 : case InlinableNative::IntrinsicUnsafeSetReservedSlot:
271 26 : return inlineUnsafeSetReservedSlot(callInfo);
272 : case InlinableNative::IntrinsicUnsafeGetReservedSlot:
273 14 : return inlineUnsafeGetReservedSlot(callInfo, MIRType::Value);
274 : case InlinableNative::IntrinsicUnsafeGetObjectFromReservedSlot:
275 0 : return inlineUnsafeGetReservedSlot(callInfo, MIRType::Object);
276 : case InlinableNative::IntrinsicUnsafeGetInt32FromReservedSlot:
277 7 : return inlineUnsafeGetReservedSlot(callInfo, MIRType::Int32);
278 : case InlinableNative::IntrinsicUnsafeGetStringFromReservedSlot:
279 0 : return inlineUnsafeGetReservedSlot(callInfo, MIRType::String);
280 : case InlinableNative::IntrinsicUnsafeGetBooleanFromReservedSlot:
281 0 : return inlineUnsafeGetReservedSlot(callInfo, MIRType::Boolean);
282 :
283 : // Utility intrinsics.
284 : case InlinableNative::IntrinsicIsCallable:
285 1 : return inlineIsCallable(callInfo);
286 : case InlinableNative::IntrinsicIsConstructor:
287 0 : return inlineIsConstructor(callInfo);
288 : case InlinableNative::IntrinsicToObject:
289 4 : return inlineToObject(callInfo);
290 : case InlinableNative::IntrinsicIsObject:
291 7 : return inlineIsObject(callInfo);
292 : case InlinableNative::IntrinsicIsWrappedArrayConstructor:
293 0 : return inlineIsWrappedArrayConstructor(callInfo);
294 : case InlinableNative::IntrinsicToInteger:
295 8 : return inlineToInteger(callInfo);
296 : case InlinableNative::IntrinsicToString:
297 0 : return inlineToString(callInfo);
298 : case InlinableNative::IntrinsicIsConstructing:
299 0 : return inlineIsConstructing(callInfo);
300 : case InlinableNative::IntrinsicSubstringKernel:
301 0 : return inlineSubstringKernel(callInfo);
302 : case InlinableNative::IntrinsicIsArrayIterator:
303 7 : return inlineHasClass(callInfo, &ArrayIteratorObject::class_);
304 : case InlinableNative::IntrinsicIsMapIterator:
305 0 : return inlineHasClass(callInfo, &MapIteratorObject::class_);
306 : case InlinableNative::IntrinsicIsSetIterator:
307 0 : return inlineHasClass(callInfo, &SetIteratorObject::class_);
308 : case InlinableNative::IntrinsicIsStringIterator:
309 0 : return inlineHasClass(callInfo, &StringIteratorObject::class_);
310 : case InlinableNative::IntrinsicObjectHasPrototype:
311 0 : return inlineObjectHasPrototype(callInfo);
312 : case InlinableNative::IntrinsicFinishBoundFunctionInit:
313 0 : return inlineFinishBoundFunctionInit(callInfo);
314 :
315 : // Map intrinsics.
316 : case InlinableNative::IntrinsicGetNextMapEntryForIterator:
317 0 : return inlineGetNextEntryForIterator(callInfo, MGetNextEntryForIterator::Map);
318 :
319 : // Set intrinsics.
320 : case InlinableNative::IntrinsicGetNextSetEntryForIterator:
321 0 : return inlineGetNextEntryForIterator(callInfo, MGetNextEntryForIterator::Set);
322 :
323 : // ArrayBuffer intrinsics.
324 : case InlinableNative::IntrinsicArrayBufferByteLength:
325 0 : return inlineArrayBufferByteLength(callInfo);
326 : case InlinableNative::IntrinsicPossiblyWrappedArrayBufferByteLength:
327 0 : return inlinePossiblyWrappedArrayBufferByteLength(callInfo);
328 :
329 : // TypedArray intrinsics.
330 : case InlinableNative::TypedArrayConstructor:
331 0 : return inlineTypedArray(callInfo, target->native());
332 : case InlinableNative::IntrinsicIsTypedArray:
333 0 : return inlineIsTypedArray(callInfo);
334 : case InlinableNative::IntrinsicIsPossiblyWrappedTypedArray:
335 7 : return inlineIsPossiblyWrappedTypedArray(callInfo);
336 : case InlinableNative::IntrinsicPossiblyWrappedTypedArrayLength:
337 0 : return inlinePossiblyWrappedTypedArrayLength(callInfo);
338 : case InlinableNative::IntrinsicTypedArrayLength:
339 0 : return inlineTypedArrayLength(callInfo);
340 : case InlinableNative::IntrinsicSetDisjointTypedElements:
341 0 : return inlineSetDisjointTypedElements(callInfo);
342 :
343 : // TypedObject intrinsics.
344 : case InlinableNative::IntrinsicObjectIsTypedObject:
345 : return inlineHasClass(callInfo,
346 : &OutlineTransparentTypedObject::class_,
347 : &OutlineOpaqueTypedObject::class_,
348 : &InlineTransparentTypedObject::class_,
349 0 : &InlineOpaqueTypedObject::class_);
350 : case InlinableNative::IntrinsicObjectIsTransparentTypedObject:
351 : return inlineHasClass(callInfo,
352 : &OutlineTransparentTypedObject::class_,
353 0 : &InlineTransparentTypedObject::class_);
354 : case InlinableNative::IntrinsicObjectIsOpaqueTypedObject:
355 : return inlineHasClass(callInfo,
356 : &OutlineOpaqueTypedObject::class_,
357 0 : &InlineOpaqueTypedObject::class_);
358 : case InlinableNative::IntrinsicObjectIsTypeDescr:
359 0 : return inlineObjectIsTypeDescr(callInfo);
360 : case InlinableNative::IntrinsicTypeDescrIsSimpleType:
361 : return inlineHasClass(callInfo,
362 0 : &ScalarTypeDescr::class_, &ReferenceTypeDescr::class_);
363 : case InlinableNative::IntrinsicTypeDescrIsArrayType:
364 0 : return inlineHasClass(callInfo, &ArrayTypeDescr::class_);
365 : case InlinableNative::IntrinsicSetTypedObjectOffset:
366 0 : return inlineSetTypedObjectOffset(callInfo);
367 : case InlinableNative::Limit:
368 0 : break;
369 : }
370 :
371 0 : MOZ_CRASH("Shouldn't get here");
372 : }
373 :
374 : IonBuilder::InliningResult
375 0 : IonBuilder::inlineNativeGetter(CallInfo& callInfo, JSFunction* target)
376 : {
377 0 : MOZ_ASSERT(target->isNative());
378 0 : JSNative native = target->native();
379 :
380 0 : if (!optimizationInfo().inlineNative())
381 0 : return InliningStatus_NotInlined;
382 :
383 0 : MDefinition* thisArg = callInfo.thisArg();
384 0 : TemporaryTypeSet* thisTypes = thisArg->resultTypeSet();
385 0 : MOZ_ASSERT(callInfo.argc() == 0);
386 :
387 0 : if (!thisTypes)
388 0 : return InliningStatus_NotInlined;
389 :
390 : // Try to optimize typed array lengths.
391 0 : if (TypedArrayObject::isOriginalLengthGetter(native)) {
392 0 : Scalar::Type type = thisTypes->getTypedArrayType(constraints());
393 0 : if (type == Scalar::MaxTypedArrayViewType)
394 0 : return InliningStatus_NotInlined;
395 :
396 0 : MInstruction* length = addTypedArrayLength(thisArg);
397 0 : current->push(length);
398 0 : return InliningStatus_Inlined;
399 : }
400 :
401 : // Try to optimize RegExp getters.
402 0 : RegExpFlag mask = NoFlags;
403 0 : if (RegExpObject::isOriginalFlagGetter(native, &mask)) {
404 0 : const Class* clasp = thisTypes->getKnownClass(constraints());
405 0 : if (clasp != &RegExpObject::class_)
406 0 : return InliningStatus_NotInlined;
407 :
408 0 : MLoadFixedSlot* flags = MLoadFixedSlot::New(alloc(), thisArg, RegExpObject::flagsSlot());
409 0 : current->add(flags);
410 0 : flags->setResultType(MIRType::Int32);
411 0 : MConstant* maskConst = MConstant::New(alloc(), Int32Value(mask));
412 0 : current->add(maskConst);
413 0 : MBitAnd* maskedFlag = MBitAnd::New(alloc(), flags, maskConst);
414 0 : maskedFlag->setInt32Specialization();
415 0 : current->add(maskedFlag);
416 :
417 0 : MDefinition* result = convertToBoolean(maskedFlag);
418 0 : current->push(result);
419 0 : return InliningStatus_Inlined;
420 : }
421 :
422 0 : return InliningStatus_NotInlined;
423 : }
424 :
425 : IonBuilder::InliningResult
426 0 : IonBuilder::inlineNonFunctionCall(CallInfo& callInfo, JSObject* target)
427 : {
428 : // Inline a call to a non-function object, invoking the object's call or
429 : // construct hook.
430 :
431 0 : if (callInfo.constructing() && target->constructHook() == TypedObject::construct)
432 0 : return inlineConstructTypedObject(callInfo, &target->as<TypeDescr>());
433 :
434 0 : if (!callInfo.constructing() && target->callHook() == SimdTypeDescr::call)
435 0 : return inlineConstructSimdObject(callInfo, &target->as<SimdTypeDescr>());
436 :
437 0 : return InliningStatus_NotInlined;
438 : }
439 :
440 : TemporaryTypeSet*
441 117 : IonBuilder::getInlineReturnTypeSet()
442 : {
443 117 : return bytecodeTypes(pc);
444 : }
445 :
446 : MIRType
447 89 : IonBuilder::getInlineReturnType()
448 : {
449 89 : TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
450 89 : return returnTypes->getKnownMIRType();
451 : }
452 :
453 : IonBuilder::InliningResult
454 0 : IonBuilder::inlineMathFunction(CallInfo& callInfo, MMathFunction::Function function)
455 : {
456 0 : if (callInfo.constructing())
457 0 : return InliningStatus_NotInlined;
458 :
459 0 : if (callInfo.argc() != 1)
460 0 : return InliningStatus_NotInlined;
461 :
462 0 : if (getInlineReturnType() != MIRType::Double)
463 0 : return InliningStatus_NotInlined;
464 0 : if (!IsNumberType(callInfo.getArg(0)->type()))
465 0 : return InliningStatus_NotInlined;
466 :
467 0 : const MathCache* cache = TlsContext.get()->caches().maybeGetMathCache();
468 :
469 0 : callInfo.fun()->setImplicitlyUsedUnchecked();
470 0 : callInfo.thisArg()->setImplicitlyUsedUnchecked();
471 :
472 0 : MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), function, cache);
473 0 : current->add(ins);
474 0 : current->push(ins);
475 0 : return InliningStatus_Inlined;
476 : }
477 :
478 : IonBuilder::InliningResult
479 0 : IonBuilder::inlineArray(CallInfo& callInfo)
480 : {
481 0 : uint32_t initLength = 0;
482 :
483 0 : JSObject* templateObject = inspector->getTemplateObjectForNative(pc, ArrayConstructor);
484 : // This is shared by ArrayConstructor and array_construct (std_Array).
485 0 : if (!templateObject)
486 0 : templateObject = inspector->getTemplateObjectForNative(pc, array_construct);
487 :
488 0 : if (!templateObject) {
489 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoTemplateObj);
490 0 : return InliningStatus_NotInlined;
491 : }
492 :
493 0 : if (templateObject->is<UnboxedArrayObject>()) {
494 0 : if (templateObject->group()->unboxedLayout().nativeGroup())
495 0 : return InliningStatus_NotInlined;
496 : }
497 :
498 : // Multiple arguments imply array initialization, not just construction.
499 0 : if (callInfo.argc() >= 2) {
500 0 : initLength = callInfo.argc();
501 :
502 0 : TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(templateObject);
503 0 : if (!key->unknownProperties()) {
504 0 : HeapTypeSetKey elemTypes = key->property(JSID_VOID);
505 :
506 0 : for (uint32_t i = 0; i < initLength; i++) {
507 0 : MDefinition* value = callInfo.getArg(i);
508 0 : if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
509 0 : elemTypes.freeze(constraints());
510 0 : return InliningStatus_NotInlined;
511 : }
512 : }
513 : }
514 : }
515 :
516 : // A single integer argument denotes initial length.
517 0 : if (callInfo.argc() == 1) {
518 0 : MDefinition* arg = callInfo.getArg(0);
519 0 : if (arg->type() != MIRType::Int32)
520 0 : return InliningStatus_NotInlined;
521 :
522 0 : if (!arg->isConstant()) {
523 0 : callInfo.setImplicitlyUsedUnchecked();
524 : MNewArrayDynamicLength* ins =
525 0 : MNewArrayDynamicLength::New(alloc(), constraints(), templateObject,
526 0 : templateObject->group()->initialHeap(constraints()),
527 0 : arg);
528 0 : current->add(ins);
529 0 : current->push(ins);
530 :
531 : // This may throw, so we need a resume point.
532 0 : MOZ_TRY(resumeAfter(ins));
533 :
534 0 : return InliningStatus_Inlined;
535 : }
536 :
537 : // The next several checks all may fail due to range conditions.
538 0 : trackOptimizationOutcome(TrackedOutcome::ArrayRange);
539 :
540 : // Negative lengths generate a RangeError, unhandled by the inline path.
541 0 : initLength = arg->toConstant()->toInt32();
542 0 : if (initLength > NativeObject::MAX_DENSE_ELEMENTS_COUNT)
543 0 : return InliningStatus_NotInlined;
544 0 : MOZ_ASSERT(initLength <= INT32_MAX);
545 :
546 : // Make sure initLength matches the template object's length. This is
547 : // not guaranteed to be the case, for instance if we're inlining the
548 : // MConstant may come from an outer script.
549 0 : if (initLength != GetAnyBoxedOrUnboxedArrayLength(templateObject))
550 0 : return InliningStatus_NotInlined;
551 :
552 : // Don't inline large allocations.
553 0 : if (initLength > ArrayObject::EagerAllocationMaxLength)
554 0 : return InliningStatus_NotInlined;
555 : }
556 :
557 0 : callInfo.setImplicitlyUsedUnchecked();
558 :
559 0 : MOZ_TRY(jsop_newarray(templateObject, initLength));
560 :
561 0 : MDefinition* array = current->peek(-1);
562 0 : if (callInfo.argc() >= 2) {
563 0 : JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
564 0 : for (uint32_t i = 0; i < initLength; i++) {
565 0 : if (!alloc().ensureBallast())
566 0 : return abort(AbortReason::Alloc);
567 0 : MDefinition* value = callInfo.getArg(i);
568 0 : MOZ_TRY(initializeArrayElement(array, i, value, unboxedType,
569 : /* addResumePoint = */ false));
570 : }
571 :
572 0 : MInstruction* setLength = setInitializedLength(array, unboxedType, initLength);
573 0 : MOZ_TRY(resumeAfter(setLength));
574 : }
575 :
576 0 : return InliningStatus_Inlined;
577 : }
578 :
579 : static bool
580 0 : IsArrayClass(const Class* clasp)
581 : {
582 0 : return clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_;
583 : }
584 :
585 : IonBuilder::InliningResult
586 0 : IonBuilder::inlineArrayIsArray(CallInfo& callInfo)
587 : {
588 0 : if (callInfo.constructing() || callInfo.argc() != 1) {
589 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
590 0 : return InliningStatus_NotInlined;
591 : }
592 :
593 0 : if (getInlineReturnType() != MIRType::Boolean)
594 0 : return InliningStatus_NotInlined;
595 :
596 0 : MDefinition* arg = callInfo.getArg(0);
597 :
598 0 : if (!arg->mightBeType(MIRType::Object)) {
599 0 : pushConstant(BooleanValue(false));
600 0 : callInfo.setImplicitlyUsedUnchecked();
601 0 : return InliningStatus_Inlined;
602 : }
603 :
604 : using ForAllResult = TemporaryTypeSet::ForAllResult;
605 :
606 0 : TemporaryTypeSet* types = arg->resultTypeSet();
607 :
608 : // Fast path for non-proxy objects.
609 0 : if (arg->type() == MIRType::Object &&
610 0 : types &&
611 0 : types->forAllClasses(constraints(), IsProxyClass) == ForAllResult::ALL_FALSE)
612 : {
613 : // Definitely not a proxy. Now check for the array classes.
614 0 : ForAllResult result = types->forAllClasses(constraints(), IsArrayClass);
615 :
616 0 : if (result == ForAllResult::ALL_FALSE || result == ForAllResult::ALL_TRUE) {
617 : // Definitely an array or definitely not an array, so we can
618 : // constant fold.
619 0 : pushConstant(BooleanValue(result == ForAllResult::ALL_TRUE));
620 0 : callInfo.setImplicitlyUsedUnchecked();
621 0 : return InliningStatus_Inlined;
622 : }
623 :
624 : // We have some array classes and some non-array classes, so we have to
625 : // check at runtime.
626 0 : MOZ_ASSERT(result == ForAllResult::MIXED);
627 :
628 0 : MHasClass* hasClass1 = MHasClass::New(alloc(), arg, &ArrayObject::class_);
629 0 : current->add(hasClass1);
630 :
631 0 : MHasClass* hasClass2 = MHasClass::New(alloc(), arg, &UnboxedArrayObject::class_);
632 0 : current->add(hasClass2);
633 :
634 0 : MBitOr* either = MBitOr::New(alloc(), hasClass1, hasClass2);
635 0 : either->infer(inspector, pc);
636 0 : current->add(either);
637 :
638 0 : MDefinition* def = convertToBoolean(either);
639 0 : current->push(def);
640 :
641 0 : callInfo.setImplicitlyUsedUnchecked();
642 0 : return InliningStatus_Inlined;
643 : }
644 :
645 : // The value might be a primitive or a proxy. MIsArray handles these cases.
646 0 : MIsArray* isArray = MIsArray::New(alloc(), arg);
647 0 : current->add(isArray);
648 0 : current->push(isArray);
649 :
650 0 : MOZ_TRY(resumeAfter(isArray));
651 :
652 0 : callInfo.setImplicitlyUsedUnchecked();
653 0 : return InliningStatus_Inlined;
654 : }
655 :
656 : IonBuilder::InliningResult
657 0 : IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
658 : {
659 0 : if (callInfo.constructing()) {
660 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
661 0 : return InliningStatus_NotInlined;
662 : }
663 :
664 0 : MIRType returnType = getInlineReturnType();
665 0 : if (returnType == MIRType::Undefined || returnType == MIRType::Null)
666 0 : return InliningStatus_NotInlined;
667 0 : if (callInfo.thisArg()->type() != MIRType::Object)
668 0 : return InliningStatus_NotInlined;
669 :
670 : // Pop and shift are only handled for dense arrays that have never been
671 : // used in an iterator: popping elements does not account for suppressing
672 : // deleted properties in active iterators.
673 : ObjectGroupFlags unhandledFlags =
674 : OBJECT_FLAG_SPARSE_INDEXES |
675 : OBJECT_FLAG_LENGTH_OVERFLOW |
676 0 : OBJECT_FLAG_ITERATED;
677 :
678 0 : MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
679 0 : TemporaryTypeSet* thisTypes = obj->resultTypeSet();
680 0 : if (!thisTypes)
681 0 : return InliningStatus_NotInlined;
682 0 : const Class* clasp = thisTypes->getKnownClass(constraints());
683 0 : if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
684 0 : return InliningStatus_NotInlined;
685 0 : if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
686 0 : trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
687 0 : return InliningStatus_NotInlined;
688 : }
689 :
690 : bool hasIndexedProperty;
691 0 : MOZ_TRY_VAR(hasIndexedProperty, ArrayPrototypeHasIndexedProperty(this, script()));
692 0 : if (hasIndexedProperty) {
693 0 : trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
694 0 : return InliningStatus_NotInlined;
695 : }
696 :
697 0 : JSValueType unboxedType = JSVAL_TYPE_MAGIC;
698 0 : if (clasp == &UnboxedArrayObject::class_) {
699 0 : unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
700 0 : if (unboxedType == JSVAL_TYPE_MAGIC)
701 0 : return InliningStatus_NotInlined;
702 : }
703 :
704 0 : callInfo.setImplicitlyUsedUnchecked();
705 :
706 0 : if (clasp == &ArrayObject::class_)
707 0 : obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
708 :
709 0 : TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
710 0 : bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED);
711 0 : bool maybeUndefined = returnTypes->hasType(TypeSet::UndefinedType());
712 :
713 0 : BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
714 0 : obj, nullptr, returnTypes);
715 0 : if (barrier != BarrierKind::NoBarrier)
716 0 : returnType = MIRType::Value;
717 :
718 0 : MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode,
719 0 : unboxedType, needsHoleCheck, maybeUndefined);
720 0 : current->add(ins);
721 0 : current->push(ins);
722 0 : ins->setResultType(returnType);
723 :
724 0 : MOZ_TRY(resumeAfter(ins));
725 0 : MOZ_TRY(pushTypeBarrier(ins, returnTypes, barrier));
726 0 : return InliningStatus_Inlined;
727 : }
728 :
729 : IonBuilder::InliningResult
730 1 : IonBuilder::inlineArrayJoin(CallInfo& callInfo)
731 : {
732 1 : if (callInfo.argc() != 1 || callInfo.constructing()) {
733 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
734 0 : return InliningStatus_NotInlined;
735 : }
736 :
737 1 : if (getInlineReturnType() != MIRType::String)
738 0 : return InliningStatus_NotInlined;
739 1 : if (callInfo.thisArg()->type() != MIRType::Object)
740 0 : return InliningStatus_NotInlined;
741 1 : if (callInfo.getArg(0)->type() != MIRType::String)
742 0 : return InliningStatus_NotInlined;
743 :
744 1 : callInfo.setImplicitlyUsedUnchecked();
745 :
746 1 : MArrayJoin* ins = MArrayJoin::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
747 :
748 1 : current->add(ins);
749 1 : current->push(ins);
750 :
751 1 : MOZ_TRY(resumeAfter(ins));
752 1 : return InliningStatus_Inlined;
753 : }
754 :
755 : IonBuilder::InliningResult
756 4 : IonBuilder::inlineArrayPush(CallInfo& callInfo)
757 : {
758 4 : if (callInfo.argc() != 1 || callInfo.constructing()) {
759 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
760 0 : return InliningStatus_NotInlined;
761 : }
762 :
763 4 : MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
764 4 : MDefinition* value = callInfo.getArg(0);
765 4 : if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
766 : &obj, nullptr, &value, /* canModify = */ false))
767 : {
768 2 : trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
769 2 : return InliningStatus_NotInlined;
770 : }
771 :
772 2 : if (getInlineReturnType() != MIRType::Int32)
773 0 : return InliningStatus_NotInlined;
774 2 : if (obj->type() != MIRType::Object)
775 0 : return InliningStatus_NotInlined;
776 :
777 2 : TemporaryTypeSet* thisTypes = obj->resultTypeSet();
778 2 : if (!thisTypes)
779 0 : return InliningStatus_NotInlined;
780 2 : const Class* clasp = thisTypes->getKnownClass(constraints());
781 2 : if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
782 0 : return InliningStatus_NotInlined;
783 2 : if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
784 : OBJECT_FLAG_LENGTH_OVERFLOW))
785 : {
786 0 : trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
787 0 : return InliningStatus_NotInlined;
788 : }
789 :
790 : bool hasIndexedProperty;
791 2 : MOZ_TRY_VAR(hasIndexedProperty, ArrayPrototypeHasIndexedProperty(this, script()));
792 2 : if (hasIndexedProperty) {
793 0 : trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
794 0 : return InliningStatus_NotInlined;
795 : }
796 :
797 : TemporaryTypeSet::DoubleConversion conversion =
798 2 : thisTypes->convertDoubleElements(constraints());
799 2 : if (conversion == TemporaryTypeSet::AmbiguousDoubleConversion) {
800 0 : trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion);
801 0 : return InliningStatus_NotInlined;
802 : }
803 :
804 2 : JSValueType unboxedType = JSVAL_TYPE_MAGIC;
805 2 : if (clasp == &UnboxedArrayObject::class_) {
806 0 : unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
807 0 : if (unboxedType == JSVAL_TYPE_MAGIC)
808 0 : return InliningStatus_NotInlined;
809 : }
810 :
811 2 : callInfo.setImplicitlyUsedUnchecked();
812 :
813 2 : if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles ||
814 : conversion == TemporaryTypeSet::MaybeConvertToDoubles)
815 : {
816 0 : MInstruction* valueDouble = MToDouble::New(alloc(), value);
817 0 : current->add(valueDouble);
818 0 : value = valueDouble;
819 : }
820 :
821 2 : if (unboxedType == JSVAL_TYPE_MAGIC)
822 2 : obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
823 :
824 2 : if (NeedsPostBarrier(value))
825 0 : current->add(MPostWriteBarrier::New(alloc(), obj, value));
826 :
827 2 : MArrayPush* ins = MArrayPush::New(alloc(), obj, value, unboxedType);
828 2 : current->add(ins);
829 2 : current->push(ins);
830 :
831 2 : MOZ_TRY(resumeAfter(ins));
832 2 : return InliningStatus_Inlined;
833 : }
834 :
835 : IonBuilder::InliningResult
836 0 : IonBuilder::inlineArraySlice(CallInfo& callInfo)
837 : {
838 0 : if (callInfo.constructing()) {
839 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
840 0 : return InliningStatus_NotInlined;
841 : }
842 :
843 0 : MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
844 :
845 : // Ensure |this| and result are objects.
846 0 : if (getInlineReturnType() != MIRType::Object)
847 0 : return InliningStatus_NotInlined;
848 0 : if (obj->type() != MIRType::Object)
849 0 : return InliningStatus_NotInlined;
850 :
851 : // Arguments for the sliced region must be integers.
852 0 : if (callInfo.argc() > 0) {
853 0 : if (callInfo.getArg(0)->type() != MIRType::Int32)
854 0 : return InliningStatus_NotInlined;
855 0 : if (callInfo.argc() > 1) {
856 0 : if (callInfo.getArg(1)->type() != MIRType::Int32)
857 0 : return InliningStatus_NotInlined;
858 : }
859 : }
860 :
861 : // |this| must be a dense array.
862 0 : TemporaryTypeSet* thisTypes = obj->resultTypeSet();
863 0 : if (!thisTypes)
864 0 : return InliningStatus_NotInlined;
865 :
866 0 : const Class* clasp = thisTypes->getKnownClass(constraints());
867 0 : if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
868 0 : return InliningStatus_NotInlined;
869 0 : if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
870 : OBJECT_FLAG_LENGTH_OVERFLOW))
871 : {
872 0 : trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
873 0 : return InliningStatus_NotInlined;
874 : }
875 :
876 0 : JSValueType unboxedType = JSVAL_TYPE_MAGIC;
877 0 : if (clasp == &UnboxedArrayObject::class_) {
878 0 : unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
879 0 : if (unboxedType == JSVAL_TYPE_MAGIC)
880 0 : return InliningStatus_NotInlined;
881 : }
882 :
883 : // Watch out for indexed properties on the prototype.
884 : bool hasIndexedProperty;
885 0 : MOZ_TRY_VAR(hasIndexedProperty, ArrayPrototypeHasIndexedProperty(this, script()));
886 0 : if (hasIndexedProperty) {
887 0 : trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
888 0 : return InliningStatus_NotInlined;
889 : }
890 :
891 : // The group of the result will be dynamically fixed up to match the input
892 : // object, allowing us to handle 'this' objects that might have more than
893 : // one group. Make sure that no singletons can be sliced here.
894 0 : for (unsigned i = 0; i < thisTypes->getObjectCount(); i++) {
895 0 : TypeSet::ObjectKey* key = thisTypes->getObject(i);
896 0 : if (key && key->isSingleton())
897 0 : return InliningStatus_NotInlined;
898 : }
899 :
900 : // Inline the call.
901 0 : JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_slice);
902 0 : if (!templateObj)
903 0 : return InliningStatus_NotInlined;
904 :
905 0 : if (unboxedType == JSVAL_TYPE_MAGIC) {
906 0 : if (!templateObj->is<ArrayObject>())
907 0 : return InliningStatus_NotInlined;
908 : } else {
909 0 : if (!templateObj->is<UnboxedArrayObject>())
910 0 : return InliningStatus_NotInlined;
911 0 : if (templateObj->as<UnboxedArrayObject>().elementType() != unboxedType)
912 0 : return InliningStatus_NotInlined;
913 : }
914 :
915 0 : callInfo.setImplicitlyUsedUnchecked();
916 :
917 : MDefinition* begin;
918 0 : if (callInfo.argc() > 0)
919 0 : begin = callInfo.getArg(0);
920 : else
921 0 : begin = constant(Int32Value(0));
922 :
923 : MDefinition* end;
924 0 : if (callInfo.argc() > 1) {
925 0 : end = callInfo.getArg(1);
926 0 : } else if (clasp == &ArrayObject::class_) {
927 0 : MElements* elements = MElements::New(alloc(), obj);
928 0 : current->add(elements);
929 :
930 0 : end = MArrayLength::New(alloc(), elements);
931 0 : current->add(end->toInstruction());
932 : } else {
933 0 : end = MUnboxedArrayLength::New(alloc(), obj);
934 0 : current->add(end->toInstruction());
935 : }
936 :
937 0 : MArraySlice* ins = MArraySlice::New(alloc(), constraints(),
938 : obj, begin, end,
939 : templateObj,
940 0 : templateObj->group()->initialHeap(constraints()),
941 0 : unboxedType);
942 0 : current->add(ins);
943 0 : current->push(ins);
944 :
945 0 : MOZ_TRY(resumeAfter(ins));
946 0 : MOZ_TRY(pushTypeBarrier(ins, getInlineReturnTypeSet(), BarrierKind::TypeSet));
947 0 : return InliningStatus_Inlined;
948 : }
949 :
950 : IonBuilder::InliningResult
951 0 : IonBuilder::inlineBoolean(CallInfo& callInfo)
952 : {
953 0 : if (callInfo.constructing()) {
954 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
955 0 : return InliningStatus_NotInlined;
956 : }
957 :
958 0 : if (getInlineReturnType() != MIRType::Boolean)
959 0 : return InliningStatus_NotInlined;
960 :
961 0 : callInfo.setImplicitlyUsedUnchecked();
962 :
963 0 : if (callInfo.argc() > 0) {
964 0 : MDefinition* result = convertToBoolean(callInfo.getArg(0));
965 0 : current->push(result);
966 : } else {
967 0 : pushConstant(BooleanValue(false));
968 : }
969 0 : return InliningStatus_Inlined;
970 : }
971 :
972 : IonBuilder::InliningResult
973 4 : IonBuilder::inlineNewIterator(CallInfo& callInfo, MNewIterator::Type type)
974 : {
975 4 : if (callInfo.argc() != 0 || callInfo.constructing()) {
976 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
977 0 : return InliningStatus_NotInlined;
978 : }
979 :
980 4 : JSObject* templateObject = nullptr;
981 4 : switch (type) {
982 : case MNewIterator::ArrayIterator:
983 4 : templateObject = inspector->getTemplateObjectForNative(pc, js::intrinsic_NewArrayIterator);
984 4 : MOZ_ASSERT_IF(templateObject, templateObject->is<ArrayIteratorObject>());
985 4 : break;
986 : case MNewIterator::StringIterator:
987 0 : templateObject = inspector->getTemplateObjectForNative(pc, js::intrinsic_NewStringIterator);
988 0 : MOZ_ASSERT_IF(templateObject, templateObject->is<StringIteratorObject>());
989 0 : break;
990 : }
991 :
992 4 : if (!templateObject)
993 0 : return InliningStatus_NotInlined;
994 :
995 4 : callInfo.setImplicitlyUsedUnchecked();
996 :
997 4 : MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
998 4 : current->add(templateConst);
999 :
1000 4 : MNewIterator* ins = MNewIterator::New(alloc(), constraints(), templateConst, type);
1001 4 : current->add(ins);
1002 4 : current->push(ins);
1003 :
1004 4 : MOZ_TRY(resumeAfter(ins));
1005 4 : return InliningStatus_Inlined;
1006 : }
1007 :
1008 : IonBuilder::InliningResult
1009 0 : IonBuilder::inlineMathAbs(CallInfo& callInfo)
1010 : {
1011 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
1012 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1013 0 : return InliningStatus_NotInlined;
1014 : }
1015 :
1016 0 : MIRType returnType = getInlineReturnType();
1017 0 : MIRType argType = callInfo.getArg(0)->type();
1018 0 : if (!IsNumberType(argType))
1019 0 : return InliningStatus_NotInlined;
1020 :
1021 : // Either argType == returnType, or
1022 : // argType == Double or Float32, returnType == Int, or
1023 : // argType == Float32, returnType == Double
1024 0 : if (argType != returnType && !(IsFloatingPointType(argType) && returnType == MIRType::Int32)
1025 0 : && !(argType == MIRType::Float32 && returnType == MIRType::Double))
1026 : {
1027 0 : return InliningStatus_NotInlined;
1028 : }
1029 :
1030 0 : callInfo.setImplicitlyUsedUnchecked();
1031 :
1032 : // If the arg is a Float32, we specialize the op as double, it will be specialized
1033 : // as float32 if necessary later.
1034 0 : MIRType absType = (argType == MIRType::Float32) ? MIRType::Double : argType;
1035 0 : MInstruction* ins = MAbs::New(alloc(), callInfo.getArg(0), absType);
1036 0 : current->add(ins);
1037 :
1038 0 : current->push(ins);
1039 0 : return InliningStatus_Inlined;
1040 : }
1041 :
1042 : IonBuilder::InliningResult
1043 0 : IonBuilder::inlineMathFloor(CallInfo& callInfo)
1044 : {
1045 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
1046 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1047 0 : return InliningStatus_NotInlined;
1048 : }
1049 :
1050 0 : MIRType argType = callInfo.getArg(0)->type();
1051 0 : MIRType returnType = getInlineReturnType();
1052 :
1053 : // Math.floor(int(x)) == int(x)
1054 0 : if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
1055 0 : 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 0 : MLimitedTruncate* ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
1061 0 : MDefinition::IndirectTruncate);
1062 0 : current->add(ins);
1063 0 : current->push(ins);
1064 0 : return InliningStatus_Inlined;
1065 : }
1066 :
1067 0 : if (IsFloatingPointType(argType)) {
1068 0 : if (returnType == MIRType::Int32) {
1069 0 : callInfo.setImplicitlyUsedUnchecked();
1070 0 : MFloor* ins = MFloor::New(alloc(), callInfo.getArg(0));
1071 0 : current->add(ins);
1072 0 : current->push(ins);
1073 0 : return InliningStatus_Inlined;
1074 : }
1075 :
1076 0 : if (returnType == MIRType::Double) {
1077 0 : callInfo.setImplicitlyUsedUnchecked();
1078 :
1079 0 : MInstruction* ins = nullptr;
1080 0 : if (MNearbyInt::HasAssemblerSupport(RoundingMode::Down)) {
1081 0 : ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType, RoundingMode::Down);
1082 : } else {
1083 0 : ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor,
1084 0 : /* cache */ nullptr);
1085 : }
1086 :
1087 0 : current->add(ins);
1088 0 : current->push(ins);
1089 0 : return InliningStatus_Inlined;
1090 : }
1091 : }
1092 :
1093 0 : return InliningStatus_NotInlined;
1094 : }
1095 :
1096 : IonBuilder::InliningResult
1097 0 : IonBuilder::inlineMathCeil(CallInfo& callInfo)
1098 : {
1099 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
1100 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1101 0 : return InliningStatus_NotInlined;
1102 : }
1103 :
1104 0 : MIRType argType = callInfo.getArg(0)->type();
1105 0 : MIRType returnType = getInlineReturnType();
1106 :
1107 : // Math.ceil(int(x)) == int(x)
1108 0 : if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
1109 0 : callInfo.setImplicitlyUsedUnchecked();
1110 : // The int operand may be something which bails out if the actual value
1111 : // is not in the range of the result type of the MIR. We need to tell
1112 : // the optimizer to preserve this bailout even if the final result is
1113 : // fully truncated.
1114 0 : MLimitedTruncate* ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
1115 0 : MDefinition::IndirectTruncate);
1116 0 : current->add(ins);
1117 0 : current->push(ins);
1118 0 : return InliningStatus_Inlined;
1119 : }
1120 :
1121 0 : if (IsFloatingPointType(argType)) {
1122 0 : if (returnType == MIRType::Int32) {
1123 0 : callInfo.setImplicitlyUsedUnchecked();
1124 0 : MCeil* ins = MCeil::New(alloc(), callInfo.getArg(0));
1125 0 : current->add(ins);
1126 0 : current->push(ins);
1127 0 : return InliningStatus_Inlined;
1128 : }
1129 :
1130 0 : if (returnType == MIRType::Double) {
1131 0 : callInfo.setImplicitlyUsedUnchecked();
1132 :
1133 0 : MInstruction* ins = nullptr;
1134 0 : if (MNearbyInt::HasAssemblerSupport(RoundingMode::Up)) {
1135 0 : ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType, RoundingMode::Up);
1136 : } else {
1137 0 : ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil,
1138 0 : /* cache */ nullptr);
1139 : }
1140 :
1141 0 : current->add(ins);
1142 0 : current->push(ins);
1143 0 : return InliningStatus_Inlined;
1144 : }
1145 : }
1146 :
1147 0 : return InliningStatus_NotInlined;
1148 : }
1149 :
1150 : IonBuilder::InliningResult
1151 0 : IonBuilder::inlineMathClz32(CallInfo& callInfo)
1152 : {
1153 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
1154 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1155 0 : return InliningStatus_NotInlined;
1156 : }
1157 :
1158 0 : MIRType returnType = getInlineReturnType();
1159 0 : if (returnType != MIRType::Int32)
1160 0 : return InliningStatus_NotInlined;
1161 :
1162 0 : if (!IsNumberType(callInfo.getArg(0)->type()))
1163 0 : return InliningStatus_NotInlined;
1164 :
1165 0 : callInfo.setImplicitlyUsedUnchecked();
1166 :
1167 0 : MClz* ins = MClz::New(alloc(), callInfo.getArg(0), MIRType::Int32);
1168 0 : current->add(ins);
1169 0 : current->push(ins);
1170 0 : return InliningStatus_Inlined;
1171 :
1172 : }
1173 :
1174 : IonBuilder::InliningResult
1175 0 : IonBuilder::inlineMathRound(CallInfo& callInfo)
1176 : {
1177 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
1178 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1179 0 : return InliningStatus_NotInlined;
1180 : }
1181 :
1182 0 : MIRType returnType = getInlineReturnType();
1183 0 : MIRType argType = callInfo.getArg(0)->type();
1184 :
1185 : // Math.round(int(x)) == int(x)
1186 0 : if (argType == MIRType::Int32 && returnType == MIRType::Int32) {
1187 0 : callInfo.setImplicitlyUsedUnchecked();
1188 : // The int operand may be something which bails out if the actual value
1189 : // is not in the range of the result type of the MIR. We need to tell
1190 : // the optimizer to preserve this bailout even if the final result is
1191 : // fully truncated.
1192 0 : MLimitedTruncate* ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
1193 0 : MDefinition::IndirectTruncate);
1194 0 : current->add(ins);
1195 0 : current->push(ins);
1196 0 : return InliningStatus_Inlined;
1197 : }
1198 :
1199 0 : if (IsFloatingPointType(argType) && returnType == MIRType::Int32) {
1200 0 : callInfo.setImplicitlyUsedUnchecked();
1201 0 : MRound* ins = MRound::New(alloc(), callInfo.getArg(0));
1202 0 : current->add(ins);
1203 0 : current->push(ins);
1204 0 : return InliningStatus_Inlined;
1205 : }
1206 :
1207 0 : if (IsFloatingPointType(argType) && returnType == MIRType::Double) {
1208 0 : callInfo.setImplicitlyUsedUnchecked();
1209 0 : MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round,
1210 0 : /* cache */ nullptr);
1211 0 : current->add(ins);
1212 0 : current->push(ins);
1213 0 : return InliningStatus_Inlined;
1214 : }
1215 :
1216 0 : return InliningStatus_NotInlined;
1217 : }
1218 :
1219 : IonBuilder::InliningResult
1220 0 : IonBuilder::inlineMathSqrt(CallInfo& callInfo)
1221 : {
1222 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
1223 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1224 0 : return InliningStatus_NotInlined;
1225 : }
1226 :
1227 0 : MIRType argType = callInfo.getArg(0)->type();
1228 0 : if (getInlineReturnType() != MIRType::Double)
1229 0 : return InliningStatus_NotInlined;
1230 0 : if (!IsNumberType(argType))
1231 0 : return InliningStatus_NotInlined;
1232 :
1233 0 : callInfo.setImplicitlyUsedUnchecked();
1234 :
1235 0 : MSqrt* sqrt = MSqrt::New(alloc(), callInfo.getArg(0), MIRType::Double);
1236 0 : current->add(sqrt);
1237 0 : current->push(sqrt);
1238 0 : return InliningStatus_Inlined;
1239 : }
1240 :
1241 : IonBuilder::InliningResult
1242 0 : IonBuilder::inlineMathAtan2(CallInfo& callInfo)
1243 : {
1244 0 : if (callInfo.argc() != 2 || callInfo.constructing()) {
1245 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1246 0 : return InliningStatus_NotInlined;
1247 : }
1248 :
1249 0 : if (getInlineReturnType() != MIRType::Double)
1250 0 : return InliningStatus_NotInlined;
1251 :
1252 0 : MIRType argType0 = callInfo.getArg(0)->type();
1253 0 : MIRType argType1 = callInfo.getArg(1)->type();
1254 :
1255 0 : if (!IsNumberType(argType0) || !IsNumberType(argType1))
1256 0 : return InliningStatus_NotInlined;
1257 :
1258 0 : callInfo.setImplicitlyUsedUnchecked();
1259 :
1260 0 : MAtan2* atan2 = MAtan2::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
1261 0 : current->add(atan2);
1262 0 : current->push(atan2);
1263 0 : return InliningStatus_Inlined;
1264 : }
1265 :
1266 : IonBuilder::InliningResult
1267 0 : IonBuilder::inlineMathHypot(CallInfo& callInfo)
1268 : {
1269 0 : if (callInfo.constructing()) {
1270 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1271 0 : return InliningStatus_NotInlined;
1272 : }
1273 :
1274 0 : uint32_t argc = callInfo.argc();
1275 0 : if (argc < 2 || argc > 4) {
1276 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1277 0 : return InliningStatus_NotInlined;
1278 : }
1279 :
1280 0 : if (getInlineReturnType() != MIRType::Double)
1281 0 : return InliningStatus_NotInlined;
1282 :
1283 0 : MDefinitionVector vector(alloc());
1284 0 : if (!vector.reserve(argc))
1285 0 : return InliningStatus_NotInlined;
1286 :
1287 0 : for (uint32_t i = 0; i < argc; ++i) {
1288 0 : MDefinition * arg = callInfo.getArg(i);
1289 0 : if (!IsNumberType(arg->type()))
1290 0 : return InliningStatus_NotInlined;
1291 0 : vector.infallibleAppend(arg);
1292 : }
1293 :
1294 0 : callInfo.setImplicitlyUsedUnchecked();
1295 0 : MHypot* hypot = MHypot::New(alloc(), vector);
1296 :
1297 0 : if (!hypot)
1298 0 : return InliningStatus_NotInlined;
1299 :
1300 0 : current->add(hypot);
1301 0 : current->push(hypot);
1302 0 : return InliningStatus_Inlined;
1303 : }
1304 :
1305 : IonBuilder::InliningResult
1306 0 : IonBuilder::inlineMathPow(CallInfo& callInfo)
1307 : {
1308 0 : if (callInfo.argc() != 2 || callInfo.constructing()) {
1309 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1310 0 : return InliningStatus_NotInlined;
1311 : }
1312 :
1313 0 : bool emitted = false;
1314 0 : MOZ_TRY(powTrySpecialized(&emitted, callInfo.getArg(0), callInfo.getArg(1),
1315 : getInlineReturnType()));
1316 :
1317 0 : if (!emitted)
1318 0 : return InliningStatus_NotInlined;
1319 :
1320 0 : callInfo.setImplicitlyUsedUnchecked();
1321 0 : return InliningStatus_Inlined;
1322 : }
1323 :
1324 : IonBuilder::InliningResult
1325 0 : IonBuilder::inlineMathRandom(CallInfo& callInfo)
1326 : {
1327 0 : if (callInfo.constructing()) {
1328 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1329 0 : return InliningStatus_NotInlined;
1330 : }
1331 :
1332 0 : if (getInlineReturnType() != MIRType::Double)
1333 0 : return InliningStatus_NotInlined;
1334 :
1335 : // MRandom JIT code directly accesses the RNG. It's (barely) possible to
1336 : // inline Math.random without it having been called yet, so ensure RNG
1337 : // state that isn't guaranteed to be initialized already.
1338 0 : script()->compartment()->ensureRandomNumberGenerator();
1339 :
1340 0 : callInfo.setImplicitlyUsedUnchecked();
1341 :
1342 0 : MRandom* rand = MRandom::New(alloc());
1343 0 : current->add(rand);
1344 0 : current->push(rand);
1345 0 : return InliningStatus_Inlined;
1346 : }
1347 :
1348 : IonBuilder::InliningResult
1349 0 : IonBuilder::inlineMathImul(CallInfo& callInfo)
1350 : {
1351 0 : if (callInfo.argc() != 2 || callInfo.constructing()) {
1352 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1353 0 : return InliningStatus_NotInlined;
1354 : }
1355 :
1356 0 : MIRType returnType = getInlineReturnType();
1357 0 : if (returnType != MIRType::Int32)
1358 0 : return InliningStatus_NotInlined;
1359 :
1360 0 : if (!IsNumberType(callInfo.getArg(0)->type()))
1361 0 : return InliningStatus_NotInlined;
1362 0 : if (!IsNumberType(callInfo.getArg(1)->type()))
1363 0 : return InliningStatus_NotInlined;
1364 :
1365 0 : callInfo.setImplicitlyUsedUnchecked();
1366 :
1367 0 : MInstruction* first = MTruncateToInt32::New(alloc(), callInfo.getArg(0));
1368 0 : current->add(first);
1369 :
1370 0 : MInstruction* second = MTruncateToInt32::New(alloc(), callInfo.getArg(1));
1371 0 : current->add(second);
1372 :
1373 0 : MMul* ins = MMul::New(alloc(), first, second, MIRType::Int32, MMul::Integer);
1374 0 : current->add(ins);
1375 0 : current->push(ins);
1376 0 : return InliningStatus_Inlined;
1377 : }
1378 :
1379 : IonBuilder::InliningResult
1380 0 : IonBuilder::inlineMathFRound(CallInfo& callInfo)
1381 : {
1382 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
1383 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1384 0 : return InliningStatus_NotInlined;
1385 : }
1386 :
1387 : // MIRType can't be Float32, as this point, as getInlineReturnType uses JSVal types
1388 : // to infer the returned MIR type.
1389 0 : TemporaryTypeSet* returned = getInlineReturnTypeSet();
1390 0 : if (returned->empty()) {
1391 : // As there's only one possible returned type, just add it to the observed
1392 : // returned typeset
1393 0 : returned->addType(TypeSet::DoubleType(), alloc_->lifoAlloc());
1394 : } else {
1395 0 : MIRType returnType = getInlineReturnType();
1396 0 : if (!IsNumberType(returnType))
1397 0 : return InliningStatus_NotInlined;
1398 : }
1399 :
1400 0 : MIRType arg = callInfo.getArg(0)->type();
1401 0 : if (!IsNumberType(arg))
1402 0 : return InliningStatus_NotInlined;
1403 :
1404 0 : callInfo.setImplicitlyUsedUnchecked();
1405 :
1406 0 : MToFloat32* ins = MToFloat32::New(alloc(), callInfo.getArg(0));
1407 0 : current->add(ins);
1408 0 : current->push(ins);
1409 0 : return InliningStatus_Inlined;
1410 : }
1411 :
1412 : IonBuilder::InliningResult
1413 16 : IonBuilder::inlineMathMinMax(CallInfo& callInfo, bool max)
1414 : {
1415 16 : if (callInfo.argc() < 1 || callInfo.constructing()) {
1416 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1417 0 : return InliningStatus_NotInlined;
1418 : }
1419 :
1420 16 : MIRType returnType = getInlineReturnType();
1421 16 : if (!IsNumberType(returnType))
1422 0 : return InliningStatus_NotInlined;
1423 :
1424 32 : MDefinitionVector int32_cases(alloc());
1425 48 : for (unsigned i = 0; i < callInfo.argc(); i++) {
1426 32 : MDefinition* arg = callInfo.getArg(i);
1427 :
1428 32 : switch (arg->type()) {
1429 : case MIRType::Int32:
1430 24 : if (!int32_cases.append(arg))
1431 0 : return abort(AbortReason::Alloc);
1432 24 : break;
1433 : case MIRType::Double:
1434 : case MIRType::Float32:
1435 : // Don't force a double MMinMax for arguments that would be a NOP
1436 : // when doing an integer MMinMax.
1437 8 : if (arg->isConstant()) {
1438 8 : double cte = arg->toConstant()->numberToDouble();
1439 : // min(int32, cte >= INT32_MAX) = int32
1440 8 : if (cte >= INT32_MAX && !max)
1441 8 : break;
1442 : // max(int32, cte <= INT32_MIN) = int32
1443 0 : if (cte <= INT32_MIN && max)
1444 0 : break;
1445 : }
1446 :
1447 : // Force double MMinMax if argument is a "effectfull" double.
1448 0 : returnType = MIRType::Double;
1449 0 : break;
1450 : default:
1451 0 : return InliningStatus_NotInlined;
1452 : }
1453 : }
1454 :
1455 16 : if (int32_cases.length() == 0)
1456 0 : returnType = MIRType::Double;
1457 :
1458 16 : callInfo.setImplicitlyUsedUnchecked();
1459 :
1460 16 : MDefinitionVector& cases = (returnType == MIRType::Int32) ? int32_cases : callInfo.argv();
1461 :
1462 16 : if (cases.length() == 1) {
1463 8 : MLimitedTruncate* limit = MLimitedTruncate::New(alloc(), cases[0], MDefinition::NoTruncate);
1464 8 : current->add(limit);
1465 8 : current->push(limit);
1466 8 : return InliningStatus_Inlined;
1467 : }
1468 :
1469 : // Chain N-1 MMinMax instructions to compute the MinMax.
1470 8 : MMinMax* last = MMinMax::New(alloc(), cases[0], cases[1], returnType, max);
1471 8 : current->add(last);
1472 :
1473 8 : for (unsigned i = 2; i < cases.length(); i++) {
1474 0 : MMinMax* ins = MMinMax::New(alloc().fallible(), last, cases[i], returnType, max);
1475 0 : if (!ins)
1476 0 : return abort(AbortReason::Alloc);
1477 0 : current->add(ins);
1478 0 : last = ins;
1479 : }
1480 :
1481 8 : current->push(last);
1482 8 : return InliningStatus_Inlined;
1483 : }
1484 :
1485 : IonBuilder::InliningResult
1486 0 : IonBuilder::inlineStringObject(CallInfo& callInfo)
1487 : {
1488 0 : if (callInfo.argc() != 1 || !callInfo.constructing()) {
1489 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1490 0 : return InliningStatus_NotInlined;
1491 : }
1492 :
1493 : // ConvertToString doesn't support objects.
1494 0 : if (callInfo.getArg(0)->mightBeType(MIRType::Object))
1495 0 : return InliningStatus_NotInlined;
1496 :
1497 0 : JSObject* templateObj = inspector->getTemplateObjectForNative(pc, StringConstructor);
1498 0 : if (!templateObj)
1499 0 : return InliningStatus_NotInlined;
1500 0 : MOZ_ASSERT(templateObj->is<StringObject>());
1501 :
1502 0 : callInfo.setImplicitlyUsedUnchecked();
1503 :
1504 0 : MNewStringObject* ins = MNewStringObject::New(alloc(), callInfo.getArg(0), templateObj);
1505 0 : current->add(ins);
1506 0 : current->push(ins);
1507 :
1508 0 : MOZ_TRY(resumeAfter(ins));
1509 0 : return InliningStatus_Inlined;
1510 : }
1511 :
1512 : IonBuilder::InliningResult
1513 0 : IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
1514 : {
1515 0 : if (!callInfo.getArg(0)->isConstant())
1516 0 : return InliningStatus_NotInlined;
1517 :
1518 0 : if (!callInfo.getArg(1)->isConstant())
1519 0 : return InliningStatus_NotInlined;
1520 :
1521 0 : MConstant* strval = callInfo.getArg(0)->toConstant();
1522 0 : if (strval->type() != MIRType::String)
1523 0 : return InliningStatus_NotInlined;
1524 :
1525 0 : MConstant* sepval = callInfo.getArg(1)->toConstant();
1526 0 : if (strval->type() != MIRType::String)
1527 0 : return InliningStatus_NotInlined;
1528 :
1529 : // Check if exist a template object in stub.
1530 0 : JSString* stringStr = nullptr;
1531 0 : JSString* stringSep = nullptr;
1532 0 : JSObject* templateObject = nullptr;
1533 0 : if (!inspector->isOptimizableConstStringSplit(pc, &stringStr, &stringSep, &templateObject))
1534 0 : return InliningStatus_NotInlined;
1535 :
1536 0 : MOZ_ASSERT(stringStr);
1537 0 : MOZ_ASSERT(stringSep);
1538 0 : MOZ_ASSERT(templateObject);
1539 :
1540 0 : if (strval->toString() != stringStr)
1541 0 : return InliningStatus_NotInlined;
1542 :
1543 0 : if (sepval->toString() != stringSep)
1544 0 : return InliningStatus_NotInlined;
1545 :
1546 : // Check if |templateObject| is valid.
1547 0 : TypeSet::ObjectKey* retType = TypeSet::ObjectKey::get(templateObject);
1548 0 : if (retType->unknownProperties())
1549 0 : return InliningStatus_NotInlined;
1550 :
1551 0 : HeapTypeSetKey key = retType->property(JSID_VOID);
1552 0 : if (!key.maybeTypes())
1553 0 : return InliningStatus_NotInlined;
1554 :
1555 0 : if (!key.maybeTypes()->hasType(TypeSet::StringType()))
1556 0 : return InliningStatus_NotInlined;
1557 :
1558 0 : uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(templateObject);
1559 0 : if (GetAnyBoxedOrUnboxedInitializedLength(templateObject) != initLength)
1560 0 : return InliningStatus_NotInlined;
1561 :
1562 0 : Vector<MConstant*, 0, SystemAllocPolicy> arrayValues;
1563 0 : for (uint32_t i = 0; i < initLength; i++) {
1564 0 : Value str = GetAnyBoxedOrUnboxedDenseElement(templateObject, i);
1565 0 : MOZ_ASSERT(str.toString()->isAtom());
1566 0 : MConstant* value = MConstant::New(alloc().fallible(), str, constraints());
1567 0 : if (!value)
1568 0 : return abort(AbortReason::Alloc);
1569 0 : if (!TypeSetIncludes(key.maybeTypes(), value->type(), value->resultTypeSet()))
1570 0 : return InliningStatus_NotInlined;
1571 :
1572 0 : if (!arrayValues.append(value))
1573 0 : return abort(AbortReason::Alloc);
1574 : }
1575 0 : callInfo.setImplicitlyUsedUnchecked();
1576 :
1577 : TemporaryTypeSet::DoubleConversion conversion =
1578 0 : getInlineReturnTypeSet()->convertDoubleElements(constraints());
1579 0 : if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles)
1580 0 : return InliningStatus_NotInlined;
1581 :
1582 0 : MOZ_TRY(jsop_newarray(templateObject, initLength));
1583 :
1584 0 : MDefinition* array = current->peek(-1);
1585 :
1586 0 : if (!initLength) {
1587 0 : if (!array->isResumePoint())
1588 0 : MOZ_TRY(resumeAfter(array->toNewArray()));
1589 0 : return InliningStatus_Inlined;
1590 : }
1591 :
1592 0 : JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
1593 :
1594 : // Store all values, no need to initialize the length after each as
1595 : // jsop_initelem_array is doing because we do not expect to bailout
1596 : // because the memory is supposed to be allocated by now.
1597 0 : for (uint32_t i = 0; i < initLength; i++) {
1598 0 : if (!alloc().ensureBallast())
1599 0 : return abort(AbortReason::Alloc);
1600 :
1601 0 : MConstant* value = arrayValues[i];
1602 0 : current->add(value);
1603 :
1604 0 : MOZ_TRY(initializeArrayElement(array, i, value, unboxedType, /* addResumePoint = */ false));
1605 : }
1606 :
1607 0 : MInstruction* setLength = setInitializedLength(array, unboxedType, initLength);
1608 0 : MOZ_TRY(resumeAfter(setLength));
1609 0 : return InliningStatus_Inlined;
1610 : }
1611 :
1612 : IonBuilder::InliningResult
1613 0 : IonBuilder::inlineStringSplitString(CallInfo& callInfo)
1614 : {
1615 0 : if (callInfo.argc() != 2 || callInfo.constructing()) {
1616 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1617 0 : return InliningStatus_NotInlined;
1618 : }
1619 :
1620 0 : MDefinition* strArg = callInfo.getArg(0);
1621 0 : MDefinition* sepArg = callInfo.getArg(1);
1622 :
1623 0 : if (strArg->type() != MIRType::String)
1624 0 : return InliningStatus_NotInlined;
1625 :
1626 0 : if (sepArg->type() != MIRType::String)
1627 0 : return InliningStatus_NotInlined;
1628 :
1629 : IonBuilder::InliningStatus resultConstStringSplit;
1630 0 : MOZ_TRY_VAR(resultConstStringSplit, inlineConstantStringSplitString(callInfo));
1631 0 : if (resultConstStringSplit != InliningStatus_NotInlined)
1632 0 : return resultConstStringSplit;
1633 :
1634 0 : JSContext* cx = GetJitContext()->cx;
1635 0 : ObjectGroup* group = ObjectGroupCompartment::getStringSplitStringGroup(cx);
1636 0 : if (!group)
1637 0 : return InliningStatus_NotInlined;
1638 0 : if (group->maybePreliminaryObjects())
1639 0 : return InliningStatus_NotInlined;
1640 :
1641 0 : TypeSet::ObjectKey* retKey = TypeSet::ObjectKey::get(group);
1642 0 : if (retKey->unknownProperties())
1643 0 : return InliningStatus_NotInlined;
1644 :
1645 0 : HeapTypeSetKey key = retKey->property(JSID_VOID);
1646 0 : if (!key.maybeTypes())
1647 0 : return InliningStatus_NotInlined;
1648 :
1649 0 : if (!key.maybeTypes()->hasType(TypeSet::StringType())) {
1650 0 : key.freeze(constraints());
1651 0 : return InliningStatus_NotInlined;
1652 : }
1653 :
1654 0 : callInfo.setImplicitlyUsedUnchecked();
1655 0 : MStringSplit* ins = MStringSplit::New(alloc(), constraints(), strArg, sepArg, group);
1656 0 : current->add(ins);
1657 0 : current->push(ins);
1658 :
1659 0 : return InliningStatus_Inlined;
1660 : }
1661 :
1662 : IonBuilder::InliningResult
1663 0 : IonBuilder::inlineObjectHasPrototype(CallInfo& callInfo)
1664 : {
1665 0 : if (callInfo.argc() != 2 || callInfo.constructing()) {
1666 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1667 0 : return InliningStatus_NotInlined;
1668 : }
1669 :
1670 0 : MDefinition* objArg = callInfo.getArg(0);
1671 0 : MDefinition* protoArg = callInfo.getArg(1);
1672 :
1673 0 : if (objArg->type() != MIRType::Object)
1674 0 : return InliningStatus_NotInlined;
1675 0 : if (protoArg->type() != MIRType::Object)
1676 0 : return InliningStatus_NotInlined;
1677 :
1678 : // Inline only when both obj and proto are singleton objects and
1679 : // obj does not have uncacheable proto and obj.__proto__ is proto.
1680 0 : TemporaryTypeSet* objTypes = objArg->resultTypeSet();
1681 0 : if (!objTypes || objTypes->unknownObject() || objTypes->getObjectCount() != 1)
1682 0 : return InliningStatus_NotInlined;
1683 :
1684 0 : TypeSet::ObjectKey* objKey = objTypes->getObject(0);
1685 0 : if (!objKey || !objKey->hasStableClassAndProto(constraints()))
1686 0 : return InliningStatus_NotInlined;
1687 0 : if (!objKey->isSingleton() || !objKey->singleton()->is<NativeObject>())
1688 0 : return InliningStatus_NotInlined;
1689 :
1690 0 : JSObject* obj = &objKey->singleton()->as<NativeObject>();
1691 0 : if (obj->hasUncacheableProto())
1692 0 : return InliningStatus_NotInlined;
1693 :
1694 0 : JSObject* actualProto = checkNurseryObject(objKey->proto().toObjectOrNull());
1695 0 : if (actualProto == nullptr)
1696 0 : return InliningStatus_NotInlined;
1697 :
1698 0 : TemporaryTypeSet* protoTypes = protoArg->resultTypeSet();
1699 0 : if (!protoTypes || protoTypes->unknownObject() || protoTypes->getObjectCount() != 1)
1700 0 : return InliningStatus_NotInlined;
1701 :
1702 0 : TypeSet::ObjectKey* protoKey = protoTypes->getObject(0);
1703 0 : if (!protoKey || !protoKey->hasStableClassAndProto(constraints()))
1704 0 : return InliningStatus_NotInlined;
1705 0 : if (!protoKey->isSingleton() || !protoKey->singleton()->is<NativeObject>())
1706 0 : return InliningStatus_NotInlined;
1707 :
1708 0 : JSObject* proto = &protoKey->singleton()->as<NativeObject>();
1709 0 : pushConstant(BooleanValue(proto == actualProto));
1710 0 : callInfo.setImplicitlyUsedUnchecked();
1711 0 : return InliningStatus_Inlined;
1712 : }
1713 :
1714 : IonBuilder::InliningResult
1715 0 : IonBuilder::inlineFinishBoundFunctionInit(CallInfo& callInfo)
1716 : {
1717 0 : MOZ_ASSERT(!callInfo.constructing());
1718 0 : MOZ_ASSERT(callInfo.argc() == 3);
1719 0 : MOZ_ASSERT(BytecodeIsPopped(pc));
1720 :
1721 0 : MDefinition* boundFunction = callInfo.getArg(0);
1722 0 : MDefinition* targetFunction = callInfo.getArg(1);
1723 0 : MDefinition* argCount = callInfo.getArg(2);
1724 :
1725 0 : if (boundFunction->type() != MIRType::Object)
1726 0 : return InliningStatus_NotInlined;
1727 0 : if (targetFunction->type() != MIRType::Object)
1728 0 : return InliningStatus_NotInlined;
1729 0 : if (argCount->type() != MIRType::Int32)
1730 0 : return InliningStatus_NotInlined;
1731 :
1732 0 : callInfo.setImplicitlyUsedUnchecked();
1733 :
1734 0 : auto* ins = MFinishBoundFunctionInit::New(alloc(), boundFunction, targetFunction, argCount);
1735 0 : current->add(ins);
1736 :
1737 0 : pushConstant(UndefinedValue());
1738 :
1739 0 : MOZ_TRY(resumeAfter(ins));
1740 0 : return InliningStatus_Inlined;
1741 : }
1742 :
1743 : IonBuilder::InliningResult
1744 3 : IonBuilder::inlineStrCharCodeAt(CallInfo& callInfo)
1745 : {
1746 3 : if (callInfo.argc() != 1 || callInfo.constructing()) {
1747 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1748 0 : return InliningStatus_NotInlined;
1749 : }
1750 :
1751 3 : if (getInlineReturnType() != MIRType::Int32)
1752 0 : return InliningStatus_NotInlined;
1753 3 : if (callInfo.thisArg()->type() != MIRType::String && callInfo.thisArg()->type() != MIRType::Value)
1754 0 : return InliningStatus_NotInlined;
1755 3 : MIRType argType = callInfo.getArg(0)->type();
1756 3 : if (argType != MIRType::Int32 && argType != MIRType::Double)
1757 0 : return InliningStatus_NotInlined;
1758 :
1759 : // Check for STR.charCodeAt(IDX) where STR is a constant string and IDX is a
1760 : // constant integer.
1761 : InliningStatus constInlineStatus;
1762 3 : MOZ_TRY_VAR(constInlineStatus, inlineConstantCharCodeAt(callInfo));
1763 3 : if (constInlineStatus != InliningStatus_NotInlined)
1764 0 : return constInlineStatus;
1765 :
1766 3 : callInfo.setImplicitlyUsedUnchecked();
1767 :
1768 3 : MInstruction* index = MToInt32::New(alloc(), callInfo.getArg(0));
1769 3 : current->add(index);
1770 :
1771 3 : MStringLength* length = MStringLength::New(alloc(), callInfo.thisArg());
1772 3 : current->add(length);
1773 :
1774 3 : index = addBoundsCheck(index, length);
1775 :
1776 3 : MCharCodeAt* charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
1777 3 : current->add(charCode);
1778 3 : current->push(charCode);
1779 3 : return InliningStatus_Inlined;
1780 : }
1781 :
1782 : IonBuilder::InliningResult
1783 3 : IonBuilder::inlineConstantCharCodeAt(CallInfo& callInfo)
1784 : {
1785 3 : if (!callInfo.thisArg()->maybeConstantValue() || !callInfo.getArg(0)->maybeConstantValue()) {
1786 3 : trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
1787 3 : return InliningStatus_NotInlined;
1788 : }
1789 :
1790 0 : MConstant* strval = callInfo.thisArg()->maybeConstantValue();
1791 0 : MConstant* idxval = callInfo.getArg(0)->maybeConstantValue();
1792 :
1793 0 : if (strval->type() != MIRType::String || idxval->type() != MIRType::Int32)
1794 0 : return InliningStatus_NotInlined;
1795 :
1796 0 : JSString* str = strval->toString();
1797 0 : if (!str->isLinear()) {
1798 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
1799 0 : return InliningStatus_NotInlined;
1800 : }
1801 :
1802 0 : int32_t idx = idxval->toInt32();
1803 0 : if (idx < 0 || (uint32_t(idx) >= str->length())) {
1804 0 : trackOptimizationOutcome(TrackedOutcome::OutOfBounds);
1805 0 : return InliningStatus_NotInlined;
1806 : }
1807 :
1808 0 : callInfo.setImplicitlyUsedUnchecked();
1809 :
1810 0 : JSLinearString& linstr = str->asLinear();
1811 0 : char16_t ch = linstr.latin1OrTwoByteChar(idx);
1812 0 : MConstant* result = MConstant::New(alloc(), Int32Value(ch));
1813 0 : current->add(result);
1814 0 : current->push(result);
1815 0 : return InliningStatus_Inlined;
1816 : }
1817 :
1818 : IonBuilder::InliningResult
1819 0 : IonBuilder::inlineStrFromCharCode(CallInfo& callInfo)
1820 : {
1821 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
1822 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1823 0 : return InliningStatus_NotInlined;
1824 : }
1825 :
1826 0 : if (getInlineReturnType() != MIRType::String)
1827 0 : return InliningStatus_NotInlined;
1828 0 : if (callInfo.getArg(0)->type() != MIRType::Int32)
1829 0 : return InliningStatus_NotInlined;
1830 :
1831 0 : callInfo.setImplicitlyUsedUnchecked();
1832 :
1833 0 : MFromCharCode* string = MFromCharCode::New(alloc(), callInfo.getArg(0));
1834 0 : current->add(string);
1835 0 : current->push(string);
1836 0 : return InliningStatus_Inlined;
1837 : }
1838 :
1839 : IonBuilder::InliningResult
1840 0 : IonBuilder::inlineStrFromCodePoint(CallInfo& callInfo)
1841 : {
1842 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
1843 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1844 0 : return InliningStatus_NotInlined;
1845 : }
1846 :
1847 0 : if (getInlineReturnType() != MIRType::String)
1848 0 : return InliningStatus_NotInlined;
1849 0 : if (callInfo.getArg(0)->type() != MIRType::Int32)
1850 0 : return InliningStatus_NotInlined;
1851 :
1852 0 : callInfo.setImplicitlyUsedUnchecked();
1853 :
1854 0 : MFromCodePoint* string = MFromCodePoint::New(alloc(), callInfo.getArg(0));
1855 0 : current->add(string);
1856 0 : current->push(string);
1857 0 : return InliningStatus_Inlined;
1858 : }
1859 :
1860 : IonBuilder::InliningResult
1861 0 : IonBuilder::inlineStrCharAt(CallInfo& callInfo)
1862 : {
1863 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
1864 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1865 0 : return InliningStatus_NotInlined;
1866 : }
1867 :
1868 0 : if (getInlineReturnType() != MIRType::String)
1869 0 : return InliningStatus_NotInlined;
1870 0 : if (callInfo.thisArg()->type() != MIRType::String)
1871 0 : return InliningStatus_NotInlined;
1872 0 : MIRType argType = callInfo.getArg(0)->type();
1873 0 : if (argType != MIRType::Int32 && argType != MIRType::Double)
1874 0 : return InliningStatus_NotInlined;
1875 :
1876 0 : callInfo.setImplicitlyUsedUnchecked();
1877 :
1878 0 : MInstruction* index = MToInt32::New(alloc(), callInfo.getArg(0));
1879 0 : current->add(index);
1880 :
1881 0 : MStringLength* length = MStringLength::New(alloc(), callInfo.thisArg());
1882 0 : current->add(length);
1883 :
1884 0 : index = addBoundsCheck(index, length);
1885 :
1886 : // String.charAt(x) = String.fromCharCode(String.charCodeAt(x))
1887 0 : MCharCodeAt* charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
1888 0 : current->add(charCode);
1889 :
1890 0 : MFromCharCode* string = MFromCharCode::New(alloc(), charCode);
1891 0 : current->add(string);
1892 0 : current->push(string);
1893 0 : return InliningStatus_Inlined;
1894 : }
1895 :
1896 : IonBuilder::InliningResult
1897 0 : IonBuilder::inlineRegExpMatcher(CallInfo& callInfo)
1898 : {
1899 : // This is called from Self-hosted JS, after testing each argument,
1900 : // most of following tests should be passed.
1901 :
1902 0 : if (callInfo.argc() != 3 || callInfo.constructing()) {
1903 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1904 0 : return InliningStatus_NotInlined;
1905 : }
1906 :
1907 0 : MDefinition* rxArg = callInfo.getArg(0);
1908 0 : MDefinition* strArg = callInfo.getArg(1);
1909 0 : MDefinition* lastIndexArg = callInfo.getArg(2);
1910 :
1911 0 : if (rxArg->type() != MIRType::Object)
1912 0 : return InliningStatus_NotInlined;
1913 :
1914 0 : TemporaryTypeSet* rxTypes = rxArg->resultTypeSet();
1915 0 : const Class* clasp = rxTypes ? rxTypes->getKnownClass(constraints()) : nullptr;
1916 0 : if (clasp != &RegExpObject::class_)
1917 0 : return InliningStatus_NotInlined;
1918 :
1919 0 : if (strArg->mightBeType(MIRType::Object))
1920 0 : return InliningStatus_NotInlined;
1921 :
1922 0 : if (lastIndexArg->type() != MIRType::Int32)
1923 0 : return InliningStatus_NotInlined;
1924 :
1925 0 : JSContext* cx = GetJitContext()->cx;
1926 0 : if (!cx->compartment()->jitCompartment()->ensureRegExpMatcherStubExists(cx)) {
1927 0 : cx->clearPendingException(); // OOM or overrecursion.
1928 0 : return InliningStatus_NotInlined;
1929 : }
1930 :
1931 0 : callInfo.setImplicitlyUsedUnchecked();
1932 :
1933 0 : MInstruction* matcher = MRegExpMatcher::New(alloc(), rxArg, strArg, lastIndexArg);
1934 0 : current->add(matcher);
1935 0 : current->push(matcher);
1936 :
1937 0 : MOZ_TRY(resumeAfter(matcher));
1938 0 : MOZ_TRY(pushTypeBarrier(matcher, getInlineReturnTypeSet(), BarrierKind::TypeSet));
1939 0 : return InliningStatus_Inlined;
1940 : }
1941 :
1942 : IonBuilder::InliningResult
1943 0 : IonBuilder::inlineRegExpSearcher(CallInfo& callInfo)
1944 : {
1945 : // This is called from Self-hosted JS, after testing each argument,
1946 : // most of following tests should be passed.
1947 :
1948 0 : if (callInfo.argc() != 3 || callInfo.constructing()) {
1949 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1950 0 : return InliningStatus_NotInlined;
1951 : }
1952 :
1953 0 : MDefinition* rxArg = callInfo.getArg(0);
1954 0 : MDefinition* strArg = callInfo.getArg(1);
1955 0 : MDefinition* lastIndexArg = callInfo.getArg(2);
1956 :
1957 0 : if (rxArg->type() != MIRType::Object)
1958 0 : return InliningStatus_NotInlined;
1959 :
1960 0 : TemporaryTypeSet* regexpTypes = rxArg->resultTypeSet();
1961 0 : const Class* clasp = regexpTypes ? regexpTypes->getKnownClass(constraints()) : nullptr;
1962 0 : if (clasp != &RegExpObject::class_)
1963 0 : return InliningStatus_NotInlined;
1964 :
1965 0 : if (strArg->mightBeType(MIRType::Object))
1966 0 : return InliningStatus_NotInlined;
1967 :
1968 0 : if (lastIndexArg->type() != MIRType::Int32)
1969 0 : return InliningStatus_NotInlined;
1970 :
1971 0 : JSContext* cx = GetJitContext()->cx;
1972 0 : if (!cx->compartment()->jitCompartment()->ensureRegExpSearcherStubExists(cx)) {
1973 0 : cx->clearPendingException(); // OOM or overrecursion.
1974 0 : return abort(AbortReason::Error);
1975 : }
1976 :
1977 0 : callInfo.setImplicitlyUsedUnchecked();
1978 :
1979 0 : MInstruction* searcher = MRegExpSearcher::New(alloc(), rxArg, strArg, lastIndexArg);
1980 0 : current->add(searcher);
1981 0 : current->push(searcher);
1982 :
1983 0 : MOZ_TRY(resumeAfter(searcher));
1984 0 : MOZ_TRY(pushTypeBarrier(searcher, getInlineReturnTypeSet(), BarrierKind::TypeSet));
1985 0 : return InliningStatus_Inlined;
1986 : }
1987 :
1988 : IonBuilder::InliningResult
1989 0 : IonBuilder::inlineRegExpTester(CallInfo& callInfo)
1990 : {
1991 : // This is called from Self-hosted JS, after testing each argument,
1992 : // most of following tests should be passed.
1993 :
1994 0 : if (callInfo.argc() != 3 || callInfo.constructing()) {
1995 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
1996 0 : return InliningStatus_NotInlined;
1997 : }
1998 :
1999 0 : MDefinition* rxArg = callInfo.getArg(0);
2000 0 : MDefinition* strArg = callInfo.getArg(1);
2001 0 : MDefinition* lastIndexArg = callInfo.getArg(2);
2002 :
2003 0 : if (rxArg->type() != MIRType::Object)
2004 0 : return InliningStatus_NotInlined;
2005 :
2006 0 : TemporaryTypeSet* rxTypes = rxArg->resultTypeSet();
2007 0 : const Class* clasp = rxTypes ? rxTypes->getKnownClass(constraints()) : nullptr;
2008 0 : if (clasp != &RegExpObject::class_)
2009 0 : return InliningStatus_NotInlined;
2010 :
2011 0 : if (strArg->mightBeType(MIRType::Object))
2012 0 : return InliningStatus_NotInlined;
2013 :
2014 0 : if (lastIndexArg->type() != MIRType::Int32)
2015 0 : return InliningStatus_NotInlined;
2016 :
2017 0 : JSContext* cx = GetJitContext()->cx;
2018 0 : if (!cx->compartment()->jitCompartment()->ensureRegExpTesterStubExists(cx)) {
2019 0 : cx->clearPendingException(); // OOM or overrecursion.
2020 0 : return InliningStatus_NotInlined;
2021 : }
2022 :
2023 0 : callInfo.setImplicitlyUsedUnchecked();
2024 :
2025 0 : MInstruction* tester = MRegExpTester::New(alloc(), rxArg, strArg, lastIndexArg);
2026 0 : current->add(tester);
2027 0 : current->push(tester);
2028 :
2029 0 : MOZ_TRY(resumeAfter(tester));
2030 0 : return InliningStatus_Inlined;
2031 : }
2032 :
2033 : IonBuilder::InliningResult
2034 0 : IonBuilder::inlineIsRegExpObject(CallInfo& callInfo)
2035 : {
2036 0 : if (callInfo.constructing() || callInfo.argc() != 1) {
2037 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2038 0 : return InliningStatus_NotInlined;
2039 : }
2040 :
2041 0 : if (getInlineReturnType() != MIRType::Boolean)
2042 0 : return InliningStatus_NotInlined;
2043 :
2044 0 : MDefinition* arg = callInfo.getArg(0);
2045 :
2046 : bool isRegExpObject;
2047 0 : if (!arg->mightBeType(MIRType::Object)) {
2048 0 : isRegExpObject = false;
2049 : } else {
2050 0 : if (arg->type() != MIRType::Object)
2051 0 : return InliningStatus_NotInlined;
2052 :
2053 0 : TemporaryTypeSet* types = arg->resultTypeSet();
2054 0 : const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
2055 0 : if (!clasp || clasp->isProxy())
2056 0 : return InliningStatus_NotInlined;
2057 :
2058 0 : isRegExpObject = (clasp == &RegExpObject::class_);
2059 : }
2060 :
2061 0 : pushConstant(BooleanValue(isRegExpObject));
2062 :
2063 0 : callInfo.setImplicitlyUsedUnchecked();
2064 0 : return InliningStatus_Inlined;
2065 : }
2066 :
2067 : IonBuilder::InliningResult
2068 0 : IonBuilder::inlineRegExpPrototypeOptimizable(CallInfo& callInfo)
2069 : {
2070 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
2071 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2072 0 : return InliningStatus_NotInlined;
2073 : }
2074 :
2075 0 : MDefinition* protoArg = callInfo.getArg(0);
2076 :
2077 0 : if (protoArg->type() != MIRType::Object)
2078 0 : return InliningStatus_NotInlined;
2079 :
2080 0 : if (getInlineReturnType() != MIRType::Boolean)
2081 0 : return InliningStatus_NotInlined;
2082 :
2083 0 : callInfo.setImplicitlyUsedUnchecked();
2084 :
2085 0 : MInstruction* opt = MRegExpPrototypeOptimizable::New(alloc(), protoArg);
2086 0 : current->add(opt);
2087 0 : current->push(opt);
2088 :
2089 0 : return InliningStatus_Inlined;
2090 : }
2091 :
2092 : IonBuilder::InliningResult
2093 0 : IonBuilder::inlineRegExpInstanceOptimizable(CallInfo& callInfo)
2094 : {
2095 0 : if (callInfo.argc() != 2 || callInfo.constructing()) {
2096 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2097 0 : return InliningStatus_NotInlined;
2098 : }
2099 :
2100 0 : MDefinition* rxArg = callInfo.getArg(0);
2101 0 : MDefinition* protoArg = callInfo.getArg(1);
2102 :
2103 0 : if (rxArg->type() != MIRType::Object)
2104 0 : return InliningStatus_NotInlined;
2105 :
2106 0 : if (protoArg->type() != MIRType::Object)
2107 0 : return InliningStatus_NotInlined;
2108 :
2109 0 : if (getInlineReturnType() != MIRType::Boolean)
2110 0 : return InliningStatus_NotInlined;
2111 :
2112 0 : callInfo.setImplicitlyUsedUnchecked();
2113 :
2114 0 : MInstruction* opt = MRegExpInstanceOptimizable::New(alloc(), rxArg, protoArg);
2115 0 : current->add(opt);
2116 0 : current->push(opt);
2117 :
2118 0 : return InliningStatus_Inlined;
2119 : }
2120 :
2121 : IonBuilder::InliningResult
2122 0 : IonBuilder::inlineGetFirstDollarIndex(CallInfo& callInfo)
2123 : {
2124 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
2125 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2126 0 : return InliningStatus_NotInlined;
2127 : }
2128 :
2129 0 : MDefinition* strArg = callInfo.getArg(0);
2130 :
2131 0 : if (strArg->type() != MIRType::String)
2132 0 : return InliningStatus_NotInlined;
2133 :
2134 0 : if (getInlineReturnType() != MIRType::Int32)
2135 0 : return InliningStatus_NotInlined;
2136 :
2137 0 : callInfo.setImplicitlyUsedUnchecked();
2138 :
2139 0 : MInstruction* ins = MGetFirstDollarIndex::New(alloc(), strArg);
2140 0 : current->add(ins);
2141 0 : current->push(ins);
2142 :
2143 0 : return InliningStatus_Inlined;
2144 : }
2145 :
2146 : IonBuilder::InliningResult
2147 0 : IonBuilder::inlineStringReplaceString(CallInfo& callInfo)
2148 : {
2149 0 : if (callInfo.argc() != 3 || callInfo.constructing()) {
2150 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2151 0 : return InliningStatus_NotInlined;
2152 : }
2153 :
2154 0 : if (getInlineReturnType() != MIRType::String)
2155 0 : return InliningStatus_NotInlined;
2156 :
2157 0 : MDefinition* strArg = callInfo.getArg(0);
2158 0 : MDefinition* patArg = callInfo.getArg(1);
2159 0 : MDefinition* replArg = callInfo.getArg(2);
2160 :
2161 0 : if (strArg->type() != MIRType::String)
2162 0 : return InliningStatus_NotInlined;
2163 :
2164 0 : if (patArg->type() != MIRType::String)
2165 0 : return InliningStatus_NotInlined;
2166 :
2167 0 : if (replArg->type() != MIRType::String)
2168 0 : return InliningStatus_NotInlined;
2169 :
2170 0 : callInfo.setImplicitlyUsedUnchecked();
2171 :
2172 0 : MInstruction* cte = MStringReplace::New(alloc(), strArg, patArg, replArg);
2173 0 : current->add(cte);
2174 0 : current->push(cte);
2175 0 : if (cte->isEffectful())
2176 0 : MOZ_TRY(resumeAfter(cte));
2177 0 : return InliningStatus_Inlined;
2178 : }
2179 :
2180 : IonBuilder::InliningResult
2181 0 : IonBuilder::inlineSubstringKernel(CallInfo& callInfo)
2182 : {
2183 0 : MOZ_ASSERT(callInfo.argc() == 3);
2184 0 : MOZ_ASSERT(!callInfo.constructing());
2185 :
2186 : // Return: String.
2187 0 : if (getInlineReturnType() != MIRType::String)
2188 0 : return InliningStatus_NotInlined;
2189 :
2190 : // Arg 0: String.
2191 0 : if (callInfo.getArg(0)->type() != MIRType::String)
2192 0 : return InliningStatus_NotInlined;
2193 :
2194 : // Arg 1: Int.
2195 0 : if (callInfo.getArg(1)->type() != MIRType::Int32)
2196 0 : return InliningStatus_NotInlined;
2197 :
2198 : // Arg 2: Int.
2199 0 : if (callInfo.getArg(2)->type() != MIRType::Int32)
2200 0 : return InliningStatus_NotInlined;
2201 :
2202 0 : callInfo.setImplicitlyUsedUnchecked();
2203 :
2204 0 : MSubstr* substr = MSubstr::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
2205 0 : callInfo.getArg(2));
2206 0 : current->add(substr);
2207 0 : current->push(substr);
2208 :
2209 0 : return InliningStatus_Inlined;
2210 : }
2211 :
2212 : IonBuilder::InliningResult
2213 0 : IonBuilder::inlineObjectCreate(CallInfo& callInfo)
2214 : {
2215 0 : if (callInfo.argc() != 1 || callInfo.constructing())
2216 0 : return InliningStatus_NotInlined;
2217 :
2218 0 : JSObject* templateObject = inspector->getTemplateObjectForNative(pc, obj_create);
2219 0 : if (!templateObject)
2220 0 : return InliningStatus_NotInlined;
2221 :
2222 0 : MOZ_ASSERT(templateObject->is<PlainObject>());
2223 0 : MOZ_ASSERT(!templateObject->isSingleton());
2224 :
2225 : // Ensure the argument matches the template object's prototype.
2226 0 : MDefinition* arg = callInfo.getArg(0);
2227 0 : if (JSObject* proto = templateObject->staticPrototype()) {
2228 0 : if (IsInsideNursery(proto))
2229 0 : return InliningStatus_NotInlined;
2230 :
2231 0 : TemporaryTypeSet* types = arg->resultTypeSet();
2232 0 : if (!types || types->maybeSingleton() != proto)
2233 0 : return InliningStatus_NotInlined;
2234 :
2235 0 : MOZ_ASSERT(types->getKnownMIRType() == MIRType::Object);
2236 : } else {
2237 0 : if (arg->type() != MIRType::Null)
2238 0 : return InliningStatus_NotInlined;
2239 : }
2240 :
2241 0 : callInfo.setImplicitlyUsedUnchecked();
2242 :
2243 0 : bool emitted = false;
2244 0 : MOZ_TRY(newObjectTryTemplateObject(&emitted, templateObject));
2245 :
2246 0 : MOZ_ASSERT(emitted);
2247 0 : return InliningStatus_Inlined;
2248 : }
2249 :
2250 : IonBuilder::InliningResult
2251 7 : IonBuilder::inlineHasClass(CallInfo& callInfo,
2252 : const Class* clasp1, const Class* clasp2,
2253 : const Class* clasp3, const Class* clasp4)
2254 : {
2255 7 : if (callInfo.constructing() || callInfo.argc() != 1) {
2256 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2257 0 : return InliningStatus_NotInlined;
2258 : }
2259 :
2260 7 : if (callInfo.getArg(0)->type() != MIRType::Object)
2261 0 : return InliningStatus_NotInlined;
2262 7 : if (getInlineReturnType() != MIRType::Boolean)
2263 0 : return InliningStatus_NotInlined;
2264 :
2265 7 : TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
2266 7 : const Class* knownClass = types ? types->getKnownClass(constraints()) : nullptr;
2267 7 : if (knownClass) {
2268 21 : pushConstant(BooleanValue(knownClass == clasp1 ||
2269 0 : knownClass == clasp2 ||
2270 7 : knownClass == clasp3 ||
2271 14 : knownClass == clasp4));
2272 : } else {
2273 0 : MHasClass* hasClass1 = MHasClass::New(alloc(), callInfo.getArg(0), clasp1);
2274 0 : current->add(hasClass1);
2275 :
2276 0 : if (!clasp2 && !clasp3 && !clasp4) {
2277 0 : current->push(hasClass1);
2278 : } else {
2279 0 : const Class* remaining[] = { clasp2, clasp3, clasp4 };
2280 0 : MDefinition* last = hasClass1;
2281 0 : for (size_t i = 0; i < ArrayLength(remaining); i++) {
2282 0 : MHasClass* hasClass = MHasClass::New(alloc(), callInfo.getArg(0), remaining[i]);
2283 0 : current->add(hasClass);
2284 0 : MBitOr* either = MBitOr::New(alloc(), last, hasClass);
2285 0 : either->infer(inspector, pc);
2286 0 : current->add(either);
2287 0 : last = either;
2288 : }
2289 :
2290 0 : MDefinition* result = convertToBoolean(last);
2291 0 : current->push(result);
2292 : }
2293 : }
2294 :
2295 7 : callInfo.setImplicitlyUsedUnchecked();
2296 7 : return InliningStatus_Inlined;
2297 : }
2298 :
2299 : IonBuilder::InliningResult
2300 0 : IonBuilder::inlineGetNextEntryForIterator(CallInfo& callInfo, MGetNextEntryForIterator::Mode mode)
2301 : {
2302 0 : if (callInfo.argc() != 2 || callInfo.constructing()) {
2303 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2304 0 : return InliningStatus_NotInlined;
2305 : }
2306 :
2307 0 : MDefinition* iterArg = callInfo.getArg(0);
2308 0 : MDefinition* resultArg = callInfo.getArg(1);
2309 :
2310 0 : if (iterArg->type() != MIRType::Object)
2311 0 : return InliningStatus_NotInlined;
2312 :
2313 0 : TemporaryTypeSet* iterTypes = iterArg->resultTypeSet();
2314 0 : const Class* iterClasp = iterTypes ? iterTypes->getKnownClass(constraints()) : nullptr;
2315 0 : if (mode == MGetNextEntryForIterator::Map) {
2316 0 : if (iterClasp != &MapIteratorObject::class_)
2317 0 : return InliningStatus_NotInlined;
2318 : } else {
2319 0 : MOZ_ASSERT(mode == MGetNextEntryForIterator::Set);
2320 :
2321 0 : if (iterClasp != &SetIteratorObject::class_)
2322 0 : return InliningStatus_NotInlined;
2323 : }
2324 :
2325 0 : if (resultArg->type() != MIRType::Object)
2326 0 : return InliningStatus_NotInlined;
2327 :
2328 0 : TemporaryTypeSet* resultTypes = resultArg->resultTypeSet();
2329 0 : const Class* resultClasp = resultTypes ? resultTypes->getKnownClass(constraints()) : nullptr;
2330 0 : if (resultClasp != &ArrayObject::class_)
2331 0 : return InliningStatus_NotInlined;
2332 :
2333 0 : callInfo.setImplicitlyUsedUnchecked();
2334 :
2335 0 : MInstruction* next = MGetNextEntryForIterator::New(alloc(), iterArg, resultArg, mode);
2336 0 : current->add(next);
2337 0 : current->push(next);
2338 :
2339 0 : MOZ_TRY(resumeAfter(next));
2340 0 : return InliningStatus_Inlined;
2341 : }
2342 :
2343 : static bool
2344 0 : IsArrayBufferObject(CompilerConstraintList* constraints, MDefinition* def)
2345 : {
2346 0 : MOZ_ASSERT(def->type() == MIRType::Object);
2347 :
2348 0 : TemporaryTypeSet* types = def->resultTypeSet();
2349 0 : if (!types)
2350 0 : return false;
2351 :
2352 0 : return types->getKnownClass(constraints) == &ArrayBufferObject::class_;
2353 : }
2354 :
2355 : IonBuilder::InliningResult
2356 0 : IonBuilder::inlineArrayBufferByteLength(CallInfo& callInfo)
2357 : {
2358 0 : MOZ_ASSERT(!callInfo.constructing());
2359 0 : MOZ_ASSERT(callInfo.argc() == 1);
2360 :
2361 0 : MDefinition* objArg = callInfo.getArg(0);
2362 0 : if (objArg->type() != MIRType::Object)
2363 0 : return InliningStatus_NotInlined;
2364 0 : if (getInlineReturnType() != MIRType::Int32)
2365 0 : return InliningStatus_NotInlined;
2366 :
2367 0 : MInstruction* ins = addArrayBufferByteLength(objArg);
2368 0 : current->push(ins);
2369 :
2370 0 : callInfo.setImplicitlyUsedUnchecked();
2371 0 : return InliningStatus_Inlined;
2372 : }
2373 :
2374 : IonBuilder::InliningResult
2375 0 : IonBuilder::inlinePossiblyWrappedArrayBufferByteLength(CallInfo& callInfo)
2376 : {
2377 0 : MOZ_ASSERT(!callInfo.constructing());
2378 0 : MOZ_ASSERT(callInfo.argc() == 1);
2379 :
2380 0 : MDefinition* objArg = callInfo.getArg(0);
2381 0 : if (objArg->type() != MIRType::Object)
2382 0 : return InliningStatus_NotInlined;
2383 0 : if (getInlineReturnType() != MIRType::Int32)
2384 0 : return InliningStatus_NotInlined;
2385 :
2386 0 : if (!IsArrayBufferObject(constraints(), objArg))
2387 0 : return InliningStatus_NotInlined;
2388 :
2389 0 : MInstruction* ins = addArrayBufferByteLength(objArg);
2390 0 : current->push(ins);
2391 :
2392 0 : callInfo.setImplicitlyUsedUnchecked();
2393 0 : return InliningStatus_Inlined;
2394 : }
2395 :
2396 : IonBuilder::InliningResult
2397 0 : IonBuilder::inlineTypedArray(CallInfo& callInfo, Native native)
2398 : {
2399 0 : if (!callInfo.constructing()) {
2400 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2401 0 : return InliningStatus_NotInlined;
2402 : }
2403 :
2404 0 : if (getInlineReturnType() != MIRType::Object)
2405 0 : return InliningStatus_NotInlined;
2406 0 : if (callInfo.argc() != 1)
2407 0 : return InliningStatus_NotInlined;
2408 :
2409 0 : MDefinition* arg = callInfo.getArg(0);
2410 :
2411 0 : if (arg->type() != MIRType::Int32)
2412 0 : return InliningStatus_NotInlined;
2413 :
2414 0 : JSObject* templateObject = inspector->getTemplateObjectForNative(pc, native);
2415 :
2416 0 : if (!templateObject) {
2417 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoTemplateObj);
2418 0 : return InliningStatus_NotInlined;
2419 : }
2420 :
2421 0 : MOZ_ASSERT(templateObject->is<TypedArrayObject>());
2422 0 : TypedArrayObject* obj = &templateObject->as<TypedArrayObject>();
2423 :
2424 : // Do not optimize when we see a template object with a singleton type,
2425 : // since it hits at most once.
2426 0 : if (templateObject->isSingleton())
2427 0 : return InliningStatus_NotInlined;
2428 :
2429 0 : MInstruction* ins = nullptr;
2430 :
2431 0 : if (!arg->isConstant()) {
2432 0 : callInfo.setImplicitlyUsedUnchecked();
2433 0 : ins = MNewTypedArrayDynamicLength::New(alloc(), constraints(), templateObject,
2434 : templateObject->group()->initialHeap(constraints()),
2435 0 : arg);
2436 : } else {
2437 : // Negative lengths must throw a RangeError. (We don't track that this
2438 : // might have previously thrown, when determining whether to inline, so we
2439 : // have to deal with this error case when inlining.)
2440 0 : int32_t providedLen = arg->maybeConstantValue()->toInt32();
2441 0 : if (providedLen <= 0)
2442 0 : return InliningStatus_NotInlined;
2443 :
2444 0 : uint32_t len = AssertedCast<uint32_t>(providedLen);
2445 :
2446 0 : if (obj->length() != len)
2447 0 : return InliningStatus_NotInlined;
2448 :
2449 0 : callInfo.setImplicitlyUsedUnchecked();
2450 0 : MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), obj);
2451 0 : current->add(templateConst);
2452 0 : ins = MNewTypedArray::New(alloc(), constraints(), templateConst,
2453 0 : obj->group()->initialHeap(constraints()));
2454 : }
2455 :
2456 0 : current->add(ins);
2457 0 : current->push(ins);
2458 0 : MOZ_TRY(resumeAfter(ins));
2459 0 : return InliningStatus_Inlined;
2460 : }
2461 :
2462 : IonBuilder::InliningResult
2463 7 : IonBuilder::inlineIsTypedArrayHelper(CallInfo& callInfo, WrappingBehavior wrappingBehavior)
2464 : {
2465 7 : MOZ_ASSERT(!callInfo.constructing());
2466 7 : MOZ_ASSERT(callInfo.argc() == 1);
2467 :
2468 7 : if (callInfo.getArg(0)->type() != MIRType::Object)
2469 0 : return InliningStatus_NotInlined;
2470 7 : if (getInlineReturnType() != MIRType::Boolean)
2471 0 : return InliningStatus_NotInlined;
2472 :
2473 : // The test is elaborate: in-line only if there is exact
2474 : // information.
2475 :
2476 7 : TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
2477 7 : if (!types)
2478 0 : return InliningStatus_NotInlined;
2479 :
2480 7 : bool result = false;
2481 7 : switch (types->forAllClasses(constraints(), IsTypedArrayClass)) {
2482 : case TemporaryTypeSet::ForAllResult::ALL_FALSE:
2483 : // Wrapped typed arrays won't appear to be typed arrays per a
2484 : // |forAllClasses| query. If wrapped typed arrays are to be considered
2485 : // typed arrays, a negative answer is not conclusive. Don't inline in
2486 : // that case.
2487 0 : if (wrappingBehavior == AllowWrappedTypedArrays) {
2488 0 : switch (types->forAllClasses(constraints(), IsProxyClass)) {
2489 : case TemporaryTypeSet::ForAllResult::ALL_FALSE:
2490 : case TemporaryTypeSet::ForAllResult::EMPTY:
2491 0 : break;
2492 : case TemporaryTypeSet::ForAllResult::ALL_TRUE:
2493 : case TemporaryTypeSet::ForAllResult::MIXED:
2494 0 : return InliningStatus_NotInlined;
2495 : }
2496 : }
2497 :
2498 : MOZ_FALLTHROUGH;
2499 :
2500 : case TemporaryTypeSet::ForAllResult::EMPTY:
2501 0 : result = false;
2502 0 : break;
2503 :
2504 : case TemporaryTypeSet::ForAllResult::ALL_TRUE:
2505 0 : result = true;
2506 0 : break;
2507 :
2508 : case TemporaryTypeSet::ForAllResult::MIXED:
2509 7 : return InliningStatus_NotInlined;
2510 : }
2511 :
2512 0 : pushConstant(BooleanValue(result));
2513 :
2514 0 : callInfo.setImplicitlyUsedUnchecked();
2515 0 : return InliningStatus_Inlined;
2516 : }
2517 :
2518 : IonBuilder::InliningResult
2519 0 : IonBuilder::inlineIsTypedArray(CallInfo& callInfo)
2520 : {
2521 0 : return inlineIsTypedArrayHelper(callInfo, RejectWrappedTypedArrays);
2522 : }
2523 :
2524 : IonBuilder::InliningResult
2525 7 : IonBuilder::inlineIsPossiblyWrappedTypedArray(CallInfo& callInfo)
2526 : {
2527 7 : return inlineIsTypedArrayHelper(callInfo, AllowWrappedTypedArrays);
2528 : }
2529 :
2530 : static bool
2531 0 : IsTypedArrayObject(CompilerConstraintList* constraints, MDefinition* def)
2532 : {
2533 0 : MOZ_ASSERT(def->type() == MIRType::Object);
2534 :
2535 0 : TemporaryTypeSet* types = def->resultTypeSet();
2536 0 : if (!types)
2537 0 : return false;
2538 :
2539 0 : return types->forAllClasses(constraints, IsTypedArrayClass) ==
2540 0 : TemporaryTypeSet::ForAllResult::ALL_TRUE;
2541 : }
2542 :
2543 : IonBuilder::InliningResult
2544 0 : IonBuilder::inlinePossiblyWrappedTypedArrayLength(CallInfo& callInfo)
2545 : {
2546 0 : MOZ_ASSERT(!callInfo.constructing());
2547 0 : MOZ_ASSERT(callInfo.argc() == 1);
2548 0 : if (callInfo.getArg(0)->type() != MIRType::Object)
2549 0 : return InliningStatus_NotInlined;
2550 0 : if (getInlineReturnType() != MIRType::Int32)
2551 0 : return InliningStatus_NotInlined;
2552 :
2553 0 : if (!IsTypedArrayObject(constraints(), callInfo.getArg(0)))
2554 0 : return InliningStatus_NotInlined;
2555 :
2556 0 : MInstruction* length = addTypedArrayLength(callInfo.getArg(0));
2557 0 : current->push(length);
2558 :
2559 0 : callInfo.setImplicitlyUsedUnchecked();
2560 0 : return InliningStatus_Inlined;
2561 : }
2562 :
2563 : IonBuilder::InliningResult
2564 0 : IonBuilder::inlineTypedArrayLength(CallInfo& callInfo)
2565 : {
2566 0 : return inlinePossiblyWrappedTypedArrayLength(callInfo);
2567 : }
2568 :
2569 : IonBuilder::InliningResult
2570 0 : IonBuilder::inlineSetDisjointTypedElements(CallInfo& callInfo)
2571 : {
2572 0 : MOZ_ASSERT(!callInfo.constructing());
2573 0 : MOZ_ASSERT(callInfo.argc() == 3);
2574 :
2575 : // Initial argument requirements.
2576 :
2577 0 : MDefinition* target = callInfo.getArg(0);
2578 0 : if (target->type() != MIRType::Object)
2579 0 : return InliningStatus_NotInlined;
2580 :
2581 0 : if (getInlineReturnType() != MIRType::Undefined)
2582 0 : return InliningStatus_NotInlined;
2583 :
2584 0 : MDefinition* targetOffset = callInfo.getArg(1);
2585 0 : MOZ_ASSERT(targetOffset->type() == MIRType::Int32);
2586 :
2587 0 : MDefinition* sourceTypedArray = callInfo.getArg(2);
2588 0 : if (sourceTypedArray->type() != MIRType::Object)
2589 0 : return InliningStatus_NotInlined;
2590 :
2591 : // Only attempt to optimize if |target| and |sourceTypedArray| are both
2592 : // definitely typed arrays. (The former always is. The latter is not,
2593 : // necessarily, because of wrappers.)
2594 0 : if (!IsTypedArrayObject(constraints(), target) ||
2595 0 : !IsTypedArrayObject(constraints(), sourceTypedArray))
2596 : {
2597 0 : return InliningStatus_NotInlined;
2598 : }
2599 :
2600 0 : auto sets = MSetDisjointTypedElements::New(alloc(), target, targetOffset, sourceTypedArray);
2601 0 : current->add(sets);
2602 :
2603 0 : pushConstant(UndefinedValue());
2604 :
2605 0 : MOZ_TRY(resumeAfter(sets));
2606 0 : callInfo.setImplicitlyUsedUnchecked();
2607 0 : return InliningStatus_Inlined;
2608 : }
2609 :
2610 : IonBuilder::InliningResult
2611 0 : IonBuilder::inlineObjectIsTypeDescr(CallInfo& callInfo)
2612 : {
2613 0 : if (callInfo.constructing() || callInfo.argc() != 1) {
2614 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2615 0 : return InliningStatus_NotInlined;
2616 : }
2617 :
2618 0 : if (callInfo.getArg(0)->type() != MIRType::Object)
2619 0 : return InliningStatus_NotInlined;
2620 0 : if (getInlineReturnType() != MIRType::Boolean)
2621 0 : return InliningStatus_NotInlined;
2622 :
2623 : // The test is elaborate: in-line only if there is exact
2624 : // information.
2625 :
2626 0 : TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
2627 0 : if (!types)
2628 0 : return InliningStatus_NotInlined;
2629 :
2630 0 : bool result = false;
2631 0 : switch (types->forAllClasses(constraints(), IsTypeDescrClass)) {
2632 : case TemporaryTypeSet::ForAllResult::ALL_FALSE:
2633 : case TemporaryTypeSet::ForAllResult::EMPTY:
2634 0 : result = false;
2635 0 : break;
2636 : case TemporaryTypeSet::ForAllResult::ALL_TRUE:
2637 0 : result = true;
2638 0 : break;
2639 : case TemporaryTypeSet::ForAllResult::MIXED:
2640 0 : return InliningStatus_NotInlined;
2641 : }
2642 :
2643 0 : pushConstant(BooleanValue(result));
2644 :
2645 0 : callInfo.setImplicitlyUsedUnchecked();
2646 0 : return InliningStatus_Inlined;
2647 : }
2648 :
2649 : IonBuilder::InliningResult
2650 0 : IonBuilder::inlineSetTypedObjectOffset(CallInfo& callInfo)
2651 : {
2652 0 : if (callInfo.argc() != 2 || callInfo.constructing()) {
2653 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2654 0 : return InliningStatus_NotInlined;
2655 : }
2656 :
2657 0 : MDefinition* typedObj = callInfo.getArg(0);
2658 0 : MDefinition* offset = callInfo.getArg(1);
2659 :
2660 : // Return type should be undefined or something wacky is going on.
2661 0 : if (getInlineReturnType() != MIRType::Undefined)
2662 0 : return InliningStatus_NotInlined;
2663 :
2664 : // Check typedObj is a, well, typed object. Go ahead and use TI
2665 : // data. If this check should fail, that is almost certainly a bug
2666 : // in self-hosted code -- either because it's not being careful
2667 : // with TI or because of something else -- but we'll just let it
2668 : // fall through to the SetTypedObjectOffset intrinsic in such
2669 : // cases.
2670 0 : TemporaryTypeSet* types = typedObj->resultTypeSet();
2671 0 : if (typedObj->type() != MIRType::Object || !types)
2672 0 : return InliningStatus_NotInlined;
2673 0 : switch (types->forAllClasses(constraints(), IsTypedObjectClass)) {
2674 : case TemporaryTypeSet::ForAllResult::ALL_FALSE:
2675 : case TemporaryTypeSet::ForAllResult::EMPTY:
2676 : case TemporaryTypeSet::ForAllResult::MIXED:
2677 0 : return InliningStatus_NotInlined;
2678 : case TemporaryTypeSet::ForAllResult::ALL_TRUE:
2679 0 : break;
2680 : }
2681 :
2682 : // Check type of offset argument is an integer.
2683 0 : if (offset->type() != MIRType::Int32)
2684 0 : return InliningStatus_NotInlined;
2685 :
2686 0 : callInfo.setImplicitlyUsedUnchecked();
2687 0 : MInstruction* ins = MSetTypedObjectOffset::New(alloc(), typedObj, offset);
2688 0 : current->add(ins);
2689 0 : current->push(ins);
2690 0 : return InliningStatus_Inlined;
2691 : }
2692 :
2693 : IonBuilder::InliningResult
2694 26 : IonBuilder::inlineUnsafeSetReservedSlot(CallInfo& callInfo)
2695 : {
2696 26 : if (callInfo.argc() != 3 || callInfo.constructing()) {
2697 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2698 0 : return InliningStatus_NotInlined;
2699 : }
2700 26 : if (getInlineReturnType() != MIRType::Undefined)
2701 0 : return InliningStatus_NotInlined;
2702 26 : if (callInfo.getArg(0)->type() != MIRType::Object)
2703 0 : return InliningStatus_NotInlined;
2704 26 : if (callInfo.getArg(1)->type() != MIRType::Int32)
2705 0 : return InliningStatus_NotInlined;
2706 :
2707 : // Don't inline if we don't have a constant slot.
2708 26 : MDefinition* arg = callInfo.getArg(1);
2709 26 : if (!arg->isConstant())
2710 0 : return InliningStatus_NotInlined;
2711 26 : uint32_t slot = uint32_t(arg->toConstant()->toInt32());
2712 :
2713 26 : callInfo.setImplicitlyUsedUnchecked();
2714 :
2715 : MStoreFixedSlot* store =
2716 26 : MStoreFixedSlot::NewBarriered(alloc(), callInfo.getArg(0), slot, callInfo.getArg(2));
2717 26 : current->add(store);
2718 26 : current->push(store);
2719 :
2720 26 : if (NeedsPostBarrier(callInfo.getArg(2)))
2721 4 : current->add(MPostWriteBarrier::New(alloc(), callInfo.getArg(0), callInfo.getArg(2)));
2722 :
2723 26 : return InliningStatus_Inlined;
2724 : }
2725 :
2726 : IonBuilder::InliningResult
2727 21 : IonBuilder::inlineUnsafeGetReservedSlot(CallInfo& callInfo, MIRType knownValueType)
2728 : {
2729 21 : if (callInfo.argc() != 2 || callInfo.constructing()) {
2730 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2731 0 : return InliningStatus_NotInlined;
2732 : }
2733 21 : if (callInfo.getArg(0)->type() != MIRType::Object)
2734 0 : return InliningStatus_NotInlined;
2735 21 : if (callInfo.getArg(1)->type() != MIRType::Int32)
2736 0 : return InliningStatus_NotInlined;
2737 :
2738 : // Don't inline if we don't have a constant slot.
2739 21 : MDefinition* arg = callInfo.getArg(1);
2740 21 : if (!arg->isConstant())
2741 0 : return InliningStatus_NotInlined;
2742 21 : uint32_t slot = uint32_t(arg->toConstant()->toInt32());
2743 :
2744 21 : callInfo.setImplicitlyUsedUnchecked();
2745 :
2746 21 : MLoadFixedSlot* load = MLoadFixedSlot::New(alloc(), callInfo.getArg(0), slot);
2747 21 : current->add(load);
2748 21 : current->push(load);
2749 21 : if (knownValueType != MIRType::Value) {
2750 : // We know what type we have in this slot. Assert that this is in fact
2751 : // what we've seen coming from this slot in the past, then tell the
2752 : // MLoadFixedSlot about its result type. That will make us do an
2753 : // infallible unbox as part of the slot load and then we'll barrier on
2754 : // the unbox result. That way the type barrier code won't end up doing
2755 : // MIRType checks and conditional unboxing.
2756 7 : MOZ_ASSERT_IF(!getInlineReturnTypeSet()->empty(),
2757 : getInlineReturnType() == knownValueType);
2758 7 : load->setResultType(knownValueType);
2759 : }
2760 :
2761 : // We don't track reserved slot types, so always emit a barrier.
2762 21 : MOZ_TRY(pushTypeBarrier(load, getInlineReturnTypeSet(), BarrierKind::TypeSet));
2763 21 : return InliningStatus_Inlined;
2764 : }
2765 :
2766 : IonBuilder::InliningResult
2767 1 : IonBuilder::inlineIsCallable(CallInfo& callInfo)
2768 : {
2769 1 : if (callInfo.argc() != 1 || callInfo.constructing()) {
2770 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2771 0 : return InliningStatus_NotInlined;
2772 : }
2773 :
2774 1 : if (getInlineReturnType() != MIRType::Boolean)
2775 0 : return InliningStatus_NotInlined;
2776 :
2777 1 : MDefinition* arg = callInfo.getArg(0);
2778 : // Do not inline if the type of arg is neither primitive nor object.
2779 1 : if (arg->type() > MIRType::Object)
2780 0 : return InliningStatus_NotInlined;
2781 :
2782 : // Try inlining with constant true/false: only objects may be callable at
2783 : // all, and if we know the class check if it is callable.
2784 1 : bool isCallableKnown = false;
2785 : bool isCallableConstant;
2786 1 : if (arg->type() != MIRType::Object) {
2787 : // Primitive (including undefined and null).
2788 0 : isCallableKnown = true;
2789 0 : isCallableConstant = false;
2790 : } else {
2791 1 : TemporaryTypeSet* types = arg->resultTypeSet();
2792 1 : const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
2793 1 : if (clasp && !clasp->isProxy()) {
2794 1 : isCallableKnown = true;
2795 1 : isCallableConstant = clasp->nonProxyCallable();
2796 : }
2797 : }
2798 :
2799 1 : callInfo.setImplicitlyUsedUnchecked();
2800 :
2801 1 : if (isCallableKnown) {
2802 1 : MConstant* constant = MConstant::New(alloc(), BooleanValue(isCallableConstant));
2803 1 : current->add(constant);
2804 1 : current->push(constant);
2805 1 : return InliningStatus_Inlined;
2806 : }
2807 :
2808 0 : MIsCallable* isCallable = MIsCallable::New(alloc(), arg);
2809 0 : current->add(isCallable);
2810 0 : current->push(isCallable);
2811 :
2812 0 : return InliningStatus_Inlined;
2813 : }
2814 :
2815 : IonBuilder::InliningResult
2816 0 : IonBuilder::inlineIsConstructor(CallInfo& callInfo)
2817 : {
2818 0 : MOZ_ASSERT(!callInfo.constructing());
2819 0 : MOZ_ASSERT(callInfo.argc() == 1);
2820 :
2821 0 : if (getInlineReturnType() != MIRType::Boolean)
2822 0 : return InliningStatus_NotInlined;
2823 0 : if (callInfo.getArg(0)->type() != MIRType::Object)
2824 0 : return InliningStatus_NotInlined;
2825 :
2826 0 : callInfo.setImplicitlyUsedUnchecked();
2827 :
2828 0 : MIsConstructor* ins = MIsConstructor::New(alloc(), callInfo.getArg(0));
2829 0 : current->add(ins);
2830 0 : current->push(ins);
2831 :
2832 0 : return InliningStatus_Inlined;
2833 : }
2834 :
2835 : IonBuilder::InliningResult
2836 7 : IonBuilder::inlineIsObject(CallInfo& callInfo)
2837 : {
2838 7 : if (callInfo.argc() != 1 || callInfo.constructing()) {
2839 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2840 0 : return InliningStatus_NotInlined;
2841 : }
2842 7 : if (getInlineReturnType() != MIRType::Boolean)
2843 0 : return InliningStatus_NotInlined;
2844 :
2845 7 : callInfo.setImplicitlyUsedUnchecked();
2846 7 : if (callInfo.getArg(0)->type() == MIRType::Object) {
2847 7 : pushConstant(BooleanValue(true));
2848 : } else {
2849 0 : MIsObject* isObject = MIsObject::New(alloc(), callInfo.getArg(0));
2850 0 : current->add(isObject);
2851 0 : current->push(isObject);
2852 : }
2853 7 : return InliningStatus_Inlined;
2854 : }
2855 :
2856 : IonBuilder::InliningResult
2857 4 : IonBuilder::inlineToObject(CallInfo& callInfo)
2858 : {
2859 4 : if (callInfo.argc() != 1 || callInfo.constructing()) {
2860 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2861 0 : return InliningStatus_NotInlined;
2862 : }
2863 :
2864 : // If we know the input type is an object, nop ToObject.
2865 4 : if (getInlineReturnType() != MIRType::Object)
2866 0 : return InliningStatus_NotInlined;
2867 4 : if (callInfo.getArg(0)->type() != MIRType::Object)
2868 0 : return InliningStatus_NotInlined;
2869 :
2870 4 : callInfo.setImplicitlyUsedUnchecked();
2871 4 : MDefinition* object = callInfo.getArg(0);
2872 :
2873 4 : current->push(object);
2874 4 : return InliningStatus_Inlined;
2875 : }
2876 :
2877 : IonBuilder::InliningResult
2878 0 : IonBuilder::inlineIsWrappedArrayConstructor(CallInfo& callInfo)
2879 : {
2880 0 : if (callInfo.constructing() || callInfo.argc() != 1) {
2881 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2882 0 : return InliningStatus_NotInlined;
2883 : }
2884 :
2885 0 : if (getInlineReturnType() != MIRType::Boolean)
2886 0 : return InliningStatus_NotInlined;
2887 0 : MDefinition* arg = callInfo.getArg(0);
2888 0 : if (arg->type() != MIRType::Object)
2889 0 : return InliningStatus_NotInlined;
2890 :
2891 0 : TemporaryTypeSet* types = arg->resultTypeSet();
2892 0 : switch (types->forAllClasses(constraints(), IsProxyClass)) {
2893 : case TemporaryTypeSet::ForAllResult::ALL_FALSE:
2894 0 : break;
2895 : case TemporaryTypeSet::ForAllResult::EMPTY:
2896 : case TemporaryTypeSet::ForAllResult::ALL_TRUE:
2897 : case TemporaryTypeSet::ForAllResult::MIXED:
2898 0 : return InliningStatus_NotInlined;
2899 : }
2900 :
2901 0 : callInfo.setImplicitlyUsedUnchecked();
2902 :
2903 : // Inline only if argument is absolutely *not* a Proxy.
2904 0 : pushConstant(BooleanValue(false));
2905 0 : return InliningStatus_Inlined;
2906 : }
2907 :
2908 : IonBuilder::InliningResult
2909 8 : IonBuilder::inlineToInteger(CallInfo& callInfo)
2910 : {
2911 8 : if (callInfo.argc() != 1 || callInfo.constructing()) {
2912 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
2913 0 : return InliningStatus_NotInlined;
2914 : }
2915 :
2916 8 : MDefinition* input = callInfo.getArg(0);
2917 :
2918 : // Only optimize cases where input contains only number, null or boolean
2919 24 : if (input->mightBeType(MIRType::Object) ||
2920 16 : input->mightBeType(MIRType::String) ||
2921 16 : input->mightBeType(MIRType::Symbol) ||
2922 24 : input->mightBeType(MIRType::Undefined) ||
2923 8 : input->mightBeMagicType())
2924 : {
2925 0 : return InliningStatus_NotInlined;
2926 : }
2927 :
2928 8 : MOZ_ASSERT(input->type() == MIRType::Value || input->type() == MIRType::Null ||
2929 : input->type() == MIRType::Boolean || IsNumberType(input->type()));
2930 :
2931 : // Only optimize cases where output is int32
2932 8 : if (getInlineReturnType() != MIRType::Int32)
2933 0 : return InliningStatus_NotInlined;
2934 :
2935 8 : callInfo.setImplicitlyUsedUnchecked();
2936 :
2937 8 : MToInt32* toInt32 = MToInt32::New(alloc(), callInfo.getArg(0));
2938 8 : current->add(toInt32);
2939 8 : current->push(toInt32);
2940 8 : return InliningStatus_Inlined;
2941 : }
2942 :
2943 : IonBuilder::InliningResult
2944 0 : IonBuilder::inlineToString(CallInfo& callInfo)
2945 : {
2946 0 : if (callInfo.argc() != 1 || callInfo.constructing())
2947 0 : return InliningStatus_NotInlined;
2948 :
2949 0 : if (getInlineReturnType() != MIRType::String)
2950 0 : return InliningStatus_NotInlined;
2951 :
2952 0 : callInfo.setImplicitlyUsedUnchecked();
2953 0 : MToString* toString = MToString::New(alloc(), callInfo.getArg(0));
2954 0 : current->add(toString);
2955 0 : current->push(toString);
2956 0 : return InliningStatus_Inlined;
2957 : }
2958 :
2959 : IonBuilder::InliningResult
2960 0 : IonBuilder::inlineBailout(CallInfo& callInfo)
2961 : {
2962 0 : callInfo.setImplicitlyUsedUnchecked();
2963 :
2964 0 : current->add(MBail::New(alloc()));
2965 :
2966 0 : MConstant* undefined = MConstant::New(alloc(), UndefinedValue());
2967 0 : current->add(undefined);
2968 0 : current->push(undefined);
2969 0 : return InliningStatus_Inlined;
2970 : }
2971 :
2972 : IonBuilder::InliningResult
2973 0 : IonBuilder::inlineAssertFloat32(CallInfo& callInfo)
2974 : {
2975 0 : if (callInfo.argc() != 2)
2976 0 : return InliningStatus_NotInlined;
2977 :
2978 0 : MDefinition* secondArg = callInfo.getArg(1);
2979 :
2980 0 : MOZ_ASSERT(secondArg->type() == MIRType::Boolean);
2981 0 : MOZ_ASSERT(secondArg->isConstant());
2982 :
2983 0 : bool mustBeFloat32 = secondArg->toConstant()->toBoolean();
2984 0 : current->add(MAssertFloat32::New(alloc(), callInfo.getArg(0), mustBeFloat32));
2985 :
2986 0 : MConstant* undefined = MConstant::New(alloc(), UndefinedValue());
2987 0 : current->add(undefined);
2988 0 : current->push(undefined);
2989 0 : callInfo.setImplicitlyUsedUnchecked();
2990 0 : return InliningStatus_Inlined;
2991 : }
2992 :
2993 : IonBuilder::InliningResult
2994 0 : IonBuilder::inlineAssertRecoveredOnBailout(CallInfo& callInfo)
2995 : {
2996 0 : if (callInfo.argc() != 2)
2997 0 : return InliningStatus_NotInlined;
2998 :
2999 : // Don't assert for recovered instructions when recovering is disabled.
3000 0 : if (JitOptions.disableRecoverIns)
3001 0 : return InliningStatus_NotInlined;
3002 :
3003 0 : if (JitOptions.checkRangeAnalysis) {
3004 : // If we are checking the range of all instructions, then the guards
3005 : // inserted by Range Analysis prevent the use of recover
3006 : // instruction. Thus, we just disable these checks.
3007 0 : current->push(constant(UndefinedValue()));
3008 0 : callInfo.setImplicitlyUsedUnchecked();
3009 0 : return InliningStatus_Inlined;
3010 : }
3011 :
3012 0 : MDefinition* secondArg = callInfo.getArg(1);
3013 :
3014 0 : MOZ_ASSERT(secondArg->type() == MIRType::Boolean);
3015 0 : MOZ_ASSERT(secondArg->isConstant());
3016 :
3017 0 : bool mustBeRecovered = secondArg->toConstant()->toBoolean();
3018 : MAssertRecoveredOnBailout* assert =
3019 0 : MAssertRecoveredOnBailout::New(alloc(), callInfo.getArg(0), mustBeRecovered);
3020 0 : current->add(assert);
3021 0 : current->push(assert);
3022 :
3023 : // Create an instruction sequence which implies that the argument of the
3024 : // assertRecoveredOnBailout function would be encoded at least in one
3025 : // Snapshot.
3026 0 : MNop* nop = MNop::New(alloc());
3027 0 : current->add(nop);
3028 0 : MOZ_TRY(resumeAfter(nop));
3029 0 : current->add(MEncodeSnapshot::New(alloc()));
3030 :
3031 0 : current->pop();
3032 0 : current->push(constant(UndefinedValue()));
3033 0 : callInfo.setImplicitlyUsedUnchecked();
3034 0 : return InliningStatus_Inlined;
3035 : }
3036 :
3037 : IonBuilder::InliningResult
3038 0 : IonBuilder::inlineAtomicsCompareExchange(CallInfo& callInfo)
3039 : {
3040 0 : if (callInfo.argc() != 4 || callInfo.constructing()) {
3041 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
3042 0 : return InliningStatus_NotInlined;
3043 : }
3044 :
3045 : // These guards are desirable here and in subsequent atomics to
3046 : // avoid bad bailouts with MTruncateToInt32, see https://bugzilla.mozilla.org/show_bug.cgi?id=1141986#c20.
3047 0 : MDefinition* oldval = callInfo.getArg(2);
3048 0 : if (oldval->mightBeType(MIRType::Object) || oldval->mightBeType(MIRType::Symbol))
3049 0 : return InliningStatus_NotInlined;
3050 :
3051 0 : MDefinition* newval = callInfo.getArg(3);
3052 0 : if (newval->mightBeType(MIRType::Object) || newval->mightBeType(MIRType::Symbol))
3053 0 : return InliningStatus_NotInlined;
3054 :
3055 : Scalar::Type arrayType;
3056 0 : bool requiresCheck = false;
3057 0 : if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
3058 0 : return InliningStatus_NotInlined;
3059 :
3060 0 : callInfo.setImplicitlyUsedUnchecked();
3061 :
3062 : MInstruction* elements;
3063 : MDefinition* index;
3064 0 : atomicsCheckBounds(callInfo, &elements, &index);
3065 :
3066 0 : if (requiresCheck)
3067 0 : addSharedTypedArrayGuard(callInfo.getArg(0));
3068 :
3069 : MCompareExchangeTypedArrayElement* cas =
3070 0 : MCompareExchangeTypedArrayElement::New(alloc(), elements, index, arrayType, oldval, newval);
3071 0 : cas->setResultType(getInlineReturnType());
3072 0 : current->add(cas);
3073 0 : current->push(cas);
3074 :
3075 0 : MOZ_TRY(resumeAfter(cas));
3076 0 : return InliningStatus_Inlined;
3077 : }
3078 :
3079 : IonBuilder::InliningResult
3080 0 : IonBuilder::inlineAtomicsExchange(CallInfo& callInfo)
3081 : {
3082 0 : if (callInfo.argc() != 3 || callInfo.constructing()) {
3083 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
3084 0 : return InliningStatus_NotInlined;
3085 : }
3086 :
3087 0 : MDefinition* value = callInfo.getArg(2);
3088 0 : if (value->mightBeType(MIRType::Object) || value->mightBeType(MIRType::Symbol))
3089 0 : return InliningStatus_NotInlined;
3090 :
3091 : Scalar::Type arrayType;
3092 0 : bool requiresCheck = false;
3093 0 : if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
3094 0 : return InliningStatus_NotInlined;
3095 :
3096 0 : callInfo.setImplicitlyUsedUnchecked();
3097 :
3098 : MInstruction* elements;
3099 : MDefinition* index;
3100 0 : atomicsCheckBounds(callInfo, &elements, &index);
3101 :
3102 0 : if (requiresCheck)
3103 0 : addSharedTypedArrayGuard(callInfo.getArg(0));
3104 :
3105 : MInstruction* exchange =
3106 0 : MAtomicExchangeTypedArrayElement::New(alloc(), elements, index, value, arrayType);
3107 0 : exchange->setResultType(getInlineReturnType());
3108 0 : current->add(exchange);
3109 0 : current->push(exchange);
3110 :
3111 0 : MOZ_TRY(resumeAfter(exchange));
3112 0 : return InliningStatus_Inlined;
3113 : }
3114 :
3115 : IonBuilder::InliningResult
3116 0 : IonBuilder::inlineAtomicsLoad(CallInfo& callInfo)
3117 : {
3118 0 : if (callInfo.argc() != 2 || callInfo.constructing()) {
3119 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
3120 0 : return InliningStatus_NotInlined;
3121 : }
3122 :
3123 : Scalar::Type arrayType;
3124 0 : bool requiresCheck = false;
3125 0 : if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
3126 0 : return InliningStatus_NotInlined;
3127 :
3128 0 : callInfo.setImplicitlyUsedUnchecked();
3129 :
3130 : MInstruction* elements;
3131 : MDefinition* index;
3132 0 : atomicsCheckBounds(callInfo, &elements, &index);
3133 :
3134 0 : if (requiresCheck)
3135 0 : addSharedTypedArrayGuard(callInfo.getArg(0));
3136 :
3137 : MLoadUnboxedScalar* load =
3138 0 : MLoadUnboxedScalar::New(alloc(), elements, index, arrayType,
3139 0 : DoesRequireMemoryBarrier);
3140 0 : load->setResultType(getInlineReturnType());
3141 0 : current->add(load);
3142 0 : current->push(load);
3143 :
3144 : // Loads are considered effectful (they execute a memory barrier).
3145 0 : MOZ_TRY(resumeAfter(load));
3146 0 : return InliningStatus_Inlined;
3147 : }
3148 :
3149 : IonBuilder::InliningResult
3150 0 : IonBuilder::inlineAtomicsStore(CallInfo& callInfo)
3151 : {
3152 0 : if (callInfo.argc() != 3 || callInfo.constructing()) {
3153 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
3154 0 : return InliningStatus_NotInlined;
3155 : }
3156 :
3157 : // Atomics.store() is annoying because it returns the result of converting
3158 : // the value by ToInteger(), not the input value, nor the result of
3159 : // converting the value by ToInt32(). It is especially annoying because
3160 : // almost nobody uses the result value.
3161 : //
3162 : // As an expedient compromise, therefore, we inline only if the result is
3163 : // obviously unused or if the argument is already Int32 and thus requires no
3164 : // conversion.
3165 :
3166 0 : MDefinition* value = callInfo.getArg(2);
3167 0 : if (!BytecodeIsPopped(pc) && value->type() != MIRType::Int32) {
3168 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType);
3169 0 : return InliningStatus_NotInlined;
3170 : }
3171 :
3172 0 : if (value->mightBeType(MIRType::Object) || value->mightBeType(MIRType::Symbol))
3173 0 : return InliningStatus_NotInlined;
3174 :
3175 : Scalar::Type arrayType;
3176 0 : bool requiresCheck = false;
3177 0 : if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck, DontCheckAtomicResult))
3178 0 : return InliningStatus_NotInlined;
3179 :
3180 0 : callInfo.setImplicitlyUsedUnchecked();
3181 :
3182 : MInstruction* elements;
3183 : MDefinition* index;
3184 0 : atomicsCheckBounds(callInfo, &elements, &index);
3185 :
3186 0 : if (requiresCheck)
3187 0 : addSharedTypedArrayGuard(callInfo.getArg(0));
3188 :
3189 0 : MDefinition* toWrite = value;
3190 0 : if (toWrite->type() != MIRType::Int32) {
3191 0 : toWrite = MTruncateToInt32::New(alloc(), toWrite);
3192 0 : current->add(toWrite->toInstruction());
3193 : }
3194 : MStoreUnboxedScalar* store =
3195 0 : MStoreUnboxedScalar::New(alloc(), elements, index, toWrite, arrayType,
3196 0 : MStoreUnboxedScalar::TruncateInput, DoesRequireMemoryBarrier);
3197 0 : current->add(store);
3198 0 : current->push(value); // Either Int32 or not used; in either case correct
3199 :
3200 0 : MOZ_TRY(resumeAfter(store));
3201 0 : return InliningStatus_Inlined;
3202 : }
3203 :
3204 : IonBuilder::InliningResult
3205 0 : IonBuilder::inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target)
3206 : {
3207 0 : if (callInfo.argc() != 3 || callInfo.constructing()) {
3208 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
3209 0 : return InliningStatus_NotInlined;
3210 : }
3211 :
3212 0 : MDefinition* value = callInfo.getArg(2);
3213 0 : if (value->mightBeType(MIRType::Object) || value->mightBeType(MIRType::Symbol))
3214 0 : return InliningStatus_NotInlined;
3215 :
3216 : Scalar::Type arrayType;
3217 0 : bool requiresCheck = false;
3218 0 : if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
3219 0 : return InliningStatus_NotInlined;
3220 :
3221 0 : callInfo.setImplicitlyUsedUnchecked();
3222 :
3223 0 : if (requiresCheck)
3224 0 : addSharedTypedArrayGuard(callInfo.getArg(0));
3225 :
3226 : MInstruction* elements;
3227 : MDefinition* index;
3228 0 : atomicsCheckBounds(callInfo, &elements, &index);
3229 :
3230 0 : AtomicOp k = AtomicFetchAddOp;
3231 0 : switch (target) {
3232 : case InlinableNative::AtomicsAdd:
3233 0 : k = AtomicFetchAddOp;
3234 0 : break;
3235 : case InlinableNative::AtomicsSub:
3236 0 : k = AtomicFetchSubOp;
3237 0 : break;
3238 : case InlinableNative::AtomicsAnd:
3239 0 : k = AtomicFetchAndOp;
3240 0 : break;
3241 : case InlinableNative::AtomicsOr:
3242 0 : k = AtomicFetchOrOp;
3243 0 : break;
3244 : case InlinableNative::AtomicsXor:
3245 0 : k = AtomicFetchXorOp;
3246 0 : break;
3247 : default:
3248 0 : MOZ_CRASH("Bad atomic operation");
3249 : }
3250 :
3251 : MAtomicTypedArrayElementBinop* binop =
3252 0 : MAtomicTypedArrayElementBinop::New(alloc(), k, elements, index, arrayType, value);
3253 0 : binop->setResultType(getInlineReturnType());
3254 0 : current->add(binop);
3255 0 : current->push(binop);
3256 :
3257 0 : MOZ_TRY(resumeAfter(binop));
3258 0 : return InliningStatus_Inlined;
3259 : }
3260 :
3261 : IonBuilder::InliningResult
3262 0 : IonBuilder::inlineAtomicsIsLockFree(CallInfo& callInfo)
3263 : {
3264 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
3265 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
3266 0 : return InliningStatus_NotInlined;
3267 : }
3268 :
3269 0 : callInfo.setImplicitlyUsedUnchecked();
3270 :
3271 : MAtomicIsLockFree* ilf =
3272 0 : MAtomicIsLockFree::New(alloc(), callInfo.getArg(0));
3273 0 : current->add(ilf);
3274 0 : current->push(ilf);
3275 :
3276 0 : return InliningStatus_Inlined;
3277 : }
3278 :
3279 : bool
3280 0 : IonBuilder::atomicsMeetsPreconditions(CallInfo& callInfo, Scalar::Type* arrayType,
3281 : bool* requiresTagCheck, AtomicCheckResult checkResult)
3282 : {
3283 0 : if (!JitSupportsAtomics())
3284 0 : return false;
3285 :
3286 0 : if (callInfo.getArg(0)->type() != MIRType::Object)
3287 0 : return false;
3288 :
3289 0 : if (callInfo.getArg(1)->type() != MIRType::Int32)
3290 0 : return false;
3291 :
3292 : // Ensure that the first argument is a TypedArray that maps shared
3293 : // memory.
3294 : //
3295 : // Then check both that the element type is something we can
3296 : // optimize and that the return type is suitable for that element
3297 : // type.
3298 :
3299 0 : TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
3300 0 : if (!arg0Types)
3301 0 : return false;
3302 :
3303 : TemporaryTypeSet::TypedArraySharedness sharedness;
3304 0 : *arrayType = arg0Types->getTypedArrayType(constraints(), &sharedness);
3305 0 : *requiresTagCheck = sharedness != TemporaryTypeSet::KnownShared;
3306 0 : switch (*arrayType) {
3307 : case Scalar::Int8:
3308 : case Scalar::Uint8:
3309 : case Scalar::Int16:
3310 : case Scalar::Uint16:
3311 : case Scalar::Int32:
3312 0 : return checkResult == DontCheckAtomicResult || getInlineReturnType() == MIRType::Int32;
3313 : case Scalar::Uint32:
3314 : // Bug 1077305: it would be attractive to allow inlining even
3315 : // if the inline return type is Int32, which it will frequently
3316 : // be.
3317 0 : return checkResult == DontCheckAtomicResult || getInlineReturnType() == MIRType::Double;
3318 : default:
3319 : // Excludes floating types and Uint8Clamped.
3320 0 : return false;
3321 : }
3322 : }
3323 :
3324 : void
3325 0 : IonBuilder::atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index)
3326 : {
3327 : // Perform bounds checking and extract the elements vector.
3328 0 : MDefinition* obj = callInfo.getArg(0);
3329 0 : MInstruction* length = nullptr;
3330 0 : *index = callInfo.getArg(1);
3331 0 : *elements = nullptr;
3332 0 : addTypedArrayLengthAndData(obj, DoBoundsCheck, index, &length, elements);
3333 0 : }
3334 :
3335 : IonBuilder::InliningResult
3336 0 : IonBuilder::inlineIsConstructing(CallInfo& callInfo)
3337 : {
3338 0 : MOZ_ASSERT(!callInfo.constructing());
3339 0 : MOZ_ASSERT(callInfo.argc() == 0);
3340 0 : MOZ_ASSERT(script()->functionNonDelazifying(),
3341 : "isConstructing() should only be called in function scripts");
3342 :
3343 0 : if (getInlineReturnType() != MIRType::Boolean)
3344 0 : return InliningStatus_NotInlined;
3345 :
3346 0 : callInfo.setImplicitlyUsedUnchecked();
3347 :
3348 0 : if (inliningDepth_ == 0) {
3349 0 : MInstruction* ins = MIsConstructing::New(alloc());
3350 0 : current->add(ins);
3351 0 : current->push(ins);
3352 0 : return InliningStatus_Inlined;
3353 : }
3354 :
3355 0 : bool constructing = inlineCallInfo_->constructing();
3356 0 : pushConstant(BooleanValue(constructing));
3357 0 : return InliningStatus_Inlined;
3358 : }
3359 :
3360 : IonBuilder::InliningResult
3361 0 : IonBuilder::inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* descr)
3362 : {
3363 : // Only inline default constructors for now.
3364 0 : if (callInfo.argc() != 0) {
3365 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
3366 0 : return InliningStatus_NotInlined;
3367 : }
3368 :
3369 0 : if (size_t(descr->size()) > InlineTypedObject::MaximumSize)
3370 0 : return InliningStatus_NotInlined;
3371 :
3372 0 : JSObject* obj = inspector->getTemplateObjectForClassHook(pc, descr->getClass());
3373 0 : if (!obj || !obj->is<InlineTypedObject>())
3374 0 : return InliningStatus_NotInlined;
3375 :
3376 0 : InlineTypedObject* templateObject = &obj->as<InlineTypedObject>();
3377 0 : if (&templateObject->typeDescr() != descr)
3378 0 : return InliningStatus_NotInlined;
3379 :
3380 0 : callInfo.setImplicitlyUsedUnchecked();
3381 :
3382 0 : MNewTypedObject* ins = MNewTypedObject::New(alloc(), constraints(), templateObject,
3383 0 : templateObject->group()->initialHeap(constraints()));
3384 0 : current->add(ins);
3385 0 : current->push(ins);
3386 :
3387 0 : return InliningStatus_Inlined;
3388 : }
3389 :
3390 : // Main entry point for SIMD inlining.
3391 : // When the controlling simdType is an integer type, sign indicates whether the lanes should
3392 : // be treated as signed or unsigned integers.
3393 : IonBuilder::InliningResult
3394 0 : IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type)
3395 : {
3396 0 : if (!JitSupportsSimd()) {
3397 0 : trackOptimizationOutcome(TrackedOutcome::NoSimdJitSupport);
3398 0 : return InliningStatus_NotInlined;
3399 : }
3400 :
3401 0 : JSNative native = target->native();
3402 0 : const JSJitInfo* jitInfo = target->jitInfo();
3403 0 : MOZ_ASSERT(jitInfo && jitInfo->type() == JSJitInfo::InlinableNative);
3404 0 : SimdOperation simdOp = SimdOperation(jitInfo->nativeOp);
3405 :
3406 0 : switch(simdOp) {
3407 : case SimdOperation::Constructor:
3408 : // SIMD constructor calls are handled via inlineNonFunctionCall(), so
3409 : // they won't show up here where target is required to be a JSFunction.
3410 : // See also inlineConstructSimdObject().
3411 0 : MOZ_CRASH("SIMD constructor call not expected.");
3412 : case SimdOperation::Fn_check:
3413 0 : return inlineSimdCheck(callInfo, native, type);
3414 : case SimdOperation::Fn_splat:
3415 0 : return inlineSimdSplat(callInfo, native, type);
3416 : case SimdOperation::Fn_extractLane:
3417 0 : return inlineSimdExtractLane(callInfo, native, type);
3418 : case SimdOperation::Fn_replaceLane:
3419 0 : return inlineSimdReplaceLane(callInfo, native, type);
3420 : case SimdOperation::Fn_select:
3421 0 : return inlineSimdSelect(callInfo, native, type);
3422 : case SimdOperation::Fn_swizzle:
3423 0 : return inlineSimdShuffle(callInfo, native, type, 1);
3424 : case SimdOperation::Fn_shuffle:
3425 0 : return inlineSimdShuffle(callInfo, native, type, 2);
3426 :
3427 : // Unary arithmetic.
3428 : case SimdOperation::Fn_abs:
3429 0 : return inlineSimdUnary(callInfo, native, MSimdUnaryArith::abs, type);
3430 : case SimdOperation::Fn_neg:
3431 0 : return inlineSimdUnary(callInfo, native, MSimdUnaryArith::neg, type);
3432 : case SimdOperation::Fn_not:
3433 0 : return inlineSimdUnary(callInfo, native, MSimdUnaryArith::not_, type);
3434 : case SimdOperation::Fn_reciprocalApproximation:
3435 : return inlineSimdUnary(callInfo, native, MSimdUnaryArith::reciprocalApproximation,
3436 0 : type);
3437 : case SimdOperation::Fn_reciprocalSqrtApproximation:
3438 : return inlineSimdUnary(callInfo, native, MSimdUnaryArith::reciprocalSqrtApproximation,
3439 0 : type);
3440 : case SimdOperation::Fn_sqrt:
3441 0 : return inlineSimdUnary(callInfo, native, MSimdUnaryArith::sqrt, type);
3442 :
3443 : // Binary arithmetic.
3444 : case SimdOperation::Fn_add:
3445 0 : return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_add, type);
3446 : case SimdOperation::Fn_sub:
3447 0 : return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_sub, type);
3448 : case SimdOperation::Fn_mul:
3449 0 : return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_mul, type);
3450 : case SimdOperation::Fn_div:
3451 0 : return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_div, type);
3452 : case SimdOperation::Fn_max:
3453 0 : return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_max, type);
3454 : case SimdOperation::Fn_min:
3455 0 : return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_min, type);
3456 : case SimdOperation::Fn_maxNum:
3457 0 : return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_maxNum, type);
3458 : case SimdOperation::Fn_minNum:
3459 0 : return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_minNum, type);
3460 :
3461 : // Binary saturating.
3462 : case SimdOperation::Fn_addSaturate:
3463 0 : return inlineSimdBinarySaturating(callInfo, native, MSimdBinarySaturating::add, type);
3464 : case SimdOperation::Fn_subSaturate:
3465 0 : return inlineSimdBinarySaturating(callInfo, native, MSimdBinarySaturating::sub, type);
3466 :
3467 : // Binary bitwise.
3468 : case SimdOperation::Fn_and:
3469 0 : return inlineSimdBinaryBitwise(callInfo, native, MSimdBinaryBitwise::and_, type);
3470 : case SimdOperation::Fn_or:
3471 0 : return inlineSimdBinaryBitwise(callInfo, native, MSimdBinaryBitwise::or_, type);
3472 : case SimdOperation::Fn_xor:
3473 0 : return inlineSimdBinaryBitwise(callInfo, native, MSimdBinaryBitwise::xor_, type);
3474 :
3475 : // Shifts.
3476 : case SimdOperation::Fn_shiftLeftByScalar:
3477 0 : return inlineSimdShift(callInfo, native, MSimdShift::lsh, type);
3478 : case SimdOperation::Fn_shiftRightByScalar:
3479 0 : return inlineSimdShift(callInfo, native, MSimdShift::rshForSign(GetSimdSign(type)), type);
3480 :
3481 : // Boolean unary.
3482 : case SimdOperation::Fn_allTrue:
3483 0 : return inlineSimdAnyAllTrue(callInfo, /* IsAllTrue= */true, native, type);
3484 : case SimdOperation::Fn_anyTrue:
3485 0 : return inlineSimdAnyAllTrue(callInfo, /* IsAllTrue= */false, native, type);
3486 :
3487 : // Comparisons.
3488 : case SimdOperation::Fn_lessThan:
3489 0 : return inlineSimdComp(callInfo, native, MSimdBinaryComp::lessThan, type);
3490 : case SimdOperation::Fn_lessThanOrEqual:
3491 0 : return inlineSimdComp(callInfo, native, MSimdBinaryComp::lessThanOrEqual, type);
3492 : case SimdOperation::Fn_equal:
3493 0 : return inlineSimdComp(callInfo, native, MSimdBinaryComp::equal, type);
3494 : case SimdOperation::Fn_notEqual:
3495 0 : return inlineSimdComp(callInfo, native, MSimdBinaryComp::notEqual, type);
3496 : case SimdOperation::Fn_greaterThan:
3497 0 : return inlineSimdComp(callInfo, native, MSimdBinaryComp::greaterThan, type);
3498 : case SimdOperation::Fn_greaterThanOrEqual:
3499 0 : return inlineSimdComp(callInfo, native, MSimdBinaryComp::greaterThanOrEqual, type);
3500 :
3501 : // Int <-> Float conversions.
3502 : case SimdOperation::Fn_fromInt32x4:
3503 0 : return inlineSimdConvert(callInfo, native, false, SimdType::Int32x4, type);
3504 : case SimdOperation::Fn_fromUint32x4:
3505 0 : return inlineSimdConvert(callInfo, native, false, SimdType::Uint32x4, type);
3506 : case SimdOperation::Fn_fromFloat32x4:
3507 0 : return inlineSimdConvert(callInfo, native, false, SimdType::Float32x4, type);
3508 :
3509 : // Load/store.
3510 : case SimdOperation::Fn_load:
3511 0 : return inlineSimdLoad(callInfo, native, type, GetSimdLanes(type));
3512 : case SimdOperation::Fn_load1:
3513 0 : return inlineSimdLoad(callInfo, native, type, 1);
3514 : case SimdOperation::Fn_load2:
3515 0 : return inlineSimdLoad(callInfo, native, type, 2);
3516 : case SimdOperation::Fn_load3:
3517 0 : return inlineSimdLoad(callInfo, native, type, 3);
3518 : case SimdOperation::Fn_store:
3519 0 : return inlineSimdStore(callInfo, native, type, GetSimdLanes(type));
3520 : case SimdOperation::Fn_store1:
3521 0 : return inlineSimdStore(callInfo, native, type, 1);
3522 : case SimdOperation::Fn_store2:
3523 0 : return inlineSimdStore(callInfo, native, type, 2);
3524 : case SimdOperation::Fn_store3:
3525 0 : return inlineSimdStore(callInfo, native, type, 3);
3526 :
3527 : // Bitcasts. One for each type with a memory representation.
3528 : case SimdOperation::Fn_fromInt32x4Bits:
3529 0 : return inlineSimdConvert(callInfo, native, true, SimdType::Int32x4, type);
3530 : case SimdOperation::Fn_fromUint32x4Bits:
3531 0 : return inlineSimdConvert(callInfo, native, true, SimdType::Uint32x4, type);
3532 : case SimdOperation::Fn_fromInt16x8Bits:
3533 0 : return inlineSimdConvert(callInfo, native, true, SimdType::Int16x8, type);
3534 : case SimdOperation::Fn_fromUint16x8Bits:
3535 0 : return inlineSimdConvert(callInfo, native, true, SimdType::Uint16x8, type);
3536 : case SimdOperation::Fn_fromInt8x16Bits:
3537 0 : return inlineSimdConvert(callInfo, native, true, SimdType::Int8x16, type);
3538 : case SimdOperation::Fn_fromUint8x16Bits:
3539 0 : return inlineSimdConvert(callInfo, native, true, SimdType::Uint8x16, type);
3540 : case SimdOperation::Fn_fromFloat32x4Bits:
3541 0 : return inlineSimdConvert(callInfo, native, true, SimdType::Float32x4, type);
3542 : case SimdOperation::Fn_fromFloat64x2Bits:
3543 0 : return InliningStatus_NotInlined;
3544 : }
3545 :
3546 0 : MOZ_CRASH("Unexpected SIMD opcode");
3547 : }
3548 :
3549 : // The representation of boolean SIMD vectors is the same as the corresponding
3550 : // integer SIMD vectors with -1 lanes meaning true and 0 lanes meaning false.
3551 : //
3552 : // Functions that set the value of a boolean vector lane work by applying
3553 : // ToBoolean on the input argument, so they accept any argument type, just like
3554 : // the MNot and MTest instructions.
3555 : //
3556 : // Convert any scalar value into an appropriate SIMD lane value: An Int32 value
3557 : // that is either 0 for false or -1 for true.
3558 : MDefinition*
3559 0 : IonBuilder::convertToBooleanSimdLane(MDefinition* scalar)
3560 : {
3561 : MSub* result;
3562 :
3563 0 : if (scalar->type() == MIRType::Boolean) {
3564 : // The input scalar is already a boolean with the int32 values 0 / 1.
3565 : // Compute result = 0 - scalar.
3566 0 : result = MSub::New(alloc(), constant(Int32Value(0)), scalar);
3567 : } else {
3568 : // For any other type, let MNot handle the conversion to boolean.
3569 : // Compute result = !scalar - 1.
3570 0 : MNot* inv = MNot::New(alloc(), scalar);
3571 0 : current->add(inv);
3572 0 : result = MSub::New(alloc(), inv, constant(Int32Value(1)));
3573 : }
3574 :
3575 0 : result->setInt32Specialization();
3576 0 : current->add(result);
3577 0 : return result;
3578 : }
3579 :
3580 : IonBuilder::InliningResult
3581 0 : IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr)
3582 : {
3583 0 : if (!JitSupportsSimd()) {
3584 0 : trackOptimizationOutcome(TrackedOutcome::NoSimdJitSupport);
3585 0 : return InliningStatus_NotInlined;
3586 : }
3587 :
3588 : // Generic constructor of SIMD valuesX4.
3589 : MIRType simdType;
3590 0 : if (!MaybeSimdTypeToMIRType(descr->type(), &simdType)) {
3591 0 : trackOptimizationOutcome(TrackedOutcome::SimdTypeNotOptimized);
3592 0 : return InliningStatus_NotInlined;
3593 : }
3594 :
3595 : // Take the templateObject out of Baseline ICs, such that we can box
3596 : // SIMD value type in the same kind of objects.
3597 0 : MOZ_ASSERT(size_t(descr->size(descr->type())) < InlineTypedObject::MaximumSize);
3598 0 : MOZ_ASSERT(descr->getClass() == &SimdTypeDescr::class_,
3599 : "getTemplateObjectForSimdCtor needs an update");
3600 :
3601 0 : JSObject* templateObject = inspector->getTemplateObjectForSimdCtor(pc, descr->type());
3602 0 : if (!templateObject)
3603 0 : return InliningStatus_NotInlined;
3604 :
3605 : // The previous assertion ensures this will never fail if we were able to
3606 : // allocate a templateObject in Baseline.
3607 0 : InlineTypedObject* inlineTypedObject = &templateObject->as<InlineTypedObject>();
3608 0 : MOZ_ASSERT(&inlineTypedObject->typeDescr() == descr);
3609 :
3610 : // When there are missing arguments, provide a default value
3611 : // containing the coercion of 'undefined' to the right type.
3612 0 : MConstant* defVal = nullptr;
3613 0 : MIRType laneType = SimdTypeToLaneType(simdType);
3614 0 : unsigned lanes = SimdTypeToLength(simdType);
3615 0 : if (lanes != 4 || callInfo.argc() < lanes) {
3616 0 : if (laneType == MIRType::Int32 || laneType == MIRType::Boolean) {
3617 : // The default lane for a boolean vector is |false|, but
3618 : // |MSimdSplat|, |MSimdValueX4|, and |MSimdInsertElement| all
3619 : // require an Int32 argument with the value 0 or 01 to initialize a
3620 : // boolean lane. See also convertToBooleanSimdLane() which is
3621 : // idempotent with a 0 argument after constant folding.
3622 0 : defVal = constant(Int32Value(0));
3623 0 : } else if (laneType == MIRType::Double) {
3624 0 : defVal = constant(DoubleNaNValue());
3625 : } else {
3626 0 : MOZ_ASSERT(laneType == MIRType::Float32);
3627 0 : defVal = MConstant::NewFloat32(alloc(), JS::GenericNaN());
3628 0 : current->add(defVal);
3629 : }
3630 : }
3631 :
3632 0 : MInstruction *values = nullptr;
3633 :
3634 : // Use the MSimdValueX4 constructor for X4 vectors.
3635 0 : if (lanes == 4) {
3636 : MDefinition* lane[4];
3637 0 : for (unsigned i = 0; i < 4; i++)
3638 0 : lane[i] = callInfo.getArgWithDefault(i, defVal);
3639 :
3640 : // Convert boolean lanes into Int32 0 / -1.
3641 0 : if (laneType == MIRType::Boolean) {
3642 0 : for (unsigned i = 0; i < 4; i++)
3643 0 : lane[i] = convertToBooleanSimdLane(lane[i]);
3644 : }
3645 :
3646 0 : values = MSimdValueX4::New(alloc(), simdType, lane[0], lane[1], lane[2], lane[3]);
3647 0 : current->add(values);
3648 : } else {
3649 : // For general constructor calls, start from splat(defVal), insert one
3650 : // lane at a time.
3651 0 : values = MSimdSplat::New(alloc(), defVal, simdType);
3652 0 : current->add(values);
3653 :
3654 : // Stop early if constructor doesn't have enough arguments. These lanes
3655 : // then get the default value.
3656 0 : if (callInfo.argc() < lanes)
3657 0 : lanes = callInfo.argc();
3658 :
3659 0 : for (unsigned i = 0; i < lanes; i++) {
3660 0 : MDefinition* lane = callInfo.getArg(i);
3661 0 : if (laneType == MIRType::Boolean)
3662 0 : lane = convertToBooleanSimdLane(lane);
3663 0 : values = MSimdInsertElement::New(alloc(), values, lane, i);
3664 0 : current->add(values);
3665 : }
3666 : }
3667 :
3668 0 : MSimdBox* obj = MSimdBox::New(alloc(), constraints(), values, inlineTypedObject, descr->type(),
3669 0 : inlineTypedObject->group()->initialHeap(constraints()));
3670 0 : current->add(obj);
3671 0 : current->push(obj);
3672 :
3673 0 : callInfo.setImplicitlyUsedUnchecked();
3674 0 : return InliningStatus_Inlined;
3675 : }
3676 :
3677 : bool
3678 0 : IonBuilder::canInlineSimd(CallInfo& callInfo, JSNative native, unsigned numArgs,
3679 : InlineTypedObject** templateObj)
3680 : {
3681 0 : if (callInfo.argc() != numArgs)
3682 0 : return false;
3683 :
3684 0 : JSObject* templateObject = inspector->getTemplateObjectForNative(pc, native);
3685 0 : if (!templateObject)
3686 0 : return false;
3687 :
3688 0 : *templateObj = &templateObject->as<InlineTypedObject>();
3689 0 : return true;
3690 : }
3691 :
3692 : IonBuilder::InliningResult
3693 0 : IonBuilder::inlineSimdCheck(CallInfo& callInfo, JSNative native, SimdType type)
3694 : {
3695 0 : InlineTypedObject* templateObj = nullptr;
3696 0 : if (!canInlineSimd(callInfo, native, 1, &templateObj))
3697 0 : return InliningStatus_NotInlined;
3698 :
3699 : // Unboxing checks the SIMD object type and throws a TypeError if it doesn't
3700 : // match type.
3701 0 : MDefinition *arg = unboxSimd(callInfo.getArg(0), type);
3702 :
3703 : // Create an unbox/box pair, expecting the box to be optimized away if
3704 : // anyone use the return value from this check() call. This is what you want
3705 : // for code like this:
3706 : //
3707 : // function f(x) {
3708 : // x = Int32x4.check(x)
3709 : // for(...) {
3710 : // y = Int32x4.add(x, ...)
3711 : // }
3712 : //
3713 : // The unboxing of x happens as early as possible, and only once.
3714 0 : return boxSimd(callInfo, arg, templateObj);
3715 : }
3716 :
3717 : // Given a value or object, insert a dynamic check that this is a SIMD object of
3718 : // the required SimdType, and unbox it into the corresponding SIMD MIRType.
3719 : //
3720 : // This represents the standard type checking that all the SIMD operations
3721 : // perform on their arguments.
3722 : MDefinition*
3723 0 : IonBuilder::unboxSimd(MDefinition* ins, SimdType type)
3724 : {
3725 : // Trivial optimization: If ins is a MSimdBox of the same SIMD type, there
3726 : // is no way the unboxing could fail, and we can skip it altogether.
3727 : // This is the same thing MSimdUnbox::foldsTo() does, but we can save the
3728 : // memory allocation here.
3729 0 : if (ins->isSimdBox()) {
3730 0 : MSimdBox* box = ins->toSimdBox();
3731 0 : if (box->simdType() == type) {
3732 0 : MDefinition* value = box->input();
3733 0 : MOZ_ASSERT(value->type() == SimdTypeToMIRType(type));
3734 0 : return value;
3735 : }
3736 : }
3737 :
3738 0 : MSimdUnbox* unbox = MSimdUnbox::New(alloc(), ins, type);
3739 0 : current->add(unbox);
3740 0 : return unbox;
3741 : }
3742 :
3743 : IonBuilder::InliningResult
3744 0 : IonBuilder::boxSimd(CallInfo& callInfo, MDefinition* ins, InlineTypedObject* templateObj)
3745 : {
3746 0 : SimdType simdType = templateObj->typeDescr().as<SimdTypeDescr>().type();
3747 0 : MSimdBox* obj = MSimdBox::New(alloc(), constraints(), ins, templateObj, simdType,
3748 0 : templateObj->group()->initialHeap(constraints()));
3749 :
3750 : // In some cases, ins has already been added to current.
3751 0 : if (!ins->block() && ins->isInstruction())
3752 0 : current->add(ins->toInstruction());
3753 0 : current->add(obj);
3754 0 : current->push(obj);
3755 :
3756 0 : callInfo.setImplicitlyUsedUnchecked();
3757 0 : return InliningStatus_Inlined;
3758 : }
3759 :
3760 : IonBuilder::InliningResult
3761 0 : IonBuilder::inlineSimdBinaryArith(CallInfo& callInfo, JSNative native,
3762 : MSimdBinaryArith::Operation op, SimdType type)
3763 : {
3764 0 : InlineTypedObject* templateObj = nullptr;
3765 0 : if (!canInlineSimd(callInfo, native, 2, &templateObj))
3766 0 : return InliningStatus_NotInlined;
3767 :
3768 0 : MDefinition* lhs = unboxSimd(callInfo.getArg(0), type);
3769 0 : MDefinition* rhs = unboxSimd(callInfo.getArg(1), type);
3770 :
3771 0 : auto* ins = MSimdBinaryArith::AddLegalized(alloc(), current, lhs, rhs, op);
3772 0 : return boxSimd(callInfo, ins, templateObj);
3773 : }
3774 :
3775 : IonBuilder::InliningResult
3776 0 : IonBuilder::inlineSimdBinaryBitwise(CallInfo& callInfo, JSNative native,
3777 : MSimdBinaryBitwise::Operation op, SimdType type)
3778 : {
3779 0 : InlineTypedObject* templateObj = nullptr;
3780 0 : if (!canInlineSimd(callInfo, native, 2, &templateObj))
3781 0 : return InliningStatus_NotInlined;
3782 :
3783 0 : MDefinition* lhs = unboxSimd(callInfo.getArg(0), type);
3784 0 : MDefinition* rhs = unboxSimd(callInfo.getArg(1), type);
3785 :
3786 0 : auto* ins = MSimdBinaryBitwise::New(alloc(), lhs, rhs, op);
3787 0 : return boxSimd(callInfo, ins, templateObj);
3788 : }
3789 :
3790 : // Inline a binary SIMD operation where both arguments are SIMD types.
3791 : IonBuilder::InliningResult
3792 0 : IonBuilder::inlineSimdBinarySaturating(CallInfo& callInfo, JSNative native,
3793 : MSimdBinarySaturating::Operation op, SimdType type)
3794 : {
3795 0 : InlineTypedObject* templateObj = nullptr;
3796 0 : if (!canInlineSimd(callInfo, native, 2, &templateObj))
3797 0 : return InliningStatus_NotInlined;
3798 :
3799 0 : MDefinition* lhs = unboxSimd(callInfo.getArg(0), type);
3800 0 : MDefinition* rhs = unboxSimd(callInfo.getArg(1), type);
3801 :
3802 : MSimdBinarySaturating* ins =
3803 0 : MSimdBinarySaturating::New(alloc(), lhs, rhs, op, GetSimdSign(type));
3804 0 : return boxSimd(callInfo, ins, templateObj);
3805 : }
3806 :
3807 : // Inline a SIMD shiftByScalar operation.
3808 : IonBuilder::InliningResult
3809 0 : IonBuilder::inlineSimdShift(CallInfo& callInfo, JSNative native, MSimdShift::Operation op,
3810 : SimdType type)
3811 : {
3812 0 : InlineTypedObject* templateObj = nullptr;
3813 0 : if (!canInlineSimd(callInfo, native, 2, &templateObj))
3814 0 : return InliningStatus_NotInlined;
3815 :
3816 0 : MDefinition* vec = unboxSimd(callInfo.getArg(0), type);
3817 :
3818 0 : MInstruction* ins = MSimdShift::AddLegalized(alloc(), current, vec, callInfo.getArg(1), op);
3819 0 : return boxSimd(callInfo, ins, templateObj);
3820 : }
3821 :
3822 : IonBuilder::InliningResult
3823 0 : IonBuilder::inlineSimdComp(CallInfo& callInfo, JSNative native, MSimdBinaryComp::Operation op,
3824 : SimdType type)
3825 : {
3826 0 : InlineTypedObject* templateObj = nullptr;
3827 0 : if (!canInlineSimd(callInfo, native, 2, &templateObj))
3828 0 : return InliningStatus_NotInlined;
3829 :
3830 0 : MDefinition* lhs = unboxSimd(callInfo.getArg(0), type);
3831 0 : MDefinition* rhs = unboxSimd(callInfo.getArg(1), type);
3832 : MInstruction* ins =
3833 0 : MSimdBinaryComp::AddLegalized(alloc(), current, lhs, rhs, op, GetSimdSign(type));
3834 0 : return boxSimd(callInfo, ins, templateObj);
3835 : }
3836 :
3837 : IonBuilder::InliningResult
3838 0 : IonBuilder::inlineSimdUnary(CallInfo& callInfo, JSNative native, MSimdUnaryArith::Operation op,
3839 : SimdType type)
3840 : {
3841 0 : InlineTypedObject* templateObj = nullptr;
3842 0 : if (!canInlineSimd(callInfo, native, 1, &templateObj))
3843 0 : return InliningStatus_NotInlined;
3844 :
3845 0 : MDefinition* arg = unboxSimd(callInfo.getArg(0), type);
3846 :
3847 0 : MSimdUnaryArith* ins = MSimdUnaryArith::New(alloc(), arg, op);
3848 0 : return boxSimd(callInfo, ins, templateObj);
3849 : }
3850 :
3851 : IonBuilder::InliningResult
3852 0 : IonBuilder::inlineSimdSplat(CallInfo& callInfo, JSNative native, SimdType type)
3853 : {
3854 0 : InlineTypedObject* templateObj = nullptr;
3855 0 : if (!canInlineSimd(callInfo, native, 1, &templateObj))
3856 0 : return InliningStatus_NotInlined;
3857 :
3858 0 : MIRType mirType = SimdTypeToMIRType(type);
3859 0 : MDefinition* arg = callInfo.getArg(0);
3860 :
3861 : // Convert to 0 / -1 before splatting a boolean lane.
3862 0 : if (SimdTypeToLaneType(mirType) == MIRType::Boolean)
3863 0 : arg = convertToBooleanSimdLane(arg);
3864 :
3865 0 : MSimdSplat* ins = MSimdSplat::New(alloc(), arg, mirType);
3866 0 : return boxSimd(callInfo, ins, templateObj);
3867 : }
3868 :
3869 : IonBuilder::InliningResult
3870 0 : IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, SimdType type)
3871 : {
3872 : // extractLane() returns a scalar, so don't use canInlineSimd() which looks
3873 : // for a template object.
3874 0 : if (callInfo.argc() != 2 || callInfo.constructing()) {
3875 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
3876 0 : return InliningStatus_NotInlined;
3877 : }
3878 :
3879 : // Lane index.
3880 0 : MDefinition* arg = callInfo.getArg(1);
3881 0 : if (!arg->isConstant() || arg->type() != MIRType::Int32)
3882 0 : return InliningStatus_NotInlined;
3883 0 : unsigned lane = arg->toConstant()->toInt32();
3884 0 : if (lane >= GetSimdLanes(type))
3885 0 : return InliningStatus_NotInlined;
3886 :
3887 : // Original vector.
3888 0 : MDefinition* orig = unboxSimd(callInfo.getArg(0), type);
3889 0 : MIRType vecType = orig->type();
3890 0 : MIRType laneType = SimdTypeToLaneType(vecType);
3891 0 : SimdSign sign = GetSimdSign(type);
3892 :
3893 : // An Uint32 lane can't be represented in MIRType::Int32. Get it as a double.
3894 0 : if (type == SimdType::Uint32x4)
3895 0 : laneType = MIRType::Double;
3896 :
3897 : MSimdExtractElement* ins =
3898 0 : MSimdExtractElement::New(alloc(), orig, laneType, lane, sign);
3899 0 : current->add(ins);
3900 0 : current->push(ins);
3901 0 : callInfo.setImplicitlyUsedUnchecked();
3902 0 : return InliningStatus_Inlined;
3903 : }
3904 :
3905 : IonBuilder::InliningResult
3906 0 : IonBuilder::inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, SimdType type)
3907 : {
3908 0 : InlineTypedObject* templateObj = nullptr;
3909 0 : if (!canInlineSimd(callInfo, native, 3, &templateObj))
3910 0 : return InliningStatus_NotInlined;
3911 :
3912 : // Lane index.
3913 0 : MDefinition* arg = callInfo.getArg(1);
3914 0 : if (!arg->isConstant() || arg->type() != MIRType::Int32)
3915 0 : return InliningStatus_NotInlined;
3916 :
3917 0 : unsigned lane = arg->toConstant()->toInt32();
3918 0 : if (lane >= GetSimdLanes(type))
3919 0 : return InliningStatus_NotInlined;
3920 :
3921 : // Original vector.
3922 0 : MDefinition* orig = unboxSimd(callInfo.getArg(0), type);
3923 0 : MIRType vecType = orig->type();
3924 :
3925 : // Convert to 0 / -1 before inserting a boolean lane.
3926 0 : MDefinition* value = callInfo.getArg(2);
3927 0 : if (SimdTypeToLaneType(vecType) == MIRType::Boolean)
3928 0 : value = convertToBooleanSimdLane(value);
3929 :
3930 0 : MSimdInsertElement* ins = MSimdInsertElement::New(alloc(), orig, value, lane);
3931 0 : return boxSimd(callInfo, ins, templateObj);
3932 : }
3933 :
3934 : // Inline a SIMD conversion or bitcast. When isCast==false, one of the types
3935 : // must be floating point and the other integer. In this case, sign indicates if
3936 : // the integer lanes should be treated as signed or unsigned integers.
3937 : IonBuilder::InliningResult
3938 0 : IonBuilder::inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast, SimdType fromType,
3939 : SimdType toType)
3940 : {
3941 0 : InlineTypedObject* templateObj = nullptr;
3942 0 : if (!canInlineSimd(callInfo, native, 1, &templateObj))
3943 0 : return InliningStatus_NotInlined;
3944 :
3945 0 : MDefinition* arg = unboxSimd(callInfo.getArg(0), fromType);
3946 0 : MIRType mirType = SimdTypeToMIRType(toType);
3947 :
3948 : MInstruction* ins;
3949 0 : if (isCast) {
3950 : // Signed/Unsigned doesn't matter for bitcasts.
3951 0 : ins = MSimdReinterpretCast::New(alloc(), arg, mirType);
3952 : } else {
3953 : // Exactly one of fromType, toType must be an integer type.
3954 0 : SimdSign sign = GetSimdSign(fromType);
3955 0 : if (sign == SimdSign::NotApplicable)
3956 0 : sign = GetSimdSign(toType);
3957 :
3958 : // Possibly expand into multiple instructions.
3959 0 : ins = MSimdConvert::AddLegalized(alloc(), current, arg, mirType, sign);
3960 : }
3961 :
3962 0 : return boxSimd(callInfo, ins, templateObj);
3963 : }
3964 :
3965 : IonBuilder::InliningResult
3966 0 : IonBuilder::inlineSimdSelect(CallInfo& callInfo, JSNative native, SimdType type)
3967 : {
3968 0 : InlineTypedObject* templateObj = nullptr;
3969 0 : if (!canInlineSimd(callInfo, native, 3, &templateObj))
3970 0 : return InliningStatus_NotInlined;
3971 :
3972 0 : MDefinition* mask = unboxSimd(callInfo.getArg(0), GetBooleanSimdType(type));
3973 0 : MDefinition* tval = unboxSimd(callInfo.getArg(1), type);
3974 0 : MDefinition* fval = unboxSimd(callInfo.getArg(2), type);
3975 :
3976 0 : MSimdSelect* ins = MSimdSelect::New(alloc(), mask, tval, fval);
3977 0 : return boxSimd(callInfo, ins, templateObj);
3978 : }
3979 :
3980 : IonBuilder::InliningResult
3981 0 : IonBuilder::inlineSimdShuffle(CallInfo& callInfo, JSNative native, SimdType type,
3982 : unsigned numVectors)
3983 : {
3984 0 : unsigned numLanes = GetSimdLanes(type);
3985 0 : InlineTypedObject* templateObj = nullptr;
3986 0 : if (!canInlineSimd(callInfo, native, numVectors + numLanes, &templateObj))
3987 0 : return InliningStatus_NotInlined;
3988 :
3989 0 : MIRType mirType = SimdTypeToMIRType(type);
3990 :
3991 0 : MSimdGeneralShuffle* ins = MSimdGeneralShuffle::New(alloc(), numVectors, numLanes, mirType);
3992 :
3993 0 : if (!ins->init(alloc()))
3994 0 : return abort(AbortReason::Alloc);
3995 :
3996 0 : for (unsigned i = 0; i < numVectors; i++)
3997 0 : ins->setVector(i, unboxSimd(callInfo.getArg(i), type));
3998 0 : for (size_t i = 0; i < numLanes; i++)
3999 0 : ins->setLane(i, callInfo.getArg(numVectors + i));
4000 :
4001 0 : return boxSimd(callInfo, ins, templateObj);
4002 : }
4003 :
4004 : IonBuilder::InliningResult
4005 0 : IonBuilder::inlineSimdAnyAllTrue(CallInfo& callInfo, bool IsAllTrue, JSNative native,
4006 : SimdType type)
4007 : {
4008 : // anyTrue() / allTrue() return a scalar, so don't use canInlineSimd() which looks
4009 : // for a template object.
4010 0 : if (callInfo.argc() != 1 || callInfo.constructing()) {
4011 0 : trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
4012 0 : return InliningStatus_NotInlined;
4013 : }
4014 :
4015 0 : MDefinition* arg = unboxSimd(callInfo.getArg(0), type);
4016 :
4017 : MUnaryInstruction* ins;
4018 0 : if (IsAllTrue)
4019 0 : ins = MSimdAllTrue::New(alloc(), arg, MIRType::Boolean);
4020 : else
4021 0 : ins = MSimdAnyTrue::New(alloc(), arg, MIRType::Boolean);
4022 :
4023 0 : current->add(ins);
4024 0 : current->push(ins);
4025 0 : callInfo.setImplicitlyUsedUnchecked();
4026 0 : return InliningStatus_Inlined;
4027 : }
4028 :
4029 : // Get the typed array element type corresponding to the lanes in a SIMD vector type.
4030 : // This only applies to SIMD types that can be loaded and stored to a typed array.
4031 : static Scalar::Type
4032 0 : SimdTypeToArrayElementType(SimdType type)
4033 : {
4034 0 : switch (type) {
4035 0 : case SimdType::Float32x4: return Scalar::Float32x4;
4036 : case SimdType::Int8x16:
4037 0 : case SimdType::Uint8x16: return Scalar::Int8x16;
4038 : case SimdType::Int16x8:
4039 0 : case SimdType::Uint16x8: return Scalar::Int16x8;
4040 : case SimdType::Int32x4:
4041 0 : case SimdType::Uint32x4: return Scalar::Int32x4;
4042 0 : default: MOZ_CRASH("unexpected simd type");
4043 : }
4044 : }
4045 :
4046 : bool
4047 0 : IonBuilder::prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType, MInstruction** elements,
4048 : MDefinition** index, Scalar::Type* arrayType)
4049 : {
4050 0 : MDefinition* array = callInfo.getArg(0);
4051 0 : *index = callInfo.getArg(1);
4052 :
4053 0 : if (!ElementAccessIsTypedArray(constraints(), array, *index, arrayType))
4054 0 : return false;
4055 :
4056 0 : MInstruction* indexAsInt32 = MToInt32::New(alloc(), *index);
4057 0 : current->add(indexAsInt32);
4058 0 : *index = indexAsInt32;
4059 :
4060 0 : MDefinition* indexForBoundsCheck = *index;
4061 :
4062 : // Artificially make sure the index is in bounds by adding the difference
4063 : // number of slots needed (e.g. reading from Float32Array we need to make
4064 : // sure to be in bounds for 4 slots, so add 3, etc.).
4065 0 : MOZ_ASSERT(Scalar::byteSize(simdType) % Scalar::byteSize(*arrayType) == 0);
4066 0 : int32_t suppSlotsNeeded = Scalar::byteSize(simdType) / Scalar::byteSize(*arrayType) - 1;
4067 0 : if (suppSlotsNeeded) {
4068 0 : MConstant* suppSlots = constant(Int32Value(suppSlotsNeeded));
4069 0 : MAdd* addedIndex = MAdd::New(alloc(), *index, suppSlots);
4070 : // We're fine even with the add overflows, as long as the generated code
4071 : // for the bounds check uses an unsigned comparison.
4072 0 : addedIndex->setInt32Specialization();
4073 0 : current->add(addedIndex);
4074 0 : indexForBoundsCheck = addedIndex;
4075 : }
4076 :
4077 : MInstruction* length;
4078 0 : addTypedArrayLengthAndData(array, SkipBoundsCheck, index, &length, elements);
4079 :
4080 : // It can be that the index is out of bounds, while the added index for the
4081 : // bounds check is in bounds, so we actually need two bounds checks here.
4082 0 : MInstruction* positiveCheck = MBoundsCheck::New(alloc(), *index, length);
4083 0 : current->add(positiveCheck);
4084 :
4085 0 : MInstruction* fullCheck = MBoundsCheck::New(alloc(), indexForBoundsCheck, length);
4086 0 : current->add(fullCheck);
4087 0 : return true;
4088 : }
4089 :
4090 : IonBuilder::InliningResult
4091 0 : IonBuilder::inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdType type, unsigned numElems)
4092 : {
4093 0 : InlineTypedObject* templateObj = nullptr;
4094 0 : if (!canInlineSimd(callInfo, native, 2, &templateObj))
4095 0 : return InliningStatus_NotInlined;
4096 :
4097 0 : Scalar::Type elemType = SimdTypeToArrayElementType(type);
4098 :
4099 0 : MDefinition* index = nullptr;
4100 0 : MInstruction* elements = nullptr;
4101 : Scalar::Type arrayType;
4102 0 : if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType))
4103 0 : return InliningStatus_NotInlined;
4104 :
4105 0 : MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
4106 0 : load->setResultType(SimdTypeToMIRType(type));
4107 0 : load->setSimdRead(elemType, numElems);
4108 :
4109 0 : return boxSimd(callInfo, load, templateObj);
4110 : }
4111 :
4112 : IonBuilder::InliningResult
4113 0 : IonBuilder::inlineSimdStore(CallInfo& callInfo, JSNative native, SimdType type, unsigned numElems)
4114 : {
4115 0 : InlineTypedObject* templateObj = nullptr;
4116 0 : if (!canInlineSimd(callInfo, native, 3, &templateObj))
4117 0 : return InliningStatus_NotInlined;
4118 :
4119 0 : Scalar::Type elemType = SimdTypeToArrayElementType(type);
4120 :
4121 0 : MDefinition* index = nullptr;
4122 0 : MInstruction* elements = nullptr;
4123 : Scalar::Type arrayType;
4124 0 : if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType))
4125 0 : return InliningStatus_NotInlined;
4126 :
4127 0 : MDefinition* valueToWrite = unboxSimd(callInfo.getArg(2), type);
4128 0 : MStoreUnboxedScalar* store = MStoreUnboxedScalar::New(alloc(), elements, index,
4129 : valueToWrite, arrayType,
4130 0 : MStoreUnboxedScalar::TruncateInput);
4131 0 : store->setSimdWrite(elemType, numElems);
4132 :
4133 0 : current->add(store);
4134 : // Produce the original boxed value as our return value.
4135 : // This is unlikely to be used, so don't bother reboxing valueToWrite.
4136 0 : current->push(callInfo.getArg(2));
4137 :
4138 0 : callInfo.setImplicitlyUsedUnchecked();
4139 :
4140 0 : MOZ_TRY(resumeAfter(store));
4141 0 : return InliningStatus_Inlined;
4142 : }
4143 :
4144 : // Note that SIMD.cpp provides its own JSJitInfo objects for SIMD.foo.* functions.
4145 : // The Simd* objects defined here represent SIMD.foo() constructor calls.
4146 : // They are encoded with .nativeOp = 0. That is the sub-opcode within the SIMD type.
4147 : static_assert(uint16_t(SimdOperation::Constructor) == 0, "Constructor opcode must be 0");
4148 :
4149 : #define ADD_NATIVE(native) const JSJitInfo JitInfo_##native { \
4150 : { nullptr }, { uint16_t(InlinableNative::native) }, { 0 }, JSJitInfo::InlinableNative };
4151 : INLINABLE_NATIVE_LIST(ADD_NATIVE)
4152 : #undef ADD_NATIVE
4153 :
4154 : } // namespace jit
4155 : } // namespace js
|