Title: [180518] trunk/Source/_javascript_Core
Revision
180518
Author
[email protected]
Date
2015-02-23 14:10:51 -0800 (Mon, 23 Feb 2015)

Log Message

Adjust the ranges of basic block statements in JSC's control flow profiler to be mutually exclusive
https://bugs.webkit.org/show_bug.cgi?id=141095

Reviewed by Mark Lam.

Suppose the control flow of a program forms basic block A with successor block
B. A's end offset will be the *same* as B's start offset in the current architecture
of the control flow profiler. This makes reasoning about the text offsets of
the control flow profiler unsound. To make reasoning about offsets sound, all
basic block ranges should be mutually exclusive.  All calls to emitProfileControlFlow
now pass in the *start* of a basic block as the text offset argument. This simplifies
all calls to emitProfileControlFlow because the previous implementation had a
lot of edge cases for getting the desired basic block text boundaries.

This patch also ensures that the basic block boundary of a block statement
is the exactly the block's open and close brace offsets (inclusive). For example,
in if/for/while statements. This also has the consequence that for statements
like "if (cond) foo();", the whitespace preceding "foo()" is not part of
the "foo()" basic block, but instead is part of the "if (cond) " basic block.
This is okay because these text offsets aren't meant to be human readable.
Instead, they reflect the text offsets of JSC's AST nodes. The Web Inspector
is the only client of this API and user of these text offsets and it is
not negatively effected by this new behavior.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::insertBasicBlockBoundariesForControlFlowProfiler):
When computing basic block boundaries in CodeBlock, we ensure that every
block's end offset is one less than its successor's start offset to
maintain that boundaries' ranges should be mutually exclusive.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
Because the control flow profiler needs to know which functions
have executed, we can't lazily create functions. This was a bug
from before that was hidden because the Type Profiler was always
enabled when the control flow profiler was enabled when profiling
was turned on from the Web Inspector. But, JSC allows for Control
Flow profiling to be turned on without Type Profiling, so we need
to ensure the Control Flow profiler has all the data it needs.

* bytecompiler/NodesCodegen.cpp:
(JSC::ConditionalNode::emitBytecode):
(JSC::IfElseNode::emitBytecode):
(JSC::WhileNode::emitBytecode):
(JSC::ForNode::emitBytecode):
(JSC::ForInNode::emitMultiLoopBytecode):
(JSC::ForOfNode::emitBytecode):
(JSC::TryNode::emitBytecode):
* jsc.cpp:
(functionHasBasicBlockExecuted):
We now assert that the substring argument is indeed a substring
of the function argument's text because subtle bugs could be
introduced otherwise.

* parser/ASTBuilder.h:
(JSC::ASTBuilder::setStartOffset):
* parser/Nodes.h:
(JSC::Node::setStartOffset):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseBlockStatement):
(JSC::Parser<LexerType>::parseStatement):
(JSC::Parser<LexerType>::parseMemberExpression):
For the various function call AST nodes, their m_position member
variable is now the start of the entire function call _expression_
and not at the start of the open paren of the arguments list.

* runtime/BasicBlockLocation.cpp:
(JSC::BasicBlockLocation::getExecutedRanges):
* runtime/ControlFlowProfiler.cpp:
(JSC::ControlFlowProfiler::getBasicBlocksForSourceID):
Function ranges inserted as gaps should follow the same criteria
that the bytecode generator uses to ensure that basic blocks
start and end offsets are mutually exclusive.

* tests/controlFlowProfiler/brace-location.js: Added.
(foo):
(bar):
(baz):
(testIf):
(testForRegular):
(testForIn):
(testForOf):
(testWhile):
(testIfNoBraces):
(testForRegularNoBraces):
(testForInNoBraces):
(testForOfNoBraces):
(testWhileNoBraces):
* tests/controlFlowProfiler/conditional-_expression_.js: Added.
(foo):
(bar):
(baz):
(testConditionalBasic):
(testConditionalFunctionCall):
* tests/controlFlowProfiler/driver/driver.js:
(checkBasicBlock):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (180517 => 180518)


