diff --git a/js/src/ion/MIR.cpp b/js/src/ion/MIR.cpp index b5f28b97699..a286633a105 100644 --- a/js/src/ion/MIR.cpp +++ b/js/src/ion/MIR.cpp @@ -324,6 +324,9 @@ MDefinition::replaceAllUsesWith(MDefinition *dom) if (dom == this) return; + for (size_t i = 0; i < numOperands(); i++) + getOperand(i)->setUseRemovedUnchecked(); + for (MUseIterator i(usesBegin()); i != usesEnd(); ) { JS_ASSERT(i->producer() == this); i = i->consumer()->replaceOperand(i, dom); diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 4450915e2a9..5ba043fb576 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -54,7 +54,20 @@ MIRType MIRTypeFromValue(const js::Value &vp) * points. */ \ _(Unused) \ - _(DOMFunction) /* Contains or uses a common DOM method function */ + _(DOMFunction) /* Contains or uses a common DOM method function */ \ + \ + /* Marks if an instruction has fewer uses than the original code. + * E.g. UCE can remove code. + * Every instruction where an use is/was removed from an instruction and + * as a result the number of operands doesn't equal the original code + * need to get marked as UseRemoved. This is important for truncation + * analysis to know, since if all original uses are still present, + * it can ignore resumepoints. + * Currently this is done for every pass after IonBuilder and before + * Truncate Doubles. So every time removeUse is called, UseRemoved needs + * to get set. + */ \ + _(UseRemoved) class MDefinition; class MInstruction; diff --git a/js/src/ion/RangeAnalysis.cpp b/js/src/ion/RangeAnalysis.cpp index e374b2cd69e..442be04b774 100644 --- a/js/src/ion/RangeAnalysis.cpp +++ b/js/src/ion/RangeAnalysis.cpp @@ -1403,13 +1403,21 @@ MToDouble::isOperandTruncated(size_t index) const return type() == MIRType_Int32; } -// Ensure that all observables (non-resume point) uses can work with a truncated +// Ensure that all observables uses can work with a truncated // version of the |candidate|'s result. static bool AllUsesTruncate(MInstruction *candidate) { - for (MUseDefIterator use(candidate); use; use++) { - if (!use.def()->isOperandTruncated(use.index())) + for (MUseIterator use(candidate->usesBegin()); use != candidate->usesEnd(); use++) { + if (!use->consumer()->isDefinition()) { + // We can only skip testing resume points, if all original uses are still present. + // Only than testing all uses is enough to guarantee the truncation isn't observerable. + if (candidate->isUseRemoved()) + return false; + continue; + } + + if (!use->consumer()->toDefinition()->isOperandTruncated(use->index())) return false; } diff --git a/js/src/ion/UnreachableCodeElimination.cpp b/js/src/ion/UnreachableCodeElimination.cpp index b664dafe39e..08bf1895276 100644 --- a/js/src/ion/UnreachableCodeElimination.cpp +++ b/js/src/ion/UnreachableCodeElimination.cpp @@ -157,10 +157,12 @@ UnreachableCodeElimination::checkDependencyAndRemoveUsesFromUnmarkedBlocks(MDefi rerunAliasAnalysis_ = true; for (MUseIterator iter(instr->usesBegin()); iter != instr->usesEnd(); ) { - if (!iter->consumer()->block()->isMarked()) + if (!iter->consumer()->block()->isMarked()) { + instr->setUseRemovedUnchecked(); iter = instr->removeUse(iter); - else + } else { iter++; + } } } @@ -235,10 +237,10 @@ UnreachableCodeElimination::removeUnmarkedBlocksAndClearDominators() } } - // When we remove a call, we can't leave the corresponding MPassArg in the graph. - // Since lowering will fail. Replace it with the argument for the exceptional - // case when it is kept alive in a ResumePoint. - // DCE will remove the unused MPassArg instruction. + // When we remove a call, we can't leave the corresponding MPassArg + // in the graph. Since lowering will fail. Replace it with the + // argument for the exceptional case when it is kept alive in a + // ResumePoint. DCE will remove the unused MPassArg instruction. for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) { if (iter->isCall()) { MCall *call = iter->toCall(); diff --git a/js/src/jit-test/tests/ion/bug882565-1.js b/js/src/jit-test/tests/ion/bug882565-1.js new file mode 100644 index 00000000000..84db9258ff5 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug882565-1.js @@ -0,0 +1,21 @@ +function zero() { return 0; } +function f(x, a) { + var test = 0x7fffffff; + + for (var i=0; i<100; i++) + { + if (i == 0) { + test += 1; + var t = (test > zero()) * (0xffffffff >>> x); + } + var test2 = test | 0; + return [test2,t]; + } +} +var t = f(0, ""); +assertEq(t[0], 0x80000000 | 0); +assertEq(t[1], 0xffffffff >>> 0); + +var t = f(0); +assertEq(t[0], 0x80000000 | 0); +assertEq(t[1], 0xffffffff >>> 0); diff --git a/js/src/jit-test/tests/ion/bug882565.js b/js/src/jit-test/tests/ion/bug882565.js new file mode 100644 index 00000000000..5e51ac79275 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug882565.js @@ -0,0 +1,4 @@ +function zero() { return 0; } +function f(x) { return (0xffffffff > zero()) * (0xffffffff >>> x); } +assertEq(f(0), 4294967295); +assertEq(f(0), 4294967295);