diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp | 609 |
1 files changed, 330 insertions, 279 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp b/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp index de100687b..57fc09529 100644 --- a/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,9 +32,7 @@ #include "DFGGraph.h" #include "DFGInsertionSet.h" #include "DFGPhase.h" -#include "DFGSSACalculator.h" -#include "DFGVariableAccessDataDump.h" -#include "JSCInlines.h" +#include "Operations.h" namespace JSC { namespace DFG { @@ -44,8 +42,8 @@ class SSAConversionPhase : public Phase { public: SSAConversionPhase(Graph& graph) : Phase(graph, "SSA conversion") - , m_calculator(graph) , m_insertionSet(graph) + , m_changed(false) { } @@ -53,318 +51,315 @@ public: { RELEASE_ASSERT(m_graph.m_form == ThreadedCPS); - m_graph.clearReplacements(); - m_graph.ensureDominators(); - - if (verbose) { - dataLog("Graph before SSA transformation:\n"); - m_graph.dump(); - } - - // Create a SSACalculator::Variable for every root VariableAccessData. - for (VariableAccessData& variable : m_graph.m_variableAccessData) { - if (!variable.isRoot()) + // Figure out which SetLocal's need flushing. Need to do this while the + // Phi graph is still intact. + for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { + BasicBlock* block = m_graph.block(blockIndex); + if (!block) continue; - - SSACalculator::Variable* ssaVariable = m_calculator.newVariable(); - ASSERT(ssaVariable->index() == m_variableForSSAIndex.size()); - m_variableForSSAIndex.append(&variable); - m_ssaVariableForVariable.add(&variable, ssaVariable); + for (unsigned nodeIndex = block->size(); nodeIndex--;) { + Node* node = block->at(nodeIndex); + if (node->op() != Flush) + continue; + addFlushedLocalOp(node); + } + } + while (!m_flushedLocalOpWorklist.isEmpty()) { + Node* node = m_flushedLocalOpWorklist.takeLast(); + ASSERT(m_flushedLocalOps.contains(node)); + DFG_NODE_DO_TO_CHILDREN(m_graph, node, addFlushedLocalEdge); } - // Find all SetLocals and create Defs for them. We handle SetArgument by creating a - // GetLocal, and recording the flush format. + // Eliminate all duplicate or self-pointing Phi edges. This means that + // we transform: + // + // p: Phi(@n1, @n2, @n3) + // + // into: + // + // p: Phi(@x) + // + // if each @ni in {@n1, @n2, @n3} is either equal to @p to is equal + // to @x, for exactly one other @x. Additionally, trivial Phis (i.e. + // p: Phi(@x)) are forwarded, so that if have an edge to such @p, we + // replace it with @x. This loop does this for Phis only; later we do + // such forwarding for Phi references found in other nodes. + // + // See Aycock and Horspool in CC'00 for a better description of what + // we're doing here. + do { + m_changed = false; + for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { + BasicBlock* block = m_graph.block(blockIndex); + if (!block) + continue; + for (unsigned phiIndex = block->phis.size(); phiIndex--;) { + Node* phi = block->phis[phiIndex]; + if (phi->variableAccessData()->isCaptured()) + continue; + forwardPhiChildren(phi); + deduplicateChildren(phi); + } + } + } while (m_changed); + + // For each basic block, for each local live at the head of that block, + // figure out what node we should be referring to instead of that local. + // If it turns out to be a non-trivial Phi, make sure that we create an + // SSA Phi and Upsilons in predecessor blocks. We reuse + // BasicBlock::variablesAtHead for tracking which nodes to refer to. for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; - // Must process the block in forward direction because we want to see the last - // assignment for every local. - for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) { - Node* node = block->at(nodeIndex); - if (node->op() != SetLocal && node->op() != SetArgument) + for (unsigned i = block->variablesAtHead.size(); i--;) { + Node* node = block->variablesAtHead[i]; + if (!node) continue; VariableAccessData* variable = node->variableAccessData(); + if (variable->isCaptured()) { + // Poison this entry in variablesAtHead because we don't + // want anyone to try to refer to it, if the variable is + // captured. + block->variablesAtHead[i] = 0; + continue; + } + + switch (node->op()) { + case Phi: + case SetArgument: + break; + case Flush: + case GetLocal: + case PhantomLocal: + node = node->child1().node(); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + RELEASE_ASSERT(node->op() == Phi || node->op() == SetArgument); - Node* childNode; - if (node->op() == SetLocal) - childNode = node->child1().node(); - else { - ASSERT(node->op() == SetArgument); - childNode = m_insertionSet.insertNode( - nodeIndex, node->variableAccessData()->prediction(), - GetStack, node->origin, - OpInfo(m_graph.m_stackAccessData.add(variable->local(), variable->flushFormat()))); - if (!ASSERT_DISABLED) - m_argumentGetters.add(childNode); - m_argumentMapping.add(node, childNode); + bool isFlushed = m_flushedLocalOps.contains(node); + + if (node->op() == Phi) { + Edge edge = node->children.justOneChild(); + if (edge) + node = edge.node(); // It's something from a different basic block. + else { + // It's a non-trivial Phi. + FlushFormat format = variable->flushFormat(); + NodeFlags result = resultFor(format); + UseKind useKind = useKindFor(format); + + node = m_insertionSet.insertNode(0, SpecNone, Phi, CodeOrigin()); + node->mergeFlags(result); + RELEASE_ASSERT((node->flags() & NodeResultMask) == result); + + for (unsigned j = block->predecessors.size(); j--;) { + BasicBlock* predecessor = block->predecessors[j]; + predecessor->appendNonTerminal( + m_graph, SpecNone, Upsilon, predecessor->last()->codeOrigin, + OpInfo(node), Edge(predecessor->variablesAtTail[i], useKind)); + } + + if (isFlushed) { + // Do nothing. For multiple reasons. + + // Reason #1: If the local is flushed then we don't need to bother + // with a MovHint since every path to this point in the code will + // have flushed the bytecode variable using a SetLocal and hence + // the Availability::flushedAt() will agree, and that will be + // sufficient for figuring out how to recover the variable's value. + + // Reason #2: If we had inserted a MovHint and the Phi function had + // died (because the only user of the value was the "flush" - i.e. + // some asynchronous runtime thingy) then the MovHint would turn + // into a ZombieHint, which would fool us into thinking that the + // variable is dead. + + // Reason #3: If we had inserted a MovHint then even if the Phi + // stayed alive, we would still end up generating inefficient code + // since we would be telling the OSR exit compiler to use some SSA + // value for the bytecode variable rather than just telling it that + // the value was already on the stack. + } else { + m_insertionSet.insertNode( + 0, SpecNone, MovHint, CodeOrigin(), + OpInfo(variable->local().offset()), Edge(node)); + } + } } - m_calculator.newDef( - m_ssaVariableForVariable.get(variable), block, childNode); + block->variablesAtHead[i] = node; } - + m_insertionSet.execute(block); } - // Decide where Phis are to be inserted. This creates the Phi's but doesn't insert them - // yet. We will later know where to insert them because SSACalculator is such a bro. - m_calculator.computePhis( - [&] (SSACalculator::Variable* ssaVariable, BasicBlock* block) -> Node* { - VariableAccessData* variable = m_variableForSSAIndex[ssaVariable->index()]; - - // Prune by liveness. This doesn't buy us much other than compile times. - Node* headNode = block->variablesAtHead.operand(variable->local()); - if (!headNode) - return nullptr; - - // There is the possibiltiy of "rebirths". The SSA calculator will already prune - // rebirths for the same VariableAccessData. But it will not be able to prune - // rebirths that arose from the same local variable number but a different - // VariableAccessData. We do that pruning here. - // - // Here's an example of a rebirth that this would catch: - // - // var x; - // if (foo) { - // if (bar) { - // x = 42; - // } else { - // x = 43; - // } - // print(x); - // x = 44; - // } else { - // x = 45; - // } - // print(x); // Without this check, we'd have a Phi for x = 42|43 here. - // - // FIXME: Consider feeding local variable numbers, not VariableAccessData*'s, as - // the "variables" for SSACalculator. That would allow us to eliminate this - // special case. - // https://bugs.webkit.org/show_bug.cgi?id=136641 - if (headNode->variableAccessData() != variable) - return nullptr; - - Node* phiNode = m_graph.addNode( - variable->prediction(), Phi, block->at(0)->origin.withInvalidExit()); - FlushFormat format = variable->flushFormat(); - NodeFlags result = resultFor(format); - phiNode->mergeFlags(result); - return phiNode; - }); - if (verbose) { - dataLog("Computed Phis, about to transform the graph.\n"); - dataLog("\n"); - dataLog("Graph:\n"); - m_graph.dump(); - dataLog("\n"); - dataLog("Mappings:\n"); - for (unsigned i = 0; i < m_variableForSSAIndex.size(); ++i) - dataLog(" ", i, ": ", VariableAccessDataDump(m_graph, m_variableForSSAIndex[i]), "\n"); - dataLog("\n"); - dataLog("SSA calculator: ", m_calculator, "\n"); + dataLog("Variables at head after SSA Phi insertion:\n"); + for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { + BasicBlock* block = m_graph.block(blockIndex); + if (!block) + continue; + dataLog(" ", *block, ": ", block->variablesAtHead, "\n"); + } } - // Do the bulk of the SSA conversion. For each block, this tracks the operand->Node - // mapping based on a combination of what the SSACalculator tells us, and us walking over - // the block in forward order. We use our own data structure, valueForOperand, for - // determining the local mapping, but we rely on SSACalculator for the non-local mapping. - // - // This does three things at once: - // - // - Inserts the Phis in all of the places where they need to go. We've already created - // them and they are accounted for in the SSACalculator's data structures, but we - // haven't inserted them yet, mostly because we want to insert all of a block's Phis in - // one go to amortize the cost of node insertion. - // - // - Create and insert Upsilons. - // - // - Convert all of the preexisting SSA nodes (other than the old CPS Phi nodes) into SSA - // form by replacing as follows: + // At this point variablesAtHead in each block refers to either: // - // - MovHint has KillLocal prepended to it. + // 1) A new SSA phi in the current block. + // 2) A SetArgument, which will soon get converted into a GetArgument. + // 3) An old CPS phi in a different block. // - // - GetLocal die and get replaced with references to the node specified by - // valueForOperand. - // - // - SetLocal turns into PutStack if it's flushed, or turns into a Check otherwise. - // - // - Flush loses its children and turns into a Phantom. - // - // - PhantomLocal becomes Phantom, and its child is whatever is specified by - // valueForOperand. - // - // - SetArgument is removed. Note that GetStack nodes have already been inserted. - Operands<Node*> valueForOperand(OperandsLike, m_graph.block(0)->variablesAtHead); - for (BasicBlock* block : m_graph.blocksInPreOrder()) { - valueForOperand.clear(); - - // CPS will claim that the root block has all arguments live. But we have already done - // the first step of SSA conversion: argument locals are no longer live at head; - // instead we have GetStack nodes for extracting the values of arguments. So, we - // skip the at-head available value calculation for the root block. - if (block != m_graph.block(0)) { - for (size_t i = valueForOperand.size(); i--;) { - Node* nodeAtHead = block->variablesAtHead[i]; - if (!nodeAtHead) - continue; - - VariableAccessData* variable = nodeAtHead->variableAccessData(); - - if (verbose) - dataLog("Considering live variable ", VariableAccessDataDump(m_graph, variable), " at head of block ", *block, "\n"); - - SSACalculator::Variable* ssaVariable = m_ssaVariableForVariable.get(variable); - SSACalculator::Def* def = m_calculator.reachingDefAtHead(block, ssaVariable); - if (!def) { - // If we are required to insert a Phi, then we won't have a reaching def - // at head. - continue; - } - - Node* node = def->value(); - if (node->replacement()) { - // This will occur when a SetLocal had a GetLocal as its source. The - // GetLocal would get replaced with an actual SSA value by the time we get - // here. Note that the SSA value with which the GetLocal got replaced - // would not in turn have a replacement. - node = node->replacement(); - ASSERT(!node->replacement()); - } - if (verbose) - dataLog("Mapping: ", VirtualRegister(valueForOperand.operandForIndex(i)), " -> ", node, "\n"); - valueForOperand[i] = node; + // We don't have to do anything for (1) and (2), but we do need to + // do a replacement for (3). + + // Clear all replacements, since other phases may have used them. + m_graph.clearReplacements(); + + // For all of the old CPS Phis, figure out what they correspond to in SSA. + for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { + BasicBlock* block = m_graph.block(blockIndex); + if (!block) + continue; + for (unsigned phiIndex = block->phis.size(); phiIndex--;) { + Node* phi = block->phis[phiIndex]; + if (verbose) { + dataLog( + "Considering ", phi, ", for r", phi->local(), + ", and its replacement in ", *block, ", ", + block->variablesAtHead.operand(phi->local()), "\n"); } + phi->misc.replacement = block->variablesAtHead.operand(phi->local()); + } + } + + // Now make sure that all variablesAtHead in each block points to the + // canonical SSA value. Prior to this, variablesAtHead[local] may point to + // an old CPS Phi in a different block. + for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { + BasicBlock* block = m_graph.block(blockIndex); + if (!block) + continue; + for (size_t i = block->variablesAtHead.size(); i--;) { + Node* node = block->variablesAtHead[i]; + if (!node) + continue; + while (node->misc.replacement) + node = node->misc.replacement; + block->variablesAtHead[i] = node; + } + } + + if (verbose) { + dataLog("Variables at head after convergence:\n"); + for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { + BasicBlock* block = m_graph.block(blockIndex); + if (!block) + continue; + dataLog(" ", *block, ": ", block->variablesAtHead, "\n"); } + } + + // Convert operations over locals into operations over SSA nodes. + // - GetLocal over captured variables lose their phis. + // - GetLocal over uncaptured variables die and get replaced with references + // to the node specified by variablesAtHead. + // - SetLocal gets NodeMustGenerate if it's flushed, or turns into a + // Check otherwise. + // - Flush loses its children but remains, because we want to know when a + // flushed SetLocal's value is no longer needed. This also makes it simpler + // to reason about the format of a local, since we can just do a backwards + // analysis (see FlushLivenessAnalysisPhase). As part of the backwards + // analysis, we say that the type of a local can be either int32, double, + // value, or dead. + // - PhantomLocal becomes Phantom, and its child is whatever is specified + // by variablesAtHead. + // - SetArgument turns into GetArgument unless it's a captured variable. + // - Upsilons get their children fixed to refer to the true value of that local + // at the end of the block. Prior to this loop, Upsilons will refer to + // variableAtTail[operand], which may be any of Flush, PhantomLocal, GetLocal, + // SetLocal, SetArgument, or Phi. We accomplish this by setting the + // replacement pointers of all of those nodes to refer to either + // variablesAtHead[operand], or the child of the SetLocal. + for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { + BasicBlock* block = m_graph.block(blockIndex); + if (!block) + continue; - // Insert Phis by asking the calculator what phis there are in this block. Also update - // valueForOperand with those Phis. For Phis associated with variables that are not - // flushed, we also insert a MovHint. - size_t phiInsertionPoint = 0; - for (SSACalculator::Def* phiDef : m_calculator.phisForBlock(block)) { - VariableAccessData* variable = m_variableForSSAIndex[phiDef->variable()->index()]; - - m_insertionSet.insert(phiInsertionPoint, phiDef->value()); - valueForOperand.operand(variable->local()) = phiDef->value(); - - m_insertionSet.insertNode( - phiInsertionPoint, SpecNone, MovHint, block->at(0)->origin.withInvalidExit(), - OpInfo(variable->local().offset()), phiDef->value()->defaultEdge()); + for (unsigned phiIndex = block->phis.size(); phiIndex--;) { + block->phis[phiIndex]->misc.replacement = + block->variablesAtHead.operand(block->phis[phiIndex]->local()); } - - if (block->at(0)->origin.exitOK) - m_insertionSet.insertNode(phiInsertionPoint, SpecNone, ExitOK, block->at(0)->origin); + for (unsigned nodeIndex = block->size(); nodeIndex--;) + ASSERT(!block->at(nodeIndex)->misc.replacement); for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) { Node* node = block->at(nodeIndex); - if (verbose) { - dataLog("Processing node ", node, ":\n"); - m_graph.dump(WTF::dataFile(), " ", node); - } - m_graph.performSubstitution(node); switch (node->op()) { - case MovHint: { - m_insertionSet.insertNode( - nodeIndex, SpecNone, KillStack, node->origin, - OpInfo(node->unlinkedLocal().offset())); - node->origin.exitOK = false; // KillStack clobbers exit. - break; - } - case SetLocal: { VariableAccessData* variable = node->variableAccessData(); - Node* child = node->child1().node(); - - if (!!(node->flags() & NodeIsFlushed)) { - node->convertToPutStack( - m_graph.m_stackAccessData.add( - variable->local(), variable->flushFormat())); - } else - node->remove(); - - if (verbose) - dataLog("Mapping: ", variable->local(), " -> ", child, "\n"); - valueForOperand.operand(variable->local()) = child; - break; - } - - case GetStack: { - ASSERT(m_argumentGetters.contains(node)); - valueForOperand.operand(node->stackAccessData()->local) = node; + if (variable->isCaptured() || m_flushedLocalOps.contains(node)) + node->mergeFlags(NodeMustGenerate); + else + node->setOpAndDefaultFlags(Check); + node->misc.replacement = node->child1().node(); // Only for Upsilons. break; } case GetLocal: { - VariableAccessData* variable = node->variableAccessData(); + // It seems tempting to just do forwardPhi(GetLocal), except that we + // could have created a new (SSA) Phi, and the GetLocal could still be + // referring to an old (CPS) Phi. Uses variablesAtHead to tell us what + // to refer to. node->children.reset(); - - node->remove(); - if (verbose) - dataLog("Replacing node ", node, " with ", valueForOperand.operand(variable->local()), "\n"); - node->setReplacement(valueForOperand.operand(variable->local())); + VariableAccessData* variable = node->variableAccessData(); + if (variable->isCaptured()) + break; + node->convertToPhantom(); + node->misc.replacement = block->variablesAtHead.operand(variable->local()); break; } case Flush: { node->children.reset(); - node->remove(); + // This is only for Upsilons. An Upsilon will only refer to a Flush if + // there were no SetLocals or GetLocals in the block. + node->misc.replacement = block->variablesAtHead.operand(node->local()); break; } case PhantomLocal: { - ASSERT(node->child1().useKind() == UntypedUse); VariableAccessData* variable = node->variableAccessData(); - node->child1() = valueForOperand.operand(variable->local())->defaultEdge(); - node->remove(); + if (variable->isCaptured()) + break; + node->child1().setNode(block->variablesAtHead.operand(variable->local())); + node->convertToPhantom(); + // This is only for Upsilons. An Upsilon will only refer to a + // PhantomLocal if there were no SetLocals or GetLocals in the block. + node->misc.replacement = block->variablesAtHead.operand(variable->local()); break; } case SetArgument: { - node->remove(); + VariableAccessData* variable = node->variableAccessData(); + if (variable->isCaptured()) + break; + node->setOpAndDefaultFlags(GetArgument); + node->mergeFlags(resultFor(node->variableAccessData()->flushFormat())); break; } - + default: break; } } - - // We want to insert Upsilons just before the end of the block. On the surface this - // seems dangerous because the Upsilon will have a checking UseKind. But, we will not - // actually be performing the check at the point of the Upsilon; the check will - // already have been performed at the point where the original SetLocal was. - NodeAndIndex terminal = block->findTerminal(); - size_t upsilonInsertionPoint = terminal.index; - NodeOrigin upsilonOrigin = terminal.node->origin; - for (unsigned successorIndex = block->numSuccessors(); successorIndex--;) { - BasicBlock* successorBlock = block->successor(successorIndex); - for (SSACalculator::Def* phiDef : m_calculator.phisForBlock(successorBlock)) { - Node* phiNode = phiDef->value(); - SSACalculator::Variable* ssaVariable = phiDef->variable(); - VariableAccessData* variable = m_variableForSSAIndex[ssaVariable->index()]; - FlushFormat format = variable->flushFormat(); - - // We can use an unchecked use kind because the SetLocal was turned into a Check. - // We have to use an unchecked use because at least sometimes, the end of the block - // is not exitOK. - UseKind useKind = uncheckedUseKindFor(format); - - m_insertionSet.insertNode( - upsilonInsertionPoint, SpecNone, Upsilon, upsilonOrigin, - OpInfo(phiNode), Edge( - valueForOperand.operand(variable->local()), - useKind)); - } - } - - m_insertionSet.execute(block); } // Free all CPS phis and reset variables vectors. @@ -379,39 +374,95 @@ public: block->variablesAtTail.clear(); block->valuesAtHead.clear(); block->valuesAtHead.clear(); - block->ssa = std::make_unique<BasicBlock::SSAData>(block); + block->ssa = adoptPtr(new BasicBlock::SSAData(block)); } - m_graph.m_argumentFormats.resize(m_graph.m_arguments.size()); - for (unsigned i = m_graph.m_arguments.size(); i--;) { - FlushFormat format = FlushedJSValue; - - Node* node = m_argumentMapping.get(m_graph.m_arguments[i]); - - RELEASE_ASSERT(node); - format = node->stackAccessData()->format; - - m_graph.m_argumentFormats[i] = format; - m_graph.m_arguments[i] = node; // Record the load that loads the arguments for the benefit of exit profiling. - } + m_graph.m_arguments.clear(); m_graph.m_form = SSA; + return true; + } - if (verbose) { - dataLog("Graph after SSA transformation:\n"); - m_graph.dump(); +private: + void forwardPhiChildren(Node* node) + { + for (unsigned i = 0; i < AdjacencyList::Size; ++i) { + Edge& edge = node->children.child(i); + if (!edge) + break; + m_changed |= forwardPhiEdge(edge); } - + } + + Node* forwardPhi(Node* node) + { + for (;;) { + switch (node->op()) { + case Phi: { + Edge edge = node->children.justOneChild(); + if (!edge) + return node; + node = edge.node(); + break; + } + case GetLocal: + case SetLocal: + if (node->variableAccessData()->isCaptured()) + return node; + node = node->child1().node(); + break; + default: + return node; + } + } + } + + bool forwardPhiEdge(Edge& edge) + { + Node* newNode = forwardPhi(edge.node()); + if (newNode == edge.node()) + return false; + edge.setNode(newNode); return true; } + + void deduplicateChildren(Node* node) + { + for (unsigned i = 0; i < AdjacencyList::Size; ++i) { + Edge edge = node->children.child(i); + if (!edge) + break; + if (edge == node) { + node->children.removeEdge(i--); + m_changed = true; + continue; + } + for (unsigned j = i + 1; j < AdjacencyList::Size; ++j) { + if (node->children.child(j) == edge) { + node->children.removeEdge(j--); + m_changed = true; + } + } + } + } + + void addFlushedLocalOp(Node* node) + { + if (m_flushedLocalOps.contains(node)) + return; + m_flushedLocalOps.add(node); + m_flushedLocalOpWorklist.append(node); + } -private: - SSACalculator m_calculator; + void addFlushedLocalEdge(Node*, Edge edge) + { + addFlushedLocalOp(edge.node()); + } + InsertionSet m_insertionSet; - HashMap<VariableAccessData*, SSACalculator::Variable*> m_ssaVariableForVariable; - HashMap<Node*, Node*> m_argumentMapping; - HashSet<Node*> m_argumentGetters; - Vector<VariableAccessData*> m_variableForSSAIndex; + HashSet<Node*> m_flushedLocalOps; + Vector<Node*> m_flushedLocalOpWorklist; + bool m_changed; }; bool performSSAConversion(Graph& graph) |