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/. */
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"
17 #include "vm/Interpreter-inl.h"
18 #include "vm/JSScript-inl.h"
21 using namespace js::jit
;
23 void IonIC::resetCodeRaw(IonScript
* ionScript
) {
24 codeRaw_
= fallbackAddr(ionScript
);
27 uint8_t* IonIC::fallbackAddr(IonScript
* ionScript
) const {
28 return ionScript
->method()->raw() + fallbackOffset_
;
31 uint8_t* IonIC::rejoinAddr(IonScript
* ionScript
) const {
32 return ionScript
->method()->raw() + rejoinOffset_
;
35 Register
IonIC::scratchRegisterForEntryJump() {
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();
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();
75 case CacheKind::TypeOf
:
76 case CacheKind::ToBool
:
77 case CacheKind::GetIntrinsic
:
78 case CacheKind::NewArray
:
79 case CacheKind::NewObject
:
80 MOZ_CRASH("Unsupported IC");
83 MOZ_CRASH("Invalid kind");
86 void IonIC::discardStubs(Zone
* zone
, IonScript
* ionScript
) {
88 // We are removing edges from IonIC to gcthings. Perform a write barrier to
89 // let the GC know about those edges.
90 PreWriteBarrier(zone
, ionScript
);
93 #ifdef JS_CRASH_DIAGNOSTICS
94 IonICStub
* stub
= firstStub_
;
96 IonICStub
* next
= stub
->next();
102 firstStub_
= nullptr;
103 resetCodeRaw(ionScript
);
104 state_
.trackUnlinkedAllStubs();
107 void IonIC::reset(Zone
* zone
, IonScript
* ionScript
) {
108 discardStubs(zone
, ionScript
);
112 void IonIC::trace(JSTracer
* trc
, IonScript
* ionScript
) {
114 TraceManuallyBarrieredEdge(trc
, &script_
, "IonIC::script_");
117 uint8_t* nextCodeRaw
= codeRaw_
;
118 for (IonICStub
* stub
= firstStub_
; stub
; stub
= stub
->next()) {
119 JitCode
* code
= JitCode::FromExecutable(nextCodeRaw
);
120 TraceManuallyBarrieredEdge(trc
, &code
, "ion-ic-code");
122 TraceCacheIRStub(trc
, stub
, stub
->stubInfo());
124 nextCodeRaw
= stub
->nextCodeRaw();
127 MOZ_ASSERT(nextCodeRaw
== fallbackAddr(ionScript
));
130 // This helper handles ICState updates/transitions while attaching CacheIR
132 template <typename IRGenerator
, typename
... Args
>
133 static void TryAttachIonStub(JSContext
* cx
, IonIC
* ic
, IonScript
* ionScript
,
135 if (ic
->state().maybeTransition()) {
136 ic
->discardStubs(cx
->zone(), ionScript
);
139 if (ic
->state().canAttachStub()) {
140 RootedScript
script(cx
, ic
->script());
141 bool attached
= false;
142 IRGenerator
gen(cx
, script
, ic
->pc(), ic
->state(),
143 std::forward
<Args
>(args
)...);
144 switch (gen
.tryAttachStub()) {
145 case AttachDecision::Attach
:
146 ic
->attachCacheIRStub(cx
, gen
.writerRef(), gen
.cacheKind(), ionScript
,
149 case AttachDecision::NoAction
:
151 case AttachDecision::TemporarilyUnoptimizable
:
154 case AttachDecision::Deferred
:
155 MOZ_ASSERT_UNREACHABLE("Not expected in generic TryAttachIonStub");
159 ic
->state().trackNotAttached();
165 bool IonGetPropertyIC::update(JSContext
* cx
, HandleScript outerScript
,
166 IonGetPropertyIC
* ic
, HandleValue val
,
167 HandleValue idVal
, MutableHandleValue res
) {
168 IonScript
* ionScript
= outerScript
->ionScript();
170 // Optimized-arguments and other magic values must not escape to Ion ICs.
171 MOZ_ASSERT(!val
.isMagic());
173 TryAttachIonStub
<GetPropIRGenerator
>(cx
, ic
, ionScript
, ic
->kind(), val
,
176 if (ic
->kind() == CacheKind::GetProp
) {
177 Rooted
<PropertyName
*> name(cx
, idVal
.toString()->asAtom().asPropertyName());
178 if (!GetProperty(cx
, val
, name
, res
)) {
182 MOZ_ASSERT(ic
->kind() == CacheKind::GetElem
);
183 if (!GetElementOperation(cx
, val
, idVal
, res
)) {
192 bool IonGetPropSuperIC::update(JSContext
* cx
, HandleScript outerScript
,
193 IonGetPropSuperIC
* ic
, HandleObject obj
,
194 HandleValue receiver
, HandleValue idVal
,
195 MutableHandleValue res
) {
196 IonScript
* ionScript
= outerScript
->ionScript();
198 if (ic
->state().maybeTransition()) {
199 ic
->discardStubs(cx
->zone(), ionScript
);
202 RootedValue
val(cx
, ObjectValue(*obj
));
204 TryAttachIonStub
<GetPropIRGenerator
>(cx
, ic
, ionScript
, ic
->kind(), val
,
207 if (ic
->kind() == CacheKind::GetPropSuper
) {
208 Rooted
<PropertyName
*> name(cx
, idVal
.toString()->asAtom().asPropertyName());
209 if (!GetProperty(cx
, obj
, receiver
, name
, res
)) {
213 MOZ_ASSERT(ic
->kind() == CacheKind::GetElemSuper
);
215 JSOp op
= JSOp(*ic
->pc());
216 MOZ_ASSERT(op
== JSOp::GetElemSuper
);
218 if (!GetObjectElementOperation(cx
, op
, obj
, receiver
, idVal
, res
)) {
227 bool IonSetPropertyIC::update(JSContext
* cx
, HandleScript outerScript
,
228 IonSetPropertyIC
* ic
, HandleObject obj
,
229 HandleValue idVal
, HandleValue rhs
) {
230 using DeferType
= SetPropIRGenerator::DeferType
;
232 Rooted
<Shape
*> oldShape(cx
);
233 IonScript
* ionScript
= outerScript
->ionScript();
235 bool attached
= false;
236 DeferType deferType
= DeferType::None
;
238 if (ic
->state().maybeTransition()) {
239 ic
->discardStubs(cx
->zone(), ionScript
);
242 if (ic
->state().canAttachStub()) {
243 oldShape
= obj
->shape();
245 RootedValue
objv(cx
, ObjectValue(*obj
));
246 RootedScript
script(cx
, ic
->script());
247 jsbytecode
* pc
= ic
->pc();
249 SetPropIRGenerator
gen(cx
, script
, pc
, ic
->kind(), ic
->state(), objv
, idVal
,
251 switch (gen
.tryAttachStub()) {
252 case AttachDecision::Attach
:
253 ic
->attachCacheIRStub(cx
, gen
.writerRef(), gen
.cacheKind(), ionScript
,
256 case AttachDecision::NoAction
:
258 case AttachDecision::TemporarilyUnoptimizable
:
261 case AttachDecision::Deferred
:
262 deferType
= gen
.deferType();
263 MOZ_ASSERT(deferType
!= DeferType::None
);
268 jsbytecode
* pc
= ic
->pc();
269 if (ic
->kind() == CacheKind::SetElem
) {
270 if (JSOp(*pc
) == JSOp::InitElemInc
) {
271 if (!InitElemIncOperation(cx
, obj
.as
<ArrayObject
>(), idVal
.toInt32(),
275 } else if (IsPropertyInitOp(JSOp(*pc
))) {
276 if (!InitElemOperation(cx
, pc
, obj
, idVal
, rhs
)) {
280 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc
)));
281 if (!SetObjectElement(cx
, obj
, idVal
, rhs
, ic
->strict())) {
286 MOZ_ASSERT(ic
->kind() == CacheKind::SetProp
);
288 if (JSOp(*pc
) == JSOp::InitGLexical
) {
289 RootedScript
script(cx
, ic
->script());
290 MOZ_ASSERT(!script
->hasNonSyntacticScope());
291 InitGlobalLexicalOperation(cx
, &cx
->global()->lexicalEnvironment(),
293 } else if (IsPropertyInitOp(JSOp(*pc
))) {
294 Rooted
<PropertyName
*> name(cx
,
295 idVal
.toString()->asAtom().asPropertyName());
296 if (!InitPropertyOperation(cx
, pc
, obj
, name
, rhs
)) {
300 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc
)));
301 Rooted
<PropertyName
*> name(cx
,
302 idVal
.toString()->asAtom().asPropertyName());
303 if (!SetProperty(cx
, obj
, name
, rhs
, ic
->strict(), pc
)) {
313 // The SetProperty call might have entered this IC recursively, so try
315 if (ic
->state().maybeTransition()) {
316 ic
->discardStubs(cx
->zone(), ionScript
);
319 bool canAttachStub
= ic
->state().canAttachStub();
320 if (deferType
!= DeferType::None
&& canAttachStub
) {
321 RootedValue
objv(cx
, ObjectValue(*obj
));
322 RootedScript
script(cx
, ic
->script());
323 jsbytecode
* pc
= ic
->pc();
324 SetPropIRGenerator
gen(cx
, script
, pc
, ic
->kind(), ic
->state(), objv
, idVal
,
326 MOZ_ASSERT(deferType
== DeferType::AddSlot
);
327 AttachDecision decision
= gen
.tryAttachAddSlotStub(oldShape
);
330 case AttachDecision::Attach
:
331 ic
->attachCacheIRStub(cx
, gen
.writerRef(), gen
.cacheKind(), ionScript
,
334 case AttachDecision::NoAction
:
335 gen
.trackAttached(IRGenerator::NotAttached
);
337 case AttachDecision::TemporarilyUnoptimizable
:
338 case AttachDecision::Deferred
:
339 MOZ_ASSERT_UNREACHABLE("Invalid attach result");
343 if (!attached
&& canAttachStub
) {
344 ic
->state().trackNotAttached();
351 bool IonGetNameIC::update(JSContext
* cx
, HandleScript outerScript
,
352 IonGetNameIC
* ic
, HandleObject envChain
,
353 MutableHandleValue res
) {
354 IonScript
* ionScript
= outerScript
->ionScript();
355 jsbytecode
* pc
= ic
->pc();
356 Rooted
<PropertyName
*> name(cx
, ic
->script()->getName(pc
));
358 TryAttachIonStub
<GetNameIRGenerator
>(cx
, ic
, ionScript
, envChain
, name
);
360 RootedObject
obj(cx
);
361 RootedObject
holder(cx
);
363 if (!LookupName(cx
, name
, envChain
, &obj
, &holder
, &prop
)) {
367 if (JSOp(*GetNextPc(pc
)) == JSOp::Typeof
) {
368 return FetchName
<GetNameMode::TypeOf
>(cx
, obj
, holder
, name
, prop
, res
);
371 return FetchName
<GetNameMode::Normal
>(cx
, obj
, holder
, name
, prop
, res
);
375 JSObject
* IonBindNameIC::update(JSContext
* cx
, HandleScript outerScript
,
376 IonBindNameIC
* ic
, HandleObject envChain
) {
377 IonScript
* ionScript
= outerScript
->ionScript();
378 jsbytecode
* pc
= ic
->pc();
379 Rooted
<PropertyName
*> name(cx
, ic
->script()->getName(pc
));
381 TryAttachIonStub
<BindNameIRGenerator
>(cx
, ic
, ionScript
, envChain
, name
);
383 RootedObject
holder(cx
);
384 if (!LookupNameUnqualified(cx
, name
, envChain
, &holder
)) {
392 JSObject
* IonGetIteratorIC::update(JSContext
* cx
, HandleScript outerScript
,
393 IonGetIteratorIC
* ic
, HandleValue value
) {
394 IonScript
* ionScript
= outerScript
->ionScript();
396 TryAttachIonStub
<GetIteratorIRGenerator
>(cx
, ic
, ionScript
, value
);
398 PropertyIteratorObject
* iterObj
= ValueToIterator(cx
, value
);
407 bool IonOptimizeSpreadCallIC::update(JSContext
* cx
, HandleScript outerScript
,
408 IonOptimizeSpreadCallIC
* ic
,
410 MutableHandleValue result
) {
411 IonScript
* ionScript
= outerScript
->ionScript();
413 TryAttachIonStub
<OptimizeSpreadCallIRGenerator
>(cx
, ic
, ionScript
, value
);
415 return OptimizeSpreadCall(cx
, value
, result
);
419 bool IonHasOwnIC::update(JSContext
* cx
, HandleScript outerScript
,
420 IonHasOwnIC
* ic
, HandleValue val
, HandleValue idVal
,
422 IonScript
* ionScript
= outerScript
->ionScript();
424 TryAttachIonStub
<HasPropIRGenerator
>(cx
, ic
, ionScript
, CacheKind::HasOwn
,
428 if (!HasOwnProperty(cx
, val
, idVal
, &found
)) {
437 bool IonCheckPrivateFieldIC::update(JSContext
* cx
, HandleScript outerScript
,
438 IonCheckPrivateFieldIC
* ic
, HandleValue val
,
439 HandleValue idVal
, bool* res
) {
440 IonScript
* ionScript
= outerScript
->ionScript();
441 jsbytecode
* pc
= ic
->pc();
443 TryAttachIonStub
<CheckPrivateFieldIRGenerator
>(
444 cx
, ic
, ionScript
, CacheKind::CheckPrivateField
, idVal
, val
);
446 return CheckPrivateFieldOperation(cx
, pc
, val
, idVal
, res
);
450 bool IonInIC::update(JSContext
* cx
, HandleScript outerScript
, IonInIC
* ic
,
451 HandleValue key
, HandleObject obj
, bool* res
) {
452 IonScript
* ionScript
= outerScript
->ionScript();
453 RootedValue
objV(cx
, ObjectValue(*obj
));
455 TryAttachIonStub
<HasPropIRGenerator
>(cx
, ic
, ionScript
, CacheKind::In
, key
,
458 return OperatorIn(cx
, key
, obj
, res
);
461 bool IonInstanceOfIC::update(JSContext
* cx
, HandleScript outerScript
,
462 IonInstanceOfIC
* ic
, HandleValue lhs
,
463 HandleObject rhs
, bool* res
) {
464 IonScript
* ionScript
= outerScript
->ionScript();
466 TryAttachIonStub
<InstanceOfIRGenerator
>(cx
, ic
, ionScript
, lhs
, rhs
);
468 return InstanceofOperator(cx
, rhs
, lhs
, res
);
472 bool IonToPropertyKeyIC::update(JSContext
* cx
, HandleScript outerScript
,
473 IonToPropertyKeyIC
* ic
, HandleValue val
,
474 MutableHandleValue res
) {
475 IonScript
* ionScript
= outerScript
->ionScript();
477 TryAttachIonStub
<ToPropertyKeyIRGenerator
>(cx
, ic
, ionScript
, val
);
479 return ToPropertyKeyOperation(cx
, val
, res
);
483 bool IonCloseIterIC::update(JSContext
* cx
, HandleScript outerScript
,
484 IonCloseIterIC
* ic
, HandleObject iter
) {
485 IonScript
* ionScript
= outerScript
->ionScript();
486 CompletionKind kind
= ic
->completionKind();
488 TryAttachIonStub
<CloseIterIRGenerator
>(cx
, ic
, ionScript
, iter
, kind
);
490 return CloseIterOperation(cx
, iter
, kind
);
494 bool IonOptimizeGetIteratorIC::update(JSContext
* cx
, HandleScript outerScript
,
495 IonOptimizeGetIteratorIC
* ic
,
496 HandleValue value
, bool* result
) {
497 IonScript
* ionScript
= outerScript
->ionScript();
499 TryAttachIonStub
<OptimizeGetIteratorIRGenerator
>(cx
, ic
, ionScript
, value
);
501 return OptimizeGetIterator(cx
, value
, result
);
505 bool IonUnaryArithIC::update(JSContext
* cx
, HandleScript outerScript
,
506 IonUnaryArithIC
* ic
, HandleValue val
,
507 MutableHandleValue res
) {
508 IonScript
* ionScript
= outerScript
->ionScript();
509 RootedScript
script(cx
, ic
->script());
510 jsbytecode
* pc
= ic
->pc();
516 if (!BitNot(cx
, res
, res
)) {
523 if (!ToNumber(cx
, res
)) {
530 if (!NegOperation(cx
, res
, res
)) {
536 if (!IncOperation(cx
, val
, res
)) {
542 if (!DecOperation(cx
, val
, res
)) {
547 case JSOp::ToNumeric
: {
549 if (!ToNumeric(cx
, res
)) {
555 MOZ_CRASH("Unexpected op");
557 MOZ_ASSERT(res
.isNumeric());
559 TryAttachIonStub
<UnaryArithIRGenerator
>(cx
, ic
, ionScript
, op
, val
, res
);
565 bool IonBinaryArithIC::update(JSContext
* cx
, HandleScript outerScript
,
566 IonBinaryArithIC
* ic
, HandleValue lhs
,
567 HandleValue rhs
, MutableHandleValue ret
) {
568 IonScript
* ionScript
= outerScript
->ionScript();
569 RootedScript
script(cx
, ic
->script());
570 jsbytecode
* pc
= ic
->pc();
573 // Don't pass lhs/rhs directly, we need the original values when
575 RootedValue
lhsCopy(cx
, lhs
);
576 RootedValue
rhsCopy(cx
, rhs
);
578 // Perform the compare operation.
582 if (!AddValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
587 if (!SubValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
592 if (!MulValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
597 if (!DivValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
602 if (!ModValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
607 if (!PowValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
612 if (!BitOr(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
618 if (!BitXor(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
624 if (!BitAnd(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
630 if (!BitLsh(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
636 if (!BitRsh(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
642 if (!UrshValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
648 MOZ_CRASH("Unhandled binary arith op");
651 TryAttachIonStub
<BinaryArithIRGenerator
>(cx
, ic
, ionScript
, op
, lhs
, rhs
,
658 bool IonCompareIC::update(JSContext
* cx
, HandleScript outerScript
,
659 IonCompareIC
* ic
, HandleValue lhs
, HandleValue rhs
,
661 IonScript
* ionScript
= outerScript
->ionScript();
662 RootedScript
script(cx
, ic
->script());
663 jsbytecode
* pc
= ic
->pc();
666 // Don't pass lhs/rhs directly, we need the original values when
668 RootedValue
lhsCopy(cx
, lhs
);
669 RootedValue
rhsCopy(cx
, rhs
);
671 // Perform the compare operation.
674 if (!LessThan(cx
, &lhsCopy
, &rhsCopy
, res
)) {
679 if (!LessThanOrEqual(cx
, &lhsCopy
, &rhsCopy
, res
)) {
684 if (!GreaterThan(cx
, &lhsCopy
, &rhsCopy
, res
)) {
689 if (!GreaterThanOrEqual(cx
, &lhsCopy
, &rhsCopy
, res
)) {
694 if (!js::LooselyEqual(cx
, lhsCopy
, rhsCopy
, res
)) {
699 if (!js::LooselyEqual(cx
, lhsCopy
, rhsCopy
, res
)) {
705 if (!js::StrictlyEqual(cx
, lhsCopy
, rhsCopy
, res
)) {
710 if (!js::StrictlyEqual(cx
, lhsCopy
, rhsCopy
, res
)) {
716 MOZ_ASSERT_UNREACHABLE("Unhandled ion compare op");
720 TryAttachIonStub
<CompareIRGenerator
>(cx
, ic
, ionScript
, op
, lhs
, rhs
);
725 uint8_t* IonICStub::stubDataStart() {
726 return reinterpret_cast<uint8_t*>(this) + stubInfo_
->stubDataOffset();
729 void IonIC::attachStub(IonICStub
* newStub
, JitCode
* code
) {
734 newStub
->setNext(firstStub_
, codeRaw_
);
736 firstStub_
= newStub
;
737 codeRaw_
= code
->raw();
739 state_
.trackAttached();