Ignore:
Timestamp:
Jun 13, 2012, 1:20:39 AM (13 years ago)
Author:
[email protected]
Message:

DFG should be able to set watchpoints on global variables
https://bugs.webkit.org/show_bug.cgi?id=88692

Source/JavaScriptCore:

Reviewed by Geoffrey Garen.

This implements global variable constant folding by allowing the optimizing
compiler to set a "watchpoint" on globals that it wishes to constant fold.
If the watchpoint fires, then an OSR exit is forced by overwriting the
machine code that the optimizing compiler generated with a jump.

As such, this patch is adding quite a bit of stuff:

  • Jump replacement on those hardware targets supported by the optimizing JIT. It is now possible to patch in a jump instruction over any recorded watchpoint label. The jump must be "local" in the sense that it must be within the range of the largest jump distance supported by a one instruction jump.


  • WatchpointSets and Watchpoints. A Watchpoint is a doubly-linked list node that records the location where a jump must be inserted and the destination to which it should jump. Watchpoints can be added to a WatchpointSet. The WatchpointSet can be fired all at once, which plants all jumps. WatchpointSet also remembers if it had ever been invalidated, which allows for monotonicity: we typically don't want to optimize using watchpoints on something for which watchpoints had previously fired. The act of notifying a WatchpointSet has a trivial fast path in case no Watchpoints are registered (one-byte load+branch).


  • SpeculativeJIT::speculationWatchpoint(). It's like speculationCheck(), except that you don't have to emit branches. But, you need to know what WatchpointSet to add the resulting Watchpoint to. Not everything that you could write a speculationCheck() for will have a WatchpointSet that would get notified if the condition you were speculating against became invalid.


  • SymbolTableEntry now has the ability to refer to a WatchpointSet. It can do so without incurring any space overhead for those entries that don't have WatchpointSets.


  • The bytecode generator infers all global function variables to be watchable, and makes all stores perform the WatchpointSet's write check, and marks all loads as being potentially watchable (i.e. you can compile them to a watchpoint and a constant).


Put together, this allows for fully sleazy inlining of calls to globally
declared functions. The inline prologue will no longer contain the load of
the function, or any checks of the function you're calling. I.e. it's
pretty much like the kind of inlining you would see in Java or C++.
Furthermore, the watchpointing functionality is built to be fairly general,
and should allow setting watchpoints on all sorts of interesting things
in the future.

