diff -r 000000000000 -r 6474c204b198 js/src/jit/mips/Lowering-mips.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/js/src/jit/mips/Lowering-mips.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,531 @@ +/* -*- 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/MathAlgorithms.h" + + +#include "jit/Lowering.h" +#include "jit/mips/Assembler-mips.h" +#include "jit/MIR.h" + +#include "jit/shared/Lowering-shared-inl.h" + +using namespace js; +using namespace js::jit; + +using mozilla::FloorLog2; + +bool +LIRGeneratorMIPS::useBox(LInstruction *lir, size_t n, MDefinition *mir, + LUse::Policy policy, bool useAtStart) +{ + MOZ_ASSERT(mir->type() == MIRType_Value); + + if (!ensureDefined(mir)) + return false; + lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart)); + lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart)); + return true; +} + +bool +LIRGeneratorMIPS::useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, + Register reg2) +{ + MOZ_ASSERT(mir->type() == MIRType_Value); + MOZ_ASSERT(reg1 != reg2); + + if (!ensureDefined(mir)) + return false; + lir->setOperand(n, LUse(reg1, mir->virtualRegister())); + lir->setOperand(n + 1, LUse(reg2, VirtualRegisterOfPayload(mir))); + return true; +} + +LAllocation +LIRGeneratorMIPS::useByteOpRegister(MDefinition *mir) +{ + return useRegister(mir); +} + +LAllocation +LIRGeneratorMIPS::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir) +{ + return useRegisterOrNonDoubleConstant(mir); +} + +bool +LIRGeneratorMIPS::lowerConstantDouble(double d, MInstruction *mir) +{ + return define(new(alloc()) LDouble(d), mir); +} + +bool +LIRGeneratorMIPS::lowerConstantFloat32(float d, MInstruction *mir) +{ + return define(new(alloc()) LFloat32(d), mir); +} + +bool +LIRGeneratorMIPS::visitConstant(MConstant *ins) +{ + if (ins->type() == MIRType_Double) + return lowerConstantDouble(ins->value().toDouble(), ins); + + if (ins->type() == MIRType_Float32) + return lowerConstantFloat32(ins->value().toDouble(), ins); + + // Emit non-double constants at their uses. + if (ins->canEmitAtUses()) + return emitAtUses(ins); + + return LIRGeneratorShared::visitConstant(ins); +} + +bool +LIRGeneratorMIPS::visitBox(MBox *box) +{ + MDefinition *inner = box->getOperand(0); + + // If the box wrapped a double, it needs a new register. + if (IsFloatingPointType(inner->type())) + return defineBox(new(alloc()) LBoxFloatingPoint(useRegisterAtStart(inner), + tempCopy(inner, 0), inner->type()), box); + + if (box->canEmitAtUses()) + return emitAtUses(box); + + if (inner->isConstant()) + return defineBox(new(alloc()) LValue(inner->toConstant()->value()), box); + + LBox *lir = new(alloc()) LBox(use(inner), inner->type()); + + // Otherwise, we should not define a new register for the payload portion + // of the output, so bypass defineBox(). + uint32_t vreg = getVirtualRegister(); + if (vreg >= MAX_VIRTUAL_REGISTERS) + return false; + + // Note that because we're using PASSTHROUGH, we do not change the type of + // the definition. We also do not define the first output as "TYPE", + // because it has no corresponding payload at (vreg + 1). Also note that + // although we copy the input's original type for the payload half of the + // definition, this is only for clarity. PASSTHROUGH definitions are + // ignored. + lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL)); + lir->setDef(1, LDefinition(inner->virtualRegister(), LDefinition::TypeFrom(inner->type()), + LDefinition::PASSTHROUGH)); + box->setVirtualRegister(vreg); + return add(lir); +} + +bool +LIRGeneratorMIPS::visitUnbox(MUnbox *unbox) +{ + // An unbox on mips reads in a type tag (either in memory or a register) and + // a payload. Unlike most instructions consuming a box, we ask for the type + // second, so that the result can re-use the first input. + MDefinition *inner = unbox->getOperand(0); + + if (!ensureDefined(inner)) + return false; + + if (IsFloatingPointType(unbox->type())) { + LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type()); + if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind())) + return false; + if (!useBox(lir, LUnboxFloatingPoint::Input, inner)) + return false; + return define(lir, unbox); + } + + // Swap the order we use the box pieces so we can re-use the payload + // register. + LUnbox *lir = new(alloc()) LUnbox; + lir->setOperand(0, usePayloadInRegisterAtStart(inner)); + lir->setOperand(1, useType(inner, LUse::REGISTER)); + + if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind())) + return false; + + // Note that PASSTHROUGH here is illegal, since types and payloads form two + // separate intervals. If the type becomes dead before the payload, it + // could be used as a Value without the type being recoverable. Unbox's + // purpose is to eagerly kill the definition of a type tag, so keeping both + // alive (for the purpose of gcmaps) is unappealing. Instead, we create a + // new virtual register. + return defineReuseInput(lir, unbox, 0); +} + +bool +LIRGeneratorMIPS::visitReturn(MReturn *ret) +{ + MDefinition *opd = ret->getOperand(0); + MOZ_ASSERT(opd->type() == MIRType_Value); + + LReturn *ins = new(alloc()) LReturn; + ins->setOperand(0, LUse(JSReturnReg_Type)); + ins->setOperand(1, LUse(JSReturnReg_Data)); + return fillBoxUses(ins, 0, opd) && add(ins); +} + +// x = !y +bool +LIRGeneratorMIPS::lowerForALU(LInstructionHelper<1, 1, 0> *ins, + MDefinition *mir, MDefinition *input) +{ + ins->setOperand(0, useRegister(input)); + return define(ins, mir, + LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::DEFAULT)); +} + +// z = x+y +bool +LIRGeneratorMIPS::lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, + MDefinition *lhs, MDefinition *rhs) +{ + ins->setOperand(0, useRegister(lhs)); + ins->setOperand(1, useRegisterOrConstant(rhs)); + return define(ins, mir, + LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::DEFAULT)); +} + +bool +LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, + MDefinition *input) +{ + ins->setOperand(0, useRegister(input)); + return define(ins, mir, + LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::DEFAULT)); +} + +bool +LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, + MDefinition *lhs, MDefinition *rhs) +{ + ins->setOperand(0, useRegister(lhs)); + ins->setOperand(1, useRegister(rhs)); + return define(ins, mir, + LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::DEFAULT)); +} + +bool +LIRGeneratorMIPS::lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir, + MDefinition *lhs, MDefinition *rhs) +{ + baab->setOperand(0, useRegisterAtStart(lhs)); + baab->setOperand(1, useRegisterOrConstantAtStart(rhs)); + return add(baab, mir); +} + +bool +LIRGeneratorMIPS::defineUntypedPhi(MPhi *phi, size_t lirIndex) +{ + LPhi *type = current->getPhi(lirIndex + VREG_TYPE_OFFSET); + LPhi *payload = current->getPhi(lirIndex + VREG_DATA_OFFSET); + + uint32_t typeVreg = getVirtualRegister(); + if (typeVreg >= MAX_VIRTUAL_REGISTERS) + return false; + + phi->setVirtualRegister(typeVreg); + + uint32_t payloadVreg = getVirtualRegister(); + if (payloadVreg >= MAX_VIRTUAL_REGISTERS) + return false; + MOZ_ASSERT(typeVreg + 1 == payloadVreg); + + type->setDef(0, LDefinition(typeVreg, LDefinition::TYPE)); + payload->setDef(0, LDefinition(payloadVreg, LDefinition::PAYLOAD)); + annotate(type); + annotate(payload); + return true; +} + +void +LIRGeneratorMIPS::lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, + LBlock *block, size_t lirIndex) +{ + MDefinition *operand = phi->getOperand(inputPosition); + LPhi *type = block->getPhi(lirIndex + VREG_TYPE_OFFSET); + LPhi *payload = block->getPhi(lirIndex + VREG_DATA_OFFSET); + type->setOperand(inputPosition, LUse(operand->virtualRegister() + VREG_TYPE_OFFSET, + LUse::ANY)); + payload->setOperand(inputPosition, LUse(VirtualRegisterOfPayload(operand), LUse::ANY)); +} + +bool +LIRGeneratorMIPS::lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, + MDefinition *lhs, MDefinition *rhs) +{ + ins->setOperand(0, useRegister(lhs)); + ins->setOperand(1, useRegisterOrConstant(rhs)); + return define(ins, mir); +} + +bool +LIRGeneratorMIPS::lowerDivI(MDiv *div) +{ + if (div->isUnsigned()) + return lowerUDiv(div); + + // Division instructions are slow. Division by constant denominators can be + // rewritten to use other instructions. + if (div->rhs()->isConstant()) { + int32_t rhs = div->rhs()->toConstant()->value().toInt32(); + // Check for division by a positive power of two, which is an easy and + // important case to optimize. Note that other optimizations are also + // possible; division by negative powers of two can be optimized in a + // similar manner as positive powers of two, and division by other + // constants can be optimized by a reciprocal multiplication technique. + int32_t shift = FloorLog2(rhs); + if (rhs > 0 && 1 << shift == rhs) { + LDivPowTwoI *lir = new(alloc()) LDivPowTwoI(useRegister(div->lhs()), shift, temp()); + if (div->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + return define(lir, div); + } + } + + LDivI *lir = new(alloc()) LDivI(useRegister(div->lhs()), useRegister(div->rhs()), temp()); + if (div->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + return define(lir, div); +} + +bool +LIRGeneratorMIPS::lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs) +{ + LMulI *lir = new(alloc()) LMulI; + if (mul->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + + return lowerForALU(lir, mul, lhs, rhs); +} + +bool +LIRGeneratorMIPS::lowerModI(MMod *mod) +{ + if (mod->isUnsigned()) + return lowerUMod(mod); + + if (mod->rhs()->isConstant()) { + int32_t rhs = mod->rhs()->toConstant()->value().toInt32(); + int32_t shift = FloorLog2(rhs); + if (rhs > 0 && 1 << shift == rhs) { + LModPowTwoI *lir = new(alloc()) LModPowTwoI(useRegister(mod->lhs()), shift); + if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + return define(lir, mod); + } else if (shift < 31 && (1 << (shift + 1)) - 1 == rhs) { + LModMaskI *lir = new(alloc()) LModMaskI(useRegister(mod->lhs()), + temp(LDefinition::GENERAL), shift + 1); + if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + return define(lir, mod); + } + } + LModI *lir = new(alloc()) LModI(useRegister(mod->lhs()), useRegister(mod->rhs()), + temp(LDefinition::GENERAL)); + + if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + return define(lir, mod); +} + +bool +LIRGeneratorMIPS::visitPowHalf(MPowHalf *ins) +{ + MDefinition *input = ins->input(); + MOZ_ASSERT(input->type() == MIRType_Double); + LPowHalfD *lir = new(alloc()) LPowHalfD(useRegisterAtStart(input)); + return defineReuseInput(lir, ins, 0); +} + +LTableSwitch * +LIRGeneratorMIPS::newLTableSwitch(const LAllocation &in, const LDefinition &inputCopy, + MTableSwitch *tableswitch) +{ + return new(alloc()) LTableSwitch(in, inputCopy, temp(), tableswitch); +} + +LTableSwitchV * +LIRGeneratorMIPS::newLTableSwitchV(MTableSwitch *tableswitch) +{ + return new(alloc()) LTableSwitchV(temp(), tempFloat32(), temp(), tableswitch); +} + +bool +LIRGeneratorMIPS::visitGuardShape(MGuardShape *ins) +{ + MOZ_ASSERT(ins->obj()->type() == MIRType_Object); + + LDefinition tempObj = temp(LDefinition::OBJECT); + LGuardShape *guard = new(alloc()) LGuardShape(useRegister(ins->obj()), tempObj); + if (!assignSnapshot(guard, ins->bailoutKind())) + return false; + if (!add(guard, ins)) + return false; + return redefine(ins, ins->obj()); +} + +bool +LIRGeneratorMIPS::visitGuardObjectType(MGuardObjectType *ins) +{ + MOZ_ASSERT(ins->obj()->type() == MIRType_Object); + + LDefinition tempObj = temp(LDefinition::OBJECT); + LGuardObjectType *guard = new(alloc()) LGuardObjectType(useRegister(ins->obj()), tempObj); + if (!assignSnapshot(guard)) + return false; + if (!add(guard, ins)) + return false; + return redefine(ins, ins->obj()); +} + +bool +LIRGeneratorMIPS::lowerUrshD(MUrsh *mir) +{ + MDefinition *lhs = mir->lhs(); + MDefinition *rhs = mir->rhs(); + + MOZ_ASSERT(lhs->type() == MIRType_Int32); + MOZ_ASSERT(rhs->type() == MIRType_Int32); + + LUrshD *lir = new(alloc()) LUrshD(useRegister(lhs), useRegisterOrConstant(rhs), temp()); + return define(lir, mir); +} + +bool +LIRGeneratorMIPS::visitAsmJSNeg(MAsmJSNeg *ins) +{ + if (ins->type() == MIRType_Int32) + return define(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins); + + if (ins->type() == MIRType_Float32) + return define(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins); + + MOZ_ASSERT(ins->type() == MIRType_Double); + return define(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins); +} + +bool +LIRGeneratorMIPS::lowerUDiv(MDiv *div) +{ + MDefinition *lhs = div->getOperand(0); + MDefinition *rhs = div->getOperand(1); + + LUDiv *lir = new(alloc()) LUDiv; + lir->setOperand(0, useRegister(lhs)); + lir->setOperand(1, useRegister(rhs)); + if (div->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + + return define(lir, div); +} + +bool +LIRGeneratorMIPS::lowerUMod(MMod *mod) +{ + MDefinition *lhs = mod->getOperand(0); + MDefinition *rhs = mod->getOperand(1); + + LUMod *lir = new(alloc()) LUMod; + lir->setOperand(0, useRegister(lhs)); + lir->setOperand(1, useRegister(rhs)); + if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo)) + return false; + + return define(lir, mod); +} + +bool +LIRGeneratorMIPS::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins) +{ + MOZ_ASSERT(ins->input()->type() == MIRType_Int32); + LAsmJSUInt32ToDouble *lir = new(alloc()) LAsmJSUInt32ToDouble(useRegisterAtStart(ins->input())); + return define(lir, ins); +} + +bool +LIRGeneratorMIPS::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins) +{ + MOZ_ASSERT(ins->input()->type() == MIRType_Int32); + LAsmJSUInt32ToFloat32 *lir = new(alloc()) LAsmJSUInt32ToFloat32(useRegisterAtStart(ins->input())); + return define(lir, ins); +} + +bool +LIRGeneratorMIPS::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins) +{ + MDefinition *ptr = ins->ptr(); + MOZ_ASSERT(ptr->type() == MIRType_Int32); + LAllocation ptrAlloc; + + // For MIPS it is best to keep the 'ptr' in a register if a bounds check + // is needed. + if (ptr->isConstant() && ins->skipBoundsCheck()) { + int32_t ptrValue = ptr->toConstant()->value().toInt32(); + // A bounds check is only skipped for a positive index. + MOZ_ASSERT(ptrValue >= 0); + ptrAlloc = LAllocation(ptr->toConstant()->vp()); + } else + ptrAlloc = useRegisterAtStart(ptr); + + return define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins); +} + +bool +LIRGeneratorMIPS::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins) +{ + MDefinition *ptr = ins->ptr(); + MOZ_ASSERT(ptr->type() == MIRType_Int32); + LAllocation ptrAlloc; + + if (ptr->isConstant() && ins->skipBoundsCheck()) { + MOZ_ASSERT(ptr->toConstant()->value().toInt32() >= 0); + ptrAlloc = LAllocation(ptr->toConstant()->vp()); + } else + ptrAlloc = useRegisterAtStart(ptr); + + return add(new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins); +} + +bool +LIRGeneratorMIPS::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins) +{ + return define(new(alloc()) LAsmJSLoadFuncPtr(useRegister(ins->index()), temp()), ins); +} + +bool +LIRGeneratorMIPS::lowerTruncateDToInt32(MTruncateToInt32 *ins) +{ + MDefinition *opd = ins->input(); + MOZ_ASSERT(opd->type() == MIRType_Double); + + return define(new(alloc()) LTruncateDToInt32(useRegister(opd), LDefinition::BogusTemp()), ins); +} + +bool +LIRGeneratorMIPS::lowerTruncateFToInt32(MTruncateToInt32 *ins) +{ + MDefinition *opd = ins->input(); + MOZ_ASSERT(opd->type() == MIRType_Float32); + + return define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins); +} + +bool +LIRGeneratorMIPS::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); +} + +bool +LIRGeneratorMIPS::visitForkJoinGetSlice(MForkJoinGetSlice *ins) +{ + MOZ_ASSUME_UNREACHABLE("NYI"); +}