LCOV - code coverage report
Current view: top level - js/src/jit - LICM.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 96 109 88.1 %
Date: 2017-07-14 16:53:18 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "jit/LICM.h"
       8             : 
       9             : #include "jit/IonAnalysis.h"
      10             : #include "jit/JitSpewer.h"
      11             : #include "jit/MIRGenerator.h"
      12             : #include "jit/MIRGraph.h"
      13             : 
      14             : using namespace js;
      15             : using namespace js::jit;
      16             : 
      17             : // Test whether any instruction in the loop possiblyCalls().
      18             : static bool
      19           5 : LoopContainsPossibleCall(MIRGraph& graph, MBasicBlock* header, MBasicBlock* backedge)
      20             : {
      21          85 :     for (auto i(graph.rpoBegin(header)); ; ++i) {
      22          85 :         MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop");
      23          85 :         MBasicBlock* block = *i;
      24          85 :         if (!block->isMarked())
      25           6 :             continue;
      26             : 
      27         289 :         for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ++insIter) {
      28         215 :             MInstruction* ins = *insIter;
      29         215 :             if (ins->possiblyCalls()) {
      30             : #ifdef JS_JITSPEW
      31           5 :                 JitSpew(JitSpew_LICM, "    Possile call found at %s%u", ins->opName(), ins->id());
      32             : #endif
      33           5 :                 return true;
      34             :             }
      35             :         }
      36             : 
      37          74 :         if (block == backedge)
      38           0 :             break;
      39          80 :     }
      40           0 :     return false;
      41             : }
      42             : 
      43             : // When a nested loop has no exits back into what would be its parent loop,
      44             : // MarkLoopBlocks on the parent loop doesn't mark the blocks of the nested
      45             : // loop, since they technically aren't part of the loop. However, AliasAnalysis
      46             : // currently does consider such nested loops to be part of their parent
      47             : // loops. Consequently, we can't use IsInLoop on dependency() values; we must
      48             : // test whether a dependency() is *before* the loop, even if it is not
      49             : // technically in the loop.
      50             : static bool
      51          32 : IsBeforeLoop(MDefinition* ins, MBasicBlock* header)
      52             : {
      53          32 :     return ins->block()->id() < header->id();
      54             : }
      55             : 
      56             : // Test whether the given instruction is inside the loop (and thus not
      57             : // loop-invariant).
      58             : static bool
      59         510 : IsInLoop(MDefinition* ins)
      60             : {
      61         510 :     return ins->block()->isMarked();
      62             : }
      63             : 
      64             : // Test whether the given instruction is cheap and not worth hoisting unless
      65             : // one of its users will be hoisted as well.
      66             : static bool
      67         548 : RequiresHoistedUse(const MDefinition* ins, bool hasCalls)
      68             : {
      69         548 :     if (ins->isConstantElements())
      70           0 :         return true;
      71             : 
      72         548 :     if (ins->isBox()) {
      73          20 :         MOZ_ASSERT(!ins->toBox()->input()->isBox(),
      74             :                 "Box of a box could lead to unbounded recursion");
      75          20 :         return true;
      76             :     }
      77             : 
      78             :     // Integer constants are usually cheap and aren't worth hoisting on their
      79             :     // own, in general. Floating-point constants typically are worth hoisting,
      80             :     // unless they'll end up being spilled (eg. due to a call).
      81         528 :     if (ins->isConstant() && (!IsFloatingPointType(ins->type()) || hasCalls))
      82         112 :         return true;
      83             : 
      84         416 :     return false;
      85             : }
      86             : 
      87             : // Test whether the given instruction has any operands defined within the loop.
      88             : static bool
      89         612 : HasOperandInLoop(MInstruction* ins, bool hasCalls)
      90             : {
      91             :     // An instruction is only loop invariant if it and all of its operands can
      92             :     // be safely hoisted into the loop preheader.
      93         706 :     for (size_t i = 0, e = ins->numOperands(); i != e; ++i) {
      94         506 :         MDefinition* op = ins->getOperand(i);
      95             : 
      96         506 :         if (!IsInLoop(op))
      97          74 :             continue;
      98             : 
      99         432 :         if (RequiresHoistedUse(op, hasCalls)) {
     100             :             // Recursively test for loop invariance. Note that the recursion is
     101             :             // bounded because we require RequiresHoistedUse to be set at each
     102             :             // level.
     103          20 :             if (!HasOperandInLoop(op->toInstruction(), hasCalls))
     104          20 :                 continue;
     105             :         }
     106             : 
     107         412 :         return true;
     108             :     }
     109         200 :     return false;
     110             : }
     111             : 
     112             : // Test whether the given instruction is hoistable, ignoring memory
     113             : // dependencies.
     114             : static bool
     115        1536 : IsHoistableIgnoringDependency(MInstruction* ins, bool hasCalls)
     116             : {
     117        2128 :     return ins->isMovable() && !ins->isEffectful() && !ins->neverHoist() &&
     118        2128 :            !HasOperandInLoop(ins, hasCalls);
     119             : }
     120             : 
     121             : // Test whether the given instruction has a memory dependency inside the loop.
     122             : static bool
     123         148 : HasDependencyInLoop(MInstruction* ins, MBasicBlock* header)
     124             : {
     125             :     // Don't hoist if this instruction depends on a store inside the loop.
     126         148 :     if (MDefinition* dep = ins->dependency())
     127          32 :         return !IsBeforeLoop(dep, header);
     128         116 :     return false;
     129             : }
     130             : 
     131             : // Test whether the given instruction is hoistable.
     132             : static bool
     133         826 : IsHoistable(MInstruction* ins, MBasicBlock* header, bool hasCalls)
     134             : {
     135         826 :     return IsHoistableIgnoringDependency(ins, hasCalls) && !HasDependencyInLoop(ins, header);
     136             : }
     137             : 
     138             : // In preparation for hoisting an instruction, hoist any of its operands which
     139             : // were too cheap to hoist on their own.
     140             : static void
     141           4 : MoveDeferredOperands(MInstruction* ins, MInstruction* hoistPoint, bool hasCalls)
     142             : {
     143             :     // If any of our operands were waiting for a user to be hoisted, make a note
     144             :     // to hoist them.
     145           8 :     for (size_t i = 0, e = ins->numOperands(); i != e; ++i) {
     146           4 :         MDefinition* op = ins->getOperand(i);
     147           4 :         if (!IsInLoop(op))
     148           4 :             continue;
     149           0 :         MOZ_ASSERT(RequiresHoistedUse(op, hasCalls),
     150             :                    "Deferred loop-invariant operand is not cheap");
     151           0 :         MInstruction* opIns = op->toInstruction();
     152             : 
     153             :         // Recursively move the operands. Note that the recursion is bounded
     154             :         // because we require RequiresHoistedUse to be set at each level.
     155           0 :         MoveDeferredOperands(opIns, hoistPoint, hasCalls);
     156             : 
     157             : #ifdef JS_JITSPEW
     158           0 :         JitSpew(JitSpew_LICM, "    Hoisting %s%u (now that a user will be hoisted)",
     159           0 :                 opIns->opName(), opIns->id());
     160             : #endif
     161             : 
     162           0 :         opIns->block()->moveBefore(hoistPoint, opIns);
     163             :     }
     164           4 : }
     165             : 
     166             : static void
     167         293 : VisitLoopBlock(MBasicBlock* block, MBasicBlock* header, MInstruction* hoistPoint, bool hasCalls)
     168             : {
     169        1119 :     for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ) {
     170         826 :         MInstruction* ins = *insIter++;
     171             : 
     172         826 :         if (!IsHoistable(ins, header, hasCalls)) {
     173             : #ifdef JS_JITSPEW
     174         710 :             if (IsHoistableIgnoringDependency(ins, hasCalls)) {
     175          96 :                 JitSpew(JitSpew_LICM, "    %s%u isn't hoistable due to dependency on %s%u",
     176          32 :                         ins->opName(), ins->id(),
     177          64 :                         ins->dependency()->opName(), ins->dependency()->id());
     178             :             }
     179             : #endif
     180         710 :             continue;
     181             :         }
     182             : 
     183             :         // Don't hoist a cheap constant if it doesn't enable us to hoist one of
     184             :         // its uses. We want those instructions as close as possible to their
     185             :         // use, to minimize register pressure.
     186         116 :         if (RequiresHoistedUse(ins, hasCalls)) {
     187             : #ifdef JS_JITSPEW
     188         224 :             JitSpew(JitSpew_LICM, "    %s%u will be hoisted only if its users are",
     189         224 :                     ins->opName(), ins->id());
     190             : #endif
     191         112 :             continue;
     192             :         }
     193             : 
     194             :         // Hoist operands which were too cheap to hoist on their own.
     195           4 :         MoveDeferredOperands(ins, hoistPoint, hasCalls);
     196             : 
     197             : #ifdef JS_JITSPEW
     198           4 :         JitSpew(JitSpew_LICM, "    Hoisting %s%u", ins->opName(), ins->id());
     199             : #endif
     200             : 
     201             :         // Move the instruction to the hoistPoint.
     202           4 :         block->moveBefore(hoistPoint, ins);
     203             :     }
     204         293 : }
     205             : 
     206             : static void
     207           5 : VisitLoop(MIRGraph& graph, MBasicBlock* header)
     208             : {
     209           5 :     MInstruction* hoistPoint = header->loopPredecessor()->lastIns();
     210             : 
     211             : #ifdef JS_JITSPEW
     212          10 :     JitSpew(JitSpew_LICM, "  Visiting loop with header block%u, hoisting to %s%u",
     213          10 :             header->id(), hoistPoint->opName(), hoistPoint->id());
     214             : #endif
     215             : 
     216           5 :     MBasicBlock* backedge = header->backedge();
     217             : 
     218             :     // This indicates whether the loop contains calls or other things which
     219             :     // clobber most or all floating-point registers. In such loops,
     220             :     // floating-point constants should not be hoisted unless it enables further
     221             :     // hoisting.
     222           5 :     bool hasCalls = LoopContainsPossibleCall(graph, header, backedge);
     223             : 
     224         318 :     for (auto i(graph.rpoBegin(header)); ; ++i) {
     225         318 :         MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop");
     226         318 :         MBasicBlock* block = *i;
     227         318 :         if (!block->isMarked())
     228          25 :             continue;
     229             : 
     230         293 :         VisitLoopBlock(block, header, hoistPoint, hasCalls);
     231             : 
     232         293 :         if (block == backedge)
     233           5 :             break;
     234         313 :     }
     235           5 : }
     236             : 
     237             : bool
     238           8 : jit::LICM(MIRGenerator* mir, MIRGraph& graph)
     239             : {
     240           8 :     JitSpew(JitSpew_LICM, "Beginning LICM pass");
     241             : 
     242             :     // Iterate in RPO to visit outer loops before inner loops. We'd hoist the
     243             :     // same things either way, but outer first means we do a little less work.
     244         411 :     for (auto i(graph.rpoBegin()), e(graph.rpoEnd()); i != e; ++i) {
     245         403 :         MBasicBlock* header = *i;
     246         403 :         if (!header->isLoopHeader())
     247         796 :             continue;
     248             : 
     249             :         bool canOsr;
     250           5 :         size_t numBlocks = MarkLoopBlocks(graph, header, &canOsr);
     251             : 
     252           5 :         if (numBlocks == 0) {
     253           0 :             JitSpew(JitSpew_LICM, "  Loop with header block%u isn't actually a loop", header->id());
     254           0 :             continue;
     255             :         }
     256             : 
     257             :         // Hoisting out of a loop that has an entry from the OSR block in
     258             :         // addition to its normal entry is tricky. In theory we could clone
     259             :         // the instruction and insert phis.
     260           5 :         if (!canOsr)
     261           5 :             VisitLoop(graph, header);
     262             :         else
     263           0 :             JitSpew(JitSpew_LICM, "  Skipping loop with header block%u due to OSR", header->id());
     264             : 
     265           5 :         UnmarkLoopBlocks(graph, header);
     266             : 
     267           5 :         if (mir->shouldCancel("LICM (main loop)"))
     268           0 :             return false;
     269             :     }
     270             : 
     271           8 :     return true;
     272             : }

Generated by: LCOV version 1.13