--- trunk/Source/_javascript_Core/ChangeLog	2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-02-23 22:10:51 UTC (rev 180518)
@@ -1,3 +1,102 @@
+2015-02-23  Saam Barati  <[email protected]>
+
+        Adjust the ranges of basic block statements in JSC's control flow profiler to be mutually exclusive
+        https://bugs.webkit.org/show_bug.cgi?id=141095
+
+        Reviewed by Mark Lam.
+
+        Suppose the control flow of a program forms basic block A with successor block
+        B. A's end offset will be the *same* as B's start offset in the current architecture 
+        of the control flow profiler. This makes reasoning about the text offsets of
+        the control flow profiler unsound. To make reasoning about offsets sound, all 
+        basic block ranges should be mutually exclusive.  All calls to emitProfileControlFlow 
+        now pass in the *start* of a basic block as the text offset argument. This simplifies 
+        all calls to emitProfileControlFlow because the previous implementation had a
+        lot of edge cases for getting the desired basic block text boundaries.
+
+        This patch also ensures that the basic block boundary of a block statement 
+        is the exactly the block's open and close brace offsets (inclusive). For example,
+        in if/for/while statements. This also has the consequence that for statements 
+        like "if (cond) foo();", the whitespace preceding "foo()" is not part of 
+        the "foo()" basic block, but instead is part of the "if (cond) " basic block. 
+        This is okay because these text offsets aren't meant to be human readable.
+        Instead, they reflect the text offsets of JSC's AST nodes. The Web Inspector 
+        is the only client of this API and user of these text offsets and it is 
+        not negatively effected by this new behavior.
+
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::insertBasicBlockBoundariesForControlFlowProfiler):
+        When computing basic block boundaries in CodeBlock, we ensure that every
+        block's end offset is one less than its successor's start offset to
+        maintain that boundaries' ranges should be mutually exclusive.
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        Because the control flow profiler needs to know which functions
+        have executed, we can't lazily create functions. This was a bug 
+        from before that was hidden because the Type Profiler was always 
+        enabled when the control flow profiler was enabled when profiling 
+        was turned on from the Web Inspector. But, JSC allows for Control 
+        Flow profiling to be turned on without Type Profiling, so we need 
+        to ensure the Control Flow profiler has all the data it needs.
+
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::ConditionalNode::emitBytecode):
+        (JSC::IfElseNode::emitBytecode):
+        (JSC::WhileNode::emitBytecode):
+        (JSC::ForNode::emitBytecode):
+        (JSC::ForInNode::emitMultiLoopBytecode):
+        (JSC::ForOfNode::emitBytecode):
+        (JSC::TryNode::emitBytecode):
+        * jsc.cpp:
+        (functionHasBasicBlockExecuted):
+        We now assert that the substring argument is indeed a substring
+        of the function argument's text because subtle bugs could be
+        introduced otherwise.
+
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::setStartOffset):
+        * parser/Nodes.h:
+        (JSC::Node::setStartOffset):
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseBlockStatement):
+        (JSC::Parser<LexerType>::parseStatement):
+        (JSC::Parser<LexerType>::parseMemberExpression):
+        For the various function call AST nodes, their m_position member 
+        variable is now the start of the entire function call _expression_ 
+        and not at the start of the open paren of the arguments list.
+
+        * runtime/BasicBlockLocation.cpp:
+        (JSC::BasicBlockLocation::getExecutedRanges):
+        * runtime/ControlFlowProfiler.cpp:
+        (JSC::ControlFlowProfiler::getBasicBlocksForSourceID):
+        Function ranges inserted as gaps should follow the same criteria
+        that the bytecode generator uses to ensure that basic blocks
+        start and end offsets are mutually exclusive.
+
+        * tests/controlFlowProfiler/brace-location.js: Added.
+        (foo):
+        (bar):
+        (baz):
+        (testIf):
+        (testForRegular):
+        (testForIn):
+        (testForOf):
+        (testWhile):
+        (testIfNoBraces):
+        (testForRegularNoBraces):
+        (testForInNoBraces):
+        (testForOfNoBraces):
+        (testWhileNoBraces):
+        * tests/controlFlowProfiler/conditional-_expression_.js: Added.
+        (foo):
+        (bar):
+        (baz):
+        (testConditionalBasic):
+        (testConditionalFunctionCall):
+        * tests/controlFlowProfiler/driver/driver.js:
+        (checkBasicBlock):
+
 2015-02-23  Matthew Mirman  <[email protected]>
 
         r9 is volatile on ARMv7 for iOS 3 and up. 

Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp (180517 => 180518)


