summaryrefslogtreecommitdiffstats
path: root/js/src/jit/Recover.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/Recover.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/Recover.cpp')
-rw-r--r--js/src/jit/Recover.cpp2156
1 files changed, 2156 insertions, 0 deletions
diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp
new file mode 100644
index 0000000000..e70722bffe
--- /dev/null
+++ b/js/src/jit/Recover.cpp
@@ -0,0 +1,2156 @@
+/* -*- 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/Recover.h"
+
+#include "jsmath.h"
+
+#include "builtin/Object.h"
+#include "builtin/RegExp.h"
+#include "builtin/String.h"
+#include "jit/Bailouts.h"
+#include "jit/CompileInfo.h"
+#include "jit/Ion.h"
+#include "jit/JitSpewer.h"
+#include "jit/JSJitFrameIter.h"
+#include "jit/MIR.h"
+#include "jit/MIRGraph.h"
+#include "jit/VMFunctions.h"
+#include "util/DifferentialTesting.h"
+#include "vm/BigIntType.h"
+#include "vm/EqualityOperations.h"
+#include "vm/Interpreter.h"
+#include "vm/Iteration.h"
+#include "vm/JSContext.h"
+#include "vm/JSObject.h"
+#include "vm/PlainObject.h" // js::PlainObject
+#include "vm/StringType.h"
+#include "vm/Watchtower.h"
+
+#include "vm/Interpreter-inl.h"
+
+using namespace js;
+using namespace js::jit;
+
+bool MNode::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_CRASH("This instruction is not serializable");
+}
+
+void RInstruction::readRecoverData(CompactBufferReader& reader,
+ RInstructionStorage* raw) {
+ uint32_t op = reader.readUnsigned();
+ switch (Opcode(op)) {
+#define MATCH_OPCODES_(op) \
+ case Recover_##op: \
+ static_assert(sizeof(R##op) <= sizeof(RInstructionStorage), \
+ "storage space must be big enough to store R" #op); \
+ static_assert(alignof(R##op) <= alignof(RInstructionStorage), \
+ "storage space must be aligned adequate to store R" #op); \
+ new (raw->addr()) R##op(reader); \
+ break;
+
+ RECOVER_OPCODE_LIST(MATCH_OPCODES_)
+#undef MATCH_OPCODES_
+
+ case Recover_Invalid:
+ default:
+ MOZ_CRASH("Bad decoding of the previous instruction?");
+ }
+}
+
+bool MResumePoint::writeRecoverData(CompactBufferWriter& writer) const {
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_ResumePoint));
+
+ MBasicBlock* bb = block();
+ bool hasFun = bb->info().hasFunMaybeLazy();
+ uint32_t nargs = bb->info().nargs();
+ JSScript* script = bb->info().script();
+ uint32_t exprStack = stackDepth() - bb->info().ninvoke();
+
+#ifdef DEBUG
+ // Ensure that all snapshot which are encoded can safely be used for
+ // bailouts.
+ uint32_t numIntermediate = NumIntermediateValues(mode());
+ if (JSContext* cx = GetJitContext()->cx) {
+ if (!AssertBailoutStackDepth(cx, script, pc(), mode(),
+ exprStack - numIntermediate)) {
+ return false;
+ }
+ }
+#endif
+
+ uint32_t formalArgs = CountArgSlots(script, hasFun, nargs);
+
+ // Test if we honor the maximum of arguments at all times. This is a sanity
+ // check and not an algorithm limit. So check might be a bit too loose. +4
+ // to account for scope chain, return value, this value and maybe
+ // arguments_object.
+ MOZ_ASSERT(formalArgs < SNAPSHOT_MAX_NARGS + 4);
+
+#ifdef JS_JITSPEW
+ uint32_t implicit = StartArgSlot(script);
+#endif
+ uint32_t nallocs = formalArgs + script->nfixed() + exprStack;
+
+ JitSpew(JitSpew_IonSnapshots,
+ "Starting frame; implicit %u, formals %u, fixed %zu, exprs %u",
+ implicit, formalArgs - implicit, script->nfixed(), exprStack);
+
+ uint32_t pcOff = script->pcToOffset(pc());
+ JitSpew(JitSpew_IonSnapshots, "Writing pc offset %u, mode %s, nslots %u",
+ pcOff, ResumeModeToString(mode()), nallocs);
+
+ uint32_t pcOffAndMode =
+ (pcOff << RResumePoint::PCOffsetShift) | uint32_t(mode());
+ MOZ_RELEASE_ASSERT((pcOffAndMode >> RResumePoint::PCOffsetShift) == pcOff,
+ "pcOff doesn't fit in pcOffAndMode");
+ writer.writeUnsigned(pcOffAndMode);
+
+ writer.writeUnsigned(nallocs);
+ return true;
+}
+
+RResumePoint::RResumePoint(CompactBufferReader& reader) {
+ pcOffsetAndMode_ = reader.readUnsigned();
+ numOperands_ = reader.readUnsigned();
+ JitSpew(JitSpew_IonSnapshots,
+ "Read RResumePoint (pc offset %u, mode %s, nslots %u)", pcOffset(),
+ ResumeModeToString(mode()), numOperands_);
+}
+
+bool RResumePoint::recover(JSContext* cx, SnapshotIterator& iter) const {
+ MOZ_CRASH("This instruction is not recoverable.");
+}
+
+bool MBitNot::writeRecoverData(CompactBufferWriter& writer) const {
+ // 64-bit int bitnots exist only when compiling wasm; they exist neither for
+ // JS nor asm.js. So we don't expect them here.
+ MOZ_ASSERT(type() != MIRType::Int64);
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BitNot));
+ return true;
+}
+
+RBitNot::RBitNot(CompactBufferReader& reader) {}
+
+bool RBitNot::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue operand(cx, iter.read());
+ RootedValue result(cx);
+
+ if (!js::BitNot(cx, &operand, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBitAnd::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BitAnd));
+ return true;
+}
+
+RBitAnd::RBitAnd(CompactBufferReader& reader) {}
+
+bool RBitAnd::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+ MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
+
+ if (!js::BitAnd(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBitOr::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BitOr));
+ return true;
+}
+
+RBitOr::RBitOr(CompactBufferReader& reader) {}
+
+bool RBitOr::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+ MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
+
+ if (!js::BitOr(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBitXor::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BitXor));
+ return true;
+}
+
+RBitXor::RBitXor(CompactBufferReader& reader) {}
+
+bool RBitXor::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ if (!js::BitXor(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MLsh::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Lsh));
+ return true;
+}
+
+RLsh::RLsh(CompactBufferReader& reader) {}
+
+bool RLsh::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+ MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
+
+ if (!js::BitLsh(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MRsh::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Rsh));
+ return true;
+}
+
+RRsh::RRsh(CompactBufferReader& reader) {}
+
+bool RRsh::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+ MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
+
+ if (!js::BitRsh(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MUrsh::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Ursh));
+ return true;
+}
+
+RUrsh::RUrsh(CompactBufferReader& reader) {}
+
+bool RUrsh::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
+
+ RootedValue result(cx);
+ if (!js::UrshValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MSignExtendInt32::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_SignExtendInt32));
+ MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_);
+ writer.writeByte(uint8_t(mode_));
+ return true;
+}
+
+RSignExtendInt32::RSignExtendInt32(CompactBufferReader& reader) {
+ mode_ = reader.readByte();
+}
+
+bool RSignExtendInt32::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue operand(cx, iter.read());
+
+ int32_t i;
+ if (!ToInt32(cx, operand, &i)) {
+ return false;
+ }
+
+ int32_t result;
+ switch (MSignExtendInt32::Mode(mode_)) {
+ case MSignExtendInt32::Byte:
+ result = static_cast<int8_t>(i);
+ break;
+ case MSignExtendInt32::Half:
+ result = static_cast<int16_t>(i);
+ break;
+ }
+
+ iter.storeInstructionResult(JS::Int32Value(result));
+ return true;
+}
+
+bool MAdd::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Add));
+ writer.writeByte(type() == MIRType::Float32);
+ return true;
+}
+
+RAdd::RAdd(CompactBufferReader& reader) {
+ isFloatOperation_ = reader.readByte();
+}
+
+bool RAdd::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
+ if (!js::AddValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ // MIRType::Float32 is a specialization embedding the fact that the result is
+ // rounded to a Float32.
+ if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MSub::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Sub));
+ writer.writeByte(type() == MIRType::Float32);
+ return true;
+}
+
+RSub::RSub(CompactBufferReader& reader) {
+ isFloatOperation_ = reader.readByte();
+}
+
+bool RSub::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
+ if (!js::SubValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ // MIRType::Float32 is a specialization embedding the fact that the result is
+ // rounded to a Float32.
+ if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MMul::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Mul));
+ writer.writeByte(type() == MIRType::Float32);
+ MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_);
+ writer.writeByte(uint8_t(mode_));
+ return true;
+}
+
+RMul::RMul(CompactBufferReader& reader) {
+ isFloatOperation_ = reader.readByte();
+ mode_ = reader.readByte();
+}
+
+bool RMul::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ if (MMul::Mode(mode_) == MMul::Normal) {
+ if (!js::MulValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ // MIRType::Float32 is a specialization embedding the fact that the
+ // result is rounded to a Float32.
+ if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) {
+ return false;
+ }
+ } else {
+ MOZ_ASSERT(MMul::Mode(mode_) == MMul::Integer);
+ if (!js::math_imul_handle(cx, lhs, rhs, &result)) {
+ return false;
+ }
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MDiv::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Div));
+ writer.writeByte(type() == MIRType::Float32);
+ return true;
+}
+
+RDiv::RDiv(CompactBufferReader& reader) {
+ isFloatOperation_ = reader.readByte();
+}
+
+bool RDiv::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ if (!js::DivValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ // MIRType::Float32 is a specialization embedding the fact that the result is
+ // rounded to a Float32.
+ if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MMod::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Mod));
+ return true;
+}
+
+RMod::RMod(CompactBufferReader& reader) {}
+
+bool RMod::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
+ if (!js::ModValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MNot::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Not));
+ return true;
+}
+
+RNot::RNot(CompactBufferReader& reader) {}
+
+bool RNot::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue v(cx, iter.read());
+ RootedValue result(cx);
+
+ result.setBoolean(!ToBoolean(v));
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntAdd::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntAdd));
+ return true;
+}
+
+RBigIntAdd::RBigIntAdd(CompactBufferReader& reader) {}
+
+bool RBigIntAdd::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(lhs.isBigInt() && rhs.isBigInt());
+ if (!js::AddValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntSub::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntSub));
+ return true;
+}
+
+RBigIntSub::RBigIntSub(CompactBufferReader& reader) {}
+
+bool RBigIntSub::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(lhs.isBigInt() && rhs.isBigInt());
+ if (!js::SubValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntMul::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntMul));
+ return true;
+}
+
+RBigIntMul::RBigIntMul(CompactBufferReader& reader) {}
+
+bool RBigIntMul::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(lhs.isBigInt() && rhs.isBigInt());
+ if (!js::MulValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntDiv::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntDiv));
+ return true;
+}
+
+RBigIntDiv::RBigIntDiv(CompactBufferReader& reader) {}
+
+bool RBigIntDiv::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(lhs.isBigInt() && rhs.isBigInt());
+ MOZ_ASSERT(!rhs.toBigInt()->isZero(),
+ "division by zero throws and therefore can't be recovered");
+ if (!js::DivValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntMod::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntMod));
+ return true;
+}
+
+RBigIntMod::RBigIntMod(CompactBufferReader& reader) {}
+
+bool RBigIntMod::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(lhs.isBigInt() && rhs.isBigInt());
+ MOZ_ASSERT(!rhs.toBigInt()->isZero(),
+ "division by zero throws and therefore can't be recovered");
+ if (!js::ModValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntPow::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPow));
+ return true;
+}
+
+RBigIntPow::RBigIntPow(CompactBufferReader& reader) {}
+
+bool RBigIntPow::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(lhs.isBigInt() && rhs.isBigInt());
+ MOZ_ASSERT(!rhs.toBigInt()->isNegative(),
+ "negative exponent throws and therefore can't be recovered");
+ if (!js::PowValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntBitAnd::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntBitAnd));
+ return true;
+}
+
+RBigIntBitAnd::RBigIntBitAnd(CompactBufferReader& reader) {}
+
+bool RBigIntBitAnd::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(lhs.isBigInt() && rhs.isBigInt());
+ if (!js::BitAnd(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntBitOr::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntBitOr));
+ return true;
+}
+
+RBigIntBitOr::RBigIntBitOr(CompactBufferReader& reader) {}
+
+bool RBigIntBitOr::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(lhs.isBigInt() && rhs.isBigInt());
+ if (!js::BitOr(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntBitXor::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntBitXor));
+ return true;
+}
+
+RBigIntBitXor::RBigIntBitXor(CompactBufferReader& reader) {}
+
+bool RBigIntBitXor::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(lhs.isBigInt() && rhs.isBigInt());
+ if (!js::BitXor(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntLsh::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntLsh));
+ return true;
+}
+
+RBigIntLsh::RBigIntLsh(CompactBufferReader& reader) {}
+
+bool RBigIntLsh::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(lhs.isBigInt() && rhs.isBigInt());
+ if (!js::BitLsh(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntRsh::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntRsh));
+ return true;
+}
+
+RBigIntRsh::RBigIntRsh(CompactBufferReader& reader) {}
+
+bool RBigIntRsh::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(lhs.isBigInt() && rhs.isBigInt());
+ if (!js::BitRsh(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntIncrement::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntIncrement));
+ return true;
+}
+
+RBigIntIncrement::RBigIntIncrement(CompactBufferReader& reader) {}
+
+bool RBigIntIncrement::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue operand(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(operand.isBigInt());
+ if (!js::IncOperation(cx, operand, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntDecrement::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntDecrement));
+ return true;
+}
+
+RBigIntDecrement::RBigIntDecrement(CompactBufferReader& reader) {}
+
+bool RBigIntDecrement::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue operand(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(operand.isBigInt());
+ if (!js::DecOperation(cx, operand, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntNegate::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntNegate));
+ return true;
+}
+
+RBigIntNegate::RBigIntNegate(CompactBufferReader& reader) {}
+
+bool RBigIntNegate::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue operand(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(operand.isBigInt());
+ if (!js::NegOperation(cx, &operand, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MBigIntBitNot::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntBitNot));
+ return true;
+}
+
+RBigIntBitNot::RBigIntBitNot(CompactBufferReader& reader) {}
+
+bool RBigIntBitNot::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue operand(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(operand.isBigInt());
+ if (!js::BitNot(cx, &operand, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MCompare::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Compare));
+
+ static_assert(sizeof(JSOp) == sizeof(uint8_t));
+ writer.writeByte(uint8_t(jsop_));
+ return true;
+}
+
+RCompare::RCompare(CompactBufferReader& reader) {
+ jsop_ = JSOp(reader.readByte());
+
+ MOZ_ASSERT(IsEqualityOp(jsop_) || IsRelationalOp(jsop_));
+}
+
+bool RCompare::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+
+ bool result;
+ switch (jsop_) {
+ case JSOp::Eq:
+ case JSOp::Ne:
+ if (!js::LooselyEqual(cx, lhs, rhs, &result)) {
+ return false;
+ }
+ if (jsop_ == JSOp::Ne) {
+ result = !result;
+ }
+ break;
+ case JSOp::StrictEq:
+ case JSOp::StrictNe:
+ if (!StrictlyEqual(cx, lhs, rhs, &result)) {
+ return false;
+ }
+ if (jsop_ == JSOp::StrictNe) {
+ result = !result;
+ }
+ break;
+ case JSOp::Lt:
+ if (!js::LessThan(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+ break;
+ case JSOp::Le:
+ if (!js::LessThanOrEqual(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+ break;
+ case JSOp::Gt:
+ if (!js::GreaterThan(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+ break;
+ case JSOp::Ge:
+ if (!js::GreaterThanOrEqual(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+ break;
+ default:
+ MOZ_CRASH("Unexpected op.");
+ }
+
+ iter.storeInstructionResult(BooleanValue(result));
+ return true;
+}
+
+bool MConcat::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Concat));
+ return true;
+}
+
+RConcat::RConcat(CompactBufferReader& reader) {}
+
+bool RConcat::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue lhs(cx, iter.read());
+ RootedValue rhs(cx, iter.read());
+ RootedValue result(cx);
+
+ MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
+ if (!js::AddValues(cx, &lhs, &rhs, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+RStringLength::RStringLength(CompactBufferReader& reader) {}
+
+bool RStringLength::recover(JSContext* cx, SnapshotIterator& iter) const {
+ JSString* string = iter.read().toString();
+
+ static_assert(JSString::MAX_LENGTH <= INT32_MAX,
+ "Can cast string length to int32_t");
+
+ iter.storeInstructionResult(Int32Value(int32_t(string->length())));
+ return true;
+}
+
+bool MStringLength::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_StringLength));
+ return true;
+}
+
+bool MArgumentsLength::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_ArgumentsLength));
+ return true;
+}
+
+RArgumentsLength::RArgumentsLength(CompactBufferReader& reader) {}
+
+bool RArgumentsLength::recover(JSContext* cx, SnapshotIterator& iter) const {
+ uintptr_t numActualArgs = iter.frame()->numActualArgs();
+
+ static_assert(ARGS_LENGTH_MAX <= INT32_MAX,
+ "Can cast arguments count to int32_t");
+ MOZ_ASSERT(numActualArgs <= ARGS_LENGTH_MAX);
+
+ iter.storeInstructionResult(JS::Int32Value(int32_t(numActualArgs)));
+ return true;
+}
+
+bool MFloor::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
+ return true;
+}
+
+RFloor::RFloor(CompactBufferReader& reader) {}
+
+bool RFloor::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double num = iter.read().toNumber();
+ double result = js::math_floor_impl(num);
+
+ iter.storeInstructionResult(NumberValue(result));
+ return true;
+}
+
+bool MCeil::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Ceil));
+ return true;
+}
+
+RCeil::RCeil(CompactBufferReader& reader) {}
+
+bool RCeil::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double num = iter.read().toNumber();
+ double result = js::math_ceil_impl(num);
+
+ iter.storeInstructionResult(NumberValue(result));
+ return true;
+}
+
+bool MRound::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Round));
+ return true;
+}
+
+RRound::RRound(CompactBufferReader& reader) {}
+
+bool RRound::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double num = iter.read().toNumber();
+ double result = js::math_round_impl(num);
+
+ iter.storeInstructionResult(NumberValue(result));
+ return true;
+}
+
+bool MTrunc::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Trunc));
+ return true;
+}
+
+RTrunc::RTrunc(CompactBufferReader& reader) {}
+
+bool RTrunc::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double num = iter.read().toNumber();
+ double result = js::math_trunc_impl(num);
+
+ iter.storeInstructionResult(NumberValue(result));
+ return true;
+}
+
+bool MCharCodeAt::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_CharCodeAt));
+ return true;
+}
+
+RCharCodeAt::RCharCodeAt(CompactBufferReader& reader) {}
+
+bool RCharCodeAt::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedString string(cx, iter.read().toString());
+ int32_t index = iter.read().toInt32();
+
+ RootedValue result(cx);
+ if (0 <= index && size_t(index) < string->length()) {
+ char16_t c;
+ if (!string->getChar(cx, index, &c)) {
+ return false;
+ }
+ result.setInt32(c);
+ } else {
+ result.setNaN();
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MFromCharCode::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_FromCharCode));
+ return true;
+}
+
+RFromCharCode::RFromCharCode(CompactBufferReader& reader) {}
+
+bool RFromCharCode::recover(JSContext* cx, SnapshotIterator& iter) const {
+ int32_t charCode = iter.read().toInt32();
+
+ JSString* str = StringFromCharCode(cx, charCode);
+ if (!str) {
+ return false;
+ }
+
+ iter.storeInstructionResult(StringValue(str));
+ return true;
+}
+
+bool MFromCharCodeEmptyIfNegative::writeRecoverData(
+ CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(
+ uint32_t(RInstruction::Recover_FromCharCodeEmptyIfNegative));
+ return true;
+}
+
+RFromCharCodeEmptyIfNegative::RFromCharCodeEmptyIfNegative(
+ CompactBufferReader& reader) {}
+
+bool RFromCharCodeEmptyIfNegative::recover(JSContext* cx,
+ SnapshotIterator& iter) const {
+ int32_t charCode = iter.read().toInt32();
+
+ JSString* str;
+ if (charCode < 0) {
+ str = cx->emptyString();
+ } else {
+ str = StringFromCharCode(cx, charCode);
+ if (!str) {
+ return false;
+ }
+ }
+
+ iter.storeInstructionResult(StringValue(str));
+ return true;
+}
+
+bool MPow::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Pow));
+ return true;
+}
+
+RPow::RPow(CompactBufferReader& reader) {}
+
+bool RPow::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double base = iter.read().toNumber();
+ double power = iter.read().toNumber();
+ double result = ecmaPow(base, power);
+
+ iter.storeInstructionResult(NumberValue(result));
+ return true;
+}
+
+bool MPowHalf::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_PowHalf));
+ return true;
+}
+
+RPowHalf::RPowHalf(CompactBufferReader& reader) {}
+
+bool RPowHalf::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double base = iter.read().toNumber();
+ double power = 0.5;
+ double result = ecmaPow(base, power);
+
+ iter.storeInstructionResult(NumberValue(result));
+ return true;
+}
+
+bool MMinMax::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_MinMax));
+ writer.writeByte(isMax_);
+ return true;
+}
+
+RMinMax::RMinMax(CompactBufferReader& reader) { isMax_ = reader.readByte(); }
+
+bool RMinMax::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double x = iter.read().toNumber();
+ double y = iter.read().toNumber();
+
+ double result;
+ if (isMax_) {
+ result = js::math_max_impl(x, y);
+ } else {
+ result = js::math_min_impl(x, y);
+ }
+
+ iter.storeInstructionResult(NumberValue(result));
+ return true;
+}
+
+bool MAbs::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Abs));
+ return true;
+}
+
+RAbs::RAbs(CompactBufferReader& reader) {}
+
+bool RAbs::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double num = iter.read().toNumber();
+ double result = js::math_abs_impl(num);
+
+ iter.storeInstructionResult(NumberValue(result));
+ return true;
+}
+
+bool MSqrt::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Sqrt));
+ writer.writeByte(type() == MIRType::Float32);
+ return true;
+}
+
+RSqrt::RSqrt(CompactBufferReader& reader) {
+ isFloatOperation_ = reader.readByte();
+}
+
+bool RSqrt::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double num = iter.read().toNumber();
+ double result = js::math_sqrt_impl(num);
+
+ // MIRType::Float32 is a specialization embedding the fact that the result is
+ // rounded to a Float32.
+ if (isFloatOperation_) {
+ result = js::RoundFloat32(result);
+ }
+
+ iter.storeInstructionResult(DoubleValue(result));
+ return true;
+}
+
+bool MAtan2::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Atan2));
+ return true;
+}
+
+RAtan2::RAtan2(CompactBufferReader& reader) {}
+
+bool RAtan2::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double y = iter.read().toNumber();
+ double x = iter.read().toNumber();
+ double result = js::ecmaAtan2(y, x);
+
+ iter.storeInstructionResult(DoubleValue(result));
+ return true;
+}
+
+bool MHypot::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Hypot));
+ writer.writeUnsigned(uint32_t(numOperands()));
+ return true;
+}
+
+RHypot::RHypot(CompactBufferReader& reader)
+ : numOperands_(reader.readUnsigned()) {}
+
+bool RHypot::recover(JSContext* cx, SnapshotIterator& iter) const {
+ JS::RootedValueVector vec(cx);
+
+ if (!vec.reserve(numOperands_)) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < numOperands_; ++i) {
+ vec.infallibleAppend(iter.read());
+ }
+
+ RootedValue result(cx);
+
+ if (!js::math_hypot_handle(cx, vec, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MNearbyInt::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ switch (roundingMode_) {
+ case RoundingMode::Up:
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Ceil));
+ return true;
+ case RoundingMode::Down:
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
+ return true;
+ case RoundingMode::TowardsZero:
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Trunc));
+ return true;
+ default:
+ MOZ_CRASH("Unsupported rounding mode.");
+ }
+}
+
+RNearbyInt::RNearbyInt(CompactBufferReader& reader) {
+ roundingMode_ = reader.readByte();
+}
+
+bool RNearbyInt::recover(JSContext* cx, SnapshotIterator& iter) const {
+ MOZ_CRASH("Unsupported rounding mode.");
+}
+
+bool MSign::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Sign));
+ return true;
+}
+
+RSign::RSign(CompactBufferReader& reader) {}
+
+bool RSign::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double num = iter.read().toNumber();
+ double result = js::math_sign_impl(num);
+
+ iter.storeInstructionResult(NumberValue(result));
+ return true;
+}
+
+bool MMathFunction::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ switch (function_) {
+ case UnaryMathFunction::Ceil:
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Ceil));
+ return true;
+ case UnaryMathFunction::Floor:
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
+ return true;
+ case UnaryMathFunction::Round:
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Round));
+ return true;
+ case UnaryMathFunction::Trunc:
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Trunc));
+ return true;
+ case UnaryMathFunction::SinNative:
+ case UnaryMathFunction::SinFdlibm:
+ case UnaryMathFunction::CosNative:
+ case UnaryMathFunction::CosFdlibm:
+ case UnaryMathFunction::TanNative:
+ case UnaryMathFunction::TanFdlibm:
+ case UnaryMathFunction::Log:
+ case UnaryMathFunction::Exp:
+ case UnaryMathFunction::ACos:
+ case UnaryMathFunction::ASin:
+ case UnaryMathFunction::ATan:
+ case UnaryMathFunction::Log10:
+ case UnaryMathFunction::Log2:
+ case UnaryMathFunction::Log1P:
+ case UnaryMathFunction::ExpM1:
+ case UnaryMathFunction::CosH:
+ case UnaryMathFunction::SinH:
+ case UnaryMathFunction::TanH:
+ case UnaryMathFunction::ACosH:
+ case UnaryMathFunction::ASinH:
+ case UnaryMathFunction::ATanH:
+ case UnaryMathFunction::Cbrt:
+ static_assert(sizeof(UnaryMathFunction) == sizeof(uint8_t));
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_MathFunction));
+ writer.writeByte(uint8_t(function_));
+ return true;
+ }
+ MOZ_CRASH("Unknown math function.");
+}
+
+RMathFunction::RMathFunction(CompactBufferReader& reader) {
+ function_ = UnaryMathFunction(reader.readByte());
+}
+
+bool RMathFunction::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double num = iter.read().toNumber();
+
+ double result;
+ switch (function_) {
+ case UnaryMathFunction::SinNative:
+ result = js::math_sin_native_impl(num);
+ break;
+ case UnaryMathFunction::SinFdlibm:
+ result = js::math_sin_fdlibm_impl(num);
+ break;
+ case UnaryMathFunction::CosNative:
+ result = js::math_cos_native_impl(num);
+ break;
+ case UnaryMathFunction::CosFdlibm:
+ result = js::math_cos_fdlibm_impl(num);
+ break;
+ case UnaryMathFunction::TanNative:
+ result = js::math_tan_native_impl(num);
+ break;
+ case UnaryMathFunction::TanFdlibm:
+ result = js::math_tan_fdlibm_impl(num);
+ break;
+ case UnaryMathFunction::Log:
+ result = js::math_log_impl(num);
+ break;
+ case UnaryMathFunction::Exp:
+ result = js::math_exp_impl(num);
+ break;
+ case UnaryMathFunction::ACos:
+ result = js::math_acos_impl(num);
+ break;
+ case UnaryMathFunction::ASin:
+ result = js::math_asin_impl(num);
+ break;
+ case UnaryMathFunction::ATan:
+ result = js::math_atan_impl(num);
+ break;
+ case UnaryMathFunction::Log10:
+ result = js::math_log10_impl(num);
+ break;
+ case UnaryMathFunction::Log2:
+ result = js::math_log2_impl(num);
+ break;
+ case UnaryMathFunction::Log1P:
+ result = js::math_log1p_impl(num);
+ break;
+ case UnaryMathFunction::ExpM1:
+ result = js::math_expm1_impl(num);
+ break;
+ case UnaryMathFunction::CosH:
+ result = js::math_cosh_impl(num);
+ break;
+ case UnaryMathFunction::SinH:
+ result = js::math_sinh_impl(num);
+ break;
+ case UnaryMathFunction::TanH:
+ result = js::math_tanh_impl(num);
+ break;
+ case UnaryMathFunction::ACosH:
+ result = js::math_acosh_impl(num);
+ break;
+ case UnaryMathFunction::ASinH:
+ result = js::math_asinh_impl(num);
+ break;
+ case UnaryMathFunction::ATanH:
+ result = js::math_atanh_impl(num);
+ break;
+ case UnaryMathFunction::Cbrt:
+ result = js::math_cbrt_impl(num);
+ break;
+
+ case UnaryMathFunction::Trunc:
+ case UnaryMathFunction::Floor:
+ case UnaryMathFunction::Ceil:
+ case UnaryMathFunction::Round:
+ // These have their own recover instructions.
+ MOZ_CRASH("Unexpected rounding math function.");
+ }
+
+ iter.storeInstructionResult(DoubleValue(result));
+ return true;
+}
+
+bool MRandom::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(this->canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Random));
+ return true;
+}
+
+bool MRandom::canRecoverOnBailout() const {
+ return !js::SupportDifferentialTesting();
+}
+
+RRandom::RRandom(CompactBufferReader& reader) {}
+
+bool RRandom::recover(JSContext* cx, SnapshotIterator& iter) const {
+ iter.storeInstructionResult(DoubleValue(math_random_impl(cx)));
+ return true;
+}
+
+bool MStringSplit::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_StringSplit));
+ return true;
+}
+
+RStringSplit::RStringSplit(CompactBufferReader& reader) {}
+
+bool RStringSplit::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedString str(cx, iter.read().toString());
+ RootedString sep(cx, iter.read().toString());
+
+ JSObject* res = StringSplitString(cx, str, sep, INT32_MAX);
+ if (!res) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*res));
+ return true;
+}
+
+bool MNaNToZero::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_NaNToZero));
+ return true;
+}
+
+RNaNToZero::RNaNToZero(CompactBufferReader& reader) {}
+
+bool RNaNToZero::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double v = iter.read().toNumber();
+ if (std::isnan(v) || mozilla::IsNegativeZero(v)) {
+ v = 0.0;
+ }
+
+ iter.storeInstructionResult(DoubleValue(v));
+ return true;
+}
+
+bool MRegExpMatcher::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpMatcher));
+ return true;
+}
+
+RRegExpMatcher::RRegExpMatcher(CompactBufferReader& reader) {}
+
+bool RRegExpMatcher::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedObject regexp(cx, &iter.read().toObject());
+ RootedString input(cx, iter.read().toString());
+ int32_t lastIndex = iter.read().toInt32();
+
+ RootedValue result(cx);
+ if (!RegExpMatcherRaw(cx, regexp, input, lastIndex, nullptr, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(result);
+ return true;
+}
+
+bool MTypeOf::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_TypeOf));
+ return true;
+}
+
+RTypeOf::RTypeOf(CompactBufferReader& reader) {}
+
+bool RTypeOf::recover(JSContext* cx, SnapshotIterator& iter) const {
+ JS::Value v = iter.read();
+
+ iter.storeInstructionResult(Int32Value(TypeOfValue(v)));
+ return true;
+}
+
+bool MTypeOfName::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_TypeOfName));
+ return true;
+}
+
+RTypeOfName::RTypeOfName(CompactBufferReader& reader) {}
+
+bool RTypeOfName::recover(JSContext* cx, SnapshotIterator& iter) const {
+ int32_t type = iter.read().toInt32();
+ MOZ_ASSERT(JSTYPE_UNDEFINED <= type && type < JSTYPE_LIMIT);
+
+ JSString* name = TypeName(JSType(type), *cx->runtime()->commonNames);
+ iter.storeInstructionResult(StringValue(name));
+ return true;
+}
+
+bool MToDouble::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_ToDouble));
+ return true;
+}
+
+RToDouble::RToDouble(CompactBufferReader& reader) {}
+
+bool RToDouble::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue v(cx, iter.read());
+
+ MOZ_ASSERT(!v.isObject());
+ MOZ_ASSERT(!v.isSymbol());
+ MOZ_ASSERT(!v.isBigInt());
+
+ double dbl;
+ if (!ToNumber(cx, v, &dbl)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(DoubleValue(dbl));
+ return true;
+}
+
+bool MToFloat32::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_ToFloat32));
+ return true;
+}
+
+RToFloat32::RToFloat32(CompactBufferReader& reader) {}
+
+bool RToFloat32::recover(JSContext* cx, SnapshotIterator& iter) const {
+ double num = iter.read().toNumber();
+ double result = js::RoundFloat32(num);
+
+ iter.storeInstructionResult(DoubleValue(result));
+ return true;
+}
+
+bool MTruncateToInt32::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_TruncateToInt32));
+ return true;
+}
+
+RTruncateToInt32::RTruncateToInt32(CompactBufferReader& reader) {}
+
+bool RTruncateToInt32::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue value(cx, iter.read());
+
+ int32_t trunc;
+ if (!JS::ToInt32(cx, value, &trunc)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(Int32Value(trunc));
+ return true;
+}
+
+bool MNewObject::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_NewObject));
+
+ // Recover instructions are only supported if we have a template object.
+ MOZ_ASSERT(mode_ == MNewObject::ObjectCreate);
+ return true;
+}
+
+RNewObject::RNewObject(CompactBufferReader& reader) {}
+
+bool RNewObject::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedObject templateObject(cx, &iter.read().toObject());
+
+ // See CodeGenerator::visitNewObjectVMCall.
+ // Note that recover instructions are only used if mode == ObjectCreate.
+ JSObject* resultObject =
+ ObjectCreateWithTemplate(cx, templateObject.as<PlainObject>());
+ if (!resultObject) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*resultObject));
+ return true;
+}
+
+bool MNewPlainObject::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_NewPlainObject));
+
+ MOZ_ASSERT(gc::AllocKind(uint8_t(allocKind_)) == allocKind_);
+ writer.writeByte(uint8_t(allocKind_));
+ MOZ_ASSERT(gc::Heap(uint8_t(initialHeap_)) == initialHeap_);
+ writer.writeByte(uint8_t(initialHeap_));
+ return true;
+}
+
+RNewPlainObject::RNewPlainObject(CompactBufferReader& reader) {
+ allocKind_ = gc::AllocKind(reader.readByte());
+ MOZ_ASSERT(gc::IsValidAllocKind(allocKind_));
+ initialHeap_ = gc::Heap(reader.readByte());
+ MOZ_ASSERT(initialHeap_ == gc::Heap::Default ||
+ initialHeap_ == gc::Heap::Tenured);
+}
+
+bool RNewPlainObject::recover(JSContext* cx, SnapshotIterator& iter) const {
+ Rooted<SharedShape*> shape(cx,
+ &iter.read().toGCCellPtr().as<Shape>().asShared());
+
+ // See CodeGenerator::visitNewPlainObject.
+ JSObject* resultObject =
+ NewPlainObjectOptimizedFallback(cx, shape, allocKind_, initialHeap_);
+ if (!resultObject) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*resultObject));
+ return true;
+}
+
+bool MNewArrayObject::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArrayObject));
+
+ writer.writeUnsigned(length_);
+ MOZ_ASSERT(gc::Heap(uint8_t(initialHeap_)) == initialHeap_);
+ writer.writeByte(uint8_t(initialHeap_));
+ return true;
+}
+
+RNewArrayObject::RNewArrayObject(CompactBufferReader& reader) {
+ length_ = reader.readUnsigned();
+ initialHeap_ = gc::Heap(reader.readByte());
+ MOZ_ASSERT(initialHeap_ == gc::Heap::Default ||
+ initialHeap_ == gc::Heap::Tenured);
+}
+
+bool RNewArrayObject::recover(JSContext* cx, SnapshotIterator& iter) const {
+ iter.read(); // Skip unused shape field.
+
+ NewObjectKind kind =
+ initialHeap_ == gc::Heap::Tenured ? TenuredObject : GenericObject;
+ JSObject* array = NewArrayOperation(cx, length_, kind);
+ if (!array) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*array));
+ return true;
+}
+
+bool MNewTypedArray::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_NewTypedArray));
+ return true;
+}
+
+RNewTypedArray::RNewTypedArray(CompactBufferReader& reader) {}
+
+bool RNewTypedArray::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedObject templateObject(cx, &iter.read().toObject());
+
+ size_t length = templateObject.as<FixedLengthTypedArrayObject>()->length();
+ MOZ_ASSERT(length <= INT32_MAX,
+ "Template objects are only created for int32 lengths");
+
+ JSObject* resultObject =
+ NewTypedArrayWithTemplateAndLength(cx, templateObject, length);
+ if (!resultObject) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*resultObject));
+ return true;
+}
+
+bool MNewArray::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArray));
+ writer.writeUnsigned(length());
+ return true;
+}
+
+RNewArray::RNewArray(CompactBufferReader& reader) {
+ count_ = reader.readUnsigned();
+}
+
+bool RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedObject templateObject(cx, &iter.read().toObject());
+ Rooted<Shape*> shape(cx, templateObject->shape());
+
+ ArrayObject* resultObject = NewArrayWithShape(cx, count_, shape);
+ if (!resultObject) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*resultObject));
+ return true;
+}
+
+bool MNewIterator::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_NewIterator));
+ writer.writeByte(type_);
+ return true;
+}
+
+RNewIterator::RNewIterator(CompactBufferReader& reader) {
+ type_ = reader.readByte();
+}
+
+bool RNewIterator::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedObject templateObject(cx, &iter.read().toObject());
+
+ JSObject* resultObject = nullptr;
+ switch (MNewIterator::Type(type_)) {
+ case MNewIterator::ArrayIterator:
+ resultObject = NewArrayIterator(cx);
+ break;
+ case MNewIterator::StringIterator:
+ resultObject = NewStringIterator(cx);
+ break;
+ case MNewIterator::RegExpStringIterator:
+ resultObject = NewRegExpStringIterator(cx);
+ break;
+ }
+
+ if (!resultObject) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*resultObject));
+ return true;
+}
+
+bool MLambda::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Lambda));
+ return true;
+}
+
+RLambda::RLambda(CompactBufferReader& reader) {}
+
+bool RLambda::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedObject scopeChain(cx, &iter.read().toObject());
+ RootedFunction fun(cx, &iter.read().toObject().as<JSFunction>());
+
+ JSObject* resultObject = js::Lambda(cx, fun, scopeChain);
+ if (!resultObject) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*resultObject));
+ return true;
+}
+
+bool MFunctionWithProto::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_FunctionWithProto));
+ return true;
+}
+
+RFunctionWithProto::RFunctionWithProto(CompactBufferReader& reader) {}
+
+bool RFunctionWithProto::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedObject scopeChain(cx, &iter.read().toObject());
+ RootedObject prototype(cx, &iter.read().toObject());
+ RootedFunction fun(cx, &iter.read().toObject().as<JSFunction>());
+
+ JSObject* resultObject =
+ js::FunWithProtoOperation(cx, fun, scopeChain, prototype);
+ if (!resultObject) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*resultObject));
+ return true;
+}
+
+bool MNewCallObject::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_NewCallObject));
+ return true;
+}
+
+RNewCallObject::RNewCallObject(CompactBufferReader& reader) {}
+
+bool RNewCallObject::recover(JSContext* cx, SnapshotIterator& iter) const {
+ Rooted<CallObject*> templateObj(cx, &iter.read().toObject().as<CallObject>());
+
+ Rooted<SharedShape*> shape(cx, templateObj->sharedShape());
+
+ JSObject* resultObject = CallObject::createWithShape(cx, shape);
+ if (!resultObject) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*resultObject));
+ return true;
+}
+
+bool MObjectKeys::canRecoverOnBailout() const {
+ // Only claim that this operation can be recovered on bailout if some other
+ // optimization already marked it as such.
+ return isRecoveredOnBailout();
+}
+
+bool MObjectKeys::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_ObjectKeys));
+ return true;
+}
+
+RObjectKeys::RObjectKeys(CompactBufferReader& reader) {}
+
+bool RObjectKeys::recover(JSContext* cx, SnapshotIterator& iter) const {
+ Rooted<JSObject*> obj(cx, &iter.read().toObject());
+
+ JSObject* resultKeys = ObjectKeys(cx, obj);
+ if (!resultKeys) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*resultKeys));
+ return true;
+}
+
+bool MObjectState::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_ObjectState));
+ writer.writeUnsigned(numSlots());
+ return true;
+}
+
+RObjectState::RObjectState(CompactBufferReader& reader) {
+ numSlots_ = reader.readUnsigned();
+}
+
+bool RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedObject object(cx, &iter.read().toObject());
+ Handle<NativeObject*> nativeObject = object.as<NativeObject>();
+ MOZ_ASSERT(!Watchtower::watchesPropertyModification(nativeObject));
+ MOZ_ASSERT(nativeObject->slotSpan() == numSlots());
+
+ for (size_t i = 0; i < numSlots(); i++) {
+ Value val = iter.read();
+ nativeObject->setSlot(i, val);
+ }
+
+ iter.storeInstructionResult(ObjectValue(*object));
+ return true;
+}
+
+bool MArrayState::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_ArrayState));
+ writer.writeUnsigned(numElements());
+ return true;
+}
+
+RArrayState::RArrayState(CompactBufferReader& reader) {
+ numElements_ = reader.readUnsigned();
+}
+
+bool RArrayState::recover(JSContext* cx, SnapshotIterator& iter) const {
+ ArrayObject* object = &iter.read().toObject().as<ArrayObject>();
+ uint32_t initLength = iter.read().toInt32();
+
+ MOZ_ASSERT(object->getDenseInitializedLength() == 0,
+ "initDenseElement call below relies on this");
+ object->setDenseInitializedLength(initLength);
+
+ for (size_t index = 0; index < numElements(); index++) {
+ Value val = iter.read();
+
+ if (index >= initLength) {
+ MOZ_ASSERT(val.isUndefined());
+ continue;
+ }
+
+ object->initDenseElement(index, val);
+ }
+
+ iter.storeInstructionResult(ObjectValue(*object));
+ return true;
+}
+
+bool MSetArrayLength::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ // For simplicity, we capture directly the object instead of the elements
+ // pointer.
+ MOZ_ASSERT(elements()->type() != MIRType::Elements);
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_SetArrayLength));
+ return true;
+}
+
+bool MSetArrayLength::canRecoverOnBailout() const {
+ return isRecoveredOnBailout();
+}
+
+RSetArrayLength::RSetArrayLength(CompactBufferReader& reader) {}
+
+bool RSetArrayLength::recover(JSContext* cx, SnapshotIterator& iter) const {
+ Rooted<ArrayObject*> obj(cx, &iter.read().toObject().as<ArrayObject>());
+ RootedValue len(cx, iter.read());
+
+ RootedId id(cx, NameToId(cx->names().length));
+ Rooted<PropertyDescriptor> desc(
+ cx, PropertyDescriptor::Data(len, JS::PropertyAttribute::Writable));
+ ObjectOpResult error;
+ if (!ArraySetLength(cx, obj, id, desc, error)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*obj));
+ return true;
+}
+
+bool MAssertRecoveredOnBailout::writeRecoverData(
+ CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ MOZ_RELEASE_ASSERT(input()->isRecoveredOnBailout() == mustBeRecovered_,
+ "assertRecoveredOnBailout failed during compilation");
+ writer.writeUnsigned(
+ uint32_t(RInstruction::Recover_AssertRecoveredOnBailout));
+ return true;
+}
+
+RAssertRecoveredOnBailout::RAssertRecoveredOnBailout(
+ CompactBufferReader& reader) {}
+
+bool RAssertRecoveredOnBailout::recover(JSContext* cx,
+ SnapshotIterator& iter) const {
+ iter.read(); // skip the unused operand.
+ iter.storeInstructionResult(UndefinedValue());
+ return true;
+}
+
+bool MStringReplace::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_StringReplace));
+ writer.writeByte(isFlatReplacement_);
+ return true;
+}
+
+RStringReplace::RStringReplace(CompactBufferReader& reader) {
+ isFlatReplacement_ = reader.readByte();
+}
+
+bool RStringReplace::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedString string(cx, iter.read().toString());
+ RootedString pattern(cx, iter.read().toString());
+ RootedString replace(cx, iter.read().toString());
+
+ JSString* result =
+ isFlatReplacement_
+ ? js::StringFlatReplaceString(cx, string, pattern, replace)
+ : js::str_replace_string_raw(cx, string, pattern, replace);
+
+ if (!result) {
+ return false;
+ }
+
+ iter.storeInstructionResult(StringValue(result));
+ return true;
+}
+
+bool MSubstr::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Substr));
+ return true;
+}
+
+RSubstr::RSubstr(CompactBufferReader& reader) {}
+
+bool RSubstr::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedString str(cx, iter.read().toString());
+ int32_t begin = iter.read().toInt32();
+ int32_t length = iter.read().toInt32();
+
+ JSString* result = SubstringKernel(cx, str, begin, length);
+ if (!result) {
+ return false;
+ }
+
+ iter.storeInstructionResult(StringValue(result));
+ return true;
+}
+
+bool MAtomicIsLockFree::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_AtomicIsLockFree));
+ return true;
+}
+
+RAtomicIsLockFree::RAtomicIsLockFree(CompactBufferReader& reader) {}
+
+bool RAtomicIsLockFree::recover(JSContext* cx, SnapshotIterator& iter) const {
+ RootedValue operand(cx, iter.read());
+ MOZ_ASSERT(operand.isInt32());
+
+ int32_t result;
+ if (!js::AtomicIsLockFree(cx, operand, &result)) {
+ return false;
+ }
+
+ iter.storeInstructionResult(Int32Value(result));
+ return true;
+}
+
+bool MBigIntAsIntN::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntAsIntN));
+ return true;
+}
+
+RBigIntAsIntN::RBigIntAsIntN(CompactBufferReader& reader) {}
+
+bool RBigIntAsIntN::recover(JSContext* cx, SnapshotIterator& iter) const {
+ int32_t bits = iter.read().toInt32();
+ RootedBigInt input(cx, iter.read().toBigInt());
+
+ MOZ_ASSERT(bits >= 0);
+ BigInt* result = BigInt::asIntN(cx, input, bits);
+ if (!result) {
+ return false;
+ }
+
+ iter.storeInstructionResult(JS::BigIntValue(result));
+ return true;
+}
+
+bool MBigIntAsUintN::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntAsUintN));
+ return true;
+}
+
+RBigIntAsUintN::RBigIntAsUintN(CompactBufferReader& reader) {}
+
+bool RBigIntAsUintN::recover(JSContext* cx, SnapshotIterator& iter) const {
+ int32_t bits = iter.read().toInt32();
+ RootedBigInt input(cx, iter.read().toBigInt());
+
+ MOZ_ASSERT(bits >= 0);
+ BigInt* result = BigInt::asUintN(cx, input, bits);
+ if (!result) {
+ return false;
+ }
+
+ iter.storeInstructionResult(JS::BigIntValue(result));
+ return true;
+}
+
+bool MCreateArgumentsObject::writeRecoverData(
+ CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_CreateArgumentsObject));
+ return true;
+}
+
+RCreateArgumentsObject::RCreateArgumentsObject(CompactBufferReader& reader) {}
+
+bool RCreateArgumentsObject::recover(JSContext* cx,
+ SnapshotIterator& iter) const {
+ RootedObject callObject(cx, &iter.read().toObject());
+ RootedObject result(
+ cx, ArgumentsObject::createForIon(cx, iter.frame(), callObject));
+ if (!result) {
+ return false;
+ }
+
+ iter.storeInstructionResult(JS::ObjectValue(*result));
+ return true;
+}
+
+bool MCreateInlinedArgumentsObject::writeRecoverData(
+ CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(
+ uint32_t(RInstruction::Recover_CreateInlinedArgumentsObject));
+ writer.writeUnsigned(numActuals());
+ return true;
+}
+
+RCreateInlinedArgumentsObject::RCreateInlinedArgumentsObject(
+ CompactBufferReader& reader) {
+ numActuals_ = reader.readUnsigned();
+}
+
+bool RCreateInlinedArgumentsObject::recover(JSContext* cx,
+ SnapshotIterator& iter) const {
+ RootedObject callObject(cx, &iter.read().toObject());
+ RootedFunction callee(cx, &iter.read().toObject().as<JSFunction>());
+
+ JS::RootedValueArray<ArgumentsObject::MaxInlinedArgs> argsArray(cx);
+ for (uint32_t i = 0; i < numActuals_; i++) {
+ argsArray[i].set(iter.read());
+ }
+
+ ArgumentsObject* result = ArgumentsObject::createFromValueArray(
+ cx, argsArray, callee, callObject, numActuals_);
+ if (!result) {
+ return false;
+ }
+
+ iter.storeInstructionResult(JS::ObjectValue(*result));
+ return true;
+}
+
+bool MRest::writeRecoverData(CompactBufferWriter& writer) const {
+ MOZ_ASSERT(canRecoverOnBailout());
+ writer.writeUnsigned(uint32_t(RInstruction::Recover_Rest));
+ writer.writeUnsigned(numFormals());
+ return true;
+}
+
+RRest::RRest(CompactBufferReader& reader) {
+ numFormals_ = reader.readUnsigned();
+}
+
+bool RRest::recover(JSContext* cx, SnapshotIterator& iter) const {
+ JitFrameLayout* frame = iter.frame();
+
+ uint32_t numActuals = iter.read().toInt32();
+ MOZ_ASSERT(numActuals == frame->numActualArgs());
+
+ uint32_t numFormals = numFormals_;
+
+ uint32_t length = std::max(numActuals, numFormals) - numFormals;
+ Value* src = frame->actualArgs() + numFormals;
+ JSObject* rest = jit::InitRestParameter(cx, length, src, nullptr);
+ if (!rest) {
+ return false;
+ }
+
+ iter.storeInstructionResult(ObjectValue(*rest));
+ return true;
+}