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