| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #include "mozilla/DebugOnly.h" |
| |
| #include "jscntxt.h" |
| #include "jscompartment.h" |
| #include "jsmath.h" |
| #include "CodeGenerator-x86-shared.h" |
| #include "CodeGenerator-shared-inl.h" |
| #include "jit/IonFrames.h" |
| #include "jit/MoveEmitter.h" |
| #include "jit/IonCompartment.h" |
| #include "jit/ParallelFunctions.h" |
| |
| using namespace js; |
| using namespace js::jit; |
| |
| namespace js { |
| namespace jit { |
| |
| CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) |
| : CodeGeneratorShared(gen, graph, masm), |
| deoptLabel_(NULL) |
| { |
| } |
| |
| double |
| test(double x, double y) |
| { |
| return x + y; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::generatePrologue() |
| { |
| // Note that this automatically sets MacroAssembler::framePushed(). |
| masm.reserveStack(frameSize()); |
| |
| // Allocate returnLabel_ on the heap, so we don't run its destructor and |
| // assert-not-bound in debug mode on compilation failure. |
| returnLabel_ = new HeapLabel(); |
| |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::generateEpilogue() |
| { |
| masm.bind(returnLabel_); |
| |
| // Pop the stack we allocated at the start of the function. |
| masm.freeStack(frameSize()); |
| JS_ASSERT(masm.framePushed() == 0); |
| |
| masm.ret(); |
| return true; |
| } |
| |
| bool |
| OutOfLineBailout::accept(CodeGeneratorX86Shared *codegen) |
| { |
| return codegen->visitOutOfLineBailout(this); |
| } |
| |
| void |
| CodeGeneratorX86Shared::emitBranch(Assembler::Condition cond, MBasicBlock *mirTrue, |
| MBasicBlock *mirFalse, Assembler::NaNCond ifNaN) |
| { |
| LBlock *ifTrue = mirTrue->lir(); |
| LBlock *ifFalse = mirFalse->lir(); |
| |
| if (ifNaN == Assembler::NaN_IsFalse) |
| masm.j(Assembler::Parity, ifFalse->label()); |
| else if (ifNaN == Assembler::NaN_IsTrue) |
| masm.j(Assembler::Parity, ifTrue->label()); |
| |
| if (isNextBlock(ifFalse)) { |
| masm.j(cond, ifTrue->label()); |
| } else { |
| masm.j(Assembler::InvertCondition(cond), ifFalse->label()); |
| if (!isNextBlock(ifTrue)) |
| masm.jmp(ifTrue->label()); |
| } |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitDouble(LDouble *ins) |
| { |
| const LDefinition *out = ins->getDef(0); |
| masm.loadConstantDouble(ins->getDouble(), ToFloatRegister(out)); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitTestIAndBranch(LTestIAndBranch *test) |
| { |
| const LAllocation *opd = test->input(); |
| |
| // Test the operand |
| masm.testl(ToRegister(opd), ToRegister(opd)); |
| emitBranch(Assembler::NonZero, test->ifTrue(), test->ifFalse()); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitTestDAndBranch(LTestDAndBranch *test) |
| { |
| const LAllocation *opd = test->input(); |
| |
| // ucomisd flags: |
| // Z P C |
| // --------- |
| // NaN 1 1 1 |
| // > 0 0 0 |
| // < 0 0 1 |
| // = 1 0 0 |
| // |
| // NaN is falsey, so comparing against 0 and then using the Z flag is |
| // enough to determine which branch to take. |
| masm.xorpd(ScratchFloatReg, ScratchFloatReg); |
| masm.ucomisd(ToFloatRegister(opd), ScratchFloatReg); |
| emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse()); |
| return true; |
| } |
| |
| void |
| CodeGeneratorX86Shared::emitCompare(MCompare::CompareType type, const LAllocation *left, const LAllocation *right) |
| { |
| #ifdef JS_CPU_X64 |
| if (type == MCompare::Compare_Object) { |
| masm.cmpq(ToRegister(left), ToOperand(right)); |
| return; |
| } |
| #endif |
| |
| if (right->isConstant()) |
| masm.cmpl(ToRegister(left), Imm32(ToInt32(right))); |
| else |
| masm.cmpl(ToRegister(left), ToOperand(right)); |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitCompare(LCompare *comp) |
| { |
| MCompare *mir = comp->mir(); |
| emitCompare(mir->compareType(), comp->left(), comp->right()); |
| masm.emitSet(JSOpToCondition(mir->compareType(), comp->jsop()), ToRegister(comp->output())); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitCompareAndBranch(LCompareAndBranch *comp) |
| { |
| MCompare *mir = comp->mir(); |
| emitCompare(mir->compareType(), comp->left(), comp->right()); |
| Assembler::Condition cond = JSOpToCondition(mir->compareType(), comp->jsop()); |
| emitBranch(cond, comp->ifTrue(), comp->ifFalse()); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitCompareD(LCompareD *comp) |
| { |
| FloatRegister lhs = ToFloatRegister(comp->left()); |
| FloatRegister rhs = ToFloatRegister(comp->right()); |
| |
| Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); |
| masm.compareDouble(cond, lhs, rhs); |
| masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()), |
| Assembler::NaNCondFromDoubleCondition(cond)); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitNotI(LNotI *ins) |
| { |
| masm.cmpl(ToRegister(ins->input()), Imm32(0)); |
| masm.emitSet(Assembler::Equal, ToRegister(ins->output())); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitNotD(LNotD *ins) |
| { |
| FloatRegister opd = ToFloatRegister(ins->input()); |
| |
| masm.xorpd(ScratchFloatReg, ScratchFloatReg); |
| masm.compareDouble(Assembler::DoubleEqualOrUnordered, opd, ScratchFloatReg); |
| masm.emitSet(Assembler::Equal, ToRegister(ins->output()), Assembler::NaN_IsTrue); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitCompareDAndBranch(LCompareDAndBranch *comp) |
| { |
| FloatRegister lhs = ToFloatRegister(comp->left()); |
| FloatRegister rhs = ToFloatRegister(comp->right()); |
| |
| Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); |
| masm.compareDouble(cond, lhs, rhs); |
| emitBranch(Assembler::ConditionFromDoubleCondition(cond), comp->ifTrue(), comp->ifFalse(), |
| Assembler::NaNCondFromDoubleCondition(cond)); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins) |
| { |
| const MAsmJSPassStackArg *mir = ins->mir(); |
| Operand dst(StackPointer, mir->spOffset()); |
| if (ins->arg()->isConstant()) { |
| masm.mov(Imm32(ToInt32(ins->arg())), dst); |
| } else { |
| if (ins->arg()->isGeneralReg()) |
| masm.mov(ToRegister(ins->arg()), dst); |
| else |
| masm.movsd(ToFloatRegister(ins->arg()), dst); |
| } |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::generateOutOfLineCode() |
| { |
| if (!CodeGeneratorShared::generateOutOfLineCode()) |
| return false; |
| |
| if (deoptLabel_) { |
| // All non-table-based bailouts will go here. |
| masm.bind(deoptLabel_); |
| |
| // Push the frame size, so the handler can recover the IonScript. |
| masm.push(Imm32(frameSize())); |
| |
| IonCompartment *ion = GetIonContext()->compartment->ionCompartment(); |
| IonCode *handler = ion->getGenericBailoutHandler(); |
| |
| masm.jmp(handler->raw(), Relocation::IONCODE); |
| } |
| |
| return true; |
| } |
| |
| class BailoutJump { |
| Assembler::Condition cond_; |
| |
| public: |
| BailoutJump(Assembler::Condition cond) : cond_(cond) |
| { } |
| #ifdef JS_CPU_X86 |
| void operator()(MacroAssembler &masm, uint8_t *code) const { |
| masm.j(cond_, code, Relocation::HARDCODED); |
| } |
| #endif |
| void operator()(MacroAssembler &masm, Label *label) const { |
| masm.j(cond_, label); |
| } |
| }; |
| |
| class BailoutLabel { |
| Label *label_; |
| |
| public: |
| BailoutLabel(Label *label) : label_(label) |
| { } |
| #ifdef JS_CPU_X86 |
| void operator()(MacroAssembler &masm, uint8_t *code) const { |
| masm.retarget(label_, code, Relocation::HARDCODED); |
| } |
| #endif |
| void operator()(MacroAssembler &masm, Label *label) const { |
| masm.retarget(label_, label); |
| } |
| }; |
| |
| template <typename T> bool |
| CodeGeneratorX86Shared::bailout(const T &binder, LSnapshot *snapshot) |
| { |
| CompileInfo &info = snapshot->mir()->block()->info(); |
| switch (info.executionMode()) { |
| case ParallelExecution: { |
| // in parallel mode, make no attempt to recover, just signal an error. |
| OutOfLineParallelAbort *ool = oolParallelAbort(ParallelBailoutUnsupported, |
| snapshot->mir()->block(), |
| snapshot->mir()->pc()); |
| binder(masm, ool->entry()); |
| return true; |
| } |
| case SequentialExecution: |
| break; |
| default: |
| JS_NOT_REACHED("No such execution mode"); |
| } |
| |
| if (!encode(snapshot)) |
| return false; |
| |
| // Though the assembler doesn't track all frame pushes, at least make sure |
| // the known value makes sense. We can't use bailout tables if the stack |
| // isn't properly aligned to the static frame size. |
| JS_ASSERT_IF(frameClass_ != FrameSizeClass::None() && deoptTable_, |
| frameClass_.frameSize() == masm.framePushed()); |
| |
| #ifdef JS_CPU_X86 |
| // On x64, bailout tables are pointless, because 16 extra bytes are |
| // reserved per external jump, whereas it takes only 10 bytes to encode a |
| // a non-table based bailout. |
| if (assignBailoutId(snapshot)) { |
| binder(masm, deoptTable_->raw() + snapshot->bailoutId() * BAILOUT_TABLE_ENTRY_SIZE); |
| return true; |
| } |
| #endif |
| |
| // We could not use a jump table, either because all bailout IDs were |
| // reserved, or a jump table is not optimal for this frame size or |
| // platform. Whatever, we will generate a lazy bailout. |
| OutOfLineBailout *ool = new OutOfLineBailout(snapshot); |
| if (!addOutOfLineCode(ool)) |
| return false; |
| |
| binder(masm, ool->entry()); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::bailoutIf(Assembler::Condition condition, LSnapshot *snapshot) |
| { |
| return bailout(BailoutJump(condition), snapshot); |
| } |
| |
| bool |
| CodeGeneratorX86Shared::bailoutFrom(Label *label, LSnapshot *snapshot) |
| { |
| JS_ASSERT(label->used() && !label->bound()); |
| return bailout(BailoutLabel(label), snapshot); |
| } |
| |
| bool |
| CodeGeneratorX86Shared::bailout(LSnapshot *snapshot) |
| { |
| Label label; |
| masm.jump(&label); |
| return bailoutFrom(&label, snapshot); |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitOutOfLineBailout(OutOfLineBailout *ool) |
| { |
| if (!deoptLabel_) |
| deoptLabel_ = new HeapLabel(); |
| |
| masm.push(Imm32(ool->snapshot()->snapshotOffset())); |
| masm.jmp(deoptLabel_); |
| return true; |
| } |
| |
| |
| bool |
| CodeGeneratorX86Shared::visitMinMaxD(LMinMaxD *ins) |
| { |
| FloatRegister first = ToFloatRegister(ins->first()); |
| FloatRegister second = ToFloatRegister(ins->second()); |
| FloatRegister output = ToFloatRegister(ins->output()); |
| |
| JS_ASSERT(first == output); |
| |
| Assembler::Condition cond = ins->mir()->isMax() |
| ? Assembler::Above |
| : Assembler::Below; |
| Label nan, equal, returnSecond, done; |
| |
| masm.ucomisd(second, first); |
| masm.j(Assembler::Parity, &nan); // first or second is NaN, result is NaN. |
| masm.j(Assembler::Equal, &equal); // make sure we handle -0 and 0 right. |
| masm.j(cond, &returnSecond); |
| masm.jmp(&done); |
| |
| // Check for zero. |
| masm.bind(&equal); |
| masm.xorpd(ScratchFloatReg, ScratchFloatReg); |
| masm.ucomisd(first, ScratchFloatReg); |
| masm.j(Assembler::NotEqual, &done); // first wasn't 0 or -0, so just return it. |
| // So now both operands are either -0 or 0. |
| if (ins->mir()->isMax()) |
| masm.addsd(second, first); // -0 + -0 = -0 and -0 + 0 = 0. |
| else |
| masm.orpd(second, first); // This just ors the sign bit. |
| masm.jmp(&done); |
| |
| masm.bind(&nan); |
| masm.loadStaticDouble(&js_NaN, output); |
| masm.jmp(&done); |
| |
| masm.bind(&returnSecond); |
| masm.movsd(second, output); |
| |
| masm.bind(&done); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitAbsD(LAbsD *ins) |
| { |
| FloatRegister input = ToFloatRegister(ins->input()); |
| JS_ASSERT(input == ToFloatRegister(ins->output())); |
| masm.xorpd(ScratchFloatReg, ScratchFloatReg); |
| masm.subsd(input, ScratchFloatReg); // negate the sign bit. |
| masm.andpd(ScratchFloatReg, input); // s & ~s |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitSqrtD(LSqrtD *ins) |
| { |
| FloatRegister input = ToFloatRegister(ins->input()); |
| JS_ASSERT(input == ToFloatRegister(ins->output())); |
| masm.sqrtsd(input, input); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitPowHalfD(LPowHalfD *ins) |
| { |
| FloatRegister input = ToFloatRegister(ins->input()); |
| Register scratch = ToRegister(ins->temp()); |
| JS_ASSERT(input == ToFloatRegister(ins->output())); |
| |
| const uint32_t NegInfinityFloatBits = 0xFF800000; |
| Label done, sqrt; |
| |
| // Branch if not -Infinity. |
| masm.move32(Imm32(NegInfinityFloatBits), scratch); |
| masm.loadFloatAsDouble(scratch, ScratchFloatReg); |
| masm.branchDouble(Assembler::DoubleNotEqualOrUnordered, input, ScratchFloatReg, &sqrt); |
| |
| // Math.pow(-Infinity, 0.5) == Infinity. |
| masm.xorpd(input, input); |
| masm.subsd(ScratchFloatReg, input); |
| masm.jump(&done); |
| |
| // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). Adding 0 converts any -0 to 0. |
| masm.bind(&sqrt); |
| masm.xorpd(ScratchFloatReg, ScratchFloatReg); |
| masm.addsd(ScratchFloatReg, input); |
| masm.sqrtsd(input, input); |
| |
| masm.bind(&done); |
| return true; |
| } |
| |
| class OutOfLineUndoALUOperation : public OutOfLineCodeBase<CodeGeneratorX86Shared> |
| { |
| LInstruction *ins_; |
| |
| public: |
| OutOfLineUndoALUOperation(LInstruction *ins) |
| : ins_(ins) |
| { } |
| |
| virtual bool accept(CodeGeneratorX86Shared *codegen) { |
| return codegen->visitOutOfLineUndoALUOperation(this); |
| } |
| LInstruction *ins() const { |
| return ins_; |
| } |
| }; |
| |
| bool |
| CodeGeneratorX86Shared::visitAddI(LAddI *ins) |
| { |
| if (ins->rhs()->isConstant()) |
| masm.addl(Imm32(ToInt32(ins->rhs())), ToOperand(ins->lhs())); |
| else |
| masm.addl(ToOperand(ins->rhs()), ToRegister(ins->lhs())); |
| |
| if (ins->snapshot()) { |
| if (ins->recoversInput()) { |
| OutOfLineUndoALUOperation *ool = new OutOfLineUndoALUOperation(ins); |
| if (!addOutOfLineCode(ool)) |
| return false; |
| masm.j(Assembler::Overflow, ool->entry()); |
| } else { |
| if (!bailoutIf(Assembler::Overflow, ins->snapshot())) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitSubI(LSubI *ins) |
| { |
| if (ins->rhs()->isConstant()) |
| masm.subl(Imm32(ToInt32(ins->rhs())), ToOperand(ins->lhs())); |
| else |
| masm.subl(ToOperand(ins->rhs()), ToRegister(ins->lhs())); |
| |
| if (ins->snapshot()) { |
| if (ins->recoversInput()) { |
| OutOfLineUndoALUOperation *ool = new OutOfLineUndoALUOperation(ins); |
| if (!addOutOfLineCode(ool)) |
| return false; |
| masm.j(Assembler::Overflow, ool->entry()); |
| } else { |
| if (!bailoutIf(Assembler::Overflow, ins->snapshot())) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitOutOfLineUndoALUOperation(OutOfLineUndoALUOperation *ool) |
| { |
| LInstruction *ins = ool->ins(); |
| Register reg = ToRegister(ins->getDef(0)); |
| |
| mozilla::DebugOnly<LAllocation *> lhs = ins->getOperand(0); |
| LAllocation *rhs = ins->getOperand(1); |
| |
| JS_ASSERT(reg == ToRegister(lhs)); |
| JS_ASSERT_IF(rhs->isGeneralReg(), reg != ToRegister(rhs)); |
| |
| // Undo the effect of the ALU operation, which was performed on the output |
| // register and overflowed. Writing to the output register clobbered an |
| // input reg, and the original value of the input needs to be recovered |
| // to satisfy the constraint imposed by any RECOVERED_INPUT operands to |
| // the bailout snapshot. |
| |
| if (rhs->isConstant()) { |
| Imm32 constant(ToInt32(rhs)); |
| if (ins->isAddI()) |
| masm.subl(constant, reg); |
| else |
| masm.addl(constant, reg); |
| } else { |
| if (ins->isAddI()) |
| masm.subl(ToOperand(rhs), reg); |
| else |
| masm.addl(ToOperand(rhs), reg); |
| } |
| |
| return bailout(ool->ins()->snapshot()); |
| } |
| |
| class MulNegativeZeroCheck : public OutOfLineCodeBase<CodeGeneratorX86Shared> |
| { |
| LMulI *ins_; |
| |
| public: |
| MulNegativeZeroCheck(LMulI *ins) |
| : ins_(ins) |
| { } |
| |
| virtual bool accept(CodeGeneratorX86Shared *codegen) { |
| return codegen->visitMulNegativeZeroCheck(this); |
| } |
| LMulI *ins() const { |
| return ins_; |
| } |
| }; |
| |
| bool |
| CodeGeneratorX86Shared::visitMulI(LMulI *ins) |
| { |
| const LAllocation *lhs = ins->lhs(); |
| const LAllocation *rhs = ins->rhs(); |
| MMul *mul = ins->mir(); |
| JS_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow()); |
| |
| if (rhs->isConstant()) { |
| // Bailout on -0.0 |
| int32_t constant = ToInt32(rhs); |
| if (mul->canBeNegativeZero() && constant <= 0) { |
| Assembler::Condition bailoutCond = (constant == 0) ? Assembler::Signed : Assembler::Equal; |
| masm.testl(ToRegister(lhs), ToRegister(lhs)); |
| if (!bailoutIf(bailoutCond, ins->snapshot())) |
| return false; |
| } |
| |
| switch (constant) { |
| case -1: |
| masm.negl(ToOperand(lhs)); |
| break; |
| case 0: |
| masm.xorl(ToOperand(lhs), ToRegister(lhs)); |
| return true; // escape overflow check; |
| case 1: |
| // nop |
| return true; // escape overflow check; |
| case 2: |
| masm.addl(ToOperand(lhs), ToRegister(lhs)); |
| break; |
| default: |
| if (!mul->canOverflow() && constant > 0) { |
| // Use shift if cannot overflow and constant is power of 2 |
| int32_t shift; |
| JS_FLOOR_LOG2(shift, constant); |
| if ((1 << shift) == constant) { |
| masm.shll(Imm32(shift), ToRegister(lhs)); |
| return true; |
| } |
| } |
| masm.imull(Imm32(ToInt32(rhs)), ToRegister(lhs)); |
| } |
| |
| // Bailout on overflow |
| if (mul->canOverflow() && !bailoutIf(Assembler::Overflow, ins->snapshot())) |
| return false; |
| } else { |
| masm.imull(ToOperand(rhs), ToRegister(lhs)); |
| |
| // Bailout on overflow |
| if (mul->canOverflow() && !bailoutIf(Assembler::Overflow, ins->snapshot())) |
| return false; |
| |
| if (mul->canBeNegativeZero()) { |
| // Jump to an OOL path if the result is 0. |
| MulNegativeZeroCheck *ool = new MulNegativeZeroCheck(ins); |
| if (!addOutOfLineCode(ool)) |
| return false; |
| |
| masm.testl(ToRegister(lhs), ToRegister(lhs)); |
| masm.j(Assembler::Zero, ool->entry()); |
| masm.bind(ool->rejoin()); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitAsmJSDivOrMod(LAsmJSDivOrMod *ins) |
| { |
| JS_ASSERT(ToRegister(ins->lhs()) == eax); |
| Register rhs = ToRegister(ins->rhs()); |
| Register output = ToRegister(ins->output()); |
| |
| JS_ASSERT_IF(output == eax, ToRegister(ins->remainder()) == edx); |
| |
| Label afterDiv; |
| |
| masm.testl(rhs, rhs); |
| Label notzero; |
| masm.j(Assembler::NonZero, ¬zero); |
| masm.xorl(output, output); |
| masm.jmp(&afterDiv); |
| masm.bind(¬zero); |
| |
| masm.xorl(edx, edx); |
| masm.udiv(rhs); |
| |
| masm.bind(&afterDiv); |
| |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitMulNegativeZeroCheck(MulNegativeZeroCheck *ool) |
| { |
| LMulI *ins = ool->ins(); |
| Register result = ToRegister(ins->output()); |
| Operand lhsCopy = ToOperand(ins->lhsCopy()); |
| Operand rhs = ToOperand(ins->rhs()); |
| JS_ASSERT_IF(lhsCopy.kind() == Operand::REG, lhsCopy.reg() != result.code()); |
| |
| // Result is -0 if lhs or rhs is negative. |
| masm.movl(lhsCopy, result); |
| masm.orl(rhs, result); |
| if (!bailoutIf(Assembler::Signed, ins->snapshot())) |
| return false; |
| |
| masm.xorl(result, result); |
| masm.jmp(ool->rejoin()); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitDivPowTwoI(LDivPowTwoI *ins) |
| { |
| Register lhs = ToRegister(ins->numerator()); |
| Register lhsCopy = ToRegister(ins->numeratorCopy()); |
| mozilla::DebugOnly<Register> output = ToRegister(ins->output()); |
| int32_t shift = ins->shift(); |
| |
| // We use defineReuseInput so these should always be the same, which is |
| // convenient since all of our instructions here are two-address. |
| JS_ASSERT(lhs == output); |
| |
| if (shift != 0) { |
| if (!ins->mir()->isTruncated()) { |
| // If the remainder is != 0, bailout since this must be a double. |
| masm.testl(lhs, Imm32(UINT32_MAX >> (32 - shift))); |
| if (!bailoutIf(Assembler::NonZero, ins->snapshot())) |
| return false; |
| } |
| |
| // Adjust the value so that shifting produces a correctly rounded result |
| // when the numerator is negative. See 10-1 "Signed Division by a Known |
| // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight. |
| // Note that we wouldn't need to do this adjustment if we could use |
| // Range Analysis to find cases when the value is never negative. We |
| // wouldn't even need the lhsCopy either in that case. |
| if (shift > 1) |
| masm.sarl(Imm32(31), lhs); |
| masm.shrl(Imm32(32 - shift), lhs); |
| masm.addl(lhsCopy, lhs); |
| |
| // Do the shift. |
| masm.sarl(Imm32(shift), lhs); |
| } |
| |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitDivI(LDivI *ins) |
| { |
| Register remainder = ToRegister(ins->remainder()); |
| Register lhs = ToRegister(ins->lhs()); |
| Register rhs = ToRegister(ins->rhs()); |
| Register output = ToRegister(ins->output()); |
| |
| MDiv *mir = ins->mir(); |
| |
| JS_ASSERT(remainder == edx); |
| JS_ASSERT(lhs == eax); |
| JS_ASSERT(output == eax); |
| |
| Label done; |
| |
| // Handle divide by zero. |
| if (mir->canBeDivideByZero()) { |
| masm.testl(rhs, rhs); |
| if (mir->isTruncated()) { |
| // Truncated division by zero is zero (Infinity|0 == 0) |
| Label notzero; |
| masm.j(Assembler::NonZero, ¬zero); |
| masm.xorl(output, output); |
| masm.jmp(&done); |
| masm.bind(¬zero); |
| } else { |
| JS_ASSERT(mir->fallible()); |
| if (!bailoutIf(Assembler::Zero, ins->snapshot())) |
| return false; |
| } |
| } |
| |
| // Handle an integer overflow exception from -2147483648 / -1. |
| if (mir->canBeNegativeOverflow()) { |
| Label notmin; |
| masm.cmpl(lhs, Imm32(INT32_MIN)); |
| masm.j(Assembler::NotEqual, ¬min); |
| masm.cmpl(rhs, Imm32(-1)); |
| if (mir->isTruncated()) { |
| // (-INT32_MIN)|0 == INT32_MIN and INT32_MIN is already in the |
| // output register (lhs == eax). |
| masm.j(Assembler::Equal, &done); |
| } else { |
| JS_ASSERT(mir->fallible()); |
| if (!bailoutIf(Assembler::Equal, ins->snapshot())) |
| return false; |
| } |
| masm.bind(¬min); |
| } |
| |
| // Handle negative 0. |
| if (!mir->isTruncated() && mir->canBeNegativeZero()) { |
| Label nonzero; |
| masm.testl(lhs, lhs); |
| masm.j(Assembler::NonZero, &nonzero); |
| masm.cmpl(rhs, Imm32(0)); |
| if (!bailoutIf(Assembler::LessThan, ins->snapshot())) |
| return false; |
| masm.bind(&nonzero); |
| } |
| |
| // Sign extend eax into edx to make (edx:eax), since idiv is 64-bit. |
| masm.cdq(); |
| masm.idiv(rhs); |
| |
| if (!mir->isTruncated()) { |
| // If the remainder is > 0, bailout since this must be a double. |
| masm.testl(remainder, remainder); |
| if (!bailoutIf(Assembler::NonZero, ins->snapshot())) |
| return false; |
| } |
| |
| masm.bind(&done); |
| |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitModPowTwoI(LModPowTwoI *ins) |
| { |
| Register lhs = ToRegister(ins->getOperand(0)); |
| int32_t shift = ins->shift(); |
| |
| Label negative, done; |
| // Switch based on sign of the lhs. |
| // Positive numbers are just a bitmask |
| masm.branchTest32(Assembler::Signed, lhs, lhs, &negative); |
| { |
| masm.andl(Imm32((1 << shift) - 1), lhs); |
| masm.jump(&done); |
| } |
| // Negative numbers need a negate, bitmask, negate |
| { |
| masm.bind(&negative); |
| // visitModI has an overflow check here to catch INT_MIN % -1, but |
| // here the rhs is a power of 2, and cannot be -1, so the check is not generated. |
| masm.negl(lhs); |
| masm.andl(Imm32((1 << shift) - 1), lhs); |
| masm.negl(lhs); |
| if (!ins->mir()->isTruncated() && !bailoutIf(Assembler::Zero, ins->snapshot())) |
| return false; |
| } |
| masm.bind(&done); |
| return true; |
| |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitModI(LModI *ins) |
| { |
| Register remainder = ToRegister(ins->remainder()); |
| Register lhs = ToRegister(ins->lhs()); |
| Register rhs = ToRegister(ins->rhs()); |
| Register temp = ToRegister(ins->getTemp(0)); |
| |
| // Required to use idiv. |
| JS_ASSERT(remainder == edx); |
| JS_ASSERT(temp == eax); |
| |
| if (lhs != temp) { |
| masm.mov(lhs, temp); |
| lhs = temp; |
| } |
| |
| Label done; |
| |
| // Prevent divide by zero |
| masm.testl(rhs, rhs); |
| if (ins->mir()->isTruncated()) { |
| Label notzero; |
| masm.j(Assembler::NonZero, ¬zero); |
| masm.xorl(edx, edx); |
| masm.jmp(&done); |
| masm.bind(¬zero); |
| } else { |
| if (!bailoutIf(Assembler::Zero, ins->snapshot())) |
| return false; |
| } |
| |
| Label negative; |
| |
| // Switch based on sign of the lhs. |
| masm.branchTest32(Assembler::Signed, lhs, lhs, &negative); |
| // If lhs >= 0 then remainder = lhs % rhs. The remainder must be positive. |
| { |
| // Since lhs >= 0, the sign-extension will be 0 |
| masm.xorl(edx, edx); |
| masm.idiv(rhs); |
| masm.jump(&done); |
| } |
| |
| // Otherwise, we have to beware of two special cases: |
| { |
| masm.bind(&negative); |
| |
| // Prevent an integer overflow exception from -2147483648 % -1 |
| Label notmin; |
| masm.cmpl(lhs, Imm32(INT32_MIN)); |
| masm.j(Assembler::NotEqual, ¬min); |
| masm.cmpl(rhs, Imm32(-1)); |
| if (ins->mir()->isTruncated()) { |
| masm.j(Assembler::NotEqual, ¬min); |
| masm.xorl(edx, edx); |
| masm.jmp(&done); |
| } else { |
| if (!bailoutIf(Assembler::Equal, ins->snapshot())) |
| return false; |
| } |
| masm.bind(¬min); |
| |
| masm.cdq(); |
| masm.idiv(rhs); |
| |
| if (!ins->mir()->isTruncated()) { |
| // A remainder of 0 means that the rval must be -0, which is a double. |
| masm.testl(remainder, remainder); |
| if (!bailoutIf(Assembler::Zero, ins->snapshot())) |
| return false; |
| } |
| } |
| |
| masm.bind(&done); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitBitNotI(LBitNotI *ins) |
| { |
| const LAllocation *input = ins->getOperand(0); |
| JS_ASSERT(!input->isConstant()); |
| |
| masm.notl(ToOperand(input)); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitBitOpI(LBitOpI *ins) |
| { |
| const LAllocation *lhs = ins->getOperand(0); |
| const LAllocation *rhs = ins->getOperand(1); |
| |
| switch (ins->bitop()) { |
| case JSOP_BITOR: |
| if (rhs->isConstant()) |
| masm.orl(Imm32(ToInt32(rhs)), ToOperand(lhs)); |
| else |
| masm.orl(ToOperand(rhs), ToRegister(lhs)); |
| break; |
| case JSOP_BITXOR: |
| if (rhs->isConstant()) |
| masm.xorl(Imm32(ToInt32(rhs)), ToOperand(lhs)); |
| else |
| masm.xorl(ToOperand(rhs), ToRegister(lhs)); |
| break; |
| case JSOP_BITAND: |
| if (rhs->isConstant()) |
| masm.andl(Imm32(ToInt32(rhs)), ToOperand(lhs)); |
| else |
| masm.andl(ToOperand(rhs), ToRegister(lhs)); |
| break; |
| default: |
| JS_NOT_REACHED("unexpected binary opcode"); |
| } |
| |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitShiftI(LShiftI *ins) |
| { |
| Register lhs = ToRegister(ins->lhs()); |
| const LAllocation *rhs = ins->rhs(); |
| |
| if (rhs->isConstant()) { |
| int32_t shift = ToInt32(rhs) & 0x1F; |
| switch (ins->bitop()) { |
| case JSOP_LSH: |
| if (shift) |
| masm.shll(Imm32(shift), lhs); |
| break; |
| case JSOP_RSH: |
| if (shift) |
| masm.sarl(Imm32(shift), lhs); |
| break; |
| case JSOP_URSH: |
| if (shift) { |
| masm.shrl(Imm32(shift), lhs); |
| } else if (ins->mir()->toUrsh()->canOverflow()) { |
| // x >>> 0 can overflow. |
| masm.testl(lhs, lhs); |
| if (!bailoutIf(Assembler::Signed, ins->snapshot())) |
| return false; |
| } |
| break; |
| default: |
| JS_NOT_REACHED("Unexpected shift op"); |
| } |
| } else { |
| JS_ASSERT(ToRegister(rhs) == ecx); |
| switch (ins->bitop()) { |
| case JSOP_LSH: |
| masm.shll_cl(lhs); |
| break; |
| case JSOP_RSH: |
| masm.sarl_cl(lhs); |
| break; |
| case JSOP_URSH: |
| masm.shrl_cl(lhs); |
| if (ins->mir()->toUrsh()->canOverflow()) { |
| // x >>> 0 can overflow. |
| masm.testl(lhs, lhs); |
| if (!bailoutIf(Assembler::Signed, ins->snapshot())) |
| return false; |
| } |
| break; |
| default: |
| JS_NOT_REACHED("Unexpected shift op"); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitUrshD(LUrshD *ins) |
| { |
| Register lhs = ToRegister(ins->lhs()); |
| JS_ASSERT(ToRegister(ins->temp()) == lhs); |
| |
| const LAllocation *rhs = ins->rhs(); |
| FloatRegister out = ToFloatRegister(ins->output()); |
| |
| if (rhs->isConstant()) { |
| int32_t shift = ToInt32(rhs) & 0x1F; |
| if (shift) |
| masm.shrl(Imm32(shift), lhs); |
| } else { |
| JS_ASSERT(ToRegister(rhs) == ecx); |
| masm.shrl_cl(lhs); |
| } |
| |
| masm.convertUInt32ToDouble(lhs, out); |
| return true; |
| } |
| |
| typedef MoveResolver::MoveOperand MoveOperand; |
| |
| MoveOperand |
| CodeGeneratorX86Shared::toMoveOperand(const LAllocation *a) const |
| { |
| if (a->isGeneralReg()) |
| return MoveOperand(ToRegister(a)); |
| if (a->isFloatReg()) |
| return MoveOperand(ToFloatRegister(a)); |
| return MoveOperand(StackPointer, ToStackOffset(a)); |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitMoveGroup(LMoveGroup *group) |
| { |
| if (!group->numMoves()) |
| return true; |
| |
| MoveResolver &resolver = masm.moveResolver(); |
| |
| for (size_t i = 0; i < group->numMoves(); i++) { |
| const LMove &move = group->getMove(i); |
| |
| const LAllocation *from = move.from(); |
| const LAllocation *to = move.to(); |
| |
| // No bogus moves. |
| JS_ASSERT(*from != *to); |
| JS_ASSERT(!from->isConstant()); |
| JS_ASSERT(from->isDouble() == to->isDouble()); |
| |
| MoveResolver::Move::Kind kind = from->isDouble() |
| ? MoveResolver::Move::DOUBLE |
| : MoveResolver::Move::GENERAL; |
| |
| if (!resolver.addMove(toMoveOperand(from), toMoveOperand(to), kind)) |
| return false; |
| } |
| |
| if (!resolver.resolve()) |
| return false; |
| |
| MoveEmitter emitter(masm); |
| emitter.emit(resolver); |
| emitter.finish(); |
| |
| return true; |
| } |
| |
| class OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorX86Shared> |
| { |
| MTableSwitch *mir_; |
| CodeLabel jumpLabel_; |
| |
| bool accept(CodeGeneratorX86Shared *codegen) { |
| return codegen->visitOutOfLineTableSwitch(this); |
| } |
| |
| public: |
| OutOfLineTableSwitch(MTableSwitch *mir) |
| : mir_(mir) |
| {} |
| |
| MTableSwitch *mir() const { |
| return mir_; |
| } |
| |
| CodeLabel *jumpLabel() { |
| return &jumpLabel_; |
| } |
| }; |
| |
| bool |
| CodeGeneratorX86Shared::visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool) |
| { |
| MTableSwitch *mir = ool->mir(); |
| |
| masm.align(sizeof(void*)); |
| masm.bind(ool->jumpLabel()->src()); |
| if (!masm.addCodeLabel(*ool->jumpLabel())) |
| return false; |
| |
| for (size_t i = 0; i < mir->numCases(); i++) { |
| LBlock *caseblock = mir->getCase(i)->lir(); |
| Label *caseheader = caseblock->label(); |
| uint32_t caseoffset = caseheader->offset(); |
| |
| // The entries of the jump table need to be absolute addresses and thus |
| // must be patched after codegen is finished. |
| CodeLabel cl; |
| masm.writeCodePointer(cl.dest()); |
| cl.src()->bind(caseoffset); |
| if (!masm.addCodeLabel(cl)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::emitTableSwitchDispatch(MTableSwitch *mir, const Register &index, |
| const Register &base) |
| { |
| Label *defaultcase = mir->getDefault()->lir()->label(); |
| |
| // Lower value with low value |
| if (mir->low() != 0) |
| masm.subl(Imm32(mir->low()), index); |
| |
| // Jump to default case if input is out of range |
| int32_t cases = mir->numCases(); |
| masm.cmpl(index, Imm32(cases)); |
| masm.j(AssemblerX86Shared::AboveOrEqual, defaultcase); |
| |
| // To fill in the CodeLabels for the case entries, we need to first |
| // generate the case entries (we don't yet know their offsets in the |
| // instruction stream). |
| OutOfLineTableSwitch *ool = new OutOfLineTableSwitch(mir); |
| if (!addOutOfLineCode(ool)) |
| return false; |
| |
| // Compute the position where a pointer to the right case stands. |
| masm.mov(ool->jumpLabel()->dest(), base); |
| Operand pointer = Operand(base, index, ScalePointer); |
| |
| // Jump to the right case |
| masm.jmp(pointer); |
| |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitMathD(LMathD *math) |
| { |
| FloatRegister lhs = ToFloatRegister(math->lhs()); |
| Operand rhs = ToOperand(math->rhs()); |
| |
| JS_ASSERT(ToFloatRegister(math->output()) == lhs); |
| |
| switch (math->jsop()) { |
| case JSOP_ADD: |
| masm.addsd(rhs, lhs); |
| break; |
| case JSOP_SUB: |
| masm.subsd(rhs, lhs); |
| break; |
| case JSOP_MUL: |
| masm.mulsd(rhs, lhs); |
| break; |
| case JSOP_DIV: |
| masm.divsd(rhs, lhs); |
| break; |
| default: |
| JS_NOT_REACHED("unexpected opcode"); |
| return false; |
| } |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitFloor(LFloor *lir) |
| { |
| FloatRegister input = ToFloatRegister(lir->input()); |
| FloatRegister scratch = ScratchFloatReg; |
| Register output = ToRegister(lir->output()); |
| |
| if (AssemblerX86Shared::HasSSE41()) { |
| // Bail on negative-zero. |
| Assembler::Condition bailCond = masm.testNegativeZero(input, output); |
| if (!bailoutIf(bailCond, lir->snapshot())) |
| return false; |
| |
| // Round toward -Infinity. |
| masm.roundsd(input, scratch, JSC::X86Assembler::RoundDown); |
| |
| masm.cvttsd2si(scratch, output); |
| masm.cmp32(output, Imm32(INT_MIN)); |
| if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
| return false; |
| } else { |
| Label negative, end; |
| |
| // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. |
| masm.xorpd(scratch, scratch); |
| masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &negative); |
| |
| // Bail on negative-zero. |
| Assembler::Condition bailCond = masm.testNegativeZero(input, output); |
| if (!bailoutIf(bailCond, lir->snapshot())) |
| return false; |
| |
| // Input is non-negative, so truncation correctly rounds. |
| masm.cvttsd2si(input, output); |
| masm.cmp32(output, Imm32(INT_MIN)); |
| if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
| return false; |
| |
| masm.jump(&end); |
| |
| // Input is negative, but isn't -0. |
| // Negative values go on a comparatively expensive path, since no |
| // native rounding mode matches JS semantics. Still better than callVM. |
| masm.bind(&negative); |
| { |
| // Truncate and round toward zero. |
| // This is off-by-one for everything but integer-valued inputs. |
| masm.cvttsd2si(input, output); |
| masm.cmp32(output, Imm32(INT_MIN)); |
| if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
| return false; |
| |
| // Test whether the input double was integer-valued. |
| masm.cvtsi2sd(output, scratch); |
| masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch, &end); |
| |
| // Input is not integer-valued, so we rounded off-by-one in the |
| // wrong direction. Correct by subtraction. |
| masm.subl(Imm32(1), output); |
| // Cannot overflow: output was already checked against INT_MIN. |
| } |
| |
| masm.bind(&end); |
| } |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitRound(LRound *lir) |
| { |
| FloatRegister input = ToFloatRegister(lir->input()); |
| FloatRegister temp = ToFloatRegister(lir->temp()); |
| FloatRegister scratch = ScratchFloatReg; |
| Register output = ToRegister(lir->output()); |
| |
| Label negative, end; |
| |
| // Load 0.5 in the temp register. |
| static const double PointFive = 0.5; |
| masm.loadStaticDouble(&PointFive, temp); |
| |
| // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. |
| masm.xorpd(scratch, scratch); |
| masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &negative); |
| |
| // Bail on negative-zero. |
| Assembler::Condition bailCond = masm.testNegativeZero(input, output); |
| if (!bailoutIf(bailCond, lir->snapshot())) |
| return false; |
| |
| // Input is non-negative. Add 0.5 and truncate, rounding down. Note that we |
| // have to add the input to the temp register (which contains 0.5) because |
| // we're not allowed to modify the input register. |
| masm.addsd(input, temp); |
| |
| masm.cvttsd2si(temp, output); |
| masm.cmp32(output, Imm32(INT_MIN)); |
| if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
| return false; |
| |
| masm.jump(&end); |
| |
| |
| // Input is negative, but isn't -0. |
| masm.bind(&negative); |
| |
| if (AssemblerX86Shared::HasSSE41()) { |
| // Add 0.5 and round toward -Infinity. The result is stored in the temp |
| // register (currently contains 0.5). |
| masm.addsd(input, temp); |
| masm.roundsd(temp, scratch, JSC::X86Assembler::RoundDown); |
| |
| // Truncate. |
| masm.cvttsd2si(scratch, output); |
| masm.cmp32(output, Imm32(INT_MIN)); |
| if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
| return false; |
| |
| // If the result is positive zero, then the actual result is -0. Bail. |
| // Otherwise, the truncation will have produced the correct negative integer. |
| masm.testl(output, output); |
| if (!bailoutIf(Assembler::Zero, lir->snapshot())) |
| return false; |
| |
| } else { |
| masm.addsd(input, temp); |
| |
| // Round toward -Infinity without the benefit of ROUNDSD. |
| Label testZero; |
| { |
| // Truncate and round toward zero. |
| // This is off-by-one for everything but integer-valued inputs. |
| masm.cvttsd2si(temp, output); |
| masm.cmp32(output, Imm32(INT_MIN)); |
| if (!bailoutIf(Assembler::Equal, lir->snapshot())) |
| return false; |
| |
| // Test whether the truncated double was integer-valued. |
| masm.cvtsi2sd(output, scratch); |
| masm.branchDouble(Assembler::DoubleEqualOrUnordered, temp, scratch, &testZero); |
| |
| // Input is not integer-valued, so we rounded off-by-one in the |
| // wrong direction. Correct by subtraction. |
| masm.subl(Imm32(1), output); |
| // Cannot overflow: output was already checked against INT_MIN. |
| |
| // Fall through to testZero. |
| } |
| |
| masm.bind(&testZero); |
| if (!bailoutIf(Assembler::Zero, lir->snapshot())) |
| return false; |
| } |
| |
| masm.bind(&end); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitGuardShape(LGuardShape *guard) |
| { |
| Register obj = ToRegister(guard->input()); |
| masm.cmpPtr(Operand(obj, JSObject::offsetOfShape()), ImmGCPtr(guard->mir()->shape())); |
| |
| return bailoutIf(Assembler::NotEqual, guard->snapshot()); |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitGuardObjectType(LGuardObjectType *guard) |
| { |
| Register obj = ToRegister(guard->input()); |
| masm.cmpPtr(Operand(obj, JSObject::offsetOfType()), ImmGCPtr(guard->mir()->typeObject())); |
| |
| Assembler::Condition cond = |
| guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; |
| return bailoutIf(cond, guard->snapshot()); |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitGuardClass(LGuardClass *guard) |
| { |
| Register obj = ToRegister(guard->input()); |
| Register tmp = ToRegister(guard->tempInt()); |
| |
| masm.loadPtr(Address(obj, JSObject::offsetOfType()), tmp); |
| masm.cmpPtr(Operand(tmp, offsetof(types::TypeObject, clasp)), ImmWord(guard->mir()->getClass())); |
| if (!bailoutIf(Assembler::NotEqual, guard->snapshot())) |
| return false; |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitEffectiveAddress(LEffectiveAddress *ins) |
| { |
| const MEffectiveAddress *mir = ins->mir(); |
| Register base = ToRegister(ins->base()); |
| Register index = ToRegister(ins->index()); |
| Register output = ToRegister(ins->output()); |
| masm.leal(Operand(base, index, mir->scale(), mir->displacement()), output); |
| return true; |
| } |
| |
| Operand |
| CodeGeneratorX86Shared::createArrayElementOperand(Register elements, const LAllocation *index) |
| { |
| if (index->isConstant()) |
| return Operand(elements, ToInt32(index) * sizeof(js::Value)); |
| |
| return Operand(elements, ToRegister(index), TimesEight); |
| } |
| bool |
| CodeGeneratorX86Shared::generateInvalidateEpilogue() |
| { |
| // Ensure that there is enough space in the buffer for the OsiPoint |
| // patching to occur. Otherwise, we could overwrite the invalidation |
| // epilogue. |
| for (size_t i = 0; i < sizeof(void *); i+= Assembler::nopSize()) |
| masm.nop(); |
| |
| masm.bind(&invalidate_); |
| |
| // Push the Ion script onto the stack (when we determine what that pointer is). |
| invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1))); |
| IonCode *thunk = GetIonContext()->compartment->ionCompartment()->getInvalidationThunk(); |
| |
| masm.call(thunk); |
| |
| // We should never reach this point in JIT code -- the invalidation thunk should |
| // pop the invalidated JS frame and return directly to its caller. |
| masm.breakpoint(); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitNegI(LNegI *ins) |
| { |
| Register input = ToRegister(ins->input()); |
| JS_ASSERT(input == ToRegister(ins->output())); |
| |
| masm.neg32(input); |
| return true; |
| } |
| |
| bool |
| CodeGeneratorX86Shared::visitNegD(LNegD *ins) |
| { |
| FloatRegister input = ToFloatRegister(ins->input()); |
| JS_ASSERT(input == ToFloatRegister(ins->output())); |
| |
| masm.negateDouble(input); |
| return true; |
| } |
| |
| |
| } // namespace jit |
| } // namespace js |