1 /*
   2  * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.lir.gen;
  24 
  25 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
  26 import static jdk.vm.ci.code.ValueUtil.isIllegal;
  27 import static jdk.vm.ci.code.ValueUtil.isLegal;
  28 import static jdk.vm.ci.meta.Value.ILLEGAL;
  29 
  30 import java.util.ArrayList;
  31 import java.util.HashMap;
  32 import java.util.List;
  33 
  34 import org.graalvm.compiler.core.common.CollectionsFactory;
  35 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
  36 import org.graalvm.compiler.lir.LIRInsertionBuffer;
  37 import org.graalvm.compiler.lir.LIRInstruction;
  38 import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory;
  39 
  40 import jdk.vm.ci.meta.AllocatableValue;
  41 import jdk.vm.ci.meta.Value;
  42 
  43 /**
  44  * Converts phi instructions into moves.
  45  *
  46  * Resolves cycles:
  47  *
  48  * <pre>
  49  *
  50  *  r1 := r2  becomes  temp := r1
  51  *  r2 := r1           r1 := r2
  52  *                     r2 := temp
  53  * </pre>
  54  *
  55  * and orders moves:
  56  *
  57  * <pre>
  58  *  r2 := r3  becomes  r1 := r2
  59  *  r1 := r2           r2 := r3
  60  * </pre>
  61  */
  62 public class PhiResolver {
  63 
  64     /**
  65      * Tracks a data flow dependency between a source operand and any number of the destination
  66      * operands.
  67      */
  68     static class PhiResolverNode {
  69 
  70         /**
  71          * A source operand whose value flows into the {@linkplain #destinations destination}
  72          * operands.
  73          */
  74         final Value operand;
  75 
  76         /**
  77          * The operands whose values are defined by the {@linkplain #operand source} operand.
  78          */
  79         final ArrayList<PhiResolverNode> destinations;
  80 
  81         /**
  82          * Denotes if a move instruction has already been emitted to initialize the value of
  83          * {@link #operand}.
  84          */
  85         boolean assigned;
  86 
  87         /**
  88          * Specifies if this operand been visited for the purpose of emitting a move instruction.
  89          */
  90         boolean visited;
  91 
  92         /**
  93          * Specifies if this is the initial definition in data flow path for a given value.
  94          */
  95         boolean startNode;
  96 
  97         PhiResolverNode(Value operand) {
  98             this.operand = operand;
  99             destinations = new ArrayList<>(4);
 100         }
 101 
 102         @Override
 103         public String toString() {
 104             StringBuilder buf = new StringBuilder(operand.toString());
 105             if (!destinations.isEmpty()) {
 106                 buf.append(" ->");
 107                 for (PhiResolverNode node : destinations) {
 108                     buf.append(' ').append(node.operand);
 109                 }
 110             }
 111             return buf.toString();
 112         }
 113     }
 114 
 115     private final LIRGeneratorTool gen;
 116     private final MoveFactory moveFactory;
 117     private final LIRInsertionBuffer buffer;
 118     private final int insertBefore;
 119 
 120     /**
 121      * The operand loop header phi for the operand currently being process in {@link #dispose()}.
 122      */
 123     private PhiResolverNode loop;
 124 
 125     private Value temp;
 126 
 127     private final ArrayList<PhiResolverNode> variableOperands = new ArrayList<>(3);
 128     private final ArrayList<PhiResolverNode> otherOperands = new ArrayList<>(3);
 129 
 130     /**
 131      * Maps operands to nodes.
 132      */
 133     private final HashMap<Value, PhiResolverNode> operandToNodeMap = CollectionsFactory.newMap();
 134 
 135     public static PhiResolver create(LIRGeneratorTool gen) {
 136         AbstractBlockBase<?> block = gen.getCurrentBlock();
 137         assert block != null;
 138         List<LIRInstruction> instructions = gen.getResult().getLIR().getLIRforBlock(block);
 139 
 140         return new PhiResolver(gen, new LIRInsertionBuffer(), instructions, instructions.size());
 141     }
 142 
 143     public static PhiResolver create(LIRGeneratorTool gen, LIRInsertionBuffer buffer, List<LIRInstruction> instructions, int insertBefore) {
 144         return new PhiResolver(gen, buffer, instructions, insertBefore);
 145     }
 146 
 147     protected PhiResolver(LIRGeneratorTool gen, LIRInsertionBuffer buffer, List<LIRInstruction> instructions, int insertBefore) {
 148         this.gen = gen;
 149         moveFactory = gen.getSpillMoveFactory();
 150         temp = ILLEGAL;
 151 
 152         this.buffer = buffer;
 153         this.buffer.init(instructions);
 154         this.insertBefore = insertBefore;
 155 
 156     }
 157 
 158     public void dispose() {
 159         // resolve any cycles in moves from and to variables
 160         for (int i = variableOperands.size() - 1; i >= 0; i--) {
 161             PhiResolverNode node = variableOperands.get(i);
 162             if (!node.visited) {
 163                 loop = null;
 164                 move(node, null);
 165                 node.startNode = true;
 166                 assert isIllegal(temp) : "moveTempTo() call missing";
 167             }
 168         }
 169 
 170         // generate move for move from non variable to arbitrary destination
 171         for (int i = otherOperands.size() - 1; i >= 0; i--) {
 172             PhiResolverNode node = otherOperands.get(i);
 173             for (int j = node.destinations.size() - 1; j >= 0; j--) {
 174                 emitMove(node.destinations.get(j).operand, node.operand);
 175             }
 176         }
 177         buffer.finish();
 178     }
 179 
 180     public void move(Value dest, Value src) {
 181         assert isVariable(dest) : "destination must be virtual";
 182         // tty.print("move "); src.print(); tty.print(" to "); dest.print(); tty.cr();
 183         assert isLegal(src) : "source for phi move is illegal";
 184         assert isLegal(dest) : "destination for phi move is illegal";
 185         PhiResolverNode srcNode = sourceNode(src);
 186         PhiResolverNode destNode = destinationNode(dest);
 187         srcNode.destinations.add(destNode);
 188     }
 189 
 190     private PhiResolverNode createNode(Value operand, boolean source) {
 191         PhiResolverNode node;
 192         if (isVariable(operand)) {
 193             node = operandToNodeMap.get(operand);
 194             assert node == null || node.operand.equals(operand);
 195             if (node == null) {
 196                 node = new PhiResolverNode(operand);
 197                 operandToNodeMap.put(operand, node);
 198             }
 199             // Make sure that all variables show up in the list when
 200             // they are used as the source of a move.
 201             if (source) {
 202                 if (!variableOperands.contains(node)) {
 203                     variableOperands.add(node);
 204                 }
 205             }
 206         } else {
 207             assert source;
 208             node = new PhiResolverNode(operand);
 209             otherOperands.add(node);
 210         }
 211         return node;
 212     }
 213 
 214     private PhiResolverNode destinationNode(Value opr) {
 215         return createNode(opr, false);
 216     }
 217 
 218     private void emitMove(Value dest, Value src) {
 219         assert isLegal(src);
 220         assert isLegal(dest);
 221         LIRInstruction move = moveFactory.createMove((AllocatableValue) dest, src);
 222         buffer.append(insertBefore, move);
 223     }
 224 
 225     // Traverse assignment graph in depth first order and generate moves in post order
 226     // ie. two assignments: b := c, a := b start with node c:
 227     // Call graph: move(c, NULL) -> move(b, c) -> move(a, b)
 228     // Generates moves in this order: move b to a and move c to b
 229     // ie. cycle a := b, b := a start with node a
 230     // Call graph: move(a, NULL) -> move(b, a) -> move(a, b)
 231     // Generates moves in this order: move b to temp, move a to b, move temp to a
 232     private void move(PhiResolverNode dest, PhiResolverNode src) {
 233         if (!dest.visited) {
 234             dest.visited = true;
 235             for (int i = dest.destinations.size() - 1; i >= 0; i--) {
 236                 move(dest.destinations.get(i), dest);
 237             }
 238         } else if (!dest.startNode) {
 239             // cycle in graph detected
 240             assert loop == null : "only one loop valid!";
 241             loop = dest;
 242             moveToTemp(src.operand);
 243             return;
 244         } // else dest is a start node
 245 
 246         if (!dest.assigned) {
 247             if (loop == dest) {
 248                 moveTempTo(dest.operand);
 249                 dest.assigned = true;
 250             } else if (src != null) {
 251                 emitMove(dest.operand, src.operand);
 252                 dest.assigned = true;
 253             }
 254         }
 255     }
 256 
 257     private void moveTempTo(Value dest) {
 258         assert isLegal(temp);
 259         emitMove(dest, temp);
 260         temp = ILLEGAL;
 261     }
 262 
 263     private void moveToTemp(Value src) {
 264         assert isIllegal(temp);
 265         temp = gen.newVariable(src.getValueKind());
 266         emitMove(temp, src);
 267     }
 268 
 269     private PhiResolverNode sourceNode(Value opr) {
 270         return createNode(opr, true);
 271     }
 272 }