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