blob: 0aef108df6b72b7dd685aba246bebfcc4c64710a [file] [log] [blame]
/*
* Copyright (C) 2011, 2013-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#if ENABLE(DFG_JIT)
#include "DFGAbstractValue.h"
#include "DFGAvailability.h"
#include "DFGAvailabilityMap.h"
#include "DFGBranchDirection.h"
#include "DFGFlushedAt.h"
#include "DFGNode.h"
#include "DFGNodeAbstractValuePair.h"
#include "DFGNodeOrigin.h"
#include "DFGStructureClobberState.h"
#include "Operands.h"
#include <wtf/Vector.h>
namespace JSC { namespace DFG {
class Graph;
class InsertionSet;
typedef Vector<BasicBlock*, 2> PredecessorList;
typedef Vector<Node*, 8> BlockNodeList;
struct BasicBlock : RefCounted<BasicBlock> {
BasicBlock(
unsigned bytecodeBegin, unsigned numArguments, unsigned numLocals,
float executionCount);
~BasicBlock();
void ensureLocals(unsigned newNumLocals);
size_t size() const { return m_nodes.size(); }
bool isEmpty() const { return !size(); }
Node*& at(size_t i) { return m_nodes[i]; }
Node* at(size_t i) const { return m_nodes[i]; }
Node* tryAt(size_t i) const
{
if (i >= size())
return nullptr;
return at(i);
}
Node*& operator[](size_t i) { return at(i); }
Node* operator[](size_t i) const { return at(i); }
// Use this to find both the index of the terminal and the terminal itself in one go. May
// return a clear NodeAndIndex if the basic block currently lacks a terminal. That may happen
// in the middle of IR transformations within a phase but should never be the case in between
// phases.
//
// The reason why this is more than just "at(size() - 1)" is that we may place non-terminal
// liveness marking instructions after the terminal. This is supposed to happen infrequently
// but some basic blocks - most notably return blocks - will have liveness markers for all of
// the flushed variables right after the return.
//
// It turns out that doing this linear search is basically perf-neutral, so long as we force
// the method to be inlined. Hence the ALWAYS_INLINE.
ALWAYS_INLINE NodeAndIndex findTerminal() const
{
size_t i = size();
while (i--) {
Node* node = at(i);
switch (node->op()) {
case Jump:
case Branch:
case Switch:
case Return:
case TailCall:
case DirectTailCall:
case TailCallVarargs:
case TailCallForwardVarargs:
case Unreachable:
return NodeAndIndex(node, i);
// The bitter end can contain Phantoms and the like. There will probably only be one or two nodes after the terminal. They are all no-ops and will not have any checked children.
case Check: // This is here because it's our universal no-op.
case Phantom:
case PhantomLocal:
case Flush:
break;
default:
return NodeAndIndex();
}
}
return NodeAndIndex();
}
ALWAYS_INLINE Node* terminal() const
{
return findTerminal().node;
}
void resize(size_t size) { m_nodes.resize(size); }
void grow(size_t size) { m_nodes.grow(size); }
void append(Node* node) { m_nodes.append(node); }
void insertBeforeTerminal(Node* node)
{
NodeAndIndex result = findTerminal();
if (!result)
append(node);
else
m_nodes.insert(result.index, node);
}
void replaceTerminal(Node*);
size_t numNodes() const { return phis.size() + size(); }
Node* node(size_t i) const
{
if (i < phis.size())
return phis[i];
return at(i - phis.size());
}
bool isPhiIndex(size_t i) const { return i < phis.size(); }
bool isInPhis(Node* node) const;
bool isInBlock(Node* myNode) const;
BlockNodeList::iterator begin() { return m_nodes.begin(); }
BlockNodeList::iterator end() { return m_nodes.end(); }
unsigned numSuccessors() { return terminal()->numSuccessors(); }
BasicBlock*& successor(unsigned index)
{
return terminal()->successor(index);
}
BasicBlock*& successorForCondition(bool condition)
{
return terminal()->successorForCondition(condition);
}
Node::SuccessorsIterable successors()
{
return terminal()->successors();
}
void removePredecessor(BasicBlock* block);
void replacePredecessor(BasicBlock* from, BasicBlock* to);
template<typename... Params>
Node* appendNode(Graph&, SpeculatedType, Params...);
template<typename... Params>
Node* appendNonTerminal(Graph&, SpeculatedType, Params...);
template<typename... Params>
Node* replaceTerminal(Graph&, SpeculatedType, Params...);
void dump(PrintStream& out) const;
void didLink()
{
#if !ASSERT_DISABLED
isLinked = true;
#endif
}
// This value is used internally for block linking and OSR entry. It is mostly meaningless
// for other purposes due to inlining.
unsigned bytecodeBegin;
BlockIndex index;
bool isOSRTarget;
bool cfaHasVisited;
bool cfaShouldRevisit;
bool cfaFoundConstants;
bool cfaDidFinish;
StructureClobberState cfaStructureClobberStateAtHead;
StructureClobberState cfaStructureClobberStateAtTail;
BranchDirection cfaBranchDirection;
#if !ASSERT_DISABLED
bool isLinked;
#endif
bool isReachable;
Vector<Node*> phis;
PredecessorList predecessors;
Operands<Node*> variablesAtHead;
Operands<Node*> variablesAtTail;
Operands<AbstractValue> valuesAtHead;
Operands<AbstractValue> valuesAtTail;
// The intersection of assumptions we have made previously at the head of this block. Note
// that under normal circumstances, each time we run the CFA, we will get strictly more precise
// results. But we don't actually require this to be the case. It's fine for the CFA to loosen
// up for any odd reason. It's fine when this happens, because anything that the CFA proves
// must be true from that point forward, except if some registered watchpoint fires, in which
// case the code won't ever run. So, the CFA proving something less precise later on is just an
// outcome of the CFA being imperfect; the more precise thing that it had proved earlier is no
// less true.
//
// But for the purpose of OSR entry, we need to make sure that we remember what assumptions we
// had used for optimizing any given basic block. That's what this is for.
//
// It's interesting that we could use this to make the CFA more precise: all future CFAs could
// filter their results with this thing to sort of maintain maximal precision. Because we
// expect CFA to usually be monotonically more precise each time we run it to fixpoint, this
// would not be a productive optimization: it would make setting up a basic block more
// expensive and would only benefit bizarre pathological cases.
Operands<AbstractValue> intersectionOfPastValuesAtHead;
bool intersectionOfCFAHasVisited;
float executionCount;
// These fields are reserved for NaturalLoops.
static const unsigned numberOfInnerMostLoopIndices = 2;
unsigned innerMostLoopIndices[numberOfInnerMostLoopIndices];
struct SSAData {
WTF_MAKE_FAST_ALLOCATED;
public:
void invalidate()
{
liveAtTail.clear();
liveAtHead.clear();
valuesAtHead.clear();
valuesAtTail.clear();
}
AvailabilityMap availabilityAtHead;
AvailabilityMap availabilityAtTail;
Vector<NodeFlowProjection> liveAtHead;
Vector<NodeFlowProjection> liveAtTail;
Vector<NodeAbstractValuePair> valuesAtHead;
Vector<NodeAbstractValuePair> valuesAtTail;
SSAData(BasicBlock*);
~SSAData();
};
std::unique_ptr<SSAData> ssa;
private:
friend class InsertionSet;
BlockNodeList m_nodes;
};
typedef Vector<BasicBlock*, 5> BlockList;
struct UnlinkedBlock {
BasicBlock* m_block;
bool m_needsNormalLinking;
bool m_needsEarlyReturnLinking;
UnlinkedBlock() { }
explicit UnlinkedBlock(BasicBlock* block)
: m_block(block)
, m_needsNormalLinking(true)
, m_needsEarlyReturnLinking(false)
{
}
};
static inline unsigned getBytecodeBeginForBlock(BasicBlock** basicBlock)
{
return (*basicBlock)->bytecodeBegin;
}
static inline BasicBlock* blockForBytecodeOffset(Vector<BasicBlock*>& linkingTargets, unsigned bytecodeBegin)
{
return *binarySearch<BasicBlock*, unsigned>(linkingTargets, linkingTargets.size(), bytecodeBegin, getBytecodeBeginForBlock);
}
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)