--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/EliminateRedundantInitializationPhase.java 2016-12-09 00:49:48.884175663 -0800 @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.hotspot.phases.aot; + +import static org.graalvm.compiler.nodes.ConstantNode.getConstantNodes; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map.Entry; + +import jdk.vm.ci.hotspot.HotSpotMetaspaceConstant; +import jdk.vm.ci.meta.JavaConstant; + +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.graph.iterators.NodeIterable; +import org.graalvm.compiler.hotspot.nodes.aot.InitializeKlassNode; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.cfg.Block; +import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; +import org.graalvm.compiler.phases.BasePhase; +import org.graalvm.compiler.phases.tiers.PhaseContext; + +public class EliminateRedundantInitializationPhase extends BasePhase { + /** + * Find blocks with class initializing nodes for the class identified the by the constant node. + * Return the map of a block to a list of initializing nodes in that block. + * + * @param cfg an instance of the {@link ControlFlowGraph}. + * @param constant common input to the instances of {@link InitializeKlassNode}. + * @return map of blocks to lists of initializing nodes. + */ + private static HashMap> findBlocksWithInitializers(ControlFlowGraph cfg, ConstantNode constant) { + // node is ConstantNode representing a metaspace constant (a klass reference). + // InitializeKlassNodes for the same class would share the same ConstantNode input. + NodeIterable initializers = constant.usages().filter(InitializeKlassNode.class); + // Map the found nodes to blocks + HashMap> blockToInits = new HashMap<>(); + for (Node i : initializers) { + Block b = cfg.blockFor(i); + ArrayList initsInBlock = blockToInits.get(b); + if (initsInBlock == null) { + initsInBlock = new ArrayList<>(); + } + initsInBlock.add(i); + blockToInits.put(b, initsInBlock); + } + return blockToInits; + } + + /** + * Process the block-to-initializers map and produce a list of blocks that contain more than one + * instance of {@link InitializeKlassNode}. + * + * @param blockToInits a map of blocks to lists of {@link InitializeKlassNode} instances. + * @return list of blocks that contain multiple instances of {@link InitializeKlassNode}. + */ + private static ArrayList findBlocksWithMultipleInitializers(HashMap> blockToInits) { + ArrayList result = new ArrayList<>(); + // Select the blocks from the blocksToInits map that have more than one InitializeKlassNode + for (Entry> e : blockToInits.entrySet()) { + if (e.getValue().size() > 1) { + result.add(e.getKey()); + } + } + return result; + } + + /** + * Iterate through blocks with multiple instances of {@link InitializeKlassNode} and identify + * redundant instances. Remove redundant instances from the block-to-list-of-initializer map. + * + * @param blockToInits a map of blocks to lists of {@link InitializeKlassNode} instances. + * @param blocksWithMultipleInits a list of blocks that contain multiple instances of + * {@link InitializeKlassNode}. + * @param constant common input to the instances of {@link InitializeKlassNode}. + * @return list of {@link InitializeKlassNode} instances that can be removed. + */ + private static ArrayList findRedundantLocalInitializers(HashMap> blockToInits, ArrayList blocksWithMultipleInits, ConstantNode constant) { + ArrayList result = new ArrayList<>(); + for (Block b : blocksWithMultipleInits) { + // First initializer for our constant in the block + InitializeKlassNode first = null; + for (Node n : b.getNodes()) { + if (n instanceof InitializeKlassNode) { + InitializeKlassNode i = (InitializeKlassNode) n; + if (i.value() == constant) { + if (first == null) { + // First instance of {@link InitializeKlassNode} stays. + first = i; + } else { + // All the following instances of {@link InitializeKlassNode} can be + // removed. + result.add(i); + } + } + } + } + assert first != null; + + // Replace the entry in the initsInBlock map to contain just a single initializer + ArrayList initsInBlock = new ArrayList<>(); + initsInBlock.add(first); + blockToInits.put(b, initsInBlock); + } + return result; + } + + /** + * Find cases when one {@link InitializeKlassNode} instance dominates another. The dominated + * instance can be removed. + * + * @param blockToInits a map of blocks to lists of {@link InitializeKlassNode} instances. + * @return list of {@link InitializeKlassNode} instances that can be removed. + */ + private static ArrayList findRedundantGlobalInitializers(HashMap> blockToInits) { + ArrayList result = new ArrayList<>(); + for (Entry> e : blockToInits.entrySet()) { + Block currentBlock = e.getKey(); + ArrayList nodesInCurrent = e.getValue(); + if (nodesInCurrent != null) { // if the list is null, the initializer has already been + // eliminated. + for (Block d : currentBlock.getDominated()) { + ArrayList nodesInDominated = blockToInits.get(d); + if (nodesInDominated != null) { // if the list is null, the initializer has + // already been eliminated. + assert nodesInDominated.size() == 1; + Node n = nodesInDominated.iterator().next(); + result.add(n); + blockToInits.put(d, null); + } + } + } + } + return result; + } + + /** + * Compute the list of redundant {@link InitializeKlassNode} instances that have the common + * {@link ConstantNode}. + * + * @param cfg an instance of the {@link ControlFlowGraph}. + * @param constant common input to the instances of {@link InitializeKlassNode}. + * @return list of {@link InitializeKlassNode} instances that can be removed. + */ + private static ArrayList processConstantNode(ControlFlowGraph cfg, ConstantNode constant) { + HashMap> blockToInits = findBlocksWithInitializers(cfg, constant); + ArrayList blocksWithMultipleInits = findBlocksWithMultipleInitializers(blockToInits); + ArrayList redundantInits = findRedundantLocalInitializers(blockToInits, blocksWithMultipleInits, constant); + // At this point each block has at most one initializer for this constant + if (blockToInits.size() > 1) { + redundantInits.addAll(findRedundantGlobalInitializers(blockToInits)); + } + return redundantInits; + } + + /** + * Find each {@link Invoke} that has a corresponding {@link InitializeKlassNode}. These + * {@link InitializeKlassNode} are redundant and are removed. + * + */ + private static void removeInitsAtStaticCalls(StructuredGraph graph) { + for (Invoke invoke : graph.getInvokes()) { + if (invoke.classInit() != null) { + Node classInit = invoke.classInit(); + classInit.replaceAtUsages(null); + graph.removeFixed((FixedWithNextNode) classInit); + } + } + } + + /** + * Find {@link InitializeKlassNode} instances that can be removed because there is an existing + * dominating initialization. + * + * @param graph the program graph. + */ + private static void removeRedundantInits(StructuredGraph graph) { + // Create cfg, we need blocks and dominators. + ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, false, true, false); + ArrayList redundantInits = new ArrayList<>(); + for (ConstantNode node : getConstantNodes(graph)) { + JavaConstant constant = node.asJavaConstant(); + if (constant instanceof HotSpotMetaspaceConstant) { + redundantInits.addAll(processConstantNode(cfg, node)); + } + } + // Remove redundant instances of {@link InitializeKlassNode} from the graph. + for (Node n : redundantInits) { + graph.removeFixed((FixedWithNextNode) n); + } + } + + @Override + protected void run(StructuredGraph graph, PhaseContext context) { + removeInitsAtStaticCalls(graph); + removeRedundantInits(graph); + } +}