The sleazy inlining means that we will now sometimes inline in code paths
that have never executed. Previously, to inline we would have either had
to have executed the call (to read the call's inline cache) or have
executed the method check (to read the method check's inline cache). Now,
we might inline when the callee is a watched global variable. This
revealed some humorous bugs. First, constant folding disagreed with CFA
over what kinds of operations can clobber (example: code path A is dead
but stores a String into variable X, all other code paths store 0 into
X, and then you do CompareEq(X, 0) - CFA will say that this is a non-
clobbering constant, but constant folding thought it was clobbering
because it saw the String prediction). Second, inlining would crash if
the inline callee had not been compiled. This patch fixes both bugs,
since otherwise run-javascriptcore-tests would report regressions.

  • CMakeLists.txt:
  • GNUmakefile.list.am:
  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • Target.pri:
  • assembler/ARMv7Assembler.h:

(ARMv7Assembler):
(JSC::ARMv7Assembler::ARMv7Assembler):
(JSC::ARMv7Assembler::labelForWatchpoint):
(JSC::ARMv7Assembler::label):
(JSC::ARMv7Assembler::replaceWithJump):
(JSC::ARMv7Assembler::maxJumpReplacementSize):

  • assembler/AbstractMacroAssembler.h:

(JSC):
(AbstractMacroAssembler):
(Label):
(JSC::AbstractMacroAssembler::watchpointLabel):

  • assembler/AssemblerBuffer.h:
  • assembler/MacroAssemblerARM.h:

(JSC::MacroAssemblerARM::replaceWithJump):
(MacroAssemblerARM):
(JSC::MacroAssemblerARM::maxJumpReplacementSize):

  • assembler/MacroAssemblerARMv7.h:

(MacroAssemblerARMv7):
(JSC::MacroAssemblerARMv7::replaceWithJump):
(JSC::MacroAssemblerARMv7::maxJumpReplacementSize):
(JSC::MacroAssemblerARMv7::branchTest8):
(JSC::MacroAssemblerARMv7::jump):
(JSC::MacroAssemblerARMv7::makeBranch):

  • assembler/MacroAssemblerMIPS.h:

(JSC::MacroAssemblerMIPS::replaceWithJump):
(MacroAssemblerMIPS):
(JSC::MacroAssemblerMIPS::maxJumpReplacementSize):

  • assembler/MacroAssemblerSH4.h:

(JSC::MacroAssemblerSH4::replaceWithJump):
(MacroAssemblerSH4):
(JSC::MacroAssemblerSH4::maxJumpReplacementSize):

  • assembler/MacroAssemblerX86.h:

(MacroAssemblerX86):
(JSC::MacroAssemblerX86::branchTest8):

  • assembler/MacroAssemblerX86Common.h:

(JSC::MacroAssemblerX86Common::replaceWithJump):
(MacroAssemblerX86Common):
(JSC::MacroAssemblerX86Common::maxJumpReplacementSize):

  • assembler/MacroAssemblerX86_64.h:

(MacroAssemblerX86_64):
(JSC::MacroAssemblerX86_64::branchTest8):

  • assembler/X86Assembler.h:

(JSC::X86Assembler::X86Assembler):
(X86Assembler):
(JSC::X86Assembler::cmpb_im):
(JSC::X86Assembler::testb_im):
(JSC::X86Assembler::labelForWatchpoint):
(JSC::X86Assembler::label):
(JSC::X86Assembler::replaceWithJump):
(JSC::X86Assembler::maxJumpReplacementSize):
(JSC::X86Assembler::X86InstructionFormatter::memoryModRM):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::dump):

  • bytecode/CodeBlock.h:

(JSC::CodeBlock::appendOSRExit):
(JSC::CodeBlock::appendSpeculationRecovery):
(CodeBlock):
(JSC::CodeBlock::appendWatchpoint):
(JSC::CodeBlock::numberOfWatchpoints):
(JSC::CodeBlock::watchpoint):
(DFGData):

  • bytecode/DFGExitProfile.h:

(JSC::DFG::exitKindToString):
(JSC::DFG::exitKindIsCountable):

  • bytecode/Instruction.h:

(Instruction):
(JSC::Instruction::Instruction):

  • bytecode/Opcode.h:

(JSC):
(JSC::padOpcodeName):

  • bytecode/Watchpoint.cpp: Added.

(JSC):
(JSC::Watchpoint::~Watchpoint):
(JSC::Watchpoint::correctLabels):
(JSC::Watchpoint::fire):
(JSC::WatchpointSet::WatchpointSet):
(JSC::WatchpointSet::~WatchpointSet):
(JSC::WatchpointSet::add):
(JSC::WatchpointSet::notifyWriteSlow):
(JSC::WatchpointSet::fireAllWatchpoints):

  • bytecode/Watchpoint.h: Added.

(JSC):
(Watchpoint):
(JSC::Watchpoint::Watchpoint):
(JSC::Watchpoint::setDestination):
(WatchpointSet):
(JSC::WatchpointSet::isStillValid):
(JSC::WatchpointSet::hasBeenInvalidated):
(JSC::WatchpointSet::startWatching):
(JSC::WatchpointSet::notifyWrite):
(JSC::WatchpointSet::addressOfIsWatched):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::ResolveResult::checkValidity):
(JSC::BytecodeGenerator::addGlobalVar):
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::resolve):
(JSC::BytecodeGenerator::emitResolve):
(JSC::BytecodeGenerator::emitResolveWithBase):
(JSC::BytecodeGenerator::emitResolveWithThis):
(JSC::BytecodeGenerator::emitGetStaticVar):
(JSC::BytecodeGenerator::emitPutStaticVar):

  • bytecompiler/BytecodeGenerator.h:

(BytecodeGenerator):

  • bytecompiler/NodesCodegen.cpp:

