Ignore:
Timestamp:
Jan 19, 2015, 8:47:55 PM (10 years ago)
Author:
[email protected]
Message:

Basic block start offsets should never be larger than end offsets in the control flow profiler
https://bugs.webkit.org/show_bug.cgi?id=140377

Reviewed by Filip Pizlo.

The bytecode generator will emit code more than once for some AST nodes. For instance,
the finally block of TryNode will emit two code paths for its finally block: one for
the normal path, and another for the path where an exception is thrown in the catch block.

This repeated code emission of the same AST node previously broke how the control
flow profiler computed text ranges of basic blocks because when the same AST node
is emitted multiple times, there is a good chance that there are ranges that span
from the end offset of one of these duplicated nodes back to the start offset of
the same duplicated node. This caused a basic block range to report a larger start
offset than end offset. This was incorrect. Now, when this situation is encountered
while linking a CodeBlock, the faulty range in question is ignored.

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::CodeBlock):
(JSC::CodeBlock::insertBasicBlockBoundariesForControlFlowProfiler):

  • bytecode/CodeBlock.h:
  • bytecompiler/NodesCodegen.cpp:

(JSC::ForInNode::emitMultiLoopBytecode):
(JSC::ForOfNode::emitBytecode):
(JSC::TryNode::emitBytecode):

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseConditionalExpression):

  • runtime/ControlFlowProfiler.cpp:

(JSC::ControlFlowProfiler::ControlFlowProfiler):

  • runtime/ControlFlowProfiler.h:

