Title: [231660] trunk/Source/_javascript_Core
Revision
231660
Author
fpi...@apple.com
Date
2018-05-10 14:31:49 -0700 (Thu, 10 May 2018)

Log Message

InPlaceAbstractState::beginBasicBlock shouldn't copy all m_variables every time
https://bugs.webkit.org/show_bug.cgi?id=185452

Reviewed by Michael Saboff.
        
We were spending a lot of time in beginBasicBlock() just copying the state of all variables
from the block head to InPlaceAbstractState::m_variables. It is necessary for
InPlaceAbstractState to have its own copy since we need to mutate it separately from
block->valuesAtHead. But most variables are untouched by most basic blocks, so this was a lot
of superfluous work.
        
This change adds a bitvector called m_activeVariables that tracks which variables have been
copied. We lazily copy the variables on first use. Variables that were never copied also have
a simplified merging path, which just needs to consider if the variable got clobbered between
head and tail.
        
This is a 1.5% speed-up on SunSpider-CompileTime and a 1.7% speed-up on V8Spider-CompileTime.

* bytecode/Operands.h:
(JSC::Operands::argumentIndex const):
(JSC::Operands::localIndex const):
(JSC::Operands::argument):
(JSC::Operands::argument const):
(JSC::Operands::local):
(JSC::Operands::local const):
(JSC::Operands::operandIndex const):
* dfg/DFGAbstractValue.h:
(JSC::DFG::AbstractValue::fastForwardFromTo):
* dfg/DFGCFAPhase.cpp:
(JSC::DFG::CFAPhase::performForwardCFA):
* dfg/DFGInPlaceAbstractState.cpp:
(JSC::DFG::InPlaceAbstractState::beginBasicBlock):
(JSC::DFG::InPlaceAbstractState::variablesForDebugging):
(JSC::DFG::InPlaceAbstractState::activateAllVariables):
(JSC::DFG::InPlaceAbstractState::endBasicBlock):
(JSC::DFG::InPlaceAbstractState::activateVariable):
(JSC::DFG::InPlaceAbstractState::mergeStateAtTail): Deleted.
* dfg/DFGInPlaceAbstractState.h:
(JSC::DFG::InPlaceAbstractState::variableAt):
(JSC::DFG::InPlaceAbstractState::operand):
(JSC::DFG::InPlaceAbstractState::local):
(JSC::DFG::InPlaceAbstractState::argument):
(JSC::DFG::InPlaceAbstractState::activateVariableIfNecessary):
(JSC::DFG::InPlaceAbstractState::variablesForDebugging): Deleted.

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (231659 => 231660)