(JSC::FunctionCallResolveNode::emitBytecode):
(JSC::PostfixResolveNode::emitBytecode):
(JSC::PrefixResolveNode::emitBytecode):
(JSC::ReadModifyResolveNode::emitBytecode):
(JSC::AssignResolveNode::emitBytecode):
(JSC::ConstDeclNode::emitCodeSingle):

  • dfg/DFGAbstractState.cpp:

(JSC::DFG::AbstractState::execute):
(JSC::DFG::AbstractState::clobberStructures):

  • dfg/DFGAbstractState.h:

(AbstractState):
(JSC::DFG::AbstractState::didClobber):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::handleInlining):
(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGCCallHelpers.h:

(CCallHelpers):
(JSC::DFG::CCallHelpers::setupArguments):

  • dfg/DFGCSEPhase.cpp:

(JSC::DFG::CSEPhase::globalVarWatchpointElimination):
(CSEPhase):
(JSC::DFG::CSEPhase::globalVarStoreElimination):
(JSC::DFG::CSEPhase::performNodeCSE):

  • dfg/DFGCapabilities.h:

(JSC::DFG::canCompileOpcode):

  • dfg/DFGConstantFoldingPhase.cpp:

(JSC::DFG::ConstantFoldingPhase::run):

  • dfg/DFGCorrectableJumpPoint.h:

(JSC::DFG::CorrectableJumpPoint::isSet):
(CorrectableJumpPoint):

  • dfg/DFGJITCompiler.cpp:

(JSC::DFG::JITCompiler::linkOSRExits):
(JSC::DFG::JITCompiler::link):

  • dfg/DFGNode.h:

(JSC::DFG::Node::hasIdentifierNumberForCheck):
(Node):
(JSC::DFG::Node::identifierNumberForCheck):
(JSC::DFG::Node::hasRegisterPointer):

  • dfg/DFGNodeType.h:

(DFG):

  • dfg/DFGOSRExit.cpp:

(JSC::DFG::OSRExit::OSRExit):

  • dfg/DFGOSRExit.h:

(OSRExit):

  • dfg/DFGOperations.cpp:
  • dfg/DFGOperations.h:
  • dfg/DFGPredictionPropagationPhase.cpp:

(JSC::DFG::PredictionPropagationPhase::propagate):

  • dfg/DFGSpeculativeJIT.h:

(JSC::DFG::SpeculativeJIT::callOperation):
(JSC::DFG::SpeculativeJIT::appendCall):
(SpeculativeJIT):
(JSC::DFG::SpeculativeJIT::speculationWatchpoint):

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):

  • jit/JIT.h:
  • jit/JITPropertyAccess.cpp:

(JSC::JIT::emit_op_put_global_var_check):
(JSC):
(JSC::JIT::emitSlow_op_put_global_var_check):

  • jit/JITPropertyAccess32_64.cpp:

(JSC::JIT::emit_op_put_global_var_check):
(JSC):
(JSC::JIT::emitSlow_op_put_global_var_check):

  • jit/JITStubs.cpp:

(JSC::JITThunks::JITThunks):
(JSC::DEFINE_STUB_FUNCTION):
(JSC):

  • jit/JITStubs.h:
  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::LLINT_SLOW_PATH_DECL):
(LLInt):

  • llint/LLIntSlowPaths.h:

(LLInt):

  • llint/LowLevelInterpreter32_64.asm:
  • llint/LowLevelInterpreter64.asm:
  • runtime/JSObject.cpp:

(JSC::JSObject::removeDirect):

  • runtime/JSObject.h:

(JSObject):

  • runtime/JSSymbolTableObject.h:

(JSC::symbolTableGet):
(JSC::symbolTablePut):
(JSC::symbolTablePutWithAttributes):

  • runtime/SymbolTable.cpp: Added.

(JSC):
(JSC::SymbolTableEntry::copySlow):
(JSC::SymbolTableEntry::freeFatEntrySlow):
(JSC::SymbolTableEntry::couldBeWatched):
(JSC::SymbolTableEntry::attemptToWatch):
(JSC::SymbolTableEntry::addressOfIsWatched):
(JSC::SymbolTableEntry::addWatchpoint):
(JSC::SymbolTableEntry::notifyWriteSlow):
(JSC::SymbolTableEntry::inflateSlow):

  • runtime/SymbolTable.h:

