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 "jit/BaselineInspector.h" michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include "jit/BaselineIC.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: using mozilla::DebugOnly; michael@0: michael@0: bool michael@0: SetElemICInspector::sawOOBDenseWrite() const michael@0: { michael@0: if (!icEntry_) michael@0: return false; michael@0: michael@0: // Check for a SetElem_DenseAdd stub. michael@0: for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) { michael@0: if (stub->isSetElem_DenseAdd()) michael@0: return true; michael@0: } michael@0: michael@0: // Check for a write hole bit on the SetElem_Fallback stub. michael@0: ICStub *stub = icEntry_->fallbackStub(); michael@0: if (stub->isSetElem_Fallback()) michael@0: return stub->toSetElem_Fallback()->hasArrayWriteHole(); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: SetElemICInspector::sawOOBTypedArrayWrite() const michael@0: { michael@0: if (!icEntry_) michael@0: return false; michael@0: michael@0: // Check for SetElem_TypedArray stubs with expectOutOfBounds set. michael@0: for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) { michael@0: if (!stub->isSetElem_TypedArray()) michael@0: continue; michael@0: if (stub->toSetElem_TypedArray()->expectOutOfBounds()) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: SetElemICInspector::sawDenseWrite() const michael@0: { michael@0: if (!icEntry_) michael@0: return false; michael@0: michael@0: // Check for a SetElem_DenseAdd or SetElem_Dense stub. michael@0: for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) { michael@0: if (stub->isSetElem_DenseAdd() || stub->isSetElem_Dense()) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: SetElemICInspector::sawTypedArrayWrite() const michael@0: { michael@0: if (!icEntry_) michael@0: return false; michael@0: michael@0: // Check for a SetElem_TypedArray stub. michael@0: for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) { michael@0: if (stub->isSetElem_TypedArray()) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes) michael@0: { michael@0: // Return a list of shapes seen by the baseline IC for the current op. michael@0: // An empty list indicates no shapes are known, or there was an uncacheable michael@0: // access. michael@0: JS_ASSERT(shapes.empty()); michael@0: michael@0: if (!hasBaselineScript()) michael@0: return true; michael@0: michael@0: JS_ASSERT(isValidPC(pc)); michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: michael@0: ICStub *stub = entry.firstStub(); michael@0: while (stub->next()) { michael@0: Shape *shape; michael@0: if (stub->isGetProp_Native()) { michael@0: shape = stub->toGetProp_Native()->shape(); michael@0: } else if (stub->isSetProp_Native()) { michael@0: shape = stub->toSetProp_Native()->shape(); michael@0: } else { michael@0: shapes.clear(); michael@0: return true; michael@0: } michael@0: michael@0: // Don't add the same shape twice (this can happen if there are multiple michael@0: // SetProp_Native stubs with different TypeObject's). michael@0: bool found = false; michael@0: for (size_t i = 0; i < shapes.length(); i++) { michael@0: if (shapes[i] == shape) { michael@0: found = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!found && !shapes.append(shape)) michael@0: return false; michael@0: michael@0: stub = stub->next(); michael@0: } michael@0: michael@0: if (stub->isGetProp_Fallback()) { michael@0: if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) michael@0: shapes.clear(); michael@0: } else { michael@0: if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) michael@0: shapes.clear(); michael@0: } michael@0: michael@0: // Don't inline if there are more than 5 shapes. michael@0: if (shapes.length() > 5) michael@0: shapes.clear(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: ICStub * michael@0: BaselineInspector::monomorphicStub(jsbytecode *pc) michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return nullptr; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: michael@0: ICStub *stub = entry.firstStub(); michael@0: ICStub *next = stub->next(); michael@0: michael@0: if (!next || !next->isFallback()) michael@0: return nullptr; michael@0: michael@0: return stub; michael@0: } michael@0: michael@0: bool michael@0: BaselineInspector::dimorphicStub(jsbytecode *pc, ICStub **pfirst, ICStub **psecond) michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return false; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: michael@0: ICStub *stub = entry.firstStub(); michael@0: ICStub *next = stub->next(); michael@0: ICStub *after = next ? next->next() : nullptr; michael@0: michael@0: if (!after || !after->isFallback()) michael@0: return false; michael@0: michael@0: *pfirst = stub; michael@0: *psecond = next; michael@0: return true; michael@0: } michael@0: michael@0: MIRType michael@0: BaselineInspector::expectedResultType(jsbytecode *pc) michael@0: { michael@0: // Look at the IC entries for this op to guess what type it will produce, michael@0: // returning MIRType_None otherwise. michael@0: michael@0: ICStub *stub = monomorphicStub(pc); michael@0: if (!stub) michael@0: return MIRType_None; michael@0: michael@0: switch (stub->kind()) { michael@0: case ICStub::BinaryArith_Int32: michael@0: if (stub->toBinaryArith_Int32()->allowDouble()) michael@0: return MIRType_Double; michael@0: return MIRType_Int32; michael@0: case ICStub::BinaryArith_BooleanWithInt32: michael@0: case ICStub::UnaryArith_Int32: michael@0: case ICStub::BinaryArith_DoubleWithInt32: michael@0: return MIRType_Int32; michael@0: case ICStub::BinaryArith_Double: michael@0: case ICStub::UnaryArith_Double: michael@0: return MIRType_Double; michael@0: case ICStub::BinaryArith_StringConcat: michael@0: case ICStub::BinaryArith_StringObjectConcat: michael@0: return MIRType_String; michael@0: default: michael@0: return MIRType_None; michael@0: } michael@0: } michael@0: michael@0: // Whether a baseline stub kind is suitable for a double comparison that michael@0: // converts its operands to doubles. michael@0: static bool michael@0: CanUseDoubleCompare(ICStub::Kind kind) michael@0: { michael@0: return kind == ICStub::Compare_Double || kind == ICStub::Compare_NumberWithUndefined; michael@0: } michael@0: michael@0: // Whether a baseline stub kind is suitable for an int32 comparison that michael@0: // converts its operands to int32. michael@0: static bool michael@0: CanUseInt32Compare(ICStub::Kind kind) michael@0: { michael@0: return kind == ICStub::Compare_Int32 || kind == ICStub::Compare_Int32WithBoolean; michael@0: } michael@0: michael@0: MCompare::CompareType michael@0: BaselineInspector::expectedCompareType(jsbytecode *pc) michael@0: { michael@0: ICStub *first = monomorphicStub(pc), *second = nullptr; michael@0: if (!first && !dimorphicStub(pc, &first, &second)) michael@0: return MCompare::Compare_Unknown; michael@0: michael@0: if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind()))) { michael@0: ICCompare_Int32WithBoolean *coerce = michael@0: first->isCompare_Int32WithBoolean() michael@0: ? first->toCompare_Int32WithBoolean() michael@0: : ((second && second->isCompare_Int32WithBoolean()) michael@0: ? second->toCompare_Int32WithBoolean() michael@0: : nullptr); michael@0: if (coerce) { michael@0: return coerce->lhsIsInt32() michael@0: ? MCompare::Compare_Int32MaybeCoerceRHS michael@0: : MCompare::Compare_Int32MaybeCoerceLHS; michael@0: } michael@0: return MCompare::Compare_Int32; michael@0: } michael@0: michael@0: if (CanUseDoubleCompare(first->kind()) && (!second || CanUseDoubleCompare(second->kind()))) { michael@0: ICCompare_NumberWithUndefined *coerce = michael@0: first->isCompare_NumberWithUndefined() michael@0: ? first->toCompare_NumberWithUndefined() michael@0: : (second && second->isCompare_NumberWithUndefined()) michael@0: ? second->toCompare_NumberWithUndefined() michael@0: : nullptr; michael@0: if (coerce) { michael@0: return coerce->lhsIsUndefined() michael@0: ? MCompare::Compare_DoubleMaybeCoerceLHS michael@0: : MCompare::Compare_DoubleMaybeCoerceRHS; michael@0: } michael@0: return MCompare::Compare_Double; michael@0: } michael@0: michael@0: return MCompare::Compare_Unknown; michael@0: } michael@0: michael@0: static bool michael@0: TryToSpecializeBinaryArithOp(ICStub **stubs, michael@0: uint32_t nstubs, michael@0: MIRType *result) michael@0: { michael@0: DebugOnly sawInt32 = false; michael@0: bool sawDouble = false; michael@0: bool sawOther = false; michael@0: michael@0: for (uint32_t i = 0; i < nstubs; i++) { michael@0: switch (stubs[i]->kind()) { michael@0: case ICStub::BinaryArith_Int32: michael@0: sawInt32 = true; michael@0: break; michael@0: case ICStub::BinaryArith_BooleanWithInt32: michael@0: sawInt32 = true; michael@0: break; michael@0: case ICStub::BinaryArith_Double: michael@0: sawDouble = true; michael@0: break; michael@0: case ICStub::BinaryArith_DoubleWithInt32: michael@0: sawDouble = true; michael@0: break; michael@0: default: michael@0: sawOther = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (sawOther) michael@0: return false; michael@0: michael@0: if (sawDouble) { michael@0: *result = MIRType_Double; michael@0: return true; michael@0: } michael@0: michael@0: JS_ASSERT(sawInt32); michael@0: *result = MIRType_Int32; michael@0: return true; michael@0: } michael@0: michael@0: MIRType michael@0: BaselineInspector::expectedBinaryArithSpecialization(jsbytecode *pc) michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return MIRType_None; michael@0: michael@0: MIRType result; michael@0: ICStub *stubs[2]; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: ICStub *stub = entry.fallbackStub(); michael@0: if (stub->isBinaryArith_Fallback() && michael@0: stub->toBinaryArith_Fallback()->hadUnoptimizableOperands()) michael@0: { michael@0: return MIRType_None; michael@0: } michael@0: michael@0: stubs[0] = monomorphicStub(pc); michael@0: if (stubs[0]) { michael@0: if (TryToSpecializeBinaryArithOp(stubs, 1, &result)) michael@0: return result; michael@0: } michael@0: michael@0: if (dimorphicStub(pc, &stubs[0], &stubs[1])) { michael@0: if (TryToSpecializeBinaryArithOp(stubs, 2, &result)) michael@0: return result; michael@0: } michael@0: michael@0: return MIRType_None; michael@0: } michael@0: michael@0: bool michael@0: BaselineInspector::hasSeenNonNativeGetElement(jsbytecode *pc) michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return false; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: ICStub *stub = entry.fallbackStub(); michael@0: michael@0: if (stub->isGetElem_Fallback()) michael@0: return stub->toGetElem_Fallback()->hasNonNativeAccess(); michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode *pc) michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return false; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: ICStub *stub = entry.fallbackStub(); michael@0: michael@0: if (stub->isGetElem_Fallback()) michael@0: return stub->toGetElem_Fallback()->hasNegativeIndex(); michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc) michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return false; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: ICStub *stub = entry.fallbackStub(); michael@0: michael@0: if (stub->isGetProp_Fallback()) michael@0: return stub->toGetProp_Fallback()->hasAccessedGetter(); michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc) michael@0: { michael@0: JS_ASSERT(JSOp(*pc) == JSOP_ITERNEXT); michael@0: michael@0: if (!hasBaselineScript()) michael@0: return false; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: ICStub *stub = entry.fallbackStub(); michael@0: michael@0: return stub->toIteratorNext_Fallback()->hasNonStringResult(); michael@0: } michael@0: michael@0: bool michael@0: BaselineInspector::hasSeenDoubleResult(jsbytecode *pc) michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return false; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: ICStub *stub = entry.fallbackStub(); michael@0: michael@0: JS_ASSERT(stub->isUnaryArith_Fallback() || stub->isBinaryArith_Fallback()); michael@0: michael@0: if (stub->isUnaryArith_Fallback()) michael@0: return stub->toUnaryArith_Fallback()->sawDoubleResult(); michael@0: else michael@0: return stub->toBinaryArith_Fallback()->sawDoubleResult(); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: JSObject * michael@0: BaselineInspector::getTemplateObject(jsbytecode *pc) michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return nullptr; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { michael@0: switch (stub->kind()) { michael@0: case ICStub::NewArray_Fallback: michael@0: return stub->toNewArray_Fallback()->templateObject(); michael@0: case ICStub::NewObject_Fallback: michael@0: return stub->toNewObject_Fallback()->templateObject(); michael@0: case ICStub::Rest_Fallback: michael@0: return stub->toRest_Fallback()->templateObject(); michael@0: case ICStub::Call_Scripted: michael@0: if (JSObject *obj = stub->toCall_Scripted()->templateObject()) michael@0: return obj; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: JSObject * michael@0: BaselineInspector::getTemplateObjectForNative(jsbytecode *pc, Native native) michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return nullptr; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { michael@0: if (stub->isCall_Native() && stub->toCall_Native()->callee()->native() == native) michael@0: return stub->toCall_Native()->templateObject(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: DeclEnvObject * michael@0: BaselineInspector::templateDeclEnvObject() michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return nullptr; michael@0: michael@0: JSObject *res = &templateCallObject()->as().enclosingScope(); michael@0: JS_ASSERT(res); michael@0: michael@0: return &res->as(); michael@0: } michael@0: michael@0: CallObject * michael@0: BaselineInspector::templateCallObject() michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return nullptr; michael@0: michael@0: JSObject *res = baselineScript()->templateScope(); michael@0: JS_ASSERT(res); michael@0: michael@0: return &res->as(); michael@0: } michael@0: michael@0: JSObject * michael@0: BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter) michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return nullptr; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { michael@0: if (stub->isGetProp_CallScripted() || michael@0: stub->isGetProp_CallNative() || michael@0: stub->isGetProp_CallNativePrototype()) michael@0: { michael@0: ICGetPropCallGetter *nstub = static_cast(stub); michael@0: *lastProperty = nstub->holderShape(); michael@0: *commonGetter = nstub->getter(); michael@0: return nstub->holder(); michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: JSObject * michael@0: BaselineInspector::commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter) michael@0: { michael@0: if (!hasBaselineScript()) michael@0: return nullptr; michael@0: michael@0: const ICEntry &entry = icEntryFromPC(pc); michael@0: for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { michael@0: if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) { michael@0: ICSetPropCallSetter *nstub = static_cast(stub); michael@0: *lastProperty = nstub->holderShape(); michael@0: *commonSetter = nstub->setter(); michael@0: return nstub->holder(); michael@0: } michael@0: } michael@0: return nullptr; michael@0: }