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/MIR.h" michael@0: michael@0: #include "mozilla/FloatingPoint.h" michael@0: michael@0: #include michael@0: michael@0: #include "jslibmath.h" michael@0: #include "jsstr.h" michael@0: michael@0: #include "jit/BaselineInspector.h" michael@0: #include "jit/IonBuilder.h" michael@0: #include "jit/IonSpewer.h" michael@0: #include "jit/MIRGraph.h" michael@0: #include "jit/RangeAnalysis.h" michael@0: michael@0: #include "jsatominlines.h" michael@0: #include "jsinferinlines.h" michael@0: #include "jsobjinlines.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: using mozilla::NumbersAreIdentical; michael@0: using mozilla::IsFloat32Representable; michael@0: using mozilla::Maybe; michael@0: michael@0: template static void michael@0: ConvertDefinitionToDouble(TempAllocator &alloc, MDefinition *def, MInstruction *consumer) michael@0: { michael@0: MInstruction *replace = MToDouble::New(alloc, def); michael@0: consumer->replaceOperand(Op, replace); michael@0: consumer->block()->insertBefore(consumer, replace); michael@0: } michael@0: michael@0: static bool michael@0: CheckUsesAreFloat32Consumers(MInstruction *ins) michael@0: { michael@0: bool allConsumerUses = true; michael@0: for (MUseDefIterator use(ins); allConsumerUses && use; use++) michael@0: allConsumerUses &= use.def()->canConsumeFloat32(use.use()); michael@0: return allConsumerUses; michael@0: } michael@0: michael@0: void michael@0: MDefinition::PrintOpcodeName(FILE *fp, MDefinition::Opcode op) michael@0: { michael@0: static const char * const names[] = michael@0: { michael@0: #define NAME(x) #x, michael@0: MIR_OPCODE_LIST(NAME) michael@0: #undef NAME michael@0: }; michael@0: const char *name = names[op]; michael@0: size_t len = strlen(name); michael@0: for (size_t i = 0; i < len; i++) michael@0: fprintf(fp, "%c", tolower(name[i])); michael@0: } michael@0: michael@0: static inline bool michael@0: EqualValues(bool useGVN, MDefinition *left, MDefinition *right) michael@0: { michael@0: if (useGVN) michael@0: return left->valueNumber() == right->valueNumber(); michael@0: michael@0: return left == right; michael@0: } michael@0: michael@0: static MConstant * michael@0: EvaluateConstantOperands(TempAllocator &alloc, MBinaryInstruction *ins, bool *ptypeChange = nullptr) michael@0: { michael@0: MDefinition *left = ins->getOperand(0); michael@0: MDefinition *right = ins->getOperand(1); michael@0: michael@0: if (!left->isConstant() || !right->isConstant()) michael@0: return nullptr; michael@0: michael@0: Value lhs = left->toConstant()->value(); michael@0: Value rhs = right->toConstant()->value(); michael@0: Value ret = UndefinedValue(); michael@0: michael@0: switch (ins->op()) { michael@0: case MDefinition::Op_BitAnd: michael@0: ret = Int32Value(lhs.toInt32() & rhs.toInt32()); michael@0: break; michael@0: case MDefinition::Op_BitOr: michael@0: ret = Int32Value(lhs.toInt32() | rhs.toInt32()); michael@0: break; michael@0: case MDefinition::Op_BitXor: michael@0: ret = Int32Value(lhs.toInt32() ^ rhs.toInt32()); michael@0: break; michael@0: case MDefinition::Op_Lsh: michael@0: ret = Int32Value(uint32_t(lhs.toInt32()) << (rhs.toInt32() & 0x1F)); michael@0: break; michael@0: case MDefinition::Op_Rsh: michael@0: ret = Int32Value(lhs.toInt32() >> (rhs.toInt32() & 0x1F)); michael@0: break; michael@0: case MDefinition::Op_Ursh: michael@0: ret.setNumber(uint32_t(lhs.toInt32()) >> (rhs.toInt32() & 0x1F)); michael@0: break; michael@0: case MDefinition::Op_Add: michael@0: ret.setNumber(lhs.toNumber() + rhs.toNumber()); michael@0: break; michael@0: case MDefinition::Op_Sub: michael@0: ret.setNumber(lhs.toNumber() - rhs.toNumber()); michael@0: break; michael@0: case MDefinition::Op_Mul: michael@0: ret.setNumber(lhs.toNumber() * rhs.toNumber()); michael@0: break; michael@0: case MDefinition::Op_Div: michael@0: ret.setNumber(NumberDiv(lhs.toNumber(), rhs.toNumber())); michael@0: break; michael@0: case MDefinition::Op_Mod: michael@0: ret.setNumber(NumberMod(lhs.toNumber(), rhs.toNumber())); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("NYI"); michael@0: } michael@0: michael@0: // setNumber eagerly transforms a number to int32. michael@0: // Transform back to double, if the output type is double. michael@0: if (ins->type() == MIRType_Double && ret.isInt32()) michael@0: ret.setDouble(ret.toNumber()); michael@0: michael@0: if (ins->type() != MIRTypeFromValue(ret)) { michael@0: if (ptypeChange) michael@0: *ptypeChange = true; michael@0: return nullptr; michael@0: } michael@0: michael@0: return MConstant::New(alloc, ret); michael@0: } michael@0: michael@0: void michael@0: MDefinition::printName(FILE *fp) const michael@0: { michael@0: PrintOpcodeName(fp, op()); michael@0: fprintf(fp, "%u", id()); michael@0: michael@0: if (valueNumber() != 0) michael@0: fprintf(fp, "-vn%u", valueNumber()); michael@0: } michael@0: michael@0: HashNumber michael@0: MDefinition::valueHash() const michael@0: { michael@0: HashNumber out = op(); michael@0: for (size_t i = 0, e = numOperands(); i < e; i++) { michael@0: uint32_t valueNumber = getOperand(i)->valueNumber(); michael@0: out = valueNumber + (out << 6) + (out << 16) - out; michael@0: } michael@0: return out; michael@0: } michael@0: michael@0: bool michael@0: MDefinition::congruentIfOperandsEqual(const MDefinition *ins) const michael@0: { michael@0: if (op() != ins->op()) michael@0: return false; michael@0: michael@0: if (type() != ins->type()) michael@0: return false; michael@0: michael@0: if (isEffectful() || ins->isEffectful()) michael@0: return false; michael@0: michael@0: if (numOperands() != ins->numOperands()) michael@0: return false; michael@0: michael@0: for (size_t i = 0, e = numOperands(); i < e; i++) { michael@0: if (getOperand(i)->valueNumber() != ins->getOperand(i)->valueNumber()) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: MDefinition * michael@0: MDefinition::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: // In the default case, there are no constants to fold. michael@0: return this; michael@0: } michael@0: michael@0: void michael@0: MDefinition::analyzeEdgeCasesForward() michael@0: { michael@0: } michael@0: michael@0: void michael@0: MDefinition::analyzeEdgeCasesBackward() michael@0: { michael@0: } michael@0: michael@0: static bool michael@0: MaybeEmulatesUndefined(MDefinition *op) michael@0: { michael@0: if (!op->mightBeType(MIRType_Object)) michael@0: return false; michael@0: michael@0: types::TemporaryTypeSet *types = op->resultTypeSet(); michael@0: if (!types) michael@0: return true; michael@0: michael@0: return types->maybeEmulatesUndefined(); michael@0: } michael@0: michael@0: static bool michael@0: MaybeCallable(MDefinition *op) michael@0: { michael@0: if (!op->mightBeType(MIRType_Object)) michael@0: return false; michael@0: michael@0: types::TemporaryTypeSet *types = op->resultTypeSet(); michael@0: if (!types) michael@0: return true; michael@0: michael@0: return types->maybeCallable(); michael@0: } michael@0: michael@0: MTest * michael@0: MTest::New(TempAllocator &alloc, MDefinition *ins, MBasicBlock *ifTrue, MBasicBlock *ifFalse) michael@0: { michael@0: return new(alloc) MTest(ins, ifTrue, ifFalse); michael@0: } michael@0: michael@0: void michael@0: MTest::infer() michael@0: { michael@0: JS_ASSERT(operandMightEmulateUndefined()); michael@0: michael@0: if (!MaybeEmulatesUndefined(getOperand(0))) michael@0: markOperandCantEmulateUndefined(); michael@0: } michael@0: michael@0: MDefinition * michael@0: MTest::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: MDefinition *op = getOperand(0); michael@0: michael@0: if (op->isNot()) michael@0: return MTest::New(alloc, op->toNot()->operand(), ifFalse(), ifTrue()); michael@0: michael@0: return this; michael@0: } michael@0: michael@0: void michael@0: MTest::filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined, michael@0: bool *filtersNull) michael@0: { michael@0: MDefinition *ins = getOperand(0); michael@0: if (ins->isCompare()) { michael@0: ins->toCompare()->filtersUndefinedOrNull(trueBranch, subject, filtersUndefined, filtersNull); michael@0: return; michael@0: } michael@0: michael@0: if (!trueBranch && ins->isNot()) { michael@0: *subject = ins->getOperand(0); michael@0: *filtersUndefined = *filtersNull = true; michael@0: return; michael@0: } michael@0: michael@0: if (trueBranch) { michael@0: *subject = ins; michael@0: *filtersUndefined = *filtersNull = true; michael@0: return; michael@0: } michael@0: michael@0: *filtersUndefined = *filtersNull = false; michael@0: *subject = nullptr; michael@0: } michael@0: michael@0: void michael@0: MDefinition::printOpcode(FILE *fp) const michael@0: { michael@0: PrintOpcodeName(fp, op()); michael@0: for (size_t j = 0, e = numOperands(); j < e; j++) { michael@0: fprintf(fp, " "); michael@0: getOperand(j)->printName(fp); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MDefinition::dump(FILE *fp) const michael@0: { michael@0: printName(fp); michael@0: fprintf(fp, " = "); michael@0: printOpcode(fp); michael@0: fprintf(fp, "\n"); michael@0: } michael@0: michael@0: void michael@0: MDefinition::dump() const michael@0: { michael@0: dump(stderr); michael@0: } michael@0: michael@0: size_t michael@0: MDefinition::useCount() const michael@0: { michael@0: size_t count = 0; michael@0: for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) michael@0: count++; michael@0: return count; michael@0: } michael@0: michael@0: size_t michael@0: MDefinition::defUseCount() const michael@0: { michael@0: size_t count = 0; michael@0: for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) michael@0: if ((*i)->consumer()->isDefinition()) michael@0: count++; michael@0: return count; michael@0: } michael@0: michael@0: bool michael@0: MDefinition::hasOneUse() const michael@0: { michael@0: MUseIterator i(uses_.begin()); michael@0: if (i == uses_.end()) michael@0: return false; michael@0: i++; michael@0: return i == uses_.end(); michael@0: } michael@0: michael@0: bool michael@0: MDefinition::hasOneDefUse() const michael@0: { michael@0: bool hasOneDefUse = false; michael@0: for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { michael@0: if (!(*i)->consumer()->isDefinition()) michael@0: continue; michael@0: michael@0: // We already have a definition use. So 1+ michael@0: if (hasOneDefUse) michael@0: return false; michael@0: michael@0: // We saw one definition. Loop to test if there is another. michael@0: hasOneDefUse = true; michael@0: } michael@0: michael@0: return hasOneDefUse; michael@0: } michael@0: michael@0: bool michael@0: MDefinition::hasDefUses() const michael@0: { michael@0: for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) { michael@0: if ((*i)->consumer()->isDefinition()) michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: MUseIterator michael@0: MDefinition::removeUse(MUseIterator use) michael@0: { michael@0: return uses_.removeAt(use); michael@0: } michael@0: michael@0: MUseIterator michael@0: MNode::replaceOperand(MUseIterator use, MDefinition *def) michael@0: { michael@0: JS_ASSERT(def != nullptr); michael@0: uint32_t index = use->index(); michael@0: MDefinition *prev = use->producer(); michael@0: michael@0: JS_ASSERT(use->index() < numOperands()); michael@0: JS_ASSERT(use->producer() == getOperand(index)); michael@0: JS_ASSERT(use->consumer() == this); michael@0: michael@0: if (prev == def) michael@0: return use; michael@0: michael@0: MUseIterator result(prev->removeUse(use)); michael@0: setOperand(index, def); michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: MNode::replaceOperand(size_t index, MDefinition *def) michael@0: { michael@0: JS_ASSERT(def != nullptr); michael@0: MUse *use = getUseFor(index); michael@0: MDefinition *prev = use->producer(); michael@0: michael@0: JS_ASSERT(use->index() == index); michael@0: JS_ASSERT(use->index() < numOperands()); michael@0: JS_ASSERT(use->producer() == getOperand(index)); michael@0: JS_ASSERT(use->consumer() == this); michael@0: michael@0: if (prev == def) michael@0: return; michael@0: michael@0: prev->removeUse(use); michael@0: setOperand(index, def); michael@0: } michael@0: michael@0: void michael@0: MNode::discardOperand(size_t index) michael@0: { michael@0: MUse *use = getUseFor(index); michael@0: michael@0: JS_ASSERT(use->index() == index); michael@0: JS_ASSERT(use->producer() == getOperand(index)); michael@0: JS_ASSERT(use->consumer() == this); michael@0: michael@0: use->producer()->removeUse(use); michael@0: michael@0: #ifdef DEBUG michael@0: // Causes any producer/consumer lookups to trip asserts. michael@0: use->set(nullptr, nullptr, index); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: MDefinition::replaceAllUsesWith(MDefinition *dom) michael@0: { michael@0: JS_ASSERT(dom != nullptr); michael@0: if (dom == this) michael@0: return; michael@0: michael@0: for (size_t i = 0, e = numOperands(); i < e; i++) michael@0: getOperand(i)->setUseRemovedUnchecked(); michael@0: michael@0: for (MUseIterator i(usesBegin()); i != usesEnd(); ) { michael@0: JS_ASSERT(i->producer() == this); michael@0: i = i->consumer()->replaceOperand(i, dom); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: MDefinition::emptyResultTypeSet() const michael@0: { michael@0: return resultTypeSet() && resultTypeSet()->empty(); michael@0: } michael@0: michael@0: MConstant * michael@0: MConstant::New(TempAllocator &alloc, const Value &v, types::CompilerConstraintList *constraints) michael@0: { michael@0: return new(alloc) MConstant(v, constraints); michael@0: } michael@0: michael@0: MConstant * michael@0: MConstant::NewAsmJS(TempAllocator &alloc, const Value &v, MIRType type) michael@0: { michael@0: MConstant *constant = new(alloc) MConstant(v, nullptr); michael@0: constant->setResultType(type); michael@0: return constant; michael@0: } michael@0: michael@0: types::TemporaryTypeSet * michael@0: jit::MakeSingletonTypeSet(types::CompilerConstraintList *constraints, JSObject *obj) michael@0: { michael@0: // Invalidate when this object's TypeObject gets unknown properties. This michael@0: // happens for instance when we mutate an object's __proto__, in this case michael@0: // we want to invalidate and mark this TypeSet as containing AnyObject michael@0: // (because mutating __proto__ will change an object's TypeObject). michael@0: JS_ASSERT(constraints); michael@0: types::TypeObjectKey *objType = types::TypeObjectKey::get(obj); michael@0: objType->hasFlags(constraints, types::OBJECT_FLAG_UNKNOWN_PROPERTIES); michael@0: michael@0: return GetIonContext()->temp->lifoAlloc()->new_(types::Type::ObjectType(obj)); michael@0: } michael@0: michael@0: MConstant::MConstant(const js::Value &vp, types::CompilerConstraintList *constraints) michael@0: : value_(vp) michael@0: { michael@0: setResultType(MIRTypeFromValue(vp)); michael@0: if (vp.isObject()) { michael@0: // Create a singleton type set for the object. This isn't necessary for michael@0: // other types as the result type encodes all needed information. michael@0: setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject())); michael@0: } michael@0: michael@0: setMovable(); michael@0: } michael@0: michael@0: HashNumber michael@0: MConstant::valueHash() const michael@0: { michael@0: // This disregards some state, since values are 64 bits. But for a hash, michael@0: // it's completely acceptable. michael@0: return (HashNumber)JSVAL_TO_IMPL(value_).asBits; michael@0: } michael@0: bool michael@0: MConstant::congruentTo(const MDefinition *ins) const michael@0: { michael@0: if (!ins->isConstant()) michael@0: return false; michael@0: return ins->toConstant()->value() == value(); michael@0: } michael@0: michael@0: void michael@0: MConstant::printOpcode(FILE *fp) const michael@0: { michael@0: PrintOpcodeName(fp, op()); michael@0: fprintf(fp, " "); michael@0: switch (type()) { michael@0: case MIRType_Undefined: michael@0: fprintf(fp, "undefined"); michael@0: break; michael@0: case MIRType_Null: michael@0: fprintf(fp, "null"); michael@0: break; michael@0: case MIRType_Boolean: michael@0: fprintf(fp, value().toBoolean() ? "true" : "false"); michael@0: break; michael@0: case MIRType_Int32: michael@0: fprintf(fp, "0x%x", value().toInt32()); michael@0: break; michael@0: case MIRType_Double: michael@0: fprintf(fp, "%f", value().toDouble()); michael@0: break; michael@0: case MIRType_Float32: michael@0: { michael@0: float val = value().toDouble(); michael@0: fprintf(fp, "%f", val); michael@0: break; michael@0: } michael@0: case MIRType_Object: michael@0: if (value().toObject().is()) { michael@0: JSFunction *fun = &value().toObject().as(); michael@0: if (fun->displayAtom()) { michael@0: fputs("function ", fp); michael@0: FileEscapedString(fp, fun->displayAtom(), 0); michael@0: } else { michael@0: fputs("unnamed function", fp); michael@0: } michael@0: if (fun->hasScript()) { michael@0: JSScript *script = fun->nonLazyScript(); michael@0: fprintf(fp, " (%s:%d)", michael@0: script->filename() ? script->filename() : "", (int) script->lineno()); michael@0: } michael@0: fprintf(fp, " at %p", (void *) fun); michael@0: break; michael@0: } michael@0: fprintf(fp, "object %p (%s)", (void *)&value().toObject(), michael@0: value().toObject().getClass()->name); michael@0: break; michael@0: case MIRType_String: michael@0: fprintf(fp, "string %p", (void *)value().toString()); michael@0: break; michael@0: case MIRType_MagicOptimizedArguments: michael@0: fprintf(fp, "magic lazyargs"); michael@0: break; michael@0: case MIRType_MagicHole: michael@0: fprintf(fp, "magic hole"); michael@0: break; michael@0: case MIRType_MagicIsConstructing: michael@0: fprintf(fp, "magic is-constructing"); michael@0: break; michael@0: case MIRType_MagicOptimizedOut: michael@0: fprintf(fp, "magic optimized-out"); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected type"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: MConstant::canProduceFloat32() const michael@0: { michael@0: if (!IsNumberType(type())) michael@0: return false; michael@0: michael@0: if (type() == MIRType_Int32) michael@0: return IsFloat32Representable(static_cast(value_.toInt32())); michael@0: if (type() == MIRType_Double) michael@0: return IsFloat32Representable(value_.toDouble()); michael@0: return true; michael@0: } michael@0: michael@0: MCloneLiteral * michael@0: MCloneLiteral::New(TempAllocator &alloc, MDefinition *obj) michael@0: { michael@0: return new(alloc) MCloneLiteral(obj); michael@0: } michael@0: michael@0: void michael@0: MControlInstruction::printOpcode(FILE *fp) const michael@0: { michael@0: MDefinition::printOpcode(fp); michael@0: for (size_t j = 0; j < numSuccessors(); j++) michael@0: fprintf(fp, " block%d", getSuccessor(j)->id()); michael@0: } michael@0: michael@0: void michael@0: MCompare::printOpcode(FILE *fp) const michael@0: { michael@0: MDefinition::printOpcode(fp); michael@0: fprintf(fp, " %s", js_CodeName[jsop()]); michael@0: } michael@0: michael@0: void michael@0: MConstantElements::printOpcode(FILE *fp) const michael@0: { michael@0: PrintOpcodeName(fp, op()); michael@0: fprintf(fp, " %p", value()); michael@0: } michael@0: michael@0: void michael@0: MLoadTypedArrayElement::printOpcode(FILE *fp) const michael@0: { michael@0: MDefinition::printOpcode(fp); michael@0: fprintf(fp, " %s", ScalarTypeDescr::typeName(arrayType())); michael@0: } michael@0: michael@0: void michael@0: MAssertRange::printOpcode(FILE *fp) const michael@0: { michael@0: MDefinition::printOpcode(fp); michael@0: Sprinter sp(GetIonContext()->cx); michael@0: sp.init(); michael@0: assertedRange()->print(sp); michael@0: fprintf(fp, " %s", sp.string()); michael@0: } michael@0: michael@0: const char * michael@0: MMathFunction::FunctionName(Function function) michael@0: { michael@0: switch (function) { michael@0: case Log: return "Log"; michael@0: case Sin: return "Sin"; michael@0: case Cos: return "Cos"; michael@0: case Exp: return "Exp"; michael@0: case Tan: return "Tan"; michael@0: case ACos: return "ACos"; michael@0: case ASin: return "ASin"; michael@0: case ATan: return "ATan"; michael@0: case Log10: return "Log10"; michael@0: case Log2: return "Log2"; michael@0: case Log1P: return "Log1P"; michael@0: case ExpM1: return "ExpM1"; michael@0: case CosH: return "CosH"; michael@0: case SinH: return "SinH"; michael@0: case TanH: return "TanH"; michael@0: case ACosH: return "ACosH"; michael@0: case ASinH: return "ASinH"; michael@0: case ATanH: return "ATanH"; michael@0: case Sign: return "Sign"; michael@0: case Trunc: return "Trunc"; michael@0: case Cbrt: return "Cbrt"; michael@0: case Floor: return "Floor"; michael@0: case Ceil: return "Ceil"; michael@0: case Round: return "Round"; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unknown math function"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MMathFunction::printOpcode(FILE *fp) const michael@0: { michael@0: MDefinition::printOpcode(fp); michael@0: fprintf(fp, " %s", FunctionName(function())); michael@0: } michael@0: michael@0: MParameter * michael@0: MParameter::New(TempAllocator &alloc, int32_t index, types::TemporaryTypeSet *types) michael@0: { michael@0: return new(alloc) MParameter(index, types); michael@0: } michael@0: michael@0: void michael@0: MParameter::printOpcode(FILE *fp) const michael@0: { michael@0: PrintOpcodeName(fp, op()); michael@0: fprintf(fp, " %d", index()); michael@0: } michael@0: michael@0: HashNumber michael@0: MParameter::valueHash() const michael@0: { michael@0: return index_; // Why not? michael@0: } michael@0: michael@0: bool michael@0: MParameter::congruentTo(const MDefinition *ins) const michael@0: { michael@0: if (!ins->isParameter()) michael@0: return false; michael@0: michael@0: return ins->toParameter()->index() == index_; michael@0: } michael@0: michael@0: MCall * michael@0: MCall::New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, size_t numActualArgs, michael@0: bool construct, bool isDOMCall) michael@0: { michael@0: JS_ASSERT(maxArgc >= numActualArgs); michael@0: MCall *ins; michael@0: if (isDOMCall) { michael@0: JS_ASSERT(!construct); michael@0: ins = new(alloc) MCallDOMNative(target, numActualArgs); michael@0: } else { michael@0: ins = new(alloc) MCall(target, numActualArgs, construct); michael@0: } michael@0: if (!ins->init(alloc, maxArgc + NumNonArgumentOperands)) michael@0: return nullptr; michael@0: return ins; michael@0: } michael@0: michael@0: AliasSet michael@0: MCallDOMNative::getAliasSet() const michael@0: { michael@0: const JSJitInfo *jitInfo = getJitInfo(); michael@0: michael@0: JS_ASSERT(jitInfo->aliasSet() != JSJitInfo::AliasNone); michael@0: // If we don't know anything about the types of our arguments, we have to michael@0: // assume that type-coercions can have side-effects, so we need to alias michael@0: // everything. michael@0: if (jitInfo->aliasSet() != JSJitInfo::AliasDOMSets || !jitInfo->isTypedMethodJitInfo()) michael@0: return AliasSet::Store(AliasSet::Any); michael@0: michael@0: uint32_t argIndex = 0; michael@0: const JSTypedMethodJitInfo *methodInfo = michael@0: reinterpret_cast(jitInfo); michael@0: for (const JSJitInfo::ArgType *argType = methodInfo->argTypes; michael@0: *argType != JSJitInfo::ArgTypeListEnd; michael@0: ++argType, ++argIndex) michael@0: { michael@0: if (argIndex >= numActualArgs()) { michael@0: // Passing through undefined can't have side-effects michael@0: continue; michael@0: } michael@0: // getArg(0) is "this", so skip it michael@0: MDefinition *arg = getArg(argIndex+1); michael@0: MIRType actualType = arg->type(); michael@0: // The only way to reliably avoid side-effects given the informtion we michael@0: // have here is if we're passing in a known primitive value to an michael@0: // argument that expects a primitive value. XXXbz maybe we need to michael@0: // communicate better information. For example, a sequence argument michael@0: // will sort of unavoidably have side effects, while a typed array michael@0: // argument won't have any, but both are claimed to be michael@0: // JSJitInfo::Object. michael@0: if ((actualType == MIRType_Value || actualType == MIRType_Object) || michael@0: (*argType & JSJitInfo::Object)) michael@0: { michael@0: return AliasSet::Store(AliasSet::Any); michael@0: } michael@0: } michael@0: michael@0: // We checked all the args, and they check out. So we only michael@0: // alias DOM mutations. michael@0: return AliasSet::Load(AliasSet::DOMProperty); michael@0: } michael@0: michael@0: void michael@0: MCallDOMNative::computeMovable() michael@0: { michael@0: // We are movable if the jitinfo says we can be and if we're also not michael@0: // effectful. The jitinfo can't check for the latter, since it depends on michael@0: // the types of our arguments. michael@0: const JSJitInfo *jitInfo = getJitInfo(); michael@0: michael@0: JS_ASSERT_IF(jitInfo->isMovable, michael@0: jitInfo->aliasSet() != JSJitInfo::AliasEverything); michael@0: michael@0: if (jitInfo->isMovable && !isEffectful()) michael@0: setMovable(); michael@0: } michael@0: michael@0: bool michael@0: MCallDOMNative::congruentTo(const MDefinition *ins) const michael@0: { michael@0: if (!isMovable()) michael@0: return false; michael@0: michael@0: if (!ins->isCall()) michael@0: return false; michael@0: michael@0: const MCall *call = ins->toCall(); michael@0: michael@0: if (!call->isCallDOMNative()) michael@0: return false; michael@0: michael@0: if (getSingleTarget() != call->getSingleTarget()) michael@0: return false; michael@0: michael@0: if (isConstructing() != call->isConstructing()) michael@0: return false; michael@0: michael@0: if (numActualArgs() != call->numActualArgs()) michael@0: return false; michael@0: michael@0: if (needsArgCheck() != call->needsArgCheck()) michael@0: return false; michael@0: michael@0: if (!congruentIfOperandsEqual(call)) michael@0: return false; michael@0: michael@0: // The other call had better be movable at this point! michael@0: JS_ASSERT(call->isMovable()); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: const JSJitInfo * michael@0: MCallDOMNative::getJitInfo() const michael@0: { michael@0: JS_ASSERT(getSingleTarget() && getSingleTarget()->isNative()); michael@0: michael@0: const JSJitInfo *jitInfo = getSingleTarget()->jitInfo(); michael@0: JS_ASSERT(jitInfo); michael@0: michael@0: return jitInfo; michael@0: } michael@0: michael@0: MApplyArgs * michael@0: MApplyArgs::New(TempAllocator &alloc, JSFunction *target, MDefinition *fun, MDefinition *argc, michael@0: MDefinition *self) michael@0: { michael@0: return new(alloc) MApplyArgs(target, fun, argc, self); michael@0: } michael@0: michael@0: MDefinition* michael@0: MStringLength::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: if ((type() == MIRType_Int32) && (string()->isConstant())) { michael@0: Value value = string()->toConstant()->value(); michael@0: JSAtom *atom = &value.toString()->asAtom(); michael@0: return MConstant::New(alloc, Int32Value(atom->length())); michael@0: } michael@0: michael@0: return this; michael@0: } michael@0: michael@0: void michael@0: MFloor::trySpecializeFloat32(TempAllocator &alloc) michael@0: { michael@0: // No need to look at the output, as it's an integer (see IonBuilder::inlineMathFloor) michael@0: if (!input()->canProduceFloat32()) { michael@0: if (input()->type() == MIRType_Float32) michael@0: ConvertDefinitionToDouble<0>(alloc, input(), this); michael@0: return; michael@0: } michael@0: michael@0: if (type() == MIRType_Double) michael@0: setResultType(MIRType_Float32); michael@0: michael@0: setPolicyType(MIRType_Float32); michael@0: } michael@0: michael@0: void michael@0: MRound::trySpecializeFloat32(TempAllocator &alloc) michael@0: { michael@0: // No need to look at the output, as it's an integer (unique way to have michael@0: // this instruction in IonBuilder::inlineMathRound) michael@0: JS_ASSERT(type() == MIRType_Int32); michael@0: michael@0: if (!input()->canProduceFloat32()) { michael@0: if (input()->type() == MIRType_Float32) michael@0: ConvertDefinitionToDouble<0>(alloc, input(), this); michael@0: return; michael@0: } michael@0: michael@0: setPolicyType(MIRType_Float32); michael@0: } michael@0: michael@0: MCompare * michael@0: MCompare::New(TempAllocator &alloc, MDefinition *left, MDefinition *right, JSOp op) michael@0: { michael@0: return new(alloc) MCompare(left, right, op); michael@0: } michael@0: michael@0: MCompare * michael@0: MCompare::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, JSOp op, michael@0: CompareType compareType) michael@0: { michael@0: JS_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 || michael@0: compareType == Compare_Double || compareType == Compare_Float32); michael@0: MCompare *comp = new(alloc) MCompare(left, right, op); michael@0: comp->compareType_ = compareType; michael@0: comp->operandMightEmulateUndefined_ = false; michael@0: comp->setResultType(MIRType_Int32); michael@0: return comp; michael@0: } michael@0: michael@0: MTableSwitch * michael@0: MTableSwitch::New(TempAllocator &alloc, MDefinition *ins, int32_t low, int32_t high) michael@0: { michael@0: return new(alloc) MTableSwitch(alloc, ins, low, high); michael@0: } michael@0: michael@0: MGoto * michael@0: MGoto::New(TempAllocator &alloc, MBasicBlock *target) michael@0: { michael@0: JS_ASSERT(target); michael@0: return new(alloc) MGoto(target); michael@0: } michael@0: michael@0: void michael@0: MUnbox::printOpcode(FILE *fp) const michael@0: { michael@0: PrintOpcodeName(fp, op()); michael@0: fprintf(fp, " "); michael@0: getOperand(0)->printName(fp); michael@0: fprintf(fp, " "); michael@0: michael@0: switch (type()) { michael@0: case MIRType_Int32: fprintf(fp, "to Int32"); break; michael@0: case MIRType_Double: fprintf(fp, "to Double"); break; michael@0: case MIRType_Boolean: fprintf(fp, "to Boolean"); break; michael@0: case MIRType_String: fprintf(fp, "to String"); break; michael@0: case MIRType_Object: fprintf(fp, "to Object"); break; michael@0: default: break; michael@0: } michael@0: michael@0: switch (mode()) { michael@0: case Fallible: fprintf(fp, " (fallible)"); break; michael@0: case Infallible: fprintf(fp, " (infallible)"); break; michael@0: case TypeBarrier: fprintf(fp, " (typebarrier)"); break; michael@0: default: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: MTypeBarrier::printOpcode(FILE *fp) const michael@0: { michael@0: PrintOpcodeName(fp, op()); michael@0: fprintf(fp, " "); michael@0: getOperand(0)->printName(fp); michael@0: } michael@0: michael@0: void michael@0: MPhi::removeOperand(size_t index) michael@0: { michael@0: MUse *use = getUseFor(index); michael@0: michael@0: JS_ASSERT(index < inputs_.length()); michael@0: JS_ASSERT(inputs_.length() > 1); michael@0: michael@0: JS_ASSERT(use->index() == index); michael@0: JS_ASSERT(use->producer() == getOperand(index)); michael@0: JS_ASSERT(use->consumer() == this); michael@0: michael@0: // Remove use from producer's use chain. michael@0: use->producer()->removeUse(use); michael@0: michael@0: // If we have phi(..., a, b, c, d, ..., z) and we plan michael@0: // on removing a, then first shift downward so that we have michael@0: // phi(..., b, c, d, ..., z, z): michael@0: size_t length = inputs_.length(); michael@0: for (size_t i = index; i < length - 1; i++) { michael@0: MUse *next = MPhi::getUseFor(i + 1); michael@0: next->producer()->removeUse(next); michael@0: MPhi::setOperand(i, next->producer()); michael@0: } michael@0: michael@0: // truncate the inputs_ list: michael@0: inputs_.shrinkBy(1); michael@0: } michael@0: michael@0: MDefinition * michael@0: MPhi::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: JS_ASSERT(!inputs_.empty()); michael@0: michael@0: MDefinition *first = getOperand(0); michael@0: michael@0: for (size_t i = 1; i < inputs_.length(); i++) { michael@0: // Phis need dominator information to fold based on value numbers. For michael@0: // simplicity, we only compare SSA names right now (bug 714727). michael@0: if (!EqualValues(false, getOperand(i), first)) michael@0: return this; michael@0: } michael@0: michael@0: return first; michael@0: } michael@0: michael@0: bool michael@0: MPhi::congruentTo(const MDefinition *ins) const michael@0: { michael@0: if (!ins->isPhi()) michael@0: return false; michael@0: // Since we do not know which predecessor we are merging from, we must michael@0: // assume that phi instructions in different blocks are not equal. michael@0: // (Bug 674656) michael@0: if (ins->block()->id() != block()->id()) michael@0: return false; michael@0: michael@0: return congruentIfOperandsEqual(ins); michael@0: } michael@0: michael@0: bool michael@0: MPhi::reserveLength(size_t length) michael@0: { michael@0: // Initializes a new MPhi to have an Operand vector of at least the given michael@0: // capacity. This permits use of addInput() instead of addInputSlow(), the michael@0: // latter of which may call realloc_(). michael@0: JS_ASSERT(numOperands() == 0); michael@0: #if DEBUG michael@0: capacity_ = length; michael@0: #endif michael@0: return inputs_.reserve(length); michael@0: } michael@0: michael@0: static inline types::TemporaryTypeSet * michael@0: MakeMIRTypeSet(MIRType type) michael@0: { michael@0: JS_ASSERT(type != MIRType_Value); michael@0: types::Type ntype = type == MIRType_Object michael@0: ? types::Type::AnyObjectType() michael@0: : types::Type::PrimitiveType(ValueTypeFromMIRType(type)); michael@0: return GetIonContext()->temp->lifoAlloc()->new_(ntype); michael@0: } michael@0: michael@0: bool michael@0: jit::MergeTypes(MIRType *ptype, types::TemporaryTypeSet **ptypeSet, michael@0: MIRType newType, types::TemporaryTypeSet *newTypeSet) michael@0: { michael@0: if (newTypeSet && newTypeSet->empty()) michael@0: return true; michael@0: if (newType != *ptype) { michael@0: if (IsNumberType(newType) && IsNumberType(*ptype)) { michael@0: *ptype = MIRType_Double; michael@0: } else if (*ptype != MIRType_Value) { michael@0: if (!*ptypeSet) { michael@0: *ptypeSet = MakeMIRTypeSet(*ptype); michael@0: if (!*ptypeSet) michael@0: return false; michael@0: } michael@0: *ptype = MIRType_Value; michael@0: } else if (*ptypeSet && (*ptypeSet)->empty()) { michael@0: *ptype = newType; michael@0: } michael@0: } michael@0: if (*ptypeSet) { michael@0: LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); michael@0: if (!newTypeSet && newType != MIRType_Value) { michael@0: newTypeSet = MakeMIRTypeSet(newType); michael@0: if (!newTypeSet) michael@0: return false; michael@0: } michael@0: if (newTypeSet) { michael@0: if (!newTypeSet->isSubset(*ptypeSet)) michael@0: *ptypeSet = types::TypeSet::unionSets(*ptypeSet, newTypeSet, alloc); michael@0: } else { michael@0: *ptypeSet = nullptr; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MPhi::specializeType() michael@0: { michael@0: #ifdef DEBUG michael@0: JS_ASSERT(!specialized_); michael@0: specialized_ = true; michael@0: #endif michael@0: michael@0: JS_ASSERT(!inputs_.empty()); michael@0: michael@0: size_t start; michael@0: if (hasBackedgeType_) { michael@0: // The type of this phi has already been populated with potential types michael@0: // that could come in via loop backedges. michael@0: start = 0; michael@0: } else { michael@0: setResultType(getOperand(0)->type()); michael@0: setResultTypeSet(getOperand(0)->resultTypeSet()); michael@0: start = 1; michael@0: } michael@0: michael@0: MIRType resultType = this->type(); michael@0: types::TemporaryTypeSet *resultTypeSet = this->resultTypeSet(); michael@0: michael@0: for (size_t i = start; i < inputs_.length(); i++) { michael@0: MDefinition *def = getOperand(i); michael@0: if (!MergeTypes(&resultType, &resultTypeSet, def->type(), def->resultTypeSet())) michael@0: return false; michael@0: } michael@0: michael@0: setResultType(resultType); michael@0: setResultTypeSet(resultTypeSet); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MPhi::addBackedgeType(MIRType type, types::TemporaryTypeSet *typeSet) michael@0: { michael@0: JS_ASSERT(!specialized_); michael@0: michael@0: if (hasBackedgeType_) { michael@0: MIRType resultType = this->type(); michael@0: types::TemporaryTypeSet *resultTypeSet = this->resultTypeSet(); michael@0: michael@0: if (!MergeTypes(&resultType, &resultTypeSet, type, typeSet)) michael@0: return false; michael@0: michael@0: setResultType(resultType); michael@0: setResultTypeSet(resultTypeSet); michael@0: } else { michael@0: setResultType(type); michael@0: setResultTypeSet(typeSet); michael@0: hasBackedgeType_ = true; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MPhi::typeIncludes(MDefinition *def) michael@0: { michael@0: if (def->type() == MIRType_Int32 && this->type() == MIRType_Double) michael@0: return true; michael@0: michael@0: if (types::TemporaryTypeSet *types = def->resultTypeSet()) { michael@0: if (this->resultTypeSet()) michael@0: return types->isSubset(this->resultTypeSet()); michael@0: if (this->type() == MIRType_Value || types->empty()) michael@0: return true; michael@0: return this->type() == types->getKnownMIRType(); michael@0: } michael@0: michael@0: if (def->type() == MIRType_Value) { michael@0: // This phi must be able to be any value. michael@0: return this->type() == MIRType_Value michael@0: && (!this->resultTypeSet() || this->resultTypeSet()->unknown()); michael@0: } michael@0: michael@0: return this->mightBeType(def->type()); michael@0: } michael@0: michael@0: void michael@0: MPhi::addInput(MDefinition *ins) michael@0: { michael@0: // This can only been done if the length was reserved through reserveLength, michael@0: // else the slower addInputSlow need to get called. michael@0: JS_ASSERT(inputs_.length() < capacity_); michael@0: michael@0: uint32_t index = inputs_.length(); michael@0: inputs_.append(MUse()); michael@0: MPhi::setOperand(index, ins); michael@0: } michael@0: michael@0: bool michael@0: MPhi::addInputSlow(MDefinition *ins, bool *ptypeChange) michael@0: { michael@0: // The list of inputs to an MPhi is given as a vector of MUse nodes, michael@0: // each of which is in the list of the producer MDefinition. michael@0: // Because appending to a vector may reallocate the vector, it is possible michael@0: // that this operation may cause the producers' linked lists to reference michael@0: // invalid memory. Therefore, in the event of moving reallocation, each michael@0: // MUse must be removed and reinserted from/into its producer's use chain. michael@0: uint32_t index = inputs_.length(); michael@0: bool performingRealloc = !inputs_.canAppendWithoutRealloc(1); michael@0: michael@0: // Remove all MUses from all use lists, in case realloc_() moves. michael@0: if (performingRealloc) { michael@0: for (uint32_t i = 0; i < index; i++) { michael@0: MUse *use = &inputs_[i]; michael@0: use->producer()->removeUse(use); michael@0: } michael@0: } michael@0: michael@0: // Insert the new input. michael@0: if (!inputs_.append(MUse())) michael@0: return false; michael@0: michael@0: MPhi::setOperand(index, ins); michael@0: michael@0: if (ptypeChange) { michael@0: MIRType resultType = this->type(); michael@0: types::TemporaryTypeSet *resultTypeSet = this->resultTypeSet(); michael@0: michael@0: if (!MergeTypes(&resultType, &resultTypeSet, ins->type(), ins->resultTypeSet())) michael@0: return false; michael@0: michael@0: if (resultType != this->type() || resultTypeSet != this->resultTypeSet()) { michael@0: *ptypeChange = true; michael@0: setResultType(resultType); michael@0: setResultTypeSet(resultTypeSet); michael@0: } michael@0: } michael@0: michael@0: // Add all previously-removed MUses back. michael@0: if (performingRealloc) { michael@0: for (uint32_t i = 0; i < index; i++) { michael@0: MUse *use = &inputs_[i]; michael@0: use->producer()->addUse(use); michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MCall::addArg(size_t argnum, MDefinition *arg) michael@0: { michael@0: // The operand vector is initialized in reverse order by the IonBuilder. michael@0: // It cannot be checked for consistency until all arguments are added. michael@0: setOperand(argnum + NumNonArgumentOperands, arg); michael@0: } michael@0: michael@0: void michael@0: MBitNot::infer() michael@0: { michael@0: if (getOperand(0)->mightBeType(MIRType_Object)) michael@0: specialization_ = MIRType_None; michael@0: else michael@0: specialization_ = MIRType_Int32; michael@0: } michael@0: michael@0: static inline bool michael@0: IsConstant(MDefinition *def, double v) michael@0: { michael@0: if (!def->isConstant()) michael@0: return false; michael@0: michael@0: return NumbersAreIdentical(def->toConstant()->value().toNumber(), v); michael@0: } michael@0: michael@0: MDefinition * michael@0: MBinaryBitwiseInstruction::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: if (specialization_ != MIRType_Int32) michael@0: return this; michael@0: michael@0: if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) michael@0: return folded; michael@0: michael@0: return this; michael@0: } michael@0: michael@0: MDefinition * michael@0: MBinaryBitwiseInstruction::foldUnnecessaryBitop() michael@0: { michael@0: if (specialization_ != MIRType_Int32) michael@0: return this; michael@0: michael@0: // Eliminate bitwise operations that are no-ops when used on integer michael@0: // inputs, such as (x | 0). michael@0: michael@0: MDefinition *lhs = getOperand(0); michael@0: MDefinition *rhs = getOperand(1); michael@0: michael@0: if (IsConstant(lhs, 0)) michael@0: return foldIfZero(0); michael@0: michael@0: if (IsConstant(rhs, 0)) michael@0: return foldIfZero(1); michael@0: michael@0: if (IsConstant(lhs, -1)) michael@0: return foldIfNegOne(0); michael@0: michael@0: if (IsConstant(rhs, -1)) michael@0: return foldIfNegOne(1); michael@0: michael@0: if (EqualValues(false, lhs, rhs)) michael@0: return foldIfEqual(); michael@0: michael@0: return this; michael@0: } michael@0: michael@0: void michael@0: MBinaryBitwiseInstruction::infer(BaselineInspector *, jsbytecode *) michael@0: { michael@0: if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) michael@0: specialization_ = MIRType_None; michael@0: else michael@0: specializeAsInt32(); michael@0: } michael@0: michael@0: void michael@0: MBinaryBitwiseInstruction::specializeAsInt32() michael@0: { michael@0: specialization_ = MIRType_Int32; michael@0: JS_ASSERT(type() == MIRType_Int32); michael@0: michael@0: if (isBitOr() || isBitAnd() || isBitXor()) michael@0: setCommutative(); michael@0: } michael@0: michael@0: void michael@0: MShiftInstruction::infer(BaselineInspector *, jsbytecode *) michael@0: { michael@0: if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) michael@0: specialization_ = MIRType_None; michael@0: else michael@0: specialization_ = MIRType_Int32; michael@0: } michael@0: michael@0: void michael@0: MUrsh::infer(BaselineInspector *inspector, jsbytecode *pc) michael@0: { michael@0: if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) { michael@0: specialization_ = MIRType_None; michael@0: setResultType(MIRType_Value); michael@0: return; michael@0: } michael@0: michael@0: if (inspector->hasSeenDoubleResult(pc)) { michael@0: specialization_ = MIRType_Double; michael@0: setResultType(MIRType_Double); michael@0: return; michael@0: } michael@0: michael@0: specialization_ = MIRType_Int32; michael@0: setResultType(MIRType_Int32); michael@0: } michael@0: michael@0: static inline bool michael@0: NeedNegativeZeroCheck(MDefinition *def) michael@0: { michael@0: // Test if all uses have the same semantics for -0 and 0 michael@0: for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) { michael@0: if (use->consumer()->isResumePoint()) michael@0: continue; michael@0: michael@0: MDefinition *use_def = use->consumer()->toDefinition(); michael@0: switch (use_def->op()) { michael@0: case MDefinition::Op_Add: { michael@0: // If add is truncating -0 and 0 are observed as the same. michael@0: if (use_def->toAdd()->isTruncated()) michael@0: break; michael@0: michael@0: // x + y gives -0, when both x and y are -0 michael@0: michael@0: // Figure out the order in which the addition's operands will michael@0: // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR michael@0: // definitions for us so that this just requires comparing ids. michael@0: MDefinition *first = use_def->toAdd()->getOperand(0); michael@0: MDefinition *second = use_def->toAdd()->getOperand(1); michael@0: if (first->id() > second->id()) { michael@0: MDefinition *temp = first; michael@0: first = second; michael@0: second = temp; michael@0: } michael@0: michael@0: if (def == first) { michael@0: // Negative zero checks can be removed on the first executed michael@0: // operand only if it is guaranteed the second executed operand michael@0: // will produce a value other than -0. While the second is michael@0: // typed as an int32, a bailout taken between execution of the michael@0: // operands may change that type and cause a -0 to flow to the michael@0: // second. michael@0: // michael@0: // There is no way to test whether there are any bailouts michael@0: // between execution of the operands, so remove negative michael@0: // zero checks from the first only if the second's type is michael@0: // independent from type changes that may occur after bailing. michael@0: switch (second->op()) { michael@0: case MDefinition::Op_Constant: michael@0: case MDefinition::Op_BitAnd: michael@0: case MDefinition::Op_BitOr: michael@0: case MDefinition::Op_BitXor: michael@0: case MDefinition::Op_BitNot: michael@0: case MDefinition::Op_Lsh: michael@0: case MDefinition::Op_Rsh: michael@0: break; michael@0: default: michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // The negative zero check can always be removed on the second michael@0: // executed operand; by the time this executes the first will have michael@0: // been evaluated as int32 and the addition's result cannot be -0. michael@0: break; michael@0: } michael@0: case MDefinition::Op_Sub: michael@0: // If sub is truncating -0 and 0 are observed as the same michael@0: if (use_def->toSub()->isTruncated()) michael@0: break; michael@0: /* Fall through... */ michael@0: case MDefinition::Op_StoreElement: michael@0: case MDefinition::Op_StoreElementHole: michael@0: case MDefinition::Op_LoadElement: michael@0: case MDefinition::Op_LoadElementHole: michael@0: case MDefinition::Op_LoadTypedArrayElement: michael@0: case MDefinition::Op_LoadTypedArrayElementHole: michael@0: case MDefinition::Op_CharCodeAt: michael@0: case MDefinition::Op_Mod: michael@0: // Only allowed to remove check when definition is the second operand michael@0: if (use_def->getOperand(0) == def) michael@0: return true; michael@0: for (size_t i = 2, e = use_def->numOperands(); i < e; i++) { michael@0: if (use_def->getOperand(i) == def) michael@0: return true; michael@0: } michael@0: break; michael@0: case MDefinition::Op_BoundsCheck: michael@0: // Only allowed to remove check when definition is the first operand michael@0: if (use_def->toBoundsCheck()->getOperand(1) == def) michael@0: return true; michael@0: break; michael@0: case MDefinition::Op_ToString: michael@0: case MDefinition::Op_FromCharCode: michael@0: case MDefinition::Op_TableSwitch: michael@0: case MDefinition::Op_Compare: michael@0: case MDefinition::Op_BitAnd: michael@0: case MDefinition::Op_BitOr: michael@0: case MDefinition::Op_BitXor: michael@0: case MDefinition::Op_Abs: michael@0: case MDefinition::Op_TruncateToInt32: michael@0: // Always allowed to remove check. No matter which operand. michael@0: break; michael@0: default: michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: MDefinition * michael@0: MBinaryArithInstruction::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: if (specialization_ == MIRType_None) michael@0: return this; michael@0: michael@0: MDefinition *lhs = getOperand(0); michael@0: MDefinition *rhs = getOperand(1); michael@0: if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) michael@0: return folded; michael@0: michael@0: // 0 + -0 = 0. So we can't remove addition michael@0: if (isAdd() && specialization_ != MIRType_Int32) michael@0: return this; michael@0: michael@0: if (IsConstant(rhs, getIdentity())) michael@0: return lhs; michael@0: michael@0: // subtraction isn't commutative. So we can't remove subtraction when lhs equals 0 michael@0: if (isSub()) michael@0: return this; michael@0: michael@0: if (IsConstant(lhs, getIdentity())) michael@0: return rhs; // x op id => x michael@0: michael@0: return this; michael@0: } michael@0: michael@0: void michael@0: MBinaryArithInstruction::trySpecializeFloat32(TempAllocator &alloc) michael@0: { michael@0: MDefinition *left = lhs(); michael@0: MDefinition *right = rhs(); michael@0: michael@0: if (!left->canProduceFloat32() || !right->canProduceFloat32() michael@0: || !CheckUsesAreFloat32Consumers(this)) michael@0: { michael@0: if (left->type() == MIRType_Float32) michael@0: ConvertDefinitionToDouble<0>(alloc, left, this); michael@0: if (right->type() == MIRType_Float32) michael@0: ConvertDefinitionToDouble<1>(alloc, right, this); michael@0: return; michael@0: } michael@0: michael@0: specialization_ = MIRType_Float32; michael@0: setResultType(MIRType_Float32); michael@0: } michael@0: michael@0: bool michael@0: MAbs::fallible() const michael@0: { michael@0: return !implicitTruncate_ && (!range() || !range()->hasInt32Bounds()); michael@0: } michael@0: michael@0: void michael@0: MAbs::trySpecializeFloat32(TempAllocator &alloc) michael@0: { michael@0: if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) { michael@0: if (input()->type() == MIRType_Float32) michael@0: ConvertDefinitionToDouble<0>(alloc, input(), this); michael@0: return; michael@0: } michael@0: michael@0: setResultType(MIRType_Float32); michael@0: specialization_ = MIRType_Float32; michael@0: } michael@0: michael@0: MDefinition * michael@0: MDiv::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: if (specialization_ == MIRType_None) michael@0: return this; michael@0: michael@0: if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) michael@0: return folded; michael@0: michael@0: return this; michael@0: } michael@0: michael@0: void michael@0: MDiv::analyzeEdgeCasesForward() michael@0: { michael@0: // This is only meaningful when doing integer division. michael@0: if (specialization_ != MIRType_Int32) michael@0: return; michael@0: michael@0: // Try removing divide by zero check michael@0: if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(0)) michael@0: canBeDivideByZero_ = false; michael@0: michael@0: // If lhs is a constant int != INT32_MIN, then michael@0: // negative overflow check can be skipped. michael@0: if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(INT32_MIN)) michael@0: canBeNegativeOverflow_ = false; michael@0: michael@0: // If rhs is a constant int != -1, likewise. michael@0: if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(-1)) michael@0: canBeNegativeOverflow_ = false; michael@0: michael@0: // If lhs is != 0, then negative zero check can be skipped. michael@0: if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(0)) michael@0: setCanBeNegativeZero(false); michael@0: michael@0: // If rhs is >= 0, likewise. michael@0: if (rhs()->isConstant()) { michael@0: const js::Value &val = rhs()->toConstant()->value(); michael@0: if (val.isInt32() && val.toInt32() >= 0) michael@0: setCanBeNegativeZero(false); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MDiv::analyzeEdgeCasesBackward() michael@0: { michael@0: if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) michael@0: setCanBeNegativeZero(false); michael@0: } michael@0: michael@0: bool michael@0: MDiv::fallible() const michael@0: { michael@0: return !isTruncated(); michael@0: } michael@0: michael@0: bool michael@0: MMod::canBeDivideByZero() const michael@0: { michael@0: JS_ASSERT(specialization_ == MIRType_Int32); michael@0: return !rhs()->isConstant() || rhs()->toConstant()->value().toInt32() == 0; michael@0: } michael@0: michael@0: bool michael@0: MMod::canBePowerOfTwoDivisor() const michael@0: { michael@0: JS_ASSERT(specialization_ == MIRType_Int32); michael@0: michael@0: if (!rhs()->isConstant()) michael@0: return true; michael@0: michael@0: int32_t i = rhs()->toConstant()->value().toInt32(); michael@0: if (i <= 0 || !IsPowerOfTwo(i)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: MDefinition * michael@0: MMod::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: if (specialization_ == MIRType_None) michael@0: return this; michael@0: michael@0: if (MDefinition *folded = EvaluateConstantOperands(alloc, this)) michael@0: return folded; michael@0: michael@0: return this; michael@0: } michael@0: michael@0: bool michael@0: MMod::fallible() const michael@0: { michael@0: return !isTruncated() && michael@0: (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend()); michael@0: } michael@0: michael@0: void michael@0: MMathFunction::trySpecializeFloat32(TempAllocator &alloc) michael@0: { michael@0: if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) { michael@0: if (input()->type() == MIRType_Float32) michael@0: ConvertDefinitionToDouble<0>(alloc, input(), this); michael@0: return; michael@0: } michael@0: michael@0: setResultType(MIRType_Float32); michael@0: setPolicyType(MIRType_Float32); michael@0: } michael@0: michael@0: bool michael@0: MAdd::fallible() const michael@0: { michael@0: // the add is fallible if range analysis does not say that it is finite, AND michael@0: // either the truncation analysis shows that there are non-truncated uses. michael@0: if (isTruncated()) michael@0: return false; michael@0: if (range() && range()->hasInt32Bounds()) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MSub::fallible() const michael@0: { michael@0: // see comment in MAdd::fallible() michael@0: if (isTruncated()) michael@0: return false; michael@0: if (range() && range()->hasInt32Bounds()) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: MDefinition * michael@0: MMul::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: MDefinition *out = MBinaryArithInstruction::foldsTo(alloc, useValueNumbers); michael@0: if (out != this) michael@0: return out; michael@0: michael@0: if (specialization() != MIRType_Int32) michael@0: return this; michael@0: michael@0: if (EqualValues(useValueNumbers, lhs(), rhs())) michael@0: setCanBeNegativeZero(false); michael@0: michael@0: return this; michael@0: } michael@0: michael@0: void michael@0: MMul::analyzeEdgeCasesForward() michael@0: { michael@0: // Try to remove the check for negative zero michael@0: // This only makes sense when using the integer multiplication michael@0: if (specialization() != MIRType_Int32) michael@0: return; michael@0: michael@0: // If lhs is > 0, no need for negative zero check. michael@0: if (lhs()->isConstant()) { michael@0: const js::Value &val = lhs()->toConstant()->value(); michael@0: if (val.isInt32() && val.toInt32() > 0) michael@0: setCanBeNegativeZero(false); michael@0: } michael@0: michael@0: // If rhs is > 0, likewise. michael@0: if (rhs()->isConstant()) { michael@0: const js::Value &val = rhs()->toConstant()->value(); michael@0: if (val.isInt32() && val.toInt32() > 0) michael@0: setCanBeNegativeZero(false); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MMul::analyzeEdgeCasesBackward() michael@0: { michael@0: if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) michael@0: setCanBeNegativeZero(false); michael@0: } michael@0: michael@0: bool michael@0: MMul::updateForReplacement(MDefinition *ins_) michael@0: { michael@0: MMul *ins = ins_->toMul(); michael@0: bool negativeZero = canBeNegativeZero() || ins->canBeNegativeZero(); michael@0: setCanBeNegativeZero(negativeZero); michael@0: // Remove the imul annotation when merging imul and normal multiplication. michael@0: if (mode_ == Integer && ins->mode() != Integer) michael@0: mode_ = Normal; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MMul::canOverflow() const michael@0: { michael@0: if (isTruncated()) michael@0: return false; michael@0: return !range() || !range()->hasInt32Bounds(); michael@0: } michael@0: michael@0: bool michael@0: MUrsh::fallible() const michael@0: { michael@0: if (bailoutsDisabled()) michael@0: return false; michael@0: return !range() || !range()->hasInt32Bounds(); michael@0: } michael@0: michael@0: static inline bool michael@0: KnownNonStringPrimitive(MDefinition *op) michael@0: { michael@0: return !op->mightBeType(MIRType_Object) michael@0: && !op->mightBeType(MIRType_String) michael@0: && !op->mightBeType(MIRType_MagicOptimizedArguments) michael@0: && !op->mightBeType(MIRType_MagicHole) michael@0: && !op->mightBeType(MIRType_MagicIsConstructing); michael@0: } michael@0: michael@0: void michael@0: MBinaryArithInstruction::infer(TempAllocator &alloc, BaselineInspector *inspector, jsbytecode *pc) michael@0: { michael@0: JS_ASSERT(this->type() == MIRType_Value); michael@0: michael@0: specialization_ = MIRType_None; michael@0: michael@0: // Don't specialize if one operand could be an object. If we specialize michael@0: // as int32 or double based on baseline feedback, we could DCE this michael@0: // instruction and fail to invoke any valueOf methods. michael@0: if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) michael@0: return; michael@0: michael@0: // Anything complex - strings and objects - are not specialized michael@0: // unless baseline type hints suggest it might be profitable michael@0: if (!KnownNonStringPrimitive(getOperand(0)) || !KnownNonStringPrimitive(getOperand(1))) michael@0: return inferFallback(inspector, pc); michael@0: michael@0: // Retrieve type information of lhs and rhs. michael@0: MIRType lhs = getOperand(0)->type(); michael@0: MIRType rhs = getOperand(1)->type(); michael@0: michael@0: // Guess a result type based on the inputs. michael@0: // Don't specialize for neither-integer-nor-double results. michael@0: if (lhs == MIRType_Int32 && rhs == MIRType_Int32) michael@0: setResultType(MIRType_Int32); michael@0: // Double operations are prioritary over float32 operations (i.e. if any operand needs michael@0: // a double as an input, convert all operands to doubles) michael@0: else if (IsFloatingPointType(lhs) || IsFloatingPointType(rhs)) michael@0: setResultType(MIRType_Double); michael@0: else michael@0: return inferFallback(inspector, pc); michael@0: michael@0: // If the operation has ever overflowed, use a double specialization. michael@0: if (inspector->hasSeenDoubleResult(pc)) michael@0: setResultType(MIRType_Double); michael@0: michael@0: // If the operation will always overflow on its constant operands, use a michael@0: // double specialization so that it can be constant folded later. michael@0: if ((isMul() || isDiv()) && lhs == MIRType_Int32 && rhs == MIRType_Int32) { michael@0: bool typeChange = false; michael@0: EvaluateConstantOperands(alloc, this, &typeChange); michael@0: if (typeChange) michael@0: setResultType(MIRType_Double); michael@0: } michael@0: michael@0: JS_ASSERT(lhs < MIRType_String || lhs == MIRType_Value); michael@0: JS_ASSERT(rhs < MIRType_String || rhs == MIRType_Value); michael@0: michael@0: MIRType rval = this->type(); michael@0: michael@0: // Don't specialize values when result isn't double michael@0: if (lhs == MIRType_Value || rhs == MIRType_Value) { michael@0: if (!IsFloatingPointType(rval)) { michael@0: specialization_ = MIRType_None; michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // Don't specialize as int32 if one of the operands is undefined, michael@0: // since ToNumber(undefined) is NaN. michael@0: if (rval == MIRType_Int32 && (lhs == MIRType_Undefined || rhs == MIRType_Undefined)) { michael@0: specialization_ = MIRType_None; michael@0: return; michael@0: } michael@0: michael@0: specialization_ = rval; michael@0: michael@0: if (isAdd() || isMul()) michael@0: setCommutative(); michael@0: setResultType(rval); michael@0: } michael@0: michael@0: void michael@0: MBinaryArithInstruction::inferFallback(BaselineInspector *inspector, michael@0: jsbytecode *pc) michael@0: { michael@0: // Try to specialize based on what baseline observed in practice. michael@0: specialization_ = inspector->expectedBinaryArithSpecialization(pc); michael@0: if (specialization_ != MIRType_None) { michael@0: setResultType(specialization_); michael@0: return; michael@0: } michael@0: michael@0: // In parallel execution, for now anyhow, we *only* support adding michael@0: // and manipulating numbers (not strings or objects). So no michael@0: // matter what we can specialize to double...if the result ought michael@0: // to have been something else, we'll fail in the various type michael@0: // guards that get inserted later. michael@0: if (block()->info().executionMode() == ParallelExecution) { michael@0: specialization_ = MIRType_Double; michael@0: setResultType(MIRType_Double); michael@0: return; michael@0: } michael@0: michael@0: // If we can't specialize because we have no type information at all for michael@0: // the lhs or rhs, mark the binary instruction as having no possible types michael@0: // either to avoid degrading subsequent analysis. michael@0: if (getOperand(0)->emptyResultTypeSet() || getOperand(1)->emptyResultTypeSet()) { michael@0: LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); michael@0: types::TemporaryTypeSet *types = alloc->new_(); michael@0: if (types) michael@0: setResultTypeSet(types); michael@0: } michael@0: } michael@0: michael@0: static bool michael@0: SafelyCoercesToDouble(MDefinition *op) michael@0: { michael@0: // Strings are unhandled -- visitToDouble() doesn't support them yet. michael@0: // Null is unhandled -- ToDouble(null) == 0, but (0 == null) is false. michael@0: return KnownNonStringPrimitive(op) && !op->mightBeType(MIRType_Null); michael@0: } michael@0: michael@0: static bool michael@0: ObjectOrSimplePrimitive(MDefinition *op) michael@0: { michael@0: // Return true if op is either undefined/null/boolean/int32 or an object. michael@0: return !op->mightBeType(MIRType_String) michael@0: && !op->mightBeType(MIRType_Double) michael@0: && !op->mightBeType(MIRType_Float32) michael@0: && !op->mightBeType(MIRType_MagicOptimizedArguments) michael@0: && !op->mightBeType(MIRType_MagicHole) michael@0: && !op->mightBeType(MIRType_MagicIsConstructing); michael@0: } michael@0: michael@0: static bool michael@0: CanDoValueBitwiseCmp(MDefinition *lhs, MDefinition *rhs, bool looseEq) michael@0: { michael@0: // Only primitive (not double/string) or objects are supported. michael@0: // I.e. Undefined/Null/Boolean/Int32 and Object michael@0: if (!ObjectOrSimplePrimitive(lhs) || !ObjectOrSimplePrimitive(rhs)) michael@0: return false; michael@0: michael@0: // Objects that emulate undefined are not supported. michael@0: if (MaybeEmulatesUndefined(lhs) || MaybeEmulatesUndefined(rhs)) michael@0: return false; michael@0: michael@0: // In the loose comparison more values could be the same, michael@0: // but value comparison reporting otherwise. michael@0: if (looseEq) { michael@0: michael@0: // Undefined compared loosy to Null is not supported, michael@0: // because tag is different, but value can be the same (undefined == null). michael@0: if ((lhs->mightBeType(MIRType_Undefined) && rhs->mightBeType(MIRType_Null)) || michael@0: (lhs->mightBeType(MIRType_Null) && rhs->mightBeType(MIRType_Undefined))) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: // Int32 compared loosy to Boolean is not supported, michael@0: // because tag is different, but value can be the same (1 == true). michael@0: if ((lhs->mightBeType(MIRType_Int32) && rhs->mightBeType(MIRType_Boolean)) || michael@0: (lhs->mightBeType(MIRType_Boolean) && rhs->mightBeType(MIRType_Int32))) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: // For loosy comparison of an object with a Boolean/Number/String michael@0: // the valueOf the object is taken. Therefore not supported. michael@0: bool simpleLHS = lhs->mightBeType(MIRType_Boolean) || lhs->mightBeType(MIRType_Int32); michael@0: bool simpleRHS = rhs->mightBeType(MIRType_Boolean) || rhs->mightBeType(MIRType_Int32); michael@0: if ((lhs->mightBeType(MIRType_Object) && simpleRHS) || michael@0: (rhs->mightBeType(MIRType_Object) && simpleLHS)) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: MIRType michael@0: MCompare::inputType() michael@0: { michael@0: switch(compareType_) { michael@0: case Compare_Undefined: michael@0: return MIRType_Undefined; michael@0: case Compare_Null: michael@0: return MIRType_Null; michael@0: case Compare_Boolean: michael@0: return MIRType_Boolean; michael@0: case Compare_UInt32: michael@0: case Compare_Int32: michael@0: case Compare_Int32MaybeCoerceBoth: michael@0: case Compare_Int32MaybeCoerceLHS: michael@0: case Compare_Int32MaybeCoerceRHS: michael@0: return MIRType_Int32; michael@0: case Compare_Double: michael@0: case Compare_DoubleMaybeCoerceLHS: michael@0: case Compare_DoubleMaybeCoerceRHS: michael@0: return MIRType_Double; michael@0: case Compare_Float32: michael@0: return MIRType_Float32; michael@0: case Compare_String: michael@0: case Compare_StrictString: michael@0: return MIRType_String; michael@0: case Compare_Object: michael@0: return MIRType_Object; michael@0: case Compare_Unknown: michael@0: case Compare_Value: michael@0: return MIRType_Value; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("No known conversion"); michael@0: } michael@0: } michael@0: michael@0: static inline bool michael@0: MustBeUInt32(MDefinition *def, MDefinition **pwrapped) michael@0: { michael@0: if (def->isUrsh()) { michael@0: *pwrapped = def->toUrsh()->getOperand(0); michael@0: MDefinition *rhs = def->toUrsh()->getOperand(1); michael@0: return !def->toUrsh()->bailoutsDisabled() michael@0: && rhs->isConstant() michael@0: && rhs->toConstant()->value().isInt32() michael@0: && rhs->toConstant()->value().toInt32() == 0; michael@0: } michael@0: michael@0: if (def->isConstant()) { michael@0: *pwrapped = def; michael@0: return def->toConstant()->value().isInt32() michael@0: && def->toConstant()->value().toInt32() >= 0; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: MBinaryInstruction::tryUseUnsignedOperands() michael@0: { michael@0: MDefinition *newlhs, *newrhs; michael@0: if (MustBeUInt32(getOperand(0), &newlhs) && MustBeUInt32(getOperand(1), &newrhs)) { michael@0: if (newlhs->type() != MIRType_Int32 || newrhs->type() != MIRType_Int32) michael@0: return false; michael@0: if (newlhs != getOperand(0)) { michael@0: getOperand(0)->setImplicitlyUsedUnchecked(); michael@0: replaceOperand(0, newlhs); michael@0: } michael@0: if (newrhs != getOperand(1)) { michael@0: getOperand(1)->setImplicitlyUsedUnchecked(); michael@0: replaceOperand(1, newrhs); michael@0: } michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: MCompare::infer(BaselineInspector *inspector, jsbytecode *pc) michael@0: { michael@0: JS_ASSERT(operandMightEmulateUndefined()); michael@0: michael@0: if (!MaybeEmulatesUndefined(getOperand(0)) && !MaybeEmulatesUndefined(getOperand(1))) michael@0: markNoOperandEmulatesUndefined(); michael@0: michael@0: MIRType lhs = getOperand(0)->type(); michael@0: MIRType rhs = getOperand(1)->type(); michael@0: michael@0: bool looseEq = jsop() == JSOP_EQ || jsop() == JSOP_NE; michael@0: bool strictEq = jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE; michael@0: bool relationalEq = !(looseEq || strictEq); michael@0: michael@0: // Comparisons on unsigned integers may be treated as UInt32. michael@0: if (tryUseUnsignedOperands()) { michael@0: compareType_ = Compare_UInt32; michael@0: return; michael@0: } michael@0: michael@0: // Integer to integer or boolean to boolean comparisons may be treated as Int32. michael@0: if ((lhs == MIRType_Int32 && rhs == MIRType_Int32) || michael@0: (lhs == MIRType_Boolean && rhs == MIRType_Boolean)) michael@0: { michael@0: compareType_ = Compare_Int32MaybeCoerceBoth; michael@0: return; michael@0: } michael@0: michael@0: // Loose/relational cross-integer/boolean comparisons may be treated as Int32. michael@0: if (!strictEq && michael@0: (lhs == MIRType_Int32 || lhs == MIRType_Boolean) && michael@0: (rhs == MIRType_Int32 || rhs == MIRType_Boolean)) michael@0: { michael@0: compareType_ = Compare_Int32MaybeCoerceBoth; michael@0: return; michael@0: } michael@0: michael@0: // Numeric comparisons against a double coerce to double. michael@0: if (IsNumberType(lhs) && IsNumberType(rhs)) { michael@0: compareType_ = Compare_Double; michael@0: return; michael@0: } michael@0: michael@0: // Any comparison is allowed except strict eq. michael@0: if (!strictEq && IsFloatingPointType(lhs) && SafelyCoercesToDouble(getOperand(1))) { michael@0: compareType_ = Compare_DoubleMaybeCoerceRHS; michael@0: return; michael@0: } michael@0: if (!strictEq && IsFloatingPointType(rhs) && SafelyCoercesToDouble(getOperand(0))) { michael@0: compareType_ = Compare_DoubleMaybeCoerceLHS; michael@0: return; michael@0: } michael@0: michael@0: // Handle object comparison. michael@0: if (!relationalEq && lhs == MIRType_Object && rhs == MIRType_Object) { michael@0: compareType_ = Compare_Object; michael@0: return; michael@0: } michael@0: michael@0: // Handle string comparisons. (Relational string compares are still unsupported). michael@0: if (!relationalEq && lhs == MIRType_String && rhs == MIRType_String) { michael@0: compareType_ = Compare_String; michael@0: return; michael@0: } michael@0: michael@0: if (strictEq && lhs == MIRType_String) { michael@0: // Lowering expects the rhs to be definitly string. michael@0: compareType_ = Compare_StrictString; michael@0: swapOperands(); michael@0: return; michael@0: } michael@0: michael@0: if (strictEq && rhs == MIRType_String) { michael@0: compareType_ = Compare_StrictString; michael@0: return; michael@0: } michael@0: michael@0: // Handle compare with lhs being Undefined or Null. michael@0: if (!relationalEq && IsNullOrUndefined(lhs)) { michael@0: // Lowering expects the rhs to be null/undefined, so we have to michael@0: // swap the operands. This is necessary since we may not know which michael@0: // operand was null/undefined during lowering (both operands may have michael@0: // MIRType_Value). michael@0: compareType_ = (lhs == MIRType_Null) ? Compare_Null : Compare_Undefined; michael@0: swapOperands(); michael@0: return; michael@0: } michael@0: michael@0: // Handle compare with rhs being Undefined or Null. michael@0: if (!relationalEq && IsNullOrUndefined(rhs)) { michael@0: compareType_ = (rhs == MIRType_Null) ? Compare_Null : Compare_Undefined; michael@0: return; michael@0: } michael@0: michael@0: // Handle strict comparison with lhs/rhs being typed Boolean. michael@0: if (strictEq && (lhs == MIRType_Boolean || rhs == MIRType_Boolean)) { michael@0: // bool/bool case got an int32 specialization earlier. michael@0: JS_ASSERT(!(lhs == MIRType_Boolean && rhs == MIRType_Boolean)); michael@0: michael@0: // Ensure the boolean is on the right so that the type policy knows michael@0: // which side to unbox. michael@0: if (lhs == MIRType_Boolean) michael@0: swapOperands(); michael@0: michael@0: compareType_ = Compare_Boolean; michael@0: return; michael@0: } michael@0: michael@0: // Determine if we can do the compare based on a quick value check. michael@0: if (!relationalEq && CanDoValueBitwiseCmp(getOperand(0), getOperand(1), looseEq)) { michael@0: compareType_ = Compare_Value; michael@0: return; michael@0: } michael@0: michael@0: // Type information is not good enough to pick out a particular type of michael@0: // comparison we can do here. Try to specialize based on any baseline michael@0: // caches that have been generated for the opcode. These will cause the michael@0: // instruction's type policy to insert fallible unboxes to the appropriate michael@0: // input types. michael@0: michael@0: if (!strictEq) michael@0: compareType_ = inspector->expectedCompareType(pc); michael@0: } michael@0: michael@0: MBitNot * michael@0: MBitNot::New(TempAllocator &alloc, MDefinition *input) michael@0: { michael@0: return new(alloc) MBitNot(input); michael@0: } michael@0: michael@0: MBitNot * michael@0: MBitNot::NewAsmJS(TempAllocator &alloc, MDefinition *input) michael@0: { michael@0: MBitNot *ins = new(alloc) MBitNot(input); michael@0: ins->specialization_ = MIRType_Int32; michael@0: JS_ASSERT(ins->type() == MIRType_Int32); michael@0: return ins; michael@0: } michael@0: michael@0: MDefinition * michael@0: MBitNot::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: if (specialization_ != MIRType_Int32) michael@0: return this; michael@0: michael@0: MDefinition *input = getOperand(0); michael@0: michael@0: if (input->isConstant()) { michael@0: js::Value v = Int32Value(~(input->toConstant()->value().toInt32())); michael@0: return MConstant::New(alloc, v); michael@0: } michael@0: michael@0: if (input->isBitNot() && input->toBitNot()->specialization_ == MIRType_Int32) { michael@0: JS_ASSERT(input->toBitNot()->getOperand(0)->type() == MIRType_Int32); michael@0: return input->toBitNot()->getOperand(0); // ~~x => x michael@0: } michael@0: michael@0: return this; michael@0: } michael@0: michael@0: MDefinition * michael@0: MTypeOf::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: // Note: we can't use input->type() here, type analysis has michael@0: // boxed the input. michael@0: JS_ASSERT(input()->type() == MIRType_Value); michael@0: michael@0: JSType type; michael@0: michael@0: switch (inputType()) { michael@0: case MIRType_Double: michael@0: case MIRType_Int32: michael@0: type = JSTYPE_NUMBER; michael@0: break; michael@0: case MIRType_String: michael@0: type = JSTYPE_STRING; michael@0: break; michael@0: case MIRType_Null: michael@0: type = JSTYPE_OBJECT; michael@0: break; michael@0: case MIRType_Undefined: michael@0: type = JSTYPE_VOID; michael@0: break; michael@0: case MIRType_Boolean: michael@0: type = JSTYPE_BOOLEAN; michael@0: break; michael@0: case MIRType_Object: michael@0: if (!inputMaybeCallableOrEmulatesUndefined()) { michael@0: // Object is not callable and does not emulate undefined, so it's michael@0: // safe to fold to "object". michael@0: type = JSTYPE_OBJECT; michael@0: break; michael@0: } michael@0: // FALL THROUGH michael@0: default: michael@0: return this; michael@0: } michael@0: michael@0: return MConstant::New(alloc, StringValue(TypeName(type, GetIonContext()->runtime->names()))); michael@0: } michael@0: michael@0: void michael@0: MTypeOf::infer() michael@0: { michael@0: JS_ASSERT(inputMaybeCallableOrEmulatesUndefined()); michael@0: michael@0: if (!MaybeEmulatesUndefined(input()) && !MaybeCallable(input())) michael@0: markInputNotCallableOrEmulatesUndefined(); michael@0: } michael@0: michael@0: MBitAnd * michael@0: MBitAnd::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: return new(alloc) MBitAnd(left, right); michael@0: } michael@0: michael@0: MBitAnd * michael@0: MBitAnd::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: MBitAnd *ins = new(alloc) MBitAnd(left, right); michael@0: ins->specializeAsInt32(); michael@0: return ins; michael@0: } michael@0: michael@0: MBitOr * michael@0: MBitOr::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: return new(alloc) MBitOr(left, right); michael@0: } michael@0: michael@0: MBitOr * michael@0: MBitOr::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: MBitOr *ins = new(alloc) MBitOr(left, right); michael@0: ins->specializeAsInt32(); michael@0: return ins; michael@0: } michael@0: michael@0: MBitXor * michael@0: MBitXor::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: return new(alloc) MBitXor(left, right); michael@0: } michael@0: michael@0: MBitXor * michael@0: MBitXor::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: MBitXor *ins = new(alloc) MBitXor(left, right); michael@0: ins->specializeAsInt32(); michael@0: return ins; michael@0: } michael@0: michael@0: MLsh * michael@0: MLsh::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: return new(alloc) MLsh(left, right); michael@0: } michael@0: michael@0: MLsh * michael@0: MLsh::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: MLsh *ins = new(alloc) MLsh(left, right); michael@0: ins->specializeAsInt32(); michael@0: return ins; michael@0: } michael@0: michael@0: MRsh * michael@0: MRsh::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: return new(alloc) MRsh(left, right); michael@0: } michael@0: michael@0: MRsh * michael@0: MRsh::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: MRsh *ins = new(alloc) MRsh(left, right); michael@0: ins->specializeAsInt32(); michael@0: return ins; michael@0: } michael@0: michael@0: MUrsh * michael@0: MUrsh::New(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: return new(alloc) MUrsh(left, right); michael@0: } michael@0: michael@0: MUrsh * michael@0: MUrsh::NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right) michael@0: { michael@0: MUrsh *ins = new(alloc) MUrsh(left, right); michael@0: ins->specializeAsInt32(); michael@0: michael@0: // Since Ion has no UInt32 type, we use Int32 and we have a special michael@0: // exception to the type rules: we can return values in michael@0: // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type michael@0: // without bailing out. This is necessary because Ion has no UInt32 michael@0: // type and we can't have bailouts in asm.js code. michael@0: ins->bailoutsDisabled_ = true; michael@0: michael@0: return ins; michael@0: } michael@0: michael@0: MResumePoint * michael@0: MResumePoint::New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc, MResumePoint *parent, michael@0: Mode mode) michael@0: { michael@0: MResumePoint *resume = new(alloc) MResumePoint(block, pc, parent, mode); michael@0: if (!resume->init(alloc)) michael@0: return nullptr; michael@0: resume->inherit(block); michael@0: return resume; michael@0: } michael@0: michael@0: MResumePoint::MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *caller, michael@0: Mode mode) michael@0: : MNode(block), michael@0: stackDepth_(block->stackDepth()), michael@0: pc_(pc), michael@0: caller_(caller), michael@0: instruction_(nullptr), michael@0: mode_(mode) michael@0: { michael@0: block->addResumePoint(this); michael@0: } michael@0: michael@0: void michael@0: MResumePoint::inherit(MBasicBlock *block) michael@0: { michael@0: for (size_t i = 0; i < stackDepth(); i++) { michael@0: MDefinition *def = block->getSlot(i); michael@0: setOperand(i, def); michael@0: } michael@0: } michael@0: michael@0: MDefinition * michael@0: MToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: MDefinition *input = getOperand(0); michael@0: if (input->type() == MIRType_Int32) michael@0: return input; michael@0: return this; michael@0: } michael@0: michael@0: void michael@0: MToInt32::analyzeEdgeCasesBackward() michael@0: { michael@0: if (!NeedNegativeZeroCheck(this)) michael@0: setCanBeNegativeZero(false); michael@0: } michael@0: michael@0: MDefinition * michael@0: MTruncateToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: MDefinition *input = getOperand(0); michael@0: if (input->type() == MIRType_Int32) michael@0: return input; michael@0: michael@0: if (input->type() == MIRType_Double && input->isConstant()) { michael@0: const Value &v = input->toConstant()->value(); michael@0: int32_t ret = ToInt32(v.toDouble()); michael@0: return MConstant::New(alloc, Int32Value(ret)); michael@0: } michael@0: michael@0: return this; michael@0: } michael@0: michael@0: MDefinition * michael@0: MToDouble::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: MDefinition *in = input(); michael@0: if (in->type() == MIRType_Double) michael@0: return in; michael@0: michael@0: if (in->isConstant()) { michael@0: const Value &v = in->toConstant()->value(); michael@0: if (v.isNumber()) { michael@0: double out = v.toNumber(); michael@0: return MConstant::New(alloc, DoubleValue(out)); michael@0: } michael@0: } michael@0: michael@0: return this; michael@0: } michael@0: michael@0: MDefinition * michael@0: MToFloat32::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: if (input()->type() == MIRType_Float32) michael@0: return input(); michael@0: michael@0: // If x is a Float32, Float32(Double(x)) == x michael@0: if (input()->isToDouble() && input()->toToDouble()->input()->type() == MIRType_Float32) michael@0: return input()->toToDouble()->input(); michael@0: michael@0: if (input()->isConstant()) { michael@0: const Value &v = input()->toConstant()->value(); michael@0: if (v.isNumber()) { michael@0: float out = v.toNumber(); michael@0: MConstant *c = MConstant::New(alloc, DoubleValue(out)); michael@0: c->setResultType(MIRType_Float32); michael@0: return c; michael@0: } michael@0: } michael@0: return this; michael@0: } michael@0: michael@0: MDefinition * michael@0: MToString::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: MDefinition *in = input(); michael@0: if (in->type() == MIRType_String) michael@0: return in; michael@0: return this; michael@0: } michael@0: michael@0: MDefinition * michael@0: MClampToUint8::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: if (input()->isConstant()) { michael@0: const Value &v = input()->toConstant()->value(); michael@0: if (v.isDouble()) { michael@0: int32_t clamped = ClampDoubleToUint8(v.toDouble()); michael@0: return MConstant::New(alloc, Int32Value(clamped)); michael@0: } michael@0: if (v.isInt32()) { michael@0: int32_t clamped = ClampIntForUint8Array(v.toInt32()); michael@0: return MConstant::New(alloc, Int32Value(clamped)); michael@0: } michael@0: } michael@0: return this; michael@0: } michael@0: michael@0: bool michael@0: MCompare::tryFold(bool *result) michael@0: { michael@0: JSOp op = jsop(); michael@0: michael@0: if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) { michael@0: JS_ASSERT(op == JSOP_EQ || op == JSOP_STRICTEQ || michael@0: op == JSOP_NE || op == JSOP_STRICTNE); michael@0: michael@0: // The LHS is the value we want to test against null or undefined. michael@0: switch (lhs()->type()) { michael@0: case MIRType_Value: michael@0: return false; michael@0: case MIRType_Undefined: michael@0: case MIRType_Null: michael@0: if (lhs()->type() == inputType()) { michael@0: // Both sides have the same type, null or undefined. michael@0: *result = (op == JSOP_EQ || op == JSOP_STRICTEQ); michael@0: } else { michael@0: // One side is null, the other side is undefined. The result is only michael@0: // true for loose equality. michael@0: *result = (op == JSOP_EQ || op == JSOP_STRICTNE); michael@0: } michael@0: return true; michael@0: case MIRType_Object: michael@0: if ((op == JSOP_EQ || op == JSOP_NE) && operandMightEmulateUndefined()) michael@0: return false; michael@0: /* FALL THROUGH */ michael@0: case MIRType_Int32: michael@0: case MIRType_Double: michael@0: case MIRType_Float32: michael@0: case MIRType_String: michael@0: case MIRType_Boolean: michael@0: *result = (op == JSOP_NE || op == JSOP_STRICTNE); michael@0: return true; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected type"); michael@0: } michael@0: } michael@0: michael@0: if (compareType_ == Compare_Boolean) { michael@0: JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); michael@0: JS_ASSERT(rhs()->type() == MIRType_Boolean); michael@0: michael@0: switch (lhs()->type()) { michael@0: case MIRType_Value: michael@0: return false; michael@0: case MIRType_Int32: michael@0: case MIRType_Double: michael@0: case MIRType_Float32: michael@0: case MIRType_String: michael@0: case MIRType_Object: michael@0: case MIRType_Null: michael@0: case MIRType_Undefined: michael@0: *result = (op == JSOP_STRICTNE); michael@0: return true; michael@0: case MIRType_Boolean: michael@0: // Int32 specialization should handle this. michael@0: MOZ_ASSUME_UNREACHABLE("Wrong specialization"); michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected type"); michael@0: } michael@0: } michael@0: michael@0: if (compareType_ == Compare_StrictString) { michael@0: JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); michael@0: JS_ASSERT(rhs()->type() == MIRType_String); michael@0: michael@0: switch (lhs()->type()) { michael@0: case MIRType_Value: michael@0: return false; 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: case MIRType_Null: michael@0: case MIRType_Undefined: michael@0: *result = (op == JSOP_STRICTNE); michael@0: return true; michael@0: case MIRType_String: michael@0: // Compare_String specialization should handle this. michael@0: MOZ_ASSUME_UNREACHABLE("Wrong specialization"); michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected type"); michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: MCompare::evaluateConstantOperands(bool *result) michael@0: { michael@0: if (type() != MIRType_Boolean && type() != MIRType_Int32) michael@0: return false; michael@0: michael@0: MDefinition *left = getOperand(0); michael@0: MDefinition *right = getOperand(1); michael@0: michael@0: if (!left->isConstant() || !right->isConstant()) michael@0: return false; michael@0: michael@0: Value lhs = left->toConstant()->value(); michael@0: Value rhs = right->toConstant()->value(); michael@0: michael@0: // Fold away some String equality comparisons. michael@0: if (lhs.isString() && rhs.isString()) { michael@0: int32_t comp = 0; // Default to equal. michael@0: if (left != right) michael@0: comp = CompareAtoms(&lhs.toString()->asAtom(), &rhs.toString()->asAtom()); michael@0: michael@0: switch (jsop_) { michael@0: case JSOP_LT: michael@0: *result = (comp < 0); michael@0: break; michael@0: case JSOP_LE: michael@0: *result = (comp <= 0); michael@0: break; michael@0: case JSOP_GT: michael@0: *result = (comp > 0); michael@0: break; michael@0: case JSOP_GE: michael@0: *result = (comp >= 0); michael@0: break; michael@0: case JSOP_STRICTEQ: // Fall through. michael@0: case JSOP_EQ: michael@0: *result = (comp == 0); michael@0: break; michael@0: case JSOP_STRICTNE: // Fall through. michael@0: case JSOP_NE: michael@0: *result = (comp != 0); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected op."); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: if (compareType_ == Compare_UInt32) { michael@0: uint32_t lhsUint = uint32_t(lhs.toInt32()); michael@0: uint32_t rhsUint = uint32_t(rhs.toInt32()); michael@0: michael@0: switch (jsop_) { michael@0: case JSOP_LT: michael@0: *result = (lhsUint < rhsUint); michael@0: break; michael@0: case JSOP_LE: michael@0: *result = (lhsUint <= rhsUint); michael@0: break; michael@0: case JSOP_GT: michael@0: *result = (lhsUint > rhsUint); michael@0: break; michael@0: case JSOP_GE: michael@0: *result = (lhsUint >= rhsUint); michael@0: break; michael@0: case JSOP_EQ: michael@0: case JSOP_STRICTEQ: michael@0: *result = (lhsUint == rhsUint); michael@0: break; michael@0: case JSOP_NE: michael@0: case JSOP_STRICTNE: michael@0: *result = (lhsUint != rhsUint); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected op."); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: if (!lhs.isNumber() || !rhs.isNumber()) michael@0: return false; michael@0: michael@0: switch (jsop_) { michael@0: case JSOP_LT: michael@0: *result = (lhs.toNumber() < rhs.toNumber()); michael@0: break; michael@0: case JSOP_LE: michael@0: *result = (lhs.toNumber() <= rhs.toNumber()); michael@0: break; michael@0: case JSOP_GT: michael@0: *result = (lhs.toNumber() > rhs.toNumber()); michael@0: break; michael@0: case JSOP_GE: michael@0: *result = (lhs.toNumber() >= rhs.toNumber()); michael@0: break; michael@0: case JSOP_EQ: michael@0: *result = (lhs.toNumber() == rhs.toNumber()); michael@0: break; michael@0: case JSOP_NE: michael@0: *result = (lhs.toNumber() != rhs.toNumber()); michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: MDefinition * michael@0: MCompare::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: bool result; michael@0: michael@0: if (tryFold(&result) || evaluateConstantOperands(&result)) { michael@0: if (type() == MIRType_Int32) michael@0: return MConstant::New(alloc, Int32Value(result)); michael@0: michael@0: JS_ASSERT(type() == MIRType_Boolean); michael@0: return MConstant::New(alloc, BooleanValue(result)); michael@0: } michael@0: michael@0: return this; michael@0: } michael@0: michael@0: void michael@0: MCompare::trySpecializeFloat32(TempAllocator &alloc) michael@0: { michael@0: MDefinition *lhs = getOperand(0); michael@0: MDefinition *rhs = getOperand(1); michael@0: michael@0: if (lhs->canProduceFloat32() && rhs->canProduceFloat32() && compareType_ == Compare_Double) { michael@0: compareType_ = Compare_Float32; michael@0: } else { michael@0: if (lhs->type() == MIRType_Float32) michael@0: ConvertDefinitionToDouble<0>(alloc, lhs, this); michael@0: if (rhs->type() == MIRType_Float32) michael@0: ConvertDefinitionToDouble<1>(alloc, rhs, this); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MCompare::filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined, michael@0: bool *filtersNull) michael@0: { michael@0: *filtersNull = *filtersUndefined = false; michael@0: *subject = nullptr; michael@0: michael@0: if (compareType() != Compare_Undefined && compareType() != Compare_Null) michael@0: return; michael@0: michael@0: JS_ASSERT(jsop() == JSOP_STRICTNE || jsop() == JSOP_NE || michael@0: jsop() == JSOP_STRICTEQ || jsop() == JSOP_EQ); michael@0: michael@0: // JSOP_*NE only removes undefined/null from if/true branch michael@0: if (!trueBranch && (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE)) michael@0: return; michael@0: michael@0: // JSOP_*EQ only removes undefined/null from else/false branch michael@0: if (trueBranch && (jsop() == JSOP_STRICTEQ || jsop() == JSOP_EQ)) michael@0: return; michael@0: michael@0: if (jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE) { michael@0: *filtersUndefined = compareType() == Compare_Undefined; michael@0: *filtersNull = compareType() == Compare_Null; michael@0: } else { michael@0: *filtersUndefined = *filtersNull = true; michael@0: } michael@0: michael@0: *subject = lhs(); michael@0: } michael@0: michael@0: void michael@0: MNot::infer() michael@0: { michael@0: JS_ASSERT(operandMightEmulateUndefined()); michael@0: michael@0: if (!MaybeEmulatesUndefined(getOperand(0))) michael@0: markOperandCantEmulateUndefined(); michael@0: } michael@0: michael@0: MDefinition * michael@0: MNot::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: // Fold if the input is constant michael@0: if (operand()->isConstant()) { michael@0: bool result = operand()->toConstant()->valueToBoolean(); michael@0: if (type() == MIRType_Int32) michael@0: return MConstant::New(alloc, Int32Value(!result)); michael@0: michael@0: // ToBoolean can't cause side effects, so this is safe. michael@0: return MConstant::New(alloc, BooleanValue(!result)); michael@0: } michael@0: michael@0: // NOT of an undefined or null value is always true michael@0: if (operand()->type() == MIRType_Undefined || operand()->type() == MIRType_Null) michael@0: return MConstant::New(alloc, BooleanValue(true)); michael@0: michael@0: // NOT of an object that can't emulate undefined is always false. michael@0: if (operand()->type() == MIRType_Object && !operandMightEmulateUndefined()) michael@0: return MConstant::New(alloc, BooleanValue(false)); michael@0: michael@0: return this; michael@0: } michael@0: michael@0: void michael@0: MNot::trySpecializeFloat32(TempAllocator &alloc) michael@0: { michael@0: MDefinition *in = input(); michael@0: if (!in->canProduceFloat32() && in->type() == MIRType_Float32) michael@0: ConvertDefinitionToDouble<0>(alloc, in, this); michael@0: } michael@0: michael@0: void michael@0: MBeta::printOpcode(FILE *fp) const michael@0: { michael@0: MDefinition::printOpcode(fp); michael@0: michael@0: Sprinter sp(GetIonContext()->cx); michael@0: sp.init(); michael@0: comparison_->print(sp); michael@0: fprintf(fp, " %s", sp.string()); michael@0: } michael@0: michael@0: bool michael@0: MNewObject::shouldUseVM() const michael@0: { michael@0: return templateObject()->hasSingletonType() || michael@0: templateObject()->hasDynamicSlots(); michael@0: } michael@0: michael@0: bool michael@0: MNewArray::shouldUseVM() const michael@0: { michael@0: JS_ASSERT(count() < JSObject::NELEMENTS_LIMIT); michael@0: michael@0: size_t arraySlots = michael@0: gc::GetGCKindSlots(templateObject()->tenuredGetAllocKind()) - ObjectElements::VALUES_PER_HEADER; michael@0: michael@0: // Allocate space using the VMCall michael@0: // when mir hints it needs to get allocated immediately, michael@0: // but only when data doesn't fit the available array slots. michael@0: bool allocating = isAllocating() && count() > arraySlots; michael@0: michael@0: return templateObject()->hasSingletonType() || allocating; michael@0: } michael@0: michael@0: bool michael@0: MLoadFixedSlot::mightAlias(const MDefinition *store) const michael@0: { michael@0: if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot()) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MAsmJSLoadHeap::mightAlias(const MDefinition *def) const michael@0: { michael@0: if (def->isAsmJSStoreHeap()) { michael@0: const MAsmJSStoreHeap *store = def->toAsmJSStoreHeap(); michael@0: if (store->viewType() != viewType()) michael@0: return true; michael@0: if (!ptr()->isConstant() || !store->ptr()->isConstant()) michael@0: return true; michael@0: const MConstant *otherPtr = store->ptr()->toConstant(); michael@0: return ptr()->toConstant()->value() == otherPtr->value(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MAsmJSLoadHeap::congruentTo(const MDefinition *ins) const michael@0: { michael@0: if (!ins->isAsmJSLoadHeap()) michael@0: return false; michael@0: const MAsmJSLoadHeap *load = ins->toAsmJSLoadHeap(); michael@0: return load->viewType() == viewType() && congruentIfOperandsEqual(load); michael@0: } michael@0: michael@0: bool michael@0: MAsmJSLoadGlobalVar::mightAlias(const MDefinition *def) const michael@0: { michael@0: if (def->isAsmJSStoreGlobalVar()) { michael@0: const MAsmJSStoreGlobalVar *store = def->toAsmJSStoreGlobalVar(); michael@0: return store->globalDataOffset() == globalDataOffset_; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MAsmJSLoadGlobalVar::congruentTo(const MDefinition *ins) const michael@0: { michael@0: if (ins->isAsmJSLoadGlobalVar()) { michael@0: const MAsmJSLoadGlobalVar *load = ins->toAsmJSLoadGlobalVar(); michael@0: return globalDataOffset_ == load->globalDataOffset_; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: MLoadSlot::mightAlias(const MDefinition *store) const michael@0: { michael@0: if (store->isStoreSlot() && store->toStoreSlot()->slot() != slot()) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: InlinePropertyTable::trimTo(ObjectVector &targets, BoolVector &choiceSet) michael@0: { michael@0: for (size_t i = 0; i < targets.length(); i++) { michael@0: // If the target was inlined, don't erase the entry. michael@0: if (choiceSet[i]) michael@0: continue; michael@0: michael@0: JSFunction *target = &targets[i]->as(); michael@0: michael@0: // Eliminate all entries containing the vetoed function from the map. michael@0: size_t j = 0; michael@0: while (j < numEntries()) { michael@0: if (entries_[j]->func == target) michael@0: entries_.erase(&entries_[j]); michael@0: else michael@0: j++; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: InlinePropertyTable::trimToTargets(ObjectVector &targets) michael@0: { michael@0: IonSpew(IonSpew_Inlining, "Got inlineable property cache with %d cases", michael@0: (int)numEntries()); michael@0: michael@0: size_t i = 0; michael@0: while (i < numEntries()) { michael@0: bool foundFunc = false; michael@0: for (size_t j = 0; j < targets.length(); j++) { michael@0: if (entries_[i]->func == targets[j]) { michael@0: foundFunc = true; michael@0: break; michael@0: } michael@0: } michael@0: if (!foundFunc) michael@0: entries_.erase(&(entries_[i])); michael@0: else michael@0: i++; michael@0: } michael@0: michael@0: IonSpew(IonSpew_Inlining, "%d inlineable cases left after trimming to %d targets", michael@0: (int)numEntries(), (int)targets.length()); michael@0: } michael@0: michael@0: bool michael@0: InlinePropertyTable::hasFunction(JSFunction *func) const michael@0: { michael@0: for (size_t i = 0; i < numEntries(); i++) { michael@0: if (entries_[i]->func == func) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: types::TemporaryTypeSet * michael@0: InlinePropertyTable::buildTypeSetForFunction(JSFunction *func) const michael@0: { michael@0: LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); michael@0: types::TemporaryTypeSet *types = alloc->new_(); michael@0: if (!types) michael@0: return nullptr; michael@0: for (size_t i = 0; i < numEntries(); i++) { michael@0: if (entries_[i]->func == func) michael@0: types->addType(types::Type::ObjectType(entries_[i]->typeObj), alloc); michael@0: } michael@0: return types; michael@0: } michael@0: michael@0: void * michael@0: MLoadTypedArrayElementStatic::base() const michael@0: { michael@0: return typedArray_->viewData(); michael@0: } michael@0: michael@0: size_t michael@0: MLoadTypedArrayElementStatic::length() const michael@0: { michael@0: return typedArray_->byteLength(); michael@0: } michael@0: michael@0: void * michael@0: MStoreTypedArrayElementStatic::base() const michael@0: { michael@0: return typedArray_->viewData(); michael@0: } michael@0: michael@0: bool michael@0: MGetElementCache::allowDoubleResult() const michael@0: { michael@0: if (!resultTypeSet()) michael@0: return true; michael@0: michael@0: return resultTypeSet()->hasType(types::Type::DoubleType()); michael@0: } michael@0: michael@0: size_t michael@0: MStoreTypedArrayElementStatic::length() const michael@0: { michael@0: return typedArray_->byteLength(); michael@0: } michael@0: michael@0: bool michael@0: MGetPropertyPolymorphic::mightAlias(const MDefinition *store) const michael@0: { michael@0: // Allow hoisting this instruction if the store does not write to a michael@0: // slot read by this instruction. michael@0: michael@0: if (!store->isStoreFixedSlot() && !store->isStoreSlot()) michael@0: return true; michael@0: michael@0: for (size_t i = 0; i < numShapes(); i++) { michael@0: const Shape *shape = this->shape(i); michael@0: if (shape->slot() < shape->numFixedSlots()) { michael@0: // Fixed slot. michael@0: uint32_t slot = shape->slot(); michael@0: if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot) michael@0: continue; michael@0: if (store->isStoreSlot()) michael@0: continue; michael@0: } else { michael@0: // Dynamic slot. michael@0: uint32_t slot = shape->slot() - shape->numFixedSlots(); michael@0: if (store->isStoreSlot() && store->toStoreSlot()->slot() != slot) michael@0: continue; michael@0: if (store->isStoreFixedSlot()) michael@0: continue; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: MGetPropertyCache::setBlock(MBasicBlock *block) michael@0: { michael@0: MDefinition::setBlock(block); michael@0: // Track where we started. michael@0: if (!location_.pc) { michael@0: location_.pc = block->trackedPc(); michael@0: location_.script = block->info().script(); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: MGetPropertyCache::updateForReplacement(MDefinition *ins) { michael@0: MGetPropertyCache *other = ins->toGetPropertyCache(); michael@0: location_.append(&other->location_); michael@0: return true; michael@0: } michael@0: michael@0: MDefinition * michael@0: MAsmJSUnsignedToDouble::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: if (input()->isConstant()) { michael@0: const Value &v = input()->toConstant()->value(); michael@0: if (v.isInt32()) michael@0: return MConstant::New(alloc, DoubleValue(uint32_t(v.toInt32()))); michael@0: } michael@0: michael@0: return this; michael@0: } michael@0: michael@0: MDefinition * michael@0: MAsmJSUnsignedToFloat32::foldsTo(TempAllocator &alloc, bool useValueNumbers) michael@0: { michael@0: if (input()->isConstant()) { michael@0: const Value &v = input()->toConstant()->value(); michael@0: if (v.isInt32()) { michael@0: double dval = double(uint32_t(v.toInt32())); michael@0: if (IsFloat32Representable(dval)) michael@0: return MConstant::NewAsmJS(alloc, JS::Float32Value(float(dval)), MIRType_Float32); michael@0: } michael@0: } michael@0: michael@0: return this; michael@0: } michael@0: michael@0: MAsmJSCall * michael@0: MAsmJSCall::New(TempAllocator &alloc, const CallSiteDesc &desc, Callee callee, michael@0: const Args &args, MIRType resultType, size_t spIncrement) michael@0: { michael@0: MAsmJSCall *call = new(alloc) MAsmJSCall(desc, callee, spIncrement); michael@0: call->setResultType(resultType); michael@0: michael@0: if (!call->argRegs_.init(alloc, args.length())) michael@0: return nullptr; michael@0: for (size_t i = 0; i < call->argRegs_.length(); i++) michael@0: call->argRegs_[i] = args[i].reg; michael@0: michael@0: if (!call->operands_.init(alloc, call->argRegs_.length() + (callee.which() == Callee::Dynamic ? 1 : 0))) michael@0: return nullptr; michael@0: for (size_t i = 0; i < call->argRegs_.length(); i++) michael@0: call->setOperand(i, args[i].def); michael@0: if (callee.which() == Callee::Dynamic) michael@0: call->setOperand(call->argRegs_.length(), callee.dynamic()); michael@0: michael@0: return call; michael@0: } michael@0: michael@0: void michael@0: MSqrt::trySpecializeFloat32(TempAllocator &alloc) { michael@0: if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) { michael@0: if (input()->type() == MIRType_Float32) michael@0: ConvertDefinitionToDouble<0>(alloc, input(), this); michael@0: return; michael@0: } michael@0: michael@0: setResultType(MIRType_Float32); michael@0: setPolicyType(MIRType_Float32); michael@0: } michael@0: michael@0: bool michael@0: jit::ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id) michael@0: { michael@0: if (obj->mightBeType(MIRType_String)) michael@0: return false; michael@0: michael@0: if (id->type() != MIRType_Int32 && id->type() != MIRType_Double) michael@0: return false; michael@0: michael@0: types::TemporaryTypeSet *types = obj->resultTypeSet(); michael@0: if (!types) michael@0: return false; michael@0: michael@0: // Typed arrays are native classes but do not have dense elements. michael@0: const Class *clasp = types->getKnownClass(); michael@0: return clasp && clasp->isNative() && !IsTypedArrayClass(clasp); michael@0: } michael@0: michael@0: bool michael@0: jit::ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id, michael@0: ScalarTypeDescr::Type *arrayType) michael@0: { michael@0: if (obj->mightBeType(MIRType_String)) michael@0: return false; michael@0: michael@0: if (id->type() != MIRType_Int32 && id->type() != MIRType_Double) michael@0: return false; michael@0: michael@0: types::TemporaryTypeSet *types = obj->resultTypeSet(); michael@0: if (!types) michael@0: return false; michael@0: michael@0: *arrayType = (ScalarTypeDescr::Type) types->getTypedArrayType(); michael@0: return *arrayType != ScalarTypeDescr::TYPE_MAX; michael@0: } michael@0: michael@0: bool michael@0: jit::ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj) michael@0: { michael@0: types::TemporaryTypeSet *types = obj->resultTypeSet(); michael@0: return types && !types->hasObjectFlags(constraints, types::OBJECT_FLAG_NON_PACKED); michael@0: } michael@0: michael@0: bool michael@0: jit::ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints, michael@0: MDefinition *obj) michael@0: { michael@0: types::TemporaryTypeSet *types = obj->resultTypeSet(); michael@0: michael@0: if (!types || types->hasObjectFlags(constraints, types::OBJECT_FLAG_LENGTH_OVERFLOW)) michael@0: return true; michael@0: michael@0: return types::TypeCanHaveExtraIndexedProperties(constraints, types); michael@0: } michael@0: michael@0: MIRType michael@0: jit::DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj) michael@0: { michael@0: types::TemporaryTypeSet *types = obj->resultTypeSet(); michael@0: MIRType elementType = MIRType_None; michael@0: unsigned count = types->getObjectCount(); michael@0: michael@0: for (unsigned i = 0; i < count; i++) { michael@0: types::TypeObjectKey *object = types->getObject(i); michael@0: if (!object) michael@0: continue; michael@0: michael@0: if (object->unknownProperties()) michael@0: return MIRType_None; michael@0: michael@0: types::HeapTypeSetKey elementTypes = object->property(JSID_VOID); michael@0: michael@0: MIRType type = elementTypes.knownMIRType(constraints); michael@0: if (type == MIRType_None) michael@0: return MIRType_None; michael@0: michael@0: if (elementType == MIRType_None) michael@0: elementType = type; michael@0: else if (elementType != type) michael@0: return MIRType_None; michael@0: } michael@0: michael@0: return elementType; michael@0: } michael@0: michael@0: static bool michael@0: PropertyReadNeedsTypeBarrier(types::CompilerConstraintList *constraints, michael@0: types::TypeObjectKey *object, PropertyName *name, michael@0: types::TypeSet *observed) michael@0: { michael@0: // If the object being read from has types for the property which haven't michael@0: // been observed at this access site, the read could produce a new type and michael@0: // a barrier is needed. Note that this only covers reads from properties michael@0: // which are accounted for by type information, i.e. native data properties michael@0: // and elements. michael@0: // michael@0: // We also need a barrier if the object is a proxy, because then all bets michael@0: // are off, just as if it has unknown properties. michael@0: if (object->unknownProperties() || observed->empty() || michael@0: object->clasp()->isProxy()) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: jsid id = name ? NameToId(name) : JSID_VOID; michael@0: types::HeapTypeSetKey property = object->property(id); michael@0: if (property.maybeTypes() && !TypeSetIncludes(observed, MIRType_Value, property.maybeTypes())) michael@0: return true; michael@0: michael@0: // Type information for global objects is not required to reflect the michael@0: // initial 'undefined' value for properties, in particular global michael@0: // variables declared with 'var'. Until the property is assigned a value michael@0: // other than undefined, a barrier is required. michael@0: if (JSObject *obj = object->singleton()) { michael@0: if (name && types::CanHaveEmptyPropertyTypesForOwnProperty(obj) && michael@0: (!property.maybeTypes() || property.maybeTypes()->empty())) michael@0: { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: property.freeze(constraints); michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx, michael@0: types::CompilerConstraintList *constraints, michael@0: types::TypeObjectKey *object, PropertyName *name, michael@0: types::TemporaryTypeSet *observed, bool updateObserved) michael@0: { michael@0: // If this access has never executed, try to add types to the observed set michael@0: // according to any property which exists on the object or its prototype. michael@0: if (updateObserved && observed->empty() && name) { michael@0: JSObject *obj; michael@0: if (object->singleton()) michael@0: obj = object->singleton(); michael@0: else if (object->hasTenuredProto()) michael@0: obj = object->proto().toObjectOrNull(); michael@0: else michael@0: obj = nullptr; michael@0: michael@0: while (obj) { michael@0: if (!obj->getClass()->isNative()) michael@0: break; michael@0: michael@0: types::TypeObjectKey *typeObj = types::TypeObjectKey::get(obj); michael@0: if (propertycx) michael@0: typeObj->ensureTrackedProperty(propertycx, NameToId(name)); michael@0: michael@0: if (!typeObj->unknownProperties()) { michael@0: types::HeapTypeSetKey property = typeObj->property(NameToId(name)); michael@0: if (property.maybeTypes()) { michael@0: types::TypeSet::TypeList types; michael@0: if (!property.maybeTypes()->enumerateTypes(&types)) michael@0: break; michael@0: if (types.length()) { michael@0: // Note: the return value here is ignored. michael@0: observed->addType(types[0], GetIonContext()->temp->lifoAlloc()); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!obj->hasTenuredProto()) michael@0: break; michael@0: obj = obj->getProto(); michael@0: } michael@0: } michael@0: michael@0: return PropertyReadNeedsTypeBarrier(constraints, object, name, observed); michael@0: } michael@0: michael@0: bool michael@0: jit::PropertyReadNeedsTypeBarrier(JSContext *propertycx, michael@0: types::CompilerConstraintList *constraints, michael@0: MDefinition *obj, PropertyName *name, michael@0: types::TemporaryTypeSet *observed) michael@0: { michael@0: if (observed->unknown()) michael@0: return false; michael@0: michael@0: types::TypeSet *types = obj->resultTypeSet(); michael@0: if (!types || types->unknownObject()) michael@0: return true; michael@0: michael@0: bool updateObserved = types->getObjectCount() == 1; michael@0: for (size_t i = 0; i < types->getObjectCount(); i++) { michael@0: types::TypeObjectKey *object = types->getObject(i); michael@0: if (object) { michael@0: if (PropertyReadNeedsTypeBarrier(propertycx, constraints, object, name, michael@0: observed, updateObserved)) michael@0: { michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: jit::PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints, michael@0: MDefinition *obj, PropertyName *name, michael@0: types::TemporaryTypeSet *observed) michael@0: { michael@0: if (observed->unknown()) michael@0: return false; michael@0: michael@0: types::TypeSet *types = obj->resultTypeSet(); michael@0: if (!types || types->unknownObject()) michael@0: return true; michael@0: michael@0: for (size_t i = 0; i < types->getObjectCount(); i++) { michael@0: types::TypeObjectKey *object = types->getObject(i); michael@0: if (!object) michael@0: continue; michael@0: while (true) { michael@0: if (!object->hasTenuredProto()) michael@0: return true; michael@0: if (!object->proto().isObject()) michael@0: break; michael@0: object = types::TypeObjectKey::get(object->proto().toObject()); michael@0: if (PropertyReadNeedsTypeBarrier(constraints, object, name, observed)) michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, michael@0: MDefinition *obj, PropertyName *name) michael@0: { michael@0: // Determine if reading a property from obj is likely to be idempotent. michael@0: michael@0: types::TypeSet *types = obj->resultTypeSet(); michael@0: if (!types || types->unknownObject()) michael@0: return false; michael@0: michael@0: for (size_t i = 0; i < types->getObjectCount(); i++) { michael@0: types::TypeObjectKey *object = types->getObject(i); michael@0: if (object) { michael@0: if (object->unknownProperties()) michael@0: return false; michael@0: michael@0: // Check if the property has been reconfigured or is a getter. michael@0: types::HeapTypeSetKey property = object->property(NameToId(name)); michael@0: if (property.nonData(constraints)) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: jit::AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name, michael@0: types::TemporaryTypeSet *observed) michael@0: { michael@0: // Add objects to observed which *could* be observed by reading name from obj, michael@0: // to hopefully avoid unnecessary type barriers and code invalidations. michael@0: michael@0: LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); michael@0: michael@0: types::TemporaryTypeSet *types = obj->resultTypeSet(); michael@0: if (!types || types->unknownObject()) { michael@0: observed->addType(types::Type::AnyObjectType(), alloc); michael@0: return; michael@0: } michael@0: michael@0: for (size_t i = 0; i < types->getObjectCount(); i++) { michael@0: types::TypeObjectKey *object = types->getObject(i); michael@0: if (!object) michael@0: continue; michael@0: michael@0: if (object->unknownProperties()) { michael@0: observed->addType(types::Type::AnyObjectType(), alloc); michael@0: return; michael@0: } michael@0: michael@0: jsid id = name ? NameToId(name) : JSID_VOID; michael@0: types::HeapTypeSetKey property = object->property(id); michael@0: types::HeapTypeSet *types = property.maybeTypes(); michael@0: if (!types) michael@0: continue; michael@0: michael@0: if (types->unknownObject()) { michael@0: observed->addType(types::Type::AnyObjectType(), alloc); michael@0: return; michael@0: } michael@0: michael@0: for (size_t i = 0; i < types->getObjectCount(); i++) { michael@0: types::TypeObjectKey *object = types->getObject(i); michael@0: if (object) michael@0: observed->addType(types::Type::ObjectType(object), alloc); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static bool michael@0: TryAddTypeBarrierForWrite(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: MBasicBlock *current, types::TemporaryTypeSet *objTypes, michael@0: PropertyName *name, MDefinition **pvalue) michael@0: { michael@0: // Return whether pvalue was modified to include a type barrier ensuring michael@0: // that writing the value to objTypes/id will not require changing type michael@0: // information. michael@0: michael@0: // All objects in the set must have the same types for name. Otherwise, we michael@0: // could bail out without subsequently triggering a type change that michael@0: // invalidates the compiled code. michael@0: Maybe aggregateProperty; michael@0: michael@0: for (size_t i = 0; i < objTypes->getObjectCount(); i++) { michael@0: types::TypeObjectKey *object = objTypes->getObject(i); michael@0: if (!object) michael@0: continue; michael@0: michael@0: if (object->unknownProperties()) michael@0: return false; michael@0: michael@0: jsid id = name ? NameToId(name) : JSID_VOID; michael@0: types::HeapTypeSetKey property = object->property(id); michael@0: if (!property.maybeTypes()) michael@0: return false; michael@0: michael@0: if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) michael@0: return false; michael@0: michael@0: // This freeze is not required for correctness, but ensures that we michael@0: // will recompile if the property types change and the barrier can michael@0: // potentially be removed. michael@0: property.freeze(constraints); michael@0: michael@0: if (aggregateProperty.empty()) { michael@0: aggregateProperty.construct(property); michael@0: } else { michael@0: if (!aggregateProperty.ref().maybeTypes()->isSubset(property.maybeTypes()) || michael@0: !property.maybeTypes()->isSubset(aggregateProperty.ref().maybeTypes())) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: JS_ASSERT(!aggregateProperty.empty()); michael@0: michael@0: MIRType propertyType = aggregateProperty.ref().knownMIRType(constraints); michael@0: switch (propertyType) { michael@0: case MIRType_Boolean: michael@0: case MIRType_Int32: michael@0: case MIRType_Double: michael@0: case MIRType_String: { michael@0: // The property is a particular primitive type, guard by unboxing the michael@0: // value before the write. michael@0: if (!(*pvalue)->mightBeType(propertyType)) { michael@0: // The value's type does not match the property type. Just do a VM michael@0: // call as it will always trigger invalidation of the compiled code. michael@0: JS_ASSERT_IF((*pvalue)->type() != MIRType_Value, (*pvalue)->type() != propertyType); michael@0: return false; michael@0: } michael@0: MInstruction *ins = MUnbox::New(alloc, *pvalue, propertyType, MUnbox::Fallible); michael@0: current->add(ins); michael@0: *pvalue = ins; michael@0: return true; michael@0: } michael@0: default:; michael@0: } michael@0: michael@0: if ((*pvalue)->type() != MIRType_Value) michael@0: return false; michael@0: michael@0: types::TemporaryTypeSet *types = aggregateProperty.ref().maybeTypes()->clone(alloc.lifoAlloc()); michael@0: if (!types) michael@0: return false; michael@0: michael@0: MInstruction *ins = MMonitorTypes::New(alloc, *pvalue, types); michael@0: current->add(ins); michael@0: return true; michael@0: } michael@0: michael@0: static MInstruction * michael@0: AddTypeGuard(TempAllocator &alloc, MBasicBlock *current, MDefinition *obj, michael@0: types::TypeObjectKey *type, bool bailOnEquality) michael@0: { michael@0: MInstruction *guard; michael@0: michael@0: if (type->isTypeObject()) michael@0: guard = MGuardObjectType::New(alloc, obj, type->asTypeObject(), bailOnEquality); michael@0: else michael@0: guard = MGuardObjectIdentity::New(alloc, obj, type->asSingleObject(), bailOnEquality); michael@0: michael@0: current->add(guard); michael@0: michael@0: // For now, never move type object guards. michael@0: guard->setNotMovable(); michael@0: michael@0: return guard; michael@0: } michael@0: michael@0: bool michael@0: jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints, michael@0: MBasicBlock *current, MDefinition **pobj, michael@0: PropertyName *name, MDefinition **pvalue, bool canModify) michael@0: { michael@0: // If any value being written is not reflected in the type information for michael@0: // objects which obj could represent, a type barrier is needed when writing michael@0: // the value. As for propertyReadNeedsTypeBarrier, this only applies for michael@0: // properties that are accounted for by type information, i.e. normal data michael@0: // properties and elements. michael@0: michael@0: types::TemporaryTypeSet *types = (*pobj)->resultTypeSet(); michael@0: if (!types || types->unknownObject()) michael@0: return true; michael@0: michael@0: // If all of the objects being written to have property types which already michael@0: // reflect the value, no barrier at all is needed. Additionally, if all michael@0: // objects being written to have the same types for the property, and those michael@0: // types do *not* reflect the value, add a type barrier for the value. michael@0: michael@0: bool success = true; michael@0: for (size_t i = 0; i < types->getObjectCount(); i++) { michael@0: types::TypeObjectKey *object = types->getObject(i); michael@0: if (!object || object->unknownProperties()) michael@0: continue; michael@0: michael@0: // TI doesn't track TypedArray objects and should never insert a type michael@0: // barrier for them. michael@0: if (!name && IsTypedArrayClass(object->clasp())) michael@0: continue; michael@0: michael@0: jsid id = name ? NameToId(name) : JSID_VOID; michael@0: types::HeapTypeSetKey property = object->property(id); michael@0: if (!TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) { michael@0: // Either pobj or pvalue needs to be modified to filter out the michael@0: // types which the value could have but are not in the property, michael@0: // or a VM call is required. A VM call is always required if pobj michael@0: // and pvalue cannot be modified. michael@0: if (!canModify) michael@0: return true; michael@0: success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (success) michael@0: return false; michael@0: michael@0: // If all of the objects except one have property types which reflect the michael@0: // value, and the remaining object has no types at all for the property, michael@0: // add a guard that the object does not have that remaining object's type. michael@0: michael@0: if (types->getObjectCount() <= 1) michael@0: return true; michael@0: michael@0: types::TypeObjectKey *excluded = nullptr; michael@0: for (size_t i = 0; i < types->getObjectCount(); i++) { michael@0: types::TypeObjectKey *object = types->getObject(i); michael@0: if (!object || object->unknownProperties()) michael@0: continue; michael@0: if (!name && IsTypedArrayClass(object->clasp())) michael@0: continue; michael@0: michael@0: jsid id = name ? NameToId(name) : JSID_VOID; michael@0: types::HeapTypeSetKey property = object->property(id); michael@0: if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) michael@0: continue; michael@0: michael@0: if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded) michael@0: return true; michael@0: excluded = object; michael@0: } michael@0: michael@0: JS_ASSERT(excluded); michael@0: michael@0: *pobj = AddTypeGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true); michael@0: return false; michael@0: }