(JSC::ControlFlowProfiler::dummyBasicBlock):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp

    r178106 r178692  
    21372137    }
    21382138
    2139     if (vm()->controlFlowProfiler()) {
    2140         const Vector<size_t>& bytecodeOffsets = unlinkedCodeBlock->opProfileControlFlowBytecodeOffsets();
    2141         for (size_t i = 0, offsetsLength = bytecodeOffsets.size(); i < offsetsLength; i++) {
    2142             // Because op_profile_control_flow is emitted at the beginning of every basic block, finding
    2143             // the next op_profile_control_flow will give us the text range of a single basic block.
    2144             size_t startIdx = bytecodeOffsets[i];
    2145             RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[startIdx].u.opcode) == op_profile_control_flow);
    2146             int basicBlockStartOffset = instructions[startIdx + 1].u.operand;
    2147             int basicBlockEndOffset;
    2148             if (i + 1 < offsetsLength) {
    2149                 size_t endIdx = bytecodeOffsets[i + 1];
    2150                 RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[endIdx].u.opcode) == op_profile_control_flow);
    2151                 basicBlockEndOffset = instructions[endIdx + 1].u.operand;
    2152             } else
    2153                 basicBlockEndOffset = m_sourceOffset + m_ownerExecutable->source().length() - 1;
    2154 
    2155             BasicBlockLocation* basicBlockLocation = vm()->controlFlowProfiler()->getBasicBlockLocation(m_ownerExecutable->sourceID(), basicBlockStartOffset, basicBlockEndOffset);
    2156 
    2157             // Find all functions that are enclosed within the range: [basicBlockStartOffset, basicBlockEndOffset]
    2158             // and insert these functions' start/end offsets as gaps in the current BasicBlockLocation.
    2159             // This is necessary because in the original source text of a JavaScript program,
    2160             // function literals form new basic blocks boundaries, but they aren't represented
    2161             // inside the CodeBlock's instruction stream.
    2162             auto insertFunctionGaps = [basicBlockLocation, basicBlockStartOffset, basicBlockEndOffset] (const WriteBarrier<FunctionExecutable>& functionExecutable) {
    2163                 const UnlinkedFunctionExecutable* executable = functionExecutable->unlinkedExecutable();
    2164                 int functionStart = executable->typeProfilingStartOffset();
    2165                 int functionEnd = executable->typeProfilingEndOffset();
    2166                 if (functionStart >= basicBlockStartOffset && functionEnd <= basicBlockEndOffset)
    2167                     basicBlockLocation->insertGap(functionStart, functionEnd);
    2168             };
    2169 
    2170             for (const WriteBarrier<FunctionExecutable>& executable : m_functionDecls)
    2171                 insertFunctionGaps(executable);
    2172             for (const WriteBarrier<FunctionExecutable>& executable : m_functionExprs)
    2173                 insertFunctionGaps(executable);
    2174 
    2175             instructions[startIdx + 1].u.basicBlockLocation = basicBlockLocation;
    2176         }
    2177     }
     2139    if (vm()->controlFlowProfiler())
     2140        insertBasicBlockBoundariesForControlFlowProfiler(instructions);
    21782141
    21792142    m_instructions = WTF::RefCountedArray<Instruction>(instructions);
     
    40674030#endif
    40684031
     4032void CodeBlock::insertBasicBlockBoundariesForControlFlowProfiler(Vector<Instruction, 0, UnsafeVectorOverflow>& instructions)
     4033{
     4034    const Vector<size_t>& bytecodeOffsets = unlinkedCodeBlock()->opProfileControlFlowBytecodeOffsets();
     4035    for (size_t i = 0, offsetsLength = bytecodeOffsets.size(); i < offsetsLength; i++) {
     4036        // Because op_profile_control_flow is emitted at the beginning of every basic block, finding
     4037        // the next op_profile_control_flow will give us the text range of a single basic block.
     4038        size_t startIdx = bytecodeOffsets[i];
     4039        RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[startIdx].u.opcode) == op_profile_control_flow);
     4040        int basicBlockStartOffset = instructions[startIdx + 1].u.operand;
     4041        int basicBlockEndOffset;
     4042        if (i + 1 < offsetsLength) {
     4043            size_t endIdx = bytecodeOffsets[i + 1];
     4044            RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[endIdx].u.opcode) == op_profile_control_flow);
     4045            basicBlockEndOffset = instructions[endIdx + 1].u.operand;
     4046        } else {
     4047            basicBlockEndOffset = m_sourceOffset + m_ownerExecutable->source().length() - 1; // Offset before the closing brace.
     4048            basicBlockStartOffset = std::min(basicBlockStartOffset, basicBlockEndOffset); // Some start offsets may be at the closing brace, ensure it is the offset before.
     4049        }
     4050
     4051        // The following check allows for the same textual JavaScript basic block to have its bytecode emitted more
     4052        // than once and still play nice with the control flow profiler. When basicBlockStartOffset is larger than
     4053        // basicBlockEndOffset, it indicates that the bytecode generator has emitted code for the same AST node
     4054        // more than once (for example: ForInNode, Finally blocks in TryNode, etc). Though these are different
     4055        // basic blocks at the bytecode level, they are generated from the same textual basic block in the JavaScript
     4056        // program. The condition:
     4057        // (basicBlockEndOffset < basicBlockStartOffset)
     4058        // is encountered when op_profile_control_flow lies across the boundary of these duplicated bytecode basic
     4059        // blocks and the textual offset goes from the end of the duplicated block back to the beginning. These
     4060        // ranges are dummy ranges and are ignored. The duplicated bytecode basic blocks point to the same
     4061        // internal data structure, so if any of them execute, it will record the same textual basic block in the
     4062        // JavaScript program as executing.
     4063        // At the bytecode level, this situation looks like:
     4064        // j: op_profile_control_flow (from j->k, we have basicBlockEndOffset < basicBlockStartOffset)
     4065        // ...
     4066        // k: op_profile_control_flow (we want to skip over the j->k block and start fresh at offset k as the start of a new basic block k->m).
     4067        // ...
     4068        // m: op_profile_control_flow
     4069        if (basicBlockEndOffset < basicBlockStartOffset) {
     4070            RELEASE_ASSERT(i + 1 < offsetsLength); // We should never encounter dummy blocks at the end of a CodeBlock.
     4071            instructions[startIdx + 1].u.basicBlockLocation = vm()->controlFlowProfiler()->dummyBasicBlock();
     4072            continue;
     4073        }
     4074
     4075        BasicBlockLocation* basicBlockLocation = vm()->controlFlowProfiler()->getBasicBlockLocation(m_ownerExecutable->sourceID(), basicBlockStartOffset, basicBlockEndOffset);
     4076
     4077        // Find all functions that are enclosed within the range: [basicBlockStartOffset, basicBlockEndOffset]
     4078        // and insert these functions' start/end offsets as gaps in the current BasicBlockLocation.
     4079        // This is necessary because in the original source text of a JavaScript program,
     4080        // function literals form new basic blocks boundaries, but they aren't represented
     4081        // inside the CodeBlock's instruction stream.
     4082        auto insertFunctionGaps = [basicBlockLocation, basicBlockStartOffset, basicBlockEndOffset] (const WriteBarrier<FunctionExecutable>& functionExecutable) {
     4083            const UnlinkedFunctionExecutable* executable = functionExecutable->unlinkedExecutable();
     4084            int functionStart = executable->typeProfilingStartOffset();
     4085            int functionEnd = executable->typeProfilingEndOffset();
     4086            if (functionStart >= basicBlockStartOffset && functionEnd <= basicBlockEndOffset)
     4087                basicBlockLocation->insertGap(functionStart, functionEnd);
     4088        };
     4089
     4090        for (const WriteBarrier<FunctionExecutable>& executable : m_functionDecls)
     4091            insertFunctionGaps(executable);
     4092        for (const WriteBarrier<FunctionExecutable>& executable : m_functionExprs)
     4093            insertFunctionGaps(executable);
     4094
     4095        instructions[startIdx + 1].u.basicBlockLocation = basicBlockLocation;
     4096    }
     4097}
     4098
    40694099} // namespace JSC
Note: See TracChangeset for help on using the changeset viewer.