--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp	2015-02-23 22:10:51 UTC (rev 180518)
@@ -4052,7 +4052,7 @@
         if (i + 1 < offsetsLength) {
             size_t endIdx = bytecodeOffsets[i + 1];
             RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[endIdx].u.opcode) == op_profile_control_flow);
-            basicBlockEndOffset = instructions[endIdx + 1].u.operand;
+            basicBlockEndOffset = instructions[endIdx + 1].u.operand - 1;
         } else {
             basicBlockEndOffset = m_sourceOffset + m_ownerExecutable->source().length() - 1; // Offset before the closing brace.
             basicBlockStartOffset = std::min(basicBlockStartOffset, basicBlockEndOffset); // Some start offsets may be at the closing brace, ensure it is the offset before.

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (180517 => 180518)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2015-02-23 22:10:51 UTC (rev 180518)
@@ -338,7 +338,7 @@
 
     m_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset());
 
-    bool canLazilyCreateFunctions = !functionNode->needsActivationForMoreThanVariables() && !m_shouldEmitDebugHooks && !m_vm->typeProfiler();
+    bool canLazilyCreateFunctions = !functionNode->needsActivationForMoreThanVariables() && !m_shouldEmitDebugHooks && !m_vm->typeProfiler() && !m_vm->controlFlowProfiler();
     m_firstLazyFunction = codeBlock->m_numVars;
     if (!shouldCaptureAllTheThings) {
         for (size_t i = 0; i < functionStack.size(); ++i) {

Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (180517 => 180518)


--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2015-02-23 22:10:51 UTC (rev 180518)
@@ -1444,12 +1444,12 @@
     generator.emitJump(afterElse.get());
 
     generator.emitLabel(beforeElse.get());
-    generator.emitProfileControlFlow(m_expr2->startOffset());
+    generator.emitProfileControlFlow(m_expr1->endOffset() + 1);
     generator.emitNode(newDst.get(), m_expr2);
 
     generator.emitLabel(afterElse.get());
 
-    generator.emitProfileControlFlow(m_expr2->endOffset());
+    generator.emitProfileControlFlow(m_expr2->endOffset() + 1);
 
     return newDst.get();
 }
@@ -1897,12 +1897,13 @@
     generator.emitLabel(beforeElse.get());
 
     if (m_elseBlock) {
-        generator.emitProfileControlFlow(m_ifBlock->endOffset());
+        generator.emitProfileControlFlow(m_ifBlock->endOffset() + (m_ifBlock->isBlock() ? 1 : 0));
         generator.emitNode(dst, m_elseBlock);
     }
 
     generator.emitLabel(afterElse.get());
-    generator.emitProfileControlFlow(m_elseBlock ? m_elseBlock->endOffset() : m_ifBlock->endOffset());
+    StatementNode* endingBlock = m_elseBlock ? m_elseBlock : m_ifBlock;
+    generator.emitProfileControlFlow(endingBlock->endOffset() + (endingBlock->isBlock() ? 1 : 0));
 }
 
 // ------------------------------ DoWhileNode ----------------------------------
@@ -1948,7 +1949,7 @@
 
     generator.emitLabel(scope->breakTarget());
 
-    generator.emitProfileControlFlow(endOffset());
+    generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0));
 }
 
 // ------------------------------ ForNode --------------------------------------
@@ -1983,7 +1984,7 @@
         generator.emitJump(topOfLoop.get());
 
     generator.emitLabel(scope->breakTarget());
-    generator.emitProfileControlFlow(endOffset());
+    generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0));
 }
 
 // ------------------------------ ForInNode ------------------------------------
@@ -2100,6 +2101,8 @@
     generator.emitNode(base.get(), m_expr);
     RefPtr<RegisterID> local = this->tryGetBoundLocal(generator);
 
+    int profilerStartOffset = m_statement->startOffset();
+    int profilerEndOffset = m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0);
     // Indexed property loop.
     {
         LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop);
@@ -2121,13 +2124,13 @@
         generator.emitToIndexString(propertyName.get(), i.get());
         this->emitLoopHeader(generator, propertyName.get());
 
