|  | /* | 
|  | * Copyright (C) 2013-2020 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. | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "DFGSSAConversionPhase.h" | 
|  |  | 
|  | #if ENABLE(DFG_JIT) | 
|  |  | 
|  | #include "DFGBasicBlockInlines.h" | 
|  | #include "DFGBlockInsertionSet.h" | 
|  | #include "DFGGraph.h" | 
|  | #include "DFGInsertionSet.h" | 
|  | #include "DFGPhase.h" | 
|  | #include "DFGSSACalculator.h" | 
|  | #include "DFGVariableAccessDataDump.h" | 
|  | #include "JSCJSValueInlines.h" | 
|  | #include "OperandsInlines.h" | 
|  |  | 
|  | #undef RELEASE_ASSERT | 
|  | #define RELEASE_ASSERT(assertion) do { \ | 
|  | if (!(assertion)) { \ | 
|  | WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion); \ | 
|  | CRASH(); \ | 
|  | } \ | 
|  | } while (0) | 
|  |  | 
|  | namespace JSC { namespace DFG { | 
|  |  | 
|  | class SSAConversionPhase : public Phase { | 
|  | static constexpr bool verbose = false; | 
|  |  | 
|  | public: | 
|  | SSAConversionPhase(Graph& graph) | 
|  | : Phase(graph, "SSA conversion") | 
|  | , m_insertionSet(graph) | 
|  | { | 
|  | } | 
|  |  | 
|  | bool run() | 
|  | { | 
|  | RELEASE_ASSERT(m_graph.m_form == ThreadedCPS); | 
|  | RELEASE_ASSERT(!m_graph.m_isInSSAConversion); | 
|  | m_graph.m_isInSSAConversion = true; | 
|  |  | 
|  | m_graph.clearReplacements(); | 
|  | m_graph.clearCPSCFGData(); | 
|  |  | 
|  | HashMap<unsigned, BasicBlock*, WTF::IntHash<unsigned>, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> entrypointIndexToArgumentsBlock; | 
|  |  | 
|  | m_graph.m_numberOfEntrypoints = m_graph.m_roots.size(); | 
|  | m_graph.m_argumentFormats.resize(m_graph.m_numberOfEntrypoints); | 
|  |  | 
|  | for (unsigned entrypointIndex = 0; entrypointIndex < m_graph.m_numberOfEntrypoints; ++entrypointIndex) { | 
|  | BasicBlock* oldRoot = m_graph.m_roots[entrypointIndex]; | 
|  | entrypointIndexToArgumentsBlock.add(entrypointIndex, oldRoot); | 
|  |  | 
|  | NodeOrigin origin = oldRoot->at(0)->origin; | 
|  | m_insertionSet.insertNode( | 
|  | 0, SpecNone, InitializeEntrypointArguments, origin, OpInfo(entrypointIndex)); | 
|  | m_insertionSet.insertNode( | 
|  | 0, SpecNone, ExitOK, origin); | 
|  | m_insertionSet.execute(oldRoot); | 
|  | } | 
|  |  | 
|  | if (m_graph.m_numberOfEntrypoints > 1) { | 
|  | BlockInsertionSet blockInsertionSet(m_graph); | 
|  | BasicBlock* newRoot = blockInsertionSet.insert(0, 1.0f); | 
|  |  | 
|  | EntrySwitchData* entrySwitchData = m_graph.m_entrySwitchData.add(); | 
|  | for (unsigned entrypointIndex = 0; entrypointIndex < m_graph.m_numberOfEntrypoints; ++entrypointIndex) { | 
|  | BasicBlock* oldRoot = m_graph.m_roots[entrypointIndex]; | 
|  | entrySwitchData->cases.append(oldRoot); | 
|  |  | 
|  | ASSERT(oldRoot->predecessors.isEmpty()); | 
|  | oldRoot->predecessors.append(newRoot); | 
|  |  | 
|  | if (oldRoot->isCatchEntrypoint) { | 
|  | ASSERT(!!entrypointIndex); | 
|  | m_graph.m_entrypointIndexToCatchBytecodeIndex.add(entrypointIndex, oldRoot->bytecodeBegin); | 
|  | } | 
|  | } | 
|  |  | 
|  | RELEASE_ASSERT(entrySwitchData->cases[0] == m_graph.block(0)); // We strongly assume the normal call entrypoint is the first item in the list. | 
|  |  | 
|  | const bool exitOK = false; | 
|  | NodeOrigin origin { CodeOrigin(BytecodeIndex(0)), CodeOrigin(BytecodeIndex(0)), exitOK }; | 
|  | newRoot->appendNode( | 
|  | m_graph, SpecNone, EntrySwitch, origin, OpInfo(entrySwitchData)); | 
|  |  | 
|  | m_graph.m_roots.clear(); | 
|  | m_graph.m_roots.append(newRoot); | 
|  |  | 
|  | blockInsertionSet.execute(); | 
|  | } | 
|  |  | 
|  | SSACalculator calculator(m_graph); | 
|  |  | 
|  | m_graph.ensureSSADominators(); | 
|  |  | 
|  | 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()) | 
|  | continue; | 
|  |  | 
|  | SSACalculator::Variable* ssaVariable = calculator.newVariable(); | 
|  | ASSERT(ssaVariable->index() == m_variableForSSAIndex.size()); | 
|  | m_variableForSSAIndex.append(&variable); | 
|  | m_ssaVariableForVariable.add(&variable, ssaVariable); | 
|  | } | 
|  |  | 
|  | // Find all SetLocals and create Defs for them. We handle SetArgumentDefinitely by creating a | 
|  | // GetLocal, and recording the flush format. | 
|  | 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() != SetArgumentDefinitely) | 
|  | continue; | 
|  |  | 
|  | VariableAccessData* variable = node->variableAccessData(); | 
|  |  | 
|  | Node* childNode; | 
|  | if (node->op() == SetLocal) | 
|  | childNode = node->child1().node(); | 
|  | else { | 
|  | ASSERT(node->op() == SetArgumentDefinitely); | 
|  | childNode = m_insertionSet.insertNode( | 
|  | nodeIndex, node->variableAccessData()->prediction(), | 
|  | GetStack, node->origin, | 
|  | OpInfo(m_graph.m_stackAccessData.add(variable->operand(), variable->flushFormat()))); | 
|  | if (ASSERT_ENABLED) | 
|  | m_argumentGetters.add(childNode); | 
|  | m_argumentMapping.add(node, childNode); | 
|  | } | 
|  |  | 
|  | calculator.newDef( | 
|  | m_ssaVariableForVariable.get(variable), block, childNode); | 
|  | } | 
|  |  | 
|  | 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 based on where SSACalculator tells us to. | 
|  | 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->operand()); | 
|  | 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: ", calculator, "\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: | 
|  | // | 
|  | //   - MovHint has KillLocal prepended to it. | 
|  | // | 
|  | //   - 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 is removed. | 
|  | // | 
|  | //   - PhantomLocal becomes Phantom, and its child is whatever is specified by | 
|  | //     valueForOperand. | 
|  | // | 
|  | //   - SetArgumentDefinitely is removed. Note that GetStack nodes have already been inserted. | 
|  | // | 
|  | //   - SetArgumentMaybe is removed. It should not have any data flow uses. | 
|  | 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 = 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: ", valueForOperand.operandForIndex(i), " -> ", node, "\n"); | 
|  | valueForOperand[i] = node; | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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 : calculator.phisForBlock(block)) { | 
|  | VariableAccessData* variable = m_variableForSSAIndex[phiDef->variable()->index()]; | 
|  |  | 
|  | m_insertionSet.insert(phiInsertionPoint, phiDef->value()); | 
|  | valueForOperand.operand(variable->operand()) = phiDef->value(); | 
|  |  | 
|  | m_insertionSet.insertNode( | 
|  | phiInsertionPoint, SpecNone, MovHint, block->at(0)->origin.withInvalidExit(), | 
|  | OpInfo(variable->operand()), phiDef->value()->defaultEdge()); | 
|  | } | 
|  |  | 
|  | if (block->at(0)->origin.exitOK) | 
|  | m_insertionSet.insertNode(phiInsertionPoint, SpecNone, ExitOK, block->at(0)->origin); | 
|  |  | 
|  | 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->unlinkedOperand())); | 
|  | 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->operand(), variable->flushFormat())); | 
|  | } else | 
|  | node->remove(m_graph); | 
|  |  | 
|  | if (verbose) | 
|  | dataLog("Mapping: ", variable->operand(), " -> ", child, "\n"); | 
|  | valueForOperand.operand(variable->operand()) = child; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case GetStack: { | 
|  | ASSERT(m_argumentGetters.contains(node)); | 
|  | valueForOperand.operand(node->stackAccessData()->operand) = node; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case GetLocal: { | 
|  | VariableAccessData* variable = node->variableAccessData(); | 
|  | node->children.reset(); | 
|  |  | 
|  | node->remove(m_graph); | 
|  | if (verbose) | 
|  | dataLog("Replacing node ", node, " with ", valueForOperand.operand(variable->operand()), "\n"); | 
|  | node->setReplacement(valueForOperand.operand(variable->operand())); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Flush: { | 
|  | node->children.reset(); | 
|  | node->remove(m_graph); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case PhantomLocal: { | 
|  | ASSERT(node->child1().useKind() == UntypedUse); | 
|  | VariableAccessData* variable = node->variableAccessData(); | 
|  | node->child1() = valueForOperand.operand(variable->operand())->defaultEdge(); | 
|  | node->remove(m_graph); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case SetArgumentDefinitely: { | 
|  | node->remove(m_graph); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case SetArgumentMaybe: { | 
|  | node->remove(m_graph); | 
|  | 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 : 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); | 
|  |  | 
|  | dataLogLnIf(verbose, "Inserting Upsilon for ", variable->operand(), " propagating ", valueForOperand.operand(variable->operand()), " to ", phiNode); | 
|  |  | 
|  | m_insertionSet.insertNode( | 
|  | upsilonInsertionPoint, SpecNone, Upsilon, upsilonOrigin, | 
|  | OpInfo(phiNode), Edge( | 
|  | valueForOperand.operand(variable->operand()), | 
|  | useKind)); | 
|  | } | 
|  | } | 
|  |  | 
|  | m_insertionSet.execute(block); | 
|  | } | 
|  |  | 
|  | // Free all CPS phis and reset variables vectors. | 
|  | for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { | 
|  | BasicBlock* block = m_graph.block(blockIndex); | 
|  | if (!block) | 
|  | continue; | 
|  | for (unsigned phiIndex = block->phis.size(); phiIndex--;) | 
|  | m_graph.deleteNode(block->phis[phiIndex]); | 
|  | block->phis.clear(); | 
|  | block->variablesAtHead.clear(); | 
|  | block->variablesAtTail.clear(); | 
|  | block->valuesAtHead.clear(); | 
|  | block->valuesAtHead.clear(); | 
|  | block->ssa = makeUnique<BasicBlock::SSAData>(block); | 
|  | } | 
|  |  | 
|  | for (auto& pair : entrypointIndexToArgumentsBlock) { | 
|  | unsigned entrypointIndex = pair.key; | 
|  | BasicBlock* oldRoot = pair.value; | 
|  | ArgumentsVector& arguments = m_graph.m_rootToArguments.find(oldRoot)->value; | 
|  | Vector<FlushFormat> argumentFormats; | 
|  | argumentFormats.reserveInitialCapacity(arguments.size()); | 
|  | for (unsigned i = 0; i < arguments.size(); ++i) { | 
|  | Node* node = m_argumentMapping.get(arguments[i]); | 
|  | RELEASE_ASSERT(node); | 
|  | argumentFormats.uncheckedAppend(node->stackAccessData()->format); | 
|  | } | 
|  | m_graph.m_argumentFormats[entrypointIndex] = WTFMove(argumentFormats); | 
|  | } | 
|  |  | 
|  | m_graph.m_rootToArguments.clear(); | 
|  |  | 
|  | RELEASE_ASSERT(m_graph.m_isInSSAConversion); | 
|  | m_graph.m_isInSSAConversion = false; | 
|  |  | 
|  | m_graph.m_form = SSA; | 
|  |  | 
|  | if (verbose) { | 
|  | dataLog("Graph after SSA transformation:\n"); | 
|  | m_graph.dump(); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | InsertionSet m_insertionSet; | 
|  | HashMap<VariableAccessData*, SSACalculator::Variable*> m_ssaVariableForVariable; | 
|  | HashMap<Node*, Node*> m_argumentMapping; | 
|  | HashSet<Node*> m_argumentGetters; | 
|  | Vector<VariableAccessData*> m_variableForSSAIndex; | 
|  | }; | 
|  |  | 
|  | bool performSSAConversion(Graph& graph) | 
|  | { | 
|  | bool result = runPhase<SSAConversionPhase>(graph); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | } } // namespace JSC::DFG | 
|  |  | 
|  | #endif // ENABLE(DFG_JIT) | 
|  |  |