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/x86/Lowering-x86.h" michael@0: michael@0: #include "jit/MIR.h" michael@0: #include "jit/x86/Assembler-x86.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: LDefinition michael@0: LIRGeneratorX86::tempForDispatchCache(MIRType outputType) michael@0: { michael@0: // x86 doesn't have a scratch register and we need one for the michael@0: // indirect jump for dispatch-style ICs. michael@0: // michael@0: // Note that currently we only install dispatch-style ICs for parallel michael@0: // execution. If this assumption changes, please change it here. michael@0: if (gen->info().executionMode() != ParallelExecution) michael@0: return LDefinition::BogusTemp(); michael@0: michael@0: // If we don't have an output register, we need a temp. michael@0: if (outputType == MIRType_None) michael@0: return temp(); michael@0: michael@0: // If we have a double output register, we need a temp. michael@0: if (outputType == MIRType_Double) michael@0: return temp(); michael@0: michael@0: // Otherwise we have a non-double output register and we can reuse it. michael@0: return LDefinition::BogusTemp(); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::useBox(LInstruction *lir, size_t n, MDefinition *mir, michael@0: LUse::Policy policy, bool useAtStart) michael@0: { michael@0: JS_ASSERT(mir->type() == MIRType_Value); michael@0: michael@0: if (!ensureDefined(mir)) michael@0: return false; michael@0: lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart)); michael@0: lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, michael@0: Register reg2) michael@0: { michael@0: JS_ASSERT(mir->type() == MIRType_Value); michael@0: JS_ASSERT(reg1 != reg2); michael@0: michael@0: if (!ensureDefined(mir)) michael@0: return false; michael@0: lir->setOperand(n, LUse(reg1, mir->virtualRegister())); michael@0: lir->setOperand(n + 1, LUse(reg2, VirtualRegisterOfPayload(mir))); michael@0: return true; michael@0: } michael@0: michael@0: LAllocation michael@0: LIRGeneratorX86::useByteOpRegister(MDefinition *mir) michael@0: { michael@0: return useFixed(mir, eax); michael@0: } michael@0: michael@0: LAllocation michael@0: LIRGeneratorX86::useByteOpRegisterOrNonDoubleConstant(MDefinition *mir) michael@0: { michael@0: return useFixed(mir, eax); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::visitBox(MBox *box) michael@0: { michael@0: MDefinition *inner = box->getOperand(0); michael@0: michael@0: // If the box wrapped a double, it needs a new register. michael@0: if (IsFloatingPointType(inner->type())) michael@0: return defineBox(new(alloc()) LBoxFloatingPoint(useRegisterAtStart(inner), tempCopy(inner, 0), michael@0: inner->type()), box); michael@0: michael@0: if (box->canEmitAtUses()) michael@0: return emitAtUses(box); michael@0: michael@0: if (inner->isConstant()) michael@0: return defineBox(new(alloc()) LValue(inner->toConstant()->value()), box); michael@0: michael@0: LBox *lir = new(alloc()) LBox(use(inner), inner->type()); michael@0: michael@0: // Otherwise, we should not define a new register for the payload portion michael@0: // of the output, so bypass defineBox(). michael@0: uint32_t vreg = getVirtualRegister(); michael@0: if (vreg >= MAX_VIRTUAL_REGISTERS) michael@0: return false; michael@0: michael@0: // Note that because we're using PASSTHROUGH, we do not change the type of michael@0: // the definition. We also do not define the first output as "TYPE", michael@0: // because it has no corresponding payload at (vreg + 1). Also note that michael@0: // although we copy the input's original type for the payload half of the michael@0: // definition, this is only for clarity. PASSTHROUGH definitions are michael@0: // ignored. michael@0: lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL)); michael@0: lir->setDef(1, LDefinition(inner->virtualRegister(), LDefinition::TypeFrom(inner->type()), michael@0: LDefinition::PASSTHROUGH)); michael@0: box->setVirtualRegister(vreg); michael@0: return add(lir); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::visitUnbox(MUnbox *unbox) michael@0: { michael@0: // An unbox on x86 reads in a type tag (either in memory or a register) and michael@0: // a payload. Unlike most instructions conusming a box, we ask for the type michael@0: // second, so that the result can re-use the first input. michael@0: MDefinition *inner = unbox->getOperand(0); michael@0: michael@0: if (!ensureDefined(inner)) michael@0: return false; michael@0: michael@0: if (IsFloatingPointType(unbox->type())) { michael@0: LUnboxFloatingPoint *lir = new(alloc()) LUnboxFloatingPoint(unbox->type()); michael@0: if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind())) michael@0: return false; michael@0: if (!useBox(lir, LUnboxFloatingPoint::Input, inner)) michael@0: return false; michael@0: return define(lir, unbox); michael@0: } michael@0: michael@0: // Swap the order we use the box pieces so we can re-use the payload register. michael@0: LUnbox *lir = new(alloc()) LUnbox; michael@0: lir->setOperand(0, usePayloadInRegisterAtStart(inner)); michael@0: lir->setOperand(1, useType(inner, LUse::ANY)); michael@0: michael@0: if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind())) michael@0: return false; michael@0: michael@0: // Note that PASSTHROUGH here is illegal, since types and payloads form two michael@0: // separate intervals. If the type becomes dead before the payload, it michael@0: // could be used as a Value without the type being recoverable. Unbox's michael@0: // purpose is to eagerly kill the definition of a type tag, so keeping both michael@0: // alive (for the purpose of gcmaps) is unappealing. Instead, we create a michael@0: // new virtual register. michael@0: return defineReuseInput(lir, unbox, 0); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::visitReturn(MReturn *ret) michael@0: { michael@0: MDefinition *opd = ret->getOperand(0); michael@0: JS_ASSERT(opd->type() == MIRType_Value); michael@0: michael@0: LReturn *ins = new(alloc()) LReturn; michael@0: ins->setOperand(0, LUse(JSReturnReg_Type)); michael@0: ins->setOperand(1, LUse(JSReturnReg_Data)); michael@0: return fillBoxUses(ins, 0, opd) && add(ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::defineUntypedPhi(MPhi *phi, size_t lirIndex) michael@0: { michael@0: LPhi *type = current->getPhi(lirIndex + VREG_TYPE_OFFSET); michael@0: LPhi *payload = current->getPhi(lirIndex + VREG_DATA_OFFSET); michael@0: michael@0: uint32_t typeVreg = getVirtualRegister(); michael@0: if (typeVreg >= MAX_VIRTUAL_REGISTERS) michael@0: return false; michael@0: michael@0: phi->setVirtualRegister(typeVreg); michael@0: michael@0: uint32_t payloadVreg = getVirtualRegister(); michael@0: if (payloadVreg >= MAX_VIRTUAL_REGISTERS) michael@0: return false; michael@0: JS_ASSERT(typeVreg + 1 == payloadVreg); michael@0: michael@0: type->setDef(0, LDefinition(typeVreg, LDefinition::TYPE)); michael@0: payload->setDef(0, LDefinition(payloadVreg, LDefinition::PAYLOAD)); michael@0: annotate(type); michael@0: annotate(payload); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: LIRGeneratorX86::lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex) michael@0: { michael@0: MDefinition *operand = phi->getOperand(inputPosition); michael@0: LPhi *type = block->getPhi(lirIndex + VREG_TYPE_OFFSET); michael@0: LPhi *payload = block->getPhi(lirIndex + VREG_DATA_OFFSET); michael@0: type->setOperand(inputPosition, LUse(operand->virtualRegister() + VREG_TYPE_OFFSET, LUse::ANY)); michael@0: payload->setOperand(inputPosition, LUse(VirtualRegisterOfPayload(operand), LUse::ANY)); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins) michael@0: { michael@0: JS_ASSERT(ins->input()->type() == MIRType_Int32); michael@0: LAsmJSUInt32ToDouble *lir = new(alloc()) LAsmJSUInt32ToDouble(useRegisterAtStart(ins->input()), temp()); michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins) michael@0: { michael@0: JS_ASSERT(ins->input()->type() == MIRType_Int32); michael@0: LAsmJSUInt32ToFloat32 *lir = new(alloc()) LAsmJSUInt32ToFloat32(useRegisterAtStart(ins->input()), temp()); michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins) michael@0: { michael@0: MDefinition *ptr = ins->ptr(); michael@0: LAllocation ptrAlloc; michael@0: JS_ASSERT(ptr->type() == MIRType_Int32); michael@0: michael@0: // For the x86 it is best to keep the 'ptr' in a register if a bounds check is needed. michael@0: if (ptr->isConstant() && ins->skipBoundsCheck()) { michael@0: int32_t ptrValue = ptr->toConstant()->value().toInt32(); michael@0: // A bounds check is only skipped for a positive index. michael@0: JS_ASSERT(ptrValue >= 0); michael@0: ptrAlloc = LAllocation(ptr->toConstant()->vp()); michael@0: } else { michael@0: ptrAlloc = useRegisterAtStart(ptr); michael@0: } michael@0: LAsmJSLoadHeap *lir = new(alloc()) LAsmJSLoadHeap(ptrAlloc); michael@0: return define(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins) michael@0: { michael@0: MDefinition *ptr = ins->ptr(); michael@0: LAsmJSStoreHeap *lir; michael@0: JS_ASSERT(ptr->type() == MIRType_Int32); michael@0: michael@0: if (ptr->isConstant() && ins->skipBoundsCheck()) { michael@0: int32_t ptrValue = ptr->toConstant()->value().toInt32(); michael@0: JS_ASSERT(ptrValue >= 0); michael@0: LAllocation ptrAlloc = LAllocation(ptr->toConstant()->vp()); michael@0: switch (ins->viewType()) { michael@0: case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8: michael@0: // See comment below. michael@0: lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useFixed(ins->value(), eax)); michael@0: break; michael@0: case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16: michael@0: case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32: michael@0: case ArrayBufferView::TYPE_FLOAT32: case ArrayBufferView::TYPE_FLOAT64: michael@0: // See comment below. michael@0: lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())); michael@0: break; michael@0: default: MOZ_ASSUME_UNREACHABLE("unexpected array type"); michael@0: } michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: switch (ins->viewType()) { michael@0: case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8: michael@0: // See comment for LIRGeneratorX86::useByteOpRegister. michael@0: lir = new(alloc()) LAsmJSStoreHeap(useRegister(ins->ptr()), useFixed(ins->value(), eax)); michael@0: break; michael@0: case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16: michael@0: case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32: michael@0: case ArrayBufferView::TYPE_FLOAT32: case ArrayBufferView::TYPE_FLOAT64: michael@0: // For now, don't allow constant values. The immediate operand michael@0: // affects instruction layout which affects patching. michael@0: lir = new(alloc()) LAsmJSStoreHeap(useRegisterAtStart(ptr), useRegisterAtStart(ins->value())); michael@0: break; michael@0: default: MOZ_ASSUME_UNREACHABLE("unexpected array type"); michael@0: } michael@0: michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins) michael@0: { michael@0: // The code generated for StoreTypedArrayElementStatic is identical to that michael@0: // for AsmJSStoreHeap, and the same concerns apply. michael@0: LStoreTypedArrayElementStatic *lir; michael@0: switch (ins->viewType()) { michael@0: case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8: michael@0: case ArrayBufferView::TYPE_UINT8_CLAMPED: michael@0: lir = new(alloc()) LStoreTypedArrayElementStatic(useRegister(ins->ptr()), michael@0: useFixed(ins->value(), eax)); michael@0: break; michael@0: case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16: michael@0: case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32: michael@0: case ArrayBufferView::TYPE_FLOAT32: case ArrayBufferView::TYPE_FLOAT64: michael@0: lir = new(alloc()) LStoreTypedArrayElementStatic(useRegisterAtStart(ins->ptr()), michael@0: useRegisterAtStart(ins->value())); michael@0: break; michael@0: default: MOZ_ASSUME_UNREACHABLE("unexpected array type"); michael@0: } michael@0: michael@0: return add(lir, ins); michael@0: } michael@0: michael@0: bool michael@0: LIRGeneratorX86::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins) michael@0: { michael@0: return define(new(alloc()) LAsmJSLoadFuncPtr(useRegisterAtStart(ins->index())), ins); michael@0: }