TraceLinearScanLifetimeAnalysisPhase.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.asVariable;
26import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
27import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
28import static org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAshareSpillInformation;
29import static org.graalvm.compiler.lir.alloc.trace.TraceRegisterAllocationPhase.Options.TraceRAuseInterTraceHints;
30import static org.graalvm.compiler.lir.alloc.trace.TraceUtil.asShadowedRegisterValue;
31import static org.graalvm.compiler.lir.alloc.trace.TraceUtil.isShadowedRegisterValue;
32import static org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.isVariableOrRegister;
33import static jdk.vm.ci.code.ValueUtil.asRegisterValue;
34import static jdk.vm.ci.code.ValueUtil.asStackSlot;
35import static jdk.vm.ci.code.ValueUtil.isRegister;
36import static jdk.vm.ci.code.ValueUtil.isStackSlot;
37
38import java.util.EnumSet;
39import java.util.List;
40import java.util.ListIterator;
41
42import org.graalvm.compiler.core.common.LIRKind;
43import org.graalvm.compiler.core.common.alloc.Trace;
44import org.graalvm.compiler.core.common.alloc.TraceBuilderResult;
45import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
46import org.graalvm.compiler.debug.Debug;
47import org.graalvm.compiler.debug.Debug.Scope;
48import org.graalvm.compiler.debug.GraalError;
49import org.graalvm.compiler.debug.Indent;
50import org.graalvm.compiler.lir.InstructionValueConsumer;
51import org.graalvm.compiler.lir.LIR;
52import org.graalvm.compiler.lir.LIRInstruction;
53import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
54import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
55import org.graalvm.compiler.lir.LIRValueUtil;
56import org.graalvm.compiler.lir.StandardOp.BlockEndOp;
57import org.graalvm.compiler.lir.StandardOp.LabelOp;
58import org.graalvm.compiler.lir.StandardOp.LoadConstantOp;
59import org.graalvm.compiler.lir.StandardOp.ValueMoveOp;
60import org.graalvm.compiler.lir.ValueProcedure;
61import org.graalvm.compiler.lir.Variable;
62import org.graalvm.compiler.lir.alloc.trace.ShadowedRegisterValue;
63import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.RegisterPriority;
64import org.graalvm.compiler.lir.alloc.trace.lsra.TraceInterval.SpillState;
65import org.graalvm.compiler.lir.alloc.trace.lsra.TraceLinearScanPhase.TraceLinearScan;
66import org.graalvm.compiler.lir.gen.LIRGenerationResult;
67import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory;
68import org.graalvm.compiler.lir.ssi.SSIUtil;
69
70import jdk.vm.ci.code.Register;
71import jdk.vm.ci.code.RegisterArray;
72import jdk.vm.ci.code.RegisterValue;
73import jdk.vm.ci.code.TargetDescription;
74import jdk.vm.ci.meta.AllocatableValue;
75import jdk.vm.ci.meta.JavaConstant;
76import jdk.vm.ci.meta.Value;
77
78public final class TraceLinearScanLifetimeAnalysisPhase extends TraceLinearScanAllocationPhase {
79
80    @Override
81    protected void run(TargetDescription target, LIRGenerationResult lirGenRes, Trace trace, TraceLinearScanAllocationContext context) {
82        TraceBuilderResult traceBuilderResult = context.resultTraces;
83        TraceLinearScan allocator = context.allocator;
84        new Analyser(allocator, traceBuilderResult).analyze();
85    }
86
87    public static final class Analyser {
88        private static final int DUMP_DURING_ANALYSIS_LEVEL = 4;
89        private final TraceLinearScan allocator;
90        private final TraceBuilderResult traceBuilderResult;
91        private int numInstructions;
92
93        public Analyser(TraceLinearScan allocator, TraceBuilderResult traceBuilderResult) {
94            this.allocator = allocator;
95            this.traceBuilderResult = traceBuilderResult;
96        }
97
98        private AbstractBlockBase<?>[] sortedBlocks() {
99            return allocator.sortedBlocks();
100        }
101
102        private LIR getLIR() {
103            return allocator.getLIR();
104        }
105
106        private RegisterArray getCallerSavedRegisters() {
107            return allocator.getRegisterAllocationConfig().getRegisterConfig().getCallerSaveRegisters();
108        }
109
110        public void analyze() {
111            countInstructions();
112            buildIntervals();
113        }
114
115        private boolean sameTrace(AbstractBlockBase<?> a, AbstractBlockBase<?> b) {
116            return traceBuilderResult.getTraceForBlock(b) == traceBuilderResult.getTraceForBlock(a);
117        }
118
119        private boolean isAllocatedOrCurrent(AbstractBlockBase<?> currentBlock, AbstractBlockBase<?> other) {
120            return traceBuilderResult.getTraceForBlock(other).getId() <= traceBuilderResult.getTraceForBlock(currentBlock).getId();
121        }
122
123        /**
124         * Count instructions in all blocks. The numbering follows the
125         * {@linkplain TraceLinearScan#sortedBlocks() register allocation order}.
126         */
127        private void countInstructions() {
128
129            allocator.initIntervals();
130
131            int numberInstructions = 0;
132            for (AbstractBlockBase<?> block : sortedBlocks()) {
133                numberInstructions += getLIR().getLIRforBlock(block).size();
134            }
135            numInstructions = numberInstructions;
136
137            // initialize with correct length
138            allocator.initOpIdMaps(numberInstructions);
139        }
140
141        private final InstructionValueConsumer outputConsumer = new InstructionValueConsumer() {
142            @Override
143            public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
144                if (isVariableOrRegister(operand)) {
145                    addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op));
146                    addRegisterHint(op, operand, mode, flags, true);
147                }
148            }
149        };
150
151        private final InstructionValueConsumer tempConsumer = new InstructionValueConsumer() {
152            @Override
153            public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
154                if (isVariableOrRegister(operand)) {
155                    addTemp((AllocatableValue) operand, op.id(), RegisterPriority.MustHaveRegister);
156                    addRegisterHint(op, operand, mode, flags, false);
157                }
158            }
159        };
160        private final InstructionValueConsumer aliveConsumer = new InstructionValueConsumer() {
161            @Override
162            public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
163                if (isVariableOrRegister(operand)) {
164                    RegisterPriority p = registerPriorityOfInputOperand(flags);
165                    int opId = op.id();
166                    int blockFrom = 0;
167                    addUse((AllocatableValue) operand, blockFrom, opId + 1, p);
168                    addRegisterHint(op, operand, mode, flags, false);
169                }
170            }
171        };
172
173        private final InstructionValueConsumer inputConsumer = new InstructionValueConsumer() {
174            @Override
175            public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
176                if (isVariableOrRegister(operand)) {
177                    int opId = op.id();
178                    RegisterPriority p = registerPriorityOfInputOperand(flags);
179                    int blockFrom = 0;
180                    addUse((AllocatableValue) operand, blockFrom, opId, p);
181                    addRegisterHint(op, operand, mode, flags, false);
182                }
183            }
184
185        };
186
187        private final InstructionValueConsumer stateProc = new InstructionValueConsumer() {
188            @Override
189            public void visitValue(LIRInstruction op, Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
190                if (isVariableOrRegister(operand)) {
191                    int opId = op.id();
192                    int blockFrom = 0;
193                    addUse((AllocatableValue) operand, blockFrom, opId + 1, RegisterPriority.None);
194                }
195            }
196        };
197
198        private void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority) {
199            if (!allocator.isProcessed(operand)) {
200                return;
201            }
202            if (isRegister(operand)) {
203                addFixedUse(asRegisterValue(operand), from, to);
204            } else {
205                assert isVariable(operand) : operand;
206                addVariableUse(asVariable(operand), from, to, registerPriority);
207            }
208        }
209
210        private void addFixedUse(RegisterValue reg, int from, int to) {
211            FixedInterval interval = allocator.getOrCreateFixedInterval(reg);
212            interval.addRange(from, to);
213            if (Debug.isLogEnabled()) {
214                Debug.log("add fixed use: %s, at %d", interval, to);
215            }
216        }
217
218        private void addVariableUse(Variable operand, int from, int to, RegisterPriority registerPriority) {
219            TraceInterval interval = allocator.getOrCreateInterval(operand);
220            interval.addRange(from, to);
221
222            // Register use position at even instruction id.
223            interval.addUsePos(to & ~1, registerPriority);
224
225            if (Debug.isLogEnabled()) {
226                Debug.log("add use: %s, at %d (%s)", interval, to, registerPriority.name());
227            }
228        }
229
230        private void addDef(AllocatableValue operand, LIRInstruction op, RegisterPriority registerPriority) {
231            if (!allocator.isProcessed(operand)) {
232                return;
233            }
234            if (isRegister(operand)) {
235                addFixedDef(asRegisterValue(operand), op);
236            } else {
237                assert isVariable(operand) : operand;
238                addVariableDef(asVariable(operand), op, registerPriority);
239            }
240        }
241
242        private void addFixedDef(RegisterValue reg, LIRInstruction op) {
243            FixedInterval interval = allocator.getOrCreateFixedInterval(reg);
244            int defPos = op.id();
245            if (interval.from() <= defPos) {
246                /*
247                 * Update the starting point (when a range is first created for a use, its start is
248                 * the beginning of the current block until a def is encountered).
249                 */
250                interval.setFrom(defPos);
251
252            } else {
253                /*
254                 * Dead value - make vacuous interval also add register priority for dead intervals
255                 */
256                interval.addRange(defPos, defPos + 1);
257                if (Debug.isLogEnabled()) {
258                    Debug.log("Warning: def of operand %s at %d occurs without use", reg, defPos);
259                }
260            }
261            if (Debug.isLogEnabled()) {
262                Debug.log("add fixed def: %s, at %d", interval, defPos);
263            }
264        }
265
266        private void addVariableDef(Variable operand, LIRInstruction op, RegisterPriority registerPriority) {
267            int defPos = op.id();
268
269            TraceInterval interval = allocator.getOrCreateInterval(operand);
270
271            if (interval.isEmpty()) {
272                /*
273                 * Dead value - make vacuous interval also add register priority for dead intervals
274                 */
275                interval.addRange(defPos, defPos + 1);
276                if (Debug.isLogEnabled()) {
277                    Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos);
278                }
279            } else {
280                /*
281                 * Update the starting point (when a range is first created for a use, its start is
282                 * the beginning of the current block until a def is encountered).
283                 */
284                interval.setFrom(defPos);
285            }
286            if (!(op instanceof LabelOp)) {
287                // no use positions for labels
288                interval.addUsePos(defPos, registerPriority);
289            }
290
291            changeSpillDefinitionPos(op, operand, interval, defPos);
292            if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal() && isStackSlot(operand)) {
293                // detection of method-parameters and roundfp-results
294                interval.setSpillState(SpillState.StartInMemory);
295            }
296            interval.addMaterializationValue(getMaterializedValue(op, operand, interval, allocator.neverSpillConstants(), allocator.getSpillMoveFactory()));
297
298            if (Debug.isLogEnabled()) {
299                Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name());
300            }
301        }
302
303        private void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority) {
304            if (!allocator.isProcessed(operand)) {
305                return;
306            }
307            if (isRegister(operand)) {
308                addFixedTemp(asRegisterValue(operand), tempPos);
309            } else {
310                assert isVariable(operand) : operand;
311                addVariableTemp(asVariable(operand), tempPos, registerPriority);
312            }
313        }
314
315        private void addFixedTemp(RegisterValue reg, int tempPos) {
316            FixedInterval interval = allocator.getOrCreateFixedInterval(reg);
317            interval.addRange(tempPos, tempPos + 1);
318            if (Debug.isLogEnabled()) {
319                Debug.log("add fixed temp: %s, at %d", interval, tempPos);
320            }
321        }
322
323        private void addVariableTemp(Variable operand, int tempPos, RegisterPriority registerPriority) {
324            TraceInterval interval = allocator.getOrCreateInterval(operand);
325
326            if (interval.isEmpty()) {
327                interval.addRange(tempPos, tempPos + 1);
328            } else if (interval.from() > tempPos) {
329                interval.setFrom(tempPos);
330            }
331
332            interval.addUsePos(tempPos, registerPriority);
333            interval.addMaterializationValue(null);
334
335            if (Debug.isLogEnabled()) {
336                Debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name());
337            }
338        }
339
340        /**
341         * Eliminates moves from register to stack if the stack slot is known to be correct.
342         *
343         * @param op
344         * @param operand
345         */
346        private void changeSpillDefinitionPos(LIRInstruction op, AllocatableValue operand, TraceInterval interval, int defPos) {
347            assert interval.isSplitParent() : "can only be called for split parents";
348
349            switch (interval.spillState()) {
350                case NoDefinitionFound:
351                    // assert interval.spillDefinitionPos() == -1 : "must no be set before";
352                    interval.setSpillDefinitionPos(defPos);
353                    if (!(op instanceof LabelOp)) {
354                        // Do not update state for labels. This will be done afterwards.
355                        interval.setSpillState(SpillState.NoSpillStore);
356                    }
357                    break;
358
359                case NoSpillStore:
360                    assert defPos <= interval.spillDefinitionPos() : "positions are processed in reverse order when intervals are created";
361                    if (defPos < interval.spillDefinitionPos() - 2) {
362                        /*
363                         * Second definition found, so no spill optimization possible for this
364                         * interval.
365                         */
366                        interval.setSpillState(SpillState.NoOptimization);
367                    } else {
368                        // two consecutive definitions (because of two-operand LIR form)
369                        assert allocator.blockForId(defPos) == allocator.blockForId(interval.spillDefinitionPos()) : "block must be equal";
370                    }
371                    break;
372
373                case NoOptimization:
374                    // nothing to do
375                    break;
376
377                default:
378                    throw GraalError.shouldNotReachHere("other states not allowed at this time");
379            }
380        }
381
382        private void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) {
383            if (flags.contains(OperandFlag.HINT) && isVariableOrRegister(targetValue)) {
384
385                ValueProcedure registerHintProc = new ValueProcedure() {
386                    @Override
387                    public Value doValue(Value registerHint, OperandMode valueMode, EnumSet<OperandFlag> valueFlags) {
388                        if (isVariableOrRegister(registerHint)) {
389                            /*
390                             * TODO (je): clean up
391                             */
392                            final AllocatableValue fromValue;
393                            final AllocatableValue toValue;
394                            /* hints always point from def to use */
395                            if (hintAtDef) {
396                                fromValue = (AllocatableValue) registerHint;
397                                toValue = (AllocatableValue) targetValue;
398                            } else {
399                                fromValue = (AllocatableValue) targetValue;
400                                toValue = (AllocatableValue) registerHint;
401                            }
402                            Debug.log("addRegisterHint %s to %s", fromValue, toValue);
403                            final TraceInterval to;
404                            final IntervalHint from;
405                            if (isRegister(toValue)) {
406                                if (isRegister(fromValue)) {
407                                    // fixed to fixed move
408                                    return null;
409                                }
410                                from = getIntervalHint(toValue);
411                                to = allocator.getOrCreateInterval(fromValue);
412                            } else {
413                                to = allocator.getOrCreateInterval(toValue);
414                                from = getIntervalHint(fromValue);
415                            }
416
417                            to.setLocationHint(from);
418                            if (Debug.isLogEnabled()) {
419                                Debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to);
420                            }
421
422                            return registerHint;
423                        }
424                        return null;
425                    }
426                };
427                op.forEachRegisterHint(targetValue, mode, registerHintProc);
428            }
429        }
430
431        private static boolean optimizeMethodArgument(Value value) {
432            /*
433             * Object method arguments that are passed on the stack are currently not optimized
434             * because this requires that the runtime visits method arguments during stack walking.
435             */
436            return isStackSlot(value) && asStackSlot(value).isInCallerFrame() && LIRKind.isValue(value);
437        }
438
439        /**
440         * Determines the register priority for an instruction's output/result operand.
441         */
442        private static RegisterPriority registerPriorityOfOutputOperand(LIRInstruction op) {
443            if (op instanceof LabelOp) {
444                // skip method header
445                return RegisterPriority.None;
446            }
447            if (op instanceof ValueMoveOp) {
448                ValueMoveOp move = (ValueMoveOp) op;
449                if (optimizeMethodArgument(move.getInput())) {
450                    return RegisterPriority.None;
451                }
452            }
453
454            // all other operands require a register
455            return RegisterPriority.MustHaveRegister;
456        }
457
458        /**
459         * Determines the priority which with an instruction's input operand will be allocated a
460         * register.
461         */
462        private static RegisterPriority registerPriorityOfInputOperand(EnumSet<OperandFlag> flags) {
463            if (flags.contains(OperandFlag.OUTGOING)) {
464                return RegisterPriority.None;
465            }
466            if (flags.contains(OperandFlag.STACK)) {
467                return RegisterPriority.ShouldHaveRegister;
468            }
469            // all other operands require a register
470            return RegisterPriority.MustHaveRegister;
471        }
472
473        @SuppressWarnings("try")
474        private void buildIntervals() {
475
476            try (Indent indent = Debug.logAndIndent("build intervals")) {
477
478                // create a list with all caller-save registers (cpu, fpu, xmm)
479                RegisterArray callerSaveRegs = getCallerSavedRegisters();
480                int instructionIndex = numInstructions;
481
482                // iterate all blocks in reverse order
483                AbstractBlockBase<?>[] blocks = sortedBlocks();
484                for (int i = blocks.length - 1; i >= 0; i--) {
485                    final AbstractBlockBase<?> block = blocks[i];
486
487                    try (Indent indent2 = Debug.logAndIndent("handle block %d", block.getId())) {
488
489                        /*
490                         * Iterate all instructions of the block in reverse order. definitions of
491                         * intervals are processed before uses.
492                         */
493                        List<LIRInstruction> instructions = getLIR().getLIRforBlock(block);
494                        ListIterator<LIRInstruction> instIt = instructions.listIterator(instructions.size());
495                        while (instIt.hasPrevious()) {
496                            final LIRInstruction op = instIt.previous();
497                            // number instruction
498                            instructionIndex--;
499                            final int opId = instructionIndex << 1;
500                            numberInstruction(block, op, instructionIndex);
501
502                            try (Indent indent3 = Debug.logAndIndent("handle inst %d: %s", opId, op)) {
503
504                                /*
505                                 * Add a temp range for each register if operation destroys
506                                 * caller-save registers.
507                                 */
508                                if (op.destroysCallerSavedRegisters()) {
509                                    for (Register r : callerSaveRegs) {
510                                        if (allocator.attributes(r).isAllocatable()) {
511                                            addTemp(r.asValue(), opId, RegisterPriority.None);
512                                        }
513                                    }
514                                    if (Debug.isLogEnabled()) {
515                                        Debug.log("operation destroys all caller-save registers");
516                                    }
517                                }
518
519                                op.visitEachOutput(outputConsumer);
520                                op.visitEachTemp(tempConsumer);
521                                op.visitEachAlive(aliveConsumer);
522                                op.visitEachInput(inputConsumer);
523
524                                /*
525                                 * Add uses of live locals from interpreter's point of view for
526                                 * proper debug information generation. Treat these operands as temp
527                                 * values (if the live range is extended to a call site, the value
528                                 * would be in a register at the call otherwise).
529                                 */
530                                op.visitEachState(stateProc);
531                            }
532
533                        }   // end of instruction iteration
534                    }
535                    if (Debug.isDumpEnabled(DUMP_DURING_ANALYSIS_LEVEL)) {
536                        allocator.printIntervals("After Block " + block);
537                    }
538                }   // end of block iteration
539                assert instructionIndex == 0 : "not at start?" + instructionIndex;
540
541                // fix spill state for phi/sigma intervals
542                for (TraceInterval interval : allocator.intervals()) {
543                    if (interval != null && interval.spillState().equals(SpillState.NoDefinitionFound) && interval.spillDefinitionPos() != -1) {
544                        // there was a definition in a phi/sigma
545                        interval.setSpillState(SpillState.NoSpillStore);
546                    }
547                }
548                if (TraceRAuseInterTraceHints.getValue()) {
549                    addInterTraceHints();
550                }
551                for (FixedInterval interval1 : allocator.fixedIntervals()) {
552                    if (interval1 != null) {
553                        /* We use [-1, 0] to avoid intersection with incoming values. */
554                        interval1.addRange(-1, 0);
555                    }
556                }
557            }
558        }
559
560        private void numberInstruction(AbstractBlockBase<?> block, LIRInstruction op, int index) {
561            int opId = index << 1;
562            assert op.id() == -1 || op.id() == opId : "must match";
563            op.setId(opId);
564            allocator.putOpIdMaps(index, op, block);
565            assert allocator.instructionForId(opId) == op : "must match";
566        }
567
568        @SuppressWarnings("try")
569        private void addInterTraceHints() {
570            try (Scope s = Debug.scope("InterTraceHints", allocator)) {
571                // set hints for phi/sigma intervals
572                for (AbstractBlockBase<?> block : sortedBlocks()) {
573                    LabelOp label = SSIUtil.incoming(getLIR(), block);
574                    for (AbstractBlockBase<?> pred : block.getPredecessors()) {
575                        if (isAllocatedOrCurrent(block, pred)) {
576                            BlockEndOp outgoing = SSIUtil.outgoing(getLIR(), pred);
577                            // do not look at phi variables as they are not same value!
578                            for (int i = outgoing.getPhiSize(); i < outgoing.getOutgoingSize(); i++) {
579                                Value toValue = label.getIncomingValue(i);
580                                assert !isShadowedRegisterValue(toValue) : "Shadowed Registers are not allowed here: " + toValue;
581                                if (isVariable(toValue)) {
582                                    Value fromValue = outgoing.getOutgoingValue(i);
583                                    assert sameTrace(block, pred) || !isVariable(fromValue) : "Unallocated variable: " + fromValue;
584                                    if (!LIRValueUtil.isConstantValue(fromValue)) {
585                                        addInterTraceHint(label, (AllocatableValue) toValue, fromValue);
586                                    }
587                                }
588                            }
589                        }
590                    }
591                }
592            } catch (Throwable e) {
593                throw Debug.handle(e);
594            }
595        }
596
597        private void addInterTraceHint(LabelOp label, AllocatableValue toValue, Value fromValue) {
598            assert isVariable(toValue) : "Wrong toValue: " + toValue;
599            assert isRegister(fromValue) || isVariable(fromValue) || isStackSlotValue(fromValue) || isShadowedRegisterValue(fromValue) : "Wrong fromValue: " + fromValue;
600            TraceInterval to = allocator.getOrCreateInterval(toValue);
601            if (isVariableOrRegister(fromValue)) {
602                IntervalHint from = getIntervalHint((AllocatableValue) fromValue);
603                setHint(label, to, from);
604            } else if (isStackSlotValue(fromValue)) {
605                setSpillSlot(label, to, (AllocatableValue) fromValue);
606            } else if (TraceRAshareSpillInformation.getValue() && isShadowedRegisterValue(fromValue)) {
607                ShadowedRegisterValue shadowedRegisterValue = asShadowedRegisterValue(fromValue);
608                IntervalHint from = getIntervalHint(shadowedRegisterValue.getRegister());
609                setHint(label, to, from);
610                setSpillSlot(label, to, shadowedRegisterValue.getStackSlot());
611            } else {
612                throw GraalError.shouldNotReachHere();
613            }
614        }
615
616        private static void setHint(final LIRInstruction op, TraceInterval to, IntervalHint from) {
617            IntervalHint currentHint = to.locationHint(false);
618            if (currentHint == null) {
619                /*
620                 * Update hint if there was none or if the hint interval starts after the hinted
621                 * interval.
622                 */
623                to.setLocationHint(from);
624                if (Debug.isLogEnabled()) {
625                    Debug.log("operation at opId %d: added hint from interval %s to %s", op.id(), from, to);
626                }
627            }
628        }
629
630        private static void setSpillSlot(LIRInstruction op, TraceInterval interval, AllocatableValue spillSlot) {
631            if (interval.spillSlot() == null) {
632                interval.setSpillSlot(spillSlot);
633                interval.setSpillState(SpillState.StartInMemory);
634                if (Debug.isLogEnabled()) {
635                    Debug.log("operation at opId %d: added spill slot %s to interval %s", op.id(), spillSlot, interval);
636                }
637            } else if (Debug.isLogEnabled()) {
638                Debug.log("operation at opId %d: has already a slot assigned %s", op.id(), interval.spillSlot());
639            }
640        }
641
642        private IntervalHint getIntervalHint(AllocatableValue from) {
643            if (isRegister(from)) {
644                return allocator.getOrCreateFixedInterval(asRegisterValue(from));
645            }
646            return allocator.getOrCreateInterval(from);
647        }
648
649    }
650
651    /**
652     * Returns a value for a interval definition, which can be used for re-materialization.
653     *
654     * @param op An instruction which defines a value
655     * @param operand The destination operand of the instruction
656     * @param interval The interval for this defined value.
657     * @return Returns the value which is moved to the instruction and which can be reused at all
658     *         reload-locations in case the interval of this instruction is spilled. Currently this
659     *         can only be a {@link JavaConstant}.
660     */
661    private static JavaConstant getMaterializedValue(LIRInstruction op, Value operand, TraceInterval interval, boolean neverSpillConstants, MoveFactory spillMoveFactory) {
662        if (op instanceof LoadConstantOp) {
663            LoadConstantOp move = (LoadConstantOp) op;
664            if (move.getConstant() instanceof JavaConstant) {
665                if (!neverSpillConstants) {
666                    if (!spillMoveFactory.allowConstantToStackMove(move.getConstant())) {
667                        return null;
668                    }
669                    /*
670                     * Check if the interval has any uses which would accept an stack location
671                     * (priority == ShouldHaveRegister). Rematerialization of such intervals can
672                     * result in a degradation, because rematerialization always inserts a constant
673                     * load, even if the value is not needed in a register.
674                     */
675                    int numUsePos = interval.numUsePos();
676                    for (int useIdx = 0; useIdx < numUsePos; useIdx++) {
677                        TraceInterval.RegisterPriority priority = interval.getUsePosRegisterPriority(useIdx);
678                        if (priority == TraceInterval.RegisterPriority.ShouldHaveRegister) {
679                            return null;
680                        }
681                    }
682                }
683                return (JavaConstant) move.getConstant();
684            }
685        }
686        return null;
687    }
688
689}
690