1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/BaselineInspector.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,513 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jit/BaselineInspector.h" 1.11 + 1.12 +#include "mozilla/DebugOnly.h" 1.13 + 1.14 +#include "jit/BaselineIC.h" 1.15 + 1.16 +using namespace js; 1.17 +using namespace js::jit; 1.18 + 1.19 +using mozilla::DebugOnly; 1.20 + 1.21 +bool 1.22 +SetElemICInspector::sawOOBDenseWrite() const 1.23 +{ 1.24 + if (!icEntry_) 1.25 + return false; 1.26 + 1.27 + // Check for a SetElem_DenseAdd stub. 1.28 + for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) { 1.29 + if (stub->isSetElem_DenseAdd()) 1.30 + return true; 1.31 + } 1.32 + 1.33 + // Check for a write hole bit on the SetElem_Fallback stub. 1.34 + ICStub *stub = icEntry_->fallbackStub(); 1.35 + if (stub->isSetElem_Fallback()) 1.36 + return stub->toSetElem_Fallback()->hasArrayWriteHole(); 1.37 + 1.38 + return false; 1.39 +} 1.40 + 1.41 +bool 1.42 +SetElemICInspector::sawOOBTypedArrayWrite() const 1.43 +{ 1.44 + if (!icEntry_) 1.45 + return false; 1.46 + 1.47 + // Check for SetElem_TypedArray stubs with expectOutOfBounds set. 1.48 + for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) { 1.49 + if (!stub->isSetElem_TypedArray()) 1.50 + continue; 1.51 + if (stub->toSetElem_TypedArray()->expectOutOfBounds()) 1.52 + return true; 1.53 + } 1.54 + return false; 1.55 +} 1.56 + 1.57 +bool 1.58 +SetElemICInspector::sawDenseWrite() const 1.59 +{ 1.60 + if (!icEntry_) 1.61 + return false; 1.62 + 1.63 + // Check for a SetElem_DenseAdd or SetElem_Dense stub. 1.64 + for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) { 1.65 + if (stub->isSetElem_DenseAdd() || stub->isSetElem_Dense()) 1.66 + return true; 1.67 + } 1.68 + return false; 1.69 +} 1.70 + 1.71 +bool 1.72 +SetElemICInspector::sawTypedArrayWrite() const 1.73 +{ 1.74 + if (!icEntry_) 1.75 + return false; 1.76 + 1.77 + // Check for a SetElem_TypedArray stub. 1.78 + for (ICStub *stub = icEntry_->firstStub(); stub; stub = stub->next()) { 1.79 + if (stub->isSetElem_TypedArray()) 1.80 + return true; 1.81 + } 1.82 + return false; 1.83 +} 1.84 + 1.85 +bool 1.86 +BaselineInspector::maybeShapesForPropertyOp(jsbytecode *pc, ShapeVector &shapes) 1.87 +{ 1.88 + // Return a list of shapes seen by the baseline IC for the current op. 1.89 + // An empty list indicates no shapes are known, or there was an uncacheable 1.90 + // access. 1.91 + JS_ASSERT(shapes.empty()); 1.92 + 1.93 + if (!hasBaselineScript()) 1.94 + return true; 1.95 + 1.96 + JS_ASSERT(isValidPC(pc)); 1.97 + const ICEntry &entry = icEntryFromPC(pc); 1.98 + 1.99 + ICStub *stub = entry.firstStub(); 1.100 + while (stub->next()) { 1.101 + Shape *shape; 1.102 + if (stub->isGetProp_Native()) { 1.103 + shape = stub->toGetProp_Native()->shape(); 1.104 + } else if (stub->isSetProp_Native()) { 1.105 + shape = stub->toSetProp_Native()->shape(); 1.106 + } else { 1.107 + shapes.clear(); 1.108 + return true; 1.109 + } 1.110 + 1.111 + // Don't add the same shape twice (this can happen if there are multiple 1.112 + // SetProp_Native stubs with different TypeObject's). 1.113 + bool found = false; 1.114 + for (size_t i = 0; i < shapes.length(); i++) { 1.115 + if (shapes[i] == shape) { 1.116 + found = true; 1.117 + break; 1.118 + } 1.119 + } 1.120 + 1.121 + if (!found && !shapes.append(shape)) 1.122 + return false; 1.123 + 1.124 + stub = stub->next(); 1.125 + } 1.126 + 1.127 + if (stub->isGetProp_Fallback()) { 1.128 + if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) 1.129 + shapes.clear(); 1.130 + } else { 1.131 + if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) 1.132 + shapes.clear(); 1.133 + } 1.134 + 1.135 + // Don't inline if there are more than 5 shapes. 1.136 + if (shapes.length() > 5) 1.137 + shapes.clear(); 1.138 + 1.139 + return true; 1.140 +} 1.141 + 1.142 +ICStub * 1.143 +BaselineInspector::monomorphicStub(jsbytecode *pc) 1.144 +{ 1.145 + if (!hasBaselineScript()) 1.146 + return nullptr; 1.147 + 1.148 + const ICEntry &entry = icEntryFromPC(pc); 1.149 + 1.150 + ICStub *stub = entry.firstStub(); 1.151 + ICStub *next = stub->next(); 1.152 + 1.153 + if (!next || !next->isFallback()) 1.154 + return nullptr; 1.155 + 1.156 + return stub; 1.157 +} 1.158 + 1.159 +bool 1.160 +BaselineInspector::dimorphicStub(jsbytecode *pc, ICStub **pfirst, ICStub **psecond) 1.161 +{ 1.162 + if (!hasBaselineScript()) 1.163 + return false; 1.164 + 1.165 + const ICEntry &entry = icEntryFromPC(pc); 1.166 + 1.167 + ICStub *stub = entry.firstStub(); 1.168 + ICStub *next = stub->next(); 1.169 + ICStub *after = next ? next->next() : nullptr; 1.170 + 1.171 + if (!after || !after->isFallback()) 1.172 + return false; 1.173 + 1.174 + *pfirst = stub; 1.175 + *psecond = next; 1.176 + return true; 1.177 +} 1.178 + 1.179 +MIRType 1.180 +BaselineInspector::expectedResultType(jsbytecode *pc) 1.181 +{ 1.182 + // Look at the IC entries for this op to guess what type it will produce, 1.183 + // returning MIRType_None otherwise. 1.184 + 1.185 + ICStub *stub = monomorphicStub(pc); 1.186 + if (!stub) 1.187 + return MIRType_None; 1.188 + 1.189 + switch (stub->kind()) { 1.190 + case ICStub::BinaryArith_Int32: 1.191 + if (stub->toBinaryArith_Int32()->allowDouble()) 1.192 + return MIRType_Double; 1.193 + return MIRType_Int32; 1.194 + case ICStub::BinaryArith_BooleanWithInt32: 1.195 + case ICStub::UnaryArith_Int32: 1.196 + case ICStub::BinaryArith_DoubleWithInt32: 1.197 + return MIRType_Int32; 1.198 + case ICStub::BinaryArith_Double: 1.199 + case ICStub::UnaryArith_Double: 1.200 + return MIRType_Double; 1.201 + case ICStub::BinaryArith_StringConcat: 1.202 + case ICStub::BinaryArith_StringObjectConcat: 1.203 + return MIRType_String; 1.204 + default: 1.205 + return MIRType_None; 1.206 + } 1.207 +} 1.208 + 1.209 +// Whether a baseline stub kind is suitable for a double comparison that 1.210 +// converts its operands to doubles. 1.211 +static bool 1.212 +CanUseDoubleCompare(ICStub::Kind kind) 1.213 +{ 1.214 + return kind == ICStub::Compare_Double || kind == ICStub::Compare_NumberWithUndefined; 1.215 +} 1.216 + 1.217 +// Whether a baseline stub kind is suitable for an int32 comparison that 1.218 +// converts its operands to int32. 1.219 +static bool 1.220 +CanUseInt32Compare(ICStub::Kind kind) 1.221 +{ 1.222 + return kind == ICStub::Compare_Int32 || kind == ICStub::Compare_Int32WithBoolean; 1.223 +} 1.224 + 1.225 +MCompare::CompareType 1.226 +BaselineInspector::expectedCompareType(jsbytecode *pc) 1.227 +{ 1.228 + ICStub *first = monomorphicStub(pc), *second = nullptr; 1.229 + if (!first && !dimorphicStub(pc, &first, &second)) 1.230 + return MCompare::Compare_Unknown; 1.231 + 1.232 + if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind()))) { 1.233 + ICCompare_Int32WithBoolean *coerce = 1.234 + first->isCompare_Int32WithBoolean() 1.235 + ? first->toCompare_Int32WithBoolean() 1.236 + : ((second && second->isCompare_Int32WithBoolean()) 1.237 + ? second->toCompare_Int32WithBoolean() 1.238 + : nullptr); 1.239 + if (coerce) { 1.240 + return coerce->lhsIsInt32() 1.241 + ? MCompare::Compare_Int32MaybeCoerceRHS 1.242 + : MCompare::Compare_Int32MaybeCoerceLHS; 1.243 + } 1.244 + return MCompare::Compare_Int32; 1.245 + } 1.246 + 1.247 + if (CanUseDoubleCompare(first->kind()) && (!second || CanUseDoubleCompare(second->kind()))) { 1.248 + ICCompare_NumberWithUndefined *coerce = 1.249 + first->isCompare_NumberWithUndefined() 1.250 + ? first->toCompare_NumberWithUndefined() 1.251 + : (second && second->isCompare_NumberWithUndefined()) 1.252 + ? second->toCompare_NumberWithUndefined() 1.253 + : nullptr; 1.254 + if (coerce) { 1.255 + return coerce->lhsIsUndefined() 1.256 + ? MCompare::Compare_DoubleMaybeCoerceLHS 1.257 + : MCompare::Compare_DoubleMaybeCoerceRHS; 1.258 + } 1.259 + return MCompare::Compare_Double; 1.260 + } 1.261 + 1.262 + return MCompare::Compare_Unknown; 1.263 +} 1.264 + 1.265 +static bool 1.266 +TryToSpecializeBinaryArithOp(ICStub **stubs, 1.267 + uint32_t nstubs, 1.268 + MIRType *result) 1.269 +{ 1.270 + DebugOnly<bool> sawInt32 = false; 1.271 + bool sawDouble = false; 1.272 + bool sawOther = false; 1.273 + 1.274 + for (uint32_t i = 0; i < nstubs; i++) { 1.275 + switch (stubs[i]->kind()) { 1.276 + case ICStub::BinaryArith_Int32: 1.277 + sawInt32 = true; 1.278 + break; 1.279 + case ICStub::BinaryArith_BooleanWithInt32: 1.280 + sawInt32 = true; 1.281 + break; 1.282 + case ICStub::BinaryArith_Double: 1.283 + sawDouble = true; 1.284 + break; 1.285 + case ICStub::BinaryArith_DoubleWithInt32: 1.286 + sawDouble = true; 1.287 + break; 1.288 + default: 1.289 + sawOther = true; 1.290 + break; 1.291 + } 1.292 + } 1.293 + 1.294 + if (sawOther) 1.295 + return false; 1.296 + 1.297 + if (sawDouble) { 1.298 + *result = MIRType_Double; 1.299 + return true; 1.300 + } 1.301 + 1.302 + JS_ASSERT(sawInt32); 1.303 + *result = MIRType_Int32; 1.304 + return true; 1.305 +} 1.306 + 1.307 +MIRType 1.308 +BaselineInspector::expectedBinaryArithSpecialization(jsbytecode *pc) 1.309 +{ 1.310 + if (!hasBaselineScript()) 1.311 + return MIRType_None; 1.312 + 1.313 + MIRType result; 1.314 + ICStub *stubs[2]; 1.315 + 1.316 + const ICEntry &entry = icEntryFromPC(pc); 1.317 + ICStub *stub = entry.fallbackStub(); 1.318 + if (stub->isBinaryArith_Fallback() && 1.319 + stub->toBinaryArith_Fallback()->hadUnoptimizableOperands()) 1.320 + { 1.321 + return MIRType_None; 1.322 + } 1.323 + 1.324 + stubs[0] = monomorphicStub(pc); 1.325 + if (stubs[0]) { 1.326 + if (TryToSpecializeBinaryArithOp(stubs, 1, &result)) 1.327 + return result; 1.328 + } 1.329 + 1.330 + if (dimorphicStub(pc, &stubs[0], &stubs[1])) { 1.331 + if (TryToSpecializeBinaryArithOp(stubs, 2, &result)) 1.332 + return result; 1.333 + } 1.334 + 1.335 + return MIRType_None; 1.336 +} 1.337 + 1.338 +bool 1.339 +BaselineInspector::hasSeenNonNativeGetElement(jsbytecode *pc) 1.340 +{ 1.341 + if (!hasBaselineScript()) 1.342 + return false; 1.343 + 1.344 + const ICEntry &entry = icEntryFromPC(pc); 1.345 + ICStub *stub = entry.fallbackStub(); 1.346 + 1.347 + if (stub->isGetElem_Fallback()) 1.348 + return stub->toGetElem_Fallback()->hasNonNativeAccess(); 1.349 + return false; 1.350 +} 1.351 + 1.352 +bool 1.353 +BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode *pc) 1.354 +{ 1.355 + if (!hasBaselineScript()) 1.356 + return false; 1.357 + 1.358 + const ICEntry &entry = icEntryFromPC(pc); 1.359 + ICStub *stub = entry.fallbackStub(); 1.360 + 1.361 + if (stub->isGetElem_Fallback()) 1.362 + return stub->toGetElem_Fallback()->hasNegativeIndex(); 1.363 + return false; 1.364 +} 1.365 + 1.366 +bool 1.367 +BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc) 1.368 +{ 1.369 + if (!hasBaselineScript()) 1.370 + return false; 1.371 + 1.372 + const ICEntry &entry = icEntryFromPC(pc); 1.373 + ICStub *stub = entry.fallbackStub(); 1.374 + 1.375 + if (stub->isGetProp_Fallback()) 1.376 + return stub->toGetProp_Fallback()->hasAccessedGetter(); 1.377 + return false; 1.378 +} 1.379 + 1.380 +bool 1.381 +BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc) 1.382 +{ 1.383 + JS_ASSERT(JSOp(*pc) == JSOP_ITERNEXT); 1.384 + 1.385 + if (!hasBaselineScript()) 1.386 + return false; 1.387 + 1.388 + const ICEntry &entry = icEntryFromPC(pc); 1.389 + ICStub *stub = entry.fallbackStub(); 1.390 + 1.391 + return stub->toIteratorNext_Fallback()->hasNonStringResult(); 1.392 +} 1.393 + 1.394 +bool 1.395 +BaselineInspector::hasSeenDoubleResult(jsbytecode *pc) 1.396 +{ 1.397 + if (!hasBaselineScript()) 1.398 + return false; 1.399 + 1.400 + const ICEntry &entry = icEntryFromPC(pc); 1.401 + ICStub *stub = entry.fallbackStub(); 1.402 + 1.403 + JS_ASSERT(stub->isUnaryArith_Fallback() || stub->isBinaryArith_Fallback()); 1.404 + 1.405 + if (stub->isUnaryArith_Fallback()) 1.406 + return stub->toUnaryArith_Fallback()->sawDoubleResult(); 1.407 + else 1.408 + return stub->toBinaryArith_Fallback()->sawDoubleResult(); 1.409 + 1.410 + return false; 1.411 +} 1.412 + 1.413 +JSObject * 1.414 +BaselineInspector::getTemplateObject(jsbytecode *pc) 1.415 +{ 1.416 + if (!hasBaselineScript()) 1.417 + return nullptr; 1.418 + 1.419 + const ICEntry &entry = icEntryFromPC(pc); 1.420 + for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { 1.421 + switch (stub->kind()) { 1.422 + case ICStub::NewArray_Fallback: 1.423 + return stub->toNewArray_Fallback()->templateObject(); 1.424 + case ICStub::NewObject_Fallback: 1.425 + return stub->toNewObject_Fallback()->templateObject(); 1.426 + case ICStub::Rest_Fallback: 1.427 + return stub->toRest_Fallback()->templateObject(); 1.428 + case ICStub::Call_Scripted: 1.429 + if (JSObject *obj = stub->toCall_Scripted()->templateObject()) 1.430 + return obj; 1.431 + break; 1.432 + default: 1.433 + break; 1.434 + } 1.435 + } 1.436 + 1.437 + return nullptr; 1.438 +} 1.439 + 1.440 +JSObject * 1.441 +BaselineInspector::getTemplateObjectForNative(jsbytecode *pc, Native native) 1.442 +{ 1.443 + if (!hasBaselineScript()) 1.444 + return nullptr; 1.445 + 1.446 + const ICEntry &entry = icEntryFromPC(pc); 1.447 + for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { 1.448 + if (stub->isCall_Native() && stub->toCall_Native()->callee()->native() == native) 1.449 + return stub->toCall_Native()->templateObject(); 1.450 + } 1.451 + 1.452 + return nullptr; 1.453 +} 1.454 + 1.455 +DeclEnvObject * 1.456 +BaselineInspector::templateDeclEnvObject() 1.457 +{ 1.458 + if (!hasBaselineScript()) 1.459 + return nullptr; 1.460 + 1.461 + JSObject *res = &templateCallObject()->as<ScopeObject>().enclosingScope(); 1.462 + JS_ASSERT(res); 1.463 + 1.464 + return &res->as<DeclEnvObject>(); 1.465 +} 1.466 + 1.467 +CallObject * 1.468 +BaselineInspector::templateCallObject() 1.469 +{ 1.470 + if (!hasBaselineScript()) 1.471 + return nullptr; 1.472 + 1.473 + JSObject *res = baselineScript()->templateScope(); 1.474 + JS_ASSERT(res); 1.475 + 1.476 + return &res->as<CallObject>(); 1.477 +} 1.478 + 1.479 +JSObject * 1.480 +BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter) 1.481 +{ 1.482 + if (!hasBaselineScript()) 1.483 + return nullptr; 1.484 + 1.485 + const ICEntry &entry = icEntryFromPC(pc); 1.486 + for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { 1.487 + if (stub->isGetProp_CallScripted() || 1.488 + stub->isGetProp_CallNative() || 1.489 + stub->isGetProp_CallNativePrototype()) 1.490 + { 1.491 + ICGetPropCallGetter *nstub = static_cast<ICGetPropCallGetter *>(stub); 1.492 + *lastProperty = nstub->holderShape(); 1.493 + *commonGetter = nstub->getter(); 1.494 + return nstub->holder(); 1.495 + } 1.496 + } 1.497 + return nullptr; 1.498 +} 1.499 + 1.500 +JSObject * 1.501 +BaselineInspector::commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter) 1.502 +{ 1.503 + if (!hasBaselineScript()) 1.504 + return nullptr; 1.505 + 1.506 + const ICEntry &entry = icEntryFromPC(pc); 1.507 + for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) { 1.508 + if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) { 1.509 + ICSetPropCallSetter *nstub = static_cast<ICSetPropCallSetter *>(stub); 1.510 + *lastProperty = nstub->holderShape(); 1.511 + *commonSetter = nstub->setter(); 1.512 + return nstub->holder(); 1.513 + } 1.514 + } 1.515 + return nullptr; 1.516 +}