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: #ifndef jit_shared_Lowering_shared_inl_h michael@0: #define jit_shared_Lowering_shared_inl_h michael@0: michael@0: #include "jit/shared/Lowering-shared.h" michael@0: michael@0: #include "jit/MIR.h" michael@0: #include "jit/MIRGenerator.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: bool michael@0: LIRGeneratorShared::emitAtUses(MInstruction *mir) michael@0: { michael@0: JS_ASSERT(mir->canEmitAtUses()); michael@0: mir->setEmittedAtUses(); michael@0: mir->setVirtualRegister(0); michael@0: return true; michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::use(MDefinition *mir, LUse policy) michael@0: { michael@0: // It is illegal to call use() on an instruction with two defs. michael@0: #if BOX_PIECES > 1 michael@0: JS_ASSERT(mir->type() != MIRType_Value); michael@0: #endif michael@0: if (!ensureDefined(mir)) michael@0: return policy; michael@0: policy.setVirtualRegister(mir->virtualRegister()); michael@0: return policy; michael@0: } michael@0: michael@0: template bool michael@0: LIRGeneratorShared::define(LInstructionHelper<1, X, Y> *lir, MDefinition *mir, const LDefinition &def) michael@0: { michael@0: // Call instructions should use defineReturn. michael@0: JS_ASSERT(!lir->isCall()); michael@0: michael@0: uint32_t vreg = getVirtualRegister(); michael@0: if (vreg >= MAX_VIRTUAL_REGISTERS) michael@0: return false; michael@0: michael@0: // Assign the definition and a virtual register. Then, propagate this michael@0: // virtual register to the MIR, so we can map MIR to LIR during lowering. michael@0: lir->setDef(0, def); michael@0: lir->getDef(0)->setVirtualRegister(vreg); michael@0: lir->setMir(mir); michael@0: mir->setVirtualRegister(vreg); michael@0: return add(lir); michael@0: } michael@0: michael@0: template bool michael@0: LIRGeneratorShared::define(LInstructionHelper<1, X, Y> *lir, MDefinition *mir, LDefinition::Policy policy) michael@0: { michael@0: LDefinition::Type type = LDefinition::TypeFrom(mir->type()); michael@0: return define(lir, mir, LDefinition(type, policy)); michael@0: } michael@0: michael@0: template bool michael@0: LIRGeneratorShared::defineFixed(LInstructionHelper<1, X, Y> *lir, MDefinition *mir, const LAllocation &output) michael@0: { michael@0: LDefinition::Type type = LDefinition::TypeFrom(mir->type()); michael@0: michael@0: LDefinition def(type, LDefinition::PRESET); michael@0: def.setOutput(output); michael@0: michael@0: // Add an LNop to avoid regalloc problems if the next op uses this value michael@0: // with a fixed or at-start policy. michael@0: if (!define(lir, mir, def)) michael@0: return false; michael@0: michael@0: if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) { michael@0: if (!add(new(alloc()) LNop)) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: template bool michael@0: LIRGeneratorShared::defineReuseInput(LInstructionHelper<1, Ops, Temps> *lir, MDefinition *mir, uint32_t operand) michael@0: { michael@0: // The input should be used at the start of the instruction, to avoid moves. michael@0: JS_ASSERT(lir->getOperand(operand)->toUse()->usedAtStart()); michael@0: michael@0: LDefinition::Type type = LDefinition::TypeFrom(mir->type()); michael@0: michael@0: LDefinition def(type, LDefinition::MUST_REUSE_INPUT); michael@0: def.setReusedInput(operand); michael@0: michael@0: return define(lir, mir, def); michael@0: } michael@0: michael@0: template bool michael@0: LIRGeneratorShared::defineBox(LInstructionHelper *lir, MDefinition *mir, michael@0: LDefinition::Policy policy) michael@0: { michael@0: // Call instructions should use defineReturn. michael@0: JS_ASSERT(!lir->isCall()); michael@0: michael@0: uint32_t vreg = getVirtualRegister(); michael@0: if (vreg >= MAX_VIRTUAL_REGISTERS) michael@0: return false; michael@0: michael@0: #if defined(JS_NUNBOX32) michael@0: lir->setDef(0, LDefinition(vreg + VREG_TYPE_OFFSET, LDefinition::TYPE, policy)); michael@0: lir->setDef(1, LDefinition(vreg + VREG_DATA_OFFSET, LDefinition::PAYLOAD, policy)); michael@0: if (getVirtualRegister() >= MAX_VIRTUAL_REGISTERS) michael@0: return false; michael@0: #elif defined(JS_PUNBOX64) michael@0: lir->setDef(0, LDefinition(vreg, LDefinition::BOX, policy)); michael@0: #endif michael@0: lir->setMir(mir); michael@0: michael@0: mir->setVirtualRegister(vreg); michael@0: return add(lir); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorShared::defineReturn(LInstruction *lir, MDefinition *mir) michael@0: { michael@0: lir->setMir(mir); michael@0: michael@0: JS_ASSERT(lir->isCall()); michael@0: michael@0: uint32_t vreg = getVirtualRegister(); michael@0: if (vreg >= MAX_VIRTUAL_REGISTERS) michael@0: return false; michael@0: michael@0: switch (mir->type()) { michael@0: case MIRType_Value: michael@0: #if defined(JS_NUNBOX32) michael@0: lir->setDef(TYPE_INDEX, LDefinition(vreg + VREG_TYPE_OFFSET, LDefinition::TYPE, michael@0: LGeneralReg(JSReturnReg_Type))); michael@0: lir->setDef(PAYLOAD_INDEX, LDefinition(vreg + VREG_DATA_OFFSET, LDefinition::PAYLOAD, michael@0: LGeneralReg(JSReturnReg_Data))); michael@0: michael@0: if (getVirtualRegister() >= MAX_VIRTUAL_REGISTERS) michael@0: return false; michael@0: #elif defined(JS_PUNBOX64) michael@0: lir->setDef(0, LDefinition(vreg, LDefinition::BOX, LGeneralReg(JSReturnReg))); michael@0: #endif michael@0: break; michael@0: case MIRType_Float32: michael@0: lir->setDef(0, LDefinition(vreg, LDefinition::FLOAT32, LFloatReg(ReturnFloatReg))); michael@0: break; michael@0: case MIRType_Double: michael@0: lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, LFloatReg(ReturnFloatReg))); michael@0: break; michael@0: default: michael@0: LDefinition::Type type = LDefinition::TypeFrom(mir->type()); michael@0: JS_ASSERT(type != LDefinition::DOUBLE && type != LDefinition::FLOAT32); michael@0: lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ReturnReg))); michael@0: break; michael@0: } michael@0: michael@0: mir->setVirtualRegister(vreg); michael@0: if (!add(lir)) michael@0: return false; michael@0: michael@0: if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) { michael@0: if (!add(new(alloc()) LNop)) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // In LIR, we treat booleans and integers as the same low-level type (INTEGER). michael@0: // When snapshotting, we recover the actual JS type from MIR. This function michael@0: // checks that when making redefinitions, we don't accidentally coerce two michael@0: // incompatible types. michael@0: static inline bool michael@0: IsCompatibleLIRCoercion(MIRType to, MIRType from) michael@0: { michael@0: if (to == from) michael@0: return true; michael@0: if ((to == MIRType_Int32 || to == MIRType_Boolean) && michael@0: (from == MIRType_Int32 || from == MIRType_Boolean)) { michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorShared::redefine(MDefinition *def, MDefinition *as) michael@0: { michael@0: JS_ASSERT(IsCompatibleLIRCoercion(def->type(), as->type())); michael@0: michael@0: // Try to emit MIR marked as emitted-at-uses at, well, uses. For michael@0: // snapshotting reasons we delay the MIRTypes match, or when we are michael@0: // coercing between bool and int32 constants. michael@0: if (as->isEmittedAtUses() && michael@0: (def->type() == as->type() || michael@0: (as->isConstant() && michael@0: (def->type() == MIRType_Int32 || def->type() == MIRType_Boolean) && michael@0: (as->type() == MIRType_Int32 || as->type() == MIRType_Boolean)))) michael@0: { michael@0: MDefinition *replacement; michael@0: if (def->type() != as->type()) { michael@0: Value v = as->toConstant()->value(); michael@0: if (as->type() == MIRType_Int32) michael@0: replacement = MConstant::New(alloc(), BooleanValue(v.toInt32())); michael@0: else michael@0: replacement = MConstant::New(alloc(), Int32Value(v.toBoolean())); michael@0: if (!emitAtUses(replacement->toInstruction())) michael@0: return false; michael@0: } else { michael@0: replacement = as; michael@0: } michael@0: def->replaceAllUsesWith(replacement); michael@0: return true; michael@0: } michael@0: michael@0: if (!ensureDefined(as)) michael@0: return false; michael@0: def->setVirtualRegister(as->virtualRegister()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorShared::defineAs(LInstruction *outLir, MDefinition *outMir, MDefinition *inMir) michael@0: { michael@0: uint32_t vreg = inMir->virtualRegister(); michael@0: LDefinition::Policy policy = LDefinition::PASSTHROUGH; michael@0: michael@0: if (outMir->type() == MIRType_Value) { michael@0: #ifdef JS_NUNBOX32 michael@0: outLir->setDef(TYPE_INDEX, michael@0: LDefinition(vreg + VREG_TYPE_OFFSET, LDefinition::TYPE, policy)); michael@0: outLir->setDef(PAYLOAD_INDEX, michael@0: LDefinition(vreg + VREG_DATA_OFFSET, LDefinition::PAYLOAD, policy)); michael@0: #elif JS_PUNBOX64 michael@0: outLir->setDef(0, LDefinition(vreg, LDefinition::BOX, policy)); michael@0: #else michael@0: # error "Unexpected boxing type" michael@0: #endif michael@0: } else { michael@0: outLir->setDef(0, LDefinition(vreg, LDefinition::TypeFrom(inMir->type()), policy)); michael@0: } michael@0: outLir->setMir(outMir); michael@0: return redefine(outMir, inMir); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorShared::ensureDefined(MDefinition *mir) michael@0: { michael@0: if (mir->isEmittedAtUses()) { michael@0: if (!mir->toInstruction()->accept(this)) michael@0: return false; michael@0: JS_ASSERT(mir->isLowered()); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::useRegister(MDefinition *mir) michael@0: { michael@0: return use(mir, LUse(LUse::REGISTER)); michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::useRegisterAtStart(MDefinition *mir) michael@0: { michael@0: return use(mir, LUse(LUse::REGISTER, true)); michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::use(MDefinition *mir) michael@0: { michael@0: return use(mir, LUse(LUse::ANY)); michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::useAtStart(MDefinition *mir) michael@0: { michael@0: return use(mir, LUse(LUse::ANY, true)); michael@0: } michael@0: michael@0: LAllocation michael@0: LIRGeneratorShared::useOrConstant(MDefinition *mir) michael@0: { michael@0: if (mir->isConstant()) michael@0: return LAllocation(mir->toConstant()->vp()); michael@0: return use(mir); michael@0: } michael@0: michael@0: LAllocation michael@0: LIRGeneratorShared::useRegisterOrConstant(MDefinition *mir) michael@0: { michael@0: if (mir->isConstant()) michael@0: return LAllocation(mir->toConstant()->vp()); michael@0: return useRegister(mir); michael@0: } michael@0: michael@0: LAllocation michael@0: LIRGeneratorShared::useRegisterOrConstantAtStart(MDefinition *mir) michael@0: { michael@0: if (mir->isConstant()) michael@0: return LAllocation(mir->toConstant()->vp()); michael@0: return useRegisterAtStart(mir); michael@0: } michael@0: michael@0: LAllocation michael@0: LIRGeneratorShared::useRegisterOrNonNegativeConstantAtStart(MDefinition *mir) michael@0: { michael@0: if (mir->isConstant() && mir->toConstant()->value().toInt32() >= 0) michael@0: return LAllocation(mir->toConstant()->vp()); michael@0: return useRegisterAtStart(mir); michael@0: } michael@0: michael@0: LAllocation michael@0: LIRGeneratorShared::useRegisterOrNonDoubleConstant(MDefinition *mir) michael@0: { michael@0: if (mir->isConstant() && mir->type() != MIRType_Double && mir->type() != MIRType_Float32) michael@0: return LAllocation(mir->toConstant()->vp()); michael@0: return useRegister(mir); michael@0: } michael@0: michael@0: #if defined(JS_CODEGEN_ARM) michael@0: LAllocation michael@0: LIRGeneratorShared::useAnyOrConstant(MDefinition *mir) michael@0: { michael@0: return useRegisterOrConstant(mir); michael@0: } michael@0: LAllocation michael@0: LIRGeneratorShared::useStorable(MDefinition *mir) michael@0: { michael@0: return useRegister(mir); michael@0: } michael@0: LAllocation michael@0: LIRGeneratorShared::useStorableAtStart(MDefinition *mir) michael@0: { michael@0: return useRegisterAtStart(mir); michael@0: } michael@0: michael@0: LAllocation michael@0: LIRGeneratorShared::useAny(MDefinition *mir) michael@0: { michael@0: return useRegister(mir); michael@0: } michael@0: #else michael@0: LAllocation michael@0: LIRGeneratorShared::useAnyOrConstant(MDefinition *mir) michael@0: { michael@0: return useOrConstant(mir); michael@0: } michael@0: michael@0: LAllocation michael@0: LIRGeneratorShared::useAny(MDefinition *mir) michael@0: { michael@0: return use(mir); michael@0: } michael@0: LAllocation michael@0: LIRGeneratorShared::useStorable(MDefinition *mir) michael@0: { michael@0: return useRegisterOrConstant(mir); michael@0: } michael@0: LAllocation michael@0: LIRGeneratorShared::useStorableAtStart(MDefinition *mir) michael@0: { michael@0: return useRegisterOrConstantAtStart(mir); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: LAllocation michael@0: LIRGeneratorShared::useKeepaliveOrConstant(MDefinition *mir) michael@0: { michael@0: if (mir->isConstant()) michael@0: return LAllocation(mir->toConstant()->vp()); michael@0: return use(mir, LUse(LUse::KEEPALIVE)); michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::useFixed(MDefinition *mir, Register reg) michael@0: { michael@0: return use(mir, LUse(reg)); michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::useFixedAtStart(MDefinition *mir, Register reg) michael@0: { michael@0: return use(mir, LUse(reg, true)); michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::useFixed(MDefinition *mir, FloatRegister reg) michael@0: { michael@0: return use(mir, LUse(reg)); michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::useFixed(MDefinition *mir, AnyRegister reg) michael@0: { michael@0: return reg.isFloat() ? use(mir, reg.fpu()) : use(mir, reg.gpr()); michael@0: } michael@0: michael@0: LDefinition michael@0: LIRGeneratorShared::temp(LDefinition::Type type, LDefinition::Policy policy) michael@0: { michael@0: uint32_t vreg = getVirtualRegister(); michael@0: if (vreg >= MAX_VIRTUAL_REGISTERS) { michael@0: gen->abort("max virtual registers"); michael@0: return LDefinition(); michael@0: } michael@0: return LDefinition(vreg, type, policy); michael@0: } michael@0: michael@0: LDefinition michael@0: LIRGeneratorShared::tempFixed(Register reg) michael@0: { michael@0: LDefinition t = temp(LDefinition::GENERAL); michael@0: t.setOutput(LGeneralReg(reg)); michael@0: return t; michael@0: } michael@0: michael@0: LDefinition michael@0: LIRGeneratorShared::tempFloat32() michael@0: { michael@0: return temp(LDefinition::FLOAT32); michael@0: } michael@0: michael@0: LDefinition michael@0: LIRGeneratorShared::tempDouble() michael@0: { michael@0: return temp(LDefinition::DOUBLE); michael@0: } michael@0: michael@0: LDefinition michael@0: LIRGeneratorShared::tempCopy(MDefinition *input, uint32_t reusedInput) michael@0: { michael@0: JS_ASSERT(input->virtualRegister()); michael@0: LDefinition t = temp(LDefinition::TypeFrom(input->type()), LDefinition::MUST_REUSE_INPUT); michael@0: t.setReusedInput(reusedInput); michael@0: return t; michael@0: } michael@0: michael@0: template void michael@0: LIRGeneratorShared::annotate(T *ins) michael@0: { michael@0: ins->setId(lirGraph_.getInstructionId()); michael@0: } michael@0: michael@0: template bool michael@0: LIRGeneratorShared::add(T *ins, MInstruction *mir) michael@0: { michael@0: JS_ASSERT(!ins->isPhi()); michael@0: current->add(ins); michael@0: if (mir) { michael@0: JS_ASSERT(current == mir->block()->lir()); michael@0: ins->setMir(mir); michael@0: } michael@0: annotate(ins); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef JS_NUNBOX32 michael@0: // Returns the virtual register of a js::Value-defining instruction. This is michael@0: // abstracted because MBox is a special value-returning instruction that michael@0: // redefines its input payload if its input is not constant. Therefore, it is michael@0: // illegal to request a box's payload by adding VREG_DATA_OFFSET to its raw id. michael@0: static inline uint32_t michael@0: VirtualRegisterOfPayload(MDefinition *mir) michael@0: { michael@0: if (mir->isBox()) { michael@0: MDefinition *inner = mir->toBox()->getOperand(0); michael@0: if (!inner->isConstant() && inner->type() != MIRType_Double && inner->type() != MIRType_Float32) michael@0: return inner->virtualRegister(); michael@0: } michael@0: if (mir->isTypeBarrier()) michael@0: return VirtualRegisterOfPayload(mir->getOperand(0)); michael@0: return mir->virtualRegister() + VREG_DATA_OFFSET; michael@0: } michael@0: michael@0: // Note: always call ensureDefined before calling useType/usePayload, michael@0: // so that emitted-at-use operands are handled correctly. michael@0: LUse michael@0: LIRGeneratorShared::useType(MDefinition *mir, LUse::Policy policy) michael@0: { michael@0: JS_ASSERT(mir->type() == MIRType_Value); michael@0: michael@0: return LUse(mir->virtualRegister() + VREG_TYPE_OFFSET, policy); michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::usePayload(MDefinition *mir, LUse::Policy policy) michael@0: { michael@0: JS_ASSERT(mir->type() == MIRType_Value); michael@0: michael@0: return LUse(VirtualRegisterOfPayload(mir), policy); michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::usePayloadAtStart(MDefinition *mir, LUse::Policy policy) michael@0: { michael@0: JS_ASSERT(mir->type() == MIRType_Value); michael@0: michael@0: return LUse(VirtualRegisterOfPayload(mir), policy, true); michael@0: } michael@0: michael@0: LUse michael@0: LIRGeneratorShared::usePayloadInRegisterAtStart(MDefinition *mir) michael@0: { michael@0: return usePayloadAtStart(mir, LUse::REGISTER); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorShared::fillBoxUses(LInstruction *lir, size_t n, MDefinition *mir) michael@0: { michael@0: if (!ensureDefined(mir)) michael@0: return false; michael@0: lir->getOperand(n)->toUse()->setVirtualRegister(mir->virtualRegister() + VREG_TYPE_OFFSET); michael@0: lir->getOperand(n + 1)->toUse()->setVirtualRegister(VirtualRegisterOfPayload(mir)); michael@0: return true; michael@0: } michael@0: #endif michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif /* jit_shared_Lowering_shared_inl_h */