blob: a7db9c6a8f8aa3bc2571702ddbde7bd35222304b [file] [log] [blame]
/* -*- 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 "LIR.h"
#include "Lowering.h"
#include "MIR.h"
#include "MIRGraph.h"
#include "IonSpewer.h"
#include "RangeAnalysis.h"
#include "jsanalyze.h"
#include "jsbool.h"
#include "jsnum.h"
#include "shared/Lowering-shared-inl.h"
#include "mozilla/DebugOnly.h"
using namespace js;
using namespace jit;
bool
LIRGenerator::visitParameter(MParameter *param)
{
ptrdiff_t offset;
if (param->index() == MParameter::THIS_SLOT)
offset = THIS_FRAME_SLOT;
else
offset = 1 + param->index();
LParameter *ins = new LParameter;
if (!defineBox(ins, param, LDefinition::PRESET))
return false;
offset *= sizeof(Value);
#if defined(JS_NUNBOX32)
# if defined(IS_BIG_ENDIAN)
ins->getDef(0)->setOutput(LArgument(LAllocation::INT_ARGUMENT, offset));
ins->getDef(1)->setOutput(LArgument(LAllocation::INT_ARGUMENT, offset + 4));
# else
ins->getDef(0)->setOutput(LArgument(LAllocation::INT_ARGUMENT, offset + 4));
ins->getDef(1)->setOutput(LArgument(LAllocation::INT_ARGUMENT, offset));
# endif
#elif defined(JS_PUNBOX64)
ins->getDef(0)->setOutput(LArgument(LAllocation::INT_ARGUMENT, offset));
#endif
return true;
}
bool
LIRGenerator::visitCallee(MCallee *ins)
{
return define(new LCallee(), ins);
}
bool
LIRGenerator::visitGoto(MGoto *ins)
{
return add(new LGoto(ins->target()));
}
bool
LIRGenerator::visitTableSwitch(MTableSwitch *tableswitch)
{
MDefinition *opd = tableswitch->getOperand(0);
// There should be at least 1 successor. The default case!
JS_ASSERT(tableswitch->numSuccessors() > 0);
// If there are no cases, the default case is always taken.
if (tableswitch->numSuccessors() == 1)
return add(new LGoto(tableswitch->getDefault()));
// If we don't know the type.
if (opd->type() == MIRType_Value) {
LTableSwitchV *lir = newLTableSwitchV(tableswitch);
if (!useBox(lir, LTableSwitchV::InputValue, opd))
return false;
return add(lir);
}
// Case indices are numeric, so other types will always go to the default case.
if (opd->type() != MIRType_Int32 && opd->type() != MIRType_Double)
return add(new LGoto(tableswitch->getDefault()));
// Return an LTableSwitch, capable of handling either an integer or
// floating-point index.
LAllocation index;
LDefinition tempInt;
if (opd->type() == MIRType_Int32) {
index = useRegisterAtStart(opd);
tempInt = tempCopy(opd, 0);
} else {
index = useRegister(opd);
tempInt = temp(LDefinition::GENERAL);
}
return add(newLTableSwitch(index, tempInt, tableswitch));
}
bool
LIRGenerator::visitCheckOverRecursed(MCheckOverRecursed *ins)
{
LCheckOverRecursed *lir = new LCheckOverRecursed();
if (!add(lir))
return false;
if (!assignSafepoint(lir, ins))
return false;
return true;
}
bool
LIRGenerator::visitParCheckOverRecursed(MParCheckOverRecursed *ins)
{
LParCheckOverRecursed *lir = new LParCheckOverRecursed(
useRegister(ins->parSlice()),
temp());
if (!add(lir, ins))
return false;
if (!assignSafepoint(lir, ins))
return false;
return true;
}
bool
LIRGenerator::visitDefVar(MDefVar *ins)
{
LDefVar *lir = new LDefVar(useRegisterAtStart(ins->scopeChain()));
if (!add(lir, ins))
return false;
if (!assignSafepoint(lir, ins))
return false;
return true;
}
bool
LIRGenerator::visitDefFun(MDefFun *ins)
{
LDefFun *lir = new LDefFun(useRegisterAtStart(ins->scopeChain()));
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitNewSlots(MNewSlots *ins)
{
// No safepoint needed, since we don't pass a cx.
LNewSlots *lir = new LNewSlots(tempFixed(CallTempReg0), tempFixed(CallTempReg1),
tempFixed(CallTempReg2));
if (!assignSnapshot(lir))
return false;
return defineReturn(lir, ins);
}
bool
LIRGenerator::visitNewParallelArray(MNewParallelArray *ins)
{
LNewParallelArray *lir = new LNewParallelArray();
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitNewArray(MNewArray *ins)
{
LNewArray *lir = new LNewArray();
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitNewObject(MNewObject *ins)
{
LNewObject *lir = new LNewObject();
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitNewDeclEnvObject(MNewDeclEnvObject *ins)
{
LNewDeclEnvObject *lir = new LNewDeclEnvObject();
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitNewCallObject(MNewCallObject *ins)
{
LAllocation slots;
if (ins->slots()->type() == MIRType_Slots)
slots = useRegister(ins->slots());
else
slots = LConstantIndex::Bogus();
LNewCallObject *lir = new LNewCallObject(slots);
if (!define(lir, ins))
return false;
if (!assignSafepoint(lir, ins))
return false;
return true;
}
bool
LIRGenerator::visitParNewCallObject(MParNewCallObject *ins)
{
const LAllocation &parThreadContext = useRegister(ins->parSlice());
const LDefinition &temp1 = temp();
const LDefinition &temp2 = temp();
LParNewCallObject *lir;
if (ins->slots()->type() == MIRType_Slots) {
const LAllocation &slots = useRegister(ins->slots());
lir = LParNewCallObject::NewWithSlots(parThreadContext, slots,
temp1, temp2);
} else {
lir = LParNewCallObject::NewSansSlots(parThreadContext, temp1, temp2);
}
return define(lir, ins);
}
bool
LIRGenerator::visitNewStringObject(MNewStringObject *ins)
{
JS_ASSERT(ins->input()->type() == MIRType_String);
LNewStringObject *lir = new LNewStringObject(useRegister(ins->input()), temp());
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitParBailout(MParBailout *ins)
{
LParBailout *lir = new LParBailout();
return add(lir, ins);
}
bool
LIRGenerator::visitInitElem(MInitElem *ins)
{
LInitElem *lir = new LInitElem(useRegisterAtStart(ins->getObject()));
if (!useBoxAtStart(lir, LInitElem::IdIndex, ins->getId()))
return false;
if (!useBoxAtStart(lir, LInitElem::ValueIndex, ins->getValue()))
return false;
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitInitProp(MInitProp *ins)
{
LInitProp *lir = new LInitProp(useRegisterAtStart(ins->getObject()));
if (!useBoxAtStart(lir, LInitProp::ValueIndex, ins->getValue()))
return false;
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitPrepareCall(MPrepareCall *ins)
{
allocateArguments(ins->argc());
#ifdef DEBUG
if (!prepareCallStack_.append(ins))
return false;
#endif
return true;
}
bool
LIRGenerator::visitPassArg(MPassArg *arg)
{
MDefinition *opd = arg->getArgument();
uint32_t argslot = getArgumentSlot(arg->getArgnum());
// Pass through the virtual register of the operand.
// This causes snapshots to correctly copy the operand on the stack.
//
// This keeps the backing store around longer than strictly required.
// We could do better by informing snapshots about the argument vector.
arg->setVirtualRegister(opd->virtualRegister());
// Values take a slow path.
if (opd->type() == MIRType_Value) {
LStackArgV *stack = new LStackArgV(argslot);
return useBox(stack, 0, opd) && add(stack);
}
// Known types can move constant types and/or payloads.
LStackArgT *stack = new LStackArgT(argslot, useRegisterOrConstant(opd));
return add(stack, arg);
}
bool
LIRGenerator::visitCreateThisWithTemplate(MCreateThisWithTemplate *ins)
{
LCreateThisWithTemplate *lir = new LCreateThisWithTemplate();
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCreateThisWithProto(MCreateThisWithProto *ins)
{
LCreateThisWithProto *lir =
new LCreateThisWithProto(useRegisterOrConstantAtStart(ins->getCallee()),
useRegisterOrConstantAtStart(ins->getPrototype()));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCreateThis(MCreateThis *ins)
{
LCreateThis *lir = new LCreateThis(useRegisterOrConstantAtStart(ins->getCallee()));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCreateArgumentsObject(MCreateArgumentsObject *ins)
{
// LAllocation callObj = useRegisterAtStart(ins->getCallObject());
LAllocation callObj = useFixed(ins->getCallObject(), CallTempReg0);
LCreateArgumentsObject *lir = new LCreateArgumentsObject(callObj, tempFixed(CallTempReg1));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitGetArgumentsObjectArg(MGetArgumentsObjectArg *ins)
{
LAllocation argsObj = useRegister(ins->getArgsObject());
LGetArgumentsObjectArg *lir = new LGetArgumentsObjectArg(argsObj, temp());
return defineBox(lir, ins);
}
bool
LIRGenerator::visitSetArgumentsObjectArg(MSetArgumentsObjectArg *ins)
{
LAllocation argsObj = useRegister(ins->getArgsObject());
LSetArgumentsObjectArg *lir = new LSetArgumentsObjectArg(argsObj, temp());
if (!useBox(lir, LSetArgumentsObjectArg::ValueIndex, ins->getValue()))
return false;
return add(lir, ins);
}
bool
LIRGenerator::visitReturnFromCtor(MReturnFromCtor *ins)
{
LReturnFromCtor *lir = new LReturnFromCtor(useRegister(ins->getObject()));
if (!useBox(lir, LReturnFromCtor::ValueIndex, ins->getValue()))
return false;
return define(lir, ins);
}
bool
LIRGenerator::visitCall(MCall *call)
{
JS_ASSERT(CallTempReg0 != CallTempReg1);
JS_ASSERT(CallTempReg0 != ArgumentsRectifierReg);
JS_ASSERT(CallTempReg1 != ArgumentsRectifierReg);
JS_ASSERT(call->getFunction()->type() == MIRType_Object);
// Height of the current argument vector.
uint32_t argslot = getArgumentSlotForCall();
freeArguments(call->numStackArgs());
// Check MPrepareCall/MCall nesting.
JS_ASSERT(prepareCallStack_.popCopy() == call->getPrepareCall());
JSFunction *target = call->getSingleTarget();
// Call DOM functions.
if (call->isDOMFunction()) {
JS_ASSERT(target && target->isNative());
Register cxReg, objReg, privReg, argsReg;
GetTempRegForIntArg(0, 0, &cxReg);
GetTempRegForIntArg(1, 0, &objReg);
GetTempRegForIntArg(2, 0, &privReg);
mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(3, 0, &argsReg);
MOZ_ASSERT(ok, "How can we not have four temp registers?");
LCallDOMNative *lir = new LCallDOMNative(argslot, tempFixed(cxReg),
tempFixed(objReg), tempFixed(privReg),
tempFixed(argsReg));
return (defineReturn(lir, call) && assignSafepoint(lir, call));
}
// Call known functions.
if (target) {
if (target->isNative()) {
Register cxReg, numReg, vpReg, tmpReg;
GetTempRegForIntArg(0, 0, &cxReg);
GetTempRegForIntArg(1, 0, &numReg);
GetTempRegForIntArg(2, 0, &vpReg);
// Even though this is just a temp reg, use the same API to avoid
// register collisions.
mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(3, 0, &tmpReg);
MOZ_ASSERT(ok, "How can we not have four temp registers?");
LCallNative *lir = new LCallNative(argslot, tempFixed(cxReg),
tempFixed(numReg),
tempFixed(vpReg),
tempFixed(tmpReg));
return (defineReturn(lir, call) && assignSafepoint(lir, call));
}
LCallKnown *lir = new LCallKnown(useFixed(call->getFunction(), CallTempReg0),
argslot, tempFixed(CallTempReg2));
return (defineReturn(lir, call) && assignSafepoint(lir, call));
}
// Call anything, using the most generic code.
LCallGeneric *lir = new LCallGeneric(useFixed(call->getFunction(), CallTempReg0),
argslot, tempFixed(ArgumentsRectifierReg), tempFixed(CallTempReg2));
return (assignSnapshot(lir) && defineReturn(lir, call) && assignSafepoint(lir, call));
}
bool
LIRGenerator::visitApplyArgs(MApplyArgs *apply)
{
JS_ASSERT(apply->getFunction()->type() == MIRType_Object);
// Assert if we cannot build a rectifier frame.
JS_ASSERT(CallTempReg0 != ArgumentsRectifierReg);
JS_ASSERT(CallTempReg1 != ArgumentsRectifierReg);
// Assert if the return value is already erased.
JS_ASSERT(CallTempReg2 != JSReturnReg_Type);
JS_ASSERT(CallTempReg2 != JSReturnReg_Data);
LApplyArgsGeneric *lir = new LApplyArgsGeneric(
useFixed(apply->getFunction(), CallTempReg3),
useFixed(apply->getArgc(), CallTempReg0),
tempFixed(CallTempReg1), // object register
tempFixed(CallTempReg2)); // copy register
MDefinition *self = apply->getThis();
if (!useBoxFixed(lir, LApplyArgsGeneric::ThisIndex, self, CallTempReg4, CallTempReg5))
return false;
// Bailout is only needed in the case of possible non-JSFunction callee.
if (!apply->getSingleTarget() && !assignSnapshot(lir))
return false;
if (!defineReturn(lir, apply))
return false;
if (!assignSafepoint(lir, apply))
return false;
return true;
}
bool
LIRGenerator::visitGetDynamicName(MGetDynamicName *ins)
{
MDefinition *scopeChain = ins->getScopeChain();
JS_ASSERT(scopeChain->type() == MIRType_Object);
MDefinition *name = ins->getName();
JS_ASSERT(name->type() == MIRType_String);
LGetDynamicName *lir = new LGetDynamicName(useFixed(scopeChain, CallTempReg0),
useFixed(name, CallTempReg1),
tempFixed(CallTempReg2),
tempFixed(CallTempReg3),
tempFixed(CallTempReg4));
return assignSnapshot(lir) && defineReturn(lir, ins);
}
bool
LIRGenerator::visitFilterArguments(MFilterArguments *ins)
{
MDefinition *string = ins->getString();
JS_ASSERT(string->type() == MIRType_String);
LFilterArguments *lir = new LFilterArguments(useFixed(string, CallTempReg0),
tempFixed(CallTempReg1),
tempFixed(CallTempReg2));
return assignSnapshot(lir) && add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCallDirectEval(MCallDirectEval *ins)
{
MDefinition *scopeChain = ins->getScopeChain();
JS_ASSERT(scopeChain->type() == MIRType_Object);
MDefinition *string = ins->getString();
JS_ASSERT(string->type() == MIRType_String);
MDefinition *thisValue = ins->getThisValue();
LCallDirectEval *lir = new LCallDirectEval(useRegisterAtStart(scopeChain),
useRegisterAtStart(string));
if (!useBoxAtStart(lir, LCallDirectEval::ThisValueInput, thisValue))
return false;
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
static JSOp
ReorderComparison(JSOp op, MDefinition **lhsp, MDefinition **rhsp)
{
MDefinition *lhs = *lhsp;
MDefinition *rhs = *rhsp;
if (lhs->isConstant()) {
*rhsp = lhs;
*lhsp = rhs;
return js::analyze::ReverseCompareOp(op);
}
return op;
}
bool
LIRGenerator::visitTest(MTest *test)
{
MDefinition *opd = test->getOperand(0);
MBasicBlock *ifTrue = test->ifTrue();
MBasicBlock *ifFalse = test->ifFalse();
// String is converted to length of string in the type analysis phase (see
// TestPolicy).
JS_ASSERT(opd->type() != MIRType_String);
if (opd->type() == MIRType_Value) {
LDefinition temp0, temp1;
if (test->operandMightEmulateUndefined()) {
temp0 = temp();
temp1 = temp();
} else {
temp0 = LDefinition::BogusTemp();
temp1 = LDefinition::BogusTemp();
}
LTestVAndBranch *lir = new LTestVAndBranch(ifTrue, ifFalse, tempFloat(), temp0, temp1);
if (!useBox(lir, LTestVAndBranch::Input, opd))
return false;
return add(lir, test);
}
if (opd->type() == MIRType_Object) {
// If the object might emulate undefined, we have to test for that.
if (test->operandMightEmulateUndefined())
return add(new LTestOAndBranch(useRegister(opd), ifTrue, ifFalse, temp()), test);
// Otherwise we know it's truthy.
return add(new LGoto(ifTrue));
}
// These must be explicitly sniffed out since they are constants and have
// no payload.
if (opd->type() == MIRType_Undefined || opd->type() == MIRType_Null)
return add(new LGoto(ifFalse));
// Constant Double operand.
if (opd->type() == MIRType_Double && opd->isConstant()) {
bool result = ToBoolean(opd->toConstant()->value());
return add(new LGoto(result ? ifTrue : ifFalse));
}
// Constant Int32 operand.
if (opd->type() == MIRType_Int32 && opd->isConstant()) {
int32_t num = opd->toConstant()->value().toInt32();
return add(new LGoto(num ? ifTrue : ifFalse));
}
// Constant Boolean operand.
if (opd->type() == MIRType_Boolean && opd->isConstant()) {
bool result = opd->toConstant()->value().toBoolean();
return add(new LGoto(result ? ifTrue : ifFalse));
}
// Check if the operand for this test is a compare operation. If it is, we want
// to emit an LCompare*AndBranch rather than an LTest*AndBranch, to fuse the
// compare and jump instructions.
if (opd->isCompare() && opd->isEmittedAtUses()) {
MCompare *comp = opd->toCompare();
MDefinition *left = comp->lhs();
MDefinition *right = comp->rhs();
// Try to fold the comparison so that we don't have to handle all cases.
bool result;
if (comp->tryFold(&result))
return add(new LGoto(result ? ifTrue : ifFalse));
// Emit LCompare*AndBranch.
// Compare and branch null/undefined.
// The second operand has known null/undefined type,
// so just test the first operand.
if (comp->compareType() == MCompare::Compare_Null ||
comp->compareType() == MCompare::Compare_Undefined)
{
if (left->type() == MIRType_Object) {
MOZ_ASSERT(comp->operandMightEmulateUndefined(),
"MCompare::tryFold should handle the never-emulates-undefined case");
LEmulatesUndefinedAndBranch *lir =
new LEmulatesUndefinedAndBranch(useRegister(left), ifTrue, ifFalse, temp());
return add(lir, comp);
}
LDefinition tmp, tmpToUnbox;
if (comp->operandMightEmulateUndefined()) {
tmp = temp();
tmpToUnbox = tempToUnbox();
} else {
tmp = LDefinition::BogusTemp();
tmpToUnbox = LDefinition::BogusTemp();
}
LIsNullOrLikeUndefinedAndBranch *lir =
new LIsNullOrLikeUndefinedAndBranch(ifTrue, ifFalse, tmp, tmpToUnbox);
if (!useBox(lir, LIsNullOrLikeUndefinedAndBranch::Value, left))
return false;
return add(lir, comp);
}
// Compare and branch booleans.
if (comp->compareType() == MCompare::Compare_Boolean) {
JS_ASSERT(left->type() == MIRType_Value);
JS_ASSERT(right->type() == MIRType_Boolean);
LAllocation rhs = useRegisterOrConstant(right);
LCompareBAndBranch *lir = new LCompareBAndBranch(rhs, ifTrue, ifFalse);
if (!useBox(lir, LCompareBAndBranch::Lhs, left))
return false;
return add(lir, comp);
}
// Compare and branch Int32 or Object pointers.
if (comp->compareType() == MCompare::Compare_Int32 ||
comp->compareType() == MCompare::Compare_UInt32 ||
comp->compareType() == MCompare::Compare_Object)
{
JSOp op = ReorderComparison(comp->jsop(), &left, &right);
LAllocation lhs = useRegister(left);
LAllocation rhs;
if (comp->compareType() == MCompare::Compare_Int32 ||
comp->compareType() == MCompare::Compare_UInt32)
{
rhs = useAnyOrConstant(right);
} else {
rhs = useRegister(right);
}
LCompareAndBranch *lir = new LCompareAndBranch(op, lhs, rhs, ifTrue, ifFalse);
return add(lir, comp);
}
// Compare and branch doubles.
if (comp->isDoubleComparison()) {
LAllocation lhs = useRegister(left);
LAllocation rhs = useRegister(right);
#if defined(JS_CPU_MIPS)
LCompareDAndBranch *lir = new LCompareDAndBranch(comp, lhs, rhs, ifTrue, ifFalse);
#else
LCompareDAndBranch *lir = new LCompareDAndBranch(lhs, rhs, ifTrue, ifFalse);
#endif
return add(lir, comp);
}
// Compare values.
if (comp->compareType() == MCompare::Compare_Value) {
LCompareVAndBranch *lir = new LCompareVAndBranch(ifTrue, ifFalse);
if (!useBoxAtStart(lir, LCompareVAndBranch::LhsInput, left))
return false;
if (!useBoxAtStart(lir, LCompareVAndBranch::RhsInput, right))
return false;
return add(lir, comp);
}
}
if (opd->type() == MIRType_Double)
return add(new LTestDAndBranch(useRegister(opd), ifTrue, ifFalse));
JS_ASSERT(opd->type() == MIRType_Int32 || opd->type() == MIRType_Boolean);
return add(new LTestIAndBranch(useRegister(opd), ifTrue, ifFalse));
}
bool
LIRGenerator::visitFunctionDispatch(MFunctionDispatch *ins)
{
LFunctionDispatch *lir = new LFunctionDispatch(useRegister(ins->input()));
return add(lir, ins);
}
bool
LIRGenerator::visitTypeObjectDispatch(MTypeObjectDispatch *ins)
{
LTypeObjectDispatch *lir = new LTypeObjectDispatch(useRegister(ins->input()), temp());
return add(lir, ins);
}
bool
LIRGenerator::visitPolyInlineDispatch(MPolyInlineDispatch *ins)
{
LDefinition tempDef = LDefinition::BogusTemp();
if (ins->propTable())
tempDef = temp();
LPolyInlineDispatch *lir = new LPolyInlineDispatch(useRegister(ins->input()), tempDef);
return add(lir, ins);
}
static inline bool
CanEmitCompareAtUses(MInstruction *ins)
{
if (!ins->canEmitAtUses())
return false;
bool foundTest = false;
for (MUseIterator iter(ins->usesBegin()); iter != ins->usesEnd(); iter++) {
MNode *node = iter->consumer();
if (!node->isDefinition())
return false;
if (!node->toDefinition()->isTest())
return false;
if (foundTest)
return false;
foundTest = true;
}
return true;
}
bool
LIRGenerator::visitCompare(MCompare *comp)
{
MDefinition *left = comp->lhs();
MDefinition *right = comp->rhs();
// Try to fold the comparison so that we don't have to handle all cases.
bool result;
if (comp->tryFold(&result))
return define(new LInteger(result), comp);
// Move below the emitAtUses call if we ever implement
// LCompareSAndBranch. Doing this now wouldn't be wrong, but doesn't
// make sense and avoids confusion.
if (comp->compareType() == MCompare::Compare_String) {
LCompareS *lir = new LCompareS(useRegister(left), useRegister(right), temp());
if (!define(lir, comp))
return false;
return assignSafepoint(lir, comp);
}
// Strict compare between value and string
if (comp->compareType() == MCompare::Compare_StrictString) {
JS_ASSERT(left->type() == MIRType_Value);
JS_ASSERT(right->type() == MIRType_String);
LCompareStrictS *lir = new LCompareStrictS(useRegister(right), temp(), tempToUnbox());
if (!useBox(lir, LCompareStrictS::Lhs, left))
return false;
if (!define(lir, comp))
return false;
return assignSafepoint(lir, comp);
}
// Unknown/unspecialized compare use a VM call.
if (comp->compareType() == MCompare::Compare_Unknown) {
LCompareVM *lir = new LCompareVM();
if (!useBoxAtStart(lir, LCompareVM::LhsInput, left))
return false;
if (!useBoxAtStart(lir, LCompareVM::RhsInput, right))
return false;
return defineReturn(lir, comp) && assignSafepoint(lir, comp);
}
// Sniff out if the output of this compare is used only for a branching.
// If it is, then we will emit an LCompare*AndBranch instruction in place
// of this compare and any test that uses this compare. Thus, we can
// ignore this Compare.
if (CanEmitCompareAtUses(comp))
return emitAtUses(comp);
// Compare Null and Undefined.
if (comp->compareType() == MCompare::Compare_Null ||
comp->compareType() == MCompare::Compare_Undefined)
{
if (left->type() == MIRType_Object) {
MOZ_ASSERT(comp->operandMightEmulateUndefined(),
"MCompare::tryFold should have folded this away");
return define(new LEmulatesUndefined(useRegister(left)), comp);
}
LDefinition tmp, tmpToUnbox;
if (comp->operandMightEmulateUndefined()) {
tmp = temp();
tmpToUnbox = tempToUnbox();
} else {
tmp = LDefinition::BogusTemp();
tmpToUnbox = LDefinition::BogusTemp();
}
LIsNullOrLikeUndefined *lir = new LIsNullOrLikeUndefined(tmp, tmpToUnbox);
if (!useBox(lir, LIsNullOrLikeUndefined::Value, left))
return false;
return define(lir, comp);
}
// Compare booleans.
if (comp->compareType() == MCompare::Compare_Boolean) {
JS_ASSERT(left->type() == MIRType_Value);
JS_ASSERT(right->type() == MIRType_Boolean);
LCompareB *lir = new LCompareB(useRegisterOrConstant(right));
if (!useBox(lir, LCompareB::Lhs, left))
return false;
return define(lir, comp);
}
// Compare Int32 or Object pointers.
if (comp->compareType() == MCompare::Compare_Int32 ||
comp->compareType() == MCompare::Compare_UInt32 ||
comp->compareType() == MCompare::Compare_Object)
{
JSOp op = ReorderComparison(comp->jsop(), &left, &right);
LAllocation lhs = useRegister(left);
LAllocation rhs;
if (comp->compareType() == MCompare::Compare_Int32 ||
comp->compareType() == MCompare::Compare_UInt32)
{
rhs = useAnyOrConstant(right);
} else {
rhs = useRegister(right);
}
return define(new LCompare(op, lhs, rhs), comp);
}
// Compare doubles.
if (comp->isDoubleComparison())
return define(new LCompareD(useRegister(left), useRegister(right)), comp);
// Compare values.
if (comp->compareType() == MCompare::Compare_Value) {
LCompareV *lir = new LCompareV();
if (!useBoxAtStart(lir, LCompareV::LhsInput, left))
return false;
if (!useBoxAtStart(lir, LCompareV::RhsInput, right))
return false;
return define(lir, comp);
}
JS_NOT_REACHED("Unrecognized compare type.");
return false;
}
static void
ReorderCommutative(MDefinition **lhsp, MDefinition **rhsp)
{
MDefinition *lhs = *lhsp;
MDefinition *rhs = *rhsp;
// Ensure that if there is a constant, then it is in rhs.
// In addition, since clobbering binary operations clobber the left
// operand, prefer a non-constant lhs operand with no further uses.
if (rhs->isConstant())
return;
if (lhs->isConstant() ||
(rhs->defUseCount() == 1 && lhs->defUseCount() > 1))
{
*rhsp = lhs;
*lhsp = rhs;
}
}
bool
LIRGenerator::lowerBitOp(JSOp op, MInstruction *ins)
{
MDefinition *lhs = ins->getOperand(0);
MDefinition *rhs = ins->getOperand(1);
if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
ReorderCommutative(&lhs, &rhs);
return lowerForALU(new LBitOpI(op), ins, lhs, rhs);
}
LBitOpV *lir = new LBitOpV(op);
if (!useBoxAtStart(lir, LBitOpV::LhsInput, lhs))
return false;
if (!useBoxAtStart(lir, LBitOpV::RhsInput, rhs))
return false;
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitTypeOf(MTypeOf *ins)
{
MDefinition *opd = ins->input();
JS_ASSERT(opd->type() == MIRType_Value);
LTypeOfV *lir = new LTypeOfV();
if (!useBox(lir, LTypeOfV::Input, opd))
return false;
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitToId(MToId *ins)
{
LToIdV *lir = new LToIdV(tempFloat());
if (!useBox(lir, LToIdV::Object, ins->lhs()))
return false;
if (!useBox(lir, LToIdV::Index, ins->rhs()))
return false;
return defineBox(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitBitNot(MBitNot *ins)
{
MDefinition *input = ins->getOperand(0);
if (input->type() == MIRType_Int32)
return lowerForALU(new LBitNotI(), ins, input);
LBitNotV *lir = new LBitNotV;
if (!useBoxAtStart(lir, LBitNotV::Input, input))
return false;
if (!defineReturn(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitBitAnd(MBitAnd *ins)
{
return lowerBitOp(JSOP_BITAND, ins);
}
bool
LIRGenerator::visitBitOr(MBitOr *ins)
{
return lowerBitOp(JSOP_BITOR, ins);
}
bool
LIRGenerator::visitBitXor(MBitXor *ins)
{
return lowerBitOp(JSOP_BITXOR, ins);
}
bool
LIRGenerator::lowerShiftOp(JSOp op, MShiftInstruction *ins)
{
MDefinition *lhs = ins->getOperand(0);
MDefinition *rhs = ins->getOperand(1);
if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
if (ins->type() == MIRType_Double) {
JS_ASSERT(op == JSOP_URSH);
return lowerUrshD(ins->toUrsh());
}
LShiftI *lir = new LShiftI(op);
if (op == JSOP_URSH) {
if (ins->toUrsh()->fallible() && !assignSnapshot(lir))
return false;
}
return lowerForShift(lir, ins, lhs, rhs);
}
JS_ASSERT(ins->specialization() == MIRType_None);
if (op == JSOP_URSH) {
// Result is either int32 or double so we have to use BinaryV.
return lowerBinaryV(JSOP_URSH, ins);
}
LBitOpV *lir = new LBitOpV(op);
if (!useBoxAtStart(lir, LBitOpV::LhsInput, lhs))
return false;
if (!useBoxAtStart(lir, LBitOpV::RhsInput, rhs))
return false;
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitLsh(MLsh *ins)
{
return lowerShiftOp(JSOP_LSH, ins);
}
bool
LIRGenerator::visitRsh(MRsh *ins)
{
return lowerShiftOp(JSOP_RSH, ins);
}
bool
LIRGenerator::visitUrsh(MUrsh *ins)
{
return lowerShiftOp(JSOP_URSH, ins);
}
bool
LIRGenerator::visitFloor(MFloor *ins)
{
JS_ASSERT(ins->num()->type() == MIRType_Double);
LFloor *lir = new LFloor(useRegister(ins->num()));
if (!assignSnapshot(lir))
return false;
return define(lir, ins);
}
bool
LIRGenerator::visitRound(MRound *ins)
{
JS_ASSERT(ins->num()->type() == MIRType_Double);
LRound *lir = new LRound(useRegister(ins->num()), tempFloat());
if (!assignSnapshot(lir))
return false;
return define(lir, ins);
}
bool
LIRGenerator::visitMinMax(MMinMax *ins)
{
MDefinition *first = ins->getOperand(0);
MDefinition *second = ins->getOperand(1);
ReorderCommutative(&first, &second);
if (ins->specialization() == MIRType_Int32) {
LMinMaxI *lir = new LMinMaxI(useRegisterAtStart(first), useRegisterOrConstant(second));
return defineReuseInput(lir, ins, 0);
}
LMinMaxD *lir = new LMinMaxD(useRegisterAtStart(first), useRegister(second));
return defineReuseInput(lir, ins, 0);
}
bool
LIRGenerator::visitAbs(MAbs *ins)
{
MDefinition *num = ins->num();
if (num->type() == MIRType_Int32) {
LAbsI *lir = new LAbsI(useRegisterAtStart(num));
// needed to handle abs(INT32_MIN)
if (ins->fallible() && !assignSnapshot(lir))
return false;
return defineReuseInput(lir, ins, 0);
}
JS_ASSERT(num->type() == MIRType_Double);
LAbsD *lir = new LAbsD(useRegisterAtStart(num));
return defineReuseInput(lir, ins, 0);
}
bool
LIRGenerator::visitSqrt(MSqrt *ins)
{
MDefinition *num = ins->num();
JS_ASSERT(num->type() == MIRType_Double);
LSqrtD *lir = new LSqrtD(useRegisterAtStart(num));
return defineReuseInput(lir, ins, 0);
}
bool
LIRGenerator::visitAtan2(MAtan2 *ins)
{
MDefinition *y = ins->y();
JS_ASSERT(y->type() == MIRType_Double);
MDefinition *x = ins->x();
JS_ASSERT(x->type() == MIRType_Double);
LAtan2D *lir = new LAtan2D(useRegisterAtStart(y), useRegisterAtStart(x), tempFixed(CallTempReg0));
return defineReturn(lir, ins);
}
bool
LIRGenerator::visitPow(MPow *ins)
{
MDefinition *input = ins->input();
JS_ASSERT(input->type() == MIRType_Double);
MDefinition *power = ins->power();
JS_ASSERT(power->type() == MIRType_Int32 || power->type() == MIRType_Double);
if (power->type() == MIRType_Int32) {
// Note: useRegisterAtStart here is safe, the temp is a GP register so
// it will never get the same register.
LPowI *lir = new LPowI(useRegisterAtStart(input), useFixed(power, CallTempReg1),
tempFixed(CallTempReg0));
return defineReturn(lir, ins);
}
LPowD *lir = new LPowD(useRegisterAtStart(input), useRegisterAtStart(power),
tempFixed(CallTempReg0));
return defineReturn(lir, ins);
}
bool
LIRGenerator::visitRandom(MRandom *ins)
{
LRandom *lir = new LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
return defineReturn(lir, ins);
}
bool
LIRGenerator::visitMathFunction(MMathFunction *ins)
{
JS_ASSERT(ins->type() == MIRType_Double);
JS_ASSERT(ins->input()->type() == MIRType_Double);
// Note: useRegisterAtStart is safe here, the temp is not a FP register.
LMathFunctionD *lir = new LMathFunctionD(useRegisterAtStart(ins->input()),
tempFixed(CallTempReg0));
return defineReturn(lir, ins);
}
// Try to mark an add or sub instruction as able to recover its input when
// bailing out.
template <typename S, typename T>
static void
MaybeSetRecoversInput(S *mir, T *lir)
{
JS_ASSERT(lir->mirRaw() == mir);
if (!mir->fallible())
return;
if (lir->output()->policy() != LDefinition::MUST_REUSE_INPUT)
return;
// The original operands to an add or sub can't be recovered if they both
// use the same register.
if (lir->lhs()->isUse() && lir->rhs()->isUse() &&
lir->lhs()->toUse()->virtualRegister() == lir->rhs()->toUse()->virtualRegister())
{
return;
}
// Add instructions that are on two different values can recover
// the input they clobbered via MUST_REUSE_INPUT. Thus, a copy
// of that input does not need to be kept alive in the snapshot
// for the instruction.
lir->setRecoversInput();
const LUse *input = lir->getOperand(lir->output()->getReusedInput())->toUse();
lir->snapshot()->rewriteRecoveredInput(*input);
}
bool
LIRGenerator::visitAdd(MAdd *ins)
{
MDefinition *lhs = ins->getOperand(0);
MDefinition *rhs = ins->getOperand(1);
JS_ASSERT(lhs->type() == rhs->type());
if (ins->specialization() == MIRType_Int32) {
JS_ASSERT(lhs->type() == MIRType_Int32);
ReorderCommutative(&lhs, &rhs);
LAddI *lir = new LAddI;
if (ins->fallible() && !assignSnapshot(lir))
return false;
if (!lowerForALU(lir, ins, lhs, rhs))
return false;
MaybeSetRecoversInput(ins, lir);
return true;
}
if (ins->specialization() == MIRType_Double) {
JS_ASSERT(lhs->type() == MIRType_Double);
ReorderCommutative(&lhs, &rhs);
return lowerForFPU(new LMathD(JSOP_ADD), ins, lhs, rhs);
}
return lowerBinaryV(JSOP_ADD, ins);
}
bool
LIRGenerator::visitSub(MSub *ins)
{
MDefinition *lhs = ins->lhs();
MDefinition *rhs = ins->rhs();
JS_ASSERT(lhs->type() == rhs->type());
if (ins->specialization() == MIRType_Int32) {
JS_ASSERT(lhs->type() == MIRType_Int32);
LSubI *lir = new LSubI;
if (ins->fallible() && !assignSnapshot(lir))
return false;
if (!lowerForALU(lir, ins, lhs, rhs))
return false;
MaybeSetRecoversInput(ins, lir);
return true;
}
if (ins->specialization() == MIRType_Double) {
JS_ASSERT(lhs->type() == MIRType_Double);
return lowerForFPU(new LMathD(JSOP_SUB), ins, lhs, rhs);
}
return lowerBinaryV(JSOP_SUB, ins);
}
bool
LIRGenerator::visitMul(MMul *ins)
{
MDefinition *lhs = ins->lhs();
MDefinition *rhs = ins->rhs();
JS_ASSERT(lhs->type() == rhs->type());
if (ins->specialization() == MIRType_Int32) {
JS_ASSERT(lhs->type() == MIRType_Int32);
ReorderCommutative(&lhs, &rhs);
return lowerMulI(ins, lhs, rhs);
}
if (ins->specialization() == MIRType_Double) {
JS_ASSERT(lhs->type() == MIRType_Double);
ReorderCommutative(&lhs, &rhs);
// If our LHS is a constant -1.0, we can optimize to an LNegD.
if (lhs->isConstant() && lhs->toConstant()->value() == DoubleValue(-1.0))
return defineReuseInput(new LNegD(useRegisterAtStart(rhs)), ins, 0);
// We can do the same for the RHS, if we just swap the operands.
if (rhs->isConstant() && rhs->toConstant()->value() == DoubleValue(-1.0))
return defineReuseInput(new LNegD(useRegisterAtStart(lhs)), ins, 0);
return lowerForFPU(new LMathD(JSOP_MUL), ins, lhs, rhs);
}
return lowerBinaryV(JSOP_MUL, ins);
}
bool
LIRGenerator::visitDiv(MDiv *ins)
{
MDefinition *lhs = ins->lhs();
MDefinition *rhs = ins->rhs();
JS_ASSERT(lhs->type() == rhs->type());
if (ins->specialization() == MIRType_Int32) {
JS_ASSERT(lhs->type() == MIRType_Int32);
return lowerDivI(ins);
}
if (ins->specialization() == MIRType_Double) {
JS_ASSERT(lhs->type() == MIRType_Double);
return lowerForFPU(new LMathD(JSOP_DIV), ins, lhs, rhs);
}
return lowerBinaryV(JSOP_DIV, ins);
}
bool
LIRGenerator::visitMod(MMod *ins)
{
JS_ASSERT(ins->lhs()->type() == ins->rhs()->type());
if (ins->specialization() == MIRType_Int32) {
JS_ASSERT(ins->type() == MIRType_Int32);
JS_ASSERT(ins->lhs()->type() == MIRType_Int32);
return lowerModI(ins);
}
if (ins->specialization() == MIRType_Double) {
JS_ASSERT(ins->type() == MIRType_Double);
JS_ASSERT(ins->lhs()->type() == MIRType_Double);
JS_ASSERT(ins->rhs()->type() == MIRType_Double);
// Note: useRegisterAtStart is safe here, the temp is not a FP register.
LModD *lir = new LModD(useRegisterAtStart(ins->lhs()), useRegisterAtStart(ins->rhs()),
tempFixed(CallTempReg0));
return defineReturn(lir, ins);
}
return lowerBinaryV(JSOP_MOD, ins);
}
bool
LIRGenerator::lowerBinaryV(JSOp op, MBinaryInstruction *ins)
{
MDefinition *lhs = ins->getOperand(0);
MDefinition *rhs = ins->getOperand(1);
JS_ASSERT(lhs->type() == MIRType_Value);
JS_ASSERT(rhs->type() == MIRType_Value);
LBinaryV *lir = new LBinaryV(op);
if (!useBoxAtStart(lir, LBinaryV::LhsInput, lhs))
return false;
if (!useBoxAtStart(lir, LBinaryV::RhsInput, rhs))
return false;
if (!defineReturn(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitConcat(MConcat *ins)
{
MDefinition *lhs = ins->getOperand(0);
MDefinition *rhs = ins->getOperand(1);
JS_ASSERT(lhs->type() == MIRType_String);
JS_ASSERT(rhs->type() == MIRType_String);
JS_ASSERT(ins->type() == MIRType_String);
LConcat *lir = new LConcat(useFixed(lhs, CallTempReg0),
useFixed(rhs, CallTempReg1),
tempFixed(CallTempReg2),
tempFixed(CallTempReg3),
tempFixed(CallTempReg4),
tempFixed(CallTempReg5));
if (!defineFixed(lir, ins, LAllocation(AnyRegister(CallTempReg6))))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCharCodeAt(MCharCodeAt *ins)
{
MDefinition *str = ins->getOperand(0);
MDefinition *idx = ins->getOperand(1);
JS_ASSERT(str->type() == MIRType_String);
JS_ASSERT(idx->type() == MIRType_Int32);
LCharCodeAt *lir = new LCharCodeAt(useRegister(str), useRegister(idx));
if (!define(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitFromCharCode(MFromCharCode *ins)
{
MDefinition *code = ins->getOperand(0);
JS_ASSERT(code->type() == MIRType_Int32);
LFromCharCode *lir = new LFromCharCode(useRegister(code));
if (!define(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitStart(MStart *start)
{
// Create a snapshot that captures the initial state of the function.
LStart *lir = new LStart;
if (!assignSnapshot(lir))
return false;
if (start->startType() == MStart::StartType_Default)
lirGraph_.setEntrySnapshot(lir->snapshot());
return add(lir);
}
bool
LIRGenerator::visitNop(MNop *nop)
{
return true;
}
bool
LIRGenerator::visitOsrEntry(MOsrEntry *entry)
{
LOsrEntry *lir = new LOsrEntry;
return defineFixed(lir, entry, LAllocation(AnyRegister(OsrFrameReg)));
}
bool
LIRGenerator::visitOsrValue(MOsrValue *value)
{
LOsrValue *lir = new LOsrValue(useRegister(value->entry()));
return defineBox(lir, value);
}
bool
LIRGenerator::visitOsrScopeChain(MOsrScopeChain *object)
{
LOsrScopeChain *lir = new LOsrScopeChain(useRegister(object->entry()));
return define(lir, object);
}
bool
LIRGenerator::visitToDouble(MToDouble *convert)
{
MDefinition *opd = convert->input();
mozilla::DebugOnly<MToDouble::ConversionKind> conversion = convert->conversion();
switch (opd->type()) {
case MIRType_Value:
{
LValueToDouble *lir = new LValueToDouble();
if (!useBox(lir, LValueToDouble::Input, opd))
return false;
return assignSnapshot(lir) && define(lir, convert);
}
case MIRType_Null:
JS_ASSERT(conversion != MToDouble::NumbersOnly && conversion != MToDouble::NonNullNonStringPrimitives);
return lowerConstantDouble(0, convert);
case MIRType_Undefined:
JS_ASSERT(conversion != MToDouble::NumbersOnly);
return lowerConstantDouble(js_NaN, convert);
case MIRType_Boolean:
JS_ASSERT(conversion != MToDouble::NumbersOnly);
/* FALLTHROUGH */
case MIRType_Int32:
{
LInt32ToDouble *lir = new LInt32ToDouble(useRegister(opd));
return define(lir, convert);
}
case MIRType_Double:
return redefine(convert, opd);
default:
// Objects might be effectful.
// Strings are complicated - we don't handle them yet.
JS_NOT_REACHED("unexpected type");
return false;
}
}
bool
LIRGenerator::visitToInt32(MToInt32 *convert)
{
MDefinition *opd = convert->input();
switch (opd->type()) {
case MIRType_Value:
{
LValueToInt32 *lir = new LValueToInt32(tempFloat(), LValueToInt32::NORMAL);
if (!useBox(lir, LValueToInt32::Input, opd))
return false;
return assignSnapshot(lir) && define(lir, convert);
}
case MIRType_Null:
return define(new LInteger(0), convert);
case MIRType_Int32:
case MIRType_Boolean:
return redefine(convert, opd);
case MIRType_Double:
{
LDoubleToInt32 *lir = new LDoubleToInt32(useRegister(opd));
return assignSnapshot(lir) && define(lir, convert);
}
case MIRType_String:
// Strings are complicated - we don't handle them yet.
IonSpew(IonSpew_Abort, "String to Int32 not supported yet.");
return false;
case MIRType_Object:
// Objects might be effectful.
IonSpew(IonSpew_Abort, "Object to Int32 not supported yet.");
return false;
case MIRType_Undefined:
IonSpew(IonSpew_Abort, "Undefined coerces to NaN, not int32_t.");
return false;
default:
JS_NOT_REACHED("unexpected type");
return false;
}
}
bool
LIRGenerator::visitTruncateToInt32(MTruncateToInt32 *truncate)
{
MDefinition *opd = truncate->input();
switch (opd->type()) {
case MIRType_Value:
{
LValueToInt32 *lir = new LValueToInt32(tempFloat(), LValueToInt32::TRUNCATE);
if (!useBox(lir, LValueToInt32::Input, opd))
return false;
return assignSnapshot(lir) && define(lir, truncate);
}
case MIRType_Null:
case MIRType_Undefined:
return define(new LInteger(0), truncate);
case MIRType_Int32:
case MIRType_Boolean:
return redefine(truncate, opd);
case MIRType_Double:
return lowerTruncateDToInt32(truncate);
default:
// Objects might be effectful.
// Strings are complicated - we don't handle them yet.
JS_NOT_REACHED("unexpected type");
return false;
}
}
bool
LIRGenerator::visitToString(MToString *ins)
{
MDefinition *opd = ins->input();
switch (opd->type()) {
case MIRType_Double:
case MIRType_Null:
case MIRType_Undefined:
case MIRType_Boolean:
JS_NOT_REACHED("NYI: Lower MToString");
return false;
case MIRType_Int32: {
LIntToString *lir = new LIntToString(useRegister(opd));
if (!define(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
default:
// Objects might be effectful. (see ToPrimitive)
JS_NOT_REACHED("unexpected type");
return false;
}
}
bool
LIRGenerator::visitRegExp(MRegExp *ins)
{
LRegExp *lir = new LRegExp();
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitRegExpTest(MRegExpTest *ins)
{
JS_ASSERT(ins->regexp()->type() == MIRType_Object);
JS_ASSERT(ins->string()->type() == MIRType_String);
LRegExpTest *lir = new LRegExpTest(useRegisterAtStart(ins->regexp()),
useRegisterAtStart(ins->string()));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitLambda(MLambda *ins)
{
if (ins->fun()->hasSingletonType() || types::UseNewTypeForClone(ins->fun())) {
// If the function has a singleton type, this instruction will only be
// executed once so we don't bother inlining it.
//
// If UseNewTypeForClone is true, we will assign a singleton type to
// the clone and we have to clone the script, we can't do that inline.
LLambdaForSingleton *lir = new LLambdaForSingleton(useRegisterAtStart(ins->scopeChain()));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
LLambda *lir = new LLambda(useRegister(ins->scopeChain()));
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitParLambda(MParLambda *ins)
{
JS_ASSERT(!ins->fun()->hasSingletonType());
JS_ASSERT(!types::UseNewTypeForClone(ins->fun()));
LParLambda *lir = new LParLambda(useRegister(ins->parSlice()),
useRegister(ins->scopeChain()),
temp(), temp());
return define(lir, ins);
}
bool
LIRGenerator::visitImplicitThis(MImplicitThis *ins)
{
JS_ASSERT(ins->callee()->type() == MIRType_Object);
LImplicitThis *lir = new LImplicitThis(useRegister(ins->callee()));
return assignSnapshot(lir) && defineBox(lir, ins);
}
bool
LIRGenerator::visitSlots(MSlots *ins)
{
return define(new LSlots(useRegisterAtStart(ins->object())), ins);
}
bool
LIRGenerator::visitElements(MElements *ins)
{
return define(new LElements(useRegisterAtStart(ins->object())), ins);
}
bool
LIRGenerator::visitConstantElements(MConstantElements *ins)
{
return define(new LPointer(ins->value(), LPointer::NON_GC_THING), ins);
}
bool
LIRGenerator::visitConvertElementsToDoubles(MConvertElementsToDoubles *ins)
{
LInstruction *check = new LConvertElementsToDoubles(useRegister(ins->elements()));
return add(check, ins) && assignSafepoint(check, ins);
}
bool
LIRGenerator::visitLoadSlot(MLoadSlot *ins)
{
switch (ins->type()) {
case MIRType_Value:
return defineBox(new LLoadSlotV(useRegister(ins->slots())), ins);
case MIRType_Undefined:
case MIRType_Null:
JS_NOT_REACHED("typed load must have a payload");
return false;
default:
return define(new LLoadSlotT(useRegister(ins->slots())), ins);
}
}
bool
LIRGenerator::visitFunctionEnvironment(MFunctionEnvironment *ins)
{
return define(new LFunctionEnvironment(useRegisterAtStart(ins->function())), ins);
}
bool
LIRGenerator::visitParSlice(MParSlice *ins)
{
LParSlice *lir = new LParSlice(tempFixed(CallTempReg0));
return defineReturn(lir, ins);
}
bool
LIRGenerator::visitParWriteGuard(MParWriteGuard *ins)
{
LParWriteGuard *lir = new LParWriteGuard(useFixed(ins->parSlice(), CallTempReg0),
useFixed(ins->object(), CallTempReg1),
tempFixed(CallTempReg2));
lir->setMir(ins);
return add(lir, ins);
}
bool
LIRGenerator::visitParCheckInterrupt(MParCheckInterrupt *ins)
{
LParCheckInterrupt *lir = new LParCheckInterrupt(
useRegister(ins->parSlice()),
temp());
if (!add(lir, ins))
return false;
if (!assignSafepoint(lir, ins))
return false;
return true;
}
bool
LIRGenerator::visitParDump(MParDump *ins)
{
LParDump *lir = new LParDump();
useBoxFixed(lir, LParDump::Value, ins->value(), CallTempReg0, CallTempReg1);
return add(lir);
}
bool
LIRGenerator::visitParNew(MParNew *ins)
{
LParNew *lir = new LParNew(useRegister(ins->parSlice()),
temp(), temp());
return define(lir, ins);
}
bool
LIRGenerator::visitParNewDenseArray(MParNewDenseArray *ins)
{
LParNewDenseArray *lir = new LParNewDenseArray(
useFixed(ins->parSlice(), CallTempReg0),
useFixed(ins->length(), CallTempReg1),
tempFixed(CallTempReg2),
tempFixed(CallTempReg3),
tempFixed(CallTempReg4));
return defineReturn(lir, ins);
}
bool
LIRGenerator::visitStoreSlot(MStoreSlot *ins)
{
LInstruction *lir;
switch (ins->value()->type()) {
case MIRType_Value:
lir = new LStoreSlotV(useRegister(ins->slots()));
if (!useBox(lir, LStoreSlotV::Value, ins->value()))
return false;
return add(lir, ins);
case MIRType_Double:
return add(new LStoreSlotT(useRegister(ins->slots()), useRegister(ins->value())), ins);
default:
return add(new LStoreSlotT(useRegister(ins->slots()), useRegisterOrConstant(ins->value())),
ins);
}
return true;
}
bool
LIRGenerator::visitTypeBarrier(MTypeBarrier *ins)
{
// Requesting a non-GC pointer is safe here since we never re-enter C++
// from inside a type barrier test.
const types::StackTypeSet *types = ins->resultTypeSet();
bool needTemp = !types->unknownObject() && types->getObjectCount() > 0;
LDefinition tmp = needTemp ? temp() : tempToUnbox();
LTypeBarrier *barrier = new LTypeBarrier(tmp);
if (!useBox(barrier, LTypeBarrier::Input, ins->input()))
return false;
if (!assignSnapshot(barrier, ins->bailoutKind()))
return false;
return redefine(ins, ins->input()) && add(barrier, ins);
}
bool
LIRGenerator::visitMonitorTypes(MMonitorTypes *ins)
{
// Requesting a non-GC pointer is safe here since we never re-enter C++
// from inside a type check.
const types::StackTypeSet *types = ins->typeSet();
bool needTemp = !types->unknownObject() && types->getObjectCount() > 0;
LDefinition tmp = needTemp ? temp() : tempToUnbox();
LMonitorTypes *lir = new LMonitorTypes(tmp);
if (!useBox(lir, LMonitorTypes::Input, ins->input()))
return false;
return assignSnapshot(lir, Bailout_Normal) && add(lir, ins);
}
bool
LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier *ins)
{
#ifdef JSGC_GENERATIONAL
switch (ins->value()->type()) {
case MIRType_Object: {
LPostWriteBarrierO *lir = new LPostWriteBarrierO(useRegisterOrConstant(ins->object()),
useRegister(ins->value()));
return add(lir, ins) && assignSafepoint(lir, ins);
}
case MIRType_Value: {
LPostWriteBarrierV *lir =
new LPostWriteBarrierV(useRegisterOrConstant(ins->object()), tempToUnbox());
if (!useBox(lir, LPostWriteBarrierV::Input, ins->value()))
return false;
return add(lir, ins) && assignSafepoint(lir, ins);
}
default:
// Currently, only objects can be in the nursery. Other instruction
// types cannot hold nursery pointers.
return true;
}
#endif // JSGC_GENERATIONAL
return true;
}
bool
LIRGenerator::visitArrayLength(MArrayLength *ins)
{
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
return define(new LArrayLength(useRegisterAtStart(ins->elements())), ins);
}
bool
LIRGenerator::visitTypedArrayLength(MTypedArrayLength *ins)
{
JS_ASSERT(ins->object()->type() == MIRType_Object);
return define(new LTypedArrayLength(useRegisterAtStart(ins->object())), ins);
}
bool
LIRGenerator::visitTypedArrayElements(MTypedArrayElements *ins)
{
JS_ASSERT(ins->type() == MIRType_Elements);
return define(new LTypedArrayElements(useRegisterAtStart(ins->object())), ins);
}
bool
LIRGenerator::visitInitializedLength(MInitializedLength *ins)
{
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
return define(new LInitializedLength(useRegisterAtStart(ins->elements())), ins);
}
bool
LIRGenerator::visitSetInitializedLength(MSetInitializedLength *ins)
{
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
JS_ASSERT(ins->index()->type() == MIRType_Int32);
JS_ASSERT(ins->index()->isConstant());
return add(new LSetInitializedLength(useRegister(ins->elements()),
useRegisterOrConstant(ins->index())), ins);
}
bool
LIRGenerator::visitNot(MNot *ins)
{
MDefinition *op = ins->operand();
// String is converted to length of string in the type analysis phase (see
// TestPolicy).
JS_ASSERT(op->type() != MIRType_String);
// - boolean: x xor 1
// - int32: LCompare(x, 0)
// - double: LCompare(x, 0)
// - null or undefined: true
// - object: false if it never emulates undefined, else LNotO(x)
switch (op->type()) {
case MIRType_Boolean: {
MConstant *cons = MConstant::New(Int32Value(1));
ins->block()->insertBefore(ins, cons);
return lowerForALU(new LBitOpI(JSOP_BITXOR), ins, op, cons);
}
case MIRType_Int32: {
return define(new LNotI(useRegisterAtStart(op)), ins);
}
case MIRType_Double:
return define(new LNotD(useRegister(op)), ins);
case MIRType_Undefined:
case MIRType_Null:
return define(new LInteger(1), ins);
case MIRType_Object: {
// Objects that don't emulate undefined can be constant-folded.
if (!ins->operandMightEmulateUndefined())
return define(new LInteger(0), ins);
// All others require further work.
return define(new LNotO(useRegister(op)), ins);
}
case MIRType_Value: {
LDefinition temp0, temp1;
if (ins->operandMightEmulateUndefined()) {
temp0 = temp();
temp1 = temp();
} else {
temp0 = LDefinition::BogusTemp();
temp1 = LDefinition::BogusTemp();
}
LNotV *lir = new LNotV(tempFloat(), temp0, temp1);
if (!useBox(lir, LNotV::Input, op))
return false;
return define(lir, ins);
}
default:
JS_NOT_REACHED("Unexpected MIRType.");
return false;
}
}
bool
LIRGenerator::visitBoundsCheck(MBoundsCheck *ins)
{
LInstruction *check;
if (ins->minimum() || ins->maximum()) {
check = new LBoundsCheckRange(useRegisterOrConstant(ins->index()),
useAny(ins->length()),
temp());
} else {
check = new LBoundsCheck(useRegisterOrConstant(ins->index()),
useAnyOrConstant(ins->length()));
}
return assignSnapshot(check, Bailout_BoundsCheck) && add(check, ins);
}
bool
LIRGenerator::visitBoundsCheckLower(MBoundsCheckLower *ins)
{
if (!ins->fallible())
return true;
LInstruction *check = new LBoundsCheckLower(useRegister(ins->index()));
return assignSnapshot(check, Bailout_BoundsCheck) && add(check, ins);
}
bool
LIRGenerator::visitInArray(MInArray *ins)
{
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
JS_ASSERT(ins->index()->type() == MIRType_Int32);
JS_ASSERT(ins->initLength()->type() == MIRType_Int32);
JS_ASSERT(ins->object()->type() == MIRType_Object);
JS_ASSERT(ins->type() == MIRType_Boolean);
LAllocation object;
if (ins->needsNegativeIntCheck())
object = useRegister(ins->object());
else
object = LConstantIndex::Bogus();
LInArray *lir = new LInArray(useRegister(ins->elements()),
useRegisterOrConstant(ins->index()),
useRegister(ins->initLength()),
object);
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitLoadElement(MLoadElement *ins)
{
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
JS_ASSERT(ins->index()->type() == MIRType_Int32);
switch (ins->type()) {
case MIRType_Value:
{
LLoadElementV *lir = new LLoadElementV(useRegister(ins->elements()),
useRegisterOrConstant(ins->index()));
if (ins->fallible() && !assignSnapshot(lir))
return false;
return defineBox(lir, ins);
}
case MIRType_Undefined:
case MIRType_Null:
JS_NOT_REACHED("typed load must have a payload");
return false;
default:
{
LLoadElementT *lir = new LLoadElementT(useRegister(ins->elements()),
useRegisterOrConstant(ins->index()));
if (ins->fallible() && !assignSnapshot(lir))
return false;
return define(lir, ins);
}
}
}
bool
LIRGenerator::visitLoadElementHole(MLoadElementHole *ins)
{
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
JS_ASSERT(ins->index()->type() == MIRType_Int32);
JS_ASSERT(ins->initLength()->type() == MIRType_Int32);
JS_ASSERT(ins->type() == MIRType_Value);
LLoadElementHole *lir = new LLoadElementHole(useRegister(ins->elements()),
useRegisterOrConstant(ins->index()),
useRegister(ins->initLength()));
if (ins->needsNegativeIntCheck() && !assignSnapshot(lir))
return false;
return defineBox(lir, ins);
}
bool
LIRGenerator::visitStoreElement(MStoreElement *ins)
{
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
JS_ASSERT(ins->index()->type() == MIRType_Int32);
const LUse elements = useRegister(ins->elements());
const LAllocation index = useRegisterOrConstant(ins->index());
switch (ins->value()->type()) {
case MIRType_Value:
{
LInstruction *lir = new LStoreElementV(elements, index);
if (ins->fallible() && !assignSnapshot(lir))
return false;
if (!useBox(lir, LStoreElementV::Value, ins->value()))
return false;
return add(lir, ins);
}
default:
{
const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
LInstruction *lir = new LStoreElementT(elements, index, value);
if (ins->fallible() && !assignSnapshot(lir))
return false;
return add(lir, ins);
}
}
}
bool
LIRGenerator::visitStoreElementHole(MStoreElementHole *ins)
{
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
JS_ASSERT(ins->index()->type() == MIRType_Int32);
const LUse object = useRegister(ins->object());
const LUse elements = useRegister(ins->elements());
const LAllocation index = useRegisterOrConstant(ins->index());
LInstruction *lir;
switch (ins->value()->type()) {
case MIRType_Value:
lir = new LStoreElementHoleV(object, elements, index);
if (!useBox(lir, LStoreElementHoleV::Value, ins->value()))
return false;
break;
default:
{
const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
lir = new LStoreElementHoleT(object, elements, index, value);
break;
}
}
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitEffectiveAddress(MEffectiveAddress *ins)
{
return define(new LEffectiveAddress(useRegister(ins->base()), useRegister(ins->index())), ins);
}
bool
LIRGenerator::visitArrayPopShift(MArrayPopShift *ins)
{
LUse object = useRegister(ins->object());
switch (ins->type()) {
case MIRType_Value:
{
LArrayPopShiftV *lir = new LArrayPopShiftV(object, temp(), temp());
return defineBox(lir, ins) && assignSafepoint(lir, ins);
}
case MIRType_Undefined:
case MIRType_Null:
JS_NOT_REACHED("typed load must have a payload");
return false;
default:
{
LArrayPopShiftT *lir = new LArrayPopShiftT(object, temp(), temp());
return define(lir, ins) && assignSafepoint(lir, ins);
}
}
}
bool
LIRGenerator::visitArrayPush(MArrayPush *ins)
{
JS_ASSERT(ins->type() == MIRType_Int32);
LUse object = useRegister(ins->object());
switch (ins->value()->type()) {
case MIRType_Value:
{
LArrayPushV *lir = new LArrayPushV(object, temp());
if (!useBox(lir, LArrayPushV::Value, ins->value()))
return false;
return define(lir, ins) && assignSafepoint(lir, ins);
}
default:
{
const LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
LArrayPushT *lir = new LArrayPushT(object, value, temp());
return define(lir, ins) && assignSafepoint(lir, ins);
}
}
}
bool
LIRGenerator::visitArrayConcat(MArrayConcat *ins)
{
JS_ASSERT(ins->type() == MIRType_Object);
JS_ASSERT(ins->lhs()->type() == MIRType_Object);
JS_ASSERT(ins->rhs()->type() == MIRType_Object);
LArrayConcat *lir = new LArrayConcat(useFixed(ins->lhs(), CallTempReg1),
useFixed(ins->rhs(), CallTempReg2),
tempFixed(CallTempReg3),
tempFixed(CallTempReg4));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitLoadTypedArrayElement(MLoadTypedArrayElement *ins)
{
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
JS_ASSERT(ins->index()->type() == MIRType_Int32);
const LUse elements = useRegister(ins->elements());
const LAllocation index = useRegisterOrConstant(ins->index());
JS_ASSERT(IsNumberType(ins->type()));
// We need a temp register for Uint32Array with known double result.
LDefinition tempDef = LDefinition::BogusTemp();
if (ins->arrayType() == TypedArray::TYPE_UINT32 && ins->type() == MIRType_Double)
tempDef = temp();
LLoadTypedArrayElement *lir = new LLoadTypedArrayElement(elements, index, tempDef);
if (ins->fallible() && !assignSnapshot(lir))
return false;
return define(lir, ins);
}
bool
LIRGenerator::visitClampToUint8(MClampToUint8 *ins)
{
MDefinition *in = ins->input();
switch (in->type()) {
case MIRType_Boolean:
return redefine(ins, in);
case MIRType_Int32:
return define(new LClampIToUint8(useRegisterAtStart(in)), ins);
case MIRType_Double:
return define(new LClampDToUint8(useRegisterAtStart(in), tempCopy(in, 0)), ins);
case MIRType_Value:
{
LClampVToUint8 *lir = new LClampVToUint8(tempFloat());
if (!useBox(lir, LClampVToUint8::Input, in))
return false;
return assignSnapshot(lir) && define(lir, ins);
}
default:
JS_NOT_REACHED("unexpected type");
return false;
}
}
bool
LIRGenerator::visitLoadTypedArrayElementHole(MLoadTypedArrayElementHole *ins)
{
JS_ASSERT(ins->object()->type() == MIRType_Object);
JS_ASSERT(ins->index()->type() == MIRType_Int32);
JS_ASSERT(ins->type() == MIRType_Value);
const LUse object = useRegister(ins->object());
const LAllocation index = useRegisterOrConstant(ins->index());
LLoadTypedArrayElementHole *lir = new LLoadTypedArrayElementHole(object, index);
if (ins->fallible() && !assignSnapshot(lir))
return false;
return defineBox(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitLoadTypedArrayElementStatic(MLoadTypedArrayElementStatic *ins)
{
LLoadTypedArrayElementStatic *lir =
new LLoadTypedArrayElementStatic(useRegisterAtStart(ins->ptr()));
if (ins->fallible() && !assignSnapshot(lir))
return false;
return define(lir, ins);
}
bool
LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot *ins)
{
JS_ASSERT(ins->object()->type() == MIRType_Object);
if (ins->type() == MIRType_Value) {
LLoadFixedSlotV *lir = new LLoadFixedSlotV(useRegister(ins->object()));
return defineBox(lir, ins);
}
LLoadFixedSlotT *lir = new LLoadFixedSlotT(useRegister(ins->object()));
return define(lir, ins);
}
bool
LIRGenerator::visitStoreFixedSlot(MStoreFixedSlot *ins)
{
JS_ASSERT(ins->object()->type() == MIRType_Object);
if (ins->value()->type() == MIRType_Value) {
LStoreFixedSlotV *lir = new LStoreFixedSlotV(useRegister(ins->object()));
if (!useBox(lir, LStoreFixedSlotV::Value, ins->value()))
return false;
return add(lir, ins);
}
LStoreFixedSlotT *lir = new LStoreFixedSlotT(useRegister(ins->object()),
useRegisterOrConstant(ins->value()));
return add(lir, ins);
}
bool
LIRGenerator::visitGetNameCache(MGetNameCache *ins)
{
JS_ASSERT(ins->scopeObj()->type() == MIRType_Object);
LGetNameCache *lir = new LGetNameCache(useRegister(ins->scopeObj()));
if (!defineBox(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCallGetIntrinsicValue(MCallGetIntrinsicValue *ins)
{
LCallGetIntrinsicValue *lir = new LCallGetIntrinsicValue();
if (!defineReturn(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCallsiteCloneCache(MCallsiteCloneCache *ins)
{
JS_ASSERT(ins->callee()->type() == MIRType_Object);
LCallsiteCloneCache *lir = new LCallsiteCloneCache(useRegister(ins->callee()));
if (!define(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitGetPropertyCache(MGetPropertyCache *ins)
{
JS_ASSERT(ins->object()->type() == MIRType_Object);
if (ins->type() == MIRType_Value) {
LGetPropertyCacheV *lir = new LGetPropertyCacheV(useRegister(ins->object()));
if (!defineBox(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
#if defined(JS_CPU_MIPS)
LGetPropertyCacheT *lir = new LGetPropertyCacheT(useRegister(ins->object()),
tempForDispatchCache(ins->type()));
#else
LGetPropertyCacheT *lir = newLGetPropertyCacheT(ins);
#endif
if (!define(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitGetPropertyPolymorphic(MGetPropertyPolymorphic *ins)
{
JS_ASSERT(ins->obj()->type() == MIRType_Object);
if (ins->type() == MIRType_Value) {
LGetPropertyPolymorphicV *lir = new LGetPropertyPolymorphicV(useRegister(ins->obj()));
return assignSnapshot(lir) && defineBox(lir, ins);
}
LDefinition maybeTemp = (ins->type() == MIRType_Double) ? temp() : LDefinition::BogusTemp();
LGetPropertyPolymorphicT *lir = new LGetPropertyPolymorphicT(useRegister(ins->obj()), maybeTemp);
return assignSnapshot(lir, Bailout_CachedShapeGuard) && define(lir, ins);
}
bool
LIRGenerator::visitSetPropertyPolymorphic(MSetPropertyPolymorphic *ins)
{
JS_ASSERT(ins->obj()->type() == MIRType_Object);
if (ins->value()->type() == MIRType_Value) {
LSetPropertyPolymorphicV *lir = new LSetPropertyPolymorphicV(useRegister(ins->obj()), temp());
if (!useBox(lir, LSetPropertyPolymorphicV::Value, ins->value()))
return false;
return assignSnapshot(lir, Bailout_CachedShapeGuard) && add(lir, ins);
}
LAllocation value = useRegisterOrConstant(ins->value());
LSetPropertyPolymorphicT *lir =
new LSetPropertyPolymorphicT(useRegister(ins->obj()), value, ins->value()->type(), temp());
return assignSnapshot(lir) && add(lir, ins);
}
bool
LIRGenerator::visitGetElementCache(MGetElementCache *ins)
{
JS_ASSERT(ins->object()->type() == MIRType_Object);
if (ins->type() == MIRType_Value) {
JS_ASSERT(ins->index()->type() == MIRType_Value);
LGetElementCacheV *lir = new LGetElementCacheV(useRegister(ins->object()));
if (!useBox(lir, LGetElementCacheV::Index, ins->index()))
return false;
return defineBox(lir, ins) && assignSafepoint(lir, ins);
}
JS_ASSERT(ins->index()->type() == MIRType_Int32);
LGetElementCacheT *lir = new LGetElementCacheT(useRegister(ins->object()),
useRegister(ins->index()));
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitBindNameCache(MBindNameCache *ins)
{
JS_ASSERT(ins->scopeChain()->type() == MIRType_Object);
JS_ASSERT(ins->type() == MIRType_Object);
LBindNameCache *lir = new LBindNameCache(useRegister(ins->scopeChain()));
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitGuardClass(MGuardClass *ins)
{
LDefinition t = temp();
LGuardClass *guard = new LGuardClass(useRegister(ins->obj()), t);
return assignSnapshot(guard) && add(guard, ins);
}
bool
LIRGenerator::visitGuardObject(MGuardObject *ins)
{
// The type policy does all the work, so at this point the input
// is guaranteed to be an object.
JS_ASSERT(ins->input()->type() == MIRType_Object);
return redefine(ins, ins->input());
}
bool
LIRGenerator::visitGuardString(MGuardString *ins)
{
// The type policy does all the work, so at this point the input
// is guaranteed to be a string.
JS_ASSERT(ins->input()->type() == MIRType_String);
return redefine(ins, ins->input());
}
bool
LIRGenerator::visitCallGetProperty(MCallGetProperty *ins)
{
LCallGetProperty *lir = new LCallGetProperty();
if (!useBoxAtStart(lir, LCallGetProperty::Value, ins->value()))
return false;
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCallGetElement(MCallGetElement *ins)
{
JS_ASSERT(ins->lhs()->type() == MIRType_Value);
JS_ASSERT(ins->rhs()->type() == MIRType_Value);
LCallGetElement *lir = new LCallGetElement();
if (!useBoxAtStart(lir, LCallGetElement::LhsInput, ins->lhs()))
return false;
if (!useBoxAtStart(lir, LCallGetElement::RhsInput, ins->rhs()))
return false;
if (!defineReturn(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCallSetProperty(MCallSetProperty *ins)
{
LInstruction *lir = new LCallSetProperty(useRegisterAtStart(ins->obj()));
if (!useBoxAtStart(lir, LCallSetProperty::Value, ins->value()))
return false;
if (!add(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitDeleteProperty(MDeleteProperty *ins)
{
LCallDeleteProperty *lir = new LCallDeleteProperty();
if(!useBoxAtStart(lir, LCallDeleteProperty::Value, ins->value()))
return false;
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitSetPropertyCache(MSetPropertyCache *ins)
{
LUse obj = useRegisterAtStart(ins->obj());
LDefinition slots = tempCopy(ins->obj(), 0);
LInstruction *lir;
if (ins->value()->type() == MIRType_Value) {
lir = new LSetPropertyCacheV(obj, slots);
if (!useBox(lir, LSetPropertyCacheV::Value, ins->value()))
return false;
} else {
LAllocation value = useRegisterOrConstant(ins->value());
lir = new LSetPropertyCacheT(obj, slots, value, ins->value()->type());
}
if (!add(lir, ins))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitSetElementCache(MSetElementCache *ins)
{
JS_ASSERT(ins->object()->type() == MIRType_Object);
JS_ASSERT(ins->index()->type() == MIRType_Value);
LInstruction *lir;
if (ins->value()->type() == MIRType_Value) {
lir = new LSetElementCacheV(useRegister(ins->object()), tempToUnbox(), temp());
if (!useBox(lir, LSetElementCacheV::Index, ins->index()))
return false;
if (!useBox(lir, LSetElementCacheV::Value, ins->value()))
return false;
} else {
lir = new LSetElementCacheT(
useRegister(ins->object()),
useRegisterOrConstant(ins->value()),
tempToUnbox(), temp());
if (!useBox(lir, LSetElementCacheT::Index, ins->index()))
return false;
}
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCallSetElement(MCallSetElement *ins)
{
JS_ASSERT(ins->object()->type() == MIRType_Object);
JS_ASSERT(ins->index()->type() == MIRType_Value);
JS_ASSERT(ins->value()->type() == MIRType_Value);
LCallSetElement *lir = new LCallSetElement();
lir->setOperand(0, useRegisterAtStart(ins->object()));
if (!useBoxAtStart(lir, LCallSetElement::Index, ins->index()))
return false;
if (!useBoxAtStart(lir, LCallSetElement::Value, ins->value()))
return false;
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCallInitElementArray(MCallInitElementArray *ins)
{
LCallInitElementArray *lir = new LCallInitElementArray();
lir->setOperand(0, useRegisterAtStart(ins->object()));
if (!useBoxAtStart(lir, LCallInitElementArray::Value, ins->value()))
return false;
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitIteratorStart(MIteratorStart *ins)
{
// Call a stub if this is not a simple for-in loop.
if (ins->flags() != JSITER_ENUMERATE) {
LCallIteratorStart *lir = new LCallIteratorStart(useRegisterAtStart(ins->object()));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
LIteratorStart *lir = new LIteratorStart(useRegister(ins->object()), temp(), temp(), temp());
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitIteratorNext(MIteratorNext *ins)
{
LIteratorNext *lir = new LIteratorNext(useRegister(ins->iterator()), temp());
return defineBox(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitIteratorMore(MIteratorMore *ins)
{
LIteratorMore *lir = new LIteratorMore(useRegister(ins->iterator()), temp());
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitIteratorEnd(MIteratorEnd *ins)
{
LIteratorEnd *lir = new LIteratorEnd(useRegister(ins->iterator()), temp(), temp(), temp());
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitStringLength(MStringLength *ins)
{
JS_ASSERT(ins->string()->type() == MIRType_String);
return define(new LStringLength(useRegisterAtStart(ins->string())), ins);
}
bool
LIRGenerator::visitArgumentsLength(MArgumentsLength *ins)
{
return define(new LArgumentsLength(), ins);
}
bool
LIRGenerator::visitGetArgument(MGetArgument *ins)
{
LGetArgument *lir = new LGetArgument(useRegisterOrConstant(ins->index()));
return defineBox(lir, ins);
}
bool
LIRGenerator::visitRunOncePrologue(MRunOncePrologue *ins)
{
LRunOncePrologue *lir = new LRunOncePrologue;
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitRest(MRest *ins)
{
JS_ASSERT(ins->numActuals()->type() == MIRType_Int32);
LRest *lir = new LRest(useFixed(ins->numActuals(), CallTempReg0),
tempFixed(CallTempReg1),
tempFixed(CallTempReg2),
tempFixed(CallTempReg3));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitParRest(MParRest *ins)
{
JS_ASSERT(ins->numActuals()->type() == MIRType_Int32);
LParRest *lir = new LParRest(useFixed(ins->parSlice(), CallTempReg0),
useFixed(ins->numActuals(), CallTempReg1),
tempFixed(CallTempReg2),
tempFixed(CallTempReg3),
tempFixed(CallTempReg4));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitThrow(MThrow *ins)
{
MDefinition *value = ins->getOperand(0);
JS_ASSERT(value->type() == MIRType_Value);
LThrow *lir = new LThrow;
if (!useBoxAtStart(lir, LThrow::Value, value))
return false;
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitIn(MIn *ins)
{
MDefinition *lhs = ins->lhs();
MDefinition *rhs = ins->rhs();
JS_ASSERT(lhs->type() == MIRType_Value);
JS_ASSERT(rhs->type() == MIRType_Object);
LIn *lir = new LIn(useRegisterAtStart(rhs));
if (!useBoxAtStart(lir, LIn::LHS, lhs))
return false;
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitInstanceOf(MInstanceOf *ins)
{
MDefinition *lhs = ins->getOperand(0);
JS_ASSERT(lhs->type() == MIRType_Value || lhs->type() == MIRType_Object);
if (lhs->type() == MIRType_Object) {
LInstanceOfO *lir = new LInstanceOfO(useRegister(lhs));
return define(lir, ins) && assignSafepoint(lir, ins);
}
LInstanceOfV *lir = new LInstanceOfV();
return useBox(lir, LInstanceOfV::LHS, lhs) && define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCallInstanceOf(MCallInstanceOf *ins)
{
MDefinition *lhs = ins->lhs();
MDefinition *rhs = ins->rhs();
JS_ASSERT(lhs->type() == MIRType_Value);
JS_ASSERT(rhs->type() == MIRType_Object);
LCallInstanceOf *lir = new LCallInstanceOf(useRegisterAtStart(rhs));
if (!useBoxAtStart(lir, LCallInstanceOf::LHS, lhs))
return false;
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitFunctionBoundary(MFunctionBoundary *ins)
{
LFunctionBoundary *lir = new LFunctionBoundary(temp());
if (!add(lir, ins))
return false;
// If slow assertions are enabled, then this node will result in a callVM
// out to a C++ function for the assertions, so we will need a safepoint.
return !gen->compartment->rt->spsProfiler.slowAssertionsEnabled() ||
assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitIsCallable(MIsCallable *ins)
{
JS_ASSERT(ins->object()->type() == MIRType_Object);
JS_ASSERT(ins->type() == MIRType_Boolean);
return define(new LIsCallable(useRegister(ins->object())), ins);
}
bool
LIRGenerator::visitHaveSameClass(MHaveSameClass *ins)
{
MDefinition *lhs = ins->lhs();
MDefinition *rhs = ins->rhs();
JS_ASSERT(lhs->type() == MIRType_Object);
JS_ASSERT(rhs->type() == MIRType_Object);
return define(new LHaveSameClass(useRegister(lhs), useRegister(rhs), temp()), ins);
}
bool
LIRGenerator::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
{
LAsmJSLoadHeap *lir = new LAsmJSLoadHeap(useRegisterAtStart(ins->ptr()));
return define(lir, ins);
}
bool
LIRGenerator::visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins)
{
return define(new LAsmJSLoadGlobalVar, ins);
}
bool
LIRGenerator::visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins)
{
return add(new LAsmJSStoreGlobalVar(useRegisterAtStart(ins->value())), ins);
}
bool
LIRGenerator::visitAsmJSLoadFFIFunc(MAsmJSLoadFFIFunc *ins)
{
return define(new LAsmJSLoadFFIFunc, ins);
}
bool
LIRGenerator::visitAsmJSParameter(MAsmJSParameter *ins)
{
ABIArg abi = ins->abi();
if (abi.argInRegister())
return defineFixed(new LAsmJSParameter, ins, LAllocation(abi.reg()));
JS_ASSERT(ins->type() == MIRType_Int32 || ins->type() == MIRType_Double);
LAllocation::Kind argKind = ins->type() == MIRType_Int32
? LAllocation::INT_ARGUMENT
: LAllocation::DOUBLE_ARGUMENT;
return defineFixed(new LAsmJSParameter, ins, LArgument(argKind, abi.offsetFromArgBase()));
}
bool
LIRGenerator::visitAsmJSReturn(MAsmJSReturn *ins)
{
MDefinition *rval = ins->getOperand(0);
LAsmJSReturn *lir = new LAsmJSReturn;
if (rval->type() == MIRType_Double)
lir->setOperand(0, useFixed(rval, ReturnFloatReg));
else if (rval->type() == MIRType_Int32)
lir->setOperand(0, useFixed(rval, ReturnReg));
else
JS_NOT_REACHED("Unexpected asm.js return type");
return add(lir);
}
bool
LIRGenerator::visitAsmJSVoidReturn(MAsmJSVoidReturn *ins)
{
return add(new LAsmJSVoidReturn);
}
bool
LIRGenerator::visitAsmJSPassStackArg(MAsmJSPassStackArg *ins)
{
if (ins->arg()->type() == MIRType_Double) {
JS_ASSERT(!ins->arg()->isEmittedAtUses());
return add(new LAsmJSPassStackArg(useRegisterAtStart(ins->arg())), ins);
}
return add(new LAsmJSPassStackArg(useRegisterOrConstantAtStart(ins->arg())), ins);
}
bool
LIRGenerator::visitAsmJSCall(MAsmJSCall *ins)
{
gen->setPerformsAsmJSCall();
LAllocation *args = gen->allocate<LAllocation>(ins->numOperands());
if (!args)
return false;
for (unsigned i = 0; i < ins->numArgs(); i++)
args[i] = useFixed(ins->getOperand(i), ins->registerForArg(i));
if (ins->callee().which() == MAsmJSCall::Callee::Dynamic)
args[ins->dynamicCalleeOperandIndex()] = useFixed(ins->callee().dynamic(), CallTempReg0);
LInstruction *lir = new LAsmJSCall(args, ins->numOperands());
if (ins->type() == MIRType_None) {
return add(lir, ins);
}
return defineReturn(lir, ins);
}
bool
LIRGenerator::visitAsmJSCheckOverRecursed(MAsmJSCheckOverRecursed *ins)
{
return add(new LAsmJSCheckOverRecursed(), ins);
}
bool
LIRGenerator::visitSetDOMProperty(MSetDOMProperty *ins)
{
MDefinition *val = ins->value();
Register cxReg, objReg, privReg, valueReg;
GetTempRegForIntArg(0, 0, &cxReg);
GetTempRegForIntArg(1, 0, &objReg);
GetTempRegForIntArg(2, 0, &privReg);
GetTempRegForIntArg(3, 0, &valueReg);
LSetDOMProperty *lir = new LSetDOMProperty(tempFixed(cxReg),
useFixed(ins->object(), objReg),
tempFixed(privReg),
tempFixed(valueReg));
// Keep using GetTempRegForIntArg, since we want to make sure we
// don't clobber registers we're already using.
Register tempReg1, tempReg2;
GetTempRegForIntArg(4, 0, &tempReg1);
mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(5, 0, &tempReg2);
MOZ_ASSERT(ok, "How can we not have six temp registers?");
if (!useBoxFixed(lir, LSetDOMProperty::Value, val, tempReg1, tempReg2))
return false;
return add(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitGetDOMProperty(MGetDOMProperty *ins)
{
Register cxReg, objReg, privReg, valueReg;
GetTempRegForIntArg(0, 0, &cxReg);
GetTempRegForIntArg(1, 0, &objReg);
GetTempRegForIntArg(2, 0, &privReg);
mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(3, 0, &valueReg);
MOZ_ASSERT(ok, "How can we not have four temp registers?");
LGetDOMProperty *lir = new LGetDOMProperty(tempFixed(cxReg),
useFixed(ins->object(), objReg),
tempFixed(privReg),
tempFixed(valueReg));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
static void
SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint)
{
fprintf(IonSpewFile, "Current resume point %p details:\n", (void *)resumePoint);
fprintf(IonSpewFile, " frame count: %u\n", resumePoint->frameCount());
if (ins) {
fprintf(IonSpewFile, " taken after: ");
ins->printName(IonSpewFile);
} else {
fprintf(IonSpewFile, " taken at block %d entry", block->id());
}
fprintf(IonSpewFile, "\n");
fprintf(IonSpewFile, " pc: %p (script: %p, offset: %d)\n",
(void *)resumePoint->pc(),
(void *)resumePoint->block()->info().script(),
int(resumePoint->pc() - resumePoint->block()->info().script()->code));
for (size_t i = 0; i < resumePoint->numOperands(); i++) {
MDefinition *in = resumePoint->getOperand(i);
fprintf(IonSpewFile, " slot%u: ", (unsigned)i);
in->printName(IonSpewFile);
fprintf(IonSpewFile, "\n");
}
}
bool
LIRGenerator::visitInstruction(MInstruction *ins)
{
if (!gen->ensureBallast())
return false;
if (!ins->accept(this))
return false;
if (ins->resumePoint())
updateResumeState(ins);
if (gen->errored())
return false;
#ifdef DEBUG
ins->setInWorklistUnchecked();
#endif
// If we added a Nop for this instruction, we'll also add a Mop, so that
// that live-ranges for fixed register defs, which with LSRA extend through
// the Nop so that they can extend through the OsiPoint don't, with their
// one-extra extension, extend into a position where they use the input
// move group for the following instruction.
bool needsMop = !current->instructions().empty() && current->rbegin()->isNop();
// If no safepoint was created, there's no need for an OSI point.
if (LOsiPoint *osiPoint = popOsiPoint()) {
if (!add(osiPoint))
return false;
}
if (needsMop) {
if (!add(new LMop()))
return false;
}
return true;
}
bool
LIRGenerator::definePhis()
{
size_t lirIndex = 0;
MBasicBlock *block = current->mir();
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) {
if (phi->type() == MIRType_Value) {
if (!defineUntypedPhi(*phi, lirIndex))
return false;
lirIndex += BOX_PIECES;
} else {
if (!defineTypedPhi(*phi, lirIndex))
return false;
lirIndex += 1;
}
}
return true;
}
void
LIRGenerator::updateResumeState(MInstruction *ins)
{
lastResumePoint_ = ins->resumePoint();
if (IonSpewEnabled(IonSpew_Snapshots) && lastResumePoint_)
SpewResumePoint(NULL, ins, lastResumePoint_);
}
void
LIRGenerator::updateResumeState(MBasicBlock *block)
{
lastResumePoint_ = block->entryResumePoint();
if (IonSpewEnabled(IonSpew_Snapshots) && lastResumePoint_)
SpewResumePoint(block, NULL, lastResumePoint_);
}
void
LIRGenerator::allocateArguments(uint32_t argc)
{
argslots_ += argc;
if (argslots_ > maxargslots_)
maxargslots_ = argslots_;
}
uint32_t
LIRGenerator::getArgumentSlot(uint32_t argnum)
{
// First slot has index 1.
JS_ASSERT(argnum < argslots_);
return argslots_ - argnum ;
}
void
LIRGenerator::freeArguments(uint32_t argc)
{
JS_ASSERT(argc <= argslots_);
argslots_ -= argc;
}
bool
LIRGenerator::visitBlock(MBasicBlock *block)
{
current = block->lir();
updateResumeState(block);
if (!definePhis())
return false;
if (!add(new LLabel()))
return false;
for (MInstructionIterator iter = block->begin(); *iter != block->lastIns(); iter++) {
if (!visitInstruction(*iter))
return false;
}
if (block->successorWithPhis()) {
// If we have a successor with phis, lower the phi input now that we
// are approaching the join point.
MBasicBlock *successor = block->successorWithPhis();
uint32_t position = block->positionInPhiSuccessor();
size_t lirIndex = 0;
for (MPhiIterator phi(successor->phisBegin()); phi != successor->phisEnd(); phi++) {
MDefinition *opd = phi->getOperand(position);
if (!ensureDefined(opd))
return false;
JS_ASSERT(opd->type() == phi->type());
if (phi->type() == MIRType_Value) {
lowerUntypedPhiInput(*phi, position, successor->lir(), lirIndex);
lirIndex += BOX_PIECES;
} else {
lowerTypedPhiInput(*phi, position, successor->lir(), lirIndex);
lirIndex += 1;
}
}
}
// Now emit the last instruction, which is some form of branch.
if (!visitInstruction(block->lastIns()))
return false;
return true;
}
bool
LIRGenerator::precreatePhi(LBlock *block, MPhi *phi)
{
LPhi *lir = LPhi::New(gen, phi);
if (!lir)
return false;
if (!block->addPhi(lir))
return false;
return true;
}
bool
LIRGenerator::generate()
{
// Create all blocks and prep all phis beforehand.
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
if (gen->shouldCancel("Lowering (preparation loop)"))
return false;
current = LBlock::New(*block);
if (!current)
return false;
if (!lirGraph_.addBlock(current))
return false;
block->assignLir(current);
// For each MIR phi, add LIR phis as appropriate. We'll fill in their
// operands on each incoming edge, and set their definitions at the
// start of their defining block.
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) {
int numPhis = (phi->type() == MIRType_Value) ? BOX_PIECES : 1;
for (int i = 0; i < numPhis; i++) {
if (!precreatePhi(block->lir(), *phi))
return false;
}
}
}
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
if (gen->shouldCancel("Lowering (main loop)"))
return false;
if (!visitBlock(*block))
return false;
}
if (graph.osrBlock())
lirGraph_.setOsrBlock(graph.osrBlock()->lir());
lirGraph_.setArgumentSlotCount(maxargslots_);
JS_ASSERT(argslots_ == 0);
JS_ASSERT(prepareCallStack_.empty());
return true;
}