(JSC):
(SymbolTableEntry):
(Fast):
(JSC::SymbolTableEntry::Fast::Fast):
(JSC::SymbolTableEntry::Fast::isNull):
(JSC::SymbolTableEntry::Fast::getIndex):
(JSC::SymbolTableEntry::Fast::isReadOnly):
(JSC::SymbolTableEntry::Fast::getAttributes):
(JSC::SymbolTableEntry::Fast::isFat):
(JSC::SymbolTableEntry::SymbolTableEntry):
(JSC::SymbolTableEntry::~SymbolTableEntry):
(JSC::SymbolTableEntry::operator=):
(JSC::SymbolTableEntry::isNull):
(JSC::SymbolTableEntry::getIndex):
(JSC::SymbolTableEntry::getFast):
(JSC::SymbolTableEntry::getAttributes):
(JSC::SymbolTableEntry::isReadOnly):
(JSC::SymbolTableEntry::watchpointSet):
(JSC::SymbolTableEntry::notifyWrite):
(FatEntry):
(JSC::SymbolTableEntry::FatEntry::FatEntry):
(JSC::SymbolTableEntry::isFat):
(JSC::SymbolTableEntry::fatEntry):
(JSC::SymbolTableEntry::inflate):
(JSC::SymbolTableEntry::bits):
(JSC::SymbolTableEntry::freeFatEntry):
(JSC::SymbolTableEntry::pack):
(JSC::SymbolTableEntry::isValidIndex):

Source/WTF:

Reviewed by Geoffrey Garen.

Added ability to set the inline capacity of segmented vectors.

  • wtf/SegmentedVector.h:

(WTF):
(SegmentedVectorIterator):
(WTF::SegmentedVectorIterator::operator=):
(WTF::SegmentedVectorIterator::SegmentedVectorIterator):
(SegmentedVector):

LayoutTests:

Rubber stamped by Geoffrey Garen.

Added a test for watchpoints. Also updated the jsc-test-list to include the latest
tests.

  • fast/js/dfg-call-function-hit-watchpoint-expected.txt: Added.
  • fast/js/dfg-call-function-hit-watchpoint.html: Added.
  • fast/js/jsc-test-list:
  • fast/js/script-tests/dfg-call-function-hit-watchpoint.js: Added.

