IonIC.cpp (22298B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "jit/IonIC.h" 8 9 #include "jit/CacheIRCompiler.h" 10 #include "jit/CacheIRGenerator.h" 11 #include "jit/IonScript.h" 12 #include "jit/VMFunctions.h" 13 #include "util/DiagnosticAssertions.h" 14 #include "vm/EqualityOperations.h" 15 #include "vm/Iteration.h" 16 17 #include "vm/Interpreter-inl.h" 18 #include "vm/JSScript-inl.h" 19 20 using namespace js; 21 using namespace js::jit; 22 23 void IonIC::resetCodeRaw(IonScript* ionScript) { 24 codeRaw_ = fallbackAddr(ionScript); 25 } 26 27 uint8_t* IonIC::fallbackAddr(IonScript* ionScript) const { 28 return ionScript->method()->raw() + fallbackOffset_; 29 } 30 31 uint8_t* IonIC::rejoinAddr(IonScript* ionScript) const { 32 return ionScript->method()->raw() + rejoinOffset_; 33 } 34 35 Register IonIC::scratchRegisterForEntryJump() { 36 switch (kind_) { 37 case CacheKind::GetProp: 38 case CacheKind::GetElem: 39 return asGetPropertyIC()->output().scratchReg(); 40 case CacheKind::GetPropSuper: 41 case CacheKind::GetElemSuper: 42 return asGetPropSuperIC()->output().scratchReg(); 43 case CacheKind::SetProp: 44 case CacheKind::SetElem: 45 return asSetPropertyIC()->temp(); 46 case CacheKind::GetName: 47 return asGetNameIC()->temp(); 48 case CacheKind::BindName: 49 return asBindNameIC()->temp(); 50 case CacheKind::In: 51 return asInIC()->temp(); 52 case CacheKind::HasOwn: 53 return asHasOwnIC()->output(); 54 case CacheKind::CheckPrivateField: 55 return asCheckPrivateFieldIC()->output(); 56 case CacheKind::GetIterator: 57 return asGetIteratorIC()->temp1(); 58 case CacheKind::OptimizeSpreadCall: 59 return asOptimizeSpreadCallIC()->temp(); 60 case CacheKind::InstanceOf: 61 return asInstanceOfIC()->output(); 62 case CacheKind::UnaryArith: 63 return asUnaryArithIC()->output().scratchReg(); 64 case CacheKind::ToPropertyKey: 65 return asToPropertyKeyIC()->output().scratchReg(); 66 case CacheKind::BinaryArith: 67 return asBinaryArithIC()->output().scratchReg(); 68 case CacheKind::Compare: 69 return asCompareIC()->output(); 70 case CacheKind::CloseIter: 71 return asCloseIterIC()->temp(); 72 case CacheKind::OptimizeGetIterator: 73 return asOptimizeGetIteratorIC()->temp(); 74 case CacheKind::Call: 75 case CacheKind::TypeOf: 76 case CacheKind::TypeOfEq: 77 case CacheKind::ToBool: 78 case CacheKind::LazyConstant: 79 case CacheKind::NewArray: 80 case CacheKind::NewObject: 81 case CacheKind::Lambda: 82 case CacheKind::GetImport: 83 MOZ_CRASH("Unsupported IC"); 84 } 85 86 MOZ_CRASH("Invalid kind"); 87 } 88 89 void IonIC::discardStubs(Zone* zone, IonScript* ionScript) { 90 if (firstStub_) { 91 // We are removing edges from IonIC to gcthings. Perform a write barrier to 92 // let the GC know about those edges. 93 PreWriteBarrier(zone, ionScript); 94 } 95 96 #ifdef JS_CRASH_DIAGNOSTICS 97 IonICStub* stub = firstStub_; 98 while (stub) { 99 IonICStub* next = stub->next(); 100 stub->poison(); 101 stub = next; 102 } 103 #endif 104 105 firstStub_ = nullptr; 106 resetCodeRaw(ionScript); 107 state_.trackUnlinkedAllStubs(); 108 } 109 110 void IonIC::reset(Zone* zone, IonScript* ionScript) { 111 discardStubs(zone, ionScript); 112 state_.reset(); 113 } 114 115 void IonIC::trace(JSTracer* trc, IonScript* ionScript) { 116 if (script_) { 117 TraceManuallyBarrieredEdge(trc, &script_, "IonIC::script_"); 118 } 119 120 uint8_t* nextCodeRaw = codeRaw_; 121 for (IonICStub* stub = firstStub_; stub; stub = stub->next()) { 122 JitCode* code = JitCode::FromExecutable(nextCodeRaw); 123 TraceManuallyBarrieredEdge(trc, &code, "ion-ic-code"); 124 125 TraceCacheIRStub(trc, stub, stub->stubInfo()); 126 127 nextCodeRaw = stub->nextCodeRaw(); 128 } 129 130 MOZ_ASSERT(nextCodeRaw == fallbackAddr(ionScript)); 131 } 132 133 // This helper handles ICState updates/transitions while attaching CacheIR 134 // stubs. 135 template <typename IRGenerator, typename... Args> 136 static void TryAttachIonStub(JSContext* cx, IonIC* ic, IonScript* ionScript, 137 Args&&... args) { 138 if (ic->state().maybeTransition()) { 139 ic->discardStubs(cx->zone(), ionScript); 140 } 141 142 if (ic->state().canAttachStub()) { 143 RootedScript script(cx, ic->script()); 144 bool attached = false; 145 IRGenerator gen(cx, script, ic->pc(), ic->state(), 146 std::forward<Args>(args)...); 147 switch (gen.tryAttachStub()) { 148 case AttachDecision::Attach: 149 ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, 150 &attached); 151 break; 152 case AttachDecision::NoAction: 153 break; 154 case AttachDecision::TemporarilyUnoptimizable: 155 attached = true; 156 break; 157 case AttachDecision::Deferred: 158 MOZ_ASSERT_UNREACHABLE("Not expected in generic TryAttachIonStub"); 159 break; 160 } 161 if (!attached) { 162 ic->state().trackNotAttached(); 163 } 164 } 165 } 166 167 /* static */ 168 bool IonGetPropertyIC::update(JSContext* cx, HandleScript outerScript, 169 IonGetPropertyIC* ic, HandleValue val, 170 HandleValue idVal, MutableHandleValue res) { 171 IonScript* ionScript = outerScript->ionScript(); 172 173 // Optimized-arguments and other magic values must not escape to Ion ICs. 174 MOZ_ASSERT(!val.isMagic()); 175 176 TryAttachIonStub<GetPropIRGenerator>(cx, ic, ionScript, ic->kind(), val, 177 idVal, val); 178 179 if (ic->kind() == CacheKind::GetProp) { 180 Rooted<PropertyName*> name(cx, idVal.toString()->asAtom().asPropertyName()); 181 182 JSOp op = JSOp(*ic->pc()); 183 if (op == JSOp::GetBoundName) { 184 RootedObject env(cx, &val.toObject()); 185 RootedId id(cx, NameToId(name)); 186 if (!GetNameBoundInEnvironment(cx, env, id, res)) { 187 return false; 188 } 189 } else { 190 MOZ_ASSERT(op == JSOp::GetProp || op == JSOp::GetElem); 191 192 if (!GetProperty(cx, val, name, res)) { 193 return false; 194 } 195 } 196 } else { 197 MOZ_ASSERT(ic->kind() == CacheKind::GetElem); 198 MOZ_ASSERT(JSOp(*ic->pc()) == JSOp::GetElem); 199 200 if (!GetElementOperation(cx, val, idVal, res)) { 201 return false; 202 } 203 } 204 205 return true; 206 } 207 208 /* static */ 209 bool IonGetPropSuperIC::update(JSContext* cx, HandleScript outerScript, 210 IonGetPropSuperIC* ic, HandleObject obj, 211 HandleValue receiver, HandleValue idVal, 212 MutableHandleValue res) { 213 IonScript* ionScript = outerScript->ionScript(); 214 215 if (ic->state().maybeTransition()) { 216 ic->discardStubs(cx->zone(), ionScript); 217 } 218 219 RootedValue val(cx, ObjectValue(*obj)); 220 221 TryAttachIonStub<GetPropIRGenerator>(cx, ic, ionScript, ic->kind(), val, 222 idVal, receiver); 223 224 if (ic->kind() == CacheKind::GetPropSuper) { 225 Rooted<PropertyName*> name(cx, idVal.toString()->asAtom().asPropertyName()); 226 if (!GetProperty(cx, obj, receiver, name, res)) { 227 return false; 228 } 229 } else { 230 MOZ_ASSERT(ic->kind() == CacheKind::GetElemSuper); 231 232 JSOp op = JSOp(*ic->pc()); 233 MOZ_ASSERT(op == JSOp::GetElemSuper); 234 235 if (!GetObjectElementOperation(cx, op, obj, receiver, idVal, res)) { 236 return false; 237 } 238 } 239 240 return true; 241 } 242 243 /* static */ 244 bool IonSetPropertyIC::update(JSContext* cx, HandleScript outerScript, 245 IonSetPropertyIC* ic, HandleObject obj, 246 HandleValue idVal, HandleValue rhs) { 247 using DeferType = SetPropIRGenerator::DeferType; 248 249 Rooted<Shape*> oldShape(cx); 250 IonScript* ionScript = outerScript->ionScript(); 251 252 bool attached = false; 253 DeferType deferType = DeferType::None; 254 255 if (ic->state().maybeTransition()) { 256 ic->discardStubs(cx->zone(), ionScript); 257 } 258 259 if (ic->state().canAttachStub()) { 260 oldShape = obj->shape(); 261 262 RootedValue objv(cx, ObjectValue(*obj)); 263 RootedScript script(cx, ic->script()); 264 jsbytecode* pc = ic->pc(); 265 266 SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state(), objv, idVal, 267 rhs); 268 switch (gen.tryAttachStub()) { 269 case AttachDecision::Attach: 270 ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, 271 &attached); 272 break; 273 case AttachDecision::NoAction: 274 break; 275 case AttachDecision::TemporarilyUnoptimizable: 276 attached = true; 277 break; 278 case AttachDecision::Deferred: 279 deferType = gen.deferType(); 280 MOZ_ASSERT(deferType != DeferType::None); 281 break; 282 } 283 if (deferType == DeferType::None && !attached) { 284 ic->state().trackNotAttached(); 285 } 286 } 287 288 jsbytecode* pc = ic->pc(); 289 if (ic->kind() == CacheKind::SetElem) { 290 if (JSOp(*pc) == JSOp::InitElemInc) { 291 if (!InitElemIncOperation(cx, obj.as<ArrayObject>(), idVal.toInt32(), 292 rhs)) { 293 return false; 294 } 295 } else if (IsPropertyInitOp(JSOp(*pc))) { 296 if (!InitElemOperation(cx, pc, obj, idVal, rhs)) { 297 return false; 298 } 299 } else { 300 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc))); 301 if (!SetObjectElement(cx, obj, idVal, rhs, ic->strict())) { 302 return false; 303 } 304 } 305 } else { 306 MOZ_ASSERT(ic->kind() == CacheKind::SetProp); 307 308 if (JSOp(*pc) == JSOp::InitGLexical) { 309 RootedScript script(cx, ic->script()); 310 MOZ_ASSERT(!script->hasNonSyntacticScope()); 311 InitGlobalLexicalOperation(cx, &cx->global()->lexicalEnvironment(), 312 script, pc, rhs); 313 } else if (IsPropertyInitOp(JSOp(*pc))) { 314 Rooted<PropertyName*> name(cx, 315 idVal.toString()->asAtom().asPropertyName()); 316 if (!InitPropertyOperation(cx, pc, obj, name, rhs)) { 317 return false; 318 } 319 } else { 320 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc))); 321 Rooted<PropertyName*> name(cx, 322 idVal.toString()->asAtom().asPropertyName()); 323 if (!SetProperty(cx, obj, name, rhs, ic->strict(), pc)) { 324 return false; 325 } 326 } 327 } 328 329 if (attached) { 330 return true; 331 } 332 333 // The SetProperty call might have entered this IC recursively, so try 334 // to transition. 335 if (ic->state().maybeTransition()) { 336 ic->discardStubs(cx->zone(), ionScript); 337 } 338 339 bool canAttachStub = ic->state().canAttachStub(); 340 if (deferType != DeferType::None && canAttachStub) { 341 RootedValue objv(cx, ObjectValue(*obj)); 342 RootedScript script(cx, ic->script()); 343 jsbytecode* pc = ic->pc(); 344 SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state(), objv, idVal, 345 rhs); 346 MOZ_ASSERT(deferType == DeferType::AddSlot); 347 AttachDecision decision = gen.tryAttachAddSlotStub(oldShape); 348 349 switch (decision) { 350 case AttachDecision::Attach: 351 ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, 352 &attached); 353 break; 354 case AttachDecision::NoAction: 355 gen.trackAttached(IRGenerator::NotAttached); 356 break; 357 case AttachDecision::TemporarilyUnoptimizable: 358 case AttachDecision::Deferred: 359 MOZ_ASSERT_UNREACHABLE("Invalid attach result"); 360 break; 361 } 362 if (!attached) { 363 ic->state().trackNotAttached(); 364 } 365 } 366 367 return true; 368 } 369 370 /* static */ 371 bool IonGetNameIC::update(JSContext* cx, HandleScript outerScript, 372 IonGetNameIC* ic, HandleObject envChain, 373 MutableHandleValue res) { 374 IonScript* ionScript = outerScript->ionScript(); 375 jsbytecode* pc = ic->pc(); 376 Rooted<PropertyName*> name(cx, ic->script()->getName(pc)); 377 378 TryAttachIonStub<GetNameIRGenerator>(cx, ic, ionScript, envChain, name); 379 380 RootedObject obj(cx); 381 RootedObject holder(cx); 382 PropertyResult prop; 383 if (!LookupName(cx, name, envChain, &obj, &holder, &prop)) { 384 return false; 385 } 386 387 if (IsTypeOfNameOp(JSOp(*GetNextPc(pc)))) { 388 return FetchName<GetNameMode::TypeOf>(cx, obj, holder, name, prop, res); 389 } 390 391 return FetchName<GetNameMode::Normal>(cx, obj, holder, name, prop, res); 392 } 393 394 /* static */ 395 JSObject* IonBindNameIC::update(JSContext* cx, HandleScript outerScript, 396 IonBindNameIC* ic, HandleObject envChain) { 397 IonScript* ionScript = outerScript->ionScript(); 398 jsbytecode* pc = ic->pc(); 399 JSOp op = JSOp(*pc); 400 MOZ_ASSERT(op == JSOp::BindName || op == JSOp::BindUnqualifiedName || 401 op == JSOp::BindUnqualifiedGName); 402 403 Rooted<PropertyName*> name(cx, ic->script()->getName(pc)); 404 405 TryAttachIonStub<BindNameIRGenerator>(cx, ic, ionScript, envChain, name); 406 407 if (op == JSOp::BindName) { 408 return LookupNameWithGlobalDefault(cx, name, envChain); 409 } 410 return LookupNameUnqualified(cx, name, envChain); 411 } 412 413 /* static */ 414 JSObject* IonGetIteratorIC::update(JSContext* cx, HandleScript outerScript, 415 IonGetIteratorIC* ic, HandleValue value) { 416 IonScript* ionScript = outerScript->ionScript(); 417 418 TryAttachIonStub<GetIteratorIRGenerator>(cx, ic, ionScript, value); 419 420 return ValueToIterator(cx, value); 421 } 422 423 /* static */ 424 bool IonOptimizeSpreadCallIC::update(JSContext* cx, HandleScript outerScript, 425 IonOptimizeSpreadCallIC* ic, 426 HandleValue value, 427 MutableHandleValue result) { 428 IonScript* ionScript = outerScript->ionScript(); 429 430 TryAttachIonStub<OptimizeSpreadCallIRGenerator>(cx, ic, ionScript, value); 431 432 return OptimizeSpreadCall(cx, value, result); 433 } 434 435 /* static */ 436 bool IonHasOwnIC::update(JSContext* cx, HandleScript outerScript, 437 IonHasOwnIC* ic, HandleValue val, HandleValue idVal, 438 int32_t* res) { 439 IonScript* ionScript = outerScript->ionScript(); 440 441 TryAttachIonStub<HasPropIRGenerator>(cx, ic, ionScript, CacheKind::HasOwn, 442 idVal, val); 443 444 bool found; 445 if (!HasOwnProperty(cx, val, idVal, &found)) { 446 return false; 447 } 448 449 *res = found; 450 return true; 451 } 452 453 /* static */ 454 bool IonCheckPrivateFieldIC::update(JSContext* cx, HandleScript outerScript, 455 IonCheckPrivateFieldIC* ic, HandleValue val, 456 HandleValue idVal, bool* res) { 457 IonScript* ionScript = outerScript->ionScript(); 458 jsbytecode* pc = ic->pc(); 459 460 TryAttachIonStub<CheckPrivateFieldIRGenerator>( 461 cx, ic, ionScript, CacheKind::CheckPrivateField, idVal, val); 462 463 return CheckPrivateFieldOperation(cx, pc, val, idVal, res); 464 } 465 466 /* static */ 467 bool IonInIC::update(JSContext* cx, HandleScript outerScript, IonInIC* ic, 468 HandleValue key, HandleObject obj, bool* res) { 469 IonScript* ionScript = outerScript->ionScript(); 470 RootedValue objV(cx, ObjectValue(*obj)); 471 472 TryAttachIonStub<HasPropIRGenerator>(cx, ic, ionScript, CacheKind::In, key, 473 objV); 474 475 return OperatorIn(cx, key, obj, res); 476 } 477 /* static */ 478 bool IonInstanceOfIC::update(JSContext* cx, HandleScript outerScript, 479 IonInstanceOfIC* ic, HandleValue lhs, 480 HandleObject rhs, bool* res) { 481 IonScript* ionScript = outerScript->ionScript(); 482 483 TryAttachIonStub<InstanceOfIRGenerator>(cx, ic, ionScript, lhs, rhs); 484 485 return InstanceofOperator(cx, rhs, lhs, res); 486 } 487 488 /* static */ 489 bool IonToPropertyKeyIC::update(JSContext* cx, HandleScript outerScript, 490 IonToPropertyKeyIC* ic, HandleValue val, 491 MutableHandleValue res) { 492 IonScript* ionScript = outerScript->ionScript(); 493 494 TryAttachIonStub<ToPropertyKeyIRGenerator>(cx, ic, ionScript, val); 495 496 return ToPropertyKeyOperation(cx, val, res); 497 } 498 499 /* static */ 500 bool IonCloseIterIC::update(JSContext* cx, HandleScript outerScript, 501 IonCloseIterIC* ic, HandleObject iter) { 502 IonScript* ionScript = outerScript->ionScript(); 503 CompletionKind kind = ic->completionKind(); 504 505 TryAttachIonStub<CloseIterIRGenerator>(cx, ic, ionScript, iter, kind); 506 507 return CloseIterOperation(cx, iter, kind); 508 } 509 510 /* static */ 511 bool IonOptimizeGetIteratorIC::update(JSContext* cx, HandleScript outerScript, 512 IonOptimizeGetIteratorIC* ic, 513 HandleValue value, bool* result) { 514 IonScript* ionScript = outerScript->ionScript(); 515 516 TryAttachIonStub<OptimizeGetIteratorIRGenerator>(cx, ic, ionScript, value); 517 518 *result = OptimizeGetIterator(value, cx); 519 return true; 520 } 521 522 /* static */ 523 bool IonUnaryArithIC::update(JSContext* cx, HandleScript outerScript, 524 IonUnaryArithIC* ic, HandleValue val, 525 MutableHandleValue res) { 526 IonScript* ionScript = outerScript->ionScript(); 527 RootedScript script(cx, ic->script()); 528 jsbytecode* pc = ic->pc(); 529 JSOp op = JSOp(*pc); 530 531 switch (op) { 532 case JSOp::BitNot: { 533 res.set(val); 534 if (!BitNot(cx, res, res)) { 535 return false; 536 } 537 break; 538 } 539 case JSOp::Pos: { 540 res.set(val); 541 if (!ToNumber(cx, res)) { 542 return false; 543 } 544 break; 545 } 546 case JSOp::Neg: { 547 res.set(val); 548 if (!NegOperation(cx, res, res)) { 549 return false; 550 } 551 break; 552 } 553 case JSOp::Inc: { 554 if (!IncOperation(cx, val, res)) { 555 return false; 556 } 557 break; 558 } 559 case JSOp::Dec: { 560 if (!DecOperation(cx, val, res)) { 561 return false; 562 } 563 break; 564 } 565 case JSOp::ToNumeric: { 566 res.set(val); 567 if (!ToNumeric(cx, res)) { 568 return false; 569 } 570 break; 571 } 572 default: 573 MOZ_CRASH("Unexpected op"); 574 } 575 MOZ_ASSERT(res.isNumeric()); 576 577 TryAttachIonStub<UnaryArithIRGenerator>(cx, ic, ionScript, op, val, res); 578 579 return true; 580 } 581 582 /* static */ 583 bool IonBinaryArithIC::update(JSContext* cx, HandleScript outerScript, 584 IonBinaryArithIC* ic, HandleValue lhs, 585 HandleValue rhs, MutableHandleValue ret) { 586 IonScript* ionScript = outerScript->ionScript(); 587 RootedScript script(cx, ic->script()); 588 jsbytecode* pc = ic->pc(); 589 JSOp op = JSOp(*pc); 590 591 // Don't pass lhs/rhs directly, we need the original values when 592 // generating stubs. 593 RootedValue lhsCopy(cx, lhs); 594 RootedValue rhsCopy(cx, rhs); 595 596 // Perform the compare operation. 597 switch (op) { 598 case JSOp::Add: 599 // Do an add. 600 if (!AddValues(cx, &lhsCopy, &rhsCopy, ret)) { 601 return false; 602 } 603 break; 604 case JSOp::Sub: 605 if (!SubValues(cx, &lhsCopy, &rhsCopy, ret)) { 606 return false; 607 } 608 break; 609 case JSOp::Mul: 610 if (!MulValues(cx, &lhsCopy, &rhsCopy, ret)) { 611 return false; 612 } 613 break; 614 case JSOp::Div: 615 if (!DivValues(cx, &lhsCopy, &rhsCopy, ret)) { 616 return false; 617 } 618 break; 619 case JSOp::Mod: 620 if (!ModValues(cx, &lhsCopy, &rhsCopy, ret)) { 621 return false; 622 } 623 break; 624 case JSOp::Pow: 625 if (!PowValues(cx, &lhsCopy, &rhsCopy, ret)) { 626 return false; 627 } 628 break; 629 case JSOp::BitOr: { 630 if (!BitOr(cx, &lhsCopy, &rhsCopy, ret)) { 631 return false; 632 } 633 break; 634 } 635 case JSOp::BitXor: { 636 if (!BitXor(cx, &lhsCopy, &rhsCopy, ret)) { 637 return false; 638 } 639 break; 640 } 641 case JSOp::BitAnd: { 642 if (!BitAnd(cx, &lhsCopy, &rhsCopy, ret)) { 643 return false; 644 } 645 break; 646 } 647 case JSOp::Lsh: { 648 if (!BitLsh(cx, &lhsCopy, &rhsCopy, ret)) { 649 return false; 650 } 651 break; 652 } 653 case JSOp::Rsh: { 654 if (!BitRsh(cx, &lhsCopy, &rhsCopy, ret)) { 655 return false; 656 } 657 break; 658 } 659 case JSOp::Ursh: { 660 if (!UrshValues(cx, &lhsCopy, &rhsCopy, ret)) { 661 return false; 662 } 663 break; 664 } 665 default: 666 MOZ_CRASH("Unhandled binary arith op"); 667 } 668 669 TryAttachIonStub<BinaryArithIRGenerator>(cx, ic, ionScript, op, lhs, rhs, 670 ret); 671 672 return true; 673 } 674 675 /* static */ 676 bool IonCompareIC::update(JSContext* cx, HandleScript outerScript, 677 IonCompareIC* ic, HandleValue lhs, HandleValue rhs, 678 bool* res) { 679 IonScript* ionScript = outerScript->ionScript(); 680 RootedScript script(cx, ic->script()); 681 jsbytecode* pc = ic->pc(); 682 JSOp op = JSOp(*pc); 683 684 // Don't pass lhs/rhs directly, we need the original values when 685 // generating stubs. 686 RootedValue lhsCopy(cx, lhs); 687 RootedValue rhsCopy(cx, rhs); 688 689 // Perform the compare operation. 690 switch (op) { 691 case JSOp::Lt: 692 if (!LessThan(cx, &lhsCopy, &rhsCopy, res)) { 693 return false; 694 } 695 break; 696 case JSOp::Le: 697 if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, res)) { 698 return false; 699 } 700 break; 701 case JSOp::Gt: 702 if (!GreaterThan(cx, &lhsCopy, &rhsCopy, res)) { 703 return false; 704 } 705 break; 706 case JSOp::Ge: 707 if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, res)) { 708 return false; 709 } 710 break; 711 case JSOp::Eq: 712 if (!js::LooselyEqual(cx, lhsCopy, rhsCopy, res)) { 713 return false; 714 } 715 break; 716 case JSOp::Ne: 717 if (!js::LooselyEqual(cx, lhsCopy, rhsCopy, res)) { 718 return false; 719 } 720 *res = !*res; 721 break; 722 case JSOp::StrictEq: 723 if (!js::StrictlyEqual(cx, lhsCopy, rhsCopy, res)) { 724 return false; 725 } 726 break; 727 case JSOp::StrictNe: 728 if (!js::StrictlyEqual(cx, lhsCopy, rhsCopy, res)) { 729 return false; 730 } 731 *res = !*res; 732 break; 733 default: 734 MOZ_ASSERT_UNREACHABLE("Unhandled ion compare op"); 735 return false; 736 } 737 738 TryAttachIonStub<CompareIRGenerator>(cx, ic, ionScript, op, lhs, rhs); 739 740 return true; 741 } 742 743 uint8_t* IonICStub::stubDataStart() { 744 return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset(); 745 } 746 747 void IonIC::attachStub(IonICStub* newStub, JitCode* code) { 748 MOZ_ASSERT(newStub); 749 MOZ_ASSERT(code); 750 751 if (firstStub_) { 752 newStub->setNext(firstStub_, codeRaw_); 753 } 754 firstStub_ = newStub; 755 codeRaw_ = code->raw(); 756 757 state_.trackAttached(); 758 }