1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/MCallOptimize.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1956 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jsmath.h" 1.11 + 1.12 +#include "builtin/TestingFunctions.h" 1.13 +#include "builtin/TypedObject.h" 1.14 +#include "jit/BaselineInspector.h" 1.15 +#include "jit/IonBuilder.h" 1.16 +#include "jit/Lowering.h" 1.17 +#include "jit/MIR.h" 1.18 +#include "jit/MIRGraph.h" 1.19 +#include "vm/ArgumentsObject.h" 1.20 + 1.21 +#include "jsscriptinlines.h" 1.22 + 1.23 +#include "vm/StringObject-inl.h" 1.24 + 1.25 +namespace js { 1.26 +namespace jit { 1.27 + 1.28 +IonBuilder::InliningStatus 1.29 +IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target) 1.30 +{ 1.31 + JS_ASSERT(target->isNative()); 1.32 + JSNative native = target->native(); 1.33 + 1.34 + if (!optimizationInfo().inlineNative()) 1.35 + return InliningStatus_NotInlined; 1.36 + 1.37 + // Array natives. 1.38 + if (native == js_Array) 1.39 + return inlineArray(callInfo); 1.40 + if (native == js::array_pop) 1.41 + return inlineArrayPopShift(callInfo, MArrayPopShift::Pop); 1.42 + if (native == js::array_shift) 1.43 + return inlineArrayPopShift(callInfo, MArrayPopShift::Shift); 1.44 + if (native == js::array_push) 1.45 + return inlineArrayPush(callInfo); 1.46 + if (native == js::array_concat) 1.47 + return inlineArrayConcat(callInfo); 1.48 + if (native == js::array_splice) 1.49 + return inlineArraySplice(callInfo); 1.50 + 1.51 + // Math natives. 1.52 + if (native == js_math_abs) 1.53 + return inlineMathAbs(callInfo); 1.54 + if (native == js::math_floor) 1.55 + return inlineMathFloor(callInfo); 1.56 + if (native == js::math_ceil) 1.57 + return inlineMathCeil(callInfo); 1.58 + if (native == js::math_round) 1.59 + return inlineMathRound(callInfo); 1.60 + if (native == js_math_sqrt) 1.61 + return inlineMathSqrt(callInfo); 1.62 + if (native == math_atan2) 1.63 + return inlineMathAtan2(callInfo); 1.64 + if (native == js::math_hypot) 1.65 + return inlineMathHypot(callInfo); 1.66 + if (native == js_math_max) 1.67 + return inlineMathMinMax(callInfo, true /* max */); 1.68 + if (native == js_math_min) 1.69 + return inlineMathMinMax(callInfo, false /* max */); 1.70 + if (native == js_math_pow) 1.71 + return inlineMathPow(callInfo); 1.72 + if (native == js_math_random) 1.73 + return inlineMathRandom(callInfo); 1.74 + if (native == js::math_imul) 1.75 + return inlineMathImul(callInfo); 1.76 + if (native == js::math_fround) 1.77 + return inlineMathFRound(callInfo); 1.78 + if (native == js::math_sin) 1.79 + return inlineMathFunction(callInfo, MMathFunction::Sin); 1.80 + if (native == js::math_cos) 1.81 + return inlineMathFunction(callInfo, MMathFunction::Cos); 1.82 + if (native == js::math_exp) 1.83 + return inlineMathFunction(callInfo, MMathFunction::Exp); 1.84 + if (native == js::math_tan) 1.85 + return inlineMathFunction(callInfo, MMathFunction::Tan); 1.86 + if (native == js::math_log) 1.87 + return inlineMathFunction(callInfo, MMathFunction::Log); 1.88 + if (native == js::math_atan) 1.89 + return inlineMathFunction(callInfo, MMathFunction::ATan); 1.90 + if (native == js::math_asin) 1.91 + return inlineMathFunction(callInfo, MMathFunction::ASin); 1.92 + if (native == js::math_acos) 1.93 + return inlineMathFunction(callInfo, MMathFunction::ACos); 1.94 + if (native == js::math_log10) 1.95 + return inlineMathFunction(callInfo, MMathFunction::Log10); 1.96 + if (native == js::math_log2) 1.97 + return inlineMathFunction(callInfo, MMathFunction::Log2); 1.98 + if (native == js::math_log1p) 1.99 + return inlineMathFunction(callInfo, MMathFunction::Log1P); 1.100 + if (native == js::math_expm1) 1.101 + return inlineMathFunction(callInfo, MMathFunction::ExpM1); 1.102 + if (native == js::math_cosh) 1.103 + return inlineMathFunction(callInfo, MMathFunction::CosH); 1.104 + if (native == js::math_sin) 1.105 + return inlineMathFunction(callInfo, MMathFunction::SinH); 1.106 + if (native == js::math_tan) 1.107 + return inlineMathFunction(callInfo, MMathFunction::TanH); 1.108 + if (native == js::math_acosh) 1.109 + return inlineMathFunction(callInfo, MMathFunction::ACosH); 1.110 + if (native == js::math_asin) 1.111 + return inlineMathFunction(callInfo, MMathFunction::ASinH); 1.112 + if (native == js::math_atan) 1.113 + return inlineMathFunction(callInfo, MMathFunction::ATanH); 1.114 + if (native == js::math_sign) 1.115 + return inlineMathFunction(callInfo, MMathFunction::Sign); 1.116 + if (native == js::math_trunc) 1.117 + return inlineMathFunction(callInfo, MMathFunction::Trunc); 1.118 + if (native == js::math_cbrt) 1.119 + return inlineMathFunction(callInfo, MMathFunction::Cbrt); 1.120 + 1.121 + // String natives. 1.122 + if (native == js_String) 1.123 + return inlineStringObject(callInfo); 1.124 + if (native == js::str_split) 1.125 + return inlineStringSplit(callInfo); 1.126 + if (native == js_str_charCodeAt) 1.127 + return inlineStrCharCodeAt(callInfo); 1.128 + if (native == js::str_fromCharCode) 1.129 + return inlineStrFromCharCode(callInfo); 1.130 + if (native == js_str_charAt) 1.131 + return inlineStrCharAt(callInfo); 1.132 + if (native == str_replace) 1.133 + return inlineStrReplace(callInfo); 1.134 + 1.135 + // RegExp natives. 1.136 + if (native == regexp_exec && CallResultEscapes(pc)) 1.137 + return inlineRegExpExec(callInfo); 1.138 + if (native == regexp_exec && !CallResultEscapes(pc)) 1.139 + return inlineRegExpTest(callInfo); 1.140 + if (native == regexp_test) 1.141 + return inlineRegExpTest(callInfo); 1.142 + 1.143 + // Array intrinsics. 1.144 + if (native == intrinsic_UnsafePutElements) 1.145 + return inlineUnsafePutElements(callInfo); 1.146 + if (native == intrinsic_NewDenseArray) 1.147 + return inlineNewDenseArray(callInfo); 1.148 + 1.149 + // Slot intrinsics. 1.150 + if (native == intrinsic_UnsafeSetReservedSlot) 1.151 + return inlineUnsafeSetReservedSlot(callInfo); 1.152 + if (native == intrinsic_UnsafeGetReservedSlot) 1.153 + return inlineUnsafeGetReservedSlot(callInfo); 1.154 + 1.155 + // Parallel intrinsics. 1.156 + if (native == intrinsic_ShouldForceSequential || 1.157 + native == intrinsic_InParallelSection) 1.158 + return inlineForceSequentialOrInParallelSection(callInfo); 1.159 + if (native == intrinsic_ForkJoinGetSlice) 1.160 + return inlineForkJoinGetSlice(callInfo); 1.161 + 1.162 + // Utility intrinsics. 1.163 + if (native == intrinsic_IsCallable) 1.164 + return inlineIsCallable(callInfo); 1.165 + if (native == intrinsic_HaveSameClass) 1.166 + return inlineHaveSameClass(callInfo); 1.167 + if (native == intrinsic_ToObject) 1.168 + return inlineToObject(callInfo); 1.169 + 1.170 + // TypedObject intrinsics. 1.171 + if (native == intrinsic_ObjectIsTypedObject) 1.172 + return inlineHasClasses(callInfo, 1.173 + &TransparentTypedObject::class_, &OpaqueTypedObject::class_); 1.174 + if (native == intrinsic_ObjectIsTransparentTypedObject) 1.175 + return inlineHasClass(callInfo, &TransparentTypedObject::class_); 1.176 + if (native == intrinsic_ObjectIsOpaqueTypedObject) 1.177 + return inlineHasClass(callInfo, &OpaqueTypedObject::class_); 1.178 + if (native == intrinsic_ObjectIsTypeDescr) 1.179 + return inlineObjectIsTypeDescr(callInfo); 1.180 + if (native == intrinsic_TypeDescrIsSimpleType) 1.181 + return inlineHasClasses(callInfo, 1.182 + &ScalarTypeDescr::class_, &ReferenceTypeDescr::class_); 1.183 + if (native == intrinsic_TypeDescrIsArrayType) 1.184 + return inlineHasClasses(callInfo, 1.185 + &SizedArrayTypeDescr::class_, &UnsizedArrayTypeDescr::class_); 1.186 + if (native == intrinsic_TypeDescrIsSizedArrayType) 1.187 + return inlineHasClass(callInfo, &SizedArrayTypeDescr::class_); 1.188 + if (native == intrinsic_TypeDescrIsUnsizedArrayType) 1.189 + return inlineHasClass(callInfo, &UnsizedArrayTypeDescr::class_); 1.190 + if (native == intrinsic_SetTypedObjectOffset) 1.191 + return inlineSetTypedObjectOffset(callInfo); 1.192 + 1.193 + // Testing Functions 1.194 + if (native == testingFunc_inParallelSection) 1.195 + return inlineForceSequentialOrInParallelSection(callInfo); 1.196 + if (native == testingFunc_bailout) 1.197 + return inlineBailout(callInfo); 1.198 + if (native == testingFunc_assertFloat32) 1.199 + return inlineAssertFloat32(callInfo); 1.200 + 1.201 + // Bound function 1.202 + if (native == js::CallOrConstructBoundFunction) 1.203 + return inlineBoundFunction(callInfo, target); 1.204 + 1.205 + return InliningStatus_NotInlined; 1.206 +} 1.207 + 1.208 +types::TemporaryTypeSet * 1.209 +IonBuilder::getInlineReturnTypeSet() 1.210 +{ 1.211 + return bytecodeTypes(pc); 1.212 +} 1.213 + 1.214 +MIRType 1.215 +IonBuilder::getInlineReturnType() 1.216 +{ 1.217 + types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet(); 1.218 + return returnTypes->getKnownMIRType(); 1.219 +} 1.220 + 1.221 +IonBuilder::InliningStatus 1.222 +IonBuilder::inlineMathFunction(CallInfo &callInfo, MMathFunction::Function function) 1.223 +{ 1.224 + if (callInfo.constructing()) 1.225 + return InliningStatus_NotInlined; 1.226 + 1.227 + if (callInfo.argc() != 1) 1.228 + return InliningStatus_NotInlined; 1.229 + 1.230 + if (getInlineReturnType() != MIRType_Double) 1.231 + return InliningStatus_NotInlined; 1.232 + if (!IsNumberType(callInfo.getArg(0)->type())) 1.233 + return InliningStatus_NotInlined; 1.234 + 1.235 + const MathCache *cache = compartment->runtime()->maybeGetMathCache(); 1.236 + 1.237 + callInfo.fun()->setImplicitlyUsedUnchecked(); 1.238 + callInfo.thisArg()->setImplicitlyUsedUnchecked(); 1.239 + 1.240 + MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), function, cache); 1.241 + current->add(ins); 1.242 + current->push(ins); 1.243 + return InliningStatus_Inlined; 1.244 +} 1.245 + 1.246 +IonBuilder::InliningStatus 1.247 +IonBuilder::inlineArray(CallInfo &callInfo) 1.248 +{ 1.249 + uint32_t initLength = 0; 1.250 + MNewArray::AllocatingBehaviour allocating = MNewArray::NewArray_Unallocating; 1.251 + 1.252 + JSObject *templateObject = inspector->getTemplateObjectForNative(pc, js_Array); 1.253 + if (!templateObject) 1.254 + return InliningStatus_NotInlined; 1.255 + JS_ASSERT(templateObject->is<ArrayObject>()); 1.256 + 1.257 + // Multiple arguments imply array initialization, not just construction. 1.258 + if (callInfo.argc() >= 2) { 1.259 + initLength = callInfo.argc(); 1.260 + allocating = MNewArray::NewArray_Allocating; 1.261 + 1.262 + types::TypeObjectKey *type = types::TypeObjectKey::get(templateObject); 1.263 + if (!type->unknownProperties()) { 1.264 + types::HeapTypeSetKey elemTypes = type->property(JSID_VOID); 1.265 + 1.266 + for (uint32_t i = 0; i < initLength; i++) { 1.267 + MDefinition *value = callInfo.getArg(i); 1.268 + if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) { 1.269 + elemTypes.freeze(constraints()); 1.270 + return InliningStatus_NotInlined; 1.271 + } 1.272 + } 1.273 + } 1.274 + } 1.275 + 1.276 + // A single integer argument denotes initial length. 1.277 + if (callInfo.argc() == 1) { 1.278 + if (callInfo.getArg(0)->type() != MIRType_Int32) 1.279 + return InliningStatus_NotInlined; 1.280 + MDefinition *arg = callInfo.getArg(0); 1.281 + if (!arg->isConstant()) 1.282 + return InliningStatus_NotInlined; 1.283 + 1.284 + // Negative lengths generate a RangeError, unhandled by the inline path. 1.285 + initLength = arg->toConstant()->value().toInt32(); 1.286 + if (initLength >= JSObject::NELEMENTS_LIMIT) 1.287 + return InliningStatus_NotInlined; 1.288 + 1.289 + // Make sure initLength matches the template object's length. This is 1.290 + // not guaranteed to be the case, for instance if we're inlining the 1.291 + // MConstant may come from an outer script. 1.292 + if (initLength != templateObject->as<ArrayObject>().length()) 1.293 + return InliningStatus_NotInlined; 1.294 + 1.295 + if (initLength <= ArrayObject::EagerAllocationMaxLength) 1.296 + allocating = MNewArray::NewArray_Allocating; 1.297 + } 1.298 + 1.299 + callInfo.setImplicitlyUsedUnchecked(); 1.300 + 1.301 + types::TemporaryTypeSet::DoubleConversion conversion = 1.302 + getInlineReturnTypeSet()->convertDoubleElements(constraints()); 1.303 + if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) 1.304 + templateObject->setShouldConvertDoubleElements(); 1.305 + else 1.306 + templateObject->clearShouldConvertDoubleElements(); 1.307 + 1.308 + MNewArray *ins = MNewArray::New(alloc(), constraints(), initLength, templateObject, 1.309 + templateObject->type()->initialHeap(constraints()), 1.310 + allocating); 1.311 + current->add(ins); 1.312 + current->push(ins); 1.313 + 1.314 + if (callInfo.argc() >= 2) { 1.315 + // Get the elements vector. 1.316 + MElements *elements = MElements::New(alloc(), ins); 1.317 + current->add(elements); 1.318 + 1.319 + // Store all values, no need to initialize the length after each as 1.320 + // jsop_initelem_array is doing because we do not expect to bailout 1.321 + // because the memory is supposed to be allocated by now. 1.322 + MConstant *id = nullptr; 1.323 + for (uint32_t i = 0; i < initLength; i++) { 1.324 + id = MConstant::New(alloc(), Int32Value(i)); 1.325 + current->add(id); 1.326 + 1.327 + MDefinition *value = callInfo.getArg(i); 1.328 + if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) { 1.329 + MInstruction *valueDouble = MToDouble::New(alloc(), value); 1.330 + current->add(valueDouble); 1.331 + value = valueDouble; 1.332 + } 1.333 + 1.334 + // There is normally no need for a post barrier on these writes 1.335 + // because the new array will be in the nursery. However, this 1.336 + // assumption is volated if we specifically requested pre-tenuring. 1.337 + if (ins->initialHeap() == gc::TenuredHeap) 1.338 + current->add(MPostWriteBarrier::New(alloc(), ins, value)); 1.339 + 1.340 + MStoreElement *store = MStoreElement::New(alloc(), elements, id, value, 1.341 + /* needsHoleCheck = */ false); 1.342 + current->add(store); 1.343 + } 1.344 + 1.345 + // Update the length. 1.346 + MSetInitializedLength *length = MSetInitializedLength::New(alloc(), elements, id); 1.347 + current->add(length); 1.348 + 1.349 + if (!resumeAfter(length)) 1.350 + return InliningStatus_Error; 1.351 + } 1.352 + 1.353 + return InliningStatus_Inlined; 1.354 +} 1.355 + 1.356 +IonBuilder::InliningStatus 1.357 +IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode) 1.358 +{ 1.359 + if (callInfo.constructing()) 1.360 + return InliningStatus_NotInlined; 1.361 + 1.362 + MIRType returnType = getInlineReturnType(); 1.363 + if (returnType == MIRType_Undefined || returnType == MIRType_Null) 1.364 + return InliningStatus_NotInlined; 1.365 + if (callInfo.thisArg()->type() != MIRType_Object) 1.366 + return InliningStatus_NotInlined; 1.367 + 1.368 + // Pop and shift are only handled for dense arrays that have never been 1.369 + // used in an iterator: popping elements does not account for suppressing 1.370 + // deleted properties in active iterators. 1.371 + types::TypeObjectFlags unhandledFlags = 1.372 + types::OBJECT_FLAG_SPARSE_INDEXES | 1.373 + types::OBJECT_FLAG_LENGTH_OVERFLOW | 1.374 + types::OBJECT_FLAG_ITERATED; 1.375 + 1.376 + types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); 1.377 + if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_) 1.378 + return InliningStatus_NotInlined; 1.379 + if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) 1.380 + return InliningStatus_NotInlined; 1.381 + 1.382 + if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) 1.383 + return InliningStatus_NotInlined; 1.384 + 1.385 + callInfo.setImplicitlyUsedUnchecked(); 1.386 + 1.387 + types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet(); 1.388 + bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED); 1.389 + bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType()); 1.390 + 1.391 + bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), 1.392 + callInfo.thisArg(), nullptr, returnTypes); 1.393 + if (barrier) 1.394 + returnType = MIRType_Value; 1.395 + 1.396 + MArrayPopShift *ins = MArrayPopShift::New(alloc(), callInfo.thisArg(), mode, 1.397 + needsHoleCheck, maybeUndefined); 1.398 + current->add(ins); 1.399 + current->push(ins); 1.400 + ins->setResultType(returnType); 1.401 + 1.402 + if (!resumeAfter(ins)) 1.403 + return InliningStatus_Error; 1.404 + 1.405 + if (!pushTypeBarrier(ins, returnTypes, barrier)) 1.406 + return InliningStatus_Error; 1.407 + 1.408 + return InliningStatus_Inlined; 1.409 +} 1.410 + 1.411 +IonBuilder::InliningStatus 1.412 +IonBuilder::inlineArraySplice(CallInfo &callInfo) 1.413 +{ 1.414 + if (callInfo.argc() != 2 || callInfo.constructing()) 1.415 + return InliningStatus_NotInlined; 1.416 + 1.417 + // Ensure |this|, argument and result are objects. 1.418 + if (getInlineReturnType() != MIRType_Object) 1.419 + return InliningStatus_NotInlined; 1.420 + if (callInfo.thisArg()->type() != MIRType_Object) 1.421 + return InliningStatus_NotInlined; 1.422 + if (callInfo.getArg(0)->type() != MIRType_Int32) 1.423 + return InliningStatus_NotInlined; 1.424 + if (callInfo.getArg(1)->type() != MIRType_Int32) 1.425 + return InliningStatus_NotInlined; 1.426 + 1.427 + callInfo.setImplicitlyUsedUnchecked(); 1.428 + 1.429 + // Specialize arr.splice(start, deleteCount) with unused return value and 1.430 + // avoid creating the result array in this case. 1.431 + if (!BytecodeIsPopped(pc)) 1.432 + return InliningStatus_NotInlined; 1.433 + 1.434 + MArraySplice *ins = MArraySplice::New(alloc(), 1.435 + callInfo.thisArg(), 1.436 + callInfo.getArg(0), 1.437 + callInfo.getArg(1)); 1.438 + 1.439 + current->add(ins); 1.440 + pushConstant(UndefinedValue()); 1.441 + 1.442 + if (!resumeAfter(ins)) 1.443 + return InliningStatus_Error; 1.444 + return InliningStatus_Inlined; 1.445 +} 1.446 + 1.447 +IonBuilder::InliningStatus 1.448 +IonBuilder::inlineArrayPush(CallInfo &callInfo) 1.449 +{ 1.450 + if (callInfo.argc() != 1 || callInfo.constructing()) 1.451 + return InliningStatus_NotInlined; 1.452 + 1.453 + MDefinition *obj = callInfo.thisArg(); 1.454 + MDefinition *value = callInfo.getArg(0); 1.455 + if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, 1.456 + &obj, nullptr, &value, /* canModify = */ false)) 1.457 + { 1.458 + return InliningStatus_NotInlined; 1.459 + } 1.460 + JS_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0)); 1.461 + 1.462 + if (getInlineReturnType() != MIRType_Int32) 1.463 + return InliningStatus_NotInlined; 1.464 + if (callInfo.thisArg()->type() != MIRType_Object) 1.465 + return InliningStatus_NotInlined; 1.466 + 1.467 + types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); 1.468 + if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_) 1.469 + return InliningStatus_NotInlined; 1.470 + if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | 1.471 + types::OBJECT_FLAG_LENGTH_OVERFLOW)) 1.472 + { 1.473 + return InliningStatus_NotInlined; 1.474 + } 1.475 + 1.476 + if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) 1.477 + return InliningStatus_NotInlined; 1.478 + 1.479 + types::TemporaryTypeSet::DoubleConversion conversion = 1.480 + thisTypes->convertDoubleElements(constraints()); 1.481 + if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion) 1.482 + return InliningStatus_NotInlined; 1.483 + 1.484 + callInfo.setImplicitlyUsedUnchecked(); 1.485 + value = callInfo.getArg(0); 1.486 + 1.487 + if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles || 1.488 + conversion == types::TemporaryTypeSet::MaybeConvertToDoubles) 1.489 + { 1.490 + MInstruction *valueDouble = MToDouble::New(alloc(), value); 1.491 + current->add(valueDouble); 1.492 + value = valueDouble; 1.493 + } 1.494 + 1.495 + if (NeedsPostBarrier(info(), value)) 1.496 + current->add(MPostWriteBarrier::New(alloc(), callInfo.thisArg(), value)); 1.497 + 1.498 + MArrayPush *ins = MArrayPush::New(alloc(), callInfo.thisArg(), value); 1.499 + current->add(ins); 1.500 + current->push(ins); 1.501 + 1.502 + if (!resumeAfter(ins)) 1.503 + return InliningStatus_Error; 1.504 + return InliningStatus_Inlined; 1.505 +} 1.506 + 1.507 +IonBuilder::InliningStatus 1.508 +IonBuilder::inlineArrayConcat(CallInfo &callInfo) 1.509 +{ 1.510 + if (callInfo.argc() != 1 || callInfo.constructing()) 1.511 + return InliningStatus_NotInlined; 1.512 + 1.513 + // Ensure |this|, argument and result are objects. 1.514 + if (getInlineReturnType() != MIRType_Object) 1.515 + return InliningStatus_NotInlined; 1.516 + if (callInfo.thisArg()->type() != MIRType_Object) 1.517 + return InliningStatus_NotInlined; 1.518 + if (callInfo.getArg(0)->type() != MIRType_Object) 1.519 + return InliningStatus_NotInlined; 1.520 + 1.521 + // |this| and the argument must be dense arrays. 1.522 + types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); 1.523 + types::TemporaryTypeSet *argTypes = callInfo.getArg(0)->resultTypeSet(); 1.524 + if (!thisTypes || !argTypes) 1.525 + return InliningStatus_NotInlined; 1.526 + 1.527 + if (thisTypes->getKnownClass() != &ArrayObject::class_) 1.528 + return InliningStatus_NotInlined; 1.529 + if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | 1.530 + types::OBJECT_FLAG_LENGTH_OVERFLOW)) 1.531 + { 1.532 + return InliningStatus_NotInlined; 1.533 + } 1.534 + 1.535 + if (argTypes->getKnownClass() != &ArrayObject::class_) 1.536 + return InliningStatus_NotInlined; 1.537 + if (argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | 1.538 + types::OBJECT_FLAG_LENGTH_OVERFLOW)) 1.539 + { 1.540 + return InliningStatus_NotInlined; 1.541 + } 1.542 + 1.543 + // Watch out for indexed properties on the prototype. 1.544 + if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) 1.545 + return InliningStatus_NotInlined; 1.546 + 1.547 + // Require the 'this' types to have a specific type matching the current 1.548 + // global, so we can create the result object inline. 1.549 + if (thisTypes->getObjectCount() != 1) 1.550 + return InliningStatus_NotInlined; 1.551 + 1.552 + types::TypeObject *baseThisType = thisTypes->getTypeObject(0); 1.553 + if (!baseThisType) 1.554 + return InliningStatus_NotInlined; 1.555 + types::TypeObjectKey *thisType = types::TypeObjectKey::get(baseThisType); 1.556 + if (thisType->unknownProperties()) 1.557 + return InliningStatus_NotInlined; 1.558 + 1.559 + // Don't inline if 'this' is packed and the argument may not be packed 1.560 + // (the result array will reuse the 'this' type). 1.561 + if (!thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED) && 1.562 + argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED)) 1.563 + { 1.564 + return InliningStatus_NotInlined; 1.565 + } 1.566 + 1.567 + // Constraints modeling this concat have not been generated by inference, 1.568 + // so check that type information already reflects possible side effects of 1.569 + // this call. 1.570 + types::HeapTypeSetKey thisElemTypes = thisType->property(JSID_VOID); 1.571 + 1.572 + types::TemporaryTypeSet *resTypes = getInlineReturnTypeSet(); 1.573 + if (!resTypes->hasType(types::Type::ObjectType(thisType))) 1.574 + return InliningStatus_NotInlined; 1.575 + 1.576 + for (unsigned i = 0; i < argTypes->getObjectCount(); i++) { 1.577 + types::TypeObjectKey *argType = argTypes->getObject(i); 1.578 + if (!argType) 1.579 + continue; 1.580 + 1.581 + if (argType->unknownProperties()) 1.582 + return InliningStatus_NotInlined; 1.583 + 1.584 + types::HeapTypeSetKey elemTypes = argType->property(JSID_VOID); 1.585 + if (!elemTypes.knownSubset(constraints(), thisElemTypes)) 1.586 + return InliningStatus_NotInlined; 1.587 + } 1.588 + 1.589 + // Inline the call. 1.590 + JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat); 1.591 + if (!templateObj || templateObj->type() != baseThisType) 1.592 + return InliningStatus_NotInlined; 1.593 + JS_ASSERT(templateObj->is<ArrayObject>()); 1.594 + 1.595 + callInfo.setImplicitlyUsedUnchecked(); 1.596 + 1.597 + MArrayConcat *ins = MArrayConcat::New(alloc(), constraints(), callInfo.thisArg(), callInfo.getArg(0), 1.598 + templateObj, templateObj->type()->initialHeap(constraints())); 1.599 + current->add(ins); 1.600 + current->push(ins); 1.601 + 1.602 + if (!resumeAfter(ins)) 1.603 + return InliningStatus_Error; 1.604 + return InliningStatus_Inlined; 1.605 +} 1.606 + 1.607 +IonBuilder::InliningStatus 1.608 +IonBuilder::inlineMathAbs(CallInfo &callInfo) 1.609 +{ 1.610 + if (callInfo.constructing()) 1.611 + return InliningStatus_NotInlined; 1.612 + 1.613 + if (callInfo.argc() != 1) 1.614 + return InliningStatus_NotInlined; 1.615 + 1.616 + MIRType returnType = getInlineReturnType(); 1.617 + MIRType argType = callInfo.getArg(0)->type(); 1.618 + if (!IsNumberType(argType)) 1.619 + return InliningStatus_NotInlined; 1.620 + 1.621 + // Either argType == returnType, or 1.622 + // argType == Double or Float32, returnType == Int, or 1.623 + // argType == Float32, returnType == Double 1.624 + if (argType != returnType && !(IsFloatingPointType(argType) && returnType == MIRType_Int32) 1.625 + && !(argType == MIRType_Float32 && returnType == MIRType_Double)) 1.626 + { 1.627 + return InliningStatus_NotInlined; 1.628 + } 1.629 + 1.630 + callInfo.setImplicitlyUsedUnchecked(); 1.631 + 1.632 + // If the arg is a Float32, we specialize the op as double, it will be specialized 1.633 + // as float32 if necessary later. 1.634 + MIRType absType = (argType == MIRType_Float32) ? MIRType_Double : argType; 1.635 + MInstruction *ins = MAbs::New(alloc(), callInfo.getArg(0), absType); 1.636 + current->add(ins); 1.637 + 1.638 + current->push(ins); 1.639 + return InliningStatus_Inlined; 1.640 +} 1.641 + 1.642 +IonBuilder::InliningStatus 1.643 +IonBuilder::inlineMathFloor(CallInfo &callInfo) 1.644 +{ 1.645 + if (callInfo.constructing()) 1.646 + return InliningStatus_NotInlined; 1.647 + 1.648 + if (callInfo.argc() != 1) 1.649 + return InliningStatus_NotInlined; 1.650 + 1.651 + MIRType argType = callInfo.getArg(0)->type(); 1.652 + MIRType returnType = getInlineReturnType(); 1.653 + 1.654 + // Math.floor(int(x)) == int(x) 1.655 + if (argType == MIRType_Int32 && returnType == MIRType_Int32) { 1.656 + callInfo.setImplicitlyUsedUnchecked(); 1.657 + current->push(callInfo.getArg(0)); 1.658 + return InliningStatus_Inlined; 1.659 + } 1.660 + 1.661 + if (IsFloatingPointType(argType) && returnType == MIRType_Int32) { 1.662 + callInfo.setImplicitlyUsedUnchecked(); 1.663 + MFloor *ins = MFloor::New(alloc(), callInfo.getArg(0)); 1.664 + current->add(ins); 1.665 + current->push(ins); 1.666 + return InliningStatus_Inlined; 1.667 + } 1.668 + 1.669 + if (IsFloatingPointType(argType) && returnType == MIRType_Double) { 1.670 + callInfo.setImplicitlyUsedUnchecked(); 1.671 + MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor, nullptr); 1.672 + current->add(ins); 1.673 + current->push(ins); 1.674 + return InliningStatus_Inlined; 1.675 + } 1.676 + 1.677 + return InliningStatus_NotInlined; 1.678 +} 1.679 + 1.680 +IonBuilder::InliningStatus 1.681 +IonBuilder::inlineMathCeil(CallInfo &callInfo) 1.682 +{ 1.683 + if (callInfo.constructing()) 1.684 + return InliningStatus_NotInlined; 1.685 + 1.686 + if (callInfo.argc() != 1) 1.687 + return InliningStatus_NotInlined; 1.688 + 1.689 + MIRType argType = callInfo.getArg(0)->type(); 1.690 + MIRType returnType = getInlineReturnType(); 1.691 + 1.692 + // Math.ceil(int(x)) == int(x) 1.693 + if (argType == MIRType_Int32 && returnType == MIRType_Int32) { 1.694 + callInfo.setImplicitlyUsedUnchecked(); 1.695 + current->push(callInfo.getArg(0)); 1.696 + return InliningStatus_Inlined; 1.697 + } 1.698 + 1.699 + if (IsFloatingPointType(argType) && returnType == MIRType_Double) { 1.700 + callInfo.setImplicitlyUsedUnchecked(); 1.701 + MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil, nullptr); 1.702 + current->add(ins); 1.703 + current->push(ins); 1.704 + return InliningStatus_Inlined; 1.705 + } 1.706 + 1.707 + return InliningStatus_NotInlined; 1.708 +} 1.709 + 1.710 +IonBuilder::InliningStatus 1.711 +IonBuilder::inlineMathRound(CallInfo &callInfo) 1.712 +{ 1.713 + if (callInfo.constructing()) 1.714 + return InliningStatus_NotInlined; 1.715 + 1.716 + if (callInfo.argc() != 1) 1.717 + return InliningStatus_NotInlined; 1.718 + 1.719 + MIRType returnType = getInlineReturnType(); 1.720 + MIRType argType = callInfo.getArg(0)->type(); 1.721 + 1.722 + // Math.round(int(x)) == int(x) 1.723 + if (argType == MIRType_Int32 && returnType == MIRType_Int32) { 1.724 + callInfo.setImplicitlyUsedUnchecked(); 1.725 + current->push(callInfo.getArg(0)); 1.726 + return InliningStatus_Inlined; 1.727 + } 1.728 + 1.729 + if (IsFloatingPointType(argType) && returnType == MIRType_Int32) { 1.730 + callInfo.setImplicitlyUsedUnchecked(); 1.731 + MRound *ins = MRound::New(alloc(), callInfo.getArg(0)); 1.732 + current->add(ins); 1.733 + current->push(ins); 1.734 + return InliningStatus_Inlined; 1.735 + } 1.736 + 1.737 + if (IsFloatingPointType(argType) && returnType == MIRType_Double) { 1.738 + callInfo.setImplicitlyUsedUnchecked(); 1.739 + MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round, nullptr); 1.740 + current->add(ins); 1.741 + current->push(ins); 1.742 + return InliningStatus_Inlined; 1.743 + } 1.744 + 1.745 + return InliningStatus_NotInlined; 1.746 +} 1.747 + 1.748 +IonBuilder::InliningStatus 1.749 +IonBuilder::inlineMathSqrt(CallInfo &callInfo) 1.750 +{ 1.751 + if (callInfo.constructing()) 1.752 + return InliningStatus_NotInlined; 1.753 + 1.754 + if (callInfo.argc() != 1) 1.755 + return InliningStatus_NotInlined; 1.756 + 1.757 + MIRType argType = callInfo.getArg(0)->type(); 1.758 + if (getInlineReturnType() != MIRType_Double) 1.759 + return InliningStatus_NotInlined; 1.760 + if (!IsNumberType(argType)) 1.761 + return InliningStatus_NotInlined; 1.762 + 1.763 + callInfo.setImplicitlyUsedUnchecked(); 1.764 + 1.765 + MSqrt *sqrt = MSqrt::New(alloc(), callInfo.getArg(0)); 1.766 + current->add(sqrt); 1.767 + current->push(sqrt); 1.768 + return InliningStatus_Inlined; 1.769 +} 1.770 + 1.771 +IonBuilder::InliningStatus 1.772 +IonBuilder::inlineMathAtan2(CallInfo &callInfo) 1.773 +{ 1.774 + if (callInfo.constructing()) 1.775 + return InliningStatus_NotInlined; 1.776 + 1.777 + if (callInfo.argc() != 2) 1.778 + return InliningStatus_NotInlined; 1.779 + 1.780 + if (getInlineReturnType() != MIRType_Double) 1.781 + return InliningStatus_NotInlined; 1.782 + 1.783 + MIRType argType0 = callInfo.getArg(0)->type(); 1.784 + MIRType argType1 = callInfo.getArg(1)->type(); 1.785 + 1.786 + if (!IsNumberType(argType0) || !IsNumberType(argType1)) 1.787 + return InliningStatus_NotInlined; 1.788 + 1.789 + callInfo.setImplicitlyUsedUnchecked(); 1.790 + 1.791 + MAtan2 *atan2 = MAtan2::New(alloc(), callInfo.getArg(0), callInfo.getArg(1)); 1.792 + current->add(atan2); 1.793 + current->push(atan2); 1.794 + return InliningStatus_Inlined; 1.795 +} 1.796 + 1.797 +IonBuilder::InliningStatus 1.798 +IonBuilder::inlineMathHypot(CallInfo &callInfo) 1.799 +{ 1.800 + if (callInfo.constructing()) 1.801 + return InliningStatus_NotInlined; 1.802 + 1.803 + if (callInfo.argc() != 2) 1.804 + return InliningStatus_NotInlined; 1.805 + 1.806 + if (getInlineReturnType() != MIRType_Double) 1.807 + return InliningStatus_NotInlined; 1.808 + 1.809 + MIRType argType0 = callInfo.getArg(0)->type(); 1.810 + MIRType argType1 = callInfo.getArg(1)->type(); 1.811 + 1.812 + if (!IsNumberType(argType0) || !IsNumberType(argType1)) 1.813 + return InliningStatus_NotInlined; 1.814 + 1.815 + callInfo.setImplicitlyUsedUnchecked(); 1.816 + 1.817 + MHypot *hypot = MHypot::New(alloc(), callInfo.getArg(0), callInfo.getArg(1)); 1.818 + current->add(hypot); 1.819 + current->push(hypot); 1.820 + return InliningStatus_Inlined; 1.821 +} 1.822 + 1.823 +IonBuilder::InliningStatus 1.824 +IonBuilder::inlineMathPow(CallInfo &callInfo) 1.825 +{ 1.826 + if (callInfo.constructing()) 1.827 + return InliningStatus_NotInlined; 1.828 + 1.829 + if (callInfo.argc() != 2) 1.830 + return InliningStatus_NotInlined; 1.831 + 1.832 + // Typechecking. 1.833 + MIRType baseType = callInfo.getArg(0)->type(); 1.834 + MIRType powerType = callInfo.getArg(1)->type(); 1.835 + MIRType outputType = getInlineReturnType(); 1.836 + 1.837 + if (outputType != MIRType_Int32 && outputType != MIRType_Double) 1.838 + return InliningStatus_NotInlined; 1.839 + if (!IsNumberType(baseType)) 1.840 + return InliningStatus_NotInlined; 1.841 + if (!IsNumberType(powerType)) 1.842 + return InliningStatus_NotInlined; 1.843 + 1.844 + callInfo.setImplicitlyUsedUnchecked(); 1.845 + 1.846 + MDefinition *base = callInfo.getArg(0); 1.847 + MDefinition *power = callInfo.getArg(1); 1.848 + MDefinition *output = nullptr; 1.849 + 1.850 + // Optimize some constant powers. 1.851 + if (callInfo.getArg(1)->isConstant() && 1.852 + callInfo.getArg(1)->toConstant()->value().isNumber()) 1.853 + { 1.854 + double pow = callInfo.getArg(1)->toConstant()->value().toNumber(); 1.855 + 1.856 + // Math.pow(x, 0.5) is a sqrt with edge-case detection. 1.857 + if (pow == 0.5) { 1.858 + MPowHalf *half = MPowHalf::New(alloc(), base); 1.859 + current->add(half); 1.860 + output = half; 1.861 + } 1.862 + 1.863 + // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases. 1.864 + if (pow == -0.5) { 1.865 + MPowHalf *half = MPowHalf::New(alloc(), base); 1.866 + current->add(half); 1.867 + MConstant *one = MConstant::New(alloc(), DoubleValue(1.0)); 1.868 + current->add(one); 1.869 + MDiv *div = MDiv::New(alloc(), one, half, MIRType_Double); 1.870 + current->add(div); 1.871 + output = div; 1.872 + } 1.873 + 1.874 + // Math.pow(x, 1) == x. 1.875 + if (pow == 1.0) 1.876 + output = base; 1.877 + 1.878 + // Math.pow(x, 2) == x*x. 1.879 + if (pow == 2.0) { 1.880 + MMul *mul = MMul::New(alloc(), base, base, outputType); 1.881 + current->add(mul); 1.882 + output = mul; 1.883 + } 1.884 + 1.885 + // Math.pow(x, 3) == x*x*x. 1.886 + if (pow == 3.0) { 1.887 + MMul *mul1 = MMul::New(alloc(), base, base, outputType); 1.888 + current->add(mul1); 1.889 + MMul *mul2 = MMul::New(alloc(), base, mul1, outputType); 1.890 + current->add(mul2); 1.891 + output = mul2; 1.892 + } 1.893 + 1.894 + // Math.pow(x, 4) == y*y, where y = x*x. 1.895 + if (pow == 4.0) { 1.896 + MMul *y = MMul::New(alloc(), base, base, outputType); 1.897 + current->add(y); 1.898 + MMul *mul = MMul::New(alloc(), y, y, outputType); 1.899 + current->add(mul); 1.900 + output = mul; 1.901 + } 1.902 + } 1.903 + 1.904 + // Use MPow for other powers 1.905 + if (!output) { 1.906 + if (powerType == MIRType_Float32) 1.907 + powerType = MIRType_Double; 1.908 + MPow *pow = MPow::New(alloc(), base, power, powerType); 1.909 + current->add(pow); 1.910 + output = pow; 1.911 + } 1.912 + 1.913 + // Cast to the right type 1.914 + if (outputType == MIRType_Int32 && output->type() != MIRType_Int32) { 1.915 + MToInt32 *toInt = MToInt32::New(alloc(), output); 1.916 + current->add(toInt); 1.917 + output = toInt; 1.918 + } 1.919 + if (outputType == MIRType_Double && output->type() != MIRType_Double) { 1.920 + MToDouble *toDouble = MToDouble::New(alloc(), output); 1.921 + current->add(toDouble); 1.922 + output = toDouble; 1.923 + } 1.924 + 1.925 + current->push(output); 1.926 + return InliningStatus_Inlined; 1.927 +} 1.928 + 1.929 +IonBuilder::InliningStatus 1.930 +IonBuilder::inlineMathRandom(CallInfo &callInfo) 1.931 +{ 1.932 + if (callInfo.constructing()) 1.933 + return InliningStatus_NotInlined; 1.934 + 1.935 + if (getInlineReturnType() != MIRType_Double) 1.936 + return InliningStatus_NotInlined; 1.937 + 1.938 + callInfo.setImplicitlyUsedUnchecked(); 1.939 + 1.940 + MRandom *rand = MRandom::New(alloc()); 1.941 + current->add(rand); 1.942 + current->push(rand); 1.943 + return InliningStatus_Inlined; 1.944 +} 1.945 + 1.946 +IonBuilder::InliningStatus 1.947 +IonBuilder::inlineMathImul(CallInfo &callInfo) 1.948 +{ 1.949 + if (callInfo.argc() != 2 || callInfo.constructing()) 1.950 + return InliningStatus_NotInlined; 1.951 + 1.952 + MIRType returnType = getInlineReturnType(); 1.953 + if (returnType != MIRType_Int32) 1.954 + return InliningStatus_NotInlined; 1.955 + 1.956 + if (!IsNumberType(callInfo.getArg(0)->type())) 1.957 + return InliningStatus_NotInlined; 1.958 + if (!IsNumberType(callInfo.getArg(1)->type())) 1.959 + return InliningStatus_NotInlined; 1.960 + 1.961 + callInfo.setImplicitlyUsedUnchecked(); 1.962 + 1.963 + MInstruction *first = MTruncateToInt32::New(alloc(), callInfo.getArg(0)); 1.964 + current->add(first); 1.965 + 1.966 + MInstruction *second = MTruncateToInt32::New(alloc(), callInfo.getArg(1)); 1.967 + current->add(second); 1.968 + 1.969 + MMul *ins = MMul::New(alloc(), first, second, MIRType_Int32, MMul::Integer); 1.970 + current->add(ins); 1.971 + current->push(ins); 1.972 + return InliningStatus_Inlined; 1.973 +} 1.974 + 1.975 +IonBuilder::InliningStatus 1.976 +IonBuilder::inlineMathFRound(CallInfo &callInfo) 1.977 +{ 1.978 + if (!LIRGenerator::allowFloat32Optimizations()) 1.979 + return InliningStatus_NotInlined; 1.980 + 1.981 + if (callInfo.argc() != 1 || callInfo.constructing()) 1.982 + return InliningStatus_NotInlined; 1.983 + 1.984 + // MIRType can't be Float32, as this point, as getInlineReturnType uses JSVal types 1.985 + // to infer the returned MIR type. 1.986 + types::TemporaryTypeSet *returned = getInlineReturnTypeSet(); 1.987 + if (returned->empty()) { 1.988 + // As there's only one possible returned type, just add it to the observed 1.989 + // returned typeset 1.990 + returned->addType(types::Type::DoubleType(), alloc_->lifoAlloc()); 1.991 + } else { 1.992 + MIRType returnType = getInlineReturnType(); 1.993 + if (!IsNumberType(returnType)) 1.994 + return InliningStatus_NotInlined; 1.995 + } 1.996 + 1.997 + MIRType arg = callInfo.getArg(0)->type(); 1.998 + if (!IsNumberType(arg)) 1.999 + return InliningStatus_NotInlined; 1.1000 + 1.1001 + callInfo.setImplicitlyUsedUnchecked(); 1.1002 + 1.1003 + MToFloat32 *ins = MToFloat32::New(alloc(), callInfo.getArg(0)); 1.1004 + current->add(ins); 1.1005 + current->push(ins); 1.1006 + return InliningStatus_Inlined; 1.1007 +} 1.1008 + 1.1009 +IonBuilder::InliningStatus 1.1010 +IonBuilder::inlineMathMinMax(CallInfo &callInfo, bool max) 1.1011 +{ 1.1012 + if (callInfo.argc() < 2 || callInfo.constructing()) 1.1013 + return InliningStatus_NotInlined; 1.1014 + 1.1015 + MIRType returnType = getInlineReturnType(); 1.1016 + if (!IsNumberType(returnType)) 1.1017 + return InliningStatus_NotInlined; 1.1018 + 1.1019 + for (unsigned i = 0; i < callInfo.argc(); i++) { 1.1020 + MIRType argType = callInfo.getArg(i)->type(); 1.1021 + if (!IsNumberType(argType)) 1.1022 + return InliningStatus_NotInlined; 1.1023 + 1.1024 + // When one of the arguments is double, do a double MMinMax. 1.1025 + if (returnType == MIRType_Int32 && IsFloatingPointType(argType)) 1.1026 + returnType = MIRType_Double; 1.1027 + } 1.1028 + 1.1029 + callInfo.setImplicitlyUsedUnchecked(); 1.1030 + 1.1031 + // Chain N-1 MMinMax instructions to compute the MinMax. 1.1032 + MMinMax *last = MMinMax::New(alloc(), callInfo.getArg(0), callInfo.getArg(1), returnType, max); 1.1033 + current->add(last); 1.1034 + 1.1035 + for (unsigned i = 2; i < callInfo.argc(); i++) { 1.1036 + MMinMax *ins = MMinMax::New(alloc(), last, callInfo.getArg(i), returnType, max); 1.1037 + current->add(ins); 1.1038 + last = ins; 1.1039 + } 1.1040 + 1.1041 + current->push(last); 1.1042 + return InliningStatus_Inlined; 1.1043 +} 1.1044 + 1.1045 +IonBuilder::InliningStatus 1.1046 +IonBuilder::inlineStringObject(CallInfo &callInfo) 1.1047 +{ 1.1048 + if (callInfo.argc() != 1 || !callInfo.constructing()) 1.1049 + return InliningStatus_NotInlined; 1.1050 + 1.1051 + // ConvertToString doesn't support objects. 1.1052 + if (callInfo.getArg(0)->mightBeType(MIRType_Object)) 1.1053 + return InliningStatus_NotInlined; 1.1054 + 1.1055 + JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js_String); 1.1056 + if (!templateObj) 1.1057 + return InliningStatus_NotInlined; 1.1058 + JS_ASSERT(templateObj->is<StringObject>()); 1.1059 + 1.1060 + callInfo.setImplicitlyUsedUnchecked(); 1.1061 + 1.1062 + MNewStringObject *ins = MNewStringObject::New(alloc(), callInfo.getArg(0), templateObj); 1.1063 + current->add(ins); 1.1064 + current->push(ins); 1.1065 + 1.1066 + if (!resumeAfter(ins)) 1.1067 + return InliningStatus_Error; 1.1068 + 1.1069 + return InliningStatus_Inlined; 1.1070 +} 1.1071 + 1.1072 +IonBuilder::InliningStatus 1.1073 +IonBuilder::inlineStringSplit(CallInfo &callInfo) 1.1074 +{ 1.1075 + if (callInfo.argc() != 1 || callInfo.constructing()) 1.1076 + return InliningStatus_NotInlined; 1.1077 + if (callInfo.thisArg()->type() != MIRType_String) 1.1078 + return InliningStatus_NotInlined; 1.1079 + if (callInfo.getArg(0)->type() != MIRType_String) 1.1080 + return InliningStatus_NotInlined; 1.1081 + 1.1082 + JSObject *templateObject = inspector->getTemplateObjectForNative(pc, js::str_split); 1.1083 + if (!templateObject) 1.1084 + return InliningStatus_NotInlined; 1.1085 + JS_ASSERT(templateObject->is<ArrayObject>()); 1.1086 + 1.1087 + types::TypeObjectKey *retType = types::TypeObjectKey::get(templateObject); 1.1088 + if (retType->unknownProperties()) 1.1089 + return InliningStatus_NotInlined; 1.1090 + 1.1091 + types::HeapTypeSetKey key = retType->property(JSID_VOID); 1.1092 + if (!key.maybeTypes()) 1.1093 + return InliningStatus_NotInlined; 1.1094 + 1.1095 + if (!key.maybeTypes()->hasType(types::Type::StringType())) { 1.1096 + key.freeze(constraints()); 1.1097 + return InliningStatus_NotInlined; 1.1098 + } 1.1099 + 1.1100 + callInfo.setImplicitlyUsedUnchecked(); 1.1101 + 1.1102 + MStringSplit *ins = MStringSplit::New(alloc(), constraints(), callInfo.thisArg(), 1.1103 + callInfo.getArg(0), templateObject); 1.1104 + current->add(ins); 1.1105 + current->push(ins); 1.1106 + 1.1107 + return InliningStatus_Inlined; 1.1108 +} 1.1109 + 1.1110 +IonBuilder::InliningStatus 1.1111 +IonBuilder::inlineStrCharCodeAt(CallInfo &callInfo) 1.1112 +{ 1.1113 + if (callInfo.argc() != 1 || callInfo.constructing()) 1.1114 + return InliningStatus_NotInlined; 1.1115 + 1.1116 + if (getInlineReturnType() != MIRType_Int32) 1.1117 + return InliningStatus_NotInlined; 1.1118 + if (callInfo.thisArg()->type() != MIRType_String && callInfo.thisArg()->type() != MIRType_Value) 1.1119 + return InliningStatus_NotInlined; 1.1120 + MIRType argType = callInfo.getArg(0)->type(); 1.1121 + if (argType != MIRType_Int32 && argType != MIRType_Double) 1.1122 + return InliningStatus_NotInlined; 1.1123 + 1.1124 + callInfo.setImplicitlyUsedUnchecked(); 1.1125 + 1.1126 + MInstruction *index = MToInt32::New(alloc(), callInfo.getArg(0)); 1.1127 + current->add(index); 1.1128 + 1.1129 + MStringLength *length = MStringLength::New(alloc(), callInfo.thisArg()); 1.1130 + current->add(length); 1.1131 + 1.1132 + index = addBoundsCheck(index, length); 1.1133 + 1.1134 + MCharCodeAt *charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index); 1.1135 + current->add(charCode); 1.1136 + current->push(charCode); 1.1137 + return InliningStatus_Inlined; 1.1138 +} 1.1139 + 1.1140 +IonBuilder::InliningStatus 1.1141 +IonBuilder::inlineStrFromCharCode(CallInfo &callInfo) 1.1142 +{ 1.1143 + if (callInfo.argc() != 1 || callInfo.constructing()) 1.1144 + return InliningStatus_NotInlined; 1.1145 + 1.1146 + if (getInlineReturnType() != MIRType_String) 1.1147 + return InliningStatus_NotInlined; 1.1148 + if (callInfo.getArg(0)->type() != MIRType_Int32) 1.1149 + return InliningStatus_NotInlined; 1.1150 + 1.1151 + callInfo.setImplicitlyUsedUnchecked(); 1.1152 + 1.1153 + MToInt32 *charCode = MToInt32::New(alloc(), callInfo.getArg(0)); 1.1154 + current->add(charCode); 1.1155 + 1.1156 + MFromCharCode *string = MFromCharCode::New(alloc(), charCode); 1.1157 + current->add(string); 1.1158 + current->push(string); 1.1159 + return InliningStatus_Inlined; 1.1160 +} 1.1161 + 1.1162 +IonBuilder::InliningStatus 1.1163 +IonBuilder::inlineStrCharAt(CallInfo &callInfo) 1.1164 +{ 1.1165 + if (callInfo.argc() != 1 || callInfo.constructing()) 1.1166 + return InliningStatus_NotInlined; 1.1167 + 1.1168 + if (getInlineReturnType() != MIRType_String) 1.1169 + return InliningStatus_NotInlined; 1.1170 + if (callInfo.thisArg()->type() != MIRType_String) 1.1171 + return InliningStatus_NotInlined; 1.1172 + MIRType argType = callInfo.getArg(0)->type(); 1.1173 + if (argType != MIRType_Int32 && argType != MIRType_Double) 1.1174 + return InliningStatus_NotInlined; 1.1175 + 1.1176 + callInfo.setImplicitlyUsedUnchecked(); 1.1177 + 1.1178 + MInstruction *index = MToInt32::New(alloc(), callInfo.getArg(0)); 1.1179 + current->add(index); 1.1180 + 1.1181 + MStringLength *length = MStringLength::New(alloc(), callInfo.thisArg()); 1.1182 + current->add(length); 1.1183 + 1.1184 + index = addBoundsCheck(index, length); 1.1185 + 1.1186 + // String.charAt(x) = String.fromCharCode(String.charCodeAt(x)) 1.1187 + MCharCodeAt *charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index); 1.1188 + current->add(charCode); 1.1189 + 1.1190 + MFromCharCode *string = MFromCharCode::New(alloc(), charCode); 1.1191 + current->add(string); 1.1192 + current->push(string); 1.1193 + return InliningStatus_Inlined; 1.1194 +} 1.1195 + 1.1196 +IonBuilder::InliningStatus 1.1197 +IonBuilder::inlineRegExpExec(CallInfo &callInfo) 1.1198 +{ 1.1199 + if (callInfo.argc() != 1 || callInfo.constructing()) 1.1200 + return InliningStatus_NotInlined; 1.1201 + 1.1202 + if (callInfo.thisArg()->type() != MIRType_Object) 1.1203 + return InliningStatus_NotInlined; 1.1204 + 1.1205 + types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); 1.1206 + const Class *clasp = thisTypes ? thisTypes->getKnownClass() : nullptr; 1.1207 + if (clasp != &RegExpObject::class_) 1.1208 + return InliningStatus_NotInlined; 1.1209 + 1.1210 + if (callInfo.getArg(0)->mightBeType(MIRType_Object)) 1.1211 + return InliningStatus_NotInlined; 1.1212 + 1.1213 + callInfo.setImplicitlyUsedUnchecked(); 1.1214 + 1.1215 + MInstruction *exec = MRegExpExec::New(alloc(), callInfo.thisArg(), callInfo.getArg(0)); 1.1216 + current->add(exec); 1.1217 + current->push(exec); 1.1218 + 1.1219 + if (!resumeAfter(exec)) 1.1220 + return InliningStatus_Error; 1.1221 + 1.1222 + if (!pushTypeBarrier(exec, getInlineReturnTypeSet(), true)) 1.1223 + return InliningStatus_Error; 1.1224 + 1.1225 + return InliningStatus_Inlined; 1.1226 +} 1.1227 + 1.1228 +IonBuilder::InliningStatus 1.1229 +IonBuilder::inlineRegExpTest(CallInfo &callInfo) 1.1230 +{ 1.1231 + if (callInfo.argc() != 1 || callInfo.constructing()) 1.1232 + return InliningStatus_NotInlined; 1.1233 + 1.1234 + // TI can infer a nullptr return type of regexp_test with eager compilation. 1.1235 + if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean) 1.1236 + return InliningStatus_NotInlined; 1.1237 + 1.1238 + if (callInfo.thisArg()->type() != MIRType_Object) 1.1239 + return InliningStatus_NotInlined; 1.1240 + types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); 1.1241 + const Class *clasp = thisTypes ? thisTypes->getKnownClass() : nullptr; 1.1242 + if (clasp != &RegExpObject::class_) 1.1243 + return InliningStatus_NotInlined; 1.1244 + if (callInfo.getArg(0)->mightBeType(MIRType_Object)) 1.1245 + return InliningStatus_NotInlined; 1.1246 + 1.1247 + callInfo.setImplicitlyUsedUnchecked(); 1.1248 + 1.1249 + MInstruction *match = MRegExpTest::New(alloc(), callInfo.thisArg(), callInfo.getArg(0)); 1.1250 + current->add(match); 1.1251 + current->push(match); 1.1252 + if (!resumeAfter(match)) 1.1253 + return InliningStatus_Error; 1.1254 + 1.1255 + return InliningStatus_Inlined; 1.1256 +} 1.1257 + 1.1258 +IonBuilder::InliningStatus 1.1259 +IonBuilder::inlineStrReplace(CallInfo &callInfo) 1.1260 +{ 1.1261 + if (callInfo.argc() != 2 || callInfo.constructing()) 1.1262 + return InliningStatus_NotInlined; 1.1263 + 1.1264 + // Return: String. 1.1265 + if (getInlineReturnType() != MIRType_String) 1.1266 + return InliningStatus_NotInlined; 1.1267 + 1.1268 + // This: String. 1.1269 + if (callInfo.thisArg()->type() != MIRType_String) 1.1270 + return InliningStatus_NotInlined; 1.1271 + 1.1272 + // Arg 0: RegExp. 1.1273 + types::TemporaryTypeSet *arg0Type = callInfo.getArg(0)->resultTypeSet(); 1.1274 + const Class *clasp = arg0Type ? arg0Type->getKnownClass() : nullptr; 1.1275 + if (clasp != &RegExpObject::class_ && callInfo.getArg(0)->type() != MIRType_String) 1.1276 + return InliningStatus_NotInlined; 1.1277 + 1.1278 + // Arg 1: String. 1.1279 + if (callInfo.getArg(1)->type() != MIRType_String) 1.1280 + return InliningStatus_NotInlined; 1.1281 + 1.1282 + callInfo.setImplicitlyUsedUnchecked(); 1.1283 + 1.1284 + MInstruction *cte; 1.1285 + if (callInfo.getArg(0)->type() == MIRType_String) { 1.1286 + cte = MStringReplace::New(alloc(), callInfo.thisArg(), callInfo.getArg(0), 1.1287 + callInfo.getArg(1)); 1.1288 + } else { 1.1289 + cte = MRegExpReplace::New(alloc(), callInfo.thisArg(), callInfo.getArg(0), 1.1290 + callInfo.getArg(1)); 1.1291 + } 1.1292 + current->add(cte); 1.1293 + current->push(cte); 1.1294 + if (cte->isEffectful() && !resumeAfter(cte)) 1.1295 + return InliningStatus_Error; 1.1296 + return InliningStatus_Inlined; 1.1297 +} 1.1298 + 1.1299 +IonBuilder::InliningStatus 1.1300 +IonBuilder::inlineUnsafePutElements(CallInfo &callInfo) 1.1301 +{ 1.1302 + uint32_t argc = callInfo.argc(); 1.1303 + if (argc < 3 || (argc % 3) != 0 || callInfo.constructing()) 1.1304 + return InliningStatus_NotInlined; 1.1305 + 1.1306 + /* Important: 1.1307 + * 1.1308 + * Here we inline each of the stores resulting from a call to 1.1309 + * UnsafePutElements(). It is essential that these stores occur 1.1310 + * atomically and cannot be interrupted by a stack or recursion 1.1311 + * check. If this is not true, race conditions can occur. 1.1312 + */ 1.1313 + 1.1314 + for (uint32_t base = 0; base < argc; base += 3) { 1.1315 + uint32_t arri = base + 0; 1.1316 + uint32_t idxi = base + 1; 1.1317 + uint32_t elemi = base + 2; 1.1318 + 1.1319 + MDefinition *obj = callInfo.getArg(arri); 1.1320 + MDefinition *id = callInfo.getArg(idxi); 1.1321 + MDefinition *elem = callInfo.getArg(elemi); 1.1322 + 1.1323 + bool isDenseNative = ElementAccessIsDenseNative(obj, id); 1.1324 + 1.1325 + bool writeNeedsBarrier = false; 1.1326 + if (isDenseNative) { 1.1327 + writeNeedsBarrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, 1.1328 + &obj, nullptr, &elem, 1.1329 + /* canModify = */ false); 1.1330 + } 1.1331 + 1.1332 + // We can only inline setelem on dense arrays that do not need type 1.1333 + // barriers and on typed arrays and on typed object arrays. 1.1334 + ScalarTypeDescr::Type arrayType; 1.1335 + if ((!isDenseNative || writeNeedsBarrier) && 1.1336 + !ElementAccessIsTypedArray(obj, id, &arrayType) && 1.1337 + !elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType)) 1.1338 + { 1.1339 + return InliningStatus_NotInlined; 1.1340 + } 1.1341 + } 1.1342 + 1.1343 + callInfo.setImplicitlyUsedUnchecked(); 1.1344 + 1.1345 + // Push the result first so that the stack depth matches up for 1.1346 + // the potential bailouts that will occur in the stores below. 1.1347 + MConstant *udef = MConstant::New(alloc(), UndefinedValue()); 1.1348 + current->add(udef); 1.1349 + current->push(udef); 1.1350 + 1.1351 + for (uint32_t base = 0; base < argc; base += 3) { 1.1352 + uint32_t arri = base + 0; 1.1353 + uint32_t idxi = base + 1; 1.1354 + 1.1355 + MDefinition *obj = callInfo.getArg(arri); 1.1356 + MDefinition *id = callInfo.getArg(idxi); 1.1357 + 1.1358 + if (ElementAccessIsDenseNative(obj, id)) { 1.1359 + if (!inlineUnsafeSetDenseArrayElement(callInfo, base)) 1.1360 + return InliningStatus_Error; 1.1361 + continue; 1.1362 + } 1.1363 + 1.1364 + ScalarTypeDescr::Type arrayType; 1.1365 + if (ElementAccessIsTypedArray(obj, id, &arrayType)) { 1.1366 + if (!inlineUnsafeSetTypedArrayElement(callInfo, base, arrayType)) 1.1367 + return InliningStatus_Error; 1.1368 + continue; 1.1369 + } 1.1370 + 1.1371 + if (elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType)) { 1.1372 + if (!inlineUnsafeSetTypedObjectArrayElement(callInfo, base, arrayType)) 1.1373 + return InliningStatus_Error; 1.1374 + continue; 1.1375 + } 1.1376 + 1.1377 + MOZ_ASSUME_UNREACHABLE("Element access not dense array nor typed array"); 1.1378 + } 1.1379 + 1.1380 + return InliningStatus_Inlined; 1.1381 +} 1.1382 + 1.1383 +bool 1.1384 +IonBuilder::elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id, 1.1385 + ScalarTypeDescr::Type *arrayType) 1.1386 +{ 1.1387 + if (obj->type() != MIRType_Object) // lookupTypeDescrSet() tests for TypedObject 1.1388 + return false; 1.1389 + 1.1390 + if (id->type() != MIRType_Int32 && id->type() != MIRType_Double) 1.1391 + return false; 1.1392 + 1.1393 + TypeDescrSet objDescrs; 1.1394 + if (!lookupTypeDescrSet(obj, &objDescrs)) 1.1395 + return false; 1.1396 + 1.1397 + if (!objDescrs.allOfArrayKind()) 1.1398 + return false; 1.1399 + 1.1400 + TypeDescrSet elemDescrs; 1.1401 + if (!objDescrs.arrayElementType(*this, &elemDescrs)) 1.1402 + return false; 1.1403 + 1.1404 + if (elemDescrs.empty() || elemDescrs.kind() != TypeDescr::Scalar) 1.1405 + return false; 1.1406 + 1.1407 + JS_ASSERT(TypeDescr::isSized(elemDescrs.kind())); 1.1408 + 1.1409 + return elemDescrs.scalarType(arrayType); 1.1410 +} 1.1411 + 1.1412 +bool 1.1413 +IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base) 1.1414 +{ 1.1415 + // Note: we do not check the conditions that are asserted as true 1.1416 + // in intrinsic_UnsafePutElements(): 1.1417 + // - arr is a dense array 1.1418 + // - idx < initialized length 1.1419 + // Furthermore, note that inlineUnsafePutElements ensures the type of the 1.1420 + // value is reflected in the JSID_VOID property of the array. 1.1421 + 1.1422 + MDefinition *obj = callInfo.getArg(base + 0); 1.1423 + MDefinition *id = callInfo.getArg(base + 1); 1.1424 + MDefinition *elem = callInfo.getArg(base + 2); 1.1425 + 1.1426 + types::TemporaryTypeSet::DoubleConversion conversion = 1.1427 + obj->resultTypeSet()->convertDoubleElements(constraints()); 1.1428 + if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem)) 1.1429 + return false; 1.1430 + return true; 1.1431 +} 1.1432 + 1.1433 +bool 1.1434 +IonBuilder::inlineUnsafeSetTypedArrayElement(CallInfo &callInfo, 1.1435 + uint32_t base, 1.1436 + ScalarTypeDescr::Type arrayType) 1.1437 +{ 1.1438 + // Note: we do not check the conditions that are asserted as true 1.1439 + // in intrinsic_UnsafePutElements(): 1.1440 + // - arr is a typed array 1.1441 + // - idx < length 1.1442 + 1.1443 + MDefinition *obj = callInfo.getArg(base + 0); 1.1444 + MDefinition *id = callInfo.getArg(base + 1); 1.1445 + MDefinition *elem = callInfo.getArg(base + 2); 1.1446 + 1.1447 + if (!jsop_setelem_typed(arrayType, SetElem_Unsafe, obj, id, elem)) 1.1448 + return false; 1.1449 + 1.1450 + return true; 1.1451 +} 1.1452 + 1.1453 +bool 1.1454 +IonBuilder::inlineUnsafeSetTypedObjectArrayElement(CallInfo &callInfo, 1.1455 + uint32_t base, 1.1456 + ScalarTypeDescr::Type arrayType) 1.1457 +{ 1.1458 + // Note: we do not check the conditions that are asserted as true 1.1459 + // in intrinsic_UnsafePutElements(): 1.1460 + // - arr is a typed array 1.1461 + // - idx < length 1.1462 + 1.1463 + MDefinition *obj = callInfo.getArg(base + 0); 1.1464 + MDefinition *id = callInfo.getArg(base + 1); 1.1465 + MDefinition *elem = callInfo.getArg(base + 2); 1.1466 + 1.1467 + if (!jsop_setelem_typed_object(arrayType, SetElem_Unsafe, true, obj, id, elem)) 1.1468 + return false; 1.1469 + 1.1470 + return true; 1.1471 +} 1.1472 + 1.1473 +IonBuilder::InliningStatus 1.1474 +IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo) 1.1475 +{ 1.1476 + if (callInfo.constructing()) 1.1477 + return InliningStatus_NotInlined; 1.1478 + 1.1479 + ExecutionMode executionMode = info().executionMode(); 1.1480 + switch (executionMode) { 1.1481 + case ParallelExecution: { 1.1482 + // During Parallel Exec, we always force sequential, so 1.1483 + // replace with true. This permits UCE to eliminate the 1.1484 + // entire path as dead, which is important. 1.1485 + callInfo.setImplicitlyUsedUnchecked(); 1.1486 + MConstant *ins = MConstant::New(alloc(), BooleanValue(true)); 1.1487 + current->add(ins); 1.1488 + current->push(ins); 1.1489 + return InliningStatus_Inlined; 1.1490 + } 1.1491 + 1.1492 + default: 1.1493 + // In sequential mode, leave as is, because we'd have to 1.1494 + // access the "in warmup" flag of the runtime. 1.1495 + return InliningStatus_NotInlined; 1.1496 + } 1.1497 + 1.1498 + MOZ_ASSUME_UNREACHABLE("Invalid execution mode"); 1.1499 +} 1.1500 + 1.1501 +IonBuilder::InliningStatus 1.1502 +IonBuilder::inlineForkJoinGetSlice(CallInfo &callInfo) 1.1503 +{ 1.1504 + if (info().executionMode() != ParallelExecution) 1.1505 + return InliningStatus_NotInlined; 1.1506 + 1.1507 + // Assert the way the function is used instead of testing, as it is a 1.1508 + // self-hosted function which must be used in a particular fashion. 1.1509 + MOZ_ASSERT(callInfo.argc() == 1 && !callInfo.constructing()); 1.1510 + MOZ_ASSERT(callInfo.getArg(0)->type() == MIRType_Int32); 1.1511 + 1.1512 + // Test this, as we might have not executed the native despite knowing the 1.1513 + // target here. 1.1514 + if (getInlineReturnType() != MIRType_Int32) 1.1515 + return InliningStatus_NotInlined; 1.1516 + 1.1517 + callInfo.setImplicitlyUsedUnchecked(); 1.1518 + 1.1519 + switch (info().executionMode()) { 1.1520 + case ParallelExecution: 1.1521 + if (LIRGenerator::allowInlineForkJoinGetSlice()) { 1.1522 + MForkJoinGetSlice *getSlice = MForkJoinGetSlice::New(alloc(), 1.1523 + graph().forkJoinContext()); 1.1524 + current->add(getSlice); 1.1525 + current->push(getSlice); 1.1526 + return InliningStatus_Inlined; 1.1527 + } 1.1528 + return InliningStatus_NotInlined; 1.1529 + 1.1530 + default: 1.1531 + // ForkJoinGetSlice acts as identity for sequential execution. 1.1532 + current->push(callInfo.getArg(0)); 1.1533 + return InliningStatus_Inlined; 1.1534 + } 1.1535 + 1.1536 + MOZ_ASSUME_UNREACHABLE("Invalid execution mode"); 1.1537 +} 1.1538 + 1.1539 +IonBuilder::InliningStatus 1.1540 +IonBuilder::inlineNewDenseArray(CallInfo &callInfo) 1.1541 +{ 1.1542 + if (callInfo.constructing() || callInfo.argc() != 1) 1.1543 + return InliningStatus_NotInlined; 1.1544 + 1.1545 + // For now, in seq. mode we just call the C function. In 1.1546 + // par. mode we use inlined MIR. 1.1547 + ExecutionMode executionMode = info().executionMode(); 1.1548 + switch (executionMode) { 1.1549 + case ParallelExecution: 1.1550 + return inlineNewDenseArrayForParallelExecution(callInfo); 1.1551 + default: 1.1552 + return inlineNewDenseArrayForSequentialExecution(callInfo); 1.1553 + } 1.1554 + 1.1555 + MOZ_ASSUME_UNREACHABLE("unknown ExecutionMode"); 1.1556 +} 1.1557 + 1.1558 +IonBuilder::InliningStatus 1.1559 +IonBuilder::inlineNewDenseArrayForSequentialExecution(CallInfo &callInfo) 1.1560 +{ 1.1561 + // not yet implemented; in seq. mode the C function is not so bad 1.1562 + return InliningStatus_NotInlined; 1.1563 +} 1.1564 + 1.1565 +IonBuilder::InliningStatus 1.1566 +IonBuilder::inlineNewDenseArrayForParallelExecution(CallInfo &callInfo) 1.1567 +{ 1.1568 + // Create the new parallel array object. Parallel arrays have specially 1.1569 + // constructed type objects, so we can only perform the inlining if we 1.1570 + // already have one of these type objects. 1.1571 + types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet(); 1.1572 + if (returnTypes->getKnownMIRType() != MIRType_Object) 1.1573 + return InliningStatus_NotInlined; 1.1574 + if (returnTypes->unknownObject() || returnTypes->getObjectCount() != 1) 1.1575 + return InliningStatus_NotInlined; 1.1576 + if (callInfo.getArg(0)->type() != MIRType_Int32) 1.1577 + return InliningStatus_NotInlined; 1.1578 + types::TypeObject *typeObject = returnTypes->getTypeObject(0); 1.1579 + 1.1580 + JSObject *templateObject = inspector->getTemplateObjectForNative(pc, intrinsic_NewDenseArray); 1.1581 + if (!templateObject || templateObject->type() != typeObject) 1.1582 + return InliningStatus_NotInlined; 1.1583 + 1.1584 + callInfo.setImplicitlyUsedUnchecked(); 1.1585 + 1.1586 + MNewDenseArrayPar *newObject = MNewDenseArrayPar::New(alloc(), 1.1587 + graph().forkJoinContext(), 1.1588 + callInfo.getArg(0), 1.1589 + templateObject); 1.1590 + current->add(newObject); 1.1591 + current->push(newObject); 1.1592 + 1.1593 + return InliningStatus_Inlined; 1.1594 +} 1.1595 + 1.1596 +IonBuilder::InliningStatus 1.1597 +IonBuilder::inlineHasClasses(CallInfo &callInfo, const Class *clasp1, const Class *clasp2) 1.1598 +{ 1.1599 + // Thus far there has been no reason to complicate this beyond two classes, 1.1600 + // though it generalizes pretty well. 1.1601 + // clasp2 may be NULL. 1.1602 + if (callInfo.constructing() || callInfo.argc() != 1) 1.1603 + return InliningStatus_NotInlined; 1.1604 + 1.1605 + if (callInfo.getArg(0)->type() != MIRType_Object) 1.1606 + return InliningStatus_NotInlined; 1.1607 + if (getInlineReturnType() != MIRType_Boolean) 1.1608 + return InliningStatus_NotInlined; 1.1609 + 1.1610 + types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet(); 1.1611 + const Class *knownClass = types ? types->getKnownClass() : nullptr; 1.1612 + if (knownClass) { 1.1613 + pushConstant(BooleanValue(knownClass == clasp1 || knownClass == clasp2)); 1.1614 + } else { 1.1615 + MHasClass *hasClass1 = MHasClass::New(alloc(), callInfo.getArg(0), clasp1); 1.1616 + current->add(hasClass1); 1.1617 + if (clasp2 == nullptr) { 1.1618 + current->push(hasClass1); 1.1619 + } else { 1.1620 + // The following turns into branch-free, box-free code on x86, and should do so on ARM. 1.1621 + MHasClass *hasClass2 = MHasClass::New(alloc(), callInfo.getArg(0), clasp2); 1.1622 + current->add(hasClass2); 1.1623 + MBitOr *either = MBitOr::New(alloc(), hasClass1, hasClass2); 1.1624 + either->infer(inspector, pc); 1.1625 + current->add(either); 1.1626 + // Convert to bool with the '!!' idiom 1.1627 + MNot *resultInverted = MNot::New(alloc(), either); 1.1628 + resultInverted->infer(); 1.1629 + current->add(resultInverted); 1.1630 + MNot *result = MNot::New(alloc(), resultInverted); 1.1631 + result->infer(); 1.1632 + current->add(result); 1.1633 + current->push(result); 1.1634 + } 1.1635 + } 1.1636 + 1.1637 + callInfo.setImplicitlyUsedUnchecked(); 1.1638 + return InliningStatus_Inlined; 1.1639 +} 1.1640 + 1.1641 +IonBuilder::InliningStatus 1.1642 +IonBuilder::inlineObjectIsTypeDescr(CallInfo &callInfo) 1.1643 +{ 1.1644 + if (callInfo.constructing() || callInfo.argc() != 1) 1.1645 + return InliningStatus_NotInlined; 1.1646 + 1.1647 + if (callInfo.getArg(0)->type() != MIRType_Object) 1.1648 + return InliningStatus_NotInlined; 1.1649 + if (getInlineReturnType() != MIRType_Boolean) 1.1650 + return InliningStatus_NotInlined; 1.1651 + 1.1652 + // The test is elaborate: in-line only if there is exact 1.1653 + // information. 1.1654 + 1.1655 + types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet(); 1.1656 + if (!types) 1.1657 + return InliningStatus_NotInlined; 1.1658 + 1.1659 + bool result = false; 1.1660 + switch (types->forAllClasses(IsTypeDescrClass)) { 1.1661 + case types::TemporaryTypeSet::ForAllResult::ALL_FALSE: 1.1662 + case types::TemporaryTypeSet::ForAllResult::EMPTY: 1.1663 + result = false; 1.1664 + break; 1.1665 + case types::TemporaryTypeSet::ForAllResult::ALL_TRUE: 1.1666 + result = true; 1.1667 + break; 1.1668 + case types::TemporaryTypeSet::ForAllResult::MIXED: 1.1669 + return InliningStatus_NotInlined; 1.1670 + } 1.1671 + 1.1672 + pushConstant(BooleanValue(result)); 1.1673 + 1.1674 + callInfo.setImplicitlyUsedUnchecked(); 1.1675 + return InliningStatus_Inlined; 1.1676 +} 1.1677 + 1.1678 +IonBuilder::InliningStatus 1.1679 +IonBuilder::inlineSetTypedObjectOffset(CallInfo &callInfo) 1.1680 +{ 1.1681 + if (callInfo.argc() != 2 || callInfo.constructing()) 1.1682 + return InliningStatus_NotInlined; 1.1683 + 1.1684 + MDefinition *typedObj = callInfo.getArg(0); 1.1685 + MDefinition *offset = callInfo.getArg(1); 1.1686 + 1.1687 + // Return type should be undefined or something wacky is going on. 1.1688 + if (getInlineReturnType() != MIRType_Undefined) 1.1689 + return InliningStatus_NotInlined; 1.1690 + 1.1691 + // Check typedObj is a, well, typed object. Go ahead and use TI 1.1692 + // data. If this check should fail, that is almost certainly a bug 1.1693 + // in self-hosted code -- either because it's not being careful 1.1694 + // with TI or because of something else -- but we'll just let it 1.1695 + // fall through to the SetTypedObjectOffset intrinsic in such 1.1696 + // cases. 1.1697 + types::TemporaryTypeSet *types = typedObj->resultTypeSet(); 1.1698 + if (typedObj->type() != MIRType_Object || !types) 1.1699 + return InliningStatus_NotInlined; 1.1700 + switch (types->forAllClasses(IsTypedObjectClass)) { 1.1701 + case types::TemporaryTypeSet::ForAllResult::ALL_FALSE: 1.1702 + case types::TemporaryTypeSet::ForAllResult::EMPTY: 1.1703 + case types::TemporaryTypeSet::ForAllResult::MIXED: 1.1704 + return InliningStatus_NotInlined; 1.1705 + case types::TemporaryTypeSet::ForAllResult::ALL_TRUE: 1.1706 + break; 1.1707 + } 1.1708 + 1.1709 + // Check type of offset argument is an integer. 1.1710 + if (offset->type() != MIRType_Int32) 1.1711 + return InliningStatus_NotInlined; 1.1712 + 1.1713 + callInfo.setImplicitlyUsedUnchecked(); 1.1714 + MInstruction *ins = MSetTypedObjectOffset::New(alloc(), typedObj, offset); 1.1715 + current->add(ins); 1.1716 + current->push(ins); 1.1717 + return InliningStatus_Inlined; 1.1718 +} 1.1719 + 1.1720 +IonBuilder::InliningStatus 1.1721 +IonBuilder::inlineUnsafeSetReservedSlot(CallInfo &callInfo) 1.1722 +{ 1.1723 + if (callInfo.argc() != 3 || callInfo.constructing()) 1.1724 + return InliningStatus_NotInlined; 1.1725 + if (getInlineReturnType() != MIRType_Undefined) 1.1726 + return InliningStatus_NotInlined; 1.1727 + if (callInfo.getArg(0)->type() != MIRType_Object) 1.1728 + return InliningStatus_NotInlined; 1.1729 + if (callInfo.getArg(1)->type() != MIRType_Int32) 1.1730 + return InliningStatus_NotInlined; 1.1731 + 1.1732 + // Don't inline if we don't have a constant slot. 1.1733 + MDefinition *arg = callInfo.getArg(1); 1.1734 + if (!arg->isConstant()) 1.1735 + return InliningStatus_NotInlined; 1.1736 + uint32_t slot = arg->toConstant()->value().toPrivateUint32(); 1.1737 + 1.1738 + callInfo.setImplicitlyUsedUnchecked(); 1.1739 + 1.1740 + MStoreFixedSlot *store = MStoreFixedSlot::New(alloc(), callInfo.getArg(0), slot, callInfo.getArg(2)); 1.1741 + current->add(store); 1.1742 + current->push(store); 1.1743 + 1.1744 + if (NeedsPostBarrier(info(), callInfo.getArg(2))) 1.1745 + current->add(MPostWriteBarrier::New(alloc(), callInfo.thisArg(), callInfo.getArg(2))); 1.1746 + 1.1747 + return InliningStatus_Inlined; 1.1748 +} 1.1749 + 1.1750 +IonBuilder::InliningStatus 1.1751 +IonBuilder::inlineUnsafeGetReservedSlot(CallInfo &callInfo) 1.1752 +{ 1.1753 + if (callInfo.argc() != 2 || callInfo.constructing()) 1.1754 + return InliningStatus_NotInlined; 1.1755 + if (callInfo.getArg(0)->type() != MIRType_Object) 1.1756 + return InliningStatus_NotInlined; 1.1757 + if (callInfo.getArg(1)->type() != MIRType_Int32) 1.1758 + return InliningStatus_NotInlined; 1.1759 + 1.1760 + // Don't inline if we don't have a constant slot. 1.1761 + MDefinition *arg = callInfo.getArg(1); 1.1762 + if (!arg->isConstant()) 1.1763 + return InliningStatus_NotInlined; 1.1764 + uint32_t slot = arg->toConstant()->value().toPrivateUint32(); 1.1765 + 1.1766 + callInfo.setImplicitlyUsedUnchecked(); 1.1767 + 1.1768 + MLoadFixedSlot *load = MLoadFixedSlot::New(alloc(), callInfo.getArg(0), slot); 1.1769 + current->add(load); 1.1770 + current->push(load); 1.1771 + 1.1772 + // We don't track reserved slot types, so always emit a barrier. 1.1773 + if (!pushTypeBarrier(load, getInlineReturnTypeSet(), true)) 1.1774 + return InliningStatus_Error; 1.1775 + 1.1776 + return InliningStatus_Inlined; 1.1777 +} 1.1778 + 1.1779 +IonBuilder::InliningStatus 1.1780 +IonBuilder::inlineHaveSameClass(CallInfo &callInfo) 1.1781 +{ 1.1782 + if (callInfo.argc() != 2 || callInfo.constructing()) 1.1783 + return InliningStatus_NotInlined; 1.1784 + if (callInfo.getArg(0)->type() != MIRType_Object) 1.1785 + return InliningStatus_NotInlined; 1.1786 + if (callInfo.getArg(1)->type() != MIRType_Object) 1.1787 + return InliningStatus_NotInlined; 1.1788 + 1.1789 + types::TemporaryTypeSet *arg1Types = callInfo.getArg(0)->resultTypeSet(); 1.1790 + types::TemporaryTypeSet *arg2Types = callInfo.getArg(1)->resultTypeSet(); 1.1791 + const Class *arg1Clasp = arg1Types ? arg1Types->getKnownClass() : nullptr; 1.1792 + const Class *arg2Clasp = arg2Types ? arg2Types->getKnownClass() : nullptr; 1.1793 + if (arg1Clasp && arg2Clasp) { 1.1794 + MConstant *constant = MConstant::New(alloc(), BooleanValue(arg1Clasp == arg2Clasp)); 1.1795 + current->add(constant); 1.1796 + current->push(constant); 1.1797 + return InliningStatus_Inlined; 1.1798 + } 1.1799 + 1.1800 + callInfo.setImplicitlyUsedUnchecked(); 1.1801 + 1.1802 + MHaveSameClass *sameClass = MHaveSameClass::New(alloc(), callInfo.getArg(0), callInfo.getArg(1)); 1.1803 + current->add(sameClass); 1.1804 + current->push(sameClass); 1.1805 + 1.1806 + return InliningStatus_Inlined; 1.1807 +} 1.1808 + 1.1809 +IonBuilder::InliningStatus 1.1810 +IonBuilder::inlineIsCallable(CallInfo &callInfo) 1.1811 +{ 1.1812 + if (callInfo.argc() != 1 || callInfo.constructing()) 1.1813 + return InliningStatus_NotInlined; 1.1814 + 1.1815 + if (getInlineReturnType() != MIRType_Boolean) 1.1816 + return InliningStatus_NotInlined; 1.1817 + if (callInfo.getArg(0)->type() != MIRType_Object) 1.1818 + return InliningStatus_NotInlined; 1.1819 + 1.1820 + // Try inlining with constant true/false: only objects may be callable at 1.1821 + // all, and if we know the class check if it is callable. 1.1822 + bool isCallableKnown = false; 1.1823 + bool isCallableConstant; 1.1824 + if (callInfo.getArg(0)->type() != MIRType_Object) { 1.1825 + isCallableKnown = true; 1.1826 + isCallableConstant = false; 1.1827 + } else { 1.1828 + types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet(); 1.1829 + const Class *clasp = types ? types->getKnownClass() : nullptr; 1.1830 + if (clasp) { 1.1831 + isCallableKnown = true; 1.1832 + isCallableConstant = clasp->isCallable(); 1.1833 + } 1.1834 + } 1.1835 + 1.1836 + callInfo.setImplicitlyUsedUnchecked(); 1.1837 + 1.1838 + if (isCallableKnown) { 1.1839 + MConstant *constant = MConstant::New(alloc(), BooleanValue(isCallableConstant)); 1.1840 + current->add(constant); 1.1841 + current->push(constant); 1.1842 + return InliningStatus_Inlined; 1.1843 + } 1.1844 + 1.1845 + MIsCallable *isCallable = MIsCallable::New(alloc(), callInfo.getArg(0)); 1.1846 + current->add(isCallable); 1.1847 + current->push(isCallable); 1.1848 + 1.1849 + return InliningStatus_Inlined; 1.1850 +} 1.1851 + 1.1852 +IonBuilder::InliningStatus 1.1853 +IonBuilder::inlineToObject(CallInfo &callInfo) 1.1854 +{ 1.1855 + if (callInfo.argc() != 1 || callInfo.constructing()) 1.1856 + return InliningStatus_NotInlined; 1.1857 + 1.1858 + // If we know the input type is an object, nop ToObject. 1.1859 + if (getInlineReturnType() != MIRType_Object) 1.1860 + return InliningStatus_NotInlined; 1.1861 + if (callInfo.getArg(0)->type() != MIRType_Object) 1.1862 + return InliningStatus_NotInlined; 1.1863 + 1.1864 + callInfo.setImplicitlyUsedUnchecked(); 1.1865 + MDefinition *object = callInfo.getArg(0); 1.1866 + 1.1867 + current->push(object); 1.1868 + return InliningStatus_Inlined; 1.1869 +} 1.1870 + 1.1871 +IonBuilder::InliningStatus 1.1872 +IonBuilder::inlineBailout(CallInfo &callInfo) 1.1873 +{ 1.1874 + callInfo.setImplicitlyUsedUnchecked(); 1.1875 + 1.1876 + current->add(MBail::New(alloc())); 1.1877 + 1.1878 + MConstant *undefined = MConstant::New(alloc(), UndefinedValue()); 1.1879 + current->add(undefined); 1.1880 + current->push(undefined); 1.1881 + return InliningStatus_Inlined; 1.1882 +} 1.1883 + 1.1884 +IonBuilder::InliningStatus 1.1885 +IonBuilder::inlineAssertFloat32(CallInfo &callInfo) 1.1886 +{ 1.1887 + callInfo.setImplicitlyUsedUnchecked(); 1.1888 + 1.1889 + MDefinition *secondArg = callInfo.getArg(1); 1.1890 + 1.1891 + JS_ASSERT(secondArg->type() == MIRType_Boolean); 1.1892 + JS_ASSERT(secondArg->isConstant()); 1.1893 + 1.1894 + bool mustBeFloat32 = JSVAL_TO_BOOLEAN(secondArg->toConstant()->value()); 1.1895 + current->add(MAssertFloat32::New(alloc(), callInfo.getArg(0), mustBeFloat32)); 1.1896 + 1.1897 + MConstant *undefined = MConstant::New(alloc(), UndefinedValue()); 1.1898 + current->add(undefined); 1.1899 + current->push(undefined); 1.1900 + return InliningStatus_Inlined; 1.1901 +} 1.1902 + 1.1903 +IonBuilder::InliningStatus 1.1904 +IonBuilder::inlineBoundFunction(CallInfo &nativeCallInfo, JSFunction *target) 1.1905 +{ 1.1906 + if (!target->getBoundFunctionTarget()->is<JSFunction>()) 1.1907 + return InliningStatus_NotInlined; 1.1908 + 1.1909 + JSFunction *scriptedTarget = &(target->getBoundFunctionTarget()->as<JSFunction>()); 1.1910 + JSRuntime *runtime = scriptedTarget->runtimeFromMainThread(); 1.1911 + 1.1912 + // Don't optimize if we're constructing and the callee is not a 1.1913 + // constructor, so that CallKnown does not have to handle this case 1.1914 + // (it should always throw). 1.1915 + if (nativeCallInfo.constructing() && !scriptedTarget->isInterpretedConstructor() && 1.1916 + !scriptedTarget->isNativeConstructor()) 1.1917 + { 1.1918 + return InliningStatus_NotInlined; 1.1919 + } 1.1920 + 1.1921 + if (gc::IsInsideNursery(runtime, scriptedTarget)) 1.1922 + return InliningStatus_NotInlined; 1.1923 + 1.1924 + for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) { 1.1925 + const Value val = target->getBoundFunctionArgument(i); 1.1926 + if (val.isObject() && gc::IsInsideNursery(runtime, &val.toObject())) 1.1927 + return InliningStatus_NotInlined; 1.1928 + } 1.1929 + 1.1930 + const Value thisVal = target->getBoundFunctionThis(); 1.1931 + if (thisVal.isObject() && gc::IsInsideNursery(runtime, &thisVal.toObject())) 1.1932 + return InliningStatus_NotInlined; 1.1933 + 1.1934 + size_t argc = target->getBoundFunctionArgumentCount() + nativeCallInfo.argc(); 1.1935 + if (argc > ARGS_LENGTH_MAX) 1.1936 + return InliningStatus_NotInlined; 1.1937 + 1.1938 + nativeCallInfo.thisArg()->setImplicitlyUsedUnchecked(); 1.1939 + 1.1940 + CallInfo callInfo(alloc(), nativeCallInfo.constructing()); 1.1941 + callInfo.setFun(constant(ObjectValue(*scriptedTarget))); 1.1942 + callInfo.setThis(constant(target->getBoundFunctionThis())); 1.1943 + 1.1944 + if (!callInfo.argv().reserve(argc)) 1.1945 + return InliningStatus_Error; 1.1946 + 1.1947 + for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) 1.1948 + callInfo.argv().infallibleAppend(constant(target->getBoundFunctionArgument(i))); 1.1949 + for (size_t i = 0; i < nativeCallInfo.argc(); i++) 1.1950 + callInfo.argv().infallibleAppend(nativeCallInfo.getArg(i)); 1.1951 + 1.1952 + if (!makeCall(scriptedTarget, callInfo, false)) 1.1953 + return InliningStatus_Error; 1.1954 + 1.1955 + return InliningStatus_Inlined; 1.1956 +} 1.1957 + 1.1958 +} // namespace jit 1.1959 +} // namespace js