--- trunk/Source/_javascript_Core/ChangeLog	2018-05-10 20:59:07 UTC (rev 231659)
+++ trunk/Source/_javascript_Core/ChangeLog	2018-05-10 21:31:49 UTC (rev 231660)
@@ -1,3 +1,50 @@
+2018-05-09  Filip Pizlo  <fpi...@apple.com>
+
+        InPlaceAbstractState::beginBasicBlock shouldn't copy all m_variables every time
+        https://bugs.webkit.org/show_bug.cgi?id=185452
+
+        Reviewed by Michael Saboff.
+        
+        We were spending a lot of time in beginBasicBlock() just copying the state of all variables
+        from the block head to InPlaceAbstractState::m_variables. It is necessary for
+        InPlaceAbstractState to have its own copy since we need to mutate it separately from
+        block->valuesAtHead. But most variables are untouched by most basic blocks, so this was a lot
+        of superfluous work.
+        
+        This change adds a bitvector called m_activeVariables that tracks which variables have been
+        copied. We lazily copy the variables on first use. Variables that were never copied also have
+        a simplified merging path, which just needs to consider if the variable got clobbered between
+        head and tail.
+        
+        This is a 1.5% speed-up on SunSpider-CompileTime and a 1.7% speed-up on V8Spider-CompileTime.
+
+        * bytecode/Operands.h:
+        (JSC::Operands::argumentIndex const):
+        (JSC::Operands::localIndex const):
+        (JSC::Operands::argument):
+        (JSC::Operands::argument const):
+        (JSC::Operands::local):
+        (JSC::Operands::local const):
+        (JSC::Operands::operandIndex const):
+        * dfg/DFGAbstractValue.h:
+        (JSC::DFG::AbstractValue::fastForwardFromTo):
+        * dfg/DFGCFAPhase.cpp:
+        (JSC::DFG::CFAPhase::performForwardCFA):
+        * dfg/DFGInPlaceAbstractState.cpp:
+        (JSC::DFG::InPlaceAbstractState::beginBasicBlock):
+        (JSC::DFG::InPlaceAbstractState::variablesForDebugging):
+        (JSC::DFG::InPlaceAbstractState::activateAllVariables):
+        (JSC::DFG::InPlaceAbstractState::endBasicBlock):
+        (JSC::DFG::InPlaceAbstractState::activateVariable):
+        (JSC::DFG::InPlaceAbstractState::mergeStateAtTail): Deleted.
+        * dfg/DFGInPlaceAbstractState.h:
+        (JSC::DFG::InPlaceAbstractState::variableAt):
+        (JSC::DFG::InPlaceAbstractState::operand):
+        (JSC::DFG::InPlaceAbstractState::local):
+        (JSC::DFG::InPlaceAbstractState::argument):
+        (JSC::DFG::InPlaceAbstractState::activateVariableIfNecessary):
+        (JSC::DFG::InPlaceAbstractState::variablesForDebugging): Deleted.
+
 2018-05-09  Caio Lima  <ticaiol...@gmail.com>
 
         [ESNext][BigInt] Implement support for "==" operation

Modified: trunk/Source/_javascript_Core/bytecode/Operands.h (231659 => 231660)


--- trunk/Source/_javascript_Core/bytecode/Operands.h	2018-05-10 20:59:07 UTC (rev 231659)
+++ trunk/Source/_javascript_Core/bytecode/Operands.h	2018-05-10 21:31:49 UTC (rev 231660)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012, 2013, 2015, 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -71,19 +71,28 @@
     size_t numberOfArguments() const { return m_numArguments; }
     size_t numberOfLocals() const { return m_values.size() - m_numArguments; }
     
-    T& argument(size_t idx)
+    size_t argumentIndex(size_t idx) const
     {
         ASSERT(idx < m_numArguments);
-        return m_values[idx];
+        return idx;
     }
+    
+    size_t localIndex(size_t idx) const
+    {
+        return m_numArguments + idx;
+    }
+    
+    T& argument(size_t idx)
+    {
+        return m_values[argumentIndex(idx)];
+    }
     const T& argument(size_t idx) const
     {
-        ASSERT(idx < m_numArguments);
-        return m_values[idx];
+        return m_values[argumentIndex(idx)];
     }
     
-    T& local(size_t idx) { return m_values[m_numArguments + idx]; }
-    const T& local(size_t idx) const { return m_values[m_numArguments + idx]; }
+    T& local(size_t idx) { return m_values[localIndex(idx)]; }
+    const T& local(size_t idx) const { return m_values[localIndex(idx)]; }
     
     template<OperandKind operandKind>
     size_t sizeFor() const
@@ -156,6 +165,18 @@
         setLocal(idx, value);
     }
     
+    size_t operandIndex(int operand) const
+    {
+        if (operandIsArgument(operand))
+            return argumentIndex(VirtualRegister(operand).toArgument());
+        return localIndex(VirtualRegister(operand).toLocal());
+    }
+    
+    size_t operandIndex(VirtualRegister virtualRegister) const
+    {
+        return operandIndex(virtualRegister.offset());
+    }
+    
     T& operand(int operand)
     {
         if (operandIsArgument(operand))

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractValue.h (231659 => 231660)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractValue.h	2018-05-10 20:59:07 UTC (rev 231659)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractValue.h	2018-05-10 21:31:49 UTC (rev 231660)
@@ -104,6 +104,22 @@
         checkConsistency();
     }
     
