diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/jit/shared/Lowering-shared-inl.h | 894 |
1 files changed, 894 insertions, 0 deletions
diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h new file mode 100644 index 0000000000..89aafa22e4 --- /dev/null +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -0,0 +1,894 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * 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/. */ + +#ifndef jit_shared_Lowering_shared_inl_h +#define jit_shared_Lowering_shared_inl_h + +#include "jit/shared/Lowering-shared.h" + +#include "jit/MIR.h" +#include "jit/MIRGenerator.h" + +namespace js { +namespace jit { + +void LIRGeneratorShared::emitAtUses(MInstruction* mir) { + MOZ_ASSERT(mir->canEmitAtUses()); + mir->setEmittedAtUses(); + mir->setVirtualRegister(0); +} + +LUse LIRGeneratorShared::use(MDefinition* mir, LUse policy) { + // It is illegal to call use() on an instruction with two defs. +#if BOX_PIECES > 1 + MOZ_ASSERT(mir->type() != MIRType::Value); +#endif +#if INT64_PIECES > 1 + MOZ_ASSERT(mir->type() != MIRType::Int64); +#endif + ensureDefined(mir); + policy.setVirtualRegister(mir->virtualRegister()); + return policy; +} + +template <size_t X> +void LIRGeneratorShared::define( + details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir, + LDefinition::Policy policy) { + LDefinition::Type type = LDefinition::TypeFrom(mir->type()); + define(lir, mir, LDefinition(type, policy)); +} + +template <size_t X> +void LIRGeneratorShared::define( + details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir, + const LDefinition& def) { + // Call instructions should use defineReturn. + MOZ_ASSERT(!lir->isCall()); + + uint32_t vreg = getVirtualRegister(); + + // Assign the definition and a virtual register. Then, propagate this + // virtual register to the MIR, so we can map MIR to LIR during lowering. + lir->setDef(0, def); + lir->getDef(0)->setVirtualRegister(vreg); + lir->setMir(mir); + mir->setVirtualRegister(vreg); + add(lir); +} + +template <size_t X, size_t Y> +void LIRGeneratorShared::defineFixed(LInstructionHelper<1, X, Y>* lir, + MDefinition* mir, + const LAllocation& output) { + LDefinition::Type type = LDefinition::TypeFrom(mir->type()); + + LDefinition def(type, LDefinition::FIXED); + def.setOutput(output); + + define(lir, mir, def); +} + +template <size_t Ops, size_t Temps> +void LIRGeneratorShared::defineInt64Fixed( + LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir, + const LInt64Allocation& output) { + uint32_t vreg = getVirtualRegister(); + +#if JS_BITS_PER_WORD == 64 + LDefinition def(LDefinition::GENERAL, LDefinition::FIXED); + def.setOutput(output.value()); + lir->setDef(0, def); + lir->getDef(0)->setVirtualRegister(vreg); +#else + LDefinition def0(LDefinition::GENERAL, LDefinition::FIXED); + def0.setOutput(output.low()); + lir->setDef(0, def0); + lir->getDef(0)->setVirtualRegister(vreg); + + getVirtualRegister(); + LDefinition def1(LDefinition::GENERAL, LDefinition::FIXED); + def1.setOutput(output.high()); + lir->setDef(1, def1); + lir->getDef(1)->setVirtualRegister(vreg + 1); +#endif + + lir->setMir(mir); + mir->setVirtualRegister(vreg); + add(lir); +} + +template <size_t Ops, size_t Temps> +void LIRGeneratorShared::defineReuseInput( + LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir, + uint32_t operand) { + // Note: Any other operand that is not the same as this operand should be + // marked as not being "atStart". The regalloc cannot handle those and can + // overwrite the inputs! + + // The input should be used at the start of the instruction, to avoid moves. + MOZ_ASSERT(lir->getOperand(operand)->toUse()->usedAtStart()); + + LDefinition::Type type = LDefinition::TypeFrom(mir->type()); + + LDefinition def(type, LDefinition::MUST_REUSE_INPUT); + def.setReusedInput(operand); + + define(lir, mir, def); +} + +template <size_t Ops, size_t Temps> +void LIRGeneratorShared::defineInt64ReuseInput( + LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir, + uint32_t operand) { + // Note: Any other operand that is not the same as this operand should be + // marked as not being "atStart". The regalloc cannot handle those and can + // overwrite the inputs! + + // The input should be used at the start of the instruction, to avoid moves. + MOZ_ASSERT(lir->getOperand(operand)->toUse()->usedAtStart()); +#if JS_BITS_PER_WORD == 32 + MOZ_ASSERT(lir->getOperand(operand + 1)->toUse()->usedAtStart()); +#endif + MOZ_ASSERT(!lir->isCall()); + + uint32_t vreg = getVirtualRegister(); + + LDefinition def1(LDefinition::GENERAL, LDefinition::MUST_REUSE_INPUT); + def1.setReusedInput(operand); + lir->setDef(0, def1); + lir->getDef(0)->setVirtualRegister(vreg); + +#if JS_BITS_PER_WORD == 32 + getVirtualRegister(); + LDefinition def2(LDefinition::GENERAL, LDefinition::MUST_REUSE_INPUT); + def2.setReusedInput(operand + 1); + lir->setDef(1, def2); + lir->getDef(1)->setVirtualRegister(vreg + 1); +#endif + + lir->setMir(mir); + mir->setVirtualRegister(vreg); + add(lir); +} + +template <size_t Ops, size_t Temps> +void LIRGeneratorShared::defineBoxReuseInput( + LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir, + uint32_t operand) { + // The input should be used at the start of the instruction, to avoid moves. + MOZ_ASSERT(lir->getOperand(operand)->toUse()->usedAtStart()); +#ifdef JS_NUNBOX32 + MOZ_ASSERT(lir->getOperand(operand + 1)->toUse()->usedAtStart()); +#endif + MOZ_ASSERT(!lir->isCall()); + MOZ_ASSERT(mir->type() == MIRType::Value); + + uint32_t vreg = getVirtualRegister(); + +#ifdef JS_NUNBOX32 + static_assert(VREG_TYPE_OFFSET == 0, + "Code below assumes VREG_TYPE_OFFSET == 0"); + static_assert(VREG_DATA_OFFSET == 1, + "Code below assumes VREG_DATA_OFFSET == 1"); + + LDefinition def1(LDefinition::TYPE, LDefinition::MUST_REUSE_INPUT); + def1.setReusedInput(operand); + def1.setVirtualRegister(vreg); + lir->setDef(0, def1); + + getVirtualRegister(); + LDefinition def2(LDefinition::PAYLOAD, LDefinition::MUST_REUSE_INPUT); + def2.setReusedInput(operand + 1); + def2.setVirtualRegister(vreg + 1); + lir->setDef(1, def2); +#else + LDefinition def(LDefinition::BOX, LDefinition::MUST_REUSE_INPUT); + def.setReusedInput(operand); + def.setVirtualRegister(vreg); + lir->setDef(0, def); +#endif + + lir->setMir(mir); + mir->setVirtualRegister(vreg); + add(lir); +} + +template <size_t Temps> +void LIRGeneratorShared::defineBox( + details::LInstructionFixedDefsTempsHelper<BOX_PIECES, Temps>* lir, + MDefinition* mir, LDefinition::Policy policy) { + // Call instructions should use defineReturn. + MOZ_ASSERT(!lir->isCall()); + MOZ_ASSERT(mir->type() == MIRType::Value); + + uint32_t vreg = getVirtualRegister(); + +#if defined(JS_NUNBOX32) + lir->setDef(0, + LDefinition(vreg + VREG_TYPE_OFFSET, LDefinition::TYPE, policy)); + lir->setDef( + 1, LDefinition(vreg + VREG_DATA_OFFSET, LDefinition::PAYLOAD, policy)); + getVirtualRegister(); +#elif defined(JS_PUNBOX64) + lir->setDef(0, LDefinition(vreg, LDefinition::BOX, policy)); +#endif + lir->setMir(mir); + + mir->setVirtualRegister(vreg); + add(lir); +} + +template <size_t Ops, size_t Temps> +void LIRGeneratorShared::defineInt64( + LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir, + LDefinition::Policy policy) { + // Call instructions should use defineReturn. + MOZ_ASSERT(!lir->isCall()); + +#ifdef JS_64BIT + MOZ_ASSERT(mir->type() == MIRType::Int64 || mir->type() == MIRType::IntPtr); +#else + MOZ_ASSERT(mir->type() == MIRType::Int64); +#endif + + uint32_t vreg = getVirtualRegister(); + +#if JS_BITS_PER_WORD == 32 + lir->setDef(0, + LDefinition(vreg + INT64LOW_INDEX, LDefinition::GENERAL, policy)); + lir->setDef( + 1, LDefinition(vreg + INT64HIGH_INDEX, LDefinition::GENERAL, policy)); + getVirtualRegister(); +#else + lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL, policy)); +#endif + lir->setMir(mir); + + mir->setVirtualRegister(vreg); + add(lir); +} + +void LIRGeneratorShared::defineReturn(LInstruction* lir, MDefinition* mir) { + lir->setMir(mir); + + MOZ_ASSERT(lir->isCall()); + + uint32_t vreg = getVirtualRegister(); + + switch (mir->type()) { + case MIRType::Value: +#if defined(JS_NUNBOX32) + lir->setDef(TYPE_INDEX, + LDefinition(vreg + VREG_TYPE_OFFSET, LDefinition::TYPE, + LGeneralReg(JSReturnReg_Type))); + lir->setDef(PAYLOAD_INDEX, + LDefinition(vreg + VREG_DATA_OFFSET, LDefinition::PAYLOAD, + LGeneralReg(JSReturnReg_Data))); + getVirtualRegister(); +#elif defined(JS_PUNBOX64) + lir->setDef( + 0, LDefinition(vreg, LDefinition::BOX, LGeneralReg(JSReturnReg))); +#endif + break; + case MIRType::Int64: +#if defined(JS_NUNBOX32) + lir->setDef(INT64LOW_INDEX, + LDefinition(vreg + INT64LOW_INDEX, LDefinition::GENERAL, + LGeneralReg(ReturnReg64.low))); + lir->setDef(INT64HIGH_INDEX, + LDefinition(vreg + INT64HIGH_INDEX, LDefinition::GENERAL, + LGeneralReg(ReturnReg64.high))); + getVirtualRegister(); +#elif defined(JS_PUNBOX64) + lir->setDef( + 0, LDefinition(vreg, LDefinition::GENERAL, LGeneralReg(ReturnReg))); +#endif + break; + case MIRType::Float32: + lir->setDef(0, LDefinition(vreg, LDefinition::FLOAT32, + LFloatReg(ReturnFloat32Reg))); + break; + case MIRType::Double: + lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, + LFloatReg(ReturnDoubleReg))); + break; + case MIRType::Simd128: +#ifdef ENABLE_WASM_SIMD + lir->setDef(0, LDefinition(vreg, LDefinition::SIMD128, + LFloatReg(ReturnSimd128Reg))); + break; +#else + MOZ_CRASH("No SIMD support"); +#endif + default: + LDefinition::Type type = LDefinition::TypeFrom(mir->type()); + switch (type) { + case LDefinition::GENERAL: + case LDefinition::INT32: + case LDefinition::OBJECT: + case LDefinition::SLOTS: + case LDefinition::STACKRESULTS: + lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ReturnReg))); + break; + case LDefinition::DOUBLE: + case LDefinition::FLOAT32: + case LDefinition::SIMD128: + MOZ_CRASH("Float cases must have been handled earlier"); + default: + MOZ_CRASH("Unexpected type"); + } + break; + } + + mir->setVirtualRegister(vreg); + add(lir); +} + +#ifdef DEBUG +// This function checks that when making redefinitions, we don't accidentally +// coerce two incompatible types. +static inline bool IsCompatibleLIRCoercion(MIRType to, MIRType from) { + if (to == from) { + return true; + } + // In LIR, we treat boolean and int32 as the same low-level type (INTEGER). + // When snapshotting, we recover the actual JS type from MIR. + if ((to == MIRType::Int32 || to == MIRType::Boolean) && + (from == MIRType::Int32 || from == MIRType::Boolean)) { + return true; + } + // On 32-bit platforms Int32 can be redefined as IntPtr and vice versa. + // On 64-bit platforms we can redefine non-negative Int32 values as IntPtr. + if (from == MIRType::Int32 && to == MIRType::IntPtr) { + return true; + } +# ifndef JS_64BIT + if (from == MIRType::IntPtr && to == MIRType::Int32) { + return true; + } +# endif + return false; +} +#endif + +void LIRGeneratorShared::redefine(MDefinition* def, MDefinition* as) { + MOZ_ASSERT(IsCompatibleLIRCoercion(def->type(), as->type())); + + // Try to emit MIR marked as emitted-at-uses at, well, uses. For + // snapshotting reasons we delay the MIRTypes match, or when we are + // coercing between bool and int32 constants. + if (as->isEmittedAtUses() && + (def->type() == as->type() || + (as->isConstant() && + (def->type() == MIRType::Int32 || def->type() == MIRType::Boolean) && + (as->type() == MIRType::Int32 || as->type() == MIRType::Boolean)))) { + MInstruction* replacement; + if (def->type() != as->type()) { + if (as->type() == MIRType::Int32) { + replacement = + MConstant::New(alloc(), BooleanValue(as->toConstant()->toInt32())); + } else { + replacement = + MConstant::New(alloc(), Int32Value(as->toConstant()->toBoolean())); + } + def->block()->insertBefore(def->toInstruction(), replacement); + emitAtUses(replacement->toInstruction()); + } else { + replacement = as->toInstruction(); + } + def->replaceAllUsesWith(replacement); + } else { + ensureDefined(as); + def->setVirtualRegister(as->virtualRegister()); + } +} + +void LIRGeneratorShared::ensureDefined(MDefinition* mir) { + if (mir->isEmittedAtUses()) { + visitEmittedAtUses(mir->toInstruction()); + MOZ_ASSERT(mir->isLowered()); + } +} + +bool LIRGeneratorShared::willHaveDifferentLIRNodes(MDefinition* mir1, + MDefinition* mir2) { + if (mir1 != mir2) { + return true; + } + if (mir1->isEmittedAtUses()) { + return true; + } + return false; +} + +template <typename LClass, typename... Args> +LClass* LIRGeneratorShared::allocateVariadic(uint32_t numOperands, + Args&&... args) { + size_t numBytes = sizeof(LClass) + numOperands * sizeof(LAllocation); + void* buf = alloc().allocate(numBytes); + if (!buf) { + return nullptr; + } + + LClass* ins = static_cast<LClass*>(buf); + new (ins) LClass(numOperands, std::forward<Args>(args)...); + + ins->initOperandsOffset(sizeof(LClass)); + + for (uint32_t i = 0; i < numOperands; i++) { + ins->setOperand(i, LAllocation()); + } + + return ins; +} + +LUse LIRGeneratorShared::useRegister(MDefinition* mir) { + return use(mir, LUse(LUse::REGISTER)); +} + +LUse LIRGeneratorShared::useRegisterAtStart(MDefinition* mir) { + return use(mir, LUse(LUse::REGISTER, true)); +} + +LUse LIRGeneratorShared::use(MDefinition* mir) { + return use(mir, LUse(LUse::ANY)); +} + +LUse LIRGeneratorShared::useAtStart(MDefinition* mir) { + return use(mir, LUse(LUse::ANY, true)); +} + +LAllocation LIRGeneratorShared::useOrConstant(MDefinition* mir) { + if (mir->isConstant()) { + return LAllocation(mir->toConstant()); + } + return use(mir); +} + +LAllocation LIRGeneratorShared::useOrConstantAtStart(MDefinition* mir) { + if (mir->isConstant()) { + return LAllocation(mir->toConstant()); + } + return useAtStart(mir); +} + +LAllocation LIRGeneratorShared::useRegisterOrConstant(MDefinition* mir) { + if (mir->isConstant()) { + return LAllocation(mir->toConstant()); + } + return useRegister(mir); +} + +LAllocation LIRGeneratorShared::useRegisterOrConstantAtStart(MDefinition* mir) { + if (mir->isConstant()) { + return LAllocation(mir->toConstant()); + } + return useRegisterAtStart(mir); +} + +inline bool CanUseInt32Constant(MDefinition* mir) { + if (!mir->isConstant()) { + return false; + } + MConstant* cst = mir->toConstant(); + if (cst->type() == MIRType::IntPtr) { + return INT32_MIN <= cst->toIntPtr() && cst->toIntPtr() <= INT32_MAX; + } + MOZ_ASSERT(cst->type() == MIRType::Int32); + return true; +} + +LAllocation LIRGeneratorShared::useRegisterOrInt32Constant(MDefinition* mir) { + if (CanUseInt32Constant(mir)) { + return LAllocation(mir->toConstant()); + } + return useRegister(mir); +} + +LAllocation LIRGeneratorShared::useAnyOrInt32Constant(MDefinition* mir) { + if (CanUseInt32Constant(mir)) { + return LAllocation(mir->toConstant()); + } + return useAny(mir); +} + +LAllocation LIRGeneratorShared::useRegisterOrZero(MDefinition* mir) { + if (mir->isConstant() && + (mir->toConstant()->isInt32(0) || mir->toConstant()->isInt64(0))) { + return LAllocation(); + } + return useRegister(mir); +} + +LAllocation LIRGeneratorShared::useRegisterOrZeroAtStart(MDefinition* mir) { + if (mir->isConstant() && + (mir->toConstant()->isInt32(0) || mir->toConstant()->isInt64(0))) { + return LAllocation(); + } + return useRegisterAtStart(mir); +} + +LAllocation LIRGeneratorShared::useRegisterOrNonDoubleConstant( + MDefinition* mir) { + if (mir->isConstant() && mir->type() != MIRType::Double && + mir->type() != MIRType::Float32) { + return LAllocation(mir->toConstant()); + } + return useRegister(mir); +} + +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ + defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) || \ + defined(JS_CODEGEN_RISCV64) +LAllocation LIRGeneratorShared::useAnyOrConstant(MDefinition* mir) { + return useRegisterOrConstant(mir); +} +LAllocation LIRGeneratorShared::useStorable(MDefinition* mir) { + return useRegister(mir); +} +LAllocation LIRGeneratorShared::useStorableAtStart(MDefinition* mir) { + return useRegisterAtStart(mir); +} + +LAllocation LIRGeneratorShared::useAny(MDefinition* mir) { + return useRegister(mir); +} +LAllocation LIRGeneratorShared::useAnyAtStart(MDefinition* mir) { + return useRegisterAtStart(mir); +} +#else +LAllocation LIRGeneratorShared::useAnyOrConstant(MDefinition* mir) { + return useOrConstant(mir); +} + +LAllocation LIRGeneratorShared::useAny(MDefinition* mir) { return use(mir); } +LAllocation LIRGeneratorShared::useAnyAtStart(MDefinition* mir) { + return useAtStart(mir); +} +LAllocation LIRGeneratorShared::useStorable(MDefinition* mir) { + return useRegisterOrConstant(mir); +} +LAllocation LIRGeneratorShared::useStorableAtStart(MDefinition* mir) { + return useRegisterOrConstantAtStart(mir); +} + +#endif + +LAllocation LIRGeneratorShared::useKeepalive(MDefinition* mir) { + return use(mir, LUse(LUse::KEEPALIVE)); +} + +LAllocation LIRGeneratorShared::useKeepaliveOrConstant(MDefinition* mir) { + if (mir->isConstant()) { + return LAllocation(mir->toConstant()); + } + return useKeepalive(mir); +} + +LUse LIRGeneratorShared::useFixed(MDefinition* mir, Register reg) { + return use(mir, LUse(reg)); +} + +LUse LIRGeneratorShared::useFixedAtStart(MDefinition* mir, Register reg) { + return use(mir, LUse(reg, true)); +} + +LUse LIRGeneratorShared::useFixed(MDefinition* mir, FloatRegister reg) { + return use(mir, LUse(reg)); +} + +LUse LIRGeneratorShared::useFixed(MDefinition* mir, AnyRegister reg) { + return reg.isFloat() ? use(mir, LUse(reg.fpu())) : use(mir, LUse(reg.gpr())); +} + +LUse LIRGeneratorShared::useFixedAtStart(MDefinition* mir, AnyRegister reg) { + return reg.isFloat() ? use(mir, LUse(reg.fpu(), true)) + : use(mir, LUse(reg.gpr(), true)); +} + +LDefinition LIRGeneratorShared::temp(LDefinition::Type type, + LDefinition::Policy policy) { + return LDefinition(getVirtualRegister(), type, policy); +} + +LInt64Definition LIRGeneratorShared::tempInt64(LDefinition::Policy policy) { +#if JS_BITS_PER_WORD == 32 + LDefinition high = temp(LDefinition::GENERAL, policy); + LDefinition low = temp(LDefinition::GENERAL, policy); + return LInt64Definition(high, low); +#else + return LInt64Definition(temp(LDefinition::GENERAL, policy)); +#endif +} + +LDefinition LIRGeneratorShared::tempFixed(Register reg) { + LDefinition t = temp(LDefinition::GENERAL); + t.setOutput(LGeneralReg(reg)); + return t; +} + +LInt64Definition LIRGeneratorShared::tempInt64Fixed(Register64 reg) { +#if JS_BITS_PER_WORD == 32 + LDefinition high = temp(LDefinition::GENERAL); + LDefinition low = temp(LDefinition::GENERAL); + high.setOutput(LGeneralReg(reg.high)); + low.setOutput(LGeneralReg(reg.low)); + return LInt64Definition(high, low); +#else + LDefinition t = temp(LDefinition::GENERAL); + t.setOutput(LGeneralReg(reg.reg)); + return LInt64Definition(t); +#endif +} + +LDefinition LIRGeneratorShared::tempFixed(FloatRegister reg) { + LDefinition t = temp(LDefinition::DOUBLE); + t.setOutput(LFloatReg(reg)); + return t; +} + +LDefinition LIRGeneratorShared::tempFloat32() { + return temp(LDefinition::FLOAT32); +} + +LDefinition LIRGeneratorShared::tempDouble() { + return temp(LDefinition::DOUBLE); +} + +#ifdef ENABLE_WASM_SIMD +LDefinition LIRGeneratorShared::tempSimd128() { + return temp(LDefinition::SIMD128); +} +#endif + +LDefinition LIRGeneratorShared::tempCopy(MDefinition* input, + uint32_t reusedInput) { + MOZ_ASSERT(input->virtualRegister()); + LDefinition t = + temp(LDefinition::TypeFrom(input->type()), LDefinition::MUST_REUSE_INPUT); + t.setReusedInput(reusedInput); + return t; +} + +template <typename T> +void LIRGeneratorShared::annotate(T* ins) { + ins->setId(lirGraph_.getInstructionId()); +} + +template <typename T> +void LIRGeneratorShared::add(T* ins, MInstruction* mir) { + MOZ_ASSERT(!ins->isPhi()); + current->add(ins); + if (mir) { + MOZ_ASSERT(current == mir->block()->lir()); + ins->setMir(mir); + } + annotate(ins); + if (ins->isCall()) { + gen->setNeedsOverrecursedCheck(); + gen->setNeedsStaticStackAlignment(); + } +} + +#ifdef JS_NUNBOX32 +// Returns the virtual register of a js::Value-defining instruction. This is +// abstracted because MBox is a special value-returning instruction that +// redefines its input payload if its input is not constant. Therefore, it is +// illegal to request a box's payload by adding VREG_DATA_OFFSET to its raw id. +static inline uint32_t VirtualRegisterOfPayload(MDefinition* mir) { + if (mir->isBox()) { + MDefinition* inner = mir->toBox()->getOperand(0); + if (!inner->isConstant() && inner->type() != MIRType::Double && + inner->type() != MIRType::Float32) { + return inner->virtualRegister(); + } + } + return mir->virtualRegister() + VREG_DATA_OFFSET; +} + +// Note: always call ensureDefined before calling useType/usePayload, +// so that emitted-at-use operands are handled correctly. +LUse LIRGeneratorShared::useType(MDefinition* mir, LUse::Policy policy) { + MOZ_ASSERT(mir->type() == MIRType::Value); + + return LUse(mir->virtualRegister() + VREG_TYPE_OFFSET, policy); +} + +LUse LIRGeneratorShared::usePayload(MDefinition* mir, LUse::Policy policy) { + MOZ_ASSERT(mir->type() == MIRType::Value); + + return LUse(VirtualRegisterOfPayload(mir), policy); +} + +LUse LIRGeneratorShared::usePayloadAtStart(MDefinition* mir, + LUse::Policy policy) { + MOZ_ASSERT(mir->type() == MIRType::Value); + + return LUse(VirtualRegisterOfPayload(mir), policy, true); +} + +LUse LIRGeneratorShared::usePayloadInRegisterAtStart(MDefinition* mir) { + return usePayloadAtStart(mir, LUse::REGISTER); +} + +void LIRGeneratorShared::fillBoxUses(LInstruction* lir, size_t n, + MDefinition* mir) { + ensureDefined(mir); + lir->getOperand(n)->toUse()->setVirtualRegister(mir->virtualRegister() + + VREG_TYPE_OFFSET); + lir->getOperand(n + 1)->toUse()->setVirtualRegister( + VirtualRegisterOfPayload(mir)); +} +#endif + +LUse LIRGeneratorShared::useRegisterForTypedLoad(MDefinition* mir, + MIRType type) { + MOZ_ASSERT(type != MIRType::Value && type != MIRType::None); + MOZ_ASSERT(mir->type() == MIRType::Object || mir->type() == MIRType::Slots); + +#ifdef JS_PUNBOX64 + // On x64, masm.loadUnboxedValue emits slightly less efficient code when + // the input and output use the same register and we're not loading an + // int32/bool/double, so we just call useRegister in this case. + if (type != MIRType::Int32 && type != MIRType::Boolean && + type != MIRType::Double) { + return useRegister(mir); + } +#endif + + return useRegisterAtStart(mir); +} + +LBoxAllocation LIRGeneratorShared::useBox(MDefinition* mir, LUse::Policy policy, + bool useAtStart) { + MOZ_ASSERT(mir->type() == MIRType::Value); + + ensureDefined(mir); + +#if defined(JS_NUNBOX32) + return LBoxAllocation( + LUse(mir->virtualRegister(), policy, useAtStart), + LUse(VirtualRegisterOfPayload(mir), policy, useAtStart)); +#else + return LBoxAllocation(LUse(mir->virtualRegister(), policy, useAtStart)); +#endif +} + +LBoxAllocation LIRGeneratorShared::useBoxOrTyped(MDefinition* mir, + bool useAtStart) { + if (mir->type() == MIRType::Value) { + return useBox(mir, LUse::REGISTER, useAtStart); + } + +#if defined(JS_NUNBOX32) + return LBoxAllocation(useAtStart ? useRegisterAtStart(mir) : useRegister(mir), + LAllocation()); +#else + return LBoxAllocation(useAtStart ? useRegisterAtStart(mir) + : useRegister(mir)); +#endif +} + +LBoxAllocation LIRGeneratorShared::useBoxOrTypedOrConstant(MDefinition* mir, + bool useConstant, + bool useAtStart) { + if (useConstant && mir->isConstant()) { +#if defined(JS_NUNBOX32) + return LBoxAllocation(LAllocation(mir->toConstant()), LAllocation()); +#else + return LBoxAllocation(LAllocation(mir->toConstant())); +#endif + } + + return useBoxOrTyped(mir, useAtStart); +} + +LInt64Allocation LIRGeneratorShared::useInt64(MDefinition* mir, + LUse::Policy policy, + bool useAtStart) { + MOZ_ASSERT(mir->type() == MIRType::Int64); + + ensureDefined(mir); + + uint32_t vreg = mir->virtualRegister(); +#if JS_BITS_PER_WORD == 32 + return LInt64Allocation(LUse(vreg + INT64HIGH_INDEX, policy, useAtStart), + LUse(vreg + INT64LOW_INDEX, policy, useAtStart)); +#else + return LInt64Allocation(LUse(vreg, policy, useAtStart)); +#endif +} + +LInt64Allocation LIRGeneratorShared::useInt64Fixed(MDefinition* mir, + Register64 regs, + bool useAtStart) { + MOZ_ASSERT(mir->type() == MIRType::Int64); + + ensureDefined(mir); + + uint32_t vreg = mir->virtualRegister(); +#if JS_BITS_PER_WORD == 32 + return LInt64Allocation(LUse(regs.high, vreg + INT64HIGH_INDEX, useAtStart), + LUse(regs.low, vreg + INT64LOW_INDEX, useAtStart)); +#else + return LInt64Allocation(LUse(regs.reg, vreg, useAtStart)); +#endif +} + +LInt64Allocation LIRGeneratorShared::useInt64FixedAtStart(MDefinition* mir, + Register64 regs) { + return useInt64Fixed(mir, regs, true); +} + +LInt64Allocation LIRGeneratorShared::useInt64(MDefinition* mir, + bool useAtStart) { + // On 32-bit platforms, always load the value in registers. +#if JS_BITS_PER_WORD == 32 + return useInt64(mir, LUse::REGISTER, useAtStart); +#else + return useInt64(mir, LUse::ANY, useAtStart); +#endif +} + +LInt64Allocation LIRGeneratorShared::useInt64AtStart(MDefinition* mir) { + return useInt64(mir, /* useAtStart = */ true); +} + +LInt64Allocation LIRGeneratorShared::useInt64Register(MDefinition* mir, + bool useAtStart) { + return useInt64(mir, LUse::REGISTER, useAtStart); +} + +LInt64Allocation LIRGeneratorShared::useInt64OrConstant(MDefinition* mir, + bool useAtStart) { + if (mir->isConstant()) { +#if defined(JS_NUNBOX32) + return LInt64Allocation(LAllocation(mir->toConstant()), LAllocation()); +#else + return LInt64Allocation(LAllocation(mir->toConstant())); +#endif + } + return useInt64(mir, useAtStart); +} + +LInt64Allocation LIRGeneratorShared::useInt64RegisterOrConstant( + MDefinition* mir, bool useAtStart) { + if (mir->isConstant()) { +#if defined(JS_NUNBOX32) + return LInt64Allocation(LAllocation(mir->toConstant()), LAllocation()); +#else + return LInt64Allocation(LAllocation(mir->toConstant())); +#endif + } + return useInt64Register(mir, useAtStart); +} + +LInt64Allocation LIRGeneratorShared::useInt64RegisterAtStart(MDefinition* mir) { + return useInt64Register(mir, /* useAtStart = */ true); +} + +LInt64Allocation LIRGeneratorShared::useInt64RegisterOrConstantAtStart( + MDefinition* mir) { + return useInt64RegisterOrConstant(mir, /* useAtStart = */ true); +} + +LInt64Allocation LIRGeneratorShared::useInt64OrConstantAtStart( + MDefinition* mir) { + return useInt64OrConstant(mir, /* useAtStart = */ true); +} + +void LIRGeneratorShared::lowerConstantDouble(double d, MInstruction* mir) { + define(new (alloc()) LDouble(d), mir); +} +void LIRGeneratorShared::lowerConstantFloat32(float f, MInstruction* mir) { + define(new (alloc()) LFloat32(f), mir); +} + +} // namespace jit +} // namespace js + +#endif /* jit_shared_Lowering_shared_inl_h */ |