michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jsmath.h" michael@0: michael@0: #include "builtin/TestingFunctions.h" michael@0: #include "builtin/TypedObject.h" michael@0: #include "jit/BaselineInspector.h" michael@0: #include "jit/IonBuilder.h" michael@0: #include "jit/Lowering.h" michael@0: #include "jit/MIR.h" michael@0: #include "jit/MIRGraph.h" michael@0: #include "vm/ArgumentsObject.h" michael@0: michael@0: #include "jsscriptinlines.h" michael@0: michael@0: #include "vm/StringObject-inl.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target) michael@0: { michael@0: JS_ASSERT(target->isNative()); michael@0: JSNative native = target->native(); michael@0: michael@0: if (!optimizationInfo().inlineNative()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Array natives. michael@0: if (native == js_Array) michael@0: return inlineArray(callInfo); michael@0: if (native == js::array_pop) michael@0: return inlineArrayPopShift(callInfo, MArrayPopShift::Pop); michael@0: if (native == js::array_shift) michael@0: return inlineArrayPopShift(callInfo, MArrayPopShift::Shift); michael@0: if (native == js::array_push) michael@0: return inlineArrayPush(callInfo); michael@0: if (native == js::array_concat) michael@0: return inlineArrayConcat(callInfo); michael@0: if (native == js::array_splice) michael@0: return inlineArraySplice(callInfo); michael@0: michael@0: // Math natives. michael@0: if (native == js_math_abs) michael@0: return inlineMathAbs(callInfo); michael@0: if (native == js::math_floor) michael@0: return inlineMathFloor(callInfo); michael@0: if (native == js::math_ceil) michael@0: return inlineMathCeil(callInfo); michael@0: if (native == js::math_round) michael@0: return inlineMathRound(callInfo); michael@0: if (native == js_math_sqrt) michael@0: return inlineMathSqrt(callInfo); michael@0: if (native == math_atan2) michael@0: return inlineMathAtan2(callInfo); michael@0: if (native == js::math_hypot) michael@0: return inlineMathHypot(callInfo); michael@0: if (native == js_math_max) michael@0: return inlineMathMinMax(callInfo, true /* max */); michael@0: if (native == js_math_min) michael@0: return inlineMathMinMax(callInfo, false /* max */); michael@0: if (native == js_math_pow) michael@0: return inlineMathPow(callInfo); michael@0: if (native == js_math_random) michael@0: return inlineMathRandom(callInfo); michael@0: if (native == js::math_imul) michael@0: return inlineMathImul(callInfo); michael@0: if (native == js::math_fround) michael@0: return inlineMathFRound(callInfo); michael@0: if (native == js::math_sin) michael@0: return inlineMathFunction(callInfo, MMathFunction::Sin); michael@0: if (native == js::math_cos) michael@0: return inlineMathFunction(callInfo, MMathFunction::Cos); michael@0: if (native == js::math_exp) michael@0: return inlineMathFunction(callInfo, MMathFunction::Exp); michael@0: if (native == js::math_tan) michael@0: return inlineMathFunction(callInfo, MMathFunction::Tan); michael@0: if (native == js::math_log) michael@0: return inlineMathFunction(callInfo, MMathFunction::Log); michael@0: if (native == js::math_atan) michael@0: return inlineMathFunction(callInfo, MMathFunction::ATan); michael@0: if (native == js::math_asin) michael@0: return inlineMathFunction(callInfo, MMathFunction::ASin); michael@0: if (native == js::math_acos) michael@0: return inlineMathFunction(callInfo, MMathFunction::ACos); michael@0: if (native == js::math_log10) michael@0: return inlineMathFunction(callInfo, MMathFunction::Log10); michael@0: if (native == js::math_log2) michael@0: return inlineMathFunction(callInfo, MMathFunction::Log2); michael@0: if (native == js::math_log1p) michael@0: return inlineMathFunction(callInfo, MMathFunction::Log1P); michael@0: if (native == js::math_expm1) michael@0: return inlineMathFunction(callInfo, MMathFunction::ExpM1); michael@0: if (native == js::math_cosh) michael@0: return inlineMathFunction(callInfo, MMathFunction::CosH); michael@0: if (native == js::math_sin) michael@0: return inlineMathFunction(callInfo, MMathFunction::SinH); michael@0: if (native == js::math_tan) michael@0: return inlineMathFunction(callInfo, MMathFunction::TanH); michael@0: if (native == js::math_acosh) michael@0: return inlineMathFunction(callInfo, MMathFunction::ACosH); michael@0: if (native == js::math_asin) michael@0: return inlineMathFunction(callInfo, MMathFunction::ASinH); michael@0: if (native == js::math_atan) michael@0: return inlineMathFunction(callInfo, MMathFunction::ATanH); michael@0: if (native == js::math_sign) michael@0: return inlineMathFunction(callInfo, MMathFunction::Sign); michael@0: if (native == js::math_trunc) michael@0: return inlineMathFunction(callInfo, MMathFunction::Trunc); michael@0: if (native == js::math_cbrt) michael@0: return inlineMathFunction(callInfo, MMathFunction::Cbrt); michael@0: michael@0: // String natives. michael@0: if (native == js_String) michael@0: return inlineStringObject(callInfo); michael@0: if (native == js::str_split) michael@0: return inlineStringSplit(callInfo); michael@0: if (native == js_str_charCodeAt) michael@0: return inlineStrCharCodeAt(callInfo); michael@0: if (native == js::str_fromCharCode) michael@0: return inlineStrFromCharCode(callInfo); michael@0: if (native == js_str_charAt) michael@0: return inlineStrCharAt(callInfo); michael@0: if (native == str_replace) michael@0: return inlineStrReplace(callInfo); michael@0: michael@0: // RegExp natives. michael@0: if (native == regexp_exec && CallResultEscapes(pc)) michael@0: return inlineRegExpExec(callInfo); michael@0: if (native == regexp_exec && !CallResultEscapes(pc)) michael@0: return inlineRegExpTest(callInfo); michael@0: if (native == regexp_test) michael@0: return inlineRegExpTest(callInfo); michael@0: michael@0: // Array intrinsics. michael@0: if (native == intrinsic_UnsafePutElements) michael@0: return inlineUnsafePutElements(callInfo); michael@0: if (native == intrinsic_NewDenseArray) michael@0: return inlineNewDenseArray(callInfo); michael@0: michael@0: // Slot intrinsics. michael@0: if (native == intrinsic_UnsafeSetReservedSlot) michael@0: return inlineUnsafeSetReservedSlot(callInfo); michael@0: if (native == intrinsic_UnsafeGetReservedSlot) michael@0: return inlineUnsafeGetReservedSlot(callInfo); michael@0: michael@0: // Parallel intrinsics. michael@0: if (native == intrinsic_ShouldForceSequential || michael@0: native == intrinsic_InParallelSection) michael@0: return inlineForceSequentialOrInParallelSection(callInfo); michael@0: if (native == intrinsic_ForkJoinGetSlice) michael@0: return inlineForkJoinGetSlice(callInfo); michael@0: michael@0: // Utility intrinsics. michael@0: if (native == intrinsic_IsCallable) michael@0: return inlineIsCallable(callInfo); michael@0: if (native == intrinsic_HaveSameClass) michael@0: return inlineHaveSameClass(callInfo); michael@0: if (native == intrinsic_ToObject) michael@0: return inlineToObject(callInfo); michael@0: michael@0: // TypedObject intrinsics. michael@0: if (native == intrinsic_ObjectIsTypedObject) michael@0: return inlineHasClasses(callInfo, michael@0: &TransparentTypedObject::class_, &OpaqueTypedObject::class_); michael@0: if (native == intrinsic_ObjectIsTransparentTypedObject) michael@0: return inlineHasClass(callInfo, &TransparentTypedObject::class_); michael@0: if (native == intrinsic_ObjectIsOpaqueTypedObject) michael@0: return inlineHasClass(callInfo, &OpaqueTypedObject::class_); michael@0: if (native == intrinsic_ObjectIsTypeDescr) michael@0: return inlineObjectIsTypeDescr(callInfo); michael@0: if (native == intrinsic_TypeDescrIsSimpleType) michael@0: return inlineHasClasses(callInfo, michael@0: &ScalarTypeDescr::class_, &ReferenceTypeDescr::class_); michael@0: if (native == intrinsic_TypeDescrIsArrayType) michael@0: return inlineHasClasses(callInfo, michael@0: &SizedArrayTypeDescr::class_, &UnsizedArrayTypeDescr::class_); michael@0: if (native == intrinsic_TypeDescrIsSizedArrayType) michael@0: return inlineHasClass(callInfo, &SizedArrayTypeDescr::class_); michael@0: if (native == intrinsic_TypeDescrIsUnsizedArrayType) michael@0: return inlineHasClass(callInfo, &UnsizedArrayTypeDescr::class_); michael@0: if (native == intrinsic_SetTypedObjectOffset) michael@0: return inlineSetTypedObjectOffset(callInfo); michael@0: michael@0: // Testing Functions michael@0: if (native == testingFunc_inParallelSection) michael@0: return inlineForceSequentialOrInParallelSection(callInfo); michael@0: if (native == testingFunc_bailout) michael@0: return inlineBailout(callInfo); michael@0: if (native == testingFunc_assertFloat32) michael@0: return inlineAssertFloat32(callInfo); michael@0: michael@0: // Bound function michael@0: if (native == js::CallOrConstructBoundFunction) michael@0: return inlineBoundFunction(callInfo, target); michael@0: michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: types::TemporaryTypeSet * michael@0: IonBuilder::getInlineReturnTypeSet() michael@0: { michael@0: return bytecodeTypes(pc); michael@0: } michael@0: michael@0: MIRType michael@0: IonBuilder::getInlineReturnType() michael@0: { michael@0: types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet(); michael@0: return returnTypes->getKnownMIRType(); michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathFunction(CallInfo &callInfo, MMathFunction::Function function) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.argc() != 1) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (getInlineReturnType() != MIRType_Double) michael@0: return InliningStatus_NotInlined; michael@0: if (!IsNumberType(callInfo.getArg(0)->type())) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: const MathCache *cache = compartment->runtime()->maybeGetMathCache(); michael@0: michael@0: callInfo.fun()->setImplicitlyUsedUnchecked(); michael@0: callInfo.thisArg()->setImplicitlyUsedUnchecked(); michael@0: michael@0: MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), function, cache); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineArray(CallInfo &callInfo) michael@0: { michael@0: uint32_t initLength = 0; michael@0: MNewArray::AllocatingBehaviour allocating = MNewArray::NewArray_Unallocating; michael@0: michael@0: JSObject *templateObject = inspector->getTemplateObjectForNative(pc, js_Array); michael@0: if (!templateObject) michael@0: return InliningStatus_NotInlined; michael@0: JS_ASSERT(templateObject->is()); michael@0: michael@0: // Multiple arguments imply array initialization, not just construction. michael@0: if (callInfo.argc() >= 2) { michael@0: initLength = callInfo.argc(); michael@0: allocating = MNewArray::NewArray_Allocating; michael@0: michael@0: types::TypeObjectKey *type = types::TypeObjectKey::get(templateObject); michael@0: if (!type->unknownProperties()) { michael@0: types::HeapTypeSetKey elemTypes = type->property(JSID_VOID); michael@0: michael@0: for (uint32_t i = 0; i < initLength; i++) { michael@0: MDefinition *value = callInfo.getArg(i); michael@0: if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) { michael@0: elemTypes.freeze(constraints()); michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // A single integer argument denotes initial length. michael@0: if (callInfo.argc() == 1) { michael@0: if (callInfo.getArg(0)->type() != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: MDefinition *arg = callInfo.getArg(0); michael@0: if (!arg->isConstant()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Negative lengths generate a RangeError, unhandled by the inline path. michael@0: initLength = arg->toConstant()->value().toInt32(); michael@0: if (initLength >= JSObject::NELEMENTS_LIMIT) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Make sure initLength matches the template object's length. This is michael@0: // not guaranteed to be the case, for instance if we're inlining the michael@0: // MConstant may come from an outer script. michael@0: if (initLength != templateObject->as().length()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (initLength <= ArrayObject::EagerAllocationMaxLength) michael@0: allocating = MNewArray::NewArray_Allocating; michael@0: } michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: types::TemporaryTypeSet::DoubleConversion conversion = michael@0: getInlineReturnTypeSet()->convertDoubleElements(constraints()); michael@0: if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) michael@0: templateObject->setShouldConvertDoubleElements(); michael@0: else michael@0: templateObject->clearShouldConvertDoubleElements(); michael@0: michael@0: MNewArray *ins = MNewArray::New(alloc(), constraints(), initLength, templateObject, michael@0: templateObject->type()->initialHeap(constraints()), michael@0: allocating); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: michael@0: if (callInfo.argc() >= 2) { michael@0: // Get the elements vector. michael@0: MElements *elements = MElements::New(alloc(), ins); michael@0: current->add(elements); michael@0: michael@0: // Store all values, no need to initialize the length after each as michael@0: // jsop_initelem_array is doing because we do not expect to bailout michael@0: // because the memory is supposed to be allocated by now. michael@0: MConstant *id = nullptr; michael@0: for (uint32_t i = 0; i < initLength; i++) { michael@0: id = MConstant::New(alloc(), Int32Value(i)); michael@0: current->add(id); michael@0: michael@0: MDefinition *value = callInfo.getArg(i); michael@0: if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) { michael@0: MInstruction *valueDouble = MToDouble::New(alloc(), value); michael@0: current->add(valueDouble); michael@0: value = valueDouble; michael@0: } michael@0: michael@0: // There is normally no need for a post barrier on these writes michael@0: // because the new array will be in the nursery. However, this michael@0: // assumption is volated if we specifically requested pre-tenuring. michael@0: if (ins->initialHeap() == gc::TenuredHeap) michael@0: current->add(MPostWriteBarrier::New(alloc(), ins, value)); michael@0: michael@0: MStoreElement *store = MStoreElement::New(alloc(), elements, id, value, michael@0: /* needsHoleCheck = */ false); michael@0: current->add(store); michael@0: } michael@0: michael@0: // Update the length. michael@0: MSetInitializedLength *length = MSetInitializedLength::New(alloc(), elements, id); michael@0: current->add(length); michael@0: michael@0: if (!resumeAfter(length)) michael@0: return InliningStatus_Error; michael@0: } michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MIRType returnType = getInlineReturnType(); michael@0: if (returnType == MIRType_Undefined || returnType == MIRType_Null) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.thisArg()->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Pop and shift are only handled for dense arrays that have never been michael@0: // used in an iterator: popping elements does not account for suppressing michael@0: // deleted properties in active iterators. michael@0: types::TypeObjectFlags unhandledFlags = michael@0: types::OBJECT_FLAG_SPARSE_INDEXES | michael@0: types::OBJECT_FLAG_LENGTH_OVERFLOW | michael@0: types::OBJECT_FLAG_ITERATED; michael@0: michael@0: types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); michael@0: if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_) michael@0: return InliningStatus_NotInlined; michael@0: if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet(); michael@0: bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED); michael@0: bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType()); michael@0: michael@0: bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), michael@0: callInfo.thisArg(), nullptr, returnTypes); michael@0: if (barrier) michael@0: returnType = MIRType_Value; michael@0: michael@0: MArrayPopShift *ins = MArrayPopShift::New(alloc(), callInfo.thisArg(), mode, michael@0: needsHoleCheck, maybeUndefined); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: ins->setResultType(returnType); michael@0: michael@0: if (!resumeAfter(ins)) michael@0: return InliningStatus_Error; michael@0: michael@0: if (!pushTypeBarrier(ins, returnTypes, barrier)) michael@0: return InliningStatus_Error; michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineArraySplice(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 2 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Ensure |this|, argument and result are objects. michael@0: if (getInlineReturnType() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.thisArg()->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(0)->type() != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(1)->type() != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: // Specialize arr.splice(start, deleteCount) with unused return value and michael@0: // avoid creating the result array in this case. michael@0: if (!BytecodeIsPopped(pc)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MArraySplice *ins = MArraySplice::New(alloc(), michael@0: callInfo.thisArg(), michael@0: callInfo.getArg(0), michael@0: callInfo.getArg(1)); michael@0: michael@0: current->add(ins); michael@0: pushConstant(UndefinedValue()); michael@0: michael@0: if (!resumeAfter(ins)) michael@0: return InliningStatus_Error; michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineArrayPush(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 1 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MDefinition *obj = callInfo.thisArg(); michael@0: MDefinition *value = callInfo.getArg(0); michael@0: if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, michael@0: &obj, nullptr, &value, /* canModify = */ false)) michael@0: { michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: JS_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0)); michael@0: michael@0: if (getInlineReturnType() != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.thisArg()->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); michael@0: if (!thisTypes || thisTypes->getKnownClass() != &ArrayObject::class_) michael@0: return InliningStatus_NotInlined; michael@0: if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | michael@0: types::OBJECT_FLAG_LENGTH_OVERFLOW)) michael@0: { michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: types::TemporaryTypeSet::DoubleConversion conversion = michael@0: thisTypes->convertDoubleElements(constraints()); michael@0: if (conversion == types::TemporaryTypeSet::AmbiguousDoubleConversion) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: value = callInfo.getArg(0); michael@0: michael@0: if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles || michael@0: conversion == types::TemporaryTypeSet::MaybeConvertToDoubles) michael@0: { michael@0: MInstruction *valueDouble = MToDouble::New(alloc(), value); michael@0: current->add(valueDouble); michael@0: value = valueDouble; michael@0: } michael@0: michael@0: if (NeedsPostBarrier(info(), value)) michael@0: current->add(MPostWriteBarrier::New(alloc(), callInfo.thisArg(), value)); michael@0: michael@0: MArrayPush *ins = MArrayPush::New(alloc(), callInfo.thisArg(), value); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: michael@0: if (!resumeAfter(ins)) michael@0: return InliningStatus_Error; michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineArrayConcat(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 1 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Ensure |this|, argument and result are objects. michael@0: if (getInlineReturnType() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.thisArg()->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(0)->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // |this| and the argument must be dense arrays. michael@0: types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); michael@0: types::TemporaryTypeSet *argTypes = callInfo.getArg(0)->resultTypeSet(); michael@0: if (!thisTypes || !argTypes) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (thisTypes->getKnownClass() != &ArrayObject::class_) michael@0: return InliningStatus_NotInlined; michael@0: if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | michael@0: types::OBJECT_FLAG_LENGTH_OVERFLOW)) michael@0: { michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: if (argTypes->getKnownClass() != &ArrayObject::class_) michael@0: return InliningStatus_NotInlined; michael@0: if (argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | michael@0: types::OBJECT_FLAG_LENGTH_OVERFLOW)) michael@0: { michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: // Watch out for indexed properties on the prototype. michael@0: if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Require the 'this' types to have a specific type matching the current michael@0: // global, so we can create the result object inline. michael@0: if (thisTypes->getObjectCount() != 1) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: types::TypeObject *baseThisType = thisTypes->getTypeObject(0); michael@0: if (!baseThisType) michael@0: return InliningStatus_NotInlined; michael@0: types::TypeObjectKey *thisType = types::TypeObjectKey::get(baseThisType); michael@0: if (thisType->unknownProperties()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Don't inline if 'this' is packed and the argument may not be packed michael@0: // (the result array will reuse the 'this' type). michael@0: if (!thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED) && michael@0: argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED)) michael@0: { michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: // Constraints modeling this concat have not been generated by inference, michael@0: // so check that type information already reflects possible side effects of michael@0: // this call. michael@0: types::HeapTypeSetKey thisElemTypes = thisType->property(JSID_VOID); michael@0: michael@0: types::TemporaryTypeSet *resTypes = getInlineReturnTypeSet(); michael@0: if (!resTypes->hasType(types::Type::ObjectType(thisType))) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: for (unsigned i = 0; i < argTypes->getObjectCount(); i++) { michael@0: types::TypeObjectKey *argType = argTypes->getObject(i); michael@0: if (!argType) michael@0: continue; michael@0: michael@0: if (argType->unknownProperties()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: types::HeapTypeSetKey elemTypes = argType->property(JSID_VOID); michael@0: if (!elemTypes.knownSubset(constraints(), thisElemTypes)) michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: // Inline the call. michael@0: JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat); michael@0: if (!templateObj || templateObj->type() != baseThisType) michael@0: return InliningStatus_NotInlined; michael@0: JS_ASSERT(templateObj->is()); michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MArrayConcat *ins = MArrayConcat::New(alloc(), constraints(), callInfo.thisArg(), callInfo.getArg(0), michael@0: templateObj, templateObj->type()->initialHeap(constraints())); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: michael@0: if (!resumeAfter(ins)) michael@0: return InliningStatus_Error; michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathAbs(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.argc() != 1) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MIRType returnType = getInlineReturnType(); michael@0: MIRType argType = callInfo.getArg(0)->type(); michael@0: if (!IsNumberType(argType)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Either argType == returnType, or michael@0: // argType == Double or Float32, returnType == Int, or michael@0: // argType == Float32, returnType == Double michael@0: if (argType != returnType && !(IsFloatingPointType(argType) && returnType == MIRType_Int32) michael@0: && !(argType == MIRType_Float32 && returnType == MIRType_Double)) michael@0: { michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: // If the arg is a Float32, we specialize the op as double, it will be specialized michael@0: // as float32 if necessary later. michael@0: MIRType absType = (argType == MIRType_Float32) ? MIRType_Double : argType; michael@0: MInstruction *ins = MAbs::New(alloc(), callInfo.getArg(0), absType); michael@0: current->add(ins); michael@0: michael@0: current->push(ins); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathFloor(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.argc() != 1) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MIRType argType = callInfo.getArg(0)->type(); michael@0: MIRType returnType = getInlineReturnType(); michael@0: michael@0: // Math.floor(int(x)) == int(x) michael@0: if (argType == MIRType_Int32 && returnType == MIRType_Int32) { michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: current->push(callInfo.getArg(0)); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: if (IsFloatingPointType(argType) && returnType == MIRType_Int32) { michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: MFloor *ins = MFloor::New(alloc(), callInfo.getArg(0)); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: if (IsFloatingPointType(argType) && returnType == MIRType_Double) { michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor, nullptr); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathCeil(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.argc() != 1) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MIRType argType = callInfo.getArg(0)->type(); michael@0: MIRType returnType = getInlineReturnType(); michael@0: michael@0: // Math.ceil(int(x)) == int(x) michael@0: if (argType == MIRType_Int32 && returnType == MIRType_Int32) { michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: current->push(callInfo.getArg(0)); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: if (IsFloatingPointType(argType) && returnType == MIRType_Double) { michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil, nullptr); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathRound(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.argc() != 1) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MIRType returnType = getInlineReturnType(); michael@0: MIRType argType = callInfo.getArg(0)->type(); michael@0: michael@0: // Math.round(int(x)) == int(x) michael@0: if (argType == MIRType_Int32 && returnType == MIRType_Int32) { michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: current->push(callInfo.getArg(0)); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: if (IsFloatingPointType(argType) && returnType == MIRType_Int32) { michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: MRound *ins = MRound::New(alloc(), callInfo.getArg(0)); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: if (IsFloatingPointType(argType) && returnType == MIRType_Double) { michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: MMathFunction *ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round, nullptr); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathSqrt(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.argc() != 1) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MIRType argType = callInfo.getArg(0)->type(); michael@0: if (getInlineReturnType() != MIRType_Double) michael@0: return InliningStatus_NotInlined; michael@0: if (!IsNumberType(argType)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MSqrt *sqrt = MSqrt::New(alloc(), callInfo.getArg(0)); michael@0: current->add(sqrt); michael@0: current->push(sqrt); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathAtan2(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.argc() != 2) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (getInlineReturnType() != MIRType_Double) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MIRType argType0 = callInfo.getArg(0)->type(); michael@0: MIRType argType1 = callInfo.getArg(1)->type(); michael@0: michael@0: if (!IsNumberType(argType0) || !IsNumberType(argType1)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MAtan2 *atan2 = MAtan2::New(alloc(), callInfo.getArg(0), callInfo.getArg(1)); michael@0: current->add(atan2); michael@0: current->push(atan2); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathHypot(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.argc() != 2) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (getInlineReturnType() != MIRType_Double) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MIRType argType0 = callInfo.getArg(0)->type(); michael@0: MIRType argType1 = callInfo.getArg(1)->type(); michael@0: michael@0: if (!IsNumberType(argType0) || !IsNumberType(argType1)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MHypot *hypot = MHypot::New(alloc(), callInfo.getArg(0), callInfo.getArg(1)); michael@0: current->add(hypot); michael@0: current->push(hypot); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathPow(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.argc() != 2) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Typechecking. michael@0: MIRType baseType = callInfo.getArg(0)->type(); michael@0: MIRType powerType = callInfo.getArg(1)->type(); michael@0: MIRType outputType = getInlineReturnType(); michael@0: michael@0: if (outputType != MIRType_Int32 && outputType != MIRType_Double) michael@0: return InliningStatus_NotInlined; michael@0: if (!IsNumberType(baseType)) michael@0: return InliningStatus_NotInlined; michael@0: if (!IsNumberType(powerType)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MDefinition *base = callInfo.getArg(0); michael@0: MDefinition *power = callInfo.getArg(1); michael@0: MDefinition *output = nullptr; michael@0: michael@0: // Optimize some constant powers. michael@0: if (callInfo.getArg(1)->isConstant() && michael@0: callInfo.getArg(1)->toConstant()->value().isNumber()) michael@0: { michael@0: double pow = callInfo.getArg(1)->toConstant()->value().toNumber(); michael@0: michael@0: // Math.pow(x, 0.5) is a sqrt with edge-case detection. michael@0: if (pow == 0.5) { michael@0: MPowHalf *half = MPowHalf::New(alloc(), base); michael@0: current->add(half); michael@0: output = half; michael@0: } michael@0: michael@0: // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases. michael@0: if (pow == -0.5) { michael@0: MPowHalf *half = MPowHalf::New(alloc(), base); michael@0: current->add(half); michael@0: MConstant *one = MConstant::New(alloc(), DoubleValue(1.0)); michael@0: current->add(one); michael@0: MDiv *div = MDiv::New(alloc(), one, half, MIRType_Double); michael@0: current->add(div); michael@0: output = div; michael@0: } michael@0: michael@0: // Math.pow(x, 1) == x. michael@0: if (pow == 1.0) michael@0: output = base; michael@0: michael@0: // Math.pow(x, 2) == x*x. michael@0: if (pow == 2.0) { michael@0: MMul *mul = MMul::New(alloc(), base, base, outputType); michael@0: current->add(mul); michael@0: output = mul; michael@0: } michael@0: michael@0: // Math.pow(x, 3) == x*x*x. michael@0: if (pow == 3.0) { michael@0: MMul *mul1 = MMul::New(alloc(), base, base, outputType); michael@0: current->add(mul1); michael@0: MMul *mul2 = MMul::New(alloc(), base, mul1, outputType); michael@0: current->add(mul2); michael@0: output = mul2; michael@0: } michael@0: michael@0: // Math.pow(x, 4) == y*y, where y = x*x. michael@0: if (pow == 4.0) { michael@0: MMul *y = MMul::New(alloc(), base, base, outputType); michael@0: current->add(y); michael@0: MMul *mul = MMul::New(alloc(), y, y, outputType); michael@0: current->add(mul); michael@0: output = mul; michael@0: } michael@0: } michael@0: michael@0: // Use MPow for other powers michael@0: if (!output) { michael@0: if (powerType == MIRType_Float32) michael@0: powerType = MIRType_Double; michael@0: MPow *pow = MPow::New(alloc(), base, power, powerType); michael@0: current->add(pow); michael@0: output = pow; michael@0: } michael@0: michael@0: // Cast to the right type michael@0: if (outputType == MIRType_Int32 && output->type() != MIRType_Int32) { michael@0: MToInt32 *toInt = MToInt32::New(alloc(), output); michael@0: current->add(toInt); michael@0: output = toInt; michael@0: } michael@0: if (outputType == MIRType_Double && output->type() != MIRType_Double) { michael@0: MToDouble *toDouble = MToDouble::New(alloc(), output); michael@0: current->add(toDouble); michael@0: output = toDouble; michael@0: } michael@0: michael@0: current->push(output); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathRandom(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (getInlineReturnType() != MIRType_Double) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MRandom *rand = MRandom::New(alloc()); michael@0: current->add(rand); michael@0: current->push(rand); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathImul(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 2 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MIRType returnType = getInlineReturnType(); michael@0: if (returnType != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (!IsNumberType(callInfo.getArg(0)->type())) michael@0: return InliningStatus_NotInlined; michael@0: if (!IsNumberType(callInfo.getArg(1)->type())) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MInstruction *first = MTruncateToInt32::New(alloc(), callInfo.getArg(0)); michael@0: current->add(first); michael@0: michael@0: MInstruction *second = MTruncateToInt32::New(alloc(), callInfo.getArg(1)); michael@0: current->add(second); michael@0: michael@0: MMul *ins = MMul::New(alloc(), first, second, MIRType_Int32, MMul::Integer); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathFRound(CallInfo &callInfo) michael@0: { michael@0: if (!LIRGenerator::allowFloat32Optimizations()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.argc() != 1 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // MIRType can't be Float32, as this point, as getInlineReturnType uses JSVal types michael@0: // to infer the returned MIR type. michael@0: types::TemporaryTypeSet *returned = getInlineReturnTypeSet(); michael@0: if (returned->empty()) { michael@0: // As there's only one possible returned type, just add it to the observed michael@0: // returned typeset michael@0: returned->addType(types::Type::DoubleType(), alloc_->lifoAlloc()); michael@0: } else { michael@0: MIRType returnType = getInlineReturnType(); michael@0: if (!IsNumberType(returnType)) michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: MIRType arg = callInfo.getArg(0)->type(); michael@0: if (!IsNumberType(arg)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MToFloat32 *ins = MToFloat32::New(alloc(), callInfo.getArg(0)); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineMathMinMax(CallInfo &callInfo, bool max) michael@0: { michael@0: if (callInfo.argc() < 2 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MIRType returnType = getInlineReturnType(); michael@0: if (!IsNumberType(returnType)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: for (unsigned i = 0; i < callInfo.argc(); i++) { michael@0: MIRType argType = callInfo.getArg(i)->type(); michael@0: if (!IsNumberType(argType)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // When one of the arguments is double, do a double MMinMax. michael@0: if (returnType == MIRType_Int32 && IsFloatingPointType(argType)) michael@0: returnType = MIRType_Double; michael@0: } michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: // Chain N-1 MMinMax instructions to compute the MinMax. michael@0: MMinMax *last = MMinMax::New(alloc(), callInfo.getArg(0), callInfo.getArg(1), returnType, max); michael@0: current->add(last); michael@0: michael@0: for (unsigned i = 2; i < callInfo.argc(); i++) { michael@0: MMinMax *ins = MMinMax::New(alloc(), last, callInfo.getArg(i), returnType, max); michael@0: current->add(ins); michael@0: last = ins; michael@0: } michael@0: michael@0: current->push(last); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineStringObject(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 1 || !callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // ConvertToString doesn't support objects. michael@0: if (callInfo.getArg(0)->mightBeType(MIRType_Object)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js_String); michael@0: if (!templateObj) michael@0: return InliningStatus_NotInlined; michael@0: JS_ASSERT(templateObj->is()); michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MNewStringObject *ins = MNewStringObject::New(alloc(), callInfo.getArg(0), templateObj); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: michael@0: if (!resumeAfter(ins)) michael@0: return InliningStatus_Error; michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineStringSplit(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 1 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.thisArg()->type() != MIRType_String) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(0)->type() != MIRType_String) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: JSObject *templateObject = inspector->getTemplateObjectForNative(pc, js::str_split); michael@0: if (!templateObject) michael@0: return InliningStatus_NotInlined; michael@0: JS_ASSERT(templateObject->is()); michael@0: michael@0: types::TypeObjectKey *retType = types::TypeObjectKey::get(templateObject); michael@0: if (retType->unknownProperties()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: types::HeapTypeSetKey key = retType->property(JSID_VOID); michael@0: if (!key.maybeTypes()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (!key.maybeTypes()->hasType(types::Type::StringType())) { michael@0: key.freeze(constraints()); michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MStringSplit *ins = MStringSplit::New(alloc(), constraints(), callInfo.thisArg(), michael@0: callInfo.getArg(0), templateObject); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineStrCharCodeAt(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 1 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (getInlineReturnType() != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.thisArg()->type() != MIRType_String && callInfo.thisArg()->type() != MIRType_Value) michael@0: return InliningStatus_NotInlined; michael@0: MIRType argType = callInfo.getArg(0)->type(); michael@0: if (argType != MIRType_Int32 && argType != MIRType_Double) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MInstruction *index = MToInt32::New(alloc(), callInfo.getArg(0)); michael@0: current->add(index); michael@0: michael@0: MStringLength *length = MStringLength::New(alloc(), callInfo.thisArg()); michael@0: current->add(length); michael@0: michael@0: index = addBoundsCheck(index, length); michael@0: michael@0: MCharCodeAt *charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index); michael@0: current->add(charCode); michael@0: current->push(charCode); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineStrFromCharCode(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 1 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (getInlineReturnType() != MIRType_String) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(0)->type() != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MToInt32 *charCode = MToInt32::New(alloc(), callInfo.getArg(0)); michael@0: current->add(charCode); michael@0: michael@0: MFromCharCode *string = MFromCharCode::New(alloc(), charCode); michael@0: current->add(string); michael@0: current->push(string); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineStrCharAt(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 1 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (getInlineReturnType() != MIRType_String) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.thisArg()->type() != MIRType_String) michael@0: return InliningStatus_NotInlined; michael@0: MIRType argType = callInfo.getArg(0)->type(); michael@0: if (argType != MIRType_Int32 && argType != MIRType_Double) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MInstruction *index = MToInt32::New(alloc(), callInfo.getArg(0)); michael@0: current->add(index); michael@0: michael@0: MStringLength *length = MStringLength::New(alloc(), callInfo.thisArg()); michael@0: current->add(length); michael@0: michael@0: index = addBoundsCheck(index, length); michael@0: michael@0: // String.charAt(x) = String.fromCharCode(String.charCodeAt(x)) michael@0: MCharCodeAt *charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index); michael@0: current->add(charCode); michael@0: michael@0: MFromCharCode *string = MFromCharCode::New(alloc(), charCode); michael@0: current->add(string); michael@0: current->push(string); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineRegExpExec(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 1 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.thisArg()->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); michael@0: const Class *clasp = thisTypes ? thisTypes->getKnownClass() : nullptr; michael@0: if (clasp != &RegExpObject::class_) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.getArg(0)->mightBeType(MIRType_Object)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MInstruction *exec = MRegExpExec::New(alloc(), callInfo.thisArg(), callInfo.getArg(0)); michael@0: current->add(exec); michael@0: current->push(exec); michael@0: michael@0: if (!resumeAfter(exec)) michael@0: return InliningStatus_Error; michael@0: michael@0: if (!pushTypeBarrier(exec, getInlineReturnTypeSet(), true)) michael@0: return InliningStatus_Error; michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineRegExpTest(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 1 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // TI can infer a nullptr return type of regexp_test with eager compilation. michael@0: if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.thisArg()->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); michael@0: const Class *clasp = thisTypes ? thisTypes->getKnownClass() : nullptr; michael@0: if (clasp != &RegExpObject::class_) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(0)->mightBeType(MIRType_Object)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MInstruction *match = MRegExpTest::New(alloc(), callInfo.thisArg(), callInfo.getArg(0)); michael@0: current->add(match); michael@0: current->push(match); michael@0: if (!resumeAfter(match)) michael@0: return InliningStatus_Error; michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineStrReplace(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 2 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Return: String. michael@0: if (getInlineReturnType() != MIRType_String) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // This: String. michael@0: if (callInfo.thisArg()->type() != MIRType_String) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Arg 0: RegExp. michael@0: types::TemporaryTypeSet *arg0Type = callInfo.getArg(0)->resultTypeSet(); michael@0: const Class *clasp = arg0Type ? arg0Type->getKnownClass() : nullptr; michael@0: if (clasp != &RegExpObject::class_ && callInfo.getArg(0)->type() != MIRType_String) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Arg 1: String. michael@0: if (callInfo.getArg(1)->type() != MIRType_String) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MInstruction *cte; michael@0: if (callInfo.getArg(0)->type() == MIRType_String) { michael@0: cte = MStringReplace::New(alloc(), callInfo.thisArg(), callInfo.getArg(0), michael@0: callInfo.getArg(1)); michael@0: } else { michael@0: cte = MRegExpReplace::New(alloc(), callInfo.thisArg(), callInfo.getArg(0), michael@0: callInfo.getArg(1)); michael@0: } michael@0: current->add(cte); michael@0: current->push(cte); michael@0: if (cte->isEffectful() && !resumeAfter(cte)) michael@0: return InliningStatus_Error; michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineUnsafePutElements(CallInfo &callInfo) michael@0: { michael@0: uint32_t argc = callInfo.argc(); michael@0: if (argc < 3 || (argc % 3) != 0 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: /* Important: michael@0: * michael@0: * Here we inline each of the stores resulting from a call to michael@0: * UnsafePutElements(). It is essential that these stores occur michael@0: * atomically and cannot be interrupted by a stack or recursion michael@0: * check. If this is not true, race conditions can occur. michael@0: */ michael@0: michael@0: for (uint32_t base = 0; base < argc; base += 3) { michael@0: uint32_t arri = base + 0; michael@0: uint32_t idxi = base + 1; michael@0: uint32_t elemi = base + 2; michael@0: michael@0: MDefinition *obj = callInfo.getArg(arri); michael@0: MDefinition *id = callInfo.getArg(idxi); michael@0: MDefinition *elem = callInfo.getArg(elemi); michael@0: michael@0: bool isDenseNative = ElementAccessIsDenseNative(obj, id); michael@0: michael@0: bool writeNeedsBarrier = false; michael@0: if (isDenseNative) { michael@0: writeNeedsBarrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, michael@0: &obj, nullptr, &elem, michael@0: /* canModify = */ false); michael@0: } michael@0: michael@0: // We can only inline setelem on dense arrays that do not need type michael@0: // barriers and on typed arrays and on typed object arrays. michael@0: ScalarTypeDescr::Type arrayType; michael@0: if ((!isDenseNative || writeNeedsBarrier) && michael@0: !ElementAccessIsTypedArray(obj, id, &arrayType) && michael@0: !elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType)) michael@0: { michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: } michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: // Push the result first so that the stack depth matches up for michael@0: // the potential bailouts that will occur in the stores below. michael@0: MConstant *udef = MConstant::New(alloc(), UndefinedValue()); michael@0: current->add(udef); michael@0: current->push(udef); michael@0: michael@0: for (uint32_t base = 0; base < argc; base += 3) { michael@0: uint32_t arri = base + 0; michael@0: uint32_t idxi = base + 1; michael@0: michael@0: MDefinition *obj = callInfo.getArg(arri); michael@0: MDefinition *id = callInfo.getArg(idxi); michael@0: michael@0: if (ElementAccessIsDenseNative(obj, id)) { michael@0: if (!inlineUnsafeSetDenseArrayElement(callInfo, base)) michael@0: return InliningStatus_Error; michael@0: continue; michael@0: } michael@0: michael@0: ScalarTypeDescr::Type arrayType; michael@0: if (ElementAccessIsTypedArray(obj, id, &arrayType)) { michael@0: if (!inlineUnsafeSetTypedArrayElement(callInfo, base, arrayType)) michael@0: return InliningStatus_Error; michael@0: continue; michael@0: } michael@0: michael@0: if (elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType)) { michael@0: if (!inlineUnsafeSetTypedObjectArrayElement(callInfo, base, arrayType)) michael@0: return InliningStatus_Error; michael@0: continue; michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Element access not dense array nor typed array"); michael@0: } michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: bool michael@0: IonBuilder::elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id, michael@0: ScalarTypeDescr::Type *arrayType) michael@0: { michael@0: if (obj->type() != MIRType_Object) // lookupTypeDescrSet() tests for TypedObject michael@0: return false; michael@0: michael@0: if (id->type() != MIRType_Int32 && id->type() != MIRType_Double) michael@0: return false; michael@0: michael@0: TypeDescrSet objDescrs; michael@0: if (!lookupTypeDescrSet(obj, &objDescrs)) michael@0: return false; michael@0: michael@0: if (!objDescrs.allOfArrayKind()) michael@0: return false; michael@0: michael@0: TypeDescrSet elemDescrs; michael@0: if (!objDescrs.arrayElementType(*this, &elemDescrs)) michael@0: return false; michael@0: michael@0: if (elemDescrs.empty() || elemDescrs.kind() != TypeDescr::Scalar) michael@0: return false; michael@0: michael@0: JS_ASSERT(TypeDescr::isSized(elemDescrs.kind())); michael@0: michael@0: return elemDescrs.scalarType(arrayType); michael@0: } michael@0: michael@0: bool michael@0: IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base) michael@0: { michael@0: // Note: we do not check the conditions that are asserted as true michael@0: // in intrinsic_UnsafePutElements(): michael@0: // - arr is a dense array michael@0: // - idx < initialized length michael@0: // Furthermore, note that inlineUnsafePutElements ensures the type of the michael@0: // value is reflected in the JSID_VOID property of the array. michael@0: michael@0: MDefinition *obj = callInfo.getArg(base + 0); michael@0: MDefinition *id = callInfo.getArg(base + 1); michael@0: MDefinition *elem = callInfo.getArg(base + 2); michael@0: michael@0: types::TemporaryTypeSet::DoubleConversion conversion = michael@0: obj->resultTypeSet()->convertDoubleElements(constraints()); michael@0: if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem)) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: IonBuilder::inlineUnsafeSetTypedArrayElement(CallInfo &callInfo, michael@0: uint32_t base, michael@0: ScalarTypeDescr::Type arrayType) michael@0: { michael@0: // Note: we do not check the conditions that are asserted as true michael@0: // in intrinsic_UnsafePutElements(): michael@0: // - arr is a typed array michael@0: // - idx < length michael@0: michael@0: MDefinition *obj = callInfo.getArg(base + 0); michael@0: MDefinition *id = callInfo.getArg(base + 1); michael@0: MDefinition *elem = callInfo.getArg(base + 2); michael@0: michael@0: if (!jsop_setelem_typed(arrayType, SetElem_Unsafe, obj, id, elem)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: IonBuilder::inlineUnsafeSetTypedObjectArrayElement(CallInfo &callInfo, michael@0: uint32_t base, michael@0: ScalarTypeDescr::Type arrayType) michael@0: { michael@0: // Note: we do not check the conditions that are asserted as true michael@0: // in intrinsic_UnsafePutElements(): michael@0: // - arr is a typed array michael@0: // - idx < length michael@0: michael@0: MDefinition *obj = callInfo.getArg(base + 0); michael@0: MDefinition *id = callInfo.getArg(base + 1); michael@0: MDefinition *elem = callInfo.getArg(base + 2); michael@0: michael@0: if (!jsop_setelem_typed_object(arrayType, SetElem_Unsafe, true, obj, id, elem)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: ExecutionMode executionMode = info().executionMode(); michael@0: switch (executionMode) { michael@0: case ParallelExecution: { michael@0: // During Parallel Exec, we always force sequential, so michael@0: // replace with true. This permits UCE to eliminate the michael@0: // entire path as dead, which is important. michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: MConstant *ins = MConstant::New(alloc(), BooleanValue(true)); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: default: michael@0: // In sequential mode, leave as is, because we'd have to michael@0: // access the "in warmup" flag of the runtime. michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid execution mode"); michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineForkJoinGetSlice(CallInfo &callInfo) michael@0: { michael@0: if (info().executionMode() != ParallelExecution) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Assert the way the function is used instead of testing, as it is a michael@0: // self-hosted function which must be used in a particular fashion. michael@0: MOZ_ASSERT(callInfo.argc() == 1 && !callInfo.constructing()); michael@0: MOZ_ASSERT(callInfo.getArg(0)->type() == MIRType_Int32); michael@0: michael@0: // Test this, as we might have not executed the native despite knowing the michael@0: // target here. michael@0: if (getInlineReturnType() != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: switch (info().executionMode()) { michael@0: case ParallelExecution: michael@0: if (LIRGenerator::allowInlineForkJoinGetSlice()) { michael@0: MForkJoinGetSlice *getSlice = MForkJoinGetSlice::New(alloc(), michael@0: graph().forkJoinContext()); michael@0: current->add(getSlice); michael@0: current->push(getSlice); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: return InliningStatus_NotInlined; michael@0: michael@0: default: michael@0: // ForkJoinGetSlice acts as identity for sequential execution. michael@0: current->push(callInfo.getArg(0)); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid execution mode"); michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineNewDenseArray(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing() || callInfo.argc() != 1) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // For now, in seq. mode we just call the C function. In michael@0: // par. mode we use inlined MIR. michael@0: ExecutionMode executionMode = info().executionMode(); michael@0: switch (executionMode) { michael@0: case ParallelExecution: michael@0: return inlineNewDenseArrayForParallelExecution(callInfo); michael@0: default: michael@0: return inlineNewDenseArrayForSequentialExecution(callInfo); michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("unknown ExecutionMode"); michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineNewDenseArrayForSequentialExecution(CallInfo &callInfo) michael@0: { michael@0: // not yet implemented; in seq. mode the C function is not so bad michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineNewDenseArrayForParallelExecution(CallInfo &callInfo) michael@0: { michael@0: // Create the new parallel array object. Parallel arrays have specially michael@0: // constructed type objects, so we can only perform the inlining if we michael@0: // already have one of these type objects. michael@0: types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet(); michael@0: if (returnTypes->getKnownMIRType() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: if (returnTypes->unknownObject() || returnTypes->getObjectCount() != 1) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(0)->type() != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: types::TypeObject *typeObject = returnTypes->getTypeObject(0); michael@0: michael@0: JSObject *templateObject = inspector->getTemplateObjectForNative(pc, intrinsic_NewDenseArray); michael@0: if (!templateObject || templateObject->type() != typeObject) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MNewDenseArrayPar *newObject = MNewDenseArrayPar::New(alloc(), michael@0: graph().forkJoinContext(), michael@0: callInfo.getArg(0), michael@0: templateObject); michael@0: current->add(newObject); michael@0: current->push(newObject); michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineHasClasses(CallInfo &callInfo, const Class *clasp1, const Class *clasp2) michael@0: { michael@0: // Thus far there has been no reason to complicate this beyond two classes, michael@0: // though it generalizes pretty well. michael@0: // clasp2 may be NULL. michael@0: if (callInfo.constructing() || callInfo.argc() != 1) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.getArg(0)->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: if (getInlineReturnType() != MIRType_Boolean) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet(); michael@0: const Class *knownClass = types ? types->getKnownClass() : nullptr; michael@0: if (knownClass) { michael@0: pushConstant(BooleanValue(knownClass == clasp1 || knownClass == clasp2)); michael@0: } else { michael@0: MHasClass *hasClass1 = MHasClass::New(alloc(), callInfo.getArg(0), clasp1); michael@0: current->add(hasClass1); michael@0: if (clasp2 == nullptr) { michael@0: current->push(hasClass1); michael@0: } else { michael@0: // The following turns into branch-free, box-free code on x86, and should do so on ARM. michael@0: MHasClass *hasClass2 = MHasClass::New(alloc(), callInfo.getArg(0), clasp2); michael@0: current->add(hasClass2); michael@0: MBitOr *either = MBitOr::New(alloc(), hasClass1, hasClass2); michael@0: either->infer(inspector, pc); michael@0: current->add(either); michael@0: // Convert to bool with the '!!' idiom michael@0: MNot *resultInverted = MNot::New(alloc(), either); michael@0: resultInverted->infer(); michael@0: current->add(resultInverted); michael@0: MNot *result = MNot::New(alloc(), resultInverted); michael@0: result->infer(); michael@0: current->add(result); michael@0: current->push(result); michael@0: } michael@0: } michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineObjectIsTypeDescr(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.constructing() || callInfo.argc() != 1) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (callInfo.getArg(0)->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: if (getInlineReturnType() != MIRType_Boolean) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // The test is elaborate: in-line only if there is exact michael@0: // information. michael@0: michael@0: types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet(); michael@0: if (!types) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: bool result = false; michael@0: switch (types->forAllClasses(IsTypeDescrClass)) { michael@0: case types::TemporaryTypeSet::ForAllResult::ALL_FALSE: michael@0: case types::TemporaryTypeSet::ForAllResult::EMPTY: michael@0: result = false; michael@0: break; michael@0: case types::TemporaryTypeSet::ForAllResult::ALL_TRUE: michael@0: result = true; michael@0: break; michael@0: case types::TemporaryTypeSet::ForAllResult::MIXED: michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: pushConstant(BooleanValue(result)); michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineSetTypedObjectOffset(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 2 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: MDefinition *typedObj = callInfo.getArg(0); michael@0: MDefinition *offset = callInfo.getArg(1); michael@0: michael@0: // Return type should be undefined or something wacky is going on. michael@0: if (getInlineReturnType() != MIRType_Undefined) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Check typedObj is a, well, typed object. Go ahead and use TI michael@0: // data. If this check should fail, that is almost certainly a bug michael@0: // in self-hosted code -- either because it's not being careful michael@0: // with TI or because of something else -- but we'll just let it michael@0: // fall through to the SetTypedObjectOffset intrinsic in such michael@0: // cases. michael@0: types::TemporaryTypeSet *types = typedObj->resultTypeSet(); michael@0: if (typedObj->type() != MIRType_Object || !types) michael@0: return InliningStatus_NotInlined; michael@0: switch (types->forAllClasses(IsTypedObjectClass)) { michael@0: case types::TemporaryTypeSet::ForAllResult::ALL_FALSE: michael@0: case types::TemporaryTypeSet::ForAllResult::EMPTY: michael@0: case types::TemporaryTypeSet::ForAllResult::MIXED: michael@0: return InliningStatus_NotInlined; michael@0: case types::TemporaryTypeSet::ForAllResult::ALL_TRUE: michael@0: break; michael@0: } michael@0: michael@0: // Check type of offset argument is an integer. michael@0: if (offset->type() != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: MInstruction *ins = MSetTypedObjectOffset::New(alloc(), typedObj, offset); michael@0: current->add(ins); michael@0: current->push(ins); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineUnsafeSetReservedSlot(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 3 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: if (getInlineReturnType() != MIRType_Undefined) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(0)->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(1)->type() != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Don't inline if we don't have a constant slot. michael@0: MDefinition *arg = callInfo.getArg(1); michael@0: if (!arg->isConstant()) michael@0: return InliningStatus_NotInlined; michael@0: uint32_t slot = arg->toConstant()->value().toPrivateUint32(); michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MStoreFixedSlot *store = MStoreFixedSlot::New(alloc(), callInfo.getArg(0), slot, callInfo.getArg(2)); michael@0: current->add(store); michael@0: current->push(store); michael@0: michael@0: if (NeedsPostBarrier(info(), callInfo.getArg(2))) michael@0: current->add(MPostWriteBarrier::New(alloc(), callInfo.thisArg(), callInfo.getArg(2))); michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineUnsafeGetReservedSlot(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 2 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(0)->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(1)->type() != MIRType_Int32) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Don't inline if we don't have a constant slot. michael@0: MDefinition *arg = callInfo.getArg(1); michael@0: if (!arg->isConstant()) michael@0: return InliningStatus_NotInlined; michael@0: uint32_t slot = arg->toConstant()->value().toPrivateUint32(); michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MLoadFixedSlot *load = MLoadFixedSlot::New(alloc(), callInfo.getArg(0), slot); michael@0: current->add(load); michael@0: current->push(load); michael@0: michael@0: // We don't track reserved slot types, so always emit a barrier. michael@0: if (!pushTypeBarrier(load, getInlineReturnTypeSet(), true)) michael@0: return InliningStatus_Error; michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineHaveSameClass(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 2 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(0)->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(1)->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: types::TemporaryTypeSet *arg1Types = callInfo.getArg(0)->resultTypeSet(); michael@0: types::TemporaryTypeSet *arg2Types = callInfo.getArg(1)->resultTypeSet(); michael@0: const Class *arg1Clasp = arg1Types ? arg1Types->getKnownClass() : nullptr; michael@0: const Class *arg2Clasp = arg2Types ? arg2Types->getKnownClass() : nullptr; michael@0: if (arg1Clasp && arg2Clasp) { michael@0: MConstant *constant = MConstant::New(alloc(), BooleanValue(arg1Clasp == arg2Clasp)); michael@0: current->add(constant); michael@0: current->push(constant); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MHaveSameClass *sameClass = MHaveSameClass::New(alloc(), callInfo.getArg(0), callInfo.getArg(1)); michael@0: current->add(sameClass); michael@0: current->push(sameClass); michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineIsCallable(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 1 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: if (getInlineReturnType() != MIRType_Boolean) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(0)->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // Try inlining with constant true/false: only objects may be callable at michael@0: // all, and if we know the class check if it is callable. michael@0: bool isCallableKnown = false; michael@0: bool isCallableConstant; michael@0: if (callInfo.getArg(0)->type() != MIRType_Object) { michael@0: isCallableKnown = true; michael@0: isCallableConstant = false; michael@0: } else { michael@0: types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet(); michael@0: const Class *clasp = types ? types->getKnownClass() : nullptr; michael@0: if (clasp) { michael@0: isCallableKnown = true; michael@0: isCallableConstant = clasp->isCallable(); michael@0: } michael@0: } michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: if (isCallableKnown) { michael@0: MConstant *constant = MConstant::New(alloc(), BooleanValue(isCallableConstant)); michael@0: current->add(constant); michael@0: current->push(constant); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: MIsCallable *isCallable = MIsCallable::New(alloc(), callInfo.getArg(0)); michael@0: current->add(isCallable); michael@0: current->push(isCallable); michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineToObject(CallInfo &callInfo) michael@0: { michael@0: if (callInfo.argc() != 1 || callInfo.constructing()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: // If we know the input type is an object, nop ToObject. michael@0: if (getInlineReturnType() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: if (callInfo.getArg(0)->type() != MIRType_Object) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: MDefinition *object = callInfo.getArg(0); michael@0: michael@0: current->push(object); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineBailout(CallInfo &callInfo) michael@0: { michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: current->add(MBail::New(alloc())); michael@0: michael@0: MConstant *undefined = MConstant::New(alloc(), UndefinedValue()); michael@0: current->add(undefined); michael@0: current->push(undefined); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineAssertFloat32(CallInfo &callInfo) michael@0: { michael@0: callInfo.setImplicitlyUsedUnchecked(); michael@0: michael@0: MDefinition *secondArg = callInfo.getArg(1); michael@0: michael@0: JS_ASSERT(secondArg->type() == MIRType_Boolean); michael@0: JS_ASSERT(secondArg->isConstant()); michael@0: michael@0: bool mustBeFloat32 = JSVAL_TO_BOOLEAN(secondArg->toConstant()->value()); michael@0: current->add(MAssertFloat32::New(alloc(), callInfo.getArg(0), mustBeFloat32)); michael@0: michael@0: MConstant *undefined = MConstant::New(alloc(), UndefinedValue()); michael@0: current->add(undefined); michael@0: current->push(undefined); michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: IonBuilder::InliningStatus michael@0: IonBuilder::inlineBoundFunction(CallInfo &nativeCallInfo, JSFunction *target) michael@0: { michael@0: if (!target->getBoundFunctionTarget()->is()) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: JSFunction *scriptedTarget = &(target->getBoundFunctionTarget()->as()); michael@0: JSRuntime *runtime = scriptedTarget->runtimeFromMainThread(); michael@0: michael@0: // Don't optimize if we're constructing and the callee is not a michael@0: // constructor, so that CallKnown does not have to handle this case michael@0: // (it should always throw). michael@0: if (nativeCallInfo.constructing() && !scriptedTarget->isInterpretedConstructor() && michael@0: !scriptedTarget->isNativeConstructor()) michael@0: { michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: if (gc::IsInsideNursery(runtime, scriptedTarget)) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) { michael@0: const Value val = target->getBoundFunctionArgument(i); michael@0: if (val.isObject() && gc::IsInsideNursery(runtime, &val.toObject())) michael@0: return InliningStatus_NotInlined; michael@0: } michael@0: michael@0: const Value thisVal = target->getBoundFunctionThis(); michael@0: if (thisVal.isObject() && gc::IsInsideNursery(runtime, &thisVal.toObject())) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: size_t argc = target->getBoundFunctionArgumentCount() + nativeCallInfo.argc(); michael@0: if (argc > ARGS_LENGTH_MAX) michael@0: return InliningStatus_NotInlined; michael@0: michael@0: nativeCallInfo.thisArg()->setImplicitlyUsedUnchecked(); michael@0: michael@0: CallInfo callInfo(alloc(), nativeCallInfo.constructing()); michael@0: callInfo.setFun(constant(ObjectValue(*scriptedTarget))); michael@0: callInfo.setThis(constant(target->getBoundFunctionThis())); michael@0: michael@0: if (!callInfo.argv().reserve(argc)) michael@0: return InliningStatus_Error; michael@0: michael@0: for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) michael@0: callInfo.argv().infallibleAppend(constant(target->getBoundFunctionArgument(i))); michael@0: for (size_t i = 0; i < nativeCallInfo.argc(); i++) michael@0: callInfo.argv().infallibleAppend(nativeCallInfo.getArg(i)); michael@0: michael@0: if (!makeCall(scriptedTarget, callInfo, false)) michael@0: return InliningStatus_Error; michael@0: michael@0: return InliningStatus_Inlined; michael@0: } michael@0: michael@0: } // namespace jit michael@0: } // namespace js