Bug 1054972 - IonMonkey: Truncation for phis r=nbp
authorDan Gohman <sunfish@mozilla.com>
Tue, 02 Sep 2014 13:01:31 -0700
changeset 203264 61f05ae95aa46a44f84ad462fa9913aaa2e1b713
parent 203263 eeb1696010875033c570bd47b2f197219b6295b7
child 203265 afac7b1435bc55c1dacb792a4e28d4448b8ae88c
push id27425
push userryanvm@gmail.com
push dateWed, 03 Sep 2014 20:38:59 +0000
treeherdermozilla-central@acbdce59da2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1054972
milestone35.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
Bug 1054972 - IonMonkey: Truncation for phis r=nbp
js/src/jit/MIR.h
js/src/jit/RangeAnalysis.cpp
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5776,16 +5776,17 @@ class MLoadArrowThis
         return AliasSet::None();
     }
 };
 
 class MPhi MOZ_FINAL : public MDefinition, public InlineListNode<MPhi>
 {
     js::Vector<MUse, 2, IonAllocPolicy> inputs_;
 
+    TruncateKind truncateKind_;
     bool hasBackedgeType_;
     bool triedToSpecialize_;
     bool isIterator_;
     bool canProduceFloat32_;
     bool canConsumeFloat32_;
 
 #if DEBUG
     bool specialized_;
@@ -5805,16 +5806,17 @@ class MPhi MOZ_FINAL : public MDefinitio
         return &inputs_[index];
     }
 
   public:
     INSTRUCTION_HEADER(Phi)
 
     MPhi(TempAllocator &alloc, MIRType resultType)
       : inputs_(alloc),
+        truncateKind_(NoTruncate),
         hasBackedgeType_(false),
         triedToSpecialize_(false),
         isIterator_(false),
         canProduceFloat32_(false),
         canConsumeFloat32_(false)
 #if DEBUG
         , specialized_(false)
         , capacity_(0)
@@ -5924,16 +5926,19 @@ class MPhi MOZ_FINAL : public MDefinitio
 
     bool canConsumeFloat32(MUse *use) const {
         return canConsumeFloat32_;
     }
 
     void setCanConsumeFloat32(bool can) {
         canConsumeFloat32_ = can;
     }
+
+    TruncateKind operandTruncateKind(size_t index) const;
+    bool truncate(TruncateKind kind);
 };
 
 // The goal of a Beta node is to split a def at a conditionally taken
 // branch, so that uses dominated by it have a different name.
 class MBeta : public MUnaryInstruction
 {
   private:
     // This is the range induced by a comparison and branch in a preceding
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -2159,16 +2159,30 @@ MConstant::truncate(TruncateKind kind)
     value_.setInt32(res);
     setResultType(MIRType_Int32);
     if (range())
         range()->setInt32(res, res);
     return true;
 }
 
 bool