(foo):
(bar):
(.foo):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/SymbolTable.h

    r109268 r120172  
    11/*
    2  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
     2 * Copyright (C) 2007, 2008, 2012 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3232#include "JSObject.h"
    3333#include "UString.h"
     34#include "Watchpoint.h"
    3435#include <wtf/AlwaysInline.h>
    3536#include <wtf/HashTraits.h>
    3637
    3738namespace JSC {
     39
     40    class Watchpoint;
     41    class WatchpointSet;
    3842
    3943    static ALWAYS_INLINE int missingSymbolMarker() { return std::numeric_limits<int>::max(); }
     
    4347    // four bits all set or all unset.
    4448
     49    // In addition to implementing semantics-mandated variable attributes and
     50    // implementation-mandated variable indexing, this class also implements
     51    // watchpoints to be used for JIT optimizations. Because watchpoints are
     52    // meant to be relatively rare, this class optimizes heavily for the case
     53    // that they are not being used. To that end, this class uses the thin-fat
     54    // idiom: either it is thin, in which case it contains an in-place encoded
     55    // word that consists of attributes, the index, and a bit saying that it is
     56    // thin; or it is fat, in which case it contains a pointer to a malloc'd
     57    // data structure and a bit saying that it is fat. The malloc'd data
     58    // structure will be malloced a second time upon copy, to preserve the
     59    // property that in-place edits to SymbolTableEntry do not manifest in any
     60    // copies. However, the malloc'd FatEntry data structure contains a ref-
     61    // counted pointer to a shared WatchpointSet. Thus, in-place edits of the
     62    // WatchpointSet will manifest in all copies. Here's a picture:
     63    //
     64    // SymbolTableEntry --> FatEntry --> WatchpointSet
     65    //
     66    // If you make a copy of a SymbolTableEntry, you will have:
     67    //
     68    // original: SymbolTableEntry --> FatEntry --> WatchpointSet
     69    // copy:     SymbolTableEntry --> FatEntry -----^
     70
    4571    struct SymbolTableEntry {
     72        // Use the SymbolTableEntry::Fast class, either via implicit cast or by calling
     73        // getFast(), when you (1) only care about isNull(), getIndex(), and isReadOnly(),
     74        // and (2) you are in a hot path where you need to minimize the number of times
     75        // that you branch on isFat() when getting the bits().
     76        class Fast {
     77        public:
     78            Fast()
     79                : m_bits(0)
     80            {
     81            }
     82           
     83            ALWAYS_INLINE Fast(const SymbolTableEntry& entry)
     84                : m_bits(entry.bits())
     85            {
     86            }
     87       
     88            bool isNull() const
     89            {
     90                return !m_bits;
     91            }
     92
     93            int getIndex() const
     94            {
     95                return static_cast<int>(m_bits >> FlagBits);
     96            }
     97       
     98            bool isReadOnly() const
     99            {
     100                return m_bits & ReadOnlyFlag;
     101            }
     102           
     103            unsigned getAttributes() const
     104            {
     105                unsigned attributes = 0;
     106                if (m_bits & ReadOnlyFlag)
     107                    attributes |= ReadOnly;
     108                if (m_bits & DontEnumFlag)
     109                    attributes |= DontEnum;
     110                return attributes;
     111            }
     112
     113            bool isFat() const
     114            {
     115                return m_bits & FatFlag;
     116            }
     117           
     118        private:
     119            friend struct SymbolTableEntry;
     120            intptr_t m_bits;
     121        };
     122
    46123        SymbolTableEntry()
    47124            : m_bits(0)
     
    50127
    51128        SymbolTableEntry(int index)
     129            : m_bits(0)
    52130        {
    53131            ASSERT(isValidIndex(index));
     
    56134
    57135        SymbolTableEntry(int index, unsigned attributes)
     136            : m_bits(0)
    58137        {
    59138            ASSERT(isValidIndex(index));
     
    61140        }
    62141       
     142        ~SymbolTableEntry()
     143        {
     144            freeFatEntry();
     145        }
     146       
     147        SymbolTableEntry(const SymbolTableEntry& other)
     148            : m_bits(0)
     149        {
     150            *this = other;
     151        }
     152       
     153        SymbolTableEntry& operator=(const SymbolTableEntry& other)
     154        {
     155            if (UNLIKELY(other.isFat()))
     156                return copySlow(other);
     157            freeFatEntry();
     158            m_bits = other.m_bits;
     159            return *this;
     160        }
     161       
    63162        bool isNull() const
    64163        {
    65             return !m_bits;
     164            return !bits();
    66165        }
    67166
    68167        int getIndex() const
    69168        {
    70             return m_bits >> FlagBits;
    71         }
    72 
     169            return static_cast<int>(bits() >> FlagBits);
     170        }
     171       
     172        ALWAYS_INLINE Fast getFast() const
     173        {
     174            return Fast(*this);
     175        }
     176       
     177        ALWAYS_INLINE Fast getFast(bool& wasFat) const
     178        {
     179            Fast result;
     180            wasFat = isFat();
     181            if (wasFat)
     182                result.m_bits = fatEntry()->m_bits;
     183            else
     184                result.m_bits = m_bits;
     185            return result;
     186        }
     187       
    73188        unsigned getAttributes() const
    74189        {
    75             unsigned attributes = 0;
    76             if (m_bits & ReadOnlyFlag)
    77                 attributes |= ReadOnly;
    78             if (m_bits & DontEnumFlag)
    79                 attributes |= DontEnum;
    80             return attributes;
     190            return getFast().getAttributes();
    81191        }
    82192
     
    88198        bool isReadOnly() const
    89199        {
    90             return m_bits & ReadOnlyFlag;
    91         }
    92 
     200            return bits() & ReadOnlyFlag;
     201        }
     202       
     203        bool couldBeWatched();
     204       
     205        // Notify an opportunity to create a watchpoint for a variable. This is
     206        // idempotent and fail-silent. It is idempotent in the sense that if
     207        // a watchpoint set had already been created, then another one will not
     208        // be created. Hence two calls to this method have the same effect as
     209        // one call. It is also fail-silent, in the sense that if a watchpoint
     210        // set had been created and had already been invalidated, then this will
     211        // just return. This means that couldBeWatched() may return false even
     212        // immediately after a call to attemptToWatch().
     213        void attemptToWatch();
     214       
     215        bool* addressOfIsWatched();
     216       
     217        void addWatchpoint(Watchpoint*);
     218       
     219        WatchpointSet* watchpointSet()
     220        {
     221            return fatEntry()->m_watchpoints.get();
     222        }
     223       
     224        ALWAYS_INLINE void notifyWrite()
     225        {
     226            if (LIKELY(!isFat()))
     227                return;
     228            notifyWriteSlow();
     229        }
     230       
    93231    private:
    94         static const unsigned ReadOnlyFlag = 0x1;
    95         static const unsigned DontEnumFlag = 0x2;
    96         static const unsigned NotNullFlag = 0x4;
    97         static const unsigned FlagBits = 3;
     232        static const intptr_t FatFlag = 0x1;
     233        static const intptr_t ReadOnlyFlag = 0x2;
     234        static const intptr_t DontEnumFlag = 0x4;
     235        static const intptr_t NotNullFlag = 0x8;
     236        static const intptr_t FlagBits = 4;
     237       
     238        class FatEntry {
     239            WTF_MAKE_FAST_ALLOCATED;
     240        public:
     241            FatEntry(intptr_t bits)
     242                : m_bits(bits | FatFlag)
     243            {
     244            }
     245           
     246            intptr_t m_bits; // always has FatFlag set and exactly matches what the bits would have been if this wasn't fat.
     247           
     248            RefPtr<WatchpointSet> m_watchpoints;
     249        };
     250       
     251        SymbolTableEntry& copySlow(const SymbolTableEntry&);
     252        JS_EXPORT_PRIVATE void notifyWriteSlow();
     253       
     254        bool isFat() const
     255        {
     256            return m_bits & FatFlag;
     257        }
     258       
     259        const FatEntry* fatEntry() const
     260        {
     261            ASSERT(isFat());
     262            return bitwise_cast<const FatEntry*>(m_bits & ~FatFlag);
     263        }
     264       
     265        FatEntry* fatEntry()
     266        {
     267            ASSERT(isFat());
     268            return bitwise_cast<FatEntry*>(m_bits & ~FatFlag);
     269        }
     270       
     271        FatEntry* inflate()
     272        {
     273            if (LIKELY(isFat()))
     274                return fatEntry();
     275            return inflateSlow();
     276        }
     277       
     278        FatEntry* inflateSlow();
     279       
     280        ALWAYS_INLINE intptr_t bits() const
     281        {
     282            if (isFat())
     283                return fatEntry()->m_bits;
     284            return m_bits;
     285        }
     286       
     287        ALWAYS_INLINE intptr_t& bits()
     288        {
     289            if (isFat())
     290                return fatEntry()->m_bits;
     291            return m_bits;
     292        }
     293       
     294        void freeFatEntry()
     295        {
     296            if (LIKELY(!isFat()))
     297                return;
     298            freeFatEntrySlow();
     299        }
     300       
     301        void freeFatEntrySlow();
    98302
    99303        void pack(int index, bool readOnly, bool dontEnum)
    100304        {
    101             m_bits = (index << FlagBits) | NotNullFlag;
     305            intptr_t& bitsRef = bits();
     306            bitsRef = (static_cast<intptr_t>(index) << FlagBits) | NotNullFlag;
    102307            if (readOnly)
    103                 m_bits |= ReadOnlyFlag;
     308                bitsRef |= ReadOnlyFlag;
    104309            if (dontEnum)
    105                 m_bits |= DontEnumFlag;
     310                bitsRef |= DontEnumFlag;
    106311        }
    107312       
    108313        bool isValidIndex(int index)
    109314        {
    110             return ((index << FlagBits) >> FlagBits) == index;
    111         }
    112 
    113         int m_bits;
     315            return ((static_cast<intptr_t>(index) << FlagBits) >> FlagBits) == static_cast<intptr_t>(index);
     316        }
     317
     318        intptr_t m_bits;
    114319    };
    115320
Note: See TracChangeset for help on using the changeset viewer.