PhiResolver.java revision 12968:4d8a004e5c6d
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 */
23package org.graalvm.compiler.lir.gen;
24
25import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
26import static jdk.vm.ci.code.ValueUtil.isIllegal;
27import static jdk.vm.ci.code.ValueUtil.isLegal;
28import static jdk.vm.ci.meta.Value.ILLEGAL;
29
30import java.util.ArrayList;
31import java.util.List;
32
33import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
34import org.graalvm.compiler.lir.LIRInsertionBuffer;
35import org.graalvm.compiler.lir.LIRInstruction;
36import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory;
37import org.graalvm.util.Equivalence;
38import org.graalvm.util.EconomicMap;
39
40import jdk.vm.ci.meta.AllocatableValue;
41import 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 */
62public 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 EconomicMap<Value, PhiResolverNode> operandToNodeMap = EconomicMap.create(Equivalence.DEFAULT);
134
135    public static PhiResolver create(LIRGeneratorTool gen) {
136        AbstractBlockBase<?> block = gen.getCurrentBlock();
137        assert block != null;
138        ArrayList<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}
273