1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "jit/Recover.h"
8 
9 #include "jsapi.h"
10 #include "jsmath.h"
11 
12 #include "builtin/RegExp.h"
13 #include "builtin/SIMD.h"
14 #include "builtin/String.h"
15 #include "builtin/TypedObject.h"
16 #include "gc/Heap.h"
17 #include "jit/JitSpewer.h"
18 #include "jit/JSJitFrameIter.h"
19 #include "jit/MIR.h"
20 #include "jit/MIRGraph.h"
21 #include "jit/VMFunctions.h"
22 #include "vm/Interpreter.h"
23 #include "vm/Iteration.h"
24 #include "vm/JSContext.h"
25 #include "vm/JSObject.h"
26 #include "vm/StringType.h"
27 
28 #include "vm/Interpreter-inl.h"
29 #include "vm/NativeObject-inl.h"
30 
31 using namespace js;
32 using namespace js::jit;
33 
writeRecoverData(CompactBufferWriter & writer) const34 bool MNode::writeRecoverData(CompactBufferWriter& writer) const {
35   MOZ_CRASH("This instruction is not serializable");
36 }
37 
readRecoverData(CompactBufferReader & reader,RInstructionStorage * raw)38 void RInstruction::readRecoverData(CompactBufferReader& reader,
39                                    RInstructionStorage* raw) {
40   uint32_t op = reader.readUnsigned();
41   switch (Opcode(op)) {
42 #define MATCH_OPCODES_(op)                                                  \
43   case Recover_##op:                                                        \
44     static_assert(sizeof(R##op) <= sizeof(RInstructionStorage),             \
45                   "storage space must be big enough to store R" #op);       \
46     static_assert(alignof(R##op) <= alignof(RInstructionStorage),           \
47                   "storage space must be aligned adequate to store R" #op); \
48     new (raw->addr()) R##op(reader);                                        \
49     break;
50 
51     RECOVER_OPCODE_LIST(MATCH_OPCODES_)
52 #undef MATCH_OPCODES_
53 
54     case Recover_Invalid:
55     default:
56       MOZ_CRASH("Bad decoding of the previous instruction?");
57   }
58 }
59 
writeRecoverData(CompactBufferWriter & writer) const60 bool MResumePoint::writeRecoverData(CompactBufferWriter& writer) const {
61   writer.writeUnsigned(uint32_t(RInstruction::Recover_ResumePoint));
62 
63   MBasicBlock* bb = block();
64   JSFunction* fun = bb->info().funMaybeLazy();
65   JSScript* script = bb->info().script();
66   uint32_t exprStack = stackDepth() - bb->info().ninvoke();
67 
68 #ifdef DEBUG
69   // Ensure that all snapshot which are encoded can safely be used for
70   // bailouts.
71   if (GetJitContext()->cx) {
72     uint32_t stackDepth;
73     bool reachablePC;
74     jsbytecode* bailPC = pc();
75 
76     if (mode() == MResumePoint::ResumeAfter) bailPC = GetNextPc(pc());
77 
78     if (!ReconstructStackDepth(GetJitContext()->cx, script, bailPC, &stackDepth,
79                                &reachablePC)) {
80       return false;
81     }
82 
83     if (reachablePC) {
84       if (JSOp(*bailPC) == JSOP_FUNCALL) {
85         // For fun.call(this, ...); the reconstructStackDepth will
86         // include the this. When inlining that is not included.  So the
87         // exprStackSlots will be one less.
88         MOZ_ASSERT(stackDepth - exprStack <= 1);
89       } else if (JSOp(*bailPC) != JSOP_FUNAPPLY && !IsGetPropPC(bailPC) &&
90                  !IsSetPropPC(bailPC)) {
91         // For fun.apply({}, arguments) the reconstructStackDepth will
92         // have stackdepth 4, but it could be that we inlined the
93         // funapply. In that case exprStackSlots, will have the real
94         // arguments in the slots and not be 4.
95 
96         // With accessors, we have different stack depths depending on
97         // whether or not we inlined the accessor, as the inlined stack
98         // contains a callee function that should never have been there
99         // and we might just be capturing an uneventful property site,
100         // in which case there won't have been any violence.
101         MOZ_ASSERT(exprStack == stackDepth);
102       }
103     }
104   }
105 #endif
106 
107   // Test if we honor the maximum of arguments at all times.  This is a sanity
108   // check and not an algorithm limit. So check might be a bit too loose.  +4
109   // to account for scope chain, return value, this value and maybe
110   // arguments_object.
111   MOZ_ASSERT(CountArgSlots(script, fun) < SNAPSHOT_MAX_NARGS + 4);
112 
113 #ifdef JS_JITSPEW
114   uint32_t implicit = StartArgSlot(script);
115 #endif
116   uint32_t formalArgs = CountArgSlots(script, fun);
117   uint32_t nallocs = formalArgs + script->nfixed() + exprStack;
118 
119   JitSpew(JitSpew_IonSnapshots,
120           "Starting frame; implicit %u, formals %u, fixed %zu, exprs %u",
121           implicit, formalArgs - implicit, script->nfixed(), exprStack);
122 
123   uint32_t pcoff = script->pcToOffset(pc());
124   JitSpew(JitSpew_IonSnapshots, "Writing pc offset %u, nslots %u", pcoff,
125           nallocs);
126   writer.writeUnsigned(pcoff);
127   writer.writeUnsigned(nallocs);
128   return true;
129 }
130 
RResumePoint(CompactBufferReader & reader)131 RResumePoint::RResumePoint(CompactBufferReader& reader) {
132   pcOffset_ = reader.readUnsigned();
133   numOperands_ = reader.readUnsigned();
134   JitSpew(JitSpew_IonSnapshots, "Read RResumePoint (pc offset %u, nslots %u)",
135           pcOffset_, numOperands_);
136 }
137 
recover(JSContext * cx,SnapshotIterator & iter) const138 bool RResumePoint::recover(JSContext* cx, SnapshotIterator& iter) const {
139   MOZ_CRASH("This instruction is not recoverable.");
140 }
141 
writeRecoverData(CompactBufferWriter & writer) const142 bool MBitNot::writeRecoverData(CompactBufferWriter& writer) const {
143   MOZ_ASSERT(canRecoverOnBailout());
144   writer.writeUnsigned(uint32_t(RInstruction::Recover_BitNot));
145   return true;
146 }
147 
RBitNot(CompactBufferReader & reader)148 RBitNot::RBitNot(CompactBufferReader& reader) {}
149 
recover(JSContext * cx,SnapshotIterator & iter) const150 bool RBitNot::recover(JSContext* cx, SnapshotIterator& iter) const {
151   RootedValue operand(cx, iter.read());
152 
153   int32_t result;
154   if (!js::BitNot(cx, operand, &result)) return false;
155 
156   RootedValue rootedResult(cx, js::Int32Value(result));
157   iter.storeInstructionResult(rootedResult);
158   return true;
159 }
160 
writeRecoverData(CompactBufferWriter & writer) const161 bool MBitAnd::writeRecoverData(CompactBufferWriter& writer) const {
162   MOZ_ASSERT(canRecoverOnBailout());
163   writer.writeUnsigned(uint32_t(RInstruction::Recover_BitAnd));
164   return true;
165 }
166 
RBitAnd(CompactBufferReader & reader)167 RBitAnd::RBitAnd(CompactBufferReader& reader) {}
168 
recover(JSContext * cx,SnapshotIterator & iter) const169 bool RBitAnd::recover(JSContext* cx, SnapshotIterator& iter) const {
170   RootedValue lhs(cx, iter.read());
171   RootedValue rhs(cx, iter.read());
172   int32_t result;
173   MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
174 
175   if (!js::BitAnd(cx, lhs, rhs, &result)) return false;
176 
177   RootedValue rootedResult(cx, js::Int32Value(result));
178   iter.storeInstructionResult(rootedResult);
179   return true;
180 }
181 
writeRecoverData(CompactBufferWriter & writer) const182 bool MBitOr::writeRecoverData(CompactBufferWriter& writer) const {
183   MOZ_ASSERT(canRecoverOnBailout());
184   writer.writeUnsigned(uint32_t(RInstruction::Recover_BitOr));
185   return true;
186 }
187 
RBitOr(CompactBufferReader & reader)188 RBitOr::RBitOr(CompactBufferReader& reader) {}
189 
recover(JSContext * cx,SnapshotIterator & iter) const190 bool RBitOr::recover(JSContext* cx, SnapshotIterator& iter) const {
191   RootedValue lhs(cx, iter.read());
192   RootedValue rhs(cx, iter.read());
193   int32_t result;
194   MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
195 
196   if (!js::BitOr(cx, lhs, rhs, &result)) return false;
197 
198   RootedValue asValue(cx, js::Int32Value(result));
199   iter.storeInstructionResult(asValue);
200   return true;
201 }
202 
writeRecoverData(CompactBufferWriter & writer) const203 bool MBitXor::writeRecoverData(CompactBufferWriter& writer) const {
204   MOZ_ASSERT(canRecoverOnBailout());
205   writer.writeUnsigned(uint32_t(RInstruction::Recover_BitXor));
206   return true;
207 }
208 
RBitXor(CompactBufferReader & reader)209 RBitXor::RBitXor(CompactBufferReader& reader) {}
210 
recover(JSContext * cx,SnapshotIterator & iter) const211 bool RBitXor::recover(JSContext* cx, SnapshotIterator& iter) const {
212   RootedValue lhs(cx, iter.read());
213   RootedValue rhs(cx, iter.read());
214 
215   int32_t result;
216   if (!js::BitXor(cx, lhs, rhs, &result)) return false;
217 
218   RootedValue rootedResult(cx, js::Int32Value(result));
219   iter.storeInstructionResult(rootedResult);
220   return true;
221 }
222 
writeRecoverData(CompactBufferWriter & writer) const223 bool MLsh::writeRecoverData(CompactBufferWriter& writer) const {
224   MOZ_ASSERT(canRecoverOnBailout());
225   writer.writeUnsigned(uint32_t(RInstruction::Recover_Lsh));
226   return true;
227 }
228 
RLsh(CompactBufferReader & reader)229 RLsh::RLsh(CompactBufferReader& reader) {}
230 
recover(JSContext * cx,SnapshotIterator & iter) const231 bool RLsh::recover(JSContext* cx, SnapshotIterator& iter) const {
232   RootedValue lhs(cx, iter.read());
233   RootedValue rhs(cx, iter.read());
234   int32_t result;
235   MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
236 
237   if (!js::BitLsh(cx, lhs, rhs, &result)) return false;
238 
239   RootedValue asValue(cx, js::Int32Value(result));
240   iter.storeInstructionResult(asValue);
241   return true;
242 }
243 
writeRecoverData(CompactBufferWriter & writer) const244 bool MRsh::writeRecoverData(CompactBufferWriter& writer) const {
245   MOZ_ASSERT(canRecoverOnBailout());
246   writer.writeUnsigned(uint32_t(RInstruction::Recover_Rsh));
247   return true;
248 }
249 
RRsh(CompactBufferReader & reader)250 RRsh::RRsh(CompactBufferReader& reader) {}
251 
recover(JSContext * cx,SnapshotIterator & iter) const252 bool RRsh::recover(JSContext* cx, SnapshotIterator& iter) const {
253   RootedValue lhs(cx, iter.read());
254   RootedValue rhs(cx, iter.read());
255   MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
256 
257   int32_t result;
258   if (!js::BitRsh(cx, lhs, rhs, &result)) return false;
259 
260   RootedValue rootedResult(cx, js::Int32Value(result));
261   iter.storeInstructionResult(rootedResult);
262   return true;
263 }
264 
writeRecoverData(CompactBufferWriter & writer) const265 bool MUrsh::writeRecoverData(CompactBufferWriter& writer) const {
266   MOZ_ASSERT(canRecoverOnBailout());
267   writer.writeUnsigned(uint32_t(RInstruction::Recover_Ursh));
268   return true;
269 }
270 
RUrsh(CompactBufferReader & reader)271 RUrsh::RUrsh(CompactBufferReader& reader) {}
272 
recover(JSContext * cx,SnapshotIterator & iter) const273 bool RUrsh::recover(JSContext* cx, SnapshotIterator& iter) const {
274   RootedValue lhs(cx, iter.read());
275   RootedValue rhs(cx, iter.read());
276   MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
277 
278   RootedValue result(cx);
279   if (!js::UrshOperation(cx, lhs, rhs, &result)) return false;
280 
281   iter.storeInstructionResult(result);
282   return true;
283 }
284 
writeRecoverData(CompactBufferWriter & writer) const285 bool MSignExtendInt32::writeRecoverData(CompactBufferWriter& writer) const {
286   MOZ_ASSERT(canRecoverOnBailout());
287   writer.writeUnsigned(uint32_t(RInstruction::Recover_SignExtendInt32));
288   MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_);
289   writer.writeByte(uint8_t(mode_));
290   return true;
291 }
292 
RSignExtendInt32(CompactBufferReader & reader)293 RSignExtendInt32::RSignExtendInt32(CompactBufferReader& reader) {
294   mode_ = reader.readByte();
295 }
296 
recover(JSContext * cx,SnapshotIterator & iter) const297 bool RSignExtendInt32::recover(JSContext* cx, SnapshotIterator& iter) const {
298   RootedValue operand(cx, iter.read());
299 
300   int32_t result;
301   switch (MSignExtendInt32::Mode(mode_)) {
302     case MSignExtendInt32::Byte:
303       if (!js::SignExtendOperation<int8_t>(cx, operand, &result)) return false;
304       break;
305     case MSignExtendInt32::Half:
306       if (!js::SignExtendOperation<int16_t>(cx, operand, &result)) return false;
307       break;
308   }
309 
310   RootedValue rootedResult(cx, js::Int32Value(result));
311   iter.storeInstructionResult(rootedResult);
312   return true;
313 }
314 
writeRecoverData(CompactBufferWriter & writer) const315 bool MAdd::writeRecoverData(CompactBufferWriter& writer) const {
316   MOZ_ASSERT(canRecoverOnBailout());
317   writer.writeUnsigned(uint32_t(RInstruction::Recover_Add));
318   writer.writeByte(specialization_ == MIRType::Float32);
319   return true;
320 }
321 
RAdd(CompactBufferReader & reader)322 RAdd::RAdd(CompactBufferReader& reader) {
323   isFloatOperation_ = reader.readByte();
324 }
325 
recover(JSContext * cx,SnapshotIterator & iter) const326 bool RAdd::recover(JSContext* cx, SnapshotIterator& iter) const {
327   RootedValue lhs(cx, iter.read());
328   RootedValue rhs(cx, iter.read());
329   RootedValue result(cx);
330 
331   MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
332   if (!js::AddValues(cx, &lhs, &rhs, &result)) return false;
333 
334   // MIRType::Float32 is a specialization embedding the fact that the result is
335   // rounded to a Float32.
336   if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) return false;
337 
338   iter.storeInstructionResult(result);
339   return true;
340 }
341 
writeRecoverData(CompactBufferWriter & writer) const342 bool MSub::writeRecoverData(CompactBufferWriter& writer) const {
343   MOZ_ASSERT(canRecoverOnBailout());
344   writer.writeUnsigned(uint32_t(RInstruction::Recover_Sub));
345   writer.writeByte(specialization_ == MIRType::Float32);
346   return true;
347 }
348 
RSub(CompactBufferReader & reader)349 RSub::RSub(CompactBufferReader& reader) {
350   isFloatOperation_ = reader.readByte();
351 }
352 
recover(JSContext * cx,SnapshotIterator & iter) const353 bool RSub::recover(JSContext* cx, SnapshotIterator& iter) const {
354   RootedValue lhs(cx, iter.read());
355   RootedValue rhs(cx, iter.read());
356   RootedValue result(cx);
357 
358   MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
359   if (!js::SubValues(cx, &lhs, &rhs, &result)) return false;
360 
361   // MIRType::Float32 is a specialization embedding the fact that the result is
362   // rounded to a Float32.
363   if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) return false;
364 
365   iter.storeInstructionResult(result);
366   return true;
367 }
368 
writeRecoverData(CompactBufferWriter & writer) const369 bool MMul::writeRecoverData(CompactBufferWriter& writer) const {
370   MOZ_ASSERT(canRecoverOnBailout());
371   writer.writeUnsigned(uint32_t(RInstruction::Recover_Mul));
372   writer.writeByte(specialization_ == MIRType::Float32);
373   MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_);
374   writer.writeByte(uint8_t(mode_));
375   return true;
376 }
377 
RMul(CompactBufferReader & reader)378 RMul::RMul(CompactBufferReader& reader) {
379   isFloatOperation_ = reader.readByte();
380   mode_ = reader.readByte();
381 }
382 
recover(JSContext * cx,SnapshotIterator & iter) const383 bool RMul::recover(JSContext* cx, SnapshotIterator& iter) const {
384   RootedValue lhs(cx, iter.read());
385   RootedValue rhs(cx, iter.read());
386   RootedValue result(cx);
387 
388   if (MMul::Mode(mode_) == MMul::Normal) {
389     if (!js::MulValues(cx, &lhs, &rhs, &result)) return false;
390 
391     // MIRType::Float32 is a specialization embedding the fact that the
392     // result is rounded to a Float32.
393     if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) return false;
394   } else {
395     MOZ_ASSERT(MMul::Mode(mode_) == MMul::Integer);
396     if (!js::math_imul_handle(cx, lhs, rhs, &result)) return false;
397   }
398 
399   iter.storeInstructionResult(result);
400   return true;
401 }
402 
writeRecoverData(CompactBufferWriter & writer) const403 bool MDiv::writeRecoverData(CompactBufferWriter& writer) const {
404   MOZ_ASSERT(canRecoverOnBailout());
405   writer.writeUnsigned(uint32_t(RInstruction::Recover_Div));
406   writer.writeByte(specialization_ == MIRType::Float32);
407   return true;
408 }
409 
RDiv(CompactBufferReader & reader)410 RDiv::RDiv(CompactBufferReader& reader) {
411   isFloatOperation_ = reader.readByte();
412 }
413 
recover(JSContext * cx,SnapshotIterator & iter) const414 bool RDiv::recover(JSContext* cx, SnapshotIterator& iter) const {
415   RootedValue lhs(cx, iter.read());
416   RootedValue rhs(cx, iter.read());
417   RootedValue result(cx);
418 
419   if (!js::DivValues(cx, &lhs, &rhs, &result)) return false;
420 
421   // MIRType::Float32 is a specialization embedding the fact that the result is
422   // rounded to a Float32.
423   if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) return false;
424 
425   iter.storeInstructionResult(result);
426   return true;
427 }
428 
writeRecoverData(CompactBufferWriter & writer) const429 bool MMod::writeRecoverData(CompactBufferWriter& writer) const {
430   MOZ_ASSERT(canRecoverOnBailout());
431   writer.writeUnsigned(uint32_t(RInstruction::Recover_Mod));
432   return true;
433 }
434 
RMod(CompactBufferReader & reader)435 RMod::RMod(CompactBufferReader& reader) {}
436 
recover(JSContext * cx,SnapshotIterator & iter) const437 bool RMod::recover(JSContext* cx, SnapshotIterator& iter) const {
438   RootedValue lhs(cx, iter.read());
439   RootedValue rhs(cx, iter.read());
440   RootedValue result(cx);
441 
442   MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
443   if (!js::ModValues(cx, &lhs, &rhs, &result)) return false;
444 
445   iter.storeInstructionResult(result);
446   return true;
447 }
448 
writeRecoverData(CompactBufferWriter & writer) const449 bool MNot::writeRecoverData(CompactBufferWriter& writer) const {
450   MOZ_ASSERT(canRecoverOnBailout());
451   writer.writeUnsigned(uint32_t(RInstruction::Recover_Not));
452   return true;
453 }
454 
RNot(CompactBufferReader & reader)455 RNot::RNot(CompactBufferReader& reader) {}
456 
recover(JSContext * cx,SnapshotIterator & iter) const457 bool RNot::recover(JSContext* cx, SnapshotIterator& iter) const {
458   RootedValue v(cx, iter.read());
459   RootedValue result(cx);
460 
461   result.setBoolean(!ToBoolean(v));
462 
463   iter.storeInstructionResult(result);
464   return true;
465 }
466 
writeRecoverData(CompactBufferWriter & writer) const467 bool MConcat::writeRecoverData(CompactBufferWriter& writer) const {
468   MOZ_ASSERT(canRecoverOnBailout());
469   writer.writeUnsigned(uint32_t(RInstruction::Recover_Concat));
470   return true;
471 }
472 
RConcat(CompactBufferReader & reader)473 RConcat::RConcat(CompactBufferReader& reader) {}
474 
recover(JSContext * cx,SnapshotIterator & iter) const475 bool RConcat::recover(JSContext* cx, SnapshotIterator& iter) const {
476   RootedValue lhs(cx, iter.read());
477   RootedValue rhs(cx, iter.read());
478   RootedValue result(cx);
479 
480   MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
481   if (!js::AddValues(cx, &lhs, &rhs, &result)) return false;
482 
483   iter.storeInstructionResult(result);
484   return true;
485 }
486 
RStringLength(CompactBufferReader & reader)487 RStringLength::RStringLength(CompactBufferReader& reader) {}
488 
recover(JSContext * cx,SnapshotIterator & iter) const489 bool RStringLength::recover(JSContext* cx, SnapshotIterator& iter) const {
490   RootedValue operand(cx, iter.read());
491   RootedValue result(cx);
492 
493   MOZ_ASSERT(!operand.isObject());
494   if (!js::GetLengthProperty(operand, &result)) return false;
495 
496   iter.storeInstructionResult(result);
497   return true;
498 }
499 
writeRecoverData(CompactBufferWriter & writer) const500 bool MStringLength::writeRecoverData(CompactBufferWriter& writer) const {
501   MOZ_ASSERT(canRecoverOnBailout());
502   writer.writeUnsigned(uint32_t(RInstruction::Recover_StringLength));
503   return true;
504 }
505 
writeRecoverData(CompactBufferWriter & writer) const506 bool MArgumentsLength::writeRecoverData(CompactBufferWriter& writer) const {
507   MOZ_ASSERT(canRecoverOnBailout());
508   writer.writeUnsigned(uint32_t(RInstruction::Recover_ArgumentsLength));
509   return true;
510 }
511 
RArgumentsLength(CompactBufferReader & reader)512 RArgumentsLength::RArgumentsLength(CompactBufferReader& reader) {}
513 
recover(JSContext * cx,SnapshotIterator & iter) const514 bool RArgumentsLength::recover(JSContext* cx, SnapshotIterator& iter) const {
515   RootedValue result(cx);
516 
517   result.setInt32(iter.readOuterNumActualArgs());
518 
519   iter.storeInstructionResult(result);
520   return true;
521 }
522 
writeRecoverData(CompactBufferWriter & writer) const523 bool MFloor::writeRecoverData(CompactBufferWriter& writer) const {
524   MOZ_ASSERT(canRecoverOnBailout());
525   writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
526   return true;
527 }
528 
RFloor(CompactBufferReader & reader)529 RFloor::RFloor(CompactBufferReader& reader) {}
530 
recover(JSContext * cx,SnapshotIterator & iter) const531 bool RFloor::recover(JSContext* cx, SnapshotIterator& iter) const {
532   RootedValue v(cx, iter.read());
533   RootedValue result(cx);
534 
535   if (!js::math_floor_handle(cx, v, &result)) return false;
536 
537   iter.storeInstructionResult(result);
538   return true;
539 }
540 
writeRecoverData(CompactBufferWriter & writer) const541 bool MCeil::writeRecoverData(CompactBufferWriter& writer) const {
542   MOZ_ASSERT(canRecoverOnBailout());
543   writer.writeUnsigned(uint32_t(RInstruction::Recover_Ceil));
544   return true;
545 }
546 
RCeil(CompactBufferReader & reader)547 RCeil::RCeil(CompactBufferReader& reader) {}
548 
recover(JSContext * cx,SnapshotIterator & iter) const549 bool RCeil::recover(JSContext* cx, SnapshotIterator& iter) const {
550   RootedValue v(cx, iter.read());
551   RootedValue result(cx);
552 
553   if (!js::math_ceil_handle(cx, v, &result)) return false;
554 
555   iter.storeInstructionResult(result);
556   return true;
557 }
558 
writeRecoverData(CompactBufferWriter & writer) const559 bool MRound::writeRecoverData(CompactBufferWriter& writer) const {
560   MOZ_ASSERT(canRecoverOnBailout());
561   writer.writeUnsigned(uint32_t(RInstruction::Recover_Round));
562   return true;
563 }
564 
RRound(CompactBufferReader & reader)565 RRound::RRound(CompactBufferReader& reader) {}
566 
recover(JSContext * cx,SnapshotIterator & iter) const567 bool RRound::recover(JSContext* cx, SnapshotIterator& iter) const {
568   RootedValue arg(cx, iter.read());
569   RootedValue result(cx);
570 
571   MOZ_ASSERT(!arg.isObject());
572   if (!js::math_round_handle(cx, arg, &result)) return false;
573 
574   iter.storeInstructionResult(result);
575   return true;
576 }
577 
writeRecoverData(CompactBufferWriter & writer) const578 bool MCharCodeAt::writeRecoverData(CompactBufferWriter& writer) const {
579   MOZ_ASSERT(canRecoverOnBailout());
580   writer.writeUnsigned(uint32_t(RInstruction::Recover_CharCodeAt));
581   return true;
582 }
583 
RCharCodeAt(CompactBufferReader & reader)584 RCharCodeAt::RCharCodeAt(CompactBufferReader& reader) {}
585 
recover(JSContext * cx,SnapshotIterator & iter) const586 bool RCharCodeAt::recover(JSContext* cx, SnapshotIterator& iter) const {
587   RootedString lhs(cx, iter.read().toString());
588   RootedValue rhs(cx, iter.read());
589   RootedValue result(cx);
590 
591   if (!js::str_charCodeAt_impl(cx, lhs, rhs, &result)) return false;
592 
593   iter.storeInstructionResult(result);
594   return true;
595 }
596 
writeRecoverData(CompactBufferWriter & writer) const597 bool MFromCharCode::writeRecoverData(CompactBufferWriter& writer) const {
598   MOZ_ASSERT(canRecoverOnBailout());
599   writer.writeUnsigned(uint32_t(RInstruction::Recover_FromCharCode));
600   return true;
601 }
602 
RFromCharCode(CompactBufferReader & reader)603 RFromCharCode::RFromCharCode(CompactBufferReader& reader) {}
604 
recover(JSContext * cx,SnapshotIterator & iter) const605 bool RFromCharCode::recover(JSContext* cx, SnapshotIterator& iter) const {
606   RootedValue operand(cx, iter.read());
607   RootedValue result(cx);
608 
609   MOZ_ASSERT(!operand.isObject());
610   if (!js::str_fromCharCode_one_arg(cx, operand, &result)) return false;
611 
612   iter.storeInstructionResult(result);
613   return true;
614 }
615 
writeRecoverData(CompactBufferWriter & writer) const616 bool MPow::writeRecoverData(CompactBufferWriter& writer) const {
617   MOZ_ASSERT(canRecoverOnBailout());
618   writer.writeUnsigned(uint32_t(RInstruction::Recover_Pow));
619   return true;
620 }
621 
RPow(CompactBufferReader & reader)622 RPow::RPow(CompactBufferReader& reader) {}
623 
recover(JSContext * cx,SnapshotIterator & iter) const624 bool RPow::recover(JSContext* cx, SnapshotIterator& iter) const {
625   RootedValue base(cx, iter.read());
626   RootedValue power(cx, iter.read());
627   RootedValue result(cx);
628 
629   MOZ_ASSERT(base.isNumber() && power.isNumber());
630   if (!js::math_pow_handle(cx, base, power, &result)) return false;
631 
632   iter.storeInstructionResult(result);
633   return true;
634 }
635 
writeRecoverData(CompactBufferWriter & writer) const636 bool MPowHalf::writeRecoverData(CompactBufferWriter& writer) const {
637   MOZ_ASSERT(canRecoverOnBailout());
638   writer.writeUnsigned(uint32_t(RInstruction::Recover_PowHalf));
639   return true;
640 }
641 
RPowHalf(CompactBufferReader & reader)642 RPowHalf::RPowHalf(CompactBufferReader& reader) {}
643 
recover(JSContext * cx,SnapshotIterator & iter) const644 bool RPowHalf::recover(JSContext* cx, SnapshotIterator& iter) const {
645   RootedValue base(cx, iter.read());
646   RootedValue power(cx);
647   RootedValue result(cx);
648   power.setNumber(0.5);
649 
650   MOZ_ASSERT(base.isNumber());
651   if (!js::math_pow_handle(cx, base, power, &result)) return false;
652 
653   iter.storeInstructionResult(result);
654   return true;
655 }
656 
writeRecoverData(CompactBufferWriter & writer) const657 bool MMinMax::writeRecoverData(CompactBufferWriter& writer) const {
658   MOZ_ASSERT(canRecoverOnBailout());
659   writer.writeUnsigned(uint32_t(RInstruction::Recover_MinMax));
660   writer.writeByte(isMax_);
661   return true;
662 }
663 
RMinMax(CompactBufferReader & reader)664 RMinMax::RMinMax(CompactBufferReader& reader) { isMax_ = reader.readByte(); }
665 
recover(JSContext * cx,SnapshotIterator & iter) const666 bool RMinMax::recover(JSContext* cx, SnapshotIterator& iter) const {
667   RootedValue a(cx, iter.read());
668   RootedValue b(cx, iter.read());
669   RootedValue result(cx);
670 
671   if (!js::minmax_impl(cx, isMax_, a, b, &result)) return false;
672 
673   iter.storeInstructionResult(result);
674   return true;
675 }
676 
writeRecoverData(CompactBufferWriter & writer) const677 bool MAbs::writeRecoverData(CompactBufferWriter& writer) const {
678   MOZ_ASSERT(canRecoverOnBailout());
679   writer.writeUnsigned(uint32_t(RInstruction::Recover_Abs));
680   return true;
681 }
682 
RAbs(CompactBufferReader & reader)683 RAbs::RAbs(CompactBufferReader& reader) {}
684 
recover(JSContext * cx,SnapshotIterator & iter) const685 bool RAbs::recover(JSContext* cx, SnapshotIterator& iter) const {
686   RootedValue v(cx, iter.read());
687   RootedValue result(cx);
688 
689   if (!js::math_abs_handle(cx, v, &result)) return false;
690 
691   iter.storeInstructionResult(result);
692   return true;
693 }
694 
writeRecoverData(CompactBufferWriter & writer) const695 bool MSqrt::writeRecoverData(CompactBufferWriter& writer) const {
696   MOZ_ASSERT(canRecoverOnBailout());
697   writer.writeUnsigned(uint32_t(RInstruction::Recover_Sqrt));
698   writer.writeByte(type() == MIRType::Float32);
699   return true;
700 }
701 
RSqrt(CompactBufferReader & reader)702 RSqrt::RSqrt(CompactBufferReader& reader) {
703   isFloatOperation_ = reader.readByte();
704 }
705 
recover(JSContext * cx,SnapshotIterator & iter) const706 bool RSqrt::recover(JSContext* cx, SnapshotIterator& iter) const {
707   RootedValue num(cx, iter.read());
708   RootedValue result(cx);
709 
710   MOZ_ASSERT(num.isNumber());
711   if (!math_sqrt_handle(cx, num, &result)) return false;
712 
713   // MIRType::Float32 is a specialization embedding the fact that the result is
714   // rounded to a Float32.
715   if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) return false;
716 
717   iter.storeInstructionResult(result);
718   return true;
719 }
720 
writeRecoverData(CompactBufferWriter & writer) const721 bool MAtan2::writeRecoverData(CompactBufferWriter& writer) const {
722   MOZ_ASSERT(canRecoverOnBailout());
723   writer.writeUnsigned(uint32_t(RInstruction::Recover_Atan2));
724   return true;
725 }
726 
RAtan2(CompactBufferReader & reader)727 RAtan2::RAtan2(CompactBufferReader& reader) {}
728 
recover(JSContext * cx,SnapshotIterator & iter) const729 bool RAtan2::recover(JSContext* cx, SnapshotIterator& iter) const {
730   RootedValue y(cx, iter.read());
731   RootedValue x(cx, iter.read());
732   RootedValue result(cx);
733 
734   if (!math_atan2_handle(cx, y, x, &result)) return false;
735 
736   iter.storeInstructionResult(result);
737   return true;
738 }
739 
writeRecoverData(CompactBufferWriter & writer) const740 bool MHypot::writeRecoverData(CompactBufferWriter& writer) const {
741   MOZ_ASSERT(canRecoverOnBailout());
742   writer.writeUnsigned(uint32_t(RInstruction::Recover_Hypot));
743   writer.writeUnsigned(uint32_t(numOperands()));
744   return true;
745 }
746 
RHypot(CompactBufferReader & reader)747 RHypot::RHypot(CompactBufferReader& reader)
748     : numOperands_(reader.readUnsigned()) {}
749 
recover(JSContext * cx,SnapshotIterator & iter) const750 bool RHypot::recover(JSContext* cx, SnapshotIterator& iter) const {
751   JS::AutoValueVector vec(cx);
752 
753   if (!vec.reserve(numOperands_)) return false;
754 
755   for (uint32_t i = 0; i < numOperands_; ++i) vec.infallibleAppend(iter.read());
756 
757   RootedValue result(cx);
758 
759   if (!js::math_hypot_handle(cx, vec, &result)) return false;
760 
761   iter.storeInstructionResult(result);
762   return true;
763 }
764 
writeRecoverData(CompactBufferWriter & writer) const765 bool MNearbyInt::writeRecoverData(CompactBufferWriter& writer) const {
766   MOZ_ASSERT(canRecoverOnBailout());
767   switch (roundingMode_) {
768     case RoundingMode::Up:
769       writer.writeUnsigned(uint32_t(RInstruction::Recover_Ceil));
770       return true;
771     case RoundingMode::Down:
772       writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
773       return true;
774     default:
775       MOZ_CRASH("Unsupported rounding mode.");
776   }
777 }
778 
RNearbyInt(CompactBufferReader & reader)779 RNearbyInt::RNearbyInt(CompactBufferReader& reader) {
780   roundingMode_ = reader.readByte();
781 }
782 
recover(JSContext * cx,SnapshotIterator & iter) const783 bool RNearbyInt::recover(JSContext* cx, SnapshotIterator& iter) const {
784   MOZ_CRASH("Unsupported rounding mode.");
785 }
786 
writeRecoverData(CompactBufferWriter & writer) const787 bool MMathFunction::writeRecoverData(CompactBufferWriter& writer) const {
788   MOZ_ASSERT(canRecoverOnBailout());
789   switch (function_) {
790     case Ceil:
791       writer.writeUnsigned(uint32_t(RInstruction::Recover_Ceil));
792       return true;
793     case Floor:
794       writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
795       return true;
796     case Round:
797       writer.writeUnsigned(uint32_t(RInstruction::Recover_Round));
798       return true;
799     case Sin:
800     case Log:
801       writer.writeUnsigned(uint32_t(RInstruction::Recover_MathFunction));
802       writer.writeByte(function_);
803       return true;
804     default:
805       MOZ_CRASH("Unknown math function.");
806   }
807 }
808 
RMathFunction(CompactBufferReader & reader)809 RMathFunction::RMathFunction(CompactBufferReader& reader) {
810   function_ = reader.readByte();
811 }
812 
recover(JSContext * cx,SnapshotIterator & iter) const813 bool RMathFunction::recover(JSContext* cx, SnapshotIterator& iter) const {
814   switch (function_) {
815     case MMathFunction::Sin: {
816       RootedValue arg(cx, iter.read());
817       RootedValue result(cx);
818 
819       if (!js::math_sin_handle(cx, arg, &result)) return false;
820 
821       iter.storeInstructionResult(result);
822       return true;
823     }
824     case MMathFunction::Log: {
825       RootedValue arg(cx, iter.read());
826       RootedValue result(cx);
827 
828       if (!js::math_log_handle(cx, arg, &result)) return false;
829 
830       iter.storeInstructionResult(result);
831       return true;
832     }
833     default:
834       MOZ_CRASH("Unknown math function.");
835   }
836 }
837 
writeRecoverData(CompactBufferWriter & writer) const838 bool MRandom::writeRecoverData(CompactBufferWriter& writer) const {
839   MOZ_ASSERT(this->canRecoverOnBailout());
840   writer.writeUnsigned(uint32_t(RInstruction::Recover_Random));
841   return true;
842 }
843 
RRandom(CompactBufferReader & reader)844 RRandom::RRandom(CompactBufferReader& reader) {}
845 
recover(JSContext * cx,SnapshotIterator & iter) const846 bool RRandom::recover(JSContext* cx, SnapshotIterator& iter) const {
847   iter.storeInstructionResult(DoubleValue(math_random_impl(cx)));
848   return true;
849 }
850 
writeRecoverData(CompactBufferWriter & writer) const851 bool MStringSplit::writeRecoverData(CompactBufferWriter& writer) const {
852   MOZ_ASSERT(canRecoverOnBailout());
853   writer.writeUnsigned(uint32_t(RInstruction::Recover_StringSplit));
854   return true;
855 }
856 
RStringSplit(CompactBufferReader & reader)857 RStringSplit::RStringSplit(CompactBufferReader& reader) {}
858 
recover(JSContext * cx,SnapshotIterator & iter) const859 bool RStringSplit::recover(JSContext* cx, SnapshotIterator& iter) const {
860   RootedString str(cx, iter.read().toString());
861   RootedString sep(cx, iter.read().toString());
862   RootedObjectGroup group(
863       cx, ObjectGroupCompartment::getStringSplitStringGroup(cx));
864   if (!group) {
865     return false;
866   }
867   RootedValue result(cx);
868 
869   JSObject* res = str_split_string(cx, group, str, sep, INT32_MAX);
870   if (!res) return false;
871 
872   result.setObject(*res);
873   iter.storeInstructionResult(result);
874   return true;
875 }
876 
writeRecoverData(CompactBufferWriter & writer) const877 bool MNaNToZero::writeRecoverData(CompactBufferWriter& writer) const {
878   MOZ_ASSERT(canRecoverOnBailout());
879   writer.writeUnsigned(uint32_t(RInstruction::Recover_NaNToZero));
880   return true;
881 }
882 
RNaNToZero(CompactBufferReader & reader)883 RNaNToZero::RNaNToZero(CompactBufferReader& reader) {}
884 
recover(JSContext * cx,SnapshotIterator & iter) const885 bool RNaNToZero::recover(JSContext* cx, SnapshotIterator& iter) const {
886   RootedValue v(cx, iter.read());
887   RootedValue result(cx);
888   MOZ_ASSERT(v.isDouble() || v.isInt32());
889 
890   // x ? x : 0.0
891   if (ToBoolean(v))
892     result = v;
893   else
894     result.setDouble(0.0);
895 
896   iter.storeInstructionResult(result);
897   return true;
898 }
899 
writeRecoverData(CompactBufferWriter & writer) const900 bool MRegExpMatcher::writeRecoverData(CompactBufferWriter& writer) const {
901   MOZ_ASSERT(canRecoverOnBailout());
902   writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpMatcher));
903   return true;
904 }
905 
RRegExpMatcher(CompactBufferReader & reader)906 RRegExpMatcher::RRegExpMatcher(CompactBufferReader& reader) {}
907 
recover(JSContext * cx,SnapshotIterator & iter) const908 bool RRegExpMatcher::recover(JSContext* cx, SnapshotIterator& iter) const {
909   RootedObject regexp(cx, &iter.read().toObject());
910   RootedString input(cx, iter.read().toString());
911   int32_t lastIndex = iter.read().toInt32();
912 
913   RootedValue result(cx);
914   if (!RegExpMatcherRaw(cx, regexp, input, lastIndex, nullptr, &result))
915     return false;
916 
917   iter.storeInstructionResult(result);
918   return true;
919 }
920 
writeRecoverData(CompactBufferWriter & writer) const921 bool MRegExpSearcher::writeRecoverData(CompactBufferWriter& writer) const {
922   MOZ_ASSERT(canRecoverOnBailout());
923   writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpSearcher));
924   return true;
925 }
926 
RRegExpSearcher(CompactBufferReader & reader)927 RRegExpSearcher::RRegExpSearcher(CompactBufferReader& reader) {}
928 
recover(JSContext * cx,SnapshotIterator & iter) const929 bool RRegExpSearcher::recover(JSContext* cx, SnapshotIterator& iter) const {
930   RootedObject regexp(cx, &iter.read().toObject());
931   RootedString input(cx, iter.read().toString());
932   int32_t lastIndex = iter.read().toInt32();
933 
934   int32_t result;
935   if (!RegExpSearcherRaw(cx, regexp, input, lastIndex, nullptr, &result))
936     return false;
937 
938   RootedValue resultVal(cx);
939   resultVal.setInt32(result);
940   iter.storeInstructionResult(resultVal);
941   return true;
942 }
943 
writeRecoverData(CompactBufferWriter & writer) const944 bool MRegExpTester::writeRecoverData(CompactBufferWriter& writer) const {
945   MOZ_ASSERT(canRecoverOnBailout());
946   writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpTester));
947   return true;
948 }
949 
RRegExpTester(CompactBufferReader & reader)950 RRegExpTester::RRegExpTester(CompactBufferReader& reader) {}
951 
recover(JSContext * cx,SnapshotIterator & iter) const952 bool RRegExpTester::recover(JSContext* cx, SnapshotIterator& iter) const {
953   RootedString string(cx, iter.read().toString());
954   RootedObject regexp(cx, &iter.read().toObject());
955   int32_t lastIndex = iter.read().toInt32();
956   int32_t endIndex;
957 
958   if (!js::RegExpTesterRaw(cx, regexp, string, lastIndex, &endIndex))
959     return false;
960 
961   RootedValue result(cx);
962   result.setInt32(endIndex);
963   iter.storeInstructionResult(result);
964   return true;
965 }
966 
writeRecoverData(CompactBufferWriter & writer) const967 bool MTypeOf::writeRecoverData(CompactBufferWriter& writer) const {
968   MOZ_ASSERT(canRecoverOnBailout());
969   writer.writeUnsigned(uint32_t(RInstruction::Recover_TypeOf));
970   return true;
971 }
972 
RTypeOf(CompactBufferReader & reader)973 RTypeOf::RTypeOf(CompactBufferReader& reader) {}
974 
recover(JSContext * cx,SnapshotIterator & iter) const975 bool RTypeOf::recover(JSContext* cx, SnapshotIterator& iter) const {
976   RootedValue v(cx, iter.read());
977 
978   RootedValue result(cx, StringValue(TypeOfOperation(v, cx->runtime())));
979   iter.storeInstructionResult(result);
980   return true;
981 }
982 
writeRecoverData(CompactBufferWriter & writer) const983 bool MToDouble::writeRecoverData(CompactBufferWriter& writer) const {
984   MOZ_ASSERT(canRecoverOnBailout());
985   writer.writeUnsigned(uint32_t(RInstruction::Recover_ToDouble));
986   return true;
987 }
988 
RToDouble(CompactBufferReader & reader)989 RToDouble::RToDouble(CompactBufferReader& reader) {}
990 
recover(JSContext * cx,SnapshotIterator & iter) const991 bool RToDouble::recover(JSContext* cx, SnapshotIterator& iter) const {
992   RootedValue v(cx, iter.read());
993   RootedValue result(cx);
994 
995   MOZ_ASSERT(!v.isObject());
996   MOZ_ASSERT(!v.isSymbol());
997 
998   double dbl;
999   if (!ToNumber(cx, v, &dbl)) return false;
1000 
1001   result.setDouble(dbl);
1002   iter.storeInstructionResult(result);
1003   return true;
1004 }
1005 
writeRecoverData(CompactBufferWriter & writer) const1006 bool MToFloat32::writeRecoverData(CompactBufferWriter& writer) const {
1007   MOZ_ASSERT(canRecoverOnBailout());
1008   writer.writeUnsigned(uint32_t(RInstruction::Recover_ToFloat32));
1009   return true;
1010 }
1011 
RToFloat32(CompactBufferReader & reader)1012 RToFloat32::RToFloat32(CompactBufferReader& reader) {}
1013 
recover(JSContext * cx,SnapshotIterator & iter) const1014 bool RToFloat32::recover(JSContext* cx, SnapshotIterator& iter) const {
1015   RootedValue v(cx, iter.read());
1016   RootedValue result(cx);
1017 
1018   MOZ_ASSERT(!v.isObject());
1019   if (!RoundFloat32(cx, v, &result)) return false;
1020 
1021   iter.storeInstructionResult(result);
1022   return true;
1023 }
1024 
writeRecoverData(CompactBufferWriter & writer) const1025 bool MTruncateToInt32::writeRecoverData(CompactBufferWriter& writer) const {
1026   MOZ_ASSERT(canRecoverOnBailout());
1027   writer.writeUnsigned(uint32_t(RInstruction::Recover_TruncateToInt32));
1028   return true;
1029 }
1030 
RTruncateToInt32(CompactBufferReader & reader)1031 RTruncateToInt32::RTruncateToInt32(CompactBufferReader& reader) {}
1032 
recover(JSContext * cx,SnapshotIterator & iter) const1033 bool RTruncateToInt32::recover(JSContext* cx, SnapshotIterator& iter) const {
1034   RootedValue value(cx, iter.read());
1035   RootedValue result(cx);
1036 
1037   int32_t trunc;
1038   if (!JS::ToInt32(cx, value, &trunc)) return false;
1039 
1040   result.setInt32(trunc);
1041   iter.storeInstructionResult(result);
1042   return true;
1043 }
1044 
writeRecoverData(CompactBufferWriter & writer) const1045 bool MNewObject::writeRecoverData(CompactBufferWriter& writer) const {
1046   MOZ_ASSERT(canRecoverOnBailout());
1047   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewObject));
1048   MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_);
1049   writer.writeByte(uint8_t(mode_));
1050   return true;
1051 }
1052 
RNewObject(CompactBufferReader & reader)1053 RNewObject::RNewObject(CompactBufferReader& reader) {
1054   mode_ = MNewObject::Mode(reader.readByte());
1055 }
1056 
recover(JSContext * cx,SnapshotIterator & iter) const1057 bool RNewObject::recover(JSContext* cx, SnapshotIterator& iter) const {
1058   RootedObject templateObject(cx, &iter.read().toObject());
1059   RootedValue result(cx);
1060   JSObject* resultObject = nullptr;
1061 
1062   // See CodeGenerator::visitNewObjectVMCall
1063   switch (mode_) {
1064     case MNewObject::ObjectLiteral:
1065       resultObject = NewObjectOperationWithTemplate(cx, templateObject);
1066       break;
1067     case MNewObject::ObjectCreate:
1068       resultObject =
1069           ObjectCreateWithTemplate(cx, templateObject.as<PlainObject>());
1070       break;
1071   }
1072 
1073   if (!resultObject) return false;
1074 
1075   result.setObject(*resultObject);
1076   iter.storeInstructionResult(result);
1077   return true;
1078 }
1079 
writeRecoverData(CompactBufferWriter & writer) const1080 bool MNewTypedArray::writeRecoverData(CompactBufferWriter& writer) const {
1081   MOZ_ASSERT(canRecoverOnBailout());
1082   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewTypedArray));
1083   return true;
1084 }
1085 
RNewTypedArray(CompactBufferReader & reader)1086 RNewTypedArray::RNewTypedArray(CompactBufferReader& reader) {}
1087 
recover(JSContext * cx,SnapshotIterator & iter) const1088 bool RNewTypedArray::recover(JSContext* cx, SnapshotIterator& iter) const {
1089   RootedObject templateObject(cx, &iter.read().toObject());
1090   RootedValue result(cx);
1091 
1092   uint32_t length = templateObject.as<TypedArrayObject>()->length();
1093   JSObject* resultObject =
1094       TypedArrayCreateWithTemplate(cx, templateObject, length);
1095   if (!resultObject) return false;
1096 
1097   result.setObject(*resultObject);
1098   iter.storeInstructionResult(result);
1099   return true;
1100 }
1101 
writeRecoverData(CompactBufferWriter & writer) const1102 bool MNewArray::writeRecoverData(CompactBufferWriter& writer) const {
1103   MOZ_ASSERT(canRecoverOnBailout());
1104   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArray));
1105   writer.writeUnsigned(length());
1106   return true;
1107 }
1108 
RNewArray(CompactBufferReader & reader)1109 RNewArray::RNewArray(CompactBufferReader& reader) {
1110   count_ = reader.readUnsigned();
1111 }
1112 
recover(JSContext * cx,SnapshotIterator & iter) const1113 bool RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const {
1114   RootedObject templateObject(cx, &iter.read().toObject());
1115   RootedValue result(cx);
1116   RootedObjectGroup group(cx, templateObject->group());
1117 
1118   ArrayObject* resultObject =
1119       NewFullyAllocatedArrayTryUseGroup(cx, group, count_);
1120   if (!resultObject) return false;
1121 
1122   result.setObject(*resultObject);
1123   iter.storeInstructionResult(result);
1124   return true;
1125 }
1126 
writeRecoverData(CompactBufferWriter & writer) const1127 bool MNewArrayCopyOnWrite::writeRecoverData(CompactBufferWriter& writer) const {
1128   MOZ_ASSERT(canRecoverOnBailout());
1129   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArrayCopyOnWrite));
1130   writer.writeByte(initialHeap());
1131   return true;
1132 }
1133 
RNewArrayCopyOnWrite(CompactBufferReader & reader)1134 RNewArrayCopyOnWrite::RNewArrayCopyOnWrite(CompactBufferReader& reader) {
1135   initialHeap_ = gc::InitialHeap(reader.readByte());
1136 }
1137 
recover(JSContext * cx,SnapshotIterator & iter) const1138 bool RNewArrayCopyOnWrite::recover(JSContext* cx,
1139                                    SnapshotIterator& iter) const {
1140   RootedArrayObject templateObject(cx,
1141                                    &iter.read().toObject().as<ArrayObject>());
1142   RootedValue result(cx);
1143 
1144   ArrayObject* resultObject =
1145       NewDenseCopyOnWriteArray(cx, templateObject, initialHeap_);
1146   if (!resultObject) return false;
1147 
1148   result.setObject(*resultObject);
1149   iter.storeInstructionResult(result);
1150   return true;
1151 }
1152 
writeRecoverData(CompactBufferWriter & writer) const1153 bool MNewIterator::writeRecoverData(CompactBufferWriter& writer) const {
1154   MOZ_ASSERT(canRecoverOnBailout());
1155   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewIterator));
1156   writer.writeByte(type_);
1157   return true;
1158 }
1159 
RNewIterator(CompactBufferReader & reader)1160 RNewIterator::RNewIterator(CompactBufferReader& reader) {
1161   type_ = reader.readByte();
1162 }
1163 
recover(JSContext * cx,SnapshotIterator & iter) const1164 bool RNewIterator::recover(JSContext* cx, SnapshotIterator& iter) const {
1165   RootedObject templateObject(cx, &iter.read().toObject());
1166   RootedValue result(cx);
1167 
1168   JSObject* resultObject = nullptr;
1169   switch (MNewIterator::Type(type_)) {
1170     case MNewIterator::ArrayIterator:
1171       resultObject = NewArrayIteratorObject(cx);
1172       break;
1173     case MNewIterator::StringIterator:
1174       resultObject = NewStringIteratorObject(cx);
1175       break;
1176   }
1177 
1178   if (!resultObject) return false;
1179 
1180   result.setObject(*resultObject);
1181   iter.storeInstructionResult(result);
1182   return true;
1183 }
1184 
writeRecoverData(CompactBufferWriter & writer) const1185 bool MNewDerivedTypedObject::writeRecoverData(
1186     CompactBufferWriter& writer) const {
1187   MOZ_ASSERT(canRecoverOnBailout());
1188   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewDerivedTypedObject));
1189   return true;
1190 }
1191 
RNewDerivedTypedObject(CompactBufferReader & reader)1192 RNewDerivedTypedObject::RNewDerivedTypedObject(CompactBufferReader& reader) {}
1193 
recover(JSContext * cx,SnapshotIterator & iter) const1194 bool RNewDerivedTypedObject::recover(JSContext* cx,
1195                                      SnapshotIterator& iter) const {
1196   Rooted<TypeDescr*> descr(cx, &iter.read().toObject().as<TypeDescr>());
1197   Rooted<TypedObject*> owner(cx, &iter.read().toObject().as<TypedObject>());
1198   int32_t offset = iter.read().toInt32();
1199 
1200   JSObject* obj = OutlineTypedObject::createDerived(cx, descr, owner, offset);
1201   if (!obj) return false;
1202 
1203   RootedValue result(cx, ObjectValue(*obj));
1204   iter.storeInstructionResult(result);
1205   return true;
1206 }
1207 
writeRecoverData(CompactBufferWriter & writer) const1208 bool MCreateThisWithTemplate::writeRecoverData(
1209     CompactBufferWriter& writer) const {
1210   MOZ_ASSERT(canRecoverOnBailout());
1211   writer.writeUnsigned(uint32_t(RInstruction::Recover_CreateThisWithTemplate));
1212   return true;
1213 }
1214 
RCreateThisWithTemplate(CompactBufferReader & reader)1215 RCreateThisWithTemplate::RCreateThisWithTemplate(CompactBufferReader& reader) {}
1216 
recover(JSContext * cx,SnapshotIterator & iter) const1217 bool RCreateThisWithTemplate::recover(JSContext* cx,
1218                                       SnapshotIterator& iter) const {
1219   RootedObject templateObject(cx, &iter.read().toObject());
1220 
1221   // See CodeGenerator::visitCreateThisWithTemplate
1222   JSObject* resultObject = NewObjectOperationWithTemplate(cx, templateObject);
1223   if (!resultObject) return false;
1224 
1225   RootedValue result(cx);
1226   result.setObject(*resultObject);
1227   iter.storeInstructionResult(result);
1228   return true;
1229 }
1230 
writeRecoverData(CompactBufferWriter & writer) const1231 bool MLambda::writeRecoverData(CompactBufferWriter& writer) const {
1232   MOZ_ASSERT(canRecoverOnBailout());
1233   writer.writeUnsigned(uint32_t(RInstruction::Recover_Lambda));
1234   return true;
1235 }
1236 
RLambda(CompactBufferReader & reader)1237 RLambda::RLambda(CompactBufferReader& reader) {}
1238 
recover(JSContext * cx,SnapshotIterator & iter) const1239 bool RLambda::recover(JSContext* cx, SnapshotIterator& iter) const {
1240   RootedObject scopeChain(cx, &iter.read().toObject());
1241   RootedFunction fun(cx, &iter.read().toObject().as<JSFunction>());
1242 
1243   JSObject* resultObject = js::Lambda(cx, fun, scopeChain);
1244   if (!resultObject) return false;
1245 
1246   RootedValue result(cx);
1247   result.setObject(*resultObject);
1248   iter.storeInstructionResult(result);
1249   return true;
1250 }
1251 
writeRecoverData(CompactBufferWriter & writer) const1252 bool MLambdaArrow::writeRecoverData(CompactBufferWriter& writer) const {
1253   MOZ_ASSERT(canRecoverOnBailout());
1254   writer.writeUnsigned(uint32_t(RInstruction::Recover_LambdaArrow));
1255   return true;
1256 }
1257 
RLambdaArrow(CompactBufferReader & reader)1258 RLambdaArrow::RLambdaArrow(CompactBufferReader& reader) {}
1259 
recover(JSContext * cx,SnapshotIterator & iter) const1260 bool RLambdaArrow::recover(JSContext* cx, SnapshotIterator& iter) const {
1261   RootedObject scopeChain(cx, &iter.read().toObject());
1262   RootedValue newTarget(cx, iter.read());
1263   RootedFunction fun(cx, &iter.read().toObject().as<JSFunction>());
1264 
1265   JSObject* resultObject = js::LambdaArrow(cx, fun, scopeChain, newTarget);
1266   if (!resultObject) return false;
1267 
1268   RootedValue result(cx);
1269   result.setObject(*resultObject);
1270   iter.storeInstructionResult(result);
1271   return true;
1272 }
1273 
writeRecoverData(CompactBufferWriter & writer) const1274 bool MNewCallObject::writeRecoverData(CompactBufferWriter& writer) const {
1275   MOZ_ASSERT(canRecoverOnBailout());
1276   writer.writeUnsigned(uint32_t(RInstruction::Recover_NewCallObject));
1277   return true;
1278 }
1279 
RNewCallObject(CompactBufferReader & reader)1280 RNewCallObject::RNewCallObject(CompactBufferReader& reader) {}
1281 
recover(JSContext * cx,SnapshotIterator & iter) const1282 bool RNewCallObject::recover(JSContext* cx, SnapshotIterator& iter) const {
1283   Rooted<CallObject*> templateObj(cx, &iter.read().toObject().as<CallObject>());
1284 
1285   RootedShape shape(cx, templateObj->lastProperty());
1286   RootedObjectGroup group(cx, templateObj->group());
1287   JSObject* resultObject = NewCallObject(cx, shape, group);
1288   if (!resultObject) return false;
1289 
1290   RootedValue result(cx);
1291   result.setObject(*resultObject);
1292   iter.storeInstructionResult(result);
1293   return true;
1294 }
1295 
writeRecoverData(CompactBufferWriter & writer) const1296 bool MSimdBox::writeRecoverData(CompactBufferWriter& writer) const {
1297   MOZ_ASSERT(canRecoverOnBailout());
1298   writer.writeUnsigned(uint32_t(RInstruction::Recover_SimdBox));
1299   static_assert(unsigned(SimdType::Count) < 0x100,
1300                 "assuming SimdType fits in 8 bits");
1301   writer.writeByte(uint8_t(simdType()));
1302   return true;
1303 }
1304 
RSimdBox(CompactBufferReader & reader)1305 RSimdBox::RSimdBox(CompactBufferReader& reader) { type_ = reader.readByte(); }
1306 
recover(JSContext * cx,SnapshotIterator & iter) const1307 bool RSimdBox::recover(JSContext* cx, SnapshotIterator& iter) const {
1308   JSObject* resultObject = nullptr;
1309   RValueAllocation a = iter.readAllocation();
1310   MOZ_ASSERT(iter.allocationReadable(a));
1311   MOZ_ASSERT_IF(a.mode() == RValueAllocation::ANY_FLOAT_REG,
1312                 a.fpuReg().isSimd128());
1313   const FloatRegisters::RegisterContent* raw = iter.floatAllocationPointer(a);
1314   switch (SimdType(type_)) {
1315     case SimdType::Bool8x16:
1316       resultObject = js::CreateSimd<Bool8x16>(cx, (const Bool8x16::Elem*)raw);
1317       break;
1318     case SimdType::Int8x16:
1319       resultObject = js::CreateSimd<Int8x16>(cx, (const Int8x16::Elem*)raw);
1320       break;
1321     case SimdType::Uint8x16:
1322       resultObject = js::CreateSimd<Uint8x16>(cx, (const Uint8x16::Elem*)raw);
1323       break;
1324     case SimdType::Bool16x8:
1325       resultObject = js::CreateSimd<Bool16x8>(cx, (const Bool16x8::Elem*)raw);
1326       break;
1327     case SimdType::Int16x8:
1328       resultObject = js::CreateSimd<Int16x8>(cx, (const Int16x8::Elem*)raw);
1329       break;
1330     case SimdType::Uint16x8:
1331       resultObject = js::CreateSimd<Uint16x8>(cx, (const Uint16x8::Elem*)raw);
1332       break;
1333     case SimdType::Bool32x4:
1334       resultObject = js::CreateSimd<Bool32x4>(cx, (const Bool32x4::Elem*)raw);
1335       break;
1336     case SimdType::Int32x4:
1337       resultObject = js::CreateSimd<Int32x4>(cx, (const Int32x4::Elem*)raw);
1338       break;
1339     case SimdType::Uint32x4:
1340       resultObject = js::CreateSimd<Uint32x4>(cx, (const Uint32x4::Elem*)raw);
1341       break;
1342     case SimdType::Float32x4:
1343       resultObject = js::CreateSimd<Float32x4>(cx, (const Float32x4::Elem*)raw);
1344       break;
1345     case SimdType::Float64x2:
1346       MOZ_CRASH("NYI, RSimdBox of Float64x2");
1347       break;
1348     case SimdType::Bool64x2:
1349       MOZ_CRASH("NYI, RSimdBox of Bool64x2");
1350       break;
1351     case SimdType::Count:
1352       MOZ_CRASH("RSimdBox of Count is unreachable");
1353   }
1354 
1355   if (!resultObject) return false;
1356 
1357   RootedValue result(cx);
1358   result.setObject(*resultObject);
1359   iter.storeInstructionResult(result);
1360   return true;
1361 }
1362 
writeRecoverData(CompactBufferWriter & writer) const1363 bool MObjectState::writeRecoverData(CompactBufferWriter& writer) const {
1364   MOZ_ASSERT(canRecoverOnBailout());
1365   writer.writeUnsigned(uint32_t(RInstruction::Recover_ObjectState));
1366   writer.writeUnsigned(numSlots());
1367   return true;
1368 }
1369 
RObjectState(CompactBufferReader & reader)1370 RObjectState::RObjectState(CompactBufferReader& reader) {
1371   numSlots_ = reader.readUnsigned();
1372 }
1373 
recover(JSContext * cx,SnapshotIterator & iter) const1374 bool RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const {
1375   RootedObject object(cx, &iter.read().toObject());
1376   RootedValue val(cx);
1377 
1378   if (object->is<UnboxedPlainObject>()) {
1379     const UnboxedLayout& layout = object->as<UnboxedPlainObject>().layout();
1380 
1381     RootedId id(cx);
1382     RootedValue receiver(cx, ObjectValue(*object));
1383     const UnboxedLayout::PropertyVector& properties = layout.properties();
1384     for (size_t i = 0; i < properties.length(); i++) {
1385       val = iter.read();
1386 
1387       // This is the default placeholder value of MObjectState, when no
1388       // properties are defined yet.
1389       if (val.isUndefined()) continue;
1390 
1391       id = NameToId(properties[i].name);
1392       ObjectOpResult result;
1393 
1394       // SetProperty can only fail due to OOM.
1395       if (!SetProperty(cx, object, id, val, receiver, result)) return false;
1396       if (!result) return result.reportError(cx, object, id);
1397     }
1398   } else {
1399     RootedNativeObject nativeObject(cx, &object->as<NativeObject>());
1400     MOZ_ASSERT(nativeObject->slotSpan() == numSlots());
1401 
1402     for (size_t i = 0; i < numSlots(); i++) {
1403       val = iter.read();
1404       nativeObject->setSlot(i, val);
1405     }
1406   }
1407 
1408   val.setObject(*object);
1409   iter.storeInstructionResult(val);
1410   return true;
1411 }
1412 
writeRecoverData(CompactBufferWriter & writer) const1413 bool MArrayState::writeRecoverData(CompactBufferWriter& writer) const {
1414   MOZ_ASSERT(canRecoverOnBailout());
1415   writer.writeUnsigned(uint32_t(RInstruction::Recover_ArrayState));
1416   writer.writeUnsigned(numElements());
1417   return true;
1418 }
1419 
RArrayState(CompactBufferReader & reader)1420 RArrayState::RArrayState(CompactBufferReader& reader) {
1421   numElements_ = reader.readUnsigned();
1422 }
1423 
recover(JSContext * cx,SnapshotIterator & iter) const1424 bool RArrayState::recover(JSContext* cx, SnapshotIterator& iter) const {
1425   RootedValue result(cx);
1426   ArrayObject* object = &iter.read().toObject().as<ArrayObject>();
1427   uint32_t initLength = iter.read().toInt32();
1428 
1429   if (!object->denseElementsAreCopyOnWrite()) {
1430     MOZ_ASSERT(object->getDenseInitializedLength() == 0,
1431                "initDenseElement call below relies on this");
1432     object->setDenseInitializedLength(initLength);
1433 
1434     for (size_t index = 0; index < numElements(); index++) {
1435       Value val = iter.read();
1436 
1437       if (index >= initLength) {
1438         MOZ_ASSERT(val.isUndefined());
1439         continue;
1440       }
1441 
1442       object->initDenseElement(index, val);
1443     }
1444   } else {
1445     MOZ_RELEASE_ASSERT(object->getDenseInitializedLength() == numElements());
1446     MOZ_RELEASE_ASSERT(initLength == numElements());
1447 
1448     for (size_t index = 0; index < numElements(); index++) {
1449       Value val = iter.read();
1450       if (object->getDenseElement(index) == val) continue;
1451       if (!object->maybeCopyElementsForWrite(cx)) return false;
1452       object->setDenseElement(index, val);
1453     }
1454   }
1455 
1456   result.setObject(*object);
1457   iter.storeInstructionResult(result);
1458   return true;
1459 }
1460 
writeRecoverData(CompactBufferWriter & writer) const1461 bool MSetArrayLength::writeRecoverData(CompactBufferWriter& writer) const {
1462   MOZ_ASSERT(canRecoverOnBailout());
1463   // For simplicity, we capture directly the object instead of the elements
1464   // pointer.
1465   MOZ_ASSERT(elements()->type() != MIRType::Elements);
1466   writer.writeUnsigned(uint32_t(RInstruction::Recover_SetArrayLength));
1467   return true;
1468 }
1469 
RSetArrayLength(CompactBufferReader & reader)1470 RSetArrayLength::RSetArrayLength(CompactBufferReader& reader) {}
1471 
recover(JSContext * cx,SnapshotIterator & iter) const1472 bool RSetArrayLength::recover(JSContext* cx, SnapshotIterator& iter) const {
1473   RootedValue result(cx);
1474   RootedArrayObject obj(cx, &iter.read().toObject().as<ArrayObject>());
1475   RootedValue len(cx, iter.read());
1476 
1477   RootedId id(cx, NameToId(cx->names().length));
1478   ObjectOpResult error;
1479   if (!ArraySetLength(cx, obj, id, JSPROP_PERMANENT, len, error)) return false;
1480 
1481   result.setObject(*obj);
1482   iter.storeInstructionResult(result);
1483   return true;
1484 }
1485 
writeRecoverData(CompactBufferWriter & writer) const1486 bool MAssertRecoveredOnBailout::writeRecoverData(
1487     CompactBufferWriter& writer) const {
1488   MOZ_ASSERT(canRecoverOnBailout());
1489   MOZ_RELEASE_ASSERT(input()->isRecoveredOnBailout() == mustBeRecovered_,
1490                      "assertRecoveredOnBailout failed during compilation");
1491   writer.writeUnsigned(
1492       uint32_t(RInstruction::Recover_AssertRecoveredOnBailout));
1493   return true;
1494 }
1495 
RAssertRecoveredOnBailout(CompactBufferReader & reader)1496 RAssertRecoveredOnBailout::RAssertRecoveredOnBailout(
1497     CompactBufferReader& reader) {}
1498 
recover(JSContext * cx,SnapshotIterator & iter) const1499 bool RAssertRecoveredOnBailout::recover(JSContext* cx,
1500                                         SnapshotIterator& iter) const {
1501   RootedValue result(cx);
1502   iter.read();  // skip the unused operand.
1503   result.setUndefined();
1504   iter.storeInstructionResult(result);
1505   return true;
1506 }
1507 
writeRecoverData(CompactBufferWriter & writer) const1508 bool MStringReplace::writeRecoverData(CompactBufferWriter& writer) const {
1509   MOZ_ASSERT(canRecoverOnBailout());
1510   writer.writeUnsigned(uint32_t(RInstruction::Recover_StringReplace));
1511   writer.writeByte(isFlatReplacement_);
1512   return true;
1513 }
1514 
RStringReplace(CompactBufferReader & reader)1515 RStringReplace::RStringReplace(CompactBufferReader& reader) {
1516   isFlatReplacement_ = reader.readByte();
1517 }
1518 
recover(JSContext * cx,SnapshotIterator & iter) const1519 bool RStringReplace::recover(JSContext* cx, SnapshotIterator& iter) const {
1520   RootedString string(cx, iter.read().toString());
1521   RootedString pattern(cx, iter.read().toString());
1522   RootedString replace(cx, iter.read().toString());
1523 
1524   JSString* result =
1525       isFlatReplacement_
1526           ? js::str_flat_replace_string(cx, string, pattern, replace)
1527           : js::str_replace_string_raw(cx, string, pattern, replace);
1528 
1529   if (!result) return false;
1530 
1531   iter.storeInstructionResult(StringValue(result));
1532   return true;
1533 }
1534 
writeRecoverData(CompactBufferWriter & writer) const1535 bool MAtomicIsLockFree::writeRecoverData(CompactBufferWriter& writer) const {
1536   MOZ_ASSERT(canRecoverOnBailout());
1537   writer.writeUnsigned(uint32_t(RInstruction::Recover_AtomicIsLockFree));
1538   return true;
1539 }
1540 
RAtomicIsLockFree(CompactBufferReader & reader)1541 RAtomicIsLockFree::RAtomicIsLockFree(CompactBufferReader& reader) {}
1542 
recover(JSContext * cx,SnapshotIterator & iter) const1543 bool RAtomicIsLockFree::recover(JSContext* cx, SnapshotIterator& iter) const {
1544   RootedValue operand(cx, iter.read());
1545   MOZ_ASSERT(operand.isInt32());
1546 
1547   int32_t result;
1548   if (!js::AtomicIsLockFree(cx, operand, &result)) return false;
1549 
1550   RootedValue rootedResult(cx, js::Int32Value(result));
1551   iter.storeInstructionResult(rootedResult);
1552   return true;
1553 }
1554