DFGValueRepReductionPhase.cpp [plain text]
#include "config.h"
#include "DFGValueRepReductionPhase.h"
#if ENABLE(DFG_JIT)
#include "DFGGraph.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
#include "DFGPhiChildren.h"
#include "JSCJSValueInlines.h"
namespace JSC { namespace DFG {
class ValueRepReductionPhase : public Phase {
static constexpr bool verbose = false;
public:
ValueRepReductionPhase(Graph& graph)
: Phase(graph, "ValueRep reduction")
, m_insertionSet(graph)
{
}
bool run()
{
ASSERT(m_graph.m_form == SSA);
return convertValueRepsToDouble();
}
private:
bool convertValueRepsToDouble()
{
HashSet<Node*> candidates;
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
for (Node* node : *block) {
if (node->op() == ValueRep && node->child1().useKind() == DoubleRepUse)
candidates.add(node);
}
}
if (!candidates.size())
return false;
HashMap<Node*, Vector<Node*>> usersOf;
auto getUsersOf = [&] (Node* candidate) {
auto iter = usersOf.find(candidate);
RELEASE_ASSERT(iter != usersOf.end());
return iter->value;
};
for (BasicBlock* block : m_graph.blocksInPreOrder()) {
for (Node* node : *block) {
if (node->op() == Phi || (node->op() == ValueRep && candidates.contains(node)))
usersOf.add(node, Vector<Node*>());
Vector<Node*, 3> alreadyAdded;
m_graph.doToChildren(node, [&] (Edge edge) {
Node* candidate = edge.node();
if (alreadyAdded.contains(candidate))
return;
auto iter = usersOf.find(candidate);
if (iter == usersOf.end())
return;
iter->value.append(node);
alreadyAdded.append(candidate);
});
}
}
PhiChildren phiChildren(m_graph);
do {
HashSet<Node*> eligiblePhis;
for (Node* candidate : candidates) {
if (candidate->op() == Phi) {
phiChildren.forAllIncomingValues(candidate, [&] (Node* incoming) {
if (incoming->op() == Phi)
eligiblePhis.add(incoming);
});
}
for (Node* user : getUsersOf(candidate)) {
if (user->op() == Upsilon)
eligiblePhis.add(user->phi());
}
}
bool sawNewCandidate = false;
for (Node* phi : eligiblePhis)
sawNewCandidate |= candidates.add(phi).isNewEntry;
if (!sawNewCandidate)
break;
} while (true);
do {
HashSet<Node*> toRemove;
auto isEscaped = [&] (Node* node) {
return !candidates.contains(node) || toRemove.contains(node);
};
for (Node* candidate : candidates) {
bool ok = true;
auto dumpEscape = [&] (const char* description, Node* node) {
if (!verbose)
return;
dataLogLn(description);
dataLog(" candidate: ");
m_graph.dump(WTF::dataFile(), Prefix::noString, candidate);
dataLog(" reason: ");
m_graph.dump(WTF::dataFile(), Prefix::noString, node);
dataLogLn();
};
if (candidate->op() == Phi) {
phiChildren.forAllIncomingValues(candidate, [&] (Node* node) {
if (node->op() == JSConstant) {
if (!node->asJSValue().isNumber()) {
ok = false;
dumpEscape("Phi Incoming JSConstant not a number: ", node);
}
} else if (node->op() == ValueRep) {
if (node->child1().useKind() != DoubleRepUse) {
ok = false;
dumpEscape("Phi Incoming ValueRep not DoubleRepUse: ", node);
}
} else if (node->op() == Phi) {
if (isEscaped(node)) {
ok = false;
dumpEscape("An incoming Phi to another Phi is escaped: ", node);
}
} else {
ok = false;
dumpEscape("Unsupported incoming value to Phi: ", node);
}
});
if (!ok) {
toRemove.add(candidate);
continue;
}
}
for (Node* user : getUsersOf(candidate)) {
switch (user->op()) {
case DoubleRep:
if (user->child1().useKind() != RealNumberUse) {
ok = false;
dumpEscape("DoubleRep escape: ", user);
}
break;
case PutHint:
case MovHint:
break;
case Upsilon: {
Node* phi = user->phi();
if (isEscaped(phi)) {
dumpEscape("Upsilon of escaped phi escapes candidate: ", phi);
ok = false;
}
break;
}
default:
dumpEscape("Normal escape: ", user);
ok = false;
break;
}
if (!ok)
break;
}
if (!ok)
toRemove.add(candidate);
}
if (toRemove.isEmpty())
break;
for (Node* node : toRemove)
candidates.remove(node);
} while (true);
if (!candidates.size())
return false;
NodeOrigin originForConstant = m_graph.block(0)->at(0)->origin;
HashSet<Node*> doubleRepRealCheckLocations;
for (Node* candidate : candidates) {
if (verbose)
dataLogLn("Optimized: ", candidate);
Node* resultNode;
if (candidate->op() == Phi) {
resultNode = candidate;
for (Node* upsilon : phiChildren.upsilonsOf(candidate)) {
Node* incomingValue = upsilon->child1().node();
Node* newChild;
if (incomingValue->op() == JSConstant) {
double number = incomingValue->asJSValue().asNumber();
newChild = m_insertionSet.insertConstant(0, originForConstant, jsDoubleNumber(number), DoubleConstant);
} else if (incomingValue->op() == ValueRep) {
ASSERT(incomingValue->child1().useKind() == DoubleRepUse);
newChild = incomingValue->child1().node();
} else if (incomingValue->op() == Phi)
newChild = incomingValue;
else
RELEASE_ASSERT_NOT_REACHED();
upsilon->child1() = Edge(newChild, DoubleRepUse);
}
candidate->setResult(NodeResultDouble);
} else if (candidate->op() == ValueRep)
resultNode = candidate->child1().node();
else
RELEASE_ASSERT_NOT_REACHED();
for (Node* user : getUsersOf(candidate)) {
switch (user->op()) {
case DoubleRep: {
ASSERT(user->child1().useKind() == RealNumberUse);
user->convertToIdentityOn(resultNode);
doubleRepRealCheckLocations.add(user);
break;
}
case PutHint:
user->child2() = Edge(resultNode, DoubleRepUse);
break;
case MovHint:
user->child1() = Edge(resultNode, DoubleRepUse);
break;
case Upsilon: {
Node* phi = user->phi();
ASSERT_UNUSED(phi, candidates.contains(phi));
break;
}
default:
RELEASE_ASSERT_NOT_REACHED();
break;
}
}
}
m_insertionSet.execute(m_graph.block(0));
if (doubleRepRealCheckLocations.size()) {
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
for (unsigned i = 0; i < block->size(); ++i) {
Node* node = block->at(i);
if (node->op() != Identity) {
ASSERT(!doubleRepRealCheckLocations.contains(node));
continue;
}
if (!doubleRepRealCheckLocations.contains(node))
continue;
m_insertionSet.insertNode(
i, SpecNone, Check, node->origin,
Edge(node->child1().node(), DoubleRepRealUse));
}
m_insertionSet.execute(block);
}
}
return true;
}
InsertionSet m_insertionSet;
};
bool performValueRepReduction(Graph& graph)
{
return runPhase<ValueRepReductionPhase>(graph);
}
} }
#endif // ENABLE(DFG_JIT)