Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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-x86-shared.h"
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/MathAlgorithms.h"
12 #include "jsmath.h"
14 #include "jit/IonFrames.h"
15 #include "jit/IonLinker.h"
16 #include "jit/JitCompartment.h"
17 #include "jit/RangeAnalysis.h"
18 #include "vm/TraceLogging.h"
20 #include "jit/shared/CodeGenerator-shared-inl.h"
22 using namespace js;
23 using namespace js::jit;
25 using mozilla::Abs;
26 using mozilla::FloatingPoint;
27 using mozilla::FloorLog2;
28 using mozilla::NegativeInfinity;
29 using mozilla::SpecificNaN;
31 namespace js {
32 namespace jit {
34 CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
35 : CodeGeneratorShared(gen, graph, masm)
36 {
37 }
39 bool
40 CodeGeneratorX86Shared::generatePrologue()
41 {
42 JS_ASSERT(!gen->compilingAsmJS());
44 // Note that this automatically sets MacroAssembler::framePushed().
45 masm.reserveStack(frameSize());
47 return true;
48 }
50 bool
51 CodeGeneratorX86Shared::generateAsmJSPrologue(Label *stackOverflowLabel)
52 {
53 JS_ASSERT(gen->compilingAsmJS());
55 // The asm.js over-recursed handler wants to be able to assume that SP
56 // points to the return address, so perform the check before pushing
57 // frameDepth.
58 if (!omitOverRecursedCheck()) {
59 masm.branchPtr(Assembler::AboveOrEqual,
60 AsmJSAbsoluteAddress(AsmJSImm_StackLimit),
61 StackPointer,
62 stackOverflowLabel);
63 }
65 // Note that this automatically sets MacroAssembler::framePushed().
66 masm.reserveStack(frameSize());
67 return true;
68 }
70 bool
71 CodeGeneratorX86Shared::generateEpilogue()
72 {
73 masm.bind(&returnLabel_);
75 #ifdef JS_TRACE_LOGGING
76 if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
77 if (!emitTracelogStopEvent(TraceLogger::IonMonkey))
78 return false;
79 if (!emitTracelogScriptStop())
80 return false;
81 }
82 #endif
84 // Pop the stack we allocated at the start of the function.
85 masm.freeStack(frameSize());
86 JS_ASSERT(masm.framePushed() == 0);
88 masm.ret();
89 return true;
90 }
92 bool
93 OutOfLineBailout::accept(CodeGeneratorX86Shared *codegen)
94 {
95 return codegen->visitOutOfLineBailout(this);
96 }
98 void
99 CodeGeneratorX86Shared::emitBranch(Assembler::Condition cond, MBasicBlock *mirTrue,
100 MBasicBlock *mirFalse, Assembler::NaNCond ifNaN)
101 {
102 if (ifNaN == Assembler::NaN_IsFalse)
103 jumpToBlock(mirFalse, Assembler::Parity);
104 else if (ifNaN == Assembler::NaN_IsTrue)
105 jumpToBlock(mirTrue, Assembler::Parity);
107 if (isNextBlock(mirFalse->lir())) {
108 jumpToBlock(mirTrue, cond);
109 } else {
110 jumpToBlock(mirFalse, Assembler::InvertCondition(cond));
111 jumpToBlock(mirTrue);
112 }
113 }
115 bool
116 CodeGeneratorX86Shared::visitDouble(LDouble *ins)
117 {
118 const LDefinition *out = ins->getDef(0);
119 masm.loadConstantDouble(ins->getDouble(), ToFloatRegister(out));
120 return true;
121 }
123 bool
124 CodeGeneratorX86Shared::visitFloat32(LFloat32 *ins)
125 {
126 const LDefinition *out = ins->getDef(0);
127 masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out));
128 return true;
129 }
131 bool
132 CodeGeneratorX86Shared::visitTestIAndBranch(LTestIAndBranch *test)
133 {
134 const LAllocation *opd = test->input();
136 // Test the operand
137 masm.testl(ToRegister(opd), ToRegister(opd));
138 emitBranch(Assembler::NonZero, test->ifTrue(), test->ifFalse());
139 return true;
140 }
142 bool
143 CodeGeneratorX86Shared::visitTestDAndBranch(LTestDAndBranch *test)
144 {
145 const LAllocation *opd = test->input();
147 // ucomisd flags:
148 // Z P C
149 // ---------
150 // NaN 1 1 1
151 // > 0 0 0
152 // < 0 0 1
153 // = 1 0 0
154 //
155 // NaN is falsey, so comparing against 0 and then using the Z flag is
156 // enough to determine which branch to take.
157 masm.xorpd(ScratchFloatReg, ScratchFloatReg);
158 masm.ucomisd(ToFloatRegister(opd), ScratchFloatReg);
159 emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse());
160 return true;
161 }
163 bool
164 CodeGeneratorX86Shared::visitTestFAndBranch(LTestFAndBranch *test)
165 {
166 const LAllocation *opd = test->input();
167 // ucomiss flags are the same as doubles; see comment above
168 masm.xorps(ScratchFloatReg, ScratchFloatReg);
169 masm.ucomiss(ToFloatRegister(opd), ScratchFloatReg);
170 emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse());
171 return true;
172 }
174 bool
175 CodeGeneratorX86Shared::visitBitAndAndBranch(LBitAndAndBranch *baab)
176 {
177 if (baab->right()->isConstant())
178 masm.testl(ToRegister(baab->left()), Imm32(ToInt32(baab->right())));
179 else
180 masm.testl(ToRegister(baab->left()), ToRegister(baab->right()));
181 emitBranch(Assembler::NonZero, baab->ifTrue(), baab->ifFalse());
182 return true;
183 }
185 void
186 CodeGeneratorX86Shared::emitCompare(MCompare::CompareType type, const LAllocation *left, const LAllocation *right)
187 {
188 #ifdef JS_CODEGEN_X64
189 if (type == MCompare::Compare_Object) {
190 masm.cmpq(ToRegister(left), ToOperand(right));
191 return;
192 }
193 #endif
195 if (right->isConstant())
196 masm.cmpl(ToRegister(left), Imm32(ToInt32(right)));
197 else
198 masm.cmpl(ToRegister(left), ToOperand(right));
199 }
201 bool
202 CodeGeneratorX86Shared::visitCompare(LCompare *comp)
203 {
204 MCompare *mir = comp->mir();
205 emitCompare(mir->compareType(), comp->left(), comp->right());
206 masm.emitSet(JSOpToCondition(mir->compareType(), comp->jsop()), ToRegister(comp->output()));
207 return true;
208 }
210 bool
211 CodeGeneratorX86Shared::visitCompareAndBranch(LCompareAndBranch *comp)
212 {
213 MCompare *mir = comp->cmpMir();
214 emitCompare(mir->compareType(), comp->left(), comp->right());
215 Assembler::Condition cond = JSOpToCondition(mir->compareType(), comp->jsop());
216 emitBranch(cond, comp->ifTrue(), comp->ifFalse());
217 return true;
218 }
220 bool
221 CodeGeneratorX86Shared::visitCompareD(LCompareD *comp)
222 {
223 FloatRegister lhs = ToFloatRegister(comp->left());
224 FloatRegister rhs = ToFloatRegister(comp->right());
226 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
228 Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond);
229 if (comp->mir()->operandsAreNeverNaN())
230 nanCond = Assembler::NaN_HandledByCond;
232 masm.compareDouble(cond, lhs, rhs);
233 masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()), nanCond);
234 return true;
235 }
237 bool
238 CodeGeneratorX86Shared::visitCompareF(LCompareF *comp)
239 {
240 FloatRegister lhs = ToFloatRegister(comp->left());
241 FloatRegister rhs = ToFloatRegister(comp->right());
243 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
245 Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond);
246 if (comp->mir()->operandsAreNeverNaN())
247 nanCond = Assembler::NaN_HandledByCond;
249 masm.compareFloat(cond, lhs, rhs);
250 masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()), nanCond);
251 return true;
252 }
254 bool
255 CodeGeneratorX86Shared::visitNotI(LNotI *ins)
256 {
257 masm.cmpl(ToRegister(ins->input()), Imm32(0));
258 masm.emitSet(Assembler::Equal, ToRegister(ins->output()));
259 return true;
260 }
262 bool
263 CodeGeneratorX86Shared::visitNotD(LNotD *ins)
264 {
265 FloatRegister opd = ToFloatRegister(ins->input());
267 // Not returns true if the input is a NaN. We don't have to worry about
268 // it if we know the input is never NaN though.
269 Assembler::NaNCond nanCond = Assembler::NaN_IsTrue;
270 if (ins->mir()->operandIsNeverNaN())
271 nanCond = Assembler::NaN_HandledByCond;
273 masm.xorpd(ScratchFloatReg, ScratchFloatReg);
274 masm.compareDouble(Assembler::DoubleEqualOrUnordered, opd, ScratchFloatReg);
275 masm.emitSet(Assembler::Equal, ToRegister(ins->output()), nanCond);
276 return true;
277 }
279 bool
280 CodeGeneratorX86Shared::visitNotF(LNotF *ins)
281 {
282 FloatRegister opd = ToFloatRegister(ins->input());
284 // Not returns true if the input is a NaN. We don't have to worry about
285 // it if we know the input is never NaN though.
286 Assembler::NaNCond nanCond = Assembler::NaN_IsTrue;
287 if (ins->mir()->operandIsNeverNaN())
288 nanCond = Assembler::NaN_HandledByCond;
290 masm.xorps(ScratchFloatReg, ScratchFloatReg);
291 masm.compareFloat(Assembler::DoubleEqualOrUnordered, opd, ScratchFloatReg);
292 masm.emitSet(Assembler::Equal, ToRegister(ins->output()), nanCond);
293 return true;
294 }
296 bool
297 CodeGeneratorX86Shared::visitCompareDAndBranch(LCompareDAndBranch *comp)
298 {
299 FloatRegister lhs = ToFloatRegister(comp->left());
300 FloatRegister rhs = ToFloatRegister(comp->right());
302 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop());
304 Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond);
305 if (comp->cmpMir()->operandsAreNeverNaN())
306 nanCond = Assembler::NaN_HandledByCond;
308 masm.compareDouble(cond, lhs, rhs);
309 emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse(), nanCond);
310 return true;
311 }
313 bool
314 CodeGeneratorX86Shared::visitCompareFAndBranch(LCompareFAndBranch *comp)
315 {
316 FloatRegister lhs = ToFloatRegister(comp->left());
317 FloatRegister rhs = ToFloatRegister(comp->right());
319 Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop());
321 Assembler::NaNCond nanCond = Assembler::NaNCondFromDoubleCondition(cond);
322 if (comp->cmpMir()->operandsAreNeverNaN())
323 nanCond = Assembler::NaN_HandledByCond;
325 masm.compareFloat(cond, lhs, rhs);
326 emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse(), nanCond);
327 return true;
328 }
330 bool
331 CodeGeneratorX86Shared::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins)
332 {
333 const MAsmJSPassStackArg *mir = ins->mir();
334 Address dst(StackPointer, mir->spOffset());
335 if (ins->arg()->isConstant()) {
336 masm.storePtr(ImmWord(ToInt32(ins->arg())), dst);
337 } else {
338 if (ins->arg()->isGeneralReg())
339 masm.storePtr(ToRegister(ins->arg()), dst);
340 else
341 masm.storeDouble(ToFloatRegister(ins->arg()), dst);
342 }
343 return true;
344 }
346 bool
347 CodeGeneratorX86Shared::generateOutOfLineCode()
348 {
349 if (!CodeGeneratorShared::generateOutOfLineCode())
350 return false;
352 if (deoptLabel_.used()) {
353 // All non-table-based bailouts will go here.
354 masm.bind(&deoptLabel_);
356 // Push the frame size, so the handler can recover the IonScript.
357 masm.push(Imm32(frameSize()));
359 JitCode *handler = gen->jitRuntime()->getGenericBailoutHandler();
360 masm.jmp(ImmPtr(handler->raw()), Relocation::JITCODE);
361 }
363 return true;
364 }
366 class BailoutJump {
367 Assembler::Condition cond_;
369 public:
370 BailoutJump(Assembler::Condition cond) : cond_(cond)
371 { }
372 #ifdef JS_CODEGEN_X86
373 void operator()(MacroAssembler &masm, uint8_t *code) const {
374 masm.j(cond_, ImmPtr(code), Relocation::HARDCODED);
375 }
376 #endif
377 void operator()(MacroAssembler &masm, Label *label) const {
378 masm.j(cond_, label);
379 }
380 };
382 class BailoutLabel {
383 Label *label_;
385 public:
386 BailoutLabel(Label *label) : label_(label)
387 { }
388 #ifdef JS_CODEGEN_X86
389 void operator()(MacroAssembler &masm, uint8_t *code) const {
390 masm.retarget(label_, ImmPtr(code), Relocation::HARDCODED);
391 }
392 #endif
393 void operator()(MacroAssembler &masm, Label *label) const {
394 masm.retarget(label_, label);
395 }
396 };
398 template <typename T> bool
399 CodeGeneratorX86Shared::bailout(const T &binder, LSnapshot *snapshot)
400 {
401 CompileInfo &info = snapshot->mir()->block()->info();
402 switch (info.executionMode()) {
403 case ParallelExecution: {
404 // in parallel mode, make no attempt to recover, just signal an error.
405 OutOfLineAbortPar *ool = oolAbortPar(ParallelBailoutUnsupported,
406 snapshot->mir()->block(),
407 snapshot->mir()->pc());
408 binder(masm, ool->entry());
409 return true;
410 }
411 case SequentialExecution:
412 break;
413 default:
414 MOZ_ASSUME_UNREACHABLE("No such execution mode");
415 }
417 if (!encode(snapshot))
418 return false;
420 // Though the assembler doesn't track all frame pushes, at least make sure
421 // the known value makes sense. We can't use bailout tables if the stack
422 // isn't properly aligned to the static frame size.
423 JS_ASSERT_IF(frameClass_ != FrameSizeClass::None() && deoptTable_,
424 frameClass_.frameSize() == masm.framePushed());
426 #ifdef JS_CODEGEN_X86
427 // On x64, bailout tables are pointless, because 16 extra bytes are
428 // reserved per external jump, whereas it takes only 10 bytes to encode a
429 // a non-table based bailout.
430 if (assignBailoutId(snapshot)) {
431 binder(masm, deoptTable_->raw() + snapshot->bailoutId() * BAILOUT_TABLE_ENTRY_SIZE);
432 return true;
433 }
434 #endif
436 // We could not use a jump table, either because all bailout IDs were
437 // reserved, or a jump table is not optimal for this frame size or
438 // platform. Whatever, we will generate a lazy bailout.
439 OutOfLineBailout *ool = new(alloc()) OutOfLineBailout(snapshot);
440 if (!addOutOfLineCode(ool))
441 return false;
443 binder(masm, ool->entry());
444 return true;
445 }
447 bool
448 CodeGeneratorX86Shared::bailoutIf(Assembler::Condition condition, LSnapshot *snapshot)
449 {
450 return bailout(BailoutJump(condition), snapshot);
451 }
453 bool
454 CodeGeneratorX86Shared::bailoutIf(Assembler::DoubleCondition condition, LSnapshot *snapshot)
455 {
456 JS_ASSERT(Assembler::NaNCondFromDoubleCondition(condition) == Assembler::NaN_HandledByCond);
457 return bailoutIf(Assembler::ConditionFromDoubleCondition(condition), snapshot);
458 }
460 bool
461 CodeGeneratorX86Shared::bailoutFrom(Label *label, LSnapshot *snapshot)
462 {
463 JS_ASSERT(label->used() && !label->bound());
464 return bailout(BailoutLabel(label), snapshot);
465 }
467 bool
468 CodeGeneratorX86Shared::bailout(LSnapshot *snapshot)
469 {
470 Label label;
471 masm.jump(&label);
472 return bailoutFrom(&label, snapshot);
473 }
475 bool
476 CodeGeneratorX86Shared::visitOutOfLineBailout(OutOfLineBailout *ool)
477 {
478 masm.push(Imm32(ool->snapshot()->snapshotOffset()));
479 masm.jmp(&deoptLabel_);
480 return true;
481 }
483 bool
484 CodeGeneratorX86Shared::visitMinMaxD(LMinMaxD *ins)
485 {
486 FloatRegister first = ToFloatRegister(ins->first());
487 FloatRegister second = ToFloatRegister(ins->second());
488 #ifdef DEBUG
489 FloatRegister output = ToFloatRegister(ins->output());
490 JS_ASSERT(first == output);
491 #endif
493 Label done, nan, minMaxInst;
495 // Do a ucomisd to catch equality and NaNs, which both require special
496 // handling. If the operands are ordered and inequal, we branch straight to
497 // the min/max instruction. If we wanted, we could also branch for less-than
498 // or greater-than here instead of using min/max, however these conditions
499 // will sometimes be hard on the branch predictor.
500 masm.ucomisd(first, second);
501 masm.j(Assembler::NotEqual, &minMaxInst);
502 if (!ins->mir()->range() || ins->mir()->range()->canBeNaN())
503 masm.j(Assembler::Parity, &nan);
505 // Ordered and equal. The operands are bit-identical unless they are zero
506 // and negative zero. These instructions merge the sign bits in that
507 // case, and are no-ops otherwise.
508 if (ins->mir()->isMax())
509 masm.andpd(second, first);
510 else
511 masm.orpd(second, first);
512 masm.jump(&done);
514 // x86's min/max are not symmetric; if either operand is a NaN, they return
515 // the read-only operand. We need to return a NaN if either operand is a
516 // NaN, so we explicitly check for a NaN in the read-write operand.
517 if (!ins->mir()->range() || ins->mir()->range()->canBeNaN()) {
518 masm.bind(&nan);
519 masm.ucomisd(first, first);
520 masm.j(Assembler::Parity, &done);
521 }
523 // When the values are inequal, or second is NaN, x86's min and max will
524 // return the value we need.
525 masm.bind(&minMaxInst);
526 if (ins->mir()->isMax())
527 masm.maxsd(second, first);
528 else
529 masm.minsd(second, first);
531 masm.bind(&done);
532 return true;
533 }
535 bool
536 CodeGeneratorX86Shared::visitAbsD(LAbsD *ins)
537 {
538 FloatRegister input = ToFloatRegister(ins->input());
539 JS_ASSERT(input == ToFloatRegister(ins->output()));
540 // Load a value which is all ones except for the sign bit.
541 masm.loadConstantDouble(SpecificNaN<double>(0, FloatingPoint<double>::SignificandBits),
542 ScratchFloatReg);
543 masm.andpd(ScratchFloatReg, input);
544 return true;
545 }
547 bool
548 CodeGeneratorX86Shared::visitAbsF(LAbsF *ins)
549 {
550 FloatRegister input = ToFloatRegister(ins->input());
551 JS_ASSERT(input == ToFloatRegister(ins->output()));
552 // Same trick as visitAbsD above.
553 masm.loadConstantFloat32(SpecificNaN<float>(0, FloatingPoint<float>::SignificandBits),
554 ScratchFloatReg);
555 masm.andps(ScratchFloatReg, input);
556 return true;
557 }
559 bool
560 CodeGeneratorX86Shared::visitSqrtD(LSqrtD *ins)
561 {
562 FloatRegister input = ToFloatRegister(ins->input());
563 FloatRegister output = ToFloatRegister(ins->output());
564 masm.sqrtsd(input, output);
565 return true;
566 }
568 bool
569 CodeGeneratorX86Shared::visitSqrtF(LSqrtF *ins)
570 {
571 FloatRegister input = ToFloatRegister(ins->input());
572 FloatRegister output = ToFloatRegister(ins->output());
573 masm.sqrtss(input, output);
574 return true;
575 }
577 bool
578 CodeGeneratorX86Shared::visitPowHalfD(LPowHalfD *ins)
579 {
580 FloatRegister input = ToFloatRegister(ins->input());
581 JS_ASSERT(input == ToFloatRegister(ins->output()));
583 Label done, sqrt;
585 if (!ins->mir()->operandIsNeverNegativeInfinity()) {
586 // Branch if not -Infinity.
587 masm.loadConstantDouble(NegativeInfinity<double>(), ScratchFloatReg);
589 Assembler::DoubleCondition cond = Assembler::DoubleNotEqualOrUnordered;
590 if (ins->mir()->operandIsNeverNaN())
591 cond = Assembler::DoubleNotEqual;
592 masm.branchDouble(cond, input, ScratchFloatReg, &sqrt);
594 // Math.pow(-Infinity, 0.5) == Infinity.
595 masm.xorpd(input, input);
596 masm.subsd(ScratchFloatReg, input);
597 masm.jump(&done);
599 masm.bind(&sqrt);
600 }
602 if (!ins->mir()->operandIsNeverNegativeZero()) {
603 // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). Adding 0 converts any -0 to 0.
604 masm.xorpd(ScratchFloatReg, ScratchFloatReg);
605 masm.addsd(ScratchFloatReg, input);
606 }
608 masm.sqrtsd(input, input);
610 masm.bind(&done);
611 return true;
612 }
614 class OutOfLineUndoALUOperation : public OutOfLineCodeBase<CodeGeneratorX86Shared>
615 {
616 LInstruction *ins_;
618 public:
619 OutOfLineUndoALUOperation(LInstruction *ins)
620 : ins_(ins)
621 { }
623 virtual bool accept(CodeGeneratorX86Shared *codegen) {
624 return codegen->visitOutOfLineUndoALUOperation(this);
625 }
626 LInstruction *ins() const {
627 return ins_;
628 }
629 };
631 bool
632 CodeGeneratorX86Shared::visitAddI(LAddI *ins)
633 {
634 if (ins->rhs()->isConstant())
635 masm.addl(Imm32(ToInt32(ins->rhs())), ToOperand(ins->lhs()));
636 else
637 masm.addl(ToOperand(ins->rhs()), ToRegister(ins->lhs()));
639 if (ins->snapshot()) {
640 if (ins->recoversInput()) {
641 OutOfLineUndoALUOperation *ool = new(alloc()) OutOfLineUndoALUOperation(ins);
642 if (!addOutOfLineCode(ool))
643 return false;
644 masm.j(Assembler::Overflow, ool->entry());
645 } else {
646 if (!bailoutIf(Assembler::Overflow, ins->snapshot()))
647 return false;
648 }
649 }
650 return true;
651 }
653 bool
654 CodeGeneratorX86Shared::visitSubI(LSubI *ins)
655 {
656 if (ins->rhs()->isConstant())
657 masm.subl(Imm32(ToInt32(ins->rhs())), ToOperand(ins->lhs()));
658 else
659 masm.subl(ToOperand(ins->rhs()), ToRegister(ins->lhs()));
661 if (ins->snapshot()) {
662 if (ins->recoversInput()) {
663 OutOfLineUndoALUOperation *ool = new(alloc()) OutOfLineUndoALUOperation(ins);
664 if (!addOutOfLineCode(ool))
665 return false;
666 masm.j(Assembler::Overflow, ool->entry());
667 } else {
668 if (!bailoutIf(Assembler::Overflow, ins->snapshot()))
669 return false;
670 }
671 }
672 return true;
673 }
675 bool
676 CodeGeneratorX86Shared::visitOutOfLineUndoALUOperation(OutOfLineUndoALUOperation *ool)
677 {
678 LInstruction *ins = ool->ins();
679 Register reg = ToRegister(ins->getDef(0));
681 mozilla::DebugOnly<LAllocation *> lhs = ins->getOperand(0);
682 LAllocation *rhs = ins->getOperand(1);
684 JS_ASSERT(reg == ToRegister(lhs));
685 JS_ASSERT_IF(rhs->isGeneralReg(), reg != ToRegister(rhs));
687 // Undo the effect of the ALU operation, which was performed on the output
688 // register and overflowed. Writing to the output register clobbered an
689 // input reg, and the original value of the input needs to be recovered
690 // to satisfy the constraint imposed by any RECOVERED_INPUT operands to
691 // the bailout snapshot.
693 if (rhs->isConstant()) {
694 Imm32 constant(ToInt32(rhs));
695 if (ins->isAddI())
696 masm.subl(constant, reg);
697 else
698 masm.addl(constant, reg);
699 } else {
700 if (ins->isAddI())
701 masm.subl(ToOperand(rhs), reg);
702 else
703 masm.addl(ToOperand(rhs), reg);
704 }
706 return bailout(ool->ins()->snapshot());
707 }
709 class MulNegativeZeroCheck : public OutOfLineCodeBase<CodeGeneratorX86Shared>
710 {
711 LMulI *ins_;
713 public:
714 MulNegativeZeroCheck(LMulI *ins)
715 : ins_(ins)
716 { }
718 virtual bool accept(CodeGeneratorX86Shared *codegen) {
719 return codegen->visitMulNegativeZeroCheck(this);
720 }
721 LMulI *ins() const {
722 return ins_;
723 }
724 };
726 bool
727 CodeGeneratorX86Shared::visitMulI(LMulI *ins)
728 {
729 const LAllocation *lhs = ins->lhs();
730 const LAllocation *rhs = ins->rhs();
731 MMul *mul = ins->mir();
732 JS_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow());
734 if (rhs->isConstant()) {
735 // Bailout on -0.0
736 int32_t constant = ToInt32(rhs);
737 if (mul->canBeNegativeZero() && constant <= 0) {
738 Assembler::Condition bailoutCond = (constant == 0) ? Assembler::Signed : Assembler::Equal;
739 masm.testl(ToRegister(lhs), ToRegister(lhs));
740 if (!bailoutIf(bailoutCond, ins->snapshot()))
741 return false;
742 }
744 switch (constant) {
745 case -1:
746 masm.negl(ToOperand(lhs));
747 break;
748 case 0:
749 masm.xorl(ToOperand(lhs), ToRegister(lhs));
750 return true; // escape overflow check;
751 case 1:
752 // nop
753 return true; // escape overflow check;
754 case 2:
755 masm.addl(ToOperand(lhs), ToRegister(lhs));
756 break;
757 default:
758 if (!mul->canOverflow() && constant > 0) {
759 // Use shift if cannot overflow and constant is power of 2
760 int32_t shift = FloorLog2(constant);
761 if ((1 << shift) == constant) {
762 masm.shll(Imm32(shift), ToRegister(lhs));
763 return true;
764 }
765 }
766 masm.imull(Imm32(ToInt32(rhs)), ToRegister(lhs));
767 }
769 // Bailout on overflow
770 if (mul->canOverflow() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
771 return false;
772 } else {
773 masm.imull(ToOperand(rhs), ToRegister(lhs));
775 // Bailout on overflow
776 if (mul->canOverflow() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
777 return false;
779 if (mul->canBeNegativeZero()) {
780 // Jump to an OOL path if the result is 0.
781 MulNegativeZeroCheck *ool = new(alloc()) MulNegativeZeroCheck(ins);
782 if (!addOutOfLineCode(ool))
783 return false;
785 masm.testl(ToRegister(lhs), ToRegister(lhs));
786 masm.j(Assembler::Zero, ool->entry());
787 masm.bind(ool->rejoin());
788 }
789 }
791 return true;
792 }
794 class ReturnZero : public OutOfLineCodeBase<CodeGeneratorX86Shared>
795 {
796 Register reg_;
798 public:
799 explicit ReturnZero(Register reg)
800 : reg_(reg)
801 { }
803 virtual bool accept(CodeGeneratorX86Shared *codegen) {
804 return codegen->visitReturnZero(this);
805 }
806 Register reg() const {
807 return reg_;
808 }
809 };
811 bool
812 CodeGeneratorX86Shared::visitReturnZero(ReturnZero *ool)
813 {
814 masm.mov(ImmWord(0), ool->reg());
815 masm.jmp(ool->rejoin());
816 return true;
817 }
819 bool
820 CodeGeneratorX86Shared::visitUDivOrMod(LUDivOrMod *ins)
821 {
822 Register lhs = ToRegister(ins->lhs());
823 Register rhs = ToRegister(ins->rhs());
824 Register output = ToRegister(ins->output());
826 JS_ASSERT_IF(lhs != rhs, rhs != eax);
827 JS_ASSERT(rhs != edx);
828 JS_ASSERT_IF(output == eax, ToRegister(ins->remainder()) == edx);
830 ReturnZero *ool = nullptr;
832 // Put the lhs in eax.
833 if (lhs != eax)
834 masm.mov(lhs, eax);
836 // Prevent divide by zero.
837 if (ins->canBeDivideByZero()) {
838 masm.testl(rhs, rhs);
839 if (ins->mir()->isTruncated()) {
840 if (!ool)
841 ool = new(alloc()) ReturnZero(output);
842 masm.j(Assembler::Zero, ool->entry());
843 } else {
844 if (!bailoutIf(Assembler::Zero, ins->snapshot()))
845 return false;
846 }
847 }
849 // Zero extend the lhs into edx to make (edx:eax), since udiv is 64-bit.
850 masm.mov(ImmWord(0), edx);
851 masm.udiv(rhs);
853 // Unsigned div or mod can return a value that's not a signed int32.
854 // If our users aren't expecting that, bail.
855 if (!ins->mir()->isTruncated()) {
856 masm.testl(output, output);
857 if (!bailoutIf(Assembler::Signed, ins->snapshot()))
858 return false;
859 }
861 if (ool) {
862 if (!addOutOfLineCode(ool))
863 return false;
864 masm.bind(ool->rejoin());
865 }
867 return true;
868 }
870 bool
871 CodeGeneratorX86Shared::visitMulNegativeZeroCheck(MulNegativeZeroCheck *ool)
872 {
873 LMulI *ins = ool->ins();
874 Register result = ToRegister(ins->output());
875 Operand lhsCopy = ToOperand(ins->lhsCopy());
876 Operand rhs = ToOperand(ins->rhs());
877 JS_ASSERT_IF(lhsCopy.kind() == Operand::REG, lhsCopy.reg() != result.code());
879 // Result is -0 if lhs or rhs is negative.
880 masm.movl(lhsCopy, result);
881 masm.orl(rhs, result);
882 if (!bailoutIf(Assembler::Signed, ins->snapshot()))
883 return false;
885 masm.mov(ImmWord(0), result);
886 masm.jmp(ool->rejoin());
887 return true;
888 }
890 bool
891 CodeGeneratorX86Shared::visitDivPowTwoI(LDivPowTwoI *ins)
892 {
893 Register lhs = ToRegister(ins->numerator());
894 mozilla::DebugOnly<Register> output = ToRegister(ins->output());
896 int32_t shift = ins->shift();
897 bool negativeDivisor = ins->negativeDivisor();
898 MDiv *mir = ins->mir();
900 // We use defineReuseInput so these should always be the same, which is
901 // convenient since all of our instructions here are two-address.
902 JS_ASSERT(lhs == output);
904 if (!mir->isTruncated() && negativeDivisor) {
905 // 0 divided by a negative number must return a double.
906 masm.testl(lhs, lhs);
907 if (!bailoutIf(Assembler::Zero, ins->snapshot()))
908 return false;
909 }
911 if (shift != 0) {
912 if (!mir->isTruncated()) {
913 // If the remainder is != 0, bailout since this must be a double.
914 masm.testl(lhs, Imm32(UINT32_MAX >> (32 - shift)));
915 if (!bailoutIf(Assembler::NonZero, ins->snapshot()))
916 return false;
917 }
919 // Adjust the value so that shifting produces a correctly rounded result
920 // when the numerator is negative. See 10-1 "Signed Division by a Known
921 // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight.
922 if (mir->canBeNegativeDividend()) {
923 Register lhsCopy = ToRegister(ins->numeratorCopy());
924 JS_ASSERT(lhsCopy != lhs);
925 if (shift > 1)
926 masm.sarl(Imm32(31), lhs);
927 masm.shrl(Imm32(32 - shift), lhs);
928 masm.addl(lhsCopy, lhs);
929 }
931 masm.sarl(Imm32(shift), lhs);
932 if (negativeDivisor)
933 masm.negl(lhs);
934 } else if (shift == 0 && negativeDivisor) {
935 // INT32_MIN / -1 overflows.
936 masm.negl(lhs);
937 if (!mir->isTruncated() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
938 return false;
939 }
941 return true;
942 }
944 bool
945 CodeGeneratorX86Shared::visitDivOrModConstantI(LDivOrModConstantI *ins) {
946 Register lhs = ToRegister(ins->numerator());
947 Register output = ToRegister(ins->output());
948 int32_t d = ins->denominator();
950 // This emits the division answer into edx or the modulus answer into eax.
951 JS_ASSERT(output == eax || output == edx);
952 JS_ASSERT(lhs != eax && lhs != edx);
953 bool isDiv = (output == edx);
955 // The absolute value of the denominator isn't a power of 2 (see LDivPowTwoI
956 // and LModPowTwoI).
957 JS_ASSERT((Abs(d) & (Abs(d) - 1)) != 0);
959 // We will first divide by Abs(d), and negate the answer if d is negative.
960 // If desired, this can be avoided by generalizing computeDivisionConstants.
961 ReciprocalMulConstants rmc = computeDivisionConstants(Abs(d));
963 // As explained in the comments of computeDivisionConstants, we first compute
964 // X >> (32 + shift), where X is either (rmc.multiplier * n) if the multiplier
965 // is non-negative or (rmc.multiplier * n) + (2^32 * n) otherwise. This is the
966 // desired division result if n is non-negative, and is one less than the result
967 // otherwise.
968 masm.movl(Imm32(rmc.multiplier), eax);
969 masm.imull(lhs);
970 if (rmc.multiplier < 0)
971 masm.addl(lhs, edx);
972 masm.sarl(Imm32(rmc.shiftAmount), edx);
974 // We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be
975 // computed with just a sign-extending shift of 31 bits.
976 if (ins->canBeNegativeDividend()) {
977 masm.movl(lhs, eax);
978 masm.sarl(Imm32(31), eax);
979 masm.subl(eax, edx);
980 }
982 // After this, edx contains the correct truncated division result.
983 if (d < 0)
984 masm.negl(edx);
986 if (!isDiv) {
987 masm.imull(Imm32(-d), edx, eax);
988 masm.addl(lhs, eax);
989 }
991 if (!ins->mir()->isTruncated()) {
992 if (isDiv) {
993 // This is a division op. Multiply the obtained value by d to check if
994 // the correct answer is an integer. This cannot overflow, since |d| > 1.
995 masm.imull(Imm32(d), edx, eax);
996 masm.cmpl(lhs, eax);
997 if (!bailoutIf(Assembler::NotEqual, ins->snapshot()))
998 return false;
1000 // If lhs is zero and the divisor is negative, the answer should have
1001 // been -0.
1002 if (d < 0) {
1003 masm.testl(lhs, lhs);
1004 if (!bailoutIf(Assembler::Zero, ins->snapshot()))
1005 return false;
1006 }
1007 } else if (ins->canBeNegativeDividend()) {
1008 // This is a mod op. If the computed value is zero and lhs
1009 // is negative, the answer should have been -0.
1010 Label done;
1012 masm.cmpl(lhs, Imm32(0));
1013 masm.j(Assembler::GreaterThanOrEqual, &done);
1015 masm.testl(eax, eax);
1016 if (!bailoutIf(Assembler::Zero, ins->snapshot()))
1017 return false;
1019 masm.bind(&done);
1020 }
1021 }
1023 return true;
1024 }
1026 bool
1027 CodeGeneratorX86Shared::visitDivI(LDivI *ins)
1028 {
1029 Register remainder = ToRegister(ins->remainder());
1030 Register lhs = ToRegister(ins->lhs());
1031 Register rhs = ToRegister(ins->rhs());
1032 Register output = ToRegister(ins->output());
1034 MDiv *mir = ins->mir();
1036 JS_ASSERT_IF(lhs != rhs, rhs != eax);
1037 JS_ASSERT(rhs != edx);
1038 JS_ASSERT(remainder == edx);
1039 JS_ASSERT(output == eax);
1041 Label done;
1042 ReturnZero *ool = nullptr;
1044 // Put the lhs in eax, for either the negative overflow case or the regular
1045 // divide case.
1046 if (lhs != eax)
1047 masm.mov(lhs, eax);
1049 // Handle divide by zero.
1050 if (mir->canBeDivideByZero()) {
1051 masm.testl(rhs, rhs);
1052 if (mir->canTruncateInfinities()) {
1053 // Truncated division by zero is zero (Infinity|0 == 0)
1054 if (!ool)
1055 ool = new(alloc()) ReturnZero(output);
1056 masm.j(Assembler::Zero, ool->entry());
1057 } else {
1058 JS_ASSERT(mir->fallible());
1059 if (!bailoutIf(Assembler::Zero, ins->snapshot()))
1060 return false;
1061 }
1062 }
1064 // Handle an integer overflow exception from -2147483648 / -1.
1065 if (mir->canBeNegativeOverflow()) {
1066 Label notmin;
1067 masm.cmpl(lhs, Imm32(INT32_MIN));
1068 masm.j(Assembler::NotEqual, ¬min);
1069 masm.cmpl(rhs, Imm32(-1));
1070 if (mir->canTruncateOverflow()) {
1071 // (-INT32_MIN)|0 == INT32_MIN and INT32_MIN is already in the
1072 // output register (lhs == eax).
1073 masm.j(Assembler::Equal, &done);
1074 } else {
1075 JS_ASSERT(mir->fallible());
1076 if (!bailoutIf(Assembler::Equal, ins->snapshot()))
1077 return false;
1078 }
1079 masm.bind(¬min);
1080 }
1082 // Handle negative 0.
1083 if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) {
1084 Label nonzero;
1085 masm.testl(lhs, lhs);
1086 masm.j(Assembler::NonZero, &nonzero);
1087 masm.cmpl(rhs, Imm32(0));
1088 if (!bailoutIf(Assembler::LessThan, ins->snapshot()))
1089 return false;
1090 masm.bind(&nonzero);
1091 }
1093 // Sign extend the lhs into edx to make (edx:eax), since idiv is 64-bit.
1094 if (lhs != eax)
1095 masm.mov(lhs, eax);
1096 masm.cdq();
1097 masm.idiv(rhs);
1099 if (!mir->canTruncateRemainder()) {
1100 // If the remainder is > 0, bailout since this must be a double.
1101 masm.testl(remainder, remainder);
1102 if (!bailoutIf(Assembler::NonZero, ins->snapshot()))
1103 return false;
1104 }
1106 masm.bind(&done);
1108 if (ool) {
1109 if (!addOutOfLineCode(ool))
1110 return false;
1111 masm.bind(ool->rejoin());
1112 }
1114 return true;
1115 }
1117 bool
1118 CodeGeneratorX86Shared::visitModPowTwoI(LModPowTwoI *ins)
1119 {
1120 Register lhs = ToRegister(ins->getOperand(0));
1121 int32_t shift = ins->shift();
1123 Label negative;
1125 if (ins->mir()->canBeNegativeDividend()) {
1126 // Switch based on sign of the lhs.
1127 // Positive numbers are just a bitmask
1128 masm.branchTest32(Assembler::Signed, lhs, lhs, &negative);
1129 }
1131 masm.andl(Imm32((uint32_t(1) << shift) - 1), lhs);
1133 if (ins->mir()->canBeNegativeDividend()) {
1134 Label done;
1135 masm.jump(&done);
1137 // Negative numbers need a negate, bitmask, negate
1138 masm.bind(&negative);
1140 // Unlike in the visitModI case, we are not computing the mod by means of a
1141 // division. Therefore, the divisor = -1 case isn't problematic (the andl
1142 // always returns 0, which is what we expect).
1143 //
1144 // The negl instruction overflows if lhs == INT32_MIN, but this is also not
1145 // a problem: shift is at most 31, and so the andl also always returns 0.
1146 masm.negl(lhs);
1147 masm.andl(Imm32((uint32_t(1) << shift) - 1), lhs);
1148 masm.negl(lhs);
1150 // Since a%b has the same sign as b, and a is negative in this branch,
1151 // an answer of 0 means the correct result is actually -0. Bail out.
1152 if (!ins->mir()->isTruncated() && !bailoutIf(Assembler::Zero, ins->snapshot()))
1153 return false;
1154 masm.bind(&done);
1155 }
1156 return true;
1158 }
1160 class ModOverflowCheck : public OutOfLineCodeBase<CodeGeneratorX86Shared>
1161 {
1162 Label done_;
1163 LModI *ins_;
1164 Register rhs_;
1166 public:
1167 explicit ModOverflowCheck(LModI *ins, Register rhs)
1168 : ins_(ins), rhs_(rhs)
1169 { }
1171 virtual bool accept(CodeGeneratorX86Shared *codegen) {
1172 return codegen->visitModOverflowCheck(this);
1173 }
1174 Label *done() {
1175 return &done_;
1176 }
1177 LModI *ins() const {
1178 return ins_;
1179 }
1180 Register rhs() const {
1181 return rhs_;
1182 }
1183 };
1185 bool
1186 CodeGeneratorX86Shared::visitModOverflowCheck(ModOverflowCheck *ool)
1187 {
1188 masm.cmpl(ool->rhs(), Imm32(-1));
1189 if (ool->ins()->mir()->isTruncated()) {
1190 masm.j(Assembler::NotEqual, ool->rejoin());
1191 masm.mov(ImmWord(0), edx);
1192 masm.jmp(ool->done());
1193 } else {
1194 if (!bailoutIf(Assembler::Equal, ool->ins()->snapshot()))
1195 return false;
1196 masm.jmp(ool->rejoin());
1197 }
1198 return true;
1199 }
1201 bool
1202 CodeGeneratorX86Shared::visitModI(LModI *ins)
1203 {
1204 Register remainder = ToRegister(ins->remainder());
1205 Register lhs = ToRegister(ins->lhs());
1206 Register rhs = ToRegister(ins->rhs());
1208 // Required to use idiv.
1209 JS_ASSERT_IF(lhs != rhs, rhs != eax);
1210 JS_ASSERT(rhs != edx);
1211 JS_ASSERT(remainder == edx);
1212 JS_ASSERT(ToRegister(ins->getTemp(0)) == eax);
1214 Label done;
1215 ReturnZero *ool = nullptr;
1216 ModOverflowCheck *overflow = nullptr;
1218 // Set up eax in preparation for doing a div.
1219 if (lhs != eax)
1220 masm.mov(lhs, eax);
1222 // Prevent divide by zero.
1223 if (ins->mir()->canBeDivideByZero()) {
1224 masm.testl(rhs, rhs);
1225 if (ins->mir()->isTruncated()) {
1226 if (!ool)
1227 ool = new(alloc()) ReturnZero(edx);
1228 masm.j(Assembler::Zero, ool->entry());
1229 } else {
1230 if (!bailoutIf(Assembler::Zero, ins->snapshot()))
1231 return false;
1232 }
1233 }
1235 Label negative;
1237 // Switch based on sign of the lhs.
1238 if (ins->mir()->canBeNegativeDividend())
1239 masm.branchTest32(Assembler::Signed, lhs, lhs, &negative);
1241 // If lhs >= 0 then remainder = lhs % rhs. The remainder must be positive.
1242 {
1243 // Check if rhs is a power-of-two.
1244 if (ins->mir()->canBePowerOfTwoDivisor()) {
1245 JS_ASSERT(rhs != remainder);
1247 // Rhs y is a power-of-two if (y & (y-1)) == 0. Note that if
1248 // y is any negative number other than INT32_MIN, both y and
1249 // y-1 will have the sign bit set so these are never optimized
1250 // as powers-of-two. If y is INT32_MIN, y-1 will be INT32_MAX
1251 // and because lhs >= 0 at this point, lhs & INT32_MAX returns
1252 // the correct value.
1253 Label notPowerOfTwo;
1254 masm.mov(rhs, remainder);
1255 masm.subl(Imm32(1), remainder);
1256 masm.branchTest32(Assembler::NonZero, remainder, rhs, ¬PowerOfTwo);
1257 {
1258 masm.andl(lhs, remainder);
1259 masm.jmp(&done);
1260 }
1261 masm.bind(¬PowerOfTwo);
1262 }
1264 // Since lhs >= 0, the sign-extension will be 0
1265 masm.mov(ImmWord(0), edx);
1266 masm.idiv(rhs);
1267 }
1269 // Otherwise, we have to beware of two special cases:
1270 if (ins->mir()->canBeNegativeDividend()) {
1271 masm.jump(&done);
1273 masm.bind(&negative);
1275 // Prevent an integer overflow exception from -2147483648 % -1
1276 Label notmin;
1277 masm.cmpl(lhs, Imm32(INT32_MIN));
1278 overflow = new(alloc()) ModOverflowCheck(ins, rhs);
1279 masm.j(Assembler::Equal, overflow->entry());
1280 masm.bind(overflow->rejoin());
1281 masm.cdq();
1282 masm.idiv(rhs);
1284 if (!ins->mir()->isTruncated()) {
1285 // A remainder of 0 means that the rval must be -0, which is a double.
1286 masm.testl(remainder, remainder);
1287 if (!bailoutIf(Assembler::Zero, ins->snapshot()))
1288 return false;
1289 }
1290 }
1292 masm.bind(&done);
1294 if (overflow) {
1295 if (!addOutOfLineCode(overflow))
1296 return false;
1297 masm.bind(overflow->done());
1298 }
1300 if (ool) {
1301 if (!addOutOfLineCode(ool))
1302 return false;
1303 masm.bind(ool->rejoin());
1304 }
1306 return true;
1307 }
1309 bool
1310 CodeGeneratorX86Shared::visitBitNotI(LBitNotI *ins)
1311 {
1312 const LAllocation *input = ins->getOperand(0);
1313 JS_ASSERT(!input->isConstant());
1315 masm.notl(ToOperand(input));
1316 return true;
1317 }
1319 bool
1320 CodeGeneratorX86Shared::visitBitOpI(LBitOpI *ins)
1321 {
1322 const LAllocation *lhs = ins->getOperand(0);
1323 const LAllocation *rhs = ins->getOperand(1);
1325 switch (ins->bitop()) {
1326 case JSOP_BITOR:
1327 if (rhs->isConstant())
1328 masm.orl(Imm32(ToInt32(rhs)), ToOperand(lhs));
1329 else
1330 masm.orl(ToOperand(rhs), ToRegister(lhs));
1331 break;
1332 case JSOP_BITXOR:
1333 if (rhs->isConstant())
1334 masm.xorl(Imm32(ToInt32(rhs)), ToOperand(lhs));
1335 else
1336 masm.xorl(ToOperand(rhs), ToRegister(lhs));
1337 break;
1338 case JSOP_BITAND:
1339 if (rhs->isConstant())
1340 masm.andl(Imm32(ToInt32(rhs)), ToOperand(lhs));
1341 else
1342 masm.andl(ToOperand(rhs), ToRegister(lhs));
1343 break;
1344 default:
1345 MOZ_ASSUME_UNREACHABLE("unexpected binary opcode");
1346 }
1348 return true;
1349 }
1351 bool
1352 CodeGeneratorX86Shared::visitShiftI(LShiftI *ins)
1353 {
1354 Register lhs = ToRegister(ins->lhs());
1355 const LAllocation *rhs = ins->rhs();
1357 if (rhs->isConstant()) {
1358 int32_t shift = ToInt32(rhs) & 0x1F;
1359 switch (ins->bitop()) {
1360 case JSOP_LSH:
1361 if (shift)
1362 masm.shll(Imm32(shift), lhs);
1363 break;
1364 case JSOP_RSH:
1365 if (shift)
1366 masm.sarl(Imm32(shift), lhs);
1367 break;
1368 case JSOP_URSH:
1369 if (shift) {
1370 masm.shrl(Imm32(shift), lhs);
1371 } else if (ins->mir()->toUrsh()->fallible()) {
1372 // x >>> 0 can overflow.
1373 masm.testl(lhs, lhs);
1374 if (!bailoutIf(Assembler::Signed, ins->snapshot()))
1375 return false;
1376 }
1377 break;
1378 default:
1379 MOZ_ASSUME_UNREACHABLE("Unexpected shift op");
1380 }
1381 } else {
1382 JS_ASSERT(ToRegister(rhs) == ecx);
1383 switch (ins->bitop()) {
1384 case JSOP_LSH:
1385 masm.shll_cl(lhs);
1386 break;
1387 case JSOP_RSH:
1388 masm.sarl_cl(lhs);
1389 break;
1390 case JSOP_URSH:
1391 masm.shrl_cl(lhs);
1392 if (ins->mir()->toUrsh()->fallible()) {
1393 // x >>> 0 can overflow.
1394 masm.testl(lhs, lhs);
1395 if (!bailoutIf(Assembler::Signed, ins->snapshot()))
1396 return false;
1397 }
1398 break;
1399 default:
1400 MOZ_ASSUME_UNREACHABLE("Unexpected shift op");
1401 }
1402 }
1404 return true;
1405 }
1407 bool
1408 CodeGeneratorX86Shared::visitUrshD(LUrshD *ins)
1409 {
1410 Register lhs = ToRegister(ins->lhs());
1411 JS_ASSERT(ToRegister(ins->temp()) == lhs);
1413 const LAllocation *rhs = ins->rhs();
1414 FloatRegister out = ToFloatRegister(ins->output());
1416 if (rhs->isConstant()) {
1417 int32_t shift = ToInt32(rhs) & 0x1F;
1418 if (shift)
1419 masm.shrl(Imm32(shift), lhs);
1420 } else {
1421 JS_ASSERT(ToRegister(rhs) == ecx);
1422 masm.shrl_cl(lhs);
1423 }
1425 masm.convertUInt32ToDouble(lhs, out);
1426 return true;
1427 }
1429 MoveOperand
1430 CodeGeneratorX86Shared::toMoveOperand(const LAllocation *a) const
1431 {
1432 if (a->isGeneralReg())
1433 return MoveOperand(ToRegister(a));
1434 if (a->isFloatReg())
1435 return MoveOperand(ToFloatRegister(a));
1436 return MoveOperand(StackPointer, ToStackOffset(a));
1437 }
1439 class OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorX86Shared>
1440 {
1441 MTableSwitch *mir_;
1442 CodeLabel jumpLabel_;
1444 bool accept(CodeGeneratorX86Shared *codegen) {
1445 return codegen->visitOutOfLineTableSwitch(this);
1446 }
1448 public:
1449 OutOfLineTableSwitch(MTableSwitch *mir)
1450 : mir_(mir)
1451 {}
1453 MTableSwitch *mir() const {
1454 return mir_;
1455 }
1457 CodeLabel *jumpLabel() {
1458 return &jumpLabel_;
1459 }
1460 };
1462 bool
1463 CodeGeneratorX86Shared::visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool)
1464 {
1465 MTableSwitch *mir = ool->mir();
1467 masm.align(sizeof(void*));
1468 masm.bind(ool->jumpLabel()->src());
1469 if (!masm.addCodeLabel(*ool->jumpLabel()))
1470 return false;
1472 for (size_t i = 0; i < mir->numCases(); i++) {
1473 LBlock *caseblock = mir->getCase(i)->lir();
1474 Label *caseheader = caseblock->label();
1475 uint32_t caseoffset = caseheader->offset();
1477 // The entries of the jump table need to be absolute addresses and thus
1478 // must be patched after codegen is finished.
1479 CodeLabel cl;
1480 masm.writeCodePointer(cl.dest());
1481 cl.src()->bind(caseoffset);
1482 if (!masm.addCodeLabel(cl))
1483 return false;
1484 }
1486 return true;
1487 }
1489 bool
1490 CodeGeneratorX86Shared::emitTableSwitchDispatch(MTableSwitch *mir, const Register &index,
1491 const Register &base)
1492 {
1493 Label *defaultcase = mir->getDefault()->lir()->label();
1495 // Lower value with low value
1496 if (mir->low() != 0)
1497 masm.subl(Imm32(mir->low()), index);
1499 // Jump to default case if input is out of range
1500 int32_t cases = mir->numCases();
1501 masm.cmpl(index, Imm32(cases));
1502 masm.j(AssemblerX86Shared::AboveOrEqual, defaultcase);
1504 // To fill in the CodeLabels for the case entries, we need to first
1505 // generate the case entries (we don't yet know their offsets in the
1506 // instruction stream).
1507 OutOfLineTableSwitch *ool = new(alloc()) OutOfLineTableSwitch(mir);
1508 if (!addOutOfLineCode(ool))
1509 return false;
1511 // Compute the position where a pointer to the right case stands.
1512 masm.mov(ool->jumpLabel()->dest(), base);
1513 Operand pointer = Operand(base, index, ScalePointer);
1515 // Jump to the right case
1516 masm.jmp(pointer);
1518 return true;
1519 }
1521 bool
1522 CodeGeneratorX86Shared::visitMathD(LMathD *math)
1523 {
1524 FloatRegister lhs = ToFloatRegister(math->lhs());
1525 Operand rhs = ToOperand(math->rhs());
1527 JS_ASSERT(ToFloatRegister(math->output()) == lhs);
1529 switch (math->jsop()) {
1530 case JSOP_ADD:
1531 masm.addsd(rhs, lhs);
1532 break;
1533 case JSOP_SUB:
1534 masm.subsd(rhs, lhs);
1535 break;
1536 case JSOP_MUL:
1537 masm.mulsd(rhs, lhs);
1538 break;
1539 case JSOP_DIV:
1540 masm.divsd(rhs, lhs);
1541 break;
1542 default:
1543 MOZ_ASSUME_UNREACHABLE("unexpected opcode");
1544 }
1545 return true;
1546 }
1548 bool
1549 CodeGeneratorX86Shared::visitMathF(LMathF *math)
1550 {
1551 FloatRegister lhs = ToFloatRegister(math->lhs());
1552 Operand rhs = ToOperand(math->rhs());
1554 JS_ASSERT(ToFloatRegister(math->output()) == lhs);
1556 switch (math->jsop()) {
1557 case JSOP_ADD:
1558 masm.addss(rhs, lhs);
1559 break;
1560 case JSOP_SUB:
1561 masm.subss(rhs, lhs);
1562 break;
1563 case JSOP_MUL:
1564 masm.mulss(rhs, lhs);
1565 break;
1566 case JSOP_DIV:
1567 masm.divss(rhs, lhs);
1568 break;
1569 default:
1570 MOZ_ASSUME_UNREACHABLE("unexpected opcode");
1571 return false;
1572 }
1573 return true;
1574 }
1576 bool
1577 CodeGeneratorX86Shared::visitFloor(LFloor *lir)
1578 {
1579 FloatRegister input = ToFloatRegister(lir->input());
1580 FloatRegister scratch = ScratchFloatReg;
1581 Register output = ToRegister(lir->output());
1583 Label bailout;
1585 if (AssemblerX86Shared::HasSSE41()) {
1586 // Bail on negative-zero.
1587 masm.branchNegativeZero(input, output, &bailout);
1588 if (!bailoutFrom(&bailout, lir->snapshot()))
1589 return false;
1591 // Round toward -Infinity.
1592 masm.roundsd(input, scratch, JSC::X86Assembler::RoundDown);
1594 masm.cvttsd2si(scratch, output);
1595 masm.cmp32(output, Imm32(INT_MIN));
1596 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1597 return false;
1598 } else {
1599 Label negative, end;
1601 // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
1602 masm.xorpd(scratch, scratch);
1603 masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &negative);
1605 // Bail on negative-zero.
1606 masm.branchNegativeZero(input, output, &bailout);
1607 if (!bailoutFrom(&bailout, lir->snapshot()))
1608 return false;
1610 // Input is non-negative, so truncation correctly rounds.
1611 masm.cvttsd2si(input, output);
1612 masm.cmp32(output, Imm32(INT_MIN));
1613 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1614 return false;
1616 masm.jump(&end);
1618 // Input is negative, but isn't -0.
1619 // Negative values go on a comparatively expensive path, since no
1620 // native rounding mode matches JS semantics. Still better than callVM.
1621 masm.bind(&negative);
1622 {
1623 // Truncate and round toward zero.
1624 // This is off-by-one for everything but integer-valued inputs.
1625 masm.cvttsd2si(input, output);
1626 masm.cmp32(output, Imm32(INT_MIN));
1627 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1628 return false;
1630 // Test whether the input double was integer-valued.
1631 masm.convertInt32ToDouble(output, scratch);
1632 masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch, &end);
1634 // Input is not integer-valued, so we rounded off-by-one in the
1635 // wrong direction. Correct by subtraction.
1636 masm.subl(Imm32(1), output);
1637 // Cannot overflow: output was already checked against INT_MIN.
1638 }
1640 masm.bind(&end);
1641 }
1642 return true;
1643 }
1645 bool
1646 CodeGeneratorX86Shared::visitFloorF(LFloorF *lir)
1647 {
1648 FloatRegister input = ToFloatRegister(lir->input());
1649 FloatRegister scratch = ScratchFloatReg;
1650 Register output = ToRegister(lir->output());
1652 Label bailout;
1654 if (AssemblerX86Shared::HasSSE41()) {
1655 // Bail on negative-zero.
1656 masm.branchNegativeZeroFloat32(input, output, &bailout);
1657 if (!bailoutFrom(&bailout, lir->snapshot()))
1658 return false;
1660 // Round toward -Infinity.
1661 masm.roundss(input, scratch, JSC::X86Assembler::RoundDown);
1663 masm.cvttss2si(scratch, output);
1664 masm.cmp32(output, Imm32(INT_MIN));
1665 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1666 return false;
1667 } else {
1668 Label negative, end;
1670 // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
1671 masm.xorps(scratch, scratch);
1672 masm.branchFloat(Assembler::DoubleLessThan, input, scratch, &negative);
1674 // Bail on negative-zero.
1675 masm.branchNegativeZeroFloat32(input, output, &bailout);
1676 if (!bailoutFrom(&bailout, lir->snapshot()))
1677 return false;
1679 // Input is non-negative, so truncation correctly rounds.
1680 masm.cvttss2si(input, output);
1681 masm.cmp32(output, Imm32(INT_MIN));
1682 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1683 return false;
1685 masm.jump(&end);
1687 // Input is negative, but isn't -0.
1688 // Negative values go on a comparatively expensive path, since no
1689 // native rounding mode matches JS semantics. Still better than callVM.
1690 masm.bind(&negative);
1691 {
1692 // Truncate and round toward zero.
1693 // This is off-by-one for everything but integer-valued inputs.
1694 masm.cvttss2si(input, output);
1695 masm.cmp32(output, Imm32(INT_MIN));
1696 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1697 return false;
1699 // Test whether the input double was integer-valued.
1700 masm.convertInt32ToFloat32(output, scratch);
1701 masm.branchFloat(Assembler::DoubleEqualOrUnordered, input, scratch, &end);
1703 // Input is not integer-valued, so we rounded off-by-one in the
1704 // wrong direction. Correct by subtraction.
1705 masm.subl(Imm32(1), output);
1706 // Cannot overflow: output was already checked against INT_MIN.
1707 }
1709 masm.bind(&end);
1710 }
1711 return true;
1712 }
1714 bool
1715 CodeGeneratorX86Shared::visitRound(LRound *lir)
1716 {
1717 FloatRegister input = ToFloatRegister(lir->input());
1718 FloatRegister temp = ToFloatRegister(lir->temp());
1719 FloatRegister scratch = ScratchFloatReg;
1720 Register output = ToRegister(lir->output());
1722 Label negative, end, bailout;
1724 // Load 0.5 in the temp register.
1725 masm.loadConstantDouble(0.5, temp);
1727 // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
1728 masm.xorpd(scratch, scratch);
1729 masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &negative);
1731 // Bail on negative-zero.
1732 masm.branchNegativeZero(input, output, &bailout);
1733 if (!bailoutFrom(&bailout, lir->snapshot()))
1734 return false;
1736 // Input is non-negative. Add 0.5 and truncate, rounding down. Note that we
1737 // have to add the input to the temp register (which contains 0.5) because
1738 // we're not allowed to modify the input register.
1739 masm.addsd(input, temp);
1741 masm.cvttsd2si(temp, output);
1742 masm.cmp32(output, Imm32(INT_MIN));
1743 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1744 return false;
1746 masm.jump(&end);
1749 // Input is negative, but isn't -0.
1750 masm.bind(&negative);
1752 if (AssemblerX86Shared::HasSSE41()) {
1753 // Add 0.5 and round toward -Infinity. The result is stored in the temp
1754 // register (currently contains 0.5).
1755 masm.addsd(input, temp);
1756 masm.roundsd(temp, scratch, JSC::X86Assembler::RoundDown);
1758 // Truncate.
1759 masm.cvttsd2si(scratch, output);
1760 masm.cmp32(output, Imm32(INT_MIN));
1761 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1762 return false;
1764 // If the result is positive zero, then the actual result is -0. Bail.
1765 // Otherwise, the truncation will have produced the correct negative integer.
1766 masm.testl(output, output);
1767 if (!bailoutIf(Assembler::Zero, lir->snapshot()))
1768 return false;
1770 } else {
1771 masm.addsd(input, temp);
1773 // Round toward -Infinity without the benefit of ROUNDSD.
1774 {
1775 // If input + 0.5 >= 0, input is a negative number >= -0.5 and the result is -0.
1776 masm.compareDouble(Assembler::DoubleGreaterThanOrEqual, temp, scratch);
1777 if (!bailoutIf(Assembler::DoubleGreaterThanOrEqual, lir->snapshot()))
1778 return false;
1780 // Truncate and round toward zero.
1781 // This is off-by-one for everything but integer-valued inputs.
1782 masm.cvttsd2si(temp, output);
1783 masm.cmp32(output, Imm32(INT_MIN));
1784 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1785 return false;
1787 // Test whether the truncated double was integer-valued.
1788 masm.convertInt32ToDouble(output, scratch);
1789 masm.branchDouble(Assembler::DoubleEqualOrUnordered, temp, scratch, &end);
1791 // Input is not integer-valued, so we rounded off-by-one in the
1792 // wrong direction. Correct by subtraction.
1793 masm.subl(Imm32(1), output);
1794 // Cannot overflow: output was already checked against INT_MIN.
1795 }
1796 }
1798 masm.bind(&end);
1799 return true;
1800 }
1802 bool
1803 CodeGeneratorX86Shared::visitRoundF(LRoundF *lir)
1804 {
1805 FloatRegister input = ToFloatRegister(lir->input());
1806 FloatRegister temp = ToFloatRegister(lir->temp());
1807 FloatRegister scratch = ScratchFloatReg;
1808 Register output = ToRegister(lir->output());
1810 Label negative, end, bailout;
1812 // Load 0.5 in the temp register.
1813 masm.loadConstantFloat32(0.5f, temp);
1815 // Branch to a slow path for negative inputs. Doesn't catch NaN or -0.
1816 masm.xorps(scratch, scratch);
1817 masm.branchFloat(Assembler::DoubleLessThan, input, scratch, &negative);
1819 // Bail on negative-zero.
1820 masm.branchNegativeZeroFloat32(input, output, &bailout);
1821 if (!bailoutFrom(&bailout, lir->snapshot()))
1822 return false;
1824 // Input is non-negative. Add 0.5 and truncate, rounding down. Note that we
1825 // have to add the input to the temp register (which contains 0.5) because
1826 // we're not allowed to modify the input register.
1827 masm.addss(input, temp);
1829 masm.cvttss2si(temp, output);
1830 masm.cmp32(output, Imm32(INT_MIN));
1831 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1832 return false;
1834 masm.jump(&end);
1837 // Input is negative, but isn't -0.
1838 masm.bind(&negative);
1840 if (AssemblerX86Shared::HasSSE41()) {
1841 // Add 0.5 and round toward -Infinity. The result is stored in the temp
1842 // register (currently contains 0.5).
1843 masm.addss(input, temp);
1844 masm.roundss(temp, scratch, JSC::X86Assembler::RoundDown);
1846 // Truncate.
1847 masm.cvttss2si(scratch, output);
1848 masm.cmp32(output, Imm32(INT_MIN));
1849 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1850 return false;
1852 // If the result is positive zero, then the actual result is -0. Bail.
1853 // Otherwise, the truncation will have produced the correct negative integer.
1854 masm.testl(output, output);
1855 if (!bailoutIf(Assembler::Zero, lir->snapshot()))
1856 return false;
1858 } else {
1859 masm.addss(input, temp);
1860 // Round toward -Infinity without the benefit of ROUNDSS.
1861 {
1862 // If input + 0.5 >= 0, input is a negative number >= -0.5 and the result is -0.
1863 masm.compareFloat(Assembler::DoubleGreaterThanOrEqual, temp, scratch);
1864 if (!bailoutIf(Assembler::DoubleGreaterThanOrEqual, lir->snapshot()))
1865 return false;
1867 // Truncate and round toward zero.
1868 // This is off-by-one for everything but integer-valued inputs.
1869 masm.cvttss2si(temp, output);
1870 masm.cmp32(output, Imm32(INT_MIN));
1871 if (!bailoutIf(Assembler::Equal, lir->snapshot()))
1872 return false;
1874 // Test whether the truncated double was integer-valued.
1875 masm.convertInt32ToFloat32(output, scratch);
1876 masm.branchFloat(Assembler::DoubleEqualOrUnordered, temp, scratch, &end);
1878 // Input is not integer-valued, so we rounded off-by-one in the
1879 // wrong direction. Correct by subtraction.
1880 masm.subl(Imm32(1), output);
1881 // Cannot overflow: output was already checked against INT_MIN.
1882 }
1883 }
1885 masm.bind(&end);
1886 return true;
1887 }
1889 bool
1890 CodeGeneratorX86Shared::visitGuardShape(LGuardShape *guard)
1891 {
1892 Register obj = ToRegister(guard->input());
1893 masm.cmpPtr(Operand(obj, JSObject::offsetOfShape()), ImmGCPtr(guard->mir()->shape()));
1895 return bailoutIf(Assembler::NotEqual, guard->snapshot());
1896 }
1898 bool
1899 CodeGeneratorX86Shared::visitGuardObjectType(LGuardObjectType *guard)
1900 {
1901 Register obj = ToRegister(guard->input());
1902 masm.cmpPtr(Operand(obj, JSObject::offsetOfType()), ImmGCPtr(guard->mir()->typeObject()));
1904 Assembler::Condition cond =
1905 guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
1906 return bailoutIf(cond, guard->snapshot());
1907 }
1909 bool
1910 CodeGeneratorX86Shared::visitGuardClass(LGuardClass *guard)
1911 {
1912 Register obj = ToRegister(guard->input());
1913 Register tmp = ToRegister(guard->tempInt());
1915 masm.loadPtr(Address(obj, JSObject::offsetOfType()), tmp);
1916 masm.cmpPtr(Operand(tmp, types::TypeObject::offsetOfClasp()), ImmPtr(guard->mir()->getClass()));
1917 if (!bailoutIf(Assembler::NotEqual, guard->snapshot()))
1918 return false;
1919 return true;
1920 }
1922 bool
1923 CodeGeneratorX86Shared::visitEffectiveAddress(LEffectiveAddress *ins)
1924 {
1925 const MEffectiveAddress *mir = ins->mir();
1926 Register base = ToRegister(ins->base());
1927 Register index = ToRegister(ins->index());
1928 Register output = ToRegister(ins->output());
1929 masm.leal(Operand(base, index, mir->scale(), mir->displacement()), output);
1930 return true;
1931 }
1933 Operand
1934 CodeGeneratorX86Shared::createArrayElementOperand(Register elements, const LAllocation *index)
1935 {
1936 if (index->isConstant())
1937 return Operand(elements, ToInt32(index) * sizeof(js::Value));
1939 return Operand(elements, ToRegister(index), TimesEight);
1940 }
1941 bool
1942 CodeGeneratorX86Shared::generateInvalidateEpilogue()
1943 {
1944 // Ensure that there is enough space in the buffer for the OsiPoint
1945 // patching to occur. Otherwise, we could overwrite the invalidation
1946 // epilogue.
1947 for (size_t i = 0; i < sizeof(void *); i+= Assembler::nopSize())
1948 masm.nop();
1950 masm.bind(&invalidate_);
1952 // Push the Ion script onto the stack (when we determine what that pointer is).
1953 invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1)));
1954 JitCode *thunk = gen->jitRuntime()->getInvalidationThunk();
1956 masm.call(thunk);
1958 // We should never reach this point in JIT code -- the invalidation thunk should
1959 // pop the invalidated JS frame and return directly to its caller.
1960 masm.assumeUnreachable("Should have returned directly to its caller instead of here.");
1961 return true;
1962 }
1964 bool
1965 CodeGeneratorX86Shared::visitNegI(LNegI *ins)
1966 {
1967 Register input = ToRegister(ins->input());
1968 JS_ASSERT(input == ToRegister(ins->output()));
1970 masm.neg32(input);
1971 return true;
1972 }
1974 bool
1975 CodeGeneratorX86Shared::visitNegD(LNegD *ins)
1976 {
1977 FloatRegister input = ToFloatRegister(ins->input());
1978 JS_ASSERT(input == ToFloatRegister(ins->output()));
1980 masm.negateDouble(input);
1981 return true;
1982 }
1984 bool
1985 CodeGeneratorX86Shared::visitNegF(LNegF *ins)
1986 {
1987 FloatRegister input = ToFloatRegister(ins->input());
1988 JS_ASSERT(input == ToFloatRegister(ins->output()));
1990 masm.negateFloat(input);
1991 return true;
1992 }
1994 bool
1995 CodeGeneratorX86Shared::visitForkJoinGetSlice(LForkJoinGetSlice *ins)
1996 {
1997 MOZ_ASSERT(gen->info().executionMode() == ParallelExecution);
1998 MOZ_ASSERT(ToRegister(ins->forkJoinContext()) == ForkJoinGetSliceReg_cx);
1999 MOZ_ASSERT(ToRegister(ins->temp1()) == eax);
2000 MOZ_ASSERT(ToRegister(ins->temp2()) == edx);
2001 MOZ_ASSERT(ToRegister(ins->temp3()) == ForkJoinGetSliceReg_temp0);
2002 MOZ_ASSERT(ToRegister(ins->temp4()) == ForkJoinGetSliceReg_temp1);
2003 MOZ_ASSERT(ToRegister(ins->output()) == ForkJoinGetSliceReg_output);
2005 masm.call(gen->jitRuntime()->forkJoinGetSliceStub());
2006 return true;
2007 }
2009 JitCode *
2010 JitRuntime::generateForkJoinGetSliceStub(JSContext *cx)
2011 {
2012 #ifdef JS_THREADSAFE
2013 MacroAssembler masm(cx);
2015 // We need two fixed temps. We need to fix eax for cmpxchg, and edx for
2016 // div.
2017 Register cxReg = ForkJoinGetSliceReg_cx, worker = cxReg;
2018 Register pool = ForkJoinGetSliceReg_temp0;
2019 Register bounds = ForkJoinGetSliceReg_temp1;
2020 Register output = ForkJoinGetSliceReg_output;
2022 MOZ_ASSERT(worker != eax && worker != edx);
2023 MOZ_ASSERT(pool != eax && pool != edx);
2024 MOZ_ASSERT(bounds != eax && bounds != edx);
2025 MOZ_ASSERT(output != eax && output != edx);
2027 Label stealWork, noMoreWork, gotSlice;
2028 Operand workerSliceBounds(Address(worker, ThreadPoolWorker::offsetOfSliceBounds()));
2030 // Clobber cx to load the worker.
2031 masm.push(cxReg);
2032 masm.loadPtr(Address(cxReg, ForkJoinContext::offsetOfWorker()), worker);
2034 // Load the thread pool, which is used in all cases below.
2035 masm.loadThreadPool(pool);
2037 {
2038 // Try to get a slice from the current thread.
2039 Label getOwnSliceLoopHead;
2040 masm.bind(&getOwnSliceLoopHead);
2042 // Load the slice bounds for the current thread.
2043 masm.loadSliceBounds(worker, bounds);
2045 // The slice bounds is a uint32 composed from two uint16s:
2046 // [ from , to ]
2047 // ^~~~ ^~
2048 // upper 16 bits | lower 16 bits
2049 masm.move32(bounds, output);
2050 masm.shrl(Imm32(16), output);
2052 // If we don't have any slices left ourselves, move on to stealing.
2053 masm.branch16(Assembler::Equal, output, bounds, &stealWork);
2055 // If we still have work, try to CAS [ from+1, to ].
2056 masm.move32(bounds, edx);
2057 masm.add32(Imm32(0x10000), edx);
2058 masm.move32(bounds, eax);
2059 masm.atomic_cmpxchg32(edx, workerSliceBounds, eax);
2060 masm.j(Assembler::NonZero, &getOwnSliceLoopHead);
2062 // If the CAS succeeded, return |from| in output.
2063 masm.jump(&gotSlice);
2064 }
2066 // Try to steal work.
2067 masm.bind(&stealWork);
2069 // It's not technically correct to test whether work-stealing is turned on
2070 // only during stub-generation time, but it's a DEBUG only thing.
2071 if (cx->runtime()->threadPool.workStealing()) {
2072 Label stealWorkLoopHead;
2073 masm.bind(&stealWorkLoopHead);
2075 // Check if we have work.
2076 masm.branch32(Assembler::Equal,
2077 Address(pool, ThreadPool::offsetOfPendingSlices()),
2078 Imm32(0), &noMoreWork);
2080 // Get an id at random. The following is an inline of
2081 // the 32-bit xorshift in ThreadPoolWorker::randomWorker().
2082 {
2083 // Reload the current worker.
2084 masm.loadPtr(Address(StackPointer, 0), cxReg);
2085 masm.loadPtr(Address(cxReg, ForkJoinContext::offsetOfWorker()), worker);
2087 // Perform the xorshift to get a random number in eax, using edx
2088 // as a temp.
2089 Address rngState(worker, ThreadPoolWorker::offsetOfSchedulerRNGState());
2090 masm.load32(rngState, eax);
2091 masm.move32(eax, edx);
2092 masm.shll(Imm32(ThreadPoolWorker::XORSHIFT_A), eax);
2093 masm.xor32(edx, eax);
2094 masm.move32(eax, edx);
2095 masm.shrl(Imm32(ThreadPoolWorker::XORSHIFT_B), eax);
2096 masm.xor32(edx, eax);
2097 masm.move32(eax, edx);
2098 masm.shll(Imm32(ThreadPoolWorker::XORSHIFT_C), eax);
2099 masm.xor32(edx, eax);
2100 masm.store32(eax, rngState);
2102 // Compute the random worker id by computing % numWorkers. Reuse
2103 // output as a temp.
2104 masm.move32(Imm32(0), edx);
2105 masm.move32(Imm32(cx->runtime()->threadPool.numWorkers()), output);
2106 masm.udiv(output);
2107 }
2109 // Load the worker from the workers array.
2110 masm.loadPtr(Address(pool, ThreadPool::offsetOfWorkers()), worker);
2111 masm.loadPtr(BaseIndex(worker, edx, ScalePointer), worker);
2113 // Try to get a slice from the designated victim worker.
2114 Label stealSliceFromWorkerLoopHead;
2115 masm.bind(&stealSliceFromWorkerLoopHead);
2117 // Load the slice bounds and decompose for the victim worker.
2118 masm.loadSliceBounds(worker, bounds);
2119 masm.move32(bounds, eax);
2120 masm.shrl(Imm32(16), eax);
2122 // If the victim worker has no more slices left, find another worker.
2123 masm.branch16(Assembler::Equal, eax, bounds, &stealWorkLoopHead);
2125 // If the victim worker still has work, try to CAS [ from, to-1 ].
2126 masm.move32(bounds, output);
2127 masm.sub32(Imm32(1), output);
2128 masm.move32(bounds, eax);
2129 masm.atomic_cmpxchg32(output, workerSliceBounds, eax);
2130 masm.j(Assembler::NonZero, &stealSliceFromWorkerLoopHead);
2132 // If the CAS succeeded, return |to-1| in output.
2133 #ifdef DEBUG
2134 masm.atomic_inc32(Operand(Address(pool, ThreadPool::offsetOfStolenSlices())));
2135 #endif
2136 // Copies lower 16 bits only.
2137 masm.movzwl(output, output);
2138 }
2140 // If we successfully got a slice, decrement pool->pendingSlices_ and
2141 // return the slice.
2142 masm.bind(&gotSlice);
2143 masm.atomic_dec32(Operand(Address(pool, ThreadPool::offsetOfPendingSlices())));
2144 masm.pop(cxReg);
2145 masm.ret();
2147 // There's no more slices to give out, return a sentinel value.
2148 masm.bind(&noMoreWork);
2149 masm.move32(Imm32(ThreadPool::MAX_SLICE_ID), output);
2150 masm.pop(cxReg);
2151 masm.ret();
2153 Linker linker(masm);
2154 JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
2156 #ifdef JS_ION_PERF
2157 writePerfSpewerJitCodeProfile(code, "ForkJoinGetSliceStub");
2158 #endif
2160 return code;
2161 #else
2162 return nullptr;
2163 #endif // JS_THREADSAFE
2164 }
2166 } // namespace jit
2167 } // namespace js