tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }