LCOV - code coverage report
Current view: directory - js/src/methodjit - LoopState.h (source / functions) Found Hit Coverage
Test: app.info Lines: 31 31 100.0 %
Date: 2012-06-02 Functions: 23 23 100.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=4 sw=4 et tw=99:
       3                 :  *
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
      18                 :  * May 28, 2008.
      19                 :  *
      20                 :  * The Initial Developer of the Original Code is
      21                 :  *   Brendan Eich <brendan@mozilla.org>
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      27                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #if !defined jsjaeger_loopstate_h__ && defined JS_METHODJIT
      40                 : #define jsjaeger_loopstate_h__
      41                 : 
      42                 : #include "jsanalyze.h"
      43                 : #include "methodjit/Compiler.h"
      44                 : 
      45                 : namespace js {
      46                 : namespace mjit {
      47                 : 
      48                 : /*
      49                 :  * The LoopState keeps track of register and analysis state within the loop
      50                 :  * currently being processed by the Compiler.
      51                 :  *
      52                 :  * There are several analyses we do that are specific to loops: loop carried
      53                 :  * registers, bounds check hoisting, and loop invariant code motion. Brief
      54                 :  * descriptions of these analyses:
      55                 :  *
      56                 :  * Loop carried registers. We allocate registers as we emit code, in a single
      57                 :  * forward pass over the script. Normally this would mean we need to pick the
      58                 :  * register allocation at the head of the loop before any of the body has been
      59                 :  * processed. Instead, while processing the loop body we retroactively mark
      60                 :  * registers as holding the payload of certain entries at the head (being
      61                 :  * carried around the loop), so that the head's allocation ends up holding
      62                 :  * registers that are likely to be used shortly. This can be done provided that
      63                 :  * (a) the register has not been touched since the loop head, (b) the slot
      64                 :  * has not been modified or separately assigned a different register, and (c)
      65                 :  * all prior slow path rejoins in the loop are patched with reloads of the
      66                 :  * register. The register allocation at the loop head must have all entries
      67                 :  * synced, so that prior slow path syncs do not also need patching.
      68                 :  *
      69                 :  * Bounds check hoisting. If we can determine a loop invariant test which
      70                 :  * implies the bounds check at one or more array accesses, we hoist that and
      71                 :  * check it when initially entering the loop (from JIT code or the
      72                 :  * interpreter) and after every stub or C++ call.
      73                 :  *
      74                 :  * Loop invariant code motion. If we can determine a computation (arithmetic,
      75                 :  * array slot pointer or property access) is loop invariant, we give it a slot
      76                 :  * on the stack and preserve its value throughout the loop. We can allocate
      77                 :  * and carry registers for loop invariant slots as for normal slots. These
      78                 :  * slots sit above the frame's normal slots, and are transient --- they are
      79                 :  * clobbered whenever a new frame is pushed. We thus regenerate the loop
      80                 :  * invariant slots after every C++ and scripted call, and avoid doing LICM on
      81                 :  * loops which have such calls. This has a nice property that the slots only
      82                 :  * need to be loop invariant wrt the side effects that happen directly in the
      83                 :  * loop; if C++ calls a getter which scribbles on the object properties
      84                 :  * involved in an 'invariant' then we will reload the invariant's new value
      85                 :  * after the call finishes.
      86                 :  */
      87                 : 
      88                 : struct TemporaryCopy;
      89                 : 
      90                 : enum InvariantArrayKind { DENSE_ARRAY, TYPED_ARRAY };
      91                 : 
      92                 : class LoopState : public MacroAssemblerTypedefs
      93           33602 : {
      94                 :     JSContext *cx;
      95                 :     analyze::CrossScriptSSA *ssa;
      96                 :     JSScript *outerScript;
      97                 :     analyze::ScriptAnalysis *outerAnalysis;
      98                 : 
      99                 :     Compiler &cc;
     100                 :     FrameState &frame;
     101                 : 
     102                 :     /* Basic information about this loop. */
     103                 :     analyze::LoopAnalysis *lifetime;
     104                 : 
     105                 :     /* Allocation at the head of the loop, has all loop carried variables. */
     106                 :     RegisterAllocation *alloc;
     107                 : 
     108                 :     /*
     109                 :      * Set if this is not a do-while loop and the compiler has advanced past
     110                 :      * the loop's entry point.
     111                 :      */
     112                 :     bool reachedEntryPoint;
     113                 : 
     114                 :     /*
     115                 :      * Jump which initially enters the loop. The state is synced when this jump
     116                 :      * occurs, and needs a trampoline generated to load the right registers
     117                 :      * before going to entryTarget.
     118                 :      */
     119                 :     Jump entry;
     120                 : 
     121                 :     /* Registers available for loop variables. */
     122                 :     Registers loopRegs;
     123                 : 
     124                 :     /* Whether to skip all bounds check hoisting and loop invariant code analysis. */
     125                 :     bool skipAnalysis;
     126                 : 
     127                 :     /* Prior stub rejoins to patch when new loop registers are allocated. */
     128          624214 :     struct StubJoin {
     129                 :         unsigned index;
     130                 :         bool script;
     131                 :     };
     132                 :     Vector<StubJoin,16,CompilerAllocPolicy> loopJoins;
     133                 : 
     134                 :     /* Pending loads to patch for stub rejoins. */
     135          171400 :     struct StubJoinPatch {
     136                 :         StubJoin join;
     137                 :         Address address;
     138                 :         AnyRegisterID reg;
     139                 :     };
     140                 :     Vector<StubJoinPatch,16,CompilerAllocPolicy> loopPatches;
     141                 : 
     142                 :     /*
     143                 :      * Pair of a jump/label immediately after each call in the loop, to patch
     144                 :      * with restores of the loop invariant stack values.
     145                 :      */
     146          102269 :     struct RestoreInvariantCall {
     147                 :         Jump jump;
     148                 :         Label label;
     149                 :         bool ool;
     150                 :         bool entry;
     151                 :         unsigned patchIndex;  /* Index into Compiler's callSites. */
     152                 : 
     153                 :         /* Any copies of temporaries on the stack */
     154                 :         Vector<TemporaryCopy> *temporaryCopies;
     155                 :     };
     156                 :     Vector<RestoreInvariantCall> restoreInvariantCalls;
     157                 : 
     158                 :     /*
     159                 :      * Aggregate structure for all loop invariant code and hoisted checks we
     160                 :      * can perform. These are all stored in the same vector as they may depend
     161                 :      * on each other and we need to emit code restoring them in order.
     162                 :      */
     163            4066 :     struct InvariantEntry {
     164                 :         enum EntryKind {
     165                 :             /*
     166                 :              * initializedLength(array) > value1 + value2 + constant.
     167                 :              * Unsigned comparison, so will fail if value + constant < 0
     168                 :              */
     169                 :             DENSE_ARRAY_BOUNDS_CHECK,
     170                 :             TYPED_ARRAY_BOUNDS_CHECK,
     171                 : 
     172                 :             /* value1 + constant >= 0 */
     173                 :             NEGATIVE_CHECK,
     174                 : 
     175                 :             /* constant >= value1 + value2 */
     176                 :             RANGE_CHECK,
     177                 : 
     178                 :             /* For dense arrays */
     179                 :             DENSE_ARRAY_SLOTS,
     180                 :             DENSE_ARRAY_LENGTH,
     181                 : 
     182                 :             /* For typed arrays */
     183                 :             TYPED_ARRAY_SLOTS,
     184                 :             TYPED_ARRAY_LENGTH,
     185                 : 
     186                 :             /* For lazy arguments */
     187                 :             INVARIANT_ARGS_BASE,
     188                 :             INVARIANT_ARGS_LENGTH,
     189                 : 
     190                 :             /* For definite properties */
     191                 :             INVARIANT_PROPERTY
     192                 :         } kind;
     193                 :         union {
     194                 :             struct {
     195                 :                 uint32_t arraySlot;
     196                 :                 uint32_t valueSlot1;
     197                 :                 uint32_t valueSlot2;
     198                 :                 int32_t constant;
     199                 :             } check;
     200                 :             struct {
     201                 :                 uint32_t arraySlot;
     202                 :                 uint32_t temporary;
     203                 :             } array;
     204                 :             struct {
     205                 :                 uint32_t objectSlot;
     206                 :                 uint32_t propertySlot;
     207                 :                 uint32_t temporary;
     208                 :                 jsid id;
     209                 :             } property;
     210                 :         } u;
     211            4434 :         InvariantEntry() { PodZero(this); }
     212           14643 :         bool isBoundsCheck() const {
     213           14643 :             return kind == DENSE_ARRAY_BOUNDS_CHECK || kind == TYPED_ARRAY_BOUNDS_CHECK;
     214                 :         }
     215           13512 :         bool isCheck() const {
     216           13512 :             return isBoundsCheck() || kind == NEGATIVE_CHECK || kind == RANGE_CHECK;
     217                 :         }
     218                 :     };
     219                 :     Vector<InvariantEntry, 4, CompilerAllocPolicy> invariantEntries;
     220                 : 
     221                 :     static inline bool entryRedundant(const InvariantEntry &e0, const InvariantEntry &e1);
     222                 :     bool checkRedundantEntry(const InvariantEntry &entry);
     223                 : 
     224                 :     bool loopInvariantEntry(uint32_t slot);
     225                 :     bool addHoistedCheck(InvariantArrayKind arrayKind, uint32_t arraySlot,
     226                 :                          uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant);
     227                 :     void addNegativeCheck(uint32_t valueSlot, int32_t constant);
     228                 :     void addRangeCheck(uint32_t valueSlot1, uint32_t valueSlot2, int32_t constant);
     229                 :     bool hasTestLinearRelationship(uint32_t slot);
     230                 : 
     231           33590 :     bool hasInvariants() { return !invariantEntries.empty(); }
     232                 :     void restoreInvariants(jsbytecode *pc, Assembler &masm,
     233                 :                            Vector<TemporaryCopy> *temporaryCopies, Vector<Jump> *jumps);
     234                 : 
     235                 :   public:
     236                 : 
     237                 :     /* Outer loop to this one, in case of loop nesting. */
     238                 :     LoopState *outer;
     239                 : 
     240                 :     /* Offset from the outermost frame at which temporaries should be allocated. */
     241                 :     uint32_t temporariesStart;
     242                 : 
     243                 :     LoopState(JSContext *cx, analyze::CrossScriptSSA *ssa,
     244                 :               Compiler *cc, FrameState *frame);
     245                 :     bool init(jsbytecode *head, Jump entry, jsbytecode *entryTarget);
     246                 : 
     247          926309 :     void setOuterPC(jsbytecode *pc)
     248                 :     {
     249          926309 :         if (uint32_t(pc - outerScript->code) == lifetime->entry && lifetime->entry != lifetime->head)
     250           33521 :             reachedEntryPoint = true;
     251          926309 :     }
     252                 : 
     253          715914 :     bool generatingInvariants() { return !skipAnalysis; }
     254                 : 
     255                 :     /* Add a call with trailing jump/label, after which invariants need to be restored. */
     256                 :     void addInvariantCall(Jump jump, Label label, bool ool, bool entry, unsigned patchIndex, Uses uses);
     257                 : 
     258           94076 :     uint32_t headOffset() { return lifetime->head; }
     259          124153 :     uint32_t getLoopRegs() { return loopRegs.freeMask; }
     260                 : 
     261           33590 :     Jump entryJump() { return entry; }
     262           33590 :     uint32_t entryOffset() { return lifetime->entry; }
     263            1286 :     uint32_t backedgeOffset() { return lifetime->backedge; }
     264                 : 
     265                 :     /* Whether the payload of slot is carried around the loop in a register. */
     266             905 :     bool carriesLoopReg(FrameEntry *fe) { return alloc->hasAnyReg(frame.entrySlot(fe)); }
     267                 : 
     268                 :     void setLoopReg(AnyRegisterID reg, FrameEntry *fe);
     269                 : 
     270          865512 :     void clearLoopReg(AnyRegisterID reg)
     271                 :     {
     272                 :         /*
     273                 :          * Mark reg as having been modified since the start of the loop; it
     274                 :          * cannot subsequently be marked to carry a register around the loop.
     275                 :          */
     276          865512 :         JS_ASSERT(loopRegs.hasReg(reg) == alloc->loop(reg));
     277          865512 :         if (loopRegs.hasReg(reg)) {
     278          136418 :             loopRegs.takeReg(reg);
     279          136418 :             alloc->setUnassigned(reg);
     280          136418 :             JaegerSpew(JSpew_Regalloc, "clearing loop register %s\n", reg.name());
     281                 :         }
     282          865512 :     }
     283                 : 
     284                 :     void addJoin(unsigned index, bool script);
     285                 :     void clearLoopRegisters();
     286                 : 
     287                 :     void flushLoop(StubCompiler &stubcc);
     288                 : 
     289                 :     /*
     290                 :      * These should only be used for entries which are known to be dense arrays
     291                 :      * (if they are objects at all).
     292                 :      */
     293                 :     bool hoistArrayLengthCheck(InvariantArrayKind arrayKind,
     294                 :                                const analyze::CrossSSAValue &obj,
     295                 :                                const analyze::CrossSSAValue &index);
     296                 :     FrameEntry *invariantArraySlots(const analyze::CrossSSAValue &obj);
     297                 : 
     298                 :     /* Methods for accesses on lazy arguments. */
     299                 :     bool hoistArgsLengthCheck(const analyze::CrossSSAValue &index);
     300                 :     FrameEntry *invariantArguments();
     301                 : 
     302                 :     FrameEntry *invariantLength(const analyze::CrossSSAValue &obj);
     303                 :     FrameEntry *invariantProperty(const analyze::CrossSSAValue &obj, jsid id);
     304                 : 
     305                 :     /* Whether a binary or inc/dec op's result cannot overflow. */
     306                 :     bool cannotIntegerOverflow(const analyze::CrossSSAValue &pushed);
     307                 : 
     308                 :     /*
     309                 :      * Whether integer overflow in addition or negative zeros in multiplication
     310                 :      * at a binary op can be safely ignored.
     311                 :      */
     312                 :     bool ignoreIntegerOverflow(const analyze::CrossSSAValue &pushed);
     313                 : 
     314                 :   private:
     315                 :     /* Analysis information for the loop. */
     316                 : 
     317                 :     /*
     318                 :      * Any inequality known to hold at the head of the loop. This has the
     319                 :      * form 'lhs <= rhs + constant' or 'lhs >= rhs + constant', depending on
     320                 :      * lessEqual. The lhs may be modified within the loop body (the test is
     321                 :      * invalid afterwards), and the rhs is invariant. This information is only
     322                 :      * valid if the LHS/RHS are known integers.
     323                 :      */
     324                 :     enum { UNASSIGNED = UINT32_MAX };
     325                 :     uint32_t testLHS;
     326                 :     uint32_t testRHS;
     327                 :     int32_t testConstant;
     328                 :     bool testLessEqual;
     329                 : 
     330                 :     /*
     331                 :      * A variable which will be incremented or decremented exactly once in each
     332                 :      * iteration of the loop. The offset of the operation is indicated, which
     333                 :      * may or may not run after the initial entry into the loop.
     334                 :      */
     335            9889 :     struct Increment {
     336                 :         uint32_t slot;
     337                 :         uint32_t offset;
     338                 :     };
     339                 :     Vector<Increment, 4, CompilerAllocPolicy> increments;
     340                 : 
     341                 :     /* It is unknown which arrays grow or which objects are modified in this loop. */
     342                 :     bool unknownModset;
     343                 : 
     344                 :     /*
     345                 :      * Arrays which might grow during this loop. This is a guess, and may
     346                 :      * underapproximate the actual set of such arrays.
     347                 :      */
     348                 :     Vector<types::TypeObject *, 4, CompilerAllocPolicy> growArrays;
     349                 : 
     350                 :     /* Properties which might be modified during this loop. */
     351            4747 :     struct ModifiedProperty {
     352                 :         types::TypeObject *object;
     353                 :         jsid id;
     354                 :     };
     355                 :     Vector<ModifiedProperty, 4, CompilerAllocPolicy> modifiedProperties;
     356                 : 
     357                 :     /*
     358                 :      * Whether this loop only performs integer and double arithmetic and dense
     359                 :      * array accesses. Integer overflows in this loop which only flow to bitops
     360                 :      * can be ignored.
     361                 :      */
     362                 :     bool constrainedLoop;
     363                 : 
     364                 :     void analyzeLoopTest();
     365                 :     void analyzeLoopIncrements();
     366                 :     void analyzeLoopBody(unsigned frame);
     367                 : 
     368                 :     bool definiteArrayAccess(const analyze::SSAValue &obj, const analyze::SSAValue &index);
     369                 :     bool getLoopTestAccess(const analyze::SSAValue &v, uint32_t *pslot, int32_t *pconstant);
     370                 : 
     371                 :     bool addGrowArray(types::TypeObject *object);
     372                 :     bool addModifiedProperty(types::TypeObject *object, jsid id);
     373                 : 
     374                 :     bool hasGrowArray(types::TypeObject *object);
     375                 :     bool hasModifiedProperty(types::TypeObject *object, jsid id);
     376                 : 
     377                 :     uint32_t getIncrement(uint32_t slot);
     378                 :     int32_t adjustConstantForIncrement(jsbytecode *pc, uint32_t slot);
     379                 : 
     380                 :     bool getEntryValue(const analyze::CrossSSAValue &v, uint32_t *pslot, int32_t *pconstant);
     381                 :     bool computeInterval(const analyze::CrossSSAValue &v, int32_t *pmin, int32_t *pmax);
     382                 :     bool valueFlowsToBitops(const analyze::SSAValue &v);
     383                 : };
     384                 : 
     385                 : } /* namespace mjit */
     386                 : } /* namespace js */
     387                 : 
     388                 : #endif /* jsjaeger_loopstate_h__ */

Generated by: LCOV version 1.7