js/src/jit/MIR.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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 }

mercurial