Ignore:
Timestamp:
Jun 13, 2012, 1:53:52 PM (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.

Rolling back in after fixing Windows build issues, and implementing
branchTest8 for the Qt port's strange assemblers.

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.def:
  • 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):
(JSC::AbstractMacroAssembler::readPointer):

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

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

  • assembler/MacroAssemblerARMv7.h:

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

  • assembler/MacroAssemblerMIPS.h:

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

  • assembler/MacroAssemblerSH4.h:

(JSC::MacroAssemblerSH4::branchTest8):
(MacroAssemblerSH4):
(JSC::MacroAssemblerSH4::replaceWithJump):
(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):
(JSC::CodeBlock::printGetByIdCacheStatus):
(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/GetByIdStatus.cpp:

(JSC::GetByIdStatus::computeForChain):

  • 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):

  • interpreter/Interpreter.cpp:

(JSC::Interpreter::privateExecute):

  • 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::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.

Also added the ability ot ASSERT_NOT_REACHED() without having to
propagate NO_RETURN macros, which would be a show-stopper for code
that is conditionally unreachable.

  • wtf/Assertions.h:

(UNREACHABLE_FOR_PLATFORM):

  • 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/dfg/DFGByteCodeParser.cpp

    r120175 r120244  
    12351235    // an inline candidate?
    12361236    CodeBlock* profiledBlock = executable->profiledCodeBlockFor(kind);
     1237    if (!profiledBlock)
     1238        return false;
     1239   
    12371240    if (!mightInlineFunctionFor(profiledBlock, kind))
    12381241        return false;
     
    22522255            SpeculatedType prediction = getPrediction();
    22532256           
     2257            JSGlobalObject* globalObject = m_inlineStackTop->m_codeBlock->globalObject();
     2258
    22542259            NodeIndex getGlobalVar = addToGraph(
    22552260                GetGlobalVar,
    2256                 OpInfo(m_inlineStackTop->m_codeBlock->globalObject()->assertRegisterIsInThisObject(currentInstruction[2].u.registerPointer)),
     2261                OpInfo(globalObject->assertRegisterIsInThisObject(currentInstruction[2].u.registerPointer)),
    22572262                OpInfo(prediction));
    22582263            set(currentInstruction[1].u.operand, getGlobalVar);
    22592264            NEXT_OPCODE(op_get_global_var);
     2265        }
     2266                   
     2267        case op_get_global_var_watchable: {
     2268            SpeculatedType prediction = getPrediction();
     2269           
     2270            JSGlobalObject* globalObject = m_inlineStackTop->m_codeBlock->globalObject();
     2271           
     2272            unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[currentInstruction[3].u.operand];
     2273            Identifier identifier = m_codeBlock->identifier(identifierNumber);
     2274            SymbolTableEntry entry = globalObject->symbolTable().get(identifier.impl());
     2275            if (!entry.couldBeWatched()) {
     2276                NodeIndex getGlobalVar = addToGraph(
     2277                    GetGlobalVar,
     2278                    OpInfo(globalObject->assertRegisterIsInThisObject(currentInstruction[2].u.registerPointer)),
     2279                    OpInfo(prediction));
     2280                set(currentInstruction[1].u.operand, getGlobalVar);
     2281                NEXT_OPCODE(op_get_global_var_watchable);
     2282            }
     2283           
     2284            // The watchpoint is still intact! This means that we will get notified if the
     2285            // current value in the global variable changes. So, we can inline that value.
     2286            // Moreover, currently we can assume that this value is a JSFunction*, which
     2287            // implies that it's a cell. This simplifies things, since in general we'd have
     2288            // to use a JSConstant for non-cells and a WeakJSConstant for cells. So instead
     2289            // of having both cases we just assert that the value is a cell.
     2290           
     2291            // NB. If it wasn't for CSE, GlobalVarWatchpoint would have no need for the
     2292            // register pointer. But CSE tracks effects on global variables by comparing
     2293            // register pointers. Because CSE executes multiple times while the backend
     2294            // executes once, we use the following performance trade-off:
     2295            // - The node refers directly to the register pointer to make CSE super cheap.
     2296            // - To perform backend code generation, the node only contains the identifier
     2297            //   number, from which it is possible to get (via a few average-time O(1)
     2298            //   lookups) to the WatchpointSet.
     2299           
     2300            addToGraph(
     2301                GlobalVarWatchpoint,
     2302                OpInfo(globalObject->assertRegisterIsInThisObject(currentInstruction[2].u.registerPointer)),
     2303                OpInfo(identifierNumber));
     2304           
     2305            JSValue specificValue = globalObject->registerAt(entry.getIndex()).get();
     2306            ASSERT(specificValue.isCell());
     2307            set(currentInstruction[1].u.operand,
     2308                addToGraph(WeakJSConstant, OpInfo(specificValue.asCell())));
     2309           
     2310            NEXT_OPCODE(op_get_global_var_watchable);
    22602311        }
    22612312
     
    22672318                value);
    22682319            NEXT_OPCODE(op_put_global_var);
     2320        }
     2321
     2322        case op_put_global_var_check: {
     2323            NodeIndex value = get(currentInstruction[2].u.operand);
     2324            CodeBlock* codeBlock = m_inlineStackTop->m_codeBlock;
     2325            JSGlobalObject* globalObject = codeBlock->globalObject();
     2326            unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[currentInstruction[4].u.operand];
     2327            Identifier identifier = m_codeBlock->identifier(identifierNumber);
     2328            SymbolTableEntry entry = globalObject->symbolTable().get(identifier.impl());
     2329            if (!entry.couldBeWatched()) {
     2330                addToGraph(
     2331                    PutGlobalVar,
     2332                    OpInfo(globalObject->assertRegisterIsInThisObject(currentInstruction[1].u.registerPointer)),
     2333                    value);
     2334                NEXT_OPCODE(op_put_global_var_check);
     2335            }
     2336            addToGraph(
     2337                PutGlobalVarCheck,
     2338                OpInfo(codeBlock->globalObject()->assertRegisterIsInThisObject(currentInstruction[1].u.registerPointer)),
     2339                OpInfo(identifierNumber),
     2340                value);
     2341            NEXT_OPCODE(op_put_global_var_check);
    22692342        }
    22702343
Note: See TracChangeset for help on using the changeset viewer.