summaryrefslogtreecommitdiffstats
path: root/js/src/jit/IonIC.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/jit/IonIC.cpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit/IonIC.cpp')
-rw-r--r--js/src/jit/IonIC.cpp740
1 files changed, 740 insertions, 0 deletions
diff --git a/js/src/jit/IonIC.cpp b/js/src/jit/IonIC.cpp
new file mode 100644
index 0000000000..55f3bbea6c
--- /dev/null
+++ b/js/src/jit/IonIC.cpp
@@ -0,0 +1,740 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jit/IonIC.h"
+
+#include "jit/CacheIRCompiler.h"
+#include "jit/CacheIRGenerator.h"
+#include "jit/IonScript.h"
+#include "jit/VMFunctions.h"
+#include "util/DiagnosticAssertions.h"
+#include "vm/EqualityOperations.h"
+#include "vm/Iteration.h"
+
+#include "vm/Interpreter-inl.h"
+#include "vm/JSScript-inl.h"
+
+using namespace js;
+using namespace js::jit;
+
+void IonIC::resetCodeRaw(IonScript* ionScript) {
+ codeRaw_ = fallbackAddr(ionScript);
+}
+
+uint8_t* IonIC::fallbackAddr(IonScript* ionScript) const {
+ return ionScript->method()->raw() + fallbackOffset_;
+}
+
+uint8_t* IonIC::rejoinAddr(IonScript* ionScript) const {
+ return ionScript->method()->raw() + rejoinOffset_;
+}
+
+Register IonIC::scratchRegisterForEntryJump() {
+ switch (kind_) {
+ case CacheKind::GetProp:
+ case CacheKind::GetElem:
+ return asGetPropertyIC()->output().scratchReg();
+ case CacheKind::GetPropSuper:
+ case CacheKind::GetElemSuper:
+ return asGetPropSuperIC()->output().scratchReg();
+ case CacheKind::SetProp:
+ case CacheKind::SetElem:
+ return asSetPropertyIC()->temp();
+ case CacheKind::GetName:
+ return asGetNameIC()->temp();
+ case CacheKind::BindName:
+ return asBindNameIC()->temp();
+ case CacheKind::In:
+ return asInIC()->temp();
+ case CacheKind::HasOwn:
+ return asHasOwnIC()->output();
+ case CacheKind::CheckPrivateField:
+ return asCheckPrivateFieldIC()->output();
+ case CacheKind::GetIterator:
+ return asGetIteratorIC()->temp1();
+ case CacheKind::OptimizeSpreadCall:
+ return asOptimizeSpreadCallIC()->temp();
+ case CacheKind::InstanceOf:
+ return asInstanceOfIC()->output();
+ case CacheKind::UnaryArith:
+ return asUnaryArithIC()->output().scratchReg();
+ case CacheKind::ToPropertyKey:
+ return asToPropertyKeyIC()->output().scratchReg();
+ case CacheKind::BinaryArith:
+ return asBinaryArithIC()->output().scratchReg();
+ case CacheKind::Compare:
+ return asCompareIC()->output();
+ case CacheKind::CloseIter:
+ return asCloseIterIC()->temp();
+ case CacheKind::OptimizeGetIterator:
+ return asOptimizeGetIteratorIC()->temp();
+ case CacheKind::Call:
+ case CacheKind::TypeOf:
+ case CacheKind::ToBool:
+ case CacheKind::GetIntrinsic:
+ case CacheKind::NewArray:
+ case CacheKind::NewObject:
+ MOZ_CRASH("Unsupported IC");
+ }
+
+ MOZ_CRASH("Invalid kind");
+}
+
+void IonIC::discardStubs(Zone* zone, IonScript* ionScript) {
+ if (firstStub_) {
+ // We are removing edges from IonIC to gcthings. Perform a write barrier to
+ // let the GC know about those edges.
+ PreWriteBarrier(zone, ionScript);
+ }
+
+#ifdef JS_CRASH_DIAGNOSTICS
+ IonICStub* stub = firstStub_;
+ while (stub) {
+ IonICStub* next = stub->next();
+ stub->poison();
+ stub = next;
+ }
+#endif
+
+ firstStub_ = nullptr;
+ resetCodeRaw(ionScript);
+ state_.trackUnlinkedAllStubs();
+}
+
+void IonIC::reset(Zone* zone, IonScript* ionScript) {
+ discardStubs(zone, ionScript);
+ state_.reset();
+}
+
+void IonIC::trace(JSTracer* trc, IonScript* ionScript) {
+ if (script_) {
+ TraceManuallyBarrieredEdge(trc, &script_, "IonIC::script_");
+ }
+
+ uint8_t* nextCodeRaw = codeRaw_;
+ for (IonICStub* stub = firstStub_; stub; stub = stub->next()) {
+ JitCode* code = JitCode::FromExecutable(nextCodeRaw);
+ TraceManuallyBarrieredEdge(trc, &code, "ion-ic-code");
+
+ TraceCacheIRStub(trc, stub, stub->stubInfo());
+
+ nextCodeRaw = stub->nextCodeRaw();
+ }
+
+ MOZ_ASSERT(nextCodeRaw == fallbackAddr(ionScript));
+}
+
+// This helper handles ICState updates/transitions while attaching CacheIR
+// stubs.
+template <typename IRGenerator, typename... Args>
+static void TryAttachIonStub(JSContext* cx, IonIC* ic, IonScript* ionScript,
+ Args&&... args) {
+ if (ic->state().maybeTransition()) {
+ ic->discardStubs(cx->zone(), ionScript);
+ }
+
+ if (ic->state().canAttachStub()) {
+ RootedScript script(cx, ic->script());
+ bool attached = false;
+ IRGenerator gen(cx, script, ic->pc(), ic->state(),
+ std::forward<Args>(args)...);
+ switch (gen.tryAttachStub()) {
+ case AttachDecision::Attach:
+ ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript,
+ &attached);
+ break;
+ case AttachDecision::NoAction:
+ break;
+ case AttachDecision::TemporarilyUnoptimizable:
+ attached = true;
+ break;
+ case AttachDecision::Deferred:
+ MOZ_ASSERT_UNREACHABLE("Not expected in generic TryAttachIonStub");
+ break;
+ }
+ if (!attached) {
+ ic->state().trackNotAttached();
+ }
+ }
+}
+
+/* static */
+bool IonGetPropertyIC::update(JSContext* cx, HandleScript outerScript,
+ IonGetPropertyIC* ic, HandleValue val,
+ HandleValue idVal, MutableHandleValue res) {
+ IonScript* ionScript = outerScript->ionScript();
+
+ // Optimized-arguments and other magic values must not escape to Ion ICs.
+ MOZ_ASSERT(!val.isMagic());
+
+ TryAttachIonStub<GetPropIRGenerator>(cx, ic, ionScript, ic->kind(), val,
+ idVal);
+
+ if (ic->kind() == CacheKind::GetProp) {
+ Rooted<PropertyName*> name(cx, idVal.toString()->asAtom().asPropertyName());
+ if (!GetProperty(cx, val, name, res)) {
+ return false;
+ }
+ } else {
+ MOZ_ASSERT(ic->kind() == CacheKind::GetElem);
+ if (!GetElementOperation(cx, val, idVal, res)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* static */
+bool IonGetPropSuperIC::update(JSContext* cx, HandleScript outerScript,
+ IonGetPropSuperIC* ic, HandleObject obj,
+ HandleValue receiver, HandleValue idVal,
+ MutableHandleValue res) {
+ IonScript* ionScript = outerScript->ionScript();
+
+ if (ic->state().maybeTransition()) {
+ ic->discardStubs(cx->zone(), ionScript);
+ }
+
+ RootedValue val(cx, ObjectValue(*obj));
+
+ TryAttachIonStub<GetPropIRGenerator>(cx, ic, ionScript, ic->kind(), val,
+ idVal);
+
+ if (ic->kind() == CacheKind::GetPropSuper) {
+ Rooted<PropertyName*> name(cx, idVal.toString()->asAtom().asPropertyName());
+ if (!GetProperty(cx, obj, receiver, name, res)) {
+ return false;
+ }
+ } else {
+ MOZ_ASSERT(ic->kind() == CacheKind::GetElemSuper);
+
+ JSOp op = JSOp(*ic->pc());
+ MOZ_ASSERT(op == JSOp::GetElemSuper);
+
+ if (!GetObjectElementOperation(cx, op, obj, receiver, idVal, res)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* static */
+bool IonSetPropertyIC::update(JSContext* cx, HandleScript outerScript,
+ IonSetPropertyIC* ic, HandleObject obj,
+ HandleValue idVal, HandleValue rhs) {
+ using DeferType = SetPropIRGenerator::DeferType;
+
+ Rooted<Shape*> oldShape(cx);
+ IonScript* ionScript = outerScript->ionScript();
+
+ bool attached = false;
+ DeferType deferType = DeferType::None;
+
+ if (ic->state().maybeTransition()) {
+ ic->discardStubs(cx->zone(), ionScript);
+ }
+
+ if (ic->state().canAttachStub()) {
+ oldShape = obj->shape();
+
+ RootedValue objv(cx, ObjectValue(*obj));
+ RootedScript script(cx, ic->script());
+ jsbytecode* pc = ic->pc();
+
+ SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state(), objv, idVal,
+ rhs);
+ switch (gen.tryAttachStub()) {
+ case AttachDecision::Attach:
+ ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript,
+ &attached);
+ break;
+ case AttachDecision::NoAction:
+ break;
+ case AttachDecision::TemporarilyUnoptimizable:
+ attached = true;
+ break;
+ case AttachDecision::Deferred:
+ deferType = gen.deferType();
+ MOZ_ASSERT(deferType != DeferType::None);
+ break;
+ }
+ }
+
+ jsbytecode* pc = ic->pc();
+ if (ic->kind() == CacheKind::SetElem) {
+ if (JSOp(*pc) == JSOp::InitElemInc) {
+ if (!InitElemIncOperation(cx, obj.as<ArrayObject>(), idVal.toInt32(),
+ rhs)) {
+ return false;
+ }
+ } else if (IsPropertyInitOp(JSOp(*pc))) {
+ if (!InitElemOperation(cx, pc, obj, idVal, rhs)) {
+ return false;
+ }
+ } else {
+ MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
+ if (!SetObjectElement(cx, obj, idVal, rhs, ic->strict())) {
+ return false;
+ }
+ }
+ } else {
+ MOZ_ASSERT(ic->kind() == CacheKind::SetProp);
+
+ if (JSOp(*pc) == JSOp::InitGLexical) {
+ RootedScript script(cx, ic->script());
+ MOZ_ASSERT(!script->hasNonSyntacticScope());
+ InitGlobalLexicalOperation(cx, &cx->global()->lexicalEnvironment(),
+ script, pc, rhs);
+ } else if (IsPropertyInitOp(JSOp(*pc))) {
+ Rooted<PropertyName*> name(cx,
+ idVal.toString()->asAtom().asPropertyName());
+ if (!InitPropertyOperation(cx, pc, obj, name, rhs)) {
+ return false;
+ }
+ } else {
+ MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
+ Rooted<PropertyName*> name(cx,
+ idVal.toString()->asAtom().asPropertyName());
+ if (!SetProperty(cx, obj, name, rhs, ic->strict(), pc)) {
+ return false;
+ }
+ }
+ }
+
+ if (attached) {
+ return true;
+ }
+
+ // The SetProperty call might have entered this IC recursively, so try
+ // to transition.
+ if (ic->state().maybeTransition()) {
+ ic->discardStubs(cx->zone(), ionScript);
+ }
+
+ bool canAttachStub = ic->state().canAttachStub();
+ if (deferType != DeferType::None && canAttachStub) {
+ RootedValue objv(cx, ObjectValue(*obj));
+ RootedScript script(cx, ic->script());
+ jsbytecode* pc = ic->pc();
+ SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state(), objv, idVal,
+ rhs);
+ MOZ_ASSERT(deferType == DeferType::AddSlot);
+ AttachDecision decision = gen.tryAttachAddSlotStub(oldShape);
+
+ switch (decision) {
+ case AttachDecision::Attach:
+ ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript,
+ &attached);
+ break;
+ case AttachDecision::NoAction:
+ gen.trackAttached(IRGenerator::NotAttached);
+ break;
+ case AttachDecision::TemporarilyUnoptimizable:
+ case AttachDecision::Deferred:
+ MOZ_ASSERT_UNREACHABLE("Invalid attach result");
+ break;
+ }
+ }
+ if (!attached && canAttachStub) {
+ ic->state().trackNotAttached();
+ }
+
+ return true;
+}
+
+/* static */
+bool IonGetNameIC::update(JSContext* cx, HandleScript outerScript,
+ IonGetNameIC* ic, HandleObject envChain,
+ MutableHandleValue res) {
+ IonScript* ionScript = outerScript->ionScript();
+ jsbytecode* pc = ic->pc();
+ Rooted<PropertyName*> name(cx, ic->script()->getName(pc));
+
+ TryAttachIonStub<GetNameIRGenerator>(cx, ic, ionScript, envChain, name);
+
+ RootedObject obj(cx);
+ RootedObject holder(cx);
+ PropertyResult prop;
+ if (!LookupName(cx, name, envChain, &obj, &holder, &prop)) {
+ return false;
+ }
+
+ if (JSOp(*GetNextPc(pc)) == JSOp::Typeof) {
+ return FetchName<GetNameMode::TypeOf>(cx, obj, holder, name, prop, res);
+ }
+
+ return FetchName<GetNameMode::Normal>(cx, obj, holder, name, prop, res);
+}
+
+/* static */
+JSObject* IonBindNameIC::update(JSContext* cx, HandleScript outerScript,
+ IonBindNameIC* ic, HandleObject envChain) {
+ IonScript* ionScript = outerScript->ionScript();
+ jsbytecode* pc = ic->pc();
+ Rooted<PropertyName*> name(cx, ic->script()->getName(pc));
+
+ TryAttachIonStub<BindNameIRGenerator>(cx, ic, ionScript, envChain, name);
+
+ RootedObject holder(cx);
+ if (!LookupNameUnqualified(cx, name, envChain, &holder)) {
+ return nullptr;
+ }
+
+ return holder;
+}
+
+/* static */
+JSObject* IonGetIteratorIC::update(JSContext* cx, HandleScript outerScript,
+ IonGetIteratorIC* ic, HandleValue value) {
+ IonScript* ionScript = outerScript->ionScript();
+
+ TryAttachIonStub<GetIteratorIRGenerator>(cx, ic, ionScript, value);
+
+ PropertyIteratorObject* iterObj = ValueToIterator(cx, value);
+ if (!iterObj) {
+ return nullptr;
+ }
+
+ return iterObj;
+}
+
+/* static */
+bool IonOptimizeSpreadCallIC::update(JSContext* cx, HandleScript outerScript,
+ IonOptimizeSpreadCallIC* ic,
+ HandleValue value,
+ MutableHandleValue result) {
+ IonScript* ionScript = outerScript->ionScript();
+
+ TryAttachIonStub<OptimizeSpreadCallIRGenerator>(cx, ic, ionScript, value);
+
+ return OptimizeSpreadCall(cx, value, result);
+}
+
+/* static */
+bool IonHasOwnIC::update(JSContext* cx, HandleScript outerScript,
+ IonHasOwnIC* ic, HandleValue val, HandleValue idVal,
+ int32_t* res) {
+ IonScript* ionScript = outerScript->ionScript();
+
+ TryAttachIonStub<HasPropIRGenerator>(cx, ic, ionScript, CacheKind::HasOwn,
+ idVal, val);
+
+ bool found;
+ if (!HasOwnProperty(cx, val, idVal, &found)) {
+ return false;
+ }
+
+ *res = found;
+ return true;
+}
+
+/* static */
+bool IonCheckPrivateFieldIC::update(JSContext* cx, HandleScript outerScript,
+ IonCheckPrivateFieldIC* ic, HandleValue val,
+ HandleValue idVal, bool* res) {
+ IonScript* ionScript = outerScript->ionScript();
+ jsbytecode* pc = ic->pc();
+
+ TryAttachIonStub<CheckPrivateFieldIRGenerator>(
+ cx, ic, ionScript, CacheKind::CheckPrivateField, idVal, val);
+
+ return CheckPrivateFieldOperation(cx, pc, val, idVal, res);
+}
+
+/* static */
+bool IonInIC::update(JSContext* cx, HandleScript outerScript, IonInIC* ic,
+ HandleValue key, HandleObject obj, bool* res) {
+ IonScript* ionScript = outerScript->ionScript();
+ RootedValue objV(cx, ObjectValue(*obj));
+
+ TryAttachIonStub<HasPropIRGenerator>(cx, ic, ionScript, CacheKind::In, key,
+ objV);
+
+ return OperatorIn(cx, key, obj, res);
+}
+/* static */
+bool IonInstanceOfIC::update(JSContext* cx, HandleScript outerScript,
+ IonInstanceOfIC* ic, HandleValue lhs,
+ HandleObject rhs, bool* res) {
+ IonScript* ionScript = outerScript->ionScript();
+
+ TryAttachIonStub<InstanceOfIRGenerator>(cx, ic, ionScript, lhs, rhs);
+
+ return InstanceofOperator(cx, rhs, lhs, res);
+}
+
+/* static */
+bool IonToPropertyKeyIC::update(JSContext* cx, HandleScript outerScript,
+ IonToPropertyKeyIC* ic, HandleValue val,
+ MutableHandleValue res) {
+ IonScript* ionScript = outerScript->ionScript();
+
+ TryAttachIonStub<ToPropertyKeyIRGenerator>(cx, ic, ionScript, val);
+
+ return ToPropertyKeyOperation(cx, val, res);
+}
+
+/* static */
+bool IonCloseIterIC::update(JSContext* cx, HandleScript outerScript,
+ IonCloseIterIC* ic, HandleObject iter) {
+ IonScript* ionScript = outerScript->ionScript();
+ CompletionKind kind = ic->completionKind();
+
+ TryAttachIonStub<CloseIterIRGenerator>(cx, ic, ionScript, iter, kind);
+
+ return CloseIterOperation(cx, iter, kind);
+}
+
+/* static */
+bool IonOptimizeGetIteratorIC::update(JSContext* cx, HandleScript outerScript,
+ IonOptimizeGetIteratorIC* ic,
+ HandleValue value, bool* result) {
+ IonScript* ionScript = outerScript->ionScript();
+
+ TryAttachIonStub<OptimizeGetIteratorIRGenerator>(cx, ic, ionScript, value);
+
+ return OptimizeGetIterator(cx, value, result);
+}
+
+/* static */
+bool IonUnaryArithIC::update(JSContext* cx, HandleScript outerScript,
+ IonUnaryArithIC* ic, HandleValue val,
+ MutableHandleValue res) {
+ IonScript* ionScript = outerScript->ionScript();
+ RootedScript script(cx, ic->script());
+ jsbytecode* pc = ic->pc();
+ JSOp op = JSOp(*pc);
+
+ switch (op) {
+ case JSOp::BitNot: {
+ res.set(val);
+ if (!BitNot(cx, res, res)) {
+ return false;
+ }
+ break;
+ }
+ case JSOp::Pos: {
+ res.set(val);
+ if (!ToNumber(cx, res)) {
+ return false;
+ }
+ break;
+ }
+ case JSOp::Neg: {
+ res.set(val);
+ if (!NegOperation(cx, res, res)) {
+ return false;
+ }
+ break;
+ }
+ case JSOp::Inc: {
+ if (!IncOperation(cx, val, res)) {
+ return false;
+ }
+ break;
+ }
+ case JSOp::Dec: {
+ if (!DecOperation(cx, val, res)) {
+ return false;
+ }
+ break;
+ }
+ case JSOp::ToNumeric: {
+ res.set(val);
+ if (!ToNumeric(cx, res)) {
+ return false;
+ }
+ break;
+ }
+ default:
+ MOZ_CRASH("Unexpected op");
+ }
+ MOZ_ASSERT(res.isNumeric());
+
+ TryAttachIonStub<UnaryArithIRGenerator>(cx, ic, ionScript, op, val, res);
+
+ return true;
+}
+
+/* static */
+bool IonBinaryArithIC::update(JSContext* cx, HandleScript outerScript,
+ IonBinaryArithIC* ic, HandleValue lhs,
+ HandleValue rhs, MutableHandleValue ret) {
+ IonScript* ionScript = outerScript->ionScript();
+ RootedScript script(cx, ic->script());
+ jsbytecode* pc = ic->pc();
+ JSOp op = JSOp(*pc);
+
+ // Don't pass lhs/rhs directly, we need the original values when
+ // generating stubs.
+ RootedValue lhsCopy(cx, lhs);
+ RootedValue rhsCopy(cx, rhs);
+
+ // Perform the compare operation.
+ switch (op) {
+ case JSOp::Add:
+ // Do an add.
+ if (!AddValues(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ case JSOp::Sub:
+ if (!SubValues(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ case JSOp::Mul:
+ if (!MulValues(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ case JSOp::Div:
+ if (!DivValues(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ case JSOp::Mod:
+ if (!ModValues(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ case JSOp::Pow:
+ if (!PowValues(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ case JSOp::BitOr: {
+ if (!BitOr(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ }
+ case JSOp::BitXor: {
+ if (!BitXor(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ }
+ case JSOp::BitAnd: {
+ if (!BitAnd(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ }
+ case JSOp::Lsh: {
+ if (!BitLsh(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ }
+ case JSOp::Rsh: {
+ if (!BitRsh(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ }
+ case JSOp::Ursh: {
+ if (!UrshValues(cx, &lhsCopy, &rhsCopy, ret)) {
+ return false;
+ }
+ break;
+ }
+ default:
+ MOZ_CRASH("Unhandled binary arith op");
+ }
+
+ TryAttachIonStub<BinaryArithIRGenerator>(cx, ic, ionScript, op, lhs, rhs,
+ ret);
+
+ return true;
+}
+
+/* static */
+bool IonCompareIC::update(JSContext* cx, HandleScript outerScript,
+ IonCompareIC* ic, HandleValue lhs, HandleValue rhs,
+ bool* res) {
+ IonScript* ionScript = outerScript->ionScript();
+ RootedScript script(cx, ic->script());
+ jsbytecode* pc = ic->pc();
+ JSOp op = JSOp(*pc);
+
+ // Don't pass lhs/rhs directly, we need the original values when
+ // generating stubs.
+ RootedValue lhsCopy(cx, lhs);
+ RootedValue rhsCopy(cx, rhs);
+
+ // Perform the compare operation.
+ switch (op) {
+ case JSOp::Lt:
+ if (!LessThan(cx, &lhsCopy, &rhsCopy, res)) {
+ return false;
+ }
+ break;
+ case JSOp::Le:
+ if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, res)) {
+ return false;
+ }
+ break;
+ case JSOp::Gt:
+ if (!GreaterThan(cx, &lhsCopy, &rhsCopy, res)) {
+ return false;
+ }
+ break;
+ case JSOp::Ge:
+ if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, res)) {
+ return false;
+ }
+ break;
+ case JSOp::Eq:
+ if (!js::LooselyEqual(cx, lhsCopy, rhsCopy, res)) {
+ return false;
+ }
+ break;
+ case JSOp::Ne:
+ if (!js::LooselyEqual(cx, lhsCopy, rhsCopy, res)) {
+ return false;
+ }
+ *res = !*res;
+ break;
+ case JSOp::StrictEq:
+ if (!js::StrictlyEqual(cx, lhsCopy, rhsCopy, res)) {
+ return false;
+ }
+ break;
+ case JSOp::StrictNe:
+ if (!js::StrictlyEqual(cx, lhsCopy, rhsCopy, res)) {
+ return false;
+ }
+ *res = !*res;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unhandled ion compare op");
+ return false;
+ }
+
+ TryAttachIonStub<CompareIRGenerator>(cx, ic, ionScript, op, lhs, rhs);
+
+ return true;
+}
+
+uint8_t* IonICStub::stubDataStart() {
+ return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
+}
+
+void IonIC::attachStub(IonICStub* newStub, JitCode* code) {
+ MOZ_ASSERT(newStub);
+ MOZ_ASSERT(code);
+
+ if (firstStub_) {
+ newStub->setNext(firstStub_, codeRaw_);
+ }
+ firstStub_ = newStub;
+ codeRaw_ = code->raw();
+
+ state_.trackAttached();
+}