michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/shared/Lowering-x86-shared.h" michael@0: michael@0: #include "mozilla/MathAlgorithms.h" michael@0: michael@0: #include "jit/MIR.h" michael@0: michael@0: #include "jit/shared/Lowering-shared-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: using mozilla::Abs; michael@0: using mozilla::FloorLog2; michael@0: michael@0: LTableSwitch * michael@0: LIRGeneratorX86Shared::newLTableSwitch(const LAllocation &in, const LDefinition &inputCopy, michael@0: MTableSwitch *tableswitch) michael@0: { michael@0: return new(alloc()) LTableSwitch(in, inputCopy, temp(), tableswitch); michael@0: } michael@0: michael@0: LTableSwitchV * michael@0: LIRGeneratorX86Shared::newLTableSwitchV(MTableSwitch *tableswitch) michael@0: { michael@0: return new(alloc()) LTableSwitchV(temp(), tempDouble(), temp(), tableswitch); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::visitGuardShape(MGuardShape *ins) michael@0: { michael@0: JS_ASSERT(ins->obj()->type() == MIRType_Object); michael@0: michael@0: LGuardShape *guard = new(alloc()) LGuardShape(useRegister(ins->obj())); michael@0: if (!assignSnapshot(guard, ins->bailoutKind())) michael@0: return false; michael@0: if (!add(guard, ins)) michael@0: return false; michael@0: return redefine(ins, ins->obj()); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::visitGuardObjectType(MGuardObjectType *ins) michael@0: { michael@0: JS_ASSERT(ins->obj()->type() == MIRType_Object); michael@0: michael@0: LGuardObjectType *guard = new(alloc()) LGuardObjectType(useRegister(ins->obj())); michael@0: if (!assignSnapshot(guard)) michael@0: return false; michael@0: if (!add(guard, ins)) michael@0: return false; michael@0: return redefine(ins, ins->obj()); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::visitPowHalf(MPowHalf *ins) michael@0: { michael@0: MDefinition *input = ins->input(); michael@0: JS_ASSERT(input->type() == MIRType_Double); michael@0: LPowHalfD *lir = new(alloc()) LPowHalfD(useRegisterAtStart(input)); michael@0: return defineReuseInput(lir, ins, 0); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, michael@0: MDefinition *lhs, MDefinition *rhs) michael@0: { michael@0: ins->setOperand(0, useRegisterAtStart(lhs)); michael@0: michael@0: // shift operator should be constant or in register ecx michael@0: // x86 can't shift a non-ecx register michael@0: if (rhs->isConstant()) michael@0: ins->setOperand(1, useOrConstant(rhs)); michael@0: else michael@0: ins->setOperand(1, useFixed(rhs, ecx)); michael@0: michael@0: return defineReuseInput(ins, mir, 0); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, michael@0: MDefinition *input) michael@0: { michael@0: ins->setOperand(0, useRegisterAtStart(input)); michael@0: return defineReuseInput(ins, mir, 0); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, michael@0: MDefinition *lhs, MDefinition *rhs) michael@0: { michael@0: ins->setOperand(0, useRegisterAtStart(lhs)); michael@0: ins->setOperand(1, useOrConstant(rhs)); michael@0: return defineReuseInput(ins, mir, 0); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs) michael@0: { michael@0: ins->setOperand(0, useRegisterAtStart(lhs)); michael@0: ins->setOperand(1, use(rhs)); michael@0: return defineReuseInput(ins, mir, 0); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir, michael@0: MDefinition *lhs, MDefinition *rhs) michael@0: { michael@0: baab->setOperand(0, useRegisterAtStart(lhs)); michael@0: baab->setOperand(1, useRegisterOrConstantAtStart(rhs)); michael@0: return add(baab, mir); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs) michael@0: { michael@0: // Note: lhs is used twice, so that we can restore the original value for the michael@0: // negative zero check. michael@0: LMulI *lir = new(alloc()) LMulI(useRegisterAtStart(lhs), useOrConstant(rhs), use(lhs)); michael@0: if (mul->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) michael@0: return false; michael@0: return defineReuseInput(lir, mul, 0); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerDivI(MDiv *div) michael@0: { michael@0: if (div->isUnsigned()) michael@0: return lowerUDiv(div); michael@0: michael@0: // Division instructions are slow. Division by constant denominators can be michael@0: // rewritten to use other instructions. michael@0: if (div->rhs()->isConstant()) { michael@0: int32_t rhs = div->rhs()->toConstant()->value().toInt32(); michael@0: michael@0: // Division by powers of two can be done by shifting, and division by michael@0: // other numbers can be done by a reciprocal multiplication technique. michael@0: int32_t shift = FloorLog2(Abs(rhs)); michael@0: if (rhs != 0 && uint32_t(1) << shift == Abs(rhs)) { michael@0: LAllocation lhs = useRegisterAtStart(div->lhs()); michael@0: LDivPowTwoI *lir; michael@0: if (!div->canBeNegativeDividend()) { michael@0: // Numerator is unsigned, so does not need adjusting. michael@0: lir = new(alloc()) LDivPowTwoI(lhs, lhs, shift, rhs < 0); michael@0: } else { michael@0: // Numerator is signed, and needs adjusting, and an extra michael@0: // lhs copy register is needed. michael@0: lir = new(alloc()) LDivPowTwoI(lhs, useRegister(div->lhs()), shift, rhs < 0); michael@0: } michael@0: if (div->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) michael@0: return false; michael@0: return defineReuseInput(lir, div, 0); michael@0: } michael@0: } michael@0: michael@0: LDivI *lir = new(alloc()) LDivI(useRegister(div->lhs()), useRegister(div->rhs()), michael@0: tempFixed(edx)); michael@0: if (div->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) michael@0: return false; michael@0: return defineFixed(lir, div, LAllocation(AnyRegister(eax))); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerModI(MMod *mod) michael@0: { michael@0: if (mod->isUnsigned()) michael@0: return lowerUMod(mod); michael@0: michael@0: if (mod->rhs()->isConstant()) { michael@0: int32_t rhs = mod->rhs()->toConstant()->value().toInt32(); michael@0: int32_t shift = FloorLog2(Abs(rhs)); michael@0: if (rhs != 0 && uint32_t(1) << shift == Abs(rhs)) { michael@0: LModPowTwoI *lir = new(alloc()) LModPowTwoI(useRegisterAtStart(mod->lhs()), shift); michael@0: if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) michael@0: return false; michael@0: return defineReuseInput(lir, mod, 0); michael@0: } michael@0: } michael@0: michael@0: LModI *lir = new(alloc()) LModI(useRegister(mod->lhs()), michael@0: useRegister(mod->rhs()), michael@0: tempFixed(eax)); michael@0: if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) michael@0: return false; michael@0: return defineFixed(lir, mod, LAllocation(AnyRegister(edx))); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::visitAsmJSNeg(MAsmJSNeg *ins) michael@0: { michael@0: if (ins->type() == MIRType_Int32) michael@0: return defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins, 0); michael@0: michael@0: if (ins->type() == MIRType_Float32) michael@0: return defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins, 0); michael@0: michael@0: JS_ASSERT(ins->type() == MIRType_Double); michael@0: return defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins, 0); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerUDiv(MDiv *div) michael@0: { michael@0: LUDivOrMod *lir = new(alloc()) LUDivOrMod(useRegister(div->lhs()), michael@0: useRegister(div->rhs()), michael@0: tempFixed(edx)); michael@0: if (div->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) michael@0: return false; michael@0: return defineFixed(lir, div, LAllocation(AnyRegister(eax))); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerUMod(MMod *mod) michael@0: { michael@0: LUDivOrMod *lir = new(alloc()) LUDivOrMod(useRegister(mod->lhs()), michael@0: useRegister(mod->rhs()), michael@0: tempFixed(eax)); michael@0: if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) michael@0: return false; michael@0: return defineFixed(lir, mod, LAllocation(AnyRegister(edx))); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerUrshD(MUrsh *mir) michael@0: { michael@0: MDefinition *lhs = mir->lhs(); michael@0: MDefinition *rhs = mir->rhs(); michael@0: michael@0: JS_ASSERT(lhs->type() == MIRType_Int32); michael@0: JS_ASSERT(rhs->type() == MIRType_Int32); michael@0: JS_ASSERT(mir->type() == MIRType_Double); michael@0: michael@0: #ifdef JS_CODEGEN_X64 michael@0: JS_ASSERT(ecx == rcx); michael@0: #endif michael@0: michael@0: LUse lhsUse = useRegisterAtStart(lhs); michael@0: LAllocation rhsAlloc = rhs->isConstant() ? useOrConstant(rhs) : useFixed(rhs, ecx); michael@0: michael@0: LUrshD *lir = new(alloc()) LUrshD(lhsUse, rhsAlloc, tempCopy(lhs, 0)); michael@0: return define(lir, mir); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerConstantDouble(double d, MInstruction *mir) michael@0: { michael@0: return define(new(alloc()) LDouble(d), mir); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerConstantFloat32(float f, MInstruction *mir) michael@0: { michael@0: return define(new(alloc()) LFloat32(f), mir); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::visitConstant(MConstant *ins) michael@0: { michael@0: if (ins->type() == MIRType_Double) michael@0: return lowerConstantDouble(ins->value().toDouble(), ins); michael@0: michael@0: if (ins->type() == MIRType_Float32) michael@0: return lowerConstantFloat32(ins->value().toDouble(), ins); michael@0: michael@0: // Emit non-double constants at their uses. michael@0: if (ins->canEmitAtUses()) michael@0: return emitAtUses(ins); michael@0: michael@0: return LIRGeneratorShared::visitConstant(ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerTruncateDToInt32(MTruncateToInt32 *ins) michael@0: { michael@0: MDefinition *opd = ins->input(); michael@0: JS_ASSERT(opd->type() == MIRType_Double); michael@0: michael@0: LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempDouble(); michael@0: return define(new(alloc()) LTruncateDToInt32(useRegister(opd), maybeTemp), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::lowerTruncateFToInt32(MTruncateToInt32 *ins) michael@0: { michael@0: MDefinition *opd = ins->input(); michael@0: JS_ASSERT(opd->type() == MIRType_Float32); michael@0: michael@0: LDefinition maybeTemp = Assembler::HasSSE3() ? LDefinition::BogusTemp() : tempFloat32(); michael@0: return define(new(alloc()) LTruncateFToInt32(useRegister(opd), maybeTemp), ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86Shared::visitForkJoinGetSlice(MForkJoinGetSlice *ins) michael@0: { michael@0: // We fix eax and edx for cmpxchg and div. michael@0: LForkJoinGetSlice *lir = new(alloc()) michael@0: LForkJoinGetSlice(useFixed(ins->forkJoinContext(), ForkJoinGetSliceReg_cx), michael@0: tempFixed(eax), michael@0: tempFixed(edx), michael@0: tempFixed(ForkJoinGetSliceReg_temp0), michael@0: tempFixed(ForkJoinGetSliceReg_temp1)); michael@0: return defineFixed(lir, ins, LAllocation(AnyRegister(ForkJoinGetSliceReg_output))); michael@0: }