js/src/jit/MCallOptimize.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:04265fa20cd7
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "jsmath.h"
8
9 #include "builtin/TestingFunctions.h"
10 #include "builtin/TypedObject.h"
11 #include "jit/BaselineInspector.h"
12 #include "jit/IonBuilder.h"
13 #include "jit/Lowering.h"
14 #include "jit/MIR.h"
15 #include "jit/MIRGraph.h"
16 #include "vm/ArgumentsObject.h"
17
18 #include "jsscriptinlines.h"
19
20 #include "vm/StringObject-inl.h"
21
22 namespace js {
23 namespace jit {
24
25 IonBuilder::InliningStatus
26 IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
27 {
28 JS_ASSERT(target->isNative());
29 JSNative native = target->native();
30
31 if (!optimizationInfo().inlineNative())
32 return InliningStatus_NotInlined;
33
34 // Array natives.
35 if (native == js_Array)
36 return inlineArray(callInfo);
37 if (native == js::array_pop)
38 return inlineArrayPopShift(callInfo, MArrayPopShift::Pop);
39 if (native == js::array_shift)
40 return inlineArrayPopShift(callInfo, MArrayPopShift::Shift);
41 if (native == js::array_push)
42 return inlineArrayPush(callInfo);
43 if (native == js::array_concat)
44 return inlineArrayConcat(callInfo);
45 if (native == js::array_splice)
46 return inlineArraySplice(callInfo);
47
48 // Math natives.
49 if (native == js_math_abs)
50 return inlineMathAbs(callInfo);
51 if (native == js::math_floor)
52 return inlineMathFloor(callInfo);
53 if (native == js::math_ceil)
54 return inlineMathCeil(callInfo);
55 if (native == js::math_round)
56 return inlineMathRound(callInfo);
57 if (native == js_math_sqrt)
58 return inlineMathSqrt(callInfo);
59 if (native == math_atan2)
60 return inlineMathAtan2(callInfo);
61 if (native == js::math_hypot)
62 return inlineMathHypot(callInfo);
63 if (native == js_math_max)
64 return inlineMathMinMax(callInfo, true /* max */);
65 if (native == js_math_min)
66 return inlineMathMinMax(callInfo, false /* max */);
67 if (native == js_math_pow)
68 return inlineMathPow(callInfo);
69 if (native == js_math_random)
70 return inlineMathRandom(callInfo);
71 if (native == js::math_imul)
72 return inlineMathImul(callInfo);
73 if (native == js::math_fround)
74 return inlineMathFRound(callInfo);
75 if (native == js::math_sin)
76 return inlineMathFunction(callInfo, MMathFunction::Sin);
77 if (native == js::math_cos)
78 return inlineMathFunction(callInfo, MMathFunction::Cos);
79 if (native == js::math_exp)
80 return inlineMathFunction(callInfo, MMathFunction::Exp);
81 if (native == js::math_tan)
82 return inlineMathFunction(callInfo, MMathFunction::Tan);
83 if (native == js::math_log)
84 return inlineMathFunction(callInfo, MMathFunction::Log);
85 if (native == js::math_atan)
86 return inlineMathFunction(callInfo, MMathFunction::ATan);
87 if (native == js::math_asin)
88 return inlineMathFunction(callInfo, MMathFunction::ASin);
89 if (native == js::math_acos)
90 return inlineMathFunction(callInfo, MMathFunction::ACos);
91 if (native == js::math_log10)
92 return inlineMathFunction(callInfo, MMathFunction::Log10);
93 if (native == js::math_log2)
94 return inlineMathFunction(callInfo, MMathFunction::Log2);
95 if (native == js::math_log1p)
96 return inlineMathFunction(callInfo, MMathFunction::Log1P);
97 if (native == js::math_expm1)
98 return inlineMathFunction(callInfo, MMathFunction::ExpM1);
99 if (native == js::math_cosh)
100 return inlineMathFunction(callInfo, MMathFunction::CosH);
101 if (native == js::math_sin)
102 return inlineMathFunction(callInfo, MMathFunction::SinH);
103 if (native == js::math_tan)
104 return inlineMathFunction(callInfo, MMathFunction::TanH);
105 if (native == js::math_acosh)
106 return inlineMathFunction(callInfo, MMathFunction::ACosH);
107 if (native == js::math_asin)
108 return inlineMathFunction(callInfo, MMathFunction::ASinH);
109 if (native == js::math_atan)
110 return inlineMathFunction(callInfo, MMathFunction::ATanH);
111 if (native == js::math_sign)
112 return inlineMathFunction(callInfo, MMathFunction::Sign);
113 if (native == js::math_trunc)
114 return inlineMathFunction(callInfo, MMathFunction::Trunc);
115 if (native == js::math_cbrt)
116 return inlineMathFunction(callInfo, MMathFunction::Cbrt);
117
118 // String natives.
119 if (native == js_String)
120 return inlineStringObject(callInfo);
121 if (native == js::str_split)
122 return inlineStringSplit(callInfo);
123 if (native == js_str_charCodeAt)
124 return inlineStrCharCodeAt(callInfo);
125 if (native == js::str_fromCharCode)
126 return inlineStrFromCharCode(callInfo);
127 if (native == js_str_charAt)
128 return inlineStrCharAt(callInfo);
129 if (native == str_replace)
130 return inlineStrReplace(callInfo);
131
132 // RegExp natives.
133 if (native == regexp_exec && CallResultEscapes(pc))
134 return inlineRegExpExec(callInfo);
135 if (native == regexp_exec && !CallResultEscapes(pc))
136 return inlineRegExpTest(callInfo);
137 if (native == regexp_test)
138 return inlineRegExpTest(callInfo);
139
140 // Array intrinsics.
141 if (native == intrinsic_UnsafePutElements)
142 return inlineUnsafePutElements(callInfo);
143 if (native == intrinsic_NewDenseArray)
144 return inlineNewDenseArray(callInfo);
145
146 // Slot intrinsics.
147 if (native == intrinsic_UnsafeSetReservedSlot)
148 return inlineUnsafeSetReservedSlot(callInfo);
149 if (native == intrinsic_UnsafeGetReservedSlot)
150 return inlineUnsafeGetReservedSlot(callInfo);
151
152 // Parallel intrinsics.
153 if (native == intrinsic_ShouldForceSequential ||
154 native == intrinsic_InParallelSection)
155 return inlineForceSequentialOrInParallelSection(callInfo);
156 if (native == intrinsic_ForkJoinGetSlice)
157 return inlineForkJoinGetSlice(callInfo);
158
159 // Utility intrinsics.
160 if (native == intrinsic_IsCallable)
161 return inlineIsCallable(callInfo);
162 if (native == intrinsic_HaveSameClass)
163 return inlineHaveSameClass(callInfo);
164 if (native == intrinsic_ToObject)
165 return inlineToObject(callInfo);
166
167 // TypedObject intrinsics.
168 if (native == intrinsic_ObjectIsTypedObject)
169 return inlineHasClasses(callInfo,
170 &TransparentTypedObject::class_, &OpaqueTypedObject::class_);
171 if (native == intrinsic_ObjectIsTransparentTypedObject)
172 return inlineHasClass(callInfo, &TransparentTypedObject::class_);
173 if (native == intrinsic_ObjectIsOpaqueTypedObject)
174 return inlineHasClass(callInfo, &OpaqueTypedObject::class_);
175 if (native == intrinsic_ObjectIsTypeDescr)
176 return inlineObjectIsTypeDescr(callInfo);
177 if (native == intrinsic_TypeDescrIsSimpleType)
178 return inlineHasClasses(callInfo,
179 &ScalarTypeDescr::class_, &ReferenceTypeDescr::class_);
180 if (native == intrinsic_TypeDescrIsArrayType)
181 return inlineHasClasses(callInfo,
182 &SizedArrayTypeDescr::class_, &UnsizedArrayTypeDescr::class_);
183 if (native == intrinsic_TypeDescrIsSizedArrayType)
184 return inlineHasClass(callInfo, &SizedArrayTypeDescr::class_);
185 if (native == intrinsic_TypeDescrIsUnsizedArrayType)
186 return inlineHasClass(callInfo, &UnsizedArrayTypeDescr::class_);
187 if (native == intrinsic_SetTypedObjectOffset)
188 return inlineSetTypedObjectOffset(callInfo);
189
190 // Testing Functions
191 if (native == testingFunc_inParallelSection)
192 return inlineForceSequentialOrInParallelSection(callInfo);
193 if (native == testingFunc_bailout)
194 return inlineBailout(callInfo);
195 if (native == testingFunc_assertFloat32)
196 return inlineAssertFloat32(callInfo);
197
198 // Bound function
199 if (native == js::CallOrConstructBoundFunction)
200 return inlineBoundFunction(callInfo, target);
201
202 return InliningStatus_NotInlined;
203 }
204
205 types::TemporaryTypeSet *
206 IonBuilder::getInlineReturnTypeSet()
207 {
208 return bytecodeTypes(pc);
209 }
210
211 MIRType
212 IonBuilder::getInlineReturnType()
213 {
214 types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
215 return returnTypes->getKnownMIRType();
216 }
217
218 IonBuilder::InliningStatus
219 IonBuilder::inlineMathFunction(CallInfo &callInfo, MMathFunction::Function function)
220 {
221 if (callInfo.constructing())
222 return InliningStatus_NotInlined;
223
224 if (callInfo.argc() != 1)
225 return InliningStatus_NotInlined;
226
227 if (getInlineReturnType() != MIRType_Double)
228 return InliningStatus_NotInlined;
229 if (!IsNumberType(callInfo.getArg(0)->type()))
230 return InliningStatus_NotInlined;
231
232 const MathCache *cache = compartment->runtime()->maybeGetMathCache();
233
234 callInfo.fun()->setImplicitlyUsedUnchecked();
235 callInfo.thisArg()->setImplicitlyUsedUnchecked();
236
237 MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), function, cache);
238 current->add(ins);
239 current->push(ins);
240 return InliningStatus_Inlined;
241 }
242
243 IonBuilder::InliningStatus
244 IonBuilder::inlineArray(CallInfo &callInfo)
245 {
246 uint32_t initLength = 0;
247 MNewArray::AllocatingBehaviour allocating = MNewArray::NewArray_Unallocating;
248
249 JSObject *templateObject = inspector->getTemplateObjectForNative(pc, js_Array);
250 if (!templateObject)
251 return InliningStatus_NotInlined;
252 JS_ASSERT(templateObject->is<ArrayObject>());
253
254 // Multiple arguments imply array initialization, not just construction.
255 if (callInfo.argc() >= 2) {
256 initLength = callInfo.argc();
257 allocating = MNewArray::NewArray_Allocating;
258
259 types::TypeObjectKey *type = types::TypeObjectKey::get(templateObject);
260 if (!type->unknownProperties()) {
261 types::HeapTypeSetKey elemTypes = type->property(JSID_VOID);
262
263 for (uint32_t i = 0; i < initLength; i++) {
264 MDefinition *value = callInfo.getArg(i);
265 if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
266 elemTypes.freeze(constraints());
267 return InliningStatus_NotInlined;
268 }
269 }
270 }
271 }
272
273 // A single integer argument denotes initial length.
274 if (callInfo.argc() == 1) {
275 if (callInfo.getArg(0)->type() != MIRType_Int32)
276 return InliningStatus_NotInlined;
277 MDefinition *arg = callInfo.getArg(0);
278 if (!arg->isConstant())
279 return InliningStatus_NotInlined;
280
281 // Negative lengths generate a RangeError, unhandled by the inline path.
282 initLength = arg->toConstant()->value().toInt32();
283 if (initLength >= JSObject::NELEMENTS_LIMIT)
284 return InliningStatus_NotInlined;
285
286 // Make sure initLength matches the template object's length. This is
287 // not guaranteed to be the case, for instance if we're inlining the
288 // MConstant may come from an outer script.
289 if (initLength != templateObject->as<ArrayObject>().length())
290 return InliningStatus_NotInlined;
291
292 if (initLength <= ArrayObject::EagerAllocationMaxLength)
293 allocating = MNewArray::NewArray_Allocating;
294 }
295
296 callInfo.setImplicitlyUsedUnchecked();
297
298 types::TemporaryTypeSet::DoubleConversion conversion =
299 getInlineReturnTypeSet()->convertDoubleElements(constraints());
300 if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles)
301 templateObject->setShouldConvertDoubleElements();
302 else
303 templateObject->clearShouldConvertDoubleElements();
304
305 MNewArray *ins = MNewArray::New(alloc(), constraints(), initLength, templateObject,
306 templateObject->type()->initialHeap(constraints()),
307 allocating);
308 current->add(ins);
309 current->push(ins);
310
311 if (callInfo.argc() >= 2) {
312 // Get the elements vector.
313 MElements *elements = MElements::New(alloc(), ins);
314 current->add(elements);
315
316 // Store all values, no need to initialize the length after each as
317 // jsop_initelem_array is doing because we do not expect to bailout
318 // because the memory is supposed to be allocated by now.
319 MConstant *id = nullptr;
320 for (uint32_t i = 0; i < initLength; i++) {
321 id = MConstant::New(alloc(), Int32Value(i));
322 current->add(id);
323
324 MDefinition *value = callInfo.getArg(i);
325 if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) {
326 MInstruction *valueDouble = MToDouble::New(alloc(), value);
327 current->add(valueDouble);
328 value = valueDouble;
329 }
330
331 // There is normally no need for a post barrier on these writes
332 // because the new array will be in the nursery. However, this
333 // assumption is volated if we specifically requested pre-tenuring.
334 if (ins->initialHeap() == gc::TenuredHeap)
335 current->add(MPostWriteBarrier::New(alloc(), ins, value));
336
337 MStoreElement *store = MStoreElement::New(alloc(), elements, id, value,
338 /* needsHoleCheck = */ false);
339 current->add(store);
340 }
341
342 // Update the length.
343 MSetInitializedLength *length = MSetInitializedLength::New(alloc(), elements, id);
344 current->add(length);
345
346 if (!resumeAfter(length))
347 return InliningStatus_Error;
348 }
349
350 return InliningStatus_Inlined;
351 }
352
353 IonBuilder::InliningStatus
354 IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode)
355 {
356 if (callInfo.constructing())
357 return InliningStatus_NotInlined;
358
359 MIRType returnType = getInlineReturnType();
360 if (returnType == MIRType_Undefined || returnType == MIRType_Null)
361 return InliningStatus_NotInlined;
362 if (callInfo.thisArg()->type() != MIRType_Object)
363 return InliningStatus_NotInlined;
364
365 // Pop and shift are only handled for dense arrays that have never been
366 // used in an iterator: popping elements does not account for suppressing
367 // deleted properties in active iterators.
368 types::TypeObjectFlags unhandledFlags =
369 types::OBJECT_FLAG_SPARSE_INDEXES |
370 types::OBJECT_FLAG_LENGTH_OVERFLOW |
371 types::OBJECT_FLAG_ITERATED;
372
373 types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
374 if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_)
375 return InliningStatus_NotInlined;
376 if (thisTypes->hasObjectFlags(constraints(), unhandledFlags))
377 return InliningStatus_NotInlined;
378
379 if (types::ArrayPrototypeHasIndexedProperty(constraints(), script()))
380 return InliningStatus_NotInlined;
381
382 callInfo.setImplicitlyUsedUnchecked();
383
384 types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
385 bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
386 bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
387
388 bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
389 callInfo.thisArg(), nullptr, returnTypes);
390 if (barrier)
391 returnType = MIRType_Value;
392
393 MArrayPopShift *ins = MArrayPopShift::New(alloc(), callInfo.thisArg(), mode,
394 needsHoleCheck, maybeUndefined);
395 current->add(ins);
396 current->push(ins);
397 ins->setResultType(returnType);
398
399 if (!resumeAfter(ins))
400 return InliningStatus_Error;
401
402 if (!pushTypeBarrier(ins, returnTypes, barrier))
403 return InliningStatus_Error;
404
405 return InliningStatus_Inlined;
406 }
407
408 IonBuilder::InliningStatus
409 IonBuilder::inlineArraySplice(CallInfo &callInfo)
410 {
411 if (callInfo.argc() != 2 || callInfo.constructing())
412 return InliningStatus_NotInlined;
413
414 // Ensure |this|, argument and result are objects.
415 if (getInlineReturnType() != MIRType_Object)
416 return InliningStatus_NotInlined;
417 if (callInfo.thisArg()->type() != MIRType_Object)
418 return InliningStatus_NotInlined;
419 if (callInfo.getArg(0)->type() != MIRType_Int32)
420 return InliningStatus_NotInlined;
421 if (callInfo.getArg(1)->type() != MIRType_Int32)
422 return InliningStatus_NotInlined;
423
424 callInfo.setImplicitlyUsedUnchecked();
425
426 // Specialize arr.splice(start, deleteCount) with unused return value and
427 // avoid creating the result array in this case.
428 if (!BytecodeIsPopped(pc))
429 return InliningStatus_NotInlined;
430
431 MArraySplice *ins = MArraySplice::New(alloc(),
432 callInfo.thisArg(),
433 callInfo.getArg(0),
434 callInfo.getArg(1));
435
436 current->add(ins);
437 pushConstant(UndefinedValue());
438
439 if (!resumeAfter(ins))
440 return InliningStatus_Error;
441 return InliningStatus_Inlined;
442 }
443
444 IonBuilder::InliningStatus
445 IonBuilder::inlineArrayPush(CallInfo &callInfo)
446 {
447 if (callInfo.argc() != 1 || callInfo.constructing())
448 return InliningStatus_NotInlined;
449
450 MDefinition *obj = callInfo.thisArg();
451 MDefinition *value = callInfo.getArg(0);
452 if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
453 &obj, nullptr, &value, /* canModify = */ false))
454 {
455 return InliningStatus_NotInlined;
456 }
457 JS_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0));
458
459 if (getInlineReturnType() != MIRType_Int32)
460 return InliningStatus_NotInlined;
461 if (callInfo.thisArg()->type() != MIRType_Object)
462 return InliningStatus_NotInlined;
463
464 types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
465 if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_)
466 return InliningStatus_NotInlined;
467 if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
468 types::OBJECT_FLAG_LENGTH_OVERFLOW))
469 {
470 return InliningStatus_NotInlined;
471 }
472
473 if (types::ArrayPrototypeHasIndexedProperty(constraints(), script()))
474 return InliningStatus_NotInlined;
475
476 types::TemporaryTypeSet::DoubleConversion conversion =
477 thisTypes->convertDoubleElements(constraints());
478 if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion)
479 return InliningStatus_NotInlined;
480
481 callInfo.setImplicitlyUsedUnchecked();
482 value = callInfo.getArg(0);
483
484 if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles ||
485 conversion == types::TemporaryTypeSet::MaybeConvertToDoubles)
486 {
487 MInstruction *valueDouble = MToDouble::New(alloc(), value);
488 current->add(valueDouble);
489 value = valueDouble;
490 }
491
492 if (NeedsPostBarrier(info(), value))
493 current->add(MPostWriteBarrier::New(alloc(), callInfo.thisArg(), value));
494
495 MArrayPush *ins = MArrayPush::New(alloc(), callInfo.thisArg(), value);
496 current->add(ins);
497 current->push(ins);
498
499 if (!resumeAfter(ins))
500 return InliningStatus_Error;
501 return InliningStatus_Inlined;
502 }
503
504 IonBuilder::InliningStatus
505 IonBuilder::inlineArrayConcat(CallInfo &callInfo)
506 {
507 if (callInfo.argc() != 1 || callInfo.constructing())
508 return InliningStatus_NotInlined;
509
510 // Ensure |this|, argument and result are objects.
511 if (getInlineReturnType() != MIRType_Object)
512 return InliningStatus_NotInlined;
513 if (callInfo.thisArg()->type() != MIRType_Object)
514 return InliningStatus_NotInlined;
515 if (callInfo.getArg(0)->type() != MIRType_Object)
516 return InliningStatus_NotInlined;
517
518 // |this| and the argument must be dense arrays.
519 types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
520 types::TemporaryTypeSet *argTypes = callInfo.getArg(0)->resultTypeSet();
521 if (!thisTypes || !argTypes)
522 return InliningStatus_NotInlined;
523
524 if (thisTypes->getKnownClass() != &ArrayObject::class_)
525 return InliningStatus_NotInlined;
526 if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
527 types::OBJECT_FLAG_LENGTH_OVERFLOW))
528 {
529 return InliningStatus_NotInlined;
530 }
531
532 if (argTypes->getKnownClass() != &ArrayObject::class_)
533 return InliningStatus_NotInlined;
534 if (argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
535 types::OBJECT_FLAG_LENGTH_OVERFLOW))
536 {
537 return InliningStatus_NotInlined;
538 }
539
540 // Watch out for indexed properties on the prototype.
541 if (types::ArrayPrototypeHasIndexedProperty(constraints(), script()))
542 return InliningStatus_NotInlined;
543
544 // Require the 'this' types to have a specific type matching the current
545 // global, so we can create the result object inline.
546 if (thisTypes->getObjectCount() != 1)
547 return InliningStatus_NotInlined;
548
549 types::TypeObject *baseThisType = thisTypes->getTypeObject(0);
550 if (!baseThisType)
551 return InliningStatus_NotInlined;
552 types::TypeObjectKey *thisType = types::TypeObjectKey::get(baseThisType);
553 if (thisType->unknownProperties())
554 return InliningStatus_NotInlined;
555
556 // Don't inline if 'this' is packed and the argument may not be packed
557 // (the result array will reuse the 'this' type).
558 if (!thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED) &&
559 argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED))
560 {
561 return InliningStatus_NotInlined;
562 }
563
564 // Constraints modeling this concat have not been generated by inference,
565 // so check that type information already reflects possible side effects of
566 // this call.
567 types::HeapTypeSetKey thisElemTypes = thisType->property(JSID_VOID);
568
569 types::TemporaryTypeSet *resTypes = getInlineReturnTypeSet();
570 if (!resTypes->hasType(types::Type::ObjectType(thisType)))
571 return InliningStatus_NotInlined;
572
573 for (unsigned i = 0; i < argTypes->getObjectCount(); i++) {
574 types::TypeObjectKey *argType = argTypes->getObject(i);
575 if (!argType)
576 continue;
577
578 if (argType->unknownProperties())
579 return InliningStatus_NotInlined;
580
581 types::HeapTypeSetKey elemTypes = argType->property(JSID_VOID);
582 if (!elemTypes.knownSubset(constraints(), thisElemTypes))
583 return InliningStatus_NotInlined;
584 }
585
586 // Inline the call.
587 JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
588 if (!templateObj || templateObj->type() != baseThisType)
589 return InliningStatus_NotInlined;
590 JS_ASSERT(templateObj->is<ArrayObject>());
591
592 callInfo.setImplicitlyUsedUnchecked();
593
594 MArrayConcat *ins = MArrayConcat::New(alloc(), constraints(), callInfo.thisArg(), callInfo.getArg(0),
595 templateObj, templateObj->type()->initialHeap(constraints()));
596 current->add(ins);
597 current->push(ins);
598
599 if (!resumeAfter(ins))
600 return InliningStatus_Error;
601 return InliningStatus_Inlined;
602 }
603
604 IonBuilder::InliningStatus
605 IonBuilder::inlineMathAbs(CallInfo &callInfo)
606 {
607 if (callInfo.constructing())
608 return InliningStatus_NotInlined;
609
610 if (callInfo.argc() != 1)
611 return InliningStatus_NotInlined;
612
613 MIRType returnType = getInlineReturnType();
614 MIRType argType = callInfo.getArg(0)->type();
615 if (!IsNumberType(argType))
616 return InliningStatus_NotInlined;
617
618 // Either argType == returnType, or
619 // argType == Double or Float32, returnType == Int, or
620 // argType == Float32, returnType == Double
621 if (argType != returnType && !(IsFloatingPointType(argType) && returnType == MIRType_Int32)
622 && !(argType == MIRType_Float32 && returnType == MIRType_Double))
623 {
624 return InliningStatus_NotInlined;
625 }
626
627 callInfo.setImplicitlyUsedUnchecked();
628
629 // If the arg is a Float32, we specialize the op as double, it will be specialized
630 // as float32 if necessary later.
631 MIRType absType = (argType == MIRType_Float32) ? MIRType_Double : argType;
632 MInstruction *ins = MAbs::New(alloc(), callInfo.getArg(0), absType);
633 current->add(ins);
634
635 current->push(ins);
636 return InliningStatus_Inlined;
637 }
638
639 IonBuilder::InliningStatus
640 IonBuilder::inlineMathFloor(CallInfo &callInfo)
641 {
642 if (callInfo.constructing())
643 return InliningStatus_NotInlined;
644
645 if (callInfo.argc() != 1)
646 return InliningStatus_NotInlined;
647
648 MIRType argType = callInfo.getArg(0)->type();
649 MIRType returnType = getInlineReturnType();
650
651 // Math.floor(int(x)) == int(x)
652 if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
653 callInfo.setImplicitlyUsedUnchecked();
654 current->push(callInfo.getArg(0));
655 return InliningStatus_Inlined;
656 }
657
658 if (IsFloatingPointType(argType) && returnType == MIRType_Int32) {
659 callInfo.setImplicitlyUsedUnchecked();
660 MFloor *ins = MFloor::New(alloc(), callInfo.getArg(0));
661 current->add(ins);
662 current->push(ins);
663 return InliningStatus_Inlined;
664 }
665
666 if (IsFloatingPointType(argType) && returnType == MIRType_Double) {
667 callInfo.setImplicitlyUsedUnchecked();
668 MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor, nullptr);
669 current->add(ins);
670 current->push(ins);
671 return InliningStatus_Inlined;
672 }
673
674 return InliningStatus_NotInlined;
675 }
676
677 IonBuilder::InliningStatus
678 IonBuilder::inlineMathCeil(CallInfo &callInfo)
679 {
680 if (callInfo.constructing())
681 return InliningStatus_NotInlined;
682
683 if (callInfo.argc() != 1)
684 return InliningStatus_NotInlined;
685
686 MIRType argType = callInfo.getArg(0)->type();
687 MIRType returnType = getInlineReturnType();
688
689 // Math.ceil(int(x)) == int(x)
690 if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
691 callInfo.setImplicitlyUsedUnchecked();
692 current->push(callInfo.getArg(0));
693 return InliningStatus_Inlined;
694 }
695
696 if (IsFloatingPointType(argType) && returnType == MIRType_Double) {
697 callInfo.setImplicitlyUsedUnchecked();
698 MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil, nullptr);
699 current->add(ins);
700 current->push(ins);
701 return InliningStatus_Inlined;
702 }
703
704 return InliningStatus_NotInlined;
705 }
706
707 IonBuilder::InliningStatus
708 IonBuilder::inlineMathRound(CallInfo &callInfo)
709 {
710 if (callInfo.constructing())
711 return InliningStatus_NotInlined;
712
713 if (callInfo.argc() != 1)
714 return InliningStatus_NotInlined;
715
716 MIRType returnType = getInlineReturnType();
717 MIRType argType = callInfo.getArg(0)->type();
718
719 // Math.round(int(x)) == int(x)
720 if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
721 callInfo.setImplicitlyUsedUnchecked();
722 current->push(callInfo.getArg(0));
723 return InliningStatus_Inlined;
724 }
725
726 if (IsFloatingPointType(argType) && returnType == MIRType_Int32) {
727 callInfo.setImplicitlyUsedUnchecked();
728 MRound *ins = MRound::New(alloc(), callInfo.getArg(0));
729 current->add(ins);
730 current->push(ins);
731 return InliningStatus_Inlined;
732 }
733
734 if (IsFloatingPointType(argType) && returnType == MIRType_Double) {
735 callInfo.setImplicitlyUsedUnchecked();
736 MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round, nullptr);
737 current->add(ins);
738 current->push(ins);
739 return InliningStatus_Inlined;
740 }
741
742 return InliningStatus_NotInlined;
743 }
744
745 IonBuilder::InliningStatus
746 IonBuilder::inlineMathSqrt(CallInfo &callInfo)
747 {
748 if (callInfo.constructing())
749 return InliningStatus_NotInlined;
750
751 if (callInfo.argc() != 1)
752 return InliningStatus_NotInlined;
753
754 MIRType argType = callInfo.getArg(0)->type();
755 if (getInlineReturnType() != MIRType_Double)
756 return InliningStatus_NotInlined;
757 if (!IsNumberType(argType))
758 return InliningStatus_NotInlined;
759
760 callInfo.setImplicitlyUsedUnchecked();
761
762 MSqrt *sqrt = MSqrt::New(alloc(), callInfo.getArg(0));
763 current->add(sqrt);
764 current->push(sqrt);
765 return InliningStatus_Inlined;
766 }
767
768 IonBuilder::InliningStatus
769 IonBuilder::inlineMathAtan2(CallInfo &callInfo)
770 {
771 if (callInfo.constructing())
772 return InliningStatus_NotInlined;
773
774 if (callInfo.argc() != 2)
775 return InliningStatus_NotInlined;
776
777 if (getInlineReturnType() != MIRType_Double)
778 return InliningStatus_NotInlined;
779
780 MIRType argType0 = callInfo.getArg(0)->type();
781 MIRType argType1 = callInfo.getArg(1)->type();
782
783 if (!IsNumberType(argType0) || !IsNumberType(argType1))
784 return InliningStatus_NotInlined;
785
786 callInfo.setImplicitlyUsedUnchecked();
787
788 MAtan2 *atan2 = MAtan2::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
789 current->add(atan2);
790 current->push(atan2);
791 return InliningStatus_Inlined;
792 }
793
794 IonBuilder::InliningStatus
795 IonBuilder::inlineMathHypot(CallInfo &callInfo)
796 {
797 if (callInfo.constructing())
798 return InliningStatus_NotInlined;
799
800 if (callInfo.argc() != 2)
801 return InliningStatus_NotInlined;
802
803 if (getInlineReturnType() != MIRType_Double)
804 return InliningStatus_NotInlined;
805
806 MIRType argType0 = callInfo.getArg(0)->type();
807 MIRType argType1 = callInfo.getArg(1)->type();
808
809 if (!IsNumberType(argType0) || !IsNumberType(argType1))
810 return InliningStatus_NotInlined;
811
812 callInfo.setImplicitlyUsedUnchecked();
813
814 MHypot *hypot = MHypot::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
815 current->add(hypot);
816 current->push(hypot);
817 return InliningStatus_Inlined;
818 }
819
820 IonBuilder::InliningStatus
821 IonBuilder::inlineMathPow(CallInfo &callInfo)
822 {
823 if (callInfo.constructing())
824 return InliningStatus_NotInlined;
825
826 if (callInfo.argc() != 2)
827 return InliningStatus_NotInlined;
828
829 // Typechecking.
830 MIRType baseType = callInfo.getArg(0)->type();
831 MIRType powerType = callInfo.getArg(1)->type();
832 MIRType outputType = getInlineReturnType();
833
834 if (outputType != MIRType_Int32 && outputType != MIRType_Double)
835 return InliningStatus_NotInlined;
836 if (!IsNumberType(baseType))
837 return InliningStatus_NotInlined;
838 if (!IsNumberType(powerType))
839 return InliningStatus_NotInlined;
840
841 callInfo.setImplicitlyUsedUnchecked();
842
843 MDefinition *base = callInfo.getArg(0);
844 MDefinition *power = callInfo.getArg(1);
845 MDefinition *output = nullptr;
846
847 // Optimize some constant powers.
848 if (callInfo.getArg(1)->isConstant() &&
849 callInfo.getArg(1)->toConstant()->value().isNumber())
850 {
851 double pow = callInfo.getArg(1)->toConstant()->value().toNumber();
852
853 // Math.pow(x, 0.5) is a sqrt with edge-case detection.
854 if (pow == 0.5) {
855 MPowHalf *half = MPowHalf::New(alloc(), base);
856 current->add(half);
857 output = half;
858 }
859
860 // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases.
861 if (pow == -0.5) {
862 MPowHalf *half = MPowHalf::New(alloc(), base);
863 current->add(half);
864 MConstant *one = MConstant::New(alloc(), DoubleValue(1.0));
865 current->add(one);
866 MDiv *div = MDiv::New(alloc(), one, half, MIRType_Double);
867 current->add(div);
868 output = div;
869 }
870
871 // Math.pow(x, 1) == x.
872 if (pow == 1.0)
873 output = base;
874
875 // Math.pow(x, 2) == x*x.
876 if (pow == 2.0) {
877 MMul *mul = MMul::New(alloc(), base, base, outputType);
878 current->add(mul);
879 output = mul;
880 }
881
882 // Math.pow(x, 3) == x*x*x.
883 if (pow == 3.0) {
884 MMul *mul1 = MMul::New(alloc(), base, base, outputType);
885 current->add(mul1);
886 MMul *mul2 = MMul::New(alloc(), base, mul1, outputType);
887 current->add(mul2);
888 output = mul2;
889 }
890
891 // Math.pow(x, 4) == y*y, where y = x*x.
892 if (pow == 4.0) {
893 MMul *y = MMul::New(alloc(), base, base, outputType);
894 current->add(y);
895 MMul *mul = MMul::New(alloc(), y, y, outputType);
896 current->add(mul);
897 output = mul;
898 }
899 }
900
901 // Use MPow for other powers
902 if (!output) {
903 if (powerType == MIRType_Float32)
904 powerType = MIRType_Double;
905 MPow *pow = MPow::New(alloc(), base, power, powerType);
906 current->add(pow);
907 output = pow;
908 }
909
910 // Cast to the right type
911 if (outputType == MIRType_Int32 && output->type() != MIRType_Int32) {
912 MToInt32 *toInt = MToInt32::New(alloc(), output);
913 current->add(toInt);
914 output = toInt;
915 }
916 if (outputType == MIRType_Double && output->type() != MIRType_Double) {
917 MToDouble *toDouble = MToDouble::New(alloc(), output);
918 current->add(toDouble);
919 output = toDouble;
920 }
921
922 current->push(output);
923 return InliningStatus_Inlined;
924 }
925
926 IonBuilder::InliningStatus
927 IonBuilder::inlineMathRandom(CallInfo &callInfo)
928 {
929 if (callInfo.constructing())
930 return InliningStatus_NotInlined;
931
932 if (getInlineReturnType() != MIRType_Double)
933 return InliningStatus_NotInlined;
934
935 callInfo.setImplicitlyUsedUnchecked();
936
937 MRandom *rand = MRandom::New(alloc());
938 current->add(rand);
939 current->push(rand);
940 return InliningStatus_Inlined;
941 }
942
943 IonBuilder::InliningStatus
944 IonBuilder::inlineMathImul(CallInfo &callInfo)
945 {
946 if (callInfo.argc() != 2 || callInfo.constructing())
947 return InliningStatus_NotInlined;
948
949 MIRType returnType = getInlineReturnType();
950 if (returnType != MIRType_Int32)
951 return InliningStatus_NotInlined;
952
953 if (!IsNumberType(callInfo.getArg(0)->type()))
954 return InliningStatus_NotInlined;
955 if (!IsNumberType(callInfo.getArg(1)->type()))
956 return InliningStatus_NotInlined;
957
958 callInfo.setImplicitlyUsedUnchecked();
959
960 MInstruction *first = MTruncateToInt32::New(alloc(), callInfo.getArg(0));
961 current->add(first);
962
963 MInstruction *second = MTruncateToInt32::New(alloc(), callInfo.getArg(1));
964 current->add(second);
965
966 MMul *ins = MMul::New(alloc(), first, second, MIRType_Int32, MMul::Integer);
967 current->add(ins);
968 current->push(ins);
969 return InliningStatus_Inlined;
970 }
971
972 IonBuilder::InliningStatus
973 IonBuilder::inlineMathFRound(CallInfo &callInfo)
974 {
975 if (!LIRGenerator::allowFloat32Optimizations())
976 return InliningStatus_NotInlined;
977
978 if (callInfo.argc() != 1 || callInfo.constructing())
979 return InliningStatus_NotInlined;
980
981 // MIRType can't be Float32, as this point, as getInlineReturnType uses JSVal types
982 // to infer the returned MIR type.
983 types::TemporaryTypeSet *returned = getInlineReturnTypeSet();
984 if (returned->empty()) {
985 // As there's only one possible returned type, just add it to the observed
986 // returned typeset
987 returned->addType(types::Type::DoubleType(), alloc_->lifoAlloc());
988 } else {
989 MIRType returnType = getInlineReturnType();
990 if (!IsNumberType(returnType))
991 return InliningStatus_NotInlined;
992 }
993
994 MIRType arg = callInfo.getArg(0)->type();
995 if (!IsNumberType(arg))
996 return InliningStatus_NotInlined;
997
998 callInfo.setImplicitlyUsedUnchecked();
999
1000 MToFloat32 *ins = MToFloat32::New(alloc(), callInfo.getArg(0));
1001 current->add(ins);
1002 current->push(ins);
1003 return InliningStatus_Inlined;
1004 }
1005
1006 IonBuilder::InliningStatus
1007 IonBuilder::inlineMathMinMax(CallInfo &callInfo, bool max)
1008 {
1009 if (callInfo.argc() < 2 || callInfo.constructing())
1010 return InliningStatus_NotInlined;
1011
1012 MIRType returnType = getInlineReturnType();
1013 if (!IsNumberType(returnType))
1014 return InliningStatus_NotInlined;
1015
1016 for (unsigned i = 0; i < callInfo.argc(); i++) {
1017 MIRType argType = callInfo.getArg(i)->type();
1018 if (!IsNumberType(argType))
1019 return InliningStatus_NotInlined;
1020
1021 // When one of the arguments is double, do a double MMinMax.
1022 if (returnType == MIRType_Int32 && IsFloatingPointType(argType))
1023 returnType = MIRType_Double;
1024 }
1025
1026 callInfo.setImplicitlyUsedUnchecked();
1027
1028 // Chain N-1 MMinMax instructions to compute the MinMax.
1029 MMinMax *last = MMinMax::New(alloc(), callInfo.getArg(0), callInfo.getArg(1), returnType, max);
1030 current->add(last);
1031
1032 for (unsigned i = 2; i < callInfo.argc(); i++) {
1033 MMinMax *ins = MMinMax::New(alloc(), last, callInfo.getArg(i), returnType, max);
1034 current->add(ins);
1035 last = ins;
1036 }
1037
1038 current->push(last);
1039 return InliningStatus_Inlined;
1040 }
1041
1042 IonBuilder::InliningStatus
1043 IonBuilder::inlineStringObject(CallInfo &callInfo)
1044 {
1045 if (callInfo.argc() != 1 || !callInfo.constructing())
1046 return InliningStatus_NotInlined;
1047
1048 // ConvertToString doesn't support objects.
1049 if (callInfo.getArg(0)->mightBeType(MIRType_Object))
1050 return InliningStatus_NotInlined;
1051
1052 JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js_String);
1053 if (!templateObj)
1054 return InliningStatus_NotInlined;
1055 JS_ASSERT(templateObj->is<StringObject>());
1056
1057 callInfo.setImplicitlyUsedUnchecked();
1058
1059 MNewStringObject *ins = MNewStringObject::New(alloc(), callInfo.getArg(0), templateObj);
1060 current->add(ins);
1061 current->push(ins);
1062
1063 if (!resumeAfter(ins))
1064 return InliningStatus_Error;
1065
1066 return InliningStatus_Inlined;
1067 }
1068
1069 IonBuilder::InliningStatus
1070 IonBuilder::inlineStringSplit(CallInfo &callInfo)
1071 {
1072 if (callInfo.argc() != 1 || callInfo.constructing())
1073 return InliningStatus_NotInlined;
1074 if (callInfo.thisArg()->type() != MIRType_String)
1075 return InliningStatus_NotInlined;
1076 if (callInfo.getArg(0)->type() != MIRType_String)
1077 return InliningStatus_NotInlined;
1078
1079 JSObject *templateObject = inspector->getTemplateObjectForNative(pc, js::str_split);
1080 if (!templateObject)
1081 return InliningStatus_NotInlined;
1082 JS_ASSERT(templateObject->is<ArrayObject>());
1083
1084 types::TypeObjectKey *retType = types::TypeObjectKey::get(templateObject);
1085 if (retType->unknownProperties())
1086 return InliningStatus_NotInlined;
1087
1088 types::HeapTypeSetKey key = retType->property(JSID_VOID);
1089 if (!key.maybeTypes())
1090 return InliningStatus_NotInlined;
1091
1092 if (!key.maybeTypes()->hasType(types::Type::StringType())) {
1093 key.freeze(constraints());
1094 return InliningStatus_NotInlined;
1095 }
1096
1097 callInfo.setImplicitlyUsedUnchecked();
1098
1099 MStringSplit *ins = MStringSplit::New(alloc(), constraints(), callInfo.thisArg(),
1100 callInfo.getArg(0), templateObject);
1101 current->add(ins);
1102 current->push(ins);
1103
1104 return InliningStatus_Inlined;
1105 }
1106
1107 IonBuilder::InliningStatus
1108 IonBuilder::inlineStrCharCodeAt(CallInfo &callInfo)
1109 {
1110 if (callInfo.argc() != 1 || callInfo.constructing())
1111 return InliningStatus_NotInlined;
1112
1113 if (getInlineReturnType() != MIRType_Int32)
1114 return InliningStatus_NotInlined;
1115 if (callInfo.thisArg()->type() != MIRType_String && callInfo.thisArg()->type() != MIRType_Value)
1116 return InliningStatus_NotInlined;
1117 MIRType argType = callInfo.getArg(0)->type();
1118 if (argType != MIRType_Int32 && argType != MIRType_Double)
1119 return InliningStatus_NotInlined;
1120
1121 callInfo.setImplicitlyUsedUnchecked();
1122
1123 MInstruction *index = MToInt32::New(alloc(), callInfo.getArg(0));
1124 current->add(index);
1125
1126 MStringLength *length = MStringLength::New(alloc(), callInfo.thisArg());
1127 current->add(length);
1128
1129 index = addBoundsCheck(index, length);
1130
1131 MCharCodeAt *charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
1132 current->add(charCode);
1133 current->push(charCode);
1134 return InliningStatus_Inlined;
1135 }
1136
1137 IonBuilder::InliningStatus
1138 IonBuilder::inlineStrFromCharCode(CallInfo &callInfo)
1139 {
1140 if (callInfo.argc() != 1 || callInfo.constructing())
1141 return InliningStatus_NotInlined;
1142
1143 if (getInlineReturnType() != MIRType_String)
1144 return InliningStatus_NotInlined;
1145 if (callInfo.getArg(0)->type() != MIRType_Int32)
1146 return InliningStatus_NotInlined;
1147
1148 callInfo.setImplicitlyUsedUnchecked();
1149
1150 MToInt32 *charCode = MToInt32::New(alloc(), callInfo.getArg(0));
1151 current->add(charCode);
1152
1153 MFromCharCode *string = MFromCharCode::New(alloc(), charCode);
1154 current->add(string);
1155 current->push(string);
1156 return InliningStatus_Inlined;
1157 }
1158
1159 IonBuilder::InliningStatus
1160 IonBuilder::inlineStrCharAt(CallInfo &callInfo)
1161 {
1162 if (callInfo.argc() != 1 || callInfo.constructing())
1163 return InliningStatus_NotInlined;
1164
1165 if (getInlineReturnType() != MIRType_String)
1166 return InliningStatus_NotInlined;
1167 if (callInfo.thisArg()->type() != MIRType_String)
1168 return InliningStatus_NotInlined;
1169 MIRType argType = callInfo.getArg(0)->type();
1170 if (argType != MIRType_Int32 && argType != MIRType_Double)
1171 return InliningStatus_NotInlined;
1172
1173 callInfo.setImplicitlyUsedUnchecked();
1174
1175 MInstruction *index = MToInt32::New(alloc(), callInfo.getArg(0));
1176 current->add(index);
1177
1178 MStringLength *length = MStringLength::New(alloc(), callInfo.thisArg());
1179 current->add(length);
1180
1181 index = addBoundsCheck(index, length);
1182
1183 // String.charAt(x) = String.fromCharCode(String.charCodeAt(x))
1184 MCharCodeAt *charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
1185 current->add(charCode);
1186
1187 MFromCharCode *string = MFromCharCode::New(alloc(), charCode);
1188 current->add(string);
1189 current->push(string);
1190 return InliningStatus_Inlined;
1191 }
1192
1193 IonBuilder::InliningStatus
1194 IonBuilder::inlineRegExpExec(CallInfo &callInfo)
1195 {
1196 if (callInfo.argc() != 1 || callInfo.constructing())
1197 return InliningStatus_NotInlined;
1198
1199 if (callInfo.thisArg()->type() != MIRType_Object)
1200 return InliningStatus_NotInlined;
1201
1202 types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
1203 const Class *clasp = thisTypes ? thisTypes->getKnownClass() : nullptr;
1204 if (clasp != &RegExpObject::class_)
1205 return InliningStatus_NotInlined;
1206
1207 if (callInfo.getArg(0)->mightBeType(MIRType_Object))
1208 return InliningStatus_NotInlined;
1209
1210 callInfo.setImplicitlyUsedUnchecked();
1211
1212 MInstruction *exec = MRegExpExec::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
1213 current->add(exec);
1214 current->push(exec);
1215
1216 if (!resumeAfter(exec))
1217 return InliningStatus_Error;
1218
1219 if (!pushTypeBarrier(exec, getInlineReturnTypeSet(), true))
1220 return InliningStatus_Error;
1221
1222 return InliningStatus_Inlined;
1223 }
1224
1225 IonBuilder::InliningStatus
1226 IonBuilder::inlineRegExpTest(CallInfo &callInfo)
1227 {
1228 if (callInfo.argc() != 1 || callInfo.constructing())
1229 return InliningStatus_NotInlined;
1230
1231 // TI can infer a nullptr return type of regexp_test with eager compilation.
1232 if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean)
1233 return InliningStatus_NotInlined;
1234
1235 if (callInfo.thisArg()->type() != MIRType_Object)
1236 return InliningStatus_NotInlined;
1237 types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
1238 const Class *clasp = thisTypes ? thisTypes->getKnownClass() : nullptr;
1239 if (clasp != &RegExpObject::class_)
1240 return InliningStatus_NotInlined;
1241 if (callInfo.getArg(0)->mightBeType(MIRType_Object))
1242 return InliningStatus_NotInlined;
1243
1244 callInfo.setImplicitlyUsedUnchecked();
1245
1246 MInstruction *match = MRegExpTest::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
1247 current->add(match);
1248 current->push(match);
1249 if (!resumeAfter(match))
1250 return InliningStatus_Error;
1251
1252 return InliningStatus_Inlined;
1253 }
1254
1255 IonBuilder::InliningStatus
1256 IonBuilder::inlineStrReplace(CallInfo &callInfo)
1257 {
1258 if (callInfo.argc() != 2 || callInfo.constructing())
1259 return InliningStatus_NotInlined;
1260
1261 // Return: String.
1262 if (getInlineReturnType() != MIRType_String)
1263 return InliningStatus_NotInlined;
1264
1265 // This: String.
1266 if (callInfo.thisArg()->type() != MIRType_String)
1267 return InliningStatus_NotInlined;
1268
1269 // Arg 0: RegExp.
1270 types::TemporaryTypeSet *arg0Type = callInfo.getArg(0)->resultTypeSet();
1271 const Class *clasp = arg0Type ? arg0Type->getKnownClass() : nullptr;
1272 if (clasp != &RegExpObject::class_ && callInfo.getArg(0)->type() != MIRType_String)
1273 return InliningStatus_NotInlined;
1274
1275 // Arg 1: String.
1276 if (callInfo.getArg(1)->type() != MIRType_String)
1277 return InliningStatus_NotInlined;
1278
1279 callInfo.setImplicitlyUsedUnchecked();
1280
1281 MInstruction *cte;
1282 if (callInfo.getArg(0)->type() == MIRType_String) {
1283 cte = MStringReplace::New(alloc(), callInfo.thisArg(), callInfo.getArg(0),
1284 callInfo.getArg(1));
1285 } else {
1286 cte = MRegExpReplace::New(alloc(), callInfo.thisArg(), callInfo.getArg(0),
1287 callInfo.getArg(1));
1288 }
1289 current->add(cte);
1290 current->push(cte);
1291 if (cte->isEffectful() && !resumeAfter(cte))
1292 return InliningStatus_Error;
1293 return InliningStatus_Inlined;
1294 }
1295
1296 IonBuilder::InliningStatus
1297 IonBuilder::inlineUnsafePutElements(CallInfo &callInfo)
1298 {
1299 uint32_t argc = callInfo.argc();
1300 if (argc < 3 || (argc % 3) != 0 || callInfo.constructing())
1301 return InliningStatus_NotInlined;
1302
1303 /* Important:
1304 *
1305 * Here we inline each of the stores resulting from a call to
1306 * UnsafePutElements(). It is essential that these stores occur
1307 * atomically and cannot be interrupted by a stack or recursion
1308 * check. If this is not true, race conditions can occur.
1309 */
1310
1311 for (uint32_t base = 0; base < argc; base += 3) {
1312 uint32_t arri = base + 0;
1313 uint32_t idxi = base + 1;
1314 uint32_t elemi = base + 2;
1315
1316 MDefinition *obj = callInfo.getArg(arri);
1317 MDefinition *id = callInfo.getArg(idxi);
1318 MDefinition *elem = callInfo.getArg(elemi);
1319
1320 bool isDenseNative = ElementAccessIsDenseNative(obj, id);
1321
1322 bool writeNeedsBarrier = false;
1323 if (isDenseNative) {
1324 writeNeedsBarrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
1325 &obj, nullptr, &elem,
1326 /* canModify = */ false);
1327 }
1328
1329 // We can only inline setelem on dense arrays that do not need type
1330 // barriers and on typed arrays and on typed object arrays.
1331 ScalarTypeDescr::Type arrayType;
1332 if ((!isDenseNative || writeNeedsBarrier) &&
1333 !ElementAccessIsTypedArray(obj, id, &arrayType) &&
1334 !elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType))
1335 {
1336 return InliningStatus_NotInlined;
1337 }
1338 }
1339
1340 callInfo.setImplicitlyUsedUnchecked();
1341
1342 // Push the result first so that the stack depth matches up for
1343 // the potential bailouts that will occur in the stores below.
1344 MConstant *udef = MConstant::New(alloc(), UndefinedValue());
1345 current->add(udef);
1346 current->push(udef);
1347
1348 for (uint32_t base = 0; base < argc; base += 3) {
1349 uint32_t arri = base + 0;
1350 uint32_t idxi = base + 1;
1351
1352 MDefinition *obj = callInfo.getArg(arri);
1353 MDefinition *id = callInfo.getArg(idxi);
1354
1355 if (ElementAccessIsDenseNative(obj, id)) {
1356 if (!inlineUnsafeSetDenseArrayElement(callInfo, base))
1357 return InliningStatus_Error;
1358 continue;
1359 }
1360
1361 ScalarTypeDescr::Type arrayType;
1362 if (ElementAccessIsTypedArray(obj, id, &arrayType)) {
1363 if (!inlineUnsafeSetTypedArrayElement(callInfo, base, arrayType))
1364 return InliningStatus_Error;
1365 continue;
1366 }
1367
1368 if (elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType)) {
1369 if (!inlineUnsafeSetTypedObjectArrayElement(callInfo, base, arrayType))
1370 return InliningStatus_Error;
1371 continue;
1372 }
1373
1374 MOZ_ASSUME_UNREACHABLE("Element access not dense array nor typed array");
1375 }
1376
1377 return InliningStatus_Inlined;
1378 }
1379
1380 bool
1381 IonBuilder::elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id,
1382 ScalarTypeDescr::Type *arrayType)
1383 {
1384 if (obj->type() != MIRType_Object) // lookupTypeDescrSet() tests for TypedObject
1385 return false;
1386
1387 if (id->type() != MIRType_Int32 && id->type() != MIRType_Double)
1388 return false;
1389
1390 TypeDescrSet objDescrs;
1391 if (!lookupTypeDescrSet(obj, &objDescrs))
1392 return false;
1393
1394 if (!objDescrs.allOfArrayKind())
1395 return false;
1396
1397 TypeDescrSet elemDescrs;
1398 if (!objDescrs.arrayElementType(*this, &elemDescrs))
1399 return false;
1400
1401 if (elemDescrs.empty() || elemDescrs.kind() != TypeDescr::Scalar)
1402 return false;
1403
1404 JS_ASSERT(TypeDescr::isSized(elemDescrs.kind()));
1405
1406 return elemDescrs.scalarType(arrayType);
1407 }
1408
1409 bool
1410 IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base)
1411 {
1412 // Note: we do not check the conditions that are asserted as true
1413 // in intrinsic_UnsafePutElements():
1414 // - arr is a dense array
1415 // - idx < initialized length
1416 // Furthermore, note that inlineUnsafePutElements ensures the type of the
1417 // value is reflected in the JSID_VOID property of the array.
1418
1419 MDefinition *obj = callInfo.getArg(base + 0);
1420 MDefinition *id = callInfo.getArg(base + 1);
1421 MDefinition *elem = callInfo.getArg(base + 2);
1422
1423 types::TemporaryTypeSet::DoubleConversion conversion =
1424 obj->resultTypeSet()->convertDoubleElements(constraints());
1425 if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem))
1426 return false;
1427 return true;
1428 }
1429
1430 bool
1431 IonBuilder::inlineUnsafeSetTypedArrayElement(CallInfo &callInfo,
1432 uint32_t base,
1433 ScalarTypeDescr::Type arrayType)
1434 {
1435 // Note: we do not check the conditions that are asserted as true
1436 // in intrinsic_UnsafePutElements():
1437 // - arr is a typed array
1438 // - idx < length
1439
1440 MDefinition *obj = callInfo.getArg(base + 0);
1441 MDefinition *id = callInfo.getArg(base + 1);
1442 MDefinition *elem = callInfo.getArg(base + 2);
1443
1444 if (!jsop_setelem_typed(arrayType, SetElem_Unsafe, obj, id, elem))
1445 return false;
1446
1447 return true;
1448 }
1449
1450 bool
1451 IonBuilder::inlineUnsafeSetTypedObjectArrayElement(CallInfo &callInfo,
1452 uint32_t base,
1453 ScalarTypeDescr::Type arrayType)
1454 {
1455 // Note: we do not check the conditions that are asserted as true
1456 // in intrinsic_UnsafePutElements():
1457 // - arr is a typed array
1458 // - idx < length
1459
1460 MDefinition *obj = callInfo.getArg(base + 0);
1461 MDefinition *id = callInfo.getArg(base + 1);
1462 MDefinition *elem = callInfo.getArg(base + 2);
1463
1464 if (!jsop_setelem_typed_object(arrayType, SetElem_Unsafe, true, obj, id, elem))
1465 return false;
1466
1467 return true;
1468 }
1469
1470 IonBuilder::InliningStatus
1471 IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo)
1472 {
1473 if (callInfo.constructing())
1474 return InliningStatus_NotInlined;
1475
1476 ExecutionMode executionMode = info().executionMode();
1477 switch (executionMode) {
1478 case ParallelExecution: {
1479 // During Parallel Exec, we always force sequential, so
1480 // replace with true. This permits UCE to eliminate the
1481 // entire path as dead, which is important.
1482 callInfo.setImplicitlyUsedUnchecked();
1483 MConstant *ins = MConstant::New(alloc(), BooleanValue(true));
1484 current->add(ins);
1485 current->push(ins);
1486 return InliningStatus_Inlined;
1487 }
1488
1489 default:
1490 // In sequential mode, leave as is, because we'd have to
1491 // access the "in warmup" flag of the runtime.
1492 return InliningStatus_NotInlined;
1493 }
1494
1495 MOZ_ASSUME_UNREACHABLE("Invalid execution mode");
1496 }
1497
1498 IonBuilder::InliningStatus
1499 IonBuilder::inlineForkJoinGetSlice(CallInfo &callInfo)
1500 {
1501 if (info().executionMode() != ParallelExecution)
1502 return InliningStatus_NotInlined;
1503
1504 // Assert the way the function is used instead of testing, as it is a
1505 // self-hosted function which must be used in a particular fashion.
1506 MOZ_ASSERT(callInfo.argc() == 1 && !callInfo.constructing());
1507 MOZ_ASSERT(callInfo.getArg(0)->type() == MIRType_Int32);
1508
1509 // Test this, as we might have not executed the native despite knowing the
1510 // target here.
1511 if (getInlineReturnType() != MIRType_Int32)
1512 return InliningStatus_NotInlined;
1513
1514 callInfo.setImplicitlyUsedUnchecked();
1515
1516 switch (info().executionMode()) {
1517 case ParallelExecution:
1518 if (LIRGenerator::allowInlineForkJoinGetSlice()) {
1519 MForkJoinGetSlice *getSlice = MForkJoinGetSlice::New(alloc(),
1520 graph().forkJoinContext());
1521 current->add(getSlice);
1522 current->push(getSlice);
1523 return InliningStatus_Inlined;
1524 }
1525 return InliningStatus_NotInlined;
1526
1527 default:
1528 // ForkJoinGetSlice acts as identity for sequential execution.
1529 current->push(callInfo.getArg(0));
1530 return InliningStatus_Inlined;
1531 }
1532
1533 MOZ_ASSUME_UNREACHABLE("Invalid execution mode");
1534 }
1535
1536 IonBuilder::InliningStatus
1537 IonBuilder::inlineNewDenseArray(CallInfo &callInfo)
1538 {
1539 if (callInfo.constructing() || callInfo.argc() != 1)
1540 return InliningStatus_NotInlined;
1541
1542 // For now, in seq. mode we just call the C function. In
1543 // par. mode we use inlined MIR.
1544 ExecutionMode executionMode = info().executionMode();
1545 switch (executionMode) {
1546 case ParallelExecution:
1547 return inlineNewDenseArrayForParallelExecution(callInfo);
1548 default:
1549 return inlineNewDenseArrayForSequentialExecution(callInfo);
1550 }
1551
1552 MOZ_ASSUME_UNREACHABLE("unknown ExecutionMode");
1553 }
1554
1555 IonBuilder::InliningStatus
1556 IonBuilder::inlineNewDenseArrayForSequentialExecution(CallInfo &callInfo)
1557 {
1558 // not yet implemented; in seq. mode the C function is not so bad
1559 return InliningStatus_NotInlined;
1560 }
1561
1562 IonBuilder::InliningStatus
1563 IonBuilder::inlineNewDenseArrayForParallelExecution(CallInfo &callInfo)
1564 {
1565 // Create the new parallel array object. Parallel arrays have specially
1566 // constructed type objects, so we can only perform the inlining if we
1567 // already have one of these type objects.
1568 types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
1569 if (returnTypes->getKnownMIRType() != MIRType_Object)
1570 return InliningStatus_NotInlined;
1571 if (returnTypes->unknownObject() || returnTypes->getObjectCount() != 1)
1572 return InliningStatus_NotInlined;
1573 if (callInfo.getArg(0)->type() != MIRType_Int32)
1574 return InliningStatus_NotInlined;
1575 types::TypeObject *typeObject = returnTypes->getTypeObject(0);
1576
1577 JSObject *templateObject = inspector->getTemplateObjectForNative(pc, intrinsic_NewDenseArray);
1578 if (!templateObject || templateObject->type() != typeObject)
1579 return InliningStatus_NotInlined;
1580
1581 callInfo.setImplicitlyUsedUnchecked();
1582
1583 MNewDenseArrayPar *newObject = MNewDenseArrayPar::New(alloc(),
1584 graph().forkJoinContext(),
1585 callInfo.getArg(0),
1586 templateObject);
1587 current->add(newObject);
1588 current->push(newObject);
1589
1590 return InliningStatus_Inlined;
1591 }
1592
1593 IonBuilder::InliningStatus
1594 IonBuilder::inlineHasClasses(CallInfo &callInfo, const Class *clasp1, const Class *clasp2)
1595 {
1596 // Thus far there has been no reason to complicate this beyond two classes,
1597 // though it generalizes pretty well.
1598 // clasp2 may be NULL.
1599 if (callInfo.constructing() || callInfo.argc() != 1)
1600 return InliningStatus_NotInlined;
1601
1602 if (callInfo.getArg(0)->type() != MIRType_Object)
1603 return InliningStatus_NotInlined;
1604 if (getInlineReturnType() != MIRType_Boolean)
1605 return InliningStatus_NotInlined;
1606
1607 types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet();
1608 const Class *knownClass = types ? types->getKnownClass() : nullptr;
1609 if (knownClass) {
1610 pushConstant(BooleanValue(knownClass == clasp1 || knownClass == clasp2));
1611 } else {
1612 MHasClass *hasClass1 = MHasClass::New(alloc(), callInfo.getArg(0), clasp1);
1613 current->add(hasClass1);
1614 if (clasp2 == nullptr) {
1615 current->push(hasClass1);
1616 } else {
1617 // The following turns into branch-free, box-free code on x86, and should do so on ARM.
1618 MHasClass *hasClass2 = MHasClass::New(alloc(), callInfo.getArg(0), clasp2);
1619 current->add(hasClass2);
1620 MBitOr *either = MBitOr::New(alloc(), hasClass1, hasClass2);
1621 either->infer(inspector, pc);
1622 current->add(either);
1623 // Convert to bool with the '!!' idiom
1624 MNot *resultInverted = MNot::New(alloc(), either);
1625 resultInverted->infer();
1626 current->add(resultInverted);
1627 MNot *result = MNot::New(alloc(), resultInverted);
1628 result->infer();
1629 current->add(result);
1630 current->push(result);
1631 }
1632 }
1633
1634 callInfo.setImplicitlyUsedUnchecked();
1635 return InliningStatus_Inlined;
1636 }
1637
1638 IonBuilder::InliningStatus
1639 IonBuilder::inlineObjectIsTypeDescr(CallInfo &callInfo)
1640 {
1641 if (callInfo.constructing() || callInfo.argc() != 1)
1642 return InliningStatus_NotInlined;
1643
1644 if (callInfo.getArg(0)->type() != MIRType_Object)
1645 return InliningStatus_NotInlined;
1646 if (getInlineReturnType() != MIRType_Boolean)
1647 return InliningStatus_NotInlined;
1648
1649 // The test is elaborate: in-line only if there is exact
1650 // information.
1651
1652 types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet();
1653 if (!types)
1654 return InliningStatus_NotInlined;
1655
1656 bool result = false;
1657 switch (types->forAllClasses(IsTypeDescrClass)) {
1658 case types::TemporaryTypeSet::ForAllResult::ALL_FALSE:
1659 case types::TemporaryTypeSet::ForAllResult::EMPTY:
1660 result = false;
1661 break;
1662 case types::TemporaryTypeSet::ForAllResult::ALL_TRUE:
1663 result = true;
1664 break;
1665 case types::TemporaryTypeSet::ForAllResult::MIXED:
1666 return InliningStatus_NotInlined;
1667 }
1668
1669 pushConstant(BooleanValue(result));
1670
1671 callInfo.setImplicitlyUsedUnchecked();
1672 return InliningStatus_Inlined;
1673 }
1674
1675 IonBuilder::InliningStatus
1676 IonBuilder::inlineSetTypedObjectOffset(CallInfo &callInfo)
1677 {
1678 if (callInfo.argc() != 2 || callInfo.constructing())
1679 return InliningStatus_NotInlined;
1680
1681 MDefinition *typedObj = callInfo.getArg(0);
1682 MDefinition *offset = callInfo.getArg(1);
1683
1684 // Return type should be undefined or something wacky is going on.
1685 if (getInlineReturnType() != MIRType_Undefined)
1686 return InliningStatus_NotInlined;
1687
1688 // Check typedObj is a, well, typed object. Go ahead and use TI
1689 // data. If this check should fail, that is almost certainly a bug
1690 // in self-hosted code -- either because it's not being careful
1691 // with TI or because of something else -- but we'll just let it
1692 // fall through to the SetTypedObjectOffset intrinsic in such
1693 // cases.
1694 types::TemporaryTypeSet *types = typedObj->resultTypeSet();
1695 if (typedObj->type() != MIRType_Object || !types)
1696 return InliningStatus_NotInlined;
1697 switch (types->forAllClasses(IsTypedObjectClass)) {
1698 case types::TemporaryTypeSet::ForAllResult::ALL_FALSE:
1699 case types::TemporaryTypeSet::ForAllResult::EMPTY:
1700 case types::TemporaryTypeSet::ForAllResult::MIXED:
1701 return InliningStatus_NotInlined;
1702 case types::TemporaryTypeSet::ForAllResult::ALL_TRUE:
1703 break;
1704 }
1705
1706 // Check type of offset argument is an integer.
1707 if (offset->type() != MIRType_Int32)
1708 return InliningStatus_NotInlined;
1709
1710 callInfo.setImplicitlyUsedUnchecked();
1711 MInstruction *ins = MSetTypedObjectOffset::New(alloc(), typedObj, offset);
1712 current->add(ins);
1713 current->push(ins);
1714 return InliningStatus_Inlined;
1715 }
1716
1717 IonBuilder::InliningStatus
1718 IonBuilder::inlineUnsafeSetReservedSlot(CallInfo &callInfo)
1719 {
1720 if (callInfo.argc() != 3 || callInfo.constructing())
1721 return InliningStatus_NotInlined;
1722 if (getInlineReturnType() != MIRType_Undefined)
1723 return InliningStatus_NotInlined;
1724 if (callInfo.getArg(0)->type() != MIRType_Object)
1725 return InliningStatus_NotInlined;
1726 if (callInfo.getArg(1)->type() != MIRType_Int32)
1727 return InliningStatus_NotInlined;
1728
1729 // Don't inline if we don't have a constant slot.
1730 MDefinition *arg = callInfo.getArg(1);
1731 if (!arg->isConstant())
1732 return InliningStatus_NotInlined;
1733 uint32_t slot = arg->toConstant()->value().toPrivateUint32();
1734
1735 callInfo.setImplicitlyUsedUnchecked();
1736
1737 MStoreFixedSlot *store = MStoreFixedSlot::New(alloc(), callInfo.getArg(0), slot, callInfo.getArg(2));
1738 current->add(store);
1739 current->push(store);
1740
1741 if (NeedsPostBarrier(info(), callInfo.getArg(2)))
1742 current->add(MPostWriteBarrier::New(alloc(), callInfo.thisArg(), callInfo.getArg(2)));
1743
1744 return InliningStatus_Inlined;
1745 }
1746
1747 IonBuilder::InliningStatus
1748 IonBuilder::inlineUnsafeGetReservedSlot(CallInfo &callInfo)
1749 {
1750 if (callInfo.argc() != 2 || callInfo.constructing())
1751 return InliningStatus_NotInlined;
1752 if (callInfo.getArg(0)->type() != MIRType_Object)
1753 return InliningStatus_NotInlined;
1754 if (callInfo.getArg(1)->type() != MIRType_Int32)
1755 return InliningStatus_NotInlined;
1756
1757 // Don't inline if we don't have a constant slot.
1758 MDefinition *arg = callInfo.getArg(1);
1759 if (!arg->isConstant())
1760 return InliningStatus_NotInlined;
1761 uint32_t slot = arg->toConstant()->value().toPrivateUint32();
1762
1763 callInfo.setImplicitlyUsedUnchecked();
1764
1765 MLoadFixedSlot *load = MLoadFixedSlot::New(alloc(), callInfo.getArg(0), slot);
1766 current->add(load);
1767 current->push(load);
1768
1769 // We don't track reserved slot types, so always emit a barrier.
1770 if (!pushTypeBarrier(load, getInlineReturnTypeSet(), true))
1771 return InliningStatus_Error;
1772
1773 return InliningStatus_Inlined;
1774 }
1775
1776 IonBuilder::InliningStatus
1777 IonBuilder::inlineHaveSameClass(CallInfo &callInfo)
1778 {
1779 if (callInfo.argc() != 2 || callInfo.constructing())
1780 return InliningStatus_NotInlined;
1781 if (callInfo.getArg(0)->type() != MIRType_Object)
1782 return InliningStatus_NotInlined;
1783 if (callInfo.getArg(1)->type() != MIRType_Object)
1784 return InliningStatus_NotInlined;
1785
1786 types::TemporaryTypeSet *arg1Types = callInfo.getArg(0)->resultTypeSet();
1787 types::TemporaryTypeSet *arg2Types = callInfo.getArg(1)->resultTypeSet();
1788 const Class *arg1Clasp = arg1Types ? arg1Types->getKnownClass() : nullptr;
1789 const Class *arg2Clasp = arg2Types ? arg2Types->getKnownClass() : nullptr;
1790 if (arg1Clasp && arg2Clasp) {
1791 MConstant *constant = MConstant::New(alloc(), BooleanValue(arg1Clasp == arg2Clasp));
1792 current->add(constant);
1793 current->push(constant);
1794 return InliningStatus_Inlined;
1795 }
1796
1797 callInfo.setImplicitlyUsedUnchecked();
1798
1799 MHaveSameClass *sameClass = MHaveSameClass::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
1800 current->add(sameClass);
1801 current->push(sameClass);
1802
1803 return InliningStatus_Inlined;
1804 }
1805
1806 IonBuilder::InliningStatus
1807 IonBuilder::inlineIsCallable(CallInfo &callInfo)
1808 {
1809 if (callInfo.argc() != 1 || callInfo.constructing())
1810 return InliningStatus_NotInlined;
1811
1812 if (getInlineReturnType() != MIRType_Boolean)
1813 return InliningStatus_NotInlined;
1814 if (callInfo.getArg(0)->type() != MIRType_Object)
1815 return InliningStatus_NotInlined;
1816
1817 // Try inlining with constant true/false: only objects may be callable at
1818 // all, and if we know the class check if it is callable.
1819 bool isCallableKnown = false;
1820 bool isCallableConstant;
1821 if (callInfo.getArg(0)->type() != MIRType_Object) {
1822 isCallableKnown = true;
1823 isCallableConstant = false;
1824 } else {
1825 types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet();
1826 const Class *clasp = types ? types->getKnownClass() : nullptr;
1827 if (clasp) {
1828 isCallableKnown = true;
1829 isCallableConstant = clasp->isCallable();
1830 }
1831 }
1832
1833 callInfo.setImplicitlyUsedUnchecked();
1834
1835 if (isCallableKnown) {
1836 MConstant *constant = MConstant::New(alloc(), BooleanValue(isCallableConstant));
1837 current->add(constant);
1838 current->push(constant);
1839 return InliningStatus_Inlined;
1840 }
1841
1842 MIsCallable *isCallable = MIsCallable::New(alloc(), callInfo.getArg(0));
1843 current->add(isCallable);
1844 current->push(isCallable);
1845
1846 return InliningStatus_Inlined;
1847 }
1848
1849 IonBuilder::InliningStatus
1850 IonBuilder::inlineToObject(CallInfo &callInfo)
1851 {
1852 if (callInfo.argc() != 1 || callInfo.constructing())
1853 return InliningStatus_NotInlined;
1854
1855 // If we know the input type is an object, nop ToObject.
1856 if (getInlineReturnType() != MIRType_Object)
1857 return InliningStatus_NotInlined;
1858 if (callInfo.getArg(0)->type() != MIRType_Object)
1859 return InliningStatus_NotInlined;
1860
1861 callInfo.setImplicitlyUsedUnchecked();
1862 MDefinition *object = callInfo.getArg(0);
1863
1864 current->push(object);
1865 return InliningStatus_Inlined;
1866 }
1867
1868 IonBuilder::InliningStatus
1869 IonBuilder::inlineBailout(CallInfo &callInfo)
1870 {
1871 callInfo.setImplicitlyUsedUnchecked();
1872
1873 current->add(MBail::New(alloc()));
1874
1875 MConstant *undefined = MConstant::New(alloc(), UndefinedValue());
1876 current->add(undefined);
1877 current->push(undefined);
1878 return InliningStatus_Inlined;
1879 }
1880
1881 IonBuilder::InliningStatus
1882 IonBuilder::inlineAssertFloat32(CallInfo &callInfo)
1883 {
1884 callInfo.setImplicitlyUsedUnchecked();
1885
1886 MDefinition *secondArg = callInfo.getArg(1);
1887
1888 JS_ASSERT(secondArg->type() == MIRType_Boolean);
1889 JS_ASSERT(secondArg->isConstant());
1890
1891 bool mustBeFloat32 = JSVAL_TO_BOOLEAN(secondArg->toConstant()->value());
1892 current->add(MAssertFloat32::New(alloc(), callInfo.getArg(0), mustBeFloat32));
1893
1894 MConstant *undefined = MConstant::New(alloc(), UndefinedValue());
1895 current->add(undefined);
1896 current->push(undefined);
1897 return InliningStatus_Inlined;
1898 }
1899
1900 IonBuilder::InliningStatus
1901 IonBuilder::inlineBoundFunction(CallInfo &nativeCallInfo, JSFunction *target)
1902 {
1903 if (!target->getBoundFunctionTarget()->is<JSFunction>())
1904 return InliningStatus_NotInlined;
1905
1906 JSFunction *scriptedTarget = &(target->getBoundFunctionTarget()->as<JSFunction>());
1907 JSRuntime *runtime = scriptedTarget->runtimeFromMainThread();
1908
1909 // Don't optimize if we're constructing and the callee is not a
1910 // constructor, so that CallKnown does not have to handle this case
1911 // (it should always throw).
1912 if (nativeCallInfo.constructing() && !scriptedTarget->isInterpretedConstructor() &&
1913 !scriptedTarget->isNativeConstructor())
1914 {
1915 return InliningStatus_NotInlined;
1916 }
1917
1918 if (gc::IsInsideNursery(runtime, scriptedTarget))
1919 return InliningStatus_NotInlined;
1920
1921 for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) {
1922 const Value val = target->getBoundFunctionArgument(i);
1923 if (val.isObject() && gc::IsInsideNursery(runtime, &val.toObject()))
1924 return InliningStatus_NotInlined;
1925 }
1926
1927 const Value thisVal = target->getBoundFunctionThis();
1928 if (thisVal.isObject() && gc::IsInsideNursery(runtime, &thisVal.toObject()))
1929 return InliningStatus_NotInlined;
1930
1931 size_t argc = target->getBoundFunctionArgumentCount() + nativeCallInfo.argc();
1932 if (argc > ARGS_LENGTH_MAX)
1933 return InliningStatus_NotInlined;
1934
1935 nativeCallInfo.thisArg()->setImplicitlyUsedUnchecked();
1936
1937 CallInfo callInfo(alloc(), nativeCallInfo.constructing());
1938 callInfo.setFun(constant(ObjectValue(*scriptedTarget)));
1939 callInfo.setThis(constant(target->getBoundFunctionThis()));
1940
1941 if (!callInfo.argv().reserve(argc))
1942 return InliningStatus_Error;
1943
1944 for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++)
1945 callInfo.argv().infallibleAppend(constant(target->getBoundFunctionArgument(i)));
1946 for (size_t i = 0; i < nativeCallInfo.argc(); i++)
1947 callInfo.argv().infallibleAppend(nativeCallInfo.getArg(i));
1948
1949 if (!makeCall(scriptedTarget, callInfo, false))
1950 return InliningStatus_Error;
1951
1952 return InliningStatus_Inlined;
1953 }
1954
1955 } // namespace jit
1956 } // namespace js

mercurial