TraceLinearScanEliminateSpillMovePhase.java revision 13017:134219a5b0ec
1/*
2 * Copyright (c) 2015, 2017, 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.alloc.trace.lsra;
24
25import static jdk.vm.ci.code.ValueUtil.isRegister;
26import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
27import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
28import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
29
30import java.util.ArrayList;
31
32import org.graalvm.compiler.core.common.alloc.RegisterAllocationConfig;
33import org.graalvm.compiler.core.common.alloc.Trace;
34import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
35import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
36import org.graalvm.compiler.debug.Debug;
37import org.graalvm.compiler.debug.Indent;
38import org.graalvm.compiler.lir.LIRInsertionBuffer;
39import org.graalvm.compiler.lir.LIRInstruction;
40import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
41import org.graalvm.compiler.lir.StandardOp.LoadConstantOp;
42import org.graalvm.compiler.lir.StandardOp.MoveOp;
43import org.graalvm.compiler.lir.StandardOp.ValueMoveOp;
44import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.SpillState;
45import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.IntervalPredicate;
46import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan;
47import org.graalvm.compiler.lir.gen.LIRGenerationResult;
48import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory;
49
50import jdk.vm.ci.code.TargetDescription;
51import jdk.vm.ci.meta.AllocatableValue;
52
53final class TraceLinearScanEliminateSpillMovePhase extends TraceLinearScanAllocationPhase {
54
55    private static final IntervalPredicate spilledIntervals = new TraceLinearScanPhase.IntervalPredicate() {
56
57        @Override
58        public boolean apply(TraceInterval i) {
59            return i.isSplitParent() && SpillState.IN_MEMORY.contains(i.spillState());
60        }
61    };
62
63    @Override
64    protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Trace trace, MoveFactory spillMoveFactory, RegisterAllocationConfig registerAllocationConfig,
65                    TraceBuilderResult traceBuilderResult, TraceLinearScan allocator) {
66        boolean shouldEliminateSpillMoves = shouldEliminateSpillMoves(traceBuilderResult, allocator);
67        eliminateSpillMoves(allocator, shouldEliminateSpillMoves, traceBuilderResult);
68    }
69
70    private static boolean shouldEliminateSpillMoves(TraceBuilderResult traceBuilderResult, TraceLinearScan allocator) {
71        return !traceBuilderResult.incomingSideEdges(traceBuilderResult.getTraceForBlock(allocator.blockAt(0)));
72    }
73
74    // called once before assignment of register numbers
75    @SuppressWarnings("try")
76    private static void eliminateSpillMoves(TraceLinearScan allocator, boolean shouldEliminateSpillMoves, TraceBuilderResult traceBuilderResult) {
77        try (Indent indent = Debug.logAndIndent("Eliminating unnecessary spill moves: Trace%d", traceBuilderResult.getTraceForBlock(allocator.blockAt(0)).getId())) {
78            allocator.sortIntervalsBySpillPos();
79
80            /*
81             * collect all intervals that must be stored after their definition. The list is sorted
82             * by Interval.spillDefinitionPos.
83             */
84            TraceInterval interval = allocator.createUnhandledListBySpillPos(spilledIntervals);
85            if (DetailedAsserts.getValue(allocator.getOptions())) {
86                checkIntervals(interval);
87            }
88            if (Debug.isLogEnabled()) {
89                try (Indent indent2 = Debug.logAndIndent("Sorted intervals")) {
90                    for (TraceInterval i = interval; i != null; i = i.next) {
91                        Debug.log("%5d: %s", i.spillDefinitionPos(), i);
92                    }
93                }
94            }
95
96            LIRInsertionBuffer insertionBuffer = new LIRInsertionBuffer();
97            for (AbstractBlockBase<?> block : allocator.sortedBlocks()) {
98                try (Indent indent1 = Debug.logAndIndent("Handle %s", block)) {
99                    ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block);
100                    int numInst = instructions.size();
101
102                    int lastOpId = -1;
103                    // iterate all instructions of the block.
104                    for (int j = 0; j < numInst; j++) {
105                        LIRInstruction op = instructions.get(j);
106                        int opId = op.id();
107                        try (Indent indent2 = Debug.logAndIndent("%5d %s", opId, op)) {
108
109                            if (opId == -1) {
110                                MoveOp move = (MoveOp) op;
111                                /*
112                                 * Remove move from register to stack if the stack slot is
113                                 * guaranteed to be correct. Only moves that have been inserted by
114                                 * LinearScan can be removed.
115                                 */
116                                if (shouldEliminateSpillMoves && canEliminateSpillMove(allocator, block, move, lastOpId)) {
117                                    /*
118                                     * Move target is a stack slot that is always correct, so
119                                     * eliminate instruction.
120                                     */
121                                    if (Debug.isLogEnabled()) {
122                                        if (move instanceof ValueMoveOp) {
123                                            ValueMoveOp vmove = (ValueMoveOp) move;
124                                            Debug.log("eliminating move from interval %d (%s) to %d (%s) in block %s", allocator.operandNumber(vmove.getInput()), vmove.getInput(),
125                                                            allocator.operandNumber(vmove.getResult()), vmove.getResult(), block);
126                                        } else {
127                                            LoadConstantOp load = (LoadConstantOp) move;
128                                            Debug.log("eliminating constant load from %s to %d (%s) in block %s", load.getConstant(), allocator.operandNumber(load.getResult()), load.getResult(),
129                                                            block);
130                                        }
131                                    }
132
133                                    // null-instructions are deleted by assignRegNum
134                                    instructions.set(j, null);
135                                }
136
137                            } else {
138                                lastOpId = opId;
139                                /*
140                                 * Insert move from register to stack just after the beginning of
141                                 * the interval.
142                                 */
143                                // assert interval == TraceInterval.EndMarker ||
144                                // interval.spillDefinitionPos() >= opId : "invalid order";
145                                assert interval == TraceInterval.EndMarker || (interval.isSplitParent() && SpillState.IN_MEMORY.contains(interval.spillState())) : "invalid interval";
146
147                                while (interval != TraceInterval.EndMarker && interval.spillDefinitionPos() == opId) {
148                                    Debug.log("handle %s", interval);
149                                    if (!interval.canMaterialize() && interval.spillState() != SpillState.StartInMemory) {
150
151                                        AllocatableValue fromLocation = interval.getSplitChildAtOpId(opId, OperandMode.DEF).location();
152                                        AllocatableValue toLocation = allocator.canonicalSpillOpr(interval);
153                                        if (!fromLocation.equals(toLocation)) {
154
155                                            if (!insertionBuffer.initialized()) {
156                                                /*
157                                                 * prepare insertion buffer (appended when all
158                                                 * instructions in the block are processed)
159                                                 */
160                                                insertionBuffer.init(instructions);
161                                            }
162
163                                            assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" +
164                                                            interval.spillState();
165                                            assert isStackSlotValue(toLocation) : "to operand must be a stack slot";
166
167                                            LIRInstruction move = allocator.getSpillMoveFactory().createMove(toLocation, fromLocation);
168                                            insertionBuffer.append(j + 1, move);
169
170                                            if (Debug.isLogEnabled()) {
171                                                Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId);
172                                            }
173                                        }
174                                    }
175                                    interval = interval.next;
176                                }
177                            }
178                        }
179                    }   // end of instruction iteration
180
181                    if (insertionBuffer.initialized()) {
182                        insertionBuffer.finish();
183                    }
184                }
185            }   // end of block iteration
186
187            assert interval == TraceInterval.EndMarker : "missed an interval";
188        }
189    }
190
191    /**
192     * @param allocator
193     * @param block The block {@code move} is located in.
194     * @param move Spill move.
195     * @param lastOpId The id of last "normal" instruction before the spill move. (Spill moves have
196     *            no valid opId but -1.)
197     */
198    private static boolean canEliminateSpillMove(TraceLinearScan allocator, AbstractBlockBase<?> block, MoveOp move, int lastOpId) {
199        assert ((LIRInstruction) move).id() == -1 : "Not a spill move: " + move;
200        assert isVariable(move.getResult()) : "LinearScan inserts only moves to variables: " + move;
201        assert lastOpId >= 0 : "Invalid lastOpId: " + lastOpId;
202
203        TraceInterval curInterval = allocator.intervalFor(move.getResult());
204
205        if (!isRegister(curInterval.location()) && curInterval.inMemoryAt(lastOpId) && !isPhiResolutionMove(allocator, move)) {
206            /* Phi resolution moves cannot be removed because they define the value. */
207            // TODO (je) check if the comment is still valid!
208            assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location();
209            return true;
210        }
211        return false;
212    }
213
214    /**
215     * Checks if a (spill or split) move is a Phi resolution move.
216     *
217     * A spill or split move connects a split parent or a split child with another split child.
218     * Therefore the destination of the move is always a split child. Phi resolution moves look like
219     * spill moves (i.e. {@link LIRInstruction#id() id} is {@code 0}, but they define a new
220     * variable. As a result the destination interval is a split parent.
221     */
222    private static boolean isPhiResolutionMove(TraceLinearScan allocator, MoveOp move) {
223        assert ((LIRInstruction) move).id() == -1 : "Not a spill move: " + move;
224        TraceInterval curInterval = allocator.intervalFor(move.getResult());
225        return curInterval.isSplitParent();
226    }
227
228    private static void checkIntervals(TraceInterval interval) {
229        TraceInterval prev = null;
230        TraceInterval temp = interval;
231        while (temp != TraceInterval.EndMarker) {
232            assert temp.spillDefinitionPos() >= 0 : "invalid spill definition pos " + temp;
233            if (prev != null) {
234                // assert temp.from() >= prev.from() : "intervals not sorted";
235                assert temp.spillDefinitionPos() >= prev.spillDefinitionPos() : "when intervals are sorted by from :  then they must also be sorted by spillDefinitionPos";
236            }
237
238            assert temp.spillSlot() != null || temp.canMaterialize() : "interval has no spill slot assigned";
239            assert temp.spillDefinitionPos() >= temp.from() : "invalid order";
240            // assert temp.spillDefinitionPos() <= temp.from() + 2 :
241            // "only intervals defined once at their start-pos can be optimized";
242
243            if (Debug.isLogEnabled()) {
244                Debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos());
245            }
246
247            prev = temp;
248            temp = temp.next;
249        }
250    }
251
252}
253