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/TypePolicy.h" michael@0: michael@0: #include "jit/Lowering.h" michael@0: #include "jit/MIR.h" michael@0: #include "jit/MIRGraph.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: using JS::DoubleNaNValue; michael@0: michael@0: static void michael@0: EnsureOperandNotFloat32(TempAllocator &alloc, MInstruction *def, unsigned op) michael@0: { michael@0: MDefinition *in = def->getOperand(op); michael@0: if (in->type() == MIRType_Float32) { michael@0: MToDouble *replace = MToDouble::New(alloc, in); michael@0: def->block()->insertBefore(def, replace); michael@0: def->replaceOperand(op, replace); michael@0: } michael@0: } michael@0: michael@0: MDefinition * michael@0: BoxInputsPolicy::boxAt(TempAllocator &alloc, MInstruction *at, MDefinition *operand) michael@0: { michael@0: if (operand->isUnbox()) michael@0: return operand->toUnbox()->input(); michael@0: return alwaysBoxAt(alloc, at, operand); michael@0: } michael@0: michael@0: MDefinition * michael@0: BoxInputsPolicy::alwaysBoxAt(TempAllocator &alloc, MInstruction *at, MDefinition *operand) michael@0: { michael@0: MDefinition *boxedOperand = operand; michael@0: // Replace Float32 by double michael@0: if (operand->type() == MIRType_Float32) { michael@0: MInstruction *replace = MToDouble::New(alloc, operand); michael@0: at->block()->insertBefore(at, replace); michael@0: boxedOperand = replace; michael@0: } michael@0: MBox *box = MBox::New(alloc, boxedOperand); michael@0: at->block()->insertBefore(at, box); michael@0: return box; michael@0: } michael@0: michael@0: bool michael@0: BoxInputsPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: for (size_t i = 0, e = ins->numOperands(); i < e; i++) { michael@0: MDefinition *in = ins->getOperand(i); michael@0: if (in->type() == MIRType_Value) michael@0: continue; michael@0: ins->replaceOperand(i, boxAt(alloc, ins, in)); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ArithPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: if (specialization_ == MIRType_None) michael@0: return BoxInputsPolicy::adjustInputs(alloc, ins); michael@0: michael@0: JS_ASSERT(ins->type() == MIRType_Double || ins->type() == MIRType_Int32 || ins->type() == MIRType_Float32); michael@0: michael@0: for (size_t i = 0, e = ins->numOperands(); i < e; i++) { michael@0: MDefinition *in = ins->getOperand(i); michael@0: if (in->type() == ins->type()) michael@0: continue; michael@0: michael@0: MInstruction *replace; michael@0: michael@0: // If the input is a string or an object, the conversion is not michael@0: // possible, at least, we can't specialize. So box the input. michael@0: if (in->type() == MIRType_Object || in->type() == MIRType_String || michael@0: (in->type() == MIRType_Undefined && specialization_ == MIRType_Int32)) michael@0: { michael@0: in = boxAt(alloc, ins, in); michael@0: } michael@0: michael@0: if (ins->type() == MIRType_Double) michael@0: replace = MToDouble::New(alloc, in); michael@0: else if (ins->type() == MIRType_Float32) michael@0: replace = MToFloat32::New(alloc, in); michael@0: else michael@0: replace = MToInt32::New(alloc, in); michael@0: michael@0: ins->block()->insertBefore(ins, replace); michael@0: ins->replaceOperand(i, replace); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ComparePolicy::adjustInputs(TempAllocator &alloc, MInstruction *def) michael@0: { michael@0: JS_ASSERT(def->isCompare()); michael@0: MCompare *compare = def->toCompare(); michael@0: michael@0: // Convert Float32 operands to doubles michael@0: for (size_t i = 0; i < 2; i++) { michael@0: MDefinition *in = def->getOperand(i); michael@0: if (in->type() == MIRType_Float32) { michael@0: MInstruction *replace = MToDouble::New(alloc, in); michael@0: def->block()->insertBefore(def, replace); michael@0: def->replaceOperand(i, replace); michael@0: } michael@0: } michael@0: michael@0: // Box inputs to get value michael@0: if (compare->compareType() == MCompare::Compare_Unknown || michael@0: compare->compareType() == MCompare::Compare_Value) michael@0: { michael@0: return BoxInputsPolicy::adjustInputs(alloc, def); michael@0: } michael@0: michael@0: // Compare_Boolean specialization is done for "Anything === Bool" michael@0: // If the LHS is boolean, we set the specialization to Compare_Int32. michael@0: // This matches other comparisons of the form bool === bool and michael@0: // generated code of Compare_Int32 is more efficient. michael@0: if (compare->compareType() == MCompare::Compare_Boolean && michael@0: def->getOperand(0)->type() == MIRType_Boolean) michael@0: { michael@0: compare->setCompareType(MCompare::Compare_Int32MaybeCoerceBoth); michael@0: } michael@0: michael@0: // Compare_Boolean specialization is done for "Anything === Bool" michael@0: // As of previous line Anything can't be Boolean michael@0: if (compare->compareType() == MCompare::Compare_Boolean) { michael@0: // Unbox rhs that is definitely Boolean michael@0: MDefinition *rhs = def->getOperand(1); michael@0: if (rhs->type() != MIRType_Boolean) { michael@0: if (rhs->type() != MIRType_Value) michael@0: rhs = boxAt(alloc, def, rhs); michael@0: MInstruction *unbox = MUnbox::New(alloc, rhs, MIRType_Boolean, MUnbox::Infallible); michael@0: def->block()->insertBefore(def, unbox); michael@0: def->replaceOperand(1, unbox); michael@0: } michael@0: michael@0: JS_ASSERT(def->getOperand(0)->type() != MIRType_Boolean); michael@0: JS_ASSERT(def->getOperand(1)->type() == MIRType_Boolean); michael@0: return true; michael@0: } michael@0: michael@0: // Compare_StrictString specialization is done for "Anything === String" michael@0: // If the LHS is string, we set the specialization to Compare_String. michael@0: if (compare->compareType() == MCompare::Compare_StrictString && michael@0: def->getOperand(0)->type() == MIRType_String) michael@0: { michael@0: compare->setCompareType(MCompare::Compare_String); michael@0: } michael@0: michael@0: // Compare_StrictString specialization is done for "Anything === String" michael@0: // As of previous line Anything can't be String michael@0: if (compare->compareType() == MCompare::Compare_StrictString) { michael@0: // Unbox rhs that is definitely String michael@0: MDefinition *rhs = def->getOperand(1); michael@0: if (rhs->type() != MIRType_String) { michael@0: if (rhs->type() != MIRType_Value) michael@0: rhs = boxAt(alloc, def, rhs); michael@0: MInstruction *unbox = MUnbox::New(alloc, rhs, MIRType_String, MUnbox::Infallible); michael@0: def->block()->insertBefore(def, unbox); michael@0: def->replaceOperand(1, unbox); michael@0: } michael@0: michael@0: JS_ASSERT(def->getOperand(0)->type() != MIRType_String); michael@0: JS_ASSERT(def->getOperand(1)->type() == MIRType_String); michael@0: return true; michael@0: } michael@0: michael@0: if (compare->compareType() == MCompare::Compare_Undefined || michael@0: compare->compareType() == MCompare::Compare_Null) michael@0: { michael@0: // Nothing to do for undefined and null, lowering handles all types. michael@0: return true; michael@0: } michael@0: michael@0: // Convert all inputs to the right input type michael@0: MIRType type = compare->inputType(); michael@0: JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double || michael@0: type == MIRType_Object || type == MIRType_String || type == MIRType_Float32); michael@0: for (size_t i = 0; i < 2; i++) { michael@0: MDefinition *in = def->getOperand(i); michael@0: if (in->type() == type) michael@0: continue; michael@0: michael@0: MInstruction *replace; michael@0: michael@0: // See BinaryArithPolicy::adjustInputs for an explanation of the following michael@0: if (in->type() == MIRType_Object || in->type() == MIRType_String || michael@0: in->type() == MIRType_Undefined) michael@0: { michael@0: in = boxAt(alloc, def, in); michael@0: } michael@0: michael@0: switch (type) { michael@0: case MIRType_Double: { michael@0: MToDouble::ConversionKind convert = MToDouble::NumbersOnly; michael@0: if (compare->compareType() == MCompare::Compare_DoubleMaybeCoerceLHS && i == 0) michael@0: convert = MToDouble::NonNullNonStringPrimitives; michael@0: else if (compare->compareType() == MCompare::Compare_DoubleMaybeCoerceRHS && i == 1) michael@0: convert = MToDouble::NonNullNonStringPrimitives; michael@0: if (in->type() == MIRType_Null || michael@0: (in->type() == MIRType_Boolean && convert == MToDouble::NumbersOnly)) michael@0: { michael@0: in = boxAt(alloc, def, in); michael@0: } michael@0: replace = MToDouble::New(alloc, in, convert); michael@0: break; michael@0: } michael@0: case MIRType_Float32: { michael@0: MToFloat32::ConversionKind convert = MToFloat32::NumbersOnly; michael@0: if (compare->compareType() == MCompare::Compare_DoubleMaybeCoerceLHS && i == 0) michael@0: convert = MToFloat32::NonNullNonStringPrimitives; michael@0: else if (compare->compareType() == MCompare::Compare_DoubleMaybeCoerceRHS && i == 1) michael@0: convert = MToFloat32::NonNullNonStringPrimitives; michael@0: if (in->type() == MIRType_Null || michael@0: (in->type() == MIRType_Boolean && convert == MToFloat32::NumbersOnly)) michael@0: { michael@0: in = boxAt(alloc, def, in); michael@0: } michael@0: replace = MToFloat32::New(alloc, in, convert); michael@0: break; michael@0: } michael@0: case MIRType_Int32: { michael@0: MacroAssembler::IntConversionInputKind convert = MacroAssembler::IntConversion_NumbersOnly; michael@0: if (compare->compareType() == MCompare::Compare_Int32MaybeCoerceBoth || michael@0: (compare->compareType() == MCompare::Compare_Int32MaybeCoerceLHS && i == 0) || michael@0: (compare->compareType() == MCompare::Compare_Int32MaybeCoerceRHS && i == 1)) michael@0: { michael@0: convert = MacroAssembler::IntConversion_NumbersOrBoolsOnly; michael@0: } michael@0: if (convert == MacroAssembler::IntConversion_NumbersOnly) { michael@0: if (in->type() != MIRType_Int32 && in->type() != MIRType_Value) michael@0: in = boxAt(alloc, def, in); michael@0: } else { michael@0: MOZ_ASSERT(convert == MacroAssembler::IntConversion_NumbersOrBoolsOnly); michael@0: if (in->type() != MIRType_Int32 && michael@0: in->type() != MIRType_Boolean && michael@0: in->type() != MIRType_Value) michael@0: { michael@0: in = boxAt(alloc, def, in); michael@0: } michael@0: } michael@0: replace = MToInt32::New(alloc, in, convert); michael@0: break; michael@0: } michael@0: case MIRType_Object: michael@0: replace = MUnbox::New(alloc, in, MIRType_Object, MUnbox::Infallible); michael@0: break; michael@0: case MIRType_String: michael@0: replace = MUnbox::New(alloc, in, MIRType_String, MUnbox::Infallible); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unknown compare specialization"); michael@0: } michael@0: michael@0: def->block()->insertBefore(def, replace); michael@0: def->replaceOperand(i, replace); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TypeBarrierPolicy::adjustInputs(TempAllocator &alloc, MInstruction *def) michael@0: { michael@0: MTypeBarrier *ins = def->toTypeBarrier(); michael@0: MIRType inputType = ins->getOperand(0)->type(); michael@0: MIRType outputType = ins->type(); michael@0: michael@0: // Input and output type are already in accordance. michael@0: if (inputType == outputType) michael@0: return true; michael@0: michael@0: // Output is a value, currently box the input. michael@0: if (outputType == MIRType_Value) { michael@0: // XXX: Possible optimization: decrease resultTypeSet to only include michael@0: // the inputType. This will remove the need for boxing. michael@0: JS_ASSERT(inputType != MIRType_Value); michael@0: ins->replaceOperand(0, boxAt(alloc, ins, ins->getOperand(0))); michael@0: return true; michael@0: } michael@0: michael@0: // Input is a value. Unbox the input to the requested type. michael@0: if (inputType == MIRType_Value) { michael@0: JS_ASSERT(outputType != MIRType_Value); michael@0: michael@0: // We can't unbox a value to null/undefined/lazyargs. So keep output michael@0: // also a value. michael@0: if (IsNullOrUndefined(outputType) || outputType == MIRType_MagicOptimizedArguments) { michael@0: JS_ASSERT(ins->defUseCount() == 0); michael@0: ins->setResultType(MIRType_Value); michael@0: return true; michael@0: } michael@0: michael@0: MUnbox *unbox = MUnbox::New(alloc, ins->getOperand(0), outputType, MUnbox::TypeBarrier); michael@0: ins->block()->insertBefore(ins, unbox); michael@0: ins->replaceOperand(0, unbox); michael@0: return true; michael@0: } michael@0: michael@0: // In the remaining cases we will alway bail. OutputType doesn't matter. michael@0: // Take inputType so we can use redefine during lowering. michael@0: JS_ASSERT(ins->alwaysBails()); michael@0: ins->setResultType(inputType); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TestPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MDefinition *op = ins->getOperand(0); michael@0: switch (op->type()) { michael@0: case MIRType_Value: michael@0: case MIRType_Null: michael@0: case MIRType_Undefined: michael@0: case MIRType_Boolean: michael@0: case MIRType_Int32: michael@0: case MIRType_Double: michael@0: case MIRType_Float32: michael@0: case MIRType_Object: michael@0: break; michael@0: michael@0: case MIRType_String: michael@0: { michael@0: MStringLength *length = MStringLength::New(alloc, op); michael@0: ins->block()->insertBefore(ins, length); michael@0: ins->replaceOperand(0, length); michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: ins->replaceOperand(0, boxAt(alloc, ins, op)); michael@0: break; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BitwisePolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: if (specialization_ == MIRType_None) michael@0: return BoxInputsPolicy::adjustInputs(alloc, ins); michael@0: michael@0: JS_ASSERT(ins->type() == specialization_); michael@0: JS_ASSERT(specialization_ == MIRType_Int32 || specialization_ == MIRType_Double); michael@0: michael@0: // This policy works for both unary and binary bitwise operations. michael@0: for (size_t i = 0, e = ins->numOperands(); i < e; i++) { michael@0: MDefinition *in = ins->getOperand(i); michael@0: if (in->type() == MIRType_Int32) michael@0: continue; michael@0: michael@0: // See BinaryArithPolicy::adjustInputs for an explanation of the following michael@0: if (in->type() == MIRType_Object || in->type() == MIRType_String) michael@0: in = boxAt(alloc, ins, in); michael@0: michael@0: MInstruction *replace = MTruncateToInt32::New(alloc, in); michael@0: ins->block()->insertBefore(ins, replace); michael@0: ins->replaceOperand(i, replace); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: PowPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: JS_ASSERT(specialization_ == MIRType_Int32 || specialization_ == MIRType_Double); michael@0: michael@0: // Input must be a double. michael@0: if (!DoublePolicy<0>::staticAdjustInputs(alloc, ins)) michael@0: return false; michael@0: michael@0: // Power may be an int32 or a double. Integers receive a faster path. michael@0: if (specialization_ == MIRType_Double) michael@0: return DoublePolicy<1>::staticAdjustInputs(alloc, ins); michael@0: return IntPolicy<1>::staticAdjustInputs(alloc, ins); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: StringPolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MDefinition *in = ins->getOperand(Op); michael@0: if (in->type() == MIRType_String) michael@0: return true; michael@0: michael@0: if (in->type() != MIRType_Value) michael@0: in = boxAt(alloc, ins, in); michael@0: michael@0: MUnbox *replace = MUnbox::New(alloc, in, MIRType_String, MUnbox::Fallible); michael@0: ins->block()->insertBefore(ins, replace); michael@0: ins->replaceOperand(Op, replace); michael@0: return true; michael@0: } michael@0: michael@0: template bool StringPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: template bool StringPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: template bool StringPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: michael@0: template michael@0: bool michael@0: ConvertToStringPolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MDefinition *in = ins->getOperand(Op); michael@0: if (in->type() == MIRType_String) michael@0: return true; michael@0: michael@0: MInstruction *replace; michael@0: if (in->mightBeType(MIRType_Object)) { michael@0: if (in->type() != MIRType_Value) michael@0: in = boxAt(alloc, ins, in); michael@0: michael@0: replace = MUnbox::New(alloc, in, MIRType_String, MUnbox::Fallible); michael@0: } else { michael@0: // TODO remove these two lines once 966957 has landed michael@0: EnsureOperandNotFloat32(alloc, ins, Op); michael@0: in = ins->getOperand(Op); michael@0: replace = MToString::New(alloc, in); michael@0: } michael@0: michael@0: ins->block()->insertBefore(ins, replace); michael@0: ins->replaceOperand(Op, replace); michael@0: return true; michael@0: } michael@0: michael@0: template bool ConvertToStringPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: template bool ConvertToStringPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: michael@0: template michael@0: bool michael@0: IntPolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *def) michael@0: { michael@0: MDefinition *in = def->getOperand(Op); michael@0: if (in->type() == MIRType_Int32) michael@0: return true; michael@0: michael@0: if (in->type() != MIRType_Value) michael@0: in = boxAt(alloc, def, in); michael@0: michael@0: MUnbox *replace = MUnbox::New(alloc, in, MIRType_Int32, MUnbox::Fallible); michael@0: def->block()->insertBefore(def, replace); michael@0: def->replaceOperand(Op, replace); michael@0: return true; michael@0: } michael@0: michael@0: template bool IntPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: template bool IntPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: template bool IntPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: michael@0: template michael@0: bool michael@0: ConvertToInt32Policy::staticAdjustInputs(TempAllocator &alloc, MInstruction *def) michael@0: { michael@0: MDefinition *in = def->getOperand(Op); michael@0: if (in->type() == MIRType_Int32) michael@0: return true; michael@0: michael@0: MToInt32 *replace = MToInt32::New(alloc, in); michael@0: def->block()->insertBefore(def, replace); michael@0: def->replaceOperand(Op, replace); michael@0: return true; michael@0: } michael@0: michael@0: template bool ConvertToInt32Policy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: michael@0: template michael@0: bool michael@0: DoublePolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *def) michael@0: { michael@0: MDefinition *in = def->getOperand(Op); michael@0: if (in->type() == MIRType_Double) michael@0: return true; michael@0: michael@0: // Force a bailout. Objects may be effectful; strings are currently unhandled. michael@0: if (in->type() == MIRType_Object || in->type() == MIRType_String) { michael@0: MBox *box = MBox::New(alloc, in); michael@0: def->block()->insertBefore(def, box); michael@0: michael@0: MUnbox *unbox = MUnbox::New(alloc, box, MIRType_Double, MUnbox::Fallible); michael@0: def->block()->insertBefore(def, unbox); michael@0: def->replaceOperand(Op, unbox); michael@0: return true; michael@0: } michael@0: michael@0: MToDouble *replace = MToDouble::New(alloc, in); michael@0: def->block()->insertBefore(def, replace); michael@0: def->replaceOperand(Op, replace); michael@0: return true; michael@0: } michael@0: michael@0: template bool DoublePolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: template bool DoublePolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: michael@0: template michael@0: bool michael@0: Float32Policy::staticAdjustInputs(TempAllocator &alloc, MInstruction *def) michael@0: { michael@0: MDefinition *in = def->getOperand(Op); michael@0: if (in->type() == MIRType_Float32) michael@0: return true; michael@0: michael@0: // Force a bailout. Objects may be effectful; strings are currently unhandled. michael@0: if (in->type() == MIRType_Object || in->type() == MIRType_String) { michael@0: MToDouble *toDouble = MToDouble::New(alloc, in); michael@0: def->block()->insertBefore(def, toDouble); michael@0: michael@0: MBox *box = MBox::New(alloc, toDouble); michael@0: def->block()->insertBefore(def, box); michael@0: michael@0: MUnbox *unbox = MUnbox::New(alloc, box, MIRType_Double, MUnbox::Fallible); michael@0: def->block()->insertBefore(def, unbox); michael@0: michael@0: MToFloat32 *toFloat32 = MToFloat32::New(alloc, unbox); michael@0: def->block()->insertBefore(def, toFloat32); michael@0: michael@0: def->replaceOperand(Op, unbox); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: MToFloat32 *replace = MToFloat32::New(alloc, in); michael@0: def->block()->insertBefore(def, replace); michael@0: def->replaceOperand(Op, replace); michael@0: return true; michael@0: } michael@0: michael@0: template bool Float32Policy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: template bool Float32Policy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: template bool Float32Policy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: michael@0: template michael@0: bool michael@0: NoFloatPolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *def) michael@0: { michael@0: EnsureOperandNotFloat32(alloc, def, Op); michael@0: return true; michael@0: } michael@0: michael@0: template bool NoFloatPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: template bool NoFloatPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: template bool NoFloatPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: template bool NoFloatPolicy<3>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def); michael@0: michael@0: template michael@0: bool michael@0: BoxPolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MDefinition *in = ins->getOperand(Op); michael@0: if (in->type() == MIRType_Value) michael@0: return true; michael@0: michael@0: ins->replaceOperand(Op, boxAt(alloc, ins, in)); michael@0: return true; michael@0: } michael@0: michael@0: template bool BoxPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: template bool BoxPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: template bool BoxPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: michael@0: template michael@0: bool michael@0: BoxExceptPolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MDefinition *in = ins->getOperand(Op); michael@0: if (in->type() == Type) michael@0: return true; michael@0: return BoxPolicy::staticAdjustInputs(alloc, ins); michael@0: } michael@0: michael@0: template bool BoxExceptPolicy<0, MIRType_String>::staticAdjustInputs(TempAllocator &alloc, michael@0: MInstruction *ins); michael@0: template bool BoxExceptPolicy<1, MIRType_String>::staticAdjustInputs(TempAllocator &alloc, michael@0: MInstruction *ins); michael@0: template bool BoxExceptPolicy<2, MIRType_String>::staticAdjustInputs(TempAllocator &alloc, michael@0: MInstruction *ins); michael@0: michael@0: bool michael@0: ToDoublePolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MDefinition *in = ins->getOperand(0); michael@0: if (in->type() != MIRType_Object && in->type() != MIRType_String) michael@0: return true; michael@0: michael@0: in = boxAt(alloc, ins, in); michael@0: ins->replaceOperand(0, in); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ToInt32Policy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: JS_ASSERT(ins->isToInt32()); michael@0: michael@0: MDefinition *in = ins->getOperand(0); michael@0: switch (in->type()) { michael@0: case MIRType_Object: michael@0: case MIRType_String: michael@0: case MIRType_Undefined: michael@0: // Objects might be effectful. Undefined coerces to NaN, not int32. michael@0: in = boxAt(alloc, ins, in); michael@0: ins->replaceOperand(0, in); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: ObjectPolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MDefinition *in = ins->getOperand(Op); michael@0: if (in->type() == MIRType_Object || in->type() == MIRType_Slots || michael@0: in->type() == MIRType_Elements) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: if (in->type() != MIRType_Value) michael@0: in = boxAt(alloc, ins, in); michael@0: michael@0: MUnbox *replace = MUnbox::New(alloc, in, MIRType_Object, MUnbox::Fallible); michael@0: ins->block()->insertBefore(ins, replace); michael@0: ins->replaceOperand(Op, replace); michael@0: return true; michael@0: } michael@0: michael@0: template bool ObjectPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: template bool ObjectPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: template bool ObjectPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: template bool ObjectPolicy<3>::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins); michael@0: michael@0: bool michael@0: CallPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MCall *call = ins->toCall(); michael@0: michael@0: MDefinition *func = call->getFunction(); michael@0: if (func->type() != MIRType_Object) { michael@0: // If the function is impossible to call, michael@0: // bail out by causing a subsequent unbox to fail. michael@0: if (func->type() != MIRType_Value) michael@0: func = boxAt(alloc, call, func); michael@0: michael@0: MInstruction *unbox = MUnbox::New(alloc, func, MIRType_Object, MUnbox::Fallible); michael@0: call->block()->insertBefore(call, unbox); michael@0: call->replaceFunction(unbox); michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < call->numStackArgs(); i++) michael@0: EnsureOperandNotFloat32(alloc, call, MCall::IndexOfStackArg(i)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CallSetElementPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: // The first operand should be an object. michael@0: SingleObjectPolicy::adjustInputs(alloc, ins); michael@0: michael@0: // Box the index and value operands. michael@0: for (size_t i = 1, e = ins->numOperands(); i < e; i++) { michael@0: MDefinition *in = ins->getOperand(i); michael@0: if (in->type() == MIRType_Value) michael@0: continue; michael@0: ins->replaceOperand(i, boxAt(alloc, ins, in)); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: InstanceOfPolicy::adjustInputs(TempAllocator &alloc, MInstruction *def) michael@0: { michael@0: // Box first operand if it isn't object michael@0: if (def->getOperand(0)->type() != MIRType_Object) michael@0: BoxPolicy<0>::staticAdjustInputs(alloc, def); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: StoreTypedArrayPolicy::adjustValueInput(TempAllocator &alloc, MInstruction *ins, int arrayType, michael@0: MDefinition *value, int valueOperand) michael@0: { michael@0: MDefinition *curValue = value; michael@0: // First, ensure the value is int32, boolean, double or Value. michael@0: // The conversion is based on TypedArrayObjectTemplate::setElementTail. michael@0: switch (value->type()) { michael@0: case MIRType_Int32: michael@0: case MIRType_Double: michael@0: case MIRType_Float32: michael@0: case MIRType_Boolean: michael@0: case MIRType_Value: michael@0: break; michael@0: case MIRType_Null: michael@0: value->setImplicitlyUsedUnchecked(); michael@0: value = MConstant::New(alloc, Int32Value(0)); michael@0: ins->block()->insertBefore(ins, value->toInstruction()); michael@0: break; michael@0: case MIRType_Undefined: michael@0: value->setImplicitlyUsedUnchecked(); michael@0: value = MConstant::New(alloc, DoubleNaNValue()); michael@0: ins->block()->insertBefore(ins, value->toInstruction()); michael@0: break; michael@0: case MIRType_Object: michael@0: case MIRType_String: michael@0: value = boxAt(alloc, ins, value); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected type"); michael@0: } michael@0: michael@0: if (value != curValue) { michael@0: ins->replaceOperand(valueOperand, value); michael@0: curValue = value; michael@0: } michael@0: michael@0: JS_ASSERT(value->type() == MIRType_Int32 || michael@0: value->type() == MIRType_Boolean || michael@0: value->type() == MIRType_Double || michael@0: value->type() == MIRType_Float32 || michael@0: value->type() == MIRType_Value); michael@0: michael@0: switch (arrayType) { michael@0: case ScalarTypeDescr::TYPE_INT8: michael@0: case ScalarTypeDescr::TYPE_UINT8: michael@0: case ScalarTypeDescr::TYPE_INT16: michael@0: case ScalarTypeDescr::TYPE_UINT16: michael@0: case ScalarTypeDescr::TYPE_INT32: michael@0: case ScalarTypeDescr::TYPE_UINT32: michael@0: if (value->type() != MIRType_Int32) { michael@0: value = MTruncateToInt32::New(alloc, value); michael@0: ins->block()->insertBefore(ins, value->toInstruction()); michael@0: } michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT8_CLAMPED: michael@0: // IonBuilder should have inserted ClampToUint8. michael@0: JS_ASSERT(value->type() == MIRType_Int32); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT32: michael@0: if (LIRGenerator::allowFloat32Optimizations()) { michael@0: if (value->type() != MIRType_Float32) { michael@0: value = MToFloat32::New(alloc, value); michael@0: ins->block()->insertBefore(ins, value->toInstruction()); michael@0: } michael@0: break; michael@0: } michael@0: // Fallthrough: if the LIRGenerator cannot directly store Float32, it will expect the michael@0: // stored value to be a double. michael@0: case ScalarTypeDescr::TYPE_FLOAT64: michael@0: if (value->type() != MIRType_Double) { michael@0: value = MToDouble::New(alloc, value); michael@0: ins->block()->insertBefore(ins, value->toInstruction()); michael@0: } michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid array type"); michael@0: } michael@0: michael@0: if (value != curValue) { michael@0: ins->replaceOperand(valueOperand, value); michael@0: curValue = value; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: StoreTypedArrayPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MStoreTypedArrayElement *store = ins->toStoreTypedArrayElement(); michael@0: JS_ASSERT(store->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(store->index()->type() == MIRType_Int32); michael@0: michael@0: return adjustValueInput(alloc, ins, store->arrayType(), store->value(), 2); michael@0: } michael@0: michael@0: bool michael@0: StoreTypedArrayHolePolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MStoreTypedArrayElementHole *store = ins->toStoreTypedArrayElementHole(); michael@0: JS_ASSERT(store->elements()->type() == MIRType_Elements); michael@0: JS_ASSERT(store->index()->type() == MIRType_Int32); michael@0: JS_ASSERT(store->length()->type() == MIRType_Int32); michael@0: michael@0: return adjustValueInput(alloc, ins, store->arrayType(), store->value(), 3); michael@0: } michael@0: michael@0: bool michael@0: StoreTypedArrayElementStaticPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MStoreTypedArrayElementStatic *store = ins->toStoreTypedArrayElementStatic(); michael@0: michael@0: return ConvertToInt32Policy<0>::staticAdjustInputs(alloc, ins) && michael@0: adjustValueInput(alloc, ins, store->viewType(), store->value(), 1); michael@0: } michael@0: michael@0: bool michael@0: ClampPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MDefinition *in = ins->toClampToUint8()->input(); michael@0: michael@0: switch (in->type()) { michael@0: case MIRType_Int32: michael@0: case MIRType_Double: michael@0: case MIRType_Value: michael@0: break; michael@0: default: michael@0: ins->replaceOperand(0, boxAt(alloc, ins, in)); michael@0: break; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: FilterTypeSetPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins) michael@0: { michael@0: MOZ_ASSERT(ins->numOperands() == 1); michael@0: michael@0: // Do nothing if already same type. michael@0: if (ins->type() == ins->getOperand(0)->type()) michael@0: return true; michael@0: michael@0: // Box input if ouput type is MIRType_Value michael@0: if (ins->type() == MIRType_Value) { michael@0: ins->replaceOperand(0, boxAt(alloc, ins, ins->getOperand(0))); michael@0: return true; michael@0: } michael@0: michael@0: // For simplicity just mark output type as MIRType_Value if input type michael@0: // is MIRType_Value. It should be possible to unbox, but we need to michael@0: // add extra code for Undefined/Null. michael@0: if (ins->getOperand(0)->type() == MIRType_Value) { michael@0: ins->setResultType(MIRType_Value); michael@0: return true; michael@0: } michael@0: michael@0: // In all other cases we will definitely bail, since types don't michael@0: // correspond. Just box and mark output as MIRType_Value. michael@0: ins->replaceOperand(0, boxAt(alloc, ins, ins->getOperand(0))); michael@0: ins->setResultType(MIRType_Value); michael@0: michael@0: return true; michael@0: }