+    ALWAYS_INLINE void fastForwardFromTo(AbstractValueClobberEpoch oldEpoch, AbstractValueClobberEpoch newEpoch)
+    {
+        if (newEpoch == oldEpoch)
+            return;
+        
+        if (!(m_type & SpecCell))
+            return;
+
+        if (newEpoch.clobberEpoch() != oldEpoch.clobberEpoch())
+            clobberStructures();
+        if (newEpoch.structureClobberState() == StructuresAreWatched)
+            m_structure.observeInvalidationPoint();
+
+        checkConsistency();
+    }
+    
     ALWAYS_INLINE void fastForwardTo(AbstractValueClobberEpoch newEpoch)
     {
         if (newEpoch == m_effectEpoch)

Modified: trunk/Source/_javascript_Core/dfg/DFGCFAPhase.cpp (231659 => 231660)


--- trunk/Source/_javascript_Core/dfg/DFGCFAPhase.cpp	2018-05-10 20:59:07 UTC (rev 231659)
+++ trunk/Source/_javascript_Core/dfg/DFGCFAPhase.cpp	2018-05-10 21:31:49 UTC (rev 231660)
@@ -203,7 +203,7 @@
     {
         ++m_count;
         if (m_verbose)
-            dataLogF("CFA [%u]\n", ++m_count);
+            dataLogF("CFA [%u]\n", m_count);
         
         for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex)
             performBlockCFA(m_graph.block(blockIndex));

Modified: trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp (231659 => 231660)


--- trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp	2018-05-10 20:59:07 UTC (rev 231659)
+++ trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp	2018-05-10 21:31:49 UTC (rev 231660)
@@ -54,8 +54,6 @@
 
 void InPlaceAbstractState::beginBasicBlock(BasicBlock* basicBlock)
 {
-    // This function is ~1.6-2% of execution time.
-    
     ASSERT(!m_block);
     
     ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->valuesAtHead.numberOfLocals());
@@ -62,23 +60,19 @@
     ASSERT(basicBlock->variablesAtTail.numberOfLocals() == basicBlock->valuesAtTail.numberOfLocals());
     ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->variablesAtTail.numberOfLocals());
 
-    m_abstractValues.resize(); // This part is ~0.1-0.4% of execution time.
+    m_abstractValues.resize();
 
     AbstractValueClobberEpoch epoch = AbstractValueClobberEpoch::first(basicBlock->cfaStructureClobberStateAtHead);
+    m_epochAtHead = epoch;
     m_effectEpoch = epoch;
 