-        generator.emitProfileControlFlow(m_statement->startOffset());
+        generator.emitProfileControlFlow(profilerStartOffset);
 
         generator.pushIndexedForInScope(local.get(), i.get());
         generator.emitNode(dst, m_statement);
         generator.popIndexedForInScope(local.get());
 
-        generator.emitProfileControlFlow(m_statement->endOffset());
+        generator.emitProfileControlFlow(profilerEndOffset);
 
         generator.emitLabel(scope->continueTarget());
         generator.emitInc(i.get());
@@ -2159,13 +2162,13 @@
 
         this->emitLoopHeader(generator, propertyName.get());
 
-        generator.emitProfileControlFlow(m_statement->startOffset());
+        generator.emitProfileControlFlow(profilerStartOffset);
 
         generator.pushStructureForInScope(local.get(), i.get(), propertyName.get(), structureEnumerator.get());
         generator.emitNode(dst, m_statement);
         generator.popStructureForInScope(local.get());
 
-        generator.emitProfileControlFlow(m_statement->endOffset());
+        generator.emitProfileControlFlow(profilerEndOffset);
 
         generator.emitLabel(scope->continueTarget());
         generator.emitInc(i.get());
@@ -2196,7 +2199,7 @@
 
         this->emitLoopHeader(generator, propertyName.get());
 
-        generator.emitProfileControlFlow(m_statement->startOffset());
+        generator.emitProfileControlFlow(profilerStartOffset);
 
         generator.emitNode(dst, m_statement);
 
@@ -2217,7 +2220,7 @@
 
     generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
     generator.emitLabel(end.get());
-    generator.emitProfileControlFlow(m_statement->endOffset());
+    generator.emitProfileControlFlow(profilerEndOffset);
 }
 
 void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
@@ -2285,7 +2288,7 @@
         generator.emitNode(dst, m_statement);
     };
     generator.emitEnumeration(this, m_expr, extractor);
-    generator.emitProfileControlFlow(m_statement->endOffset());
+    generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0));
 }
 
 // ------------------------------ ContinueNode ---------------------------------
@@ -2631,7 +2634,7 @@
         }
 
         generator.emitPushCatchScope(generator.scopeRegister(), m_exceptionIdent, exceptionRegister.get(), DontDelete);
-        generator.emitProfileControlFlow(m_tryBlock->endOffset());
+        generator.emitProfileControlFlow(m_tryBlock->endOffset() + 1);
         generator.emitNode(dst, m_catchBlock);
         generator.emitPopScope(generator.scopeRegister());
         generator.emitLabel(catchEndLabel.get());
@@ -2644,12 +2647,12 @@
 
         RefPtr<Label> finallyEndLabel = generator.newLabel();
 
-        int finallyStartOffset = m_catchBlock ? m_catchBlock->endOffset() : m_tryBlock->endOffset();
+        int finallyStartOffset = m_catchBlock ? m_catchBlock->endOffset() + 1 : m_tryBlock->endOffset() + 1;
 
         // Normal path: run the finally code, and jump to the end.
         generator.emitProfileControlFlow(finallyStartOffset);
         generator.emitNode(dst, m_finallyBlock);
-        generator.emitProfileControlFlow(m_finallyBlock->endOffset());
+        generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1);
         generator.emitJump(finallyEndLabel.get());
 
         // Uncaught exception path: invoke the finally block, then re-throw the exception.
@@ -2659,9 +2662,9 @@
         generator.emitThrow(tempExceptionRegister.get());
 
         generator.emitLabel(finallyEndLabel.get());
-        generator.emitProfileControlFlow(m_finallyBlock->endOffset());
+        generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1);
     } else
-        generator.emitProfileControlFlow(m_catchBlock->endOffset());
+        generator.emitProfileControlFlow(m_catchBlock->endOffset() + 1);
 
 }
 

Modified: trunk/Source/_javascript_Core/jsc.cpp (180517 => 180518)


--- trunk/Source/_javascript_Core/jsc.cpp	2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/jsc.cpp	2015-02-23 22:10:51 UTC (rev 180518)
@@ -1127,6 +1127,7 @@
     RELEASE_ASSERT(exec->argument(1).isString());
     String substring = exec->argument(1).getString(exec);
     String sourceCodeText = executable->source().toString();
