Bug 1054972 - IonMonkey: Truncation for phis. r=nbp, a=sledru
authorDan Gohman <sunfish@mozilla.com>
Tue, 02 Sep 2014 13:01:31 -0700
changeset 216744 94dc71a06159
parent 216743 3e6571e74e01
child 216745 c0d46e44a6cb
push id3896
push userryanvm@gmail.com
push date2014-09-15 19:15 +0000
treeherdermozilla-beta@c5ee54bc44f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp, sledru
bugs1054972
milestone33.0
Bug 1054972 - IonMonkey: Truncation for phis. r=nbp, a=sledru
js/src/jit/MIR.h
js/src/jit/RangeAnalysis.cpp
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5035,16 +5035,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_;
@@ -5064,16 +5065,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)
@@ -5183,16 +5185,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
@@ -2196,16 +2196,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);
@@ -2334,16 +2348,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
@@ -2491,17 +2513,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++) {
@@ -2522,17 +2544,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
@@ -2549,17 +2571,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());
 
@@ -2568,45 +2590,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.
 //
@@ -2622,17 +2650,17 @@ RangeAnalysis::truncate()
     IonSpew(IonSpew_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;
@@ -2660,25 +2688,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.
     IonSpew(IonSpew_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();