+MPhi::truncate(TruncateKind kind)
+{
+    if (type() == MIRType_Double || type() == MIRType_Int32) {
+        truncateKind_ = kind;
+        setResultType(MIRType_Int32);
+        if (kind >= IndirectTruncate && range())
+            range()->wrapAroundToInt32();
+        return true;
+    }
+
+    return false;
+}
+
+bool
 MAdd::truncate(TruncateKind kind)
 {
     // Remember analysis, needed for fallible checks.
     setTruncateKind(kind);
 
     if (type() == MIRType_Double || type() == MIRType_Int32) {
         specialization_ = MIRType_Int32;
         setResultType(MIRType_Int32);
@@ -2298,16 +2312,24 @@ MLimitedTruncate::truncate(TruncateKind 
 MDefinition::TruncateKind
 MDefinition::operandTruncateKind(size_t index) const
 {
     // Generic routine: We don't know anything.
     return NoTruncate;
 }
 
 MDefinition::TruncateKind
+MPhi::operandTruncateKind(size_t index) const
+{
+    // The truncation applied to a phi is effectively applied to the phi's
+    // operands.
+    return truncateKind_;
+}
+
+MDefinition::TruncateKind
 MTruncateToInt32::operandTruncateKind(size_t index) const
 {
     // This operator is an explicit truncate to int32.
     return Truncate;
 }
 
 MDefinition::TruncateKind
 MBinaryBitwiseInstruction::operandTruncateKind(size_t index) const
@@ -2455,17 +2477,17 @@ TruncateTest(TempAllocator &alloc, MTest
     }
 
     phi->setResultType(MIRType_Int32);
 }
 
 // Examine all the users of |candidate| and determine the most aggressive
 // truncate kind that satisfies all of them.
 static MDefinition::TruncateKind
-ComputeRequestedTruncateKind(MInstruction *candidate)
+ComputeRequestedTruncateKind(MDefinition *candidate)
 {
     // If the value naturally produces an int32 value (before bailout checks)
     // that needs no conversion, we don't have to worry about resume points
     // seeing truncated values.
     bool needsConversion = !candidate->range() || !candidate->range()->isInt32();
 
     MDefinition::TruncateKind kind = MDefinition::Truncate;
     for (MUseIterator use(candidate->usesBegin()); use != candidate->usesEnd(); use++) {
@@ -2486,17 +2508,17 @@ ComputeRequestedTruncateKind(MInstructio
         if (kind == MDefinition::NoTruncate)
             break;
     }
 
     return kind;
 }
 
 static MDefinition::TruncateKind
-ComputeTruncateKind(MInstruction *candidate)
+ComputeTruncateKind(MDefinition *candidate)
 {
     // Compare operations might coerce its inputs to int32 if the ranges are
     // correct.  So we do not need to check if all uses are coerced.
     if (candidate->isCompare())
         return MDefinition::TruncateAfterBailouts;
 
     // Set truncated flag if range analysis ensure that it has no
     // rounding errors and no fractional part. Note that we can't use
@@ -2513,17 +2535,17 @@ ComputeTruncateKind(MInstruction *candid
     if (canHaveRoundingErrors)
         return MDefinition::NoTruncate;
 
     // Ensure all observable uses are truncated.
     return ComputeRequestedTruncateKind(candidate);
 }
 
 static void
-RemoveTruncatesOnOutput(MInstruction *truncated)
+RemoveTruncatesOnOutput(MDefinition *truncated)
 {
     // Compare returns a boolean so it doen't have any output truncates.
     if (truncated->isCompare())
         return;
 
     JS_ASSERT(truncated->type() == MIRType_Int32);
     JS_ASSERT(Range(truncated).isInt32());
 
@@ -2532,45 +2554,51 @@ RemoveTruncatesOnOutput(MInstruction *tr
         if (!def->isTruncateToInt32() || !def->isToInt32())
             continue;
 
         def->replaceAllUsesWith(truncated);
     }
 }
 
 static void
-AdjustTruncatedInputs(TempAllocator &alloc, MInstruction *truncated)
+AdjustTruncatedInputs(TempAllocator &alloc, MDefinition *truncated)
 {
     MBasicBlock *block = truncated->block();
     for (size_t i = 0, e = truncated->numOperands(); i < e; i++) {
         MDefinition::TruncateKind kind = truncated->operandTruncateKind(i);
         if (kind == MDefinition::NoTruncate)
             continue;
 
         MDefinition *input = truncated->getOperand(i);
         if (input->type() == MIRType_Int32)
             continue;
 
         if (input->isToDouble() && input->getOperand(0)->type() == MIRType_Int32) {
             JS_ASSERT(input->range()->isInt32());
             truncated->replaceOperand(i, input->getOperand(0));
-        } else if (kind == MDefinition::TruncateAfterBailouts) {
-            MToInt32 *op = MToInt32::New(alloc, truncated->getOperand(i));
-            block->insertBefore(truncated, op);
-            truncated->replaceOperand(i, op);
         } else {
-            MTruncateToInt32 *op = MTruncateToInt32::New(alloc, truncated->getOperand(i));
-            block->insertBefore(truncated, op);
+            MInstruction *op;
+            if (kind == MDefinition::TruncateAfterBailouts)
+                op = MToInt32::New(alloc, truncated->getOperand(i));
+            else
+                op = MTruncateToInt32::New(alloc, truncated->getOperand(i));
+
+            if (truncated->isPhi()) {
+                MBasicBlock *pred = op->block()->getPredecessor(i);
+                pred->insertBefore(pred->lastIns(), op);
+            } else {
+                block->insertBefore(truncated->toInstruction(), op);
+            }
             truncated->replaceOperand(i, op);
         }
     }
 
     if (truncated->isToDouble()) {
-        truncated->replaceAllUsesWith(truncated->getOperand(0));
-        block->discard(truncated);
+        truncated->replaceAllUsesWith(truncated->toToDouble()->getOperand(0));
+        block->discard(truncated->toToDouble());
     }
 }
 
 // Iterate backward on all instruction and attempt to truncate operations for
 // each instruction which respect the following list of predicates: Has been
 // analyzed by range analysis, the range has no rounding errors, all uses cases
 // are truncating the result.
 //
@@ -2586,17 +2614,17 @@ RangeAnalysis::truncate()
     JitSpew(JitSpew_Range, "Do range-base truncation (backward loop)");
 
     // Automatic truncation is disabled for AsmJS because the truncation logic
     // is based on IonMonkey which assumes that we can bailout if the truncation
     // logic fails. As AsmJS code has no bailout mechanism, it is safer to avoid
     // any automatic truncations.
     MOZ_ASSERT(!mir->compilingAsmJS());
 
-    Vector<MInstruction *, 16, SystemAllocPolicy> worklist;
+    Vector<MDefinition *, 16, SystemAllocPolicy> worklist;
     Vector<MBinaryBitwiseInstruction *, 16, SystemAllocPolicy> bitops;
 
     for (PostorderIterator block(graph_.poBegin()); block != graph_.poEnd(); block++) {
         for (MInstructionReverseIterator iter(block->rbegin()); iter != block->rend(); iter++) {
             if (iter->type() == MIRType_None) {
                 if (iter->isTest())
                     TruncateTest(alloc(), iter->toTest());
                 continue;
@@ -2624,25 +2652,40 @@ RangeAnalysis::truncate()
                 continue;
 
             // Delay updates of inputs/outputs to avoid creating node which
             // would be removed by the truncation of the next operations.
             iter->setInWorklist();
             if (!worklist.append(*iter))
                 return false;
         }
+        for (MPhiIterator iter(block->phisBegin()), end(block->phisEnd()); iter != end; ++iter) {
+            MDefinition::TruncateKind kind = ComputeTruncateKind(*iter);
+            if (kind == MDefinition::NoTruncate)
+                continue;
+
+            // Truncate this phi if possible.
+            if (!iter->truncate(kind))
+                continue;
+
+            // Delay updates of inputs/outputs to avoid creating node which
+            // would be removed by the truncation of the next operations.
+            iter->setInWorklist();
+            if (!worklist.append(*iter))
+                return false;
+        }
     }
 
     // Update inputs/outputs of truncated instructions.
     JitSpew(JitSpew_Range, "Do graph type fixup (dequeue)");
     while (!worklist.empty()) {
-        MInstruction *ins = worklist.popCopy();
-        ins->setNotInWorklist();
-        RemoveTruncatesOnOutput(ins);
-        AdjustTruncatedInputs(alloc(), ins);
+        MDefinition *def = worklist.popCopy();
+        def->setNotInWorklist();
+        RemoveTruncatesOnOutput(def);
+        AdjustTruncatedInputs(alloc(), def);
     }
 
     // Fold any unnecessary bitops in the graph, such as (x | 0) on an integer
     // input. This is done after range analysis rather than during GVN as the
     // presence of the bitop can change which instructions are truncated.
     for (size_t i = 0; i < bitops.length(); i++) {
         MBinaryBitwiseInstruction *ins = bitops[i];
         MDefinition *folded = ins->foldUnnecessaryBitop();