author | Brian Hackett <bhackett1024@gmail.com> |
Mon, 10 Dec 2012 12:02:31 -0700 (2012-12-10) | |
changeset 115546 | 8275b86c0b62e2b1f284388af7a4cacc6eaa8ecc |
parent 115545 | 508d1f5c60b050dbd72330204db528a796973229 |
child 115547 | c73e30eaccddedf560d3247a385a48721362a0f7 |
push id | 24015 |
push user | emorley@mozilla.com |
push date | Tue, 11 Dec 2012 15:51:15 +0000 (2012-12-11) |
treeherder | mozilla-central@87f8165c5a0b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 818869 |
milestone | 20.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/ion/IonAnalysis.cpp +++ b/js/src/ion/IonAnalysis.cpp @@ -69,16 +69,22 @@ ion::EliminateDeadResumePointOperands(MI // Scanning uses does not give us sufficient information to tell // where instructions that are involved in box/unbox operations or // parameter passing might be live. Rewriting uses of these terms // in resume points may affect the interpreter's behavior. Rather // than doing a more sophisticated analysis, just ignore these. if (ins->isUnbox() || ins->isParameter()) continue; + // If the instruction's behavior has been constant folded into a + // separate instruction, we can't determine precisely where the + // instruction becomes dead and can't eliminate its uses. + if (ins->isFolded()) + continue; + // Check if this instruction's result is only used within the // current block, and keep track of its last use in a definition // (not resume point). This requires the instructions in the block // to be numbered, ensured by running this immediately after alias // analysis. uint32_t maxDefinition = 0; for (MUseDefIterator uses(*ins); uses; uses++) { if (uses.def()->block() != *block || uses.def()->isBox() || uses.def()->isPassArg()) { @@ -114,20 +120,16 @@ ion::EliminateDeadResumePointOperands(MI // cannot be observed. If the undefined value were to flow to, // say, a dead property access the interpreter could throw an // exception; we avoid this problem by removing dead operands // before removing dead code. MConstant *constant = MConstant::New(UndefinedValue()); block->insertBefore(*(block->begin()), constant); uses = mrp->replaceOperand(uses, constant); } - - MResumePoint *mrp = ins->resumePoint(); - if (!mrp) - continue; } } return true; } // Instructions are useless if they are unused and have no side effects. // This pass eliminates useless instructions. @@ -154,19 +156,19 @@ ion::EliminateDeadCode(MIRGenerator *mir } return true; } static inline bool IsPhiObservable(MPhi *phi) { - // If the phi has bytecode uses, there may be no SSA uses but the value - // is still observable in the interpreter after a bailout. - if (phi->hasBytecodeUses()) + // If the phi has uses which are not reflected in SSA, then behavior in the + // interpreter may be affected by removing the phi. + if (phi->isFolded()) return true; // Check for any SSA uses. Note that this skips reading resume points, // which we don't count as actual uses. If the only uses are resume points, // then the SSA name is never consumed by the program. for (MUseDefIterator iter(phi); iter; iter++) { if (!iter.def()->isPhi()) return true; @@ -198,20 +200,19 @@ IsPhiRedundant(MPhi *phi) { MDefinition *first = phi->getOperand(0); for (size_t i = 1; i < phi->numOperands(); i++) { if (phi->getOperand(i) != first && phi->getOperand(i) != phi) return NULL; } - // Propagate the HasBytecodeUses flag if |phi| is replaced with - // another phi. - if (phi->hasBytecodeUses() && first->isPhi()) - first->toPhi()->setHasBytecodeUses(); + // Propagate the Folded flag if |phi| is replaced with another phi. + if (phi->isFolded()) + first->setFoldedUnchecked(); return first; } bool ion::EliminatePhis(MIRGenerator *mir, MIRGraph &graph) { Vector<MPhi *, 16, SystemAllocPolicy> worklist;
--- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -376,17 +376,17 @@ IonBuilder::processIterators() } } // Propagate the iterator and live status of phis to all other connected // phis. while (!worklist.empty()) { MPhi *phi = worklist.popCopy(); phi->setIterator(); - phi->setHasBytecodeUses(); + phi->setFoldedUnchecked(); for (MUseDefIterator iter(phi); iter; iter++) { if (iter.def()->isPhi()) { MPhi *other = iter.def()->toPhi(); if (!other->isIterator() && !worklist.append(other)) return false; } } @@ -514,20 +514,22 @@ IonBuilder::rewriteParameters() JSValueType definiteType = types->getKnownTypeTag(); if (definiteType == JSVAL_TYPE_UNKNOWN) continue; MInstruction *actual = NULL; switch (definiteType) { case JSVAL_TYPE_UNDEFINED: + param->setFoldedUnchecked(); actual = MConstant::New(UndefinedValue()); break; case JSVAL_TYPE_NULL: + param->setFoldedUnchecked(); actual = MConstant::New(NullValue()); break; default: actual = MUnbox::New(param, MIRTypeFromValueType(definiteType), MUnbox::Infallible); break; } @@ -681,17 +683,16 @@ IonBuilder::traverseBytecode() if (status == ControlStatus_Error) return false; if (!current) return true; } // Nothing in inspectOpcode() is allowed to advance the pc. JSOp op = JSOp(*pc); - markPhiBytecodeUses(pc); if (!inspectOpcode(op)) return false; pc += js_CodeSpec[op].length; #ifdef TRACK_SNAPSHOTS current->updateTrackedPc(pc); #endif } @@ -757,29 +758,16 @@ IonBuilder::snoopControlFlow(JSOp op) return ControlStatus_Error; default: break; } return ControlStatus_None; } -void -IonBuilder::markPhiBytecodeUses(jsbytecode *pc) -{ - unsigned nuses = analyze::GetUseCount(script_, pc - script_->code); - for (unsigned i = 0; i < nuses; i++) { - MDefinition *def = current->peek(-int32_t(i + 1)); - if (def->isPassArg()) - def = def->toPassArg()->getArgument(); - if (def->isPhi()) - def->toPhi()->setHasBytecodeUses(); - } -} - bool IonBuilder::inspectOpcode(JSOp op) { AssertCanGC(); // Don't compile fat opcodes, run the decomposed version instead. if (js_CodeSpec[op].format & JOF_DECOMPOSE) return true; @@ -4863,19 +4851,21 @@ IonBuilder::pushTypeBarrier(MInstruction return true; } if (!observed) { JSValueType type = actual->getKnownTypeTag(); MInstruction *replace = NULL; switch (type) { case JSVAL_TYPE_UNDEFINED: + ins->setFoldedUnchecked(); replace = MConstant::New(UndefinedValue()); break; case JSVAL_TYPE_NULL: + ins->setFoldedUnchecked(); replace = MConstant::New(NullValue()); break; case JSVAL_TYPE_UNKNOWN: break; default: { MIRType replaceType = MIRTypeFromValueType(type); if (ins->type() == MIRType_Value) replace = MUnbox::New(ins, replaceType, MUnbox::Infallible); @@ -5305,27 +5295,29 @@ IonBuilder::jsop_getelem_dense() } static MInstruction * GetTypedArrayLength(MDefinition *obj) { if (obj->isConstant()) { JSObject *array = &obj->toConstant()->value().toObject(); int32_t length = (int32_t) TypedArray::length(array); + obj->setFoldedUnchecked(); return MConstant::New(Int32Value(length)); } return MTypedArrayLength::New(obj); } static MInstruction * GetTypedArrayElements(MDefinition *obj) { if (obj->isConstant()) { JSObject *array = &obj->toConstant()->value().toObject(); void *data = TypedArray::viewData(array); + obj->setFoldedUnchecked(); return MConstantElements::New(data); } return MTypedArrayElements::New(obj); } bool IonBuilder::jsop_getelem_typed(int arrayType) { @@ -5634,34 +5626,36 @@ IonBuilder::jsop_arguments() current->push(lazyArguments_); return true; } bool IonBuilder::jsop_arguments_length() { // Type Inference has guaranteed this is an optimized arguments object. - current->pop(); + MDefinition *args = current->pop(); + args->setFoldedUnchecked(); MInstruction *ins = MArgumentsLength::New(); current->add(ins); current->push(ins); return true; } bool IonBuilder::jsop_arguments_getelem() { types::StackTypeSet *barrier = oracle->propertyReadBarrier(script_, pc); types::StackTypeSet *types = oracle->propertyRead(script_, pc); MDefinition *idx = current->pop(); // Type Inference has guaranteed this is an optimized arguments object. - current->pop(); + MDefinition *args = current->pop(); + args->setFoldedUnchecked(); // To ensure that we are not looking above the number of actual arguments. MArgumentsLength *length = MArgumentsLength::New(); current->add(length); // Ensure idx is an integer. MInstruction *index = MToInt32::New(idx); current->add(index); @@ -6267,21 +6261,23 @@ IonBuilder::getPropTryConstant(bool *emi return false; if (!isConstant) return true; MDefinition *obj = current->pop(); // Property access is a known constant -- safe to emit. - JS_ASSERT(!testString || !testObject); + JS_ASSERT(!testString || !testObject); if (testObject) current->add(MGuardObject::New(obj)); - else if (testString) + else if (testString) current->add(MGuardString::New(obj)); + else + obj->setFoldedUnchecked(); MConstant *known = MConstant::New(ObjectValue(*singleton)); if (singleton->isFunction()) { RootedFunction singletonFunc(cx, singleton->toFunction()); if (TestAreKnownDOMTypes(cx, unaryTypes.inTypes) && TestShouldDOMCall(cx, unaryTypes.inTypes, singletonFunc)) { FreezeDOMTypes(cx, unaryTypes.inTypes);
--- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -186,17 +186,16 @@ class IonBuilder : public MIRGenerator bool build(); bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint, MDefinition *thisDefn, MDefinitionVector &args); private: bool traverseBytecode(); ControlStatus snoopControlFlow(JSOp op); - void markPhiBytecodeUses(jsbytecode *pc); bool processIterators(); bool inspectOpcode(JSOp op); uint32_t readIndex(jsbytecode *pc); JSAtom *readAtom(jsbytecode *pc); bool abort(const char *message, ...); void spew(const char *message); static bool inliningEnabled() {
--- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -43,16 +43,17 @@ MIRType MIRTypeFromValue(const js::Value #define MIR_FLAG_LIST(_) \ _(InWorklist) \ _(EmittedAtUses) \ _(LoopInvariant) \ _(Commutative) \ _(Movable) /* Allow LICM and GVN to move this instruction */ \ _(Lowered) /* (Debug only) has a virtual register */ \ _(Guard) /* Not removable if uses == 0 */ \ + _(Folded) /* Has constant folded uses not reflected in SSA */ \ \ /* The instruction has been marked dead for lazy removal from resume * points. */ \ _(Unused) \ _(DOMFunction) /* Contains or uses a common DOM method function */ class MDefinition; @@ -2731,22 +2732,20 @@ class MFromCharCode } }; class MPhi : public MDefinition, public InlineForwardListNode<MPhi> { js::Vector<MDefinition *, 2, IonAllocPolicy> inputs_; uint32_t slot_; bool triedToSpecialize_; - bool hasBytecodeUses_; bool isIterator_; MPhi(uint32_t slot) : slot_(slot), triedToSpecialize_(false), - hasBytecodeUses_(false), isIterator_(false) { setResultType(MIRType_Value); } protected: void setOperand(size_t index, MDefinition *operand) { inputs_[index] = operand; @@ -2773,22 +2772,16 @@ class MPhi : public MDefinition, public setResultType(type); } bool addInput(MDefinition *ins); MDefinition *foldsTo(bool useValueNumbers); bool congruentTo(MDefinition * const &ins) const; - bool hasBytecodeUses() const { - return hasBytecodeUses_; - } - void setHasBytecodeUses() { - hasBytecodeUses_ = true; - } bool isIterator() const { return isIterator_; } void setIterator() { isIterator_ = true; } AliasSet getAliasSet() const {
--- a/js/src/ion/TypePolicy.cpp +++ b/js/src/ion/TypePolicy.cpp @@ -403,21 +403,23 @@ StoreTypedArrayPolicy::adjustInputs(MIns // The conversion is based on TypedArrayTemplate::setElementTail. switch (value->type()) { case MIRType_Int32: case MIRType_Double: case MIRType_Boolean: case MIRType_Value: break; case MIRType_Null: + value->setFoldedUnchecked(); value = MConstant::New(Int32Value(0)); ins->block()->insertBefore(ins, value->toInstruction()); break; case MIRType_Object: case MIRType_Undefined: + value->setFoldedUnchecked(); value = MConstant::New(DoubleValue(js_NaN)); ins->block()->insertBefore(ins, value->toInstruction()); break; case MIRType_String: value = boxAt(ins, value); break; default: JS_NOT_REACHED("Unexpected type");