TraceLinearScanAssignLocationsPhase.java revision 12657:6ef01bd40ce2
1/*
2 * Copyright (c) 2015, 2016, 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 org.graalvm.compiler.lir.LIRValueUtil.isConstantValue;
26import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
27import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
28import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
29import static org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAshareSpillInformation;
30import static jdk.vm.ci.code.ValueUtil.isIllegal;
31import static jdk.vm.ci.code.ValueUtil.isRegister;
32
33import java.util.Collections;
34import java.util.EnumSet;
35import java.util.List;
36
37import org.graalvm.compiler.core.common.alloc.Trace;
38import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
39import org.graalvm.compiler.debug.Debug;
40import org.graalvm.compiler.debug.GraalError;
41import org.graalvm.compiler.debug.Indent;
42import org.graalvm.compiler.lir.ConstantValue;
43import org.graalvm.compiler.lir.InstructionValueProcedure;
44import org.graalvm.compiler.lir.LIRFrameState;
45import org.graalvm.compiler.lir.LIRInstruction;
46import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
47import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
48import org.graalvm.compiler.lir.StandardOp;
49import org.graalvm.compiler.lir.StandardOp.BlockEndOp;
50import org.graalvm.compiler.lir.StandardOp.LabelOp;
51import org.graalvm.compiler.lir.StandardOp.MoveOp;
52import org.graalvm.compiler.lir.StandardOp.ValueMoveOp;
53import org.graalvm.compiler.lir.Variable;
54import org.graalvm.compiler.lir.alloc.trace.ShadowedRegisterValue;
55import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan;
56import org.graalvm.compiler.lir.gen.LIRGenerationResult;
57import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory;
58
59import jdk.vm.ci.code.RegisterValue;
60import jdk.vm.ci.code.StackSlot;
61import jdk.vm.ci.code.TargetDescription;
62import jdk.vm.ci.meta.AllocatableValue;
63import jdk.vm.ci.meta.Value;
64
65/**
66 * Specialization of {@link org.graalvm.compiler.lir.alloc.lsra.LinearScanAssignLocationsPhase} that
67 * inserts {@link ShadowedRegisterValue}s to describe {@link RegisterValue}s that are also available
68 * on the {@link StackSlot stack}.
69 */
70final class TraceLinearScanAssignLocationsPhase extends TraceLinearScanAllocationPhase {
71
72    @Override
73    protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Trace trace, TraceLinearScanAllocationContext context) {
74        TraceLinearScan allocator = context.allocator;
75        MoveFactory spillMoveFactory = context.spillMoveFactory;
76        new Assigner(allocator, spillMoveFactory).assignLocations();
77    }
78
79    private static final class Assigner {
80        private final TraceLinearScan allocator;
81        private final MoveFactory spillMoveFactory;
82
83        private Assigner(TraceLinearScan allocator, MoveFactory spillMoveFactory) {
84            this.allocator = allocator;
85            this.spillMoveFactory = spillMoveFactory;
86        }
87
88        /**
89         * Assigns the allocated location for an LIR instruction operand back into the instruction.
90         *
91         * @param op current {@link LIRInstruction}
92         * @param operand an LIR instruction operand
93         * @param mode the usage mode for {@code operand} by the instruction
94         * @return the location assigned for the operand
95         */
96        private Value colorLirOperand(LIRInstruction op, Variable operand, OperandMode mode) {
97            int opId = op.id();
98            TraceInterval interval = allocator.intervalFor(operand);
99            assert interval != null : "interval must exist";
100
101            if (opId != -1) {
102                /*
103                 * Operands are not changed when an interval is split during allocation, so search
104                 * the right interval here.
105                 */
106                interval = allocator.splitChildAtOpId(interval, opId, mode);
107            }
108
109            if (isIllegal(interval.location()) && interval.canMaterialize()) {
110                if (op instanceof LabelOp) {
111                    /*
112                     * Spilled materialized value in a LabelOp (i.e. incoming): no need for move
113                     * resolution so we can ignore it.
114                     */
115                    return Value.ILLEGAL;
116                }
117                assert mode != OperandMode.DEF;
118                return new ConstantValue(interval.kind(), interval.getMaterializedValue());
119            }
120            return interval.location();
121        }
122
123        /**
124         * @param op
125         * @param operand
126         * @param valueMode
127         * @param flags
128         * @see InstructionValueProcedure#doValue(LIRInstruction, Value, OperandMode, EnumSet)
129         */
130        private Value debugInfoProcedure(LIRInstruction op, Value operand, OperandMode valueMode, EnumSet<OperandFlag> flags) {
131            if (isVirtualStackSlot(operand)) {
132                return operand;
133            }
134            int tempOpId = op.id();
135            OperandMode mode = OperandMode.USE;
136            AbstractBlockBase<?> block = allocator.blockForId(tempOpId);
137            if (block.getSuccessorCount() == 1 && tempOpId == allocator.getLastLirInstructionId(block)) {
138                /*
139                 * Generating debug information for the last instruction of a block. If this
140                 * instruction is a branch, spill moves are inserted before this branch and so the
141                 * wrong operand would be returned (spill moves at block boundaries are not
142                 * considered in the live ranges of intervals).
143                 *
144                 * Solution: use the first opId of the branch target block instead.
145                 */
146                final LIRInstruction instr = allocator.getLIR().getLIRforBlock(block).get(allocator.getLIR().getLIRforBlock(block).size() - 1);
147                if (instr instanceof StandardOp.JumpOp) {
148                    throw GraalError.unimplemented("DebugInfo on jumps are not supported!");
149                }
150            }
151
152            /*
153             * Get current location of operand. The operand must be live because debug information
154             * is considered when building the intervals if the interval is not live,
155             * colorLirOperand will cause an assert on failure.
156             */
157            Value result = colorLirOperand(op, (Variable) operand, mode);
158            assert !allocator.hasCall(tempOpId) || isStackSlotValue(result) || isConstantValue(result) || !allocator.isCallerSave(result) : "cannot have caller-save register operands at calls";
159            return result;
160        }
161
162        private void computeDebugInfo(final LIRInstruction op, LIRFrameState info) {
163            info.forEachState(op, this::debugInfoProcedure);
164        }
165
166        private void assignLocations(List<LIRInstruction> instructions) {
167            int numInst = instructions.size();
168            boolean hasDead = false;
169
170            for (int j = 0; j < numInst; j++) {
171                final LIRInstruction op = instructions.get(j);
172                if (op == null) {
173                    /*
174                     * this can happen when spill-moves are removed in eliminateSpillMoves
175                     */
176                    hasDead = true;
177                } else if (assignLocations(op, instructions, j)) {
178                    hasDead = true;
179                }
180            }
181
182            if (hasDead) {
183                // Remove null values from the list.
184                instructions.removeAll(Collections.singleton(null));
185            }
186        }
187
188        private final InstructionValueProcedure assignProc = new InstructionValueProcedure() {
189            @Override
190            public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
191                if (isVariable(value)) {
192                    return colorLirOperand(instruction, (Variable) value, mode);
193                }
194                return value;
195            }
196        };
197
198        /**
199         * Assigns the operand of an {@link LIRInstruction}.
200         *
201         * @param op The {@link LIRInstruction} that should be colored.
202         * @param j The index of {@code op} in the {@code instructions} list.
203         * @param instructions The instructions of the current block.
204         * @return {@code true} if the instruction was deleted.
205         */
206        private boolean assignLocations(LIRInstruction op, List<LIRInstruction> instructions, int j) {
207            assert op != null && instructions.get(j) == op;
208            if (TraceRAshareSpillInformation.getValue()) {
209                if (op instanceof BlockEndOp) {
210                    ((BlockEndOp) op).forEachOutgoingValue(colorOutgoingIncomingValues);
211                } else if (op instanceof LabelOp) {
212                    ((LabelOp) op).forEachIncomingValue(colorOutgoingIncomingValues);
213                }
214            }
215
216            // remove useless moves
217            if (op instanceof MoveOp) {
218                AllocatableValue result = ((MoveOp) op).getResult();
219                if (isVariable(result) && allocator.isMaterialized(result, op.id(), OperandMode.DEF)) {
220                    /*
221                     * This happens if a materializable interval is originally not spilled but then
222                     * kicked out in LinearScanWalker.splitForSpilling(). When kicking out such an
223                     * interval this move operation was already generated.
224                     */
225                    instructions.set(j, null);
226                    return true;
227                }
228            }
229
230            op.forEachInput(assignProc);
231            op.forEachAlive(assignProc);
232            op.forEachTemp(assignProc);
233            op.forEachOutput(assignProc);
234
235            // compute reference map and debug information
236            op.forEachState((inst, state) -> computeDebugInfo(inst, state));
237
238            // remove useless moves
239            if (op instanceof ValueMoveOp) {
240                ValueMoveOp move = (ValueMoveOp) op;
241                if (move.getInput().equals(move.getResult())) {
242                    instructions.set(j, null);
243                    return true;
244                }
245                if (isStackSlotValue(move.getInput()) && isStackSlotValue(move.getResult())) {
246                    // rewrite stack to stack moves
247                    instructions.set(j, spillMoveFactory.createStackMove(move.getResult(), move.getInput()));
248                    return true;
249                }
250            }
251            return false;
252        }
253
254        @SuppressWarnings("try")
255        private void assignLocations() {
256            try (Indent indent = Debug.logAndIndent("assign locations")) {
257                for (AbstractBlockBase<?> block : allocator.sortedBlocks()) {
258                    try (Indent indent2 = Debug.logAndIndent("assign locations in block B%d", block.getId())) {
259                        assignLocations(allocator.getLIR().getLIRforBlock(block));
260                    }
261                }
262            }
263        }
264
265        private final InstructionValueProcedure colorOutgoingIncomingValues = new InstructionValueProcedure() {
266
267            @Override
268            public Value doValue(LIRInstruction instruction, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
269                if (isVariable(value)) {
270                    TraceInterval interval = allocator.intervalFor(value);
271                    assert interval != null : "interval must exist";
272                    interval = allocator.splitChildAtOpId(interval, instruction.id(), mode);
273
274                    if (interval.inMemoryAt(instruction.id()) && isRegister(interval.location())) {
275                        return new ShadowedRegisterValue((RegisterValue) interval.location(), interval.spillSlot());
276                    }
277                }
278                return value;
279            }
280        };
281    }
282}
283