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