Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
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/. */
7 #include "jit/shared/CodeGenerator-shared-inl.h"
9 #include "mozilla/DebugOnly.h"
11 #include "jit/IonCaches.h"
12 #include "jit/IonMacroAssembler.h"
13 #include "jit/IonSpewer.h"
14 #include "jit/MIR.h"
15 #include "jit/MIRGenerator.h"
16 #include "jit/ParallelFunctions.h"
17 #include "vm/TraceLogging.h"
19 #include "jit/IonFrames-inl.h"
21 using namespace js;
22 using namespace js::jit;
24 using mozilla::DebugOnly;
26 namespace js {
27 namespace jit {
29 MacroAssembler &
30 CodeGeneratorShared::ensureMasm(MacroAssembler *masmArg)
31 {
32 if (masmArg)
33 return *masmArg;
34 maybeMasm_.construct();
35 return maybeMasm_.ref();
36 }
38 CodeGeneratorShared::CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masmArg)
39 : oolIns(nullptr),
40 maybeMasm_(),
41 masm(ensureMasm(masmArg)),
42 gen(gen),
43 graph(*graph),
44 current(nullptr),
45 snapshots_(),
46 recovers_(),
47 deoptTable_(nullptr),
48 #ifdef DEBUG
49 pushedArgs_(0),
50 #endif
51 lastOsiPointOffset_(0),
52 sps_(&GetIonContext()->runtime->spsProfiler(), &lastPC_),
53 osrEntryOffset_(0),
54 skipArgCheckEntryOffset_(0),
55 frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize())
56 {
57 if (!gen->compilingAsmJS())
58 masm.setInstrumentation(&sps_);
60 // Since asm.js uses the system ABI which does not necessarily use a
61 // regular array where all slots are sizeof(Value), it maintains the max
62 // argument stack depth separately.
63 if (gen->compilingAsmJS()) {
64 JS_ASSERT(graph->argumentSlotCount() == 0);
65 frameDepth_ += gen->maxAsmJSStackArgBytes();
67 // An MAsmJSCall does not align the stack pointer at calls sites but instead
68 // relies on the a priori stack adjustment (in the prologue) on platforms
69 // (like x64) which require the stack to be aligned.
70 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
71 bool forceAlign = true;
72 #else
73 bool forceAlign = false;
74 #endif
75 if (gen->performsAsmJSCall() || forceAlign) {
76 unsigned alignmentAtCall = AlignmentMidPrologue + frameDepth_;
77 if (unsigned rem = alignmentAtCall % StackAlignment)
78 frameDepth_ += StackAlignment - rem;
79 }
81 // FrameSizeClass is only used for bailing, which cannot happen in
82 // asm.js code.
83 frameClass_ = FrameSizeClass::None();
84 } else {
85 frameClass_ = FrameSizeClass::FromDepth(frameDepth_);
86 }
87 }
89 bool
90 CodeGeneratorShared::generateOutOfLineCode()
91 {
92 for (size_t i = 0; i < outOfLineCode_.length(); i++) {
93 if (!gen->alloc().ensureBallast())
94 return false;
95 masm.setFramePushed(outOfLineCode_[i]->framePushed());
96 lastPC_ = outOfLineCode_[i]->pc();
97 if (!sps_.prepareForOOL())
98 return false;
99 sps_.setPushed(outOfLineCode_[i]->script());
100 outOfLineCode_[i]->bind(&masm);
102 oolIns = outOfLineCode_[i];
103 if (!outOfLineCode_[i]->generate(this))
104 return false;
105 sps_.finishOOL();
106 }
107 oolIns = nullptr;
109 return true;
110 }
112 bool
113 CodeGeneratorShared::addOutOfLineCode(OutOfLineCode *code)
114 {
115 code->setFramePushed(masm.framePushed());
116 // If an OOL instruction adds another OOL instruction, then use the original
117 // instruction's script/pc instead of the basic block's that we're on
118 // because they're probably not relevant any more.
119 if (oolIns)
120 code->setSource(oolIns->script(), oolIns->pc());
121 else
122 code->setSource(current ? current->mir()->info().script() : nullptr, lastPC_);
123 JS_ASSERT_IF(code->script(), code->script()->containsPC(code->pc()));
124 return outOfLineCode_.append(code);
125 }
127 // see OffsetOfFrameSlot
128 static inline int32_t
129 ToStackIndex(LAllocation *a)
130 {
131 if (a->isStackSlot()) {
132 JS_ASSERT(a->toStackSlot()->slot() >= 1);
133 return a->toStackSlot()->slot();
134 }
135 JS_ASSERT(-int32_t(sizeof(IonJSFrameLayout)) <= a->toArgument()->index());
136 return -int32_t(sizeof(IonJSFrameLayout) + a->toArgument()->index());
137 }
139 bool
140 CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resumePoint,
141 uint32_t *startIndex)
142 {
143 IonSpew(IonSpew_Codegen, "Encoding %u of resume point %p's operands starting from %u",
144 resumePoint->numOperands(), (void *) resumePoint, *startIndex);
145 for (uint32_t allocno = 0, e = resumePoint->numOperands(); allocno < e; allocno++) {
146 uint32_t i = allocno + *startIndex;
147 MDefinition *mir = resumePoint->getOperand(allocno);
149 if (mir->isBox())
150 mir = mir->toBox()->getOperand(0);
152 MIRType type = mir->isUnused()
153 ? MIRType_MagicOptimizedOut
154 : mir->type();
156 RValueAllocation alloc;
158 switch (type) {
159 case MIRType_Undefined:
160 alloc = RValueAllocation::Undefined();
161 break;
162 case MIRType_Null:
163 alloc = RValueAllocation::Null();
164 break;
165 case MIRType_Int32:
166 case MIRType_String:
167 case MIRType_Object:
168 case MIRType_Boolean:
169 case MIRType_Double:
170 case MIRType_Float32:
171 {
172 LAllocation *payload = snapshot->payloadOfSlot(i);
173 JSValueType valueType = ValueTypeFromMIRType(type);
174 if (payload->isMemory()) {
175 if (type == MIRType_Float32)
176 alloc = RValueAllocation::Float32(ToStackIndex(payload));
177 else
178 alloc = RValueAllocation::Typed(valueType, ToStackIndex(payload));
179 } else if (payload->isGeneralReg()) {
180 alloc = RValueAllocation::Typed(valueType, ToRegister(payload));
181 } else if (payload->isFloatReg()) {
182 FloatRegister reg = ToFloatRegister(payload);
183 if (type == MIRType_Float32)
184 alloc = RValueAllocation::Float32(reg);
185 else
186 alloc = RValueAllocation::Double(reg);
187 } else {
188 MConstant *constant = mir->toConstant();
189 uint32_t index;
190 if (!graph.addConstantToPool(constant->value(), &index))
191 return false;
192 alloc = RValueAllocation::ConstantPool(index);
193 }
194 break;
195 }
196 case MIRType_MagicOptimizedArguments:
197 case MIRType_MagicOptimizedOut:
198 {
199 uint32_t index;
200 JSWhyMagic why = (type == MIRType_MagicOptimizedArguments
201 ? JS_OPTIMIZED_ARGUMENTS
202 : JS_OPTIMIZED_OUT);
203 Value v = MagicValue(why);
204 if (!graph.addConstantToPool(v, &index))
205 return false;
206 alloc = RValueAllocation::ConstantPool(index);
207 break;
208 }
209 default:
210 {
211 JS_ASSERT(mir->type() == MIRType_Value);
212 LAllocation *payload = snapshot->payloadOfSlot(i);
213 #ifdef JS_NUNBOX32
214 LAllocation *type = snapshot->typeOfSlot(i);
215 if (type->isRegister()) {
216 if (payload->isRegister())
217 alloc = RValueAllocation::Untyped(ToRegister(type), ToRegister(payload));
218 else
219 alloc = RValueAllocation::Untyped(ToRegister(type), ToStackIndex(payload));
220 } else {
221 if (payload->isRegister())
222 alloc = RValueAllocation::Untyped(ToStackIndex(type), ToRegister(payload));
223 else
224 alloc = RValueAllocation::Untyped(ToStackIndex(type), ToStackIndex(payload));
225 }
226 #elif JS_PUNBOX64
227 if (payload->isRegister())
228 alloc = RValueAllocation::Untyped(ToRegister(payload));
229 else
230 alloc = RValueAllocation::Untyped(ToStackIndex(payload));
231 #endif
232 break;
233 }
234 }
236 snapshots_.add(alloc);
237 }
239 *startIndex += resumePoint->numOperands();
240 return true;
241 }
243 bool
244 CodeGeneratorShared::encode(LRecoverInfo *recover)
245 {
246 if (recover->recoverOffset() != INVALID_RECOVER_OFFSET)
247 return true;
249 uint32_t frameCount = recover->mir()->frameCount();
250 IonSpew(IonSpew_Snapshots, "Encoding LRecoverInfo %p (frameCount %u)",
251 (void *)recover, frameCount);
253 MResumePoint::Mode mode = recover->mir()->mode();
254 JS_ASSERT(mode != MResumePoint::Outer);
255 bool resumeAfter = (mode == MResumePoint::ResumeAfter);
257 RecoverOffset offset = recovers_.startRecover(frameCount, resumeAfter);
259 for (MResumePoint **it = recover->begin(), **end = recover->end();
260 it != end;
261 ++it)
262 {
263 if (!recovers_.writeFrame(*it))
264 return false;
265 }
267 recovers_.endRecover();
268 recover->setRecoverOffset(offset);
269 return !recovers_.oom();
270 }
272 bool
273 CodeGeneratorShared::encode(LSnapshot *snapshot)
274 {
275 if (snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET)
276 return true;
278 LRecoverInfo *recoverInfo = snapshot->recoverInfo();
279 if (!encode(recoverInfo))
280 return false;
282 RecoverOffset recoverOffset = recoverInfo->recoverOffset();
283 MOZ_ASSERT(recoverOffset != INVALID_RECOVER_OFFSET);
285 IonSpew(IonSpew_Snapshots, "Encoding LSnapshot %p (LRecover %p)",
286 (void *)snapshot, (void*) recoverInfo);
288 SnapshotOffset offset = snapshots_.startSnapshot(recoverOffset, snapshot->bailoutKind());
290 #ifdef TRACK_SNAPSHOTS
291 uint32_t pcOpcode = 0;
292 uint32_t lirOpcode = 0;
293 uint32_t lirId = 0;
294 uint32_t mirOpcode = 0;
295 uint32_t mirId = 0;
297 if (LInstruction *ins = instruction()) {
298 lirOpcode = ins->op();
299 lirId = ins->id();
300 if (ins->mirRaw()) {
301 mirOpcode = ins->mirRaw()->op();
302 mirId = ins->mirRaw()->id();
303 if (ins->mirRaw()->trackedPc())
304 pcOpcode = *ins->mirRaw()->trackedPc();
305 }
306 }
307 snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId);
308 #endif
310 uint32_t startIndex = 0;
311 for (MResumePoint **it = recoverInfo->begin(), **end = recoverInfo->end();
312 it != end;
313 ++it)
314 {
315 MResumePoint *mir = *it;
316 if (!encodeAllocations(snapshot, mir, &startIndex))
317 return false;
318 }
320 MOZ_ASSERT(snapshots_.allocWritten() == snapshot->numSlots());
321 snapshots_.endSnapshot();
322 snapshot->setSnapshotOffset(offset);
323 return !snapshots_.oom();
324 }
326 bool
327 CodeGeneratorShared::assignBailoutId(LSnapshot *snapshot)
328 {
329 JS_ASSERT(snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET);
331 // Can we not use bailout tables at all?
332 if (!deoptTable_)
333 return false;
335 JS_ASSERT(frameClass_ != FrameSizeClass::None());
337 if (snapshot->bailoutId() != INVALID_BAILOUT_ID)
338 return true;
340 // Is the bailout table full?
341 if (bailouts_.length() >= BAILOUT_TABLE_SIZE)
342 return false;
344 unsigned bailoutId = bailouts_.length();
345 snapshot->setBailoutId(bailoutId);
346 IonSpew(IonSpew_Snapshots, "Assigned snapshot bailout id %u", bailoutId);
347 return bailouts_.append(snapshot->snapshotOffset());
348 }
350 void
351 CodeGeneratorShared::encodeSafepoints()
352 {
353 for (SafepointIndex *it = safepointIndices_.begin(), *end = safepointIndices_.end();
354 it != end;
355 ++it)
356 {
357 LSafepoint *safepoint = it->safepoint();
359 if (!safepoint->encoded()) {
360 safepoint->fixupOffset(&masm);
361 safepoints_.encode(safepoint);
362 }
364 it->resolve();
365 }
366 }
368 bool
369 CodeGeneratorShared::markSafepoint(LInstruction *ins)
370 {
371 return markSafepointAt(masm.currentOffset(), ins);
372 }
374 bool
375 CodeGeneratorShared::markSafepointAt(uint32_t offset, LInstruction *ins)
376 {
377 JS_ASSERT_IF(!safepointIndices_.empty(),
378 offset - safepointIndices_.back().displacement() >= sizeof(uint32_t));
379 return safepointIndices_.append(SafepointIndex(offset, ins->safepoint()));
380 }
382 void
383 CodeGeneratorShared::ensureOsiSpace()
384 {
385 // For a refresher, an invalidation point is of the form:
386 // 1: call <target>
387 // 2: ...
388 // 3: <osipoint>
389 //
390 // The four bytes *before* instruction 2 are overwritten with an offset.
391 // Callers must ensure that the instruction itself has enough bytes to
392 // support this.
393 //
394 // The bytes *at* instruction 3 are overwritten with an invalidation jump.
395 // jump. These bytes may be in a completely different IR sequence, but
396 // represent the join point of the call out of the function.
397 //
398 // At points where we want to ensure that invalidation won't corrupt an
399 // important instruction, we make sure to pad with nops.
400 if (masm.currentOffset() - lastOsiPointOffset_ < Assembler::patchWrite_NearCallSize()) {
401 int32_t paddingSize = Assembler::patchWrite_NearCallSize();
402 paddingSize -= masm.currentOffset() - lastOsiPointOffset_;
403 for (int32_t i = 0; i < paddingSize; ++i)
404 masm.nop();
405 }
406 JS_ASSERT(masm.currentOffset() - lastOsiPointOffset_ >= Assembler::patchWrite_NearCallSize());
407 lastOsiPointOffset_ = masm.currentOffset();
408 }
410 bool
411 CodeGeneratorShared::markOsiPoint(LOsiPoint *ins, uint32_t *callPointOffset)
412 {
413 if (!encode(ins->snapshot()))
414 return false;
416 ensureOsiSpace();
418 *callPointOffset = masm.currentOffset();
419 SnapshotOffset so = ins->snapshot()->snapshotOffset();
420 return osiIndices_.append(OsiIndex(*callPointOffset, so));
421 }
423 #ifdef CHECK_OSIPOINT_REGISTERS
424 template <class Op>
425 static void
426 HandleRegisterDump(Op op, MacroAssembler &masm, RegisterSet liveRegs, Register activation,
427 Register scratch)
428 {
429 const size_t baseOffset = JitActivation::offsetOfRegs();
431 // Handle live GPRs.
432 for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); iter++) {
433 Register reg = *iter;
434 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
436 if (reg == activation) {
437 // To use the original value of the activation register (that's
438 // now on top of the stack), we need the scratch register.
439 masm.push(scratch);
440 masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), scratch);
441 op(scratch, dump);
442 masm.pop(scratch);
443 } else {
444 op(reg, dump);
445 }
446 }
448 // Handle live FPRs.
449 for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); iter++) {
450 FloatRegister reg = *iter;
451 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
452 op(reg, dump);
453 }
454 }
456 class StoreOp
457 {
458 MacroAssembler &masm;
460 public:
461 StoreOp(MacroAssembler &masm)
462 : masm(masm)
463 {}
465 void operator()(Register reg, Address dump) {
466 masm.storePtr(reg, dump);
467 }
468 void operator()(FloatRegister reg, Address dump) {
469 masm.storeDouble(reg, dump);
470 }
471 };
473 static void
474 StoreAllLiveRegs(MacroAssembler &masm, RegisterSet liveRegs)
475 {
476 // Store a copy of all live registers before performing the call.
477 // When we reach the OsiPoint, we can use this to check nothing
478 // modified them in the meantime.
480 // Load pointer to the JitActivation in a scratch register.
481 GeneralRegisterSet allRegs(GeneralRegisterSet::All());
482 Register scratch = allRegs.takeAny();
483 masm.push(scratch);
484 masm.loadJitActivation(scratch);
486 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
487 masm.add32(Imm32(1), checkRegs);
489 StoreOp op(masm);
490 HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny());
492 masm.pop(scratch);
493 }
495 class VerifyOp
496 {
497 MacroAssembler &masm;
498 Label *failure_;
500 public:
501 VerifyOp(MacroAssembler &masm, Label *failure)
502 : masm(masm), failure_(failure)
503 {}
505 void operator()(Register reg, Address dump) {
506 masm.branchPtr(Assembler::NotEqual, dump, reg, failure_);
507 }
508 void operator()(FloatRegister reg, Address dump) {
509 masm.loadDouble(dump, ScratchFloatReg);
510 masm.branchDouble(Assembler::DoubleNotEqual, ScratchFloatReg, reg, failure_);
511 }
512 };
514 static void
515 OsiPointRegisterCheckFailed()
516 {
517 // Any live register captured by a safepoint (other than temp registers)
518 // must remain unchanged between the call and the OsiPoint instruction.
519 MOZ_ASSUME_UNREACHABLE("Modified registers between VM call and OsiPoint");
520 }
522 void
523 CodeGeneratorShared::verifyOsiPointRegs(LSafepoint *safepoint)
524 {
525 // Ensure the live registers stored by callVM did not change between
526 // the call and this OsiPoint. Try-catch relies on this invariant.
528 // Load pointer to the JitActivation in a scratch register.
529 GeneralRegisterSet allRegs(GeneralRegisterSet::All());
530 Register scratch = allRegs.takeAny();
531 masm.push(scratch);
532 masm.loadJitActivation(scratch);
534 // If we should not check registers (because the instruction did not call
535 // into the VM, or a GC happened), we're done.
536 Label failure, done;
537 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
538 masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done);
540 // Having more than one VM function call made in one visit function at
541 // runtime is a sec-ciritcal error, because if we conservatively assume that
542 // one of the function call can re-enter Ion, then the invalidation process
543 // will potentially add a call at a random location, by patching the code
544 // before the return address.
545 masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure);
547 // Ignore clobbered registers. Some instructions (like LValueToInt32) modify
548 // temps after calling into the VM. This is fine because no other
549 // instructions (including this OsiPoint) will depend on them. Also
550 // backtracking can also use the same register for an input and an output.
551 // These are marked as clobbered and shouldn't get checked.
552 RegisterSet liveRegs = safepoint->liveRegs();
553 liveRegs = RegisterSet::Intersect(liveRegs, RegisterSet::Not(safepoint->clobberedRegs()));
555 VerifyOp op(masm, &failure);
556 HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny());
558 masm.jump(&done);
560 // Do not profile the callWithABI that occurs below. This is to avoid a
561 // rare corner case that occurs when profiling interacts with itself:
562 //
563 // When slow profiling assertions are turned on, FunctionBoundary ops
564 // (which update the profiler pseudo-stack) may emit a callVM, which
565 // forces them to have an osi point associated with them. The
566 // FunctionBoundary for inline function entry is added to the caller's
567 // graph with a PC from the caller's code, but during codegen it modifies
568 // SPS instrumentation to add the callee as the current top-most script.
569 // When codegen gets to the OSIPoint, and the callWithABI below is
570 // emitted, the codegen thinks that the current frame is the callee, but
571 // the PC it's using from the OSIPoint refers to the caller. This causes
572 // the profiler instrumentation of the callWithABI below to ASSERT, since
573 // the script and pc are mismatched. To avoid this, we simply omit
574 // instrumentation for these callWithABIs.
575 masm.bind(&failure);
576 masm.setupUnalignedABICall(0, scratch);
577 masm.callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, OsiPointRegisterCheckFailed));
578 masm.breakpoint();
580 masm.bind(&done);
581 masm.pop(scratch);
582 }
584 bool
585 CodeGeneratorShared::shouldVerifyOsiPointRegs(LSafepoint *safepoint)
586 {
587 if (!js_JitOptions.checkOsiPointRegisters)
588 return false;
590 if (gen->info().executionMode() != SequentialExecution)
591 return false;
593 if (safepoint->liveRegs().empty(true) && safepoint->liveRegs().empty(false))
594 return false; // No registers to check.
596 return true;
597 }
599 void
600 CodeGeneratorShared::resetOsiPointRegs(LSafepoint *safepoint)
601 {
602 if (!shouldVerifyOsiPointRegs(safepoint))
603 return;
605 // Set checkRegs to 0. If we perform a VM call, the instruction
606 // will set it to 1.
607 GeneralRegisterSet allRegs(GeneralRegisterSet::All());
608 Register scratch = allRegs.takeAny();
609 masm.push(scratch);
610 masm.loadJitActivation(scratch);
611 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
612 masm.store32(Imm32(0), checkRegs);
613 masm.pop(scratch);
614 }
615 #endif
617 // Before doing any call to Cpp, you should ensure that volatile
618 // registers are evicted by the register allocator.
619 bool
620 CodeGeneratorShared::callVM(const VMFunction &fun, LInstruction *ins, const Register *dynStack)
621 {
622 // Different execution modes have different sets of VM functions.
623 JS_ASSERT(fun.executionMode == gen->info().executionMode());
625 // If we're calling a function with an out parameter type of double, make
626 // sure we have an FPU.
627 JS_ASSERT_IF(fun.outParam == Type_Double, GetIonContext()->runtime->jitSupportsFloatingPoint());
629 #ifdef DEBUG
630 if (ins->mirRaw()) {
631 JS_ASSERT(ins->mirRaw()->isInstruction());
632 MInstruction *mir = ins->mirRaw()->toInstruction();
633 JS_ASSERT_IF(mir->isEffectful(), mir->resumePoint());
634 }
635 #endif
637 #ifdef JS_TRACE_LOGGING
638 if (!emitTracelogStartEvent(TraceLogger::VM))
639 return false;
640 #endif
642 // Stack is:
643 // ... frame ...
644 // [args]
645 #ifdef DEBUG
646 JS_ASSERT(pushedArgs_ == fun.explicitArgs);
647 pushedArgs_ = 0;
648 #endif
650 // Get the wrapper of the VM function.
651 JitCode *wrapper = gen->jitRuntime()->getVMWrapper(fun);
652 if (!wrapper)
653 return false;
655 #ifdef CHECK_OSIPOINT_REGISTERS
656 if (shouldVerifyOsiPointRegs(ins->safepoint()))
657 StoreAllLiveRegs(masm, ins->safepoint()->liveRegs());
658 #endif
660 // Call the wrapper function. The wrapper is in charge to unwind the stack
661 // when returning from the call. Failures are handled with exceptions based
662 // on the return value of the C functions. To guard the outcome of the
663 // returned value, use another LIR instruction.
664 uint32_t callOffset;
665 if (dynStack)
666 callOffset = masm.callWithExitFrame(wrapper, *dynStack);
667 else
668 callOffset = masm.callWithExitFrame(wrapper);
670 if (!markSafepointAt(callOffset, ins))
671 return false;
673 // Remove rest of the frame left on the stack. We remove the return address
674 // which is implicitly poped when returning.
675 int framePop = sizeof(IonExitFrameLayout) - sizeof(void*);
677 // Pop arguments from framePushed.
678 masm.implicitPop(fun.explicitStackSlots() * sizeof(void *) + framePop);
679 // Stack is:
680 // ... frame ...
682 #ifdef JS_TRACE_LOGGING
683 if (!emitTracelogStopEvent(TraceLogger::VM))
684 return false;
685 #endif
687 return true;
688 }
690 class OutOfLineTruncateSlow : public OutOfLineCodeBase<CodeGeneratorShared>
691 {
692 FloatRegister src_;
693 Register dest_;
694 bool needFloat32Conversion_;
696 public:
697 OutOfLineTruncateSlow(FloatRegister src, Register dest, bool needFloat32Conversion = false)
698 : src_(src), dest_(dest), needFloat32Conversion_(needFloat32Conversion)
699 { }
701 bool accept(CodeGeneratorShared *codegen) {
702 return codegen->visitOutOfLineTruncateSlow(this);
703 }
704 FloatRegister src() const {
705 return src_;
706 }
707 Register dest() const {
708 return dest_;
709 }
710 bool needFloat32Conversion() const {
711 return needFloat32Conversion_;
712 }
714 };
716 OutOfLineCode *
717 CodeGeneratorShared::oolTruncateDouble(const FloatRegister &src, const Register &dest)
718 {
719 OutOfLineTruncateSlow *ool = new(alloc()) OutOfLineTruncateSlow(src, dest);
720 if (!addOutOfLineCode(ool))
721 return nullptr;
722 return ool;
723 }
725 bool
726 CodeGeneratorShared::emitTruncateDouble(const FloatRegister &src, const Register &dest)
727 {
728 OutOfLineCode *ool = oolTruncateDouble(src, dest);
729 if (!ool)
730 return false;
732 masm.branchTruncateDouble(src, dest, ool->entry());
733 masm.bind(ool->rejoin());
734 return true;
735 }
737 bool
738 CodeGeneratorShared::emitTruncateFloat32(const FloatRegister &src, const Register &dest)
739 {
740 OutOfLineTruncateSlow *ool = new(alloc()) OutOfLineTruncateSlow(src, dest, true);
741 if (!addOutOfLineCode(ool))
742 return false;
744 masm.branchTruncateFloat32(src, dest, ool->entry());
745 masm.bind(ool->rejoin());
746 return true;
747 }
749 bool
750 CodeGeneratorShared::visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool)
751 {
752 FloatRegister src = ool->src();
753 Register dest = ool->dest();
755 saveVolatile(dest);
757 if (ool->needFloat32Conversion()) {
758 masm.push(src);
759 masm.convertFloat32ToDouble(src, src);
760 }
762 masm.setupUnalignedABICall(1, dest);
763 masm.passABIArg(src, MoveOp::DOUBLE);
764 if (gen->compilingAsmJS())
765 masm.callWithABI(AsmJSImm_ToInt32);
766 else
767 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32));
768 masm.storeCallResult(dest);
770 if (ool->needFloat32Conversion())
771 masm.pop(src);
773 restoreVolatile(dest);
775 masm.jump(ool->rejoin());
776 return true;
777 }
779 bool
780 CodeGeneratorShared::omitOverRecursedCheck() const
781 {
782 // If the current function makes no calls (which means it isn't recursive)
783 // and it uses only a small amount of stack space, it doesn't need a
784 // stack overflow check. Note that the actual number here is somewhat
785 // arbitrary, and codegen actually uses small bounded amounts of
786 // additional stack space in some cases too.
787 return frameSize() < 64 && !gen->performsCall();
788 }
790 void
791 CodeGeneratorShared::emitPreBarrier(Register base, const LAllocation *index, MIRType type)
792 {
793 if (index->isConstant()) {
794 Address address(base, ToInt32(index) * sizeof(Value));
795 masm.patchableCallPreBarrier(address, type);
796 } else {
797 BaseIndex address(base, ToRegister(index), TimesEight);
798 masm.patchableCallPreBarrier(address, type);
799 }
800 }
802 void
803 CodeGeneratorShared::emitPreBarrier(Address address, MIRType type)
804 {
805 masm.patchableCallPreBarrier(address, type);
806 }
808 void
809 CodeGeneratorShared::dropArguments(unsigned argc)
810 {
811 pushedArgumentSlots_.shrinkBy(argc);
812 }
814 bool
815 CodeGeneratorShared::markArgumentSlots(LSafepoint *safepoint)
816 {
817 for (size_t i = 0; i < pushedArgumentSlots_.length(); i++) {
818 if (!safepoint->addValueSlot(pushedArgumentSlots_[i]))
819 return false;
820 }
821 return true;
822 }
824 OutOfLineAbortPar *
825 CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, MBasicBlock *basicBlock,
826 jsbytecode *bytecode)
827 {
828 OutOfLineAbortPar *ool = new(alloc()) OutOfLineAbortPar(cause, basicBlock, bytecode);
829 if (!ool || !addOutOfLineCode(ool))
830 return nullptr;
831 return ool;
832 }
834 OutOfLineAbortPar *
835 CodeGeneratorShared::oolAbortPar(ParallelBailoutCause cause, LInstruction *lir)
836 {
837 MDefinition *mir = lir->mirRaw();
838 MBasicBlock *block = mir->block();
839 jsbytecode *pc = mir->trackedPc();
840 if (!pc) {
841 if (lir->snapshot())
842 pc = lir->snapshot()->mir()->pc();
843 else
844 pc = block->pc();
845 }
846 return oolAbortPar(cause, block, pc);
847 }
849 OutOfLinePropagateAbortPar *
850 CodeGeneratorShared::oolPropagateAbortPar(LInstruction *lir)
851 {
852 OutOfLinePropagateAbortPar *ool = new(alloc()) OutOfLinePropagateAbortPar(lir);
853 if (!ool || !addOutOfLineCode(ool))
854 return nullptr;
855 return ool;
856 }
858 bool
859 OutOfLineAbortPar::generate(CodeGeneratorShared *codegen)
860 {
861 codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar");
862 return codegen->visitOutOfLineAbortPar(this);
863 }
865 bool
866 OutOfLinePropagateAbortPar::generate(CodeGeneratorShared *codegen)
867 {
868 codegen->callTraceLIR(0xDEADBEEF, nullptr, "AbortPar");
869 return codegen->visitOutOfLinePropagateAbortPar(this);
870 }
872 bool
873 CodeGeneratorShared::callTraceLIR(uint32_t blockIndex, LInstruction *lir,
874 const char *bailoutName)
875 {
876 JS_ASSERT_IF(!lir, bailoutName);
878 if (!IonSpewEnabled(IonSpew_Trace))
879 return true;
881 uint32_t execMode = (uint32_t) gen->info().executionMode();
882 uint32_t lirIndex;
883 const char *lirOpName;
884 const char *mirOpName;
885 JSScript *script;
886 jsbytecode *pc;
888 masm.PushRegsInMask(RegisterSet::Volatile());
889 masm.reserveStack(sizeof(IonLIRTraceData));
891 // This first move is here so that when you scan the disassembly,
892 // you can easily pick out where each instruction begins. The
893 // next few items indicate to you the Basic Block / LIR.
894 masm.move32(Imm32(0xDEADBEEF), CallTempReg0);
896 if (lir) {
897 lirIndex = lir->id();
898 lirOpName = lir->opName();
899 if (MDefinition *mir = lir->mirRaw()) {
900 mirOpName = mir->opName();
901 script = mir->block()->info().script();
902 pc = mir->trackedPc();
903 } else {
904 mirOpName = nullptr;
905 script = nullptr;
906 pc = nullptr;
907 }
908 } else {
909 blockIndex = lirIndex = 0xDEADBEEF;
910 lirOpName = mirOpName = bailoutName;
911 script = nullptr;
912 pc = nullptr;
913 }
915 masm.store32(Imm32(blockIndex),
916 Address(StackPointer, offsetof(IonLIRTraceData, blockIndex)));
917 masm.store32(Imm32(lirIndex),
918 Address(StackPointer, offsetof(IonLIRTraceData, lirIndex)));
919 masm.store32(Imm32(execMode),
920 Address(StackPointer, offsetof(IonLIRTraceData, execModeInt)));
921 masm.storePtr(ImmPtr(lirOpName),
922 Address(StackPointer, offsetof(IonLIRTraceData, lirOpName)));
923 masm.storePtr(ImmPtr(mirOpName),
924 Address(StackPointer, offsetof(IonLIRTraceData, mirOpName)));
925 masm.storePtr(ImmGCPtr(script),
926 Address(StackPointer, offsetof(IonLIRTraceData, script)));
927 masm.storePtr(ImmPtr(pc),
928 Address(StackPointer, offsetof(IonLIRTraceData, pc)));
930 masm.movePtr(StackPointer, CallTempReg0);
931 masm.setupUnalignedABICall(1, CallTempReg1);
932 masm.passABIArg(CallTempReg0);
933 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, TraceLIR));
935 masm.freeStack(sizeof(IonLIRTraceData));
936 masm.PopRegsInMask(RegisterSet::Volatile());
938 return true;
939 }
941 typedef bool (*InterruptCheckFn)(JSContext *);
942 const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
944 Label *
945 CodeGeneratorShared::labelForBackedgeWithImplicitCheck(MBasicBlock *mir)
946 {
947 // If this is a loop backedge to a loop header with an implicit interrupt
948 // check, use a patchable jump. Skip this search if compiling without a
949 // script for asm.js, as there will be no interrupt check instruction.
950 // Due to critical edge unsplitting there may no longer be unique loop
951 // backedges, so just look for any edge going to an earlier block in RPO.
952 if (!gen->compilingAsmJS() && mir->isLoopHeader() && mir->id() <= current->mir()->id()) {
953 for (LInstructionIterator iter = mir->lir()->begin(); iter != mir->lir()->end(); iter++) {
954 if (iter->isLabel() || iter->isMoveGroup()) {
955 // Continue searching for an interrupt check.
956 } else if (iter->isInterruptCheckImplicit()) {
957 return iter->toInterruptCheckImplicit()->oolEntry();
958 } else {
959 // The interrupt check should be the first instruction in the
960 // loop header other than the initial label and move groups.
961 JS_ASSERT(iter->isInterruptCheck() || iter->isInterruptCheckPar());
962 return nullptr;
963 }
964 }
965 }
967 return nullptr;
968 }
970 void
971 CodeGeneratorShared::jumpToBlock(MBasicBlock *mir)
972 {
973 // No jump necessary if we can fall through to the next block.
974 if (isNextBlock(mir->lir()))
975 return;
977 if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
978 // Note: the backedge is initially a jump to the next instruction.
979 // It will be patched to the target block's label during link().
980 RepatchLabel rejoin;
981 CodeOffsetJump backedge = masm.jumpWithPatch(&rejoin);
982 masm.bind(&rejoin);
984 masm.propagateOOM(patchableBackedges_.append(PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry)));
985 } else {
986 masm.jump(mir->lir()->label());
987 }
988 }
990 // This function is not used for MIPS. MIPS has branchToBlock.
991 #ifndef JS_CODEGEN_MIPS
992 void
993 CodeGeneratorShared::jumpToBlock(MBasicBlock *mir, Assembler::Condition cond)
994 {
995 if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
996 // Note: the backedge is initially a jump to the next instruction.
997 // It will be patched to the target block's label during link().
998 RepatchLabel rejoin;
999 CodeOffsetJump backedge = masm.jumpWithPatch(&rejoin, cond);
1000 masm.bind(&rejoin);
1002 masm.propagateOOM(patchableBackedges_.append(PatchableBackedgeInfo(backedge, mir->lir()->label(), oolEntry)));
1003 } else {
1004 masm.j(cond, mir->lir()->label());
1005 }
1006 }
1007 #endif
1009 size_t
1010 CodeGeneratorShared::addCacheLocations(const CacheLocationList &locs, size_t *numLocs)
1011 {
1012 size_t firstIndex = runtimeData_.length();
1013 size_t numLocations = 0;
1014 for (CacheLocationList::iterator iter = locs.begin(); iter != locs.end(); iter++) {
1015 // allocateData() ensures that sizeof(CacheLocation) is word-aligned.
1016 // If this changes, we will need to pad to ensure alignment.
1017 size_t curIndex = allocateData(sizeof(CacheLocation));
1018 new (&runtimeData_[curIndex]) CacheLocation(iter->pc, iter->script);
1019 numLocations++;
1020 }
1021 JS_ASSERT(numLocations != 0);
1022 *numLocs = numLocations;
1023 return firstIndex;
1024 }
1026 ReciprocalMulConstants
1027 CodeGeneratorShared::computeDivisionConstants(int d) {
1028 // In what follows, d is positive and is not a power of 2.
1029 JS_ASSERT(d > 0 && (d & (d - 1)) != 0);
1031 // Speeding up division by non power-of-2 constants is possible by
1032 // calculating, during compilation, a value M such that high-order
1033 // bits of M*n correspond to the result of the division. Formally,
1034 // we compute values 0 <= M < 2^32 and 0 <= s < 31 such that
1035 // (M * n) >> (32 + s) = floor(n/d) if n >= 0
1036 // (M * n) >> (32 + s) = ceil(n/d) - 1 if n < 0.
1037 // The original presentation of this technique appears in Hacker's
1038 // Delight, a book by Henry S. Warren, Jr.. A proof of correctness
1039 // for our version follows.
1041 // Define p = 32 + s, M = ceil(2^p/d), and assume that s satisfies
1042 // M - 2^p/d <= 2^(s+1)/d. (1)
1043 // (Observe that s = FloorLog32(d) satisfies this, because in this
1044 // case d <= 2^(s+1) and so the RHS of (1) is at least one). Then,
1045 //
1046 // a) If s <= FloorLog32(d), then M <= 2^32 - 1.
1047 // Proof: Indeed, M is monotone in s and, for s = FloorLog32(d),
1048 // the inequalities 2^31 > d >= 2^s + 1 readily imply
1049 // 2^p / d = 2^p/(d - 1) * (d - 1)/d
1050 // <= 2^32 * (1 - 1/d) < 2 * (2^31 - 1) = 2^32 - 2.
1051 // The claim follows by applying the ceiling function.
1052 //
1053 // b) For any 0 <= n < 2^31, floor(Mn/2^p) = floor(n/d).
1054 // Proof: Put x = floor(Mn/2^p); it's the unique integer for which
1055 // Mn/2^p - 1 < x <= Mn/2^p. (2)
1056 // Using M >= 2^p/d on the LHS and (1) on the RHS, we get
1057 // n/d - 1 < x <= n/d + n/(2^31 d) < n/d + 1/d.
1058 // Since x is an integer, it's not in the interval (n/d, (n+1)/d),
1059 // and so n/d - 1 < x <= n/d, which implies x = floor(n/d).
1060 //
1061 // c) For any -2^31 <= n < 0, floor(Mn/2^p) + 1 = ceil(n/d).
1062 // Proof: The proof is similar. Equation (2) holds as above. Using
1063 // M > 2^p/d (d isn't a power of 2) on the RHS and (1) on the LHS,
1064 // n/d + n/(2^31 d) - 1 < x < n/d.
1065 // Using n >= -2^31 and summing 1,
1066 // n/d - 1/d < x + 1 < n/d + 1.
1067 // Since x + 1 is an integer, this implies n/d <= x + 1 < n/d + 1.
1068 // In other words, x + 1 = ceil(n/d).
1069 //
1070 // Condition (1) isn't necessary for the existence of M and s with
1071 // the properties above. Hacker's Delight provides a slightly less
1072 // restrictive condition when d >= 196611, at the cost of a 3-page
1073 // proof of correctness.
1075 // Note that, since d*M - 2^p = d - (2^p)%d, (1) can be written as
1076 // 2^(s+1) >= d - (2^p)%d.
1077 // We now compute the least s with this property...
1079 int32_t shift = 0;
1080 while ((int64_t(1) << (shift+1)) + (int64_t(1) << (shift+32)) % d < d)
1081 shift++;
1083 // ...and the corresponding M. This may not fit in a signed 32-bit
1084 // integer; we will compute (M - 2^32) * n + (2^32 * n) instead of
1085 // M * n if this is the case (cf. item (a) above).
1086 ReciprocalMulConstants rmc;
1087 rmc.multiplier = int32_t((int64_t(1) << (shift+32))/d + 1);
1088 rmc.shiftAmount = shift;
1090 return rmc;
1091 }
1094 #ifdef JS_TRACE_LOGGING
1096 bool
1097 CodeGeneratorShared::emitTracelogScript(bool isStart)
1098 {
1099 RegisterSet regs = RegisterSet::Volatile();
1100 Register logger = regs.takeGeneral();
1101 Register script = regs.takeGeneral();
1103 masm.Push(logger);
1104 masm.Push(script);
1106 CodeOffsetLabel patchLogger = masm.movWithPatch(ImmPtr(nullptr), logger);
1107 if (!patchableTraceLoggers_.append(patchLogger))
1108 return false;
1110 CodeOffsetLabel patchScript = masm.movWithPatch(ImmWord(0), script);
1111 if (!patchableTLScripts_.append(patchScript))
1112 return false;
1114 if (isStart)
1115 masm.tracelogStart(logger, script);
1116 else
1117 masm.tracelogStop(logger, script);
1119 masm.Pop(script);
1120 masm.Pop(logger);
1121 return true;
1122 }
1124 bool
1125 CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId)
1126 {
1127 if (!TraceLogTextIdEnabled(textId))
1128 return true;
1130 RegisterSet regs = RegisterSet::Volatile();
1131 Register logger = regs.takeGeneral();
1133 masm.Push(logger);
1135 CodeOffsetLabel patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger);
1136 if (!patchableTraceLoggers_.append(patchLocation))
1137 return false;
1139 if (isStart) {
1140 masm.tracelogStart(logger, textId);
1141 } else {
1142 #ifdef DEBUG
1143 masm.tracelogStop(logger, textId);
1144 #else
1145 masm.tracelogStop(logger);
1146 #endif
1147 }
1149 masm.Pop(logger);
1150 return true;
1151 }
1152 #endif
1154 } // namespace jit
1155 } // namespace js