-    // This loop is 0.9-1.2% of execution time.
-    // FIXME: Lazily populate m_variables when GetLocal/SetLocal happens. Apply the same idea to
-    // merging. Alternatively, we could just use liveness here.
-    // https://bugs.webkit.org/show_bug.cgi?id=185452
-    for (size_t i = m_variables.size(); i--;) {
-        AbstractValue& value = m_variables[i];
-        value = basicBlock->valuesAtHead[i];
-        value.m_effectEpoch = epoch;
-    }
+    m_block = basicBlock;
+
+    m_activeVariables.clearRange(0, std::min(m_variables.size(), m_activeVariables.size()));
+    if (m_variables.size() > m_activeVariables.size())
+        m_activeVariables.resize(m_variables.size());
     
     if (m_graph.m_form == SSA) {
-        // This loop is 0.05-0.17% of execution time.
         for (NodeAbstractValuePair& entry : basicBlock->ssa->valuesAtHead) {
             if (entry.node.isStillValid()) {
                 AbstractValue& value = m_abstractValues.at(entry.node);
@@ -89,7 +83,6 @@
     }
     basicBlock->cfaShouldRevisit = false;
     basicBlock->cfaHasVisited = true;
-    m_block = basicBlock;
     m_isValid = true;
     m_foundConstants = false;
     m_branchDirection = InvalidBranchDirection;
@@ -104,6 +97,18 @@
         values.uncheckedAppend(NodeAbstractValuePair { node, AbstractValue() });
 }
 
+Operands<AbstractValue>& InPlaceAbstractState::variablesForDebugging()
+{
+    activateAllVariables();
+    return m_variables;
+}
+
+void InPlaceAbstractState::activateAllVariables()
+{
+    for (size_t i = m_variables.size(); i--;)
+        activateVariableIfNecessary(i);
+}
+
 void InPlaceAbstractState::initialize()
 {
     for (BasicBlock* entrypoint : m_graph.m_roots) {
@@ -206,25 +211,67 @@
         return false;
     }
 
+    AbstractValueClobberEpoch epochAtHead = m_epochAtHead;
+    AbstractValueClobberEpoch currentEpoch = m_effectEpoch;
+
     block->cfaStructureClobberStateAtTail = m_structureClobberState;
-
+    
     switch (m_graph.m_form) {
     case ThreadedCPS: {
-        for (size_t argument = 0; argument < block->variablesAtTail.numberOfArguments(); ++argument) {
-            AbstractValue& destination = block->valuesAtTail.argument(argument);
-            mergeStateAtTail(destination, this->argument(argument), block->variablesAtTail.argument(argument));
+        ASSERT(block->variablesAtTail.size() == block->valuesAtTail.size());
+        ASSERT(block->variablesAtTail.size() == m_variables.size());
+        for (size_t index = m_variables.size(); index--;) {
+            Node* node = block->variablesAtTail[index];
+            if (!node)
+                continue;
+            AbstractValue& destination = block->valuesAtTail[index];
+            
+            if (!m_activeVariables[index]) {
+                destination = block->valuesAtHead[index];
+                destination.fastForwardFromTo(epochAtHead, currentEpoch);
+                continue;
+            }
+            
+            switch (node->op()) {
+            case Phi:
+            case SetArgument:
+            case PhantomLocal:
+            case Flush:
+                // The block transfers the value from head to tail.
+                destination = variableAt(index);
+                break;
+                
+            case GetLocal:
+                // The block refines the value with additional speculations.
+                destination = forNode(node);
+                break;
+                
+            case SetLocal:
+                // The block sets the variable, and potentially refines it, both
+                // before and after setting it.
+                destination = forNode(node->child1());
+                break;
+                
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+                break;
+            }
         }
-
-        for (size_t local = 0; local < block->variablesAtTail.numberOfLocals(); ++local) {
-            AbstractValue& destination = block->valuesAtTail.local(local);
-            mergeStateAtTail(destination, this->local(local), block->variablesAtTail.local(local));
-        }
         break;
     }
 
     case SSA: {
-        for (size_t i = 0; i < block->valuesAtTail.size(); ++i)
+        for (size_t i = 0; i < block->valuesAtTail.size(); ++i) {
+            AbstractValue& destination = block->valuesAtTail[i];
+
+            if (!m_activeVariables[i]) {
+                destination = block->valuesAtHead[i];
+                destination.fastForwardFromTo(epochAtHead, currentEpoch);
+                continue;
+            }
+            
             block->valuesAtTail[i] = variableAt(i);
+        }
 
         for (NodeAbstractValuePair& valueAtTail : block->ssa->valuesAtTail)
             valueAtTail.value = forNode(valueAtTail.node);
@@ -248,40 +295,12 @@
     m_structureClobberState = StructuresAreWatched;
 }
 
-void InPlaceAbstractState::mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, Node* node)
+void InPlaceAbstractState::activateVariable(size_t variableIndex)
 {
-    if (!node)
-        return;
-
-    const AbstractValue* source = nullptr;
-    
-    switch (node->op()) {
-    case Phi:
-    case SetArgument:
-    case PhantomLocal:
-    case Flush:
-        // The block transfers the value from head to tail.
-        source = &inVariable;
-        break;
-            
-    case GetLocal:
-        // The block refines the value with additional speculations.
-        source = &forNode(node);
-        break;
-            
-    case SetLocal:
-        // The block sets the variable, and potentially refines it, both
-        // before and after setting it.
-        source = &forNode(node->child1());
-        if (node->variableAccessData()->flushFormat() == FlushedDouble)
-            RELEASE_ASSERT(!(source->m_type & ~SpecFullDouble));
-        break;
-        
-    default:
-        RELEASE_ASSERT_NOT_REACHED();
-        break;
-    }
-    destination = *source;
+    AbstractValue& value = m_variables[variableIndex];
+    value = m_block->valuesAtHead[variableIndex];
+    value.m_effectEpoch = m_epochAtHead;
+    m_activeVariables[variableIndex] = true;
 }
 
 bool InPlaceAbstractState::merge(BasicBlock* from, BasicBlock* to)

Modified: trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.h (231659 => 231660)


--- trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.h	2018-05-10 20:59:07 UTC (rev 231659)
+++ trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.h	2018-05-10 21:31:49 UTC (rev 231660)
@@ -156,16 +156,34 @@
         makeHeapTopForNode(edge.node());
     }
     
-    Operands<AbstractValue>& variablesForDebugging() { return m_variables; }
+    Operands<AbstractValue>& variablesForDebugging();
 
     unsigned numberOfArguments() const { return m_variables.numberOfArguments(); }
     unsigned numberOfLocals() const { return m_variables.numberOfLocals(); }
-    AbstractValue& operand(int operand) { return fastForward(m_variables.operand(operand)); }
-    AbstractValue& operand(VirtualRegister operand) { return fastForward(m_variables.operand(operand)); }
-    AbstractValue& local(size_t index) { return fastForward(m_variables.local(index)); }
-    AbstractValue& argument(size_t index) { return fastForward(m_variables.argument(index)); }
-    AbstractValue& variableAt(size_t index) { return fastForward(m_variables[index]); }
     
+    AbstractValue& variableAt(size_t index)
+    {
+        activateVariableIfNecessary(index);
+        return fastForward(m_variables[index]);
+    }
+
+    AbstractValue& operand(int operand)
+    {
+        return variableAt(m_variables.operandIndex(operand));
+    }
+    
+    AbstractValue& operand(VirtualRegister operand) { return this->operand(operand.offset()); }
+    
+    AbstractValue& local(size_t index)
+    {
+        return variableAt(m_variables.localIndex(index));
+    }
+    
+    AbstractValue& argument(size_t index)
+    {
+        return variableAt(m_variables.argumentIndex(index));
+    }
+    
     // Call this before beginning CFA to initialize the abstract values of
     // arguments, and to indicate which blocks should be listed for CFA
     // execution.
@@ -247,8 +265,15 @@
     }
 
 private:
-    void mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, Node*);
-
+    ALWAYS_INLINE void activateVariableIfNecessary(size_t variableIndex)
+    {
+        if (!m_activeVariables[variableIndex])
+            activateVariable(variableIndex);
+    }
+    
+    void activateVariable(size_t variableIndex);
+    void activateAllVariables();
+    
     static bool mergeVariableBetweenBlocks(AbstractValue& destination, AbstractValue& source, Node* destinationNode, Node* sourceNode);
     
     Graph& m_graph;
@@ -255,6 +280,7 @@
 
     FlowMap<AbstractValue>& m_abstractValues;
     Operands<AbstractValue> m_variables;
+    FastBitVector m_activeVariables;
     BasicBlock* m_block;
     
     bool m_foundConstants;
@@ -262,6 +288,7 @@
     bool m_isValid;
     AbstractInterpreterClobberState m_clobberState;
     StructureClobberState m_structureClobberState;
+    AbstractValueClobberEpoch m_epochAtHead;
     AbstractValueClobberEpoch m_effectEpoch;
     
     BranchDirection m_branchDirection; // This is only set for blocks that end in Branch and that execute to completion (i.e. m_isValid == true).
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to