+    RELEASE_ASSERT(sourceCodeText.contains(substring));
     int offset = sourceCodeText.find(substring) + executable->source().startOffset();
     
     bool hasExecuted = exec->vm().controlFlowProfiler()->hasBasicBlockAtTextOffsetBeenExecuted(offset, executable->sourceID(), exec->vm());

Modified: trunk/Source/_javascript_Core/parser/ASTBuilder.h (180517 => 180518)


--- trunk/Source/_javascript_Core/parser/ASTBuilder.h	2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/parser/ASTBuilder.h	2015-02-23 22:10:51 UTC (rev 180518)
@@ -713,6 +713,11 @@
     {
         node->setStartOffset(offset);
     }
+
+    void setStartOffset(Node* node, int offset)
+    {
+        node->setStartOffset(offset);
+    }
     
 private:
     struct Scope {

Modified: trunk/Source/_javascript_Core/parser/Nodes.h (180517 => 180518)


--- trunk/Source/_javascript_Core/parser/Nodes.h	2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/parser/Nodes.h	2015-02-23 22:10:51 UTC (rev 180518)
@@ -132,6 +132,7 @@
         int lineStartOffset() const { return m_position.lineStartOffset; }
         const JSTextPosition& position() const { return m_position; }
         void setEndOffset(int offset) { m_endOffset = offset; }
+        void setStartOffset(int offset) { m_position.offset = offset; }
 
     protected:
         JSTextPosition m_position;

Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (180517 => 180518)


--- trunk/Source/_javascript_Core/parser/Parser.cpp	2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp	2015-02-23 22:10:51 UTC (rev 180518)
@@ -1115,21 +1115,24 @@
 {
     ASSERT(match(OPENBRACE));
     JSTokenLocation location(tokenLocation());
+    int startOffset = m_token.m_data.offset;
     int start = tokenLine();
     next();
     if (match(CLOSEBRACE)) {
-        unsigned endOffset = m_lexer->currentOffset();
+        int endOffset = m_token.m_data.offset;
         next();
         TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line);
+        context.setStartOffset(result, startOffset);
         context.setEndOffset(result, endOffset);
         return result;
     }
     TreeSourceElements subtree = parseSourceElements(context, DontCheckForStrictMode);
     failIfFalse(subtree, "Cannot parse the body of the block statement");
     matchOrFail(CLOSEBRACE, "Expected a closing '}' at the end of a block statement");
-    unsigned endOffset = m_lexer->currentOffset();
+    int endOffset = m_token.m_data.offset;
     next();
     TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line);
+    context.setStartOffset(result, startOffset);
     context.setEndOffset(result, endOffset);
     return result;
 }
@@ -1143,9 +1146,11 @@
     int nonTrivialExpressionCount = 0;
     failIfStackOverflow();
     TreeStatement result = 0;
+    bool shouldSetEndOffset = true;
     switch (m_token.m_type) {
     case OPENBRACE:
         result = parseBlockStatement(context);
+        shouldSetEndOffset = false;
         break;
     case VAR:
         result = parseVarDeclaration(context);
@@ -1228,7 +1233,7 @@
         break;
     }
 
-    if (result)
+    if (result && shouldSetEndOffset)
         context.setEndOffset(result, m_lastTokenEndPosition.offset);
     return result;
 }
@@ -2312,6 +2317,7 @@
     TreeExpression base = 0;
     JSTextPosition expressionStart = tokenStartPosition();
     int newCount = 0;
+    JSTokenLocation startLocation = tokenLocation();
     JSTokenLocation location;
     while (match(NEW)) {
         next();
@@ -2350,7 +2356,7 @@
                 JSTextPosition expressionEnd = lastTokenEndPosition();
                 TreeArguments arguments = parseArguments(context, AllowSpread);
                 failIfFalse(arguments, "Cannot parse call arguments");
-                base = context.makeFunctionCallNode(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition());
+                base = context.makeFunctionCallNode(startLocation, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition());
             }
             m_nonLHSCount = nonLHSCount;
             break;

Modified: trunk/Source/_javascript_Core/runtime/BasicBlockLocation.cpp (180517 => 180518)


