001/*
002 * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.lir.gen;
024
025import static com.oracle.graal.lir.LIRValueUtil.*;
026import static jdk.internal.jvmci.code.ValueUtil.*;
027import static jdk.internal.jvmci.meta.Value.*;
028
029import java.util.*;
030
031import jdk.internal.jvmci.meta.*;
032
033import com.oracle.graal.compiler.common.*;
034import com.oracle.graal.compiler.common.cfg.*;
035import com.oracle.graal.lir.*;
036import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory;
037
038/**
039 * Converts phi instructions into moves.
040 *
041 * Resolves cycles:
042 *
043 * <pre>
044 *
045 *  r1 := r2  becomes  temp := r1
046 *  r2 := r1           r1 := r2
047 *                     r2 := temp
048 * </pre>
049 *
050 * and orders moves:
051 *
052 * <pre>
053 *  r2 := r3  becomes  r1 := r2
054 *  r1 := r2           r2 := r3
055 * </pre>
056 */
057public class PhiResolver {
058
059    /**
060     * Tracks a data flow dependency between a source operand and any number of the destination
061     * operands.
062     */
063    static class PhiResolverNode {
064
065        /**
066         * A source operand whose value flows into the {@linkplain #destinations destination}
067         * operands.
068         */
069        final Value operand;
070
071        /**
072         * The operands whose values are defined by the {@linkplain #operand source} operand.
073         */
074        final ArrayList<PhiResolverNode> destinations;
075
076        /**
077         * Denotes if a move instruction has already been emitted to initialize the value of
078         * {@link #operand}.
079         */
080        boolean assigned;
081
082        /**
083         * Specifies if this operand been visited for the purpose of emitting a move instruction.
084         */
085        boolean visited;
086
087        /**
088         * Specifies if this is the initial definition in data flow path for a given value.
089         */
090        boolean startNode;
091
092        PhiResolverNode(Value operand) {
093            this.operand = operand;
094            destinations = new ArrayList<>(4);
095        }
096
097        @Override
098        public String toString() {
099            StringBuilder buf = new StringBuilder(operand.toString());
100            if (!destinations.isEmpty()) {
101                buf.append(" ->");
102                for (PhiResolverNode node : destinations) {
103                    buf.append(' ').append(node.operand);
104                }
105            }
106            return buf.toString();
107        }
108    }
109
110    private final LIRGeneratorTool gen;
111    private final SpillMoveFactory moveFactory;
112    private final LIRInsertionBuffer buffer;
113    private final int insertBefore;
114
115    /**
116     * The operand loop header phi for the operand currently being process in {@link #dispose()}.
117     */
118    private PhiResolverNode loop;
119
120    private Value temp;
121
122    private final ArrayList<PhiResolverNode> variableOperands = new ArrayList<>(3);
123    private final ArrayList<PhiResolverNode> otherOperands = new ArrayList<>(3);
124
125    /**
126     * Maps operands to nodes.
127     */
128    private final HashMap<Value, PhiResolverNode> operandToNodeMap = CollectionsFactory.newMap();
129
130    public static PhiResolver create(LIRGeneratorTool gen) {
131        AbstractBlockBase<?> block = gen.getCurrentBlock();
132        assert block != null;
133        List<LIRInstruction> instructions = gen.getResult().getLIR().getLIRforBlock(block);
134
135        return new PhiResolver(gen, new LIRInsertionBuffer(), instructions, instructions.size());
136    }
137
138    public static PhiResolver create(LIRGeneratorTool gen, LIRInsertionBuffer buffer, List<LIRInstruction> instructions, int insertBefore) {
139        return new PhiResolver(gen, buffer, instructions, insertBefore);
140    }
141
142    protected PhiResolver(LIRGeneratorTool gen, LIRInsertionBuffer buffer, List<LIRInstruction> instructions, int insertBefore) {
143        this.gen = gen;
144        moveFactory = gen.getSpillMoveFactory();
145        temp = ILLEGAL;
146
147        this.buffer = buffer;
148        this.buffer.init(instructions);
149        this.insertBefore = insertBefore;
150
151    }
152
153    public void dispose() {
154        // resolve any cycles in moves from and to variables
155        for (int i = variableOperands.size() - 1; i >= 0; i--) {
156            PhiResolverNode node = variableOperands.get(i);
157            if (!node.visited) {
158                loop = null;
159                move(node, null);
160                node.startNode = true;
161                assert isIllegal(temp) : "moveTempTo() call missing";
162            }
163        }
164
165        // generate move for move from non variable to arbitrary destination
166        for (int i = otherOperands.size() - 1; i >= 0; i--) {
167            PhiResolverNode node = otherOperands.get(i);
168            for (int j = node.destinations.size() - 1; j >= 0; j--) {
169                emitMove(node.destinations.get(j).operand, node.operand);
170            }
171        }
172        buffer.finish();
173    }
174
175    public void move(Value dest, Value src) {
176        assert isVariable(dest) : "destination must be virtual";
177        // tty.print("move "); src.print(); tty.print(" to "); dest.print(); tty.cr();
178        assert isLegal(src) : "source for phi move is illegal";
179        assert isLegal(dest) : "destination for phi move is illegal";
180        PhiResolverNode srcNode = sourceNode(src);
181        PhiResolverNode destNode = destinationNode(dest);
182        srcNode.destinations.add(destNode);
183    }
184
185    private PhiResolverNode createNode(Value operand, boolean source) {
186        PhiResolverNode node;
187        if (isVariable(operand)) {
188            node = operandToNodeMap.get(operand);
189            assert node == null || node.operand.equals(operand);
190            if (node == null) {
191                node = new PhiResolverNode(operand);
192                operandToNodeMap.put(operand, node);
193            }
194            // Make sure that all variables show up in the list when
195            // they are used as the source of a move.
196            if (source) {
197                if (!variableOperands.contains(node)) {
198                    variableOperands.add(node);
199                }
200            }
201        } else {
202            assert source;
203            node = new PhiResolverNode(operand);
204            otherOperands.add(node);
205        }
206        return node;
207    }
208
209    private PhiResolverNode destinationNode(Value opr) {
210        return createNode(opr, false);
211    }
212
213    private void emitMove(Value dest, Value src) {
214        assert isLegal(src);
215        assert isLegal(dest);
216        LIRInstruction move = moveFactory.createMove((AllocatableValue) dest, src);
217        buffer.append(insertBefore, move);
218    }
219
220    // Traverse assignment graph in depth first order and generate moves in post order
221    // ie. two assignments: b := c, a := b start with node c:
222    // Call graph: move(c, NULL) -> move(b, c) -> move(a, b)
223    // Generates moves in this order: move b to a and move c to b
224    // ie. cycle a := b, b := a start with node a
225    // Call graph: move(a, NULL) -> move(b, a) -> move(a, b)
226    // Generates moves in this order: move b to temp, move a to b, move temp to a
227    private void move(PhiResolverNode dest, PhiResolverNode src) {
228        if (!dest.visited) {
229            dest.visited = true;
230            for (int i = dest.destinations.size() - 1; i >= 0; i--) {
231                move(dest.destinations.get(i), dest);
232            }
233        } else if (!dest.startNode) {
234            // cycle in graph detected
235            assert loop == null : "only one loop valid!";
236            loop = dest;
237            moveToTemp(src.operand);
238            return;
239        } // else dest is a start node
240
241        if (!dest.assigned) {
242            if (loop == dest) {
243                moveTempTo(dest.operand);
244                dest.assigned = true;
245            } else if (src != null) {
246                emitMove(dest.operand, src.operand);
247                dest.assigned = true;
248            }
249        }
250    }
251
252    private void moveTempTo(Value dest) {
253        assert isLegal(temp);
254        emitMove(dest, temp);
255        temp = ILLEGAL;
256    }
257
258    private void moveToTemp(Value src) {
259        assert isIllegal(temp);
260        temp = gen.newVariable(src.getLIRKind());
261        emitMove(temp, src);
262    }
263
264    private PhiResolverNode sourceNode(Value opr) {
265        return createNode(opr, true);
266    }
267}