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();
73 case CacheKind::TypeOf
:
74 case CacheKind::ToBool
:
75 case CacheKind::GetIntrinsic
:
76 case CacheKind::NewArray
:
77 case CacheKind::NewObject
:
78 MOZ_CRASH("Unsupported IC");
81 MOZ_CRASH("Invalid kind");
84 void IonIC::discardStubs(Zone
* zone
, IonScript
* ionScript
) {
86 // We are removing edges from IonIC to gcthings. Perform a write barrier to
87 // let the GC know about those edges.
88 PreWriteBarrier(zone
, ionScript
);
91 #ifdef JS_CRASH_DIAGNOSTICS
92 IonICStub
* stub
= firstStub_
;
94 IonICStub
* next
= stub
->next();
100 firstStub_
= nullptr;
101 resetCodeRaw(ionScript
);
102 state_
.trackUnlinkedAllStubs();
105 void IonIC::reset(Zone
* zone
, IonScript
* ionScript
) {
106 discardStubs(zone
, ionScript
);
110 void IonIC::trace(JSTracer
* trc
, IonScript
* ionScript
) {
112 TraceManuallyBarrieredEdge(trc
, &script_
, "IonIC::script_");
115 uint8_t* nextCodeRaw
= codeRaw_
;
116 for (IonICStub
* stub
= firstStub_
; stub
; stub
= stub
->next()) {
117 JitCode
* code
= JitCode::FromExecutable(nextCodeRaw
);
118 TraceManuallyBarrieredEdge(trc
, &code
, "ion-ic-code");
120 TraceCacheIRStub(trc
, stub
, stub
->stubInfo());
122 nextCodeRaw
= stub
->nextCodeRaw();
125 MOZ_ASSERT(nextCodeRaw
== fallbackAddr(ionScript
));
128 // This helper handles ICState updates/transitions while attaching CacheIR
130 template <typename IRGenerator
, typename
... Args
>
131 static void TryAttachIonStub(JSContext
* cx
, IonIC
* ic
, IonScript
* ionScript
,
133 if (ic
->state().maybeTransition()) {
134 ic
->discardStubs(cx
->zone(), ionScript
);
137 if (ic
->state().canAttachStub()) {
138 RootedScript
script(cx
, ic
->script());
139 bool attached
= false;
140 IRGenerator
gen(cx
, script
, ic
->pc(), ic
->state(),
141 std::forward
<Args
>(args
)...);
142 switch (gen
.tryAttachStub()) {
143 case AttachDecision::Attach
:
144 ic
->attachCacheIRStub(cx
, gen
.writerRef(), gen
.cacheKind(), ionScript
,
147 case AttachDecision::NoAction
:
149 case AttachDecision::TemporarilyUnoptimizable
:
152 case AttachDecision::Deferred
:
153 MOZ_ASSERT_UNREACHABLE("Not expected in generic TryAttachIonStub");
157 ic
->state().trackNotAttached();
163 bool IonGetPropertyIC::update(JSContext
* cx
, HandleScript outerScript
,
164 IonGetPropertyIC
* ic
, HandleValue val
,
165 HandleValue idVal
, MutableHandleValue res
) {
166 IonScript
* ionScript
= outerScript
->ionScript();
168 // Optimized-arguments and other magic values must not escape to Ion ICs.
169 MOZ_ASSERT(!val
.isMagic());
171 TryAttachIonStub
<GetPropIRGenerator
>(cx
, ic
, ionScript
, ic
->kind(), val
,
174 if (ic
->kind() == CacheKind::GetProp
) {
175 Rooted
<PropertyName
*> name(cx
, idVal
.toString()->asAtom().asPropertyName());
176 if (!GetProperty(cx
, val
, name
, res
)) {
180 MOZ_ASSERT(ic
->kind() == CacheKind::GetElem
);
181 if (!GetElementOperation(cx
, val
, idVal
, res
)) {
190 bool IonGetPropSuperIC::update(JSContext
* cx
, HandleScript outerScript
,
191 IonGetPropSuperIC
* ic
, HandleObject obj
,
192 HandleValue receiver
, HandleValue idVal
,
193 MutableHandleValue res
) {
194 IonScript
* ionScript
= outerScript
->ionScript();
196 if (ic
->state().maybeTransition()) {
197 ic
->discardStubs(cx
->zone(), ionScript
);
200 RootedValue
val(cx
, ObjectValue(*obj
));
202 TryAttachIonStub
<GetPropIRGenerator
>(cx
, ic
, ionScript
, ic
->kind(), val
,
205 if (ic
->kind() == CacheKind::GetPropSuper
) {
206 Rooted
<PropertyName
*> name(cx
, idVal
.toString()->asAtom().asPropertyName());
207 if (!GetProperty(cx
, obj
, receiver
, name
, res
)) {
211 MOZ_ASSERT(ic
->kind() == CacheKind::GetElemSuper
);
213 JSOp op
= JSOp(*ic
->pc());
214 MOZ_ASSERT(op
== JSOp::GetElemSuper
);
216 if (!GetObjectElementOperation(cx
, op
, obj
, receiver
, idVal
, res
)) {
225 bool IonSetPropertyIC::update(JSContext
* cx
, HandleScript outerScript
,
226 IonSetPropertyIC
* ic
, HandleObject obj
,
227 HandleValue idVal
, HandleValue rhs
) {
228 using DeferType
= SetPropIRGenerator::DeferType
;
230 Rooted
<Shape
*> oldShape(cx
);
231 IonScript
* ionScript
= outerScript
->ionScript();
233 bool attached
= false;
234 DeferType deferType
= DeferType::None
;
236 if (ic
->state().maybeTransition()) {
237 ic
->discardStubs(cx
->zone(), ionScript
);
240 if (ic
->state().canAttachStub()) {
241 oldShape
= obj
->shape();
243 RootedValue
objv(cx
, ObjectValue(*obj
));
244 RootedScript
script(cx
, ic
->script());
245 jsbytecode
* pc
= ic
->pc();
247 SetPropIRGenerator
gen(cx
, script
, pc
, ic
->kind(), ic
->state(), objv
, idVal
,
249 switch (gen
.tryAttachStub()) {
250 case AttachDecision::Attach
:
251 ic
->attachCacheIRStub(cx
, gen
.writerRef(), gen
.cacheKind(), ionScript
,
254 case AttachDecision::NoAction
:
256 case AttachDecision::TemporarilyUnoptimizable
:
259 case AttachDecision::Deferred
:
260 deferType
= gen
.deferType();
261 MOZ_ASSERT(deferType
!= DeferType::None
);
266 jsbytecode
* pc
= ic
->pc();
267 if (ic
->kind() == CacheKind::SetElem
) {
268 if (JSOp(*pc
) == JSOp::InitElemInc
) {
269 if (!InitElemIncOperation(cx
, obj
.as
<ArrayObject
>(), idVal
.toInt32(),
273 } else if (IsPropertyInitOp(JSOp(*pc
))) {
274 if (!InitElemOperation(cx
, pc
, obj
, idVal
, rhs
)) {
278 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc
)));
279 if (!SetObjectElement(cx
, obj
, idVal
, rhs
, ic
->strict())) {
284 MOZ_ASSERT(ic
->kind() == CacheKind::SetProp
);
286 if (JSOp(*pc
) == JSOp::InitGLexical
) {
287 RootedScript
script(cx
, ic
->script());
288 MOZ_ASSERT(!script
->hasNonSyntacticScope());
289 InitGlobalLexicalOperation(cx
, &cx
->global()->lexicalEnvironment(),
291 } else if (IsPropertyInitOp(JSOp(*pc
))) {
292 Rooted
<PropertyName
*> name(cx
,
293 idVal
.toString()->asAtom().asPropertyName());
294 if (!InitPropertyOperation(cx
, pc
, obj
, name
, rhs
)) {
298 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc
)));
299 Rooted
<PropertyName
*> name(cx
,
300 idVal
.toString()->asAtom().asPropertyName());
301 if (!SetProperty(cx
, obj
, name
, rhs
, ic
->strict(), pc
)) {
311 // The SetProperty call might have entered this IC recursively, so try
313 if (ic
->state().maybeTransition()) {
314 ic
->discardStubs(cx
->zone(), ionScript
);
317 bool canAttachStub
= ic
->state().canAttachStub();
318 if (deferType
!= DeferType::None
&& canAttachStub
) {
319 RootedValue
objv(cx
, ObjectValue(*obj
));
320 RootedScript
script(cx
, ic
->script());
321 jsbytecode
* pc
= ic
->pc();
322 SetPropIRGenerator
gen(cx
, script
, pc
, ic
->kind(), ic
->state(), objv
, idVal
,
324 MOZ_ASSERT(deferType
== DeferType::AddSlot
);
325 AttachDecision decision
= gen
.tryAttachAddSlotStub(oldShape
);
328 case AttachDecision::Attach
:
329 ic
->attachCacheIRStub(cx
, gen
.writerRef(), gen
.cacheKind(), ionScript
,
332 case AttachDecision::NoAction
:
333 gen
.trackAttached(IRGenerator::NotAttached
);
335 case AttachDecision::TemporarilyUnoptimizable
:
336 case AttachDecision::Deferred
:
337 MOZ_ASSERT_UNREACHABLE("Invalid attach result");
341 if (!attached
&& canAttachStub
) {
342 ic
->state().trackNotAttached();
349 bool IonGetNameIC::update(JSContext
* cx
, HandleScript outerScript
,
350 IonGetNameIC
* ic
, HandleObject envChain
,
351 MutableHandleValue res
) {
352 IonScript
* ionScript
= outerScript
->ionScript();
353 jsbytecode
* pc
= ic
->pc();
354 Rooted
<PropertyName
*> name(cx
, ic
->script()->getName(pc
));
356 TryAttachIonStub
<GetNameIRGenerator
>(cx
, ic
, ionScript
, envChain
, name
);
358 RootedObject
obj(cx
);
359 RootedObject
holder(cx
);
361 if (!LookupName(cx
, name
, envChain
, &obj
, &holder
, &prop
)) {
365 if (JSOp(*GetNextPc(pc
)) == JSOp::Typeof
) {
366 return FetchName
<GetNameMode::TypeOf
>(cx
, obj
, holder
, name
, prop
, res
);
369 return FetchName
<GetNameMode::Normal
>(cx
, obj
, holder
, name
, prop
, res
);
373 JSObject
* IonBindNameIC::update(JSContext
* cx
, HandleScript outerScript
,
374 IonBindNameIC
* ic
, HandleObject envChain
) {
375 IonScript
* ionScript
= outerScript
->ionScript();
376 jsbytecode
* pc
= ic
->pc();
377 Rooted
<PropertyName
*> name(cx
, ic
->script()->getName(pc
));
379 TryAttachIonStub
<BindNameIRGenerator
>(cx
, ic
, ionScript
, envChain
, name
);
381 RootedObject
holder(cx
);
382 if (!LookupNameUnqualified(cx
, name
, envChain
, &holder
)) {
390 JSObject
* IonGetIteratorIC::update(JSContext
* cx
, HandleScript outerScript
,
391 IonGetIteratorIC
* ic
, HandleValue value
) {
392 IonScript
* ionScript
= outerScript
->ionScript();
394 TryAttachIonStub
<GetIteratorIRGenerator
>(cx
, ic
, ionScript
, value
);
396 PropertyIteratorObject
* iterObj
= ValueToIterator(cx
, value
);
405 bool IonOptimizeSpreadCallIC::update(JSContext
* cx
, HandleScript outerScript
,
406 IonOptimizeSpreadCallIC
* ic
,
408 MutableHandleValue result
) {
409 IonScript
* ionScript
= outerScript
->ionScript();
411 TryAttachIonStub
<OptimizeSpreadCallIRGenerator
>(cx
, ic
, ionScript
, value
);
413 return OptimizeSpreadCall(cx
, value
, result
);
417 bool IonHasOwnIC::update(JSContext
* cx
, HandleScript outerScript
,
418 IonHasOwnIC
* ic
, HandleValue val
, HandleValue idVal
,
420 IonScript
* ionScript
= outerScript
->ionScript();
422 TryAttachIonStub
<HasPropIRGenerator
>(cx
, ic
, ionScript
, CacheKind::HasOwn
,
426 if (!HasOwnProperty(cx
, val
, idVal
, &found
)) {
435 bool IonCheckPrivateFieldIC::update(JSContext
* cx
, HandleScript outerScript
,
436 IonCheckPrivateFieldIC
* ic
, HandleValue val
,
437 HandleValue idVal
, bool* res
) {
438 IonScript
* ionScript
= outerScript
->ionScript();
439 jsbytecode
* pc
= ic
->pc();
441 TryAttachIonStub
<CheckPrivateFieldIRGenerator
>(
442 cx
, ic
, ionScript
, CacheKind::CheckPrivateField
, idVal
, val
);
444 return CheckPrivateFieldOperation(cx
, pc
, val
, idVal
, res
);
448 bool IonInIC::update(JSContext
* cx
, HandleScript outerScript
, IonInIC
* ic
,
449 HandleValue key
, HandleObject obj
, bool* res
) {
450 IonScript
* ionScript
= outerScript
->ionScript();
451 RootedValue
objV(cx
, ObjectValue(*obj
));
453 TryAttachIonStub
<HasPropIRGenerator
>(cx
, ic
, ionScript
, CacheKind::In
, key
,
456 return OperatorIn(cx
, key
, obj
, res
);
459 bool IonInstanceOfIC::update(JSContext
* cx
, HandleScript outerScript
,
460 IonInstanceOfIC
* ic
, HandleValue lhs
,
461 HandleObject rhs
, bool* res
) {
462 IonScript
* ionScript
= outerScript
->ionScript();
464 TryAttachIonStub
<InstanceOfIRGenerator
>(cx
, ic
, ionScript
, lhs
, rhs
);
466 return InstanceofOperator(cx
, rhs
, lhs
, res
);
470 bool IonToPropertyKeyIC::update(JSContext
* cx
, HandleScript outerScript
,
471 IonToPropertyKeyIC
* ic
, HandleValue val
,
472 MutableHandleValue res
) {
473 IonScript
* ionScript
= outerScript
->ionScript();
475 TryAttachIonStub
<ToPropertyKeyIRGenerator
>(cx
, ic
, ionScript
, val
);
477 return ToPropertyKeyOperation(cx
, val
, res
);
481 bool IonCloseIterIC::update(JSContext
* cx
, HandleScript outerScript
,
482 IonCloseIterIC
* ic
, HandleObject iter
) {
483 IonScript
* ionScript
= outerScript
->ionScript();
484 CompletionKind kind
= ic
->completionKind();
486 TryAttachIonStub
<CloseIterIRGenerator
>(cx
, ic
, ionScript
, iter
, kind
);
488 return CloseIterOperation(cx
, iter
, kind
);
492 bool IonUnaryArithIC::update(JSContext
* cx
, HandleScript outerScript
,
493 IonUnaryArithIC
* ic
, HandleValue val
,
494 MutableHandleValue res
) {
495 IonScript
* ionScript
= outerScript
->ionScript();
496 RootedScript
script(cx
, ic
->script());
497 jsbytecode
* pc
= ic
->pc();
503 if (!BitNot(cx
, res
, res
)) {
510 if (!ToNumber(cx
, res
)) {
517 if (!NegOperation(cx
, res
, res
)) {
523 if (!IncOperation(cx
, val
, res
)) {
529 if (!DecOperation(cx
, val
, res
)) {
534 case JSOp::ToNumeric
: {
536 if (!ToNumeric(cx
, res
)) {
542 MOZ_CRASH("Unexpected op");
544 MOZ_ASSERT(res
.isNumeric());
546 TryAttachIonStub
<UnaryArithIRGenerator
>(cx
, ic
, ionScript
, op
, val
, res
);
552 bool IonBinaryArithIC::update(JSContext
* cx
, HandleScript outerScript
,
553 IonBinaryArithIC
* ic
, HandleValue lhs
,
554 HandleValue rhs
, MutableHandleValue ret
) {
555 IonScript
* ionScript
= outerScript
->ionScript();
556 RootedScript
script(cx
, ic
->script());
557 jsbytecode
* pc
= ic
->pc();
560 // Don't pass lhs/rhs directly, we need the original values when
562 RootedValue
lhsCopy(cx
, lhs
);
563 RootedValue
rhsCopy(cx
, rhs
);
565 // Perform the compare operation.
569 if (!AddValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
574 if (!SubValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
579 if (!MulValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
584 if (!DivValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
589 if (!ModValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
594 if (!PowValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
599 if (!BitOr(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
605 if (!BitXor(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
611 if (!BitAnd(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
617 if (!BitLsh(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
623 if (!BitRsh(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
629 if (!UrshValues(cx
, &lhsCopy
, &rhsCopy
, ret
)) {
635 MOZ_CRASH("Unhandled binary arith op");
638 TryAttachIonStub
<BinaryArithIRGenerator
>(cx
, ic
, ionScript
, op
, lhs
, rhs
,
645 bool IonCompareIC::update(JSContext
* cx
, HandleScript outerScript
,
646 IonCompareIC
* ic
, HandleValue lhs
, HandleValue rhs
,
648 IonScript
* ionScript
= outerScript
->ionScript();
649 RootedScript
script(cx
, ic
->script());
650 jsbytecode
* pc
= ic
->pc();
653 // Don't pass lhs/rhs directly, we need the original values when
655 RootedValue
lhsCopy(cx
, lhs
);
656 RootedValue
rhsCopy(cx
, rhs
);
658 // Perform the compare operation.
661 if (!LessThan(cx
, &lhsCopy
, &rhsCopy
, res
)) {
666 if (!LessThanOrEqual(cx
, &lhsCopy
, &rhsCopy
, res
)) {
671 if (!GreaterThan(cx
, &lhsCopy
, &rhsCopy
, res
)) {
676 if (!GreaterThanOrEqual(cx
, &lhsCopy
, &rhsCopy
, res
)) {
681 if (!js::LooselyEqual(cx
, lhsCopy
, rhsCopy
, res
)) {
686 if (!js::LooselyEqual(cx
, lhsCopy
, rhsCopy
, res
)) {
692 if (!js::StrictlyEqual(cx
, lhsCopy
, rhsCopy
, res
)) {
697 if (!js::StrictlyEqual(cx
, lhsCopy
, rhsCopy
, res
)) {
703 MOZ_ASSERT_UNREACHABLE("Unhandled ion compare op");
707 TryAttachIonStub
<CompareIRGenerator
>(cx
, ic
, ionScript
, op
, lhs
, rhs
);
712 uint8_t* IonICStub::stubDataStart() {
713 return reinterpret_cast<uint8_t*>(this) + stubInfo_
->stubDataOffset();
716 void IonIC::attachStub(IonICStub
* newStub
, JitCode
* code
) {
721 newStub
->setNext(firstStub_
, codeRaw_
);
723 firstStub_
= newStub
;
724 codeRaw_
= code
->raw();
726 state_
.trackAttached();