blob: 7d4b5bd68ba7d468f1b7ba40c92bd21904de1725 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "BaselineIC.h"
#include "BaselineInspector.h"
using namespace js;
using namespace js::jit;
bool
SetElemICInspector::sawOOBDenseWrite() const
{
if (!icEntry_)
return false;
// Check for a SetElem_DenseAdd stub.
for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
if (stub->isSetElem_DenseAdd())
return true;
}
return false;
}
bool
SetElemICInspector::sawOOBTypedArrayWrite() const
{
if (!icEntry_)
return false;
// Check for SetElem_TypedArray stubs with expectOutOfBounds set.
for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
if (!stub->isSetElem_TypedArray())
continue;
if (stub->toSetElem_TypedArray()->expectOutOfBounds())
return true;
}
return false;
}
bool
SetElemICInspector::sawDenseWrite() const
{
if (!icEntry_)
return false;
// Check for a SetElem_DenseAdd or SetElem_Dense stub.
for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) {
if (stub->isSetElem_DenseAdd() || stub->isSetElem_Dense())
return true;
}
return false;
}
bool
BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, Vector<Shape *> &shapes)
{
// Return a list of shapes seen by the baseline IC for the current op.
// An empty list indicates no shapes are known, or there was an uncacheable
// access.
JS_ASSERT(shapes.empty());
if (!hasBaselineScript())
return true;
JS_ASSERT(isValidPC(pc));
const ICEntry &entry = icEntryFromPC(pc);
ICStub *stub = entry.firstStub();
while (stub->next()) {
Shape *shape;
if (stub->isGetProp_Native()) {
shape = stub->toGetProp_Native()->shape();
} else if (stub->isSetProp_Native()) {
shape = stub->toSetProp_Native()->shape();
} else {
shapes.clear();
return true;
}
// Don't add the same shape twice (this can happen if there are multiple
// SetProp_Native stubs with different TypeObject's).
bool found = false;
for (size_t i = 0; i < shapes.length(); i++) {
if (shapes[i] == shape) {
found = true;
break;
}
}
if (!found && !shapes.append(shape))
return false;
stub = stub->next();
}
if (stub->isGetProp_Fallback()) {
if (stub->toGetProp_Fallback()->hadUnoptimizableAccess())
shapes.clear();
} else {
if (stub->toSetProp_Fallback()->hadUnoptimizableAccess())
shapes.clear();
}
// Don't inline if there are more than 5 shapes.
if (shapes.length() > 5)
shapes.clear();
return true;
}
ICStub *
BaselineInspector::monomorphicStub(jsbytecode *pc)
{
if (!hasBaselineScript())
return NULL;
const ICEntry &entry = icEntryFromPC(pc);
ICStub *stub = entry.firstStub();
ICStub *next = stub->next();
if (!next || !next->isFallback())
return NULL;
return stub;
}
bool
BaselineInspector::dimorphicStub(jsbytecode *pc, ICStub **pfirst, ICStub **psecond)
{
if (!hasBaselineScript())
return false;
const ICEntry &entry = icEntryFromPC(pc);
ICStub *stub = entry.firstStub();
ICStub *next = stub->next();
ICStub *after = next ? next->next() : NULL;
if (!after || !after->isFallback())
return false;
*pfirst = stub;
*psecond = next;
return true;
}
MIRType
BaselineInspector::expectedResultType(jsbytecode *pc)
{
// Look at the IC entries for this op to guess what type it will produce,
// returning MIRType_None otherwise.
ICStub *stub = monomorphicStub(pc);
if (!stub)
return MIRType_None;
switch (stub->kind()) {
case ICStub::BinaryArith_Int32:
if (stub->toBinaryArith_Int32()->allowDouble())
return MIRType_Double;
return MIRType_Int32;
case ICStub::BinaryArith_BooleanWithInt32:
case ICStub::UnaryArith_Int32:
case ICStub::BinaryArith_DoubleWithInt32:
return MIRType_Int32;
case ICStub::BinaryArith_Double:
case ICStub::UnaryArith_Double:
return MIRType_Double;
case ICStub::BinaryArith_StringConcat:
case ICStub::BinaryArith_StringObjectConcat:
return MIRType_String;
default:
return MIRType_None;
}
}
// Whether a baseline stub kind is suitable for a double comparison that
// converts its operands to doubles.
static bool
CanUseDoubleCompare(ICStub::Kind kind)
{
return kind == ICStub::Compare_Double || kind == ICStub::Compare_NumberWithUndefined;
}
// Whether a baseline stub kind is suitable for an int32 comparison that
// converts its operands to int32.
static bool
CanUseInt32Compare(ICStub::Kind kind)
{
return kind == ICStub::Compare_Int32 || kind == ICStub::Compare_Int32WithBoolean;
}
MCompare::CompareType
BaselineInspector::expectedCompareType(jsbytecode *pc)
{
ICStub *first = monomorphicStub(pc), *second = NULL;
if (!first && !dimorphicStub(pc, &first, &second))
return MCompare::Compare_Unknown;
if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind())))
return MCompare::Compare_Int32;
if (CanUseDoubleCompare(first->kind()) && (!second || CanUseDoubleCompare(second->kind()))) {
ICCompare_NumberWithUndefined *coerce =
first->isCompare_NumberWithUndefined()
? first->toCompare_NumberWithUndefined()
: (second && second->isCompare_NumberWithUndefined())
? second->toCompare_NumberWithUndefined()
: NULL;
if (coerce) {
return coerce->lhsIsUndefined()
? MCompare::Compare_DoubleMaybeCoerceLHS
: MCompare::Compare_DoubleMaybeCoerceRHS;
}
return MCompare::Compare_Double;
}
return MCompare::Compare_Unknown;
}
static bool
TryToSpecializeBinaryArithOp(ICStub **stubs,
uint32_t nstubs,
MIRType *result)
{
bool sawInt32 = false;
bool sawDouble = false;
bool sawOther = false;
for (uint32_t i = 0; i < nstubs; i++) {
switch (stubs[i]->kind()) {
case ICStub::BinaryArith_Int32:
sawInt32 = true;
break;
case ICStub::BinaryArith_BooleanWithInt32:
sawInt32 = true;
break;
case ICStub::BinaryArith_Double:
sawDouble = true;
break;
case ICStub::BinaryArith_DoubleWithInt32:
sawDouble = true;
break;
default:
sawOther = true;
break;
}
}
if (sawOther)
return false;
if (sawDouble) {
*result = MIRType_Double;
return true;
}
JS_ASSERT(sawInt32);
*result = MIRType_Int32;
return true;
}
MIRType
BaselineInspector::expectedBinaryArithSpecialization(jsbytecode *pc)
{
MIRType result;
ICStub *stubs[2];
stubs[0] = monomorphicStub(pc);
if (stubs[0]) {
if (TryToSpecializeBinaryArithOp(stubs, 1, &result))
return result;
}
if (dimorphicStub(pc, &stubs[0], &stubs[1])) {
if (TryToSpecializeBinaryArithOp(stubs, 2, &result))
return result;
}
return MIRType_None;
}
bool
BaselineInspector::hasSeenNonNativeGetElement(jsbytecode *pc)
{
if (!hasBaselineScript())
return false;
const ICEntry &entry = icEntryFromPC(pc);
ICStub *stub = entry.fallbackStub();
if (stub->isGetElem_Fallback())
return stub->toGetElem_Fallback()->hasNonNativeAccess();
return false;
}
bool
BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode *pc)
{
if (!hasBaselineScript())
return false;
const ICEntry &entry = icEntryFromPC(pc);
ICStub *stub = entry.fallbackStub();
if (stub->isGetElem_Fallback())
return stub->toGetElem_Fallback()->hasNegativeIndex();
return false;
}
bool
BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc)
{
if (!hasBaselineScript())
return false;
const ICEntry &entry = icEntryFromPC(pc);
ICStub *stub = entry.fallbackStub();
if (stub->isGetProp_Fallback())
return stub->toGetProp_Fallback()->hasAccessedGetter();
return false;
}
bool
BaselineInspector::hasSeenDoubleResult(jsbytecode *pc)
{
if (!hasBaselineScript())
return false;
const ICEntry &entry = icEntryFromPC(pc);
ICStub *stub = entry.fallbackStub();
JS_ASSERT(stub->isUnaryArith_Fallback() || stub->isBinaryArith_Fallback());
if (stub->isUnaryArith_Fallback())
return stub->toUnaryArith_Fallback()->sawDoubleResult();
else
return stub->toBinaryArith_Fallback()->sawDoubleResult();
return false;
}