js/src/jit/mips/CodeGenerator-mips.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:86db83b16eef
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/mips/CodeGenerator-mips.h"
8
9 #include "mozilla/MathAlgorithms.h"
10
11 #include "jscntxt.h"
12 #include "jscompartment.h"
13 #include "jsnum.h"
14
15 #include "jit/CodeGenerator.h"
16 #include "jit/IonFrames.h"
17 #include "jit/JitCompartment.h"
18 #include "jit/MIR.h"
19 #include "jit/MIRGraph.h"
20 #include "vm/Shape.h"
21
22 #include "jsscriptinlines.h"
23
24 #include "jit/shared/CodeGenerator-shared-inl.h"
25
26 using namespace js;
27 using namespace js::jit;
28
29 using mozilla::FloorLog2;
30 using mozilla::NegativeInfinity;
31 using JS::GenericNaN;
32
33 // shared
34 CodeGeneratorMIPS::CodeGeneratorMIPS(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
35 : CodeGeneratorShared(gen, graph, masm)
36 {
37 }
38
39 bool
40 CodeGeneratorMIPS::generatePrologue()
41 {
42 if (gen->compilingAsmJS()) {
43 masm.Push(ra);
44 // Note that this automatically sets MacroAssembler::framePushed().
45 masm.reserveStack(frameDepth_);
46 } else {
47 // Note that this automatically sets MacroAssembler::framePushed().
48 masm.reserveStack(frameSize());
49 masm.checkStackAlignment();
50 }
51
52 return true;
53 }
54
55 bool
56 CodeGeneratorMIPS::generateEpilogue()
57 {
58 masm.bind(&returnLabel_);
59 #if JS_TRACE_LOGGING
60 masm.tracelogStop();
61 #endif
62 if (gen->compilingAsmJS()) {
63 // Pop the stack we allocated at the start of the function.
64 masm.freeStack(frameDepth_);
65 masm.Pop(ra);
66 masm.abiret();
67 MOZ_ASSERT(masm.framePushed() == 0);
68 } else {
69 // Pop the stack we allocated at the start of the function.
70 masm.freeStack(frameSize());
71 MOZ_ASSERT(masm.framePushed() == 0);
72 masm.ret();
73 }
74 return true;
75 }
76
77 void
78 CodeGeneratorMIPS::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
79 MBasicBlock *mir, Assembler::DoubleCondition cond)
80 {
81 Label *label = mir->lir()->label();
82 if (Label *oolEntry = labelForBackedgeWithImplicitCheck(mir)) {
83 // Note: the backedge is initially a jump to the next instruction.
84 // It will be patched to the target block's label during link().
85 RepatchLabel rejoin;
86
87 CodeOffsetJump backedge;
88 Label skip;
89 if (fmt == Assembler::DoubleFloat)
90 masm.ma_bc1d(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump);
91 else
92 masm.ma_bc1s(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump);
93
94 backedge = masm.jumpWithPatch(&rejoin);
95 masm.bind(&rejoin);
96 masm.bind(&skip);
97
98 if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry)))
99 MOZ_CRASH();
100 } else {
101 if (fmt == Assembler::DoubleFloat)
102 masm.branchDouble(cond, lhs, rhs, mir->lir()->label());
103 else
104 masm.branchFloat(cond, lhs, rhs, mir->lir()->label());
105 }
106 }
107
108 bool
109 OutOfLineBailout::accept(CodeGeneratorMIPS *codegen)
110 {
111 return codegen->visitOutOfLineBailout(this);
112 }
113
114 bool
115 CodeGeneratorMIPS::visitTestIAndBranch(LTestIAndBranch *test)
116 {
117 const LAllocation *opd = test->getOperand(0);
118 MBasicBlock *ifTrue = test->ifTrue();
119 MBasicBlock *ifFalse = test->ifFalse();
120
121 emitBranch(ToRegister(opd), Imm32(0), Assembler::NonZero, ifTrue, ifFalse);
122 return true;
123 }
124
125 bool
126 CodeGeneratorMIPS::visitCompare(LCompare *comp)
127 {
128 Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop());
129 const LAllocation *left = comp->getOperand(0);
130 const LAllocation *right = comp->getOperand(1);
131 const LDefinition *def = comp->getDef(0);
132
133 if (right->isConstant())
134 masm.cmp32Set(cond, ToRegister(left), Imm32(ToInt32(right)), ToRegister(def));
135 else if (right->isGeneralReg())
136 masm.cmp32Set(cond, ToRegister(left), ToRegister(right), ToRegister(def));
137 else
138 masm.cmp32Set(cond, ToRegister(left), ToAddress(right), ToRegister(def));
139
140 return true;
141 }
142
143 bool
144 CodeGeneratorMIPS::visitCompareAndBranch(LCompareAndBranch *comp)
145 {
146 Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop());
147 if (comp->right()->isConstant()) {
148 emitBranch(ToRegister(comp->left()), Imm32(ToInt32(comp->right())), cond,
149 comp->ifTrue(), comp->ifFalse());
150 } else if (comp->right()->isGeneralReg()) {
151 emitBranch(ToRegister(comp->left()), ToRegister(comp->right()), cond,
152 comp->ifTrue(), comp->ifFalse());
153 } else {
154 emitBranch(ToRegister(comp->left()), ToAddress(comp->right()), cond,
155 comp->ifTrue(), comp->ifFalse());
156 }
157
158 return true;
159 }
160
161 bool
162 CodeGeneratorMIPS::generateOutOfLineCode()
163 {
164 if (!CodeGeneratorShared::generateOutOfLineCode())
165 return false;
166
167 if (deoptLabel_.used()) {
168 // All non-table-based bailouts will go here.
169 masm.bind(&deoptLabel_);
170
171 // Push the frame size, so the handler can recover the IonScript.
172 // Frame size is stored in 'ra' and pushed by GenerateBailoutThunk
173 // We have to use 'ra' because generateBailoutTable will implicitly do
174 // the same.
175 masm.move32(Imm32(frameSize()), ra);
176
177 JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler();
178
179 masm.branch(handler);
180 }
181
182 return true;
183 }
184
185 bool
186 CodeGeneratorMIPS::bailoutFrom(Label *label, LSnapshot *snapshot)
187 {
188 if (masm.bailed())
189 return false;
190 MOZ_ASSERT(label->used());
191 MOZ_ASSERT(!label->bound());
192
193 CompileInfo &info = snapshot->mir()->block()->info();
194 switch (info.executionMode()) {
195 case ParallelExecution: {
196 // in parallel mode, make no attempt to recover, just signal an error.
197 OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
198 snapshot->mir()->block(),
199 snapshot->mir()->pc());
200 masm.retarget(label, ool->entry());
201 return true;
202 }
203 case SequentialExecution:
204 break;
205 default:
206 MOZ_ASSUME_UNREACHABLE("No such execution mode");
207 }
208
209 if (!encode(snapshot))
210 return false;
211
212 // Though the assembler doesn't track all frame pushes, at least make sure
213 // the known value makes sense. We can't use bailout tables if the stack
214 // isn't properly aligned to the static frame size.
215 MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None(),
216 frameClass_.frameSize() == masm.framePushed());
217
218 // We don't use table bailouts because retargeting is easier this way.
219 OutOfLineBailout *ool = new(alloc()) OutOfLineBailout(snapshot, masm.framePushed());
220 if (!addOutOfLineCode(ool)) {
221 return false;
222 }
223
224 masm.retarget(label, ool->entry());
225
226 return true;
227 }
228
229 bool
230 CodeGeneratorMIPS::bailout(LSnapshot *snapshot)
231 {
232 Label label;
233 masm.jump(&label);
234 return bailoutFrom(&label, snapshot);
235 }
236
237 bool
238 CodeGeneratorMIPS::visitOutOfLineBailout(OutOfLineBailout *ool)
239 {
240 // Push snapshotOffset and make sure stack is aligned.
241 masm.subPtr(Imm32(2 * sizeof(void *)), StackPointer);
242 masm.storePtr(ImmWord(ool->snapshot()->snapshotOffset()), Address(StackPointer, 0));
243
244 masm.jump(&deoptLabel_);
245 return true;
246 }
247
248 bool
249 CodeGeneratorMIPS::visitMinMaxD(LMinMaxD *ins)
250 {
251 FloatRegister first = ToFloatRegister(ins->first());
252 FloatRegister second = ToFloatRegister(ins->second());
253 FloatRegister output = ToFloatRegister(ins->output());
254
255 MOZ_ASSERT(first == output);
256
257 Assembler::DoubleCondition cond = ins->mir()->isMax()
258 ? Assembler::DoubleLessThanOrEqual
259 : Assembler::DoubleGreaterThanOrEqual;
260 Label nan, equal, returnSecond, done;
261
262 // First or second is NaN, result is NaN.
263 masm.ma_bc1d(first, second, &nan, Assembler::DoubleUnordered, ShortJump);
264 // Make sure we handle -0 and 0 right.
265 masm.ma_bc1d(first, second, &equal, Assembler::DoubleEqual, ShortJump);
266 masm.ma_bc1d(first, second, &returnSecond, cond, ShortJump);
267 masm.ma_b(&done, ShortJump);
268
269 // Check for zero.
270 masm.bind(&equal);
271 masm.loadConstantDouble(0.0, ScratchFloatReg);
272 // First wasn't 0 or -0, so just return it.
273 masm.ma_bc1d(first, ScratchFloatReg, &done, Assembler::DoubleNotEqualOrUnordered, ShortJump);
274
275 // So now both operands are either -0 or 0.
276 if (ins->mir()->isMax()) {
277 // -0 + -0 = -0 and -0 + 0 = 0.
278 masm.addDouble(second, first);
279 } else {
280 masm.negateDouble(first);
281 masm.subDouble(second, first);
282 masm.negateDouble(first);
283 }
284 masm.ma_b(&done, ShortJump);
285
286 masm.bind(&nan);
287 masm.loadConstantDouble(GenericNaN(), output);
288 masm.ma_b(&done, ShortJump);
289
290 masm.bind(&returnSecond);
291 masm.moveDouble(second, output);
292
293 masm.bind(&done);
294 return true;
295 }
296
297 bool
298 CodeGeneratorMIPS::visitAbsD(LAbsD *ins)
299 {
300 FloatRegister input = ToFloatRegister(ins->input());
301 MOZ_ASSERT(input == ToFloatRegister(ins->output()));
302 masm.as_absd(input, input);
303 return true;
304 }
305
306 bool
307 CodeGeneratorMIPS::visitAbsF(LAbsF *ins)
308 {
309 FloatRegister input = ToFloatRegister(ins->input());
310 MOZ_ASSERT(input == ToFloatRegister(ins->output()));
311 masm.as_abss(input, input);
312 return true;
313 }
314
315 bool
316 CodeGeneratorMIPS::visitSqrtD(LSqrtD *ins)
317 {
318 FloatRegister input = ToFloatRegister(ins->input());
319 FloatRegister output = ToFloatRegister(ins->output());
320 masm.as_sqrtd(output, input);
321 return true;
322 }
323
324 bool
325 CodeGeneratorMIPS::visitSqrtF(LSqrtF *ins)
326 {
327 FloatRegister input = ToFloatRegister(ins->input());
328 FloatRegister output = ToFloatRegister(ins->output());
329 masm.as_sqrts(output, input);
330 return true;
331 }
332
333 bool
334 CodeGeneratorMIPS::visitAddI(LAddI *ins)
335 {
336 const LAllocation *lhs = ins->getOperand(0);
337 const LAllocation *rhs = ins->getOperand(1);
338 const LDefinition *dest = ins->getDef(0);
339
340 MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg());
341
342 // If there is no snapshot, we don't need to check for overflow
343 if (!ins->snapshot()) {
344 if (rhs->isConstant())
345 masm.ma_addu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
346 else
347 masm.as_addu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
348 return true;
349 }
350
351 Label overflow;
352 if (rhs->isConstant())
353 masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow);
354 else
355 masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow);
356
357 if (!bailoutFrom(&overflow, ins->snapshot()))
358 return false;
359
360 return true;
361 }
362
363 bool
364 CodeGeneratorMIPS::visitSubI(LSubI *ins)
365 {
366 const LAllocation *lhs = ins->getOperand(0);
367 const LAllocation *rhs = ins->getOperand(1);
368 const LDefinition *dest = ins->getDef(0);
369
370 MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg());
371
372 // If there is no snapshot, we don't need to check for overflow
373 if (!ins->snapshot()) {
374 if (rhs->isConstant())
375 masm.ma_subu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
376 else
377 masm.as_subu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
378 return true;
379 }
380
381 Label overflow;
382 if (rhs->isConstant())
383 masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow);
384 else
385 masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow);
386
387 if (!bailoutFrom(&overflow, ins->snapshot()))
388 return false;
389
390 return true;
391 }
392
393 bool
394 CodeGeneratorMIPS::visitMulI(LMulI *ins)
395 {
396 const LAllocation *lhs = ins->lhs();
397 const LAllocation *rhs = ins->rhs();
398 Register dest = ToRegister(ins->output());
399 MMul *mul = ins->mir();
400
401 MOZ_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow());
402
403 if (rhs->isConstant()) {
404 int32_t constant = ToInt32(rhs);
405 Register src = ToRegister(lhs);
406
407 // Bailout on -0.0
408 if (mul->canBeNegativeZero() && constant <= 0) {
409 Assembler::Condition cond = (constant == 0) ? Assembler::LessThan : Assembler::Equal;
410 if (!bailoutCmp32(cond, src, Imm32(0), ins->snapshot()))
411 return false;
412 }
413
414 switch (constant) {
415 case -1:
416 if (mul->canOverflow()) {
417 if (!bailoutCmp32(Assembler::Equal, src, Imm32(INT32_MIN), ins->snapshot()))
418 return false;
419 }
420 masm.ma_negu(dest, src);
421 break;
422 case 0:
423 masm.move32(Imm32(0), dest);
424 break;
425 case 1:
426 masm.move32(src, dest);
427 break;
428 case 2:
429 if (mul->canOverflow()) {
430 Label mulTwoOverflow;
431 masm.ma_addTestOverflow(dest, src, src, &mulTwoOverflow);
432
433 if (!bailoutFrom(&mulTwoOverflow, ins->snapshot()))
434 return false;
435 } else {
436 masm.as_addu(dest, src, src);
437 }
438 break;
439 default:
440 uint32_t shift = FloorLog2(constant);
441
442 if (!mul->canOverflow() && (constant > 0)) {
443 // If it cannot overflow, we can do lots of optimizations.
444 uint32_t rest = constant - (1 << shift);
445
446 // See if the constant has one bit set, meaning it can be
447 // encoded as a bitshift.
448 if ((1 << shift) == constant) {
449 masm.ma_sll(dest, src, Imm32(shift));
450 return true;
451 }
452
453 // If the constant cannot be encoded as (1<<C1), see if it can
454 // be encoded as (1<<C1) | (1<<C2), which can be computed
455 // using an add and a shift.
456 uint32_t shift_rest = FloorLog2(rest);
457 if (src != dest && (1u << shift_rest) == rest) {
458 masm.ma_sll(dest, src, Imm32(shift - shift_rest));
459 masm.add32(src, dest);
460 if (shift_rest != 0)
461 masm.ma_sll(dest, dest, Imm32(shift_rest));
462 return true;
463 }
464 }
465
466 if (mul->canOverflow() && (constant > 0) && (src != dest)) {
467 // To stay on the safe side, only optimize things that are a
468 // power of 2.
469
470 if ((1 << shift) == constant) {
471 // dest = lhs * pow(2, shift)
472 masm.ma_sll(dest, src, Imm32(shift));
473 // At runtime, check (lhs == dest >> shift), if this does
474 // not hold, some bits were lost due to overflow, and the
475 // computation should be resumed as a double.
476 masm.ma_sra(ScratchRegister, dest, Imm32(shift));
477 if (!bailoutCmp32(Assembler::NotEqual, src, ScratchRegister, ins->snapshot()))
478 return false;
479 return true;
480 }
481 }
482
483 if (mul->canOverflow()) {
484 Label mulConstOverflow;
485 masm.ma_mul_branch_overflow(dest, ToRegister(lhs), Imm32(ToInt32(rhs)),
486 &mulConstOverflow);
487
488 if (!bailoutFrom(&mulConstOverflow, ins->snapshot()))
489 return false;
490 } else {
491 masm.ma_mult(src, Imm32(ToInt32(rhs)));
492 masm.as_mflo(dest);
493 }
494 break;
495 }
496 } else {
497 Label multRegOverflow;
498
499 if (mul->canOverflow()) {
500 masm.ma_mul_branch_overflow(dest, ToRegister(lhs), ToRegister(rhs), &multRegOverflow);
501 if (!bailoutFrom(&multRegOverflow, ins->snapshot()))
502 return false;
503 } else {
504 masm.as_mult(ToRegister(lhs), ToRegister(rhs));
505 masm.as_mflo(dest);
506 }
507
508 if (mul->canBeNegativeZero()) {
509 Label done;
510 masm.ma_b(dest, dest, &done, Assembler::NonZero, ShortJump);
511
512 // Result is -0 if lhs or rhs is negative.
513 // In that case result must be double value so bailout
514 Register scratch = SecondScratchReg;
515 masm.ma_or(scratch, ToRegister(lhs), ToRegister(rhs));
516 if (!bailoutCmp32(Assembler::Signed, scratch, scratch, ins->snapshot()))
517 return false;
518
519 masm.bind(&done);
520 }
521 }
522
523 return true;
524 }
525
526 bool
527 CodeGeneratorMIPS::visitDivI(LDivI *ins)
528 {
529 // Extract the registers from this instruction
530 Register lhs = ToRegister(ins->lhs());
531 Register rhs = ToRegister(ins->rhs());
532 Register dest = ToRegister(ins->output());
533 Register temp = ToRegister(ins->getTemp(0));
534 MDiv *mir = ins->mir();
535
536 Label done;
537
538 // Handle divide by zero.
539 if (mir->canBeDivideByZero()) {
540 if (mir->canTruncateInfinities()) {
541 // Truncated division by zero is zero (Infinity|0 == 0)
542 Label notzero;
543 masm.ma_b(rhs, rhs, &notzero, Assembler::NonZero, ShortJump);
544 masm.move32(Imm32(0), dest);
545 masm.ma_b(&done, ShortJump);
546 masm.bind(&notzero);
547 } else {
548 MOZ_ASSERT(mir->fallible());
549 if (!bailoutCmp32(Assembler::Zero, rhs, rhs, ins->snapshot()))
550 return false;
551 }
552 }
553
554 // Handle an integer overflow exception from -2147483648 / -1.
555 if (mir->canBeNegativeOverflow()) {
556 Label notMinInt;
557 masm.move32(Imm32(INT32_MIN), temp);
558 masm.ma_b(lhs, temp, &notMinInt, Assembler::NotEqual, ShortJump);
559
560 masm.move32(Imm32(-1), temp);
561 if (mir->canTruncateOverflow()) {
562 // (-INT32_MIN)|0 == INT32_MIN
563 Label skip;
564 masm.ma_b(rhs, temp, &skip, Assembler::NotEqual, ShortJump);
565 masm.move32(Imm32(INT32_MIN), dest);
566 masm.ma_b(&done, ShortJump);
567 masm.bind(&skip);
568 } else {
569 MOZ_ASSERT(mir->fallible());
570 if (!bailoutCmp32(Assembler::Equal, rhs, temp, ins->snapshot()))
571 return false;
572 }
573 masm.bind(&notMinInt);
574 }
575
576 // Handle negative 0. (0/-Y)
577 if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) {
578 Label nonzero;
579 masm.ma_b(lhs, lhs, &nonzero, Assembler::NonZero, ShortJump);
580 if (!bailoutCmp32(Assembler::LessThan, rhs, Imm32(0), ins->snapshot()))
581 return false;
582 masm.bind(&nonzero);
583 }
584 // Note: above safety checks could not be verified as Ion seems to be
585 // smarter and requires double arithmetic in such cases.
586
587 // All regular. Lets call div.
588 if (mir->canTruncateRemainder()) {
589 masm.as_div(lhs, rhs);
590 masm.as_mflo(dest);
591 } else {
592 MOZ_ASSERT(mir->fallible());
593
594 Label remainderNonZero;
595 masm.ma_div_branch_overflow(dest, lhs, rhs, &remainderNonZero);
596 if (!bailoutFrom(&remainderNonZero, ins->snapshot()))
597 return false;
598 }
599
600 masm.bind(&done);
601
602 return true;
603 }
604
605 bool
606 CodeGeneratorMIPS::visitDivPowTwoI(LDivPowTwoI *ins)
607 {
608 Register lhs = ToRegister(ins->numerator());
609 Register dest = ToRegister(ins->output());
610 Register tmp = ToRegister(ins->getTemp(0));
611 int32_t shift = ins->shift();
612
613 if (shift != 0) {
614 MDiv *mir = ins->mir();
615 if (!mir->isTruncated()) {
616 // If the remainder is going to be != 0, bailout since this must
617 // be a double.
618 masm.ma_sll(tmp, lhs, Imm32(32 - shift));
619 if (!bailoutCmp32(Assembler::NonZero, tmp, tmp, ins->snapshot()))
620 return false;
621 }
622
623 if (!mir->canBeNegativeDividend()) {
624 // Numerator is unsigned, so needs no adjusting. Do the shift.
625 masm.ma_sra(dest, lhs, Imm32(shift));
626 return true;
627 }
628
629 // Adjust the value so that shifting produces a correctly rounded result
630 // when the numerator is negative. See 10-1 "Signed Division by a Known
631 // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight.
632 if (shift > 1) {
633 masm.ma_sra(tmp, lhs, Imm32(31));
634 masm.ma_srl(tmp, tmp, Imm32(32 - shift));
635 masm.add32(lhs, tmp);
636 } else {
637 masm.ma_srl(tmp, lhs, Imm32(32 - shift));
638 masm.add32(lhs, tmp);
639 }
640
641 // Do the shift.
642 masm.ma_sra(dest, tmp, Imm32(shift));
643 } else {
644 masm.move32(lhs, dest);
645 }
646
647 return true;
648
649 }
650
651 bool
652 CodeGeneratorMIPS::visitModI(LModI *ins)
653 {
654 // Extract the registers from this instruction
655 Register lhs = ToRegister(ins->lhs());
656 Register rhs = ToRegister(ins->rhs());
657 Register dest = ToRegister(ins->output());
658 Register callTemp = ToRegister(ins->callTemp());
659 MMod *mir = ins->mir();
660 Label done, prevent;
661
662 masm.move32(lhs, callTemp);
663
664 // Prevent INT_MIN % -1;
665 // The integer division will give INT_MIN, but we want -(double)INT_MIN.
666 if (mir->canBeNegativeDividend()) {
667 masm.ma_b(lhs, Imm32(INT_MIN), &prevent, Assembler::NotEqual, ShortJump);
668 if (mir->isTruncated()) {
669 // (INT_MIN % -1)|0 == 0
670 Label skip;
671 masm.ma_b(rhs, Imm32(-1), &skip, Assembler::NotEqual, ShortJump);
672 masm.move32(Imm32(0), dest);
673 masm.ma_b(&done, ShortJump);
674 masm.bind(&skip);
675 } else {
676 MOZ_ASSERT(mir->fallible());
677 if (!bailoutCmp32(Assembler::Equal, rhs, Imm32(-1), ins->snapshot()))
678 return false;
679 }
680 masm.bind(&prevent);
681 }
682
683 // 0/X (with X < 0) is bad because both of these values *should* be
684 // doubles, and the result should be -0.0, which cannot be represented in
685 // integers. X/0 is bad because it will give garbage (or abort), when it
686 // should give either \infty, -\infty or NAN.
687
688 // Prevent 0 / X (with X < 0) and X / 0
689 // testing X / Y. Compare Y with 0.
690 // There are three cases: (Y < 0), (Y == 0) and (Y > 0)
691 // If (Y < 0), then we compare X with 0, and bail if X == 0
692 // If (Y == 0), then we simply want to bail.
693 // if (Y > 0), we don't bail.
694
695 if (mir->canBeDivideByZero()) {
696 if (mir->isTruncated()) {
697 Label skip;
698 masm.ma_b(rhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump);
699 masm.move32(Imm32(0), dest);
700 masm.ma_b(&done, ShortJump);
701 masm.bind(&skip);
702 } else {
703 MOZ_ASSERT(mir->fallible());
704 if (!bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()))
705 return false;
706 }
707 }
708
709 if (mir->canBeNegativeDividend()) {
710 Label notNegative;
711 masm.ma_b(rhs, Imm32(0), &notNegative, Assembler::GreaterThan, ShortJump);
712 if (mir->isTruncated()) {
713 // NaN|0 == 0 and (0 % -X)|0 == 0
714 Label skip;
715 masm.ma_b(lhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump);
716 masm.move32(Imm32(0), dest);
717 masm.ma_b(&done, ShortJump);
718 masm.bind(&skip);
719 } else {
720 MOZ_ASSERT(mir->fallible());
721 if (!bailoutCmp32(Assembler::Equal, lhs, Imm32(0), ins->snapshot()))
722 return false;
723 }
724 masm.bind(&notNegative);
725 }
726
727 masm.as_div(lhs, rhs);
728 masm.as_mfhi(dest);
729
730 // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0
731 if (mir->canBeNegativeDividend()) {
732 if (mir->isTruncated()) {
733 // -0.0|0 == 0
734 } else {
735 MOZ_ASSERT(mir->fallible());
736 // See if X < 0
737 masm.ma_b(dest, Imm32(0), &done, Assembler::NotEqual, ShortJump);
738 if (!bailoutCmp32(Assembler::Signed, callTemp, Imm32(0), ins->snapshot()))
739 return false;
740 }
741 }
742 masm.bind(&done);
743 return true;
744 }
745
746 bool
747 CodeGeneratorMIPS::visitModPowTwoI(LModPowTwoI *ins)
748 {
749 Register in = ToRegister(ins->getOperand(0));
750 Register out = ToRegister(ins->getDef(0));
751 MMod *mir = ins->mir();
752 Label negative, done;
753
754 masm.move32(in, out);
755 masm.ma_b(in, in, &done, Assembler::Zero, ShortJump);
756 // Switch based on sign of the lhs.
757 // Positive numbers are just a bitmask
758 masm.ma_b(in, in, &negative, Assembler::Signed, ShortJump);
759 {
760 masm.and32(Imm32((1 << ins->shift()) - 1), out);
761 masm.ma_b(&done, ShortJump);
762 }
763
764 // Negative numbers need a negate, bitmask, negate
765 {
766 masm.bind(&negative);
767 masm.neg32(out);
768 masm.and32(Imm32((1 << ins->shift()) - 1), out);
769 masm.neg32(out);
770 }
771 if (mir->canBeNegativeDividend()) {
772 if (!mir->isTruncated()) {
773 MOZ_ASSERT(mir->fallible());
774 if (!bailoutCmp32(Assembler::Equal, out, zero, ins->snapshot()))
775 return false;
776 } else {
777 // -0|0 == 0
778 }
779 }
780 masm.bind(&done);
781 return true;
782 }
783
784 bool
785 CodeGeneratorMIPS::visitModMaskI(LModMaskI *ins)
786 {
787 Register src = ToRegister(ins->getOperand(0));
788 Register dest = ToRegister(ins->getDef(0));
789 Register tmp = ToRegister(ins->getTemp(0));
790 MMod *mir = ins->mir();
791
792 if (!mir->isTruncated() && mir->canBeNegativeDividend()) {
793 MOZ_ASSERT(mir->fallible());
794
795 Label bail;
796 masm.ma_mod_mask(src, dest, tmp, ins->shift(), &bail);
797 if (!bailoutFrom(&bail, ins->snapshot()))
798 return false;
799 } else {
800 masm.ma_mod_mask(src, dest, tmp, ins->shift(), nullptr);
801 }
802 return true;
803 }
804 bool
805 CodeGeneratorMIPS::visitBitNotI(LBitNotI *ins)
806 {
807 const LAllocation *input = ins->getOperand(0);
808 const LDefinition *dest = ins->getDef(0);
809 MOZ_ASSERT(!input->isConstant());
810
811 masm.ma_not(ToRegister(dest), ToRegister(input));
812 return true;
813 }
814
815 bool
816 CodeGeneratorMIPS::visitBitOpI(LBitOpI *ins)
817 {
818 const LAllocation *lhs = ins->getOperand(0);
819 const LAllocation *rhs = ins->getOperand(1);
820 const LDefinition *dest = ins->getDef(0);
821 // all of these bitops should be either imm32's, or integer registers.
822 switch (ins->bitop()) {
823 case JSOP_BITOR:
824 if (rhs->isConstant())
825 masm.ma_or(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
826 else
827 masm.ma_or(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
828 break;
829 case JSOP_BITXOR:
830 if (rhs->isConstant())
831 masm.ma_xor(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
832 else
833 masm.ma_xor(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
834 break;
835 case JSOP_BITAND:
836 if (rhs->isConstant())
837 masm.ma_and(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
838 else
839 masm.ma_and(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
840 break;
841 default:
842 MOZ_ASSUME_UNREACHABLE("unexpected binary opcode");
843 }
844
845 return true;
846 }
847
848 bool
849 CodeGeneratorMIPS::visitShiftI(LShiftI *ins)
850 {
851 Register lhs = ToRegister(ins->lhs());
852 const LAllocation *rhs = ins->rhs();
853 Register dest = ToRegister(ins->output());
854
855 if (rhs->isConstant()) {
856 int32_t shift = ToInt32(rhs) & 0x1F;
857 switch (ins->bitop()) {
858 case JSOP_LSH:
859 if (shift)
860 masm.ma_sll(dest, lhs, Imm32(shift));
861 else
862 masm.move32(lhs, dest);
863 break;
864 case JSOP_RSH:
865 if (shift)
866 masm.ma_sra(dest, lhs, Imm32(shift));
867 else
868 masm.move32(lhs, dest);
869 break;
870 case JSOP_URSH:
871 if (shift) {
872 masm.ma_srl(dest, lhs, Imm32(shift));
873 } else {
874 // x >>> 0 can overflow.
875 masm.move32(lhs, dest);
876 if (ins->mir()->toUrsh()->fallible()) {
877 if (!bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()))
878 return false;
879 }
880 }
881 break;
882 default:
883 MOZ_ASSUME_UNREACHABLE("Unexpected shift op");
884 }
885 } else {
886 // The shift amounts should be AND'ed into the 0-31 range
887 masm.ma_and(dest, ToRegister(rhs), Imm32(0x1F));
888
889 switch (ins->bitop()) {
890 case JSOP_LSH:
891 masm.ma_sll(dest, lhs, dest);
892 break;
893 case JSOP_RSH:
894 masm.ma_sra(dest, lhs, dest);
895 break;
896 case JSOP_URSH:
897 masm.ma_srl(dest, lhs, dest);
898 if (ins->mir()->toUrsh()->fallible()) {
899 // x >>> 0 can overflow.
900 if (!bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()))
901 return false;
902 }
903 break;
904 default:
905 MOZ_ASSUME_UNREACHABLE("Unexpected shift op");
906 }
907 }
908
909 return true;
910 }
911
912 bool
913 CodeGeneratorMIPS::visitUrshD(LUrshD *ins)
914 {
915 Register lhs = ToRegister(ins->lhs());
916 Register temp = ToRegister(ins->temp());
917
918 const LAllocation *rhs = ins->rhs();
919 FloatRegister out = ToFloatRegister(ins->output());
920
921 if (rhs->isConstant()) {
922 masm.ma_srl(temp, lhs, Imm32(ToInt32(rhs)));
923 } else {
924 masm.ma_srl(temp, lhs, ToRegister(rhs));
925 }
926
927 masm.convertUInt32ToDouble(temp, out);
928 return true;
929 }
930
931 bool
932 CodeGeneratorMIPS::visitPowHalfD(LPowHalfD *ins)
933 {
934 FloatRegister input = ToFloatRegister(ins->input());
935 FloatRegister output = ToFloatRegister(ins->output());
936
937 Label done, skip;
938
939 // Masm.pow(-Infinity, 0.5) == Infinity.
940 masm.loadConstantDouble(NegativeInfinity<double>(), ScratchFloatReg);
941 masm.ma_bc1d(input, ScratchFloatReg, &skip, Assembler::DoubleNotEqualOrUnordered, ShortJump);
942 masm.as_negd(output, ScratchFloatReg);
943 masm.ma_b(&done, ShortJump);
944
945 masm.bind(&skip);
946 // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5).
947 // Adding 0 converts any -0 to 0.
948 masm.loadConstantDouble(0.0, ScratchFloatReg);
949 masm.as_addd(output, input, ScratchFloatReg);
950 masm.as_sqrtd(output, output);
951
952 masm.bind(&done);
953 return true;
954 }
955
956 MoveOperand
957 CodeGeneratorMIPS::toMoveOperand(const LAllocation *a) const
958 {
959 if (a->isGeneralReg())
960 return MoveOperand(ToRegister(a));
961 if (a->isFloatReg()) {
962 return MoveOperand(ToFloatRegister(a));
963 }
964 MOZ_ASSERT((ToStackOffset(a) & 3) == 0);
965 int32_t offset = ToStackOffset(a);
966
967 // The way the stack slots work, we assume that everything from
968 // depth == 0 downwards is writable. However, since our frame is included
969 // in this, ensure that the frame gets skipped.
970 if (gen->compilingAsmJS())
971 offset -= AlignmentMidPrologue;
972
973 return MoveOperand(StackPointer, offset);
974 }
975
976 class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorMIPS>
977 {
978 MTableSwitch *mir_;
979 CodeLabel jumpLabel_;
980
981 bool accept(CodeGeneratorMIPS *codegen) {
982 return codegen->visitOutOfLineTableSwitch(this);
983 }
984
985 public:
986 OutOfLineTableSwitch(MTableSwitch *mir)
987 : mir_(mir)
988 {}
989
990 MTableSwitch *mir() const {
991 return mir_;
992 }
993
994 CodeLabel *jumpLabel() {
995 return &jumpLabel_;
996 }
997 };
998
999 bool
1000 CodeGeneratorMIPS::visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool)
1001 {
1002 MTableSwitch *mir = ool->mir();
1003
1004 masm.align(sizeof(void*));
1005 masm.bind(ool->jumpLabel()->src());
1006 if (!masm.addCodeLabel(*ool->jumpLabel()))
1007 return false;
1008
1009 for (size_t i = 0; i < mir->numCases(); i++) {
1010 LBlock *caseblock = mir->getCase(i)->lir();
1011 Label *caseheader = caseblock->label();
1012 uint32_t caseoffset = caseheader->offset();
1013
1014 // The entries of the jump table need to be absolute addresses and thus
1015 // must be patched after codegen is finished.
1016 CodeLabel cl;
1017 masm.ma_li(ScratchRegister, cl.dest());
1018 masm.branch(ScratchRegister);
1019 cl.src()->bind(caseoffset);
1020 if (!masm.addCodeLabel(cl))
1021 return false;
1022 }
1023
1024 return true;
1025 }
1026
1027 bool
1028 CodeGeneratorMIPS::emitTableSwitchDispatch(MTableSwitch *mir, const Register &index,
1029 const Register &address)
1030 {
1031 Label *defaultcase = mir->getDefault()->lir()->label();
1032
1033 // Lower value with low value
1034 if (mir->low() != 0)
1035 masm.subPtr(Imm32(mir->low()), index);
1036
1037 // Jump to default case if input is out of range
1038 int32_t cases = mir->numCases();
1039 masm.branchPtr(Assembler::AboveOrEqual, index, ImmWord(cases), defaultcase);
1040
1041 // To fill in the CodeLabels for the case entries, we need to first
1042 // generate the case entries (we don't yet know their offsets in the
1043 // instruction stream).
1044 OutOfLineTableSwitch *ool = new(alloc()) OutOfLineTableSwitch(mir);
1045 if (!addOutOfLineCode(ool))
1046 return false;
1047
1048 // Compute the position where a pointer to the right case stands.
1049 masm.ma_li(address, ool->jumpLabel()->dest());
1050 masm.lshiftPtr(Imm32(4), index);
1051 masm.addPtr(index, address);
1052
1053 masm.branch(address);
1054 return true;
1055 }
1056
1057 bool
1058 CodeGeneratorMIPS::visitMathD(LMathD *math)
1059 {
1060 const LAllocation *src1 = math->getOperand(0);
1061 const LAllocation *src2 = math->getOperand(1);
1062 const LDefinition *output = math->getDef(0);
1063
1064 switch (math->jsop()) {
1065 case JSOP_ADD:
1066 masm.as_addd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
1067 break;
1068 case JSOP_SUB:
1069 masm.as_subd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
1070 break;
1071 case JSOP_MUL:
1072 masm.as_muld(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
1073 break;
1074 case JSOP_DIV:
1075 masm.as_divd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
1076 break;
1077 default:
1078 MOZ_ASSUME_UNREACHABLE("unexpected opcode");
1079 }
1080 return true;
1081 }
1082
1083 bool
1084 CodeGeneratorMIPS::visitMathF(LMathF *math)
1085 {
1086 const LAllocation *src1 = math->getOperand(0);
1087 const LAllocation *src2 = math->getOperand(1);
1088 const LDefinition *output = math->getDef(0);
1089
1090 switch (math->jsop()) {
1091 case JSOP_ADD:
1092 masm.as_adds(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
1093 break;
1094 case JSOP_SUB:
1095 masm.as_subs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
1096 break;
1097 case JSOP_MUL:
1098 masm.as_muls(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
1099 break;
1100 case JSOP_DIV:
1101 masm.as_divs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2));
1102 break;
1103 default:
1104 MOZ_ASSUME_UNREACHABLE("unexpected opcode");
1105 }
1106 return true;
1107 }
1108
1109 bool
1110 CodeGeneratorMIPS::visitFloor(LFloor *lir)
1111 {
1112 FloatRegister input = ToFloatRegister(lir->input());
1113 FloatRegister scratch = ScratchFloatReg;
1114 Register output = ToRegister(lir->output());
1115
1116 Label skipCheck, done;
1117
1118 // If Nan, 0 or -0 check for bailout
1119 masm.loadConstantDouble(0.0, scratch);
1120 masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump);
1121
1122 // If high part is not zero, it is NaN or -0, so we bail.
1123 masm.moveFromDoubleHi(input, SecondScratchReg);
1124 if (!bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()))
1125 return false;
1126
1127 // Input was zero, so return zero.
1128 masm.move32(Imm32(0), output);
1129 masm.ma_b(&done, ShortJump);
1130
1131 masm.bind(&skipCheck);
1132 masm.as_floorwd(scratch, input);
1133 masm.moveFromDoubleLo(scratch, output);
1134
1135 if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()))
1136 return false;
1137
1138 if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()))
1139 return false;
1140
1141 masm.bind(&done);
1142
1143 return true;
1144 }
1145
1146 bool
1147 CodeGeneratorMIPS::visitFloorF(LFloorF *lir)
1148 {
1149 FloatRegister input = ToFloatRegister(lir->input());
1150 FloatRegister scratch = ScratchFloatReg;
1151 Register output = ToRegister(lir->output());
1152
1153 Label skipCheck, done;
1154
1155 // If Nan, 0 or -0 check for bailout
1156 masm.loadConstantFloat32(0.0, scratch);
1157 masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump);
1158
1159 // If binary value is not zero, it is NaN or -0, so we bail.
1160 masm.moveFromDoubleLo(input, SecondScratchReg);
1161 if (!bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()))
1162 return false;
1163
1164 // Input was zero, so return zero.
1165 masm.move32(Imm32(0), output);
1166 masm.ma_b(&done, ShortJump);
1167
1168 masm.bind(&skipCheck);
1169 masm.as_floorws(scratch, input);
1170 masm.moveFromDoubleLo(scratch, output);
1171
1172 if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()))
1173 return false;
1174
1175 if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()))
1176 return false;
1177
1178 masm.bind(&done);
1179
1180 return true;
1181 }
1182
1183 bool
1184 CodeGeneratorMIPS::visitRound(LRound *lir)
1185 {
1186 FloatRegister input = ToFloatRegister(lir->input());
1187 FloatRegister temp = ToFloatRegister(lir->temp());
1188 FloatRegister scratch = ScratchFloatReg;
1189 Register output = ToRegister(lir->output());
1190
1191 Label bail, negative, end, skipCheck;
1192
1193 // Load 0.5 in the temp register.
1194 masm.loadConstantDouble(0.5, temp);
1195
1196 // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
1197 masm.loadConstantDouble(0.0, scratch);
1198 masm.ma_bc1d(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump);
1199
1200 // If Nan, 0 or -0 check for bailout
1201 masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump);
1202
1203 // If high part is not zero, it is NaN or -0, so we bail.
1204 masm.moveFromDoubleHi(input, SecondScratchReg);
1205 if (!bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()))
1206 return false;
1207
1208 // Input was zero, so return zero.
1209 masm.move32(Imm32(0), output);
1210 masm.ma_b(&end, ShortJump);
1211
1212 masm.bind(&skipCheck);
1213 masm.loadConstantDouble(0.5, scratch);
1214 masm.addDouble(input, scratch);
1215 masm.as_floorwd(scratch, scratch);
1216
1217 masm.moveFromDoubleLo(scratch, output);
1218
1219 if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()))
1220 return false;
1221
1222 if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()))
1223 return false;
1224
1225 masm.jump(&end);
1226
1227 // Input is negative, but isn't -0.
1228 masm.bind(&negative);
1229 masm.addDouble(input, temp);
1230
1231 // If input + 0.5 >= 0, input is a negative number >= -0.5 and the
1232 // result is -0.
1233 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail);
1234 if (!bailoutFrom(&bail, lir->snapshot()))
1235 return false;
1236
1237 // Truncate and round toward zero.
1238 // This is off-by-one for everything but integer-valued inputs.
1239 masm.as_floorwd(scratch, temp);
1240 masm.moveFromDoubleLo(scratch, output);
1241
1242 if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()))
1243 return false;
1244
1245 masm.bind(&end);
1246 return true;
1247 }
1248
1249 bool
1250 CodeGeneratorMIPS::visitRoundF(LRoundF *lir)
1251 {
1252 FloatRegister input = ToFloatRegister(lir->input());
1253 FloatRegister temp = ToFloatRegister(lir->temp());
1254 FloatRegister scratch = ScratchFloatReg;
1255 Register output = ToRegister(lir->output());
1256
1257 Label bail, negative, end, skipCheck;
1258
1259 // Load 0.5 in the temp register.
1260 masm.loadConstantFloat32(0.5, temp);
1261
1262 // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
1263 masm.loadConstantFloat32(0.0, scratch);
1264 masm.ma_bc1s(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump);
1265
1266 // If Nan, 0 or -0 check for bailout
1267 masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump);
1268
1269 // If binary value is not zero, it is NaN or -0, so we bail.
1270 masm.moveFromFloat32(input, SecondScratchReg);
1271 if (!bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()))
1272 return false;
1273
1274 // Input was zero, so return zero.
1275 masm.move32(Imm32(0), output);
1276 masm.ma_b(&end, ShortJump);
1277
1278 masm.bind(&skipCheck);
1279 masm.loadConstantFloat32(0.5, scratch);
1280 masm.as_adds(scratch, input, scratch);
1281 masm.as_floorws(scratch, scratch);
1282
1283 masm.moveFromFloat32(scratch, output);
1284
1285 if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()))
1286 return false;
1287
1288 if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()))
1289 return false;
1290
1291 masm.jump(&end);
1292
1293 // Input is negative, but isn't -0.
1294 masm.bind(&negative);
1295 masm.as_adds(temp, input, temp);
1296
1297 // If input + 0.5 >= 0, input is a negative number >= -0.5 and the
1298 // result is -0.
1299 masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail);
1300 if (!bailoutFrom(&bail, lir->snapshot()))
1301 return false;
1302
1303 // Truncate and round toward zero.
1304 // This is off-by-one for everything but integer-valued inputs.
1305 masm.as_floorws(scratch, temp);
1306 masm.moveFromFloat32(scratch, output);
1307
1308 if (!bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()))
1309 return false;
1310
1311 masm.bind(&end);
1312 return true;
1313 }
1314
1315 bool
1316 CodeGeneratorMIPS::visitTruncateDToInt32(LTruncateDToInt32 *ins)
1317 {
1318 return emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()));
1319 }
1320
1321 bool
1322 CodeGeneratorMIPS::visitTruncateFToInt32(LTruncateFToInt32 *ins)
1323 {
1324 return emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()));
1325 }
1326
1327 static const uint32_t FrameSizes[] = { 128, 256, 512, 1024 };
1328
1329 FrameSizeClass
1330 FrameSizeClass::FromDepth(uint32_t frameDepth)
1331 {
1332 for (uint32_t i = 0; i < JS_ARRAY_LENGTH(FrameSizes); i++) {
1333 if (frameDepth < FrameSizes[i])
1334 return FrameSizeClass(i);
1335 }
1336
1337 return FrameSizeClass::None();
1338 }
1339
1340 FrameSizeClass
1341 FrameSizeClass::ClassLimit()
1342 {
1343 return FrameSizeClass(JS_ARRAY_LENGTH(FrameSizes));
1344 }
1345
1346 uint32_t
1347 FrameSizeClass::frameSize() const
1348 {
1349 MOZ_ASSERT(class_ != NO_FRAME_SIZE_CLASS_ID);
1350 MOZ_ASSERT(class_ < JS_ARRAY_LENGTH(FrameSizes));
1351
1352 return FrameSizes[class_];
1353 }
1354
1355 ValueOperand
1356 CodeGeneratorMIPS::ToValue(LInstruction *ins, size_t pos)
1357 {
1358 Register typeReg = ToRegister(ins->getOperand(pos + TYPE_INDEX));
1359 Register payloadReg = ToRegister(ins->getOperand(pos + PAYLOAD_INDEX));
1360 return ValueOperand(typeReg, payloadReg);
1361 }
1362
1363 ValueOperand
1364 CodeGeneratorMIPS::ToOutValue(LInstruction *ins)
1365 {
1366 Register typeReg = ToRegister(ins->getDef(TYPE_INDEX));
1367 Register payloadReg = ToRegister(ins->getDef(PAYLOAD_INDEX));
1368 return ValueOperand(typeReg, payloadReg);
1369 }
1370
1371 ValueOperand
1372 CodeGeneratorMIPS::ToTempValue(LInstruction *ins, size_t pos)
1373 {
1374 Register typeReg = ToRegister(ins->getTemp(pos + TYPE_INDEX));
1375 Register payloadReg = ToRegister(ins->getTemp(pos + PAYLOAD_INDEX));
1376 return ValueOperand(typeReg, payloadReg);
1377 }
1378
1379 bool
1380 CodeGeneratorMIPS::visitValue(LValue *value)
1381 {
1382 const ValueOperand out = ToOutValue(value);
1383
1384 masm.moveValue(value->value(), out);
1385 return true;
1386 }
1387
1388 bool
1389 CodeGeneratorMIPS::visitBox(LBox *box)
1390 {
1391 const LDefinition *type = box->getDef(TYPE_INDEX);
1392
1393 MOZ_ASSERT(!box->getOperand(0)->isConstant());
1394
1395 // For NUNBOX32, the input operand and the output payload have the same
1396 // virtual register. All that needs to be written is the type tag for
1397 // the type definition.
1398 masm.move32(Imm32(MIRTypeToTag(box->type())), ToRegister(type));
1399 return true;
1400 }
1401
1402 bool
1403 CodeGeneratorMIPS::visitBoxFloatingPoint(LBoxFloatingPoint *box)
1404 {
1405 const LDefinition *payload = box->getDef(PAYLOAD_INDEX);
1406 const LDefinition *type = box->getDef(TYPE_INDEX);
1407 const LAllocation *in = box->getOperand(0);
1408
1409 FloatRegister reg = ToFloatRegister(in);
1410 if (box->type() == MIRType_Float32) {
1411 masm.convertFloat32ToDouble(reg, ScratchFloatReg);
1412 reg = ScratchFloatReg;
1413 }
1414 masm.ma_mv(reg, ValueOperand(ToRegister(type), ToRegister(payload)));
1415 return true;
1416 }
1417
1418 bool
1419 CodeGeneratorMIPS::visitUnbox(LUnbox *unbox)
1420 {
1421 // Note that for unbox, the type and payload indexes are switched on the
1422 // inputs.
1423 MUnbox *mir = unbox->mir();
1424 Register type = ToRegister(unbox->type());
1425
1426 if (mir->fallible()) {
1427 if (!bailoutCmp32(Assembler::NotEqual, type, Imm32(MIRTypeToTag(mir->type())),
1428 unbox->snapshot()))
1429 return false;
1430 }
1431 return true;
1432 }
1433
1434 bool
1435 CodeGeneratorMIPS::visitDouble(LDouble *ins)
1436 {
1437 const LDefinition *out = ins->getDef(0);
1438
1439 masm.loadConstantDouble(ins->getDouble(), ToFloatRegister(out));
1440 return true;
1441 }
1442
1443 bool
1444 CodeGeneratorMIPS::visitFloat32(LFloat32 *ins)
1445 {
1446 const LDefinition *out = ins->getDef(0);
1447 masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out));
1448 return true;
1449 }
1450
1451 Register
1452 CodeGeneratorMIPS::splitTagForTest(const ValueOperand &value)
1453 {
1454 return value.typeReg();
1455 }
1456
1457 bool
1458 CodeGeneratorMIPS::visitTestDAndBranch(LTestDAndBranch *test)
1459 {
1460 FloatRegister input = ToFloatRegister(test->input());
1461
1462 MBasicBlock *ifTrue = test->ifTrue();
1463 MBasicBlock *ifFalse = test->ifFalse();
1464
1465 masm.loadConstantDouble(0.0, ScratchFloatReg);
1466 // If 0, or NaN, the result is false.
1467
1468 if (isNextBlock(ifFalse->lir())) {
1469 branchToBlock(Assembler::DoubleFloat, input, ScratchFloatReg, ifTrue,
1470 Assembler::DoubleNotEqual);
1471 } else {
1472 branchToBlock(Assembler::DoubleFloat, input, ScratchFloatReg, ifFalse,
1473 Assembler::DoubleEqualOrUnordered);
1474 jumpToBlock(ifTrue);
1475 }
1476
1477 return true;
1478 }
1479
1480 bool
1481 CodeGeneratorMIPS::visitTestFAndBranch(LTestFAndBranch *test)
1482 {
1483 FloatRegister input = ToFloatRegister(test->input());
1484
1485 MBasicBlock *ifTrue = test->ifTrue();
1486 MBasicBlock *ifFalse = test->ifFalse();
1487
1488 masm.loadConstantFloat32(0.0, ScratchFloatReg);
1489 // If 0, or NaN, the result is false.
1490
1491 if (isNextBlock(ifFalse->lir())) {
1492 branchToBlock(Assembler::SingleFloat, input, ScratchFloatReg, ifTrue,
1493 Assembler::DoubleNotEqual);
1494 } else {
1495 branchToBlock(Assembler::SingleFloat, input, ScratchFloatReg, ifFalse,
1496 Assembler::DoubleEqualOrUnordered);
1497 jumpToBlock(ifTrue);
1498 }
1499
1500 return true;
1501 }
1502
1503 bool
1504 CodeGeneratorMIPS::visitCompareD(LCompareD *comp)
1505 {
1506 FloatRegister lhs = ToFloatRegister(comp->left());
1507 FloatRegister rhs = ToFloatRegister(comp->right());
1508 Register dest = ToRegister(comp->output());
1509
1510 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
1511 masm.ma_cmp_set_double(dest, lhs, rhs, cond);
1512 return true;
1513 }
1514
1515 bool
1516 CodeGeneratorMIPS::visitCompareF(LCompareF *comp)
1517 {
1518 FloatRegister lhs = ToFloatRegister(comp->left());
1519 FloatRegister rhs = ToFloatRegister(comp->right());
1520 Register dest = ToRegister(comp->output());
1521
1522 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
1523 masm.ma_cmp_set_float32(dest, lhs, rhs, cond);
1524 return true;
1525 }
1526
1527
1528 bool
1529 CodeGeneratorMIPS::visitCompareDAndBranch(LCompareDAndBranch *comp)
1530 {
1531 FloatRegister lhs = ToFloatRegister(comp->left());
1532 FloatRegister rhs = ToFloatRegister(comp->right());
1533
1534 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop());
1535 MBasicBlock *ifTrue = comp->ifTrue();
1536 MBasicBlock *ifFalse = comp->ifFalse();
1537
1538 if (isNextBlock(ifFalse->lir())) {
1539 branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifTrue, cond);
1540 } else {
1541 branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifFalse,
1542 Assembler::InvertCondition(cond));
1543 jumpToBlock(ifTrue);
1544 }
1545
1546 return true;
1547 }
1548
1549 bool
1550 CodeGeneratorMIPS::visitCompareFAndBranch(LCompareFAndBranch *comp)
1551 {
1552 FloatRegister lhs = ToFloatRegister(comp->left());
1553 FloatRegister rhs = ToFloatRegister(comp->right());
1554
1555 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop());
1556 MBasicBlock *ifTrue = comp->ifTrue();
1557 MBasicBlock *ifFalse = comp->ifFalse();
1558
1559 if (isNextBlock(ifFalse->lir())) {
1560 branchToBlock(Assembler::SingleFloat, lhs, rhs, ifTrue, cond);
1561 } else {
1562 branchToBlock(Assembler::SingleFloat, lhs, rhs, ifFalse,
1563 Assembler::InvertCondition(cond));
1564 jumpToBlock(ifTrue);
1565 }
1566
1567 return true;
1568 }
1569
1570 bool
1571 CodeGeneratorMIPS::visitCompareB(LCompareB *lir)
1572 {
1573 MCompare *mir = lir->mir();
1574
1575 const ValueOperand lhs = ToValue(lir, LCompareB::Lhs);
1576 const LAllocation *rhs = lir->rhs();
1577 const Register output = ToRegister(lir->output());
1578
1579 MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
1580 Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
1581
1582 Label notBoolean, done;
1583 masm.branchTestBoolean(Assembler::NotEqual, lhs, &notBoolean);
1584 {
1585 if (rhs->isConstant())
1586 masm.cmp32Set(cond, lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean()), output);
1587 else
1588 masm.cmp32Set(cond, lhs.payloadReg(), ToRegister(rhs), output);
1589 masm.jump(&done);
1590 }
1591
1592 masm.bind(&notBoolean);
1593 {
1594 masm.move32(Imm32(mir->jsop() == JSOP_STRICTNE), output);
1595 }
1596
1597 masm.bind(&done);
1598 return true;
1599 }
1600
1601 bool
1602 CodeGeneratorMIPS::visitCompareBAndBranch(LCompareBAndBranch *lir)
1603 {
1604 MCompare *mir = lir->cmpMir();
1605 const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
1606 const LAllocation *rhs = lir->rhs();
1607
1608 MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
1609
1610 MBasicBlock *mirNotBoolean = (mir->jsop() == JSOP_STRICTEQ) ? lir->ifFalse() : lir->ifTrue();
1611 branchToBlock(lhs.typeReg(), ImmType(JSVAL_TYPE_BOOLEAN), mirNotBoolean, Assembler::NotEqual);
1612
1613 Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
1614 if (rhs->isConstant())
1615 emitBranch(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean()), cond, lir->ifTrue(),
1616 lir->ifFalse());
1617 else
1618 emitBranch(lhs.payloadReg(), ToRegister(rhs), cond, lir->ifTrue(), lir->ifFalse());
1619
1620 return true;
1621 }
1622
1623 bool
1624 CodeGeneratorMIPS::visitCompareV(LCompareV *lir)
1625 {
1626 MCompare *mir = lir->mir();
1627 Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
1628 const ValueOperand lhs = ToValue(lir, LCompareV::LhsInput);
1629 const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput);
1630 const Register output = ToRegister(lir->output());
1631
1632 MOZ_ASSERT(IsEqualityOp(mir->jsop()));
1633
1634 Label notEqual, done;
1635 masm.ma_b(lhs.typeReg(), rhs.typeReg(), &notEqual, Assembler::NotEqual, ShortJump);
1636 {
1637 masm.cmp32Set(cond, lhs.payloadReg(), rhs.payloadReg(), output);
1638 masm.ma_b(&done, ShortJump);
1639 }
1640 masm.bind(&notEqual);
1641 {
1642 masm.move32(Imm32(cond == Assembler::NotEqual), output);
1643 }
1644
1645 masm.bind(&done);
1646 return true;
1647 }
1648
1649 bool
1650 CodeGeneratorMIPS::visitCompareVAndBranch(LCompareVAndBranch *lir)
1651 {
1652 MCompare *mir = lir->cmpMir();
1653 Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
1654 const ValueOperand lhs = ToValue(lir, LCompareVAndBranch::LhsInput);
1655 const ValueOperand rhs = ToValue(lir, LCompareVAndBranch::RhsInput);
1656
1657 MOZ_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
1658 mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
1659
1660 MBasicBlock *notEqual = (cond == Assembler::Equal) ? lir->ifFalse() : lir->ifTrue();
1661
1662 branchToBlock(lhs.typeReg(), rhs.typeReg(), notEqual, Assembler::NotEqual);
1663 emitBranch(lhs.payloadReg(), rhs.payloadReg(), cond, lir->ifTrue(), lir->ifFalse());
1664
1665 return true;
1666 }
1667
1668 bool
1669 CodeGeneratorMIPS::visitBitAndAndBranch(LBitAndAndBranch *lir)
1670 {
1671 if (lir->right()->isConstant())
1672 masm.ma_and(ScratchRegister, ToRegister(lir->left()), Imm32(ToInt32(lir->right())));
1673 else
1674 masm.ma_and(ScratchRegister, ToRegister(lir->left()), ToRegister(lir->right()));
1675 emitBranch(ScratchRegister, ScratchRegister, Assembler::NonZero, lir->ifTrue(),
1676 lir->ifFalse());
1677 return true;
1678 }
1679
1680 bool
1681 CodeGeneratorMIPS::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble *lir)
1682 {
1683 masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
1684 return true;
1685 }
1686
1687 bool
1688 CodeGeneratorMIPS::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32 *lir)
1689 {
1690 masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
1691 return true;
1692 }
1693
1694 bool
1695 CodeGeneratorMIPS::visitNotI(LNotI *ins)
1696 {
1697 masm.cmp32Set(Assembler::Equal, ToRegister(ins->input()), Imm32(0),
1698 ToRegister(ins->output()));
1699 return true;
1700 }
1701
1702 bool
1703 CodeGeneratorMIPS::visitNotD(LNotD *ins)
1704 {
1705 // Since this operation is not, we want to set a bit if
1706 // the double is falsey, which means 0.0, -0.0 or NaN.
1707 FloatRegister in = ToFloatRegister(ins->input());
1708 Register dest = ToRegister(ins->output());
1709
1710 Label falsey, done;
1711 masm.loadConstantDouble(0.0, ScratchFloatReg);
1712 masm.ma_bc1d(in, ScratchFloatReg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump);
1713
1714 masm.move32(Imm32(0), dest);
1715 masm.ma_b(&done, ShortJump);
1716
1717 masm.bind(&falsey);
1718 masm.move32(Imm32(1), dest);
1719
1720 masm.bind(&done);
1721 return true;
1722 }
1723
1724 bool
1725 CodeGeneratorMIPS::visitNotF(LNotF *ins)
1726 {
1727 // Since this operation is not, we want to set a bit if
1728 // the float32 is falsey, which means 0.0, -0.0 or NaN.
1729 FloatRegister in = ToFloatRegister(ins->input());
1730 Register dest = ToRegister(ins->output());
1731
1732 Label falsey, done;
1733 masm.loadConstantFloat32(0.0, ScratchFloatReg);
1734 masm.ma_bc1s(in, ScratchFloatReg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump);
1735
1736 masm.move32(Imm32(0), dest);
1737 masm.ma_b(&done, ShortJump);
1738
1739 masm.bind(&falsey);
1740 masm.move32(Imm32(1), dest);
1741
1742 masm.bind(&done);
1743 return true;
1744 }
1745
1746 bool
1747 CodeGeneratorMIPS::visitLoadSlotV(LLoadSlotV *load)
1748 {
1749 const ValueOperand out = ToOutValue(load);
1750 Register base = ToRegister(load->input());
1751 int32_t offset = load->mir()->slot() * sizeof(js::Value);
1752
1753 masm.loadValue(Address(base, offset), out);
1754 return true;
1755 }
1756
1757 bool
1758 CodeGeneratorMIPS::visitLoadSlotT(LLoadSlotT *load)
1759 {
1760 Register base = ToRegister(load->input());
1761 int32_t offset = load->mir()->slot() * sizeof(js::Value);
1762
1763 if (load->mir()->type() == MIRType_Double)
1764 masm.loadInt32OrDouble(Address(base, offset), ToFloatRegister(load->output()));
1765 else
1766 masm.load32(Address(base, offset + NUNBOX32_PAYLOAD_OFFSET), ToRegister(load->output()));
1767 return true;
1768 }
1769
1770 bool
1771 CodeGeneratorMIPS::visitStoreSlotT(LStoreSlotT *store)
1772 {
1773 Register base = ToRegister(store->slots());
1774 int32_t offset = store->mir()->slot() * sizeof(js::Value);
1775
1776 const LAllocation *value = store->value();
1777 MIRType valueType = store->mir()->value()->type();
1778
1779 if (store->mir()->needsBarrier())
1780 emitPreBarrier(Address(base, offset), store->mir()->slotType());
1781
1782 if (valueType == MIRType_Double) {
1783 masm.storeDouble(ToFloatRegister(value), Address(base, offset));
1784 return true;
1785 }
1786
1787 // Store the type tag if needed.
1788 if (valueType != store->mir()->slotType())
1789 masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), Address(base, offset));
1790
1791 // Store the payload.
1792 if (value->isConstant())
1793 masm.storePayload(*value->toConstant(), Address(base, offset));
1794 else
1795 masm.storePayload(ToRegister(value), Address(base, offset));
1796
1797 return true;
1798 }
1799
1800 bool
1801 CodeGeneratorMIPS::visitLoadElementT(LLoadElementT *load)
1802 {
1803 Register base = ToRegister(load->elements());
1804 if (load->mir()->type() == MIRType_Double) {
1805 FloatRegister fpreg = ToFloatRegister(load->output());
1806 if (load->index()->isConstant()) {
1807 Address source(base, ToInt32(load->index()) * sizeof(Value));
1808 if (load->mir()->loadDoubles())
1809 masm.loadDouble(source, fpreg);
1810 else
1811 masm.loadInt32OrDouble(source, fpreg);
1812 } else {
1813 Register index = ToRegister(load->index());
1814 if (load->mir()->loadDoubles())
1815 masm.loadDouble(BaseIndex(base, index, TimesEight), fpreg);
1816 else
1817 masm.loadInt32OrDouble(base, index, fpreg);
1818 }
1819 } else {
1820 if (load->index()->isConstant()) {
1821 Address source(base, ToInt32(load->index()) * sizeof(Value));
1822 masm.load32(source, ToRegister(load->output()));
1823 } else {
1824 BaseIndex source(base, ToRegister(load->index()), TimesEight);
1825 masm.load32(source, ToRegister(load->output()));
1826 }
1827 }
1828 MOZ_ASSERT(!load->mir()->needsHoleCheck());
1829 return true;
1830 }
1831
1832 void
1833 CodeGeneratorMIPS::storeElementTyped(const LAllocation *value, MIRType valueType,
1834 MIRType elementType, const Register &elements,
1835 const LAllocation *index)
1836 {
1837 if (index->isConstant()) {
1838 Address dest = Address(elements, ToInt32(index) * sizeof(Value));
1839 if (valueType == MIRType_Double) {
1840 masm.storeDouble(ToFloatRegister(value), Address(dest.base, dest.offset));
1841 return;
1842 }
1843
1844 // Store the type tag if needed.
1845 if (valueType != elementType)
1846 masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest);
1847
1848 // Store the payload.
1849 if (value->isConstant())
1850 masm.storePayload(*value->toConstant(), dest);
1851 else
1852 masm.storePayload(ToRegister(value), dest);
1853 } else {
1854 Register indexReg = ToRegister(index);
1855 if (valueType == MIRType_Double) {
1856 masm.storeDouble(ToFloatRegister(value), BaseIndex(elements, indexReg, TimesEight));
1857 return;
1858 }
1859
1860 // Store the type tag if needed.
1861 if (valueType != elementType)
1862 masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), elements, indexReg);
1863
1864 // Store the payload.
1865 if (value->isConstant())
1866 masm.storePayload(*value->toConstant(), elements, indexReg);
1867 else
1868 masm.storePayload(ToRegister(value), elements, indexReg);
1869 }
1870 }
1871
1872 bool
1873 CodeGeneratorMIPS::visitGuardShape(LGuardShape *guard)
1874 {
1875 Register obj = ToRegister(guard->input());
1876 Register tmp = ToRegister(guard->tempInt());
1877
1878 masm.loadPtr(Address(obj, JSObject::offsetOfShape()), tmp);
1879 return bailoutCmpPtr(Assembler::NotEqual, tmp, ImmGCPtr(guard->mir()->shape()),
1880 guard->snapshot());
1881 }
1882
1883 bool
1884 CodeGeneratorMIPS::visitGuardObjectType(LGuardObjectType *guard)
1885 {
1886 Register obj = ToRegister(guard->input());
1887 Register tmp = ToRegister(guard->tempInt());
1888
1889 masm.loadPtr(Address(obj, JSObject::offsetOfType()), tmp);
1890 Assembler::Condition cond = guard->mir()->bailOnEquality()
1891 ? Assembler::Equal
1892 : Assembler::NotEqual;
1893 return bailoutCmpPtr(cond, tmp, ImmGCPtr(guard->mir()->typeObject()), guard->snapshot());
1894 }
1895
1896 bool
1897 CodeGeneratorMIPS::visitGuardClass(LGuardClass *guard)
1898 {
1899 Register obj = ToRegister(guard->input());
1900 Register tmp = ToRegister(guard->tempInt());
1901
1902 masm.loadObjClass(obj, tmp);
1903 if (!bailoutCmpPtr(Assembler::NotEqual, tmp, Imm32((uint32_t)guard->mir()->getClass()),
1904 guard->snapshot()))
1905 return false;
1906 return true;
1907 }
1908
1909 bool
1910 CodeGeneratorMIPS::visitImplicitThis(LImplicitThis *lir)
1911 {
1912 Register callee = ToRegister(lir->callee());
1913 const ValueOperand out = ToOutValue(lir);
1914
1915 // The implicit |this| is always |undefined| if the function's environment
1916 // is the current global.
1917 masm.loadPtr(Address(callee, JSFunction::offsetOfEnvironment()), out.typeReg());
1918 GlobalObject *global = &gen->info().script()->global();
1919
1920 // TODO: OOL stub path.
1921 if (!bailoutCmpPtr(Assembler::NotEqual, out.typeReg(), ImmGCPtr(global), lir->snapshot()))
1922 return false;
1923
1924 masm.moveValue(UndefinedValue(), out);
1925 return true;
1926 }
1927
1928 bool
1929 CodeGeneratorMIPS::visitInterruptCheck(LInterruptCheck *lir)
1930 {
1931 OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
1932 if (!ool)
1933 return false;
1934
1935 masm.branch32(Assembler::NotEqual,
1936 AbsoluteAddress(GetIonContext()->runtime->addressOfInterrupt()), Imm32(0),
1937 ool->entry());
1938 masm.bind(ool->rejoin());
1939 return true;
1940 }
1941
1942 bool
1943 CodeGeneratorMIPS::generateInvalidateEpilogue()
1944 {
1945 // Ensure that there is enough space in the buffer for the OsiPoint
1946 // patching to occur. Otherwise, we could overwrite the invalidation
1947 // epilogue.
1948 for (size_t i = 0; i < sizeof(void *); i += Assembler::nopSize())
1949 masm.nop();
1950
1951 masm.bind(&invalidate_);
1952
1953 // Push the return address of the point that we bailed out at to the stack
1954 masm.Push(ra);
1955
1956 // Push the Ion script onto the stack (when we determine what that
1957 // pointer is).
1958 invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1)));
1959 JitCode *thunk = gen->jitRuntime()->getInvalidationThunk();
1960
1961 masm.branch(thunk);
1962
1963 // We should never reach this point in JIT code -- the invalidation thunk
1964 // should pop the invalidated JS frame and return directly to its caller.
1965 masm.assumeUnreachable("Should have returned directly to its caller instead of here.");
1966 return true;
1967 }
1968
1969 void
1970 DispatchIonCache::initializeAddCacheState(LInstruction *ins, AddCacheState *addState)
1971 {
1972 // Can always use the scratch register on MIPS.
1973 addState->dispatchScratch = ScratchRegister;
1974 }
1975
1976 bool
1977 CodeGeneratorMIPS::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
1978 {
1979 MOZ_ASSUME_UNREACHABLE("NYI");
1980 }
1981
1982 bool
1983 CodeGeneratorMIPS::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
1984 {
1985 MOZ_ASSUME_UNREACHABLE("NYI");
1986 }
1987
1988 bool
1989 CodeGeneratorMIPS::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
1990 {
1991 const MAsmJSLoadHeap *mir = ins->mir();
1992 const LAllocation *ptr = ins->ptr();
1993 const LDefinition *out = ins->output();
1994
1995 bool isSigned;
1996 int size;
1997 bool isFloat = false;
1998 switch (mir->viewType()) {
1999 case ArrayBufferView::TYPE_INT8: isSigned = true; size = 8; break;
2000 case ArrayBufferView::TYPE_UINT8: isSigned = false; size = 8; break;
2001 case ArrayBufferView::TYPE_INT16: isSigned = true; size = 16; break;
2002 case ArrayBufferView::TYPE_UINT16: isSigned = false; size = 16; break;
2003 case ArrayBufferView::TYPE_INT32: isSigned = true; size = 32; break;
2004 case ArrayBufferView::TYPE_UINT32: isSigned = false; size = 32; break;
2005 case ArrayBufferView::TYPE_FLOAT64: isFloat = true; size = 64; break;
2006 case ArrayBufferView::TYPE_FLOAT32: isFloat = true; size = 32; break;
2007 default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
2008 }
2009
2010 if (ptr->isConstant()) {
2011 MOZ_ASSERT(mir->skipBoundsCheck());
2012 int32_t ptrImm = ptr->toConstant()->toInt32();
2013 MOZ_ASSERT(ptrImm >= 0);
2014 if (isFloat) {
2015 if (size == 32) {
2016 masm.loadFloat32(Address(HeapReg, ptrImm), ToFloatRegister(out));
2017 } else {
2018 masm.loadDouble(Address(HeapReg, ptrImm), ToFloatRegister(out));
2019 }
2020 } else {
2021 masm.ma_load(ToRegister(out), Address(HeapReg, ptrImm),
2022 static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
2023 }
2024 return true;
2025 }
2026
2027 Register ptrReg = ToRegister(ptr);
2028
2029 if (mir->skipBoundsCheck()) {
2030 if (isFloat) {
2031 if (size == 32) {
2032 masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
2033 } else {
2034 masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
2035 }
2036 } else {
2037 masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne),
2038 static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
2039 }
2040 return true;
2041 }
2042
2043 BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister);
2044
2045 Label outOfRange;
2046 Label done;
2047 masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump);
2048 // Offset is ok, let's load value.
2049 if (isFloat) {
2050 if (size == 32)
2051 masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
2052 else
2053 masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
2054 } else {
2055 masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne),
2056 static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
2057 }
2058 masm.ma_b(&done, ShortJump);
2059 masm.bind(&outOfRange);
2060 // Offset is out of range. Load default values.
2061 if (isFloat) {
2062 if (size == 32)
2063 masm.convertDoubleToFloat32(NANReg, ToFloatRegister(out));
2064 else
2065 masm.moveDouble(NANReg, ToFloatRegister(out));
2066 } else {
2067 masm.move32(Imm32(0), ToRegister(out));
2068 }
2069 masm.bind(&done);
2070
2071 return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset()));
2072 }
2073
2074 bool
2075 CodeGeneratorMIPS::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
2076 {
2077 const MAsmJSStoreHeap *mir = ins->mir();
2078 const LAllocation *value = ins->value();
2079 const LAllocation *ptr = ins->ptr();
2080
2081 bool isSigned;
2082 int size;
2083 bool isFloat = false;
2084 switch (mir->viewType()) {
2085 case ArrayBufferView::TYPE_INT8: isSigned = true; size = 8; break;
2086 case ArrayBufferView::TYPE_UINT8: isSigned = false; size = 8; break;
2087 case ArrayBufferView::TYPE_INT16: isSigned = true; size = 16; break;
2088 case ArrayBufferView::TYPE_UINT16: isSigned = false; size = 16; break;
2089 case ArrayBufferView::TYPE_INT32: isSigned = true; size = 32; break;
2090 case ArrayBufferView::TYPE_UINT32: isSigned = false; size = 32; break;
2091 case ArrayBufferView::TYPE_FLOAT64: isFloat = true; size = 64; break;
2092 case ArrayBufferView::TYPE_FLOAT32: isFloat = true; size = 32; break;
2093 default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
2094 }
2095
2096 if (ptr->isConstant()) {
2097 MOZ_ASSERT(mir->skipBoundsCheck());
2098 int32_t ptrImm = ptr->toConstant()->toInt32();
2099 MOZ_ASSERT(ptrImm >= 0);
2100
2101 if (isFloat) {
2102 if (size == 32) {
2103 masm.storeFloat32(ToFloatRegister(value), Address(HeapReg, ptrImm));
2104 } else {
2105 masm.storeDouble(ToFloatRegister(value), Address(HeapReg, ptrImm));
2106 }
2107 } else {
2108 masm.ma_store(ToRegister(value), Address(HeapReg, ptrImm),
2109 static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
2110 }
2111 return true;
2112 }
2113
2114 Register ptrReg = ToRegister(ptr);
2115 Address dstAddr(ptrReg, 0);
2116
2117 if (mir->skipBoundsCheck()) {
2118 if (isFloat) {
2119 if (size == 32) {
2120 masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
2121 } else
2122 masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
2123 } else {
2124 masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne),
2125 static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
2126 }
2127 return true;
2128 }
2129
2130 BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister);
2131
2132 Label rejoin;
2133 masm.ma_b(ptrReg, ScratchRegister, &rejoin, Assembler::AboveOrEqual, ShortJump);
2134
2135 // Offset is ok, let's store value.
2136 if (isFloat) {
2137 if (size == 32) {
2138 masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
2139 } else
2140 masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
2141 } else {
2142 masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne),
2143 static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
2144 }
2145 masm.bind(&rejoin);
2146
2147 return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset()));
2148 }
2149
2150 bool
2151 CodeGeneratorMIPS::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins)
2152 {
2153 const MAsmJSPassStackArg *mir = ins->mir();
2154 if (ins->arg()->isConstant()) {
2155 masm.storePtr(ImmWord(ToInt32(ins->arg())), Address(StackPointer, mir->spOffset()));
2156 } else {
2157 if (ins->arg()->isGeneralReg()) {
2158 masm.storePtr(ToRegister(ins->arg()), Address(StackPointer, mir->spOffset()));
2159 } else {
2160 masm.storeDouble(ToFloatRegister(ins->arg()), Address(StackPointer, mir->spOffset()));
2161 }
2162 }
2163
2164 return true;
2165 }
2166
2167 bool
2168 CodeGeneratorMIPS::visitUDiv(LUDiv *ins)
2169 {
2170 Register lhs = ToRegister(ins->lhs());
2171 Register rhs = ToRegister(ins->rhs());
2172 Register output = ToRegister(ins->output());
2173
2174 Label done;
2175 if (ins->mir()->canBeDivideByZero()) {
2176 if (ins->mir()->isTruncated()) {
2177 Label notzero;
2178 masm.ma_b(rhs, rhs, &notzero, Assembler::NonZero, ShortJump);
2179 masm.move32(Imm32(0), output);
2180 masm.ma_b(&done, ShortJump);
2181 masm.bind(&notzero);
2182 } else {
2183 MOZ_ASSERT(ins->mir()->fallible());
2184 if (!bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()))
2185 return false;
2186 }
2187 }
2188
2189 masm.as_divu(lhs, rhs);
2190 masm.as_mflo(output);
2191
2192 if (!ins->mir()->isTruncated()) {
2193 if (!bailoutCmp32(Assembler::LessThan, output, Imm32(0), ins->snapshot()))
2194 return false;
2195 }
2196
2197 masm.bind(&done);
2198 return true;
2199 }
2200
2201 bool
2202 CodeGeneratorMIPS::visitUMod(LUMod *ins)
2203 {
2204 Register lhs = ToRegister(ins->lhs());
2205 Register rhs = ToRegister(ins->rhs());
2206 Register output = ToRegister(ins->output());
2207 Label done;
2208
2209 if (ins->mir()->canBeDivideByZero()) {
2210 if (ins->mir()->isTruncated()) {
2211 // Infinity|0 == 0
2212 Label notzero;
2213 masm.ma_b(rhs, rhs, &notzero, Assembler::NonZero, ShortJump);
2214 masm.move32(Imm32(0), output);
2215 masm.ma_b(&done, ShortJump);
2216 masm.bind(&notzero);
2217 } else {
2218 MOZ_ASSERT(ins->mir()->fallible());
2219 if (!bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()))
2220 return false;
2221 }
2222 }
2223
2224 masm.as_divu(lhs, rhs);
2225 masm.as_mfhi(output);
2226
2227 if (!ins->mir()->isTruncated()) {
2228 if (!bailoutCmp32(Assembler::LessThan, output, Imm32(0), ins->snapshot()))
2229 return false;
2230 }
2231
2232 masm.bind(&done);
2233 return true;
2234 }
2235
2236 bool
2237 CodeGeneratorMIPS::visitEffectiveAddress(LEffectiveAddress *ins)
2238 {
2239 const MEffectiveAddress *mir = ins->mir();
2240 Register base = ToRegister(ins->base());
2241 Register index = ToRegister(ins->index());
2242 Register output = ToRegister(ins->output());
2243
2244 BaseIndex address(base, index, mir->scale(), mir->displacement());
2245 masm.computeEffectiveAddress(address, output);
2246 return true;
2247 }
2248
2249 bool
2250 CodeGeneratorMIPS::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
2251 {
2252 const MAsmJSLoadGlobalVar *mir = ins->mir();
2253 unsigned addr = mir->globalDataOffset();
2254 if (mir->type() == MIRType_Int32)
2255 masm.load32(Address(GlobalReg, addr), ToRegister(ins->output()));
2256 else if (mir->type() == MIRType_Float32)
2257 masm.loadFloat32(Address(GlobalReg, addr), ToFloatRegister(ins->output()));
2258 else
2259 masm.loadDouble(Address(GlobalReg, addr), ToFloatRegister(ins->output()));
2260 return true;
2261 }
2262
2263 bool
2264 CodeGeneratorMIPS::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
2265 {
2266 const MAsmJSStoreGlobalVar *mir = ins->mir();
2267
2268 MIRType type = mir->value()->type();
2269 MOZ_ASSERT(IsNumberType(type));
2270 unsigned addr = mir->globalDataOffset();
2271 if (mir->value()->type() == MIRType_Int32)
2272 masm.store32(ToRegister(ins->value()), Address(GlobalReg, addr));
2273 else if (mir->value()->type() == MIRType_Float32)
2274 masm.storeFloat32(ToFloatRegister(ins->value()), Address(GlobalReg, addr));
2275 else
2276 masm.storeDouble(ToFloatRegister(ins->value()), Address(GlobalReg, addr));
2277 return true;
2278 }
2279
2280 bool
2281 CodeGeneratorMIPS::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins)
2282 {
2283 const MAsmJSLoadFuncPtr *mir = ins->mir();
2284
2285 Register index = ToRegister(ins->index());
2286 Register tmp = ToRegister(ins->temp());
2287 Register out = ToRegister(ins->output());
2288 unsigned addr = mir->globalDataOffset();
2289
2290 BaseIndex source(GlobalReg, index, TimesFour, addr);
2291 masm.load32(source, out);
2292 return true;
2293 }
2294
2295 bool
2296 CodeGeneratorMIPS::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins)
2297 {
2298 const MAsmJSLoadFFIFunc *mir = ins->mir();
2299 masm.loadPtr(Address(GlobalReg, mir->globalDataOffset()), ToRegister(ins->output()));
2300 return true;
2301 }
2302
2303 bool
2304 CodeGeneratorMIPS::visitNegI(LNegI *ins)
2305 {
2306 Register input = ToRegister(ins->input());
2307 Register output = ToRegister(ins->output());
2308
2309 masm.ma_negu(output, input);
2310 return true;
2311 }
2312
2313 bool
2314 CodeGeneratorMIPS::visitNegD(LNegD *ins)
2315 {
2316 FloatRegister input = ToFloatRegister(ins->input());
2317 FloatRegister output = ToFloatRegister(ins->output());
2318
2319 masm.as_negd(output, input);
2320 return true;
2321 }
2322
2323 bool
2324 CodeGeneratorMIPS::visitNegF(LNegF *ins)
2325 {
2326 FloatRegister input = ToFloatRegister(ins->input());
2327 FloatRegister output = ToFloatRegister(ins->output());
2328
2329 masm.as_negs(output, input);
2330 return true;
2331 }
2332
2333 bool
2334 CodeGeneratorMIPS::visitForkJoinGetSlice(LForkJoinGetSlice *ins)
2335 {
2336 MOZ_ASSUME_UNREACHABLE("NYI");
2337 }
2338
2339 JitCode *
2340 JitRuntime::generateForkJoinGetSliceStub(JSContext *cx)
2341 {
2342 MOZ_ASSUME_UNREACHABLE("NYI");
2343 }

mercurial