--- trunk/Source/_javascript_Core/runtime/BasicBlockLocation.cpp	2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/runtime/BasicBlockLocation.cpp	2015-02-23 22:10:51 UTC (rev 180518)
@@ -62,8 +62,8 @@
                 minIdx = idx;
             }
         }
-        result.append(Gap(nextRangeStart, minGap.first));
-        nextRangeStart = minGap.second;
+        result.append(Gap(nextRangeStart, minGap.first - 1));
+        nextRangeStart = minGap.second + 1;
         gaps.remove(minIdx);
     }
 

Modified: trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.cpp (180517 => 180518)


--- trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.cpp	2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.cpp	2015-02-23 22:10:51 UTC (rev 180518)
@@ -91,7 +91,7 @@
         BasicBlockRange range;
         range.m_hasExecuted = std::get<0>(functionRange);
         range.m_startOffset = static_cast<int>(std::get<1>(functionRange));
-        range.m_endOffset = static_cast<int>(std::get<2>(functionRange) + 1);
+        range.m_endOffset = static_cast<int>(std::get<2>(functionRange));
         result.append(range);
     }
 

Added: trunk/Source/_javascript_Core/tests/controlFlowProfiler/brace-location.js (0 => 180518)


--- trunk/Source/_javascript_Core/tests/controlFlowProfiler/brace-location.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/controlFlowProfiler/brace-location.js	2015-02-23 22:10:51 UTC (rev 180518)
@@ -0,0 +1,159 @@
+load("./driver/driver.js");
+
+function foo() {}
+function bar() {}
+function baz() {}
+
+function testIf(x) {
+    if (x < 10) { foo(); } else if (x < 20) { bar(); } else { baz() }
+}
+testIf(9);
+// Note, the check will be against the basic block that contains the first matched character. 
+// So in this following case, the block that contains the '{'.
+checkBasicBlock(testIf, "{ foo", ShouldHaveExecuted);
+// In this case, it will test the basic block that contains the ' ' character.
+checkBasicBlock(testIf, " foo", ShouldHaveExecuted);
+checkBasicBlock(testIf, "} else if", ShouldHaveExecuted);
+checkBasicBlock(testIf, "else if", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, "{ bar", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, " bar", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, "else {", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, "{ baz", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, " baz", ShouldNotHaveExecuted);
+testIf(21);
+checkBasicBlock(testIf, "else if (x < 20)", ShouldHaveExecuted); 
+checkBasicBlock(testIf, "{ bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testIf, " bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testIf, "else {", ShouldHaveExecuted); 
+checkBasicBlock(testIf, "{ baz", ShouldHaveExecuted); 
+checkBasicBlock(testIf, " baz", ShouldHaveExecuted); 
+testIf(11);
+checkBasicBlock(testIf, "{ bar", ShouldHaveExecuted); 
+checkBasicBlock(testIf, " bar", ShouldHaveExecuted); 
+
+function testForRegular(x) {
+    for (var i = 0; i < x; i++) { foo(); } bar();
+}
+testForRegular(0);
+checkBasicBlock(testForRegular, "{ foo", ShouldNotHaveExecuted); 
+checkBasicBlock(testForRegular, "} bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testForRegular, " bar", ShouldHaveExecuted); 
+testForRegular(1);
+checkBasicBlock(testForRegular, "{ foo", ShouldHaveExecuted); 
+checkBasicBlock(testForRegular, "} bar", ShouldHaveExecuted); 
+
+function testForIn(x) {
+    for (var i in x) { foo(); } bar();
+}
+testForIn({});
+checkBasicBlock(testForIn, "{ foo", ShouldNotHaveExecuted); 
+checkBasicBlock(testForIn, "} bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testForIn, " bar", ShouldHaveExecuted); 
+testForIn({foo: 20});
+checkBasicBlock(testForIn, "{ foo", ShouldHaveExecuted); 
+checkBasicBlock(testForIn, "} bar", ShouldHaveExecuted); 
+
+function testForOf(x) {
+    for (var i of x) { foo(); } bar();
+}
+testForOf([]);
+checkBasicBlock(testForOf, "{ foo", ShouldNotHaveExecuted); 
+checkBasicBlock(testForOf, " foo", ShouldNotHaveExecuted); 
+checkBasicBlock(testForOf, "} bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testForOf, " bar", ShouldHaveExecuted); 
+testForOf([20]);
+checkBasicBlock(testForOf, "{ foo", ShouldHaveExecuted); 
+checkBasicBlock(testForOf, "} bar", ShouldHaveExecuted); 
+
+function testWhile(x) {
+    var i = 0; while (i++ < x) { foo(); } bar();
+}
+testWhile(0);
+checkBasicBlock(testWhile, "{ foo", ShouldNotHaveExecuted); 
+checkBasicBlock(testWhile, " foo", ShouldNotHaveExecuted); 
+checkBasicBlock(testWhile, "} bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testWhile, " bar", ShouldHaveExecuted); 
+testWhile(1);
+checkBasicBlock(testWhile, "{ foo", ShouldHaveExecuted); 
+checkBasicBlock(testWhile, "} bar", ShouldHaveExecuted); 
+
+
+// No braces tests.
+
+function testIfNoBraces(x) {
+    if (x < 10) foo(); else if (x < 20) bar(); else baz();
+}
+testIfNoBraces(9);
+checkBasicBlock(testIfNoBraces, "foo", ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, " foo", ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, "; else if", ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, " else if", ShouldNotHaveExecuted);
+checkBasicBlock(testIfNoBraces, " bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testIfNoBraces, "bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testIfNoBraces, "else baz", ShouldNotHaveExecuted); 
+checkBasicBlock(testIfNoBraces, "baz", ShouldNotHaveExecuted); 
+testIfNoBraces(21);
+checkBasicBlock(testIfNoBraces, "else baz", ShouldHaveExecuted); 
+checkBasicBlock(testIfNoBraces, "baz", ShouldHaveExecuted); 
+checkBasicBlock(testIfNoBraces, "; else baz", ShouldNotHaveExecuted); 
+checkBasicBlock(testIfNoBraces, "else if (x < 20)", ShouldHaveExecuted);
+// Note that the whitespace preceding bar is part of the previous basic block.
+// An if statement's if-true basic block text offset begins at the start offset
+// of the if-true block, in this case, just the _expression_ "bar()".
+checkBasicBlock(testIfNoBraces, " bar", ShouldHaveExecuted); 
+checkBasicBlock(testIfNoBraces, "bar", ShouldNotHaveExecuted); 
+testIfNoBraces(11);
+checkBasicBlock(testIfNoBraces, " bar", ShouldHaveExecuted); 
+checkBasicBlock(testIfNoBraces, "bar", ShouldHaveExecuted); 
+
+function testForRegularNoBraces(x) {
+    for (var i = 0; i < x; i++) foo(); bar();
+}
+testForRegularNoBraces(0);
+checkBasicBlock(testForRegularNoBraces, " foo", ShouldHaveExecuted); 
+checkBasicBlock(testForRegularNoBraces, "foo", ShouldNotHaveExecuted); 
+checkBasicBlock(testForRegularNoBraces, "; bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testForRegularNoBraces, " bar", ShouldHaveExecuted); 
+testForRegularNoBraces(1);
+checkBasicBlock(testForRegularNoBraces, " foo", ShouldHaveExecuted); 
+checkBasicBlock(testForRegularNoBraces, "foo", ShouldHaveExecuted); 
+checkBasicBlock(testForRegularNoBraces, " bar", ShouldHaveExecuted); 
+
+function testForInNoBraces(x) {
+    for (var i in x) foo(); bar();
+}
+testForInNoBraces({});
+checkBasicBlock(testForInNoBraces, " foo", ShouldHaveExecuted); 
+checkBasicBlock(testForInNoBraces, "foo", ShouldNotHaveExecuted); 
+checkBasicBlock(testForInNoBraces, "; bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testForInNoBraces, " bar", ShouldHaveExecuted); 
+testForInNoBraces({foo: 20});
+checkBasicBlock(testForInNoBraces, " foo", ShouldHaveExecuted); 
+checkBasicBlock(testForInNoBraces, "foo", ShouldHaveExecuted); 
+checkBasicBlock(testForInNoBraces, "; bar", ShouldHaveExecuted); 
+
+function testForOfNoBraces(x) {
+    for (var i of x) foo(); bar();
+}
+testForOfNoBraces([]);
+checkBasicBlock(testForOfNoBraces, " foo", ShouldHaveExecuted); 
+checkBasicBlock(testForOfNoBraces, "foo", ShouldNotHaveExecuted); 
+checkBasicBlock(testForOfNoBraces, "; bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testForOfNoBraces, " bar", ShouldHaveExecuted); 
+testForOfNoBraces([20]);
+checkBasicBlock(testForOfNoBraces, " foo", ShouldHaveExecuted); 
+checkBasicBlock(testForOfNoBraces, "foo", ShouldHaveExecuted); 
+checkBasicBlock(testForOfNoBraces, "; bar", ShouldHaveExecuted); 
+
+function testWhileNoBraces(x) {
+    var i = 0; while (i++ < x) foo(); bar();
+}
+testWhileNoBraces(0);
+checkBasicBlock(testWhileNoBraces, " foo", ShouldHaveExecuted); 
+checkBasicBlock(testWhileNoBraces, "foo", ShouldNotHaveExecuted); 
+checkBasicBlock(testWhileNoBraces, "; bar", ShouldNotHaveExecuted); 
+checkBasicBlock(testWhileNoBraces, " bar", ShouldHaveExecuted); 
+testWhileNoBraces(1);
+checkBasicBlock(testWhileNoBraces, " foo", ShouldHaveExecuted); 
+checkBasicBlock(testWhileNoBraces, "foo", ShouldHaveExecuted); 
+checkBasicBlock(testWhileNoBraces, "; bar", ShouldHaveExecuted); 

Added: trunk/Source/_javascript_Core/tests/controlFlowProfiler/conditional-_expression_.js (0 => 180518)


--- trunk/Source/_javascript_Core/tests/controlFlowProfiler/conditional-_expression_.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/controlFlowProfiler/conditional-_expression_.js	2015-02-23 22:10:51 UTC (rev 180518)
@@ -0,0 +1,44 @@
+load("./driver/driver.js");
+
+function foo(){ }
+function bar(){ }
+function baz(){ }
+
+function testConditionalBasic(x) {
+    return x ? 10 : 20;
+}
+
+
+testConditionalBasic(false);
+checkBasicBlock(testConditionalBasic, "x", ShouldHaveExecuted);
+checkBasicBlock(testConditionalBasic, "20", ShouldHaveExecuted);
+checkBasicBlock(testConditionalBasic, "10", ShouldNotHaveExecuted);
+
+testConditionalBasic(true);
+checkBasicBlock(testConditionalBasic, "10", ShouldHaveExecuted);
+
+
+function testConditionalFunctionCall(x, y) {
+    x ? y ? foo() 
+        : baz() 
+        : bar()
+}
+testConditionalFunctionCall(false, false);
+checkBasicBlock(testConditionalFunctionCall, "x", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "? y", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "bar", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, ": bar", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "y", ShouldNotHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "? foo", ShouldNotHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "foo", ShouldNotHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "baz", ShouldNotHaveExecuted);
+
+testConditionalFunctionCall(true, false);
+checkBasicBlock(testConditionalFunctionCall, "y", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "? foo", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, ": baz", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "baz", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "foo", ShouldNotHaveExecuted);
+
+testConditionalFunctionCall(true, true);
+checkBasicBlock(testConditionalFunctionCall, "foo", ShouldHaveExecuted);

Modified: trunk/Source/_javascript_Core/tests/controlFlowProfiler/driver/driver.js (180517 => 180518)


--- trunk/Source/_javascript_Core/tests/controlFlowProfiler/driver/driver.js	2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/tests/controlFlowProfiler/driver/driver.js	2015-02-23 22:10:51 UTC (rev 180518)
@@ -2,3 +2,13 @@
     if (!condition)
         throw new Error(reason);
 }
+
+var ShouldHaveExecuted = true;
+var ShouldNotHaveExecuted = false;
+
+function checkBasicBlock(func, expr, expectation) {
+    if (expectation === ShouldHaveExecuted)
+        assert(hasBasicBlockExecuted(func, expr, "should have executed"));
+    else
+        assert(!hasBasicBlockExecuted(func, expr, "should not have executed"));
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to