Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "jit/MIR.h" |
michael@0 | 8 | |
michael@0 | 9 | #include "mozilla/FloatingPoint.h" |
michael@0 | 10 | |
michael@0 | 11 | #include <ctype.h> |
michael@0 | 12 | |
michael@0 | 13 | #include "jslibmath.h" |
michael@0 | 14 | #include "jsstr.h" |
michael@0 | 15 | |
michael@0 | 16 | #include "jit/BaselineInspector.h" |
michael@0 | 17 | #include "jit/IonBuilder.h" |
michael@0 | 18 | #include "jit/IonSpewer.h" |
michael@0 | 19 | #include "jit/MIRGraph.h" |
michael@0 | 20 | #include "jit/RangeAnalysis.h" |
michael@0 | 21 | |
michael@0 | 22 | #include "jsatominlines.h" |
michael@0 | 23 | #include "jsinferinlines.h" |
michael@0 | 24 | #include "jsobjinlines.h" |
michael@0 | 25 | |
michael@0 | 26 | using namespace js; |
michael@0 | 27 | using namespace js::jit; |
michael@0 | 28 | |
michael@0 | 29 | using mozilla::NumbersAreIdentical; |
michael@0 | 30 | using mozilla::IsFloat32Representable; |
michael@0 | 31 | using mozilla::Maybe; |
michael@0 | 32 | |
michael@0 | 33 | template<size_t Op> static void |
michael@0 | 34 | ConvertDefinitionToDouble(TempAllocator &alloc, MDefinition *def, MInstruction *consumer) |
michael@0 | 35 | { |
michael@0 | 36 | MInstruction *replace = MToDouble::New(alloc, def); |
michael@0 | 37 | consumer->replaceOperand(Op, replace); |
michael@0 | 38 | consumer->block()->insertBefore(consumer, replace); |
michael@0 | 39 | } |
michael@0 | 40 | |
michael@0 | 41 | static bool |
michael@0 | 42 | CheckUsesAreFloat32Consumers(MInstruction *ins) |
michael@0 | 43 | { |
michael@0 | 44 | bool allConsumerUses = true; |
michael@0 | 45 | for (MUseDefIterator use(ins); allConsumerUses && use; use++) |
michael@0 | 46 | allConsumerUses &= use.def()->canConsumeFloat32(use.use()); |
michael@0 | 47 | return allConsumerUses; |
michael@0 | 48 | } |
michael@0 | 49 | |
michael@0 | 50 | void |
michael@0 | 51 | MDefinition::PrintOpcodeName(FILE *fp, MDefinition::Opcode op) |
michael@0 | 52 | { |
michael@0 | 53 | static const char * const names[] = |
michael@0 | 54 | { |
michael@0 | 55 | #define NAME(x) #x, |
michael@0 | 56 | MIR_OPCODE_LIST(NAME) |
michael@0 | 57 | #undef NAME |
michael@0 | 58 | }; |
michael@0 | 59 | const char *name = names[op]; |
michael@0 | 60 | size_t len = strlen(name); |
michael@0 | 61 | for (size_t i = 0; i < len; i++) |
michael@0 | 62 | fprintf(fp, "%c", tolower(name[i])); |
michael@0 | 63 | } |
michael@0 | 64 | |
michael@0 | 65 | static inline bool |
michael@0 | 66 | EqualValues(bool useGVN, MDefinition *left, MDefinition *right) |
michael@0 | 67 | { |
michael@0 | 68 | if (useGVN) |
michael@0 | 69 | return left->valueNumber() == right->valueNumber(); |
michael@0 | 70 | |
michael@0 | 71 | return left == right; |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | static MConstant * |
michael@0 | 75 | EvaluateConstantOperands(TempAllocator &alloc, MBinaryInstruction *ins, bool *ptypeChange = nullptr) |
michael@0 | 76 | { |
michael@0 | 77 | MDefinition *left = ins->getOperand(0); |
michael@0 | 78 | MDefinition *right = ins->getOperand(1); |
michael@0 | 79 | |
michael@0 | 80 | if (!left->isConstant() || !right->isConstant()) |
michael@0 | 81 | return nullptr; |
michael@0 | 82 | |
michael@0 | 83 | Value lhs = left->toConstant()->value(); |
michael@0 | 84 | Value rhs = right->toConstant()->value(); |
michael@0 | 85 | Value ret = UndefinedValue(); |
michael@0 | 86 | |
michael@0 | 87 | switch (ins->op()) { |
michael@0 | 88 | case MDefinition::Op_BitAnd: |
michael@0 | 89 | ret = Int32Value(lhs.toInt32() & rhs.toInt32()); |
michael@0 | 90 | break; |
michael@0 | 91 | case MDefinition::Op_BitOr: |
michael@0 | 92 | ret = Int32Value(lhs.toInt32() | rhs.toInt32()); |
michael@0 | 93 | break; |
michael@0 | 94 | case MDefinition::Op_BitXor: |
michael@0 | 95 | ret = Int32Value(lhs.toInt32() ^ rhs.toInt32()); |
michael@0 | 96 | break; |
michael@0 | 97 | case MDefinition::Op_Lsh: |
michael@0 | 98 | ret = Int32Value(uint32_t(lhs.toInt32()) << (rhs.toInt32() & 0x1F)); |
michael@0 | 99 | break; |
michael@0 | 100 | case MDefinition::Op_Rsh: |
michael@0 | 101 | ret = Int32Value(lhs.toInt32() >> (rhs.toInt32() & 0x1F)); |
michael@0 | 102 | break; |
michael@0 | 103 | case MDefinition::Op_Ursh: |
michael@0 | 104 | ret.setNumber(uint32_t(lhs.toInt32()) >> (rhs.toInt32() & 0x1F)); |
michael@0 | 105 | break; |
michael@0 | 106 | case MDefinition::Op_Add: |
michael@0 | 107 | ret.setNumber(lhs.toNumber() + rhs.toNumber()); |
michael@0 | 108 | break; |
michael@0 | 109 | case MDefinition::Op_Sub: |
michael@0 | 110 | ret.setNumber(lhs.toNumber() - rhs.toNumber()); |
michael@0 | 111 | break; |
michael@0 | 112 | case MDefinition::Op_Mul: |
michael@0 | 113 | ret.setNumber(lhs.toNumber() * rhs.toNumber()); |
michael@0 | 114 | break; |
michael@0 | 115 | case MDefinition::Op_Div: |
michael@0 | 116 | ret.setNumber(NumberDiv(lhs.toNumber(), rhs.toNumber())); |
michael@0 | 117 | break; |
michael@0 | 118 | case MDefinition::Op_Mod: |
michael@0 | 119 | ret.setNumber(NumberMod(lhs.toNumber(), rhs.toNumber())); |
michael@0 | 120 | break; |
michael@0 | 121 | default: |
michael@0 | 122 | MOZ_ASSUME_UNREACHABLE("NYI"); |
michael@0 | 123 | } |
michael@0 | 124 | |
michael@0 | 125 | // setNumber eagerly transforms a number to int32. |
michael@0 | 126 | // Transform back to double, if the output type is double. |
michael@0 | 127 | if (ins->type() == MIRType_Double && ret.isInt32()) |
michael@0 | 128 | ret.setDouble(ret.toNumber()); |
michael@0 | 129 | |
michael@0 | 130 | if (ins->type() != MIRTypeFromValue(ret)) { |
michael@0 | 131 | if (ptypeChange) |
michael@0 | 132 | *ptypeChange = true; |
michael@0 | 133 | return nullptr; |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | return MConstant::New(alloc, ret); |
michael@0 | 137 | } |
michael@0 | 138 | |
michael@0 | 139 | void |
michael@0 | 140 | MDefinition::printName(FILE *fp) const |
michael@0 | 141 | { |
michael@0 | 142 | PrintOpcodeName(fp, op()); |
michael@0 | 143 | fprintf(fp, "%u", id()); |
michael@0 | 144 | |
michael@0 | 145 | if (valueNumber() != 0) |
michael@0 | 146 | fprintf(fp, "-vn%u", valueNumber()); |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | HashNumber |
michael@0 | 150 | MDefinition::valueHash() const |
michael@0 | 151 | { |
michael@0 | 152 | HashNumber out = op(); |
michael@0 | 153 | for (size_t i = 0, e = numOperands(); i < e; i++) { |
michael@0 | 154 | uint32_t valueNumber = getOperand(i)->valueNumber(); |
michael@0 | 155 | out = valueNumber + (out << 6) + (out << 16) - out; |
michael@0 | 156 | } |
michael@0 | 157 | return out; |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | bool |
michael@0 | 161 | MDefinition::congruentIfOperandsEqual(const MDefinition *ins) const |
michael@0 | 162 | { |
michael@0 | 163 | if (op() != ins->op()) |
michael@0 | 164 | return false; |
michael@0 | 165 | |
michael@0 | 166 | if (type() != ins->type()) |
michael@0 | 167 | return false; |
michael@0 | 168 | |
michael@0 | 169 | if (isEffectful() || ins->isEffectful()) |
michael@0 | 170 | return false; |
michael@0 | 171 | |
michael@0 | 172 | if (numOperands() != ins->numOperands()) |
michael@0 | 173 | return false; |
michael@0 | 174 | |
michael@0 | 175 | for (size_t i = 0, e = numOperands(); i < e; i++) { |
michael@0 | 176 | if (getOperand(i)->valueNumber() != ins->getOperand(i)->valueNumber()) |
michael@0 | 177 | return false; |
michael@0 | 178 | } |
michael@0 | 179 | |
michael@0 | 180 | return true; |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | MDefinition * |
michael@0 | 184 | MDefinition::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 185 | { |
michael@0 | 186 | // In the default case, there are no constants to fold. |
michael@0 | 187 | return this; |
michael@0 | 188 | } |
michael@0 | 189 | |
michael@0 | 190 | void |
michael@0 | 191 | MDefinition::analyzeEdgeCasesForward() |
michael@0 | 192 | { |
michael@0 | 193 | } |
michael@0 | 194 | |
michael@0 | 195 | void |
michael@0 | 196 | MDefinition::analyzeEdgeCasesBackward() |
michael@0 | 197 | { |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | static bool |
michael@0 | 201 | MaybeEmulatesUndefined(MDefinition *op) |
michael@0 | 202 | { |
michael@0 | 203 | if (!op->mightBeType(MIRType_Object)) |
michael@0 | 204 | return false; |
michael@0 | 205 | |
michael@0 | 206 | types::TemporaryTypeSet *types = op->resultTypeSet(); |
michael@0 | 207 | if (!types) |
michael@0 | 208 | return true; |
michael@0 | 209 | |
michael@0 | 210 | return types->maybeEmulatesUndefined(); |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | static bool |
michael@0 | 214 | MaybeCallable(MDefinition *op) |
michael@0 | 215 | { |
michael@0 | 216 | if (!op->mightBeType(MIRType_Object)) |
michael@0 | 217 | return false; |
michael@0 | 218 | |
michael@0 | 219 | types::TemporaryTypeSet *types = op->resultTypeSet(); |
michael@0 | 220 | if (!types) |
michael@0 | 221 | return true; |
michael@0 | 222 | |
michael@0 | 223 | return types->maybeCallable(); |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | MTest * |
michael@0 | 227 | MTest::New(TempAllocator &alloc, MDefinition *ins, MBasicBlock *ifTrue, MBasicBlock *ifFalse) |
michael@0 | 228 | { |
michael@0 | 229 | return new(alloc) MTest(ins, ifTrue, ifFalse); |
michael@0 | 230 | } |
michael@0 | 231 | |
michael@0 | 232 | void |
michael@0 | 233 | MTest::infer() |
michael@0 | 234 | { |
michael@0 | 235 | JS_ASSERT(operandMightEmulateUndefined()); |
michael@0 | 236 | |
michael@0 | 237 | if (!MaybeEmulatesUndefined(getOperand(0))) |
michael@0 | 238 | markOperandCantEmulateUndefined(); |
michael@0 | 239 | } |
michael@0 | 240 | |
michael@0 | 241 | MDefinition * |
michael@0 | 242 | MTest::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 243 | { |
michael@0 | 244 | MDefinition *op = getOperand(0); |
michael@0 | 245 | |
michael@0 | 246 | if (op->isNot()) |
michael@0 | 247 | return MTest::New(alloc, op->toNot()->operand(), ifFalse(), ifTrue()); |
michael@0 | 248 | |
michael@0 | 249 | return this; |
michael@0 | 250 | } |
michael@0 | 251 | |
michael@0 | 252 | void |
michael@0 | 253 | MTest::filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined, |
michael@0 | 254 | bool *filtersNull) |
michael@0 | 255 | { |
michael@0 | 256 | MDefinition *ins = getOperand(0); |
michael@0 | 257 | if (ins->isCompare()) { |
michael@0 | 258 | ins->toCompare()->filtersUndefinedOrNull(trueBranch, subject, filtersUndefined, filtersNull); |
michael@0 | 259 | return; |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | if (!trueBranch && ins->isNot()) { |
michael@0 | 263 | *subject = ins->getOperand(0); |
michael@0 | 264 | *filtersUndefined = *filtersNull = true; |
michael@0 | 265 | return; |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | if (trueBranch) { |
michael@0 | 269 | *subject = ins; |
michael@0 | 270 | *filtersUndefined = *filtersNull = true; |
michael@0 | 271 | return; |
michael@0 | 272 | } |
michael@0 | 273 | |
michael@0 | 274 | *filtersUndefined = *filtersNull = false; |
michael@0 | 275 | *subject = nullptr; |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | void |
michael@0 | 279 | MDefinition::printOpcode(FILE *fp) const |
michael@0 | 280 | { |
michael@0 | 281 | PrintOpcodeName(fp, op()); |
michael@0 | 282 | for (size_t j = 0, e = numOperands(); j < e; j++) { |
michael@0 | 283 | fprintf(fp, " "); |
michael@0 | 284 | getOperand(j)->printName(fp); |
michael@0 | 285 | } |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | void |
michael@0 | 289 | MDefinition::dump(FILE *fp) const |
michael@0 | 290 | { |
michael@0 | 291 | printName(fp); |
michael@0 | 292 | fprintf(fp, " = "); |
michael@0 | 293 | printOpcode(fp); |
michael@0 | 294 | fprintf(fp, "\n"); |
michael@0 | 295 | } |
michael@0 | 296 | |
michael@0 | 297 | void |
michael@0 | 298 | MDefinition::dump() const |
michael@0 | 299 | { |
michael@0 | 300 | dump(stderr); |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | size_t |
michael@0 | 304 | MDefinition::useCount() const |
michael@0 | 305 | { |
michael@0 | 306 | size_t count = 0; |
michael@0 | 307 | for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) |
michael@0 | 308 | count++; |
michael@0 | 309 | return count; |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | size_t |
michael@0 | 313 | MDefinition::defUseCount() const |
michael@0 | 314 | { |
michael@0 | 315 | size_t count = 0; |
michael@0 | 316 | for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) |
michael@0 | 317 | if ((*i)->consumer()->isDefinition()) |
michael@0 | 318 | count++; |
michael@0 | 319 | return count; |
michael@0 | 320 | } |
michael@0 | 321 | |
michael@0 | 322 | bool |
michael@0 | 323 | MDefinition::hasOneUse() const |
michael@0 | 324 | { |
michael@0 | 325 | MUseIterator i(uses_.begin()); |
michael@0 | 326 | if (i == uses_.end()) |
michael@0 | 327 | return false; |
michael@0 | 328 | i++; |
michael@0 | 329 | return i == uses_.end(); |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | bool |
michael@0 | 333 | MDefinition::hasOneDefUse() const |
michael@0 | 334 | { |
michael@0 | 335 | bool hasOneDefUse = false; |
michael@0 | 336 | for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { |
michael@0 | 337 | if (!(*i)->consumer()->isDefinition()) |
michael@0 | 338 | continue; |
michael@0 | 339 | |
michael@0 | 340 | // We already have a definition use. So 1+ |
michael@0 | 341 | if (hasOneDefUse) |
michael@0 | 342 | return false; |
michael@0 | 343 | |
michael@0 | 344 | // We saw one definition. Loop to test if there is another. |
michael@0 | 345 | hasOneDefUse = true; |
michael@0 | 346 | } |
michael@0 | 347 | |
michael@0 | 348 | return hasOneDefUse; |
michael@0 | 349 | } |
michael@0 | 350 | |
michael@0 | 351 | bool |
michael@0 | 352 | MDefinition::hasDefUses() const |
michael@0 | 353 | { |
michael@0 | 354 | for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { |
michael@0 | 355 | if ((*i)->consumer()->isDefinition()) |
michael@0 | 356 | return true; |
michael@0 | 357 | } |
michael@0 | 358 | |
michael@0 | 359 | return false; |
michael@0 | 360 | } |
michael@0 | 361 | |
michael@0 | 362 | MUseIterator |
michael@0 | 363 | MDefinition::removeUse(MUseIterator use) |
michael@0 | 364 | { |
michael@0 | 365 | return uses_.removeAt(use); |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | MUseIterator |
michael@0 | 369 | MNode::replaceOperand(MUseIterator use, MDefinition *def) |
michael@0 | 370 | { |
michael@0 | 371 | JS_ASSERT(def != nullptr); |
michael@0 | 372 | uint32_t index = use->index(); |
michael@0 | 373 | MDefinition *prev = use->producer(); |
michael@0 | 374 | |
michael@0 | 375 | JS_ASSERT(use->index() < numOperands()); |
michael@0 | 376 | JS_ASSERT(use->producer() == getOperand(index)); |
michael@0 | 377 | JS_ASSERT(use->consumer() == this); |
michael@0 | 378 | |
michael@0 | 379 | if (prev == def) |
michael@0 | 380 | return use; |
michael@0 | 381 | |
michael@0 | 382 | MUseIterator result(prev->removeUse(use)); |
michael@0 | 383 | setOperand(index, def); |
michael@0 | 384 | return result; |
michael@0 | 385 | } |
michael@0 | 386 | |
michael@0 | 387 | void |
michael@0 | 388 | MNode::replaceOperand(size_t index, MDefinition *def) |
michael@0 | 389 | { |
michael@0 | 390 | JS_ASSERT(def != nullptr); |
michael@0 | 391 | MUse *use = getUseFor(index); |
michael@0 | 392 | MDefinition *prev = use->producer(); |
michael@0 | 393 | |
michael@0 | 394 | JS_ASSERT(use->index() == index); |
michael@0 | 395 | JS_ASSERT(use->index() < numOperands()); |
michael@0 | 396 | JS_ASSERT(use->producer() == getOperand(index)); |
michael@0 | 397 | JS_ASSERT(use->consumer() == this); |
michael@0 | 398 | |
michael@0 | 399 | if (prev == def) |
michael@0 | 400 | return; |
michael@0 | 401 | |
michael@0 | 402 | prev->removeUse(use); |
michael@0 | 403 | setOperand(index, def); |
michael@0 | 404 | } |
michael@0 | 405 | |
michael@0 | 406 | void |
michael@0 | 407 | MNode::discardOperand(size_t index) |
michael@0 | 408 | { |
michael@0 | 409 | MUse *use = getUseFor(index); |
michael@0 | 410 | |
michael@0 | 411 | JS_ASSERT(use->index() == index); |
michael@0 | 412 | JS_ASSERT(use->producer() == getOperand(index)); |
michael@0 | 413 | JS_ASSERT(use->consumer() == this); |
michael@0 | 414 | |
michael@0 | 415 | use->producer()->removeUse(use); |
michael@0 | 416 | |
michael@0 | 417 | #ifdef DEBUG |
michael@0 | 418 | // Causes any producer/consumer lookups to trip asserts. |
michael@0 | 419 | use->set(nullptr, nullptr, index); |
michael@0 | 420 | #endif |
michael@0 | 421 | } |
michael@0 | 422 | |
michael@0 | 423 | void |
michael@0 | 424 | MDefinition::replaceAllUsesWith(MDefinition *dom) |
michael@0 | 425 | { |
michael@0 | 426 | JS_ASSERT(dom != nullptr); |
michael@0 | 427 | if (dom == this) |
michael@0 | 428 | return; |
michael@0 | 429 | |
michael@0 | 430 | for (size_t i = 0, e = numOperands(); i < e; i++) |
michael@0 | 431 | getOperand(i)->setUseRemovedUnchecked(); |
michael@0 | 432 | |
michael@0 | 433 | for (MUseIterator i(usesBegin()); i != usesEnd(); ) { |
michael@0 | 434 | JS_ASSERT(i->producer() == this); |
michael@0 | 435 | i = i->consumer()->replaceOperand(i, dom); |
michael@0 | 436 | } |
michael@0 | 437 | } |
michael@0 | 438 | |
michael@0 | 439 | bool |
michael@0 | 440 | MDefinition::emptyResultTypeSet() const |
michael@0 | 441 | { |
michael@0 | 442 | return resultTypeSet() && resultTypeSet()->empty(); |
michael@0 | 443 | } |
michael@0 | 444 | |
michael@0 | 445 | MConstant * |
michael@0 | 446 | MConstant::New(TempAllocator &alloc, const Value &v, types::CompilerConstraintList *constraints) |
michael@0 | 447 | { |
michael@0 | 448 | return new(alloc) MConstant(v, constraints); |
michael@0 | 449 | } |
michael@0 | 450 | |
michael@0 | 451 | MConstant * |
michael@0 | 452 | MConstant::NewAsmJS(TempAllocator &alloc, const Value &v, MIRType type) |
michael@0 | 453 | { |
michael@0 | 454 | MConstant *constant = new(alloc) MConstant(v, nullptr); |
michael@0 | 455 | constant->setResultType(type); |
michael@0 | 456 | return constant; |
michael@0 | 457 | } |
michael@0 | 458 | |
michael@0 | 459 | types::TemporaryTypeSet * |
michael@0 | 460 | jit::MakeSingletonTypeSet(types::CompilerConstraintList *constraints, JSObject *obj) |
michael@0 | 461 | { |
michael@0 | 462 | // Invalidate when this object's TypeObject gets unknown properties. This |
michael@0 | 463 | // happens for instance when we mutate an object's __proto__, in this case |
michael@0 | 464 | // we want to invalidate and mark this TypeSet as containing AnyObject |
michael@0 | 465 | // (because mutating __proto__ will change an object's TypeObject). |
michael@0 | 466 | JS_ASSERT(constraints); |
michael@0 | 467 | types::TypeObjectKey *objType = types::TypeObjectKey::get(obj); |
michael@0 | 468 | objType->hasFlags(constraints, types::OBJECT_FLAG_UNKNOWN_PROPERTIES); |
michael@0 | 469 | |
michael@0 | 470 | return GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(types::Type::ObjectType(obj)); |
michael@0 | 471 | } |
michael@0 | 472 | |
michael@0 | 473 | MConstant::MConstant(const js::Value &vp, types::CompilerConstraintList *constraints) |
michael@0 | 474 | : value_(vp) |
michael@0 | 475 | { |
michael@0 | 476 | setResultType(MIRTypeFromValue(vp)); |
michael@0 | 477 | if (vp.isObject()) { |
michael@0 | 478 | // Create a singleton type set for the object. This isn't necessary for |
michael@0 | 479 | // other types as the result type encodes all needed information. |
michael@0 | 480 | setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject())); |
michael@0 | 481 | } |
michael@0 | 482 | |
michael@0 | 483 | setMovable(); |
michael@0 | 484 | } |
michael@0 | 485 | |
michael@0 | 486 | HashNumber |
michael@0 | 487 | MConstant::valueHash() const |
michael@0 | 488 | { |
michael@0 | 489 | // This disregards some state, since values are 64 bits. But for a hash, |
michael@0 | 490 | // it's completely acceptable. |
michael@0 | 491 | return (HashNumber)JSVAL_TO_IMPL(value_).asBits; |
michael@0 | 492 | } |
michael@0 | 493 | bool |
michael@0 | 494 | MConstant::congruentTo(const MDefinition *ins) const |
michael@0 | 495 | { |
michael@0 | 496 | if (!ins->isConstant()) |
michael@0 | 497 | return false; |
michael@0 | 498 | return ins->toConstant()->value() == value(); |
michael@0 | 499 | } |
michael@0 | 500 | |
michael@0 | 501 | void |
michael@0 | 502 | MConstant::printOpcode(FILE *fp) const |
michael@0 | 503 | { |
michael@0 | 504 | PrintOpcodeName(fp, op()); |
michael@0 | 505 | fprintf(fp, " "); |
michael@0 | 506 | switch (type()) { |
michael@0 | 507 | case MIRType_Undefined: |
michael@0 | 508 | fprintf(fp, "undefined"); |
michael@0 | 509 | break; |
michael@0 | 510 | case MIRType_Null: |
michael@0 | 511 | fprintf(fp, "null"); |
michael@0 | 512 | break; |
michael@0 | 513 | case MIRType_Boolean: |
michael@0 | 514 | fprintf(fp, value().toBoolean() ? "true" : "false"); |
michael@0 | 515 | break; |
michael@0 | 516 | case MIRType_Int32: |
michael@0 | 517 | fprintf(fp, "0x%x", value().toInt32()); |
michael@0 | 518 | break; |
michael@0 | 519 | case MIRType_Double: |
michael@0 | 520 | fprintf(fp, "%f", value().toDouble()); |
michael@0 | 521 | break; |
michael@0 | 522 | case MIRType_Float32: |
michael@0 | 523 | { |
michael@0 | 524 | float val = value().toDouble(); |
michael@0 | 525 | fprintf(fp, "%f", val); |
michael@0 | 526 | break; |
michael@0 | 527 | } |
michael@0 | 528 | case MIRType_Object: |
michael@0 | 529 | if (value().toObject().is<JSFunction>()) { |
michael@0 | 530 | JSFunction *fun = &value().toObject().as<JSFunction>(); |
michael@0 | 531 | if (fun->displayAtom()) { |
michael@0 | 532 | fputs("function ", fp); |
michael@0 | 533 | FileEscapedString(fp, fun->displayAtom(), 0); |
michael@0 | 534 | } else { |
michael@0 | 535 | fputs("unnamed function", fp); |
michael@0 | 536 | } |
michael@0 | 537 | if (fun->hasScript()) { |
michael@0 | 538 | JSScript *script = fun->nonLazyScript(); |
michael@0 | 539 | fprintf(fp, " (%s:%d)", |
michael@0 | 540 | script->filename() ? script->filename() : "", (int) script->lineno()); |
michael@0 | 541 | } |
michael@0 | 542 | fprintf(fp, " at %p", (void *) fun); |
michael@0 | 543 | break; |
michael@0 | 544 | } |
michael@0 | 545 | fprintf(fp, "object %p (%s)", (void *)&value().toObject(), |
michael@0 | 546 | value().toObject().getClass()->name); |
michael@0 | 547 | break; |
michael@0 | 548 | case MIRType_String: |
michael@0 | 549 | fprintf(fp, "string %p", (void *)value().toString()); |
michael@0 | 550 | break; |
michael@0 | 551 | case MIRType_MagicOptimizedArguments: |
michael@0 | 552 | fprintf(fp, "magic lazyargs"); |
michael@0 | 553 | break; |
michael@0 | 554 | case MIRType_MagicHole: |
michael@0 | 555 | fprintf(fp, "magic hole"); |
michael@0 | 556 | break; |
michael@0 | 557 | case MIRType_MagicIsConstructing: |
michael@0 | 558 | fprintf(fp, "magic is-constructing"); |
michael@0 | 559 | break; |
michael@0 | 560 | case MIRType_MagicOptimizedOut: |
michael@0 | 561 | fprintf(fp, "magic optimized-out"); |
michael@0 | 562 | break; |
michael@0 | 563 | default: |
michael@0 | 564 | MOZ_ASSUME_UNREACHABLE("unexpected type"); |
michael@0 | 565 | } |
michael@0 | 566 | } |
michael@0 | 567 | |
michael@0 | 568 | bool |
michael@0 | 569 | MConstant::canProduceFloat32() const |
michael@0 | 570 | { |
michael@0 | 571 | if (!IsNumberType(type())) |
michael@0 | 572 | return false; |
michael@0 | 573 | |
michael@0 | 574 | if (type() == MIRType_Int32) |
michael@0 | 575 | return IsFloat32Representable(static_cast<double>(value_.toInt32())); |
michael@0 | 576 | if (type() == MIRType_Double) |
michael@0 | 577 | return IsFloat32Representable(value_.toDouble()); |
michael@0 | 578 | return true; |
michael@0 | 579 | } |
michael@0 | 580 | |
michael@0 | 581 | MCloneLiteral * |
michael@0 | 582 | MCloneLiteral::New(TempAllocator &alloc, MDefinition *obj) |
michael@0 | 583 | { |
michael@0 | 584 | return new(alloc) MCloneLiteral(obj); |
michael@0 | 585 | } |
michael@0 | 586 | |
michael@0 | 587 | void |
michael@0 | 588 | MControlInstruction::printOpcode(FILE *fp) const |
michael@0 | 589 | { |
michael@0 | 590 | MDefinition::printOpcode(fp); |
michael@0 | 591 | for (size_t j = 0; j < numSuccessors(); j++) |
michael@0 | 592 | fprintf(fp, " block%d", getSuccessor(j)->id()); |
michael@0 | 593 | } |
michael@0 | 594 | |
michael@0 | 595 | void |
michael@0 | 596 | MCompare::printOpcode(FILE *fp) const |
michael@0 | 597 | { |
michael@0 | 598 | MDefinition::printOpcode(fp); |
michael@0 | 599 | fprintf(fp, " %s", js_CodeName[jsop()]); |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | void |
michael@0 | 603 | MConstantElements::printOpcode(FILE *fp) const |
michael@0 | 604 | { |
michael@0 | 605 | PrintOpcodeName(fp, op()); |
michael@0 | 606 | fprintf(fp, " %p", value()); |
michael@0 | 607 | } |
michael@0 | 608 | |
michael@0 | 609 | void |
michael@0 | 610 | MLoadTypedArrayElement::printOpcode(FILE *fp) const |
michael@0 | 611 | { |
michael@0 | 612 | MDefinition::printOpcode(fp); |
michael@0 | 613 | fprintf(fp, " %s", ScalarTypeDescr::typeName(arrayType())); |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | void |
michael@0 | 617 | MAssertRange::printOpcode(FILE *fp) const |
michael@0 | 618 | { |
michael@0 | 619 | MDefinition::printOpcode(fp); |
michael@0 | 620 | Sprinter sp(GetIonContext()->cx); |
michael@0 | 621 | sp.init(); |
michael@0 | 622 | assertedRange()->print(sp); |
michael@0 | 623 | fprintf(fp, " %s", sp.string()); |
michael@0 | 624 | } |
michael@0 | 625 | |
michael@0 | 626 | const char * |
michael@0 | 627 | MMathFunction::FunctionName(Function function) |
michael@0 | 628 | { |
michael@0 | 629 | switch (function) { |
michael@0 | 630 | case Log: return "Log"; |
michael@0 | 631 | case Sin: return "Sin"; |
michael@0 | 632 | case Cos: return "Cos"; |
michael@0 | 633 | case Exp: return "Exp"; |
michael@0 | 634 | case Tan: return "Tan"; |
michael@0 | 635 | case ACos: return "ACos"; |
michael@0 | 636 | case ASin: return "ASin"; |
michael@0 | 637 | case ATan: return "ATan"; |
michael@0 | 638 | case Log10: return "Log10"; |
michael@0 | 639 | case Log2: return "Log2"; |
michael@0 | 640 | case Log1P: return "Log1P"; |
michael@0 | 641 | case ExpM1: return "ExpM1"; |
michael@0 | 642 | case CosH: return "CosH"; |
michael@0 | 643 | case SinH: return "SinH"; |
michael@0 | 644 | case TanH: return "TanH"; |
michael@0 | 645 | case ACosH: return "ACosH"; |
michael@0 | 646 | case ASinH: return "ASinH"; |
michael@0 | 647 | case ATanH: return "ATanH"; |
michael@0 | 648 | case Sign: return "Sign"; |
michael@0 | 649 | case Trunc: return "Trunc"; |
michael@0 | 650 | case Cbrt: return "Cbrt"; |
michael@0 | 651 | case Floor: return "Floor"; |
michael@0 | 652 | case Ceil: return "Ceil"; |
michael@0 | 653 | case Round: return "Round"; |
michael@0 | 654 | default: |
michael@0 | 655 | MOZ_ASSUME_UNREACHABLE("Unknown math function"); |
michael@0 | 656 | } |
michael@0 | 657 | } |
michael@0 | 658 | |
michael@0 | 659 | void |
michael@0 | 660 | MMathFunction::printOpcode(FILE *fp) const |
michael@0 | 661 | { |
michael@0 | 662 | MDefinition::printOpcode(fp); |
michael@0 | 663 | fprintf(fp, " %s", FunctionName(function())); |
michael@0 | 664 | } |
michael@0 | 665 | |
michael@0 | 666 | MParameter * |
michael@0 | 667 | MParameter::New(TempAllocator &alloc, int32_t index, types::TemporaryTypeSet *types) |
michael@0 | 668 | { |
michael@0 | 669 | return new(alloc) MParameter(index, types); |
michael@0 | 670 | } |
michael@0 | 671 | |
michael@0 | 672 | void |
michael@0 | 673 | MParameter::printOpcode(FILE *fp) const |
michael@0 | 674 | { |
michael@0 | 675 | PrintOpcodeName(fp, op()); |
michael@0 | 676 | fprintf(fp, " %d", index()); |
michael@0 | 677 | } |
michael@0 | 678 | |
michael@0 | 679 | HashNumber |
michael@0 | 680 | MParameter::valueHash() const |
michael@0 | 681 | { |
michael@0 | 682 | return index_; // Why not? |
michael@0 | 683 | } |
michael@0 | 684 | |
michael@0 | 685 | bool |
michael@0 | 686 | MParameter::congruentTo(const MDefinition *ins) const |
michael@0 | 687 | { |
michael@0 | 688 | if (!ins->isParameter()) |
michael@0 | 689 | return false; |
michael@0 | 690 | |
michael@0 | 691 | return ins->toParameter()->index() == index_; |
michael@0 | 692 | } |
michael@0 | 693 | |
michael@0 | 694 | MCall * |
michael@0 | 695 | MCall::New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, size_t numActualArgs, |
michael@0 | 696 | bool construct, bool isDOMCall) |
michael@0 | 697 | { |
michael@0 | 698 | JS_ASSERT(maxArgc >= numActualArgs); |
michael@0 | 699 | MCall *ins; |
michael@0 | 700 | if (isDOMCall) { |
michael@0 | 701 | JS_ASSERT(!construct); |
michael@0 | 702 | ins = new(alloc) MCallDOMNative(target, numActualArgs); |
michael@0 | 703 | } else { |
michael@0 | 704 | ins = new(alloc) MCall(target, numActualArgs, construct); |
michael@0 | 705 | } |
michael@0 | 706 | if (!ins->init(alloc, maxArgc + NumNonArgumentOperands)) |
michael@0 | 707 | return nullptr; |
michael@0 | 708 | return ins; |
michael@0 | 709 | } |
michael@0 | 710 | |
michael@0 | 711 | AliasSet |
michael@0 | 712 | MCallDOMNative::getAliasSet() const |
michael@0 | 713 | { |
michael@0 | 714 | const JSJitInfo *jitInfo = getJitInfo(); |
michael@0 | 715 | |
michael@0 | 716 | JS_ASSERT(jitInfo->aliasSet() != JSJitInfo::AliasNone); |
michael@0 | 717 | // If we don't know anything about the types of our arguments, we have to |
michael@0 | 718 | // assume that type-coercions can have side-effects, so we need to alias |
michael@0 | 719 | // everything. |
michael@0 | 720 | if (jitInfo->aliasSet() != JSJitInfo::AliasDOMSets || !jitInfo->isTypedMethodJitInfo()) |
michael@0 | 721 | return AliasSet::Store(AliasSet::Any); |
michael@0 | 722 | |
michael@0 | 723 | uint32_t argIndex = 0; |
michael@0 | 724 | const JSTypedMethodJitInfo *methodInfo = |
michael@0 | 725 | reinterpret_cast<const JSTypedMethodJitInfo*>(jitInfo); |
michael@0 | 726 | for (const JSJitInfo::ArgType *argType = methodInfo->argTypes; |
michael@0 | 727 | *argType != JSJitInfo::ArgTypeListEnd; |
michael@0 | 728 | ++argType, ++argIndex) |
michael@0 | 729 | { |
michael@0 | 730 | if (argIndex >= numActualArgs()) { |
michael@0 | 731 | // Passing through undefined can't have side-effects |
michael@0 | 732 | continue; |
michael@0 | 733 | } |
michael@0 | 734 | // getArg(0) is "this", so skip it |
michael@0 | 735 | MDefinition *arg = getArg(argIndex+1); |
michael@0 | 736 | MIRType actualType = arg->type(); |
michael@0 | 737 | // The only way to reliably avoid side-effects given the informtion we |
michael@0 | 738 | // have here is if we're passing in a known primitive value to an |
michael@0 | 739 | // argument that expects a primitive value. XXXbz maybe we need to |
michael@0 | 740 | // communicate better information. For example, a sequence argument |
michael@0 | 741 | // will sort of unavoidably have side effects, while a typed array |
michael@0 | 742 | // argument won't have any, but both are claimed to be |
michael@0 | 743 | // JSJitInfo::Object. |
michael@0 | 744 | if ((actualType == MIRType_Value || actualType == MIRType_Object) || |
michael@0 | 745 | (*argType & JSJitInfo::Object)) |
michael@0 | 746 | { |
michael@0 | 747 | return AliasSet::Store(AliasSet::Any); |
michael@0 | 748 | } |
michael@0 | 749 | } |
michael@0 | 750 | |
michael@0 | 751 | // We checked all the args, and they check out. So we only |
michael@0 | 752 | // alias DOM mutations. |
michael@0 | 753 | return AliasSet::Load(AliasSet::DOMProperty); |
michael@0 | 754 | } |
michael@0 | 755 | |
michael@0 | 756 | void |
michael@0 | 757 | MCallDOMNative::computeMovable() |
michael@0 | 758 | { |
michael@0 | 759 | // We are movable if the jitinfo says we can be and if we're also not |
michael@0 | 760 | // effectful. The jitinfo can't check for the latter, since it depends on |
michael@0 | 761 | // the types of our arguments. |
michael@0 | 762 | const JSJitInfo *jitInfo = getJitInfo(); |
michael@0 | 763 | |
michael@0 | 764 | JS_ASSERT_IF(jitInfo->isMovable, |
michael@0 | 765 | jitInfo->aliasSet() != JSJitInfo::AliasEverything); |
michael@0 | 766 | |
michael@0 | 767 | if (jitInfo->isMovable && !isEffectful()) |
michael@0 | 768 | setMovable(); |
michael@0 | 769 | } |
michael@0 | 770 | |
michael@0 | 771 | bool |
michael@0 | 772 | MCallDOMNative::congruentTo(const MDefinition *ins) const |
michael@0 | 773 | { |
michael@0 | 774 | if (!isMovable()) |
michael@0 | 775 | return false; |
michael@0 | 776 | |
michael@0 | 777 | if (!ins->isCall()) |
michael@0 | 778 | return false; |
michael@0 | 779 | |
michael@0 | 780 | const MCall *call = ins->toCall(); |
michael@0 | 781 | |
michael@0 | 782 | if (!call->isCallDOMNative()) |
michael@0 | 783 | return false; |
michael@0 | 784 | |
michael@0 | 785 | if (getSingleTarget() != call->getSingleTarget()) |
michael@0 | 786 | return false; |
michael@0 | 787 | |
michael@0 | 788 | if (isConstructing() != call->isConstructing()) |
michael@0 | 789 | return false; |
michael@0 | 790 | |
michael@0 | 791 | if (numActualArgs() != call->numActualArgs()) |
michael@0 | 792 | return false; |
michael@0 | 793 | |
michael@0 | 794 | if (needsArgCheck() != call->needsArgCheck()) |
michael@0 | 795 | return false; |
michael@0 | 796 | |
michael@0 | 797 | if (!congruentIfOperandsEqual(call)) |
michael@0 | 798 | return false; |
michael@0 | 799 | |
michael@0 | 800 | // The other call had better be movable at this point! |
michael@0 | 801 | JS_ASSERT(call->isMovable()); |
michael@0 | 802 | |
michael@0 | 803 | return true; |
michael@0 | 804 | } |
michael@0 | 805 | |
michael@0 | 806 | const JSJitInfo * |
michael@0 | 807 | MCallDOMNative::getJitInfo() const |
michael@0 | 808 | { |
michael@0 | 809 | JS_ASSERT(getSingleTarget() && getSingleTarget()->isNative()); |
michael@0 | 810 | |
michael@0 | 811 | const JSJitInfo *jitInfo = getSingleTarget()->jitInfo(); |
michael@0 | 812 | JS_ASSERT(jitInfo); |
michael@0 | 813 | |
michael@0 | 814 | return jitInfo; |
michael@0 | 815 | } |
michael@0 | 816 | |
michael@0 | 817 | MApplyArgs * |
michael@0 | 818 | MApplyArgs::New(TempAllocator &alloc, JSFunction *target, MDefinition *fun, MDefinition *argc, |
michael@0 | 819 | MDefinition *self) |
michael@0 | 820 | { |
michael@0 | 821 | return new(alloc) MApplyArgs(target, fun, argc, self); |
michael@0 | 822 | } |
michael@0 | 823 | |
michael@0 | 824 | MDefinition* |
michael@0 | 825 | MStringLength::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 826 | { |
michael@0 | 827 | if ((type() == MIRType_Int32) && (string()->isConstant())) { |
michael@0 | 828 | Value value = string()->toConstant()->value(); |
michael@0 | 829 | JSAtom *atom = &value.toString()->asAtom(); |
michael@0 | 830 | return MConstant::New(alloc, Int32Value(atom->length())); |
michael@0 | 831 | } |
michael@0 | 832 | |
michael@0 | 833 | return this; |
michael@0 | 834 | } |
michael@0 | 835 | |
michael@0 | 836 | void |
michael@0 | 837 | MFloor::trySpecializeFloat32(TempAllocator &alloc) |
michael@0 | 838 | { |
michael@0 | 839 | // No need to look at the output, as it's an integer (see IonBuilder::inlineMathFloor) |
michael@0 | 840 | if (!input()->canProduceFloat32()) { |
michael@0 | 841 | if (input()->type() == MIRType_Float32) |
michael@0 | 842 | ConvertDefinitionToDouble<0>(alloc, input(), this); |
michael@0 | 843 | return; |
michael@0 | 844 | } |
michael@0 | 845 | |
michael@0 | 846 | if (type() == MIRType_Double) |
michael@0 | 847 | setResultType(MIRType_Float32); |
michael@0 | 848 | |
michael@0 | 849 | setPolicyType(MIRType_Float32); |
michael@0 | 850 | } |
michael@0 | 851 | |
michael@0 | 852 | void |
michael@0 | 853 | MRound::trySpecializeFloat32(TempAllocator &alloc) |
michael@0 | 854 | { |
michael@0 | 855 | // No need to look at the output, as it's an integer (unique way to have |
michael@0 | 856 | // this instruction in IonBuilder::inlineMathRound) |
michael@0 | 857 | JS_ASSERT(type() == MIRType_Int32); |
michael@0 | 858 | |
michael@0 | 859 | if (!input()->canProduceFloat32()) { |
michael@0 | 860 | if (input()->type() == MIRType_Float32) |
michael@0 | 861 | ConvertDefinitionToDouble<0>(alloc, input(), this); |
michael@0 | 862 | return; |
michael@0 | 863 | } |
michael@0 | 864 | |
michael@0 | 865 | setPolicyType(MIRType_Float32); |
michael@0 | 866 | } |
michael@0 | 867 | |
michael@0 | 868 | MCompare * |
michael@0 | 869 | MCompare::New(TempAllocator &alloc, MDefinition *left, MDefinition *right, JSOp op) |
michael@0 | 870 | { |
michael@0 | 871 | return new(alloc) MCompare(left, right, op); |
michael@0 | 872 | } |
michael@0 | 873 | |
michael@0 | 874 | MCompare * |
michael@0 | 875 | MCompare::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, JSOp op, |
michael@0 | 876 | CompareType compareType) |
michael@0 | 877 | { |
michael@0 | 878 | JS_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 || |
michael@0 | 879 | compareType == Compare_Double || compareType == Compare_Float32); |
michael@0 | 880 | MCompare *comp = new(alloc) MCompare(left, right, op); |
michael@0 | 881 | comp->compareType_ = compareType; |
michael@0 | 882 | comp->operandMightEmulateUndefined_ = false; |
michael@0 | 883 | comp->setResultType(MIRType_Int32); |
michael@0 | 884 | return comp; |
michael@0 | 885 | } |
michael@0 | 886 | |
michael@0 | 887 | MTableSwitch * |
michael@0 | 888 | MTableSwitch::New(TempAllocator &alloc, MDefinition *ins, int32_t low, int32_t high) |
michael@0 | 889 | { |
michael@0 | 890 | return new(alloc) MTableSwitch(alloc, ins, low, high); |
michael@0 | 891 | } |
michael@0 | 892 | |
michael@0 | 893 | MGoto * |
michael@0 | 894 | MGoto::New(TempAllocator &alloc, MBasicBlock *target) |
michael@0 | 895 | { |
michael@0 | 896 | JS_ASSERT(target); |
michael@0 | 897 | return new(alloc) MGoto(target); |
michael@0 | 898 | } |
michael@0 | 899 | |
michael@0 | 900 | void |
michael@0 | 901 | MUnbox::printOpcode(FILE *fp) const |
michael@0 | 902 | { |
michael@0 | 903 | PrintOpcodeName(fp, op()); |
michael@0 | 904 | fprintf(fp, " "); |
michael@0 | 905 | getOperand(0)->printName(fp); |
michael@0 | 906 | fprintf(fp, " "); |
michael@0 | 907 | |
michael@0 | 908 | switch (type()) { |
michael@0 | 909 | case MIRType_Int32: fprintf(fp, "to Int32"); break; |
michael@0 | 910 | case MIRType_Double: fprintf(fp, "to Double"); break; |
michael@0 | 911 | case MIRType_Boolean: fprintf(fp, "to Boolean"); break; |
michael@0 | 912 | case MIRType_String: fprintf(fp, "to String"); break; |
michael@0 | 913 | case MIRType_Object: fprintf(fp, "to Object"); break; |
michael@0 | 914 | default: break; |
michael@0 | 915 | } |
michael@0 | 916 | |
michael@0 | 917 | switch (mode()) { |
michael@0 | 918 | case Fallible: fprintf(fp, " (fallible)"); break; |
michael@0 | 919 | case Infallible: fprintf(fp, " (infallible)"); break; |
michael@0 | 920 | case TypeBarrier: fprintf(fp, " (typebarrier)"); break; |
michael@0 | 921 | default: break; |
michael@0 | 922 | } |
michael@0 | 923 | } |
michael@0 | 924 | |
michael@0 | 925 | void |
michael@0 | 926 | MTypeBarrier::printOpcode(FILE *fp) const |
michael@0 | 927 | { |
michael@0 | 928 | PrintOpcodeName(fp, op()); |
michael@0 | 929 | fprintf(fp, " "); |
michael@0 | 930 | getOperand(0)->printName(fp); |
michael@0 | 931 | } |
michael@0 | 932 | |
michael@0 | 933 | void |
michael@0 | 934 | MPhi::removeOperand(size_t index) |
michael@0 | 935 | { |
michael@0 | 936 | MUse *use = getUseFor(index); |
michael@0 | 937 | |
michael@0 | 938 | JS_ASSERT(index < inputs_.length()); |
michael@0 | 939 | JS_ASSERT(inputs_.length() > 1); |
michael@0 | 940 | |
michael@0 | 941 | JS_ASSERT(use->index() == index); |
michael@0 | 942 | JS_ASSERT(use->producer() == getOperand(index)); |
michael@0 | 943 | JS_ASSERT(use->consumer() == this); |
michael@0 | 944 | |
michael@0 | 945 | // Remove use from producer's use chain. |
michael@0 | 946 | use->producer()->removeUse(use); |
michael@0 | 947 | |
michael@0 | 948 | // If we have phi(..., a, b, c, d, ..., z) and we plan |
michael@0 | 949 | // on removing a, then first shift downward so that we have |
michael@0 | 950 | // phi(..., b, c, d, ..., z, z): |
michael@0 | 951 | size_t length = inputs_.length(); |
michael@0 | 952 | for (size_t i = index; i < length - 1; i++) { |
michael@0 | 953 | MUse *next = MPhi::getUseFor(i + 1); |
michael@0 | 954 | next->producer()->removeUse(next); |
michael@0 | 955 | MPhi::setOperand(i, next->producer()); |
michael@0 | 956 | } |
michael@0 | 957 | |
michael@0 | 958 | // truncate the inputs_ list: |
michael@0 | 959 | inputs_.shrinkBy(1); |
michael@0 | 960 | } |
michael@0 | 961 | |
michael@0 | 962 | MDefinition * |
michael@0 | 963 | MPhi::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 964 | { |
michael@0 | 965 | JS_ASSERT(!inputs_.empty()); |
michael@0 | 966 | |
michael@0 | 967 | MDefinition *first = getOperand(0); |
michael@0 | 968 | |
michael@0 | 969 | for (size_t i = 1; i < inputs_.length(); i++) { |
michael@0 | 970 | // Phis need dominator information to fold based on value numbers. For |
michael@0 | 971 | // simplicity, we only compare SSA names right now (bug 714727). |
michael@0 | 972 | if (!EqualValues(false, getOperand(i), first)) |
michael@0 | 973 | return this; |
michael@0 | 974 | } |
michael@0 | 975 | |
michael@0 | 976 | return first; |
michael@0 | 977 | } |
michael@0 | 978 | |
michael@0 | 979 | bool |
michael@0 | 980 | MPhi::congruentTo(const MDefinition *ins) const |
michael@0 | 981 | { |
michael@0 | 982 | if (!ins->isPhi()) |
michael@0 | 983 | return false; |
michael@0 | 984 | // Since we do not know which predecessor we are merging from, we must |
michael@0 | 985 | // assume that phi instructions in different blocks are not equal. |
michael@0 | 986 | // (Bug 674656) |
michael@0 | 987 | if (ins->block()->id() != block()->id()) |
michael@0 | 988 | return false; |
michael@0 | 989 | |
michael@0 | 990 | return congruentIfOperandsEqual(ins); |
michael@0 | 991 | } |
michael@0 | 992 | |
michael@0 | 993 | bool |
michael@0 | 994 | MPhi::reserveLength(size_t length) |
michael@0 | 995 | { |
michael@0 | 996 | // Initializes a new MPhi to have an Operand vector of at least the given |
michael@0 | 997 | // capacity. This permits use of addInput() instead of addInputSlow(), the |
michael@0 | 998 | // latter of which may call realloc_(). |
michael@0 | 999 | JS_ASSERT(numOperands() == 0); |
michael@0 | 1000 | #if DEBUG |
michael@0 | 1001 | capacity_ = length; |
michael@0 | 1002 | #endif |
michael@0 | 1003 | return inputs_.reserve(length); |
michael@0 | 1004 | } |
michael@0 | 1005 | |
michael@0 | 1006 | static inline types::TemporaryTypeSet * |
michael@0 | 1007 | MakeMIRTypeSet(MIRType type) |
michael@0 | 1008 | { |
michael@0 | 1009 | JS_ASSERT(type != MIRType_Value); |
michael@0 | 1010 | types::Type ntype = type == MIRType_Object |
michael@0 | 1011 | ? types::Type::AnyObjectType() |
michael@0 | 1012 | : types::Type::PrimitiveType(ValueTypeFromMIRType(type)); |
michael@0 | 1013 | return GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype); |
michael@0 | 1014 | } |
michael@0 | 1015 | |
michael@0 | 1016 | bool |
michael@0 | 1017 | jit::MergeTypes(MIRType *ptype, types::TemporaryTypeSet **ptypeSet, |
michael@0 | 1018 | MIRType newType, types::TemporaryTypeSet *newTypeSet) |
michael@0 | 1019 | { |
michael@0 | 1020 | if (newTypeSet && newTypeSet->empty()) |
michael@0 | 1021 | return true; |
michael@0 | 1022 | if (newType != *ptype) { |
michael@0 | 1023 | if (IsNumberType(newType) && IsNumberType(*ptype)) { |
michael@0 | 1024 | *ptype = MIRType_Double; |
michael@0 | 1025 | } else if (*ptype != MIRType_Value) { |
michael@0 | 1026 | if (!*ptypeSet) { |
michael@0 | 1027 | *ptypeSet = MakeMIRTypeSet(*ptype); |
michael@0 | 1028 | if (!*ptypeSet) |
michael@0 | 1029 | return false; |
michael@0 | 1030 | } |
michael@0 | 1031 | *ptype = MIRType_Value; |
michael@0 | 1032 | } else if (*ptypeSet && (*ptypeSet)->empty()) { |
michael@0 | 1033 | *ptype = newType; |
michael@0 | 1034 | } |
michael@0 | 1035 | } |
michael@0 | 1036 | if (*ptypeSet) { |
michael@0 | 1037 | LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); |
michael@0 | 1038 | if (!newTypeSet && newType != MIRType_Value) { |
michael@0 | 1039 | newTypeSet = MakeMIRTypeSet(newType); |
michael@0 | 1040 | if (!newTypeSet) |
michael@0 | 1041 | return false; |
michael@0 | 1042 | } |
michael@0 | 1043 | if (newTypeSet) { |
michael@0 | 1044 | if (!newTypeSet->isSubset(*ptypeSet)) |
michael@0 | 1045 | *ptypeSet = types::TypeSet::unionSets(*ptypeSet, newTypeSet, alloc); |
michael@0 | 1046 | } else { |
michael@0 | 1047 | *ptypeSet = nullptr; |
michael@0 | 1048 | } |
michael@0 | 1049 | } |
michael@0 | 1050 | return true; |
michael@0 | 1051 | } |
michael@0 | 1052 | |
michael@0 | 1053 | bool |
michael@0 | 1054 | MPhi::specializeType() |
michael@0 | 1055 | { |
michael@0 | 1056 | #ifdef DEBUG |
michael@0 | 1057 | JS_ASSERT(!specialized_); |
michael@0 | 1058 | specialized_ = true; |
michael@0 | 1059 | #endif |
michael@0 | 1060 | |
michael@0 | 1061 | JS_ASSERT(!inputs_.empty()); |
michael@0 | 1062 | |
michael@0 | 1063 | size_t start; |
michael@0 | 1064 | if (hasBackedgeType_) { |
michael@0 | 1065 | // The type of this phi has already been populated with potential types |
michael@0 | 1066 | // that could come in via loop backedges. |
michael@0 | 1067 | start = 0; |
michael@0 | 1068 | } else { |
michael@0 | 1069 | setResultType(getOperand(0)->type()); |
michael@0 | 1070 | setResultTypeSet(getOperand(0)->resultTypeSet()); |
michael@0 | 1071 | start = 1; |
michael@0 | 1072 | } |
michael@0 | 1073 | |
michael@0 | 1074 | MIRType resultType = this->type(); |
michael@0 | 1075 | types::TemporaryTypeSet *resultTypeSet = this->resultTypeSet(); |
michael@0 | 1076 | |
michael@0 | 1077 | for (size_t i = start; i < inputs_.length(); i++) { |
michael@0 | 1078 | MDefinition *def = getOperand(i); |
michael@0 | 1079 | if (!MergeTypes(&resultType, &resultTypeSet, def->type(), def->resultTypeSet())) |
michael@0 | 1080 | return false; |
michael@0 | 1081 | } |
michael@0 | 1082 | |
michael@0 | 1083 | setResultType(resultType); |
michael@0 | 1084 | setResultTypeSet(resultTypeSet); |
michael@0 | 1085 | return true; |
michael@0 | 1086 | } |
michael@0 | 1087 | |
michael@0 | 1088 | bool |
michael@0 | 1089 | MPhi::addBackedgeType(MIRType type, types::TemporaryTypeSet *typeSet) |
michael@0 | 1090 | { |
michael@0 | 1091 | JS_ASSERT(!specialized_); |
michael@0 | 1092 | |
michael@0 | 1093 | if (hasBackedgeType_) { |
michael@0 | 1094 | MIRType resultType = this->type(); |
michael@0 | 1095 | types::TemporaryTypeSet *resultTypeSet = this->resultTypeSet(); |
michael@0 | 1096 | |
michael@0 | 1097 | if (!MergeTypes(&resultType, &resultTypeSet, type, typeSet)) |
michael@0 | 1098 | return false; |
michael@0 | 1099 | |
michael@0 | 1100 | setResultType(resultType); |
michael@0 | 1101 | setResultTypeSet(resultTypeSet); |
michael@0 | 1102 | } else { |
michael@0 | 1103 | setResultType(type); |
michael@0 | 1104 | setResultTypeSet(typeSet); |
michael@0 | 1105 | hasBackedgeType_ = true; |
michael@0 | 1106 | } |
michael@0 | 1107 | return true; |
michael@0 | 1108 | } |
michael@0 | 1109 | |
michael@0 | 1110 | bool |
michael@0 | 1111 | MPhi::typeIncludes(MDefinition *def) |
michael@0 | 1112 | { |
michael@0 | 1113 | if (def->type() == MIRType_Int32 && this->type() == MIRType_Double) |
michael@0 | 1114 | return true; |
michael@0 | 1115 | |
michael@0 | 1116 | if (types::TemporaryTypeSet *types = def->resultTypeSet()) { |
michael@0 | 1117 | if (this->resultTypeSet()) |
michael@0 | 1118 | return types->isSubset(this->resultTypeSet()); |
michael@0 | 1119 | if (this->type() == MIRType_Value || types->empty()) |
michael@0 | 1120 | return true; |
michael@0 | 1121 | return this->type() == types->getKnownMIRType(); |
michael@0 | 1122 | } |
michael@0 | 1123 | |
michael@0 | 1124 | if (def->type() == MIRType_Value) { |
michael@0 | 1125 | // This phi must be able to be any value. |
michael@0 | 1126 | return this->type() == MIRType_Value |
michael@0 | 1127 | && (!this->resultTypeSet() || this->resultTypeSet()->unknown()); |
michael@0 | 1128 | } |
michael@0 | 1129 | |
michael@0 | 1130 | return this->mightBeType(def->type()); |
michael@0 | 1131 | } |
michael@0 | 1132 | |
michael@0 | 1133 | void |
michael@0 | 1134 | MPhi::addInput(MDefinition *ins) |
michael@0 | 1135 | { |
michael@0 | 1136 | // This can only been done if the length was reserved through reserveLength, |
michael@0 | 1137 | // else the slower addInputSlow need to get called. |
michael@0 | 1138 | JS_ASSERT(inputs_.length() < capacity_); |
michael@0 | 1139 | |
michael@0 | 1140 | uint32_t index = inputs_.length(); |
michael@0 | 1141 | inputs_.append(MUse()); |
michael@0 | 1142 | MPhi::setOperand(index, ins); |
michael@0 | 1143 | } |
michael@0 | 1144 | |
michael@0 | 1145 | bool |
michael@0 | 1146 | MPhi::addInputSlow(MDefinition *ins, bool *ptypeChange) |
michael@0 | 1147 | { |
michael@0 | 1148 | // The list of inputs to an MPhi is given as a vector of MUse nodes, |
michael@0 | 1149 | // each of which is in the list of the producer MDefinition. |
michael@0 | 1150 | // Because appending to a vector may reallocate the vector, it is possible |
michael@0 | 1151 | // that this operation may cause the producers' linked lists to reference |
michael@0 | 1152 | // invalid memory. Therefore, in the event of moving reallocation, each |
michael@0 | 1153 | // MUse must be removed and reinserted from/into its producer's use chain. |
michael@0 | 1154 | uint32_t index = inputs_.length(); |
michael@0 | 1155 | bool performingRealloc = !inputs_.canAppendWithoutRealloc(1); |
michael@0 | 1156 | |
michael@0 | 1157 | // Remove all MUses from all use lists, in case realloc_() moves. |
michael@0 | 1158 | if (performingRealloc) { |
michael@0 | 1159 | for (uint32_t i = 0; i < index; i++) { |
michael@0 | 1160 | MUse *use = &inputs_[i]; |
michael@0 | 1161 | use->producer()->removeUse(use); |
michael@0 | 1162 | } |
michael@0 | 1163 | } |
michael@0 | 1164 | |
michael@0 | 1165 | // Insert the new input. |
michael@0 | 1166 | if (!inputs_.append(MUse())) |
michael@0 | 1167 | return false; |
michael@0 | 1168 | |
michael@0 | 1169 | MPhi::setOperand(index, ins); |
michael@0 | 1170 | |
michael@0 | 1171 | if (ptypeChange) { |
michael@0 | 1172 | MIRType resultType = this->type(); |
michael@0 | 1173 | types::TemporaryTypeSet *resultTypeSet = this->resultTypeSet(); |
michael@0 | 1174 | |
michael@0 | 1175 | if (!MergeTypes(&resultType, &resultTypeSet, ins->type(), ins->resultTypeSet())) |
michael@0 | 1176 | return false; |
michael@0 | 1177 | |
michael@0 | 1178 | if (resultType != this->type() || resultTypeSet != this->resultTypeSet()) { |
michael@0 | 1179 | *ptypeChange = true; |
michael@0 | 1180 | setResultType(resultType); |
michael@0 | 1181 | setResultTypeSet(resultTypeSet); |
michael@0 | 1182 | } |
michael@0 | 1183 | } |
michael@0 | 1184 | |
michael@0 | 1185 | // Add all previously-removed MUses back. |
michael@0 | 1186 | if (performingRealloc) { |
michael@0 | 1187 | for (uint32_t i = 0; i < index; i++) { |
michael@0 | 1188 | MUse *use = &inputs_[i]; |
michael@0 | 1189 | use->producer()->addUse(use); |
michael@0 | 1190 | } |
michael@0 | 1191 | } |
michael@0 | 1192 | |
michael@0 | 1193 | return true; |
michael@0 | 1194 | } |
michael@0 | 1195 | |
michael@0 | 1196 | void |
michael@0 | 1197 | MCall::addArg(size_t argnum, MDefinition *arg) |
michael@0 | 1198 | { |
michael@0 | 1199 | // The operand vector is initialized in reverse order by the IonBuilder. |
michael@0 | 1200 | // It cannot be checked for consistency until all arguments are added. |
michael@0 | 1201 | setOperand(argnum + NumNonArgumentOperands, arg); |
michael@0 | 1202 | } |
michael@0 | 1203 | |
michael@0 | 1204 | void |
michael@0 | 1205 | MBitNot::infer() |
michael@0 | 1206 | { |
michael@0 | 1207 | if (getOperand(0)->mightBeType(MIRType_Object)) |
michael@0 | 1208 | specialization_ = MIRType_None; |
michael@0 | 1209 | else |
michael@0 | 1210 | specialization_ = MIRType_Int32; |
michael@0 | 1211 | } |
michael@0 | 1212 | |
michael@0 | 1213 | static inline bool |
michael@0 | 1214 | IsConstant(MDefinition *def, double v) |
michael@0 | 1215 | { |
michael@0 | 1216 | if (!def->isConstant()) |
michael@0 | 1217 | return false; |
michael@0 | 1218 | |
michael@0 | 1219 | return NumbersAreIdentical(def->toConstant()->value().toNumber(), v); |
michael@0 | 1220 | } |
michael@0 | 1221 | |
michael@0 | 1222 | MDefinition * |
michael@0 | 1223 | MBinaryBitwiseInstruction::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 1224 | { |
michael@0 | 1225 | if (specialization_ != MIRType_Int32) |
michael@0 | 1226 | return this; |
michael@0 | 1227 | |
michael@0 | 1228 | if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) |
michael@0 | 1229 | return folded; |
michael@0 | 1230 | |
michael@0 | 1231 | return this; |
michael@0 | 1232 | } |
michael@0 | 1233 | |
michael@0 | 1234 | MDefinition * |
michael@0 | 1235 | MBinaryBitwiseInstruction::foldUnnecessaryBitop() |
michael@0 | 1236 | { |
michael@0 | 1237 | if (specialization_ != MIRType_Int32) |
michael@0 | 1238 | return this; |
michael@0 | 1239 | |
michael@0 | 1240 | // Eliminate bitwise operations that are no-ops when used on integer |
michael@0 | 1241 | // inputs, such as (x | 0). |
michael@0 | 1242 | |
michael@0 | 1243 | MDefinition *lhs = getOperand(0); |
michael@0 | 1244 | MDefinition *rhs = getOperand(1); |
michael@0 | 1245 | |
michael@0 | 1246 | if (IsConstant(lhs, 0)) |
michael@0 | 1247 | return foldIfZero(0); |
michael@0 | 1248 | |
michael@0 | 1249 | if (IsConstant(rhs, 0)) |
michael@0 | 1250 | return foldIfZero(1); |
michael@0 | 1251 | |
michael@0 | 1252 | if (IsConstant(lhs, -1)) |
michael@0 | 1253 | return foldIfNegOne(0); |
michael@0 | 1254 | |
michael@0 | 1255 | if (IsConstant(rhs, -1)) |
michael@0 | 1256 | return foldIfNegOne(1); |
michael@0 | 1257 | |
michael@0 | 1258 | if (EqualValues(false, lhs, rhs)) |
michael@0 | 1259 | return foldIfEqual(); |
michael@0 | 1260 | |
michael@0 | 1261 | return this; |
michael@0 | 1262 | } |
michael@0 | 1263 | |
michael@0 | 1264 | void |
michael@0 | 1265 | MBinaryBitwiseInstruction::infer(BaselineInspector *, jsbytecode *) |
michael@0 | 1266 | { |
michael@0 | 1267 | if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) |
michael@0 | 1268 | specialization_ = MIRType_None; |
michael@0 | 1269 | else |
michael@0 | 1270 | specializeAsInt32(); |
michael@0 | 1271 | } |
michael@0 | 1272 | |
michael@0 | 1273 | void |
michael@0 | 1274 | MBinaryBitwiseInstruction::specializeAsInt32() |
michael@0 | 1275 | { |
michael@0 | 1276 | specialization_ = MIRType_Int32; |
michael@0 | 1277 | JS_ASSERT(type() == MIRType_Int32); |
michael@0 | 1278 | |
michael@0 | 1279 | if (isBitOr() || isBitAnd() || isBitXor()) |
michael@0 | 1280 | setCommutative(); |
michael@0 | 1281 | } |
michael@0 | 1282 | |
michael@0 | 1283 | void |
michael@0 | 1284 | MShiftInstruction::infer(BaselineInspector *, jsbytecode *) |
michael@0 | 1285 | { |
michael@0 | 1286 | if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) |
michael@0 | 1287 | specialization_ = MIRType_None; |
michael@0 | 1288 | else |
michael@0 | 1289 | specialization_ = MIRType_Int32; |
michael@0 | 1290 | } |
michael@0 | 1291 | |
michael@0 | 1292 | void |
michael@0 | 1293 | MUrsh::infer(BaselineInspector *inspector, jsbytecode *pc) |
michael@0 | 1294 | { |
michael@0 | 1295 | if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) { |
michael@0 | 1296 | specialization_ = MIRType_None; |
michael@0 | 1297 | setResultType(MIRType_Value); |
michael@0 | 1298 | return; |
michael@0 | 1299 | } |
michael@0 | 1300 | |
michael@0 | 1301 | if (inspector->hasSeenDoubleResult(pc)) { |
michael@0 | 1302 | specialization_ = MIRType_Double; |
michael@0 | 1303 | setResultType(MIRType_Double); |
michael@0 | 1304 | return; |
michael@0 | 1305 | } |
michael@0 | 1306 | |
michael@0 | 1307 | specialization_ = MIRType_Int32; |
michael@0 | 1308 | setResultType(MIRType_Int32); |
michael@0 | 1309 | } |
michael@0 | 1310 | |
michael@0 | 1311 | static inline bool |
michael@0 | 1312 | NeedNegativeZeroCheck(MDefinition *def) |
michael@0 | 1313 | { |
michael@0 | 1314 | // Test if all uses have the same semantics for -0 and 0 |
michael@0 | 1315 | for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) { |
michael@0 | 1316 | if (use->consumer()->isResumePoint()) |
michael@0 | 1317 | continue; |
michael@0 | 1318 | |
michael@0 | 1319 | MDefinition *use_def = use->consumer()->toDefinition(); |
michael@0 | 1320 | switch (use_def->op()) { |
michael@0 | 1321 | case MDefinition::Op_Add: { |
michael@0 | 1322 | // If add is truncating -0 and 0 are observed as the same. |
michael@0 | 1323 | if (use_def->toAdd()->isTruncated()) |
michael@0 | 1324 | break; |
michael@0 | 1325 | |
michael@0 | 1326 | // x + y gives -0, when both x and y are -0 |
michael@0 | 1327 | |
michael@0 | 1328 | // Figure out the order in which the addition's operands will |
michael@0 | 1329 | // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR |
michael@0 | 1330 | // definitions for us so that this just requires comparing ids. |
michael@0 | 1331 | MDefinition *first = use_def->toAdd()->getOperand(0); |
michael@0 | 1332 | MDefinition *second = use_def->toAdd()->getOperand(1); |
michael@0 | 1333 | if (first->id() > second->id()) { |
michael@0 | 1334 | MDefinition *temp = first; |
michael@0 | 1335 | first = second; |
michael@0 | 1336 | second = temp; |
michael@0 | 1337 | } |
michael@0 | 1338 | |
michael@0 | 1339 | if (def == first) { |
michael@0 | 1340 | // Negative zero checks can be removed on the first executed |
michael@0 | 1341 | // operand only if it is guaranteed the second executed operand |
michael@0 | 1342 | // will produce a value other than -0. While the second is |
michael@0 | 1343 | // typed as an int32, a bailout taken between execution of the |
michael@0 | 1344 | // operands may change that type and cause a -0 to flow to the |
michael@0 | 1345 | // second. |
michael@0 | 1346 | // |
michael@0 | 1347 | // There is no way to test whether there are any bailouts |
michael@0 | 1348 | // between execution of the operands, so remove negative |
michael@0 | 1349 | // zero checks from the first only if the second's type is |
michael@0 | 1350 | // independent from type changes that may occur after bailing. |
michael@0 | 1351 | switch (second->op()) { |
michael@0 | 1352 | case MDefinition::Op_Constant: |
michael@0 | 1353 | case MDefinition::Op_BitAnd: |
michael@0 | 1354 | case MDefinition::Op_BitOr: |
michael@0 | 1355 | case MDefinition::Op_BitXor: |
michael@0 | 1356 | case MDefinition::Op_BitNot: |
michael@0 | 1357 | case MDefinition::Op_Lsh: |
michael@0 | 1358 | case MDefinition::Op_Rsh: |
michael@0 | 1359 | break; |
michael@0 | 1360 | default: |
michael@0 | 1361 | return true; |
michael@0 | 1362 | } |
michael@0 | 1363 | } |
michael@0 | 1364 | |
michael@0 | 1365 | // The negative zero check can always be removed on the second |
michael@0 | 1366 | // executed operand; by the time this executes the first will have |
michael@0 | 1367 | // been evaluated as int32 and the addition's result cannot be -0. |
michael@0 | 1368 | break; |
michael@0 | 1369 | } |
michael@0 | 1370 | case MDefinition::Op_Sub: |
michael@0 | 1371 | // If sub is truncating -0 and 0 are observed as the same |
michael@0 | 1372 | if (use_def->toSub()->isTruncated()) |
michael@0 | 1373 | break; |
michael@0 | 1374 | /* Fall through... */ |
michael@0 | 1375 | case MDefinition::Op_StoreElement: |
michael@0 | 1376 | case MDefinition::Op_StoreElementHole: |
michael@0 | 1377 | case MDefinition::Op_LoadElement: |
michael@0 | 1378 | case MDefinition::Op_LoadElementHole: |
michael@0 | 1379 | case MDefinition::Op_LoadTypedArrayElement: |
michael@0 | 1380 | case MDefinition::Op_LoadTypedArrayElementHole: |
michael@0 | 1381 | case MDefinition::Op_CharCodeAt: |
michael@0 | 1382 | case MDefinition::Op_Mod: |
michael@0 | 1383 | // Only allowed to remove check when definition is the second operand |
michael@0 | 1384 | if (use_def->getOperand(0) == def) |
michael@0 | 1385 | return true; |
michael@0 | 1386 | for (size_t i = 2, e = use_def->numOperands(); i < e; i++) { |
michael@0 | 1387 | if (use_def->getOperand(i) == def) |
michael@0 | 1388 | return true; |
michael@0 | 1389 | } |
michael@0 | 1390 | break; |
michael@0 | 1391 | case MDefinition::Op_BoundsCheck: |
michael@0 | 1392 | // Only allowed to remove check when definition is the first operand |
michael@0 | 1393 | if (use_def->toBoundsCheck()->getOperand(1) == def) |
michael@0 | 1394 | return true; |
michael@0 | 1395 | break; |
michael@0 | 1396 | case MDefinition::Op_ToString: |
michael@0 | 1397 | case MDefinition::Op_FromCharCode: |
michael@0 | 1398 | case MDefinition::Op_TableSwitch: |
michael@0 | 1399 | case MDefinition::Op_Compare: |
michael@0 | 1400 | case MDefinition::Op_BitAnd: |
michael@0 | 1401 | case MDefinition::Op_BitOr: |
michael@0 | 1402 | case MDefinition::Op_BitXor: |
michael@0 | 1403 | case MDefinition::Op_Abs: |
michael@0 | 1404 | case MDefinition::Op_TruncateToInt32: |
michael@0 | 1405 | // Always allowed to remove check. No matter which operand. |
michael@0 | 1406 | break; |
michael@0 | 1407 | default: |
michael@0 | 1408 | return true; |
michael@0 | 1409 | } |
michael@0 | 1410 | } |
michael@0 | 1411 | return false; |
michael@0 | 1412 | } |
michael@0 | 1413 | |
michael@0 | 1414 | MDefinition * |
michael@0 | 1415 | MBinaryArithInstruction::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 1416 | { |
michael@0 | 1417 | if (specialization_ == MIRType_None) |
michael@0 | 1418 | return this; |
michael@0 | 1419 | |
michael@0 | 1420 | MDefinition *lhs = getOperand(0); |
michael@0 | 1421 | MDefinition *rhs = getOperand(1); |
michael@0 | 1422 | if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) |
michael@0 | 1423 | return folded; |
michael@0 | 1424 | |
michael@0 | 1425 | // 0 + -0 = 0. So we can't remove addition |
michael@0 | 1426 | if (isAdd() && specialization_ != MIRType_Int32) |
michael@0 | 1427 | return this; |
michael@0 | 1428 | |
michael@0 | 1429 | if (IsConstant(rhs, getIdentity())) |
michael@0 | 1430 | return lhs; |
michael@0 | 1431 | |
michael@0 | 1432 | // subtraction isn't commutative. So we can't remove subtraction when lhs equals 0 |
michael@0 | 1433 | if (isSub()) |
michael@0 | 1434 | return this; |
michael@0 | 1435 | |
michael@0 | 1436 | if (IsConstant(lhs, getIdentity())) |
michael@0 | 1437 | return rhs; // x op id => x |
michael@0 | 1438 | |
michael@0 | 1439 | return this; |
michael@0 | 1440 | } |
michael@0 | 1441 | |
michael@0 | 1442 | void |
michael@0 | 1443 | MBinaryArithInstruction::trySpecializeFloat32(TempAllocator &alloc) |
michael@0 | 1444 | { |
michael@0 | 1445 | MDefinition *left = lhs(); |
michael@0 | 1446 | MDefinition *right = rhs(); |
michael@0 | 1447 | |
michael@0 | 1448 | if (!left->canProduceFloat32() || !right->canProduceFloat32() |
michael@0 | 1449 | || !CheckUsesAreFloat32Consumers(this)) |
michael@0 | 1450 | { |
michael@0 | 1451 | if (left->type() == MIRType_Float32) |
michael@0 | 1452 | ConvertDefinitionToDouble<0>(alloc, left, this); |
michael@0 | 1453 | if (right->type() == MIRType_Float32) |
michael@0 | 1454 | ConvertDefinitionToDouble<1>(alloc, right, this); |
michael@0 | 1455 | return; |
michael@0 | 1456 | } |
michael@0 | 1457 | |
michael@0 | 1458 | specialization_ = MIRType_Float32; |
michael@0 | 1459 | setResultType(MIRType_Float32); |
michael@0 | 1460 | } |
michael@0 | 1461 | |
michael@0 | 1462 | bool |
michael@0 | 1463 | MAbs::fallible() const |
michael@0 | 1464 | { |
michael@0 | 1465 | return !implicitTruncate_ && (!range() || !range()->hasInt32Bounds()); |
michael@0 | 1466 | } |
michael@0 | 1467 | |
michael@0 | 1468 | void |
michael@0 | 1469 | MAbs::trySpecializeFloat32(TempAllocator &alloc) |
michael@0 | 1470 | { |
michael@0 | 1471 | if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) { |
michael@0 | 1472 | if (input()->type() == MIRType_Float32) |
michael@0 | 1473 | ConvertDefinitionToDouble<0>(alloc, input(), this); |
michael@0 | 1474 | return; |
michael@0 | 1475 | } |
michael@0 | 1476 | |
michael@0 | 1477 | setResultType(MIRType_Float32); |
michael@0 | 1478 | specialization_ = MIRType_Float32; |
michael@0 | 1479 | } |
michael@0 | 1480 | |
michael@0 | 1481 | MDefinition * |
michael@0 | 1482 | MDiv::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 1483 | { |
michael@0 | 1484 | if (specialization_ == MIRType_None) |
michael@0 | 1485 | return this; |
michael@0 | 1486 | |
michael@0 | 1487 | if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) |
michael@0 | 1488 | return folded; |
michael@0 | 1489 | |
michael@0 | 1490 | return this; |
michael@0 | 1491 | } |
michael@0 | 1492 | |
michael@0 | 1493 | void |
michael@0 | 1494 | MDiv::analyzeEdgeCasesForward() |
michael@0 | 1495 | { |
michael@0 | 1496 | // This is only meaningful when doing integer division. |
michael@0 | 1497 | if (specialization_ != MIRType_Int32) |
michael@0 | 1498 | return; |
michael@0 | 1499 | |
michael@0 | 1500 | // Try removing divide by zero check |
michael@0 | 1501 | if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(0)) |
michael@0 | 1502 | canBeDivideByZero_ = false; |
michael@0 | 1503 | |
michael@0 | 1504 | // If lhs is a constant int != INT32_MIN, then |
michael@0 | 1505 | // negative overflow check can be skipped. |
michael@0 | 1506 | if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(INT32_MIN)) |
michael@0 | 1507 | canBeNegativeOverflow_ = false; |
michael@0 | 1508 | |
michael@0 | 1509 | // If rhs is a constant int != -1, likewise. |
michael@0 | 1510 | if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(-1)) |
michael@0 | 1511 | canBeNegativeOverflow_ = false; |
michael@0 | 1512 | |
michael@0 | 1513 | // If lhs is != 0, then negative zero check can be skipped. |
michael@0 | 1514 | if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(0)) |
michael@0 | 1515 | setCanBeNegativeZero(false); |
michael@0 | 1516 | |
michael@0 | 1517 | // If rhs is >= 0, likewise. |
michael@0 | 1518 | if (rhs()->isConstant()) { |
michael@0 | 1519 | const js::Value &val = rhs()->toConstant()->value(); |
michael@0 | 1520 | if (val.isInt32() && val.toInt32() >= 0) |
michael@0 | 1521 | setCanBeNegativeZero(false); |
michael@0 | 1522 | } |
michael@0 | 1523 | } |
michael@0 | 1524 | |
michael@0 | 1525 | void |
michael@0 | 1526 | MDiv::analyzeEdgeCasesBackward() |
michael@0 | 1527 | { |
michael@0 | 1528 | if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) |
michael@0 | 1529 | setCanBeNegativeZero(false); |
michael@0 | 1530 | } |
michael@0 | 1531 | |
michael@0 | 1532 | bool |
michael@0 | 1533 | MDiv::fallible() const |
michael@0 | 1534 | { |
michael@0 | 1535 | return !isTruncated(); |
michael@0 | 1536 | } |
michael@0 | 1537 | |
michael@0 | 1538 | bool |
michael@0 | 1539 | MMod::canBeDivideByZero() const |
michael@0 | 1540 | { |
michael@0 | 1541 | JS_ASSERT(specialization_ == MIRType_Int32); |
michael@0 | 1542 | return !rhs()->isConstant() || rhs()->toConstant()->value().toInt32() == 0; |
michael@0 | 1543 | } |
michael@0 | 1544 | |
michael@0 | 1545 | bool |
michael@0 | 1546 | MMod::canBePowerOfTwoDivisor() const |
michael@0 | 1547 | { |
michael@0 | 1548 | JS_ASSERT(specialization_ == MIRType_Int32); |
michael@0 | 1549 | |
michael@0 | 1550 | if (!rhs()->isConstant()) |
michael@0 | 1551 | return true; |
michael@0 | 1552 | |
michael@0 | 1553 | int32_t i = rhs()->toConstant()->value().toInt32(); |
michael@0 | 1554 | if (i <= 0 || !IsPowerOfTwo(i)) |
michael@0 | 1555 | return false; |
michael@0 | 1556 | |
michael@0 | 1557 | return true; |
michael@0 | 1558 | } |
michael@0 | 1559 | |
michael@0 | 1560 | MDefinition * |
michael@0 | 1561 | MMod::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 1562 | { |
michael@0 | 1563 | if (specialization_ == MIRType_None) |
michael@0 | 1564 | return this; |
michael@0 | 1565 | |
michael@0 | 1566 | if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) |
michael@0 | 1567 | return folded; |
michael@0 | 1568 | |
michael@0 | 1569 | return this; |
michael@0 | 1570 | } |
michael@0 | 1571 | |
michael@0 | 1572 | bool |
michael@0 | 1573 | MMod::fallible() const |
michael@0 | 1574 | { |
michael@0 | 1575 | return !isTruncated() && |
michael@0 | 1576 | (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend()); |
michael@0 | 1577 | } |
michael@0 | 1578 | |
michael@0 | 1579 | void |
michael@0 | 1580 | MMathFunction::trySpecializeFloat32(TempAllocator &alloc) |
michael@0 | 1581 | { |
michael@0 | 1582 | if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) { |
michael@0 | 1583 | if (input()->type() == MIRType_Float32) |
michael@0 | 1584 | ConvertDefinitionToDouble<0>(alloc, input(), this); |
michael@0 | 1585 | return; |
michael@0 | 1586 | } |
michael@0 | 1587 | |
michael@0 | 1588 | setResultType(MIRType_Float32); |
michael@0 | 1589 | setPolicyType(MIRType_Float32); |
michael@0 | 1590 | } |
michael@0 | 1591 | |
michael@0 | 1592 | bool |
michael@0 | 1593 | MAdd::fallible() const |
michael@0 | 1594 | { |
michael@0 | 1595 | // the add is fallible if range analysis does not say that it is finite, AND |
michael@0 | 1596 | // either the truncation analysis shows that there are non-truncated uses. |
michael@0 | 1597 | if (isTruncated()) |
michael@0 | 1598 | return false; |
michael@0 | 1599 | if (range() && range()->hasInt32Bounds()) |
michael@0 | 1600 | return false; |
michael@0 | 1601 | return true; |
michael@0 | 1602 | } |
michael@0 | 1603 | |
michael@0 | 1604 | bool |
michael@0 | 1605 | MSub::fallible() const |
michael@0 | 1606 | { |
michael@0 | 1607 | // see comment in MAdd::fallible() |
michael@0 | 1608 | if (isTruncated()) |
michael@0 | 1609 | return false; |
michael@0 | 1610 | if (range() && range()->hasInt32Bounds()) |
michael@0 | 1611 | return false; |
michael@0 | 1612 | return true; |
michael@0 | 1613 | } |
michael@0 | 1614 | |
michael@0 | 1615 | MDefinition * |
michael@0 | 1616 | MMul::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 1617 | { |
michael@0 | 1618 | MDefinition *out = MBinaryArithInstruction::foldsTo(alloc, useValueNumbers); |
michael@0 | 1619 | if (out != this) |
michael@0 | 1620 | return out; |
michael@0 | 1621 | |
michael@0 | 1622 | if (specialization() != MIRType_Int32) |
michael@0 | 1623 | return this; |
michael@0 | 1624 | |
michael@0 | 1625 | if (EqualValues(useValueNumbers, lhs(), rhs())) |
michael@0 | 1626 | setCanBeNegativeZero(false); |
michael@0 | 1627 | |
michael@0 | 1628 | return this; |
michael@0 | 1629 | } |
michael@0 | 1630 | |
michael@0 | 1631 | void |
michael@0 | 1632 | MMul::analyzeEdgeCasesForward() |
michael@0 | 1633 | { |
michael@0 | 1634 | // Try to remove the check for negative zero |
michael@0 | 1635 | // This only makes sense when using the integer multiplication |
michael@0 | 1636 | if (specialization() != MIRType_Int32) |
michael@0 | 1637 | return; |
michael@0 | 1638 | |
michael@0 | 1639 | // If lhs is > 0, no need for negative zero check. |
michael@0 | 1640 | if (lhs()->isConstant()) { |
michael@0 | 1641 | const js::Value &val = lhs()->toConstant()->value(); |
michael@0 | 1642 | if (val.isInt32() && val.toInt32() > 0) |
michael@0 | 1643 | setCanBeNegativeZero(false); |
michael@0 | 1644 | } |
michael@0 | 1645 | |
michael@0 | 1646 | // If rhs is > 0, likewise. |
michael@0 | 1647 | if (rhs()->isConstant()) { |
michael@0 | 1648 | const js::Value &val = rhs()->toConstant()->value(); |
michael@0 | 1649 | if (val.isInt32() && val.toInt32() > 0) |
michael@0 | 1650 | setCanBeNegativeZero(false); |
michael@0 | 1651 | } |
michael@0 | 1652 | } |
michael@0 | 1653 | |
michael@0 | 1654 | void |
michael@0 | 1655 | MMul::analyzeEdgeCasesBackward() |
michael@0 | 1656 | { |
michael@0 | 1657 | if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) |
michael@0 | 1658 | setCanBeNegativeZero(false); |
michael@0 | 1659 | } |
michael@0 | 1660 | |
michael@0 | 1661 | bool |
michael@0 | 1662 | MMul::updateForReplacement(MDefinition *ins_) |
michael@0 | 1663 | { |
michael@0 | 1664 | MMul *ins = ins_->toMul(); |
michael@0 | 1665 | bool negativeZero = canBeNegativeZero() || ins->canBeNegativeZero(); |
michael@0 | 1666 | setCanBeNegativeZero(negativeZero); |
michael@0 | 1667 | // Remove the imul annotation when merging imul and normal multiplication. |
michael@0 | 1668 | if (mode_ == Integer && ins->mode() != Integer) |
michael@0 | 1669 | mode_ = Normal; |
michael@0 | 1670 | return true; |
michael@0 | 1671 | } |
michael@0 | 1672 | |
michael@0 | 1673 | bool |
michael@0 | 1674 | MMul::canOverflow() const |
michael@0 | 1675 | { |
michael@0 | 1676 | if (isTruncated()) |
michael@0 | 1677 | return false; |
michael@0 | 1678 | return !range() || !range()->hasInt32Bounds(); |
michael@0 | 1679 | } |
michael@0 | 1680 | |
michael@0 | 1681 | bool |
michael@0 | 1682 | MUrsh::fallible() const |
michael@0 | 1683 | { |
michael@0 | 1684 | if (bailoutsDisabled()) |
michael@0 | 1685 | return false; |
michael@0 | 1686 | return !range() || !range()->hasInt32Bounds(); |
michael@0 | 1687 | } |
michael@0 | 1688 | |
michael@0 | 1689 | static inline bool |
michael@0 | 1690 | KnownNonStringPrimitive(MDefinition *op) |
michael@0 | 1691 | { |
michael@0 | 1692 | return !op->mightBeType(MIRType_Object) |
michael@0 | 1693 | && !op->mightBeType(MIRType_String) |
michael@0 | 1694 | && !op->mightBeType(MIRType_MagicOptimizedArguments) |
michael@0 | 1695 | && !op->mightBeType(MIRType_MagicHole) |
michael@0 | 1696 | && !op->mightBeType(MIRType_MagicIsConstructing); |
michael@0 | 1697 | } |
michael@0 | 1698 | |
michael@0 | 1699 | void |
michael@0 | 1700 | MBinaryArithInstruction::infer(TempAllocator &alloc, BaselineInspector *inspector, jsbytecode *pc) |
michael@0 | 1701 | { |
michael@0 | 1702 | JS_ASSERT(this->type() == MIRType_Value); |
michael@0 | 1703 | |
michael@0 | 1704 | specialization_ = MIRType_None; |
michael@0 | 1705 | |
michael@0 | 1706 | // Don't specialize if one operand could be an object. If we specialize |
michael@0 | 1707 | // as int32 or double based on baseline feedback, we could DCE this |
michael@0 | 1708 | // instruction and fail to invoke any valueOf methods. |
michael@0 | 1709 | if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) |
michael@0 | 1710 | return; |
michael@0 | 1711 | |
michael@0 | 1712 | // Anything complex - strings and objects - are not specialized |
michael@0 | 1713 | // unless baseline type hints suggest it might be profitable |
michael@0 | 1714 | if (!KnownNonStringPrimitive(getOperand(0)) || !KnownNonStringPrimitive(getOperand(1))) |
michael@0 | 1715 | return inferFallback(inspector, pc); |
michael@0 | 1716 | |
michael@0 | 1717 | // Retrieve type information of lhs and rhs. |
michael@0 | 1718 | MIRType lhs = getOperand(0)->type(); |
michael@0 | 1719 | MIRType rhs = getOperand(1)->type(); |
michael@0 | 1720 | |
michael@0 | 1721 | // Guess a result type based on the inputs. |
michael@0 | 1722 | // Don't specialize for neither-integer-nor-double results. |
michael@0 | 1723 | if (lhs == MIRType_Int32 && rhs == MIRType_Int32) |
michael@0 | 1724 | setResultType(MIRType_Int32); |
michael@0 | 1725 | // Double operations are prioritary over float32 operations (i.e. if any operand needs |
michael@0 | 1726 | // a double as an input, convert all operands to doubles) |
michael@0 | 1727 | else if (IsFloatingPointType(lhs) || IsFloatingPointType(rhs)) |
michael@0 | 1728 | setResultType(MIRType_Double); |
michael@0 | 1729 | else |
michael@0 | 1730 | return inferFallback(inspector, pc); |
michael@0 | 1731 | |
michael@0 | 1732 | // If the operation has ever overflowed, use a double specialization. |
michael@0 | 1733 | if (inspector->hasSeenDoubleResult(pc)) |
michael@0 | 1734 | setResultType(MIRType_Double); |
michael@0 | 1735 | |
michael@0 | 1736 | // If the operation will always overflow on its constant operands, use a |
michael@0 | 1737 | // double specialization so that it can be constant folded later. |
michael@0 | 1738 | if ((isMul() || isDiv()) && lhs == MIRType_Int32 && rhs == MIRType_Int32) { |
michael@0 | 1739 | bool typeChange = false; |
michael@0 | 1740 | EvaluateConstantOperands(alloc, this, &typeChange); |
michael@0 | 1741 | if (typeChange) |
michael@0 | 1742 | setResultType(MIRType_Double); |
michael@0 | 1743 | } |
michael@0 | 1744 | |
michael@0 | 1745 | JS_ASSERT(lhs < MIRType_String || lhs == MIRType_Value); |
michael@0 | 1746 | JS_ASSERT(rhs < MIRType_String || rhs == MIRType_Value); |
michael@0 | 1747 | |
michael@0 | 1748 | MIRType rval = this->type(); |
michael@0 | 1749 | |
michael@0 | 1750 | // Don't specialize values when result isn't double |
michael@0 | 1751 | if (lhs == MIRType_Value || rhs == MIRType_Value) { |
michael@0 | 1752 | if (!IsFloatingPointType(rval)) { |
michael@0 | 1753 | specialization_ = MIRType_None; |
michael@0 | 1754 | return; |
michael@0 | 1755 | } |
michael@0 | 1756 | } |
michael@0 | 1757 | |
michael@0 | 1758 | // Don't specialize as int32 if one of the operands is undefined, |
michael@0 | 1759 | // since ToNumber(undefined) is NaN. |
michael@0 | 1760 | if (rval == MIRType_Int32 && (lhs == MIRType_Undefined || rhs == MIRType_Undefined)) { |
michael@0 | 1761 | specialization_ = MIRType_None; |
michael@0 | 1762 | return; |
michael@0 | 1763 | } |
michael@0 | 1764 | |
michael@0 | 1765 | specialization_ = rval; |
michael@0 | 1766 | |
michael@0 | 1767 | if (isAdd() || isMul()) |
michael@0 | 1768 | setCommutative(); |
michael@0 | 1769 | setResultType(rval); |
michael@0 | 1770 | } |
michael@0 | 1771 | |
michael@0 | 1772 | void |
michael@0 | 1773 | MBinaryArithInstruction::inferFallback(BaselineInspector *inspector, |
michael@0 | 1774 | jsbytecode *pc) |
michael@0 | 1775 | { |
michael@0 | 1776 | // Try to specialize based on what baseline observed in practice. |
michael@0 | 1777 | specialization_ = inspector->expectedBinaryArithSpecialization(pc); |
michael@0 | 1778 | if (specialization_ != MIRType_None) { |
michael@0 | 1779 | setResultType(specialization_); |
michael@0 | 1780 | return; |
michael@0 | 1781 | } |
michael@0 | 1782 | |
michael@0 | 1783 | // In parallel execution, for now anyhow, we *only* support adding |
michael@0 | 1784 | // and manipulating numbers (not strings or objects). So no |
michael@0 | 1785 | // matter what we can specialize to double...if the result ought |
michael@0 | 1786 | // to have been something else, we'll fail in the various type |
michael@0 | 1787 | // guards that get inserted later. |
michael@0 | 1788 | if (block()->info().executionMode() == ParallelExecution) { |
michael@0 | 1789 | specialization_ = MIRType_Double; |
michael@0 | 1790 | setResultType(MIRType_Double); |
michael@0 | 1791 | return; |
michael@0 | 1792 | } |
michael@0 | 1793 | |
michael@0 | 1794 | // If we can't specialize because we have no type information at all for |
michael@0 | 1795 | // the lhs or rhs, mark the binary instruction as having no possible types |
michael@0 | 1796 | // either to avoid degrading subsequent analysis. |
michael@0 | 1797 | if (getOperand(0)->emptyResultTypeSet() || getOperand(1)->emptyResultTypeSet()) { |
michael@0 | 1798 | LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); |
michael@0 | 1799 | types::TemporaryTypeSet *types = alloc->new_<types::TemporaryTypeSet>(); |
michael@0 | 1800 | if (types) |
michael@0 | 1801 | setResultTypeSet(types); |
michael@0 | 1802 | } |
michael@0 | 1803 | } |
michael@0 | 1804 | |
michael@0 | 1805 | static bool |
michael@0 | 1806 | SafelyCoercesToDouble(MDefinition *op) |
michael@0 | 1807 | { |
michael@0 | 1808 | // Strings are unhandled -- visitToDouble() doesn't support them yet. |
michael@0 | 1809 | // Null is unhandled -- ToDouble(null) == 0, but (0 == null) is false. |
michael@0 | 1810 | return KnownNonStringPrimitive(op) && !op->mightBeType(MIRType_Null); |
michael@0 | 1811 | } |
michael@0 | 1812 | |
michael@0 | 1813 | static bool |
michael@0 | 1814 | ObjectOrSimplePrimitive(MDefinition *op) |
michael@0 | 1815 | { |
michael@0 | 1816 | // Return true if op is either undefined/null/boolean/int32 or an object. |
michael@0 | 1817 | return !op->mightBeType(MIRType_String) |
michael@0 | 1818 | && !op->mightBeType(MIRType_Double) |
michael@0 | 1819 | && !op->mightBeType(MIRType_Float32) |
michael@0 | 1820 | && !op->mightBeType(MIRType_MagicOptimizedArguments) |
michael@0 | 1821 | && !op->mightBeType(MIRType_MagicHole) |
michael@0 | 1822 | && !op->mightBeType(MIRType_MagicIsConstructing); |
michael@0 | 1823 | } |
michael@0 | 1824 | |
michael@0 | 1825 | static bool |
michael@0 | 1826 | CanDoValueBitwiseCmp(MDefinition *lhs, MDefinition *rhs, bool looseEq) |
michael@0 | 1827 | { |
michael@0 | 1828 | // Only primitive (not double/string) or objects are supported. |
michael@0 | 1829 | // I.e. Undefined/Null/Boolean/Int32 and Object |
michael@0 | 1830 | if (!ObjectOrSimplePrimitive(lhs) || !ObjectOrSimplePrimitive(rhs)) |
michael@0 | 1831 | return false; |
michael@0 | 1832 | |
michael@0 | 1833 | // Objects that emulate undefined are not supported. |
michael@0 | 1834 | if (MaybeEmulatesUndefined(lhs) || MaybeEmulatesUndefined(rhs)) |
michael@0 | 1835 | return false; |
michael@0 | 1836 | |
michael@0 | 1837 | // In the loose comparison more values could be the same, |
michael@0 | 1838 | // but value comparison reporting otherwise. |
michael@0 | 1839 | if (looseEq) { |
michael@0 | 1840 | |
michael@0 | 1841 | // Undefined compared loosy to Null is not supported, |
michael@0 | 1842 | // because tag is different, but value can be the same (undefined == null). |
michael@0 | 1843 | if ((lhs->mightBeType(MIRType_Undefined) && rhs->mightBeType(MIRType_Null)) || |
michael@0 | 1844 | (lhs->mightBeType(MIRType_Null) && rhs->mightBeType(MIRType_Undefined))) |
michael@0 | 1845 | { |
michael@0 | 1846 | return false; |
michael@0 | 1847 | } |
michael@0 | 1848 | |
michael@0 | 1849 | // Int32 compared loosy to Boolean is not supported, |
michael@0 | 1850 | // because tag is different, but value can be the same (1 == true). |
michael@0 | 1851 | if ((lhs->mightBeType(MIRType_Int32) && rhs->mightBeType(MIRType_Boolean)) || |
michael@0 | 1852 | (lhs->mightBeType(MIRType_Boolean) && rhs->mightBeType(MIRType_Int32))) |
michael@0 | 1853 | { |
michael@0 | 1854 | return false; |
michael@0 | 1855 | } |
michael@0 | 1856 | |
michael@0 | 1857 | // For loosy comparison of an object with a Boolean/Number/String |
michael@0 | 1858 | // the valueOf the object is taken. Therefore not supported. |
michael@0 | 1859 | bool simpleLHS = lhs->mightBeType(MIRType_Boolean) || lhs->mightBeType(MIRType_Int32); |
michael@0 | 1860 | bool simpleRHS = rhs->mightBeType(MIRType_Boolean) || rhs->mightBeType(MIRType_Int32); |
michael@0 | 1861 | if ((lhs->mightBeType(MIRType_Object) && simpleRHS) || |
michael@0 | 1862 | (rhs->mightBeType(MIRType_Object) && simpleLHS)) |
michael@0 | 1863 | { |
michael@0 | 1864 | return false; |
michael@0 | 1865 | } |
michael@0 | 1866 | } |
michael@0 | 1867 | |
michael@0 | 1868 | return true; |
michael@0 | 1869 | } |
michael@0 | 1870 | |
michael@0 | 1871 | MIRType |
michael@0 | 1872 | MCompare::inputType() |
michael@0 | 1873 | { |
michael@0 | 1874 | switch(compareType_) { |
michael@0 | 1875 | case Compare_Undefined: |
michael@0 | 1876 | return MIRType_Undefined; |
michael@0 | 1877 | case Compare_Null: |
michael@0 | 1878 | return MIRType_Null; |
michael@0 | 1879 | case Compare_Boolean: |
michael@0 | 1880 | return MIRType_Boolean; |
michael@0 | 1881 | case Compare_UInt32: |
michael@0 | 1882 | case Compare_Int32: |
michael@0 | 1883 | case Compare_Int32MaybeCoerceBoth: |
michael@0 | 1884 | case Compare_Int32MaybeCoerceLHS: |
michael@0 | 1885 | case Compare_Int32MaybeCoerceRHS: |
michael@0 | 1886 | return MIRType_Int32; |
michael@0 | 1887 | case Compare_Double: |
michael@0 | 1888 | case Compare_DoubleMaybeCoerceLHS: |
michael@0 | 1889 | case Compare_DoubleMaybeCoerceRHS: |
michael@0 | 1890 | return MIRType_Double; |
michael@0 | 1891 | case Compare_Float32: |
michael@0 | 1892 | return MIRType_Float32; |
michael@0 | 1893 | case Compare_String: |
michael@0 | 1894 | case Compare_StrictString: |
michael@0 | 1895 | return MIRType_String; |
michael@0 | 1896 | case Compare_Object: |
michael@0 | 1897 | return MIRType_Object; |
michael@0 | 1898 | case Compare_Unknown: |
michael@0 | 1899 | case Compare_Value: |
michael@0 | 1900 | return MIRType_Value; |
michael@0 | 1901 | default: |
michael@0 | 1902 | MOZ_ASSUME_UNREACHABLE("No known conversion"); |
michael@0 | 1903 | } |
michael@0 | 1904 | } |
michael@0 | 1905 | |
michael@0 | 1906 | static inline bool |
michael@0 | 1907 | MustBeUInt32(MDefinition *def, MDefinition **pwrapped) |
michael@0 | 1908 | { |
michael@0 | 1909 | if (def->isUrsh()) { |
michael@0 | 1910 | *pwrapped = def->toUrsh()->getOperand(0); |
michael@0 | 1911 | MDefinition *rhs = def->toUrsh()->getOperand(1); |
michael@0 | 1912 | return !def->toUrsh()->bailoutsDisabled() |
michael@0 | 1913 | && rhs->isConstant() |
michael@0 | 1914 | && rhs->toConstant()->value().isInt32() |
michael@0 | 1915 | && rhs->toConstant()->value().toInt32() == 0; |
michael@0 | 1916 | } |
michael@0 | 1917 | |
michael@0 | 1918 | if (def->isConstant()) { |
michael@0 | 1919 | *pwrapped = def; |
michael@0 | 1920 | return def->toConstant()->value().isInt32() |
michael@0 | 1921 | && def->toConstant()->value().toInt32() >= 0; |
michael@0 | 1922 | } |
michael@0 | 1923 | |
michael@0 | 1924 | return false; |
michael@0 | 1925 | } |
michael@0 | 1926 | |
michael@0 | 1927 | bool |
michael@0 | 1928 | MBinaryInstruction::tryUseUnsignedOperands() |
michael@0 | 1929 | { |
michael@0 | 1930 | MDefinition *newlhs, *newrhs; |
michael@0 | 1931 | if (MustBeUInt32(getOperand(0), &newlhs) && MustBeUInt32(getOperand(1), &newrhs)) { |
michael@0 | 1932 | if (newlhs->type() != MIRType_Int32 || newrhs->type() != MIRType_Int32) |
michael@0 | 1933 | return false; |
michael@0 | 1934 | if (newlhs != getOperand(0)) { |
michael@0 | 1935 | getOperand(0)->setImplicitlyUsedUnchecked(); |
michael@0 | 1936 | replaceOperand(0, newlhs); |
michael@0 | 1937 | } |
michael@0 | 1938 | if (newrhs != getOperand(1)) { |
michael@0 | 1939 | getOperand(1)->setImplicitlyUsedUnchecked(); |
michael@0 | 1940 | replaceOperand(1, newrhs); |
michael@0 | 1941 | } |
michael@0 | 1942 | return true; |
michael@0 | 1943 | } |
michael@0 | 1944 | return false; |
michael@0 | 1945 | } |
michael@0 | 1946 | |
michael@0 | 1947 | void |
michael@0 | 1948 | MCompare::infer(BaselineInspector *inspector, jsbytecode *pc) |
michael@0 | 1949 | { |
michael@0 | 1950 | JS_ASSERT(operandMightEmulateUndefined()); |
michael@0 | 1951 | |
michael@0 | 1952 | if (!MaybeEmulatesUndefined(getOperand(0)) && !MaybeEmulatesUndefined(getOperand(1))) |
michael@0 | 1953 | markNoOperandEmulatesUndefined(); |
michael@0 | 1954 | |
michael@0 | 1955 | MIRType lhs = getOperand(0)->type(); |
michael@0 | 1956 | MIRType rhs = getOperand(1)->type(); |
michael@0 | 1957 | |
michael@0 | 1958 | bool looseEq = jsop() == JSOP_EQ || jsop() == JSOP_NE; |
michael@0 | 1959 | bool strictEq = jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE; |
michael@0 | 1960 | bool relationalEq = !(looseEq || strictEq); |
michael@0 | 1961 | |
michael@0 | 1962 | // Comparisons on unsigned integers may be treated as UInt32. |
michael@0 | 1963 | if (tryUseUnsignedOperands()) { |
michael@0 | 1964 | compareType_ = Compare_UInt32; |
michael@0 | 1965 | return; |
michael@0 | 1966 | } |
michael@0 | 1967 | |
michael@0 | 1968 | // Integer to integer or boolean to boolean comparisons may be treated as Int32. |
michael@0 | 1969 | if ((lhs == MIRType_Int32 && rhs == MIRType_Int32) || |
michael@0 | 1970 | (lhs == MIRType_Boolean && rhs == MIRType_Boolean)) |
michael@0 | 1971 | { |
michael@0 | 1972 | compareType_ = Compare_Int32MaybeCoerceBoth; |
michael@0 | 1973 | return; |
michael@0 | 1974 | } |
michael@0 | 1975 | |
michael@0 | 1976 | // Loose/relational cross-integer/boolean comparisons may be treated as Int32. |
michael@0 | 1977 | if (!strictEq && |
michael@0 | 1978 | (lhs == MIRType_Int32 || lhs == MIRType_Boolean) && |
michael@0 | 1979 | (rhs == MIRType_Int32 || rhs == MIRType_Boolean)) |
michael@0 | 1980 | { |
michael@0 | 1981 | compareType_ = Compare_Int32MaybeCoerceBoth; |
michael@0 | 1982 | return; |
michael@0 | 1983 | } |
michael@0 | 1984 | |
michael@0 | 1985 | // Numeric comparisons against a double coerce to double. |
michael@0 | 1986 | if (IsNumberType(lhs) && IsNumberType(rhs)) { |
michael@0 | 1987 | compareType_ = Compare_Double; |
michael@0 | 1988 | return; |
michael@0 | 1989 | } |
michael@0 | 1990 | |
michael@0 | 1991 | // Any comparison is allowed except strict eq. |
michael@0 | 1992 | if (!strictEq && IsFloatingPointType(lhs) && SafelyCoercesToDouble(getOperand(1))) { |
michael@0 | 1993 | compareType_ = Compare_DoubleMaybeCoerceRHS; |
michael@0 | 1994 | return; |
michael@0 | 1995 | } |
michael@0 | 1996 | if (!strictEq && IsFloatingPointType(rhs) && SafelyCoercesToDouble(getOperand(0))) { |
michael@0 | 1997 | compareType_ = Compare_DoubleMaybeCoerceLHS; |
michael@0 | 1998 | return; |
michael@0 | 1999 | } |
michael@0 | 2000 | |
michael@0 | 2001 | // Handle object comparison. |
michael@0 | 2002 | if (!relationalEq && lhs == MIRType_Object && rhs == MIRType_Object) { |
michael@0 | 2003 | compareType_ = Compare_Object; |
michael@0 | 2004 | return; |
michael@0 | 2005 | } |
michael@0 | 2006 | |
michael@0 | 2007 | // Handle string comparisons. (Relational string compares are still unsupported). |
michael@0 | 2008 | if (!relationalEq && lhs == MIRType_String && rhs == MIRType_String) { |
michael@0 | 2009 | compareType_ = Compare_String; |
michael@0 | 2010 | return; |
michael@0 | 2011 | } |
michael@0 | 2012 | |
michael@0 | 2013 | if (strictEq && lhs == MIRType_String) { |
michael@0 | 2014 | // Lowering expects the rhs to be definitly string. |
michael@0 | 2015 | compareType_ = Compare_StrictString; |
michael@0 | 2016 | swapOperands(); |
michael@0 | 2017 | return; |
michael@0 | 2018 | } |
michael@0 | 2019 | |
michael@0 | 2020 | if (strictEq && rhs == MIRType_String) { |
michael@0 | 2021 | compareType_ = Compare_StrictString; |
michael@0 | 2022 | return; |
michael@0 | 2023 | } |
michael@0 | 2024 | |
michael@0 | 2025 | // Handle compare with lhs being Undefined or Null. |
michael@0 | 2026 | if (!relationalEq && IsNullOrUndefined(lhs)) { |
michael@0 | 2027 | // Lowering expects the rhs to be null/undefined, so we have to |
michael@0 | 2028 | // swap the operands. This is necessary since we may not know which |
michael@0 | 2029 | // operand was null/undefined during lowering (both operands may have |
michael@0 | 2030 | // MIRType_Value). |
michael@0 | 2031 | compareType_ = (lhs == MIRType_Null) ? Compare_Null : Compare_Undefined; |
michael@0 | 2032 | swapOperands(); |
michael@0 | 2033 | return; |
michael@0 | 2034 | } |
michael@0 | 2035 | |
michael@0 | 2036 | // Handle compare with rhs being Undefined or Null. |
michael@0 | 2037 | if (!relationalEq && IsNullOrUndefined(rhs)) { |
michael@0 | 2038 | compareType_ = (rhs == MIRType_Null) ? Compare_Null : Compare_Undefined; |
michael@0 | 2039 | return; |
michael@0 | 2040 | } |
michael@0 | 2041 | |
michael@0 | 2042 | // Handle strict comparison with lhs/rhs being typed Boolean. |
michael@0 | 2043 | if (strictEq && (lhs == MIRType_Boolean || rhs == MIRType_Boolean)) { |
michael@0 | 2044 | // bool/bool case got an int32 specialization earlier. |
michael@0 | 2045 | JS_ASSERT(!(lhs == MIRType_Boolean && rhs == MIRType_Boolean)); |
michael@0 | 2046 | |
michael@0 | 2047 | // Ensure the boolean is on the right so that the type policy knows |
michael@0 | 2048 | // which side to unbox. |
michael@0 | 2049 | if (lhs == MIRType_Boolean) |
michael@0 | 2050 | swapOperands(); |
michael@0 | 2051 | |
michael@0 | 2052 | compareType_ = Compare_Boolean; |
michael@0 | 2053 | return; |
michael@0 | 2054 | } |
michael@0 | 2055 | |
michael@0 | 2056 | // Determine if we can do the compare based on a quick value check. |
michael@0 | 2057 | if (!relationalEq && CanDoValueBitwiseCmp(getOperand(0), getOperand(1), looseEq)) { |
michael@0 | 2058 | compareType_ = Compare_Value; |
michael@0 | 2059 | return; |
michael@0 | 2060 | } |
michael@0 | 2061 | |
michael@0 | 2062 | // Type information is not good enough to pick out a particular type of |
michael@0 | 2063 | // comparison we can do here. Try to specialize based on any baseline |
michael@0 | 2064 | // caches that have been generated for the opcode. These will cause the |
michael@0 | 2065 | // instruction's type policy to insert fallible unboxes to the appropriate |
michael@0 | 2066 | // input types. |
michael@0 | 2067 | |
michael@0 | 2068 | if (!strictEq) |
michael@0 | 2069 | compareType_ = inspector->expectedCompareType(pc); |
michael@0 | 2070 | } |
michael@0 | 2071 | |
michael@0 | 2072 | MBitNot * |
michael@0 | 2073 | MBitNot::New(TempAllocator &alloc, MDefinition *input) |
michael@0 | 2074 | { |
michael@0 | 2075 | return new(alloc) MBitNot(input); |
michael@0 | 2076 | } |
michael@0 | 2077 | |
michael@0 | 2078 | MBitNot * |
michael@0 | 2079 | MBitNot::NewAsmJS(TempAllocator &alloc, MDefinition *input) |
michael@0 | 2080 | { |
michael@0 | 2081 | MBitNot *ins = new(alloc) MBitNot(input); |
michael@0 | 2082 | ins->specialization_ = MIRType_Int32; |
michael@0 | 2083 | JS_ASSERT(ins->type() == MIRType_Int32); |
michael@0 | 2084 | return ins; |
michael@0 | 2085 | } |
michael@0 | 2086 | |
michael@0 | 2087 | MDefinition * |
michael@0 | 2088 | MBitNot::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2089 | { |
michael@0 | 2090 | if (specialization_ != MIRType_Int32) |
michael@0 | 2091 | return this; |
michael@0 | 2092 | |
michael@0 | 2093 | MDefinition *input = getOperand(0); |
michael@0 | 2094 | |
michael@0 | 2095 | if (input->isConstant()) { |
michael@0 | 2096 | js::Value v = Int32Value(~(input->toConstant()->value().toInt32())); |
michael@0 | 2097 | return MConstant::New(alloc, v); |
michael@0 | 2098 | } |
michael@0 | 2099 | |
michael@0 | 2100 | if (input->isBitNot() && input->toBitNot()->specialization_ == MIRType_Int32) { |
michael@0 | 2101 | JS_ASSERT(input->toBitNot()->getOperand(0)->type() == MIRType_Int32); |
michael@0 | 2102 | return input->toBitNot()->getOperand(0); // ~~x => x |
michael@0 | 2103 | } |
michael@0 | 2104 | |
michael@0 | 2105 | return this; |
michael@0 | 2106 | } |
michael@0 | 2107 | |
michael@0 | 2108 | MDefinition * |
michael@0 | 2109 | MTypeOf::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2110 | { |
michael@0 | 2111 | // Note: we can't use input->type() here, type analysis has |
michael@0 | 2112 | // boxed the input. |
michael@0 | 2113 | JS_ASSERT(input()->type() == MIRType_Value); |
michael@0 | 2114 | |
michael@0 | 2115 | JSType type; |
michael@0 | 2116 | |
michael@0 | 2117 | switch (inputType()) { |
michael@0 | 2118 | case MIRType_Double: |
michael@0 | 2119 | case MIRType_Int32: |
michael@0 | 2120 | type = JSTYPE_NUMBER; |
michael@0 | 2121 | break; |
michael@0 | 2122 | case MIRType_String: |
michael@0 | 2123 | type = JSTYPE_STRING; |
michael@0 | 2124 | break; |
michael@0 | 2125 | case MIRType_Null: |
michael@0 | 2126 | type = JSTYPE_OBJECT; |
michael@0 | 2127 | break; |
michael@0 | 2128 | case MIRType_Undefined: |
michael@0 | 2129 | type = JSTYPE_VOID; |
michael@0 | 2130 | break; |
michael@0 | 2131 | case MIRType_Boolean: |
michael@0 | 2132 | type = JSTYPE_BOOLEAN; |
michael@0 | 2133 | break; |
michael@0 | 2134 | case MIRType_Object: |
michael@0 | 2135 | if (!inputMaybeCallableOrEmulatesUndefined()) { |
michael@0 | 2136 | // Object is not callable and does not emulate undefined, so it's |
michael@0 | 2137 | // safe to fold to "object". |
michael@0 | 2138 | type = JSTYPE_OBJECT; |
michael@0 | 2139 | break; |
michael@0 | 2140 | } |
michael@0 | 2141 | // FALL THROUGH |
michael@0 | 2142 | default: |
michael@0 | 2143 | return this; |
michael@0 | 2144 | } |
michael@0 | 2145 | |
michael@0 | 2146 | return MConstant::New(alloc, StringValue(TypeName(type, GetIonContext()->runtime->names()))); |
michael@0 | 2147 | } |
michael@0 | 2148 | |
michael@0 | 2149 | void |
michael@0 | 2150 | MTypeOf::infer() |
michael@0 | 2151 | { |
michael@0 | 2152 | JS_ASSERT(inputMaybeCallableOrEmulatesUndefined()); |
michael@0 | 2153 | |
michael@0 | 2154 | if (!MaybeEmulatesUndefined(input()) && !MaybeCallable(input())) |
michael@0 | 2155 | markInputNotCallableOrEmulatesUndefined(); |
michael@0 | 2156 | } |
michael@0 | 2157 | |
michael@0 | 2158 | MBitAnd * |
michael@0 | 2159 | MBitAnd::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2160 | { |
michael@0 | 2161 | return new(alloc) MBitAnd(left, right); |
michael@0 | 2162 | } |
michael@0 | 2163 | |
michael@0 | 2164 | MBitAnd * |
michael@0 | 2165 | MBitAnd::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2166 | { |
michael@0 | 2167 | MBitAnd *ins = new(alloc) MBitAnd(left, right); |
michael@0 | 2168 | ins->specializeAsInt32(); |
michael@0 | 2169 | return ins; |
michael@0 | 2170 | } |
michael@0 | 2171 | |
michael@0 | 2172 | MBitOr * |
michael@0 | 2173 | MBitOr::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2174 | { |
michael@0 | 2175 | return new(alloc) MBitOr(left, right); |
michael@0 | 2176 | } |
michael@0 | 2177 | |
michael@0 | 2178 | MBitOr * |
michael@0 | 2179 | MBitOr::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2180 | { |
michael@0 | 2181 | MBitOr *ins = new(alloc) MBitOr(left, right); |
michael@0 | 2182 | ins->specializeAsInt32(); |
michael@0 | 2183 | return ins; |
michael@0 | 2184 | } |
michael@0 | 2185 | |
michael@0 | 2186 | MBitXor * |
michael@0 | 2187 | MBitXor::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2188 | { |
michael@0 | 2189 | return new(alloc) MBitXor(left, right); |
michael@0 | 2190 | } |
michael@0 | 2191 | |
michael@0 | 2192 | MBitXor * |
michael@0 | 2193 | MBitXor::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2194 | { |
michael@0 | 2195 | MBitXor *ins = new(alloc) MBitXor(left, right); |
michael@0 | 2196 | ins->specializeAsInt32(); |
michael@0 | 2197 | return ins; |
michael@0 | 2198 | } |
michael@0 | 2199 | |
michael@0 | 2200 | MLsh * |
michael@0 | 2201 | MLsh::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2202 | { |
michael@0 | 2203 | return new(alloc) MLsh(left, right); |
michael@0 | 2204 | } |
michael@0 | 2205 | |
michael@0 | 2206 | MLsh * |
michael@0 | 2207 | MLsh::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2208 | { |
michael@0 | 2209 | MLsh *ins = new(alloc) MLsh(left, right); |
michael@0 | 2210 | ins->specializeAsInt32(); |
michael@0 | 2211 | return ins; |
michael@0 | 2212 | } |
michael@0 | 2213 | |
michael@0 | 2214 | MRsh * |
michael@0 | 2215 | MRsh::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2216 | { |
michael@0 | 2217 | return new(alloc) MRsh(left, right); |
michael@0 | 2218 | } |
michael@0 | 2219 | |
michael@0 | 2220 | MRsh * |
michael@0 | 2221 | MRsh::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2222 | { |
michael@0 | 2223 | MRsh *ins = new(alloc) MRsh(left, right); |
michael@0 | 2224 | ins->specializeAsInt32(); |
michael@0 | 2225 | return ins; |
michael@0 | 2226 | } |
michael@0 | 2227 | |
michael@0 | 2228 | MUrsh * |
michael@0 | 2229 | MUrsh::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2230 | { |
michael@0 | 2231 | return new(alloc) MUrsh(left, right); |
michael@0 | 2232 | } |
michael@0 | 2233 | |
michael@0 | 2234 | MUrsh * |
michael@0 | 2235 | MUrsh::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) |
michael@0 | 2236 | { |
michael@0 | 2237 | MUrsh *ins = new(alloc) MUrsh(left, right); |
michael@0 | 2238 | ins->specializeAsInt32(); |
michael@0 | 2239 | |
michael@0 | 2240 | // Since Ion has no UInt32 type, we use Int32 and we have a special |
michael@0 | 2241 | // exception to the type rules: we can return values in |
michael@0 | 2242 | // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type |
michael@0 | 2243 | // without bailing out. This is necessary because Ion has no UInt32 |
michael@0 | 2244 | // type and we can't have bailouts in asm.js code. |
michael@0 | 2245 | ins->bailoutsDisabled_ = true; |
michael@0 | 2246 | |
michael@0 | 2247 | return ins; |
michael@0 | 2248 | } |
michael@0 | 2249 | |
michael@0 | 2250 | MResumePoint * |
michael@0 | 2251 | MResumePoint::New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc, MResumePoint *parent, |
michael@0 | 2252 | Mode mode) |
michael@0 | 2253 | { |
michael@0 | 2254 | MResumePoint *resume = new(alloc) MResumePoint(block, pc, parent, mode); |
michael@0 | 2255 | if (!resume->init(alloc)) |
michael@0 | 2256 | return nullptr; |
michael@0 | 2257 | resume->inherit(block); |
michael@0 | 2258 | return resume; |
michael@0 | 2259 | } |
michael@0 | 2260 | |
michael@0 | 2261 | MResumePoint::MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *caller, |
michael@0 | 2262 | Mode mode) |
michael@0 | 2263 | : MNode(block), |
michael@0 | 2264 | stackDepth_(block->stackDepth()), |
michael@0 | 2265 | pc_(pc), |
michael@0 | 2266 | caller_(caller), |
michael@0 | 2267 | instruction_(nullptr), |
michael@0 | 2268 | mode_(mode) |
michael@0 | 2269 | { |
michael@0 | 2270 | block->addResumePoint(this); |
michael@0 | 2271 | } |
michael@0 | 2272 | |
michael@0 | 2273 | void |
michael@0 | 2274 | MResumePoint::inherit(MBasicBlock *block) |
michael@0 | 2275 | { |
michael@0 | 2276 | for (size_t i = 0; i < stackDepth(); i++) { |
michael@0 | 2277 | MDefinition *def = block->getSlot(i); |
michael@0 | 2278 | setOperand(i, def); |
michael@0 | 2279 | } |
michael@0 | 2280 | } |
michael@0 | 2281 | |
michael@0 | 2282 | MDefinition * |
michael@0 | 2283 | MToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2284 | { |
michael@0 | 2285 | MDefinition *input = getOperand(0); |
michael@0 | 2286 | if (input->type() == MIRType_Int32) |
michael@0 | 2287 | return input; |
michael@0 | 2288 | return this; |
michael@0 | 2289 | } |
michael@0 | 2290 | |
michael@0 | 2291 | void |
michael@0 | 2292 | MToInt32::analyzeEdgeCasesBackward() |
michael@0 | 2293 | { |
michael@0 | 2294 | if (!NeedNegativeZeroCheck(this)) |
michael@0 | 2295 | setCanBeNegativeZero(false); |
michael@0 | 2296 | } |
michael@0 | 2297 | |
michael@0 | 2298 | MDefinition * |
michael@0 | 2299 | MTruncateToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2300 | { |
michael@0 | 2301 | MDefinition *input = getOperand(0); |
michael@0 | 2302 | if (input->type() == MIRType_Int32) |
michael@0 | 2303 | return input; |
michael@0 | 2304 | |
michael@0 | 2305 | if (input->type() == MIRType_Double && input->isConstant()) { |
michael@0 | 2306 | const Value &v = input->toConstant()->value(); |
michael@0 | 2307 | int32_t ret = ToInt32(v.toDouble()); |
michael@0 | 2308 | return MConstant::New(alloc, Int32Value(ret)); |
michael@0 | 2309 | } |
michael@0 | 2310 | |
michael@0 | 2311 | return this; |
michael@0 | 2312 | } |
michael@0 | 2313 | |
michael@0 | 2314 | MDefinition * |
michael@0 | 2315 | MToDouble::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2316 | { |
michael@0 | 2317 | MDefinition *in = input(); |
michael@0 | 2318 | if (in->type() == MIRType_Double) |
michael@0 | 2319 | return in; |
michael@0 | 2320 | |
michael@0 | 2321 | if (in->isConstant()) { |
michael@0 | 2322 | const Value &v = in->toConstant()->value(); |
michael@0 | 2323 | if (v.isNumber()) { |
michael@0 | 2324 | double out = v.toNumber(); |
michael@0 | 2325 | return MConstant::New(alloc, DoubleValue(out)); |
michael@0 | 2326 | } |
michael@0 | 2327 | } |
michael@0 | 2328 | |
michael@0 | 2329 | return this; |
michael@0 | 2330 | } |
michael@0 | 2331 | |
michael@0 | 2332 | MDefinition * |
michael@0 | 2333 | MToFloat32::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2334 | { |
michael@0 | 2335 | if (input()->type() == MIRType_Float32) |
michael@0 | 2336 | return input(); |
michael@0 | 2337 | |
michael@0 | 2338 | // If x is a Float32, Float32(Double(x)) == x |
michael@0 | 2339 | if (input()->isToDouble() && input()->toToDouble()->input()->type() == MIRType_Float32) |
michael@0 | 2340 | return input()->toToDouble()->input(); |
michael@0 | 2341 | |
michael@0 | 2342 | if (input()->isConstant()) { |
michael@0 | 2343 | const Value &v = input()->toConstant()->value(); |
michael@0 | 2344 | if (v.isNumber()) { |
michael@0 | 2345 | float out = v.toNumber(); |
michael@0 | 2346 | MConstant *c = MConstant::New(alloc, DoubleValue(out)); |
michael@0 | 2347 | c->setResultType(MIRType_Float32); |
michael@0 | 2348 | return c; |
michael@0 | 2349 | } |
michael@0 | 2350 | } |
michael@0 | 2351 | return this; |
michael@0 | 2352 | } |
michael@0 | 2353 | |
michael@0 | 2354 | MDefinition * |
michael@0 | 2355 | MToString::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2356 | { |
michael@0 | 2357 | MDefinition *in = input(); |
michael@0 | 2358 | if (in->type() == MIRType_String) |
michael@0 | 2359 | return in; |
michael@0 | 2360 | return this; |
michael@0 | 2361 | } |
michael@0 | 2362 | |
michael@0 | 2363 | MDefinition * |
michael@0 | 2364 | MClampToUint8::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2365 | { |
michael@0 | 2366 | if (input()->isConstant()) { |
michael@0 | 2367 | const Value &v = input()->toConstant()->value(); |
michael@0 | 2368 | if (v.isDouble()) { |
michael@0 | 2369 | int32_t clamped = ClampDoubleToUint8(v.toDouble()); |
michael@0 | 2370 | return MConstant::New(alloc, Int32Value(clamped)); |
michael@0 | 2371 | } |
michael@0 | 2372 | if (v.isInt32()) { |
michael@0 | 2373 | int32_t clamped = ClampIntForUint8Array(v.toInt32()); |
michael@0 | 2374 | return MConstant::New(alloc, Int32Value(clamped)); |
michael@0 | 2375 | } |
michael@0 | 2376 | } |
michael@0 | 2377 | return this; |
michael@0 | 2378 | } |
michael@0 | 2379 | |
michael@0 | 2380 | bool |
michael@0 | 2381 | MCompare::tryFold(bool *result) |
michael@0 | 2382 | { |
michael@0 | 2383 | JSOp op = jsop(); |
michael@0 | 2384 | |
michael@0 | 2385 | if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) { |
michael@0 | 2386 | JS_ASSERT(op == JSOP_EQ || op == JSOP_STRICTEQ || |
michael@0 | 2387 | op == JSOP_NE || op == JSOP_STRICTNE); |
michael@0 | 2388 | |
michael@0 | 2389 | // The LHS is the value we want to test against null or undefined. |
michael@0 | 2390 | switch (lhs()->type()) { |
michael@0 | 2391 | case MIRType_Value: |
michael@0 | 2392 | return false; |
michael@0 | 2393 | case MIRType_Undefined: |
michael@0 | 2394 | case MIRType_Null: |
michael@0 | 2395 | if (lhs()->type() == inputType()) { |
michael@0 | 2396 | // Both sides have the same type, null or undefined. |
michael@0 | 2397 | *result = (op == JSOP_EQ || op == JSOP_STRICTEQ); |
michael@0 | 2398 | } else { |
michael@0 | 2399 | // One side is null, the other side is undefined. The result is only |
michael@0 | 2400 | // true for loose equality. |
michael@0 | 2401 | *result = (op == JSOP_EQ || op == JSOP_STRICTNE); |
michael@0 | 2402 | } |
michael@0 | 2403 | return true; |
michael@0 | 2404 | case MIRType_Object: |
michael@0 | 2405 | if ((op == JSOP_EQ || op == JSOP_NE) && operandMightEmulateUndefined()) |
michael@0 | 2406 | return false; |
michael@0 | 2407 | /* FALL THROUGH */ |
michael@0 | 2408 | case MIRType_Int32: |
michael@0 | 2409 | case MIRType_Double: |
michael@0 | 2410 | case MIRType_Float32: |
michael@0 | 2411 | case MIRType_String: |
michael@0 | 2412 | case MIRType_Boolean: |
michael@0 | 2413 | *result = (op == JSOP_NE || op == JSOP_STRICTNE); |
michael@0 | 2414 | return true; |
michael@0 | 2415 | default: |
michael@0 | 2416 | MOZ_ASSUME_UNREACHABLE("Unexpected type"); |
michael@0 | 2417 | } |
michael@0 | 2418 | } |
michael@0 | 2419 | |
michael@0 | 2420 | if (compareType_ == Compare_Boolean) { |
michael@0 | 2421 | JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); |
michael@0 | 2422 | JS_ASSERT(rhs()->type() == MIRType_Boolean); |
michael@0 | 2423 | |
michael@0 | 2424 | switch (lhs()->type()) { |
michael@0 | 2425 | case MIRType_Value: |
michael@0 | 2426 | return false; |
michael@0 | 2427 | case MIRType_Int32: |
michael@0 | 2428 | case MIRType_Double: |
michael@0 | 2429 | case MIRType_Float32: |
michael@0 | 2430 | case MIRType_String: |
michael@0 | 2431 | case MIRType_Object: |
michael@0 | 2432 | case MIRType_Null: |
michael@0 | 2433 | case MIRType_Undefined: |
michael@0 | 2434 | *result = (op == JSOP_STRICTNE); |
michael@0 | 2435 | return true; |
michael@0 | 2436 | case MIRType_Boolean: |
michael@0 | 2437 | // Int32 specialization should handle this. |
michael@0 | 2438 | MOZ_ASSUME_UNREACHABLE("Wrong specialization"); |
michael@0 | 2439 | default: |
michael@0 | 2440 | MOZ_ASSUME_UNREACHABLE("Unexpected type"); |
michael@0 | 2441 | } |
michael@0 | 2442 | } |
michael@0 | 2443 | |
michael@0 | 2444 | if (compareType_ == Compare_StrictString) { |
michael@0 | 2445 | JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); |
michael@0 | 2446 | JS_ASSERT(rhs()->type() == MIRType_String); |
michael@0 | 2447 | |
michael@0 | 2448 | switch (lhs()->type()) { |
michael@0 | 2449 | case MIRType_Value: |
michael@0 | 2450 | return false; |
michael@0 | 2451 | case MIRType_Boolean: |
michael@0 | 2452 | case MIRType_Int32: |
michael@0 | 2453 | case MIRType_Double: |
michael@0 | 2454 | case MIRType_Float32: |
michael@0 | 2455 | case MIRType_Object: |
michael@0 | 2456 | case MIRType_Null: |
michael@0 | 2457 | case MIRType_Undefined: |
michael@0 | 2458 | *result = (op == JSOP_STRICTNE); |
michael@0 | 2459 | return true; |
michael@0 | 2460 | case MIRType_String: |
michael@0 | 2461 | // Compare_String specialization should handle this. |
michael@0 | 2462 | MOZ_ASSUME_UNREACHABLE("Wrong specialization"); |
michael@0 | 2463 | default: |
michael@0 | 2464 | MOZ_ASSUME_UNREACHABLE("Unexpected type"); |
michael@0 | 2465 | } |
michael@0 | 2466 | } |
michael@0 | 2467 | |
michael@0 | 2468 | return false; |
michael@0 | 2469 | } |
michael@0 | 2470 | |
michael@0 | 2471 | bool |
michael@0 | 2472 | MCompare::evaluateConstantOperands(bool *result) |
michael@0 | 2473 | { |
michael@0 | 2474 | if (type() != MIRType_Boolean && type() != MIRType_Int32) |
michael@0 | 2475 | return false; |
michael@0 | 2476 | |
michael@0 | 2477 | MDefinition *left = getOperand(0); |
michael@0 | 2478 | MDefinition *right = getOperand(1); |
michael@0 | 2479 | |
michael@0 | 2480 | if (!left->isConstant() || !right->isConstant()) |
michael@0 | 2481 | return false; |
michael@0 | 2482 | |
michael@0 | 2483 | Value lhs = left->toConstant()->value(); |
michael@0 | 2484 | Value rhs = right->toConstant()->value(); |
michael@0 | 2485 | |
michael@0 | 2486 | // Fold away some String equality comparisons. |
michael@0 | 2487 | if (lhs.isString() && rhs.isString()) { |
michael@0 | 2488 | int32_t comp = 0; // Default to equal. |
michael@0 | 2489 | if (left != right) |
michael@0 | 2490 | comp = CompareAtoms(&lhs.toString()->asAtom(), &rhs.toString()->asAtom()); |
michael@0 | 2491 | |
michael@0 | 2492 | switch (jsop_) { |
michael@0 | 2493 | case JSOP_LT: |
michael@0 | 2494 | *result = (comp < 0); |
michael@0 | 2495 | break; |
michael@0 | 2496 | case JSOP_LE: |
michael@0 | 2497 | *result = (comp <= 0); |
michael@0 | 2498 | break; |
michael@0 | 2499 | case JSOP_GT: |
michael@0 | 2500 | *result = (comp > 0); |
michael@0 | 2501 | break; |
michael@0 | 2502 | case JSOP_GE: |
michael@0 | 2503 | *result = (comp >= 0); |
michael@0 | 2504 | break; |
michael@0 | 2505 | case JSOP_STRICTEQ: // Fall through. |
michael@0 | 2506 | case JSOP_EQ: |
michael@0 | 2507 | *result = (comp == 0); |
michael@0 | 2508 | break; |
michael@0 | 2509 | case JSOP_STRICTNE: // Fall through. |
michael@0 | 2510 | case JSOP_NE: |
michael@0 | 2511 | *result = (comp != 0); |
michael@0 | 2512 | break; |
michael@0 | 2513 | default: |
michael@0 | 2514 | MOZ_ASSUME_UNREACHABLE("Unexpected op."); |
michael@0 | 2515 | } |
michael@0 | 2516 | |
michael@0 | 2517 | return true; |
michael@0 | 2518 | } |
michael@0 | 2519 | |
michael@0 | 2520 | if (compareType_ == Compare_UInt32) { |
michael@0 | 2521 | uint32_t lhsUint = uint32_t(lhs.toInt32()); |
michael@0 | 2522 | uint32_t rhsUint = uint32_t(rhs.toInt32()); |
michael@0 | 2523 | |
michael@0 | 2524 | switch (jsop_) { |
michael@0 | 2525 | case JSOP_LT: |
michael@0 | 2526 | *result = (lhsUint < rhsUint); |
michael@0 | 2527 | break; |
michael@0 | 2528 | case JSOP_LE: |
michael@0 | 2529 | *result = (lhsUint <= rhsUint); |
michael@0 | 2530 | break; |
michael@0 | 2531 | case JSOP_GT: |
michael@0 | 2532 | *result = (lhsUint > rhsUint); |
michael@0 | 2533 | break; |
michael@0 | 2534 | case JSOP_GE: |
michael@0 | 2535 | *result = (lhsUint >= rhsUint); |
michael@0 | 2536 | break; |
michael@0 | 2537 | case JSOP_EQ: |
michael@0 | 2538 | case JSOP_STRICTEQ: |
michael@0 | 2539 | *result = (lhsUint == rhsUint); |
michael@0 | 2540 | break; |
michael@0 | 2541 | case JSOP_NE: |
michael@0 | 2542 | case JSOP_STRICTNE: |
michael@0 | 2543 | *result = (lhsUint != rhsUint); |
michael@0 | 2544 | break; |
michael@0 | 2545 | default: |
michael@0 | 2546 | MOZ_ASSUME_UNREACHABLE("Unexpected op."); |
michael@0 | 2547 | } |
michael@0 | 2548 | |
michael@0 | 2549 | return true; |
michael@0 | 2550 | } |
michael@0 | 2551 | |
michael@0 | 2552 | if (!lhs.isNumber() || !rhs.isNumber()) |
michael@0 | 2553 | return false; |
michael@0 | 2554 | |
michael@0 | 2555 | switch (jsop_) { |
michael@0 | 2556 | case JSOP_LT: |
michael@0 | 2557 | *result = (lhs.toNumber() < rhs.toNumber()); |
michael@0 | 2558 | break; |
michael@0 | 2559 | case JSOP_LE: |
michael@0 | 2560 | *result = (lhs.toNumber() <= rhs.toNumber()); |
michael@0 | 2561 | break; |
michael@0 | 2562 | case JSOP_GT: |
michael@0 | 2563 | *result = (lhs.toNumber() > rhs.toNumber()); |
michael@0 | 2564 | break; |
michael@0 | 2565 | case JSOP_GE: |
michael@0 | 2566 | *result = (lhs.toNumber() >= rhs.toNumber()); |
michael@0 | 2567 | break; |
michael@0 | 2568 | case JSOP_EQ: |
michael@0 | 2569 | *result = (lhs.toNumber() == rhs.toNumber()); |
michael@0 | 2570 | break; |
michael@0 | 2571 | case JSOP_NE: |
michael@0 | 2572 | *result = (lhs.toNumber() != rhs.toNumber()); |
michael@0 | 2573 | break; |
michael@0 | 2574 | default: |
michael@0 | 2575 | return false; |
michael@0 | 2576 | } |
michael@0 | 2577 | |
michael@0 | 2578 | return true; |
michael@0 | 2579 | } |
michael@0 | 2580 | |
michael@0 | 2581 | MDefinition * |
michael@0 | 2582 | MCompare::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2583 | { |
michael@0 | 2584 | bool result; |
michael@0 | 2585 | |
michael@0 | 2586 | if (tryFold(&result) || evaluateConstantOperands(&result)) { |
michael@0 | 2587 | if (type() == MIRType_Int32) |
michael@0 | 2588 | return MConstant::New(alloc, Int32Value(result)); |
michael@0 | 2589 | |
michael@0 | 2590 | JS_ASSERT(type() == MIRType_Boolean); |
michael@0 | 2591 | return MConstant::New(alloc, BooleanValue(result)); |
michael@0 | 2592 | } |
michael@0 | 2593 | |
michael@0 | 2594 | return this; |
michael@0 | 2595 | } |
michael@0 | 2596 | |
michael@0 | 2597 | void |
michael@0 | 2598 | MCompare::trySpecializeFloat32(TempAllocator &alloc) |
michael@0 | 2599 | { |
michael@0 | 2600 | MDefinition *lhs = getOperand(0); |
michael@0 | 2601 | MDefinition *rhs = getOperand(1); |
michael@0 | 2602 | |
michael@0 | 2603 | if (lhs->canProduceFloat32() && rhs->canProduceFloat32() && compareType_ == Compare_Double) { |
michael@0 | 2604 | compareType_ = Compare_Float32; |
michael@0 | 2605 | } else { |
michael@0 | 2606 | if (lhs->type() == MIRType_Float32) |
michael@0 | 2607 | ConvertDefinitionToDouble<0>(alloc, lhs, this); |
michael@0 | 2608 | if (rhs->type() == MIRType_Float32) |
michael@0 | 2609 | ConvertDefinitionToDouble<1>(alloc, rhs, this); |
michael@0 | 2610 | } |
michael@0 | 2611 | } |
michael@0 | 2612 | |
michael@0 | 2613 | void |
michael@0 | 2614 | MCompare::filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined, |
michael@0 | 2615 | bool *filtersNull) |
michael@0 | 2616 | { |
michael@0 | 2617 | *filtersNull = *filtersUndefined = false; |
michael@0 | 2618 | *subject = nullptr; |
michael@0 | 2619 | |
michael@0 | 2620 | if (compareType() != Compare_Undefined && compareType() != Compare_Null) |
michael@0 | 2621 | return; |
michael@0 | 2622 | |
michael@0 | 2623 | JS_ASSERT(jsop() == JSOP_STRICTNE || jsop() == JSOP_NE || |
michael@0 | 2624 | jsop() == JSOP_STRICTEQ || jsop() == JSOP_EQ); |
michael@0 | 2625 | |
michael@0 | 2626 | // JSOP_*NE only removes undefined/null from if/true branch |
michael@0 | 2627 | if (!trueBranch && (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE)) |
michael@0 | 2628 | return; |
michael@0 | 2629 | |
michael@0 | 2630 | // JSOP_*EQ only removes undefined/null from else/false branch |
michael@0 | 2631 | if (trueBranch && (jsop() == JSOP_STRICTEQ || jsop() == JSOP_EQ)) |
michael@0 | 2632 | return; |
michael@0 | 2633 | |
michael@0 | 2634 | if (jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE) { |
michael@0 | 2635 | *filtersUndefined = compareType() == Compare_Undefined; |
michael@0 | 2636 | *filtersNull = compareType() == Compare_Null; |
michael@0 | 2637 | } else { |
michael@0 | 2638 | *filtersUndefined = *filtersNull = true; |
michael@0 | 2639 | } |
michael@0 | 2640 | |
michael@0 | 2641 | *subject = lhs(); |
michael@0 | 2642 | } |
michael@0 | 2643 | |
michael@0 | 2644 | void |
michael@0 | 2645 | MNot::infer() |
michael@0 | 2646 | { |
michael@0 | 2647 | JS_ASSERT(operandMightEmulateUndefined()); |
michael@0 | 2648 | |
michael@0 | 2649 | if (!MaybeEmulatesUndefined(getOperand(0))) |
michael@0 | 2650 | markOperandCantEmulateUndefined(); |
michael@0 | 2651 | } |
michael@0 | 2652 | |
michael@0 | 2653 | MDefinition * |
michael@0 | 2654 | MNot::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2655 | { |
michael@0 | 2656 | // Fold if the input is constant |
michael@0 | 2657 | if (operand()->isConstant()) { |
michael@0 | 2658 | bool result = operand()->toConstant()->valueToBoolean(); |
michael@0 | 2659 | if (type() == MIRType_Int32) |
michael@0 | 2660 | return MConstant::New(alloc, Int32Value(!result)); |
michael@0 | 2661 | |
michael@0 | 2662 | // ToBoolean can't cause side effects, so this is safe. |
michael@0 | 2663 | return MConstant::New(alloc, BooleanValue(!result)); |
michael@0 | 2664 | } |
michael@0 | 2665 | |
michael@0 | 2666 | // NOT of an undefined or null value is always true |
michael@0 | 2667 | if (operand()->type() == MIRType_Undefined || operand()->type() == MIRType_Null) |
michael@0 | 2668 | return MConstant::New(alloc, BooleanValue(true)); |
michael@0 | 2669 | |
michael@0 | 2670 | // NOT of an object that can't emulate undefined is always false. |
michael@0 | 2671 | if (operand()->type() == MIRType_Object && !operandMightEmulateUndefined()) |
michael@0 | 2672 | return MConstant::New(alloc, BooleanValue(false)); |
michael@0 | 2673 | |
michael@0 | 2674 | return this; |
michael@0 | 2675 | } |
michael@0 | 2676 | |
michael@0 | 2677 | void |
michael@0 | 2678 | MNot::trySpecializeFloat32(TempAllocator &alloc) |
michael@0 | 2679 | { |
michael@0 | 2680 | MDefinition *in = input(); |
michael@0 | 2681 | if (!in->canProduceFloat32() && in->type() == MIRType_Float32) |
michael@0 | 2682 | ConvertDefinitionToDouble<0>(alloc, in, this); |
michael@0 | 2683 | } |
michael@0 | 2684 | |
michael@0 | 2685 | void |
michael@0 | 2686 | MBeta::printOpcode(FILE *fp) const |
michael@0 | 2687 | { |
michael@0 | 2688 | MDefinition::printOpcode(fp); |
michael@0 | 2689 | |
michael@0 | 2690 | Sprinter sp(GetIonContext()->cx); |
michael@0 | 2691 | sp.init(); |
michael@0 | 2692 | comparison_->print(sp); |
michael@0 | 2693 | fprintf(fp, " %s", sp.string()); |
michael@0 | 2694 | } |
michael@0 | 2695 | |
michael@0 | 2696 | bool |
michael@0 | 2697 | MNewObject::shouldUseVM() const |
michael@0 | 2698 | { |
michael@0 | 2699 | return templateObject()->hasSingletonType() || |
michael@0 | 2700 | templateObject()->hasDynamicSlots(); |
michael@0 | 2701 | } |
michael@0 | 2702 | |
michael@0 | 2703 | bool |
michael@0 | 2704 | MNewArray::shouldUseVM() const |
michael@0 | 2705 | { |
michael@0 | 2706 | JS_ASSERT(count() < JSObject::NELEMENTS_LIMIT); |
michael@0 | 2707 | |
michael@0 | 2708 | size_t arraySlots = |
michael@0 | 2709 | gc::GetGCKindSlots(templateObject()->tenuredGetAllocKind()) - ObjectElements::VALUES_PER_HEADER; |
michael@0 | 2710 | |
michael@0 | 2711 | // Allocate space using the VMCall |
michael@0 | 2712 | // when mir hints it needs to get allocated immediately, |
michael@0 | 2713 | // but only when data doesn't fit the available array slots. |
michael@0 | 2714 | bool allocating = isAllocating() && count() > arraySlots; |
michael@0 | 2715 | |
michael@0 | 2716 | return templateObject()->hasSingletonType() || allocating; |
michael@0 | 2717 | } |
michael@0 | 2718 | |
michael@0 | 2719 | bool |
michael@0 | 2720 | MLoadFixedSlot::mightAlias(const MDefinition *store) const |
michael@0 | 2721 | { |
michael@0 | 2722 | if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot()) |
michael@0 | 2723 | return false; |
michael@0 | 2724 | return true; |
michael@0 | 2725 | } |
michael@0 | 2726 | |
michael@0 | 2727 | bool |
michael@0 | 2728 | MAsmJSLoadHeap::mightAlias(const MDefinition *def) const |
michael@0 | 2729 | { |
michael@0 | 2730 | if (def->isAsmJSStoreHeap()) { |
michael@0 | 2731 | const MAsmJSStoreHeap *store = def->toAsmJSStoreHeap(); |
michael@0 | 2732 | if (store->viewType() != viewType()) |
michael@0 | 2733 | return true; |
michael@0 | 2734 | if (!ptr()->isConstant() || !store->ptr()->isConstant()) |
michael@0 | 2735 | return true; |
michael@0 | 2736 | const MConstant *otherPtr = store->ptr()->toConstant(); |
michael@0 | 2737 | return ptr()->toConstant()->value() == otherPtr->value(); |
michael@0 | 2738 | } |
michael@0 | 2739 | return true; |
michael@0 | 2740 | } |
michael@0 | 2741 | |
michael@0 | 2742 | bool |
michael@0 | 2743 | MAsmJSLoadHeap::congruentTo(const MDefinition *ins) const |
michael@0 | 2744 | { |
michael@0 | 2745 | if (!ins->isAsmJSLoadHeap()) |
michael@0 | 2746 | return false; |
michael@0 | 2747 | const MAsmJSLoadHeap *load = ins->toAsmJSLoadHeap(); |
michael@0 | 2748 | return load->viewType() == viewType() && congruentIfOperandsEqual(load); |
michael@0 | 2749 | } |
michael@0 | 2750 | |
michael@0 | 2751 | bool |
michael@0 | 2752 | MAsmJSLoadGlobalVar::mightAlias(const MDefinition *def) const |
michael@0 | 2753 | { |
michael@0 | 2754 | if (def->isAsmJSStoreGlobalVar()) { |
michael@0 | 2755 | const MAsmJSStoreGlobalVar *store = def->toAsmJSStoreGlobalVar(); |
michael@0 | 2756 | return store->globalDataOffset() == globalDataOffset_; |
michael@0 | 2757 | } |
michael@0 | 2758 | return true; |
michael@0 | 2759 | } |
michael@0 | 2760 | |
michael@0 | 2761 | bool |
michael@0 | 2762 | MAsmJSLoadGlobalVar::congruentTo(const MDefinition *ins) const |
michael@0 | 2763 | { |
michael@0 | 2764 | if (ins->isAsmJSLoadGlobalVar()) { |
michael@0 | 2765 | const MAsmJSLoadGlobalVar *load = ins->toAsmJSLoadGlobalVar(); |
michael@0 | 2766 | return globalDataOffset_ == load->globalDataOffset_; |
michael@0 | 2767 | } |
michael@0 | 2768 | return false; |
michael@0 | 2769 | } |
michael@0 | 2770 | |
michael@0 | 2771 | bool |
michael@0 | 2772 | MLoadSlot::mightAlias(const MDefinition *store) const |
michael@0 | 2773 | { |
michael@0 | 2774 | if (store->isStoreSlot() && store->toStoreSlot()->slot() != slot()) |
michael@0 | 2775 | return false; |
michael@0 | 2776 | return true; |
michael@0 | 2777 | } |
michael@0 | 2778 | |
michael@0 | 2779 | void |
michael@0 | 2780 | InlinePropertyTable::trimTo(ObjectVector &targets, BoolVector &choiceSet) |
michael@0 | 2781 | { |
michael@0 | 2782 | for (size_t i = 0; i < targets.length(); i++) { |
michael@0 | 2783 | // If the target was inlined, don't erase the entry. |
michael@0 | 2784 | if (choiceSet[i]) |
michael@0 | 2785 | continue; |
michael@0 | 2786 | |
michael@0 | 2787 | JSFunction *target = &targets[i]->as<JSFunction>(); |
michael@0 | 2788 | |
michael@0 | 2789 | // Eliminate all entries containing the vetoed function from the map. |
michael@0 | 2790 | size_t j = 0; |
michael@0 | 2791 | while (j < numEntries()) { |
michael@0 | 2792 | if (entries_[j]->func == target) |
michael@0 | 2793 | entries_.erase(&entries_[j]); |
michael@0 | 2794 | else |
michael@0 | 2795 | j++; |
michael@0 | 2796 | } |
michael@0 | 2797 | } |
michael@0 | 2798 | } |
michael@0 | 2799 | |
michael@0 | 2800 | void |
michael@0 | 2801 | InlinePropertyTable::trimToTargets(ObjectVector &targets) |
michael@0 | 2802 | { |
michael@0 | 2803 | IonSpew(IonSpew_Inlining, "Got inlineable property cache with %d cases", |
michael@0 | 2804 | (int)numEntries()); |
michael@0 | 2805 | |
michael@0 | 2806 | size_t i = 0; |
michael@0 | 2807 | while (i < numEntries()) { |
michael@0 | 2808 | bool foundFunc = false; |
michael@0 | 2809 | for (size_t j = 0; j < targets.length(); j++) { |
michael@0 | 2810 | if (entries_[i]->func == targets[j]) { |
michael@0 | 2811 | foundFunc = true; |
michael@0 | 2812 | break; |
michael@0 | 2813 | } |
michael@0 | 2814 | } |
michael@0 | 2815 | if (!foundFunc) |
michael@0 | 2816 | entries_.erase(&(entries_[i])); |
michael@0 | 2817 | else |
michael@0 | 2818 | i++; |
michael@0 | 2819 | } |
michael@0 | 2820 | |
michael@0 | 2821 | IonSpew(IonSpew_Inlining, "%d inlineable cases left after trimming to %d targets", |
michael@0 | 2822 | (int)numEntries(), (int)targets.length()); |
michael@0 | 2823 | } |
michael@0 | 2824 | |
michael@0 | 2825 | bool |
michael@0 | 2826 | InlinePropertyTable::hasFunction(JSFunction *func) const |
michael@0 | 2827 | { |
michael@0 | 2828 | for (size_t i = 0; i < numEntries(); i++) { |
michael@0 | 2829 | if (entries_[i]->func == func) |
michael@0 | 2830 | return true; |
michael@0 | 2831 | } |
michael@0 | 2832 | return false; |
michael@0 | 2833 | } |
michael@0 | 2834 | |
michael@0 | 2835 | types::TemporaryTypeSet * |
michael@0 | 2836 | InlinePropertyTable::buildTypeSetForFunction(JSFunction *func) const |
michael@0 | 2837 | { |
michael@0 | 2838 | LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); |
michael@0 | 2839 | types::TemporaryTypeSet *types = alloc->new_<types::TemporaryTypeSet>(); |
michael@0 | 2840 | if (!types) |
michael@0 | 2841 | return nullptr; |
michael@0 | 2842 | for (size_t i = 0; i < numEntries(); i++) { |
michael@0 | 2843 | if (entries_[i]->func == func) |
michael@0 | 2844 | types->addType(types::Type::ObjectType(entries_[i]->typeObj), alloc); |
michael@0 | 2845 | } |
michael@0 | 2846 | return types; |
michael@0 | 2847 | } |
michael@0 | 2848 | |
michael@0 | 2849 | void * |
michael@0 | 2850 | MLoadTypedArrayElementStatic::base() const |
michael@0 | 2851 | { |
michael@0 | 2852 | return typedArray_->viewData(); |
michael@0 | 2853 | } |
michael@0 | 2854 | |
michael@0 | 2855 | size_t |
michael@0 | 2856 | MLoadTypedArrayElementStatic::length() const |
michael@0 | 2857 | { |
michael@0 | 2858 | return typedArray_->byteLength(); |
michael@0 | 2859 | } |
michael@0 | 2860 | |
michael@0 | 2861 | void * |
michael@0 | 2862 | MStoreTypedArrayElementStatic::base() const |
michael@0 | 2863 | { |
michael@0 | 2864 | return typedArray_->viewData(); |
michael@0 | 2865 | } |
michael@0 | 2866 | |
michael@0 | 2867 | bool |
michael@0 | 2868 | MGetElementCache::allowDoubleResult() const |
michael@0 | 2869 | { |
michael@0 | 2870 | if (!resultTypeSet()) |
michael@0 | 2871 | return true; |
michael@0 | 2872 | |
michael@0 | 2873 | return resultTypeSet()->hasType(types::Type::DoubleType()); |
michael@0 | 2874 | } |
michael@0 | 2875 | |
michael@0 | 2876 | size_t |
michael@0 | 2877 | MStoreTypedArrayElementStatic::length() const |
michael@0 | 2878 | { |
michael@0 | 2879 | return typedArray_->byteLength(); |
michael@0 | 2880 | } |
michael@0 | 2881 | |
michael@0 | 2882 | bool |
michael@0 | 2883 | MGetPropertyPolymorphic::mightAlias(const MDefinition *store) const |
michael@0 | 2884 | { |
michael@0 | 2885 | // Allow hoisting this instruction if the store does not write to a |
michael@0 | 2886 | // slot read by this instruction. |
michael@0 | 2887 | |
michael@0 | 2888 | if (!store->isStoreFixedSlot() && !store->isStoreSlot()) |
michael@0 | 2889 | return true; |
michael@0 | 2890 | |
michael@0 | 2891 | for (size_t i = 0; i < numShapes(); i++) { |
michael@0 | 2892 | const Shape *shape = this->shape(i); |
michael@0 | 2893 | if (shape->slot() < shape->numFixedSlots()) { |
michael@0 | 2894 | // Fixed slot. |
michael@0 | 2895 | uint32_t slot = shape->slot(); |
michael@0 | 2896 | if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot) |
michael@0 | 2897 | continue; |
michael@0 | 2898 | if (store->isStoreSlot()) |
michael@0 | 2899 | continue; |
michael@0 | 2900 | } else { |
michael@0 | 2901 | // Dynamic slot. |
michael@0 | 2902 | uint32_t slot = shape->slot() - shape->numFixedSlots(); |
michael@0 | 2903 | if (store->isStoreSlot() && store->toStoreSlot()->slot() != slot) |
michael@0 | 2904 | continue; |
michael@0 | 2905 | if (store->isStoreFixedSlot()) |
michael@0 | 2906 | continue; |
michael@0 | 2907 | } |
michael@0 | 2908 | |
michael@0 | 2909 | return true; |
michael@0 | 2910 | } |
michael@0 | 2911 | |
michael@0 | 2912 | return false; |
michael@0 | 2913 | } |
michael@0 | 2914 | |
michael@0 | 2915 | void |
michael@0 | 2916 | MGetPropertyCache::setBlock(MBasicBlock *block) |
michael@0 | 2917 | { |
michael@0 | 2918 | MDefinition::setBlock(block); |
michael@0 | 2919 | // Track where we started. |
michael@0 | 2920 | if (!location_.pc) { |
michael@0 | 2921 | location_.pc = block->trackedPc(); |
michael@0 | 2922 | location_.script = block->info().script(); |
michael@0 | 2923 | } |
michael@0 | 2924 | } |
michael@0 | 2925 | |
michael@0 | 2926 | bool |
michael@0 | 2927 | MGetPropertyCache::updateForReplacement(MDefinition *ins) { |
michael@0 | 2928 | MGetPropertyCache *other = ins->toGetPropertyCache(); |
michael@0 | 2929 | location_.append(&other->location_); |
michael@0 | 2930 | return true; |
michael@0 | 2931 | } |
michael@0 | 2932 | |
michael@0 | 2933 | MDefinition * |
michael@0 | 2934 | MAsmJSUnsignedToDouble::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2935 | { |
michael@0 | 2936 | if (input()->isConstant()) { |
michael@0 | 2937 | const Value &v = input()->toConstant()->value(); |
michael@0 | 2938 | if (v.isInt32()) |
michael@0 | 2939 | return MConstant::New(alloc, DoubleValue(uint32_t(v.toInt32()))); |
michael@0 | 2940 | } |
michael@0 | 2941 | |
michael@0 | 2942 | return this; |
michael@0 | 2943 | } |
michael@0 | 2944 | |
michael@0 | 2945 | MDefinition * |
michael@0 | 2946 | MAsmJSUnsignedToFloat32::foldsTo(TempAllocator &alloc, bool useValueNumbers) |
michael@0 | 2947 | { |
michael@0 | 2948 | if (input()->isConstant()) { |
michael@0 | 2949 | const Value &v = input()->toConstant()->value(); |
michael@0 | 2950 | if (v.isInt32()) { |
michael@0 | 2951 | double dval = double(uint32_t(v.toInt32())); |
michael@0 | 2952 | if (IsFloat32Representable(dval)) |
michael@0 | 2953 | return MConstant::NewAsmJS(alloc, JS::Float32Value(float(dval)), MIRType_Float32); |
michael@0 | 2954 | } |
michael@0 | 2955 | } |
michael@0 | 2956 | |
michael@0 | 2957 | return this; |
michael@0 | 2958 | } |
michael@0 | 2959 | |
michael@0 | 2960 | MAsmJSCall * |
michael@0 | 2961 | MAsmJSCall::New(TempAllocator &alloc, const CallSiteDesc &desc, Callee callee, |
michael@0 | 2962 | const Args &args, MIRType resultType, size_t spIncrement) |
michael@0 | 2963 | { |
michael@0 | 2964 | MAsmJSCall *call = new(alloc) MAsmJSCall(desc, callee, spIncrement); |
michael@0 | 2965 | call->setResultType(resultType); |
michael@0 | 2966 | |
michael@0 | 2967 | if (!call->argRegs_.init(alloc, args.length())) |
michael@0 | 2968 | return nullptr; |
michael@0 | 2969 | for (size_t i = 0; i < call->argRegs_.length(); i++) |
michael@0 | 2970 | call->argRegs_[i] = args[i].reg; |
michael@0 | 2971 | |
michael@0 | 2972 | if (!call->operands_.init(alloc, call->argRegs_.length() + (callee.which() == Callee::Dynamic ? 1 : 0))) |
michael@0 | 2973 | return nullptr; |
michael@0 | 2974 | for (size_t i = 0; i < call->argRegs_.length(); i++) |
michael@0 | 2975 | call->setOperand(i, args[i].def); |
michael@0 | 2976 | if (callee.which() == Callee::Dynamic) |
michael@0 | 2977 | call->setOperand(call->argRegs_.length(), callee.dynamic()); |
michael@0 | 2978 | |
michael@0 | 2979 | return call; |
michael@0 | 2980 | } |
michael@0 | 2981 | |
michael@0 | 2982 | void |
michael@0 | 2983 | MSqrt::trySpecializeFloat32(TempAllocator &alloc) { |
michael@0 | 2984 | if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) { |
michael@0 | 2985 | if (input()->type() == MIRType_Float32) |
michael@0 | 2986 | ConvertDefinitionToDouble<0>(alloc, input(), this); |
michael@0 | 2987 | return; |
michael@0 | 2988 | } |
michael@0 | 2989 | |
michael@0 | 2990 | setResultType(MIRType_Float32); |
michael@0 | 2991 | setPolicyType(MIRType_Float32); |
michael@0 | 2992 | } |
michael@0 | 2993 | |
michael@0 | 2994 | bool |
michael@0 | 2995 | jit::ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id) |
michael@0 | 2996 | { |
michael@0 | 2997 | if (obj->mightBeType(MIRType_String)) |
michael@0 | 2998 | return false; |
michael@0 | 2999 | |
michael@0 | 3000 | if (id->type() != MIRType_Int32 && id->type() != MIRType_Double) |
michael@0 | 3001 | return false; |
michael@0 | 3002 | |
michael@0 | 3003 | types::TemporaryTypeSet *types = obj->resultTypeSet(); |
michael@0 | 3004 | if (!types) |
michael@0 | 3005 | return false; |
michael@0 | 3006 | |
michael@0 | 3007 | // Typed arrays are native classes but do not have dense elements. |
michael@0 | 3008 | const Class *clasp = types->getKnownClass(); |
michael@0 | 3009 | return clasp && clasp->isNative() && !IsTypedArrayClass(clasp); |
michael@0 | 3010 | } |
michael@0 | 3011 | |
michael@0 | 3012 | bool |
michael@0 | 3013 | jit::ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id, |
michael@0 | 3014 | ScalarTypeDescr::Type *arrayType) |
michael@0 | 3015 | { |
michael@0 | 3016 | if (obj->mightBeType(MIRType_String)) |
michael@0 | 3017 | return false; |
michael@0 | 3018 | |
michael@0 | 3019 | if (id->type() != MIRType_Int32 && id->type() != MIRType_Double) |
michael@0 | 3020 | return false; |
michael@0 | 3021 | |
michael@0 | 3022 | types::TemporaryTypeSet *types = obj->resultTypeSet(); |
michael@0 | 3023 | if (!types) |
michael@0 | 3024 | return false; |
michael@0 | 3025 | |
michael@0 | 3026 | *arrayType = (ScalarTypeDescr::Type) types->getTypedArrayType(); |
michael@0 | 3027 | return *arrayType != ScalarTypeDescr::TYPE_MAX; |
michael@0 | 3028 | } |
michael@0 | 3029 | |
michael@0 | 3030 | bool |
michael@0 | 3031 | jit::ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj) |
michael@0 | 3032 | { |
michael@0 | 3033 | types::TemporaryTypeSet *types = obj->resultTypeSet(); |
michael@0 | 3034 | return types && !types->hasObjectFlags(constraints, types::OBJECT_FLAG_NON_PACKED); |
michael@0 | 3035 | } |
michael@0 | 3036 | |
michael@0 | 3037 | bool |
michael@0 | 3038 | jit::ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints, |
michael@0 | 3039 | MDefinition *obj) |
michael@0 | 3040 | { |
michael@0 | 3041 | types::TemporaryTypeSet *types = obj->resultTypeSet(); |
michael@0 | 3042 | |
michael@0 | 3043 | if (!types || types->hasObjectFlags(constraints, types::OBJECT_FLAG_LENGTH_OVERFLOW)) |
michael@0 | 3044 | return true; |
michael@0 | 3045 | |
michael@0 | 3046 | return types::TypeCanHaveExtraIndexedProperties(constraints, types); |
michael@0 | 3047 | } |
michael@0 | 3048 | |
michael@0 | 3049 | MIRType |
michael@0 | 3050 | jit::DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj) |
michael@0 | 3051 | { |
michael@0 | 3052 | types::TemporaryTypeSet *types = obj->resultTypeSet(); |
michael@0 | 3053 | MIRType elementType = MIRType_None; |
michael@0 | 3054 | unsigned count = types->getObjectCount(); |
michael@0 | 3055 | |
michael@0 | 3056 | for (unsigned i = 0; i < count; i++) { |
michael@0 | 3057 | types::TypeObjectKey *object = types->getObject(i); |
michael@0 | 3058 | if (!object) |
michael@0 | 3059 | continue; |
michael@0 | 3060 | |
michael@0 | 3061 | if (object->unknownProperties()) |
michael@0 | 3062 | return MIRType_None; |
michael@0 | 3063 | |
michael@0 | 3064 | types::HeapTypeSetKey elementTypes = object->property(JSID_VOID); |
michael@0 | 3065 | |
michael@0 | 3066 | MIRType type = elementTypes.knownMIRType(constraints); |
michael@0 | 3067 | if (type == MIRType_None) |
michael@0 | 3068 | return MIRType_None; |
michael@0 | 3069 | |
michael@0 | 3070 | if (elementType == MIRType_None) |
michael@0 | 3071 | elementType = type; |
michael@0 | 3072 | else if (elementType != type) |
michael@0 | 3073 | return MIRType_None; |
michael@0 | 3074 | } |
michael@0 | 3075 | |
michael@0 | 3076 | return elementType; |
michael@0 | 3077 | } |
michael@0 | 3078 | |
michael@0 | 3079 | static bool |
michael@0 | 3080 | PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints, |
michael@0 | 3081 | types::TypeObjectKey *object, PropertyName *name, |
michael@0 | 3082 | types::TypeSet *observed) |
michael@0 | 3083 | { |
michael@0 | 3084 | // If the object being read from has types for the property which haven't |
michael@0 | 3085 | // been observed at this access site, the read could produce a new type and |
michael@0 | 3086 | // a barrier is needed. Note that this only covers reads from properties |
michael@0 | 3087 | // which are accounted for by type information, i.e. native data properties |
michael@0 | 3088 | // and elements. |
michael@0 | 3089 | // |
michael@0 | 3090 | // We also need a barrier if the object is a proxy, because then all bets |
michael@0 | 3091 | // are off, just as if it has unknown properties. |
michael@0 | 3092 | if (object->unknownProperties() || observed->empty() || |
michael@0 | 3093 | object->clasp()->isProxy()) |
michael@0 | 3094 | { |
michael@0 | 3095 | return true; |
michael@0 | 3096 | } |
michael@0 | 3097 | |
michael@0 | 3098 | jsid id = name ? NameToId(name) : JSID_VOID; |
michael@0 | 3099 | types::HeapTypeSetKey property = object->property(id); |
michael@0 | 3100 | if (property.maybeTypes() && !TypeSetIncludes(observed, MIRType_Value, property.maybeTypes())) |
michael@0 | 3101 | return true; |
michael@0 | 3102 | |
michael@0 | 3103 | // Type information for global objects is not required to reflect the |
michael@0 | 3104 | // initial 'undefined' value for properties, in particular global |
michael@0 | 3105 | // variables declared with 'var'. Until the property is assigned a value |
michael@0 | 3106 | // other than undefined, a barrier is required. |
michael@0 | 3107 | if (JSObject *obj = object->singleton()) { |
michael@0 | 3108 | if (name && types::CanHaveEmptyPropertyTypesForOwnProperty(obj) && |
michael@0 | 3109 | (!property.maybeTypes() || property.maybeTypes()->empty())) |
michael@0 | 3110 | { |
michael@0 | 3111 | return true; |
michael@0 | 3112 | } |
michael@0 | 3113 | } |
michael@0 | 3114 | |
michael@0 | 3115 | property.freeze(constraints); |
michael@0 | 3116 | return false; |
michael@0 | 3117 | } |
michael@0 | 3118 | |
michael@0 | 3119 | bool |
michael@0 | 3120 | jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx, |
michael@0 | 3121 | types::CompilerConstraintList *constraints, |
michael@0 | 3122 | types::TypeObjectKey *object, PropertyName *name, |
michael@0 | 3123 | types::TemporaryTypeSet *observed, bool updateObserved) |
michael@0 | 3124 | { |
michael@0 | 3125 | // If this access has never executed, try to add types to the observed set |
michael@0 | 3126 | // according to any property which exists on the object or its prototype. |
michael@0 | 3127 | if (updateObserved && observed->empty() && name) { |
michael@0 | 3128 | JSObject *obj; |
michael@0 | 3129 | if (object->singleton()) |
michael@0 | 3130 | obj = object->singleton(); |
michael@0 | 3131 | else if (object->hasTenuredProto()) |
michael@0 | 3132 | obj = object->proto().toObjectOrNull(); |
michael@0 | 3133 | else |
michael@0 | 3134 | obj = nullptr; |
michael@0 | 3135 | |
michael@0 | 3136 | while (obj) { |
michael@0 | 3137 | if (!obj->getClass()->isNative()) |
michael@0 | 3138 | break; |
michael@0 | 3139 | |
michael@0 | 3140 | types::TypeObjectKey *typeObj = types::TypeObjectKey::get(obj); |
michael@0 | 3141 | if (propertycx) |
michael@0 | 3142 | typeObj->ensureTrackedProperty(propertycx, NameToId(name)); |
michael@0 | 3143 | |
michael@0 | 3144 | if (!typeObj->unknownProperties()) { |
michael@0 | 3145 | types::HeapTypeSetKey property = typeObj->property(NameToId(name)); |
michael@0 | 3146 | if (property.maybeTypes()) { |
michael@0 | 3147 | types::TypeSet::TypeList types; |
michael@0 | 3148 | if (!property.maybeTypes()->enumerateTypes(&types)) |
michael@0 | 3149 | break; |
michael@0 | 3150 | if (types.length()) { |
michael@0 | 3151 | // Note: the return value here is ignored. |
michael@0 | 3152 | observed->addType(types[0], GetIonContext()->temp->lifoAlloc()); |
michael@0 | 3153 | break; |
michael@0 | 3154 | } |
michael@0 | 3155 | } |
michael@0 | 3156 | } |
michael@0 | 3157 | |
michael@0 | 3158 | if (!obj->hasTenuredProto()) |
michael@0 | 3159 | break; |
michael@0 | 3160 | obj = obj->getProto(); |
michael@0 | 3161 | } |
michael@0 | 3162 | } |
michael@0 | 3163 | |
michael@0 | 3164 | return PropertyReadNeedsTypeBarrier(constraints, object, name, observed); |
michael@0 | 3165 | } |
michael@0 | 3166 | |
michael@0 | 3167 | bool |
michael@0 | 3168 | jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx, |
michael@0 | 3169 | types::CompilerConstraintList *constraints, |
michael@0 | 3170 | MDefinition *obj, PropertyName *name, |
michael@0 | 3171 | types::TemporaryTypeSet *observed) |
michael@0 | 3172 | { |
michael@0 | 3173 | if (observed->unknown()) |
michael@0 | 3174 | return false; |
michael@0 | 3175 | |
michael@0 | 3176 | types::TypeSet *types = obj->resultTypeSet(); |
michael@0 | 3177 | if (!types || types->unknownObject()) |
michael@0 | 3178 | return true; |
michael@0 | 3179 | |
michael@0 | 3180 | bool updateObserved = types->getObjectCount() == 1; |
michael@0 | 3181 | for (size_t i = 0; i < types->getObjectCount(); i++) { |
michael@0 | 3182 | types::TypeObjectKey *object = types->getObject(i); |
michael@0 | 3183 | if (object) { |
michael@0 | 3184 | if (PropertyReadNeedsTypeBarrier(propertycx, constraints, object, name, |
michael@0 | 3185 | observed, updateObserved)) |
michael@0 | 3186 | { |
michael@0 | 3187 | return true; |
michael@0 | 3188 | } |
michael@0 | 3189 | } |
michael@0 | 3190 | } |
michael@0 | 3191 | |
michael@0 | 3192 | return false; |
michael@0 | 3193 | } |
michael@0 | 3194 | |
michael@0 | 3195 | bool |
michael@0 | 3196 | jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints, |
michael@0 | 3197 | MDefinition *obj, PropertyName *name, |
michael@0 | 3198 | types::TemporaryTypeSet *observed) |
michael@0 | 3199 | { |
michael@0 | 3200 | if (observed->unknown()) |
michael@0 | 3201 | return false; |
michael@0 | 3202 | |
michael@0 | 3203 | types::TypeSet *types = obj->resultTypeSet(); |
michael@0 | 3204 | if (!types || types->unknownObject()) |
michael@0 | 3205 | return true; |
michael@0 | 3206 | |
michael@0 | 3207 | for (size_t i = 0; i < types->getObjectCount(); i++) { |
michael@0 | 3208 | types::TypeObjectKey *object = types->getObject(i); |
michael@0 | 3209 | if (!object) |
michael@0 | 3210 | continue; |
michael@0 | 3211 | while (true) { |
michael@0 | 3212 | if (!object->hasTenuredProto()) |
michael@0 | 3213 | return true; |
michael@0 | 3214 | if (!object->proto().isObject()) |
michael@0 | 3215 | break; |
michael@0 | 3216 | object = types::TypeObjectKey::get(object->proto().toObject()); |
michael@0 | 3217 | if (PropertyReadNeedsTypeBarrier(constraints, object, name, observed)) |
michael@0 | 3218 | return true; |
michael@0 | 3219 | } |
michael@0 | 3220 | } |
michael@0 | 3221 | |
michael@0 | 3222 | return false; |
michael@0 | 3223 | } |
michael@0 | 3224 | |
michael@0 | 3225 | bool |
michael@0 | 3226 | jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, |
michael@0 | 3227 | MDefinition *obj, PropertyName *name) |
michael@0 | 3228 | { |
michael@0 | 3229 | // Determine if reading a property from obj is likely to be idempotent. |
michael@0 | 3230 | |
michael@0 | 3231 | types::TypeSet *types = obj->resultTypeSet(); |
michael@0 | 3232 | if (!types || types->unknownObject()) |
michael@0 | 3233 | return false; |
michael@0 | 3234 | |
michael@0 | 3235 | for (size_t i = 0; i < types->getObjectCount(); i++) { |
michael@0 | 3236 | types::TypeObjectKey *object = types->getObject(i); |
michael@0 | 3237 | if (object) { |
michael@0 | 3238 | if (object->unknownProperties()) |
michael@0 | 3239 | return false; |
michael@0 | 3240 | |
michael@0 | 3241 | // Check if the property has been reconfigured or is a getter. |
michael@0 | 3242 | types::HeapTypeSetKey property = object->property(NameToId(name)); |
michael@0 | 3243 | if (property.nonData(constraints)) |
michael@0 | 3244 | return false; |
michael@0 | 3245 | } |
michael@0 | 3246 | } |
michael@0 | 3247 | |
michael@0 | 3248 | return true; |
michael@0 | 3249 | } |
michael@0 | 3250 | |
michael@0 | 3251 | void |
michael@0 | 3252 | jit::AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name, |
michael@0 | 3253 | types::TemporaryTypeSet *observed) |
michael@0 | 3254 | { |
michael@0 | 3255 | // Add objects to observed which *could* be observed by reading name from obj, |
michael@0 | 3256 | // to hopefully avoid unnecessary type barriers and code invalidations. |
michael@0 | 3257 | |
michael@0 | 3258 | LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); |
michael@0 | 3259 | |
michael@0 | 3260 | types::TemporaryTypeSet *types = obj->resultTypeSet(); |
michael@0 | 3261 | if (!types || types->unknownObject()) { |
michael@0 | 3262 | observed->addType(types::Type::AnyObjectType(), alloc); |
michael@0 | 3263 | return; |
michael@0 | 3264 | } |
michael@0 | 3265 | |
michael@0 | 3266 | for (size_t i = 0; i < types->getObjectCount(); i++) { |
michael@0 | 3267 | types::TypeObjectKey *object = types->getObject(i); |
michael@0 | 3268 | if (!object) |
michael@0 | 3269 | continue; |
michael@0 | 3270 | |
michael@0 | 3271 | if (object->unknownProperties()) { |
michael@0 | 3272 | observed->addType(types::Type::AnyObjectType(), alloc); |
michael@0 | 3273 | return; |
michael@0 | 3274 | } |
michael@0 | 3275 | |
michael@0 | 3276 | jsid id = name ? NameToId(name) : JSID_VOID; |
michael@0 | 3277 | types::HeapTypeSetKey property = object->property(id); |
michael@0 | 3278 | types::HeapTypeSet *types = property.maybeTypes(); |
michael@0 | 3279 | if (!types) |
michael@0 | 3280 | continue; |
michael@0 | 3281 | |
michael@0 | 3282 | if (types->unknownObject()) { |
michael@0 | 3283 | observed->addType(types::Type::AnyObjectType(), alloc); |
michael@0 | 3284 | return; |
michael@0 | 3285 | } |
michael@0 | 3286 | |
michael@0 | 3287 | for (size_t i = 0; i < types->getObjectCount(); i++) { |
michael@0 | 3288 | types::TypeObjectKey *object = types->getObject(i); |
michael@0 | 3289 | if (object) |
michael@0 | 3290 | observed->addType(types::Type::ObjectType(object), alloc); |
michael@0 | 3291 | } |
michael@0 | 3292 | } |
michael@0 | 3293 | } |
michael@0 | 3294 | |
michael@0 | 3295 | static bool |
michael@0 | 3296 | TryAddTypeBarrierForWrite(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 3297 | MBasicBlock *current, types::TemporaryTypeSet *objTypes, |
michael@0 | 3298 | PropertyName *name, MDefinition **pvalue) |
michael@0 | 3299 | { |
michael@0 | 3300 | // Return whether pvalue was modified to include a type barrier ensuring |
michael@0 | 3301 | // that writing the value to objTypes/id will not require changing type |
michael@0 | 3302 | // information. |
michael@0 | 3303 | |
michael@0 | 3304 | // All objects in the set must have the same types for name. Otherwise, we |
michael@0 | 3305 | // could bail out without subsequently triggering a type change that |
michael@0 | 3306 | // invalidates the compiled code. |
michael@0 | 3307 | Maybe<types::HeapTypeSetKey> aggregateProperty; |
michael@0 | 3308 | |
michael@0 | 3309 | for (size_t i = 0; i < objTypes->getObjectCount(); i++) { |
michael@0 | 3310 | types::TypeObjectKey *object = objTypes->getObject(i); |
michael@0 | 3311 | if (!object) |
michael@0 | 3312 | continue; |
michael@0 | 3313 | |
michael@0 | 3314 | if (object->unknownProperties()) |
michael@0 | 3315 | return false; |
michael@0 | 3316 | |
michael@0 | 3317 | jsid id = name ? NameToId(name) : JSID_VOID; |
michael@0 | 3318 | types::HeapTypeSetKey property = object->property(id); |
michael@0 | 3319 | if (!property.maybeTypes()) |
michael@0 | 3320 | return false; |
michael@0 | 3321 | |
michael@0 | 3322 | if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) |
michael@0 | 3323 | return false; |
michael@0 | 3324 | |
michael@0 | 3325 | // This freeze is not required for correctness, but ensures that we |
michael@0 | 3326 | // will recompile if the property types change and the barrier can |
michael@0 | 3327 | // potentially be removed. |
michael@0 | 3328 | property.freeze(constraints); |
michael@0 | 3329 | |
michael@0 | 3330 | if (aggregateProperty.empty()) { |
michael@0 | 3331 | aggregateProperty.construct(property); |
michael@0 | 3332 | } else { |
michael@0 | 3333 | if (!aggregateProperty.ref().maybeTypes()->isSubset(property.maybeTypes()) || |
michael@0 | 3334 | !property.maybeTypes()->isSubset(aggregateProperty.ref().maybeTypes())) |
michael@0 | 3335 | { |
michael@0 | 3336 | return false; |
michael@0 | 3337 | } |
michael@0 | 3338 | } |
michael@0 | 3339 | } |
michael@0 | 3340 | |
michael@0 | 3341 | JS_ASSERT(!aggregateProperty.empty()); |
michael@0 | 3342 | |
michael@0 | 3343 | MIRType propertyType = aggregateProperty.ref().knownMIRType(constraints); |
michael@0 | 3344 | switch (propertyType) { |
michael@0 | 3345 | case MIRType_Boolean: |
michael@0 | 3346 | case MIRType_Int32: |
michael@0 | 3347 | case MIRType_Double: |
michael@0 | 3348 | case MIRType_String: { |
michael@0 | 3349 | // The property is a particular primitive type, guard by unboxing the |
michael@0 | 3350 | // value before the write. |
michael@0 | 3351 | if (!(*pvalue)->mightBeType(propertyType)) { |
michael@0 | 3352 | // The value's type does not match the property type. Just do a VM |
michael@0 | 3353 | // call as it will always trigger invalidation of the compiled code. |
michael@0 | 3354 | JS_ASSERT_IF((*pvalue)->type() != MIRType_Value, (*pvalue)->type() != propertyType); |
michael@0 | 3355 | return false; |
michael@0 | 3356 | } |
michael@0 | 3357 | MInstruction *ins = MUnbox::New(alloc, *pvalue, propertyType, MUnbox::Fallible); |
michael@0 | 3358 | current->add(ins); |
michael@0 | 3359 | *pvalue = ins; |
michael@0 | 3360 | return true; |
michael@0 | 3361 | } |
michael@0 | 3362 | default:; |
michael@0 | 3363 | } |
michael@0 | 3364 | |
michael@0 | 3365 | if ((*pvalue)->type() != MIRType_Value) |
michael@0 | 3366 | return false; |
michael@0 | 3367 | |
michael@0 | 3368 | types::TemporaryTypeSet *types = aggregateProperty.ref().maybeTypes()->clone(alloc.lifoAlloc()); |
michael@0 | 3369 | if (!types) |
michael@0 | 3370 | return false; |
michael@0 | 3371 | |
michael@0 | 3372 | MInstruction *ins = MMonitorTypes::New(alloc, *pvalue, types); |
michael@0 | 3373 | current->add(ins); |
michael@0 | 3374 | return true; |
michael@0 | 3375 | } |
michael@0 | 3376 | |
michael@0 | 3377 | static MInstruction * |
michael@0 | 3378 | AddTypeGuard(TempAllocator &alloc, MBasicBlock *current, MDefinition *obj, |
michael@0 | 3379 | types::TypeObjectKey *type, bool bailOnEquality) |
michael@0 | 3380 | { |
michael@0 | 3381 | MInstruction *guard; |
michael@0 | 3382 | |
michael@0 | 3383 | if (type->isTypeObject()) |
michael@0 | 3384 | guard = MGuardObjectType::New(alloc, obj, type->asTypeObject(), bailOnEquality); |
michael@0 | 3385 | else |
michael@0 | 3386 | guard = MGuardObjectIdentity::New(alloc, obj, type->asSingleObject(), bailOnEquality); |
michael@0 | 3387 | |
michael@0 | 3388 | current->add(guard); |
michael@0 | 3389 | |
michael@0 | 3390 | // For now, never move type object guards. |
michael@0 | 3391 | guard->setNotMovable(); |
michael@0 | 3392 | |
michael@0 | 3393 | return guard; |
michael@0 | 3394 | } |
michael@0 | 3395 | |
michael@0 | 3396 | bool |
michael@0 | 3397 | jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 3398 | MBasicBlock *current, MDefinition **pobj, |
michael@0 | 3399 | PropertyName *name, MDefinition **pvalue, bool canModify) |
michael@0 | 3400 | { |
michael@0 | 3401 | // If any value being written is not reflected in the type information for |
michael@0 | 3402 | // objects which obj could represent, a type barrier is needed when writing |
michael@0 | 3403 | // the value. As for propertyReadNeedsTypeBarrier, this only applies for |
michael@0 | 3404 | // properties that are accounted for by type information, i.e. normal data |
michael@0 | 3405 | // properties and elements. |
michael@0 | 3406 | |
michael@0 | 3407 | types::TemporaryTypeSet *types = (*pobj)->resultTypeSet(); |
michael@0 | 3408 | if (!types || types->unknownObject()) |
michael@0 | 3409 | return true; |
michael@0 | 3410 | |
michael@0 | 3411 | // If all of the objects being written to have property types which already |
michael@0 | 3412 | // reflect the value, no barrier at all is needed. Additionally, if all |
michael@0 | 3413 | // objects being written to have the same types for the property, and those |
michael@0 | 3414 | // types do *not* reflect the value, add a type barrier for the value. |
michael@0 | 3415 | |
michael@0 | 3416 | bool success = true; |
michael@0 | 3417 | for (size_t i = 0; i < types->getObjectCount(); i++) { |
michael@0 | 3418 | types::TypeObjectKey *object = types->getObject(i); |
michael@0 | 3419 | if (!object || object->unknownProperties()) |
michael@0 | 3420 | continue; |
michael@0 | 3421 | |
michael@0 | 3422 | // TI doesn't track TypedArray objects and should never insert a type |
michael@0 | 3423 | // barrier for them. |
michael@0 | 3424 | if (!name && IsTypedArrayClass(object->clasp())) |
michael@0 | 3425 | continue; |
michael@0 | 3426 | |
michael@0 | 3427 | jsid id = name ? NameToId(name) : JSID_VOID; |
michael@0 | 3428 | types::HeapTypeSetKey property = object->property(id); |
michael@0 | 3429 | if (!TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) { |
michael@0 | 3430 | // Either pobj or pvalue needs to be modified to filter out the |
michael@0 | 3431 | // types which the value could have but are not in the property, |
michael@0 | 3432 | // or a VM call is required. A VM call is always required if pobj |
michael@0 | 3433 | // and pvalue cannot be modified. |
michael@0 | 3434 | if (!canModify) |
michael@0 | 3435 | return true; |
michael@0 | 3436 | success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue); |
michael@0 | 3437 | break; |
michael@0 | 3438 | } |
michael@0 | 3439 | } |
michael@0 | 3440 | |
michael@0 | 3441 | if (success) |
michael@0 | 3442 | return false; |
michael@0 | 3443 | |
michael@0 | 3444 | // If all of the objects except one have property types which reflect the |
michael@0 | 3445 | // value, and the remaining object has no types at all for the property, |
michael@0 | 3446 | // add a guard that the object does not have that remaining object's type. |
michael@0 | 3447 | |
michael@0 | 3448 | if (types->getObjectCount() <= 1) |
michael@0 | 3449 | return true; |
michael@0 | 3450 | |
michael@0 | 3451 | types::TypeObjectKey *excluded = nullptr; |
michael@0 | 3452 | for (size_t i = 0; i < types->getObjectCount(); i++) { |
michael@0 | 3453 | types::TypeObjectKey *object = types->getObject(i); |
michael@0 | 3454 | if (!object || object->unknownProperties()) |
michael@0 | 3455 | continue; |
michael@0 | 3456 | if (!name && IsTypedArrayClass(object->clasp())) |
michael@0 | 3457 | continue; |
michael@0 | 3458 | |
michael@0 | 3459 | jsid id = name ? NameToId(name) : JSID_VOID; |
michael@0 | 3460 | types::HeapTypeSetKey property = object->property(id); |
michael@0 | 3461 | if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) |
michael@0 | 3462 | continue; |
michael@0 | 3463 | |
michael@0 | 3464 | if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded) |
michael@0 | 3465 | return true; |
michael@0 | 3466 | excluded = object; |
michael@0 | 3467 | } |
michael@0 | 3468 | |
michael@0 | 3469 | JS_ASSERT(excluded); |
michael@0 | 3470 | |
michael@0 | 3471 | *pobj = AddTypeGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true); |
michael@0 | 3472 | return false; |
michael@0 | 3473 | } |