Wed, 31 Dec 2014 06:09:35 +0100
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 |