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/shared/CodeGenerator-shared-inl.h"
8 
9 #include "mozilla/DebugOnly.h"
10 
11 #include "jit/CompactBuffer.h"
12 #include "jit/JitcodeMap.h"
13 #include "jit/JitSpewer.h"
14 #include "jit/MacroAssembler.h"
15 #include "jit/MIR.h"
16 #include "jit/MIRGenerator.h"
17 #include "jit/OptimizationTracking.h"
18 #include "js/Conversions.h"
19 #include "vm/TraceLogging.h"
20 
21 #include "jit/JitFrames-inl.h"
22 #include "jit/MacroAssembler-inl.h"
23 
24 using namespace js;
25 using namespace js::jit;
26 
27 using mozilla::BitwiseCast;
28 using mozilla::DebugOnly;
29 
30 namespace js {
31 namespace jit {
32 
ensureMasm(MacroAssembler * masmArg)33 MacroAssembler& CodeGeneratorShared::ensureMasm(MacroAssembler* masmArg) {
34   if (masmArg) return *masmArg;
35   maybeMasm_.emplace();
36   return *maybeMasm_;
37 }
38 
CodeGeneratorShared(MIRGenerator * gen,LIRGraph * graph,MacroAssembler * masmArg)39 CodeGeneratorShared::CodeGeneratorShared(MIRGenerator* gen, LIRGraph* graph,
40                                          MacroAssembler* masmArg)
41     : maybeMasm_(),
42       masm(ensureMasm(masmArg)),
43       gen(gen),
44       graph(*graph),
45       current(nullptr),
46       snapshots_(),
47       recovers_(),
48       deoptTable_(),
49 #ifdef DEBUG
50       pushedArgs_(0),
51 #endif
52       lastOsiPointOffset_(0),
53       safepoints_(graph->totalSlotCount(),
54                   (gen->info().nargs() + 1) * sizeof(Value)),
55       returnLabel_(),
56       stubSpace_(),
57       nativeToBytecodeMap_(nullptr),
58       nativeToBytecodeMapSize_(0),
59       nativeToBytecodeTableOffset_(0),
60       nativeToBytecodeNumRegions_(0),
61       nativeToBytecodeScriptList_(nullptr),
62       nativeToBytecodeScriptListLength_(0),
63       trackedOptimizationsMap_(nullptr),
64       trackedOptimizationsMapSize_(0),
65       trackedOptimizationsRegionTableOffset_(0),
66       trackedOptimizationsTypesTableOffset_(0),
67       trackedOptimizationsAttemptsTableOffset_(0),
68       osrEntryOffset_(0),
69       skipArgCheckEntryOffset_(0),
70 #ifdef CHECK_OSIPOINT_REGISTERS
71       checkOsiPointRegisters(JitOptions.checkOsiPointRegisters),
72 #endif
73       frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize()),
74       frameInitialAdjustment_(0) {
75   if (gen->isProfilerInstrumentationEnabled())
76     masm.enableProfilingInstrumentation();
77 
78   if (gen->compilingWasm()) {
79     // Since wasm uses the system ABI which does not necessarily use a
80     // regular array where all slots are sizeof(Value), it maintains the max
81     // argument stack depth separately.
82     MOZ_ASSERT(graph->argumentSlotCount() == 0);
83     frameDepth_ += gen->wasmMaxStackArgBytes();
84 
85     if (gen->usesSimd()) {
86       // If the function uses any SIMD then we may need to insert padding
87       // so that local slots are aligned for SIMD.
88       frameInitialAdjustment_ =
89           ComputeByteAlignment(sizeof(wasm::Frame), WasmStackAlignment);
90       frameDepth_ += frameInitialAdjustment_;
91 
92       // Keep the stack aligned. Some SIMD sequences build values on the
93       // stack and need the stack aligned.
94       frameDepth_ += ComputeByteAlignment(sizeof(wasm::Frame) + frameDepth_,
95                                           WasmStackAlignment);
96     } else if (gen->needsStaticStackAlignment()) {
97       // An MWasmCall does not align the stack pointer at calls sites but
98       // instead relies on the a priori stack adjustment. This must be the
99       // last adjustment of frameDepth_.
100       frameDepth_ += ComputeByteAlignment(sizeof(wasm::Frame) + frameDepth_,
101                                           WasmStackAlignment);
102     }
103 
104     // FrameSizeClass is only used for bailing, which cannot happen in
105     // wasm code.
106     frameClass_ = FrameSizeClass::None();
107   } else {
108     frameClass_ = FrameSizeClass::FromDepth(frameDepth_);
109   }
110 }
111 
generatePrologue()112 bool CodeGeneratorShared::generatePrologue() {
113   MOZ_ASSERT(masm.framePushed() == 0);
114   MOZ_ASSERT(!gen->compilingWasm());
115 
116 #ifdef JS_USE_LINK_REGISTER
117   masm.pushReturnAddress();
118 #endif
119 
120   // If profiling, save the current frame pointer to a per-thread global field.
121   if (isProfilerInstrumentationEnabled())
122     masm.profilerEnterFrame(masm.getStackPointer(), CallTempReg0);
123 
124   // Ensure that the Ion frame is properly aligned.
125   masm.assertStackAlignment(JitStackAlignment, 0);
126 
127   // Note that this automatically sets MacroAssembler::framePushed().
128   masm.reserveStack(frameSize());
129   masm.checkStackAlignment();
130 
131   emitTracelogIonStart();
132   return true;
133 }
134 
generateEpilogue()135 bool CodeGeneratorShared::generateEpilogue() {
136   MOZ_ASSERT(!gen->compilingWasm());
137   masm.bind(&returnLabel_);
138 
139   emitTracelogIonStop();
140 
141   masm.freeStack(frameSize());
142   MOZ_ASSERT(masm.framePushed() == 0);
143 
144   // If profiling, reset the per-thread global lastJitFrame to point to
145   // the previous frame.
146   if (isProfilerInstrumentationEnabled()) masm.profilerExitFrame();
147 
148   masm.ret();
149 
150   // On systems that use a constant pool, this is a good time to emit.
151   masm.flushBuffer();
152   return true;
153 }
154 
generateOutOfLineCode()155 bool CodeGeneratorShared::generateOutOfLineCode() {
156   // OOL paths should not attempt to use |current| as it's the last block
157   // instead of the block corresponding to the OOL path.
158   current = nullptr;
159 
160   for (size_t i = 0; i < outOfLineCode_.length(); i++) {
161     // Add native => bytecode mapping entries for OOL sites.
162     // Not enabled on wasm yet since it doesn't contain bytecode mappings.
163     if (!gen->compilingWasm()) {
164       if (!addNativeToBytecodeEntry(outOfLineCode_[i]->bytecodeSite()))
165         return false;
166     }
167 
168     if (!gen->alloc().ensureBallast()) return false;
169 
170     JitSpew(JitSpew_Codegen, "# Emitting out of line code");
171 
172     masm.setFramePushed(outOfLineCode_[i]->framePushed());
173     lastPC_ = outOfLineCode_[i]->pc();
174     outOfLineCode_[i]->bind(&masm);
175 
176     outOfLineCode_[i]->generate(this);
177   }
178 
179   return !masm.oom();
180 }
181 
addOutOfLineCode(OutOfLineCode * code,const MInstruction * mir)182 void CodeGeneratorShared::addOutOfLineCode(OutOfLineCode* code,
183                                            const MInstruction* mir) {
184   MOZ_ASSERT(mir);
185   addOutOfLineCode(code, mir->trackedSite());
186 }
187 
addOutOfLineCode(OutOfLineCode * code,const BytecodeSite * site)188 void CodeGeneratorShared::addOutOfLineCode(OutOfLineCode* code,
189                                            const BytecodeSite* site) {
190   code->setFramePushed(masm.framePushed());
191   code->setBytecodeSite(site);
192   MOZ_ASSERT_IF(!gen->compilingWasm(), code->script()->containsPC(code->pc()));
193   masm.propagateOOM(outOfLineCode_.append(code));
194 }
195 
addNativeToBytecodeEntry(const BytecodeSite * site)196 bool CodeGeneratorShared::addNativeToBytecodeEntry(const BytecodeSite* site) {
197   // Skip the table entirely if profiling is not enabled.
198   if (!isProfilerInstrumentationEnabled()) return true;
199 
200   // Fails early if the last added instruction caused the macro assembler to
201   // run out of memory as continuity assumption below do not hold.
202   if (masm.oom()) return false;
203 
204   MOZ_ASSERT(site);
205   MOZ_ASSERT(site->tree());
206   MOZ_ASSERT(site->pc());
207 
208   InlineScriptTree* tree = site->tree();
209   jsbytecode* pc = site->pc();
210   uint32_t nativeOffset = masm.currentOffset();
211 
212   MOZ_ASSERT_IF(nativeToBytecodeList_.empty(), nativeOffset == 0);
213 
214   if (!nativeToBytecodeList_.empty()) {
215     size_t lastIdx = nativeToBytecodeList_.length() - 1;
216     NativeToBytecode& lastEntry = nativeToBytecodeList_[lastIdx];
217 
218     MOZ_ASSERT(nativeOffset >= lastEntry.nativeOffset.offset());
219 
220     // If the new entry is for the same inlineScriptTree and same
221     // bytecodeOffset, but the nativeOffset has changed, do nothing.
222     // The same site just generated some more code.
223     if (lastEntry.tree == tree && lastEntry.pc == pc) {
224       JitSpew(JitSpew_Profiling, " => In-place update [%zu-%" PRIu32 "]",
225               lastEntry.nativeOffset.offset(), nativeOffset);
226       return true;
227     }
228 
229     // If the new entry is for the same native offset, then update the
230     // previous entry with the new bytecode site, since the previous
231     // bytecode site did not generate any native code.
232     if (lastEntry.nativeOffset.offset() == nativeOffset) {
233       lastEntry.tree = tree;
234       lastEntry.pc = pc;
235       JitSpew(JitSpew_Profiling, " => Overwriting zero-length native region.");
236 
237       // This overwrite might have made the entry merge-able with a
238       // previous one.  If so, merge it.
239       if (lastIdx > 0) {
240         NativeToBytecode& nextToLastEntry = nativeToBytecodeList_[lastIdx - 1];
241         if (nextToLastEntry.tree == lastEntry.tree &&
242             nextToLastEntry.pc == lastEntry.pc) {
243           JitSpew(JitSpew_Profiling, " => Merging with previous region");
244           nativeToBytecodeList_.erase(&lastEntry);
245         }
246       }
247 
248       dumpNativeToBytecodeEntry(nativeToBytecodeList_.length() - 1);
249       return true;
250     }
251   }
252 
253   // Otherwise, some native code was generated for the previous bytecode site.
254   // Add a new entry for code that is about to be generated.
255   NativeToBytecode entry;
256   entry.nativeOffset = CodeOffset(nativeOffset);
257   entry.tree = tree;
258   entry.pc = pc;
259   if (!nativeToBytecodeList_.append(entry)) return false;
260 
261   JitSpew(JitSpew_Profiling, " => Push new entry.");
262   dumpNativeToBytecodeEntry(nativeToBytecodeList_.length() - 1);
263   return true;
264 }
265 
dumpNativeToBytecodeEntries()266 void CodeGeneratorShared::dumpNativeToBytecodeEntries() {
267 #ifdef JS_JITSPEW
268   InlineScriptTree* topTree = gen->info().inlineScriptTree();
269   JitSpewStart(JitSpew_Profiling, "Native To Bytecode Entries for %s:%zu\n",
270                topTree->script()->filename(), topTree->script()->lineno());
271   for (unsigned i = 0; i < nativeToBytecodeList_.length(); i++)
272     dumpNativeToBytecodeEntry(i);
273 #endif
274 }
275 
dumpNativeToBytecodeEntry(uint32_t idx)276 void CodeGeneratorShared::dumpNativeToBytecodeEntry(uint32_t idx) {
277 #ifdef JS_JITSPEW
278   NativeToBytecode& ref = nativeToBytecodeList_[idx];
279   InlineScriptTree* tree = ref.tree;
280   JSScript* script = tree->script();
281   uint32_t nativeOffset = ref.nativeOffset.offset();
282   unsigned nativeDelta = 0;
283   unsigned pcDelta = 0;
284   if (idx + 1 < nativeToBytecodeList_.length()) {
285     NativeToBytecode* nextRef = &ref + 1;
286     nativeDelta = nextRef->nativeOffset.offset() - nativeOffset;
287     if (nextRef->tree == ref.tree) pcDelta = nextRef->pc - ref.pc;
288   }
289   JitSpewStart(
290       JitSpew_Profiling, "    %08zx [+%-6d] => %-6ld [%-4d] {%-10s} (%s:%zu",
291       ref.nativeOffset.offset(), nativeDelta, (long)(ref.pc - script->code()),
292       pcDelta, CodeName[JSOp(*ref.pc)], script->filename(), script->lineno());
293 
294   for (tree = tree->caller(); tree; tree = tree->caller()) {
295     JitSpewCont(JitSpew_Profiling, " <= %s:%zu", tree->script()->filename(),
296                 tree->script()->lineno());
297   }
298   JitSpewCont(JitSpew_Profiling, ")");
299   JitSpewFin(JitSpew_Profiling);
300 #endif
301 }
302 
addTrackedOptimizationsEntry(const TrackedOptimizations * optimizations)303 bool CodeGeneratorShared::addTrackedOptimizationsEntry(
304     const TrackedOptimizations* optimizations) {
305   if (!isOptimizationTrackingEnabled()) return true;
306 
307   MOZ_ASSERT(optimizations);
308 
309   uint32_t nativeOffset = masm.currentOffset();
310 
311   if (!trackedOptimizations_.empty()) {
312     NativeToTrackedOptimizations& lastEntry = trackedOptimizations_.back();
313     MOZ_ASSERT_IF(!masm.oom(), nativeOffset >= lastEntry.endOffset.offset());
314 
315     // If we're still generating code for the same set of optimizations,
316     // we are done.
317     if (lastEntry.optimizations == optimizations) return true;
318   }
319 
320   // If we're generating code for a new set of optimizations, add a new
321   // entry.
322   NativeToTrackedOptimizations entry;
323   entry.startOffset = CodeOffset(nativeOffset);
324   entry.endOffset = CodeOffset(nativeOffset);
325   entry.optimizations = optimizations;
326   return trackedOptimizations_.append(entry);
327 }
328 
extendTrackedOptimizationsEntry(const TrackedOptimizations * optimizations)329 void CodeGeneratorShared::extendTrackedOptimizationsEntry(
330     const TrackedOptimizations* optimizations) {
331   if (!isOptimizationTrackingEnabled()) return;
332 
333   uint32_t nativeOffset = masm.currentOffset();
334   NativeToTrackedOptimizations& entry = trackedOptimizations_.back();
335   MOZ_ASSERT(entry.optimizations == optimizations);
336   MOZ_ASSERT_IF(!masm.oom(), nativeOffset >= entry.endOffset.offset());
337 
338   entry.endOffset = CodeOffset(nativeOffset);
339 
340   // If we generated no code, remove the last entry.
341   if (nativeOffset == entry.startOffset.offset())
342     trackedOptimizations_.popBack();
343 }
344 
345 // see OffsetOfFrameSlot
ToStackIndex(LAllocation * a)346 static inline int32_t ToStackIndex(LAllocation* a) {
347   if (a->isStackSlot()) {
348     MOZ_ASSERT(a->toStackSlot()->slot() >= 1);
349     return a->toStackSlot()->slot();
350   }
351   return -int32_t(sizeof(JitFrameLayout) + a->toArgument()->index());
352 }
353 
encodeAllocation(LSnapshot * snapshot,MDefinition * mir,uint32_t * allocIndex)354 void CodeGeneratorShared::encodeAllocation(LSnapshot* snapshot,
355                                            MDefinition* mir,
356                                            uint32_t* allocIndex) {
357   if (mir->isBox()) mir = mir->toBox()->getOperand(0);
358 
359   MIRType type =
360       mir->isRecoveredOnBailout()
361           ? MIRType::None
362           : mir->isUnused() ? MIRType::MagicOptimizedOut : mir->type();
363 
364   RValueAllocation alloc;
365 
366   switch (type) {
367     case MIRType::None: {
368       MOZ_ASSERT(mir->isRecoveredOnBailout());
369       uint32_t index = 0;
370       LRecoverInfo* recoverInfo = snapshot->recoverInfo();
371       MNode** it = recoverInfo->begin();
372       MNode** end = recoverInfo->end();
373       while (it != end && mir != *it) {
374         ++it;
375         ++index;
376       }
377 
378       // This MDefinition is recovered, thus it should be listed in the
379       // LRecoverInfo.
380       MOZ_ASSERT(it != end && mir == *it);
381 
382       // Lambda should have a default value readable for iterating over the
383       // inner frames.
384       if (mir->isLambda() || mir->isLambdaArrow()) {
385         MConstant* constant = mir->isLambda()
386                                   ? mir->toLambda()->functionOperand()
387                                   : mir->toLambdaArrow()->functionOperand();
388         uint32_t cstIndex;
389         masm.propagateOOM(
390             graph.addConstantToPool(constant->toJSValue(), &cstIndex));
391         alloc = RValueAllocation::RecoverInstruction(index, cstIndex);
392         break;
393       }
394 
395       alloc = RValueAllocation::RecoverInstruction(index);
396       break;
397     }
398     case MIRType::Undefined:
399       alloc = RValueAllocation::Undefined();
400       break;
401     case MIRType::Null:
402       alloc = RValueAllocation::Null();
403       break;
404     case MIRType::Int32:
405     case MIRType::String:
406     case MIRType::Symbol:
407     case MIRType::Object:
408     case MIRType::ObjectOrNull:
409     case MIRType::Boolean:
410     case MIRType::Double: {
411       LAllocation* payload = snapshot->payloadOfSlot(*allocIndex);
412       if (payload->isConstant()) {
413         MConstant* constant = mir->toConstant();
414         uint32_t index;
415         masm.propagateOOM(
416             graph.addConstantToPool(constant->toJSValue(), &index));
417         alloc = RValueAllocation::ConstantPool(index);
418         break;
419       }
420 
421       JSValueType valueType = (type == MIRType::ObjectOrNull)
422                                   ? JSVAL_TYPE_OBJECT
423                                   : ValueTypeFromMIRType(type);
424 
425       MOZ_DIAGNOSTIC_ASSERT(payload->isMemory() || payload->isRegister());
426       if (payload->isMemory())
427         alloc = RValueAllocation::Typed(valueType, ToStackIndex(payload));
428       else if (payload->isGeneralReg())
429         alloc = RValueAllocation::Typed(valueType, ToRegister(payload));
430       else if (payload->isFloatReg())
431         alloc = RValueAllocation::Double(ToFloatRegister(payload));
432       else
433         MOZ_CRASH("Unexpected payload type.");
434       break;
435     }
436     case MIRType::Float32:
437     case MIRType::Int8x16:
438     case MIRType::Int16x8:
439     case MIRType::Int32x4:
440     case MIRType::Float32x4:
441     case MIRType::Bool8x16:
442     case MIRType::Bool16x8:
443     case MIRType::Bool32x4: {
444       LAllocation* payload = snapshot->payloadOfSlot(*allocIndex);
445       if (payload->isConstant()) {
446         MConstant* constant = mir->toConstant();
447         uint32_t index;
448         masm.propagateOOM(
449             graph.addConstantToPool(constant->toJSValue(), &index));
450         alloc = RValueAllocation::ConstantPool(index);
451         break;
452       }
453 
454       MOZ_ASSERT(payload->isMemory() || payload->isFloatReg());
455       if (payload->isFloatReg())
456         alloc = RValueAllocation::AnyFloat(ToFloatRegister(payload));
457       else
458         alloc = RValueAllocation::AnyFloat(ToStackIndex(payload));
459       break;
460     }
461     case MIRType::MagicOptimizedArguments:
462     case MIRType::MagicOptimizedOut:
463     case MIRType::MagicUninitializedLexical:
464     case MIRType::MagicIsConstructing: {
465       uint32_t index;
466       JSWhyMagic why = JS_GENERIC_MAGIC;
467       switch (type) {
468         case MIRType::MagicOptimizedArguments:
469           why = JS_OPTIMIZED_ARGUMENTS;
470           break;
471         case MIRType::MagicOptimizedOut:
472           why = JS_OPTIMIZED_OUT;
473           break;
474         case MIRType::MagicUninitializedLexical:
475           why = JS_UNINITIALIZED_LEXICAL;
476           break;
477         case MIRType::MagicIsConstructing:
478           why = JS_IS_CONSTRUCTING;
479           break;
480         default:
481           MOZ_CRASH("Invalid Magic MIRType");
482       }
483 
484       Value v = MagicValue(why);
485       masm.propagateOOM(graph.addConstantToPool(v, &index));
486       alloc = RValueAllocation::ConstantPool(index);
487       break;
488     }
489     default: {
490       MOZ_ASSERT(mir->type() == MIRType::Value);
491       LAllocation* payload = snapshot->payloadOfSlot(*allocIndex);
492 #ifdef JS_NUNBOX32
493       LAllocation* type = snapshot->typeOfSlot(*allocIndex);
494       if (type->isRegister()) {
495         if (payload->isRegister())
496           alloc =
497               RValueAllocation::Untyped(ToRegister(type), ToRegister(payload));
498         else
499           alloc = RValueAllocation::Untyped(ToRegister(type),
500                                             ToStackIndex(payload));
501       } else {
502         if (payload->isRegister())
503           alloc = RValueAllocation::Untyped(ToStackIndex(type),
504                                             ToRegister(payload));
505         else
506           alloc = RValueAllocation::Untyped(ToStackIndex(type),
507                                             ToStackIndex(payload));
508       }
509 #elif JS_PUNBOX64
510       if (payload->isRegister())
511         alloc = RValueAllocation::Untyped(ToRegister(payload));
512       else
513         alloc = RValueAllocation::Untyped(ToStackIndex(payload));
514 #endif
515       break;
516     }
517   }
518   MOZ_DIAGNOSTIC_ASSERT(alloc.valid());
519 
520   // This set an extra bit as part of the RValueAllocation, such that we know
521   // that recover instruction have to be executed without wrapping the
522   // instruction in a no-op recover instruction.
523   if (mir->isIncompleteObject()) alloc.setNeedSideEffect();
524 
525   masm.propagateOOM(snapshots_.add(alloc));
526 
527   *allocIndex += mir->isRecoveredOnBailout() ? 0 : 1;
528 }
529 
encode(LRecoverInfo * recover)530 void CodeGeneratorShared::encode(LRecoverInfo* recover) {
531   if (recover->recoverOffset() != INVALID_RECOVER_OFFSET) return;
532 
533   uint32_t numInstructions = recover->numInstructions();
534   JitSpew(JitSpew_IonSnapshots,
535           "Encoding LRecoverInfo %p (frameCount %u, instructions %u)",
536           (void*)recover, recover->mir()->frameCount(), numInstructions);
537 
538   MResumePoint::Mode mode = recover->mir()->mode();
539   MOZ_ASSERT(mode != MResumePoint::Outer);
540   bool resumeAfter = (mode == MResumePoint::ResumeAfter);
541 
542   RecoverOffset offset = recovers_.startRecover(numInstructions, resumeAfter);
543 
544   for (MNode* insn : *recover) recovers_.writeInstruction(insn);
545 
546   recovers_.endRecover();
547   recover->setRecoverOffset(offset);
548   masm.propagateOOM(!recovers_.oom());
549 }
550 
encode(LSnapshot * snapshot)551 void CodeGeneratorShared::encode(LSnapshot* snapshot) {
552   if (snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET) return;
553 
554   LRecoverInfo* recoverInfo = snapshot->recoverInfo();
555   encode(recoverInfo);
556 
557   RecoverOffset recoverOffset = recoverInfo->recoverOffset();
558   MOZ_ASSERT(recoverOffset != INVALID_RECOVER_OFFSET);
559 
560   JitSpew(JitSpew_IonSnapshots, "Encoding LSnapshot %p (LRecover %p)",
561           (void*)snapshot, (void*)recoverInfo);
562 
563   SnapshotOffset offset =
564       snapshots_.startSnapshot(recoverOffset, snapshot->bailoutKind());
565 
566 #ifdef TRACK_SNAPSHOTS
567   uint32_t pcOpcode = 0;
568   uint32_t lirOpcode = 0;
569   uint32_t lirId = 0;
570   uint32_t mirOpcode = 0;
571   uint32_t mirId = 0;
572 
573   if (LNode* ins = instruction()) {
574     lirOpcode = ins->op();
575     lirId = ins->id();
576     if (ins->mirRaw()) {
577       mirOpcode = uint32_t(ins->mirRaw()->op());
578       mirId = ins->mirRaw()->id();
579       if (ins->mirRaw()->trackedPc()) pcOpcode = *ins->mirRaw()->trackedPc();
580     }
581   }
582   snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId);
583 #endif
584 
585   uint32_t allocIndex = 0;
586   for (LRecoverInfo::OperandIter it(recoverInfo); !it; ++it) {
587     DebugOnly<uint32_t> allocWritten = snapshots_.allocWritten();
588     encodeAllocation(snapshot, *it, &allocIndex);
589     MOZ_ASSERT_IF(!snapshots_.oom(),
590                   allocWritten + 1 == snapshots_.allocWritten());
591   }
592 
593   MOZ_ASSERT(allocIndex == snapshot->numSlots());
594   snapshots_.endSnapshot();
595   snapshot->setSnapshotOffset(offset);
596   masm.propagateOOM(!snapshots_.oom());
597 }
598 
assignBailoutId(LSnapshot * snapshot)599 bool CodeGeneratorShared::assignBailoutId(LSnapshot* snapshot) {
600   MOZ_ASSERT(snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET);
601 
602   // Can we not use bailout tables at all?
603   if (!deoptTable_) return false;
604 
605   MOZ_ASSERT(frameClass_ != FrameSizeClass::None());
606 
607   if (snapshot->bailoutId() != INVALID_BAILOUT_ID) return true;
608 
609   // Is the bailout table full?
610   if (bailouts_.length() >= BAILOUT_TABLE_SIZE) return false;
611 
612   unsigned bailoutId = bailouts_.length();
613   snapshot->setBailoutId(bailoutId);
614   JitSpew(JitSpew_IonSnapshots, "Assigned snapshot bailout id %u", bailoutId);
615   masm.propagateOOM(bailouts_.append(snapshot->snapshotOffset()));
616   return true;
617 }
618 
encodeSafepoints()619 bool CodeGeneratorShared::encodeSafepoints() {
620   for (SafepointIndex& index : safepointIndices_) {
621     LSafepoint* safepoint = index.safepoint();
622 
623     if (!safepoint->encoded()) safepoints_.encode(safepoint);
624 
625     index.resolve();
626   }
627 
628   return !safepoints_.oom();
629 }
630 
createNativeToBytecodeScriptList(JSContext * cx)631 bool CodeGeneratorShared::createNativeToBytecodeScriptList(JSContext* cx) {
632   js::Vector<JSScript*, 0, SystemAllocPolicy> scriptList;
633   InlineScriptTree* tree = gen->info().inlineScriptTree();
634   for (;;) {
635     // Add script from current tree.
636     bool found = false;
637     for (uint32_t i = 0; i < scriptList.length(); i++) {
638       if (scriptList[i] == tree->script()) {
639         found = true;
640         break;
641       }
642     }
643     if (!found) {
644       if (!scriptList.append(tree->script())) return false;
645     }
646 
647     // Process rest of tree
648 
649     // If children exist, emit children.
650     if (tree->hasChildren()) {
651       tree = tree->firstChild();
652       continue;
653     }
654 
655     // Otherwise, find the first tree up the chain (including this one)
656     // that contains a next sibling.
657     while (!tree->hasNextCallee() && tree->hasCaller()) tree = tree->caller();
658 
659     // If we found a sibling, use it.
660     if (tree->hasNextCallee()) {
661       tree = tree->nextCallee();
662       continue;
663     }
664 
665     // Otherwise, we must have reached the top without finding any siblings.
666     MOZ_ASSERT(tree->isOutermostCaller());
667     break;
668   }
669 
670   // Allocate array for list.
671   JSScript** data = cx->zone()->pod_malloc<JSScript*>(scriptList.length());
672   if (!data) return false;
673 
674   for (uint32_t i = 0; i < scriptList.length(); i++) data[i] = scriptList[i];
675 
676   // Success.
677   nativeToBytecodeScriptListLength_ = scriptList.length();
678   nativeToBytecodeScriptList_ = data;
679   return true;
680 }
681 
generateCompactNativeToBytecodeMap(JSContext * cx,JitCode * code)682 bool CodeGeneratorShared::generateCompactNativeToBytecodeMap(JSContext* cx,
683                                                              JitCode* code) {
684   MOZ_ASSERT(nativeToBytecodeScriptListLength_ == 0);
685   MOZ_ASSERT(nativeToBytecodeScriptList_ == nullptr);
686   MOZ_ASSERT(nativeToBytecodeMap_ == nullptr);
687   MOZ_ASSERT(nativeToBytecodeMapSize_ == 0);
688   MOZ_ASSERT(nativeToBytecodeTableOffset_ == 0);
689   MOZ_ASSERT(nativeToBytecodeNumRegions_ == 0);
690 
691   if (!createNativeToBytecodeScriptList(cx)) return false;
692 
693   MOZ_ASSERT(nativeToBytecodeScriptListLength_ > 0);
694   MOZ_ASSERT(nativeToBytecodeScriptList_ != nullptr);
695 
696   CompactBufferWriter writer;
697   uint32_t tableOffset = 0;
698   uint32_t numRegions = 0;
699 
700   if (!JitcodeIonTable::WriteIonTable(
701           writer, nativeToBytecodeScriptList_,
702           nativeToBytecodeScriptListLength_, &nativeToBytecodeList_[0],
703           &nativeToBytecodeList_[0] + nativeToBytecodeList_.length(),
704           &tableOffset, &numRegions)) {
705     js_free(nativeToBytecodeScriptList_);
706     return false;
707   }
708 
709   MOZ_ASSERT(tableOffset > 0);
710   MOZ_ASSERT(numRegions > 0);
711 
712   // Writer is done, copy it to sized buffer.
713   uint8_t* data = cx->zone()->pod_malloc<uint8_t>(writer.length());
714   if (!data) {
715     js_free(nativeToBytecodeScriptList_);
716     return false;
717   }
718 
719   memcpy(data, writer.buffer(), writer.length());
720   nativeToBytecodeMap_ = data;
721   nativeToBytecodeMapSize_ = writer.length();
722   nativeToBytecodeTableOffset_ = tableOffset;
723   nativeToBytecodeNumRegions_ = numRegions;
724 
725   verifyCompactNativeToBytecodeMap(code);
726 
727   JitSpew(JitSpew_Profiling, "Compact Native To Bytecode Map [%p-%p]", data,
728           data + nativeToBytecodeMapSize_);
729 
730   return true;
731 }
732 
verifyCompactNativeToBytecodeMap(JitCode * code)733 void CodeGeneratorShared::verifyCompactNativeToBytecodeMap(JitCode* code) {
734 #ifdef DEBUG
735   MOZ_ASSERT(nativeToBytecodeScriptListLength_ > 0);
736   MOZ_ASSERT(nativeToBytecodeScriptList_ != nullptr);
737   MOZ_ASSERT(nativeToBytecodeMap_ != nullptr);
738   MOZ_ASSERT(nativeToBytecodeMapSize_ > 0);
739   MOZ_ASSERT(nativeToBytecodeTableOffset_ > 0);
740   MOZ_ASSERT(nativeToBytecodeNumRegions_ > 0);
741 
742   // The pointer to the table must be 4-byte aligned
743   const uint8_t* tablePtr = nativeToBytecodeMap_ + nativeToBytecodeTableOffset_;
744   MOZ_ASSERT(uintptr_t(tablePtr) % sizeof(uint32_t) == 0);
745 
746   // Verify that numRegions was encoded correctly.
747   const JitcodeIonTable* ionTable =
748       reinterpret_cast<const JitcodeIonTable*>(tablePtr);
749   MOZ_ASSERT(ionTable->numRegions() == nativeToBytecodeNumRegions_);
750 
751   // Region offset for first region should be at the start of the payload
752   // region. Since the offsets are backward from the start of the table, the
753   // first entry backoffset should be equal to the forward table offset from the
754   // start of the allocated data.
755   MOZ_ASSERT(ionTable->regionOffset(0) == nativeToBytecodeTableOffset_);
756 
757   // Verify each region.
758   for (uint32_t i = 0; i < ionTable->numRegions(); i++) {
759     // Back-offset must point into the payload region preceding the table, not
760     // before it.
761     MOZ_ASSERT(ionTable->regionOffset(i) <= nativeToBytecodeTableOffset_);
762 
763     // Back-offset must point to a later area in the payload region than
764     // previous back-offset.  This means that back-offsets decrease
765     // monotonically.
766     MOZ_ASSERT_IF(i > 0,
767                   ionTable->regionOffset(i) < ionTable->regionOffset(i - 1));
768 
769     JitcodeRegionEntry entry = ionTable->regionEntry(i);
770 
771     // Ensure native code offset for region falls within jitcode.
772     MOZ_ASSERT(entry.nativeOffset() <= code->instructionsSize());
773 
774     // Read out script/pc stack and verify.
775     JitcodeRegionEntry::ScriptPcIterator scriptPcIter =
776         entry.scriptPcIterator();
777     while (scriptPcIter.hasMore()) {
778       uint32_t scriptIdx = 0, pcOffset = 0;
779       scriptPcIter.readNext(&scriptIdx, &pcOffset);
780 
781       // Ensure scriptIdx refers to a valid script in the list.
782       MOZ_ASSERT(scriptIdx < nativeToBytecodeScriptListLength_);
783       JSScript* script = nativeToBytecodeScriptList_[scriptIdx];
784 
785       // Ensure pcOffset falls within the script.
786       MOZ_ASSERT(pcOffset < script->length());
787     }
788 
789     // Obtain the original nativeOffset and pcOffset and script.
790     uint32_t curNativeOffset = entry.nativeOffset();
791     JSScript* script = nullptr;
792     uint32_t curPcOffset = 0;
793     {
794       uint32_t scriptIdx = 0;
795       scriptPcIter.reset();
796       scriptPcIter.readNext(&scriptIdx, &curPcOffset);
797       script = nativeToBytecodeScriptList_[scriptIdx];
798     }
799 
800     // Read out nativeDeltas and pcDeltas and verify.
801     JitcodeRegionEntry::DeltaIterator deltaIter = entry.deltaIterator();
802     while (deltaIter.hasMore()) {
803       uint32_t nativeDelta = 0;
804       int32_t pcDelta = 0;
805       deltaIter.readNext(&nativeDelta, &pcDelta);
806 
807       curNativeOffset += nativeDelta;
808       curPcOffset = uint32_t(int32_t(curPcOffset) + pcDelta);
809 
810       // Ensure that nativeOffset still falls within jitcode after delta.
811       MOZ_ASSERT(curNativeOffset <= code->instructionsSize());
812 
813       // Ensure that pcOffset still falls within bytecode after delta.
814       MOZ_ASSERT(curPcOffset < script->length());
815     }
816   }
817 #endif  // DEBUG
818 }
819 
generateCompactTrackedOptimizationsMap(JSContext * cx,JitCode * code,IonTrackedTypeVector * allTypes)820 bool CodeGeneratorShared::generateCompactTrackedOptimizationsMap(
821     JSContext* cx, JitCode* code, IonTrackedTypeVector* allTypes) {
822   MOZ_ASSERT(trackedOptimizationsMap_ == nullptr);
823   MOZ_ASSERT(trackedOptimizationsMapSize_ == 0);
824   MOZ_ASSERT(trackedOptimizationsRegionTableOffset_ == 0);
825   MOZ_ASSERT(trackedOptimizationsTypesTableOffset_ == 0);
826   MOZ_ASSERT(trackedOptimizationsAttemptsTableOffset_ == 0);
827 
828   if (trackedOptimizations_.empty()) return true;
829 
830   UniqueTrackedOptimizations unique(cx);
831   if (!unique.init()) return false;
832 
833   // Iterate through all entries to deduplicate their optimization attempts.
834   for (size_t i = 0; i < trackedOptimizations_.length(); i++) {
835     NativeToTrackedOptimizations& entry = trackedOptimizations_[i];
836     if (!unique.add(entry.optimizations)) return false;
837   }
838 
839   // Sort the unique optimization attempts by frequency to stabilize the
840   // attempts' indices in the compact table we will write later.
841   if (!unique.sortByFrequency(cx)) return false;
842 
843   // Write out the ranges and the table.
844   CompactBufferWriter writer;
845   uint32_t numRegions;
846   uint32_t regionTableOffset;
847   uint32_t typesTableOffset;
848   uint32_t attemptsTableOffset;
849   if (!WriteIonTrackedOptimizationsTable(
850           cx, writer, trackedOptimizations_.begin(),
851           trackedOptimizations_.end(), unique, &numRegions, &regionTableOffset,
852           &typesTableOffset, &attemptsTableOffset, allTypes)) {
853     return false;
854   }
855 
856   MOZ_ASSERT(regionTableOffset > 0);
857   MOZ_ASSERT(typesTableOffset > 0);
858   MOZ_ASSERT(attemptsTableOffset > 0);
859   MOZ_ASSERT(typesTableOffset > regionTableOffset);
860   MOZ_ASSERT(attemptsTableOffset > typesTableOffset);
861 
862   // Copy over the table out of the writer's buffer.
863   uint8_t* data = cx->zone()->pod_malloc<uint8_t>(writer.length());
864   if (!data) return false;
865 
866   memcpy(data, writer.buffer(), writer.length());
867   trackedOptimizationsMap_ = data;
868   trackedOptimizationsMapSize_ = writer.length();
869   trackedOptimizationsRegionTableOffset_ = regionTableOffset;
870   trackedOptimizationsTypesTableOffset_ = typesTableOffset;
871   trackedOptimizationsAttemptsTableOffset_ = attemptsTableOffset;
872 
873   verifyCompactTrackedOptimizationsMap(code, numRegions, unique, allTypes);
874 
875   JitSpew(JitSpew_OptimizationTrackingExtended,
876           "== Compact Native To Optimizations Map [%p-%p] size %u", data,
877           data + trackedOptimizationsMapSize_, trackedOptimizationsMapSize_);
878   JitSpew(JitSpew_OptimizationTrackingExtended,
879           "     with type list of length %zu, size %zu", allTypes->length(),
880           allTypes->length() * sizeof(IonTrackedTypeWithAddendum));
881 
882   return true;
883 }
884 
885 #ifdef DEBUG
886 class ReadTempAttemptsVectorOp
887     : public JS::ForEachTrackedOptimizationAttemptOp {
888   TempOptimizationAttemptsVector* attempts_;
889   bool oom_;
890 
891  public:
ReadTempAttemptsVectorOp(TempOptimizationAttemptsVector * attempts)892   explicit ReadTempAttemptsVectorOp(TempOptimizationAttemptsVector* attempts)
893       : attempts_(attempts), oom_(false) {}
894 
oom()895   bool oom() { return oom_; }
896 
operator ()(JS::TrackedStrategy strategy,JS::TrackedOutcome outcome)897   void operator()(JS::TrackedStrategy strategy,
898                   JS::TrackedOutcome outcome) override {
899     if (!attempts_->append(OptimizationAttempt(strategy, outcome))) oom_ = true;
900   }
901 };
902 
903 struct ReadTempTypeInfoVectorOp
904     : public IonTrackedOptimizationsTypeInfo::ForEachOp {
905   TempAllocator& alloc_;
906   TempOptimizationTypeInfoVector* types_;
907   TempTypeList accTypes_;
908   bool oom_;
909 
910  public:
ReadTempTypeInfoVectorOpjs::jit::ReadTempTypeInfoVectorOp911   ReadTempTypeInfoVectorOp(TempAllocator& alloc,
912                            TempOptimizationTypeInfoVector* types)
913       : alloc_(alloc), types_(types), accTypes_(alloc), oom_(false) {}
914 
oomjs::jit::ReadTempTypeInfoVectorOp915   bool oom() { return oom_; }
916 
readTypejs::jit::ReadTempTypeInfoVectorOp917   void readType(const IonTrackedTypeWithAddendum& tracked) override {
918     if (!accTypes_.append(tracked.type)) oom_ = true;
919   }
920 
operator ()js::jit::ReadTempTypeInfoVectorOp921   void operator()(JS::TrackedTypeSite site, MIRType mirType) override {
922     OptimizationTypeInfo ty(alloc_, site, mirType);
923     for (uint32_t i = 0; i < accTypes_.length(); i++) {
924       if (!ty.trackType(accTypes_[i])) oom_ = true;
925     }
926     if (!types_->append(mozilla::Move(ty))) oom_ = true;
927     accTypes_.clear();
928   }
929 };
930 #endif  // DEBUG
931 
verifyCompactTrackedOptimizationsMap(JitCode * code,uint32_t numRegions,const UniqueTrackedOptimizations & unique,const IonTrackedTypeVector * allTypes)932 void CodeGeneratorShared::verifyCompactTrackedOptimizationsMap(
933     JitCode* code, uint32_t numRegions,
934     const UniqueTrackedOptimizations& unique,
935     const IonTrackedTypeVector* allTypes) {
936 #ifdef DEBUG
937   MOZ_ASSERT(trackedOptimizationsMap_ != nullptr);
938   MOZ_ASSERT(trackedOptimizationsMapSize_ > 0);
939   MOZ_ASSERT(trackedOptimizationsRegionTableOffset_ > 0);
940   MOZ_ASSERT(trackedOptimizationsTypesTableOffset_ > 0);
941   MOZ_ASSERT(trackedOptimizationsAttemptsTableOffset_ > 0);
942 
943   // Table pointers must all be 4-byte aligned.
944   const uint8_t* regionTableAddr =
945       trackedOptimizationsMap_ + trackedOptimizationsRegionTableOffset_;
946   const uint8_t* typesTableAddr =
947       trackedOptimizationsMap_ + trackedOptimizationsTypesTableOffset_;
948   const uint8_t* attemptsTableAddr =
949       trackedOptimizationsMap_ + trackedOptimizationsAttemptsTableOffset_;
950   MOZ_ASSERT(uintptr_t(regionTableAddr) % sizeof(uint32_t) == 0);
951   MOZ_ASSERT(uintptr_t(typesTableAddr) % sizeof(uint32_t) == 0);
952   MOZ_ASSERT(uintptr_t(attemptsTableAddr) % sizeof(uint32_t) == 0);
953 
954   // Assert that the number of entries matches up for the tables.
955   const IonTrackedOptimizationsRegionTable* regionTable =
956       (const IonTrackedOptimizationsRegionTable*)regionTableAddr;
957   MOZ_ASSERT(regionTable->numEntries() == numRegions);
958   const IonTrackedOptimizationsTypesTable* typesTable =
959       (const IonTrackedOptimizationsTypesTable*)typesTableAddr;
960   MOZ_ASSERT(typesTable->numEntries() == unique.count());
961   const IonTrackedOptimizationsAttemptsTable* attemptsTable =
962       (const IonTrackedOptimizationsAttemptsTable*)attemptsTableAddr;
963   MOZ_ASSERT(attemptsTable->numEntries() == unique.count());
964 
965   // Verify each region.
966   uint32_t trackedIdx = 0;
967   for (uint32_t regionIdx = 0; regionIdx < regionTable->numEntries();
968        regionIdx++) {
969     // Check reverse offsets are within bounds.
970     MOZ_ASSERT(regionTable->entryOffset(regionIdx) <=
971                trackedOptimizationsRegionTableOffset_);
972     MOZ_ASSERT_IF(regionIdx > 0, regionTable->entryOffset(regionIdx) <
973                                      regionTable->entryOffset(regionIdx - 1));
974 
975     IonTrackedOptimizationsRegion region = regionTable->entry(regionIdx);
976 
977     // Check the region range is covered by jitcode.
978     MOZ_ASSERT(region.startOffset() <= code->instructionsSize());
979     MOZ_ASSERT(region.endOffset() <= code->instructionsSize());
980 
981     IonTrackedOptimizationsRegion::RangeIterator iter = region.ranges();
982     while (iter.more()) {
983       // Assert that the offsets are correctly decoded from the delta.
984       uint32_t startOffset, endOffset;
985       uint8_t index;
986       iter.readNext(&startOffset, &endOffset, &index);
987       NativeToTrackedOptimizations& entry = trackedOptimizations_[trackedIdx++];
988       MOZ_ASSERT(startOffset == entry.startOffset.offset());
989       MOZ_ASSERT(endOffset == entry.endOffset.offset());
990       MOZ_ASSERT(index == unique.indexOf(entry.optimizations));
991 
992       // Assert that the type info and attempts vectors are correctly
993       // decoded. This is disabled for now if the types table might
994       // contain nursery pointers, in which case the types might not
995       // match, see bug 1175761.
996       if (!code->zone()->group()->storeBuffer().cancelIonCompilations()) {
997         IonTrackedOptimizationsTypeInfo typeInfo = typesTable->entry(index);
998         TempOptimizationTypeInfoVector tvec(alloc());
999         ReadTempTypeInfoVectorOp top(alloc(), &tvec);
1000         typeInfo.forEach(top, allTypes);
1001         MOZ_ASSERT_IF(!top.oom(), entry.optimizations->matchTypes(tvec));
1002       }
1003 
1004       IonTrackedOptimizationsAttempts attempts = attemptsTable->entry(index);
1005       TempOptimizationAttemptsVector avec(alloc());
1006       ReadTempAttemptsVectorOp aop(&avec);
1007       attempts.forEach(aop);
1008       MOZ_ASSERT_IF(!aop.oom(), entry.optimizations->matchAttempts(avec));
1009     }
1010   }
1011 #endif
1012 }
1013 
markSafepoint(LInstruction * ins)1014 void CodeGeneratorShared::markSafepoint(LInstruction* ins) {
1015   markSafepointAt(masm.currentOffset(), ins);
1016 }
1017 
markSafepointAt(uint32_t offset,LInstruction * ins)1018 void CodeGeneratorShared::markSafepointAt(uint32_t offset, LInstruction* ins) {
1019   MOZ_ASSERT_IF(
1020       !safepointIndices_.empty() && !masm.oom(),
1021       offset - safepointIndices_.back().displacement() >= sizeof(uint32_t));
1022   masm.propagateOOM(
1023       safepointIndices_.append(SafepointIndex(offset, ins->safepoint())));
1024 }
1025 
ensureOsiSpace()1026 void CodeGeneratorShared::ensureOsiSpace() {
1027   // For a refresher, an invalidation point is of the form:
1028   // 1: call <target>
1029   // 2: ...
1030   // 3: <osipoint>
1031   //
1032   // The four bytes *before* instruction 2 are overwritten with an offset.
1033   // Callers must ensure that the instruction itself has enough bytes to
1034   // support this.
1035   //
1036   // The bytes *at* instruction 3 are overwritten with an invalidation jump.
1037   // jump. These bytes may be in a completely different IR sequence, but
1038   // represent the join point of the call out of the function.
1039   //
1040   // At points where we want to ensure that invalidation won't corrupt an
1041   // important instruction, we make sure to pad with nops.
1042   if (masm.currentOffset() - lastOsiPointOffset_ <
1043       Assembler::PatchWrite_NearCallSize()) {
1044     int32_t paddingSize = Assembler::PatchWrite_NearCallSize();
1045     paddingSize -= masm.currentOffset() - lastOsiPointOffset_;
1046     for (int32_t i = 0; i < paddingSize; ++i) masm.nop();
1047   }
1048   MOZ_ASSERT_IF(!masm.oom(), masm.currentOffset() - lastOsiPointOffset_ >=
1049                                  Assembler::PatchWrite_NearCallSize());
1050   lastOsiPointOffset_ = masm.currentOffset();
1051 }
1052 
markOsiPoint(LOsiPoint * ins)1053 uint32_t CodeGeneratorShared::markOsiPoint(LOsiPoint* ins) {
1054   encode(ins->snapshot());
1055   ensureOsiSpace();
1056 
1057   uint32_t offset = masm.currentOffset();
1058   SnapshotOffset so = ins->snapshot()->snapshotOffset();
1059   masm.propagateOOM(osiIndices_.append(OsiIndex(offset, so)));
1060 
1061   return offset;
1062 }
1063 
1064 #ifdef CHECK_OSIPOINT_REGISTERS
1065 template <class Op>
HandleRegisterDump(Op op,MacroAssembler & masm,LiveRegisterSet liveRegs,Register activation,Register scratch)1066 static void HandleRegisterDump(Op op, MacroAssembler& masm,
1067                                LiveRegisterSet liveRegs, Register activation,
1068                                Register scratch) {
1069   const size_t baseOffset = JitActivation::offsetOfRegs();
1070 
1071   // Handle live GPRs.
1072   for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); ++iter) {
1073     Register reg = *iter;
1074     Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
1075 
1076     if (reg == activation) {
1077       // To use the original value of the activation register (that's
1078       // now on top of the stack), we need the scratch register.
1079       masm.push(scratch);
1080       masm.loadPtr(Address(masm.getStackPointer(), sizeof(uintptr_t)), scratch);
1081       op(scratch, dump);
1082       masm.pop(scratch);
1083     } else {
1084       op(reg, dump);
1085     }
1086   }
1087 
1088   // Handle live FPRs.
1089   for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); ++iter) {
1090     FloatRegister reg = *iter;
1091     Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
1092     op(reg, dump);
1093   }
1094 }
1095 
1096 class StoreOp {
1097   MacroAssembler& masm;
1098 
1099  public:
StoreOp(MacroAssembler & masm)1100   explicit StoreOp(MacroAssembler& masm) : masm(masm) {}
1101 
operator ()(Register reg,Address dump)1102   void operator()(Register reg, Address dump) { masm.storePtr(reg, dump); }
operator ()(FloatRegister reg,Address dump)1103   void operator()(FloatRegister reg, Address dump) {
1104     if (reg.isDouble())
1105       masm.storeDouble(reg, dump);
1106     else if (reg.isSingle())
1107       masm.storeFloat32(reg, dump);
1108 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
1109     else if (reg.isSimd128())
1110       masm.storeUnalignedSimd128Float(reg, dump);
1111 #endif
1112     else
1113       MOZ_CRASH("Unexpected register type.");
1114   }
1115 };
1116 
StoreAllLiveRegs(MacroAssembler & masm,LiveRegisterSet liveRegs)1117 static void StoreAllLiveRegs(MacroAssembler& masm, LiveRegisterSet liveRegs) {
1118   // Store a copy of all live registers before performing the call.
1119   // When we reach the OsiPoint, we can use this to check nothing
1120   // modified them in the meantime.
1121 
1122   // Load pointer to the JitActivation in a scratch register.
1123   AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
1124   Register scratch = allRegs.takeAny();
1125   masm.push(scratch);
1126   masm.loadJitActivation(scratch);
1127 
1128   Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
1129   masm.add32(Imm32(1), checkRegs);
1130 
1131   StoreOp op(masm);
1132   HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny());
1133 
1134   masm.pop(scratch);
1135 }
1136 
1137 class VerifyOp {
1138   MacroAssembler& masm;
1139   Label* failure_;
1140 
1141  public:
VerifyOp(MacroAssembler & masm,Label * failure)1142   VerifyOp(MacroAssembler& masm, Label* failure)
1143       : masm(masm), failure_(failure) {}
1144 
operator ()(Register reg,Address dump)1145   void operator()(Register reg, Address dump) {
1146     masm.branchPtr(Assembler::NotEqual, dump, reg, failure_);
1147   }
operator ()(FloatRegister reg,Address dump)1148   void operator()(FloatRegister reg, Address dump) {
1149     FloatRegister scratch;
1150     if (reg.isDouble()) {
1151       scratch = ScratchDoubleReg;
1152       masm.loadDouble(dump, scratch);
1153       masm.branchDouble(Assembler::DoubleNotEqual, scratch, reg, failure_);
1154     } else if (reg.isSingle()) {
1155       scratch = ScratchFloat32Reg;
1156       masm.loadFloat32(dump, scratch);
1157       masm.branchFloat(Assembler::DoubleNotEqual, scratch, reg, failure_);
1158     }
1159 
1160     // :TODO: (Bug 1133745) Add support to verify SIMD registers.
1161   }
1162 };
1163 
verifyOsiPointRegs(LSafepoint * safepoint)1164 void CodeGeneratorShared::verifyOsiPointRegs(LSafepoint* safepoint) {
1165   // Ensure the live registers stored by callVM did not change between
1166   // the call and this OsiPoint. Try-catch relies on this invariant.
1167 
1168   // Load pointer to the JitActivation in a scratch register.
1169   AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
1170   Register scratch = allRegs.takeAny();
1171   masm.push(scratch);
1172   masm.loadJitActivation(scratch);
1173 
1174   // If we should not check registers (because the instruction did not call
1175   // into the VM, or a GC happened), we're done.
1176   Label failure, done;
1177   Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
1178   masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done);
1179 
1180   // Having more than one VM function call made in one visit function at
1181   // runtime is a sec-ciritcal error, because if we conservatively assume that
1182   // one of the function call can re-enter Ion, then the invalidation process
1183   // will potentially add a call at a random location, by patching the code
1184   // before the return address.
1185   masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure);
1186 
1187   // Set checkRegs to 0, so that we don't try to verify registers after we
1188   // return from this script to the caller.
1189   masm.store32(Imm32(0), checkRegs);
1190 
1191   // Ignore clobbered registers. Some instructions (like LValueToInt32) modify
1192   // temps after calling into the VM. This is fine because no other
1193   // instructions (including this OsiPoint) will depend on them. Also
1194   // backtracking can also use the same register for an input and an output.
1195   // These are marked as clobbered and shouldn't get checked.
1196   LiveRegisterSet liveRegs;
1197   liveRegs.set() = RegisterSet::Intersect(
1198       safepoint->liveRegs().set(),
1199       RegisterSet::Not(safepoint->clobberedRegs().set()));
1200 
1201   VerifyOp op(masm, &failure);
1202   HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny());
1203 
1204   masm.jump(&done);
1205 
1206   // Do not profile the callWithABI that occurs below.  This is to avoid a
1207   // rare corner case that occurs when profiling interacts with itself:
1208   //
1209   // When slow profiling assertions are turned on, FunctionBoundary ops
1210   // (which update the profiler pseudo-stack) may emit a callVM, which
1211   // forces them to have an osi point associated with them.  The
1212   // FunctionBoundary for inline function entry is added to the caller's
1213   // graph with a PC from the caller's code, but during codegen it modifies
1214   // Gecko Profiler instrumentation to add the callee as the current top-most
1215   // script. When codegen gets to the OSIPoint, and the callWithABI below is
1216   // emitted, the codegen thinks that the current frame is the callee, but
1217   // the PC it's using from the OSIPoint refers to the caller.  This causes
1218   // the profiler instrumentation of the callWithABI below to ASSERT, since
1219   // the script and pc are mismatched.  To avoid this, we simply omit
1220   // instrumentation for these callWithABIs.
1221 
1222   // Any live register captured by a safepoint (other than temp registers)
1223   // must remain unchanged between the call and the OsiPoint instruction.
1224   masm.bind(&failure);
1225   masm.assumeUnreachable("Modified registers between VM call and OsiPoint");
1226 
1227   masm.bind(&done);
1228   masm.pop(scratch);
1229 }
1230 
shouldVerifyOsiPointRegs(LSafepoint * safepoint)1231 bool CodeGeneratorShared::shouldVerifyOsiPointRegs(LSafepoint* safepoint) {
1232   if (!checkOsiPointRegisters) return false;
1233 
1234   if (safepoint->liveRegs().emptyGeneral() &&
1235       safepoint->liveRegs().emptyFloat())
1236     return false;  // No registers to check.
1237 
1238   return true;
1239 }
1240 
resetOsiPointRegs(LSafepoint * safepoint)1241 void CodeGeneratorShared::resetOsiPointRegs(LSafepoint* safepoint) {
1242   if (!shouldVerifyOsiPointRegs(safepoint)) return;
1243 
1244   // Set checkRegs to 0. If we perform a VM call, the instruction
1245   // will set it to 1.
1246   AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
1247   Register scratch = allRegs.takeAny();
1248   masm.push(scratch);
1249   masm.loadJitActivation(scratch);
1250   Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
1251   masm.store32(Imm32(0), checkRegs);
1252   masm.pop(scratch);
1253 }
1254 #endif
1255 
1256 // Before doing any call to Cpp, you should ensure that volatile
1257 // registers are evicted by the register allocator.
callVM(const VMFunction & fun,LInstruction * ins,const Register * dynStack)1258 void CodeGeneratorShared::callVM(const VMFunction& fun, LInstruction* ins,
1259                                  const Register* dynStack) {
1260   // If we're calling a function with an out parameter type of double, make
1261   // sure we have an FPU.
1262   MOZ_ASSERT_IF(fun.outParam == Type_Double,
1263                 gen->runtime->jitSupportsFloatingPoint());
1264 
1265 #ifdef DEBUG
1266   if (ins->mirRaw()) {
1267     MOZ_ASSERT(ins->mirRaw()->isInstruction());
1268     MInstruction* mir = ins->mirRaw()->toInstruction();
1269     MOZ_ASSERT_IF(mir->needsResumePoint(), mir->resumePoint());
1270   }
1271 #endif
1272 
1273     // Stack is:
1274     //    ... frame ...
1275     //    [args]
1276 #ifdef DEBUG
1277   MOZ_ASSERT(pushedArgs_ == fun.explicitArgs);
1278   pushedArgs_ = 0;
1279 #endif
1280 
1281   // Get the wrapper of the VM function.
1282   TrampolinePtr wrapper = gen->jitRuntime()->getVMWrapper(fun);
1283 
1284 #ifdef CHECK_OSIPOINT_REGISTERS
1285   if (shouldVerifyOsiPointRegs(ins->safepoint()))
1286     StoreAllLiveRegs(masm, ins->safepoint()->liveRegs());
1287 #endif
1288 
1289   // Push an exit frame descriptor. If |dynStack| is a valid pointer to a
1290   // register, then its value is added to the value of the |framePushed()| to
1291   // fill the frame descriptor.
1292   if (dynStack) {
1293     masm.addPtr(Imm32(masm.framePushed()), *dynStack);
1294     masm.makeFrameDescriptor(*dynStack, JitFrame_IonJS,
1295                              ExitFrameLayout::Size());
1296     masm.Push(*dynStack);  // descriptor
1297   } else {
1298     masm.pushStaticFrameDescriptor(JitFrame_IonJS, ExitFrameLayout::Size());
1299   }
1300 
1301   // Call the wrapper function.  The wrapper is in charge to unwind the stack
1302   // when returning from the call.  Failures are handled with exceptions based
1303   // on the return value of the C functions.  To guard the outcome of the
1304   // returned value, use another LIR instruction.
1305   uint32_t callOffset = masm.callJit(wrapper);
1306   markSafepointAt(callOffset, ins);
1307 
1308   // Remove rest of the frame left on the stack. We remove the return address
1309   // which is implicitly poped when returning.
1310   int framePop = sizeof(ExitFrameLayout) - sizeof(void*);
1311 
1312   // Pop arguments from framePushed.
1313   masm.implicitPop(fun.explicitStackSlots() * sizeof(void*) + framePop);
1314   // Stack is:
1315   //    ... frame ...
1316 }
1317 
1318 class OutOfLineTruncateSlow : public OutOfLineCodeBase<CodeGeneratorShared> {
1319   FloatRegister src_;
1320   Register dest_;
1321   bool widenFloatToDouble_;
1322   wasm::BytecodeOffset bytecodeOffset_;
1323 
1324  public:
OutOfLineTruncateSlow(FloatRegister src,Register dest,bool widenFloatToDouble=false,wasm::BytecodeOffset bytecodeOffset=wasm::BytecodeOffset ())1325   OutOfLineTruncateSlow(
1326       FloatRegister src, Register dest, bool widenFloatToDouble = false,
1327       wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset())
1328       : src_(src),
1329         dest_(dest),
1330         widenFloatToDouble_(widenFloatToDouble),
1331         bytecodeOffset_(bytecodeOffset) {}
1332 
accept(CodeGeneratorShared * codegen)1333   void accept(CodeGeneratorShared* codegen) override {
1334     codegen->visitOutOfLineTruncateSlow(this);
1335   }
src() const1336   FloatRegister src() const { return src_; }
dest() const1337   Register dest() const { return dest_; }
widenFloatToDouble() const1338   bool widenFloatToDouble() const { return widenFloatToDouble_; }
bytecodeOffset() const1339   wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
1340 };
1341 
oolTruncateDouble(FloatRegister src,Register dest,MInstruction * mir,wasm::BytecodeOffset bytecodeOffset)1342 OutOfLineCode* CodeGeneratorShared::oolTruncateDouble(
1343     FloatRegister src, Register dest, MInstruction* mir,
1344     wasm::BytecodeOffset bytecodeOffset) {
1345   MOZ_ASSERT_IF(IsCompilingWasm(), bytecodeOffset.isValid());
1346 
1347   OutOfLineTruncateSlow* ool = new (alloc())
1348       OutOfLineTruncateSlow(src, dest, /* float32 */ false, bytecodeOffset);
1349   addOutOfLineCode(ool, mir);
1350   return ool;
1351 }
1352 
emitTruncateDouble(FloatRegister src,Register dest,MTruncateToInt32 * mir)1353 void CodeGeneratorShared::emitTruncateDouble(FloatRegister src, Register dest,
1354                                              MTruncateToInt32* mir) {
1355   OutOfLineCode* ool = oolTruncateDouble(src, dest, mir, mir->bytecodeOffset());
1356 
1357   masm.branchTruncateDoubleMaybeModUint32(src, dest, ool->entry());
1358   masm.bind(ool->rejoin());
1359 }
1360 
emitTruncateFloat32(FloatRegister src,Register dest,MTruncateToInt32 * mir)1361 void CodeGeneratorShared::emitTruncateFloat32(FloatRegister src, Register dest,
1362                                               MTruncateToInt32* mir) {
1363   OutOfLineTruncateSlow* ool = new (alloc()) OutOfLineTruncateSlow(
1364       src, dest, /* float32 */ true, mir->bytecodeOffset());
1365   addOutOfLineCode(ool, mir);
1366 
1367   masm.branchTruncateFloat32MaybeModUint32(src, dest, ool->entry());
1368   masm.bind(ool->rejoin());
1369 }
1370 
visitOutOfLineTruncateSlow(OutOfLineTruncateSlow * ool)1371 void CodeGeneratorShared::visitOutOfLineTruncateSlow(
1372     OutOfLineTruncateSlow* ool) {
1373   FloatRegister src = ool->src();
1374   Register dest = ool->dest();
1375 
1376   saveVolatile(dest);
1377   masm.outOfLineTruncateSlow(src, dest, ool->widenFloatToDouble(),
1378                              gen->compilingWasm(), ool->bytecodeOffset());
1379   restoreVolatile(dest);
1380 
1381   masm.jump(ool->rejoin());
1382 }
1383 
omitOverRecursedCheck() const1384 bool CodeGeneratorShared::omitOverRecursedCheck() const {
1385   // If the current function makes no calls (which means it isn't recursive)
1386   // and it uses only a small amount of stack space, it doesn't need a
1387   // stack overflow check. Note that the actual number here is somewhat
1388   // arbitrary, and codegen actually uses small bounded amounts of
1389   // additional stack space in some cases too.
1390   return frameSize() < MAX_UNCHECKED_LEAF_FRAME_SIZE &&
1391          !gen->needsOverrecursedCheck();
1392 }
1393 
emitWasmCallBase(MWasmCall * mir,bool needsBoundsCheck)1394 void CodeGeneratorShared::emitWasmCallBase(MWasmCall* mir,
1395                                            bool needsBoundsCheck) {
1396   if (mir->spIncrement()) masm.freeStack(mir->spIncrement());
1397 
1398   MOZ_ASSERT((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment ==
1399              0);
1400   static_assert(
1401       WasmStackAlignment >= ABIStackAlignment &&
1402           WasmStackAlignment % ABIStackAlignment == 0,
1403       "The wasm stack alignment should subsume the ABI-required alignment");
1404 
1405 #ifdef DEBUG
1406   Label ok;
1407   masm.branchTestStackPtr(Assembler::Zero, Imm32(WasmStackAlignment - 1), &ok);
1408   masm.breakpoint();
1409   masm.bind(&ok);
1410 #endif
1411 
1412   // LWasmCallBase::isCallPreserved() assumes that all MWasmCalls preserve the
1413   // TLS and pinned regs. The only case where where we don't have to reload
1414   // the TLS and pinned regs is when the callee preserves them.
1415   bool reloadRegs = true;
1416 
1417   const wasm::CallSiteDesc& desc = mir->desc();
1418   const wasm::CalleeDesc& callee = mir->callee();
1419   switch (callee.which()) {
1420     case wasm::CalleeDesc::Func:
1421       masm.call(desc, callee.funcIndex());
1422       reloadRegs = false;
1423       break;
1424     case wasm::CalleeDesc::Import:
1425       masm.wasmCallImport(desc, callee);
1426       break;
1427     case wasm::CalleeDesc::AsmJSTable:
1428     case wasm::CalleeDesc::WasmTable:
1429       masm.wasmCallIndirect(desc, callee, needsBoundsCheck);
1430       reloadRegs = callee.which() == wasm::CalleeDesc::WasmTable &&
1431                    callee.wasmTableIsExternal();
1432       break;
1433     case wasm::CalleeDesc::Builtin:
1434       masm.call(desc, callee.builtin());
1435       reloadRegs = false;
1436       break;
1437     case wasm::CalleeDesc::BuiltinInstanceMethod:
1438       masm.wasmCallBuiltinInstanceMethod(desc, mir->instanceArg(),
1439                                          callee.builtin());
1440       break;
1441   }
1442 
1443   if (reloadRegs) {
1444     masm.loadWasmTlsRegFromFrame();
1445     masm.loadWasmPinnedRegsFromTls();
1446   }
1447 
1448   if (mir->spIncrement()) masm.reserveStack(mir->spIncrement());
1449 }
1450 
visitWasmLoadGlobalVar(LWasmLoadGlobalVar * ins)1451 void CodeGeneratorShared::visitWasmLoadGlobalVar(LWasmLoadGlobalVar* ins) {
1452   MWasmLoadGlobalVar* mir = ins->mir();
1453 
1454   MIRType type = mir->type();
1455   MOZ_ASSERT(IsNumberType(type) || IsSimdType(type));
1456 
1457   Register tls = ToRegister(ins->tlsPtr());
1458   Address addr(tls,
1459                offsetof(wasm::TlsData, globalArea) + mir->globalDataOffset());
1460   switch (type) {
1461     case MIRType::Int32:
1462       masm.load32(addr, ToRegister(ins->output()));
1463       break;
1464     case MIRType::Float32:
1465       masm.loadFloat32(addr, ToFloatRegister(ins->output()));
1466       break;
1467     case MIRType::Double:
1468       masm.loadDouble(addr, ToFloatRegister(ins->output()));
1469       break;
1470     // Aligned access: code is aligned on PageSize + there is padding
1471     // before the global data section.
1472     case MIRType::Int8x16:
1473     case MIRType::Int16x8:
1474     case MIRType::Int32x4:
1475     case MIRType::Bool8x16:
1476     case MIRType::Bool16x8:
1477     case MIRType::Bool32x4:
1478       masm.loadInt32x4(addr, ToFloatRegister(ins->output()));
1479       break;
1480     case MIRType::Float32x4:
1481       masm.loadFloat32x4(addr, ToFloatRegister(ins->output()));
1482       break;
1483     default:
1484       MOZ_CRASH("unexpected type in visitWasmLoadGlobalVar");
1485   }
1486 }
1487 
visitWasmStoreGlobalVar(LWasmStoreGlobalVar * ins)1488 void CodeGeneratorShared::visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins) {
1489   MWasmStoreGlobalVar* mir = ins->mir();
1490 
1491   MIRType type = mir->value()->type();
1492   MOZ_ASSERT(IsNumberType(type) || IsSimdType(type));
1493 
1494   Register tls = ToRegister(ins->tlsPtr());
1495   Address addr(tls,
1496                offsetof(wasm::TlsData, globalArea) + mir->globalDataOffset());
1497   switch (type) {
1498     case MIRType::Int32:
1499       masm.store32(ToRegister(ins->value()), addr);
1500       break;
1501     case MIRType::Float32:
1502       masm.storeFloat32(ToFloatRegister(ins->value()), addr);
1503       break;
1504     case MIRType::Double:
1505       masm.storeDouble(ToFloatRegister(ins->value()), addr);
1506       break;
1507     // Aligned access: code is aligned on PageSize + there is padding
1508     // before the global data section.
1509     case MIRType::Int8x16:
1510     case MIRType::Int16x8:
1511     case MIRType::Int32x4:
1512     case MIRType::Bool8x16:
1513     case MIRType::Bool16x8:
1514     case MIRType::Bool32x4:
1515       masm.storeInt32x4(ToFloatRegister(ins->value()), addr);
1516       break;
1517     case MIRType::Float32x4:
1518       masm.storeFloat32x4(ToFloatRegister(ins->value()), addr);
1519       break;
1520     default:
1521       MOZ_CRASH("unexpected type in visitWasmStoreGlobalVar");
1522   }
1523 }
1524 
visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64 * ins)1525 void CodeGeneratorShared::visitWasmLoadGlobalVarI64(
1526     LWasmLoadGlobalVarI64* ins) {
1527   MWasmLoadGlobalVar* mir = ins->mir();
1528   MOZ_ASSERT(mir->type() == MIRType::Int64);
1529 
1530   Register tls = ToRegister(ins->tlsPtr());
1531   Address addr(tls,
1532                offsetof(wasm::TlsData, globalArea) + mir->globalDataOffset());
1533 
1534   Register64 output = ToOutRegister64(ins);
1535   masm.load64(addr, output);
1536 }
1537 
visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64 * ins)1538 void CodeGeneratorShared::visitWasmStoreGlobalVarI64(
1539     LWasmStoreGlobalVarI64* ins) {
1540   MWasmStoreGlobalVar* mir = ins->mir();
1541   MOZ_ASSERT(mir->value()->type() == MIRType::Int64);
1542 
1543   Register tls = ToRegister(ins->tlsPtr());
1544   Address addr(tls,
1545                offsetof(wasm::TlsData, globalArea) + mir->globalDataOffset());
1546 
1547   Register64 value = ToRegister64(ins->value());
1548   masm.store64(value, addr);
1549 }
1550 
emitPreBarrier(Register base,const LAllocation * index,int32_t offsetAdjustment)1551 void CodeGeneratorShared::emitPreBarrier(Register base,
1552                                          const LAllocation* index,
1553                                          int32_t offsetAdjustment) {
1554   if (index->isConstant()) {
1555     Address address(base, ToInt32(index) * sizeof(Value) + offsetAdjustment);
1556     masm.guardedCallPreBarrier(address, MIRType::Value);
1557   } else {
1558     BaseIndex address(base, ToRegister(index), TimesEight, offsetAdjustment);
1559     masm.guardedCallPreBarrier(address, MIRType::Value);
1560   }
1561 }
1562 
emitPreBarrier(Address address)1563 void CodeGeneratorShared::emitPreBarrier(Address address) {
1564   masm.guardedCallPreBarrier(address, MIRType::Value);
1565 }
1566 
labelForBackedgeWithImplicitCheck(MBasicBlock * mir)1567 Label* CodeGeneratorShared::labelForBackedgeWithImplicitCheck(
1568     MBasicBlock* mir) {
1569   // If this is a loop backedge to a loop header with an implicit interrupt
1570   // check, use a patchable jump. Skip this search if compiling without a
1571   // script for wasm, as there will be no interrupt check instruction.
1572   // Due to critical edge unsplitting there may no longer be unique loop
1573   // backedges, so just look for any edge going to an earlier block in RPO.
1574   if (!gen->compilingWasm() && mir->isLoopHeader() &&
1575       mir->id() <= current->mir()->id()) {
1576     for (LInstructionIterator iter = mir->lir()->begin();
1577          iter != mir->lir()->end(); iter++) {
1578       if (iter->isMoveGroup()) {
1579         // Continue searching for an interrupt check.
1580       } else {
1581         // The interrupt check should be the first instruction in the
1582         // loop header other than move groups.
1583         MOZ_ASSERT(iter->isInterruptCheck());
1584         if (iter->toInterruptCheck()->implicit())
1585           return iter->toInterruptCheck()->oolEntry();
1586         return nullptr;
1587       }
1588     }
1589   }
1590 
1591   return nullptr;
1592 }
1593 
jumpToBlock(MBasicBlock * mir)1594 void CodeGeneratorShared::jumpToBlock(MBasicBlock* mir) {
1595   // Skip past trivial blocks.
1596   mir = skipTrivialBlocks(mir);
1597 
1598   // No jump necessary if we can fall through to the next block.
1599   if (isNextBlock(mir->lir())) return;
1600 
1601   if (Label* oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
1602     // Note: the backedge is initially a jump to the next instruction.
1603     // It will be patched to the target block's label during link().
1604     RepatchLabel rejoin;
1605     CodeOffsetJump backedge = masm.backedgeJump(&rejoin, mir->lir()->label());
1606     masm.bind(&rejoin);
1607 
1608     masm.propagateOOM(patchableBackedges_.append(
1609         PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry)));
1610   } else {
1611     masm.jump(mir->lir()->label());
1612   }
1613 }
1614 
getJumpLabelForBranch(MBasicBlock * block)1615 Label* CodeGeneratorShared::getJumpLabelForBranch(MBasicBlock* block) {
1616   // Skip past trivial blocks.
1617   block = skipTrivialBlocks(block);
1618 
1619   if (!labelForBackedgeWithImplicitCheck(block)) return block->lir()->label();
1620 
1621   // We need to use a patchable jump for this backedge, but want to treat
1622   // this as a normal label target to simplify codegen. Efficiency isn't so
1623   // important here as these tests are extremely unlikely to be used in loop
1624   // backedges, so emit inline code for the patchable jump. Heap allocating
1625   // the label allows it to be used by out of line blocks.
1626   Label* res = alloc().lifoAlloc()->newInfallible<Label>();
1627   Label after;
1628   masm.jump(&after);
1629   masm.bind(res);
1630   jumpToBlock(block);
1631   masm.bind(&after);
1632   return res;
1633 }
1634 
1635 // This function is not used for MIPS/MIPS64. MIPS has branchToBlock.
1636 #if !defined(JS_CODEGEN_MIPS32) && !defined(JS_CODEGEN_MIPS64)
jumpToBlock(MBasicBlock * mir,Assembler::Condition cond)1637 void CodeGeneratorShared::jumpToBlock(MBasicBlock* mir,
1638                                       Assembler::Condition cond) {
1639   // Skip past trivial blocks.
1640   mir = skipTrivialBlocks(mir);
1641 
1642   if (Label* oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
1643     // Note: the backedge is initially a jump to the next instruction.
1644     // It will be patched to the target block's label during link().
1645     RepatchLabel rejoin;
1646     CodeOffsetJump backedge =
1647         masm.jumpWithPatch(&rejoin, cond, mir->lir()->label());
1648     masm.bind(&rejoin);
1649 
1650     masm.propagateOOM(patchableBackedges_.append(
1651         PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry)));
1652   } else {
1653     masm.j(cond, mir->lir()->label());
1654   }
1655 }
1656 #endif
1657 
computeDivisionConstants(uint32_t d,int maxLog)1658 ReciprocalMulConstants CodeGeneratorShared::computeDivisionConstants(
1659     uint32_t d, int maxLog) {
1660   MOZ_ASSERT(maxLog >= 2 && maxLog <= 32);
1661   // In what follows, 0 < d < 2^maxLog and d is not a power of 2.
1662   MOZ_ASSERT(d < (uint64_t(1) << maxLog) && (d & (d - 1)) != 0);
1663 
1664   // Speeding up division by non power-of-2 constants is possible by
1665   // calculating, during compilation, a value M such that high-order
1666   // bits of M*n correspond to the result of the division of n by d.
1667   // No value of M can serve this purpose for arbitrarily big values
1668   // of n but, for optimizing integer division, we're just concerned
1669   // with values of n whose absolute value is bounded (by fitting in
1670   // an integer type, say). With this in mind, we'll find a constant
1671   // M as above that works for -2^maxLog <= n < 2^maxLog; maxLog can
1672   // then be 31 for signed division or 32 for unsigned division.
1673   //
1674   // The original presentation of this technique appears in Hacker's
1675   // Delight, a book by Henry S. Warren, Jr.. A proof of correctness
1676   // for our version follows; we'll denote maxLog by L in the proof,
1677   // for conciseness.
1678   //
1679   // Formally, for |d| < 2^L, we'll compute two magic values M and s
1680   // in the ranges 0 <= M < 2^(L+1) and 0 <= s <= L such that
1681   //     (M * n) >> (32 + s) = floor(n/d)    if    0 <= n < 2^L
1682   //     (M * n) >> (32 + s) = ceil(n/d) - 1 if -2^L <= n < 0.
1683   //
1684   // Define p = 32 + s, M = ceil(2^p/d), and assume that s satisfies
1685   //                     M - 2^p/d <= 2^(p-L)/d.                 (1)
1686   // (Observe that p = CeilLog32(d) + L satisfies this, as the right
1687   // side of (1) is at least one in this case). Then,
1688   //
1689   // a) If p <= CeilLog32(d) + L, then M < 2^(L+1) - 1.
1690   // Proof: Indeed, M is monotone in p and, for p equal to the above
1691   // value, the bounds 2^L > d >= 2^(p-L-1) + 1 readily imply that
1692   //    2^p / d <  2^p/(d - 1) * (d - 1)/d
1693   //            <= 2^(L+1) * (1 - 1/d) < 2^(L+1) - 2.
1694   // The claim follows by applying the ceiling function.
1695   //
1696   // b) For any 0 <= n < 2^L, floor(Mn/2^p) = floor(n/d).
1697   // Proof: Put x = floor(Mn/2^p); it's the unique integer for which
1698   //                    Mn/2^p - 1 < x <= Mn/2^p.                (2)
1699   // Using M >= 2^p/d on the LHS and (1) on the RHS, we get
1700   //           n/d - 1 < x <= n/d + n/(2^L d) < n/d + 1/d.
1701   // Since x is an integer, it's not in the interval (n/d, (n+1)/d),
1702   // and so n/d - 1 < x <= n/d, which implies x = floor(n/d).
1703   //
1704   // c) For any -2^L <= n < 0, floor(Mn/2^p) + 1 = ceil(n/d).
1705   // Proof: The proof is similar. Equation (2) holds as above. Using
1706   // M > 2^p/d (d isn't a power of 2) on the RHS and (1) on the LHS,
1707   //                 n/d + n/(2^L d) - 1 < x < n/d.
1708   // Using n >= -2^L and summing 1,
1709   //                  n/d - 1/d < x + 1 < n/d + 1.
1710   // Since x + 1 is an integer, this implies n/d <= x + 1 < n/d + 1.
1711   // In other words, x + 1 = ceil(n/d).
1712   //
1713   // Condition (1) isn't necessary for the existence of M and s with
1714   // the properties above. Hacker's Delight provides a slightly less
1715   // restrictive condition when d >= 196611, at the cost of a 3-page
1716   // proof of correctness, for the case L = 31.
1717   //
1718   // Note that, since d*M - 2^p = d - (2^p)%d, (1) can be written as
1719   //                   2^(p-L) >= d - (2^p)%d.
1720   // In order to avoid overflow in the (2^p) % d calculation, we can
1721   // compute it as (2^p-1) % d + 1, where 2^p-1 can then be computed
1722   // without overflow as UINT64_MAX >> (64-p).
1723 
1724   // We now compute the least p >= 32 with the property above...
1725   int32_t p = 32;
1726   while ((uint64_t(1) << (p - maxLog)) + (UINT64_MAX >> (64 - p)) % d + 1 < d)
1727     p++;
1728 
1729   // ...and the corresponding M. For either the signed (L=31) or the
1730   // unsigned (L=32) case, this value can be too large (cf. item a).
1731   // Codegen can still multiply by M by multiplying by (M - 2^L) and
1732   // adjusting the value afterwards, if this is the case.
1733   ReciprocalMulConstants rmc;
1734   rmc.multiplier = (UINT64_MAX >> (64 - p)) / d + 1;
1735   rmc.shiftAmount = p - 32;
1736 
1737   return rmc;
1738 }
1739 
1740 #ifdef JS_TRACE_LOGGING
1741 
emitTracelogScript(bool isStart)1742 void CodeGeneratorShared::emitTracelogScript(bool isStart) {
1743   if (!TraceLogTextIdEnabled(TraceLogger_Scripts)) return;
1744 
1745   Label done;
1746 
1747   AllocatableRegisterSet regs(RegisterSet::Volatile());
1748   Register logger = regs.takeAnyGeneral();
1749   Register script = regs.takeAnyGeneral();
1750 
1751   masm.Push(logger);
1752 
1753   masm.loadTraceLogger(logger);
1754   masm.branchTestPtr(Assembler::Zero, logger, logger, &done);
1755 
1756   Address enabledAddress(logger, TraceLoggerThread::offsetOfEnabled());
1757   masm.branch32(Assembler::Equal, enabledAddress, Imm32(0), &done);
1758 
1759   masm.Push(script);
1760 
1761   CodeOffset patchScript = masm.movWithPatch(ImmWord(0), script);
1762   masm.propagateOOM(patchableTLScripts_.append(patchScript));
1763 
1764   if (isStart)
1765     masm.tracelogStartId(logger, script);
1766   else
1767     masm.tracelogStopId(logger, script);
1768 
1769   masm.Pop(script);
1770 
1771   masm.bind(&done);
1772 
1773   masm.Pop(logger);
1774 }
1775 
emitTracelogTree(bool isStart,uint32_t textId)1776 void CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId) {
1777   if (!TraceLogTextIdEnabled(textId)) return;
1778 
1779   Label done;
1780   AllocatableRegisterSet regs(RegisterSet::Volatile());
1781   Register logger = regs.takeAnyGeneral();
1782 
1783   masm.Push(logger);
1784 
1785   masm.loadTraceLogger(logger);
1786   masm.branchTestPtr(Assembler::Zero, logger, logger, &done);
1787 
1788   Address enabledAddress(logger, TraceLoggerThread::offsetOfEnabled());
1789   masm.branch32(Assembler::Equal, enabledAddress, Imm32(0), &done);
1790 
1791   if (isStart)
1792     masm.tracelogStartId(logger, textId);
1793   else
1794     masm.tracelogStopId(logger, textId);
1795 
1796   masm.bind(&done);
1797 
1798   masm.Pop(logger);
1799 }
1800 
emitTracelogTree(bool isStart,const char * text,TraceLoggerTextId enabledTextId)1801 void CodeGeneratorShared::emitTracelogTree(bool isStart, const char* text,
1802                                            TraceLoggerTextId enabledTextId) {
1803   if (!TraceLogTextIdEnabled(enabledTextId)) return;
1804 
1805   Label done;
1806 
1807   AllocatableRegisterSet regs(RegisterSet::Volatile());
1808   Register loggerReg = regs.takeAnyGeneral();
1809   Register eventReg = regs.takeAnyGeneral();
1810 
1811   masm.Push(loggerReg);
1812 
1813   masm.loadTraceLogger(loggerReg);
1814   masm.branchTestPtr(Assembler::Zero, loggerReg, loggerReg, &done);
1815 
1816   Address enabledAddress(loggerReg, TraceLoggerThread::offsetOfEnabled());
1817   masm.branch32(Assembler::Equal, enabledAddress, Imm32(0), &done);
1818 
1819   masm.Push(eventReg);
1820 
1821   PatchableTLEvent patchEvent(masm.movWithPatch(ImmWord(0), eventReg), text);
1822   masm.propagateOOM(patchableTLEvents_.append(Move(patchEvent)));
1823 
1824   if (isStart)
1825     masm.tracelogStartId(loggerReg, eventReg);
1826   else
1827     masm.tracelogStopId(loggerReg, eventReg);
1828 
1829   masm.Pop(eventReg);
1830 
1831   masm.bind(&done);
1832 
1833   masm.Pop(loggerReg);
1834 }
1835 #endif
1836 
1837 }  // namespace jit
1838 }  // namespace js
1839