js/src/jit/MCallOptimize.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial